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 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']

View File

@ -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>

View File

@ -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,
});
}

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>
<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
})

View File

@ -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 }

View File

@ -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 }

View File

@ -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;
}
}
}
}

View File

@ -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;

View File

@ -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('/');
}
}

View File

@ -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>

View File

@ -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('操作成功!');

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