feat: 路由树扁平化处理
This commit is contained in:
parent
512bfa7cad
commit
44ad51c7b0
@ -6,21 +6,22 @@ export const useMenuMethod = () => {
|
||||
/**
|
||||
* 多级菜单的显示隐藏
|
||||
* @param {Menu.MenuOptions} item 菜单的item
|
||||
* @returns 若多级菜单有children,并且不隐藏,返回true,否则返回false
|
||||
* @returns type:1为目录,并且不隐藏,返回true,否则返回false
|
||||
*/
|
||||
const menuShow = (item: Menu.MenuOptions) => {
|
||||
if (item.children && item.children?.length > 0 && !item.meta.hide) return true;
|
||||
if (item.meta.type == 1 && !item.meta.hide) return true;
|
||||
return false;
|
||||
};
|
||||
/**
|
||||
* 单级菜单的显示隐藏
|
||||
* @param {Menu.MenuOptions} item 菜单的item
|
||||
* @returns 若单级菜单不隐藏,返回true,否则返回false
|
||||
* @returns type:2为菜单,并且单级菜单不隐藏,返回true,否则返回false
|
||||
*/
|
||||
const aMenuShow = (item: Menu.MenuOptions) => {
|
||||
if (!item.meta.hide) return true;
|
||||
if (item.meta.type == 2 && !item.meta.hide) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
menuShow,
|
||||
aMenuShow
|
||||
|
||||
@ -21,7 +21,19 @@ const i18n = createI18n({
|
||||
legacy: false, // Composition API模式需要设为false
|
||||
globalInjection: true, // 全局生效: $
|
||||
locale: getLang(), // 默认语言
|
||||
messages // 数据源
|
||||
messages, // 数据源
|
||||
missing: (_: string, key: string) => {
|
||||
return removeBeforeFirstDot(key);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param { string } str 国际化key
|
||||
* @returns 去掉第一个点之前的字符串,例如:"menu.home" => "home"
|
||||
*/
|
||||
function removeBeforeFirstDot(str: string) {
|
||||
const dotIndex = str.indexOf(".");
|
||||
return dotIndex >= 0 ? str.slice(dotIndex + 1) : "未定义";
|
||||
}
|
||||
|
||||
export default i18n;
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
:popup-max-height="600"
|
||||
>
|
||||
<template v-for="item in routeTree" :key="item.name">
|
||||
<a-menu-item v-if="aMenuShow(item)" :key="item.name" :popup-max-height="600">
|
||||
<a-menu-item v-if="!item.meta.hide" :key="item.name" :popup-max-height="600">
|
||||
<template #icon v-if="item.meta.svgIcon || item.meta.icon">
|
||||
<MenuItemIcon :svg-icon="item.meta.svgIcon" :icon="item.meta.icon" />
|
||||
</template>
|
||||
@ -52,7 +52,6 @@ import { useRoutesConfigStore } from "@/store/modules/route-config";
|
||||
import { useRoutingMethod } from "@/hooks/useRoutingMethod";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useThemeConfig } from "@/store/modules/theme-config";
|
||||
import { useMenuMethod } from "@/hooks/useMenuMethod";
|
||||
import { useDevicesSize } from "@/hooks/useDevicesSize";
|
||||
defineOptions({ name: "LayoutMixing" });
|
||||
const route = useRoute();
|
||||
@ -61,7 +60,6 @@ const routerStore = useRoutesConfigStore();
|
||||
const themeStore = useThemeConfig();
|
||||
const { isFooter, collapsed, asideDark, language } = storeToRefs(themeStore);
|
||||
const { routeTree } = storeToRefs(routerStore);
|
||||
const { aMenuShow } = useMenuMethod();
|
||||
const { isPc } = useDevicesSize();
|
||||
|
||||
const drawing = ref<boolean>(true);
|
||||
@ -89,10 +87,18 @@ const onMenuItem = (key: string) => {
|
||||
if (find) {
|
||||
// 给左侧树赋值
|
||||
setAsideMenu(find);
|
||||
// 这里直接跳转父级path,因为父级路由做了重定向
|
||||
// 若有重定向,则跳转到重定向的路由
|
||||
// 如果有子路由则重定向到自己的第一个菜单
|
||||
// 如果没有子路由则说明当前父级是一个菜单,直接跳转
|
||||
router.push(find.path);
|
||||
let path = "";
|
||||
if (find.redirect) {
|
||||
path = find.redirect;
|
||||
} else if (find.children && find.children.length > 0) {
|
||||
path = find.children[0].path;
|
||||
} else {
|
||||
path = find.path;
|
||||
}
|
||||
router.push(path);
|
||||
} else {
|
||||
router.push("/404");
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -52,8 +52,8 @@ export const treeSort = (tree: Menu.MenuOptions[]) => {
|
||||
|
||||
/**
|
||||
* 过滤路由树,返回有权限的树
|
||||
* 1、先过滤停用的菜单,该菜单是不可访问的,直接去掉
|
||||
* 2、根据角色权限过滤原始路由树
|
||||
* 1、根据角色权限过滤原始路由树
|
||||
* 2、过滤禁用的菜单,该菜单是不可访问的,直接去掉
|
||||
* @param {array} tree 根据角色权限过滤原始路由树
|
||||
* @returns 返回有权限的树
|
||||
*/
|
||||
@ -65,7 +65,6 @@ export const filterByRole = (tree: any, userRoles: Array<string>) => {
|
||||
}
|
||||
// 过滤是否禁用
|
||||
if (item?.meta?.disable) return false;
|
||||
if (item.children) item.children = filterByRole(item.children, userRoles);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
@ -122,3 +121,32 @@ export function deepClone(data: any) {
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将扁平路由组装成树形结构
|
||||
* @param {array} nodes 扁平数组
|
||||
* @returns 树形结构
|
||||
*/
|
||||
export const buildTreeOptimized = (nodes: Menu.MenuOptions[]) => {
|
||||
const nodeMap = new Map(); // 哈希映射存储节点引用
|
||||
const roots = []; // 存储顶层节点
|
||||
|
||||
// 单次遍历构建结构(兼容乱序数据)
|
||||
for (const node of nodes) {
|
||||
const { id, parentId } = node;
|
||||
node.children = []; // 初始化子节点数组
|
||||
|
||||
// 将当前节点存入哈希表
|
||||
nodeMap.set(id, node);
|
||||
|
||||
// 处理父子关系
|
||||
if (parentId === "0") {
|
||||
roots.push(node); // 顶层节点直接加入结果
|
||||
} else {
|
||||
const parent = nodeMap.get(parentId);
|
||||
parent?.children.push(node); // 子节点挂载到父级
|
||||
}
|
||||
}
|
||||
|
||||
return roots;
|
||||
};
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
import type { MockMethod } from "vite-plugin-mock";
|
||||
import { deepClone, filterByRole, treeSort, resultSuccess } from "../_utils";
|
||||
import { deepClone, filterByRole, buildTreeOptimized, treeSort, resultSuccess } from "../_utils";
|
||||
import systemMenu from "../_data/system_menu";
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* 后端TODO
|
||||
* 后端接口需要做的事情:
|
||||
* 1、根据token判断角色
|
||||
* 2、根据角色过滤权限树
|
||||
* 2、过滤扁平路由,根据角色过滤权限和非禁用的节点
|
||||
* 3、将扁平路由转换为树结构
|
||||
* 3、路由树排序
|
||||
* 4、返回路由树
|
||||
*
|
||||
* 前端TODO
|
||||
* 前端调用接口拿到路由数据后:
|
||||
* 1、将模块设置为真实模块
|
||||
* 2、存储路由树,用于生成菜单
|
||||
* 3、根据树生成一维路由数组
|
||||
@ -29,7 +30,12 @@ export default [
|
||||
// 这里模拟两个角色,admin、common
|
||||
let userRoles = token === "Admin-Token" ? ["admin"] : ["common"];
|
||||
const originTree: any = deepClone(systemMenu);
|
||||
return resultSuccess(treeSort(filterByRole(originTree, userRoles)));
|
||||
// 1. 过滤扁平路由,根据角色返回有权限且非禁用的节点
|
||||
const survivalTree = filterByRole(originTree, userRoles);
|
||||
// 2. 将扁平路由转换为树结构
|
||||
// 2. 给路由树排序
|
||||
// 3. 返回路由树
|
||||
return resultSuccess(treeSort(buildTreeOptimized(survivalTree)));
|
||||
}
|
||||
}
|
||||
] as MockMethod[];
|
||||
|
||||
3
src/typings/global.d.ts
vendored
3
src/typings/global.d.ts
vendored
@ -2,6 +2,8 @@
|
||||
/* 路由-Menu */
|
||||
declare namespace Menu {
|
||||
interface MenuOptions {
|
||||
id: string;
|
||||
parentId: string;
|
||||
path: string;
|
||||
name: string;
|
||||
redirect?: string;
|
||||
@ -21,6 +23,7 @@ declare namespace Menu {
|
||||
icon?: string;
|
||||
svgIcon?: string;
|
||||
sort?: number;
|
||||
type?: number;
|
||||
}
|
||||
}
|
||||
/* tabs菜单 */
|
||||
|
||||
@ -1,33 +1,33 @@
|
||||
<template>
|
||||
<div class="snow-page">
|
||||
<div class="snow-inner">
|
||||
<p>{{ $t(`system.switch-language-to-preview`) }}</p>
|
||||
<br />
|
||||
<div>
|
||||
<a-date-picker style="width: 200px" v-model="form.time" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-time-picker type="time-range" style="width: 252px" v-model="form.timeRange" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-range-picker style="width: 360px" show-time format="YYYY-MM-DD HH:mm" v-model="form.date" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-pagination :total="50" show-total show-jumper show-page-size />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const form = reactive({
|
||||
time: "",
|
||||
timeRange: [],
|
||||
date: []
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<template>
|
||||
<div class="snow-page">
|
||||
<div class="snow-inner">
|
||||
<p>{{ $t(`system.switch-language-to-preview`) }}</p>
|
||||
<br />
|
||||
<div>
|
||||
<a-date-picker style="width: 200px" v-model="form.time" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-time-picker type="time-range" style="width: 252px" v-model="form.timeRange" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-range-picker style="width: 360px" show-time format="YYYY-MM-DD HH:mm" v-model="form.date" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-pagination :total="50" show-total show-jumper show-page-size />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const form = reactive({
|
||||
time: "",
|
||||
timeRange: [],
|
||||
date: []
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@ -83,7 +83,7 @@
|
||||
<template #icon><icon-edit /></template>
|
||||
<span>修改</span>
|
||||
</a-button>
|
||||
<a-popconfirm type="warning" content="确定删除该角色吗?">
|
||||
<a-popconfirm type="warning" content="确定删除该账号吗?">
|
||||
<a-button type="primary" status="danger" size="mini" :disabled="record.admin">
|
||||
<template #icon><icon-delete /></template>
|
||||
<span>删除</span>
|
||||
@ -166,7 +166,7 @@
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item field="description" label="描述" validate-trigger="blur">
|
||||
<a-textarea v-model="addFrom.description" placeholder="请输入字典描述" allow-clear />
|
||||
<a-textarea v-model="addFrom.description" placeholder="请输入描述" allow-clear />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
<template #icon><icon-edit /></template>
|
||||
<span>修改</span>
|
||||
</a-button>
|
||||
<a-popconfirm type="warning" content="确定删除该角色吗?" @ok="onDelete">
|
||||
<a-popconfirm type="warning" content="确定删除该字典吗?" @ok="onDelete">
|
||||
<a-button type="primary" status="danger" size="mini">
|
||||
<template #icon><icon-delete /></template>
|
||||
<span>删除</span>
|
||||
@ -150,7 +150,7 @@
|
||||
<template #icon><icon-edit /></template>
|
||||
<span>修改</span>
|
||||
</a-button>
|
||||
<a-popconfirm type="warning" content="确定删除该角色吗?">
|
||||
<a-popconfirm type="warning" content="确定删除该字典吗?">
|
||||
<a-button type="primary" status="danger" size="mini">
|
||||
<template #icon><icon-delete /></template>
|
||||
<span>删除</span>
|
||||
|
||||
@ -142,7 +142,7 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="description" label="描述" validate-trigger="blur">
|
||||
<a-textarea v-model="addFrom.description" placeholder="请输入字典描述" allow-clear />
|
||||
<a-textarea v-model="addFrom.description" placeholder="请输入描述" allow-clear />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
@ -28,8 +28,10 @@
|
||||
<span>新增</span>
|
||||
</a-button>
|
||||
<a-button type="primary" status="success" @click="onExpand">
|
||||
<template #icon><icon-swap /></template>
|
||||
<span>折叠</span>
|
||||
<template #icon>
|
||||
<icon-swap />
|
||||
</template>
|
||||
<span>{{ expand ? "收起" : "展开" }}</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-row>
|
||||
@ -118,7 +120,7 @@
|
||||
<template #icon><icon-edit /></template>
|
||||
<span>修改</span>
|
||||
</a-button>
|
||||
<a-button size="mini" type="primary" status="success">
|
||||
<a-button size="mini" type="primary" status="success" v-if="record.meta.type != 3">
|
||||
<template #icon><icon-plus /></template>
|
||||
<span>新增</span>
|
||||
</a-button>
|
||||
@ -138,26 +140,33 @@
|
||||
<div>
|
||||
<a-form ref="formRef" auto-label-width :rules="rules" :model="addFrom">
|
||||
<a-form-item field="type" label="菜单类型" validate-trigger="blur">
|
||||
<a-radio-group type="button" v-model="addFrom.type">
|
||||
<a-radio-group type="button" v-model="addFrom.type" @change="typeChange">
|
||||
<a-radio v-for="item in menuType" :key="item.value" :value="item.value">{{ item.name }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="id" label="上级菜单" validate-trigger="blur">
|
||||
<a-form-item field="parentId" label="上级菜单" validate-trigger="blur">
|
||||
<a-tree-select
|
||||
v-model="addFrom.id"
|
||||
v-model="addFrom.parentId"
|
||||
:data="menuTree"
|
||||
:field-names="{
|
||||
key: 'id',
|
||||
title: 'i18n',
|
||||
children: 'children'
|
||||
}"
|
||||
allow-clear
|
||||
placeholder="请选择上级菜单"
|
||||
></a-tree-select>
|
||||
<template #extra>
|
||||
<div>未选择则默认第一级</div>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-row :gutter="24">
|
||||
<a-row :gutter="24" v-if="[1, 2].includes(addFrom.type)">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="svgIcon" label="自定义图标" validate-trigger="blur">
|
||||
<SelectIcon type="svg" v-model="addFrom.svgIcon" />
|
||||
<template #extra>
|
||||
<div>自定义图标优先级高于菜单图标</div>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
@ -166,42 +175,59 @@
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="12">
|
||||
<a-form-item field="name" label="菜单名称" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.name" placeholder="请输入菜单名称" allow-clear />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item field="title" label="国际化Key" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.title" placeholder="请输入国际化Key" allow-clear />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="path" label="路由路径" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.path" placeholder="请输入路由路径" allow-clear />
|
||||
<a-form-item field="name" label="菜单名称" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.name" placeholder="请输入菜单名称,如:home" allow-clear @change="nameChange" />
|
||||
</a-form-item>
|
||||
<a-form-item field="redirect" label="重定向路径" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.redirect" placeholder="请输入重定向路径" allow-clear />
|
||||
<a-form-item v-if="[1, 2].includes(addFrom.type)" field="path" label="路由路径" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.path" placeholder="请输入路由路径,如:/home" allow-clear disabled />
|
||||
</a-form-item>
|
||||
<a-form-item field="title" label="菜单标题" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.title" placeholder="请输入菜单标题" allow-clear />
|
||||
<template #extra>
|
||||
<div>
|
||||
优先匹配国际化Key
|
||||
<span v-if="addFrom.title">menu.{{ addFrom.title }}</span>
|
||||
(无对应Key则直接取标题展示)
|
||||
</div>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="addFrom.type == 3" field="permission" label="权限标识" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.permission" placeholder="请输入权限标识,如:sys:btn:add" allow-clear />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="[1, 2].includes(addFrom.type)" field="redirect" label="路由重定向" validate-trigger="blur">
|
||||
<a-input v-model="addFrom.redirect" placeholder="请输入路由重定向" allow-clear />
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="addFrom.type == 2"
|
||||
field="component"
|
||||
label="组件路径"
|
||||
validate-trigger="blur"
|
||||
:disabled="addFrom.isLink"
|
||||
>
|
||||
<a-input v-model="addFrom.component" placeholder="请输入组件路径" allow-clear>
|
||||
<template #prepend>@/views/</template>
|
||||
<template #append>.vue</template>
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-row :gutter="24">
|
||||
<a-col :span="8">
|
||||
<a-col :span="8" v-if="[1, 2].includes(addFrom.type)">
|
||||
<a-form-item field="hide" label="显示状态" validate-trigger="blur">
|
||||
<a-switch type="round" v-model="addFrom.hide">
|
||||
<a-switch type="round" v-model="addFrom.hide" :checked-value="false" :unchecked-value="true">
|
||||
<template #checked> 显示 </template>
|
||||
<template #unchecked> 隐藏 </template>
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="8" v-if="[1, 2].includes(addFrom.type)">
|
||||
<a-form-item field="disable" label="启用状态" validate-trigger="blur">
|
||||
<a-switch type="round" v-model="addFrom.disable">
|
||||
<a-switch type="round" v-model="addFrom.disable" :checked-value="false" :unchecked-value="true">
|
||||
<template #checked> 启用 </template>
|
||||
<template #unchecked> 禁用 </template>
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-col :span="8" v-if="addFrom.type == 2">
|
||||
<a-form-item field="keepAlive" label="是否缓存" validate-trigger="blur">
|
||||
<a-switch type="round" v-model="addFrom.keepAlive">
|
||||
<template #checked> 是 </template>
|
||||
@ -210,7 +236,7 @@
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row :gutter="24">
|
||||
<a-row :gutter="24" v-if="addFrom.type == 2">
|
||||
<a-col :span="8">
|
||||
<a-form-item field="affix" label="固定Tabs" validate-trigger="blur">
|
||||
<a-switch type="round" v-model="addFrom.affix">
|
||||
@ -221,22 +247,22 @@
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="isLink" label="是否外链" validate-trigger="blur">
|
||||
<a-switch type="round" v-model="addFrom.isLink">
|
||||
<a-switch type="round" v-model="addFrom.isLink" @change="onIsLink">
|
||||
<template #checked> 是 </template>
|
||||
<template #unchecked> 否 </template>
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item field="iframe" label="内嵌外链窗口" validate-trigger="blur" :disabled="!addFrom.isLink">
|
||||
<a-switch type="round" v-model="addFrom.iframe">
|
||||
<a-form-item field="iframe" label="内嵌窗口" validate-trigger="blur" :disabled="!addFrom.isLink">
|
||||
<a-switch type="round" v-model="addFrom.iframe" @change="onIframe">
|
||||
<template #checked> 是 </template>
|
||||
<template #unchecked> 否 </template>
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-form-item field="link" label="外链路径" validate-trigger="blur" v-if="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-form-item>
|
||||
<a-form-item field="link" label="菜单排序" validate-trigger="blur">
|
||||
@ -280,44 +306,14 @@ const onEdit = (row: Menu.MenuOptions) => {
|
||||
};
|
||||
// 新增
|
||||
const open = ref(false);
|
||||
const rules = {
|
||||
userName: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入用户名称"
|
||||
}
|
||||
],
|
||||
nickName: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入昵称"
|
||||
}
|
||||
],
|
||||
sex: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择性别"
|
||||
}
|
||||
],
|
||||
deptId: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择所属部门"
|
||||
}
|
||||
],
|
||||
roles: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择角色"
|
||||
}
|
||||
],
|
||||
status: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择状态"
|
||||
}
|
||||
]
|
||||
};
|
||||
const rules = ref({
|
||||
parentId: [{ required: false, message: "请选择上级菜单" }],
|
||||
name: [{ required: true, message: "请输入菜单名称" }],
|
||||
title: [{ required: true, message: "请输入菜单标题" }],
|
||||
path: [{ required: true, message: "请输入路由路径" }],
|
||||
permission: [{ required: true, message: "请输入权限标识" }]
|
||||
});
|
||||
|
||||
const menuType = ref([
|
||||
{ name: "目录", value: 1 },
|
||||
{ name: "菜单", value: 2 },
|
||||
@ -325,13 +321,15 @@ const menuType = ref([
|
||||
]);
|
||||
const addFrom = ref<any>({
|
||||
type: 1,
|
||||
id: "",
|
||||
parentId: "",
|
||||
svgIcon: "",
|
||||
icon: "",
|
||||
name: "",
|
||||
title: "",
|
||||
permission: "",
|
||||
path: "",
|
||||
redirect: "",
|
||||
component: "",
|
||||
hide: false,
|
||||
disable: false,
|
||||
keepAlive: true,
|
||||
@ -353,19 +351,22 @@ const handleOk = async () => {
|
||||
let state = await formRef.value.validate();
|
||||
if (state) return (open.value = true); // 校验不通过
|
||||
arcoMessage("success", "模拟提交成功");
|
||||
console.log("提交", addFrom.value);
|
||||
};
|
||||
// 关闭对话框动画结束后触发
|
||||
const afterClose = () => {
|
||||
formRef.value.resetFields();
|
||||
addFrom.value = {
|
||||
type: 1,
|
||||
id: "",
|
||||
parentId: "",
|
||||
svgIcon: "",
|
||||
icon: "",
|
||||
name: "",
|
||||
title: "",
|
||||
permission: "",
|
||||
path: "",
|
||||
redirect: "",
|
||||
component: "",
|
||||
hide: false,
|
||||
disable: false,
|
||||
keepAlive: true,
|
||||
@ -377,17 +378,59 @@ const afterClose = () => {
|
||||
};
|
||||
};
|
||||
|
||||
// 菜单类型
|
||||
const typeChange = (val: number) => {
|
||||
rules.value.parentId[0].required = val == 3;
|
||||
formRef.value.clearValidate();
|
||||
};
|
||||
|
||||
// 菜单名称
|
||||
const nameChange = (str: string) => {
|
||||
// 自定关联路由路径
|
||||
addFrom.value.path = str ? "/" + str : "";
|
||||
};
|
||||
|
||||
// 是否外链
|
||||
const onIsLink = (is: boolean) => {
|
||||
// 非外链
|
||||
if (!is) {
|
||||
// 关联iframe和link
|
||||
addFrom.value.iframe = false;
|
||||
addFrom.value.link = "";
|
||||
addFrom.value.component = "";
|
||||
} else {
|
||||
// 外链
|
||||
addFrom.value.component = "link/external/external";
|
||||
}
|
||||
};
|
||||
|
||||
// 是否内嵌外链窗口
|
||||
const onIframe = (is: boolean) => {
|
||||
// 非内嵌
|
||||
if (!is) {
|
||||
// 关联iframe和link
|
||||
addFrom.value.component = "link/external/external";
|
||||
} else {
|
||||
// 内嵌
|
||||
addFrom.value.component = "link/internal/internal";
|
||||
}
|
||||
};
|
||||
|
||||
const onSearch = () => getMenu();
|
||||
const loading = ref(false);
|
||||
const tableRef = ref();
|
||||
const tableTree = ref([]);
|
||||
const menuTree = ref<any>([]);
|
||||
const getMenu = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
let { data } = await getMenuListAPI();
|
||||
// 语言翻译
|
||||
translation(data);
|
||||
menuTree.value = filterTree(data);
|
||||
// 列表数据
|
||||
tableTree.value = data;
|
||||
// 过滤type:3的节点,该节点是按钮权限,不显示在菜单中-用于下拉选择
|
||||
menuTree.value = filterTree(data);
|
||||
console.log("数据", data, menuTree.value);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@ -410,9 +453,12 @@ const translation = (tree: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 过滤掉按钮菜单-type:3为按钮菜单-用于下拉选择
|
||||
const menuTree = ref([]);
|
||||
function filterTree(nodes: any) {
|
||||
/**
|
||||
* 过滤type:3的节点,该节点是按钮权限,不显示在菜单中-用于下拉选择
|
||||
* @param {object} nodes 路由树
|
||||
* @returns 节点过滤后的路由树
|
||||
*/
|
||||
const filterTree = (nodes: Menu.MenuOptions[]) => {
|
||||
// 过滤当前层级的节点,排除 type 为 3 的节点
|
||||
return nodes
|
||||
.filter((node: any) => node.meta.type !== 3)
|
||||
@ -431,7 +477,7 @@ function filterTree(nodes: any) {
|
||||
}
|
||||
return newNode;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getMenu();
|
||||
</script>
|
||||
|
||||
@ -17,7 +17,7 @@ export default defineConfig(({ mode }) => {
|
||||
base: env.VITE_PUBLIC_PATH,
|
||||
server: {
|
||||
// host: "0.0.0.0",
|
||||
open: true,
|
||||
open: false,
|
||||
// 为开发服务器配置自定义代理规则-用于开发时的代理
|
||||
proxy: {
|
||||
"/api": {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user