|
|
@@ -4,11 +4,20 @@ import type {
|
|
|
VxeTableGridOptions,
|
|
|
} from '#/adapter';
|
|
|
|
|
|
-import { h } from 'vue';
|
|
|
-
|
|
|
+import { z } from '#/adapter';
|
|
|
import { EnumApi, MenuApi } from '#/api';
|
|
|
import { boolOptions } from '#/api/model';
|
|
|
-import { Icon } from '#/components/icon';
|
|
|
+import { componentKeys } from '#/router/routes';
|
|
|
+
|
|
|
+export function getTypeOptions() {
|
|
|
+ return [
|
|
|
+ { color: 'processing', label: '目录', value: 0 },
|
|
|
+ { color: 'default', label: '菜单', value: 1 },
|
|
|
+ { color: 'error', label: '按钮', value: 2 },
|
|
|
+ { color: 'success', label: '内嵌', value: 3 },
|
|
|
+ { color: 'warning', label: '外连', value: 4 },
|
|
|
+ ];
|
|
|
+}
|
|
|
|
|
|
export const useSearchSchema = (): VbenFormSchema[] => {
|
|
|
return [
|
|
|
@@ -39,49 +48,46 @@ export function useColumns(
|
|
|
{
|
|
|
align: 'left',
|
|
|
field: 'meta.title',
|
|
|
- title: '菜单名称',
|
|
|
- width: 200,
|
|
|
+ title: '标题',
|
|
|
+ width: 250,
|
|
|
treeNode: true,
|
|
|
- showOverflow: true,
|
|
|
- slots: {
|
|
|
- default: ({ row }: any) => {
|
|
|
- return row.meta.icon
|
|
|
- ? h(
|
|
|
- 'span',
|
|
|
- {
|
|
|
- style: {
|
|
|
- display: 'flex',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- },
|
|
|
- [
|
|
|
- h(Icon, {
|
|
|
- icon: row.meta.icon,
|
|
|
- }),
|
|
|
- h(
|
|
|
- 'span',
|
|
|
- {
|
|
|
- style: {
|
|
|
- paddingLeft: '6px',
|
|
|
- },
|
|
|
- },
|
|
|
- row.meta.title,
|
|
|
- ),
|
|
|
- ],
|
|
|
- )
|
|
|
- : h('span', {}, row.meta.title);
|
|
|
- },
|
|
|
- },
|
|
|
+ slots: { default: 'title' },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ align: 'center',
|
|
|
+ cellRender: { name: 'CellTag', options: getTypeOptions() },
|
|
|
+ field: 'type',
|
|
|
+ title: '类型',
|
|
|
+ width: 100,
|
|
|
},
|
|
|
- { align: 'left', field: 'path', title: '路由地址', width: 180 },
|
|
|
- { align: 'left', field: 'component', title: '组件' },
|
|
|
{
|
|
|
align: 'left',
|
|
|
field: 'permission',
|
|
|
title: '权限标识',
|
|
|
- width: 120,
|
|
|
+ width: 200,
|
|
|
showOverflow: true,
|
|
|
},
|
|
|
+ { align: 'left', field: 'path', title: '路由地址', width: 200 },
|
|
|
+ {
|
|
|
+ align: 'left',
|
|
|
+ field: 'component',
|
|
|
+ title: '页面组件',
|
|
|
+ formatter: ({ row }) => {
|
|
|
+ switch (row.type) {
|
|
|
+ case 0:
|
|
|
+ case 1: {
|
|
|
+ return row.component ?? '';
|
|
|
+ }
|
|
|
+ case 2: {
|
|
|
+ return row.meta?.iframeSrc ?? '';
|
|
|
+ }
|
|
|
+ case 3: {
|
|
|
+ return row.meta?.link ?? '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return '';
|
|
|
+ },
|
|
|
+ },
|
|
|
{
|
|
|
field: 'status',
|
|
|
title: '状态',
|
|
|
@@ -101,7 +107,7 @@ export function useColumns(
|
|
|
options: [
|
|
|
{
|
|
|
code: 'append',
|
|
|
- label: '添加项',
|
|
|
+ label: '添加下级',
|
|
|
auth: ['menu:add'],
|
|
|
},
|
|
|
{
|
|
|
@@ -119,7 +125,7 @@ export function useColumns(
|
|
|
headerAlign: 'center',
|
|
|
showOverflow: false,
|
|
|
title: '操作',
|
|
|
- width: 100,
|
|
|
+ width: 162,
|
|
|
},
|
|
|
];
|
|
|
}
|
|
|
@@ -139,22 +145,30 @@ export const useSchema = (): VbenFormSchema[] => {
|
|
|
fieldName: 'type',
|
|
|
label: '菜单类型',
|
|
|
rules: 'required',
|
|
|
+ formItemClass: 'col-span-2 md:col-span-2',
|
|
|
},
|
|
|
{
|
|
|
component: 'Input',
|
|
|
componentProps: {
|
|
|
- placeholder: '请输入路由名称',
|
|
|
+ placeholder: '请输入菜单名称',
|
|
|
},
|
|
|
fieldName: 'name',
|
|
|
- label: '路由名称',
|
|
|
+ label: '菜单名称',
|
|
|
help: 'route.name',
|
|
|
- rules: 'required',
|
|
|
- dependencies: {
|
|
|
- show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
- },
|
|
|
- triggerFields: ['type'],
|
|
|
+ rules: z
|
|
|
+ .string()
|
|
|
+ .min(2, '菜单名称至少2个字符')
|
|
|
+ .max(30, '菜单名称最多30个字符'),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Input',
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入标题',
|
|
|
},
|
|
|
+ fieldName: 'meta.title',
|
|
|
+ help: 'meta.title',
|
|
|
+ label: '标题',
|
|
|
+ rules: 'required',
|
|
|
},
|
|
|
{
|
|
|
component: 'Input',
|
|
|
@@ -168,165 +182,111 @@ export const useSchema = (): VbenFormSchema[] => {
|
|
|
dependencies: {
|
|
|
triggerFields: ['type'],
|
|
|
show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- {
|
|
|
- component: 'Input',
|
|
|
- componentProps: {
|
|
|
- placeholder: '请输入组件路径',
|
|
|
- },
|
|
|
- dependencies: {
|
|
|
- show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
+ return [0, 1, 3].includes(values.type);
|
|
|
},
|
|
|
- triggerFields: ['type'],
|
|
|
},
|
|
|
- fieldName: 'component',
|
|
|
- label: '组件路径',
|
|
|
- help: 'route.component',
|
|
|
- rules: 'required',
|
|
|
},
|
|
|
{
|
|
|
component: 'Input',
|
|
|
componentProps: {
|
|
|
- placeholder: '请输入重定向',
|
|
|
- },
|
|
|
+ placeholder: '请输入激活路径',
|
|
|
+ },
|
|
|
+ fieldName: 'activePath',
|
|
|
+ label: '激活路径',
|
|
|
+ help: 'route.activePath',
|
|
|
+ rules: z
|
|
|
+ .string()
|
|
|
+ .min(2, '激活路径至少2个字符')
|
|
|
+ .max(30, '激活路径最多100个字符')
|
|
|
+ .refine((value: string) => {
|
|
|
+ return value.startsWith('/');
|
|
|
+ }, '激活路径必须以/开头')
|
|
|
+ .optional(),
|
|
|
dependencies: {
|
|
|
triggerFields: ['type'],
|
|
|
show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
+ return [1, 3].includes(values.type);
|
|
|
},
|
|
|
},
|
|
|
- fieldName: 'redirect',
|
|
|
- label: '重定向',
|
|
|
- help: 'route.redirect',
|
|
|
- },
|
|
|
- {
|
|
|
- component: 'Input',
|
|
|
- componentProps: {
|
|
|
- placeholder: '请输入菜单名称',
|
|
|
- },
|
|
|
- fieldName: 'meta.title',
|
|
|
- help: 'meta.title',
|
|
|
- label: '菜单名称',
|
|
|
- rules: 'required',
|
|
|
},
|
|
|
{
|
|
|
component: 'IconPicker',
|
|
|
componentProps: {
|
|
|
placeholder: '请输入图标',
|
|
|
+ prefix: 'carbon',
|
|
|
},
|
|
|
fieldName: 'meta.icon',
|
|
|
label: '图标',
|
|
|
help: 'meta.icon',
|
|
|
- rules: 'required',
|
|
|
dependencies: {
|
|
|
show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
+ return [0, 1, 3, 4].includes(values.type);
|
|
|
},
|
|
|
triggerFields: ['type'],
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
- component: 'RadioGroup',
|
|
|
- defaultValue: true,
|
|
|
+ component: 'IconPicker',
|
|
|
componentProps: {
|
|
|
- placeholder: '请输入',
|
|
|
- options: boolOptions,
|
|
|
- optionType: 'button',
|
|
|
- buttonStyle: 'solid',
|
|
|
+ prefix: 'carbon',
|
|
|
},
|
|
|
dependencies: {
|
|
|
- triggerFields: ['type'],
|
|
|
- show(values) {
|
|
|
- return [1].includes(values.type);
|
|
|
+ show: (values) => {
|
|
|
+ return [0, 1, 3].includes(values.type);
|
|
|
},
|
|
|
- },
|
|
|
- fieldName: 'meta.keepAlive',
|
|
|
- label: '缓存',
|
|
|
- help: 'meta.keepAlive',
|
|
|
- },
|
|
|
- {
|
|
|
- component: 'RadioGroup',
|
|
|
- defaultValue: false,
|
|
|
- componentProps: {
|
|
|
- placeholder: '请输入',
|
|
|
- options: boolOptions,
|
|
|
- optionType: 'button',
|
|
|
- buttonStyle: 'solid',
|
|
|
- },
|
|
|
- dependencies: {
|
|
|
triggerFields: ['type'],
|
|
|
- show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
- },
|
|
|
},
|
|
|
- fieldName: 'meta.hideInTab',
|
|
|
- label: '隐藏',
|
|
|
- help: 'meta.keepAlive',
|
|
|
+ fieldName: 'meta.activeIcon',
|
|
|
+ label: '激活图标',
|
|
|
},
|
|
|
{
|
|
|
- component: 'ApiSelect',
|
|
|
- defaultValue: 0,
|
|
|
+ component: 'AutoComplete',
|
|
|
componentProps: {
|
|
|
- placeholder: '请输入',
|
|
|
- api: {
|
|
|
- type: 'enum',
|
|
|
- params: EnumApi.EnumType.PathType,
|
|
|
+ allowClear: true,
|
|
|
+ class: 'w-full',
|
|
|
+ filterOption(input: string, option: { value: string }) {
|
|
|
+ return option.value.toLowerCase().includes(input.toLowerCase());
|
|
|
},
|
|
|
+ options: componentKeys.map((v: any) => ({ value: v })),
|
|
|
},
|
|
|
dependencies: {
|
|
|
- triggerFields: ['type'],
|
|
|
+ rules: (values) => {
|
|
|
+ return values.type === 0 ? 'required' : null;
|
|
|
+ },
|
|
|
show(values) {
|
|
|
- return [0, 1].includes(values.type);
|
|
|
+ return [1].includes(values.type);
|
|
|
},
|
|
|
+ triggerFields: ['type'],
|
|
|
},
|
|
|
- fieldName: 'meta.pathType',
|
|
|
- label: '路由类型',
|
|
|
+ fieldName: 'component',
|
|
|
+ label: '组件路径',
|
|
|
+ help: 'route.component',
|
|
|
rules: 'required',
|
|
|
},
|
|
|
{
|
|
|
component: 'Input',
|
|
|
- componentProps: {
|
|
|
- placeholder: '请输入内嵌页面',
|
|
|
- },
|
|
|
dependencies: {
|
|
|
- triggerFields: ['pathType'],
|
|
|
- show(values) {
|
|
|
- return [1].includes(values.pathType);
|
|
|
+ show: (values) => {
|
|
|
+ return [3].includes(values.type);
|
|
|
},
|
|
|
+ triggerFields: ['type'],
|
|
|
},
|
|
|
- fieldName: 'meta.iframeSrc',
|
|
|
- help: 'meta.iframeSrc',
|
|
|
- label: '内嵌页面',
|
|
|
- rules: 'required',
|
|
|
+ fieldName: 'iframeSrc',
|
|
|
+ label: '内嵌地址',
|
|
|
+ rules: z.string().url('请输入有效的连接'),
|
|
|
},
|
|
|
{
|
|
|
component: 'Input',
|
|
|
- componentProps: {
|
|
|
- placeholder: '请输入外链地址',
|
|
|
- },
|
|
|
dependencies: {
|
|
|
- triggerFields: ['pathType'],
|
|
|
- show(values) {
|
|
|
- return [2].includes(values.pathType);
|
|
|
+ show: (values) => {
|
|
|
+ return [4].includes(values.type);
|
|
|
},
|
|
|
+ triggerFields: ['type'],
|
|
|
},
|
|
|
- fieldName: 'meta.link',
|
|
|
+ fieldName: 'link',
|
|
|
+ label: '外连地址',
|
|
|
help: 'meta.link',
|
|
|
- label: '外链地址',
|
|
|
- rules: 'required',
|
|
|
- },
|
|
|
- {
|
|
|
- component: 'InputNumber',
|
|
|
- componentProps: {
|
|
|
- placeholder: '请输入',
|
|
|
- },
|
|
|
- fieldName: 'sort',
|
|
|
- label: '排序',
|
|
|
- rules: 'required',
|
|
|
+ rules: z.string().url('请输入有效的连接'),
|
|
|
},
|
|
|
{
|
|
|
component: 'Input',
|
|
|
@@ -356,6 +316,138 @@ export const useSchema = (): VbenFormSchema[] => {
|
|
|
fieldName: 'status',
|
|
|
label: '状态',
|
|
|
},
|
|
|
+ {
|
|
|
+ component: 'InputNumber',
|
|
|
+ componentProps: {
|
|
|
+ placeholder: '请输入',
|
|
|
+ },
|
|
|
+ fieldName: 'sort',
|
|
|
+ label: '排序',
|
|
|
+ rules: 'required',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Divider',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return ![2, 4].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'divider1',
|
|
|
+ formItemClass: 'col-span-2 md:col-span-2 pb-0',
|
|
|
+ hideLabel: true,
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '其它设置',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Checkbox',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return [1].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'meta.keepAlive',
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '缓存标签页',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Checkbox',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return [1, 3].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'meta.affixTab',
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '固定标签页',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Checkbox',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return ![2].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'meta.hideInMenu',
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '隐藏菜单',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Checkbox',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return [0, 1].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'meta.hideChildrenInMenu',
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '隐藏子菜单',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Checkbox',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return ![2, 4].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'meta.hideInBreadcrumb',
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '在面包屑中隐藏',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ component: 'Checkbox',
|
|
|
+ dependencies: {
|
|
|
+ show: (values) => {
|
|
|
+ return ![2, 4].includes(values.type);
|
|
|
+ },
|
|
|
+ triggerFields: ['type'],
|
|
|
+ },
|
|
|
+ fieldName: 'meta.hideInTab',
|
|
|
+ renderComponentContent() {
|
|
|
+ return {
|
|
|
+ default: () => '在标签栏中隐藏',
|
|
|
+ };
|
|
|
+ },
|
|
|
+ },
|
|
|
+
|
|
|
+ // {
|
|
|
+ // component: 'Input',
|
|
|
+ // componentProps: {
|
|
|
+ // placeholder: '请输入重定向',
|
|
|
+ // },
|
|
|
+ // dependencies: {
|
|
|
+ // triggerFields: ['type'],
|
|
|
+ // show(values) {
|
|
|
+ // return [0, 1].includes(values.type);
|
|
|
+ // },
|
|
|
+ // },
|
|
|
+ // fieldName: 'redirect',
|
|
|
+ // label: '重定向',
|
|
|
+ // help: 'route.redirect',
|
|
|
+ // },
|
|
|
];
|
|
|
};
|
|
|
|