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