diff --git a/README.zh.md b/README.zh.md index dea0106..a95a364 100644 --- a/README.zh.md +++ b/README.zh.md @@ -114,7 +114,7 @@ cd web # 安装依赖 npm install yarn -yarn install --registry=https://registry.npm.taobao.org +yarn install --registry=https://registry.npmmirror.com # 启动服务 yarn build diff --git a/backend/application/__init__.py b/backend/application/__init__.py index e69de29..b7ddb6f 100644 --- a/backend/application/__init__.py +++ b/backend/application/__init__.py @@ -0,0 +1,5 @@ +# This will make sure the app is always imported when +# Django starts so that shared_task will use this app. +from .celery import app as celery_app + +__all__ = ('celery_app',) \ No newline at end of file diff --git a/backend/application/celery.py b/backend/application/celery.py index d57b92a..10bce56 100644 --- a/backend/application/celery.py +++ b/backend/application/celery.py @@ -15,7 +15,7 @@ else: from celery import Celery app = Celery(f"application") -app.config_from_object('django.conf:settings') +app.config_from_object('django.conf:settings', namespace='CELERY') app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) platforms.C_FORCE_ROOT = True diff --git a/backend/dvadmin/system/views/area.py b/backend/dvadmin/system/views/area.py index 3a72728..7a19eb0 100644 --- a/backend/dvadmin/system/views/area.py +++ b/backend/dvadmin/system/views/area.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import pypinyin from django.db.models import Q from rest_framework import serializers @@ -15,6 +16,11 @@ class AreaSerializer(CustomModelSerializer): """ pcode_count = serializers.SerializerMethodField(read_only=True) hasChild = serializers.SerializerMethodField() + pcode_info = serializers.SerializerMethodField() + + def get_pcode_info(self, instance): + pcode = Area.objects.filter(code=instance.pcode_id).values("name", "code") + return pcode def get_pcode_count(self, instance: Area): return Area.objects.filter(pcode=instance).count() @@ -36,6 +42,18 @@ class AreaCreateUpdateSerializer(CustomModelSerializer): 地区管理 创建/更新时的列化器 """ + def to_internal_value(self, data): + pinyin = ''.join([''.join(i) for i in pypinyin.pinyin(data["name"], style=pypinyin.NORMAL)]) + data["level"] = 1 + data["pinyin"] = pinyin + data["initials"] = pinyin[0].upper() if pinyin else "#" + pcode = data["pcode"] if 'pcode' in data else None + if pcode: + pcode = Area.objects.get(pk=pcode) + data["pcode"] = pcode.code + data["level"] = pcode.level + 1 + return super().to_internal_value(data) + class Meta: model = Area fields = '__all__' @@ -52,20 +70,28 @@ class AreaViewSet(CustomModelViewSet, FieldPermissionMixin): """ queryset = Area.objects.all() serializer_class = AreaSerializer + create_serializer_class = AreaCreateUpdateSerializer + update_serializer_class = AreaCreateUpdateSerializer extra_filter_class = [] - def get_queryset(self): + def list(self, request, *args, **kwargs): self.request.query_params._mutable = True params = self.request.query_params - pcode = params.get('pcode', None) - page = params.get('page', None) - limit = params.get('limit', None) - if page: - del params['page'] - if limit: - del params['limit'] - if params and pcode: - queryset = self.queryset.filter(enable=True, pcode=pcode) - else: + known_params = {'page', 'limit', 'pcode'} + # 使用集合操作检查是否有未知参数 + other_params_exist = any(param not in known_params for param in params) + if other_params_exist: queryset = self.queryset.filter(enable=True) - return queryset + else: + pcode = params.get('pcode', None) + params['limit'] = 999 + if params and pcode: + queryset = self.queryset.filter(enable=True, pcode=pcode) + else: + queryset = self.queryset.filter(enable=True, level=1) + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True, request=request) + return self.get_paginated_response(serializer.data) + serializer = self.get_serializer(queryset, many=True, request=request) + return SuccessResponse(data=serializer.data, msg="获取成功") diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py index 4023a82..1996906 100644 --- a/backend/dvadmin/system/views/login.py +++ b/backend/dvadmin/system/views/login.py @@ -124,6 +124,7 @@ class LoginSerializer(TokenObtainPairSerializer): user.is_active = False user.save() raise CustomValidationError("账号已被锁定,联系管理员解锁") + user.save() count = 5 - user.login_error_count raise CustomValidationError(f"账号/密码错误;重试{count}次后将被锁定~") diff --git a/backend/dvadmin/utils/field_permission.py b/backend/dvadmin/utils/field_permission.py index 20b4cb9..7c259f6 100644 --- a/backend/dvadmin/utils/field_permission.py +++ b/backend/dvadmin/utils/field_permission.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- + +from itertools import groupby + from django.db.models import F from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated @@ -35,4 +38,34 @@ class FieldPermissionMixin: data= FieldPermission.objects.filter( field__model=model['model'],role__in=roles ).values( 'is_create', 'is_query', 'is_update',field_name=F('field__field_name')) + + """ + 合并权限 + + 这段代码首先根据 field_name 对列表进行排序, + 然后使用 groupby 按 field_name 进行分组。 + 对于每个组,它创建一个新的字典 merged, + 并遍历组中的每个字典,将布尔值字段使用逻辑或(or)操作符进行合并(如果 merged 中还没有该字段,则默认为 False), + 其他字段(如 field_name)则直接取组的关键字(即 key) + """ + + # 使用field_name对列表进行分组, # groupby 需要先对列表进行排序,因为它只能对连续相同的元素进行分组。 + grouped = groupby(sorted(list(data), key=lambda x: x['field_name']), key=lambda x: x['field_name']) + + data = [] + + # 遍历分组,合并权限 + for key, group in grouped: + + # 初始化一个空字典来存储合并后的结果 + merged = {} + for item in group: + # 合并权限, True值优先 + merged['is_create'] = merged.get('is_create', False) or item['is_create'] + merged['is_query'] = merged.get('is_query', False) or item['is_query'] + merged['is_update'] = merged.get('is_update', False) or item['is_update'] + merged['field_name'] = key + + data.append(merged) + return DetailResponse(data=data) \ No newline at end of file diff --git a/backend/dvadmin/utils/filters.py b/backend/dvadmin/utils/filters.py index 05c0dfb..d09a8a1 100644 --- a/backend/dvadmin/utils/filters.py +++ b/backend/dvadmin/utils/filters.py @@ -37,11 +37,11 @@ class CoreModelFilterBankend(BaseFilterBackend): if any([create_datetime_after, create_datetime_before, update_datetime_after, update_datetime_before]): create_filter = Q() if create_datetime_after and create_datetime_before: - create_filter &= Q(create_datetime__gte=create_datetime_after) & Q(create_datetime__lte=create_datetime_before) + create_filter &= Q(create_datetime__gte=create_datetime_after) & Q(create_datetime__lte=f'{create_datetime_before} 23:59:59') elif create_datetime_after: create_filter &= Q(create_datetime__gte=create_datetime_after) elif create_datetime_before: - create_filter &= Q(create_datetime__lte=create_datetime_before) + create_filter &= Q(create_datetime__lte=f'{create_datetime_before} 23:59:59') # 更新时间范围过滤条件 update_filter = Q() diff --git a/backend/dvadmin/utils/middleware.py b/backend/dvadmin/utils/middleware.py index f4717a4..6029c5a 100644 --- a/backend/dvadmin/utils/middleware.py +++ b/backend/dvadmin/utils/middleware.py @@ -32,6 +32,14 @@ class ApiLoggingMiddleware(MiddlewareMixin): request.request_path = get_request_path(request) def __handle_response(self, request, response): + + # 判断有无log_id属性,使用All记录时,会出现此情况 + if request.request_data.get('log_id', None) is None: + return + + # 移除log_id,不记录此ID + log_id = request.request_data.pop('log_id') + # request_data,request_ip由PermissionInterfaceMiddleware中间件中添加的属性 body = getattr(request, 'request_data', {}) # 请求含有password则用*替换掉(暂时先用于所有接口的password请求参数) @@ -60,7 +68,7 @@ class ApiLoggingMiddleware(MiddlewareMixin): 'status': True if response.data.get('code') in [2000, ] else False, 'json_result': {"code": response.data.get('code'), "msg": response.data.get('msg')}, } - operation_log, creat = OperationLog.objects.update_or_create(defaults=info, id=self.operation_log_id) + operation_log, creat = OperationLog.objects.update_or_create(defaults=info, id=log_id) if not operation_log.request_modular and settings.API_MODEL_MAP.get(request.request_path, None): operation_log.request_modular = settings.API_MODEL_MAP[request.request_path] operation_log.save() @@ -71,7 +79,8 @@ class ApiLoggingMiddleware(MiddlewareMixin): if self.methods == 'ALL' or request.method in self.methods: log = OperationLog(request_modular=get_verbose_name(view_func.cls.queryset)) log.save() - self.operation_log_id = log.id + # self.operation_log_id = log.id + request.request_data['log_id'] = log.id return diff --git a/backend/dvadmin/utils/models.py b/backend/dvadmin/utils/models.py index 7ed2f92..09d13f1 100644 --- a/backend/dvadmin/utils/models.py +++ b/backend/dvadmin/utils/models.py @@ -216,9 +216,13 @@ def get_all_models_objects(model_name=None): def get_model_from_app(app_name): """获取模型里的字段""" model_module = import_module(app_name + '.models') + exclude_models = getattr(model_module, 'exclude_models', []) filter_model = [ - getattr(model_module, item) for item in dir(model_module) - if item != 'CoreModel' and issubclass(getattr(model_module, item).__class__, models.base.ModelBase) + value for key, value in model_module.__dict__.items() + if key != 'CoreModel' + and isinstance(value, type) + and issubclass(value, models.Model) + and key not in exclude_models ] model_list = [] for model in filter_model: diff --git a/web/src/components/tableSelector/index.vue b/web/src/components/tableSelector/index.vue index 7ad3cad..8e8c91a 100644 --- a/web/src/components/tableSelector/index.vue +++ b/web/src/components/tableSelector/index.vue @@ -1,203 +1,211 @@ diff --git a/web/src/utils/columnPermission.ts b/web/src/utils/columnPermission.ts index 4a9d9f1..adff4e1 100644 --- a/web/src/utils/columnPermission.ts +++ b/web/src/utils/columnPermission.ts @@ -22,33 +22,18 @@ export const handleColumnPermission = async (func: Function, crudOptions: any,ex } } const columns = crudOptions.columns; - const excludeColumns = ['_index','id', 'create_datetime', 'update_datetime'].concat(excludeColumn) + const excludeColumns = ['checked','_index','id', 'create_datetime', 'update_datetime'].concat(excludeColumn) for (let col in columns) { - if (excludeColumns.includes(col)) { - continue - }else{ - if (columns[col].column) { - columns[col].column.show = false - } else { - columns[col]['column'] = { - show: false - } - } - columns[col].addForm = { - show: false - } - columns[col].editForm = { - show: false - } - } - for (let item of res.data) { if (excludeColumns.includes(item.field_name)) { continue } else if(item.field_name === col) { - columns[col].column.show = item['is_query'] // 如果列表不可见,则禁止在列设置中选择 - if(!item['is_query'])columns[col].column.columnSetDisabled = true + // 只有列表不可见,才修改列配置,这样才不影响默认的配置 + if(!item['is_query']){ + columns[col].column.show = false + columns[col].column.columnSetDisabled = true + } columns[col].addForm = { show: item['is_create'] } diff --git a/web/src/views/system/areas/crud.tsx b/web/src/views/system/areas/crud.tsx index 01d1860..1a1e47b 100644 --- a/web/src/views/system/areas/crud.tsx +++ b/web/src/views/system/areas/crud.tsx @@ -1,244 +1,202 @@ import * as api from './api'; -import { - dict, - UserPageQuery, - AddReq, - DelReq, - EditReq, - compute, - CreateCrudOptionsProps, - CreateCrudOptionsRet -} from '@fast-crud/fast-crud'; -import {dictionary} from '/@/utils/dictionary'; -import {successMessage} from '/@/utils/message'; -import {auth} from "/@/utils/authFunction"; +import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'; +import { dictionary } from '/@/utils/dictionary'; +import { successMessage } from '/@/utils/message'; +import { auth } from '/@/utils/authFunction'; +import tableSelector from '/@/components/tableSelector/index.vue'; +import { shallowRef } from 'vue'; -export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps): CreateCrudOptionsRet { - const pageRequest = async (query: UserPageQuery) => { - return await api.GetList(query); - }; - const editRequest = async ({form, row}: EditReq) => { - form.id = row.id; - return await api.UpdateObj(form); - }; - const delRequest = async ({row}: DelReq) => { - return await api.DelObj(row.id); - }; - const addRequest = async ({form}: AddReq) => { - return await api.AddObj(form); - }; +export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const pageRequest = async (query: UserPageQuery) => { + return await api.GetList(query); + }; + const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({ row }: DelReq) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({ form }: AddReq) => { + return await api.AddObj(form); + }; - /** - * 懒加载 - * @param row - * @returns {Promise} - */ - const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => { - pageRequest({pcode: tree.code}).then((res: APIResponseData) => { - resolve(res.data); - }); - }; + /** + * 懒加载 + * @param row + * @returns {Promise} + */ + const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => { + pageRequest({ pcode: tree.code }).then((res: APIResponseData) => { + resolve(res.data); + }); + }; - return { - crudOptions: { - request: { - pageRequest, - addRequest, - editRequest, - delRequest, - }, - actionbar: { - buttons: { - add: { - show: auth('area:Create'), - } - } - }, - rowHandle: { - //固定右侧 - fixed: 'right', - width: 200, - buttons: { - view: { - show: false, - }, - edit: { - iconRight: 'Edit', - type: 'text', - show: auth('area:Update') - }, - remove: { - iconRight: 'Delete', - type: 'text', - show: auth('area:Delete') - }, - }, - }, - pagination: { - show: false, - }, - table: { - rowKey: 'id', - lazy: true, - load: loadContentMethod, - treeProps: {children: 'children', hasChildren: 'hasChild'}, - }, - columns: { - _index: { - title: '序号', - form: {show: false}, - column: { - type: 'index', - align: 'center', - width: '70px', - columnSetDisabled: true, //禁止在列设置中选择 - }, - }, - // pcode: { - // title: '父级地区', - // show: false, - // search: { - // show: true, - // }, - // type: 'dict-tree', - // form: { - // component: { - // showAllLevels: false, // 仅显示最后一级 - // props: { - // elProps: { - // clearable: true, - // showAllLevels: false, // 仅显示最后一级 - // props: { - // checkStrictly: true, // 可以不需要选到最后一级 - // emitPath: false, - // clearable: true, - // }, - // }, - // }, - // }, - // }, - // }, - name: { - title: '名称', - search: { - show: true, - }, - treeNode: true, - type: 'input', - column: { - minWidth: 120, - }, - form: { - rules: [ - // 表单校验规则 - {required: true, message: '名称必填项'}, - ], - component: { - placeholder: '请输入名称', - }, - }, - }, - code: { - title: '地区编码', - search: { - show: true, - }, - type: 'input', - column: { - minWidth: 90, - }, - form: { - rules: [ - // 表单校验规则 - {required: true, message: '地区编码必填项'}, - ], - component: { - placeholder: '请输入地区编码', - }, - }, - }, - pinyin: { - title: '拼音', - search: { - disabled: true, - }, - type: 'input', - column: { - minWidth: 120, - }, - form: { - rules: [ - // 表单校验规则 - {required: true, message: '拼音必填项'}, - ], - component: { - placeholder: '请输入拼音', - }, - }, - }, - level: { - title: '地区层级', - search: { - disabled: true, - }, - type: 'input', - column: { - minWidth: 100, - }, - form: { - disabled: false, - rules: [ - // 表单校验规则 - {required: true, message: '拼音必填项'}, - ], - component: { - placeholder: '请输入拼音', - }, - }, - }, - initials: { - title: '首字母', - column: { - minWidth: 100, - }, - form: { - rules: [ - // 表单校验规则 - {required: true, message: '首字母必填项'}, - ], - - component: { - placeholder: '请输入首字母', - }, - }, - }, - enable: { - title: '是否启用', - search: { - show: true, - }, - type: 'dict-radio', - column: { - minWidth: 90, - component: { - name: 'fs-dict-switch', - activeText: '', - inactiveText: '', - style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6', - onChange: compute((context) => { - return () => { - api.UpdateObj(context.row).then((res: APIResponseData) => { - successMessage(res.msg as string); - }); - }; - }), - }, - }, - dict: dict({ - data: dictionary('button_status_bool'), - }), - }, - }, - }, - }; + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + actionbar: { + buttons: { + add: { + show: auth('area:Create'), + }, + }, + }, + rowHandle: { + //固定右侧 + fixed: 'right', + width: 200, + buttons: { + view: { + show: false, + }, + edit: { + iconRight: 'Edit', + type: 'text', + show: auth('area:Update'), + }, + remove: { + iconRight: 'Delete', + type: 'text', + show: auth('area:Delete'), + }, + }, + }, + pagination: { + show: false, + }, + table: { + rowKey: 'id', + lazy: true, + load: loadContentMethod, + treeProps: { children: 'children', hasChildren: 'hasChild' }, + }, + columns: { + _index: { + title: '序号', + form: { show: false }, + column: { + type: 'index', + align: 'center', + width: '70px', + columnSetDisabled: true, //禁止在列设置中选择 + }, + }, + name: { + title: '名称', + search: { + show: true, + }, + treeNode: true, + type: 'input', + column: { + minWidth: 120, + }, + form: { + rules: [ + // 表单校验规则 + { required: true, message: '名称必填项' }, + ], + component: { + placeholder: '请输入名称', + }, + }, + }, + pcode: { + title: '父级地区', + search: { + disabled: true, + }, + width: 130, + type: 'table-selector', + form: { + component: { + name: shallowRef(tableSelector), + vModel: 'modelValue', + displayLabel: compute(({ row }) => { + if (row) { + return row.pcode_info; + } + return null; + }), + tableConfig: { + url: '/api/system/area/', + label: 'name', + value: 'id', + isTree: true, + isMultiple: false, + lazy: true, + load: loadContentMethod, + treeProps: { children: 'children', hasChildren: 'hasChild' }, + columns: [ + { + prop: 'name', + label: '地区', + width: 150, + }, + { + prop: 'code', + label: '地区编码', + }, + ], + }, + }, + }, + column: { + show: false, + }, + }, + code: { + title: '地区编码', + search: { + show: true, + }, + type: 'input', + column: { + minWidth: 90, + }, + form: { + rules: [ + // 表单校验规则 + { required: true, message: '地区编码必填项' }, + ], + component: { + placeholder: '请输入地区编码', + }, + }, + }, + enable: { + title: '是否启用', + search: { + show: true, + }, + type: 'dict-radio', + column: { + minWidth: 90, + component: { + name: 'fs-dict-switch', + activeText: '', + inactiveText: '', + style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6', + onChange: compute((context) => { + return () => { + api.UpdateObj(context.row).then((res: APIResponseData) => { + successMessage(res.msg as string); + }); + }; + }), + }, + }, + dict: dict({ + data: dictionary('button_status_bool'), + }), + }, + }, + }, + }; }; diff --git a/web/src/views/system/dept/components/DeptUserCom/index.vue b/web/src/views/system/dept/components/DeptUserCom/index.vue index fb50ff9..f52db55 100644 --- a/web/src/views/system/dept/components/DeptUserCom/index.vue +++ b/web/src/views/system/dept/components/DeptUserCom/index.vue @@ -277,7 +277,8 @@ const { resetCrudOptions } = useCrud({ padding: 0 10px; border-radius: 8px 0 0 8px; box-sizing: border-box; - background-color: #fff; + color: var(--next-bg-topBarColor); + background-color: var(--el-fill-color-blank);; } .dept-user-com-table { height: calc(100% - 200px); diff --git a/web/src/views/system/dept/index.vue b/web/src/views/system/dept/index.vue index 60b803b..52df92b 100644 --- a/web/src/views/system/dept/index.vue +++ b/web/src/views/system/dept/index.vue @@ -133,7 +133,7 @@ onMounted(() => { } .dept-left { - background-color: #fff; + background-color: var(--el-fill-color-blank);; border-radius: 0 8px 8px 0; padding: 10px; } diff --git a/web/src/views/system/menu/components/MenuButtonCom/api.ts b/web/src/views/system/menu/components/MenuButtonCom/api.ts index d91f920..612c49d 100644 --- a/web/src/views/system/menu/components/MenuButtonCom/api.ts +++ b/web/src/views/system/menu/components/MenuButtonCom/api.ts @@ -48,3 +48,10 @@ export function BatchAdd(obj: AddReq) { }); } +export function BatchDelete(keys: any) { + return request({ + url: apiPrefix + 'multiple_delete/', + method: 'delete', + data: { keys }, + }); +} diff --git a/web/src/views/system/menu/components/MenuButtonCom/crud.tsx b/web/src/views/system/menu/components/MenuButtonCom/crud.tsx index 908cf2a..79675fb 100644 --- a/web/src/views/system/menu/components/MenuButtonCom/crud.tsx +++ b/web/src/views/system/menu/components/MenuButtonCom/crud.tsx @@ -4,6 +4,8 @@ import {auth} from '/@/utils/authFunction' import {request} from '/@/utils/service'; import { successNotification } from '/@/utils/message'; import { ElMessage } from 'element-plus'; +import { nextTick, ref } from 'vue'; +import XEUtils from 'xe-utils'; //此处为crudOptions配置 export const createCrudOptions = function ({crudExpose, context}: CreateCrudOptionsProps): CreateCrudOptionsRet { const pageRequest = async () => { @@ -22,7 +24,42 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti const addRequest = async ({form}: AddReq) => { return await api.AddObj({...form, ...{menu: context!.selectOptions.value.id}}); }; + // 记录选中的行 + const selectedRows = ref([]); + + const onSelectionChange = (changed: any) => { + const tableData = crudExpose.getTableData(); + const unChanged = tableData.filter((row: any) => !changed.includes(row)); + // 添加已选择的行 + XEUtils.arrayEach(changed, (item: any) => { + const ids = XEUtils.pluck(selectedRows.value, 'id'); + if (!ids.includes(item.id)) { + selectedRows.value = XEUtils.union(selectedRows.value, [item]); + } + }); + // 剔除未选择的行 + XEUtils.arrayEach(unChanged, (unItem: any) => { + selectedRows.value = XEUtils.remove(selectedRows.value, (item: any) => item.id !== unItem.id); + }); + }; + const toggleRowSelection = () => { + // 多选后,回显默认勾选 + const tableRef = crudExpose.getBaseTableRef(); + const tableData = crudExpose.getTableData(); + const selected = XEUtils.filter(tableData, (item: any) => { + const ids = XEUtils.pluck(selectedRows.value, 'id'); + return ids.includes(item.id); + }); + + nextTick(() => { + XEUtils.arrayEach(selected, (item) => { + tableRef.toggleRowSelection(item, true); + }); + }); + }; + return { + selectedRows, crudOptions: { pagination:{ show:false @@ -84,6 +121,11 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti editRequest, delRequest, }, + table: { + rowKey: 'id', //设置你的主键id, 默认rowKey=id + onSelectionChange, + onRefreshed: () => toggleRowSelection(), + }, form: { col: {span: 24}, labelWidth: '100px', @@ -93,6 +135,16 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti }, }, columns: { + $checked: { + title: '选择', + form: { show: false }, + column: { + type: 'selection', + align: 'center', + width: '70px', + columnSetDisabled: true, //禁止在列设置中选择 + }, + }, _index: { title: '序号', form: {show: false}, diff --git a/web/src/views/system/menu/components/MenuButtonCom/index.vue b/web/src/views/system/menu/components/MenuButtonCom/index.vue index 9dddaf9..ec55e35 100644 --- a/web/src/views/system/menu/components/MenuButtonCom/index.vue +++ b/web/src/views/system/menu/components/MenuButtonCom/index.vue @@ -1,19 +1,72 @@ diff --git a/web/src/views/system/menu/index.vue b/web/src/views/system/menu/index.vue index 0637a3f..4ab466f 100644 --- a/web/src/views/system/menu/index.vue +++ b/web/src/views/system/menu/index.vue @@ -16,12 +16,12 @@ -
+
-
+
@@ -138,7 +138,7 @@ onMounted(() => { .menu-box { height: 100%; padding: 10px; - background-color: #fff; + background-color: var(--el-fill-color-blank);; box-sizing: border-box; }