diff --git a/backend/dvadmin/system/fixtures/init_menu.json b/backend/dvadmin/system/fixtures/init_menu.json index c9ce920..0eeedd2 100644 --- a/backend/dvadmin/system/fixtures/init_menu.json +++ b/backend/dvadmin/system/fixtures/init_menu.json @@ -255,13 +255,13 @@ { "name": "重设密码", "value": "ResetPassword", - "api": "/api/system/user/reset_password/{id}/", + "api": "/api/system/user/{id}/reset_password/", "method": 2 }, { "name": "重置密码", "value": "DefaultPassword", - "api": "/api/system/user/reset_to_default_password/{id}/", + "api": "/api/system/user/{id}/reset_to_default_password/", "method": 2 }, { diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index 396296f..6dec1b1 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -14,7 +14,7 @@ STATUS_CHOICES = ( class Users(AbstractUser, CoreModel): - username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name='用户账号', help_text="用户账号") + username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号", help_text="用户账号") email = models.EmailField(max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱") mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话") avatar = models.CharField(max_length=255, verbose_name="头像", null=True, blank=True, help_text="头像") @@ -24,27 +24,36 @@ class Users(AbstractUser, CoreModel): (1, "男"), (2, "女"), ) - gender = models.IntegerField(choices=GENDER_CHOICES, default=0, verbose_name="性别", null=True, blank=True, - help_text="性别") + gender = models.IntegerField( + choices=GENDER_CHOICES, default=0, verbose_name="性别", null=True, blank=True, help_text="性别" + ) USER_TYPE = ( (0, "后台用户"), (1, "前台用户"), ) - user_type = models.IntegerField(choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True, - help_text="用户类型") - post = models.ManyToManyField(to='Post', verbose_name='关联岗位', db_constraint=False, help_text="关联岗位") - role = models.ManyToManyField(to='Role', verbose_name='关联角色', db_constraint=False, help_text="关联角色") - dept = models.ForeignKey(to='Dept', verbose_name='所属部门', on_delete=models.PROTECT, db_constraint=False, null=True, - blank=True, help_text="关联部门") + user_type = models.IntegerField( + choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True, help_text="用户类型" + ) + post = models.ManyToManyField(to="Post", verbose_name="关联岗位", db_constraint=False, help_text="关联岗位") + role = models.ManyToManyField(to="Role", verbose_name="关联角色", db_constraint=False, help_text="关联角色") + dept = models.ForeignKey( + to="Dept", + verbose_name="所属部门", + on_delete=models.PROTECT, + db_constraint=False, + null=True, + blank=True, + help_text="关联部门", + ) def set_password(self, raw_password): - super().set_password(hashlib.md5(raw_password.encode(encoding='UTF-8')).hexdigest()) + super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest()) class Meta: db_table = table_prefix + "system_users" - verbose_name = '用户表' + verbose_name = "用户表" verbose_name_plural = verbose_name - ordering = ('-create_datetime',) + ordering = ("-create_datetime",) class Post(CoreModel): @@ -59,9 +68,9 @@ class Post(CoreModel): class Meta: db_table = table_prefix + "system_post" - verbose_name = '岗位表' + verbose_name = "岗位表" verbose_name_plural = verbose_name - ordering = ('sort',) + ordering = ("sort",) class Role(CoreModel): @@ -79,16 +88,17 @@ class Role(CoreModel): ) data_range = models.IntegerField(default=0, choices=DATASCOPE_CHOICES, verbose_name="数据权限范围", help_text="数据权限范围") remark = models.TextField(verbose_name="备注", help_text="备注", null=True, blank=True) - dept = models.ManyToManyField(to='Dept', verbose_name='数据权限-关联部门', db_constraint=False, help_text="数据权限-关联部门") - menu = models.ManyToManyField(to='Menu', verbose_name='关联菜单', db_constraint=False, help_text="关联菜单") - permission = models.ManyToManyField(to='MenuButton', verbose_name='关联菜单的接口按钮', db_constraint=False, - help_text="关联菜单的接口按钮") + dept = models.ManyToManyField(to="Dept", verbose_name="数据权限-关联部门", db_constraint=False, help_text="数据权限-关联部门") + menu = models.ManyToManyField(to="Menu", verbose_name="关联菜单", db_constraint=False, help_text="关联菜单") + permission = models.ManyToManyField( + to="MenuButton", verbose_name="关联菜单的接口按钮", db_constraint=False, help_text="关联菜单的接口按钮" + ) class Meta: - db_table = table_prefix + 'system_role' - verbose_name = '角色表' + db_table = table_prefix + "system_role" + verbose_name = "角色表" verbose_name_plural = verbose_name - ordering = ('sort',) + ordering = ("sort",) class Dept(CoreModel): @@ -97,21 +107,35 @@ class Dept(CoreModel): owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人") phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话") email = models.EmailField(max_length=32, verbose_name="邮箱", null=True, blank=True, help_text="邮箱") - status = models.BooleanField(default=True, verbose_name="部门状态", null=True, blank=True, - help_text="部门状态") - parent = models.ForeignKey(to='Dept', on_delete=models.CASCADE, default=None, verbose_name="上级部门", - db_constraint=False, null=True, blank=True, help_text="上级部门") + status = models.BooleanField(default=True, verbose_name="部门状态", null=True, blank=True, help_text="部门状态") + parent = models.ForeignKey( + to="Dept", + on_delete=models.CASCADE, + default=None, + verbose_name="上级部门", + db_constraint=False, + null=True, + blank=True, + help_text="上级部门", + ) class Meta: db_table = table_prefix + "system_dept" - verbose_name = '部门表' + verbose_name = "部门表" verbose_name_plural = verbose_name - ordering = ('sort',) + ordering = ("sort",) class Menu(CoreModel): - parent = models.ForeignKey(to='Menu', on_delete=models.CASCADE, verbose_name="上级菜单", null=True, blank=True, - db_constraint=False, help_text="上级菜单") + parent = models.ForeignKey( + to="Menu", + on_delete=models.CASCADE, + verbose_name="上级菜单", + null=True, + blank=True, + db_constraint=False, + help_text="上级菜单", + ) icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标") name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称") sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序") @@ -130,14 +154,20 @@ class Menu(CoreModel): class Meta: db_table = table_prefix + "system_menu" - verbose_name = '菜单表' + verbose_name = "菜单表" verbose_name_plural = verbose_name - ordering = ('sort',) + ordering = ("sort",) class MenuButton(CoreModel): - menu = models.ForeignKey(to="Menu", db_constraint=False, related_name="menuPermission", on_delete=models.CASCADE, - verbose_name="关联菜单", help_text='关联菜单') + menu = models.ForeignKey( + to="Menu", + db_constraint=False, + related_name="menuPermission", + on_delete=models.CASCADE, + verbose_name="关联菜单", + help_text="关联菜单", + ) name = models.CharField(max_length=64, verbose_name="名称", help_text="名称") value = models.CharField(max_length=64, verbose_name="权限值", help_text="权限值") api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址") @@ -151,26 +181,34 @@ class MenuButton(CoreModel): class Meta: db_table = table_prefix + "system_menu_button" - verbose_name = '菜单权限表' + verbose_name = "菜单权限表" verbose_name_plural = verbose_name - ordering = ('-name',) + ordering = ("-name",) class Dictionary(CoreModel): TYPE_LIST = ( - (0, 'text'), - (1, 'number'), - (2, 'date'), - (3, 'datetime'), - (4, 'time'), - (5, 'files'), - (6, 'boolean'), - (7, 'images'), + (0, "text"), + (1, "number"), + (2, "date"), + (3, "datetime"), + (4, "time"), + (5, "files"), + (6, "boolean"), + (7, "images"), ) label = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称") value = models.CharField(max_length=200, blank=True, null=True, verbose_name="字典编号", help_text="字典编号/实际值") - parent = models.ForeignKey(to='self', related_name='sublist', db_constraint=False, on_delete=models.PROTECT, - blank=True, null=True, verbose_name="父级", help_text="父级") + parent = models.ForeignKey( + to="self", + related_name="sublist", + db_constraint=False, + on_delete=models.PROTECT, + blank=True, + null=True, + verbose_name="父级", + help_text="父级", + ) type = models.IntegerField(choices=TYPE_LIST, default=0, verbose_name="数据值类型", help_text="数据值类型") color = models.CharField(max_length=20, blank=True, null=True, verbose_name="颜色", help_text="颜色") is_value = models.BooleanField(default=False, verbose_name="是否为value值", help_text="是否为value值,用来做具体值存放") @@ -179,15 +217,15 @@ class Dictionary(CoreModel): remark = models.CharField(max_length=2000, blank=True, null=True, verbose_name="备注", help_text="备注") class Meta: - db_table = table_prefix + 'system_dictionary' + db_table = table_prefix + "system_dictionary" verbose_name = "字典表" verbose_name_plural = verbose_name - ordering = ('sort',) + ordering = ("sort",) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): super().save(force_insert, force_update, using, update_fields) - dispatch.refresh_dictionary() # 有更新则刷新字典配置 - + dispatch.refresh_dictionary() # 有更新则刷新字典配置 + def delete(self, using=None, keep_parents=False): res = super().delete(using, keep_parents) dispatch.refresh_dictionary() @@ -208,16 +246,16 @@ class OperationLog(CoreModel): status = models.BooleanField(default=False, verbose_name="响应状态", help_text="响应状态") class Meta: - db_table = table_prefix + 'system_operation_log' - verbose_name = '操作日志' + db_table = table_prefix + "system_operation_log" + verbose_name = "操作日志" verbose_name_plural = verbose_name - ordering = ('-create_datetime',) + ordering = ("-create_datetime",) def media_file_name(instance, filename): h = instance.md5sum basename, ext = os.path.splitext(filename) - return os.path.join('files', h[0:1], h[1:2], h + ext.lower()) + return os.path.join("files", h[0:1], h[1:2], h + ext.lower()) class FileList(CoreModel): @@ -234,10 +272,10 @@ class FileList(CoreModel): super(FileList, self).save(*args, **kwargs) class Meta: - db_table = table_prefix + 'system_file_list' - verbose_name = '文件管理' + db_table = table_prefix + "system_file_list" + verbose_name = "文件管理" verbose_name_plural = verbose_name - ordering = ('-create_datetime',) + ordering = ("-create_datetime",) class Area(CoreModel): @@ -247,14 +285,22 @@ class Area(CoreModel): pinyin = models.CharField(max_length=255, verbose_name="拼音", help_text="拼音") initials = models.CharField(max_length=20, verbose_name="首字母", help_text="首字母") enable = models.BooleanField(default=True, verbose_name="是否启用", help_text="是否启用") - pcode = models.ForeignKey(to='self', verbose_name='父地区编码', to_field="code", on_delete=models.CASCADE, - db_constraint=False, null=True, blank=True, help_text="父地区编码") + pcode = models.ForeignKey( + to="self", + verbose_name="父地区编码", + to_field="code", + on_delete=models.CASCADE, + db_constraint=False, + null=True, + blank=True, + help_text="父地区编码", + ) class Meta: db_table = table_prefix + "system_area" - verbose_name = '地区表' + verbose_name = "地区表" verbose_name_plural = verbose_name - ordering = ('code',) + ordering = ("code",) def __str__(self): return f"{self.name}" @@ -273,14 +319,21 @@ class ApiWhiteList(CoreModel): class Meta: db_table = table_prefix + "api_white_list" - verbose_name = '接口白名单' + verbose_name = "接口白名单" verbose_name_plural = verbose_name - ordering = ('-create_datetime',) + ordering = ("-create_datetime",) class SystemConfig(CoreModel): - parent = models.ForeignKey(to='self', verbose_name='父级', on_delete=models.CASCADE, - db_constraint=False, null=True, blank=True, help_text="父级") + parent = models.ForeignKey( + to="self", + verbose_name="父级", + on_delete=models.CASCADE, + db_constraint=False, + null=True, + blank=True, + help_text="父级", + ) title = models.CharField(max_length=50, verbose_name="标题", help_text="标题") key = models.CharField(max_length=20, verbose_name="键", help_text="键", db_index=True) value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True) @@ -288,35 +341,35 @@ class SystemConfig(CoreModel): status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态") data_options = models.JSONField(verbose_name="数据options", help_text="数据options", null=True, blank=True) FORM_ITEM_TYPE_LIST = ( - (0, 'text'), - (1, 'datetime'), - (2, 'date'), - (3, 'textarea'), - (4, 'select'), - (5, 'checkbox'), - (6, 'radio'), - (7, 'img'), - (8, 'file'), - (9, 'switch'), - (10, 'number'), - (11, 'array'), - (12, 'imgs'), - (13, 'foreignkey'), - (14, 'manytomany'), - (15, 'time'), - + (0, "text"), + (1, "datetime"), + (2, "date"), + (3, "textarea"), + (4, "select"), + (5, "checkbox"), + (6, "radio"), + (7, "img"), + (8, "file"), + (9, "switch"), + (10, "number"), + (11, "array"), + (12, "imgs"), + (13, "foreignkey"), + (14, "manytomany"), + (15, "time"), + ) + form_item_type = models.IntegerField( + choices=FORM_ITEM_TYPE_LIST, verbose_name="表单类型", help_text="表单类型", default=0, blank=True ) - form_item_type = models.IntegerField(choices=FORM_ITEM_TYPE_LIST, verbose_name="表单类型", help_text="表单类型", default=0, - blank=True) rule = models.JSONField(null=True, blank=True, verbose_name="校验规则", help_text="校验规则") placeholder = models.CharField(max_length=50, null=True, blank=True, verbose_name="提示信息", help_text="提示信息") setting = models.JSONField(null=True, blank=True, verbose_name="配置", help_text="配置") class Meta: db_table = table_prefix + "system_config" - verbose_name = '系统配置表' + verbose_name = "系统配置表" verbose_name_plural = verbose_name - ordering = ('sort',) + ordering = ("sort",) unique_together = (("key", "parent_id"),) def __str__(self): @@ -324,8 +377,8 @@ class SystemConfig(CoreModel): def save(self, force_insert=False, force_update=False, using=None, update_fields=None): super().save(force_insert, force_update, using, update_fields) - dispatch.refresh_system_config() # 有更新则刷新系统配置 - + dispatch.refresh_system_config() # 有更新则刷新系统配置 + def delete(self, using=None, keep_parents=False): res = super().delete(using, keep_parents) dispatch.refresh_system_config() @@ -333,9 +386,7 @@ class SystemConfig(CoreModel): class LoginLog(CoreModel): - LOGIN_TYPE_CHOICES = ( - (1, '普通登录'), - ) + LOGIN_TYPE_CHOICES = ((1, "普通登录"),) username = models.CharField(max_length=32, verbose_name="登录用户名", null=True, blank=True, help_text="登录用户名") ip = models.CharField(max_length=32, verbose_name="登录ip", null=True, blank=True, help_text="登录ip") agent = models.TextField(verbose_name="agent信息", null=True, blank=True, help_text="agent信息") @@ -355,7 +406,7 @@ class LoginLog(CoreModel): login_type = models.IntegerField(default=1, choices=LOGIN_TYPE_CHOICES, verbose_name="登录类型", help_text="登录类型") class Meta: - db_table = table_prefix + 'system_login_log' - verbose_name = '登录日志' + db_table = table_prefix + "system_login_log" + verbose_name = "登录日志" verbose_name_plural = verbose_name - ordering = ('-create_datetime',) + ordering = ("-create_datetime",) diff --git a/backend/dvadmin/system/urls.py b/backend/dvadmin/system/urls.py index 9d6c89f..06fcd9e 100644 --- a/backend/dvadmin/system/urls.py +++ b/backend/dvadmin/system/urls.py @@ -28,12 +28,6 @@ system_url.register(r'api_white_list', ApiWhiteListViewSet) system_url.register(r'system_config', SystemConfigViewSet) urlpatterns = [ - path('role/roleId_get_menu//', RoleViewSet.as_view({'get': 'roleId_get_menu'})), - path('menu/web_router/', MenuViewSet.as_view({'get': 'web_router'})), - path('user/user_info/', UserViewSet.as_view({'get': 'user_info', 'put': 'update_user_info'})), - path('user/change_password//', UserViewSet.as_view({'put': 'change_password'})), - path('user/reset_to_default_password//', UserViewSet.as_view({'put': 'reset_to_default_password'})), - path('user/reset_password//', UserViewSet.as_view({'put': 'reset_password'})), path('user/export/', UserViewSet.as_view({'post': 'export_data', })), path('user/import/', UserViewSet.as_view({'get': 'import_data', 'post': 'import_data'})), path('system_config/save_content/', SystemConfigViewSet.as_view({'put': 'save_content'})), diff --git a/backend/dvadmin/system/views/menu.py b/backend/dvadmin/system/views/menu.py index b791b28..cd75951 100644 --- a/backend/dvadmin/system/views/menu.py +++ b/backend/dvadmin/system/views/menu.py @@ -23,7 +23,7 @@ class MenuSerializer(CustomModelSerializer): menuPermission = serializers.SerializerMethodField(read_only=True) def get_menuPermission(self, instance): - queryset = MenuButton.objects.filter(menu=instance.id).order_by('-name').values_list('name', flat=True) + queryset = instance.menuPermission.order_by('-name').values_list('name', flat=True) if queryset: return queryset else: @@ -65,7 +65,7 @@ class MenuInitSerializer(CustomModelSerializer): def get_menu_button(self, obj: Menu): data = [] - instance = MenuButton.objects.filter(menu_id=obj.id).order_by('method') + instance = obj.menuPermission.order_by('method') if instance: data = list(instance.values('name', 'value', 'api', 'method')) return data @@ -124,11 +124,11 @@ class WebRouterSerializer(CustomModelSerializer): def get_menuPermission(self, instance): # 判断是否是超级管理员 if self.request.user.is_superuser: - return MenuButton.objects.values_list('value', flat=True) + return instance.menuPermission.values_list('value', flat=True) else: # 根据当前角色获取权限按钮id集合 permissionIds = self.request.user.role.values_list('permission', flat=True) - queryset = MenuButton.objects.filter(id__in=permissionIds, menu=instance.id).values_list('value', flat=True) + queryset = instance.menuPermission.filter(id__in=permissionIds, menu=instance.id).values_list('value', flat=True) if queryset: return queryset else: @@ -136,7 +136,8 @@ class WebRouterSerializer(CustomModelSerializer): class Meta: model = Menu - fields = "__all__" + fields = ('id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component', + 'component_name', 'cache', 'visible', 'menuPermission') read_only_fields = ["id"] @@ -155,9 +156,9 @@ class MenuViewSet(CustomModelViewSet): update_serializer_class = MenuCreateSerializer search_fields = ['name', 'status'] filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog'] - extra_filter_backends = [] + # extra_filter_backends = [] - @action(methods=['GET'], detail=True, permission_classes=[]) + @action(methods=['GET'], detail=False, permission_classes=[]) def web_router(self, request): """用于前端获取当前角色的路由""" user = request.user diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py index 28e6462..ef9f40b 100644 --- a/backend/dvadmin/system/views/user.py +++ b/backend/dvadmin/system/views/user.py @@ -229,7 +229,7 @@ class UserViewSet(CustomModelViewSet): "role": "角色ID", } - @action(methods=["GET"], detail=True, permission_classes=[IsAuthenticated]) + @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated]) def user_info(self, request): """获取当前用户信息""" user = request.user @@ -242,7 +242,7 @@ class UserViewSet(CustomModelViewSet): } return DetailResponse(data=result, msg="获取成功") - @action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated]) + @action(methods=["PUT"], detail=False, permission_classes=[IsAuthenticated]) def update_user_info(self, request): """修改当前用户信息""" user = request.user diff --git a/backend/dvadmin/utils/import_export.py b/backend/dvadmin/utils/import_export.py index 2f3db9c..5a01968 100644 --- a/backend/dvadmin/utils/import_export.py +++ b/backend/dvadmin/utils/import_export.py @@ -21,15 +21,17 @@ def import_to_data(file_url, field_data): # 创建一个空列表,存储Excel的数据 tables = [] for i, row in enumerate(range(table.max_row)): - if i == 0: continue + if i == 0: + continue array = {} for index, ele in enumerate(field_data.keys()): cell_value = table.cell(row=row + 1, column=index + 2).value + print(cell_value) # 由于excel导入数字类型后,会出现数字加 .0 的,进行处理 - if type(cell_value) is float and str(cell_value).split('.')[1] == '0': - cell_value = int(str(cell_value).split('.')[0]) + if type(cell_value) is float and str(cell_value).split(".")[1] == "0": + cell_value = int(str(cell_value).split(".")[0]) if type(cell_value) is str: - cell_value = cell_value.strip(' \t\n\r') + cell_value = cell_value.strip(" \t\n\r") array[ele] = cell_value tables.append(array) return tables diff --git a/backend/dvadmin/utils/import_export_mixin.py b/backend/dvadmin/utils/import_export_mixin.py index ee8cfdb..5a5c9f2 100644 --- a/backend/dvadmin/utils/import_export_mixin.py +++ b/backend/dvadmin/utils/import_export_mixin.py @@ -17,6 +17,7 @@ class ImportSerializerMixin: """ 自定义导出模板、导入功能 """ + # 导入字段 import_field_dict = {} # 导入序列化器 @@ -31,37 +32,42 @@ class ImportSerializerMixin: :param kwargs: :return: """ - assert self.import_field_dict, ( - "'%s' 请配置对应的导出模板字段。" - % self.__class__.__name__ - ) + assert self.import_field_dict, "'%s' 请配置对应的导出模板字段。" % self.__class__.__name__ # 导出模板 - if request.method == 'GET': + if request.method == "GET": # 示例数据 queryset = self.filter_queryset(self.get_queryset()) # 导出excel 表 - response = HttpResponse(content_type='application/msexcel') - response['Access-Control-Expose-Headers'] = f'Content-Disposition' - response['Content-Disposition'] = f'attachment;filename={quote(str(f"导入{get_verbose_name(queryset)}模板.xlsx"))}' + response = HttpResponse(content_type="application/msexcel") + response["Access-Control-Expose-Headers"] = f"Content-Disposition" + response[ + "Content-Disposition" + ] = f'attachment;filename={quote(str(f"导入{get_verbose_name(queryset)}模板.xlsx"))}' wb = Workbook() ws = wb.active row = get_column_letter(len(self.import_field_dict) + 1) column = 10 - ws.append(['序号', *self.import_field_dict.values()]) + ws.append(["序号", *self.import_field_dict.values()]) tab = Table(displayName="Table1", ref=f"A1:{row}{column}") # 名称管理器 - style = TableStyleInfo(name='TableStyleLight11', showFirstColumn=True, - showLastColumn=True, showRowStripes=True, showColumnStripes=True) + style = TableStyleInfo( + name="TableStyleLight11", + showFirstColumn=True, + showLastColumn=True, + showRowStripes=True, + showColumnStripes=True, + ) tab.tableStyleInfo = style ws.add_table(tab) wb.save(response) return response - updateSupport = request.data.get('updateSupport') + updateSupport = request.data.get("updateSupport") # 从excel中组织对应的数据结构,然后使用序列化器保存 - data = import_to_data(request.data.get('url'), self.import_field_dict) + data = import_to_data(request.data.get("url"), self.import_field_dict) queryset = self.filter_queryset(self.get_queryset()) - unique_list = [ele.attname for ele in queryset.model._meta.get_fields() if - hasattr(ele, 'unique') and ele.unique == True] + unique_list = [ + ele.attname for ele in queryset.model._meta.get_fields() if hasattr(ele, "unique") and ele.unique == True + ] for ele in data: # 获取 unique 字段 filter_dic = {i: ele.get(i) for i in list(set(self.import_field_dict.keys()) & set(unique_list))} @@ -80,6 +86,7 @@ class ExportSerializerMixin: """ 自定义导出功能 """ + # 导出字段 export_field_label = [] # 导出序列化器 @@ -93,27 +100,29 @@ class ExportSerializerMixin: :param kwargs: :return: """ - assert self.export_field_label, ( - "'%s' 请配置对应的导出模板字段。" - % self.__class__.__name__ - ) + assert self.export_field_label, "'%s' 请配置对应的导出模板字段。" % self.__class__.__name__ queryset = self.filter_queryset(self.get_queryset()) data = self.export_serializer_class(queryset, many=True).data # 导出excel 表 - response = HttpResponse(content_type='application/msexcel') - response['Access-Control-Expose-Headers'] = f'Content-Disposition' - response['Content-Disposition'] = f'attachment;filename={quote(str(f"导出{get_verbose_name(queryset)}.xlsx"))}' + response = HttpResponse(content_type="application/msexcel") + response["Access-Control-Expose-Headers"] = f"Content-Disposition" + response["Content-Disposition"] = f'attachment;filename={quote(str(f"导出{get_verbose_name(queryset)}.xlsx"))}' wb = Workbook() ws = wb.active row = get_column_letter(len(self.export_field_label) + 1) column = 1 - ws.append(['序号', *self.export_field_label]) + ws.append(["序号", *self.export_field_label]) for index, results in enumerate(data): ws.append([index + 1, *list(results.values())]) column += 1 tab = Table(displayName="Table2", ref=f"A1:{row}{column}") # 名称管理器 - style = TableStyleInfo(name='TableStyleLight11', showFirstColumn=True, - showLastColumn=True, showRowStripes=True, showColumnStripes=True) + style = TableStyleInfo( + name="TableStyleLight11", + showFirstColumn=True, + showLastColumn=True, + showRowStripes=True, + showColumnStripes=True, + ) tab.tableStyleInfo = style ws.add_table(tab) wb.save(response) diff --git a/backend/dvadmin/utils/permission.py b/backend/dvadmin/utils/permission.py index 35e5b4b..2be0691 100644 --- a/backend/dvadmin/utils/permission.py +++ b/backend/dvadmin/utils/permission.py @@ -65,14 +65,6 @@ class CustomPermission(BasePermission): def has_permission(self, request, view): if isinstance(request.user, AnonymousUser): return False - # 对ViewSet下的def方法进行权限判断 - # 当权限为空时,则可以访问 - is_head = getattr(view, 'head', None) - if is_head: - head_kwargs = getattr(view.head, 'kwargs', {}) - _permission_classes = head_kwargs.get('permission_classes', None) - if _permission_classes == []: - return True # 判断是否是超级管理员 if request.user.is_superuser: return True diff --git a/backend/plugins/__init__.py b/backend/plugins/__init__.py index e69de29..40a96af 100644 --- a/backend/plugins/__init__.py +++ b/backend/plugins/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/web/package.json b/web/package.json index be3729b..41966e1 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "django-vue-admin", - "version": "2.0.1", + "version": "2.0.4", "scripts": { "serve": "vue-cli-service serve --open", "start": "npm run serve", @@ -36,6 +36,7 @@ "lodash": "^4.17.15", "lowdb": "^1.0.0", "nprogress": "^0.2.0", + "qiankun": "^2.7.2", "screenfull": "^5.0.2", "sortablejs": "^1.10.1", "ua-parser-js": "^0.7.20", diff --git a/web/src/App.vue b/web/src/App.vue index 7f3c9c6..7c3db52 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -1,6 +1,8 @@ diff --git a/web/src/layout/header-aside/components/header-user/userinfo.vue b/web/src/layout/header-aside/components/header-user/userinfo.vue index c327ffb..a761f9c 100644 --- a/web/src/layout/header-aside/components/header-user/userinfo.vue +++ b/web/src/layout/header-aside/components/header-user/userinfo.vue @@ -218,7 +218,7 @@ export default { _self.$refs.userInfoForm.validate((valid) => { if (valid) { request({ - url: '/api/system/user/user_info/', + url: '/api/system/user/update_user_info/', method: 'put', data: _self.userInfo }).then((res) => { @@ -266,7 +266,7 @@ export default { params.newPassword = _self.$md5(params.newPassword) params.newPassword2 = _self.$md5(params.newPassword2) request({ - url: '/api/system/user/change_password/' + userId + '/', + url: '/api/system/user/' + userId + '/change_password/', method: 'put', data: params }).then((res) => { diff --git a/web/src/router/index.js b/web/src/router/index.js index b237907..6d19422 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -75,6 +75,15 @@ router.beforeEach(async (to, from, next) => { }) } else { next() + const childrenPath = window.qiankunActiveRule || [] + if (to.name) { + // 有 name 属性,说明是主应用的路由 + next() + } else if (childrenPath.some((item) => to.path.includes(item))) { + next() + } else { + next({ name: '404' }) + } } } else { // 没有登录的时候跳转到登录界面 diff --git a/web/src/router/routes.js b/web/src/router/routes.js index 7d9b192..7e03791 100644 --- a/web/src/router/routes.js +++ b/web/src/router/routes.js @@ -29,34 +29,6 @@ const frameIn = [{ }, component: () => import('@/layout/header-aside/components/header-user/userinfo') }, - // 演示页面 - { - path: 'page1', - name: 'page1', - meta: { - title: '页面 1', - auth: true - }, - component: _import('demo/page1') - }, - { - path: 'page2', - name: 'page2', - meta: { - title: '页面 2', - auth: true - }, - component: _import('demo/page2') - }, - { - path: 'page3', - name: 'page3', - meta: { - title: '页面 3', - auth: true - }, - component: _import('demo/page3') - }, // dashboard 工作台 { path: 'workbench', @@ -67,97 +39,6 @@ const frameIn = [{ }, component: _import('dashboard/workbench') }, - // // 系统 菜单 - // { - // path: 'menu', - // name: 'menu', - // meta: { - // title: '菜单', - // auth: true - // }, - // component: _import('system/menu') - // }, - // // 系统 用户 - // { - // path: 'user', - // name: 'user', - // meta: { - // title: '用户', - // auth: true - // }, - // component: _import('system/user') - // }, - // // 系统 按钮配置 - // { - // path: 'button', - // name: 'button', - // meta: { - // title: '按钮', - // auth: true - // }, - // component: _import('system/button') - // }, - // // // 系统 菜单权限 - // { - // path: 'menuButton/:id', - // name: 'menuButton', - // meta: { - // title: '菜单按钮', - // auth: true - // }, - // component: _import('system/menuButton') - // }, - // // 系统 角色管理 - // { - // path: 'role', - // name: 'role', - // meta: { - // title: '角色', - // auth: true - // }, - // component: _import('system/role') - // }, - // // 系统 角色权限 - // { - // path: 'rolePermission', - // name: 'rolePermission', - // meta: { - // title: '权限管理', - // auth: true - // }, - // component: _import('system/rolePermission') - // }, - - // // 系统 角色管理 - // { - // path: 'dept', - // name: 'dept', - // meta: { - // title: '部门', - // auth: true - // }, - // component: _import('system/dept') - // }, - // // 系统 操作日志 - // { - // path: 'operationLog', - // name: 'operationLog', - // meta: { - // title: '操作日志', - // auth: true - // }, - // component: _import('system/log/operationLog') - // }, - // 系统 前端日志 - // { - // path: 'frontendLog', - // name: 'frontendLog', - // meta: { - // title: '前端日志', - // auth: true - // }, - // component: _import('system/log/frontendLog') - // }, // 刷新页面 必须保留 { path: 'refresh', @@ -201,7 +82,7 @@ if (pluginsType) { * 错误页面 */ const errorPage = [{ - path: '*', + path: '/404', name: '404', component: _import('system/error/404') }] diff --git a/web/src/views/system/menu/api.js b/web/src/views/system/menu/api.js index 142f164..2221eaf 100644 --- a/web/src/views/system/menu/api.js +++ b/web/src/views/system/menu/api.js @@ -7,7 +7,6 @@ * @文件介绍: 菜单管理接口 */ import { request } from '@/api/service' -import XEUtils from 'xe-utils' export const urlPrefix = '/api/system/menu/' @@ -22,7 +21,7 @@ export function GetList (query) { params: { ...query, limit: 999 } }).then(res => { // 将列表数据转换为树形数据 - res.data.data = XEUtils.toArrayTree(res.data.data, { parentKey: 'parent', strict: false }) + // res.data.data = XEUtils.toArrayTree(res.data.data, { parentKey: 'parent', strict: false }) return res }) } diff --git a/web/src/views/system/menu/crud.js b/web/src/views/system/menu/crud.js index 2bbe558..536c143 100644 --- a/web/src/views/system/menu/crud.js +++ b/web/src/views/system/menu/crud.js @@ -23,10 +23,19 @@ export const crudOptions = (vm) => { compact: true }, options: { + tableType: 'vxe-table', + rowKey: true, rowId: 'id', height: '100%', // 表格高度100%, 使用toolbar必须设置 highlightCurrentRow: false, - defaultExpandAll: true + // defaultExpandAll: true, + // expandAll: true, + treeConfig: { + transform: true, + rowField: 'id', + parentField: 'parent', + expandAll: true + } }, rowHandle: { view: { diff --git a/web/src/views/system/rolePermission/api.js b/web/src/views/system/rolePermission/api.js index 23c9866..2b6eb6b 100644 --- a/web/src/views/system/rolePermission/api.js +++ b/web/src/views/system/rolePermission/api.js @@ -47,7 +47,7 @@ export function DelObj (id) { // 通过角色id,获取菜单数据 export function GetMenuData (obj) { return request({ - url: '/api/system/role/roleId_get_menu/' + obj.id + '/', + url: '/api/system/role/' + obj.id + '/roleId_get_menu/', method: 'get', params: {} }).then(res => { diff --git a/web/src/views/system/user/api.js b/web/src/views/system/user/api.js index 8a705a3..5743af2 100644 --- a/web/src/views/system/user/api.js +++ b/web/src/views/system/user/api.js @@ -1,13 +1,4 @@ -/* - * @创建文件时间: 2021-06-01 22:41:21 - * @Auther: 猿小天 - * @最后修改人: 猿小天 - * @最后修改时间: 2021-06-06 10:14:14 - * 联系Qq:1638245306 - * @文件介绍: 用户接口 - */ -import { request } from '@/api/service' - +import { request, downloadFile } from '@/api/service' export const urlPrefix = '/api/system/user/' export function GetList (query) { @@ -50,8 +41,20 @@ export function DelObj (id) { */ export function ResetPwd (obj) { return request({ - url: urlPrefix + 'reset_password/' + obj.id + '/', + url: urlPrefix + obj.id + '/reset_password/', method: 'put', data: obj }) } + +/** + * 导出 + * @param params + */ +export function exportData (params) { + return downloadFile({ + url: urlPrefix + 'export/', + params: params, + method: 'post' + }) +} diff --git a/web/src/views/system/user/crud.js b/web/src/views/system/user/crud.js index e68ab65..9838106 100644 --- a/web/src/views/system/user/crud.js +++ b/web/src/views/system/user/crud.js @@ -177,14 +177,6 @@ export const crudOptions = (vm) => { url: deptPrefix, value: 'id', // 数据字典中value字段的属性名 label: 'name' // 数据字典中label字段的属性名 - // getData: (url, dict, { form, component }) => { - // return request({ url: url, params: { page: 1, limit: 10, status: 1 } }).then(ret => { - // component._elProps.page = ret.data.page - // component._elProps.limit = ret.data.limit - // component._elProps.total = ret.data.total - // return ret.data.data - // }) - // } }, form: { rules: [ // 表单校验规则 @@ -199,23 +191,7 @@ export const crudOptions = (vm) => { component: { span: 12, pagination: true, - props: { multiple: false }, - elProps: { - columns: [ - { - field: 'name', - title: '部门名称' - }, - { - field: 'status_label', - title: '状态' - }, - { - field: 'parent_name', - title: '父级部门' - } - ] - } + props: { multiple: false } } } }, @@ -284,7 +260,7 @@ export const crudOptions = (vm) => { search: { disabled: false }, - width: 140, + width: 145, type: 'select', dict: { data: vm.dictionary('user_type') @@ -388,10 +364,6 @@ export const crudOptions = (vm) => { { field: 'key', title: '权限标识' - }, - { - field: 'status_label', - title: '状态' } ] } diff --git a/web/src/views/system/user/index.vue b/web/src/views/system/user/index.vue index 087e2bf..6be1535 100644 --- a/web/src/views/system/user/index.vue +++ b/web/src/views/system/user/index.vue @@ -21,6 +21,18 @@ > 新增 + 导出 + + 导入 +