mirror of
https://gitee.com/yiming_chang/vue-pure-admin.git
synced 2024-12-01 18:57:39 +08:00
feat: 添加系统管理-菜单管理 (#929)
* feat: 添加系统管理-菜单管理 * chore: update * chore: add Copyright in login page * chore: 将页脚放在一屏可视区 * chore: 依赖更新 * chore: update * chore: update * chore: 更新依赖 * chore: update `husky v9` * style: 适配`el-dialog`样式的更新 * style: update `src/layout/components/search/components/SearchResult.vue` * chore: update * style: update * fix: 修复`ReDialog`中点击取消和确定按钮会触发两次关闭回调 * chore: update * chore: update src/views/system/menu/README.md * chore: update * chore: update * chore: done * chore: update
This commit is contained in:
parent
f762587fa7
commit
c314b0cd1c
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
src/views/system/menu/README.md
|
@ -29,6 +29,7 @@ menus:
|
||||
hssysManagement: System Manage
|
||||
hsUser: User Manage
|
||||
hsRole: Role Manage
|
||||
hsSystemMenu: Menu Manage
|
||||
hsDept: Dept Manage
|
||||
hseditor: Editor
|
||||
hsabnormal: Abnormal Page
|
||||
|
@ -29,6 +29,7 @@ menus:
|
||||
hssysManagement: 系统管理
|
||||
hsUser: 用户管理
|
||||
hsRole: 角色管理
|
||||
hsSystemMenu: 菜单管理
|
||||
hsDept: 部门管理
|
||||
hseditor: 编辑器
|
||||
hsabnormal: 异常页面
|
||||
|
@ -11,7 +11,7 @@ import { system, permission, frame, tabs } from "@/router/enums";
|
||||
const systemRouter = {
|
||||
path: "/system",
|
||||
meta: {
|
||||
icon: "setting",
|
||||
icon: "ri:settings-3-line",
|
||||
title: "menus.hssysManagement",
|
||||
rank: system
|
||||
},
|
||||
@ -20,7 +20,7 @@ const systemRouter = {
|
||||
path: "/system/user/index",
|
||||
name: "SystemUser",
|
||||
meta: {
|
||||
icon: "flUser",
|
||||
icon: "ri:admin-line",
|
||||
title: "menus.hsUser",
|
||||
roles: ["admin"]
|
||||
}
|
||||
@ -29,16 +29,25 @@ const systemRouter = {
|
||||
path: "/system/role/index",
|
||||
name: "SystemRole",
|
||||
meta: {
|
||||
icon: "role",
|
||||
icon: "ri:admin-fill",
|
||||
title: "menus.hsRole",
|
||||
roles: ["admin"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/system/menu/index",
|
||||
name: "SystemMenu",
|
||||
meta: {
|
||||
icon: "ep:menu",
|
||||
title: "menus.hsSystemMenu",
|
||||
roles: ["admin"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/system/dept/index",
|
||||
name: "SystemDept",
|
||||
meta: {
|
||||
icon: "dept",
|
||||
icon: "ri:git-branch-line",
|
||||
title: "menus.hsDept",
|
||||
roles: ["admin"]
|
||||
}
|
||||
@ -50,7 +59,7 @@ const permissionRouter = {
|
||||
path: "/permission",
|
||||
meta: {
|
||||
title: "menus.permission",
|
||||
icon: "lollipop",
|
||||
icon: "ep:lollipop",
|
||||
rank: permission
|
||||
},
|
||||
children: [
|
||||
@ -68,7 +77,11 @@ const permissionRouter = {
|
||||
meta: {
|
||||
title: "menus.permissionButton",
|
||||
roles: ["admin", "common"],
|
||||
auths: ["btn_add", "btn_edit", "btn_delete"]
|
||||
auths: [
|
||||
"permission:btn:add",
|
||||
"permission:btn:edit",
|
||||
"permission:btn:delete"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -77,7 +90,7 @@ const permissionRouter = {
|
||||
const frameRouter = {
|
||||
path: "/iframe",
|
||||
meta: {
|
||||
icon: "monitor",
|
||||
icon: "ep:monitor",
|
||||
title: "menus.hsExternalPage",
|
||||
rank: frame
|
||||
},
|
||||
@ -180,7 +193,7 @@ const frameRouter = {
|
||||
const tabsRouter = {
|
||||
path: "/tabs",
|
||||
meta: {
|
||||
icon: "tag",
|
||||
icon: "ri:bookmark-2-line",
|
||||
title: "menus.hstabs",
|
||||
rank: tabs
|
||||
},
|
||||
|
614
mock/system.ts
614
mock/system.ts
@ -140,6 +140,620 @@ export default defineFakeRoute([
|
||||
};
|
||||
}
|
||||
},
|
||||
// 菜单管理
|
||||
{
|
||||
url: "/menu",
|
||||
method: "post",
|
||||
response: () => {
|
||||
return {
|
||||
success: true,
|
||||
data: [
|
||||
// 外部页面
|
||||
{
|
||||
parentId: 0,
|
||||
id: 100,
|
||||
menuType: 0, // 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)
|
||||
title: "menus.hsExternalPage",
|
||||
name: "PureIframe",
|
||||
path: "/iframe",
|
||||
component: "",
|
||||
rank: 7,
|
||||
redirect: "",
|
||||
icon: "ep:monitor",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 100,
|
||||
id: 101,
|
||||
menuType: 0,
|
||||
title: "menus.hsExternalDoc",
|
||||
name: "PureIframeExternal",
|
||||
path: "/iframe/external",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 101,
|
||||
id: 102,
|
||||
menuType: 2,
|
||||
title: "menus.externalLink",
|
||||
name: "https://yiming_chang.gitee.io/pure-admin-doc",
|
||||
path: "/external",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 101,
|
||||
id: 103,
|
||||
menuType: 2,
|
||||
title: "menus.pureutilsLink",
|
||||
name: "https://pure-admin-utils.netlify.app/",
|
||||
path: "/pureutilsLink",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 100,
|
||||
id: 104,
|
||||
menuType: 1,
|
||||
title: "menus.hsEmbeddedDoc",
|
||||
name: "PureIframeEmbedded",
|
||||
path: "/iframe/embedded",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 104,
|
||||
id: 105,
|
||||
menuType: 1,
|
||||
title: "menus.hsEpDocument",
|
||||
name: "FrameEp",
|
||||
path: "/iframe/ep",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "https://element-plus.org/zh-CN/",
|
||||
frameLoading: true,
|
||||
keepAlive: true,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 104,
|
||||
id: 106,
|
||||
menuType: 1,
|
||||
title: "menus.hsTailwindcssDocument",
|
||||
name: "FrameTailwindcss",
|
||||
path: "/iframe/tailwindcss",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "https://tailwindcss.com/docs/installation",
|
||||
frameLoading: true,
|
||||
keepAlive: true,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 104,
|
||||
id: 107,
|
||||
menuType: 1,
|
||||
title: "menus.hsVueDocument",
|
||||
name: "FrameVue",
|
||||
path: "/iframe/vue3",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "https://cn.vuejs.org/",
|
||||
frameLoading: true,
|
||||
keepAlive: true,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 104,
|
||||
id: 108,
|
||||
menuType: 1,
|
||||
title: "menus.hsViteDocument",
|
||||
name: "FrameVite",
|
||||
path: "/iframe/vite",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "https://cn.vitejs.dev/",
|
||||
frameLoading: true,
|
||||
keepAlive: true,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 104,
|
||||
id: 109,
|
||||
menuType: 1,
|
||||
title: "menus.hsPiniaDocument",
|
||||
name: "FramePinia",
|
||||
path: "/iframe/pinia",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "https://pinia.vuejs.org/zh/index.html",
|
||||
frameLoading: true,
|
||||
keepAlive: true,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 104,
|
||||
id: 110,
|
||||
menuType: 1,
|
||||
title: "menus.hsRouterDocument",
|
||||
name: "FrameRouter",
|
||||
path: "/iframe/vue-router",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "https://router.vuejs.org/zh/",
|
||||
frameLoading: true,
|
||||
keepAlive: true,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
// 权限管理
|
||||
{
|
||||
parentId: 0,
|
||||
id: 200,
|
||||
menuType: 0,
|
||||
title: "menus.permission",
|
||||
name: "PurePermission",
|
||||
path: "/permission",
|
||||
component: "",
|
||||
rank: 9,
|
||||
redirect: "",
|
||||
icon: "ep:lollipop",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 200,
|
||||
id: 201,
|
||||
menuType: 0,
|
||||
title: "menus.permissionPage",
|
||||
name: "PermissionPage",
|
||||
path: "/permission/page/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 200,
|
||||
id: 202,
|
||||
menuType: 0,
|
||||
title: "menus.permissionButton",
|
||||
name: "PermissionButton",
|
||||
path: "/permission/button/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 202,
|
||||
id: 203,
|
||||
menuType: 3,
|
||||
title: "添加",
|
||||
name: "",
|
||||
path: "",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "permission:btn:add",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 202,
|
||||
id: 204,
|
||||
menuType: 3,
|
||||
title: "修改",
|
||||
name: "",
|
||||
path: "",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "permission:btn:edit",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 202,
|
||||
id: 205,
|
||||
menuType: 3,
|
||||
title: "删除",
|
||||
name: "",
|
||||
path: "",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "permission:btn:delete",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
// 系统管理
|
||||
{
|
||||
parentId: 0,
|
||||
id: 300,
|
||||
menuType: 0,
|
||||
title: "menus.hssysManagement",
|
||||
name: "PureSystem",
|
||||
path: "/system",
|
||||
component: "",
|
||||
rank: 10,
|
||||
redirect: "",
|
||||
icon: "ri:settings-3-line",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 300,
|
||||
id: 301,
|
||||
menuType: 0,
|
||||
title: "menus.hsUser",
|
||||
name: "SystemUser",
|
||||
path: "/system/user/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "ri:admin-line",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 300,
|
||||
id: 302,
|
||||
menuType: 0,
|
||||
title: "menus.hsRole",
|
||||
name: "SystemRole",
|
||||
path: "/system/role/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "ri:admin-fill",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 300,
|
||||
id: 303,
|
||||
menuType: 0,
|
||||
title: "menus.hsSystemMenu",
|
||||
name: "SystemMenu",
|
||||
path: "/system/menu/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "ep:menu",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 300,
|
||||
id: 304,
|
||||
menuType: 0,
|
||||
title: "menus.hsDept",
|
||||
name: "SystemDept",
|
||||
path: "/system/dept/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "ri:git-branch-line",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
// 标签页操作
|
||||
{
|
||||
parentId: 0,
|
||||
id: 400,
|
||||
menuType: 0,
|
||||
title: "menus.hstabs",
|
||||
name: "PureTabs",
|
||||
path: "/tabs",
|
||||
component: "",
|
||||
rank: 11,
|
||||
redirect: "",
|
||||
icon: "ri:bookmark-2-line",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 400,
|
||||
id: 401,
|
||||
menuType: 0,
|
||||
title: "menus.hstabs",
|
||||
name: "Tabs",
|
||||
path: "/tabs/index",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 400,
|
||||
id: 402,
|
||||
menuType: 0,
|
||||
title: "query传参模式",
|
||||
name: "TabQueryDetail",
|
||||
path: "/tabs/query-detail",
|
||||
component: "",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "/tabs/index",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: false,
|
||||
showParent: false
|
||||
},
|
||||
{
|
||||
parentId: 400,
|
||||
id: 403,
|
||||
menuType: 0,
|
||||
title: "params传参模式",
|
||||
name: "TabParamsDetail",
|
||||
path: "/tabs/params-detail/:id",
|
||||
component: "params-detail",
|
||||
rank: null,
|
||||
redirect: "",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "/tabs/index",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: false,
|
||||
showParent: false
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
},
|
||||
// 部门管理
|
||||
{
|
||||
url: "/dept",
|
||||
|
74
package.json
74
package.json
@ -19,7 +19,7 @@
|
||||
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
|
||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
|
||||
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
|
||||
"prepare": "husky install",
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"keywords": [
|
||||
@ -50,13 +50,13 @@
|
||||
"dependencies": {
|
||||
"@amap/amap-jsapi-loader": "^1.0.1",
|
||||
"@howdyjs/mouse-menu": "2.0.9",
|
||||
"@logicflow/core": "^1.2.18",
|
||||
"@logicflow/extension": "^1.2.19",
|
||||
"@logicflow/core": "^1.2.22",
|
||||
"@logicflow/extension": "^1.2.22",
|
||||
"@pureadmin/descriptions": "^1.2.0",
|
||||
"@pureadmin/table": "^3.0.2",
|
||||
"@pureadmin/utils": "^2.4.4",
|
||||
"@vueuse/core": "^10.7.2",
|
||||
"@vueuse/motion": "^2.0.0",
|
||||
"@vueuse/core": "^10.8.0",
|
||||
"@vueuse/motion": "^2.1.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"@zxcvbn-ts/core": "^3.0.4",
|
||||
@ -65,9 +65,9 @@
|
||||
"china-area-data": "^5.0.1",
|
||||
"cropperjs": "^1.6.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.4.3",
|
||||
"echarts": "^5.5.0",
|
||||
"el-table-infinite-scroll": "^3.0.3",
|
||||
"element-plus": "^2.5.3",
|
||||
"element-plus": "^2.5.6",
|
||||
"intro.js": "^7.2.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jsbarcode": "^3.11.6",
|
||||
@ -77,37 +77,37 @@
|
||||
"nprogress": "^0.2.0",
|
||||
"path": "^0.12.7",
|
||||
"pinia": "^2.1.7",
|
||||
"pinyin-pro": "^3.19.3",
|
||||
"pinyin-pro": "^3.19.6",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.2",
|
||||
"responsive-storage": "^2.2.0",
|
||||
"sortablejs": "^1.15.2",
|
||||
"swiper": "^11.0.5",
|
||||
"swiper": "^11.0.6",
|
||||
"typeit": "8.7.1",
|
||||
"v-contextmenu": "3.0.0",
|
||||
"v-contextmenu": "^3.2.0",
|
||||
"v3-infinite-loading": "^1.3.1",
|
||||
"version-rocket": "^1.7.1",
|
||||
"vue": "3.4.14",
|
||||
"vue-i18n": "^9.9.0",
|
||||
"vue-i18n": "^9.9.1",
|
||||
"vue-json-pretty": "^2.3.0",
|
||||
"vue-pdf-embed": "1.2.1",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-router": "^4.3.0",
|
||||
"vue-tippy": "^6.4.1",
|
||||
"vue-types": "^5.1.1",
|
||||
"vue-virtual-scroller": "2.0.0-beta.8",
|
||||
"vue-waterfall-plugin-next": "^2.3.1",
|
||||
"vue3-danmaku": "^1.6.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"wavesurfer.js": "^7.7.1",
|
||||
"xgplayer": "^3.0.11",
|
||||
"wavesurfer.js": "^7.7.3",
|
||||
"xgplayer": "^3.0.13",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^18.6.0",
|
||||
"@commitlint/config-conventional": "^18.6.0",
|
||||
"@commitlint/types": "^18.6.0",
|
||||
"@eslint/js": "^8.56.0",
|
||||
"@faker-js/faker": "^8.4.0",
|
||||
"@commitlint/cli": "^18.6.1",
|
||||
"@commitlint/config-conventional": "^18.6.2",
|
||||
"@commitlint/types": "^18.6.1",
|
||||
"@eslint/js": "^8.57.0",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@iconify-icons/ep": "^1.2.12",
|
||||
"@iconify-icons/ri": "^1.2.10",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
@ -116,44 +116,44 @@
|
||||
"@types/gradient-string": "^1.1.5",
|
||||
"@types/intro.js": "^5.1.5",
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/node": "^20.11.7",
|
||||
"@types/node": "^20.11.20",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/qs": "^6.9.11",
|
||||
"@types/sortablejs": "^1.15.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.2",
|
||||
"@typescript-eslint/parser": "^7.0.2",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"boxen": "^7.1.1",
|
||||
"cloc": "^2.11.0",
|
||||
"cssnano": "^6.0.3",
|
||||
"eslint": "^8.56.0",
|
||||
"cssnano": "^6.0.5",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-define-config": "^2.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.20.1",
|
||||
"eslint-plugin-vue": "^9.22.0",
|
||||
"gradient-string": "^2.0.2",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^15.2.0",
|
||||
"postcss": "^8.4.33",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss-html": "^1.6.0",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-import": "^16.0.1",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"sass": "^1.70.0",
|
||||
"stylelint": "^16.2.0",
|
||||
"stylelint-config-recess-order": "^4.4.0",
|
||||
"sass": "^1.71.1",
|
||||
"stylelint": "^16.2.1",
|
||||
"stylelint-config-recess-order": "^4.6.0",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-config-standard-scss": "^12.0.0",
|
||||
"stylelint-config-standard-scss": "^13.0.0",
|
||||
"stylelint-prettier": "^5.0.0",
|
||||
"svgo": "^3.2.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.12",
|
||||
"vite": "^5.1.4",
|
||||
"vite-plugin-cdn-import": "^0.3.5",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-fake-server": "^2.1.1",
|
||||
|
2008
pnpm-lock.yaml
2008
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@
|
||||
"Grey": false,
|
||||
"Weak": false,
|
||||
"HideTabs": false,
|
||||
"HideFooter": true,
|
||||
"HideFooter": false,
|
||||
"SidebarStatus": true,
|
||||
"EpThemeColor": "#409EFF",
|
||||
"ShowLogo": true,
|
||||
|
@ -43,3 +43,8 @@ export const getRoleList = (data?: object) => {
|
||||
export const getDeptList = (data?: object) => {
|
||||
return http.request<Result>("post", "/dept", { data });
|
||||
};
|
||||
|
||||
/** 获取菜单管理列表 */
|
||||
export const getMenuList = (data?: object) => {
|
||||
return http.request<Result>("post", "/menu", { data });
|
||||
};
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 6.7 KiB |
@ -1,12 +1,19 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import { animates } from "./animate";
|
||||
import { ref, computed, toRef } from "vue";
|
||||
import { cloneDeep } from "@pureadmin/utils";
|
||||
|
||||
defineOptions({
|
||||
name: "ReAnimateSelector"
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请选择动画"
|
||||
}
|
||||
});
|
||||
|
||||
const inputValue = defineModel({ type: String });
|
||||
|
||||
const searchVal = ref();
|
||||
@ -74,7 +81,7 @@ function onMouseleave() {
|
||||
<el-select
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择动画"
|
||||
:placeholder="props.placeholder"
|
||||
popper-class="pure-animate-popper"
|
||||
:model-value="inputValue"
|
||||
:filter-method="filterMethod"
|
||||
|
@ -90,7 +90,7 @@ function handleClose(
|
||||
v-model="options.visible"
|
||||
class="pure-dialog"
|
||||
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
|
||||
@close="handleClose(options, index)"
|
||||
@closed="handleClose(options, index)"
|
||||
@opened="eventsCallBack('open', options, index)"
|
||||
@openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
|
||||
@closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
|
||||
|
@ -3,7 +3,7 @@ import type { CSSProperties, VNode, Component } from "vue";
|
||||
type DoneFn = (cancel?: boolean) => void;
|
||||
type EventType = "open" | "close" | "openAutoFocus" | "closeAutoFocus";
|
||||
type ArgsType = {
|
||||
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或者空白页 */
|
||||
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||
command: "cancel" | "sure" | "close";
|
||||
};
|
||||
|
||||
@ -157,7 +157,7 @@ interface DialogOptions extends DialogProps {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或者空白页关闭页面时才会触发) */
|
||||
/** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或空白页或按下了esc键关闭页面时才会触发) */
|
||||
close?: ({
|
||||
options,
|
||||
index
|
||||
@ -165,7 +165,7 @@ interface DialogOptions extends DialogProps {
|
||||
options: DialogOptions;
|
||||
index: number;
|
||||
}) => void;
|
||||
/** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或者空白页 */
|
||||
/** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了esc键 */
|
||||
closeCallBack?: ({
|
||||
options,
|
||||
index,
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { IconJson } from "@/components/ReIcon/data";
|
||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
||||
import { ref, computed, CSSProperties, toRef, watch } from "vue";
|
||||
import { ref, computed, CSSProperties, watch } from "vue";
|
||||
import Search from "@iconify-icons/ri/search-eye-line";
|
||||
|
||||
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
|
||||
|
@ -30,7 +30,7 @@ import Table from "@iconify-icons/ri/table-line";
|
||||
import Info from "@iconify-icons/ri/file-info-line";
|
||||
import Artboard from "@iconify-icons/ri/artboard-line";
|
||||
addIcon("ubuntuFill", UbuntuFill);
|
||||
addIcon("menu", Menu);
|
||||
addIcon("ep:menu", Menu);
|
||||
addIcon("edit", Edit);
|
||||
addIcon("informationLine", InformationLine);
|
||||
addIcon("setUp", SetUp);
|
||||
@ -42,14 +42,14 @@ addIcon("listCheck", ListCheck);
|
||||
addIcon("histogram", Histogram);
|
||||
addIcon("ppt", Ppt);
|
||||
addIcon("checkboxCircleLine", CheckboxCircleLine);
|
||||
addIcon("flUser", FlUser);
|
||||
addIcon("role", Role);
|
||||
addIcon("setting", Setting);
|
||||
addIcon("dept", Dept);
|
||||
addIcon("ri:admin-line", FlUser);
|
||||
addIcon("ri:admin-fill", Role);
|
||||
addIcon("ri:settings-3-line", Setting);
|
||||
addIcon("ri:git-branch-line", Dept);
|
||||
addIcon("search", Search);
|
||||
addIcon("lollipop", Lollipop);
|
||||
addIcon("monitor", Monitor);
|
||||
addIcon("tag", Tag);
|
||||
addIcon("ep:lollipop", Lollipop);
|
||||
addIcon("ep:monitor", Monitor);
|
||||
addIcon("ri:bookmark-2-line", Tag);
|
||||
addIcon("table", Table);
|
||||
addIcon("info", Info);
|
||||
addIcon("artboard", Artboard);
|
||||
|
@ -29,6 +29,10 @@ const props = {
|
||||
columns: {
|
||||
type: Array as PropType<TableColumnList>,
|
||||
default: () => []
|
||||
},
|
||||
isExpandAll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
};
|
||||
|
||||
@ -38,10 +42,10 @@ export default defineComponent({
|
||||
emits: ["refresh"],
|
||||
setup(props, { emit, slots, attrs }) {
|
||||
const size = ref("default");
|
||||
const isExpandAll = ref(true);
|
||||
const loading = ref(false);
|
||||
const checkAll = ref(true);
|
||||
const isIndeterminate = ref(false);
|
||||
const isExpandAll = ref(props.isExpandAll);
|
||||
const filterColumns = cloneDeep(props?.columns).filter(column =>
|
||||
isBoolean(column?.hide)
|
||||
? !column.hide
|
||||
|
@ -12,7 +12,7 @@ export interface OptionsType {
|
||||
/** 图标属性、样式配置 */
|
||||
iconAttrs?: iconType;
|
||||
/** 值 */
|
||||
value?: string | number;
|
||||
value?: any;
|
||||
/** 是否禁用 */
|
||||
disabled?: boolean;
|
||||
/** `tooltip` 提示 */
|
||||
|
@ -5,14 +5,16 @@ const TITLE = getConfig("Title");
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="layout-footer">
|
||||
MIT © 2020-PRESENT
|
||||
<footer
|
||||
class="layout-footer text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]"
|
||||
>
|
||||
Copyright © 2020-present
|
||||
<a
|
||||
class="ml-1 hover:text-primary"
|
||||
class="hover:text-primary"
|
||||
href="https://github.com/pure-admin"
|
||||
target="_blank"
|
||||
>
|
||||
{{ TITLE }}
|
||||
{{ TITLE }}
|
||||
</a>
|
||||
</footer>
|
||||
</template>
|
||||
@ -24,6 +26,6 @@ const TITLE = getConfig("Title");
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0 0 8px;
|
||||
color: #c0c4cc;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
@ -4,7 +4,7 @@ export interface ListItem {
|
||||
datetime: string;
|
||||
type: string;
|
||||
description: string;
|
||||
status?: "" | "success" | "warning" | "info" | "danger";
|
||||
status?: "primary" | "success" | "warning" | "info" | "danger";
|
||||
extra?: string;
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ defineExpose({ handleScroll });
|
||||
cursor: pointer;
|
||||
border: 0.1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
transition: font-size 0.16s;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
|
@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import { computed } from "vue";
|
||||
import { useGlobal } from "@pureadmin/utils";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
|
||||
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
|
||||
|
||||
interface Props {
|
||||
@ -12,7 +13,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
isActive: false
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
const { tooltipEffect } = useNav();
|
||||
|
||||
const iconClass = computed(() => {
|
||||
@ -42,21 +42,18 @@ const toggleClick = () => {
|
||||
|
||||
<template>
|
||||
<div class="collapse-container">
|
||||
<el-tooltip
|
||||
placement="right"
|
||||
:visible="visible"
|
||||
:effect="tooltipEffect"
|
||||
:content="props.isActive ? '点击折叠' : '点击展开'"
|
||||
>
|
||||
<IconifyIconOffline
|
||||
:icon="MenuFold"
|
||||
:class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
|
||||
:style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }"
|
||||
@click="toggleClick"
|
||||
@mouseenter="visible = true"
|
||||
@mouseleave="visible = false"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<IconifyIconOffline
|
||||
v-tippy="{
|
||||
content: props.isActive ? '点击折叠' : '点击展开',
|
||||
theme: tooltipEffect,
|
||||
hideOnClick: 'toggle',
|
||||
placement: 'right'
|
||||
}"
|
||||
:icon="MenuFold"
|
||||
:class="[iconClass, themeColor === 'light' ? '' : 'text-primary']"
|
||||
:style="{ transform: props.isActive ? 'none' : 'rotateY(180deg)' }"
|
||||
@click="toggleClick"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -3,7 +3,6 @@ import path from "path";
|
||||
import { getConfig } from "@/config";
|
||||
import { menuType } from "../../types";
|
||||
import extraIcon from "./extraIcon.vue";
|
||||
import { useDark } from "@pureadmin/utils";
|
||||
import { ReText } from "@/components/ReText";
|
||||
import { useNav } from "@/layout/hooks/useNav";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
@ -16,7 +15,6 @@ import ArrowLeft from "@iconify-icons/ep/arrow-left-bold";
|
||||
import ArrowRight from "@iconify-icons/ep/arrow-right-bold";
|
||||
|
||||
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
|
||||
const { isDark } = useDark();
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
@ -143,7 +141,7 @@ function resolvePath(routePath) {
|
||||
<ReText
|
||||
:tippyProps="{
|
||||
offset: [0, -10],
|
||||
theme: !isDark ? tooltipEffect : undefined
|
||||
theme: tooltipEffect
|
||||
}"
|
||||
class="!text-inherit"
|
||||
>
|
||||
@ -181,7 +179,7 @@ function resolvePath(routePath) {
|
||||
"
|
||||
:tippyProps="{
|
||||
offset: [0, -10],
|
||||
theme: !isDark ? tooltipEffect : undefined
|
||||
theme: tooltipEffect
|
||||
}"
|
||||
:class="{
|
||||
'!text-inherit': true,
|
||||
|
@ -1,6 +1,6 @@
|
||||
// 完整版菜单比较多,将 rank 抽离出来,在此方便维护
|
||||
|
||||
const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从 1 开始哦
|
||||
const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从非 0 开始
|
||||
components = 1,
|
||||
able = 2,
|
||||
table = 3,
|
||||
|
@ -5,7 +5,7 @@ export default {
|
||||
path: "/components",
|
||||
redirect: "/components/dialog",
|
||||
meta: {
|
||||
icon: "menu",
|
||||
icon: "ep:menu",
|
||||
title: $t("menus.hscomponents"),
|
||||
rank: components
|
||||
},
|
||||
|
@ -44,15 +44,23 @@
|
||||
}
|
||||
|
||||
.pure-dialog {
|
||||
.el-dialog__header.show-close {
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
top: 16px;
|
||||
right: 12px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.pure-dialog-svg {
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
top: 20px;
|
||||
right: 14px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.el-dialog__footer {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,17 +156,14 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding-bottom: 10px;
|
||||
width: calc(100% + 32px);
|
||||
padding: 10px 20px;
|
||||
margin: auto -16px -16px;
|
||||
box-shadow:
|
||||
0 -1px 0 0 #e0e3e8,
|
||||
0 -3px 6px 0 rgb(69 98 155 / 12%);
|
||||
|
@ -1,6 +1,7 @@
|
||||
.wave {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
width: 80%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
|
@ -6,10 +6,10 @@ defineOptions({
|
||||
name: "AnimateCss"
|
||||
});
|
||||
|
||||
const icon = ref("");
|
||||
const animate = ref("");
|
||||
|
||||
watch(icon, () => {
|
||||
console.log("icon", icon.value);
|
||||
watch(animate, () => {
|
||||
console.log("animate", animate.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -29,6 +29,6 @@ watch(icon, () => {
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<ReAnimateSelector v-model="icon" class="!w-[200px]" />
|
||||
<ReAnimateSelector v-model="animate" class="!w-[200px]" />
|
||||
</el-card>
|
||||
</template>
|
||||
|
@ -203,7 +203,7 @@ function onCloseCallBackClick() {
|
||||
} else if (args?.command === "sure") {
|
||||
text = "您点击了确定按钮";
|
||||
} else {
|
||||
text = "您点击了右上角关闭按钮或者空白页";
|
||||
text = "您点击了右上角关闭按钮或空白页或按下了esc键";
|
||||
}
|
||||
message(text);
|
||||
},
|
||||
@ -301,7 +301,9 @@ function onFormOneClick() {
|
||||
} else if (args?.command === "sure") {
|
||||
message(`您点击了确定按钮,当前表单数据为 ${text}`);
|
||||
} else {
|
||||
message(`您点击了右上角关闭按钮或者空白页,当前表单数据为 ${text}`);
|
||||
message(
|
||||
`您点击了右上角关闭按钮或空白页或按下了esc键,当前表单数据为 ${text}`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -12,8 +12,8 @@ const checked2 = ref(false);
|
||||
const baseTag = ref("dark");
|
||||
const tagList = ref([
|
||||
{
|
||||
type: "",
|
||||
text: "Default"
|
||||
type: "primary",
|
||||
text: "Primary"
|
||||
},
|
||||
{
|
||||
type: "success",
|
||||
|
@ -323,6 +323,18 @@ watch(loginDay, value => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w-full flex-c absolute bottom-3 text-sm text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]"
|
||||
>
|
||||
Copyright © 2020-present
|
||||
<a
|
||||
class="hover:text-primary"
|
||||
href="https://github.com/pure-admin"
|
||||
target="_blank"
|
||||
>
|
||||
{{ title }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -15,19 +15,26 @@ defineOptions({
|
||||
<div class="card-header">组件方式判断权限</div>
|
||||
</template>
|
||||
<el-space wrap>
|
||||
<Auth value="btn_add">
|
||||
<Auth value="permission:btn:add">
|
||||
<el-button plain type="warning">
|
||||
拥有code:'btn_add' 权限可见
|
||||
拥有code:'permission:btn:add' 权限可见
|
||||
</el-button>
|
||||
</Auth>
|
||||
<Auth :value="['btn_edit']">
|
||||
<Auth :value="['permission:btn:edit']">
|
||||
<el-button plain type="primary">
|
||||
拥有code:['btn_edit'] 权限可见
|
||||
拥有code:['permission:btn:edit'] 权限可见
|
||||
</el-button>
|
||||
</Auth>
|
||||
<Auth :value="['btn_add', 'btn_edit', 'btn_delete']">
|
||||
<Auth
|
||||
:value="[
|
||||
'permission:btn:add',
|
||||
'permission:btn:edit',
|
||||
'permission:btn:delete'
|
||||
]"
|
||||
>
|
||||
<el-button plain type="danger">
|
||||
拥有code:['btn_add', 'btn_edit', 'btn_delete'] 权限可见
|
||||
拥有code:['permission:btn:add', 'permission:btn:edit',
|
||||
'permission:btn:delete'] 权限可见
|
||||
</el-button>
|
||||
</Auth>
|
||||
</el-space>
|
||||
@ -38,18 +45,25 @@ defineOptions({
|
||||
<div class="card-header">函数方式判断权限</div>
|
||||
</template>
|
||||
<el-space wrap>
|
||||
<el-button v-if="hasAuth('btn_add')" plain type="warning">
|
||||
拥有code:'btn_add' 权限可见
|
||||
<el-button v-if="hasAuth('permission:btn:add')" plain type="warning">
|
||||
拥有code:'permission:btn:add' 权限可见
|
||||
</el-button>
|
||||
<el-button v-if="hasAuth(['btn_edit'])" plain type="primary">
|
||||
拥有code:['btn_edit'] 权限可见
|
||||
<el-button v-if="hasAuth(['permission:btn:edit'])" plain type="primary">
|
||||
拥有code:['permission:btn:edit'] 权限可见
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="hasAuth(['btn_add', 'btn_edit', 'btn_delete'])"
|
||||
v-if="
|
||||
hasAuth([
|
||||
'permission:btn:add',
|
||||
'permission:btn:edit',
|
||||
'permission:btn:delete'
|
||||
])
|
||||
"
|
||||
plain
|
||||
type="danger"
|
||||
>
|
||||
拥有code:['btn_add', 'btn_edit', 'btn_delete'] 权限可见
|
||||
拥有code:['permission:btn:add', 'permission:btn:edit',
|
||||
'permission:btn:delete'] 权限可见
|
||||
</el-button>
|
||||
</el-space>
|
||||
</el-card>
|
||||
@ -61,18 +75,23 @@ defineOptions({
|
||||
</div>
|
||||
</template>
|
||||
<el-space wrap>
|
||||
<el-button v-auth="'btn_add'" plain type="warning">
|
||||
拥有code:'btn_add' 权限可见
|
||||
<el-button v-auth="'permission:btn:add'" plain type="warning">
|
||||
拥有code:'permission:btn:add' 权限可见
|
||||
</el-button>
|
||||
<el-button v-auth="['btn_edit']" plain type="primary">
|
||||
拥有code:['btn_edit'] 权限可见
|
||||
<el-button v-auth="['permission:btn:edit']" plain type="primary">
|
||||
拥有code:['permission:btn:edit'] 权限可见
|
||||
</el-button>
|
||||
<el-button
|
||||
v-auth="['btn_add', 'btn_edit', 'btn_delete']"
|
||||
v-auth="[
|
||||
'permission:btn:add',
|
||||
'permission:btn:edit',
|
||||
'permission:btn:delete'
|
||||
]"
|
||||
plain
|
||||
type="danger"
|
||||
>
|
||||
拥有code:['btn_add', 'btn_edit', 'btn_delete'] 权限可见
|
||||
拥有code:['permission:btn:add', 'permission:btn:edit',
|
||||
'permission:btn:delete'] 权限可见
|
||||
</el-button>
|
||||
</el-space>
|
||||
</el-card>
|
||||
|
@ -95,7 +95,10 @@ const tableData = [
|
||||
:columns="columns"
|
||||
>
|
||||
<template #tag="{ row }">
|
||||
<el-tag :type="row.tag === 'Home' ? '' : 'success'" disable-transitions>
|
||||
<el-tag
|
||||
:type="row.tag === 'Home' ? null : 'success'"
|
||||
disable-transitions
|
||||
>
|
||||
{{ row.tag }}
|
||||
</el-tag>
|
||||
</template>
|
||||
|
@ -89,7 +89,7 @@ const {
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 32 }"
|
||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
@ -111,20 +111,20 @@ const {
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('修改', row)"
|
||||
>
|
||||
新增
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('修改', row)"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
||||
>
|
||||
修改
|
||||
新增
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
:title="`是否确认删除部门名称为${row.name}的这条数据`"
|
||||
@ -150,6 +150,14 @@ const {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-table__inner-wrapper::before) {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin: 24px 24px 0 !important;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
|
27
src/views/system/menu/README.md
Normal file
27
src/views/system/menu/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
<!-- 初版,持续完善中 -->
|
||||
|
||||
## 字段含义
|
||||
|
||||
| 字段 | 说明 |
|
||||
| :---------------- | :----------------------------------------------------------- |
|
||||
| `menuType` | 菜单类型(`0`代表菜单、`1`代表`iframe`、`2`代表外链、`3`代表按钮) |
|
||||
| `parentId` | |
|
||||
| `title` | 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的`locales`文件夹下对应添加) |
|
||||
| `name` | 路由名称(必须唯一并且和当前路由`component`字段对应的页面里用`defineOptions`包起来的`name`保持一致) |
|
||||
| `path` | 路由路径 |
|
||||
| `component` | 组件路径(传`component`组件路径,那么`path`可以随便写,如果不传,`component`组件路径会跟`path`保持一致) |
|
||||
| `rank` | 菜单排序(平台规定只有`home`路由的`rank`才能为`0`,所以后端在返回`rank`的时候需要从非`0`开始 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E8%8F%9C%E5%8D%95%E6%8E%92%E5%BA%8F-rank)) |
|
||||
| `redirect` | 路由重定向 |
|
||||
| `icon` | 菜单图标 |
|
||||
| `extraIcon` | 右侧图标 |
|
||||
| `enterTransition` | 进场动画(页面加载动画) |
|
||||
| `leaveTransition` | 离场动画(页面加载动画) |
|
||||
| `activePath` | 菜单激活(将某个菜单激活,主要用于通过`query`或`params`传参的路由,当它们通过配置`showLink: false`后不在菜单中显示,就不会有任何菜单高亮,而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`) |
|
||||
| `auths` | 权限标识(按钮级别权限设置) |
|
||||
| `frameSrc` | 链接地址(需要内嵌的`iframe`链接地址) |
|
||||
| `frameLoading` | 加载动画(内嵌的`iframe`页面是否开启首次加载动画) |
|
||||
| `keepAlive` | 缓存页面(是否缓存该路由页面,开启后会保存该页面的整体状态,刷新后会清空状态) |
|
||||
| `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) |
|
||||
| `showLink` | 菜单(是否显示该菜单) |
|
||||
| `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF)) |
|
||||
|
326
src/views/system/menu/form.vue
Normal file
326
src/views/system/menu/form.vue
Normal file
@ -0,0 +1,326 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import ReCol from "@/components/ReCol";
|
||||
import { formRules } from "./utils/rule";
|
||||
import { FormProps } from "./utils/types";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { IconSelect } from "@/components/ReIcon";
|
||||
import Segmented from "@/components/ReSegmented";
|
||||
import ReAnimateSelector from "@/components/ReAnimateSelector";
|
||||
import {
|
||||
menuTypeOptions,
|
||||
showLinkOptions,
|
||||
keepAliveOptions,
|
||||
hiddenTagOptions,
|
||||
showParentOptions,
|
||||
frameLoadingOptions
|
||||
} from "./utils/enums";
|
||||
|
||||
const props = withDefaults(defineProps<FormProps>(), {
|
||||
formInline: () => ({
|
||||
menuType: 0,
|
||||
higherMenuOptions: [],
|
||||
parentId: 0,
|
||||
title: "",
|
||||
name: "",
|
||||
path: "",
|
||||
component: "",
|
||||
rank: 99,
|
||||
redirect: " ",
|
||||
icon: "",
|
||||
extraIcon: "",
|
||||
enterTransition: "",
|
||||
leaveTransition: "",
|
||||
activePath: "",
|
||||
auths: "",
|
||||
frameSrc: "",
|
||||
frameLoading: true,
|
||||
keepAlive: false,
|
||||
hiddenTag: false,
|
||||
showLink: true,
|
||||
showParent: false
|
||||
})
|
||||
});
|
||||
|
||||
const ruleFormRef = ref();
|
||||
const newFormInline = ref(props.formInline);
|
||||
|
||||
function getRef() {
|
||||
return ruleFormRef.value;
|
||||
}
|
||||
|
||||
defineExpose({ getRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form
|
||||
ref="ruleFormRef"
|
||||
:model="newFormInline"
|
||||
:rules="formRules"
|
||||
label-width="82px"
|
||||
>
|
||||
<el-row :gutter="30">
|
||||
<re-col>
|
||||
<el-form-item label="菜单类型">
|
||||
<Segmented
|
||||
v-model="newFormInline.menuType"
|
||||
:options="menuTypeOptions"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col>
|
||||
<el-form-item label="上级菜单">
|
||||
<el-cascader
|
||||
v-model="newFormInline.parentId"
|
||||
class="w-full"
|
||||
:options="newFormInline.higherMenuOptions"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'title',
|
||||
emitPath: false,
|
||||
checkStrictly: true
|
||||
}"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择上级菜单"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ transformI18n(data.title) }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="菜单名称" prop="title">
|
||||
<el-input
|
||||
v-model="newFormInline.title"
|
||||
clearable
|
||||
placeholder="请输入菜单名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="路由名称" prop="name">
|
||||
<el-input
|
||||
v-model="newFormInline.name"
|
||||
clearable
|
||||
placeholder="请输入路由名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="路由路径" prop="path">
|
||||
<el-input
|
||||
v-model="newFormInline.path"
|
||||
clearable
|
||||
placeholder="请输入路由路径"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 0"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="组件路径">
|
||||
<el-input
|
||||
v-model="newFormInline.component"
|
||||
clearable
|
||||
placeholder="请输入组件路径"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="菜单排序">
|
||||
<el-input-number
|
||||
v-model="newFormInline.rank"
|
||||
class="!w-full"
|
||||
:min="1"
|
||||
:max="9999"
|
||||
controls-position="right"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 0"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="路由重定向">
|
||||
<el-input
|
||||
v-model="newFormInline.redirect"
|
||||
clearable
|
||||
placeholder="请输入默认跳转地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="菜单图标">
|
||||
<IconSelect v-model="newFormInline.icon" class="w-full" />
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="右侧图标">
|
||||
<el-input
|
||||
v-model="newFormInline.extraIcon"
|
||||
clearable
|
||||
placeholder="菜单名称右侧的额外图标"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="进场动画">
|
||||
<ReAnimateSelector
|
||||
v-model="newFormInline.enterTransition"
|
||||
placeholder="请选择页面进场加载动画"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="离场动画">
|
||||
<ReAnimateSelector
|
||||
v-model="newFormInline.leaveTransition"
|
||||
placeholder="请选择页面离场加载动画"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 0"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="菜单激活">
|
||||
<el-input
|
||||
v-model="newFormInline.activePath"
|
||||
clearable
|
||||
placeholder="请输入需要激活的菜单"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-if="newFormInline.menuType === 3" :value="12" :xs="24" :sm="24">
|
||||
<!-- 按钮级别权限设置 -->
|
||||
<el-form-item label="权限标识" prop="auths">
|
||||
<el-input
|
||||
v-model="newFormInline.auths"
|
||||
clearable
|
||||
placeholder="请输入权限标识"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType === 1"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<!-- iframe -->
|
||||
<el-form-item label="链接地址">
|
||||
<el-input
|
||||
v-model="newFormInline.frameSrc"
|
||||
clearable
|
||||
placeholder="请输入 iframe 链接地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-if="newFormInline.menuType === 1" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="加载动画">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.frameLoading ? 0 : 1"
|
||||
:options="frameLoadingOptions"
|
||||
@change="
|
||||
({ option: { value } }) => {
|
||||
newFormInline.frameLoading = value;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="缓存页面">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.keepAlive ? 0 : 1"
|
||||
:options="keepAliveOptions"
|
||||
@change="
|
||||
({ option: { value } }) => {
|
||||
newFormInline.keepAlive = value;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
||||
<el-form-item label="标签页">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.hiddenTag ? 1 : 0"
|
||||
:options="hiddenTagOptions"
|
||||
@change="
|
||||
({ option: { value } }) => {
|
||||
newFormInline.hiddenTag = value;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="12"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="菜单">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.showLink ? 0 : 1"
|
||||
:options="showLinkOptions"
|
||||
@change="
|
||||
({ option: { value } }) => {
|
||||
newFormInline.showLink = value;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
<re-col
|
||||
v-show="newFormInline.menuType !== 3"
|
||||
:value="8"
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
>
|
||||
<el-form-item label="父级菜单">
|
||||
<Segmented
|
||||
:modelValue="newFormInline.showParent ? 0 : 1"
|
||||
:options="showParentOptions"
|
||||
@change="
|
||||
({ option: { value } }) => {
|
||||
newFormInline.showParent = value;
|
||||
}
|
||||
"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</template>
|
157
src/views/system/menu/index.vue
Normal file
157
src/views/system/menu/index.vue
Normal file
@ -0,0 +1,157 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { useMenu } from "./utils/hook";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
|
||||
defineOptions({
|
||||
name: "SystemMenu"
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const tableRef = ref();
|
||||
const {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
onSearch,
|
||||
resetForm,
|
||||
openDialog,
|
||||
handleDelete,
|
||||
handleSelectionChange
|
||||
} = useMenu();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:inline="true"
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
||||
>
|
||||
<el-form-item label="菜单名称:" prop="title">
|
||||
<el-input
|
||||
v-model="form.title"
|
||||
placeholder="请输入菜单名称"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon('search')"
|
||||
:loading="loading"
|
||||
@click="onSearch"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="菜单管理(初版,持续完善中)"
|
||||
:columns="columns"
|
||||
:isExpandAll="false"
|
||||
:tableRef="tableRef?.getTableRef()"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<template #buttons>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog()"
|
||||
>
|
||||
新增菜单
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
ref="tableRef"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||
align-whole="center"
|
||||
row-key="id"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-fill-color-light)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('修改', row)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
v-show="row.menuType !== 3"
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
:title="`是否确认删除菜单名称为${transformI18n(row.title)}的这条数据${row?.children?.length > 0 ? '。注意下级菜单也会一并删除,请谨慎操作' : ''}`"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</pure-table>
|
||||
</template>
|
||||
</PureTableBar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-table__inner-wrapper::before) {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin: 24px 24px 0 !important;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
94
src/views/system/menu/utils/enums.ts
Normal file
94
src/views/system/menu/utils/enums.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import type { OptionsType } from "@/components/ReSegmented";
|
||||
|
||||
const menuTypeOptions: Array<OptionsType> = [
|
||||
{
|
||||
label: "菜单",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
label: "iframe",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "外链",
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: "按钮",
|
||||
value: 3
|
||||
}
|
||||
];
|
||||
|
||||
const showLinkOptions: Array<OptionsType> = [
|
||||
{
|
||||
label: "显示",
|
||||
tip: "会在菜单中显示",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: "隐藏",
|
||||
tip: "不会在菜单中显示",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
||||
const keepAliveOptions: Array<OptionsType> = [
|
||||
{
|
||||
label: "缓存",
|
||||
tip: "会保存该页面的整体状态,刷新后会清空状态",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: "不缓存",
|
||||
tip: "不会保存该页面的整体状态",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
||||
const hiddenTagOptions: Array<OptionsType> = [
|
||||
{
|
||||
label: "允许",
|
||||
tip: "当前菜单名称或自定义信息允许添加到标签页",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
label: "禁止",
|
||||
tip: "当前菜单名称或自定义信息禁止添加到标签页",
|
||||
value: true
|
||||
}
|
||||
];
|
||||
|
||||
const showParentOptions: Array<OptionsType> = [
|
||||
{
|
||||
label: "显示",
|
||||
tip: "会显示父级菜单",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: "隐藏",
|
||||
tip: "不会显示父级菜单",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
||||
const frameLoadingOptions: Array<OptionsType> = [
|
||||
{
|
||||
label: "开启",
|
||||
tip: "有首次加载动画",
|
||||
value: true
|
||||
},
|
||||
{
|
||||
label: "关闭",
|
||||
tip: "无首次加载动画",
|
||||
value: false
|
||||
}
|
||||
];
|
||||
|
||||
export {
|
||||
menuTypeOptions,
|
||||
showLinkOptions,
|
||||
keepAliveOptions,
|
||||
hiddenTagOptions,
|
||||
showParentOptions,
|
||||
frameLoadingOptions
|
||||
};
|
223
src/views/system/menu/utils/hook.tsx
Normal file
223
src/views/system/menu/utils/hook.tsx
Normal file
@ -0,0 +1,223 @@
|
||||
import editForm from "../form.vue";
|
||||
import { handleTree } from "@/utils/tree";
|
||||
import { message } from "@/utils/message";
|
||||
import { getMenuList } from "@/api/system";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import { reactive, ref, onMounted, h } from "vue";
|
||||
import type { FormItemProps } from "../utils/types";
|
||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
export function useMenu() {
|
||||
const form = reactive({
|
||||
title: ""
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const dataList = ref([]);
|
||||
const loading = ref(true);
|
||||
|
||||
const getMenuType = (type, text = false) => {
|
||||
switch (type) {
|
||||
case 0:
|
||||
return text ? "菜单" : "primary";
|
||||
case 1:
|
||||
return text ? "iframe" : "warning";
|
||||
case 2:
|
||||
return text ? "外链" : "danger";
|
||||
case 3:
|
||||
return text ? "按钮" : "info";
|
||||
}
|
||||
};
|
||||
|
||||
const columns: TableColumnList = [
|
||||
{
|
||||
label: "菜单名称",
|
||||
prop: "title",
|
||||
align: "left",
|
||||
cellRenderer: ({ row }) => (
|
||||
<>
|
||||
<span class="inline-block mr-1">
|
||||
{h(useRenderIcon(row.icon), {
|
||||
style: { paddingTop: "1px" }
|
||||
})}
|
||||
</span>
|
||||
<span>{transformI18n(row.title)}</span>
|
||||
</>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "菜单类型",
|
||||
prop: "menuType",
|
||||
width: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag
|
||||
size={props.size}
|
||||
type={getMenuType(row.menuType)}
|
||||
effect="plain"
|
||||
>
|
||||
{getMenuType(row.menuType, true)}
|
||||
</el-tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "路由路径",
|
||||
prop: "path"
|
||||
},
|
||||
{
|
||||
label: "组件路径",
|
||||
prop: "component",
|
||||
formatter: ({ path, component }) =>
|
||||
isAllEmpty(component) ? path : component
|
||||
},
|
||||
{
|
||||
label: "权限标识",
|
||||
prop: "auths"
|
||||
},
|
||||
{
|
||||
label: "排序",
|
||||
prop: "rank",
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: "隐藏",
|
||||
prop: "showLink",
|
||||
formatter: ({ showLink }) => (showLink ? "否" : "是"),
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: "操作",
|
||||
fixed: "right",
|
||||
width: 210,
|
||||
slot: "operation"
|
||||
}
|
||||
];
|
||||
|
||||
function handleSelectionChange(val) {
|
||||
console.log("handleSelectionChange", val);
|
||||
}
|
||||
|
||||
function resetForm(formEl) {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
onSearch();
|
||||
}
|
||||
|
||||
async function onSearch() {
|
||||
loading.value = true;
|
||||
const { data } = await getMenuList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
||||
let newData = data;
|
||||
if (!isAllEmpty(form.title)) {
|
||||
// 前端搜索菜单名称
|
||||
newData = newData.filter(item =>
|
||||
transformI18n(item.title).includes(form.title)
|
||||
);
|
||||
}
|
||||
dataList.value = handleTree(newData); // 处理成树结构
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function formatHigherMenuOptions(treeList) {
|
||||
if (!treeList || !treeList.length) return;
|
||||
const newTreeList = [];
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
treeList[i].title = transformI18n(treeList[i].title);
|
||||
formatHigherMenuOptions(treeList[i].children);
|
||||
newTreeList.push(treeList[i]);
|
||||
}
|
||||
return newTreeList;
|
||||
}
|
||||
|
||||
function openDialog(title = "新增", row?: FormItemProps) {
|
||||
addDialog({
|
||||
title: `${title}菜单`,
|
||||
props: {
|
||||
formInline: {
|
||||
menuType: row?.menuType ?? 0,
|
||||
higherMenuOptions: formatHigherMenuOptions(cloneDeep(dataList.value)),
|
||||
parentId: row?.parentId ?? 0,
|
||||
title: row?.title ?? "",
|
||||
name: row?.name ?? "",
|
||||
path: row?.path ?? "",
|
||||
component: row?.component ?? "",
|
||||
rank: row?.rank ?? 99,
|
||||
redirect: row?.redirect ?? "",
|
||||
icon: row?.icon ?? "",
|
||||
extraIcon: row?.extraIcon ?? "",
|
||||
enterTransition: row?.enterTransition ?? "",
|
||||
leaveTransition: row?.leaveTransition ?? "",
|
||||
activePath: row?.activePath ?? "",
|
||||
auths: row?.auths ?? "",
|
||||
frameSrc: row?.frameSrc ?? "",
|
||||
frameLoading: row?.frameLoading ?? true,
|
||||
keepAlive: row?.keepAlive ?? false,
|
||||
hiddenTag: row?.hiddenTag ?? false,
|
||||
showLink: row?.showLink ?? true,
|
||||
showParent: row?.showParent ?? false
|
||||
}
|
||||
},
|
||||
width: "45%",
|
||||
draggable: true,
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(editForm, { ref: formRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const FormRef = formRef.value.getRef();
|
||||
const curData = options.props.formInline as FormItemProps;
|
||||
function chores() {
|
||||
message(
|
||||
`您${title}了菜单名称为${transformI18n(curData.title)}的这条数据`,
|
||||
{
|
||||
type: "success"
|
||||
}
|
||||
);
|
||||
done(); // 关闭弹框
|
||||
onSearch(); // 刷新表格数据
|
||||
}
|
||||
FormRef.validate(valid => {
|
||||
if (valid) {
|
||||
console.log("curData", curData);
|
||||
// 表单规则校验通过
|
||||
if (title === "新增") {
|
||||
// 实际开发先调用新增接口,再进行下面操作
|
||||
chores();
|
||||
} else {
|
||||
// 实际开发先调用修改接口,再进行下面操作
|
||||
chores();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(row) {
|
||||
message(`您删除了菜单名称为${transformI18n(row.title)}的这条数据`, {
|
||||
type: "success"
|
||||
});
|
||||
onSearch();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
});
|
||||
|
||||
return {
|
||||
form,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
/** 搜索 */
|
||||
onSearch,
|
||||
/** 重置 */
|
||||
resetForm,
|
||||
/** 新增、修改菜单 */
|
||||
openDialog,
|
||||
/** 删除菜单 */
|
||||
handleDelete,
|
||||
handleSelectionChange
|
||||
};
|
||||
}
|
10
src/views/system/menu/utils/rule.ts
Normal file
10
src/views/system/menu/utils/rule.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { reactive } from "vue";
|
||||
import type { FormRules } from "element-plus";
|
||||
|
||||
/** 自定义表单规则校验 */
|
||||
export const formRules = reactive(<FormRules>{
|
||||
title: [{ required: true, message: "菜单名称为必填项", trigger: "blur" }],
|
||||
name: [{ required: true, message: "路由名称为必填项", trigger: "blur" }],
|
||||
path: [{ required: true, message: "路由路径为必填项", trigger: "blur" }],
|
||||
auths: [{ required: true, message: "权限标识为必填项", trigger: "blur" }]
|
||||
});
|
29
src/views/system/menu/utils/types.ts
Normal file
29
src/views/system/menu/utils/types.ts
Normal file
@ -0,0 +1,29 @@
|
||||
interface FormItemProps {
|
||||
/** 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)*/
|
||||
menuType: number;
|
||||
higherMenuOptions: Record<string, unknown>[];
|
||||
parentId: number;
|
||||
title: string;
|
||||
name: string;
|
||||
path: string;
|
||||
component: string;
|
||||
rank: number;
|
||||
redirect: string;
|
||||
icon: string;
|
||||
extraIcon: string;
|
||||
enterTransition: string;
|
||||
leaveTransition: string;
|
||||
activePath: string;
|
||||
auths: string;
|
||||
frameSrc: string;
|
||||
frameLoading: boolean;
|
||||
keepAlive: boolean;
|
||||
hiddenTag: boolean;
|
||||
showLink: boolean;
|
||||
showParent: boolean;
|
||||
}
|
||||
interface FormProps {
|
||||
formInline: FormItemProps;
|
||||
}
|
||||
|
||||
export type { FormItemProps, FormProps };
|
@ -108,6 +108,7 @@ const {
|
||||
:loading="loading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:pagination="pagination"
|
||||
@ -206,6 +207,10 @@ const {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin: 24px 24px 0 !important;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
|
@ -150,6 +150,7 @@ const {
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
adaptive
|
||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||
align-whole="center"
|
||||
table-layout="auto"
|
||||
:loading="loading"
|
||||
@ -260,6 +261,10 @@ const {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin: 24px 24px 0 !important;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
|
@ -96,7 +96,7 @@ defineExpose({ onTreeReset });
|
||||
<div
|
||||
v-loading="props.treeLoading"
|
||||
class="h-full bg-bg_color overflow-auto"
|
||||
:style="{ minHeight: `calc(100vh - 133px)` }"
|
||||
:style="{ minHeight: `calc(100vh - 145px)` }"
|
||||
>
|
||||
<div class="flex items-center h-[34px]">
|
||||
<el-input
|
||||
|
@ -104,7 +104,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag
|
||||
size={props.size}
|
||||
type={row.sex === 1 ? "danger" : ""}
|
||||
type={row.sex === 1 ? "danger" : null}
|
||||
effect="plain"
|
||||
>
|
||||
{row.sex === 1 ? "女" : "男"}
|
||||
|
Loading…
Reference in New Issue
Block a user