feat:大修改

This commit is contained in:
fangyunong 2025-07-19 16:12:56 +08:00
parent 991736f760
commit b727b0d998
17 changed files with 257 additions and 71 deletions

2
auto-imports.d.ts vendored
View File

@ -9,8 +9,10 @@ declare global {
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const addChildDict: typeof import('./src/api/dictApi')['addChildDict'] const addChildDict: typeof import('./src/api/dictApi')['addChildDict']
const addChildMenu: typeof import('./src/api/menu')['addChildMenu'] 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 addParentDict: typeof import('./src/api/dictApi')['addParentDict']
const addParentMenu: typeof import('./src/api/menu')['addParentMenu'] const addParentMenu: typeof import('./src/api/menu')['addParentMenu']
const assignMenu: typeof import('./src/api/roleApi')['assignMenu']
const computed: typeof import('vue')['computed'] const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp'] const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef'] const customRef: typeof import('vue')['customRef']

View File

@ -12,6 +12,8 @@
<script setup lang='ts'> <script setup lang='ts'>
import { type GlobalThemeOverrides, dateZhCN, zhCN } from "naive-ui"; import { type GlobalThemeOverrides, dateZhCN, zhCN } from "naive-ui";
import InitAxiosInterceptors from "@/components/InitAxiosInterceptors.vue"; import InitAxiosInterceptors from "@/components/InitAxiosInterceptors.vue";
import { useAppStore } from "@/store/app";
import { generateRoutes } from "@/utils/permission";
const themeOverrides: GlobalThemeOverrides = { const themeOverrides: GlobalThemeOverrides = {
// //
@ -22,6 +24,23 @@ const themeOverrides: GlobalThemeOverrides = {
'primaryColorSuppl': '#5A95FF' // hover '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> </script>
<style scoped lang='scss'></style> <style scoped lang='scss'></style>

View File

@ -4,7 +4,7 @@ interface UserListQuery {
roleName?: string; roleName?: string;
page: number; page: number;
pageSize: number; pageSize: number;
userName?:string userName?: string;
} }
interface UserListReturn { interface UserListReturn {
totalCount: number; totalCount: number;
@ -48,10 +48,37 @@ interface EnableRoleQuery {
userId: string; userId: string;
roleName: string; roleName: string;
} }
export function enableRole(data:EnableRoleQuery) { export function enableRole(data: EnableRoleQuery) {
return http({ return http({
url: "/api/v1/AdminRoleControllers/role", url: "/api/v1/AdminRoleControllers/role",
method: "POST", method: "POST",
data, 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,
});
}

View 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

View 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

View 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

View 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

View 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

View File

@ -1,27 +1,39 @@
<template> <template>
<n-modal v-model:show="show"> <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"> <n-table :bordered="false" :single-line="false">
<thead> <thead>
<tr> <tr>
<th>角色Code</th> <th>角色Code</th>
<th>角色Name</th> <th>角色Name</th>
<th>操作</th> <th style="text-align: center;">操作</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="item in list" :key="item.id"> <tr v-for="item in list" :key="item.id">
<td>{{ item.name }}</td> <td>{{ item.name }}</td>
<td>{{ item.normalizedName }}</td> <td>{{ item.normalizedName }}</td>
<td> <td align="center">
<n-button type="primary" size="small" @click="chooseRole(item)">选择</n-button> <n-button type="primary" size="small" @click="chooseRole(item)">选择</n-button>
</td> </td>
</tr> </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> </tbody>
</n-table> </n-table>
<template #footer> <template #footer>
<div class="flex-content right"> <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> <n-button @click="show = false" size="small"> </n-button>
</div> </div>
</template> </template>
@ -30,17 +42,22 @@
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { getRoleList } from '@/api/roleApi'; import { getRoleList, addNewRole } from '@/api/roleApi';
import { useMessage } from 'naive-ui'; import { useMessage } from 'naive-ui';
import type { RoleListReturn } from '@/api/roleApi'; import type { RoleListReturn } from '@/api/roleApi';
const emit = defineEmits<{ const emit = defineEmits<{
(event: 'choose', row:RoleListReturn): void (event: 'choose', row: RoleListReturn): void
}>() }>()
const show = ref(false); const show = ref(false);
const loading = ref(false);
const showNewTr = ref(false);
const list = ref([]); const list = ref([]);
const message = useMessage(); const message = useMessage();
const addRole = ref({
const chooseRole = (row:RoleListReturn) => { rolename: '',
normalizedname: ''
});
const chooseRole = (row: RoleListReturn) => {
emit('choose', toRaw(row)); emit('choose', toRaw(row));
show.value = false; show.value = false;
} }
@ -56,6 +73,21 @@ const init = async () => {
} }
}; };
init(); 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({ defineExpose({
openDialog openDialog
}) })

View File

@ -12,13 +12,21 @@ export const useAppStore = defineStore(
// 设置菜单列表 // 设置菜单列表
const setMenuList = (menu: MenuNode[]) => { const setMenuList = (menu: MenuNode[]) => {
menuApp.menuList.length = 0; menuApp.menuList.length = 0;
menu.forEach(item => { menu.forEach((item) => {
menuApp.menuList.push(item); menuApp.menuList.push(item);
}) });
};
// 重置仓库
const reset = () => {
Object.assign(menuApp, {
isExpaned: true, //折叠控制
menuList: [],
});
}; };
return { return {
menuApp, menuApp,
setMenuList setMenuList,
reset,
}; };
}, },
{ persist: true } { persist: true }

View File

@ -33,9 +33,24 @@ export const useUserStore = defineStore(
const setUserInfo = (data: UserInfo) => { const setUserInfo = (data: UserInfo) => {
Object.assign(userInfo, data); 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 { return {
userInfo, userInfo,
setUserInfo setUserInfo,
reset,
}; };
}, },
{ persist: true } { persist: true }

View File

@ -18,7 +18,7 @@
</n-tag> </n-tag>
<p class="time">2025-07-05</p> <p class="time">2025-07-05</p>
</div> </div>
<img src="@/assets/icons/mainproject.svg"> <svg-icon color="#4090EF" iconClass="mainproject" width="50px" height="50px"></svg-icon>
</footer> </footer>
</div> </div>
</div> </div>
@ -84,11 +84,6 @@ import LsEmpty from '@/components/Ls-UI/LsEmpty.vue';
font-size: 12px; font-size: 12px;
} }
} }
img {
width: 50px;
height: 50px;
}
} }
} }
} }

View File

@ -3,7 +3,7 @@
<main> <main>
<div class="icon__item"> <div class="icon__item">
<div class="icon"> <div class="icon">
<img src="@/assets/icons/order.svg"> <svg-icon iconClass="order" color="#FFF" width="25px" height="25px"></svg-icon>
</div> </div>
<p>系统菜单</p> <p>系统菜单</p>
</div> </div>
@ -40,11 +40,6 @@ main {
height: 30px; height: 30px;
background:$primaryColor; background:$primaryColor;
border-radius: 4px; border-radius: 4px;
img{
height: 25px;
width: 25px;
}
} }
> p{ > p{
font-size: 12px; font-size: 12px;

View File

@ -27,8 +27,10 @@ import {
CaretDownOutline CaretDownOutline
} from '@vicons/ionicons5'; } from '@vicons/ionicons5';
import { NIcon } from 'naive-ui'; import { NIcon } from 'naive-ui';
import { useAppStore } from '@/store/app';
const userStore = useUserStore(); const userStore = useUserStore();
const appStore = useAppStore();
const router = useRouter(); const router = useRouter();
function renderIcon(icon: Component) { function renderIcon(icon: Component) {
return () => { return () => {
@ -58,6 +60,8 @@ const options = [
const handleSelect = (key: string | number) => { const handleSelect = (key: string | number) => {
if (key === 'logout') { if (key === 'logout') {
removeToken(); removeToken();
userStore.reset();
appStore.reset();
router.push('/'); router.push('/');
} }
} }

View File

@ -10,7 +10,7 @@
<ls-list-button @search="init" @reset="resetList"></ls-list-button> <ls-list-button @search="init" @reset="resetList"></ls-list-button>
</header> </header>
<main class="table-main" ref="tableMainRef"> <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`"> :min-height="`${height}px`">
<template #empty> <template #empty>
<LsEmpty type="no-data" title="暂无数据" description="Not Found 404"></LsEmpty> <LsEmpty type="no-data" title="暂无数据" description="Not Found 404"></LsEmpty>
@ -48,19 +48,32 @@
</template> </template>
</n-card> </n-card>
</n-modal> </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> <LsRoleBase ref="roleRef" @choose="chooseRole"></LsRoleBase>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { Server } from '@vicons/ionicons5'; import { Server } from '@vicons/ionicons5';
import { getUserList, deleteUser, enableRole } from '@/api/roleApi'; import { getUserList, deleteUser, enableRole, assignMenu } from '@/api/roleApi';
import { DataTableColumns, NButton, NTag, useDialog, useMessage } from 'naive-ui'; import { DataTableColumns, NButton, NTag, SelectOption, useDialog, useMessage } from 'naive-ui';
import { getDict } from '@/api/dictApi';
import LsEmpty from '@/components/Ls-UI/LsEmpty.vue'; import LsEmpty from '@/components/Ls-UI/LsEmpty.vue';
import LsRoleBase from '@/components/Ls-UI/LsRoleBase.vue'; import LsRoleBase from '@/components/Ls-UI/LsRoleBase.vue';
const message = useMessage(); const message = useMessage();
const tableMainRef = ref<HTMLElement | null>(null); const tableMainRef = ref<HTMLElement | null>(null);
const loading = ref(false);
const roleRef = ref<InstanceType<typeof LsRoleBase> | null>(null); const roleRef = ref<InstanceType<typeof LsRoleBase> | null>(null);
const queryInfo = reactive({ const queryInfo = reactive({
roleName: void 0, roleName: void 0,
@ -72,11 +85,17 @@ const pagination = reactive<PageController>({
page: 1, page: 1,
}); });
const options = ref([]); const options = ref([]);
const menuOptions = ref([])
const menuChoose = ref({
menuCode: [],
menuName: []
})
const data = ref([]); const data = ref([]);
const height = ref(null); const height = ref(null);
const dialog = useDialog(); const dialog = useDialog();
const addRoleDialog = ref(false); const addRoleDialog = ref(false);
const addMenuDialog = ref(false);
const operationUuid = ref(''); const operationUuid = ref('');
const roleName = ref(''); // const roleName = ref(''); //
@ -212,7 +231,10 @@ const columns = createColumns({
}) })
}, },
handleMenuAssign(rowData) { 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) { handleAssignJob(rowData) {
message.error(`我点击了${rowData.userName}`); message.error(`我点击了${rowData.userName}`);
@ -220,6 +242,7 @@ const columns = createColumns({
}) })
const init = async () => { const init = async () => {
try { try {
loading.value = true;
const { users, totalCount } = await getUserList({ const { users, totalCount } = await getUserList({
...pagination, ...pagination,
...queryInfo ...queryInfo
@ -228,6 +251,8 @@ const init = async () => {
data.value = users; data.value = users;
} catch (error) { } catch (error) {
message.error(error.message); message.error(error.message);
} finally {
loading.value = false;
} }
}; };
const resetList = () => { const resetList = () => {
@ -247,6 +272,7 @@ onMounted(() => {
const openRoleDialog = () => { const openRoleDialog = () => {
roleRef.value?.openDialog(); roleRef.value?.openDialog();
} }
//
const handleEnableRole = async () => { const handleEnableRole = async () => {
try { try {
const query = { const query = {
@ -260,23 +286,50 @@ const handleEnableRole = async () => {
message.error(error.message); 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 () => { const initRoleList = async () => {
try { try {
const result = await getRoleList(); const result = await getRoleList();
options.value = result.map(item => { options.value = result.map(item => {
return { return {
label:item.normalizedName, label: item.normalizedName,
value:item.name 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) { } catch (error) {
message.error(error instanceof Error ? error.message : error); message.error(error instanceof Error ? error.message : error);
} }
}; };
// //
const chooseRole = (row) => { const chooseRole = (row) => {
console.log(row, 'row');
roleName.value = row.name; roleName.value = row.name;
} }
</script> </script>

View File

@ -44,8 +44,8 @@
</template> </template>
<n-input v-model:value="parentForm.path" placeholder="请输入菜单路径" /> <n-input v-model:value="parentForm.path" placeholder="请输入菜单路径" />
</n-form-item> </n-form-item>
<n-form-item label="菜单编码" path="menuCode"> <n-form-item label="菜单权限" path="menuCode">
<n-input v-model:value="parentForm.menuCode" placeholder="请输入菜单编码" /> <n-select v-model:value="parentForm.menuCode" multiple :options="menuOptions" placeholder="请选择菜单权限" />
</n-form-item> </n-form-item>
<n-form-item label="菜单图标" path="icon"> <n-form-item label="菜单图标" path="icon">
<n-select v-model:value="parentForm.icon" :options="iconOptions" filterable clearable placeholder="请选择图标名称" <n-select v-model:value="parentForm.icon" :options="iconOptions" filterable clearable placeholder="请选择图标名称"
@ -125,8 +125,8 @@
</template> </template>
<n-input v-model:value="childForm.path" placeholder="请输入菜单路径" /> <n-input v-model:value="childForm.path" placeholder="请输入菜单路径" />
</n-form-item> </n-form-item>
<n-form-item label="菜单编码" path="menuCode"> <n-form-item label="菜单权限" path="menuCode">
<n-input v-model:value="childForm.menuCode" placeholder="请输入菜单编码" /> <n-select v-model:value="childForm.menuCode" multiple :options="menuOptions" placeholder="请选择菜单权限" />
</n-form-item> </n-form-item>
<n-form-item label="菜单图标" path="icon"> <n-form-item label="菜单图标" path="icon">
<n-select v-model:value="childForm.icon" :options="iconOptions" filterable clearable placeholder="请选择图标名称" <n-select v-model:value="childForm.icon" :options="iconOptions" filterable clearable placeholder="请选择图标名称"
@ -192,8 +192,9 @@ import {
type MenuNode, type MenuNode,
type RawMenu type RawMenu
} from '@/api/menu' } from '@/api/menu'
import { FormInst, FormRules, NButton, NIcon, NSpace, NTag, SelectOption, TreeOption, useDialog, useMessage } from 'naive-ui' import { FormInst, FormRules, NButton, NIcon, NSpace, NTag, SelectOption, TreeOption, useDialog, useMessage } from 'naive-ui';
import SvgIcon from '@/components/SvgIcon.vue' import SvgIcon from '@/components/SvgIcon.vue';
import { getMenuName } from './utils'
// //
const loading = ref(false) const loading = ref(false)
@ -203,11 +204,17 @@ const menuTree = ref<TreeOption[]>([])
const showParentModal = ref(false); const showParentModal = ref(false);
const parentFormRef = ref<FormInst | null>(null); const parentFormRef = ref<FormInst | null>(null);
const menuType = ref<'menu' | 'list'>('menu'); // 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: '', path: '',
label: '', label: '',
icon: '', icon: '',
menuCode: '', menuCode: [],
menuName: '', menuName: '',
adaptability: 'pc', adaptability: 'pc',
component: '', component: '',
@ -271,12 +278,12 @@ const renderLabel = (option: IconOption) => {
// //
const showChildModal = ref(false) const showChildModal = ref(false)
const childFormRef = ref<FormInst | null>(null) const childFormRef = ref<FormInst | null>(null)
const childForm = ref<Omit<RawMenu, 'uuid'>>({ const childForm = ref<Omit<MenuVO, 'uuid'>>({
parentId: '', parentId: '',
path: '', path: '',
label: '', label: '',
icon: '', icon: '',
menuCode: '', menuCode: [],
menuName: '', menuName: '',
adaptability: 'pc', adaptability: 'pc',
component: '', component: '',
@ -294,30 +301,32 @@ const childRules: FormRules = {
const isEditChild = ref(false) const isEditChild = ref(false)
const currentChildId = ref('') const currentChildId = ref('')
// //
const adaptabilityOptions = ref([]); const initDict = async () => {
try {
getDict("page_adaptability").then((res) => { const [res1, res2] = await Promise.all([getDict("page_adaptability"), getDict('menuCode')]);
console.log('获取适配方式选项', res); adaptabilityOptions.value = res1.map(item => {
adaptabilityOptions.value = res.map(item => { return {
const {label, value, ...rest} = item; label: item.label,
return { value: item.value,
label: label, }
value: value, })
} menuOptions.value = res2.map(item => {
}) return {
}).catch((errors) => { label: item.label,
console.log('获取适配方式选项失败', errors); value: item.value
message.error('获取适配方式选项失败'); }
}) })
} catch (error) {
message.error(error.message);
}
}
// //
const parentModalTitle = computed(() => (isEditParent.value ? '编辑父级菜单' : '添加父级菜单')) const parentModalTitle = computed(() => (isEditParent.value ? '编辑父级菜单' : '添加父级菜单'))
const childModalTitle = computed(() => (isEditChild.value ? '编辑子级菜单' : '添加子级菜单')) const childModalTitle = computed(() => (isEditChild.value ? '编辑子级菜单' : '添加子级菜单'))
// //
const parentMenuOptions = computed<SelectOption[]>(() => { const parentMenuOptions = computed<SelectOption[]>(() => {
console.log(menuTree.value, 'menuTree.value');
return menuTree.value.map(menu => ({ return menuTree.value.map(menu => ({
label: menu.label, label: menu.label,
value: menu.key value: menu.key
@ -336,6 +345,7 @@ const { stop } = watch(menuType, (newVal) => {
}) })
// //
onMounted(() => { onMounted(() => {
initDict();
loadSvgIcons(); loadSvgIcons();
fetchMenuData(); fetchMenuData();
}); });
@ -344,7 +354,6 @@ onBeforeUnmount(() => {
}) })
const renderTreeLabel = ({ option }) => { const renderTreeLabel = ({ option }) => {
console.log(option,'option');
return h('div', { return h('div', {
style: { style: {
display: 'flex', display: 'flex',
@ -364,7 +373,7 @@ const renderTreeLabel = ({ option }) => {
h(NTag, { h(NTag, {
type: option?.rawData.status === 'enable' ? 'success' : 'error', type: option?.rawData.status === 'enable' ? 'success' : 'error',
size: 'small' size: 'small'
}, option?.rawData.status === 'enable' ? 'Enabled' : 'Disabled') }, option?.rawData.status === 'enable' ? '启用' : '停用')
]) ])
} }
/** /**
@ -480,7 +489,7 @@ const handleAddParent = () => {
path: '', path: '',
label: '', label: '',
icon: '', icon: '',
menuCode: '', menuCode: [],
menuName: '', menuName: '',
adaptability: 'pc', adaptability: 'pc',
component: '', component: '',
@ -499,7 +508,7 @@ const handleEditParent = (menu: MenuNode) => {
path: menu.path, path: menu.path,
label: menu.label, label: menu.label,
icon: menu.icon, icon: menu.icon,
menuCode: menu.menuCode, menuCode: menu.menuCode ? menu.menuCode.split(',') : [],
menuName: menu.menuName, menuName: menu.menuName,
adaptability: menu.adaptability, adaptability: menu.adaptability,
component: menu.component, component: menu.component,
@ -518,10 +527,16 @@ const handleSubmitParent = () => {
if (isEditParent.value) { if (isEditParent.value) {
await editParentMenu({ await editParentMenu({
uuid: currentParentId.value, uuid: currentParentId.value,
...parentForm.value ...parentForm.value,
menuCode: parentForm.value.menuCode.join(','),
menuName: getMenuName(parentForm.value.menuCode, menuOptions.value)
}) })
} else { } else {
await addParentMenu(parentForm.value) await addParentMenu({
...parentForm.value,
menuCode: parentForm.value.menuCode.join(','),
menuName: getMenuName(parentForm.value.menuCode, menuOptions.value)
})
} }
showParentModal.value = false; showParentModal.value = false;
message.success('操作成功!'); message.success('操作成功!');
@ -540,7 +555,7 @@ const handleAddChild = (parentId: string) => {
path: '', path: '',
label: '', label: '',
icon: '', icon: '',
menuCode: '', menuCode: [],
menuName: '', menuName: '',
adaptability: 'pc', adaptability: 'pc',
component: '', component: '',
@ -560,7 +575,7 @@ const handleEditChild = (menu: MenuNode) => {
path: menu.path, path: menu.path,
label: menu.label, label: menu.label,
icon: menu.icon, icon: menu.icon,
menuCode: menu.menuCode, menuCode: menu.menuCode ? menu.menuCode.split(',') : [],
menuName: menu.menuName, menuName: menu.menuName,
adaptability: menu.adaptability, adaptability: menu.adaptability,
component: menu.component, component: menu.component,
@ -579,10 +594,16 @@ const handleSubmitChild = () => {
if (isEditChild.value) { if (isEditChild.value) {
await eidtChildMenu({ await eidtChildMenu({
uuid: currentChildId.value, uuid: currentChildId.value,
...childForm.value ...childForm.value,
menuCode: childForm.value.menuCode.join(','),
menuName: getMenuName(childForm.value.menuCode, menuOptions.value)
}) })
} else { } else {
await addChildMenu(childForm.value) await addChildMenu({
...childForm.value,
menuCode: childForm.value.menuCode.join(','),
menuName: getMenuName(childForm.value.menuCode, menuOptions.value)
})
} }
showChildModal.value = false; showChildModal.value = false;
message.success('操作成功!'); message.success('操作成功!');

View 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(',');
}