From c72b3a33aab7d3605770a64d23b8a84ef4ad68d2 Mon Sep 17 00:00:00 2001 From: kailong321200875 <321200875@qq.com> Date: Sat, 5 Aug 2023 17:43:24 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=8F=9C=E5=8D=95=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mock/menu/index.ts | 246 ++++++++++++++++++ mock/role/index.ts | 8 - package.json | 3 +- src/api/menu/index.ts | 5 + src/hooks/web/useTable.ts | 4 +- src/locales/en.ts | 21 +- src/locales/zh-CN.ts | 20 +- src/router/index.ts | 24 +- .../Authorization/Department/Department.vue | 12 +- .../Department/components/Detail.vue | 4 +- .../Department/components/Write.vue | 4 +- src/views/Authorization/Menu/Menu.vue | 193 ++++++++++++++ .../Authorization/Menu/components/Write.vue | 164 ++++++++++++ src/views/Authorization/{ => Role}/Role.vue | 0 .../Authorization/User/components/Write.vue | 1 + types/router.d.ts | 5 +- 16 files changed, 681 insertions(+), 33 deletions(-) create mode 100644 mock/menu/index.ts create mode 100644 src/api/menu/index.ts create mode 100644 src/views/Authorization/Menu/Menu.vue create mode 100644 src/views/Authorization/Menu/components/Write.vue rename src/views/Authorization/{ => Role}/Role.vue (100%) diff --git a/mock/menu/index.ts b/mock/menu/index.ts new file mode 100644 index 0000000..d5958e9 --- /dev/null +++ b/mock/menu/index.ts @@ -0,0 +1,246 @@ +import config from '@/config/axios/config' +import { MockMethod } from 'vite-plugin-mock' +import Mock from 'mockjs' +import { toAnyString } from '@/utils' + +const { code } = config + +const timeout = 1000 + +export default [ + // 列表接口 + { + url: '/menu/list', + method: 'get', + timeout, + response: () => { + return { + data: { + code: code, + data: { + list: [ + { + path: '/dashboard', + component: '#', + redirect: '/dashboard/analysis', + name: 'Dashboard', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '首页', + icon: 'ant-design:dashboard-filled', + alwaysShow: true + }, + children: [ + { + path: 'analysis', + component: 'views/Dashboard/Analysis', + name: 'Analysis', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '分析页', + noCache: true + } + }, + { + path: 'workplace', + component: 'views/Dashboard/Workplace', + name: 'Workplace', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '工作台', + noCache: true + } + } + ] + }, + { + path: '/external-link', + component: '#', + meta: { + title: '文档', + icon: 'clarity:document-solid' + }, + name: 'ExternalLink', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + children: [ + { + path: 'https://element-plus-admin-doc.cn/', + name: 'DocumentLink', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '文档' + } + } + ] + }, + { + path: '/level', + component: '#', + redirect: '/level/menu1/menu1-1/menu1-1-1', + name: 'Level', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '菜单', + icon: 'carbon:skill-level-advanced' + }, + children: [ + { + path: 'menu1', + name: 'Menu1', + component: '##', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1' + }, + children: [ + { + path: 'menu1-1', + name: 'Menu11', + component: '##', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + redirect: '/level/menu1/menu1-1/menu1-1-1', + meta: { + title: '菜单1-1', + alwaysShow: true + }, + children: [ + { + path: 'menu1-1-1', + name: 'Menu111', + component: 'views/Level/Menu111', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '菜单1-1-1', + permission: ['edit', 'add'] + } + } + ] + }, + { + path: 'menu1-2', + name: 'Menu12', + component: 'views/Level/Menu12', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '菜单1-2', + permission: ['edit', 'add'] + } + } + ] + }, + { + path: 'menu2', + name: 'Menu2Demo', + component: 'views/Level/Menu2', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '菜单2', + permission: ['edit', 'add'] + } + } + ] + }, + { + path: '/example', + component: '#', + redirect: '/example/example-dialog', + name: 'Example', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '综合示例', + icon: 'ep:management', + alwaysShow: true + }, + children: [ + { + path: 'example-dialog', + component: 'views/Example/Dialog/ExampleDialog', + name: 'ExampleDialog', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '综合示例-弹窗', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-page', + component: 'views/Example/Page/ExamplePage', + name: 'ExamplePage', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '综合示例-页面', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-add', + component: 'views/Example/Page/ExampleAdd', + name: 'ExampleAdd', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '综合示例-新增', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-edit', + component: 'views/Example/Page/ExampleEdit', + name: 'ExampleEdit', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '综合示例-编辑', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + }, + { + path: 'example-detail', + component: 'views/Example/Page/ExampleDetail', + name: 'ExampleDetail', + status: Mock.Random.integer(0, 1), + id: toAnyString(), + meta: { + title: '综合示例-详情', + noTagsView: true, + noCache: true, + hidden: true, + showMainRoute: true, + activeMenu: '/example/example-page', + permission: ['edit', 'add', 'delete'] + } + } + ] + } + ] + } + } + } + } + } +] as MockMethod[] diff --git a/mock/role/index.ts b/mock/role/index.ts index e871a80..47d14ac 100644 --- a/mock/role/index.ts +++ b/mock/role/index.ts @@ -105,14 +105,6 @@ const adminList = [ meta: { title: 'UseForm' } - }, - { - path: 'ref-form', - component: 'views/Components/Form/RefForm', - name: 'RefForm', - meta: { - title: 'RefForm' - } } ] }, diff --git a/package.json b/package.json index a8e575c..843c13d 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,7 @@ "vue": "3.3.4", "vue-i18n": "9.2.2", "vue-router": "^4.2.4", - "vue-types": "^5.1.0", - "web-storage-cache": "^1.1.1" + "vue-types": "^5.1.0" }, "devDependencies": { "@commitlint/cli": "^17.6.7", diff --git a/src/api/menu/index.ts b/src/api/menu/index.ts new file mode 100644 index 0000000..a7c55a7 --- /dev/null +++ b/src/api/menu/index.ts @@ -0,0 +1,5 @@ +import request from '@/config/axios' + +export const getMenuListApi = () => { + return request.get({ url: '/menu/list' }) +} diff --git a/src/hooks/web/useTable.ts b/src/hooks/web/useTable.ts index dc53872..eaa75dd 100644 --- a/src/hooks/web/useTable.ts +++ b/src/hooks/web/useTable.ts @@ -12,7 +12,7 @@ interface UseTableConfig { immediate?: boolean fetchDataApi: () => Promise<{ list: any[] - total: number + total?: number }> fetchDelApi?: () => Promise } @@ -83,7 +83,7 @@ export const useTable = (config: UseTableConfig) => { console.log('fetchDataApi res', res) if (res) { dataList.value = res.list - total.value = res.total + total.value = res.total || 0 } } catch (err) { console.log('fetchDataApi error') diff --git a/src/locales/en.ts b/src/locales/en.ts index 00cf611..3344dc8 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -161,7 +161,8 @@ export default { sticky: 'Sticky', treeTable: 'Tree table', PicturePreview: 'Table Image Preview', - department: 'Department management' + department: 'Department management', + menuManagement: 'Menu management' }, permission: { hasPermission: 'Please set the operation permission value' @@ -504,6 +505,24 @@ export default { disable: 'Disable', superiorDepartment: 'Superior department' }, + menu: { + menuName: 'Menu name', + icon: 'Icon', + // 权限 + permission: 'Permission', + component: 'Component', + path: 'Path', + status: 'Status', + hidden: 'Hidden', + alwaysShow: 'Always show', + noCache: 'No cache', + breadcrumb: 'Breadcrumb', + affix: 'Affix', + noTagsView: 'No tags view', + activeMenu: 'Active menu', + canTo: 'Can to', + name: 'Name' + }, inputPasswordDemo: { title: 'InputPassword', inputPasswordDes: 'Secondary packaging of Input components based on ElementPlus' diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 8a86b76..80510ce 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -161,7 +161,8 @@ export default { sticky: '黏性', treeTable: '树形表格', PicturePreview: '表格图片预览', - department: '部门管理' + department: '部门管理', + menuManagement: '菜单管理' }, permission: { hasPermission: '请设置操作权限值' @@ -499,6 +500,23 @@ export default { // 上级部门 superiorDepartment: '上级部门' }, + menu: { + menuName: '菜单名称', + icon: '图标', + permission: '权限标识', + component: '组件', + path: '路径', + status: '状态', + hidden: '是否隐藏', + alwaysShow: '是否一直显示', + noCache: '是否清除缓存', + breadcrumb: '是否显示面包屑', + affix: '是否固定在标签页', + noTagsView: '是否隐藏标签页', + activeMenu: '高亮菜单', + canTo: '是否可跳转', + name: '组件名称' + }, inputPasswordDemo: { title: '密码输入框', inputPasswordDes: '基于 ElementPlus 的 Input 组件二次封装' diff --git a/src/router/index.ts b/src/router/index.ts index 9d2e796..cf338ab 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -517,6 +517,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [ alwaysShow: true }, children: [ + { + path: 'department', + component: () => import('@/views/Authorization/Department/Department.vue'), + name: 'Department', + meta: { + title: t('router.department') + } + }, { path: 'user', component: () => import('@/views/Authorization/User/User.vue'), @@ -526,19 +534,19 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [ } }, { - path: 'role', - component: () => import('@/views/Authorization/Role.vue'), - name: 'Role', + path: 'menu', + component: () => import('@/views/Authorization/Menu/Menu.vue'), + name: 'Menu', meta: { - title: t('router.role') + title: t('router.menuManagement') } }, { - path: 'department', - component: () => import('@/views/Authorization/Department/Department.vue'), - name: 'Department', + path: 'role', + component: () => import('@/views/Authorization/Role/Role.vue'), + name: 'Role', meta: { - title: t('router.department') + title: t('router.role') } } ] diff --git a/src/views/Authorization/Department/Department.vue b/src/views/Authorization/Department/Department.vue index 0b45aa5..864226a 100644 --- a/src/views/Authorization/Department/Department.vue +++ b/src/views/Authorization/Department/Department.vue @@ -11,8 +11,8 @@ import { saveDepartmentApi, deleteDepartmentApi } from '@/api/department' +import type { DepartmentItem } from '@/api/department/types' import { useTable } from '@/hooks/web/useTable' -import { TableData } from '@/api/table/types' import { ref, unref, reactive } from 'vue' import Write from './components/Write.vue' import Detail from './components/Detail.vue' @@ -238,7 +238,7 @@ const { allSchemas } = useCrudSchemas(crudSchemas) const dialogVisible = ref(false) const dialogTitle = ref('') -const currentRow = ref(null) +const currentRow = ref(null) const actionType = ref('') const AddAction = () => { @@ -250,16 +250,18 @@ const AddAction = () => { const delLoading = ref(false) -const delData = async (row: TableData | null) => { +const delData = async (row: DepartmentItem | null) => { const elTableExpose = await getElTableExpose() - ids.value = row ? [row.id] : elTableExpose?.getSelectionRows().map((v: TableData) => v.id) || [] + ids.value = row + ? [row.id] + : elTableExpose?.getSelectionRows().map((v: DepartmentItem) => v.id) || [] delLoading.value = true await delList(unref(ids).length).finally(() => { delLoading.value = false }) } -const action = (row: TableData, type: string) => { +const action = (row: DepartmentItem, type: string) => { dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail') actionType.value = type currentRow.value = row diff --git a/src/views/Authorization/Department/components/Detail.vue b/src/views/Authorization/Department/components/Detail.vue index 2496e75..8262b95 100644 --- a/src/views/Authorization/Department/components/Detail.vue +++ b/src/views/Authorization/Department/components/Detail.vue @@ -1,11 +1,11 @@ + + diff --git a/src/views/Authorization/Menu/components/Write.vue b/src/views/Authorization/Menu/components/Write.vue new file mode 100644 index 0000000..a622a95 --- /dev/null +++ b/src/views/Authorization/Menu/components/Write.vue @@ -0,0 +1,164 @@ + + + diff --git a/src/views/Authorization/Role.vue b/src/views/Authorization/Role/Role.vue similarity index 100% rename from src/views/Authorization/Role.vue rename to src/views/Authorization/Role/Role.vue diff --git a/src/views/Authorization/User/components/Write.vue b/src/views/Authorization/User/components/Write.vue index bdcbdf8..16100cc 100644 --- a/src/views/Authorization/User/components/Write.vue +++ b/src/views/Authorization/User/components/Write.vue @@ -21,6 +21,7 @@ const props = defineProps({ const rules = reactive({ username: [required()], account: [required()], + 'department.id': [required()], role: [required()], email: [required()], createTime: [required()] diff --git a/types/router.d.ts b/types/router.d.ts index 00c7443..12ca499 100644 --- a/types/router.d.ts +++ b/types/router.d.ts @@ -27,9 +27,9 @@ import { defineComponent } from 'vue' activeMenu: '/dashboard' 显示高亮的路由路径 - followAuth: '/dashboard' 跟随哪个路由进行权限过滤 - canTo: true 设置为true即使hidden为true,也依然可以进行路由跳转(默认 false) + + permission: ['edit','add', 'delete'] 设置该路由的权限 } **/ declare module 'vue-router' { @@ -45,6 +45,7 @@ declare module 'vue-router' { noTagsView?: boolean followAuth?: string canTo?: boolean + permission?: string[] } }