diff --git a/backend/dvadmin/__init__.py b/backend/dvadmin/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..40a96afc6ff09d58a702b76e3f7dd412fe975e26 --- /dev/null +++ b/backend/dvadmin/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/backend/dvadmin/system/fixtures/init_menu.json b/backend/dvadmin/system/fixtures/init_menu.json index 425887234ac2af9b6d904e786b969173c8e27729..b683f0af73fb39e6746918b0b0f3c8f05ff17a8f 100644 --- a/backend/dvadmin/system/fixtures/init_menu.json +++ b/backend/dvadmin/system/fixtures/init_menu.json @@ -167,19 +167,13 @@ "method": 0 }, { - "name": "查询所有", + "name": "获取所有部门", "value": "dept:SearchAll", "api": "/api/system/dept/all_dept/", "method": 0 }, { - "name": "懒加载查询所有", - "value": "dept:LazySearchAll", - "api": "/api/system/dept/dept_lazy_tree/", - "method": 0 - }, - { - "name": "头信息", + "name": "部门顶部信息", "value": "dept:HeaderInfo", "api": "/api/system/dept/dept_info/", "method": 0 diff --git a/backend/dvadmin/system/urls.py b/backend/dvadmin/system/urls.py index 8d21507155942d5ad158f22ee8a906bc9e3972c5..a41e04868416e9db410eb3c59afd1fca64bc6ffe 100644 --- a/backend/dvadmin/system/urls.py +++ b/backend/dvadmin/system/urls.py @@ -35,6 +35,7 @@ system_url.register(r'message_center', MessageCenterViewSet) system_url.register(r'role_menu_button_permission', RoleMenuButtonPermissionViewSet) system_url.register(r'role_menu_permission', RoleMenuPermissionViewSet) system_url.register(r'column', MenuFieldViewSet) +system_url.register(r'login_log', LoginLogViewSet) urlpatterns = [ @@ -44,8 +45,8 @@ urlpatterns = [ path('system_config/get_association_table/', SystemConfigViewSet.as_view({'get': 'get_association_table'})), path('system_config/get_table_data//', SystemConfigViewSet.as_view({'get': 'get_table_data'})), path('system_config/get_relation_info/', SystemConfigViewSet.as_view({'get': 'get_relation_info'})), - path('login_log/', LoginLogViewSet.as_view({'get': 'list'})), - path('login_log//', LoginLogViewSet.as_view({'get': 'retrieve'})), + # path('login_log/', LoginLogViewSet.as_view({'get': 'list'})), + # path('login_log//', LoginLogViewSet.as_view({'get': 'retrieve'})), path('dept_lazy_tree/', DeptViewSet.as_view({'get': 'dept_lazy_tree'})), path('clause/privacy.html', PrivacyView.as_view()), path('clause/terms_service.html', TermsServiceView.as_view()), diff --git a/backend/dvadmin/system/views/area.py b/backend/dvadmin/system/views/area.py index dfa2353202f051b7d89943428c82dc5a8fe0baee..7a19eb076cd33cd95b7bbfa60592b4c34a284cda 100644 --- a/backend/dvadmin/system/views/area.py +++ b/backend/dvadmin/system/views/area.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- +import pypinyin from django.db.models import Q from rest_framework import serializers from dvadmin.system.models import Area +from dvadmin.utils.field_permission import FieldPermissionMixin from dvadmin.utils.json_response import SuccessResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet @@ -14,13 +16,21 @@ 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() + def get_hasChild(self, instance): hasChild = Area.objects.filter(pcode=instance.code) if hasChild: return True return False + class Meta: model = Area fields = "__all__" @@ -32,12 +42,24 @@ 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__' -class AreaViewSet(CustomModelViewSet): +class AreaViewSet(CustomModelViewSet, FieldPermissionMixin): """ 地区管理接口 list:查询 @@ -48,21 +70,28 @@ class AreaViewSet(CustomModelViewSet): """ 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/dept.py b/backend/dvadmin/system/views/dept.py index 5c5105b7fe2dbbae14249d15ce86d6e1005f8c48..de48b0dbba652a91ceeadf47c9738ddc2345f5c0 100644 --- a/backend/dvadmin/system/views/dept.py +++ b/backend/dvadmin/system/views/dept.py @@ -10,6 +10,7 @@ from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from dvadmin.system.models import Dept, RoleMenuButtonPermission, Users +from dvadmin.utils.filters import DataLevelPermissionsFilter from dvadmin.utils.json_response import DetailResponse, SuccessResponse, ErrorResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet @@ -124,33 +125,7 @@ class DeptViewSet(CustomModelViewSet): data = serializer.data return SuccessResponse(data=data) - @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[]) - def dept_lazy_tree(self, request, *args, **kwargs): - parent = self.request.query_params.get('parent') - is_superuser = request.user.is_superuser - if is_superuser: - queryset = Dept.objects.values('id', 'name', 'parent') - else: - role_ids = request.user.role.values_list('id', flat=True) - data_range = RoleMenuButtonPermission.objects.filter(role__in=role_ids).values_list('data_range', flat=True) - user_dept_id = request.user.dept.id - dept_list = [user_dept_id] - data_range_list = list(set(data_range)) - for item in data_range_list: - if item in [0, 2]: - dept_list = [user_dept_id] - elif item == 1: - dept_list = Dept.recursion_all_dept(dept_id=user_dept_id) - elif item == 3: - dept_list = Dept.objects.values_list('id', flat=True) - elif item == 4: - dept_list = request.user.role.values_list('dept', flat=True) - else: - dept_list = [] - queryset = Dept.objects.filter(id__in=dept_list).values('id', 'name', 'parent') - return DetailResponse(data=queryset, msg="获取成功") - - @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[]) + @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated]) def all_dept(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) data = queryset.filter(status=True).order_by('sort').values('name', 'id', 'parent') diff --git a/backend/dvadmin/system/views/file_list.py b/backend/dvadmin/system/views/file_list.py index c122f03fc4107bbe42848e6e36546620f2c1f46f..c5956998d8fe3335a171a1b1dd0cbd8ccf6ce319 100644 --- a/backend/dvadmin/system/views/file_list.py +++ b/backend/dvadmin/system/views/file_list.py @@ -15,8 +15,8 @@ class FileSerializer(CustomModelSerializer): url = serializers.SerializerMethodField(read_only=True) def get_url(self, instance): - # return 'media/' + str(instance.url) - return instance.file_url or (f'media/{str(instance.url)}') + base_url = f"{self.request.scheme}://{self.request.get_host()}/" + return base_url + (instance.file_url or (f'media/{str(instance.url)}')) class Meta: model = FileList diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py index e04743c6d2a54b3ae1a5ed03a0a59312c9613b3b..4023a822548151efff747937b81742af8e692bc0 100644 --- a/backend/dvadmin/system/views/login.py +++ b/backend/dvadmin/system/views/login.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta from captcha.views import CaptchaStore, captcha_image from django.contrib import auth from django.contrib.auth import login +from django.db.models import Q from django.shortcuts import redirect from django.utils.translation import gettext_lazy as _ from drf_yasg import openapi @@ -83,11 +84,18 @@ class LoginSerializer(TokenObtainPairSerializer): else: self.image_code and self.image_code.delete() raise CustomValidationError("图片验证码错误") - - user = Users.objects.get(username=attrs['username']) + try: + user = Users.objects.get( + Q(username=attrs['username']) | Q(email=attrs['username']) | Q(mobile=attrs['username'])) + except Users.DoesNotExist: + raise CustomValidationError("您登录的账号不存在") + except Users.MultipleObjectsReturned: + raise CustomValidationError("您登录的账号存在多个,请联系管理员检查登录账号唯一性") if not user.is_active: raise CustomValidationError("账号已被锁定,联系管理员解锁") try: + # 必须重置用户名为username,否则使用邮箱手机号登录会提示密码错误 + attrs['username'] = user.username data = super().validate(attrs) data["name"] = self.user.name data["userId"] = self.user.id @@ -114,8 +122,8 @@ class LoginSerializer(TokenObtainPairSerializer): user.login_error_count += 1 if user.login_error_count >= 5: 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/system/views/login_log.py b/backend/dvadmin/system/views/login_log.py index d362ceaf2feac292b84d1d53d97dfed9e2135651..4d8bf74a5daef1b15f15dd152cde72bada4765c9 100644 --- a/backend/dvadmin/system/views/login_log.py +++ b/backend/dvadmin/system/views/login_log.py @@ -7,6 +7,7 @@ @Remark: 按钮权限管理 """ from dvadmin.system.models import LoginLog +from dvadmin.utils.field_permission import FieldPermissionMixin from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet @@ -22,7 +23,7 @@ class LoginLogSerializer(CustomModelSerializer): read_only_fields = ["id"] -class LoginLogViewSet(CustomModelViewSet): +class LoginLogViewSet(CustomModelViewSet, FieldPermissionMixin): """ 登录日志接口 list:查询 @@ -33,4 +34,4 @@ class LoginLogViewSet(CustomModelViewSet): """ queryset = LoginLog.objects.all() serializer_class = LoginLogSerializer - extra_filter_class = [] + # extra_filter_class = [] diff --git a/backend/dvadmin/system/views/menu_button.py b/backend/dvadmin/system/views/menu_button.py index f91839a8d794737af6ea73aef0f8e4b82d124cac..d290bb1c30a3bc54d71b1779971038c43482ef52 100644 --- a/backend/dvadmin/system/views/menu_button.py +++ b/backend/dvadmin/system/views/menu_button.py @@ -10,12 +10,14 @@ from django.db.models import F from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated -from dvadmin.system.models import MenuButton, RoleMenuButtonPermission +from dvadmin.system.models import MenuButton, RoleMenuButtonPermission, Menu from dvadmin.utils.json_response import DetailResponse, SuccessResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet + + class MenuButtonSerializer(CustomModelSerializer): """ 菜单按钮-序列化器 @@ -92,17 +94,15 @@ class MenuButtonViewSet(CustomModelViewSet): """ menu_obj = Menu.objects.filter(id=request.data['menu']).first() result_list = [ - {'menu': menu_obj.id, 'name': '新增', 'value': f'{menu_obj.component_name}:Create', 'api': f'/api{menu_obj.web_path}/', - 'method': 1}, - {'menu': menu_obj.id, 'name': '删除', 'value': f'{menu_obj.component_name}:Delete', 'api': f'/api{menu_obj.web_path}/{{id}}/', - 'method': 3}, - {'menu': menu_obj.id, 'name': '修改', 'value': f'{menu_obj.component_name}:Update', 'api': f'/api{menu_obj.web_path}/{{id}}/', - 'method': 2}, - {'menu': menu_obj.id, 'name': '查询', 'value': f'{menu_obj.component_name}:Search', 'api': f'/api{menu_obj.web_path}/', - 'method': 0}, - {'menu': menu_obj.id, 'name': '详情', 'value': f'{menu_obj.component_name}:Retrieve', 'api': f'/api{menu_obj.web_path}/{{id}}/', - 'method': 0}] + {'menu': menu_obj.id, 'name': '新增', 'value': f'{menu_obj.component_name}:Create', 'api': f'/api/{menu_obj.component_name}/', 'method': 1}, + {'menu': menu_obj.id, 'name': '删除', 'value': f'{menu_obj.component_name}:Delete', 'api': f'/api/{menu_obj.component_name}/{{id}}/', 'method': 3}, + {'menu': menu_obj.id, 'name': '编辑', 'value': f'{menu_obj.component_name}:Update', 'api': f'/api/{menu_obj.component_name}/{{id}}/', 'method': 2}, + {'menu': menu_obj.id, 'name': '查询', 'value': f'{menu_obj.component_name}:Search', 'api': f'/api/{menu_obj.component_name}/', 'method': 0}, + {'menu': menu_obj.id, 'name': '详情', 'value': f'{menu_obj.component_name}:Retrieve', 'api': f'/api/{menu_obj.component_name}/{{id}}/', 'method': 0}, + {'menu': menu_obj.id, 'name': '复制', 'value': f'{menu_obj.component_name}:Copy', 'api': f'/api/{menu_obj.component_name}/', 'method': 1}, + {'menu': menu_obj.id, 'name': '导入', 'value': f'{menu_obj.component_name}:Import', 'api': f'/api/{menu_obj.component_name}/import_data/', 'method': 1}, + {'menu': menu_obj.id, 'name': '导出', 'value': f'{menu_obj.component_name}:Export', 'api': f'/api{menu_obj.component_name}/export_data/', 'method': 1},] serializer = self.get_serializer(data=result_list, many=True) serializer.is_valid(raise_exception=True) serializer.save() - return SuccessResponse(serializer.data, msg="批量创建成功") + return SuccessResponse(serializer.data, msg="批量创建成功") \ No newline at end of file diff --git a/backend/dvadmin/system/views/message_center.py b/backend/dvadmin/system/views/message_center.py index 96b23c335d399572c956fad157d3e40effb3efa0..d46209f6a441e8fa7eafb2cf748f58c399c9b191 100644 --- a/backend/dvadmin/system/views/message_center.py +++ b/backend/dvadmin/system/views/message_center.py @@ -80,6 +80,9 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer): """ 目标用户序列化器-序列化器 """ + role_info = DynamicSerializerMethodField() + user_info = DynamicSerializerMethodField() + dept_info = DynamicSerializerMethodField() is_read = serializers.SerializerMethodField() def get_is_read(self, instance): @@ -90,6 +93,42 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer): return queryset.is_read return False + def get_role_info(self, instance, parsed_query): + roles = instance.target_role.all() + # You can do what ever you want in here + # `parsed_query` param is passed to BookSerializer to allow further querying + from dvadmin.system.views.role import RoleSerializer + serializer = RoleSerializer( + roles, + many=True, + parsed_query=parsed_query + ) + return serializer.data + + def get_user_info(self, instance, parsed_query): + users = instance.target_user.all() + # You can do what ever you want in here + # `parsed_query` param is passed to BookSerializer to allow further querying + from dvadmin.system.views.user import UserSerializer + serializer = UserSerializer( + users, + many=True, + parsed_query=parsed_query + ) + return serializer.data + + def get_dept_info(self, instance, parsed_query): + dept = instance.target_dept.all() + # You can do what ever you want in here + # `parsed_query` param is passed to BookSerializer to allow further querying + from dvadmin.system.views.dept import DeptSerializer + serializer = DeptSerializer( + dept, + many=True, + parsed_query=parsed_query + ) + return serializer.data + class Meta: model = MessageCenter fields = "__all__" diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index 604ebe24c0cbfe6cb35f6420a5ad63ef8079de64..3ee596df38d01d44393910c881c6749c422ae446 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -6,9 +6,11 @@ @Created on: 2021/6/3 003 0:30 @Remark: 菜单按钮管理 """ -from django.db.models import F, Subquery, OuterRef, Exists +from django.db.models import F, Subquery, OuterRef, Exists, BooleanField, Q, Case, Value, When +from django.db.models.functions import Coalesce from rest_framework import serializers from rest_framework.decorators import action +from rest_framework.fields import ListField from rest_framework.permissions import IsAuthenticated from dvadmin.system.models import RoleMenuButtonPermission, Menu, MenuButton, Dept, RoleMenuPermission, FieldPermission, \ @@ -172,59 +174,103 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): update_serializer_class = RoleMenuButtonPermissionCreateUpdateSerializer extra_filter_class = [] + # @action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated]) + # def get_role_premission(self, request): + # """ + # 角色授权获取: + # :param request: role + # :return: menu,btns,columns + # """ + # params = request.query_params + # is_superuser = request.user.is_superuser + # if is_superuser: + # queryset = Menu.objects.filter(status=1, is_catalog=True).values('name', 'id').all() + # else: + # role_id = request.user.role.values_list('id', flat=True) + # menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('menu__id', flat=True) + # queryset = Menu.objects.filter(status=1, is_catalog=True, id__in=menu_list).values('name', 'id').all() + # serializer = RoleMenuSerializer(queryset, many=True, request=request) + # data = serializer.data + # return DetailResponse(data=data) + @action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated]) - def get_role_premission(self, request): - """ - 角色授权获取: - :param request: role - :return: menu,btns,columns - """ + def get_role_permission(self, request): params = request.query_params - role = params.get('role', None) - if role is None: - return ErrorResponse(msg="未获取到角色信息") + # 需要授权的角色信息 + current_role = params.get('role', None) + # 当前登录用户的角色 + role_list = request.user.role.values_list('id', flat=True) + if current_role is None: + return ErrorResponse(msg='参数错误') is_superuser = request.user.is_superuser if is_superuser: - queryset = Menu.objects.filter(status=1, is_catalog=True).values('name', 'id').all() + menu_queryset = Menu.objects.prefetch_related('menuPermission').prefetch_related( + 'menufield_set') else: - role_id = request.user.role.values_list('id', flat=True) - menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id', flat=True) - queryset = Menu.objects.filter(status=1, is_catalog=True, id__in=menu_list).values('name', 'id').all() - serializer = RoleMenuSerializer(queryset, many=True, request=request) - data = serializer.data - return DetailResponse(data=data) - # data = [] - # if is_superuser: - # queryset = Menu.objects.filter(status=1, is_catalog=False).values('name', 'id').all() - # else: - # role_id = request.user.role.values_list('id', flat=True) - # menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id', flat=True) - # queryset = Menu.objects.filter(status=1, is_catalog=False, id__in=menu_list).values('name', 'id') - # for item in queryset: - # parent_list = Menu.get_all_parent(item['id']) - # names = [d["name"] for d in parent_list] - # completeName = "/".join(names) - # isCheck = RoleMenuPermission.objects.filter( - # menu__id=item['id'], - # role__id=role, - # ).exists() - # mbCheck = RoleMenuButtonPermission.objects.filter( - # menu_button=OuterRef("pk"), - # role__id=role, - # ) - # btns = MenuButton.objects.filter( - # menu__id=item['id'], - # ).annotate(isCheck=Exists(mbCheck)).values('id', 'name', 'value', 'isCheck', - # data_range=F('menu_button_permission__data_range')) - # dicts = { - # 'name': completeName, - # 'id': item['id'], - # 'isCheck': isCheck, - # 'btns': btns, - # - # } - # data.append(dicts) - # return DetailResponse(data=data) + role_id_list = request.user.role.values_list('id', flat=True) + menu_list = RoleMenuPermission.objects.filter(role__in=role_id_list).values_list('menu__id', flat=True) + + # 当前角色已授权的菜单 + menu_queryset = Menu.objects.filter(id__in=menu_list).prefetch_related('menuPermission').prefetch_related( + 'menufield_set') + result = [] + for menu_item in menu_queryset: + isCheck = RoleMenuPermission.objects.filter( + menu_id=menu_item.id, + role_id=current_role + ).exists() + dicts = { + 'name': menu_item.name, + 'id': menu_item.id, + 'parent': menu_item.parent.id if menu_item.parent else None, + 'isCheck': isCheck, + 'btns': [], + 'columns': [] + } + for mb_item in menu_item.menuPermission.all(): + rolemenubuttonpermission_queryset = RoleMenuButtonPermission.objects.filter( + menu_button_id=mb_item.id, + role_id=current_role + ).first() + dicts['btns'].append( + { + 'id': mb_item.id, + 'name': mb_item.name, + 'value': mb_item.value, + 'data_range': rolemenubuttonpermission_queryset.data_range + if rolemenubuttonpermission_queryset + else None, + 'isCheck': bool(rolemenubuttonpermission_queryset), + 'dept': rolemenubuttonpermission_queryset.dept.all().values_list('id', flat=True) + if rolemenubuttonpermission_queryset + else [], + } + ) + for column_item in menu_item.menufield_set.all(): + # 需要授权角色已拥有的列权限 + fieldpermission_queryset = column_item.menu_field.filter(role_id=current_role).first() + is_query = fieldpermission_queryset.is_query if fieldpermission_queryset else False + is_create = fieldpermission_queryset.is_create if fieldpermission_queryset else False + is_update = fieldpermission_queryset.is_update if fieldpermission_queryset else False + # 当前登录用户角色可分配的列权限 + fieldpermission_queryset_disabled = column_item.menu_field.filter(role_id__in=role_list).first() + disabled_query = fieldpermission_queryset_disabled.is_query if fieldpermission_queryset_disabled else True + disabled_create = fieldpermission_queryset_disabled.is_create if fieldpermission_queryset_disabled else True + disabled_update = fieldpermission_queryset_disabled.is_update if fieldpermission_queryset_disabled else True + + dicts['columns'].append({ + 'id': column_item.id, + 'field_name': column_item.field_name, + 'title': column_item.title, + 'is_query': is_query, + 'is_create': is_create, + 'is_update': is_update, + 'disabled_query': False if is_superuser else not disabled_query, + 'disabled_create': False if is_superuser else not disabled_create, + 'disabled_update': False if is_superuser else not disabled_update, + }) + result.append(dicts) + return DetailResponse(data=result) @action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated]) def set_role_premission(self, request, pk): @@ -238,27 +284,21 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): RoleMenuPermission.objects.filter(role=pk).delete() RoleMenuButtonPermission.objects.filter(role=pk).delete() for item in body: - for menu in item["menus"]: - if menu.get('isCheck'): - menu_parent = Menu.get_all_parent(menu.get('id')) - role_menu_permission_list = [] - for d in menu_parent: - role_menu_permission_list.append(RoleMenuPermission(role_id=pk, menu_id=d["id"])) - RoleMenuPermission.objects.bulk_create(role_menu_permission_list) - # RoleMenuPermission.objects.create(role_id=pk, menu_id=menu.get('id')) - for btn in menu.get('btns'): - if btn.get('isCheck'): - data_range = btn.get('data_range', 0) or 0 - instance = RoleMenuButtonPermission.objects.create(role_id=pk, menu_button_id=btn.get('id'), - data_range=data_range) - instance.dept.set(btn.get('dept', [])) - for col in menu.get('columns'): - FieldPermission.objects.update_or_create(role_id=pk, field_id=col.get('id'), - defaults={ - 'is_query': col.get('is_query'), - 'is_create': col.get('is_create'), - 'is_update': col.get('is_update') - }) + if item.get('isCheck'): + RoleMenuPermission.objects.create(role_id=pk, menu_id=item["id"]) + for btn in item.get('btns'): + if btn.get('isCheck'): + data_range = btn.get('data_range', 0) or 0 + instance = RoleMenuButtonPermission.objects.create(role_id=pk, menu_button_id=btn.get('id'), + data_range=data_range) + instance.dept.set(btn.get('dept', [])) + for col in item.get('columns'): + FieldPermission.objects.update_or_create(role_id=pk, field_id=col.get('id'), + defaults={ + 'is_query': col.get('is_query'), + 'is_create': col.get('is_create'), + 'is_update': col.get('is_update') + }) return DetailResponse(msg="授权成功") @action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated]) @@ -291,86 +331,45 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): is_superuser = request.user.is_superuser if is_superuser: data = [ - { - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 1, - "label": '本部门及以下数据权限' - }, - { - "value": 2, - "label": '本部门数据权限' - }, - { - "value": 3, - "label": '全部数据权限' - }, - { - "value": 4, - "label": '自定义数据权限' - } + {"value": 0, "label": '仅本人数据权限'}, + {"value": 1, "label": '本部门及以下数据权限'}, + {"value": 2, "label": '本部门数据权限'}, + {"value": 3, "label": '全部数据权限'}, + {"value": 4, "label": '自定义数据权限'} ] return DetailResponse(data=data) else: - data = [] + params = request.query_params + data = [{"value": 0, "label": '仅本人数据权限'}] role_list = request.user.role.values_list('id', flat=True) - if params := request.query_params: - if menu_button_id := params.get('menu_button', None): - role_queryset = RoleMenuButtonPermission.objects.filter( - role__in=role_list, menu_button__id=menu_button_id - ).values_list('data_range', flat=True) - data_range_list = list(set(role_queryset)) - for item in data_range_list: - if item == 0: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }] - elif item == 1: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, { - "value": 1, - "label": '本部门及以下数据权限' - }, - { - "value": 2, - "label": '本部门数据权限' - }] - elif item == 2: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 2, - "label": '本部门数据权限' - }] - elif item == 3: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 3, - "label": '全部数据权限' - }, ] - elif item == 4: - data = [{ - "value": 0, - "label": '仅本人数据权限' - }, - { - "value": 4, - "label": '自定义数据权限' - }] - else: - data = [] - return DetailResponse(data=data) - return ErrorResponse(msg="参数错误") + # 权限页面进入初始化获取所有的数据权限范围 + role_queryset = RoleMenuButtonPermission.objects.filter( + role__in=role_list + ).values_list('data_range', flat=True) + # 通过按钮小齿轮获取指定按钮的权限 + if menu_button_id := params.get('menu_button', None): + role_queryset = RoleMenuButtonPermission.objects.filter( + role__in=role_list, menu_button__id=menu_button_id + ).values_list('data_range', flat=True) + + data_range_list = list(set(role_queryset)) + for item in data_range_list: + if item == 0: + data = data + elif item == 1: + data.extend([ + {"value": 1, "label": '本部门及以下数据权限'}, + {"value": 2, "label": '本部门数据权限'} + ]) + elif item == 2: + data.extend([{"value": 2, "label": '本部门数据权限'}]) + elif item == 3: + data.extend([{"value": 3, "label": '全部数据权限'}]) + elif item == 4: + data.extend([{"value": 4, "label": '自定义数据权限'}]) + else: + data = [] + return DetailResponse(data=data) @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def role_to_dept_all(self, request): @@ -379,23 +378,23 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): :param request: :return: """ - params = request.query_params is_superuser = request.user.is_superuser - if is_superuser: - queryset = Dept.objects.values('id', 'name', 'parent') - else: - if not params: - return ErrorResponse(msg="参数错误") - menu_button = params.get('menu_button') - if menu_button is None: - return ErrorResponse(msg="参数错误") - role_list = request.user.role.values_list('id', flat=True) - queryset = RoleMenuButtonPermission.objects.filter(role__in=role_list, menu_button=None).values( - dept_id=F('dept__id'), - name=F('dept__name'), - parent=F('dept__parent') - ) - return DetailResponse(data=queryset) + params = request.query_params + # 当前登录用户的角色 + role_list = request.user.role.values_list('id', flat=True) + + menu_button_id = params.get('menu_button') + # 当前登录用户角色可以分配的自定义部门权限 + dept_checked_disabled = RoleMenuButtonPermission.objects.filter( + role_id__in=role_list, menu_button_id=menu_button_id + ).values_list('dept', flat=True) + dept_list = Dept.objects.values('id', 'name', 'parent') + + data = [] + for dept in dept_list: + dept["disabled"] = False if is_superuser else dept["id"] not in dept_checked_disabled + data.append(dept) + return DetailResponse(data=data) @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def menu_to_button(self, request): diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py index c6c002e8817abb75a52e8dea166bbf200f51cfdb..116d71f1138f09512b30c2bb43f39c943aeee270 100644 --- a/backend/dvadmin/system/views/user.py +++ b/backend/dvadmin/system/views/user.py @@ -119,7 +119,6 @@ class UserUpdateSerializer(CustomModelSerializer): """ 更改激活状态 """ - print(111, value) if value: self.initial_data["login_error_count"] = 0 return value @@ -407,9 +406,12 @@ class UserViewSet(CustomModelViewSet): queryset = self.filter_queryset(self.get_queryset()) else: queryset = self.filter_queryset(self.get_queryset()) + # print(queryset.values('id','name','dept__id')) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True, request=request) + # print(serializer.data) 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/utils/field_permission.py b/backend/dvadmin/utils/field_permission.py index 20b4cb95310b7951531ac48f22ae58b833591ada..7c259f6820b2040171c9db8f2d06b25b35ea9d95 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/middleware.py b/backend/dvadmin/utils/middleware.py index f4717a4dd231b278df272c77b2579b4388881ae6..6029c5a74d7b882d7c213a834ead72097b8b5054 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 fb0c2ba70a5035c540634a773fe25815246e50d6..09d13f1a1943194620346921905bf5b7dac8e6dd 100644 --- a/backend/dvadmin/utils/models.py +++ b/backend/dvadmin/utils/models.py @@ -6,13 +6,14 @@ @Created on: 2021/5/31 031 22:08 @Remark: 公共基础model类 """ +from datetime import datetime from importlib import import_module +from application import settings from django.apps import apps -from django.db import models from django.conf import settings - -from application import settings +from django.db import models +from rest_framework.request import Request table_prefix = settings.TABLE_PREFIX # 数据库表名前缀 @@ -87,6 +88,111 @@ class CoreModel(models.Model): verbose_name = '核心模型' verbose_name_plural = verbose_name + def get_request_user(self, request: Request): + if getattr(request, "user", None): + return request.user + return None + + def get_request_user_id(self, request: Request): + if getattr(request, "user", None): + return getattr(request.user, "id", None) + return None + + def get_request_user_name(self, request: Request): + if getattr(request, "user", None): + return getattr(request.user, "name", None) + return None + + def get_request_user_username(self, request: Request): + if getattr(request, "user", None): + return getattr(request.user, "username", None) + return None + + def common_insert_data(self, request: Request): + data = { + 'create_datetime': datetime.now(), + 'creator': self.get_request_user(request) + } + return {**data, **self.common_update_data(request)} + + def common_update_data(self, request: Request): + return { + 'update_datetime': datetime.now(), + 'modifier': self.get_request_user_username(request) + } + + exclude_fields = [ + '_state', + 'pk', + 'id', + 'create_datetime', + 'update_datetime', + 'creator', + 'creator_id', + 'creator_pk', + 'creator_name', + 'modifier', + 'modifier_id', + 'modifier_pk', + 'modifier_name', + 'dept_belong_id', + ] + + def get_exclude_fields(self): + return self.exclude_fields + + def get_all_fields(self): + return self._meta.fields + + def get_all_fields_names(self): + return [field.name for field in self.get_all_fields()] + + def get_need_fields_names(self): + return [field.name for field in self.get_all_fields() if field.name not in self.exclude_fields] + + def to_data(self): + """将模型转化为字典(去除不包含字段)(注意与to_dict_data区分)。 + """ + res = {} + for field in self.get_need_fields_names(): + field_value = getattr(self, field) + res[field] = field_value.id if (issubclass(field_value.__class__, CoreModel)) else field_value + return res + + @property + def DATA(self): + return self.to_data() + + def to_dict_data(self): + """需要导出的字段(去除不包含字段)(注意与to_data区分) + """ + return {field: getattr(self, field) for field in self.get_need_fields_names()} + + @property + def DICT_DATA(self): + return self.to_dict_data() + + def insert(self, request): + """插入模型 + """ + assert self.pk is None, f'模型{self.__class__.__name__}还没有保存到数据中,不能手动指定ID' + validated_data = {**self.common_insert_data(request), **self.DICT_DATA} + return self.__class__._default_manager.create(**validated_data) + + def update(self, request, update_data: dict[str, any] = None): + """更新模型 + """ + assert isinstance(update_data, dict), 'update_data必须为字典' + validated_data = {**self.common_insert_data(request), **update_data} + for key, value in validated_data.items(): + # 不允许修改id,pk,uuid字段 + if key in ['id', 'pk', 'uuid']: + continue + if hasattr(self, key): + setattr(self, key, value) + self.save() + return self + def get_all_models_objects(model_name=None): """ @@ -97,16 +203,9 @@ def get_all_models_objects(model_name=None): if not settings.ALL_MODELS_OBJECTS: all_models = apps.get_models() for item in list(all_models): - table = { - "tableName": item._meta.verbose_name, - "table": item.__name__, - "tableFields": [] - } + table = {"tableName": item._meta.verbose_name, "table": item.__name__, "tableFields": []} for field in item._meta.fields: - fields = { - "title": field.verbose_name, - "field": field.name - } + fields = {"title": field.verbose_name, "field": field.name} table['tableFields'].append(fields) settings.ALL_MODELS_OBJECTS.setdefault(item.__name__, {"table": table, "object": item}) if model_name: @@ -117,25 +216,20 @@ 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: if model.__name__ == 'AbstractUser': continue - fields = [ - {'title': field.verbose_name, 'name': field.name, 'object': field} - for field in model._meta.fields - ] - model_list.append({ - 'app': app_name, - 'verbose': model._meta.verbose_name, - 'model': model.__name__, - 'object': model, - 'fields': fields - }) + fields = [{'title': field.verbose_name, 'name': field.name, 'object': field} for field in model._meta.fields] + model_list.append({'app': app_name, 'verbose': model._meta.verbose_name, 'model': model.__name__, 'object': model, 'fields': fields}) return model_list diff --git a/backend/main.py b/backend/main.py index 2408243cdb5fc1c9320565f1d680df38a0c3194e..c0419d9918b273be725e99f86d2a9e86fdd75401 100644 --- a/backend/main.py +++ b/backend/main.py @@ -9,5 +9,9 @@ from application.settings import LOGGING if __name__ == '__main__': multiprocessing.freeze_support() - uvicorn.run("application.asgi:application", reload=False, host="0.0.0.0", port=8000, workers=4, + workers = 4 + if os.sys.platform.startswith('win'): + # Windows操作系统 + workers = None + uvicorn.run("application.asgi:application", reload=False, host="0.0.0.0", port=8000, workers=workers, log_config=LOGGING) diff --git a/backend/requirements.txt b/backend/requirements.txt index 6309e7c11080e7c0871da7f9e40d5dd206f9e52e..2e7c6cb6131855497d1be97d0b97f21a4e6eea7c 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ Django==4.2.7 django-comment-migrate==0.1.7 -django-cors-headers==4.3.0 +django-cors-headers==4.4.0 django-filter==23.3 django-ranged-response==0.2.0 djangorestframework==3.14.0 diff --git a/backend/static/logo.icns b/backend/static/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..99572efcb34019f76a56baed454a7900e63505bf Binary files /dev/null and b/backend/static/logo.icns differ diff --git a/docker_env/django/Dockerfile b/docker_env/django/Dockerfile index 427ed0c846350b848f28b2588f13f877abc7e918..cad6fed52a59efcc8b5de3ce61e3f8aa12d88131 100644 --- a/docker_env/django/Dockerfile +++ b/docker_env/django/Dockerfile @@ -6,4 +6,4 @@ RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" | RUN sed -i "s|DATABASE_HOST = '127.0.0.1'|DATABASE_HOST = '177.10.0.1'|g" ./conf/env.py RUN sed -i "s|REDIS_HOST = '127.0.0.1'|REDIS_HOST = '177.10.0.1'|g" ./conf/env.py RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt -CMD ["/backend/docker_start.sh"] +CMD ["sh","docker_start.sh"] diff --git a/docker_env/nginx/my-80.conf b/docker_env/nginx/my-80.conf index e50bdc4697cbe418cc227e1b9e7fe55b334e6322..98d33bd98b9c50792e91a431f43a7ad6996a575d 100644 --- a/docker_env/nginx/my-80.conf +++ b/docker_env/nginx/my-80.conf @@ -7,6 +7,10 @@ server { index index.html index.htm; root /usr/share/nginx/html; try_files $uri $uri/ /index.html; + # 禁止缓存html文件,避免前端页面不及时更新,需要用户手动刷新的情况 + if ($request_uri ~* "^/$|^/index.html|^/index.htm") { + add_header Cache-Control "no-store"; + } } location ~ ^/api/ { diff --git a/docker_env/nginx/my.conf b/docker_env/nginx/my.conf index 178d9793e1db511bc38ee372c104be6ffaacc14e..dd6b3337a19eb93ffdc786635c11995a823a1b06 100644 --- a/docker_env/nginx/my.conf +++ b/docker_env/nginx/my.conf @@ -11,6 +11,10 @@ server { real_ip_header X-Forwarded-For; root /usr/share/nginx/html; index index.html index.php index.htm; + # 禁止缓存html文件,避免前端页面不及时更新,需要用户手动刷新的情况 + if ($request_uri ~* "^/$|^/index.html|^/index.htm") { + add_header Cache-Control "no-store"; + } } location /api/ { diff --git a/web/.gitignore b/web/.gitignore index aa1baf223b2cc0a3f75b7a01f6792b7c780de240..8cef9186824172b87665ec830e8e4483b0fd0885 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -21,3 +21,5 @@ pnpm-debug.log* *.njsproj *.sln *.sw? +# 构建版本文件,无需上传git +public/version-build diff --git a/web/src/components/tableSelector/index.vue b/web/src/components/tableSelector/index.vue index 7ad3cad84a0c5a695606a22a0642f41d621e97f2..8e8c91a9dc56db58087acc8c4ae2af414919c625 100644 --- a/web/src/components/tableSelector/index.vue +++ b/web/src/components/tableSelector/index.vue @@ -1,203 +1,211 @@ diff --git a/web/src/i18n/pages/login/zh-cn.ts b/web/src/i18n/pages/login/zh-cn.ts index 6ab9813aa1e51a68b70d35a8b83c0fb2e47c67b9..07fd82eb54ce941bc4bb76d106d24d25a0ce3720 100644 --- a/web/src/i18n/pages/login/zh-cn.ts +++ b/web/src/i18n/pages/login/zh-cn.ts @@ -9,7 +9,7 @@ export default { two4: '友情链接', }, account: { - accountPlaceholder1: '请输入登录账号', + accountPlaceholder1: '请输入登录账号/邮箱/手机号', accountPlaceholder2: '请输入登录密码', accountPlaceholder3: '请输入验证码', accountBtnText: '登 录', diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 3f564eb2fc6c389739b2138569914211d774244f..53aa3d962499efb550acd74b1155866c3419fa52 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -13,6 +13,7 @@ import {initBackEndControlRoutes, setRouters} from '/@/router/backEnd'; import {useFrontendMenuStore} from "/@/stores/frontendMenu"; import {useTagsViewRoutes} from "/@/stores/tagsViewRoutes"; import {toRaw} from "vue"; +import {checkVersion} from "/@/utils/upgrade"; /** * 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。 @@ -95,6 +96,8 @@ export function formatTwoStageRoutes(arr: any) { // 路由加载前 router.beforeEach(async (to, from, next) => { + // 检查浏览器本地版本与线上版本是否一致,判断是否需要刷新页面进行更新 + await checkVersion() NProgress.configure({showSpinner: false}); if (to.meta.title) NProgress.start(); const token = Session.get('token'); diff --git a/web/src/utils/columnPermission.ts b/web/src/utils/columnPermission.ts index 4a9d9f1d766e157fd8a61e1124980754ca1643f0..7ef06de0ba44a26f93a4a4b709697964ff3b4863 100644 --- a/web/src/utils/columnPermission.ts +++ b/web/src/utils/columnPermission.ts @@ -24,31 +24,16 @@ export const handleColumnPermission = async (func: Function, crudOptions: any,ex const columns = crudOptions.columns; const excludeColumns = ['_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/utils/loading.ts b/web/src/utils/loading.ts index 5fd020c89f349c651599907f949f50046535c4a0..9dce4bd451213c76817968dd14eb9dc0f09bd224 100644 --- a/web/src/utils/loading.ts +++ b/web/src/utils/loading.ts @@ -1,5 +1,7 @@ import { nextTick } from 'vue'; import '/@/theme/loading.scss'; +import { showUpgrade } from "/@/utils/upgrade"; + /** * 页面全局 Loading @@ -9,6 +11,8 @@ import '/@/theme/loading.scss'; export const NextLoading = { // 创建 loading start: () => { + // 显示升级提示 + showUpgrade() const bodys: Element = document.body; const div = document.createElement('div'); div.setAttribute('class', 'loading-next'); diff --git a/web/src/utils/service.ts b/web/src/utils/service.ts index 0dfe45aa67df7515f662aceb37083018b16a52b6..3775e26612c42553bae8534aacccd3b67b34140b 100644 --- a/web/src/utils/service.ts +++ b/web/src/utils/service.ts @@ -82,7 +82,7 @@ function createService() { ElMessageBox.alert(dataAxios.msg, '提示', { confirmButtonText: 'OK', callback: (action: Action) => { - window.location.reload(); + // window.location.reload(); }, }); errorCreate(`${dataAxios.msg}: ${response.config.url}`); diff --git a/web/src/utils/upgrade.ts b/web/src/utils/upgrade.ts new file mode 100644 index 0000000000000000000000000000000000000000..a571863db809e1fd2110b25400e891117f7995ff --- /dev/null +++ b/web/src/utils/upgrade.ts @@ -0,0 +1,55 @@ +import axios from "axios"; +import * as process from "process"; +import {Local, Session} from '/@/utils/storage'; +import {ElNotification} from "element-plus"; +import fs from "fs"; + +// 是否显示升级提示信息框 +const IS_SHOW_UPGRADE_SESSION_KEY = 'isShowUpgrade'; +const VERSION_KEY = 'DVADMIN3_VERSION' +const VERSION_FILE_NAME = 'version-build' + +export function showUpgrade () { + const isShowUpgrade = Session.get(IS_SHOW_UPGRADE_SESSION_KEY) ?? false + if (isShowUpgrade) { + Session.remove(IS_SHOW_UPGRADE_SESSION_KEY) + ElNotification({ + title: '新版本升级', + message: "检测到系统新版本,正在更新中!不用担心,更新很快的哦!", + type: 'success', + duration: 5000, + }); + } +} + +// 生产环境前端版本校验, +export async function checkVersion(){ + if (process.env.NODE_ENV === 'development') { + // 开发环境无需校验前端版本 + return + } + // 获取线上版本号 t为时间戳,防止缓存 + await axios.get(`/${VERSION_FILE_NAME}?t=${new Date().getTime()}`).then(res => { + const {status, data} = res || {} + if (status === 200) { + // 获取当前版本号 + const localVersion = Local.get(VERSION_KEY) + // 将当前版本号持久缓存至本地 + Local.set(VERSION_KEY, data) + // 当用户本地存在版本号并且和线上版本号不一致时,进行页面刷新操作 + if (localVersion && localVersion !== data) { + // 本地缓存版本号和线上版本号不一致,弹出升级提示框 + // 此处无法直接使用消息框进行提醒,因为 window.location.reload()会导致消息框消失,将在loading页面判断是否需要显示升级提示框 + Session.set(IS_SHOW_UPGRADE_SESSION_KEY, true) + window.location.reload() + + } + } + }) +} + +export function generateVersionFile (){ + // 生成版本文件到public目录下version文件中 + const version = `${process.env.npm_package_version}.${new Date().getTime()}`; + fs.writeFileSync(`public/${VERSION_FILE_NAME}`, version); +} diff --git a/web/src/views/plugins/dvadmin_form_flow/install.js b/web/src/views/plugins/dvadmin_form_flow/install.js deleted file mode 100644 index 02154d40947dfd04d53e9c8099c57c760920cdb5..0000000000000000000000000000000000000000 --- a/web/src/views/plugins/dvadmin_form_flow/install.js +++ /dev/null @@ -1,73 +0,0 @@ -import axios from 'axios' - -import VFormDesigner from '@/components/form-designer/index.vue' -import VFormRender from '@/components/form-render/index.vue' - -import Draggable from '@/../lib/vuedraggable/dist/vuedraggable.umd.js' -import {registerIcon} from '@/utils/el-icons' -import 'virtual:svg-icons-register' -import '@/iconfont/iconfont.css' - -import ContainerWidgets from '@/components/form-designer/form-widget/container-widget/index' -import ContainerItems from '@/components/form-render/container-item/index' - -import { addDirective } from '@/utils/directive' -import { installI18n } from '@/utils/i18n' -import { loadExtension } from '@/extension/extension-loader' - - -VFormDesigner.install = function (app) { - addDirective(app) - installI18n(app) - loadExtension(app) - - app.use(ContainerWidgets) - app.use(ContainerItems) - - registerIcon(app) - app.component('draggable', Draggable) - app.component(VFormDesigner.name, VFormDesigner) -} - -VFormRender.install = function (app) { - installI18n(app) - loadExtension(app) - - app.use(ContainerItems) - - registerIcon(app) - app.component(VFormRender.name, VFormRender) -} - -const components = [ - VFormDesigner, - VFormRender -] - -const install = (app) => { - addDirective(app) - installI18n(app) - loadExtension(app) - - app.use(ContainerWidgets) - app.use(ContainerItems) - - registerIcon(app) - app.component('draggable', Draggable) - - components.forEach(component => { - app.component(component.name, component) - }) - - window.axios = axios -} - -if (typeof window !== 'undefined' && window.Vue) { /* scriptʽʱֵaxios */ - //window.axios = axios -} - -export default { - install, - VFormDesigner, - VFormRender -} diff --git a/web/src/views/plugins/dvadmin_form_flow/src/index.ts b/web/src/views/plugins/dvadmin_form_flow/src/index.ts deleted file mode 100644 index c57cc05a00d2cf0adadfdae3cad6b328ee6bb7de..0000000000000000000000000000000000000000 --- a/web/src/views/plugins/dvadmin_form_flow/src/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -import DVAFormDesigner from './components/DVAFormDesigner.vue' - -// 浽һ -const components = [ - DVAFormDesigner -] - -// install -const install = function (Vue) { - - if (install.installed) return - install.installed = true - // бעȫ - components.map(component => { - Vue.component(component.name, component) //component.name ˴ʹõvueļе name - }) -} - -if (typeof window !== 'undefined' && window.Vue) { - install(window.Vue) -} - -export default { - // Ķ߱һ install - install, - // б - ...components -} diff --git a/web/src/views/system/areas/api.ts b/web/src/views/system/areas/api.ts index bad818bd676999dbd0c26846a479e064ae4a074b..f2560589d1d773c6bca3d495285634d263812f18 100644 --- a/web/src/views/system/areas/api.ts +++ b/web/src/views/system/areas/api.ts @@ -39,3 +39,9 @@ export function DelObj(id: DelReq) { data: { id }, }); } +export function GetPermission() { + return request({ + url: apiPrefix + 'field_permission/', + method: 'get', + }); +} diff --git a/web/src/views/system/areas/crud.tsx b/web/src/views/system/areas/crud.tsx index 01d1860253220ac98200c98e4a6446c79a5daab5..1a1e47b0c4f13b651617bc1e1689e61ca076a349 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/areas/index.vue b/web/src/views/system/areas/index.vue index 51c6c772ee048c1fa0189870ea17bbef276042f9..8201f7ec3e79d36b0fdf70f0150c2096f6584604 100644 --- a/web/src/views/system/areas/index.vue +++ b/web/src/views/system/areas/index.vue @@ -5,14 +5,21 @@ diff --git a/web/src/views/system/dept/components/DeptUserCom/api.ts b/web/src/views/system/dept/components/DeptUserCom/api.ts index 28602c720f22c51b0c7d4f3405fecb4c51d316cc..b395c39d5634d52c7c11d5c8473bc6cc589a0e49 100644 --- a/web/src/views/system/dept/components/DeptUserCom/api.ts +++ b/web/src/views/system/dept/components/DeptUserCom/api.ts @@ -5,13 +5,13 @@ type GetListType = PageQuery & { show_all: string }; export const apiPrefix = '/api/system/user/'; -export function GetDept(query: PageQuery) { - return request({ - url: '/api/system/dept/dept_lazy_tree/', - method: 'get', - params: query, - }); -} +// export function GetDept(query: PageQuery) { +// return request({ +// url: '/api/system/dept/dept_all/', +// method: 'get', +// params: query, +// }); +// } export function GetList(query: GetListType) { return request({ diff --git a/web/src/views/system/dept/components/DeptUserCom/crud.tsx b/web/src/views/system/dept/components/DeptUserCom/crud.tsx index 8bb2b3cfb0e5fb496ab709bebcb180d884f18ba0..bcee5b09a5fad3f46df39fb01929ae106d585dcc 100644 --- a/web/src/views/system/dept/components/DeptUserCom/crud.tsx +++ b/web/src/views/system/dept/components/DeptUserCom/crud.tsx @@ -220,7 +220,10 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp label: 'name', }), column: { - minWidth: 150, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + return row.dept_name_all + } }, form: { rules: [ @@ -259,7 +262,11 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp label: 'name', }), column: { - minWidth: 100, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + const values = row.role_info.map((item:any) => item.name); + return values.join(',') + } }, form: { rules: [ @@ -382,6 +389,10 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp form: { show: false, }, + column:{ + width:150, + showOverflowTooltip: true, + } }, }, }, diff --git a/web/src/views/system/log/loginLog/api.ts b/web/src/views/system/log/loginLog/api.ts index defefc49441777cb5a1c1768fdd3fd7d7a6718f8..273aa90b72a1e008b7aba2ea1c530376089018d1 100644 --- a/web/src/views/system/log/loginLog/api.ts +++ b/web/src/views/system/log/loginLog/api.ts @@ -39,3 +39,10 @@ export function DelObj(id: DelReq) { data: { id }, }); } + +export function GetPermission() { + return request({ + url: apiPrefix + 'field_permission/', + method: 'get', + }); +} diff --git a/web/src/views/system/log/loginLog/index.vue b/web/src/views/system/log/loginLog/index.vue index 618e6cf0ca44eaad564d0be3c06995593557af06..924429bbd6c071e3c17e440b3171719b3bba2380 100644 --- a/web/src/views/system/log/loginLog/index.vue +++ b/web/src/views/system/log/loginLog/index.vue @@ -5,14 +5,21 @@ diff --git a/web/src/views/system/menu/components/MenuFormCom/index.vue b/web/src/views/system/menu/components/MenuFormCom/index.vue index 5fa4abae0a90b584b2e822b5c5c6dc2595069740..d958a62e8d72d6823b2077a018158bfa9564bb50 100644 --- a/web/src/views/system/menu/components/MenuFormCom/index.vue +++ b/web/src/views/system/menu/components/MenuFormCom/index.vue @@ -10,21 +10,12 @@ - + - + @@ -35,12 +26,14 @@ - + - + @@ -48,46 +41,45 @@ - + - + + + + + + + + + + + - - - - - - - - - - - +
- + - + @@ -96,7 +88,8 @@ - +
@@ -111,6 +104,7 @@ diff --git a/web/src/views/system/role/components/PermissionComNew/api.ts b/web/src/views/system/role/components/PermissionComNew/api.ts index be37ef5bc7522096cc6a1909c2276ac22c334c71..0243ab6930f90481c66d157e880fff1a2cab9f02 100644 --- a/web/src/views/system/role/components/PermissionComNew/api.ts +++ b/web/src/views/system/role/components/PermissionComNew/api.ts @@ -1,15 +1,17 @@ import { request } from "/@/utils/service"; - +import XEUtils from "xe-utils"; /** * 获取角色的授权列表 * @param roleId * @param query */ -export function getRolePremission(query:object) { +export function getRolePermission(query:object) { return request({ - url: '/api/system/role_menu_button_permission/get_role_premission/', + url: '/api/system/role_menu_button_permission/get_role_permission/', method: 'get', params:query + }).then((res:any)=>{ + return XEUtils.toArrayTree(res.data, {key: 'id', parentKey: 'parent',children: 'children',strict: false}) }) } @@ -26,16 +28,25 @@ export function setRolePremission(roleId:any,data:object) { }) } -export function getDataPermissionRange() { +export function getDataPermissionRange(query:object) { return request({ url: '/api/system/role_menu_button_permission/data_scope/', method: 'get', + params:query }) } -export function getDataPermissionDept() { + +export function getDataPermissionRangeAll() { + return request({ + url: '/api/system/role_menu_button_permission/data_scope/', + method: 'get', + }) +} +export function getDataPermissionDept(query:object) { return request({ url: '/api/system/role_menu_button_permission/role_to_dept_all/', - method: 'get' + method: 'get', + params:query }) } diff --git a/web/src/views/system/role/components/PermissionComNew/index.vue b/web/src/views/system/role/components/PermissionComNew/index.vue index 058f5fc26cd4333b626896f3807b792eb3f030e5..b1da73fd3488d91ba0a287e9869d7f97f9c4b8cb 100644 --- a/web/src/views/system/role/components/PermissionComNew/index.vue +++ b/web/src/views/system/role/components/PermissionComNew/index.vue @@ -1,392 +1,451 @@ diff --git a/web/src/views/system/role/components/PermissionComNew/types.ts b/web/src/views/system/role/components/PermissionComNew/types.ts index 64268103c3cb28a424b365aa2f26ea3705f07c03..5afc5c0de1d3fb3c6e0f449971ea0d4ba4a05885 100644 --- a/web/src/views/system/role/components/PermissionComNew/types.ts +++ b/web/src/views/system/role/components/PermissionComNew/types.ts @@ -1,36 +1,29 @@ export interface DataPermissionRangeType { - label: string; - value: number; + label: string; + value: number; } export interface CustomDataPermissionDeptType { - id: number; - name: string; - patent: number; - children: CustomDataPermissionDeptType[] + id: number; + name: string; + patent: number; + children: CustomDataPermissionDeptType[]; } export interface CustomDataPermissionMenuType { - id: number; - name: string; - is_catalog: boolean; - menuPermission: { id: number; name: string; value: string }[] | null; - columns: { id: number; name: string; title: string }[] | null; - children: CustomDataPermissionMenuType[] -} - -export interface MenusType{ - id: string; - name: string; - isCheck: boolean; - radio: string; - btns: { id:number,name: string; value: string; isCheck: boolean; data_range: number; dept:object; name:string }[]; - columns: { [key: string]: boolean | string; }[] + id: number; + name: string; + is_catalog: boolean; + menuPermission: { id: number; name: string; value: string }[] | null; + columns: { id: number; name: string; title: string }[] | null; + children: CustomDataPermissionMenuType[]; } export interface MenuDataType { - id: string; - name: string; - menus:MenusType[]; + id: string; + name: string; + isCheck: boolean; + btns: { id: number; name: string; value: string; isCheck: boolean; data_range: number; dept: object }[]; + columns: { [key: string]: boolean | string; }[]; + children: MenuDataType[]; } - diff --git a/web/src/views/system/user/api.ts b/web/src/views/system/user/api.ts index 78a15ec98b73aed16403b065a1a9544257b2c918..02a2ac6e9bc7baef18ed80b3e73c4f0bbf22608e 100644 --- a/web/src/views/system/user/api.ts +++ b/web/src/views/system/user/api.ts @@ -5,7 +5,7 @@ export const apiPrefix = '/api/system/user/'; export function GetDept(query: PageQuery) { return request({ - url: "/api/system/dept/dept_lazy_tree/", + url: "/api/system/dept/all_dept/", method: 'get', params: query, }); diff --git a/web/src/views/system/user/crud.tsx b/web/src/views/system/user/crud.tsx index 72815251fc60a5f639118eaa1e472bcab3627215..dd24c406010a65c5b56702ed869b125739e5ce62 100644 --- a/web/src/views/system/user/crud.tsx +++ b/web/src/views/system/user/crud.tsx @@ -218,7 +218,10 @@ export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps) label: 'name' }), column: { - minWidth: 150, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + return row.dept_name_all + } }, form: { rules: [ @@ -253,7 +256,11 @@ export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps) label: 'name', }), column: { - minWidth: 100, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + const values = row.role_info.map((item:any) => item.name); + return values.join(',') + } }, form: { rules: [ diff --git a/web/src/views/system/user/index.vue b/web/src/views/system/user/index.vue index 4ea5033d91b0322d68b346654d5270ae15a75edd..71085d1ba30005bbe2e108fa4b125bfe1faf2366 100644 --- a/web/src/views/system/user/index.vue +++ b/web/src/views/system/user/index.vue @@ -98,7 +98,6 @@ const getData = () => { const result = XEUtils.toArrayTree(responseData, { parentKey: 'parent', children: 'children', - strict: true, }); data.value = result; diff --git a/web/src/views/system/whiteList/crud.tsx b/web/src/views/system/whiteList/crud.tsx index 7104cdaedbb284f757667368e38158e3baa32726..33059c91557db23952ef56451cf54b07a6e6a70a 100644 --- a/web/src/views/system/whiteList/crud.tsx +++ b/web/src/views/system/whiteList/crud.tsx @@ -200,11 +200,9 @@ export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps) component: { span: 24, props: { - elProps: { - allowCreate: true, - filterable: true, - clearable: true, - }, + allowCreate: true, + filterable: true, + clearable: true, }, }, itemProps: { diff --git a/web/vite.config.ts b/web/vite.config.ts index c86cfc48afe8be9b0a9800e4848d13fefede34b4..ed6dec257ed6df4429a48a23b187bed608bca971 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -3,6 +3,7 @@ import { resolve } from 'path'; import { defineConfig, loadEnv, ConfigEnv } from 'vite'; import vueSetupExtend from 'vite-plugin-vue-setup-extend'; import vueJsx from '@vitejs/plugin-vue-jsx' +import { generateVersionFile } from "/@/utils/upgrade"; const pathResolve = (dir: string) => { return resolve(__dirname, '.', dir); @@ -17,6 +18,8 @@ const alias: Record = { const viteConfig = defineConfig((mode: ConfigEnv) => { const env = loadEnv(mode.mode, process.cwd()); + // 当Vite构建时,生成版本文件 + generateVersionFile() return { plugins: [vue(), vueJsx(), vueSetupExtend()], root: process.cwd(),