From bd8fef9a04a85eedf71f463f7063e0a0a1bdf1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=BF=E5=B0=8F=E5=A4=A9?= <1638245306@qq.com> Date: Fri, 27 Oct 2023 16:59:25 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=AE=8C=E6=88=90=E6=96=B0=E7=89=88=E8=8F=9C?= =?UTF-8?q?=E5=8D=95=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/application/settings.py | 4 +- .../dvadmin/system/fixtures/initSerializer.py | 2 +- .../dvadmin/system/fixtures/init_menu.json | 34 +- backend/dvadmin/system/models.py | 19 +- backend/dvadmin/system/views/column.py | 5 +- backend/dvadmin/system/views/menu.py | 42 +- .../views/role_menu_button_permission.py | 2 +- backend/dvadmin/utils/pagination.py | 3 +- web/src/settings.ts | 29 +- web/src/utils/dictionary.ts | 18 +- web/src/views/plugins/index.ts | 2 +- .../views/system/dictionary/subDict/index.vue | 3 +- web/src/views/system/menu/api.ts | 9 +- web/src/views/system/menu/crud.ts | 364 ++++++++++++++++++ web/src/views/system/menu/index.vue | 240 ++++++------ 15 files changed, 570 insertions(+), 206 deletions(-) create mode 100644 web/src/views/system/menu/crud.ts diff --git a/backend/application/settings.py b/backend/application/settings.py index f59140f..124d7f9 100644 --- a/backend/application/settings.py +++ b/backend/application/settings.py @@ -178,6 +178,7 @@ CHANNEL_LAYERS = { "BACKEND": "channels.layers.InMemoryChannelLayer" } } +REDIS_URL = locals().get('REDIS_URL', "") # CHANNEL_LAYERS = { # 'default': { # 'BACKEND': 'channels_redis.core.RedisChannelLayer', @@ -399,10 +400,11 @@ TENANT_SHARED_APPS = [] PLUGINS_URL_PATTERNS = [] # ********** 一键导入插件配置开始 ********** # 例如: -# from dvadmin_upgrade_center.settings import * # 升级中心 +from dvadmin3_upgrade_center.settings import * # 升级中心 # from dvadmin_celery.settings import * # celery 异步任务 # from dvadmin_third.settings import * # 第三方用户管理 # from dvadmin_ak_sk.settings import * # 秘钥管理管理 # from dvadmin_tenants.settings import * # 租户管理 +# from dvadmin_uniapp.settings import * # ... # ********** 一键导入插件配置结束 ********** diff --git a/backend/dvadmin/system/fixtures/initSerializer.py b/backend/dvadmin/system/fixtures/initSerializer.py index fc701bc..35cf5f3 100644 --- a/backend/dvadmin/system/fixtures/initSerializer.py +++ b/backend/dvadmin/system/fixtures/initSerializer.py @@ -112,7 +112,7 @@ class MenuInitSerializer(CustomModelSerializer): class Meta: model = Menu - fields = ['name', 'icon', 'sort', 'is_link', 'is_catalog', 'web_path', 'component', 'component_name', 'status', + fields = ['name', 'icon', 'sort', 'is_link', 'menu_type', 'web_path', 'component', 'component_name', 'status', 'cache', 'visible', 'parent', 'children', 'menu_button', 'creator', 'dept_belong_id'] extra_kwargs = { 'creator': {'write_only': True}, diff --git a/backend/dvadmin/system/fixtures/init_menu.json b/backend/dvadmin/system/fixtures/init_menu.json index 9d514fe..6e1c898 100644 --- a/backend/dvadmin/system/fixtures/init_menu.json +++ b/backend/dvadmin/system/fixtures/init_menu.json @@ -4,7 +4,7 @@ "icon": "iconfont icon-xitongshezhi", "sort": 1, "is_link": false, - "is_catalog": true, + "menu_type": 0, "web_path": "/system", "component": "", "component_name": "", @@ -17,7 +17,7 @@ "icon": "iconfont icon-caidan", "sort": 1, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/menu", "component": "system/menu/index", "component_name": "menu", @@ -87,7 +87,7 @@ "icon": "dot-circle-o", "sort": 2, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/menuButton", "component": "system/menuButton/index", "component_name": "menuButton", @@ -127,7 +127,7 @@ "icon": "ele-OfficeBuilding", "sort": 3, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/dept", "component": "system/dept/index", "component_name": "dept", @@ -203,7 +203,7 @@ "icon": "ele-ColdDrink", "sort": 4, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/role", "component": "system/role/index", "component_name": "role", @@ -255,7 +255,7 @@ "icon": "iconfont icon-bolangneng", "sort": 5, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/columns", "component": "system/columns/index", "component_name": "columns", @@ -313,7 +313,7 @@ "icon": "iconfont icon-icon-", "sort": 6, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/user", "component": "system/user/index", "component_name": "user", @@ -383,7 +383,7 @@ "icon": "iconfont icon-xiaoxizhongxin", "sort": 7, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/messageCenter", "component": "system/messageCenter/index", "component_name": "messageCenter", @@ -429,7 +429,7 @@ "icon": "ele-SetUp", "sort": 8, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/apiWhiteList", "component": "system/whiteList/index", "component_name": "whiteList", @@ -478,7 +478,7 @@ "icon": "iconfont icon-configure", "sort": 2, "is_link": false, - "is_catalog": true, + "menu_type": 0, "web_path": "/generalConfig", "component": "", "component_name": "", @@ -491,7 +491,7 @@ "icon": "iconfont icon-system", "sort": 0, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/config", "component": "system/config/index", "component_name": "config", @@ -537,7 +537,7 @@ "icon": "iconfont icon-dict", "sort": 1, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/dictionary", "component": "system/dictionary/index", "component_name": "dictionary", @@ -583,7 +583,7 @@ "icon": "iconfont icon-Area", "sort": 2, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/areas", "component": "system/areas/index", "component_name": "areas", @@ -629,7 +629,7 @@ "icon": "iconfont icon-file", "sort": 3, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/file", "component": "system/fileList/index", "component_name": "file", @@ -672,7 +672,7 @@ "icon": "iconfont icon-rizhi", "sort": 3, "is_link": false, - "is_catalog": true, + "menu_type": 0, "web_path": "/log", "component": "", "component_name": "", @@ -685,7 +685,7 @@ "icon": "iconfont icon-guanlidenglurizhi", "sort": 1, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/loginLog", "component": "system/log/loginLog/index", "component_name": "loginLog", @@ -713,7 +713,7 @@ "icon": "iconfont icon-caozuorizhi", "sort": 2, "is_link": false, - "is_catalog": false, + "menu_type": 1, "web_path": "/operationLog", "component": "system/log/operationLog/index", "component_name": "operationLog", diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index ed728ea..26abd1e 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -156,22 +156,24 @@ class Menu(CoreModel): help_text="上级菜单", ) icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标") - name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称") + name = models.CharField(max_length=64, verbose_name="目录名称/菜单名称/按钮名称", help_text="目录名称/菜单名称/按钮名称") sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序") - ISLINK_CHOICES = ( - (0, "否"), - (1, "是"), - ) is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链") - is_catalog = models.BooleanField(default=False, verbose_name="是否目录", help_text="是否目录") + MENU_TYPE_CHOICES =( + (0, "目录"), + (1, "菜单"), + (2, "按钮"), + ) + menu_type = models.IntegerField(default=0, verbose_name="菜单类型", help_text="菜单类型") web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址") - component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址") + component = models.CharField(max_length=200, verbose_name="组件地址/按钮权限值", null=True, blank=True, help_text="组件地址/按钮权限值") component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True, help_text="组件名称") status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态") cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存") visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示", help_text="侧边栏中是否显示") + frame_out = models.BooleanField(default=False, blank=True, verbose_name="是否主框架外", help_text="是否主框架外") class Meta: db_table = table_prefix + "system_menu" @@ -184,7 +186,6 @@ class Columns(CoreModel): role = models.ForeignKey(to='Role', on_delete=models.CASCADE, verbose_name='角色', db_constraint=False) app = models.CharField(max_length=64, verbose_name='应用名') model = models.CharField(max_length=64, verbose_name='表名') - menu = models.ForeignKey(to='Menu', on_delete=models.CASCADE, verbose_name='菜单', db_constraint=False) field_name = models.CharField(max_length=64, verbose_name='模型表字段名') title = models.CharField(max_length=64, verbose_name='字段显示名') is_query = models.BooleanField(default=1, verbose_name='是否可查询') @@ -457,7 +458,7 @@ class SystemConfig(CoreModel): help_text="父级", ) title = models.CharField(max_length=50, verbose_name="标题", help_text="标题") - key = models.CharField(max_length=20, verbose_name="键", help_text="键", db_index=True) + key = models.CharField(max_length=50, verbose_name="键", help_text="键", db_index=True) value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True) sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True) status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态") diff --git a/backend/dvadmin/system/views/column.py b/backend/dvadmin/system/views/column.py index a23d27c..5b9024c 100644 --- a/backend/dvadmin/system/views/column.py +++ b/backend/dvadmin/system/views/column.py @@ -32,10 +32,9 @@ class ColumnViewSet(CustomModelViewSet): role_id = request.query_params.get('role') app_name = request.query_params.get('app') model_name = request.query_params.get('model') - menu = request.query_params.get('menu') - if not role_id or not model_name or not app_name or not menu: + if not role_id or not model_name or not app_name: return SuccessResponse([]) - queryset = self.filter_queryset(self.get_queryset().filter(role_id=role_id, model=model_name, app=app_name,menu_id=menu)) + queryset = self.filter_queryset(self.get_queryset().filter(role_id=role_id, model=model_name, app=app_name)) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True, request=request) diff --git a/backend/dvadmin/system/views/menu.py b/backend/dvadmin/system/views/menu.py index 6899646..88dd52f 100644 --- a/backend/dvadmin/system/views/menu.py +++ b/backend/dvadmin/system/views/menu.py @@ -11,7 +11,7 @@ from rest_framework.decorators import action from dvadmin.system.models import Menu, RoleMenuPermission from dvadmin.system.views.menu_button import MenuButtonSerializer -from dvadmin.utils.json_response import SuccessResponse, ErrorResponse +from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet @@ -21,7 +21,7 @@ class MenuSerializer(CustomModelSerializer): 菜单表的简单序列化器 """ menuPermission = serializers.SerializerMethodField(read_only=True) - hasChild = serializers.SerializerMethodField() + hasChildren = serializers.SerializerMethodField() def get_menuPermission(self, instance): queryset = instance.menuPermission.order_by('-name').values('id', 'name', 'value') @@ -31,7 +31,7 @@ class MenuSerializer(CustomModelSerializer): else: return None - def get_hasChild(self, instance): + def get_hasChildren(self, instance): hasChild = Menu.objects.filter(parent=instance.id) if hasChild: return True @@ -71,7 +71,7 @@ class WebRouterSerializer(CustomModelSerializer): class Meta: model = Menu fields = ( - 'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component', + 'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'menu_type', 'web_path', 'component', 'component_name', 'cache', 'visible', 'status') read_only_fields = ["id"] @@ -90,37 +90,31 @@ class MenuViewSet(CustomModelViewSet): create_serializer_class = MenuCreateSerializer update_serializer_class = MenuCreateSerializer search_fields = ['name', 'status'] - filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog'] + filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'menu_type'] - def list(self, request): + @action(methods=['get'], detail=False) + def tree(self, request): """懒加载""" - request.query_params._mutable = True params = request.query_params - parent = params.get('parent', None) - page = params.get('page', None) - limit = params.get('limit', None) - if page: - del params['page'] - if limit: - del params['limit'] - if params: - if parent: - queryset = self.queryset.filter(parent=parent) - else: - queryset = self.queryset.filter() - else: - queryset = self.queryset.filter(parent__isnull=True) - queryset = self.filter_queryset(queryset) + menu_type = params.get('menu_type', 0) + queryset = Menu.objects.filter(menu_type=menu_type).order_by('sort') serializer = MenuSerializer(queryset, many=True, request=request) data = serializer.data - return SuccessResponse(data=data) + return DetailResponse(data=data) + + @action(methods=['get'], detail=True) + def getChildren(self,request,pk): + queryset = Menu.objects.filter(parent=pk) + serializer = MenuSerializer(queryset, many=True, request=request) + data = serializer.data + return DetailResponse(data=data) @action(methods=['GET'], detail=False, permission_classes=[]) def web_router(self, request): """用于前端获取当前角色的路由""" user = request.user is_admin = user.role.values_list('admin', flat=True) - if user.is_superuser or True in is_admin: + if user.is_superuser: queryset = self.queryset.filter(status=1) else: role_list = user.role.values_list('id', flat=True) diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index 109b27b..f43b4ee 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -100,7 +100,7 @@ class RoleMenuPermissionSerializer(CustomModelSerializer): def get_columns(self, instance): params = self.request.query_params - col_list = Columns.objects.filter(role__id=params.get('role'),menu__id=instance['id']) + col_list = Columns.objects.filter(role__id=params.get('role')) serializer = RoleColumnsSerializer(col_list,many=True,request=self.request) return serializer.data diff --git a/backend/dvadmin/utils/pagination.py b/backend/dvadmin/utils/pagination.py index 52bf143..5e80e57 100644 --- a/backend/dvadmin/utils/pagination.py +++ b/backend/dvadmin/utils/pagination.py @@ -79,6 +79,5 @@ class CustomPagination(PageNumberPagination): ('total', total), ('is_next', is_next), ('is_previous', is_previous), - ('data', data), - ('permission', self.request.permission_fields) + ('data', data) ])) diff --git a/web/src/settings.ts b/web/src/settings.ts index b43526e..2e79056 100644 --- a/web/src/settings.ts +++ b/web/src/settings.ts @@ -9,7 +9,7 @@ import { request } from '/@/utils/service'; //扩展包 import { FsExtendsEditor,FsExtendsUploader } from '@fast-crud/fast-extends'; import '@fast-crud/fast-extends/dist/style.css'; -import { successMessage, successNotification } from '/@/utils/message'; +import { ElMessage } from "element-plus"; export default { async install(app: any, options: any) { // 先安装ui @@ -18,9 +18,9 @@ export default { app.use(FastCrud, { //i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件 // 此处配置公共的dictRequest(字典请求) - async dictRequest({ dict }: any) { + async dictRequest({ url }: any) { //根据dict的url,异步返回一个字典数组 - return await request({ url: dict.url, params: dict.params || {} }).then((res:any)=>{ + return await request({ url: url, }).then((res:any)=>{ return res.data }); }, @@ -41,14 +41,21 @@ export default { transformRes: ({ res }: any) => { //将pageRequest的返回数据,转换为fast-crud所需要的格式 //return {records,currentPage,pageSize,total}; - return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total }; + if(res.page){ + return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total }; + }else{ + return { records: res.data,currentPage: 1, pageSize: res.data.length, total: res.data.length }; + } + }, }, form: { - afterSubmit(ctx: any) { - // 增加crud提示 - if (ctx.res.code == 2000) { - successNotification(ctx.res.msg); + afterSubmit(ctx:any ) { + const {mode} = ctx + if (mode === "add") { + ElMessage.success({ message: "添加成功" }); + } else if (mode === "edit") { + ElMessage.success({ message: "保存成功" }); } }, }, @@ -100,10 +107,12 @@ export default { }); }, successHandle(ret) { + console.log(111,ret) // 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx} return { - url: getBaseURL() + ret.data.url, - key: ret.data.id + url: ret.data.url, + key: ret.data.id, + ...ret.data }; } } diff --git a/web/src/utils/dictionary.ts b/web/src/utils/dictionary.ts index e35a205..0305891 100644 --- a/web/src/utils/dictionary.ts +++ b/web/src/utils/dictionary.ts @@ -1,11 +1,19 @@ import { toRaw } from 'vue'; import { DictionaryStore } from '/@/stores/dictionary'; -/** - * @method 获取指定name字典 +/** + * @method 获取指定name字典 */ -export const dictionary = (name: string) => { +export const dictionary = (key: string,value:string|number) => { const dict = DictionaryStore() const dictionary = toRaw(dict.data) - return dictionary[name] -} \ No newline at end of file + if(value!==null || value !==''){ + for (let item of dictionary[key]) { + if (item.value === value) { + return item.label + } + } + return '' + } + return dictionary[key] +} diff --git a/web/src/views/plugins/index.ts b/web/src/views/plugins/index.ts index 7ba8bf9..5e1d1fd 100644 --- a/web/src/views/plugins/index.ts +++ b/web/src/views/plugins/index.ts @@ -12,5 +12,5 @@ export const scanAndInstallPlugins = (app: any) => { pluginNames.add(pluginsName); } pluginsAll = Array.from(pluginNames); - console.log('已发现插件:', pluginsAll); + console.table('已注册插件:', pluginsAll); }; diff --git a/web/src/views/system/dictionary/subDict/index.vue b/web/src/views/system/dictionary/subDict/index.vue index 65dd400..12d9b6c 100644 --- a/web/src/views/system/dictionary/subDict/index.vue +++ b/web/src/views/system/dictionary/subDict/index.vue @@ -1,8 +1,6 @@ @@ -25,6 +23,7 @@ const handleClose = (done: () => void) => { }) .then(() => { done(); + }) .catch(() => { // catch error diff --git a/web/src/views/system/menu/api.ts b/web/src/views/system/menu/api.ts index da6afb2..4182155 100644 --- a/web/src/views/system/menu/api.ts +++ b/web/src/views/system/menu/api.ts @@ -5,12 +5,19 @@ export const apiPrefix = '/api/system/menu/'; export function GetList(query: UserPageQuery) { return request({ - url: apiPrefix, + url: apiPrefix+"tree/", method: 'get', params: query, }); } +export function GetChildren(id: InfoReq) { + return request({ + url: apiPrefix + id + '/getChildren/', + method: 'get', + }); +} + export function GetObj(id: InfoReq) { return request({ url: apiPrefix + id + '/', diff --git a/web/src/views/system/menu/crud.ts b/web/src/views/system/menu/crud.ts new file mode 100644 index 0000000..0dc7a78 --- /dev/null +++ b/web/src/views/system/menu/crud.ts @@ -0,0 +1,364 @@ +import * as api from './api'; +import { CreateCrudOptionsProps, CreateCrudOptionsRet, dict, useCompute } from '@fast-crud/fast-crud'; +const { compute } = useCompute(); +import { shallowRef } from "vue"; +import IconSelector from "/@/components/IconSelector/index.vue" +export default function ({ crudExpose, onAddCatalog, onAddChildren, onAddButton }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const pageRequest = async (query) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }) => { + form.id = row.id; + await api.UpdateObj(form); + if (row.parent) { + //刷新父节点的状态 + reloadTreeChildren(row.parent); + } + }; + const delRequest = async ({ row }:any) => { + await api.DelObj(row.id); + if (row.parent) { + //刷新父节点的状态 + reloadTreeChildren(row.parent); + } + }; + + const addRequest = async (context:any) => { + return await api.AddObj(context.form); + }; + + //刷新父节点状态 + function reloadTreeChildren(parent:string|number) { + const data = crudExpose.getBaseTableRef().store.states.treeData; + if (data.value != null) { + const item = data.value[parent]; + if (item != null) { + item.loaded = false; + item.expanded = false; + } + } + } + + const createFilter = (queryString: string) => { + return (file: any) => { + return file.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1; + }; + }; + + // 获取组件地址 + const getCompoent = (queryString: string, cb: any) => { + const files: any = import.meta.glob('@views/**/*.vue'); + let fileLists: Array = []; + Object.keys(files).forEach((queryString: string) => { + fileLists.push({ + label: queryString.replace(/(\.\/|\.vue)/g, ''), + value: queryString.replace(/(\.\/|\.vue)/g, ''), + }); + }); + const results = queryString ? fileLists.filter(createFilter(queryString)) : fileLists; + // 统一去掉/src/views/前缀 + results.forEach((val) => { + val.label = val.label.replace('/src/views/', ''); + val.value = val.value.replace('/src/views/', ''); + }); + cb(results); + }; + + // 验证路由地址 + const { getFormData } = crudExpose; + const validateWebPath = (rule: any, value: string, callback: Function) => { + let pattern = /^\/.*?/; + let patternUrl = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/; + const reg = getFormData().is_link ? patternUrl.test(value) : pattern.test(value); + if (reg) { + callback(); + } else { + callback(new Error('请输入正确的地址')); + } + }; + + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + pagination: { + show: false, + }, + form: { + labelWidth: '120px', + row: { gutter: 20 }, + // group: { + // groupType: 'tabs', //collapse, tabs + // accordion: false, + // groups: { + // catalog: { + // label: '目录', + // icon: 'el-icon-goods', + // columns: ['name', 'icon', 'sort', 'status'], + // }, + // menu: { + // label: '菜单', + // icon: 'el-icon-price-tag', + // columns: ['parent', 'name', 'icon', 'is_link', 'web_path', 'component', 'cache', 'visible', 'frame_out', 'sort', 'status'], + // }, + // info: { + // label: '按钮', + // collapsed: true, //默认折叠 + // icon: 'el-icon-warning-outline', + // columns: ['name', 'component'], + // }, + // }, + // }, + }, + table: { + lazy: true, + load: async (row: any, treeNode: unknown, resolve: (date: any[]) => void) => { + //懒加载,更新和删除后,需要刷新父节点的状态,见上方 + const obj = await api.GetChildren(row.id); + resolve([...obj.data]); + }, + }, + columns: { + id: { + title: 'ID', + key: 'id', + type: 'number', + column: { + width: 100, + }, + form: { + show: false, + }, + }, + menu_type: { + title: '类型', + type: 'dict-radio', + dict: dict({ + data: [ + { label: '目录', value: 0 }, + { label: '菜单', value: 1 }, + { label: '按钮', value: 2 }, + ], + }), + form: { + value:0, + valueChange({ form, value, getComponentRef }) { + if (value) { + getComponentRef("parent").reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典 + } + } + }, + column: { + show: false, + }, + }, + parent: { + title: '父级', + dict: dict({ + prototype: true, + url({form}){ + if(form && form.menu_type===1){ + return '/api/system/menu/tree/?menu_type=0' + }else{ + return `/api/system/menu/tree/?menu_type=1` + } + return undefined + }, + label: 'name', + value: 'id', + }), + type: 'dict-select', + form: { + show:compute(({form})=>{ + return [1,2].includes(form.menu_type); + }), + rules: [{ required: true, message: '必填项' }], + component: {}, + }, + column: { + show: false, + }, + }, + name: { + title: '名称', + search: { show: true }, + type: 'text', + form: { + rules: [{ required: true, message: '请输入名称' }], + component: { + placeholder: '请输入名称', + }, + }, + }, + icon: { + title: '图标', + form:{ + show:compute(({form})=>{ + return [0,1].includes(form.menu_type); + }), + component:{ + name: shallowRef(IconSelector), + vModel: "modelValue", + } + }, + column: { + component: { + style: 'font-size:18px', + }, + }, + }, + sort: { + title: '排序', + type: 'number', + form: { + value: 1, + show:compute(({form})=>{ + return [0,1].includes(form.menu_type); + }), + }, + }, + is_link: { + title: '外链接', + type: 'dict-switch', + dict: dict({ + data: [ + { label: '是', value: true }, + { label: '否', value: false }, + ], + }), + form: { + value: false, + show:compute(({form})=>{ + return [1].includes(form.menu_type); + }), + }, + }, + web_path: { + title: '路由地址', + form: { + show:compute(({form})=>{ + return [1].includes(form.menu_type); + }), + helper: compute(({ form }) => { + return form.is_link ? '请输入http开头的地址' : '浏览器中url的地址,请以/开头'; + }), + rules: [{ required: true, message: '请输入路由地址', validator: validateWebPath, trigger: 'blur' }], + component: { + placeholder: '请输入路由地址', + }, + }, + column: { + show: false, + }, + }, + component: { + title: '组件地址', + form: { + show: compute(({ form }) => { + return [1,2].includes(form.menu_type) + }), + helper: compute(({ form }) => { + return form.menu_type === 1 ? 'src/views下的文件夹地址' : '按钮权限值是唯一的标识'; + }), + rules: [{ required: true, message: '请输入组件地址', trigger: 'blur' }], + component: { + style: { + width: '100%', + }, + disabled: compute(({ form }) => { + if(form.is_link&&form.menu_type===1){ + form.component ="无" + return form.is_link + } + form.component =null + return false + }), + name: compute(({ form }) => { + return [1,2].includes(form.menu_type)? 'el-autocomplete' : 'el-input'; + }), + triggerOnFocus: false, + fetchSuggestions: (query, cb) => { + return getCompoent(query, cb); + }, + }, + }, + column: { + show: false, + }, + }, + visible: { + title: '侧边可见', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + data: [ + { label: '是', value: true }, + { label: '否', value: false }, + ], + }), + form: { + value: true, + show:compute(({form})=>{ + return [1].includes(form.menu_type) + }), + }, + }, + cache: { + title: '是否缓存', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + data: [ + { label: '是', value: true }, + { label: '否', value: false }, + ], + }), + form: { + value: false, + show:compute(({form})=>{ + return [1].includes(form.menu_type) + }), + }, + }, + frame_out: { + title: '主框架外展示', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + data: [ + { label: '是', value: true }, + { label: '否', value: false }, + ], + }), + form: { + value: false, + show:compute(({form})=>{ + return [1].includes(form.menu_type) + }), + }, + }, + status: { + title: '状态', + search: { show: true }, + type: 'dict-radio', + dict: dict({ + data: [ + { label: '启用', value: true }, + { label: '禁用', value: false }, + ], + }), + form: { + value: true, + show:compute(({form})=>{ + return [0,1].includes(form.menu_type); + }), + }, + }, + }, + }, + }; +} diff --git a/web/src/views/system/menu/index.vue b/web/src/views/system/menu/index.vue index 97e26c3..c71d378 100644 --- a/web/src/views/system/menu/index.vue +++ b/web/src/views/system/menu/index.vue @@ -1,142 +1,124 @@ - - -