import { DictEnum } from '@vben/constants'; import { getPopupContainer } from '@vben/utils'; import { type FormSchemaGetter, type VxeGridProps, z } from '#/adapter'; import { getDictOptions } from '#/utils/dict'; import { renderDict, renderIcon } from '#/utils/render'; export const querySchema: FormSchemaGetter = () => [ { component: 'Input', fieldName: 'menuName', label: '菜单名称 ', }, { component: 'Select', componentProps: { getPopupContainer, options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), }, fieldName: 'status', label: '菜单状态 ', }, { component: 'Select', componentProps: { getPopupContainer, options: getDictOptions(DictEnum.SYS_SHOW_HIDE), }, fieldName: 'visible', label: '显示状态', }, ]; // 菜单类型(M目录 C菜单 F按钮) export const menuTypeOptions = [ { label: '目录', value: 'M' }, { label: '菜单', value: 'C' }, { label: '按钮', value: 'F' }, ]; export const yesNoOptions = [ { label: '是', value: '0' }, { label: '否', value: '1' }, ]; // (M目录 C菜单 F按钮) const menuTypes = { M: { value: '目录', icon: 'fxemoji:folder' }, C: { value: '菜单', icon: 'fluent-emoji-flat:open-book' }, F: { value: '按钮', icon: 'fluent-emoji:ok-button' }, }; export const columns: VxeGridProps['columns'] = [ { title: '菜单名称', field: 'menuName', treeNode: true, width: 200, }, { title: '图标', field: 'icon', width: 80, slots: { default: ({ row }) => { if (row?.icon === '#') { return ''; } return renderIcon(row.icon); }, }, }, { title: '排序', field: 'orderNum', width: 120, }, { title: '组件类型', field: 'menuType', width: 150, slots: { default: ({ row }) => { const current = menuTypes[row.menuType as 'C' | 'F' | 'M']; if (!current) { return '未知'; } return ( {renderIcon(current.icon)} {current.value} ); }, }, }, { title: '权限标识', field: 'perms', }, { title: '组件路径', field: 'component', }, { title: '状态', field: 'status', width: 100, slots: { default: ({ row }) => { return renderDict(row.status, DictEnum.SYS_NORMAL_DISABLE); }, }, }, { title: '显示', field: 'visible', width: 100, slots: { default: ({ row }) => { return renderDict(row.visible, DictEnum.SYS_SHOW_HIDE); }, }, }, { title: '创建时间', field: 'createTime', }, { field: 'action', fixed: 'right', slots: { default: 'action' }, title: '操作', width: 200, }, ]; export const drawerSchema: FormSchemaGetter = () => [ { component: 'Input', dependencies: { show: () => false, triggerFields: [''], }, fieldName: 'menuId', }, { component: 'TreeSelect', defaultValue: 0, fieldName: 'parentId', label: '上级菜单', rules: 'selectRequired', }, { component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: menuTypeOptions, optionType: 'button', }, defaultValue: 'M', dependencies: { componentProps: (_, api) => { // 切换时清空校验 // 直接抄的源码 没有清空校验的方法 Object.keys(api.errors.value).forEach((key) => { api.setFieldError(key, undefined); }); return {}; }, triggerFields: ['menuType'], }, fieldName: 'menuType', label: '菜单类型', }, { component: 'Input', dependencies: { // 类型不为按钮时显示 show: (values) => values.menuType !== 'F', triggerFields: ['menuType'], }, fieldName: 'icon', help: '选择或者从 https://icon-sets.iconify.design/ 查找名称粘贴', label: '菜单图标', }, { component: 'Input', fieldName: 'menuName', label: '菜单名称', rules: 'required', }, { component: 'InputNumber', fieldName: 'orderNum', help: '排序, 数字越小越靠前', label: '显示排序', rules: 'required', }, { component: 'Input', componentProps: (model) => { const placeholder = model.isFrame === '0' ? '填写链接地址http(s):// 使用新页面打开' : '填写`路由地址`或者`链接地址` 链接默认使用内部iframe内嵌打开'; return { placeholder, }; }, dependencies: { rules: (model) => { if (model.isFrame !== '0') { return z .string({ message: '请输入路由地址' }) .refine((val) => !val.startsWith('/'), { message: '路由地址不需要带/', }); } // 为链接 return z .string({ message: '请输入链接地址' }) .regex(/^https?:\/\//, { message: '请输入正确的链接地址' }); }, // 类型不为按钮时显示 show: (values) => values?.menuType !== 'F', triggerFields: ['isFrame', 'menuType'], }, fieldName: 'path', help: `路由地址不带/, 如: menu, user 链接为http(s)://开头 链接默认使用内部iframe打开, 可通过{是否外链}控制打开方式`, label: '路由地址', }, { component: 'Input', componentProps: (model) => { return { // 为链接时组件disabled disabled: model.isFrame === '0', }; }, defaultValue: '', dependencies: { rules: (model) => { // 非链接时为必填项 if (model.path && !/^https?:\/\//.test(model.path)) { return z .string() .min(1, { message: '非链接时必填组件路径' }) .refine((val) => !val.startsWith('/') && !val.endsWith('/'), { message: '组件路径开头/末尾不需要带/', }); } // 为链接时非必填 return z.string().optional(); }, // 类型为菜单时显示 show: (values) => values.menuType === 'C', triggerFields: ['menuType', 'path'], }, fieldName: 'component', help: '填写./src/views下的组件路径, 如system/menu/index', label: '组件路径', }, { component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: yesNoOptions, optionType: 'button', }, defaultValue: '1', dependencies: { // 类型不为按钮时显示 show: (values) => values.menuType !== 'F', triggerFields: ['menuType'], }, fieldName: 'isFrame', help: '外链为http(s)://开头 选择否时, 使用iframe从内部打开页面, 否则新窗口打开', label: '是否外链', }, { component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: getDictOptions(DictEnum.SYS_SHOW_HIDE), optionType: 'button', }, defaultValue: '0', dependencies: { // 类型不为按钮时显示 show: (values) => values.menuType !== 'F', triggerFields: ['menuType'], }, fieldName: 'visible', help: '隐藏后不会出现在菜单栏, 但仍然可以访问', label: '是否显示', }, { component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: getDictOptions(DictEnum.SYS_NORMAL_DISABLE), optionType: 'button', }, defaultValue: '0', dependencies: { // 类型不为按钮时显示 show: (values) => values.menuType !== 'F', triggerFields: ['menuType'], }, fieldName: 'status', help: '停用后不会出现在菜单栏, 也无法访问', label: '菜单状态', }, { component: 'Input', dependencies: { // 类型为菜单/按钮时显示 show: (values) => values.menuType !== 'M', triggerFields: ['menuType'], }, fieldName: 'perms', help: `控制器中定义的权限字符, 如: @SaCheckPermission("system:user:import")`, label: '权限标识', }, { component: 'Input', componentProps: (model) => ({ // 为链接时组件disabled disabled: model.isFrame === '0', }), dependencies: { // 类型为菜单时显示 show: (values) => values.menuType === 'C', triggerFields: ['menuType'], }, fieldName: 'queryParam', help: 'vue-router中的query属性, 如{"name": "xxx", "age": 16}', label: '路由参数', }, { component: 'RadioGroup', componentProps: { buttonStyle: 'solid', options: yesNoOptions, optionType: 'button', }, defaultValue: '0', dependencies: { // 类型为菜单时显示 show: (values) => values.menuType === 'C', triggerFields: ['menuType'], }, fieldName: 'isCache', help: '路由的keepAlive属性', label: '是否缓存', }, ];