completepage: 🍻 菜单管理
This commit is contained in:
parent
ed277c1b81
commit
024188ee3d
56
README.md
56
README.md
|
@ -17,3 +17,59 @@
|
|||
## 许可证
|
||||
|
||||
[MIT © 2020-present, pure-admin](./LICENSE)
|
||||
|
||||
## 配置文件
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "5.8.0",
|
||||
// 平台版本号
|
||||
"Title": "PureAdmin",
|
||||
// 平台标题
|
||||
"FixedHeader": true,
|
||||
// 是否固定页头和标签页(true 内容区超出出现纵向滚动条 false 页头、标签页、内容区可纵向滚动)
|
||||
"HiddenSideBar": false,
|
||||
// 隐藏菜单和页头,只显示标签页和内容区
|
||||
"MultiTagsCache": false,
|
||||
// 是否开启持久化标签 (会缓存)
|
||||
"KeepAlive": true,
|
||||
// 是否开启组件缓存(此处不同于路由的 keepAlive,如果此处为 true 表示设置路由的 keepAlive 起效,反之设置 false 屏蔽平台整体的 keepAlive,即使路由设置了keepAlive 也不再起作用)
|
||||
"Locale": "zh",
|
||||
// 默认国际化语言 (zh 中文、en 英文)(会缓存)(max版本额外配置:tw 繁體中文、ja 日语、ko 韩语)
|
||||
"Layout": "vertical",
|
||||
// 导航菜单模式 (vertical 左侧菜单模式、horizontal 顶部菜单模式、mix 混合菜单模式)(会缓存)(max版本额外配置:double 左侧双栏菜单模式)
|
||||
"Theme": "light",
|
||||
// 主题模式(会缓存)
|
||||
"DarkMode": false,
|
||||
// 是否开启暗黑模式 (会缓存)
|
||||
"OverallStyle": "light",
|
||||
// 整体风格(浅色:light、深色:dark、自动:system)(会缓存)更多详情看 https://github.com/pure-admin/vue-pure-admin/commit/dd783136229da9e291b518df93227111f4216ad0#commitcomment-137027417
|
||||
"Grey": false,
|
||||
// 灰色模式(会缓存)
|
||||
"Weak": false,
|
||||
// 色弱模式(会缓存)
|
||||
"HideTabs": false,
|
||||
// 是否隐藏标签页(会缓存)
|
||||
"HideFooter": false,
|
||||
// 是否隐藏页脚(会缓存)
|
||||
"SidebarStatus": true,
|
||||
// vertical左侧菜单模式模式下侧边栏状态(true 展开、false 收起)(会缓存)
|
||||
"EpThemeColor": "#409EFF",
|
||||
// 主题色(会缓存)
|
||||
"ShowLogo": true,
|
||||
// 是否显示logo(会缓存)
|
||||
"ShowModel": "smart",
|
||||
// 标签页风格(smart 灵动模式、card 卡片模式)(会缓存)
|
||||
"MenuArrowIconNoTransition": false,
|
||||
// 菜单展开、收起图标是否开启动画,如遇菜单展开、收起卡顿设置成 true 即可(默认 false,开启动画)
|
||||
"CachingAsyncRoutes": false,
|
||||
// 是否开启动态路由缓存本地的全局配置,默认 false
|
||||
"TooltipEffect": "light",
|
||||
// 可配置平台主体所有 el-tooltip 的 effect 属性,默认 light,不会影响业务代码
|
||||
"ResponsiveStorageNameSpace": "responsive-",
|
||||
// 本地响应式存储的命名空间
|
||||
"MenuSearchHistory": 6
|
||||
// 菜单搜索历史的最大条目
|
||||
}
|
||||
|
||||
```
|
|
@ -1,6 +0,0 @@
|
|||
import { http } from '@/api/service/request';
|
||||
import type { BaseResult } from '@/api/service/types';
|
||||
|
||||
export const getRouterAsync = () => {
|
||||
return http.request<BaseResult<any>>('get', 'router/getRouterAsync');
|
||||
};
|
|
@ -12,6 +12,32 @@ type ResultTable = {
|
|||
pageNo?: number;
|
||||
};
|
||||
|
||||
/** 系统管理-用户路由获取 */
|
||||
export const getRouterAsync = () => {
|
||||
return http.request<BaseResult<any>>('get', 'router/getRouterAsync');
|
||||
};
|
||||
|
||||
/** 系统管理-菜单管理列表 */
|
||||
export const getMenuList = (data?: any) => {
|
||||
return http.request<BaseResult<ResultTable>>('get', `router/getMenus`, { data });
|
||||
};
|
||||
|
||||
/** 系统管理-添加菜单 */
|
||||
export const addMenu = (data?: any) => {
|
||||
return http.request<BaseResult<any>>('post', `router/addMenu`, { data });
|
||||
};
|
||||
|
||||
/** 系统管理-更新菜单 */
|
||||
export const updateMenu = (data?: any) => {
|
||||
return http.request<BaseResult<any>>('put', `router/updateMenu`, { data });
|
||||
};
|
||||
|
||||
/** 系统管理-删除菜单 */
|
||||
export const deletedMenuByIds = (data?: any) => {
|
||||
return http.request<BaseResult<any>>('delete', `router/deletedMenuByIds`, { data });
|
||||
};
|
||||
|
||||
// ------------未确认------------
|
||||
/** 获取系统管理-用户管理列表 */
|
||||
export const getUserList = (data?: object) => {
|
||||
return http.request<BaseResult<ResultTable>>('post', '/user', { data });
|
||||
|
@ -32,12 +58,6 @@ export const getRoleList = (data?: object) => {
|
|||
return http.request<ResultTable>('post', '/role', { data });
|
||||
};
|
||||
|
||||
/** 获取系统管理-菜单管理列表 */
|
||||
export const getMenuList = (data?: any) => {
|
||||
// /${data.page}/${data.limit}
|
||||
return http.request<BaseResult<ResultTable>>('get', `router/getMenus`, { data });
|
||||
};
|
||||
|
||||
/** 获取系统管理-部门管理列表 */
|
||||
export const getDeptList = (data?: object) => {
|
||||
return http.request<any>('post', '/dept', { data });
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
import axios from "axios";
|
||||
import type { App } from "vue";
|
||||
import axios from 'axios';
|
||||
import type { App } from 'vue';
|
||||
|
||||
let config: object = {};
|
||||
const { VITE_PUBLIC_PATH } = import.meta.env;
|
||||
|
||||
const setConfig = (cfg?: unknown) => {
|
||||
config = Object.assign(config, cfg);
|
||||
config = Object.assign(config, cfg);
|
||||
};
|
||||
|
||||
const getConfig = (key?: string): PlatformConfigs => {
|
||||
if (typeof key === "string") {
|
||||
const arr = key.split(".");
|
||||
if (arr && arr.length) {
|
||||
let data = config;
|
||||
arr.forEach(v => {
|
||||
if (data && typeof data[v] !== "undefined") {
|
||||
data = data[v];
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
if (typeof key === 'string') {
|
||||
const arr = key.split('.');
|
||||
if (arr && arr.length) {
|
||||
let data = config;
|
||||
arr.forEach(v => {
|
||||
if (data && typeof data[v] !== 'undefined') {
|
||||
data = data[v];
|
||||
} else {
|
||||
data = null;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
};
|
||||
|
||||
/** 获取项目动态全局配置 */
|
||||
export const getPlatformConfig = async (app: App): Promise<undefined> => {
|
||||
app.config.globalProperties.$config = getConfig();
|
||||
return axios({
|
||||
method: "get",
|
||||
url: `${VITE_PUBLIC_PATH}platform-config.json`
|
||||
})
|
||||
.then(({ data: config }) => {
|
||||
let $config = app.config.globalProperties.$config;
|
||||
// 自动注入系统配置
|
||||
if (app && $config && typeof config === "object") {
|
||||
$config = Object.assign($config, config);
|
||||
app.config.globalProperties.$config = $config;
|
||||
// 设置全局配置
|
||||
setConfig($config);
|
||||
}
|
||||
return $config;
|
||||
})
|
||||
.catch(() => {
|
||||
throw "请在public文件夹下添加platform-config.json配置文件";
|
||||
});
|
||||
app.config.globalProperties.$config = getConfig();
|
||||
return axios({
|
||||
method: 'get',
|
||||
url: `${VITE_PUBLIC_PATH}platform-config.json`,
|
||||
})
|
||||
.then(({ data: config }) => {
|
||||
let $config = app.config.globalProperties.$config;
|
||||
// 自动注入系统配置
|
||||
if (app && $config && typeof config === 'object') {
|
||||
$config = Object.assign($config, config);
|
||||
app.config.globalProperties.$config = $config;
|
||||
// 设置全局配置
|
||||
setConfig($config);
|
||||
}
|
||||
return $config;
|
||||
})
|
||||
.catch(() => {
|
||||
throw '请在public文件夹下添加platform-config.json配置文件';
|
||||
});
|
||||
};
|
||||
|
||||
/** 本地响应式存储的命名空间 */
|
||||
|
|
|
@ -1,38 +1,38 @@
|
|||
import { $t } from "@/plugins/i18n";
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
export default {
|
||||
path: "/error",
|
||||
redirect: "/error/403",
|
||||
meta: {
|
||||
icon: "ri:information-line",
|
||||
showLink: false,
|
||||
title: "menus.pureAbnormal",
|
||||
rank: 9
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/error/403",
|
||||
name: "403",
|
||||
component: () => import("@/views/error/403.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFourZeroOne")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/error/404",
|
||||
name: "404",
|
||||
component: () => import("@/views/error/404.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFourZeroFour")
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/error/500",
|
||||
name: "500",
|
||||
component: () => import("@/views/error/500.vue"),
|
||||
meta: {
|
||||
title: $t("menus.pureFive")
|
||||
}
|
||||
}
|
||||
]
|
||||
path: '/error',
|
||||
redirect: '/error/403',
|
||||
meta: {
|
||||
icon: 'ri:information-line',
|
||||
showLink: false,
|
||||
title: 'menus.pureAbnormal',
|
||||
rank: 9,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/error/403',
|
||||
name: '403',
|
||||
component: () => import('@/components/error/403.vue'),
|
||||
meta: {
|
||||
title: $t('menus.pureFourZeroOne'),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/error/404',
|
||||
name: '404',
|
||||
component: () => import('@/components/error/404.vue'),
|
||||
meta: {
|
||||
title: $t('menus.pureFourZeroFour'),
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/error/500',
|
||||
name: '500',
|
||||
component: () => import('@/components/error/500.vue'),
|
||||
meta: {
|
||||
title: $t('menus.pureFive'),
|
||||
},
|
||||
},
|
||||
],
|
||||
} satisfies RouteConfigsTable;
|
||||
|
|
|
@ -10,7 +10,7 @@ import { type menuType, routerArrays } from '@/layout/types';
|
|||
import { useMultiTagsStoreHook } from '@/store/multiTags';
|
||||
import { usePermissionStoreHook } from '@/store/permission';
|
||||
// 动态路由
|
||||
import { getRouterAsync } from '@/api/v1/routes';
|
||||
import { getRouterAsync } from '@/api/v1/system';
|
||||
// import { getAsyncRoutes } from '@/api/routes';
|
||||
|
||||
const IFrame = () => import('@/layout/frame.vue');
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { addMenu, deletedMenuByIds, updateMenu } from '@/api/v1/system';
|
||||
import { storeMessage } from '@/utils/message';
|
||||
|
||||
export const userRouterStore = defineStore('routerStore', {
|
||||
state() {
|
||||
return {};
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
/**
|
||||
* * 添加菜单
|
||||
* @param data
|
||||
*/
|
||||
async addMenu(data: object) {
|
||||
const result = await addMenu(data);
|
||||
return storeMessage(result);
|
||||
},
|
||||
|
||||
/**
|
||||
* * 更新菜单
|
||||
* @param data
|
||||
*/
|
||||
async updateMenu(data: object) {
|
||||
const result = await updateMenu(data);
|
||||
return storeMessage(result);
|
||||
},
|
||||
|
||||
/**
|
||||
* * 删除菜单
|
||||
* @param data
|
||||
*/
|
||||
async deletedMenuByIds(data: object) {
|
||||
const result = await deletedMenuByIds(data);
|
||||
return storeMessage(result);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import type { VNode } from 'vue';
|
||||
import { isFunction } from '@pureadmin/utils';
|
||||
import { ElMessage, type MessageHandler } from 'element-plus';
|
||||
import type { BaseResult } from '@/api/service/types';
|
||||
|
||||
type messageStyle = 'el' | 'antd';
|
||||
type messageTypes = 'info' | 'success' | 'warning' | 'error';
|
||||
|
@ -35,7 +36,7 @@ interface MessageParams {
|
|||
/**
|
||||
* `Message` 消息提示函数
|
||||
*/
|
||||
const message = (message: string | VNode | (() => VNode), params?: MessageParams): MessageHandler => {
|
||||
export const message = (message: string | VNode | (() => VNode), params?: MessageParams): MessageHandler => {
|
||||
if (!params) {
|
||||
return ElMessage({
|
||||
message,
|
||||
|
@ -77,6 +78,16 @@ const message = (message: string | VNode | (() => VNode), params?: MessageParams
|
|||
/**
|
||||
* 关闭所有 `Message` 消息提示函数
|
||||
*/
|
||||
const closeAllMessage = (): void => ElMessage.closeAll();
|
||||
export const closeAllMessage = (): void => ElMessage.closeAll();
|
||||
|
||||
export { message, closeAllMessage };
|
||||
/**
|
||||
* 仓库消息展示
|
||||
* @param result
|
||||
*/
|
||||
export const storeMessage = (result: BaseResult<any>) => {
|
||||
if (result.code !== 200) {
|
||||
return false;
|
||||
}
|
||||
message(result.message, { type: 'success' });
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ import { FormProps } from './utils/types';
|
|||
import { IconSelect } from '@/components/ReIcon';
|
||||
import Segmented from '@/components/ReSegmented';
|
||||
import ReAnimateSelector from '@/components/AnimateSelector';
|
||||
import { fixedTagOptions, frameLoadingOptions, hiddenTagOptions, keepAliveOptions, menuTypeOptions, showLinkOptions, showParentOptions } from '../../enums/enums';
|
||||
import { fixedTagOptions, frameLoadingOptions, hiddenTagOptions, keepAliveOptions, menuTypeOptions, showLinkOptions, showParentOptions } from '@/enums';
|
||||
import { $t } from '@/plugins/i18n';
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
|
@ -36,7 +36,7 @@ const props = withDefaults(defineProps<FormProps>(), {
|
|||
const ruleFormRef = ref();
|
||||
const newFormInline = ref(props.formInline);
|
||||
|
||||
defineExpose({ getRef: ruleFormRef.value });
|
||||
defineExpose({ menuFormRef: ruleFormRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -8,13 +8,21 @@ import { h, reactive, ref } from 'vue';
|
|||
import type { FormItemProps } from '../utils/types';
|
||||
|
||||
import { cloneDeep, deviceDetection, isAllEmpty } from '@pureadmin/utils';
|
||||
import { userRouterStore } from '@/store/modules/router';
|
||||
|
||||
const routerStore = userRouterStore();
|
||||
export const form = reactive({
|
||||
title: '',
|
||||
});
|
||||
export const formRef = ref();
|
||||
export const dataList = ref([]);
|
||||
export const loading = ref(true);
|
||||
|
||||
/**
|
||||
* 标签栏菜单类型匹配
|
||||
* @param type
|
||||
* @param text
|
||||
*/
|
||||
export const getMenuType = (type, text = false) => {
|
||||
switch (type) {
|
||||
case 0:
|
||||
|
@ -26,21 +34,29 @@ export const getMenuType = (type, text = false) => {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 表格选择
|
||||
* @param val
|
||||
*/
|
||||
export const handleSelectionChange = val => {
|
||||
console.log('handleSelectionChange', val);
|
||||
};
|
||||
|
||||
/**
|
||||
* 表单重置
|
||||
* @param formEl
|
||||
*/
|
||||
export const resetForm = async formEl => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
await onSearch();
|
||||
};
|
||||
|
||||
export async function onSearch() {
|
||||
export const onSearch = async () => {
|
||||
loading.value = true;
|
||||
|
||||
// 获取菜单数据
|
||||
const result = await getMenuList();
|
||||
const result: any = await getMenuList();
|
||||
if (result.code !== 200) message(result.message, { type: 'error' });
|
||||
|
||||
// 前端搜索菜单名称
|
||||
|
@ -52,9 +68,9 @@ export async function onSearch() {
|
|||
dataList.value = handleTree(result.data);
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
export function formatHigherMenuOptions(treeList) {
|
||||
export const formatHigherMenuOptions = (treeList: any) => {
|
||||
if (!treeList || !treeList.length) return;
|
||||
const newTreeList = [];
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
|
@ -63,7 +79,7 @@ export function formatHigherMenuOptions(treeList) {
|
|||
newTreeList.push(treeList[i]);
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
};
|
||||
|
||||
export function openDialog(title = '新增', row?: FormItemProps) {
|
||||
addDialog({
|
||||
|
@ -98,28 +114,24 @@ export function openDialog(title = '新增', row?: FormItemProps) {
|
|||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(editForm, { ref: formRef, formInline: null }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const FormRef = formRef.value.getRef();
|
||||
const FormRef = formRef.value.menuFormRef;
|
||||
const curData = options.props.formInline as FormItemProps;
|
||||
|
||||
FormRef.validate(async valid => {
|
||||
FormRef.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
console.log('curData', curData);
|
||||
delete curData.higherMenuOptions;
|
||||
|
||||
// 表单规则校验通过
|
||||
let result = false;
|
||||
if (title === '新增') {
|
||||
// 实际开发先调用新增接口,再进行下面操作
|
||||
message(`您${title}了菜单名称为${$t(curData.title)}的这条数据`, {
|
||||
type: 'success',
|
||||
});
|
||||
done(); // 关闭弹框
|
||||
await onSearch(); // 刷新表格数据
|
||||
result = await routerStore.addMenu(curData);
|
||||
} else {
|
||||
// 实际开发先调用修改接口,再进行下面操作
|
||||
message(`您${title}了菜单名称为${$t(curData.title)}的这条数据`, {
|
||||
type: 'success',
|
||||
});
|
||||
done(); // 关闭弹框
|
||||
await onSearch(); // 刷新表格数据
|
||||
curData.id = row.id;
|
||||
result = await routerStore.updateMenu(curData);
|
||||
}
|
||||
|
||||
// 刷新表格数据
|
||||
if (result) {
|
||||
done();
|
||||
await onSearch();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -127,9 +139,11 @@ export function openDialog(title = '新增', row?: FormItemProps) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* * 删除菜单
|
||||
* @param row
|
||||
*/
|
||||
export const handleDelete = async row => {
|
||||
message(`您删除了菜单名称为${$t(row.title)}的这条数据`, {
|
||||
type: 'success',
|
||||
});
|
||||
await routerStore.deletedMenuByIds([row.id]);
|
||||
await onSearch();
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
interface FormItemProps {
|
||||
/** 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)*/
|
||||
id: string;
|
||||
menuType: number;
|
||||
higherMenuOptions: Record<string, unknown>[];
|
||||
parentId: number;
|
||||
|
|
Loading…
Reference in New Issue