feat: 菜单管理
This commit is contained in:
parent
f55dbe5c60
commit
89eabbeb86
@ -65,7 +65,7 @@ module.exports = {
|
|||||||
"vue/no-mutating-props": "error", // 不允许改变组件 prop
|
"vue/no-mutating-props": "error", // 不允许改变组件 prop
|
||||||
"vue/custom-event-name-casing": "error", // 为自定义事件名称强制使用特定大小写
|
"vue/custom-event-name-casing": "error", // 为自定义事件名称强制使用特定大小写
|
||||||
"vue/html-closing-bracket-newline": "error", // 在标签的右括号之前要求或禁止换行
|
"vue/html-closing-bracket-newline": "error", // 在标签的右括号之前要求或禁止换行
|
||||||
"vue/attribute-hyphenation": "error", // 对模板中的自定义组件强制执行属性命名样式:my-prop="prop"
|
"vue/attribute-hyphenation": "off", // 对模板中的自定义组件强制执行kebab-case命名:my-prop="prop"
|
||||||
"vue/attributes-order": "off", // vue api使用顺序,强制执行属性顺序
|
"vue/attributes-order": "off", // vue api使用顺序,强制执行属性顺序
|
||||||
"vue/no-v-html": "off", // 禁止使用 v-html
|
"vue/no-v-html": "off", // 禁止使用 v-html
|
||||||
"vue/require-default-prop": "off", // 此规则要求为每个 prop 为必填时,必须提供默认值
|
"vue/require-default-prop": "off", // 此规则要求为每个 prop 为必填时,必须提供默认值
|
||||||
|
|||||||
@ -47,3 +47,12 @@ export const getMenuListAPI = () => {
|
|||||||
method: "get"
|
method: "get"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 根据角色获取权限数据
|
||||||
|
export const getUserPermissionAPI = (params: { role: string }) => {
|
||||||
|
return axios({
|
||||||
|
url: "/mock/menu/getUserPermission",
|
||||||
|
method: "get",
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@ -122,7 +122,7 @@ export const roleData = [
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "超级管理员",
|
name: "超级管理员",
|
||||||
key: "admin",
|
code: "admin",
|
||||||
sort: 1,
|
sort: 1,
|
||||||
status: 1,
|
status: 1,
|
||||||
admin: true,
|
admin: true,
|
||||||
@ -135,7 +135,7 @@ export const roleData = [
|
|||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "普通员工",
|
name: "普通员工",
|
||||||
key: "common",
|
code: "common",
|
||||||
sort: 2,
|
sort: 2,
|
||||||
status: 1,
|
status: 1,
|
||||||
admin: false,
|
admin: false,
|
||||||
|
|||||||
@ -19,7 +19,12 @@ import { systemMenu, permissionData } from "../_data/system_menu";
|
|||||||
* 5、缓存一维路由
|
* 5、缓存一维路由
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// post请求body,get请求query
|
/**
|
||||||
|
* post请求body,get请求query
|
||||||
|
* /mock/menu/getMenu 获取菜单数据
|
||||||
|
* /mock/menu/getMenuList 获取菜单列表数据-菜单管理
|
||||||
|
* /mock/menu/getUserPermission 根据角色获取权限数据
|
||||||
|
*/
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
url: "/mock/menu/getMenu",
|
url: "/mock/menu/getMenu",
|
||||||
@ -51,5 +56,18 @@ export default [
|
|||||||
// 3. 返回路由树
|
// 3. 返回路由树
|
||||||
return resultSuccess(treeSort(buildTreeOptimized(originMenu)));
|
return resultSuccess(treeSort(buildTreeOptimized(originMenu)));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: "/mock/menu/getUserPermission",
|
||||||
|
method: "get",
|
||||||
|
timeout: 300,
|
||||||
|
response: ({ query }: any) => {
|
||||||
|
let { role } = query;
|
||||||
|
// 将扁平路由和权限菜单合并
|
||||||
|
const originMenu: any = [...deepClone(systemMenu), ...deepClone(permissionData)];
|
||||||
|
// 根据角色过滤id
|
||||||
|
let idList = originMenu.filter((item: any) => item.meta.roles.includes(role)).map((item: any) => item.id);
|
||||||
|
return resultSuccess(idList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
] as MockMethod[];
|
] as MockMethod[];
|
||||||
|
|||||||
@ -2,7 +2,13 @@ import type { MockMethod } from "vite-plugin-mock";
|
|||||||
import { resultSuccess } from "../_utils";
|
import { resultSuccess } from "../_utils";
|
||||||
import { dictData, divisionData, roleData, accountData } from "../_data/system_data";
|
import { dictData, divisionData, roleData, accountData } from "../_data/system_data";
|
||||||
|
|
||||||
// post请求body,get请求query
|
/**
|
||||||
|
* post请求body,get请求query
|
||||||
|
* /mock/system/getDict 获取字典数据
|
||||||
|
* /mock/system/getDivision 获取部门数据
|
||||||
|
* /mock/system/getRole 获取角色数据
|
||||||
|
* /mock/system/getAccount 获取账号数据
|
||||||
|
*/
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
url: "/mock/system/getDict",
|
url: "/mock/system/getDict",
|
||||||
|
|||||||
@ -267,7 +267,7 @@
|
|||||||
<a-form-item field="link" label="外链路径" validate-trigger="blur" v-if="addFrom.type == 2 && addFrom.isLink">
|
<a-form-item field="link" label="外链路径" validate-trigger="blur" v-if="addFrom.type == 2 && addFrom.isLink">
|
||||||
<a-input v-model="addFrom.link" placeholder="请输入路由路径" allow-clear />
|
<a-input v-model="addFrom.link" placeholder="请输入路由路径" allow-clear />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="link" label="菜单排序" validate-trigger="blur">
|
<a-form-item field="sort" label="菜单排序" validate-trigger="blur">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-model="addFrom.sort"
|
v-model="addFrom.sort"
|
||||||
:step="1"
|
:step="1"
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
<template #icon><icon-plus /></template>
|
<template #icon><icon-plus /></template>
|
||||||
<span>新增</span>
|
<span>新增</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" status="danger" @click="onDelete">
|
<a-button type="primary" status="danger">
|
||||||
<template #icon><icon-delete /></template>
|
<template #icon><icon-delete /></template>
|
||||||
<span>删除</span>
|
<span>删除</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<template #cell="cell">{{ cell.rowIndex + 1 }}</template>
|
<template #cell="cell">{{ cell.rowIndex + 1 }}</template>
|
||||||
</a-table-column>
|
</a-table-column>
|
||||||
<a-table-column title="角色名称" data-index="name"></a-table-column>
|
<a-table-column title="角色名称" data-index="name"></a-table-column>
|
||||||
<a-table-column title="角色标识" data-index="key"></a-table-column>
|
<a-table-column title="角色标识" data-index="code"></a-table-column>
|
||||||
<a-table-column title="排序" data-index="sort" :width="100"></a-table-column>
|
<a-table-column title="排序" data-index="sort" :width="100"></a-table-column>
|
||||||
<a-table-column title="状态" :width="100" align="center">
|
<a-table-column title="状态" :width="100" align="center">
|
||||||
<template #cell="{ record }">
|
<template #cell="{ record }">
|
||||||
@ -61,51 +61,115 @@
|
|||||||
<a-table-column title="操作" :width="280" align="center" :fixed="'right'">
|
<a-table-column title="操作" :width="280" align="center" :fixed="'right'">
|
||||||
<template #cell="{ record }">
|
<template #cell="{ record }">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button type="primary" size="mini">
|
<a-button type="primary" status="success" size="mini" :disabled="record.admin" @click="onPrivileges(record)">
|
||||||
|
<template #icon><icon-safe /></template>
|
||||||
|
<span>分配权限</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" size="mini" :disabled="record.admin" @click="onUpdate(record)">
|
||||||
<template #icon><icon-edit /></template>
|
<template #icon><icon-edit /></template>
|
||||||
<span>修改</span>
|
<span>修改</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-popconfirm type="warning" content="确定删除该角色吗?">
|
<a-popconfirm type="warning" content="确定删除该角色吗?">
|
||||||
<a-button type="primary" status="danger" size="mini">
|
<a-button type="primary" status="danger" size="mini" :disabled="record.admin">
|
||||||
<template #icon><icon-delete /></template>
|
<template #icon><icon-delete /></template>
|
||||||
<span>删除</span>
|
<span>删除</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
<a-dropdown trigger="hover">
|
|
||||||
<a-button type="primary" status="success" size="mini">
|
|
||||||
<template #icon><icon-double-right /></template>
|
|
||||||
<span>分配</span>
|
|
||||||
</a-button>
|
|
||||||
<template #content>
|
|
||||||
<a-doption>
|
|
||||||
<template #default>
|
|
||||||
<a-button type="primary" status="success" size="mini" @click="onPrivileges(record)">
|
|
||||||
<template #icon><icon-safe /></template>
|
|
||||||
<span>分配权限</span>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
</a-doption>
|
|
||||||
<a-doption>
|
|
||||||
<template #default>
|
|
||||||
<a-button type="primary" size="mini" @click="onUsers(record)">
|
|
||||||
<template #icon><icon-user /></template>
|
|
||||||
<span>分配账户</span>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
</a-doption>
|
|
||||||
</template>
|
|
||||||
</a-dropdown>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</a-table-column>
|
</a-table-column>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
|
<a-modal width="40%" v-model:visible="open" @close="afterClose" @ok="handleOk" @cancel="afterClose">
|
||||||
|
<template #title> {{ title }} </template>
|
||||||
|
<div>
|
||||||
|
<a-form ref="formRef" auto-label-width :rules="rules" :model="addFrom">
|
||||||
|
<a-form-item field="name" label="角色名称" validate-trigger="blur">
|
||||||
|
<a-input v-model="addFrom.name" placeholder="请输入角色名称" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="code" label="角色编码" validate-trigger="blur">
|
||||||
|
<a-input v-model="addFrom.code" placeholder="请输入角色编码" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="status" label="状态" validate-trigger="blur">
|
||||||
|
<a-switch type="round" :checked-value="1" :unchecked-value="0" v-model="addFrom.status">
|
||||||
|
<template #checked> 启用 </template>
|
||||||
|
<template #unchecked> 禁用 </template>
|
||||||
|
</a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="sort" label="排序" validate-trigger="blur">
|
||||||
|
<a-input-number
|
||||||
|
v-model="addFrom.sort"
|
||||||
|
:step="1"
|
||||||
|
:precision="0"
|
||||||
|
:min="1"
|
||||||
|
:max="9999"
|
||||||
|
:style="{ width: '150px' }"
|
||||||
|
placeholder="请输入"
|
||||||
|
mode="button"
|
||||||
|
class="input-demo"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item field="description" label="描述" validate-trigger="blur">
|
||||||
|
<a-textarea v-model="addFrom.description" placeholder="请输入描述" allow-clear />
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<a-drawer :visible="drawerOpen" :width="500" @ok="drawerOk" @cancel="drawerCancel">
|
||||||
|
<template #title> 分配权限 </template>
|
||||||
|
<div>
|
||||||
|
<a-card>
|
||||||
|
<a-row :gutter="24" justify="center">
|
||||||
|
<a-col :span="8">
|
||||||
|
<span class="text-right-gap">展开全部</span>
|
||||||
|
<a-switch type="round" v-model="treeSwitch.expandAll" @change="onExpandAll">
|
||||||
|
<template #checked> 是 </template>
|
||||||
|
<template #unchecked> 否 </template>
|
||||||
|
</a-switch>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<span class="text-right-gap">全选节点</span>
|
||||||
|
<a-switch type="round" v-model="treeSwitch.selectAll" @change="onSelectAll">
|
||||||
|
<template #checked> 是 </template>
|
||||||
|
<template #unchecked> 否 </template>
|
||||||
|
</a-switch>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<a-tooltip
|
||||||
|
content="权限树的父子节点独立,因为若节点关联,父节点会存在半选情况,半选节点的ID不会返回,会导致目录无法渲染"
|
||||||
|
>
|
||||||
|
<span>父子关联 <icon-question-circle-fill /></span>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-tree
|
||||||
|
ref="treeRef"
|
||||||
|
:fieldNames="{
|
||||||
|
key: 'id',
|
||||||
|
title: 'i18n',
|
||||||
|
children: 'children'
|
||||||
|
}"
|
||||||
|
:check-strictly="true"
|
||||||
|
:checkable="true"
|
||||||
|
:show-line="true"
|
||||||
|
:unmount-on-close="true"
|
||||||
|
v-model:checked-keys="permissionKeys"
|
||||||
|
:data="permissionTree"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getRoleAPI } from "@/api/modules/system/index";
|
import { getRoleAPI, getMenuListAPI, getUserPermissionAPI } from "@/api/modules/system/index";
|
||||||
|
import { deepClone } from "@/utils";
|
||||||
|
import useGlobalProperties from "@/hooks/useGlobalProperties";
|
||||||
|
const proxy = useGlobalProperties();
|
||||||
const openState = ref(dictFilter("status"));
|
const openState = ref(dictFilter("status"));
|
||||||
const form = ref({
|
const form = ref({
|
||||||
name: "",
|
name: "",
|
||||||
@ -113,33 +177,77 @@ const form = ref({
|
|||||||
status: null
|
status: null
|
||||||
});
|
});
|
||||||
const search = () => {
|
const search = () => {
|
||||||
console.log("搜索");
|
getRole();
|
||||||
};
|
};
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
console.log("重置");
|
form.value = {
|
||||||
|
name: "",
|
||||||
|
code: "",
|
||||||
|
status: null
|
||||||
|
};
|
||||||
|
getRole();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const open = ref(false);
|
||||||
|
const rules = {
|
||||||
|
name: [{ required: true, message: "请输入角色名称" }],
|
||||||
|
code: [{ required: true, message: "请输入角色编码" }],
|
||||||
|
status: [{ required: true, message: "请选择状态" }]
|
||||||
|
};
|
||||||
|
const addFrom = ref<any>({
|
||||||
|
name: "",
|
||||||
|
code: "",
|
||||||
|
status: 1,
|
||||||
|
sort: 1,
|
||||||
|
description: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const title = ref("");
|
||||||
|
const formRef = ref();
|
||||||
const onAdd = () => {
|
const onAdd = () => {
|
||||||
console.log("新增");
|
title.value = "新增角色";
|
||||||
|
open.value = true;
|
||||||
};
|
};
|
||||||
const onDelete = () => {
|
const handleOk = async () => {
|
||||||
console.log("删除");
|
let state = await formRef.value.validate();
|
||||||
|
if (state) return (open.value = true); // 校验不通过
|
||||||
|
arcoMessage("success", "模拟提交成功");
|
||||||
};
|
};
|
||||||
|
// 关闭对话框动画结束后触发
|
||||||
|
const afterClose = () => {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
addFrom.value = {
|
||||||
|
name: "",
|
||||||
|
code: "",
|
||||||
|
status: 1,
|
||||||
|
sort: 1,
|
||||||
|
description: ""
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// 修改角色
|
||||||
|
const onUpdate = (row: any) => {
|
||||||
|
title.value = "修改角色";
|
||||||
|
addFrom.value = deepClone(row);
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取列表
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const pagination = ref({
|
const pagination = ref({
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
showPageSize: true
|
showPageSize: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const onPrivileges = (e: any) => {
|
|
||||||
console.log("分配权限", e);
|
|
||||||
};
|
|
||||||
const onUsers = (e: any) => {
|
|
||||||
console.log("分配账户", e);
|
|
||||||
};
|
|
||||||
const roleList = ref([]);
|
const roleList = ref([]);
|
||||||
const getRole = async () => {
|
const getRole = async () => {
|
||||||
let res = await getRoleAPI();
|
try {
|
||||||
roleList.value = res.data;
|
loading.value = true;
|
||||||
|
let res = await getRoleAPI();
|
||||||
|
res.data.forEach((item: any) => item.admin && (item.disabled = true));
|
||||||
|
roleList.value = res.data;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const selectedKeys = ref([]);
|
const selectedKeys = ref([]);
|
||||||
const select = (list: []) => {
|
const select = (list: []) => {
|
||||||
@ -149,7 +257,71 @@ const selectAll = (state: boolean) => {
|
|||||||
selectedKeys.value = state ? (roleList.value.map((el: any) => el.id) as []) : [];
|
selectedKeys.value = state ? (roleList.value.map((el: any) => el.id) as []) : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 获取权限树
|
||||||
|
const treeRef = ref();
|
||||||
|
const treeSwitch = ref({
|
||||||
|
expandAll: true, // 展开全部
|
||||||
|
selectAll: false // 全选
|
||||||
|
});
|
||||||
|
// 展开全部
|
||||||
|
const onExpandAll = (state: boolean) => {
|
||||||
|
treeRef.value.expandAll(state);
|
||||||
|
};
|
||||||
|
// 全选
|
||||||
|
const onSelectAll = (state: boolean) => {
|
||||||
|
treeRef.value.checkAll(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重置节点操作开关
|
||||||
|
const treeSwitchReset = () => {
|
||||||
|
treeSwitch.value = {
|
||||||
|
expandAll: true, // 固定
|
||||||
|
selectAll: false // 全选
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const permissionTree = ref([]);
|
||||||
|
const permissionKeys = ref([]);
|
||||||
|
const getMenuList = async () => {
|
||||||
|
let { data } = await getMenuListAPI();
|
||||||
|
translation(data);
|
||||||
|
permissionTree.value = data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 分配权限
|
||||||
|
const drawerOpen = ref(false);
|
||||||
|
const onPrivileges = async (row: any) => {
|
||||||
|
let res = await getUserPermissionAPI({ role: row.code });
|
||||||
|
permissionKeys.value = res.data;
|
||||||
|
drawerOpen.value = true;
|
||||||
|
treeRef.value.expandAll(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawerOk = () => {
|
||||||
|
console.log("drawerOk", permissionKeys.value);
|
||||||
|
drawerOpen.value = false;
|
||||||
|
treeSwitchReset();
|
||||||
|
};
|
||||||
|
const drawerCancel = () => {
|
||||||
|
drawerOpen.value = false;
|
||||||
|
treeSwitchReset();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 语言转化
|
||||||
|
const translation = (tree: any) => {
|
||||||
|
tree.forEach((item: any) => {
|
||||||
|
if (item.children) translation(item.children);
|
||||||
|
if (item.meta.title) {
|
||||||
|
item.i18n = proxy.$t(`menu.${item.meta.title}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
getRole();
|
getRole();
|
||||||
|
getMenuList();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.text-right-gap {
|
||||||
|
margin-right: $margin;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user