添加角色页面.
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