feat: 菜单管理
This commit is contained in:
parent
28d0785be8
commit
c72b3a33aa
|
@ -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[]
|
|
@ -105,14 +105,6 @@ const adminList = [
|
||||||
meta: {
|
meta: {
|
||||||
title: 'UseForm'
|
title: 'UseForm'
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'ref-form',
|
|
||||||
component: 'views/Components/Form/RefForm',
|
|
||||||
name: 'RefForm',
|
|
||||||
meta: {
|
|
||||||
title: 'RefForm'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -52,8 +52,7 @@
|
||||||
"vue": "3.3.4",
|
"vue": "3.3.4",
|
||||||
"vue-i18n": "9.2.2",
|
"vue-i18n": "9.2.2",
|
||||||
"vue-router": "^4.2.4",
|
"vue-router": "^4.2.4",
|
||||||
"vue-types": "^5.1.0",
|
"vue-types": "^5.1.0"
|
||||||
"web-storage-cache": "^1.1.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17.6.7",
|
"@commitlint/cli": "^17.6.7",
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export const getMenuListApi = () => {
|
||||||
|
return request.get({ url: '/menu/list' })
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ interface UseTableConfig {
|
||||||
immediate?: boolean
|
immediate?: boolean
|
||||||
fetchDataApi: () => Promise<{
|
fetchDataApi: () => Promise<{
|
||||||
list: any[]
|
list: any[]
|
||||||
total: number
|
total?: number
|
||||||
}>
|
}>
|
||||||
fetchDelApi?: () => Promise<boolean>
|
fetchDelApi?: () => Promise<boolean>
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ export const useTable = (config: UseTableConfig) => {
|
||||||
console.log('fetchDataApi res', res)
|
console.log('fetchDataApi res', res)
|
||||||
if (res) {
|
if (res) {
|
||||||
dataList.value = res.list
|
dataList.value = res.list
|
||||||
total.value = res.total
|
total.value = res.total || 0
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('fetchDataApi error')
|
console.log('fetchDataApi error')
|
||||||
|
|
|
@ -161,7 +161,8 @@ export default {
|
||||||
sticky: 'Sticky',
|
sticky: 'Sticky',
|
||||||
treeTable: 'Tree table',
|
treeTable: 'Tree table',
|
||||||
PicturePreview: 'Table Image Preview',
|
PicturePreview: 'Table Image Preview',
|
||||||
department: 'Department management'
|
department: 'Department management',
|
||||||
|
menuManagement: 'Menu management'
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
hasPermission: 'Please set the operation permission value'
|
hasPermission: 'Please set the operation permission value'
|
||||||
|
@ -504,6 +505,24 @@ export default {
|
||||||
disable: 'Disable',
|
disable: 'Disable',
|
||||||
superiorDepartment: 'Superior department'
|
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: {
|
inputPasswordDemo: {
|
||||||
title: 'InputPassword',
|
title: 'InputPassword',
|
||||||
inputPasswordDes: 'Secondary packaging of Input components based on ElementPlus'
|
inputPasswordDes: 'Secondary packaging of Input components based on ElementPlus'
|
||||||
|
|
|
@ -161,7 +161,8 @@ export default {
|
||||||
sticky: '黏性',
|
sticky: '黏性',
|
||||||
treeTable: '树形表格',
|
treeTable: '树形表格',
|
||||||
PicturePreview: '表格图片预览',
|
PicturePreview: '表格图片预览',
|
||||||
department: '部门管理'
|
department: '部门管理',
|
||||||
|
menuManagement: '菜单管理'
|
||||||
},
|
},
|
||||||
permission: {
|
permission: {
|
||||||
hasPermission: '请设置操作权限值'
|
hasPermission: '请设置操作权限值'
|
||||||
|
@ -499,6 +500,23 @@ export default {
|
||||||
// 上级部门
|
// 上级部门
|
||||||
superiorDepartment: '上级部门'
|
superiorDepartment: '上级部门'
|
||||||
},
|
},
|
||||||
|
menu: {
|
||||||
|
menuName: '菜单名称',
|
||||||
|
icon: '图标',
|
||||||
|
permission: '权限标识',
|
||||||
|
component: '组件',
|
||||||
|
path: '路径',
|
||||||
|
status: '状态',
|
||||||
|
hidden: '是否隐藏',
|
||||||
|
alwaysShow: '是否一直显示',
|
||||||
|
noCache: '是否清除缓存',
|
||||||
|
breadcrumb: '是否显示面包屑',
|
||||||
|
affix: '是否固定在标签页',
|
||||||
|
noTagsView: '是否隐藏标签页',
|
||||||
|
activeMenu: '高亮菜单',
|
||||||
|
canTo: '是否可跳转',
|
||||||
|
name: '组件名称'
|
||||||
|
},
|
||||||
inputPasswordDemo: {
|
inputPasswordDemo: {
|
||||||
title: '密码输入框',
|
title: '密码输入框',
|
||||||
inputPasswordDes: '基于 ElementPlus 的 Input 组件二次封装'
|
inputPasswordDes: '基于 ElementPlus 的 Input 组件二次封装'
|
||||||
|
|
|
@ -517,6 +517,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
||||||
alwaysShow: true
|
alwaysShow: true
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: 'department',
|
||||||
|
component: () => import('@/views/Authorization/Department/Department.vue'),
|
||||||
|
name: 'Department',
|
||||||
|
meta: {
|
||||||
|
title: t('router.department')
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'user',
|
path: 'user',
|
||||||
component: () => import('@/views/Authorization/User/User.vue'),
|
component: () => import('@/views/Authorization/User/User.vue'),
|
||||||
|
@ -526,19 +534,19 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'role',
|
path: 'menu',
|
||||||
component: () => import('@/views/Authorization/Role.vue'),
|
component: () => import('@/views/Authorization/Menu/Menu.vue'),
|
||||||
name: 'Role',
|
name: 'Menu',
|
||||||
meta: {
|
meta: {
|
||||||
title: t('router.role')
|
title: t('router.menuManagement')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'department',
|
path: 'role',
|
||||||
component: () => import('@/views/Authorization/Department/Department.vue'),
|
component: () => import('@/views/Authorization/Role/Role.vue'),
|
||||||
name: 'Department',
|
name: 'Role',
|
||||||
meta: {
|
meta: {
|
||||||
title: t('router.department')
|
title: t('router.role')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,8 +11,8 @@ import {
|
||||||
saveDepartmentApi,
|
saveDepartmentApi,
|
||||||
deleteDepartmentApi
|
deleteDepartmentApi
|
||||||
} from '@/api/department'
|
} from '@/api/department'
|
||||||
|
import type { DepartmentItem } from '@/api/department/types'
|
||||||
import { useTable } from '@/hooks/web/useTable'
|
import { useTable } from '@/hooks/web/useTable'
|
||||||
import { TableData } from '@/api/table/types'
|
|
||||||
import { ref, unref, reactive } from 'vue'
|
import { ref, unref, reactive } from 'vue'
|
||||||
import Write from './components/Write.vue'
|
import Write from './components/Write.vue'
|
||||||
import Detail from './components/Detail.vue'
|
import Detail from './components/Detail.vue'
|
||||||
|
@ -238,7 +238,7 @@ const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const dialogTitle = ref('')
|
const dialogTitle = ref('')
|
||||||
|
|
||||||
const currentRow = ref<TableData | null>(null)
|
const currentRow = ref<DepartmentItem | null>(null)
|
||||||
const actionType = ref('')
|
const actionType = ref('')
|
||||||
|
|
||||||
const AddAction = () => {
|
const AddAction = () => {
|
||||||
|
@ -250,16 +250,18 @@ const AddAction = () => {
|
||||||
|
|
||||||
const delLoading = ref(false)
|
const delLoading = ref(false)
|
||||||
|
|
||||||
const delData = async (row: TableData | null) => {
|
const delData = async (row: DepartmentItem | null) => {
|
||||||
const elTableExpose = await getElTableExpose()
|
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
|
delLoading.value = true
|
||||||
await delList(unref(ids).length).finally(() => {
|
await delList(unref(ids).length).finally(() => {
|
||||||
delLoading.value = false
|
delLoading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const action = (row: TableData, type: string) => {
|
const action = (row: DepartmentItem, type: string) => {
|
||||||
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
|
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
|
||||||
actionType.value = type
|
actionType.value = type
|
||||||
currentRow.value = row
|
currentRow.value = row
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from 'vue'
|
import { PropType } from 'vue'
|
||||||
import type { TableData } from '@/api/table/types'
|
import { DepartmentItem } from '@/api/department/types'
|
||||||
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
|
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
currentRow: {
|
currentRow: {
|
||||||
type: Object as PropType<Nullable<TableData>>,
|
type: Object as PropType<Nullable<DepartmentItem>>,
|
||||||
default: () => null
|
default: () => null
|
||||||
},
|
},
|
||||||
detailSchema: {
|
detailSchema: {
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
import { Form, FormSchema } from '@/components/Form'
|
import { Form, FormSchema } from '@/components/Form'
|
||||||
import { useForm } from '@/hooks/web/useForm'
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
import { PropType, reactive, watch } from 'vue'
|
import { PropType, reactive, watch } from 'vue'
|
||||||
import { TableData } from '@/api/table/types'
|
|
||||||
import { useValidator } from '@/hooks/web/useValidator'
|
import { useValidator } from '@/hooks/web/useValidator'
|
||||||
|
import { DepartmentItem } from '@/api/department/types'
|
||||||
|
|
||||||
const { required } = useValidator()
|
const { required } = useValidator()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
currentRow: {
|
currentRow: {
|
||||||
type: Object as PropType<Nullable<TableData>>,
|
type: Object as PropType<Nullable<DepartmentItem>>,
|
||||||
default: () => null
|
default: () => null
|
||||||
},
|
},
|
||||||
formSchema: {
|
formSchema: {
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { reactive, ref, unref } from 'vue'
|
||||||
|
import { getMenuListApi } from '@/api/menu'
|
||||||
|
import { useTable } from '@/hooks/web/useTable'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
import { Table, TableColumn } from '@/components/Table'
|
||||||
|
import { ElButton, ElTag } from 'element-plus'
|
||||||
|
import { Icon } from '@/components/Icon'
|
||||||
|
import { Search } from '@/components/Search'
|
||||||
|
import { FormSchema } from '@/components/Form'
|
||||||
|
import { ContentWrap } from '@/components/ContentWrap'
|
||||||
|
import Write from './components/Write.vue'
|
||||||
|
import { Dialog } from '@/components/Dialog'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const { tableRegister, tableState, tableMethods } = useTable({
|
||||||
|
fetchDataApi: async () => {
|
||||||
|
const res = await getMenuListApi()
|
||||||
|
return {
|
||||||
|
list: res.data.list || []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const { dataList, loading } = tableState
|
||||||
|
const { getList } = tableMethods
|
||||||
|
|
||||||
|
const tableColumns = reactive<TableColumn[]>([
|
||||||
|
{
|
||||||
|
field: 'index',
|
||||||
|
label: t('userDemo.index'),
|
||||||
|
type: 'index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.title',
|
||||||
|
label: t('menu.menuName')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.icon',
|
||||||
|
label: t('menu.icon'),
|
||||||
|
slots: {
|
||||||
|
default: (data: any) => {
|
||||||
|
const icon = data[0].row.meta.icon
|
||||||
|
if (icon) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Icon icon={icon} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.permission',
|
||||||
|
label: t('menu.permission'),
|
||||||
|
slots: {
|
||||||
|
default: (data: any) => {
|
||||||
|
const permission = data[0].row.meta.permission
|
||||||
|
return permission ? <>{permission.join(', ')}</> : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'component',
|
||||||
|
label: t('menu.component'),
|
||||||
|
slots: {
|
||||||
|
default: (data: any) => {
|
||||||
|
const component = data[0].row.component
|
||||||
|
return <>{component === '#' ? '顶级目录' : component === '##' ? '子目录' : component}</>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'path',
|
||||||
|
label: t('menu.path')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
label: t('menu.status'),
|
||||||
|
slots: {
|
||||||
|
default: (data: any) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ElTag type={data[0].row.status === 0 ? 'danger' : 'success'}>
|
||||||
|
{data[0].row.status === 1 ? t('userDemo.enable') : t('userDemo.disable')}
|
||||||
|
</ElTag>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'action',
|
||||||
|
label: t('userDemo.action'),
|
||||||
|
width: 240,
|
||||||
|
slots: {
|
||||||
|
default: (data: any) => {
|
||||||
|
const row = data[0].row
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ElButton type="primary" onClick={() => action(row, 'edit')}>
|
||||||
|
{t('exampleDemo.edit')}
|
||||||
|
</ElButton>
|
||||||
|
<ElButton type="danger">{t('exampleDemo.del')}</ElButton>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const searchSchema = reactive<FormSchema[]>([
|
||||||
|
{
|
||||||
|
field: 'meta.title',
|
||||||
|
label: t('menu.menuName'),
|
||||||
|
component: 'Input'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const searchParams = ref({})
|
||||||
|
const setSearchParams = (data: any) => {
|
||||||
|
searchParams.value = data
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const dialogTitle = ref('')
|
||||||
|
|
||||||
|
const currentRow = ref()
|
||||||
|
const actionType = ref('')
|
||||||
|
|
||||||
|
const writeRef = ref<ComponentRef<typeof Write>>()
|
||||||
|
|
||||||
|
const saveLoading = ref(false)
|
||||||
|
|
||||||
|
const action = (row: any, type: string) => {
|
||||||
|
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
|
||||||
|
actionType.value = type
|
||||||
|
currentRow.value = row
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddAction = () => {
|
||||||
|
dialogTitle.value = t('exampleDemo.add')
|
||||||
|
currentRow.value = undefined
|
||||||
|
dialogVisible.value = true
|
||||||
|
actionType.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const save = async () => {
|
||||||
|
const write = unref(writeRef)
|
||||||
|
const formData = await write?.submit()
|
||||||
|
if (formData) {
|
||||||
|
saveLoading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
saveLoading.value = false
|
||||||
|
dialogVisible.value = false
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
|
||||||
|
<div class="mb-10px">
|
||||||
|
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
|
||||||
|
</div>
|
||||||
|
<Table
|
||||||
|
:columns="tableColumns"
|
||||||
|
default-expand-all
|
||||||
|
node-key="id"
|
||||||
|
:data="dataList"
|
||||||
|
:loading="loading"
|
||||||
|
@register="tableRegister"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<Dialog v-model="dialogVisible" :title="dialogTitle">
|
||||||
|
<Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">
|
||||||
|
{{ t('exampleDemo.save') }}
|
||||||
|
</ElButton>
|
||||||
|
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
|
@ -0,0 +1,164 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Form, FormSchema } from '@/components/Form'
|
||||||
|
import { useForm } from '@/hooks/web/useForm'
|
||||||
|
import { PropType, reactive, watch } from 'vue'
|
||||||
|
import { useValidator } from '@/hooks/web/useValidator'
|
||||||
|
import { useI18n } from '@/hooks/web/useI18n'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const { required } = useValidator()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
currentRow: {
|
||||||
|
type: Object as PropType<any>,
|
||||||
|
default: () => null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formSchema = reactive<FormSchema[]>([
|
||||||
|
{
|
||||||
|
field: 'meta.title',
|
||||||
|
label: t('menu.menuName'),
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'component',
|
||||||
|
label: t('menu.component'),
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'name',
|
||||||
|
label: t('menu.name'),
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.icon',
|
||||||
|
label: t('menu.icon'),
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'path',
|
||||||
|
label: t('menu.path'),
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
label: t('menu.status'),
|
||||||
|
component: 'Select',
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: t('userDemo.disable'),
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('userDemo.enable'),
|
||||||
|
value: 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.activeMenu',
|
||||||
|
label: t('menu.activeMenu'),
|
||||||
|
component: 'Input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.permission',
|
||||||
|
label: t('menu.permission'),
|
||||||
|
component: 'CheckboxGroup',
|
||||||
|
componentProps: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'add',
|
||||||
|
value: 'add'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'edit',
|
||||||
|
value: 'edit'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'delete',
|
||||||
|
value: 'delete'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.hidden',
|
||||||
|
label: t('menu.hidden'),
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.alwaysShow',
|
||||||
|
label: t('menu.alwaysShow'),
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.noCache',
|
||||||
|
label: t('menu.noCache'),
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.breadcrumb',
|
||||||
|
label: t('menu.breadcrumb'),
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.affix',
|
||||||
|
label: t('menu.affix'),
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'meta.noTagsView',
|
||||||
|
label: t('menu.noTagsView'),
|
||||||
|
component: 'Switch'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'canTo',
|
||||||
|
label: t('menu.canTo'),
|
||||||
|
component: 'Switch'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
component: [required()],
|
||||||
|
path: [required()],
|
||||||
|
'meta.title': [required()]
|
||||||
|
})
|
||||||
|
|
||||||
|
const { formRegister, formMethods } = useForm()
|
||||||
|
const { setValues, getFormData, getElFormExpose } = formMethods
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
const elForm = await getElFormExpose()
|
||||||
|
const valid = await elForm?.validate().catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
})
|
||||||
|
if (valid) {
|
||||||
|
const formData = getFormData()
|
||||||
|
return formData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.currentRow,
|
||||||
|
(currentRow) => {
|
||||||
|
if (!currentRow) return
|
||||||
|
setValues(currentRow)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
submit
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Form :rules="rules" @register="formRegister" :schema="formSchema" />
|
||||||
|
</template>
|
|
@ -21,6 +21,7 @@ const props = defineProps({
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
username: [required()],
|
username: [required()],
|
||||||
account: [required()],
|
account: [required()],
|
||||||
|
'department.id': [required()],
|
||||||
role: [required()],
|
role: [required()],
|
||||||
email: [required()],
|
email: [required()],
|
||||||
createTime: [required()]
|
createTime: [required()]
|
||||||
|
|
|
@ -27,9 +27,9 @@ import { defineComponent } from 'vue'
|
||||||
|
|
||||||
activeMenu: '/dashboard' 显示高亮的路由路径
|
activeMenu: '/dashboard' 显示高亮的路由路径
|
||||||
|
|
||||||
followAuth: '/dashboard' 跟随哪个路由进行权限过滤
|
|
||||||
|
|
||||||
canTo: true 设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)
|
canTo: true 设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)
|
||||||
|
|
||||||
|
permission: ['edit','add', 'delete'] 设置该路由的权限
|
||||||
}
|
}
|
||||||
**/
|
**/
|
||||||
declare module 'vue-router' {
|
declare module 'vue-router' {
|
||||||
|
@ -45,6 +45,7 @@ declare module 'vue-router' {
|
||||||
noTagsView?: boolean
|
noTagsView?: boolean
|
||||||
followAuth?: string
|
followAuth?: string
|
||||||
canTo?: boolean
|
canTo?: boolean
|
||||||
|
permission?: string[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue