diff --git a/src/api/types/menu.d.ts b/src/api/types/menu.d.ts index aac6430..f91aba6 100644 --- a/src/api/types/menu.d.ts +++ b/src/api/types/menu.d.ts @@ -6,12 +6,11 @@ export interface MenuFormParams { path: string; // 前端路由 api: string; // 后端接口路由 icon: string; // 菜单图标 - parentId: number | string; // 上级菜单ID(number类型) - sort: number; // 排序(number类型) - type: number; // 类型:1菜单2按钮(number类型) - status?: number; // 状态:1启用2禁用(可选,编辑时用) - id?: number; // 菜单ID(仅编辑/删除时传) parent_id?: string | null; // 上级菜单ID(number类型) + sort: number | null; // 排序(number类型) + type: number | null; // 类型:1菜单2按钮(number类型) + status?: number | null; // 状态:1启用2禁用(可选,编辑时用) + id?: number | null; // 菜单ID(仅编辑/删除时传) } /** 删除菜单参数类型 */ diff --git a/src/views/system/menuManagement/index.vue b/src/views/system/menuManagement/index.vue index 1925a57..cb12b06 100644 --- a/src/views/system/menuManagement/index.vue +++ b/src/views/system/menuManagement/index.vue @@ -33,8 +33,8 @@ - + @@ -50,7 +50,7 @@ import MenuForm from '@/components/common/Form.vue' import { MenuTableColumns, MenuiconOptions, menuiconMap, MenuFormItemOptions } from './options' import { rules } from './rules' -import { getMenuApi,createMenuApi,editMenuApi,deleteMenuApi } from '@/api/modules/menu' +import { getMenuApi, createMenuApi, editMenuApi, deleteMenuApi } from '@/api/modules/menu' // 定义header配置 const menuTitle = ref('菜单管理') const menuHeaderButtons = [ @@ -69,21 +69,20 @@ const dialogType = ref('add') // 定义菜单表单数据 const menuForm = ref({ name: '', // 菜单名称 - nameValue: '', // 默认一级菜单 + parentName: '', // 上级菜单名称 path: '', // 路由路径 api: '', // 后端路由 icon: '', // 图标名称 sort: null, // 排序(默认1) - type: null // 默认是菜单类型 + type: null, // 默认是菜单类型 + status: null, // 默认是启用状态 }) const menuFormRef = ref() // 表单验证规则 const menuFormRules = rules(menuForm.value, MenuiconOptions) -// 菜单表单选项映射 +// icon和菜单表单选项映射 const menuFormOptionsMap = ref({ - 'parentName': [ - { label: '一级菜单', value: '1' }, - ], + 'parentName': [] as any[], 'icon': MenuiconOptions, 'type': [ { label: '菜单', value: 1 }, @@ -113,7 +112,7 @@ const filterMenus = (menuList: any[]) => { return enabledMenus } // 获取菜单列表 -onMounted(async () => { +const fetchMenuList = async () => { try { const data = await getMenuApi() menuTree.value = filterMenus(data) @@ -123,12 +122,17 @@ onMounted(async () => { value: item.id ?? '' })) menuFormOptionsMap.value.parentName = [ - { label: '一级菜单', value: '1' }, + { label: '一级菜单', value: "0" }, ...parentNameOptions ] } catch (err) { - console.error('获取菜单列表失败') + console.error('获取菜单列表失败', err) + ElMessage.error('获取菜单列表失败,请稍后重试') } +} +// 页面加载时获取菜单列表 +onMounted(async () => { + await fetchMenuList() }) /** @@ -138,16 +142,23 @@ const handleEditMenu = (row: any) => { dialogVisible.value = true dialogTitle.value = '编辑菜单' dialogType.value = 'edit' - menuForm.value = { ...row, - parentName: row.name - } - console.log('编辑菜单', row) + menuForm.value = { + ...row, + id: row.id, // 菜单id + parentName: row.name, + status: row.status ?? 1, // 默认是启用状态 + } } -const handleDeleteMenu = (row: any) => { - console.log('删除菜单', row) +const handleDeleteMenu = async (row: any) => { + try { + await deleteMenuApi({id: row.id}) + await fetchMenuList() + } catch (err) { + console.error('删除菜单失败') + } } const handleToggleStatus = (row: any) => { - console.log('切换菜单状态', row) + row.status = row.status === 1 ? 2 : 1 } /** @@ -159,16 +170,21 @@ const handleSubmit = async () => { await menuFormRef.value?.validate() if (dialogType.value === 'add') { const params = { - ...menuForm.value, - parent_id: '0' + name: menuForm.value.name, // 菜单名称 + parent_id: menuForm.value.parentName, // 上级菜单id + path: menuForm.value.path, // 路由路径 + api: menuForm.value.api, // 后端路由 + icon: menuForm.value.icon, // 图标名称 + sort: menuForm.value.sort ?? 1, // 排序(默认1) + type: menuForm.value.type ?? 1, // 默认是菜单类型 } await createMenuApi(params) + await fetchMenuList() } else { await editMenuApi(menuForm.value) + await fetchMenuList() } dialogVisible.value = false - // 刷新菜单列表 - // onMounted() } catch (err) { console.log('表单验证失败') return diff --git a/src/views/system/menuManagement/options.ts b/src/views/system/menuManagement/options.ts index 0263e9a..9f4ab94 100644 --- a/src/views/system/menuManagement/options.ts +++ b/src/views/system/menuManagement/options.ts @@ -42,6 +42,6 @@ export const MenuFormItemOptions = [ { prop: 'path', label: '路由路径', type: 'input' as const, placeholder: '请输入路由路径' }, { prop: 'api', label: '后端路由', type: 'input' as const, placeholder: '请输入后端路由' }, { prop: 'icon', label: '图标名称', type: 'select' as const, placeholder: '请选择图标名称' }, - { prop: 'sort', label: '排序', type: 'number' as const, placeholder: '请输入排序' }, + { prop: 'sort', label: '排序', type: 'number' as const, placeholder: '请输入排序,数字越小越靠前' }, { prop: 'type', label: '菜单类型', type: 'radio' as const, placeholder: '请选择菜单类型' } ] diff --git a/src/views/system/menuManagement/rules.ts b/src/views/system/menuManagement/rules.ts index 6b03c27..ab2e2da 100644 --- a/src/views/system/menuManagement/rules.ts +++ b/src/views/system/menuManagement/rules.ts @@ -8,38 +8,20 @@ export const rules = (menuForm: any, iconOptions: any[]): FormRules => { { min: 2, max: 20, message: '菜单名称长度在 2 到 20 个字符之间', trigger: 'blur' } ], // 2. 父级菜单:必填(默认0,无需手动填) - parentId: [{ required: true, message: '请选择父级菜单', trigger: 'change' } + parentName: [{ required: true, message: '请选择父级菜单', trigger: 'change' } ], // 3. 路由路径:必填 + 格式校验(以/开头,仅包含字母/数字/下划线/斜杠) path: [ { required: true, message: '请输入路由路径', trigger: 'blur' }, { pattern: /^\/[a-zA-Z0-9_\/]+$/, message: '路由路径必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' } ], - // 4. 后端路由:可选,但填了就必须符合格式 + // 4. 后端路由:必填 + 格式校验(以/开头,仅包含字母/数字/下划线/斜杠) api: [ - { required: true, pattern: /(^$)|(^\/[a-zA-Z0-9_\/]+$)/, message: '后端路由若填写,必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' } + { required: true, pattern: /^\/[a-zA-Z0-9_\/]+$/, message: '后端路由必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' } ], // 5. 图标名称:可选,但填了就必须是指定范围(匹配你提供的图标列表) icon: [ - { - validator: (rule, value, callback) => { - // 一级菜单必须选图标 - if (menuForm.parentId === "0" && !value) { - callback(new Error("一级菜单必须选择图标")) - } - // 若填写了图标,必须是指定范围内的 - else if ( - value && - !iconOptions.some(item => item.value === value) - ) { - callback(new Error("请选择预设图标")) - } - else { - callback() - } - }, - trigger: "change", - }, + {required: true, message: '请选择图标名称', trigger: 'blur'} ], // 6. 排序:必填 + 数字 + 最小值0 sort: [