menu
This commit is contained in:
parent
a2afd0a787
commit
7e8d963095
|
|
@ -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(仅编辑/删除时传)
|
||||
}
|
||||
|
||||
/** 删除菜单参数类型 */
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@
|
|||
</MenuTable>
|
||||
</div>
|
||||
<!-- 菜单新增/编辑弹窗 -->
|
||||
<MenuDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit" @cancel="handleCancel"
|
||||
@close="handleClose">
|
||||
<MenuDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" @confirm="handleSubmit"
|
||||
@cancel="handleCancel" @close="handleClose">
|
||||
<MenuForm :form-data="menuForm" :form-rules="menuFormRules" :form-items="MenuFormItemOptions"
|
||||
:options-map="menuFormOptionsMap" ref="menuFormRef" />
|
||||
</MenuDialog>
|
||||
|
|
@ -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<FormInstance>()
|
||||
// 表单验证规则
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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: '请选择菜单类型' }
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
Loading…
Reference in New Issue