From ef48bdd0dfd7a12647e7f6a34f56e9c3b7cda5f9 Mon Sep 17 00:00:00 2001 From: liqiang <1206709430@qq.com> Date: Wed, 25 Jun 2025 06:55:15 +0800 Subject: [PATCH 01/17] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5=E9=9D=A2=E7=89=88=E6=9D=83=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=92=8C=E5=A4=87=E6=A1=88=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将默认版权信息修改为 "2021-2025 django-vue-admin.com" - 将默认备案号修改为 "晋ICP备18005113号-3" --- web/src/views/system/login/index.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/views/system/login/index.vue b/web/src/views/system/login/index.vue index 897ddc4121..36acbe6f20 100644 --- a/web/src/views/system/login/index.vue +++ b/web/src/views/system/login/index.vue @@ -47,10 +47,10 @@
-

Copyright © {{ getSystemConfig['login.copyright'] || '2021-2024 北京信码新创科技有限公司' }} 版权所有

+

Copyright © {{ getSystemConfig['login.copyright'] || '2021-2025 django-vue-admin.com' }} 版权所有

{{ getSystemConfig['login.keep_record'] || - '京ICP备2021031018号' }} + '晋ICP备18005113号-3' }} | 帮助 -- Gitee From ac3bfb6b80c4d58669acfdeeb286a691a1454905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com> Date: Wed, 25 Jun 2025 10:42:01 +0800 Subject: [PATCH 02/17] =?UTF-8?q?refactor(theme):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E6=A0=B7=E5=BC=8F=E5=B9=B6=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 @import 替换为 @use,提高代码的可维护性 - 统一使用 index 作为命名空间,避免变量名冲突 - 移除不必要的注释和空格,精简代码 - 删除未使用的 isSocketOpen 属性,简化数据结构 --- web/src/layout/navBars/breadcrumb/user.vue | 11 ++----- web/src/stores/interface/index.ts | 1 - web/src/stores/userInfo.ts | 1 - web/src/theme/element.scss | 4 +-- web/src/theme/index.scss | 16 +++++----- web/src/theme/media/chart.scss | 8 ++--- web/src/theme/media/cityLinkage.scss | 4 +-- web/src/theme/media/date.scss | 4 +-- web/src/theme/media/dialog.scss | 2 +- web/src/theme/media/error.scss | 8 ++--- web/src/theme/media/form.scss | 4 +-- web/src/theme/media/home.scss | 6 ++-- web/src/theme/media/layout.scss | 6 ++-- web/src/theme/media/login.scss | 8 ++--- web/src/theme/media/media.scss | 26 ++++++++-------- web/src/theme/media/pagination.scss | 4 +-- web/src/theme/media/personal.scss | 4 +-- web/src/theme/media/scrollbar.scss | 4 +-- web/src/theme/media/tagsView.scss | 4 +-- web/src/views/system/personal/index.vue | 36 +++++++++++----------- 20 files changed, 77 insertions(+), 84 deletions(-) diff --git a/web/src/layout/navBars/breadcrumb/user.vue b/web/src/layout/navBars/breadcrumb/user.vue index 92e68c6c7a..06b0effe64 100644 --- a/web/src/layout/navBars/breadcrumb/user.vue +++ b/web/src/layout/navBars/breadcrumb/user.vue @@ -60,11 +60,9 @@

- - - - - + + + {{ userInfos.username === '' ? 'common' : userInfos.username }} @@ -125,9 +123,6 @@ const layoutUserFlexNum = computed(() => { return num; }); -// 定义变量内容 -const { isSocketOpen } = storeToRefs(useUserInfo()); - // 全屏点击时 const onScreenfullClick = () => { if (!screenfull.isEnabled) { diff --git a/web/src/stores/interface/index.ts b/web/src/stores/interface/index.ts index 9f73c9212b..5083cf45b0 100644 --- a/web/src/stores/interface/index.ts +++ b/web/src/stores/interface/index.ts @@ -23,7 +23,6 @@ export interface UserInfosState { } export interface UserInfosStates { userInfos: UserInfosState; - isSocketOpen: boolean } // 路由缓存列表 diff --git a/web/src/stores/userInfo.ts b/web/src/stores/userInfo.ts index a33d8cb793..bdaf4bc2a4 100644 --- a/web/src/stores/userInfo.ts +++ b/web/src/stores/userInfo.ts @@ -32,7 +32,6 @@ export const useUserInfo = defineStore('userInfo', { }, ], }, - isSocketOpen: false }), actions: { async setPwdChangeCount(count: number) { diff --git a/web/src/theme/element.scss b/web/src/theme/element.scss index 6cdebc9ac3..741d731ca2 100644 --- a/web/src/theme/element.scss +++ b/web/src/theme/element.scss @@ -1,4 +1,4 @@ -@import 'mixins/index.scss'; +@use 'mixins/index.scss' as index; /* Button 按钮 ------------------------------- */ @@ -100,7 +100,7 @@ .el-sub-menu .iconfont, .el-menu-item .fa, .el-sub-menu .fa { - @include generalIcon; + @include index.generalIcon; } // 水平菜单、横向菜单高亮 背景色,鼠标 hover 时,有子级菜单的背景色 .el-menu-item.is-active, diff --git a/web/src/theme/index.scss b/web/src/theme/index.scss index a94595ce57..80b7ce0000 100644 --- a/web/src/theme/index.scss +++ b/web/src/theme/index.scss @@ -1,8 +1,8 @@ -@import './app.scss'; -@import 'common/transition.scss'; -@import './other.scss'; -@import './element.scss'; -@import './media/media.scss'; -@import './waves.scss'; -@import './dark.scss'; -@import './fa/css/font-awesome.min.css'; +@use './app.scss'; +@use 'common/transition.scss'; +@use './other.scss'; +@use './element.scss'; +@use './media/media.scss'; +@use './waves.scss'; +@use './dark.scss'; +@use './fa/css/font-awesome.min.css'; diff --git a/web/src/theme/media/chart.scss b/web/src/theme/media/chart.scss index 8485e39c21..79883390c6 100644 --- a/web/src/theme/media/chart.scss +++ b/web/src/theme/media/chart.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { .big-data-down-left { width: 100% !important; flex-direction: unset !important; @@ -51,7 +51,7 @@ /* 页面宽度大于768px小于1200px ------------------------------- */ -@media screen and (min-width: $sm) and (max-width: $lg) { +@media screen and (min-width: index.$sm) and (max-width: index.$lg) { .chart-warp-bottom { .big-data-down-left { width: 50% !important; @@ -72,7 +72,7 @@ /* 页面宽度小于1200px ------------------------------- */ -@media screen and (max-width: $lg) { +@media screen and (max-width: index.$lg) { .chart-warp-top { .up-left { display: none; diff --git a/web/src/theme/media/cityLinkage.scss b/web/src/theme/media/cityLinkage.scss index 1394156ee1..edc6d110ce 100644 --- a/web/src/theme/media/cityLinkage.scss +++ b/web/src/theme/media/cityLinkage.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于576px ------------------------------- */ -@media screen and (max-width: $xs) { +@media screen and (max-width: index.$xs) { .el-cascader__dropdown.el-popper { overflow: auto; max-width: 100%; diff --git a/web/src/theme/media/date.scss b/web/src/theme/media/date.scss index 1a50397068..4b3c5e888c 100644 --- a/web/src/theme/media/date.scss +++ b/web/src/theme/media/date.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { // 时间选择器适配 .el-date-range-picker { width: 100vw; diff --git a/web/src/theme/media/dialog.scss b/web/src/theme/media/dialog.scss index 023ccae0eb..64355037b6 100644 --- a/web/src/theme/media/dialog.scss +++ b/web/src/theme/media/dialog.scss @@ -1,4 +1,4 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于800px ------------------------------- */ diff --git a/web/src/theme/media/error.scss b/web/src/theme/media/error.scss index f35015fda6..3ee65db472 100644 --- a/web/src/theme/media/error.scss +++ b/web/src/theme/media/error.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { .error { .error-flex { flex-direction: column-reverse !important; @@ -26,7 +26,7 @@ /* 页面宽度大于768px小于992px ------------------------------- */ -@media screen and (min-width: $sm) and (max-width: $md) { +@media screen and (min-width: index.$sm) and (max-width: index.$md) { .error { .error-flex { padding-left: 30px !important; @@ -36,7 +36,7 @@ /* 页面宽度小于1200px ------------------------------- */ -@media screen and (max-width: $lg) { +@media screen and (max-width: index.$lg) { .error { .error-flex { padding: 0 30px; diff --git a/web/src/theme/media/form.scss b/web/src/theme/media/form.scss index eb1d883072..ab382f2df7 100644 --- a/web/src/theme/media/form.scss +++ b/web/src/theme/media/form.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于576px ------------------------------- */ -@media screen and (max-width: $xs) { +@media screen and (max-width: index.$xs) { .el-form-item__label { width: 100% !important; text-align: left !important; diff --git a/web/src/theme/media/home.scss b/web/src/theme/media/home.scss index 5a2417e18d..88a966988a 100644 --- a/web/src/theme/media/home.scss +++ b/web/src/theme/media/home.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { .home-media, .home-media-sm { margin-top: 15px; @@ -11,7 +11,7 @@ /* 页面宽度小于1200px ------------------------------- */ -@media screen and (max-width: $lg) { +@media screen and (max-width: index.$lg) { .home-media-lg { margin-top: 15px; } diff --git a/web/src/theme/media/layout.scss b/web/src/theme/media/layout.scss index df8ce56adc..620991b9f7 100644 --- a/web/src/theme/media/layout.scss +++ b/web/src/theme/media/layout.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于576px ------------------------------- */ -@media screen and (max-width: $xs) { +@media screen and (max-width: index.$xs) { // MessageBox 弹框 .el-message-box { width: 80% !important; @@ -11,7 +11,7 @@ /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { // Breadcrumb 面包屑 .layout-navbars-breadcrumb-hide { display: none; diff --git a/web/src/theme/media/login.scss b/web/src/theme/media/login.scss index 29cdbb0191..f041ffa3bd 100644 --- a/web/src/theme/media/login.scss +++ b/web/src/theme/media/login.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于1200px ------------------------------- */ -@media screen and (max-width: $lg) and (min-width: $xs) { +@media screen and (max-width: index.$lg) and (min-width: index.$xs) { .login-container { .login-left { .login-left-img { @@ -23,7 +23,7 @@ /* 页面宽度小于576px ------------------------------- */ -@media screen and (max-width: $xs) { +@media screen and (max-width: index.$xs) { .login-container { .login-left { display: none; @@ -59,7 +59,7 @@ /* 页面宽度小于375px ------------------------------- */ -@media screen and (max-width: $us) { +@media screen and (max-width: index.$us) { .login-container { .login-right { .login-right-warp { diff --git a/web/src/theme/media/media.scss b/web/src/theme/media/media.scss index bed1c356a2..f5f00c744d 100644 --- a/web/src/theme/media/media.scss +++ b/web/src/theme/media/media.scss @@ -1,13 +1,13 @@ -@import './login.scss'; -@import './error.scss'; -@import './layout.scss'; -@import './personal.scss'; -@import './tagsView.scss'; -@import './home.scss'; -@import './chart.scss'; -@import './form.scss'; -@import './scrollbar.scss'; -@import './pagination.scss'; -@import './dialog.scss'; -@import './cityLinkage.scss'; -@import './date.scss'; +@use './login.scss'; +@use './error.scss'; +@use './layout.scss'; +@use './personal.scss'; +@use './tagsView.scss'; +@use './home.scss'; +@use './chart.scss'; +@use './form.scss'; +@use './scrollbar.scss'; +@use './pagination.scss'; +@use './dialog.scss'; +@use './cityLinkage.scss'; +@use './date.scss'; diff --git a/web/src/theme/media/pagination.scss b/web/src/theme/media/pagination.scss index 37af75f265..107703997b 100644 --- a/web/src/theme/media/pagination.scss +++ b/web/src/theme/media/pagination.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于576px ------------------------------- */ -@media screen and (max-width: $xs) { +@media screen and (max-width: index.$xs) { .el-pager, .el-pagination__jump { display: none !important; diff --git a/web/src/theme/media/personal.scss b/web/src/theme/media/personal.scss index 7ec0d4abcc..d5c1d8e3c1 100644 --- a/web/src/theme/media/personal.scss +++ b/web/src/theme/media/personal.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { .personal-info { padding-left: 0 !important; margin-top: 15px; diff --git a/web/src/theme/media/scrollbar.scss b/web/src/theme/media/scrollbar.scss index 968a79d595..9a36d80189 100644 --- a/web/src/theme/media/scrollbar.scss +++ b/web/src/theme/media/scrollbar.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { // 滚动条的宽度 ::-webkit-scrollbar { width: 3px !important; diff --git a/web/src/theme/media/tagsView.scss b/web/src/theme/media/tagsView.scss index b71674ef43..ad531daca3 100644 --- a/web/src/theme/media/tagsView.scss +++ b/web/src/theme/media/tagsView.scss @@ -1,8 +1,8 @@ -@import './index.scss'; +@use './index.scss' as index; /* 页面宽度小于768px ------------------------------- */ -@media screen and (max-width: $sm) { +@media screen and (max-width: index.$sm) { .tags-view-form { .tags-view-form-col { margin-bottom: 20px; diff --git a/web/src/views/system/personal/index.vue b/web/src/views/system/personal/index.vue index 216ae77062..8241d40df0 100644 --- a/web/src/views/system/personal/index.vue +++ b/web/src/views/system/personal/index.vue @@ -32,7 +32,7 @@
角色:
- {{ item.name }} + {{ item.name }}
@@ -84,10 +84,10 @@ - - - - + + + + @@ -181,8 +181,8 @@ import { Session } from '/@/utils/storage'; import { useRouter } from 'vue-router'; import { useUserInfo } from '/@/stores/userInfo'; import { successMessage } from '/@/utils/message'; -import {dictionary} from "/@/utils/dictionary"; -import {Md5} from "ts-md5"; +import { dictionary } from '/@/utils/dictionary'; +import { Md5 } from 'ts-md5'; const router = useRouter(); // 头像裁剪组件 @@ -237,7 +237,7 @@ const genderList = ref(); const getUserInfo = function () { api.GetUserInfo({}).then((res: any) => { const { data } = res; - genderList.value = dictionary('gender') + genderList.value = dictionary('gender'); state.personalForm.avatar = data.avatar || ''; state.personalForm.username = data.username || ''; state.personalForm.name = data.name || ''; @@ -335,10 +335,10 @@ const settingPassword = () => { if (valid) { api.UpdatePassword(userPasswordInfo).then((res: any) => { ElMessage.success('密码修改成功'); - setTimeout(() => { - Session.remove('token'); - router.push('/login'); - }, 1000); + setTimeout(() => { + Session.remove('token'); + router.push('/login'); + }, 1000); }); } else { // 校验失败 @@ -369,7 +369,7 @@ const uploadImg = (data: any) => { - \ No newline at end of file +} + \ No newline at end of file diff --git a/web/src/views/system/user/crud.tsx b/web/src/views/system/user/crud.tsx index e5d4ea9e6b..d5c1696865 100644 --- a/web/src/views/system/user/crud.tsx +++ b/web/src/views/system/user/crud.tsx @@ -91,7 +91,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp rowHandle: { //固定右侧 fixed: 'right', - width: 200, + width: 220, buttons: { view: { show: false, -- Gitee From 6e9b94aed22c30c8fab80acd2b9ad4ca94a63a9b Mon Sep 17 00:00:00 2001 From: ahui Date: Thu, 14 Aug 2025 10:35:51 +0800 Subject: [PATCH 16/17] =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=9A=84=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=83=A8=E9=97=A8=E6=9D=83=E9=99=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/dvadmin/system/models.py | 8 +++++ backend/dvadmin/system/views/user.py | 7 +++-- backend/dvadmin/utils/filters.py | 42 ++++++++++++++++++++----- backend/dvadmin/utils/viewset.py | 4 +-- web/src/views/system/user/crud.tsx | 47 +++++++++++++++++++++++----- 5 files changed, 88 insertions(+), 20 deletions(-) diff --git a/backend/dvadmin/system/models.py b/backend/dvadmin/system/models.py index ebdaaca701..7a71c67ed8 100644 --- a/backend/dvadmin/system/models.py +++ b/backend/dvadmin/system/models.py @@ -74,6 +74,14 @@ class Users(CoreModel, AbstractUser): blank=True, help_text="关联部门", ) + manage_dept = models.ManyToManyField( + to="Dept", + verbose_name="管理部门", + db_constraint=False, + blank=True, + help_text="管理部门", + related_name='manage_dept_set' + ) login_error_count = models.IntegerField(default=0, verbose_name="登录错误次数", help_text="登录错误次数") pwd_change_count = models.IntegerField(default=0,blank=True, verbose_name="密码修改次数", help_text="密码修改次数") objects = CustomUserManager() diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py index c31540c22c..4e09f90da9 100644 --- a/backend/dvadmin/system/views/user.py +++ b/backend/dvadmin/system/views/user.py @@ -90,6 +90,8 @@ class UserCreateSerializer(CustomModelSerializer): data = super().save(**kwargs) data.dept_belong_id = data.dept_id data.save() + if not self.validated_data.get('manage_dept', None): + data.manage_dept.add(data.dept_id) data.post.set(self.initial_data.get("post", [])) return data @@ -127,6 +129,8 @@ class UserUpdateSerializer(CustomModelSerializer): data = super().save(**kwargs) data.dept_belong_id = data.dept_id data.save() + if not self.validated_data.get('manage_dept', None): + data.manage_dept.add(data.dept_id) data.post.set(self.initial_data.get("post", [])) return data @@ -426,12 +430,9 @@ 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/filters.py b/backend/dvadmin/utils/filters.py index f61fc62ed2..9b98edc462 100644 --- a/backend/dvadmin/utils/filters.py +++ b/backend/dvadmin/utils/filters.py @@ -15,15 +15,16 @@ import six from django.db import models from django.db.models import Q, F from django.db.models.constants import LOOKUP_SEP -from django_filters import utils, FilterSet from django_filters.constants import ALL_FIELDS -from django_filters.filters import CharFilter, DateTimeFromToRangeFilter from django_filters.rest_framework import DjangoFilterBackend -from django_filters.utils import get_model_field +from django_filters.utils import get_model_field, translate_validation, deprecate +from rest_framework.request import Request from rest_framework.filters import BaseFilterBackend from django_filters.conf import settings -from dvadmin.system.models import Dept, ApiWhiteList, RoleMenuButtonPermission, MenuButton -from dvadmin.utils.models import CoreModel + +from dvadmin.system.models import Dept, ApiWhiteList, RoleMenuButtonPermission, MenuButton, Users +from util.currency import recursion_down_fast + class CoreModelFilterBankend(BaseFilterBackend): """ @@ -200,6 +201,31 @@ class DataLevelPermissionsFilter(BaseFilterBackend): return queryset.filter(dept_belong_id__in=list(set(dept_list))) +class DataLevelPermissionsSubFilter(DataLevelPermissionsFilter): + """数据级权限过滤的子过滤器,过滤管理部门字段manage_dept""" + + def _extracted_from_filter_queryset_33(self, request:Request, queryset, api, method): + u:Users = request.user + manage_depts = u.manage_dept.all() + if not manage_depts: + return queryset + dept_list = [] + for dept in manage_depts: + dept_list.extend(recursion_down_fast(dept, 'parent', 'id')) + # 自己创建的数据要能看到 + # 应对归属a管b、c等情况,如果自己创建数据则是a,不显式指定自己的数据就查不到 + if queryset.model._meta.model_name == 'dept': + return queryset.filter(Q(id__in=set(dept_list)) | Q(creator=u)) + return queryset.filter(Q(dept_belong_id__in=set(dept_list)) | + Q(creator=u)) + + +class DataLevelPermissionMargeFilter(DataLevelPermissionsFilter): + def _extracted_from_filter_queryset_33(self, request, queryset, api, method): + queryset = super()._extracted_from_filter_queryset_33(request, queryset, api, method) + return DataLevelPermissionsSubFilter._extracted_from_filter_queryset_33(self, request, queryset, api, method) + + class CustomDjangoFilterBackend(DjangoFilterBackend): lookup_prefixes = { "^": "istartswith", @@ -240,14 +266,14 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): # TODO: remove assertion in 2.1 if filterset_class is None and hasattr(view, "filter_class"): - utils.deprecate( + deprecate( "`%s.filter_class` attribute should be renamed `filterset_class`." % view.__class__.__name__ ) filterset_class = getattr(view, "filter_class", None) # TODO: remove assertion in 2.1 if filterset_fields is None and hasattr(view, "filter_fields"): - utils.deprecate( + deprecate( "`%s.filter_fields` attribute should be renamed `filterset_fields`." % view.__class__.__name__ ) self.filter_fields = getattr(view, "filter_fields", None) @@ -427,5 +453,5 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): return queryset if not filterset.is_valid() and self.raise_exception: - raise utils.translate_validation(filterset.errors) + raise translate_validation(filterset.errors) return filterset.qs diff --git a/backend/dvadmin/utils/viewset.py b/backend/dvadmin/utils/viewset.py index 89de67a28f..43160e696c 100644 --- a/backend/dvadmin/utils/viewset.py +++ b/backend/dvadmin/utils/viewset.py @@ -16,7 +16,7 @@ from drf_yasg.utils import swagger_auto_schema from rest_framework.decorators import action from rest_framework.viewsets import ModelViewSet -from dvadmin.utils.filters import DataLevelPermissionsFilter, CoreModelFilterBankend +from dvadmin.utils.filters import CoreModelFilterBankend, DataLevelPermissionMargeFilter from dvadmin.utils.import_export_mixin import ExportSerializerMixin, ImportSerializerMixin from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse from dvadmin.utils.permission import CustomPermission @@ -41,7 +41,7 @@ class CustomModelViewSet(ModelViewSet, ImportSerializerMixin, ExportSerializerMi update_serializer_class = None filter_fields = '__all__' search_fields = () - extra_filter_class = [CoreModelFilterBankend,DataLevelPermissionsFilter] + extra_filter_class = [CoreModelFilterBankend,DataLevelPermissionMargeFilter] permission_classes = [CustomPermission] import_field_dict = {} export_field_label = {} diff --git a/web/src/views/system/user/crud.tsx b/web/src/views/system/user/crud.tsx index d5c1696865..30515d976c 100644 --- a/web/src/views/system/user/crud.tsx +++ b/web/src/views/system/user/crud.tsx @@ -205,10 +205,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp }, }, dept: { - title: '部门', - search: { - disabled: true, - }, + title: '所属部门', type: 'dict-tree', dict: dict({ isTree: true, @@ -217,7 +214,7 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp label: 'name' }), column: { - minWidth: 200, //最小列宽 + minWidth: 300, //最小列宽 formatter({ value, row, index }) { return row.dept_name_all } @@ -243,6 +240,39 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp }, }, }, + manage_dept: { + title: '管理部门', + type: 'dict-tree', + dict: dict({ + isTree: true, + url: '/api/system/dept/all_dept/', + value: 'id', + label: 'name' + }), + column: { + minWidth: 300 + }, + form: { + value: [], + component: { + filterable: true, + multiple: true, + placeholder: '请选择', + clearable: true, + collapseTags: true, + maxCollapseTags: 2, + collapseTagsTooltip: true, + props: { + checkStrictly: true, + props: { + value: 'id', + label: 'name', + }, + }, + }, + helper: '不选则默认为所属部门', + }, + }, role: { title: '角色', search: { @@ -378,6 +408,9 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp dict: dict({ data: dictionary('button_status_bool'), }), + form: { + value: true + } }, avatar: { title: '头像', @@ -392,8 +425,8 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp }, ...commonCrudConfig({ dept_belong_id: { - form: true, - table: true + form: false, + table: false } }) }, -- Gitee From fa734dd4794480a53d1c4b1491c9a0ef82efffcd Mon Sep 17 00:00:00 2001 From: ahui Date: Fri, 22 Aug 2025 18:03:51 +0800 Subject: [PATCH 17/17] =?UTF-8?q?=E7=AE=A1=E7=90=86=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BA=A7=E8=BF=87=E6=BB=A4=E5=99=A8=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/dvadmin/utils/filters.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/backend/dvadmin/utils/filters.py b/backend/dvadmin/utils/filters.py index 9b98edc462..f62de0c211 100644 --- a/backend/dvadmin/utils/filters.py +++ b/backend/dvadmin/utils/filters.py @@ -206,18 +206,21 @@ class DataLevelPermissionsSubFilter(DataLevelPermissionsFilter): def _extracted_from_filter_queryset_33(self, request:Request, queryset, api, method): u:Users = request.user - manage_depts = u.manage_dept.all() - if not manage_depts: + if u.is_superuser: return queryset dept_list = [] - for dept in manage_depts: - dept_list.extend(recursion_down_fast(dept, 'parent', 'id')) + # 自己部门 交 管理部门 + if u.manage_dept.exists(): # 兼容旧数据 + for dept in u.manage_dept.all(): + dept_list.extend(recursion_down_fast(dept, 'parent', 'id')) + else: + dept_list = recursion_down_fast(u.dept, 'parent', 'id') + dept_list = set(recursion_down_fast(u.dept)) & set(dept_list) # 自己创建的数据要能看到 # 应对归属a管b、c等情况,如果自己创建数据则是a,不显式指定自己的数据就查不到 if queryset.model._meta.model_name == 'dept': - return queryset.filter(Q(id__in=set(dept_list)) | Q(creator=u)) - return queryset.filter(Q(dept_belong_id__in=set(dept_list)) | - Q(creator=u)) + return queryset.filter(Q(id__in=dept_list) | Q(creator=u)) + return queryset.filter(Q(dept_belong_id__in=dept_list) | Q(creator=u)) class DataLevelPermissionMargeFilter(DataLevelPermissionsFilter): -- Gitee