feat:大修改
This commit is contained in:
parent
991736f760
commit
b727b0d998
2
auto-imports.d.ts
vendored
2
auto-imports.d.ts
vendored
@ -9,8 +9,10 @@ declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const addChildDict: typeof import('./src/api/dictApi')['addChildDict']
|
||||
const addChildMenu: typeof import('./src/api/menu')['addChildMenu']
|
||||
const addNewRole: typeof import('./src/api/roleApi')['addNewRole']
|
||||
const addParentDict: typeof import('./src/api/dictApi')['addParentDict']
|
||||
const addParentMenu: typeof import('./src/api/menu')['addParentMenu']
|
||||
const assignMenu: typeof import('./src/api/roleApi')['assignMenu']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
|
19
src/App.vue
19
src/App.vue
@ -12,6 +12,8 @@
|
||||
<script setup lang='ts'>
|
||||
import { type GlobalThemeOverrides, dateZhCN, zhCN } from "naive-ui";
|
||||
import InitAxiosInterceptors from "@/components/InitAxiosInterceptors.vue";
|
||||
import { useAppStore } from "@/store/app";
|
||||
import { generateRoutes } from "@/utils/permission";
|
||||
|
||||
const themeOverrides: GlobalThemeOverrides = {
|
||||
// 待配置主题色
|
||||
@ -22,6 +24,23 @@ const themeOverrides: GlobalThemeOverrides = {
|
||||
'primaryColorSuppl': '#5A95FF' // 介于主色和hover之间的蓝色
|
||||
},
|
||||
};
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
// 等待 Pinia 数据恢复
|
||||
router.isReady().then(() => {
|
||||
const appStore = useAppStore();
|
||||
console.log(appStore.menuApp.menuList, 'appStore.menuApp.menuList');
|
||||
|
||||
if (appStore.menuApp.menuList?.length) {
|
||||
const dynamicRoutes = generateRoutes(appStore.menuApp.menuList);
|
||||
dynamicRoutes.forEach(route => {
|
||||
router.addRoute('Layout', route); // 确保添加到正确的父路由
|
||||
});
|
||||
|
||||
// 重新匹配当前路由,避免 404
|
||||
router.replace(router.currentRoute.value.fullPath);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang='scss'></style>
|
||||
|
@ -4,7 +4,7 @@ interface UserListQuery {
|
||||
roleName?: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
userName?:string
|
||||
userName?: string;
|
||||
}
|
||||
interface UserListReturn {
|
||||
totalCount: number;
|
||||
@ -48,10 +48,37 @@ interface EnableRoleQuery {
|
||||
userId: string;
|
||||
roleName: string;
|
||||
}
|
||||
export function enableRole(data:EnableRoleQuery) {
|
||||
export function enableRole(data: EnableRoleQuery) {
|
||||
return http({
|
||||
url: "/api/v1/AdminRoleControllers/role",
|
||||
method: "POST",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
// 添加新角色
|
||||
interface NewRoleReq{
|
||||
rolename:string
|
||||
normalizedname:string
|
||||
}
|
||||
export function addNewRole(params:NewRoleReq){
|
||||
return http({
|
||||
url:'/api/v1/AdminRoleControllers/role',
|
||||
method:'POST',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
interface MenuRequest {
|
||||
id: string;
|
||||
menuName: string;
|
||||
menuCode: string;
|
||||
}
|
||||
// 分配菜单权限
|
||||
export function assignMenu(data:MenuRequest) {
|
||||
return http({
|
||||
url: "/api/v1/AdminRoleControllers/menu",
|
||||
method: "POST",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
1
src/assets/icons/column.svg
Normal file
1
src/assets/icons/column.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752906479986" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6024" id="mx_n_1752906479987" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M704 300.8H320c-10.666667 0-21.333333 8.533333-21.333333 21.333333v42.666667c0 10.666667 8.533333 21.333333 21.333333 21.333333h149.333333v320c0 10.666667 8.533333 21.333333 21.333334 21.333334h42.666666c10.666667 0 21.333333-8.533333 21.333334-21.333334v-320h149.333333c10.666667 0 21.333333-8.533333 21.333333-21.333333v-42.666667c-2.133333-12.8-10.666667-21.333333-21.333333-21.333333z" fill="#297AFF" p-id="6025"></path><path d="M810.666667 128H213.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333v597.333334c0 46.933333 38.4 85.333333 85.333333 85.333333h320c12.8 0 21.333333-8.533333 21.333334-21.333333v-42.666667c0-12.8-8.533333-21.333333-21.333334-21.333333H256c-23.466667 0-42.666667-19.2-42.666667-42.666667V256c0-23.466667 19.2-42.666667 42.666667-42.666667h512c23.466667 0 42.666667 19.2 42.666667 42.666667v277.333333c0 12.8 8.533333 21.333333 21.333333 21.333334h42.666667c12.8 0 21.333333-8.533333 21.333333-21.333334V213.333333c0-46.933333-38.4-85.333333-85.333333-85.333333z" fill="#297AFF" p-id="6026"></path><path d="M612.266667 800l206.933333-200.533333c8.533333-8.533333 25.6-8.533333 34.133333 0l34.133334 34.133333c8.533333 8.533333 8.533333 23.466667 0 34.133333l-209.066667 202.666667c-2.133333 2.133333-6.4 4.266667-10.666667 6.4l-68.266666 19.2c-8.533333 2.133333-17.066667-6.4-14.933334-14.933333l21.333334-70.4c2.133333-4.266667 4.266667-6.4 6.4-10.666667z" fill="#297AFF" p-id="6027"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
src/assets/icons/dict.svg
Normal file
1
src/assets/icons/dict.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752905819007" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4503" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M934.911577 142.847857A140.799859 140.799859 0 0 0 794.111718 0H427.008085a38.399962 38.399962 0 0 0 0 76.799923h63.999936v324.095676a38.399962 38.399962 0 0 0 63.487937 28.671971l85.503914-74.751925 80.895919 74.239926a38.399962 38.399962 0 0 0 26.111974 10.23999 37.887962 37.887962 0 0 0 15.359985-3.071997 38.399962 38.399962 0 0 0 23.039977-35.327965V78.847921h8.703991a63.999936 63.999936 0 0 1 63.999936 63.999936v582.655417H210.944301a147.455853 147.455853 0 0 0-72.703927 19.455981V142.847857A63.999936 63.999936 0 0 1 204.800307 78.847921h30.719969a38.399962 38.399962 0 0 0 0-76.799923H204.800307a140.799859 140.799859 0 0 0-143.359856 140.799859V870.39913a148.991851 148.991851 0 0 0 149.50385 153.599846h685.567314a38.399962 38.399962 0 0 0 0-76.799923c-41.471959 0-51.199949-38.911961-51.199948-71.167929a102.399898 102.399898 0 0 1 51.199948-71.679928 38.399962 38.399962 0 0 0 38.399962-38.399962 36.351964 36.351964 0 0 0 0-7.679992 36.351964 36.351964 0 0 0 0-7.679993z m-225.791774 171.007829L665.599846 274.943725a38.399962 38.399962 0 0 0-51.199948 0l-48.127952 41.983958V78.847921h141.311858z m73.727926 631.295369H210.944301a72.191928 72.191928 0 0 1-72.703927-70.14393 70.655929 70.655929 0 0 1 21.503978-51.199949 72.191928 72.191928 0 0 1 51.199949-21.503978h580.09542a143.359857 143.359857 0 0 0-21.503979 71.679928 190.46381 190.46381 0 0 0 12.799988 71.167929z" p-id="4504"></path></svg>
|
After Width: | Height: | Size: 1.7 KiB |
1
src/assets/icons/menu.svg
Normal file
1
src/assets/icons/menu.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752906446737" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4093" id="mx_n_1752906446738" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M133.310936 296.552327l757.206115 0c19.781623 0 35.950949-16.169326 35.950949-35.950949 0-19.781623-15.997312-35.950949-35.950949-35.950949L133.310936 224.650428c-19.781623 0-35.950949 16.169326-35.950949 35.950949C97.359987 280.383 113.529313 296.552327 133.310936 296.552327z" fill="#575B66" p-id="4094"></path><path d="M890.51705 476.135058 133.310936 476.135058c-19.781623 0-35.950949 16.169326-35.950949 35.950949 0 19.781623 16.169326 35.950949 35.950949 35.950949l757.206115 0c19.781623 0 35.950949-16.169326 35.950949-35.950949C926.467999 492.304384 910.298673 476.135058 890.51705 476.135058z" fill="#575B66" p-id="4095"></path><path d="M890.51705 727.447673 133.310936 727.447673c-19.781623 0-35.950949 15.997312-35.950949 35.950949s16.169326 35.950949 35.950949 35.950949l757.206115 0c19.781623 0 35.950949-15.997312 35.950949-35.950949S910.298673 727.447673 890.51705 727.447673z" fill="#575B66" p-id="4096"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
src/assets/icons/system.svg
Normal file
1
src/assets/icons/system.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752906142706" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8590" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M913.493333 464.426667a53.76 53.76 0 0 0-37.76-39.466667l-13.013333-2.986667a131.413333 131.413333 0 0 1-89.6-156.373333l3.84-12.373333a54.186667 54.186667 0 0 0-15.146667-52.48A369.92 369.92 0 0 0 714.666667 170.666667a372.693333 372.693333 0 0 0-49.92-25.6 53.546667 53.546667 0 0 0-52.906667 13.013333l-9.173333 9.813333a130.773333 130.773333 0 0 1-180.053334 0l-8.746666-9.6a53.546667 53.546667 0 0 0-52.906667-13.013333A370.56 370.56 0 0 0 310.613333 170.666667a369.493333 369.493333 0 0 0-47.146666 30.506666 53.76 53.76 0 0 0-15.36 52.48l3.84 12.373334a131.2 131.2 0 0 1-89.813334 155.946666l-12.8 2.986667a53.76 53.76 0 0 0-37.546666 39.466667 368.426667 368.426667 0 0 0-2.773334 56.32A366.506667 366.506667 0 0 0 111.786667 576 53.973333 53.973333 0 0 0 149.333333 616.533333l12.373334 2.773334a131.413333 131.413333 0 0 1 90.24 156.8l-3.84 12.373333a53.76 53.76 0 0 0 15.36 52.48 362.666667 362.666667 0 0 0 47.146666 30.506667A362.666667 362.666667 0 0 0 360.746667 896a53.76 53.76 0 0 0 52.906666-12.8l8.746667-8.533333a130.986667 130.986667 0 0 1 180.48 0l8.746667 9.386666a53.76 53.76 0 0 0 52.906666 12.8 384 384 0 0 0 50.133334-25.6 366.293333 366.293333 0 0 0 47.146666-30.506666 53.973333 53.973333 0 0 0 15.36-52.48l-3.84-12.8A131.413333 131.413333 0 0 1 863.573333 618.666667l12.373334-2.773334A53.546667 53.546667 0 0 0 913.706667 576a368 368 0 0 0 3.626666-55.466667 371.413333 371.413333 0 0 0-3.84-56.106666zM512 642.133333a130.133333 130.133333 0 1 1 129.706667-130.133333A129.92 129.92 0 0 1 512 642.133333z" p-id="8591"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
1
src/assets/icons/user.svg
Normal file
1
src/assets/icons/user.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752905909586" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5604" width="64" height="64" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 1022.1056c-282.3168 0-511.232-228.864-511.232-511.0784S229.632 0 512 0c282.3168 0 511.232 228.8128 511.232 511.0272 0 282.2656-228.864 511.0784-511.232 511.0784z m0-926.2592a415.2832 415.2832 0 0 0-415.3856 415.232c0 107.2128 41.0624 204.6464 107.8272 278.3232 60.16-29.1328 38.0416-4.9152 116.736-37.2736 80.5888-33.1264 99.6352-44.6464 99.6352-44.6464l0.768-76.288s-30.1568-22.8864-39.5264-94.72c-18.8928 5.4272-25.088-22.016-26.2144-39.424-1.024-16.896-10.9568-69.4784 12.0832-64.7168-4.7104-35.1744-8.0896-66.8672-6.4-83.6608 5.7344-58.88 62.976-120.5248 151.0912-124.9792 103.68 4.4544 144.7424 66.048 150.528 124.928 1.6384 16.8448-2.048 48.5376-6.7584 83.6096 23.04-4.6592 13.0048 47.872 11.8784 64.7168-1.024 17.408-7.3728 44.7488-26.2144 39.3728-9.4208 71.7824-39.5776 94.464-39.5776 94.464l0.7168 75.9296s19.0976 10.8032 99.6352 43.9296c78.6944 32.3584 56.576 9.5744 116.736 38.7584a413.184 413.184 0 0 0 107.776-278.3744A415.232 415.232 0 0 0 512 95.7952z" p-id="5605"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -1,27 +1,39 @@
|
||||
<template>
|
||||
<n-modal v-model:show="show">
|
||||
<n-card style="width: 450px" title="角色库" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<n-card style="width: 800px" title="角色库" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<n-table :bordered="false" :single-line="false">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>角色Code</th>
|
||||
<th>角色Name</th>
|
||||
<th>操作</th>
|
||||
<th style="text-align: center;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in list" :key="item.id">
|
||||
<td>{{ item.name }}</td>
|
||||
<td>{{ item.normalizedName }}</td>
|
||||
<td>
|
||||
<td align="center">
|
||||
<n-button type="primary" size="small" @click="chooseRole(item)">选择</n-button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-show="showNewTr">
|
||||
<td :colspan="3">
|
||||
<n-input-group>
|
||||
<n-input v-model:value="addRole.rolename" placeholder="请输入角色编码(英文)"
|
||||
:style="{ width: '40%' }" size="small" :disabled="loading" clearable />
|
||||
<n-input v-model:value="addRole.normalizedname" placeholder="请输入角色中文"
|
||||
:style="{ width: '40%' }" size="small" :disabled="loading" clearable />
|
||||
<n-button :style="{ width: '20%' }" size="small" type="primary" @click="handleAddNewRole"
|
||||
:disabled="loading">新增</n-button>
|
||||
</n-input-group>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</n-table>
|
||||
<template #footer>
|
||||
<div class="flex-content right">
|
||||
<n-button type="primary" size="small">新增角色</n-button>
|
||||
<n-button type="primary" size="small" @click="showNewTr = true">新增角色</n-button>
|
||||
<n-button @click="show = false" size="small">关 闭</n-button>
|
||||
</div>
|
||||
</template>
|
||||
@ -30,17 +42,22 @@
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { getRoleList } from '@/api/roleApi';
|
||||
import { getRoleList, addNewRole } from '@/api/roleApi';
|
||||
import { useMessage } from 'naive-ui';
|
||||
import type { RoleListReturn } from '@/api/roleApi';
|
||||
const emit = defineEmits<{
|
||||
(event: 'choose', row:RoleListReturn): void
|
||||
(event: 'choose', row: RoleListReturn): void
|
||||
}>()
|
||||
const show = ref(false);
|
||||
const loading = ref(false);
|
||||
const showNewTr = ref(false);
|
||||
const list = ref([]);
|
||||
const message = useMessage();
|
||||
|
||||
const chooseRole = (row:RoleListReturn) => {
|
||||
const addRole = ref({
|
||||
rolename: '',
|
||||
normalizedname: ''
|
||||
});
|
||||
const chooseRole = (row: RoleListReturn) => {
|
||||
emit('choose', toRaw(row));
|
||||
show.value = false;
|
||||
}
|
||||
@ -56,6 +73,21 @@ const init = async () => {
|
||||
}
|
||||
};
|
||||
init();
|
||||
const handleAddNewRole = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
await addNewRole(addRole.value);
|
||||
message.success('新增角色成功!');
|
||||
addRole.value.rolename = void 0;
|
||||
addRole.value.normalizedname = void 0;
|
||||
init();
|
||||
showNewTr.value = false;
|
||||
} catch (error) {
|
||||
message.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
defineExpose({
|
||||
openDialog
|
||||
})
|
||||
|
@ -12,13 +12,21 @@ export const useAppStore = defineStore(
|
||||
// 设置菜单列表
|
||||
const setMenuList = (menu: MenuNode[]) => {
|
||||
menuApp.menuList.length = 0;
|
||||
menu.forEach(item => {
|
||||
menu.forEach((item) => {
|
||||
menuApp.menuList.push(item);
|
||||
})
|
||||
});
|
||||
};
|
||||
// 重置仓库
|
||||
const reset = () => {
|
||||
Object.assign(menuApp, {
|
||||
isExpaned: true, //折叠控制
|
||||
menuList: [],
|
||||
});
|
||||
};
|
||||
return {
|
||||
menuApp,
|
||||
setMenuList
|
||||
setMenuList,
|
||||
reset,
|
||||
};
|
||||
},
|
||||
{ persist: true }
|
||||
|
@ -33,9 +33,24 @@ export const useUserStore = defineStore(
|
||||
const setUserInfo = (data: UserInfo) => {
|
||||
Object.assign(userInfo, data);
|
||||
};
|
||||
const reset = () => {
|
||||
Object.assign(userInfo, {
|
||||
birthday: void 0,
|
||||
config: void 0,
|
||||
description: void 0,
|
||||
email: "",
|
||||
id: "",
|
||||
jobCode: void 0,
|
||||
jobName: void 0,
|
||||
menuName: void 0,
|
||||
sex: "",
|
||||
userName: "",
|
||||
});
|
||||
};
|
||||
return {
|
||||
userInfo,
|
||||
setUserInfo
|
||||
setUserInfo,
|
||||
reset,
|
||||
};
|
||||
},
|
||||
{ persist: true }
|
||||
|
@ -18,7 +18,7 @@
|
||||
</n-tag>
|
||||
<p class="time">2025-07-05</p>
|
||||
</div>
|
||||
<img src="@/assets/icons/mainproject.svg">
|
||||
<svg-icon color="#4090EF" iconClass="mainproject" width="50px" height="50px"></svg-icon>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,11 +84,6 @@ import LsEmpty from '@/components/Ls-UI/LsEmpty.vue';
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<main>
|
||||
<div class="icon__item">
|
||||
<div class="icon">
|
||||
<img src="@/assets/icons/order.svg">
|
||||
<svg-icon iconClass="order" color="#FFF" width="25px" height="25px"></svg-icon>
|
||||
</div>
|
||||
<p>系统菜单</p>
|
||||
</div>
|
||||
@ -40,11 +40,6 @@ main {
|
||||
height: 30px;
|
||||
background:$primaryColor;
|
||||
border-radius: 4px;
|
||||
img{
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
|
||||
}
|
||||
}
|
||||
> p{
|
||||
font-size: 12px;
|
||||
|
@ -27,8 +27,10 @@ import {
|
||||
CaretDownOutline
|
||||
} from '@vicons/ionicons5';
|
||||
import { NIcon } from 'naive-ui';
|
||||
import { useAppStore } from '@/store/app';
|
||||
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStore();
|
||||
const router = useRouter();
|
||||
function renderIcon(icon: Component) {
|
||||
return () => {
|
||||
@ -58,6 +60,8 @@ const options = [
|
||||
const handleSelect = (key: string | number) => {
|
||||
if (key === 'logout') {
|
||||
removeToken();
|
||||
userStore.reset();
|
||||
appStore.reset();
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
<ls-list-button @search="init" @reset="resetList"></ls-list-button>
|
||||
</header>
|
||||
<main class="table-main" ref="tableMainRef">
|
||||
<n-data-table v-if="height" :columns="columns" :data="data" :max-height="`${height}px`"
|
||||
<n-data-table v-if="height" :columns="columns" :data="data" :loading="loading" :max-height="`${height}px`"
|
||||
:min-height="`${height}px`">
|
||||
<template #empty>
|
||||
<LsEmpty type="no-data" title="暂无数据" description="Not Found 404"></LsEmpty>
|
||||
@ -48,19 +48,32 @@
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<n-modal v-model:show="addMenuDialog">
|
||||
<n-card style="width: 450px" title="菜单分配" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<n-select v-model:value="menuChoose.menuCode" multiple :options="menuOptions" @update:value="handleUpdateValue" />
|
||||
<template #footer>
|
||||
<div class="flex-content right">
|
||||
<n-button type="primary" size="small" @click="handleAssignMenu">分 配</n-button>
|
||||
<n-button @click="addMenuDialog = false" size="small">关 闭</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<!-- 角色库 -->
|
||||
<LsRoleBase ref="roleRef" @choose="chooseRole"></LsRoleBase>
|
||||
</template>
|
||||
|
||||
<script setup lang='ts'>
|
||||
import { Server } from '@vicons/ionicons5';
|
||||
import { getUserList, deleteUser, enableRole } from '@/api/roleApi';
|
||||
import { DataTableColumns, NButton, NTag, useDialog, useMessage } from 'naive-ui';
|
||||
import { getUserList, deleteUser, enableRole, assignMenu } from '@/api/roleApi';
|
||||
import { DataTableColumns, NButton, NTag, SelectOption, useDialog, useMessage } from 'naive-ui';
|
||||
import { getDict } from '@/api/dictApi';
|
||||
import LsEmpty from '@/components/Ls-UI/LsEmpty.vue';
|
||||
import LsRoleBase from '@/components/Ls-UI/LsRoleBase.vue';
|
||||
|
||||
const message = useMessage();
|
||||
const tableMainRef = ref<HTMLElement | null>(null);
|
||||
const loading = ref(false);
|
||||
const roleRef = ref<InstanceType<typeof LsRoleBase> | null>(null);
|
||||
const queryInfo = reactive({
|
||||
roleName: void 0,
|
||||
@ -72,11 +85,17 @@ const pagination = reactive<PageController>({
|
||||
page: 1,
|
||||
});
|
||||
const options = ref([]);
|
||||
const menuOptions = ref([])
|
||||
const menuChoose = ref({
|
||||
menuCode: [],
|
||||
menuName: []
|
||||
})
|
||||
|
||||
const data = ref([]);
|
||||
const height = ref(null);
|
||||
const dialog = useDialog();
|
||||
const addRoleDialog = ref(false);
|
||||
const addMenuDialog = ref(false);
|
||||
const operationUuid = ref('');
|
||||
const roleName = ref(''); //授权角色
|
||||
|
||||
@ -212,7 +231,10 @@ const columns = createColumns({
|
||||
})
|
||||
},
|
||||
handleMenuAssign(rowData) {
|
||||
message.error(`我点击了${rowData.userName}`);
|
||||
addMenuDialog.value = true;
|
||||
menuChoose.value.menuCode = rowData.menuCode ? rowData.menuCode.split(',') : [];
|
||||
menuChoose.value.menuName = rowData.menuName ? rowData.menuName.split(',') : [];
|
||||
operationUuid.value = rowData.id;
|
||||
},
|
||||
handleAssignJob(rowData) {
|
||||
message.error(`我点击了${rowData.userName}`);
|
||||
@ -220,6 +242,7 @@ const columns = createColumns({
|
||||
})
|
||||
const init = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const { users, totalCount } = await getUserList({
|
||||
...pagination,
|
||||
...queryInfo
|
||||
@ -228,6 +251,8 @@ const init = async () => {
|
||||
data.value = users;
|
||||
} catch (error) {
|
||||
message.error(error.message);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const resetList = () => {
|
||||
@ -247,6 +272,7 @@ onMounted(() => {
|
||||
const openRoleDialog = () => {
|
||||
roleRef.value?.openDialog();
|
||||
}
|
||||
// 授权角色
|
||||
const handleEnableRole = async () => {
|
||||
try {
|
||||
const query = {
|
||||
@ -260,23 +286,50 @@ const handleEnableRole = async () => {
|
||||
message.error(error.message);
|
||||
}
|
||||
}
|
||||
// 分配菜单
|
||||
const handleAssignMenu = async () => {
|
||||
try {
|
||||
const req = {
|
||||
id: operationUuid.value,
|
||||
menuCode: menuChoose.value.menuCode.join(','),
|
||||
menuName: menuChoose.value.menuName.join(',')
|
||||
}
|
||||
await assignMenu(req);
|
||||
message.success('分配成功!');
|
||||
addMenuDialog.value = false;
|
||||
init();
|
||||
} catch (error) {
|
||||
message.error(error.message);
|
||||
}
|
||||
}
|
||||
const handleUpdateValue = (value: string, option: SelectOption[]) => {
|
||||
menuChoose.value.menuName = option.map(item => item.label)
|
||||
}
|
||||
// 获取角色
|
||||
const initRoleList = async () => {
|
||||
try {
|
||||
const result = await getRoleList();
|
||||
options.value = result.map(item => {
|
||||
return {
|
||||
label:item.normalizedName,
|
||||
value:item.name
|
||||
label: item.normalizedName,
|
||||
value: item.name
|
||||
}
|
||||
});
|
||||
const dict = await getDict('menuCode');
|
||||
if (dict && Array.isArray(dict) && dict.length > 0) {
|
||||
menuOptions.value = dict.map(item => {
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(error instanceof Error ? error.message : error);
|
||||
}
|
||||
};
|
||||
// 赋予角色
|
||||
const chooseRole = (row) => {
|
||||
console.log(row, 'row');
|
||||
roleName.value = row.name;
|
||||
}
|
||||
</script>
|
||||
|
@ -44,8 +44,8 @@
|
||||
</template>
|
||||
<n-input v-model:value="parentForm.path" placeholder="请输入菜单路径" />
|
||||
</n-form-item>
|
||||
<n-form-item label="菜单编码" path="menuCode">
|
||||
<n-input v-model:value="parentForm.menuCode" placeholder="请输入菜单编码" />
|
||||
<n-form-item label="菜单权限" path="menuCode">
|
||||
<n-select v-model:value="parentForm.menuCode" multiple :options="menuOptions" placeholder="请选择菜单权限" />
|
||||
</n-form-item>
|
||||
<n-form-item label="菜单图标" path="icon">
|
||||
<n-select v-model:value="parentForm.icon" :options="iconOptions" filterable clearable placeholder="请选择图标名称"
|
||||
@ -125,8 +125,8 @@
|
||||
</template>
|
||||
<n-input v-model:value="childForm.path" placeholder="请输入菜单路径" />
|
||||
</n-form-item>
|
||||
<n-form-item label="菜单编码" path="menuCode">
|
||||
<n-input v-model:value="childForm.menuCode" placeholder="请输入菜单编码" />
|
||||
<n-form-item label="菜单权限" path="menuCode">
|
||||
<n-select v-model:value="childForm.menuCode" multiple :options="menuOptions" placeholder="请选择菜单权限" />
|
||||
</n-form-item>
|
||||
<n-form-item label="菜单图标" path="icon">
|
||||
<n-select v-model:value="childForm.icon" :options="iconOptions" filterable clearable placeholder="请选择图标名称"
|
||||
@ -192,8 +192,9 @@ import {
|
||||
type MenuNode,
|
||||
type RawMenu
|
||||
} from '@/api/menu'
|
||||
import { FormInst, FormRules, NButton, NIcon, NSpace, NTag, SelectOption, TreeOption, useDialog, useMessage } from 'naive-ui'
|
||||
import SvgIcon from '@/components/SvgIcon.vue'
|
||||
import { FormInst, FormRules, NButton, NIcon, NSpace, NTag, SelectOption, TreeOption, useDialog, useMessage } from 'naive-ui';
|
||||
import SvgIcon from '@/components/SvgIcon.vue';
|
||||
import { getMenuName } from './utils'
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false)
|
||||
@ -203,11 +204,17 @@ const menuTree = ref<TreeOption[]>([])
|
||||
const showParentModal = ref(false);
|
||||
const parentFormRef = ref<FormInst | null>(null);
|
||||
const menuType = ref<'menu' | 'list'>('menu'); //菜单类型
|
||||
const parentForm = ref<Omit<RawMenu, 'uuid' | 'parentId'>>({
|
||||
const menuOptions = ref([]); //初始化菜单权限
|
||||
const adaptabilityOptions = ref([]);// 适配方式选项
|
||||
interface MenuVO extends Omit<RawMenu, 'menuCode'> {
|
||||
menuCode: string[];
|
||||
}
|
||||
|
||||
const parentForm = ref<Omit<MenuVO, 'uuid' | 'parentId'>>({
|
||||
path: '',
|
||||
label: '',
|
||||
icon: '',
|
||||
menuCode: '',
|
||||
menuCode: [],
|
||||
menuName: '',
|
||||
adaptability: 'pc',
|
||||
component: '',
|
||||
@ -271,12 +278,12 @@ const renderLabel = (option: IconOption) => {
|
||||
// 子级菜单表单相关
|
||||
const showChildModal = ref(false)
|
||||
const childFormRef = ref<FormInst | null>(null)
|
||||
const childForm = ref<Omit<RawMenu, 'uuid'>>({
|
||||
const childForm = ref<Omit<MenuVO, 'uuid'>>({
|
||||
parentId: '',
|
||||
path: '',
|
||||
label: '',
|
||||
icon: '',
|
||||
menuCode: '',
|
||||
menuCode: [],
|
||||
menuName: '',
|
||||
adaptability: 'pc',
|
||||
component: '',
|
||||
@ -294,30 +301,32 @@ const childRules: FormRules = {
|
||||
const isEditChild = ref(false)
|
||||
const currentChildId = ref('')
|
||||
|
||||
// 适配方式选项
|
||||
const adaptabilityOptions = ref([]);
|
||||
|
||||
getDict("page_adaptability").then((res) => {
|
||||
console.log('获取适配方式选项', res);
|
||||
adaptabilityOptions.value = res.map(item => {
|
||||
const {label, value, ...rest} = item;
|
||||
return {
|
||||
label: label,
|
||||
value: value,
|
||||
}
|
||||
})
|
||||
}).catch((errors) => {
|
||||
console.log('获取适配方式选项失败', errors);
|
||||
message.error('获取适配方式选项失败');
|
||||
})
|
||||
|
||||
// 初始化字典
|
||||
const initDict = async () => {
|
||||
try {
|
||||
const [res1, res2] = await Promise.all([getDict("page_adaptability"), getDict('menuCode')]);
|
||||
adaptabilityOptions.value = res1.map(item => {
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}
|
||||
})
|
||||
menuOptions.value = res2.map(item => {
|
||||
return {
|
||||
label: item.label,
|
||||
value: item.value
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
message.error(error.message);
|
||||
}
|
||||
}
|
||||
// 计算属性
|
||||
const parentModalTitle = computed(() => (isEditParent.value ? '编辑父级菜单' : '添加父级菜单'))
|
||||
const childModalTitle = computed(() => (isEditChild.value ? '编辑子级菜单' : '添加子级菜单'))
|
||||
|
||||
// 父级菜单选项
|
||||
const parentMenuOptions = computed<SelectOption[]>(() => {
|
||||
console.log(menuTree.value, 'menuTree.value');
|
||||
return menuTree.value.map(menu => ({
|
||||
label: menu.label,
|
||||
value: menu.key
|
||||
@ -336,6 +345,7 @@ const { stop } = watch(menuType, (newVal) => {
|
||||
})
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initDict();
|
||||
loadSvgIcons();
|
||||
fetchMenuData();
|
||||
});
|
||||
@ -344,7 +354,6 @@ onBeforeUnmount(() => {
|
||||
})
|
||||
|
||||
const renderTreeLabel = ({ option }) => {
|
||||
console.log(option,'option');
|
||||
return h('div', {
|
||||
style: {
|
||||
display: 'flex',
|
||||
@ -364,7 +373,7 @@ const renderTreeLabel = ({ option }) => {
|
||||
h(NTag, {
|
||||
type: option?.rawData.status === 'enable' ? 'success' : 'error',
|
||||
size: 'small'
|
||||
}, option?.rawData.status === 'enable' ? 'Enabled' : 'Disabled')
|
||||
}, option?.rawData.status === 'enable' ? '启用' : '停用')
|
||||
])
|
||||
}
|
||||
/**
|
||||
@ -480,7 +489,7 @@ const handleAddParent = () => {
|
||||
path: '',
|
||||
label: '',
|
||||
icon: '',
|
||||
menuCode: '',
|
||||
menuCode: [],
|
||||
menuName: '',
|
||||
adaptability: 'pc',
|
||||
component: '',
|
||||
@ -499,7 +508,7 @@ const handleEditParent = (menu: MenuNode) => {
|
||||
path: menu.path,
|
||||
label: menu.label,
|
||||
icon: menu.icon,
|
||||
menuCode: menu.menuCode,
|
||||
menuCode: menu.menuCode ? menu.menuCode.split(',') : [],
|
||||
menuName: menu.menuName,
|
||||
adaptability: menu.adaptability,
|
||||
component: menu.component,
|
||||
@ -518,10 +527,16 @@ const handleSubmitParent = () => {
|
||||
if (isEditParent.value) {
|
||||
await editParentMenu({
|
||||
uuid: currentParentId.value,
|
||||
...parentForm.value
|
||||
...parentForm.value,
|
||||
menuCode: parentForm.value.menuCode.join(','),
|
||||
menuName: getMenuName(parentForm.value.menuCode, menuOptions.value)
|
||||
})
|
||||
} else {
|
||||
await addParentMenu(parentForm.value)
|
||||
await addParentMenu({
|
||||
...parentForm.value,
|
||||
menuCode: parentForm.value.menuCode.join(','),
|
||||
menuName: getMenuName(parentForm.value.menuCode, menuOptions.value)
|
||||
})
|
||||
}
|
||||
showParentModal.value = false;
|
||||
message.success('操作成功!');
|
||||
@ -540,7 +555,7 @@ const handleAddChild = (parentId: string) => {
|
||||
path: '',
|
||||
label: '',
|
||||
icon: '',
|
||||
menuCode: '',
|
||||
menuCode: [],
|
||||
menuName: '',
|
||||
adaptability: 'pc',
|
||||
component: '',
|
||||
@ -560,7 +575,7 @@ const handleEditChild = (menu: MenuNode) => {
|
||||
path: menu.path,
|
||||
label: menu.label,
|
||||
icon: menu.icon,
|
||||
menuCode: menu.menuCode,
|
||||
menuCode: menu.menuCode ? menu.menuCode.split(',') : [],
|
||||
menuName: menu.menuName,
|
||||
adaptability: menu.adaptability,
|
||||
component: menu.component,
|
||||
@ -579,10 +594,16 @@ const handleSubmitChild = () => {
|
||||
if (isEditChild.value) {
|
||||
await eidtChildMenu({
|
||||
uuid: currentChildId.value,
|
||||
...childForm.value
|
||||
...childForm.value,
|
||||
menuCode: childForm.value.menuCode.join(','),
|
||||
menuName: getMenuName(childForm.value.menuCode, menuOptions.value)
|
||||
})
|
||||
} else {
|
||||
await addChildMenu(childForm.value)
|
||||
await addChildMenu({
|
||||
...childForm.value,
|
||||
menuCode: childForm.value.menuCode.join(','),
|
||||
menuName: getMenuName(childForm.value.menuCode, menuOptions.value)
|
||||
})
|
||||
}
|
||||
showChildModal.value = false;
|
||||
message.success('操作成功!');
|
||||
|
10
src/views/system/menu/utils.ts
Normal file
10
src/views/system/menu/utils.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export function getMenuName(value:string[], options) {
|
||||
let result = []
|
||||
if(value.length === 0) return '';
|
||||
for(let i = 0; i < value.length; i++){
|
||||
const val = value[i] as string;
|
||||
const label = options.find(item => item.value === val);
|
||||
result.push(label);
|
||||
}
|
||||
return result.join(',');
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user