添加角色页面.
This commit is contained in:
114
orion-ops-ui/src/api/user/role.ts
Normal file
114
orion-ops-ui/src/api/user/role.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import axios from 'axios';
|
||||
import { DataGrid, Pagination } from '@/types/global';
|
||||
|
||||
/**
|
||||
* 角色创建请求
|
||||
*/
|
||||
export interface RoleCreateRequest {
|
||||
name?: string;
|
||||
code?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色更新请求
|
||||
*/
|
||||
export interface RoleUpdateRequest extends RoleCreateRequest {
|
||||
id: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色 菜单绑定请求
|
||||
*/
|
||||
export interface RoleMenuBindRequest extends RoleCreateRequest {
|
||||
roleId: number;
|
||||
menuIdList: Array<number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色查询请求
|
||||
*/
|
||||
export interface RoleQueryRequest extends Pagination {
|
||||
id?: number;
|
||||
name?: string;
|
||||
code?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色查询响应
|
||||
*/
|
||||
export interface RoleQueryResponse {
|
||||
id?: number;
|
||||
name?: string;
|
||||
code?: string;
|
||||
status?: number;
|
||||
createTime: number;
|
||||
updateTime: number;
|
||||
creator: string;
|
||||
updater: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
*/
|
||||
export function createRole(request: RoleCreateRequest) {
|
||||
return axios.post('/infra/system-role/create', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 id 更新角色
|
||||
*/
|
||||
export function updateRole(request: RoleUpdateRequest) {
|
||||
return axios.put('/infra/system-role/update', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 id 更新角色状态
|
||||
*/
|
||||
export function updateRoleStatus(request: RoleUpdateRequest) {
|
||||
return axios.put('/infra/system-role/update-status', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 id 查询角色
|
||||
*/
|
||||
export function getRole(id: number) {
|
||||
return axios.get<RoleQueryResponse>('/infra/system-role/get', { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有角色
|
||||
*/
|
||||
export function getRoleList() {
|
||||
return axios.get<RoleQueryResponse[]>('/infra/system-role/list');
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询角色
|
||||
*/
|
||||
export function getRolePage(request: RoleQueryRequest) {
|
||||
return axios.post<DataGrid<RoleQueryResponse>>('/infra/system-role/query', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 id 删除角色
|
||||
*/
|
||||
export function deleteRole(id: number) {
|
||||
return axios.delete('/infra/system-role/delete', { params: { id } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定角色菜单
|
||||
*/
|
||||
export function bindRoleMenu(request: RoleMenuBindRequest) {
|
||||
return axios.put('/infra/system-role/bind', request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色菜单id
|
||||
*/
|
||||
export function getRoleMenuId(roleId: number) {
|
||||
return axios.get<Array<number>>('/infra/system-role/get-menu-id', { params: { roleId } });
|
||||
}
|
||||
|
||||
147
orion-ops-ui/src/components/menu/selector/menu-selector-tree.vue
Normal file
147
orion-ops-ui/src/components/menu/selector/menu-selector-tree.vue
Normal file
@@ -0,0 +1,147 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 按钮组 -->
|
||||
<a-button-group class="mb4">
|
||||
<!-- 全选 -->
|
||||
<a-button type="text" size="mini" @click="toggleChecked">
|
||||
{{ checkedKeys?.length === allCheckedKeys?.length ? '反选' : '全选' }}
|
||||
</a-button>
|
||||
<!-- 展开 -->
|
||||
<a-button type="text" size="mini" @click="toggleExpanded">
|
||||
{{ expandedKeys?.length ? '折叠' : '展开' }}
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
<!-- 菜单树 -->
|
||||
<a-tree
|
||||
checked-strategy="child"
|
||||
:checkable="true"
|
||||
:animation="false"
|
||||
v-model:checked-keys="checkedKeys"
|
||||
v-model:expanded-keys="expandedKeys"
|
||||
:data="treeData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'menu-selector-tree'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { TreeNodeData } from '@arco-design/web-vue';
|
||||
import { useCacheStore } from '@/store';
|
||||
|
||||
const treeData = ref<Array<TreeNodeData>>([]);
|
||||
|
||||
const allCheckedKeys = ref<Array<number>>([]);
|
||||
const allExpandedKeys = ref<Array<number>>([]);
|
||||
|
||||
const checkedKeys = ref<Array<number>>([]);
|
||||
const expandedKeys = ref<Array<number>>([]);
|
||||
|
||||
// 修改选中状态
|
||||
const toggleChecked = () => {
|
||||
checkedKeys.value = checkedKeys.value.length === allCheckedKeys.value.length ? [] : allCheckedKeys.value;
|
||||
};
|
||||
|
||||
// 修改折叠状态
|
||||
const toggleExpanded = () => {
|
||||
expandedKeys.value = expandedKeys?.value.length ? [] : allExpandedKeys.value;
|
||||
};
|
||||
|
||||
// 循环选中的 key
|
||||
const eachAllCheckKeys = (arr: Array<any>) => {
|
||||
arr.forEach((item) => {
|
||||
allCheckedKeys.value.push(item.key);
|
||||
if (item.children && item.children.length) {
|
||||
eachAllCheckKeys(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 循环展开的 key
|
||||
const eachAllExpandKeys = (arr: Array<any>) => {
|
||||
arr.forEach((item) => {
|
||||
if (item.children && item.children.length) {
|
||||
allExpandedKeys.value.push(item.key);
|
||||
eachAllExpandKeys(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 渲染数据
|
||||
const init = (keys: Array<number>) => {
|
||||
// 初始化数据
|
||||
allCheckedKeys.value = [];
|
||||
allExpandedKeys.value = [];
|
||||
checkedKeys.value = keys;
|
||||
expandedKeys.value = [];
|
||||
|
||||
// 渲染菜单
|
||||
const cacheStore = useCacheStore();
|
||||
let render = (arr: any[]): TreeNodeData[] => {
|
||||
return arr.map((s) => {
|
||||
// 当前节点
|
||||
const node = {
|
||||
key: s.id,
|
||||
title: s.name,
|
||||
children: undefined as unknown
|
||||
} as TreeNodeData;
|
||||
// 子节点
|
||||
if (s.children && s.children.length) {
|
||||
node.children = render(s.children);
|
||||
}
|
||||
return node;
|
||||
}).filter(Boolean);
|
||||
};
|
||||
|
||||
// 加载菜单
|
||||
treeData.value = render([...cacheStore.menus]);
|
||||
// 加载所有选中的key
|
||||
eachAllCheckKeys(treeData.value);
|
||||
// 加载所有展开的key
|
||||
eachAllExpandKeys(treeData.value);
|
||||
};
|
||||
init();
|
||||
|
||||
// 获取值
|
||||
const getValue = () => {
|
||||
if (!checkedKeys.value.length) {
|
||||
return [];
|
||||
}
|
||||
// 查询子节点上级父节点
|
||||
const mixed: number[] = [];
|
||||
const findParent = (arr: Array<TreeNodeData>, key: number) => {
|
||||
for (let node of arr) {
|
||||
// 是子节点 并且相同
|
||||
if (node.key === key) {
|
||||
mixed.push(key);
|
||||
return true;
|
||||
}
|
||||
if (node.children?.length) {
|
||||
const isFind = findParent(node.children, key);
|
||||
if (isFind) {
|
||||
mixed.push(node.key as number);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// 设置所有节点
|
||||
for (let key of checkedKeys.value) {
|
||||
findParent(treeData.value, key);
|
||||
}
|
||||
return new Set(mixed);
|
||||
};
|
||||
|
||||
defineExpose({ init, getValue });
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>;
|
||||
@@ -1,15 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>UserChild 1</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UserChild1',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>UserChild 2</p>
|
||||
<h1 v-permission="['admin']">123</h1>
|
||||
<button @click="red">red</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import router from '@/router';
|
||||
|
||||
function red() {
|
||||
router.push({ name: 'workplace' });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
138
orion-ops-ui/src/views/user/role/components/role-form-modal.vue
Normal file
138
orion-ops-ui/src/views/user/role/components/role-form-modal.vue
Normal file
@@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible"
|
||||
body-class="modal-form"
|
||||
title-align="start"
|
||||
:title="title"
|
||||
:top="120"
|
||||
:align-center="false"
|
||||
:draggable="true"
|
||||
:mask-closable="false"
|
||||
:unmount-on-close="true"
|
||||
:ok-button-props="{ disabled: loading }"
|
||||
:cancel-button-props="{ disabled: loading }"
|
||||
:on-before-ok="handlerOk"
|
||||
@close="handleClose">
|
||||
<a-spin :loading="loading">
|
||||
<a-form :model="formModel"
|
||||
ref="formRef"
|
||||
label-align="right"
|
||||
:style="{ width: '460px' }"
|
||||
:label-col-props="{ span: 6 }"
|
||||
:wrapper-col-props="{ span: 18 }"
|
||||
:rules="formRules">
|
||||
<!-- 角色名称 -->
|
||||
<a-form-item field="name" label="角色名称">
|
||||
<a-input v-model="formModel.name" placeholder="请输入角色名称" />
|
||||
</a-form-item>
|
||||
<!-- 角色编码 -->
|
||||
<a-form-item field="code" label="角色编码" v-if="isAddHandle">
|
||||
<a-input v-model="formModel.code" placeholder="请输入角色编码" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'user-role-form-modal'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import formRules from '../types/form.rules';
|
||||
import { createRole, updateRole } from '@/api/user/role';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const title = ref<string>();
|
||||
const isAddHandle = ref<boolean>(true);
|
||||
|
||||
const defaultForm = () => {
|
||||
return {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const formRef = ref<any>();
|
||||
const formModel = reactive<Record<string, any>>(defaultForm());
|
||||
|
||||
const emits = defineEmits(['added', 'updated']);
|
||||
|
||||
// 打开新增
|
||||
const openAdd = () => {
|
||||
title.value = '添加角色';
|
||||
isAddHandle.value = true;
|
||||
renderForm({ ...defaultForm() });
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
// 打开修改
|
||||
const openUpdate = (record: any) => {
|
||||
title.value = '修改角色';
|
||||
isAddHandle.value = false;
|
||||
renderForm({ ...defaultForm(), ...record });
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
// 渲染表单
|
||||
const renderForm = (record: any) => {
|
||||
Object.keys(formModel).forEach(k => {
|
||||
formModel[k] = record[k];
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ openAdd, openUpdate });
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 验证参数
|
||||
const error = await formRef.value.validate();
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
if (isAddHandle.value) {
|
||||
// 新增
|
||||
await createRole(formModel as any);
|
||||
Message.success('创建成功');
|
||||
emits('added');
|
||||
} else {
|
||||
// 修改
|
||||
await updateRole(formModel as any);
|
||||
Message.success('修改成功');
|
||||
emits('updated');
|
||||
}
|
||||
// 清空
|
||||
handlerClear();
|
||||
} catch (e) {
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭
|
||||
const handleClose = () => {
|
||||
handlerClear();
|
||||
};
|
||||
|
||||
// 清空
|
||||
const handlerClear = () => {
|
||||
setLoading(false);
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<a-modal v-model:visible="visible"
|
||||
body-class="modal-form"
|
||||
title-align="start"
|
||||
title="绑定菜单"
|
||||
:top="80"
|
||||
:width="480"
|
||||
:body-style="{padding: '16px 16px 0 16px'}"
|
||||
:align-center="false"
|
||||
:draggable="true"
|
||||
:mask-closable="false"
|
||||
:unmount-on-close="true"
|
||||
:ok-button-props="{ disabled: loading }"
|
||||
:cancel-button-props="{ disabled: loading }"
|
||||
:on-before-ok="handlerOk"
|
||||
@close="handleClose">
|
||||
<a-spin :loading="loading">
|
||||
<div class="role-menu-wrapper">
|
||||
<!-- 角色名称 -->
|
||||
<div class="item-wrapper">
|
||||
<span class="item-label">角色名称</span>
|
||||
<span class="item-value ml4">{{ roleRecord.name }}</span>
|
||||
</div>
|
||||
<!-- 角色编码 -->
|
||||
<div class="item-wrapper">
|
||||
<span class="item-label">角色编码</span>
|
||||
<a-tag class="item-value">{{ roleRecord.code }}</a-tag>
|
||||
</div>
|
||||
<!-- 菜单 -->
|
||||
<div class="menu-tree-wrapper item-wrapper">
|
||||
<span class="item-label">菜单选择</span>
|
||||
<menu-selector-tree ref="tree" class="item-value" />
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'role-menu-bind-modal'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MenuSelectorTree from '@/components/menu/selector/menu-selector-tree.vue';
|
||||
|
||||
import { reactive, ref } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useVisible from '@/hooks/visible';
|
||||
import { getRoleMenuId, bindRoleMenu, RoleMenuBindRequest } from '@/api/user/role';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useCacheStore } from '@/store';
|
||||
import { getMenuList } from '@/api/system/menu';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
const { loading, setLoading } = useLoading();
|
||||
|
||||
const tree = ref<any>();
|
||||
const roleRecord = reactive<Record<string, any>>({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
});
|
||||
|
||||
// 打开新增
|
||||
const open = async (record: any) => {
|
||||
renderRecord(record);
|
||||
setVisible(true);
|
||||
try {
|
||||
setLoading(true);
|
||||
// 获取菜单列表
|
||||
const cacheStore = useCacheStore();
|
||||
if (!cacheStore.menus?.length) {
|
||||
// 加载菜单
|
||||
const { data: menuData } = await getMenuList({});
|
||||
cacheStore.updateMenu(menuData);
|
||||
}
|
||||
// 获取角色菜单
|
||||
const { data: roleMenuIdList } = await getRoleMenuId(record.id);
|
||||
tree.value.init(roleMenuIdList);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染对象
|
||||
const renderRecord = (record: any) => {
|
||||
Object.keys(roleRecord).forEach(k => {
|
||||
roleRecord[k] = record[k];
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
// 确定
|
||||
const handlerOk = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 修改
|
||||
await bindRoleMenu({
|
||||
roleId: roleRecord.id,
|
||||
menuIdList: [...tree.value.getValue()]
|
||||
} as RoleMenuBindRequest);
|
||||
Message.success('绑定成功');
|
||||
// 清空
|
||||
handlerClear();
|
||||
} catch (e) {
|
||||
return false;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭
|
||||
const handleClose = () => {
|
||||
handlerClear();
|
||||
};
|
||||
|
||||
// 清空
|
||||
const handlerClear = () => {
|
||||
setLoading(false);
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.role-menu-wrapper {
|
||||
min-width: 400px;
|
||||
max-height: calc(100vh - 285px);
|
||||
|
||||
.item-wrapper {
|
||||
display: flex;
|
||||
margin-bottom: 16px;
|
||||
align-items: baseline;
|
||||
|
||||
.item-label {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
width: 80px;
|
||||
height: 32px;
|
||||
margin-right: 12px;
|
||||
line-height: 32px;
|
||||
color: var(--color-text-2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
215
orion-ops-ui/src/views/user/role/components/role-table.vue
Normal file
215
orion-ops-ui/src/views/user/role/components/role-table.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<a-card class="general-card table-search-card">
|
||||
<a-query-header :model="formModel"
|
||||
label-align="left"
|
||||
@submit="fetchTableData"
|
||||
@reset="fetchTableData">
|
||||
<!-- 角色名称 -->
|
||||
<a-form-item field="name" label="角色名称" label-col-flex="50px">
|
||||
<a-input v-model="formModel.name" placeholder="请输入角色名称" allow-clear />
|
||||
</a-form-item>
|
||||
<!-- 角色编码 -->
|
||||
<a-form-item field="code" label="角色编码" label-col-flex="50px">
|
||||
<a-input v-model="formModel.code" placeholder="请输入角色编码" allow-clear />
|
||||
</a-form-item>
|
||||
<!-- 角色状态 -->
|
||||
<a-form-item field="status" label="角色状态" label-col-flex="50px">
|
||||
<a-select v-model="formModel.status"
|
||||
placeholder="请选择角色状态"
|
||||
:options="toOptions(ROLE_STATUS_ENUM)"
|
||||
allow-clear />
|
||||
</a-form-item>
|
||||
</a-query-header>
|
||||
</a-card>
|
||||
<!-- 表格 -->
|
||||
<a-card class="general-card table-card">
|
||||
<template #title>
|
||||
<!-- 左侧标题 -->
|
||||
<div class="table-title">
|
||||
角色列表
|
||||
</div>
|
||||
<!-- 右侧按钮 -->
|
||||
<div class="table-bar-handle">
|
||||
<a-space>
|
||||
<!-- 新增 -->
|
||||
<a-button type="primary"
|
||||
v-permission="['infra:system-role:create']"
|
||||
@click="emits('openAdd')">
|
||||
新增
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
</template>
|
||||
<!-- table -->
|
||||
<a-table row-key="id"
|
||||
class="table-wrapper-8"
|
||||
ref="tableRef"
|
||||
label-align="left"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableRenderData"
|
||||
:pagination="pagination"
|
||||
@page-change="(page) => fetchTableData(page, pagination.pageSize)"
|
||||
@page-size-change="(size) => fetchTableData(pagination.current, size)"
|
||||
:bordered="false">
|
||||
<!-- 编码 -->
|
||||
<template #code="{ record }">
|
||||
<a-tag>{{ record.code }}</a-tag>
|
||||
</template>
|
||||
<!-- 状态 -->
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="getEnumValue(record.status, ROLE_STATUS_ENUM,'color')">
|
||||
{{ getEnumValue(record.status, ROLE_STATUS_ENUM) }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<!-- 操作 -->
|
||||
<template #handle="{ record }">
|
||||
<div class="table-handle-wrapper">
|
||||
<!-- 修改状态 -->
|
||||
<a-popconfirm :content="`确定要${toggleEnumValue(record.status, ROLE_STATUS_ENUM, 'label')}当前角色吗?`"
|
||||
position="left"
|
||||
type="warning"
|
||||
@ok="toggleRoleStatus(record)">
|
||||
<a-button v-permission="['infra:system-role:delete']"
|
||||
:disabled="record.code === 'admin'"
|
||||
:status="toggleEnumValue(record.status, ROLE_STATUS_ENUM, 'status')"
|
||||
type="text"
|
||||
size="mini">
|
||||
{{ toggleEnumValue(record.status, ROLE_STATUS_ENUM, 'label') }}
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<!-- 绑定菜单 -->
|
||||
<a-button v-permission="['infra:system-menu:bind']"
|
||||
:disabled="record.code === 'admin'"
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="emits('openBind', record)">
|
||||
绑定菜单
|
||||
</a-button>
|
||||
<!-- 修改 -->
|
||||
<a-button v-permission="['infra:system-role:update']"
|
||||
type="text"
|
||||
size="mini"
|
||||
@click="emits('openUpdate', record)">
|
||||
修改
|
||||
</a-button>
|
||||
<!-- 删除 -->
|
||||
<a-popconfirm content="确认删除这条记录吗?"
|
||||
position="left"
|
||||
type="warning"
|
||||
@ok="deleteRow(record)">
|
||||
<a-button v-permission="['infra:system-role:delete']"
|
||||
:disabled="record.code === 'admin'"
|
||||
type="text"
|
||||
size="mini"
|
||||
status="danger">
|
||||
删除
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'user-role-table'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { deleteRole, getRolePage, updateRoleStatus, RoleQueryRequest, RoleQueryResponse } from '@/api/user/role';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import columns from '../types/table.columns';
|
||||
import { ROLE_STATUS_ENUM } from '../types/enum.types';
|
||||
import { toOptions, getEnumValue, toggleEnumValue, toggleEnum } from '@/utils/enum';
|
||||
import { defaultPagination } from '@/types/table';
|
||||
|
||||
const tableRenderData = ref<RoleQueryResponse[]>();
|
||||
const { loading, setLoading } = useLoading();
|
||||
const emits = defineEmits(['openAdd', 'openUpdate', 'openBind']);
|
||||
|
||||
const pagination = reactive(defaultPagination());
|
||||
|
||||
const formModel = reactive<RoleQueryRequest>({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
status: undefined,
|
||||
});
|
||||
|
||||
// 修改状态
|
||||
const toggleRoleStatus = async (record: any) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const toggleStatus = toggleEnum(record.status, ROLE_STATUS_ENUM);
|
||||
// 调用修改接口
|
||||
await updateRoleStatus({ id: record.id, status: toggleStatus.value });
|
||||
Message.success(`${toggleStatus.label}成功`);
|
||||
// 修改行状态
|
||||
record.status = toggleStatus.value;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除当前行
|
||||
const deleteRow = async ({ id }: { id: number }) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// 调用删除接口
|
||||
await deleteRole(id);
|
||||
Message.success('删除成功');
|
||||
// 重新加载数据
|
||||
await fetchTableData();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 添加后回调
|
||||
const addedCallback = () => {
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
// 更新后回调
|
||||
const updatedCallback = () => {
|
||||
fetchTableData();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
addedCallback, updatedCallback
|
||||
});
|
||||
|
||||
// 加载数据
|
||||
const doFetchTableData = async (request: RoleQueryRequest) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const { data } = await getRolePage(request);
|
||||
tableRenderData.value = data.rows;
|
||||
pagination.total = data.total;
|
||||
pagination.current = request.page;
|
||||
pagination.pageSize = request.limit;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 切换页码
|
||||
const fetchTableData = (page = 1, limit = pagination.pageSize, form = formModel) => {
|
||||
doFetchTableData({ page, limit, ...form });
|
||||
};
|
||||
fetchTableData();
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
44
orion-ops-ui/src/views/user/role/index.vue
Normal file
44
orion-ops-ui/src/views/user/role/index.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div class="layout-container">
|
||||
<!-- 表格 -->
|
||||
<role-table ref="table"
|
||||
@openAdd="() => modal.openAdd()"
|
||||
@openUpdate="(e) => modal.openUpdate(e)"
|
||||
@openBind="(e) => bindModal.open(e)" />
|
||||
<!-- 添加修改模态框 -->
|
||||
<role-form-modal ref="modal"
|
||||
@added="() => table.addedCallback()"
|
||||
@updated="() => table.updatedCallback()" />
|
||||
<!-- 角色菜单绑定模态框 -->
|
||||
<role-menu-bind-modal ref="bindModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'userRole'
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import RoleTable from './components/role-table.vue';
|
||||
import RoleFormModal from './components/role-form-modal.vue';
|
||||
import RoleMenuBindModal from '@/views/user/role/components/role-menu-bind-modal.vue';
|
||||
import { onUnmounted, ref } from 'vue';
|
||||
import { useCacheStore } from '@/store';
|
||||
|
||||
const table = ref();
|
||||
const modal = ref();
|
||||
const bindModal = ref();
|
||||
|
||||
// 卸载时清除 menu cache
|
||||
onUnmounted(() => {
|
||||
const cacheStore = useCacheStore();
|
||||
cacheStore.resetMenu();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
18
orion-ops-ui/src/views/user/role/types/enum.types.ts
Normal file
18
orion-ops-ui/src/views/user/role/types/enum.types.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 角色状态
|
||||
*/
|
||||
export const ROLE_STATUS_ENUM = {
|
||||
DISABLED: {
|
||||
value: 0,
|
||||
label: '停用',
|
||||
color: 'orange',
|
||||
status: 'danger',
|
||||
},
|
||||
ENABLED: {
|
||||
value: 1,
|
||||
label: '启用',
|
||||
color: 'blue',
|
||||
status: 'default',
|
||||
},
|
||||
};
|
||||
|
||||
22
orion-ops-ui/src/views/user/role/types/form.rules.ts
Normal file
22
orion-ops-ui/src/views/user/role/types/form.rules.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { FieldRule } from '@arco-design/web-vue';
|
||||
|
||||
export const name = [{
|
||||
required: true,
|
||||
message: '请输入角色名称'
|
||||
}, {
|
||||
maxLength: 32,
|
||||
message: '角色名称长度不能大于32位'
|
||||
}] as FieldRule[];
|
||||
|
||||
export const code = [{
|
||||
required: true,
|
||||
message: '请输入角色编码'
|
||||
}, {
|
||||
maxLength: 32,
|
||||
message: '角色编码长度不能大于32位'
|
||||
}] as FieldRule[];
|
||||
|
||||
export default {
|
||||
name,
|
||||
code,
|
||||
} as Record<string, FieldRule | FieldRule[]>;
|
||||
42
orion-ops-ui/src/views/user/role/types/table.columns.ts
Normal file
42
orion-ops-ui/src/views/user/role/types/table.columns.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table/interface';
|
||||
import { dateFormat } from '@/utils';
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'id',
|
||||
dataIndex: 'id',
|
||||
slotName: 'id',
|
||||
width: 70,
|
||||
align: 'left',
|
||||
fixed: 'left',
|
||||
}, {
|
||||
title: '角色名称',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
}, {
|
||||
title: '角色编码',
|
||||
dataIndex: 'code',
|
||||
slotName: 'code',
|
||||
}, {
|
||||
title: '角色状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
align: 'center',
|
||||
}, {
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
slotName: 'createTime',
|
||||
align: 'center',
|
||||
render: ({ record }) => {
|
||||
return dateFormat(new Date(record.createTime));
|
||||
},
|
||||
}, {
|
||||
title: '操作',
|
||||
slotName: 'handle',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
},
|
||||
] as TableColumnData[];
|
||||
|
||||
export default columns;
|
||||
Reference in New Issue
Block a user