From 0dcf8ae7941e6364ed2242ca46db7ee722c887a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=A8=E5=AD=90-=E6=9D=8E?= <1537080775@qq.com>
Date: Thu, 4 Jul 2024 05:51:38 +0000
Subject: [PATCH 01/12] =?UTF-8?q?update=20backend/dvadmin/utils/models.py.?=
=?UTF-8?q?=20=E8=B7=A8models=E5=BC=95=E7=94=A8=E6=A8=A1=E5=9E=8B=E7=9A=84?=
=?UTF-8?q?=E6=97=B6=E5=80=99=EF=BC=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 木子-李 <1537080775@qq.com>
---
backend/dvadmin/utils/models.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/backend/dvadmin/utils/models.py b/backend/dvadmin/utils/models.py
index fb0c2ba..7073d0f 100644
--- a/backend/dvadmin/utils/models.py
+++ b/backend/dvadmin/utils/models.py
@@ -117,9 +117,13 @@ def get_all_models_objects(model_name=None):
def get_model_from_app(app_name):
"""获取模型里的字段"""
model_module = import_module(app_name + '.models')
+ exclude_models = getattr(model_module, 'exclude_models', [])
filter_model = [
- getattr(model_module, item) for item in dir(model_module)
- if item != 'CoreModel' and issubclass(getattr(model_module, item).__class__, models.base.ModelBase)
+ value for key, value in model_module.__dict__.items()
+ if key != 'CoreModel'
+ and isinstance(value, type)
+ and issubclass(value, models.Model)
+ and key not in exclude_models
]
model_list = []
for model in filter_model:
From 71ca7370e214fb56738b3c45f0819bd13d6baa9a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=A8=E5=AD=90-=E6=9D=8E?= <1537080775@qq.com>
Date: Thu, 4 Jul 2024 14:56:42 +0000
Subject: [PATCH 02/12] update web/src/utils/columnPermission.ts.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 木子-李 <1537080775@qq.com>
---
web/src/utils/columnPermission.ts | 24 +++++-------------------
1 file changed, 5 insertions(+), 19 deletions(-)
diff --git a/web/src/utils/columnPermission.ts b/web/src/utils/columnPermission.ts
index 4a9d9f1..aff1441 100644
--- a/web/src/utils/columnPermission.ts
+++ b/web/src/utils/columnPermission.ts
@@ -24,31 +24,17 @@ 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']
}
From 630ec1e774ca3b530abbce69480641d0ff1ed2c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com>
Date: Fri, 5 Jul 2024 10:24:07 +0800
Subject: [PATCH 03/12] =?UTF-8?q?feat(20240705=5FtableSelector):=20?=
=?UTF-8?q?=E8=A1=A8=E6=A0=BC=E9=80=89=E6=8B=A9=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 增加树形结构懒加载
---
web/src/components/tableSelector/index.vue | 296 +++++++++++----------
1 file changed, 152 insertions(+), 144 deletions(-)
diff --git a/web/src/components/tableSelector/index.vue b/web/src/components/tableSelector/index.vue
index 7ad3cad..ab6ca7d 100644
--- a/web/src/components/tableSelector/index.vue
+++ b/web/src/components/tableSelector/index.vue
@@ -1,203 +1,211 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 6d587fc1e2761790c515553650f55eb5b29cfe3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com>
Date: Fri, 5 Jul 2024 17:24:36 +0800
Subject: [PATCH 04/12] =?UTF-8?q?feat(20240705=5Farea):=20=E5=9C=B0?=
=?UTF-8?q?=E5=8C=BA=E7=AE=A1=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 优化地区管理:增删改查
- 优化tableSelect组件:增加树形结构和懒加载
---
backend/dvadmin/system/views/area.py | 50 ++-
web/src/components/tableSelector/index.vue | 4 +-
web/src/utils/columnPermission.ts | 1 -
web/src/views/system/areas/crud.tsx | 436 ++++++++++-----------
4 files changed, 237 insertions(+), 254 deletions(-)
diff --git a/backend/dvadmin/system/views/area.py b/backend/dvadmin/system/views/area.py
index 3a72728..7a19eb0 100644
--- a/backend/dvadmin/system/views/area.py
+++ b/backend/dvadmin/system/views/area.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
+import pypinyin
from django.db.models import Q
from rest_framework import serializers
@@ -15,6 +16,11 @@ class AreaSerializer(CustomModelSerializer):
"""
pcode_count = serializers.SerializerMethodField(read_only=True)
hasChild = serializers.SerializerMethodField()
+ pcode_info = serializers.SerializerMethodField()
+
+ def get_pcode_info(self, instance):
+ pcode = Area.objects.filter(code=instance.pcode_id).values("name", "code")
+ return pcode
def get_pcode_count(self, instance: Area):
return Area.objects.filter(pcode=instance).count()
@@ -36,6 +42,18 @@ class AreaCreateUpdateSerializer(CustomModelSerializer):
地区管理 创建/更新时的列化器
"""
+ def to_internal_value(self, data):
+ pinyin = ''.join([''.join(i) for i in pypinyin.pinyin(data["name"], style=pypinyin.NORMAL)])
+ data["level"] = 1
+ data["pinyin"] = pinyin
+ data["initials"] = pinyin[0].upper() if pinyin else "#"
+ pcode = data["pcode"] if 'pcode' in data else None
+ if pcode:
+ pcode = Area.objects.get(pk=pcode)
+ data["pcode"] = pcode.code
+ data["level"] = pcode.level + 1
+ return super().to_internal_value(data)
+
class Meta:
model = Area
fields = '__all__'
@@ -52,20 +70,28 @@ class AreaViewSet(CustomModelViewSet, FieldPermissionMixin):
"""
queryset = Area.objects.all()
serializer_class = AreaSerializer
+ create_serializer_class = AreaCreateUpdateSerializer
+ update_serializer_class = AreaCreateUpdateSerializer
extra_filter_class = []
- def get_queryset(self):
+ def list(self, request, *args, **kwargs):
self.request.query_params._mutable = True
params = self.request.query_params
- pcode = params.get('pcode', None)
- page = params.get('page', None)
- limit = params.get('limit', None)
- if page:
- del params['page']
- if limit:
- del params['limit']
- if params and pcode:
- queryset = self.queryset.filter(enable=True, pcode=pcode)
- else:
+ known_params = {'page', 'limit', 'pcode'}
+ # 使用集合操作检查是否有未知参数
+ other_params_exist = any(param not in known_params for param in params)
+ if other_params_exist:
queryset = self.queryset.filter(enable=True)
- return queryset
+ else:
+ pcode = params.get('pcode', None)
+ params['limit'] = 999
+ if params and pcode:
+ queryset = self.queryset.filter(enable=True, pcode=pcode)
+ else:
+ queryset = self.queryset.filter(enable=True, level=1)
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True, request=request)
+ return self.get_paginated_response(serializer.data)
+ serializer = self.get_serializer(queryset, many=True, request=request)
+ return SuccessResponse(data=serializer.data, msg="获取成功")
diff --git a/web/src/components/tableSelector/index.vue b/web/src/components/tableSelector/index.vue
index ab6ca7d..8e8c91a 100644
--- a/web/src/components/tableSelector/index.vue
+++ b/web/src/components/tableSelector/index.vue
@@ -2,7 +2,7 @@
{
const handleCurrentChange = (val: any) => {
const { tableConfig } = props;
if (!tableConfig.isMultiple && val) {
- data.value = val[tableConfig.label];
+ data.value = [val[tableConfig.label]];
emit('update:modelValue', val[tableConfig.value]);
}
};
diff --git a/web/src/utils/columnPermission.ts b/web/src/utils/columnPermission.ts
index aff1441..7ef06de 100644
--- a/web/src/utils/columnPermission.ts
+++ b/web/src/utils/columnPermission.ts
@@ -28,7 +28,6 @@ export const handleColumnPermission = async (func: Function, crudOptions: any,ex
if (excludeColumns.includes(item.field_name)) {
continue
} else if(item.field_name === col) {
- columns[col].column.show = item['is_query']
// 如果列表不可见,则禁止在列设置中选择
// 只有列表不可见,才修改列配置,这样才不影响默认的配置
if(!item['is_query']){
diff --git a/web/src/views/system/areas/crud.tsx b/web/src/views/system/areas/crud.tsx
index 01d1860..1a1e47b 100644
--- a/web/src/views/system/areas/crud.tsx
+++ b/web/src/views/system/areas/crud.tsx
@@ -1,244 +1,202 @@
import * as api from './api';
-import {
- dict,
- UserPageQuery,
- AddReq,
- DelReq,
- EditReq,
- compute,
- CreateCrudOptionsProps,
- CreateCrudOptionsRet
-} from '@fast-crud/fast-crud';
-import {dictionary} from '/@/utils/dictionary';
-import {successMessage} from '/@/utils/message';
-import {auth} from "/@/utils/authFunction";
+import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import { dictionary } from '/@/utils/dictionary';
+import { successMessage } from '/@/utils/message';
+import { auth } from '/@/utils/authFunction';
+import tableSelector from '/@/components/tableSelector/index.vue';
+import { shallowRef } from 'vue';
-export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps): CreateCrudOptionsRet {
- const pageRequest = async (query: UserPageQuery) => {
- return await api.GetList(query);
- };
- const editRequest = async ({form, row}: EditReq) => {
- form.id = row.id;
- return await api.UpdateObj(form);
- };
- const delRequest = async ({row}: DelReq) => {
- return await api.DelObj(row.id);
- };
- const addRequest = async ({form}: AddReq) => {
- return await api.AddObj(form);
- };
+export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+ const pageRequest = async (query: UserPageQuery) => {
+ return await api.GetList(query);
+ };
+ const editRequest = async ({ form, row }: EditReq) => {
+ form.id = row.id;
+ return await api.UpdateObj(form);
+ };
+ const delRequest = async ({ row }: DelReq) => {
+ return await api.DelObj(row.id);
+ };
+ const addRequest = async ({ form }: AddReq) => {
+ return await api.AddObj(form);
+ };
- /**
- * 懒加载
- * @param row
- * @returns {Promise}
- */
- const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => {
- pageRequest({pcode: tree.code}).then((res: APIResponseData) => {
- resolve(res.data);
- });
- };
+ /**
+ * 懒加载
+ * @param row
+ * @returns {Promise}
+ */
+ const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => {
+ pageRequest({ pcode: tree.code }).then((res: APIResponseData) => {
+ resolve(res.data);
+ });
+ };
- return {
- crudOptions: {
- request: {
- pageRequest,
- addRequest,
- editRequest,
- delRequest,
- },
- actionbar: {
- buttons: {
- add: {
- show: auth('area:Create'),
- }
- }
- },
- rowHandle: {
- //固定右侧
- fixed: 'right',
- width: 200,
- buttons: {
- view: {
- show: false,
- },
- edit: {
- iconRight: 'Edit',
- type: 'text',
- show: auth('area:Update')
- },
- remove: {
- iconRight: 'Delete',
- type: 'text',
- show: auth('area:Delete')
- },
- },
- },
- pagination: {
- show: false,
- },
- table: {
- rowKey: 'id',
- lazy: true,
- load: loadContentMethod,
- treeProps: {children: 'children', hasChildren: 'hasChild'},
- },
- columns: {
- _index: {
- title: '序号',
- form: {show: false},
- column: {
- type: 'index',
- align: 'center',
- width: '70px',
- columnSetDisabled: true, //禁止在列设置中选择
- },
- },
- // pcode: {
- // title: '父级地区',
- // show: false,
- // search: {
- // show: true,
- // },
- // type: 'dict-tree',
- // form: {
- // component: {
- // showAllLevels: false, // 仅显示最后一级
- // props: {
- // elProps: {
- // clearable: true,
- // showAllLevels: false, // 仅显示最后一级
- // props: {
- // checkStrictly: true, // 可以不需要选到最后一级
- // emitPath: false,
- // clearable: true,
- // },
- // },
- // },
- // },
- // },
- // },
- name: {
- title: '名称',
- search: {
- show: true,
- },
- treeNode: true,
- type: 'input',
- column: {
- minWidth: 120,
- },
- form: {
- rules: [
- // 表单校验规则
- {required: true, message: '名称必填项'},
- ],
- component: {
- placeholder: '请输入名称',
- },
- },
- },
- code: {
- title: '地区编码',
- search: {
- show: true,
- },
- type: 'input',
- column: {
- minWidth: 90,
- },
- form: {
- rules: [
- // 表单校验规则
- {required: true, message: '地区编码必填项'},
- ],
- component: {
- placeholder: '请输入地区编码',
- },
- },
- },
- pinyin: {
- title: '拼音',
- search: {
- disabled: true,
- },
- type: 'input',
- column: {
- minWidth: 120,
- },
- form: {
- rules: [
- // 表单校验规则
- {required: true, message: '拼音必填项'},
- ],
- component: {
- placeholder: '请输入拼音',
- },
- },
- },
- level: {
- title: '地区层级',
- search: {
- disabled: true,
- },
- type: 'input',
- column: {
- minWidth: 100,
- },
- form: {
- disabled: false,
- rules: [
- // 表单校验规则
- {required: true, message: '拼音必填项'},
- ],
- component: {
- placeholder: '请输入拼音',
- },
- },
- },
- initials: {
- title: '首字母',
- column: {
- minWidth: 100,
- },
- form: {
- rules: [
- // 表单校验规则
- {required: true, message: '首字母必填项'},
- ],
-
- component: {
- placeholder: '请输入首字母',
- },
- },
- },
- enable: {
- title: '是否启用',
- search: {
- show: true,
- },
- type: 'dict-radio',
- column: {
- minWidth: 90,
- component: {
- name: 'fs-dict-switch',
- activeText: '',
- inactiveText: '',
- style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
- onChange: compute((context) => {
- return () => {
- api.UpdateObj(context.row).then((res: APIResponseData) => {
- successMessage(res.msg as string);
- });
- };
- }),
- },
- },
- dict: dict({
- data: dictionary('button_status_bool'),
- }),
- },
- },
- },
- };
+ return {
+ crudOptions: {
+ request: {
+ pageRequest,
+ addRequest,
+ editRequest,
+ delRequest,
+ },
+ actionbar: {
+ buttons: {
+ add: {
+ show: auth('area:Create'),
+ },
+ },
+ },
+ rowHandle: {
+ //固定右侧
+ fixed: 'right',
+ width: 200,
+ buttons: {
+ view: {
+ show: false,
+ },
+ edit: {
+ iconRight: 'Edit',
+ type: 'text',
+ show: auth('area:Update'),
+ },
+ remove: {
+ iconRight: 'Delete',
+ type: 'text',
+ show: auth('area:Delete'),
+ },
+ },
+ },
+ pagination: {
+ show: false,
+ },
+ table: {
+ rowKey: 'id',
+ lazy: true,
+ load: loadContentMethod,
+ treeProps: { children: 'children', hasChildren: 'hasChild' },
+ },
+ columns: {
+ _index: {
+ title: '序号',
+ form: { show: false },
+ column: {
+ type: 'index',
+ align: 'center',
+ width: '70px',
+ columnSetDisabled: true, //禁止在列设置中选择
+ },
+ },
+ name: {
+ title: '名称',
+ search: {
+ show: true,
+ },
+ treeNode: true,
+ type: 'input',
+ column: {
+ minWidth: 120,
+ },
+ form: {
+ rules: [
+ // 表单校验规则
+ { required: true, message: '名称必填项' },
+ ],
+ component: {
+ placeholder: '请输入名称',
+ },
+ },
+ },
+ pcode: {
+ title: '父级地区',
+ search: {
+ disabled: true,
+ },
+ width: 130,
+ type: 'table-selector',
+ form: {
+ component: {
+ name: shallowRef(tableSelector),
+ vModel: 'modelValue',
+ displayLabel: compute(({ row }) => {
+ if (row) {
+ return row.pcode_info;
+ }
+ return null;
+ }),
+ tableConfig: {
+ url: '/api/system/area/',
+ label: 'name',
+ value: 'id',
+ isTree: true,
+ isMultiple: false,
+ lazy: true,
+ load: loadContentMethod,
+ treeProps: { children: 'children', hasChildren: 'hasChild' },
+ columns: [
+ {
+ prop: 'name',
+ label: '地区',
+ width: 150,
+ },
+ {
+ prop: 'code',
+ label: '地区编码',
+ },
+ ],
+ },
+ },
+ },
+ column: {
+ show: false,
+ },
+ },
+ code: {
+ title: '地区编码',
+ search: {
+ show: true,
+ },
+ type: 'input',
+ column: {
+ minWidth: 90,
+ },
+ form: {
+ rules: [
+ // 表单校验规则
+ { required: true, message: '地区编码必填项' },
+ ],
+ component: {
+ placeholder: '请输入地区编码',
+ },
+ },
+ },
+ enable: {
+ title: '是否启用',
+ search: {
+ show: true,
+ },
+ type: 'dict-radio',
+ column: {
+ minWidth: 90,
+ component: {
+ name: 'fs-dict-switch',
+ activeText: '',
+ inactiveText: '',
+ style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
+ onChange: compute((context) => {
+ return () => {
+ api.UpdateObj(context.row).then((res: APIResponseData) => {
+ successMessage(res.msg as string);
+ });
+ };
+ }),
+ },
+ },
+ dict: dict({
+ data: dictionary('button_status_bool'),
+ }),
+ },
+ },
+ },
+ };
};
From 6f8bae8d5ced721319250178351a0986f377f89a Mon Sep 17 00:00:00 2001
From: lxy <46486798@qq.com>
Date: Mon, 8 Jul 2024 09:33:51 +0000
Subject: [PATCH 05/12] =?UTF-8?q?update=20backend/dvadmin/utils/middleware?=
=?UTF-8?q?.py.=20=E5=BD=93=E5=90=8C=E4=B8=80=E6=97=B6=E5=88=BB=E8=BF=9B?=
=?UTF-8?q?=E6=9D=A5=E5=A4=9A=E4=B8=AA=E8=AF=B7=E6=B1=82=E4=B8=94=E9=83=BD?=
=?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=AE=8C=E6=88=90=E5=93=8D=E5=BA=94=E6=97=B6?=
=?UTF-8?q?=EF=BC=8Coperation=5Flog=5Fid=E4=BC=9A=E4=BF=9D=E7=95=99?=
=?UTF-8?q?=E6=9C=80=E5=90=8E=E4=B8=80=E4=B8=AA=E8=BF=9B=E6=9D=A5=E7=9A=84?=
=?UTF-8?q?ID=EF=BC=8C=E5=AF=BC=E8=87=B4=E4=B9=8B=E5=89=8D=E6=8C=89?=
=?UTF-8?q?=E8=BF=9B=E6=9D=A5=E7=9A=84=E8=AF=B7=E6=B1=82=E8=AE=B0=E5=BD=95?=
=?UTF-8?q?=E5=88=B0=E5=90=8C=E4=B8=80=E4=B8=AAid=E4=B8=8A=EF=BC=8C?=
=?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=97=A5=E5=BF=97=E8=AE=B0=E5=BD=95=E4=B8=A2?=
=?UTF-8?q?=E5=A4=B1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: lxy <46486798@qq.com>
---
backend/dvadmin/utils/middleware.py | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/backend/dvadmin/utils/middleware.py b/backend/dvadmin/utils/middleware.py
index f4717a4..6029c5a 100644
--- a/backend/dvadmin/utils/middleware.py
+++ b/backend/dvadmin/utils/middleware.py
@@ -32,6 +32,14 @@ class ApiLoggingMiddleware(MiddlewareMixin):
request.request_path = get_request_path(request)
def __handle_response(self, request, response):
+
+ # 判断有无log_id属性,使用All记录时,会出现此情况
+ if request.request_data.get('log_id', None) is None:
+ return
+
+ # 移除log_id,不记录此ID
+ log_id = request.request_data.pop('log_id')
+
# request_data,request_ip由PermissionInterfaceMiddleware中间件中添加的属性
body = getattr(request, 'request_data', {})
# 请求含有password则用*替换掉(暂时先用于所有接口的password请求参数)
@@ -60,7 +68,7 @@ class ApiLoggingMiddleware(MiddlewareMixin):
'status': True if response.data.get('code') in [2000, ] else False,
'json_result': {"code": response.data.get('code'), "msg": response.data.get('msg')},
}
- operation_log, creat = OperationLog.objects.update_or_create(defaults=info, id=self.operation_log_id)
+ operation_log, creat = OperationLog.objects.update_or_create(defaults=info, id=log_id)
if not operation_log.request_modular and settings.API_MODEL_MAP.get(request.request_path, None):
operation_log.request_modular = settings.API_MODEL_MAP[request.request_path]
operation_log.save()
@@ -71,7 +79,8 @@ class ApiLoggingMiddleware(MiddlewareMixin):
if self.methods == 'ALL' or request.method in self.methods:
log = OperationLog(request_modular=get_verbose_name(view_func.cls.queryset))
log.save()
- self.operation_log_id = log.id
+ # self.operation_log_id = log.id
+ request.request_data['log_id'] = log.id
return
From 3dcef90bbe9ea0377898b3e461909c37fb4c2d68 Mon Sep 17 00:00:00 2001
From: lxy <46486798@qq.com>
Date: Tue, 9 Jul 2024 03:56:45 +0000
Subject: [PATCH 06/12] =?UTF-8?q?update=20backend/dvadmin/utils/field=5Fpe?=
=?UTF-8?q?rmission.py.=20=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA=E8=B4=A6?=
=?UTF-8?q?=E5=8F=B7=E6=8B=A5=E6=9C=89=E5=A4=9A=E4=B8=AA=E8=A7=92=E8=89=B2?=
=?UTF-8?q?=E6=9D=83=E9=99=90=EF=BC=8C=E5=AF=BC=E8=87=B4=E5=89=8D=E7=AB=AF?=
=?UTF-8?q?=E6=9F=90=E4=BA=9B=E6=A8=A1=E5=9D=97=E6=B2=A1=E5=A4=84=E7=90=86?=
=?UTF-8?q?=E5=A5=BD=E5=AF=BC=E8=87=B4=E7=9A=84=E6=97=A0=E6=9D=83=E9=99=90?=
=?UTF-8?q?=E9=97=AE=E9=A2=98=EF=BC=88=E5=9C=B0=E5=8C=BA=E7=AE=A1=E7=90=86?=
=?UTF-8?q?=E3=80=81=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=EF=BC=89=EF=BC=8C?=
=?UTF-8?q?=E5=90=88=E5=B9=B6=E6=9D=83=E9=99=90=EF=BC=8C=E4=B8=8D=E5=86=8D?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=A4=9A=E4=B8=AA=E7=9B=B8=E5=90=8C=E7=9A=84?=
=?UTF-8?q?=E5=AD=97=E6=AE=B5=E5=90=8D=E6=9D=83=E9=99=90=EF=BC=8C=E7=9B=B8?=
=?UTF-8?q?=E5=90=8C=E5=AD=97=E6=AE=B5=E5=90=8D=E7=9A=84=E6=9D=83=E9=99=90?=
=?UTF-8?q?True=E5=80=BC=E4=BF=9D=E7=95=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: lxy <46486798@qq.com>
---
backend/dvadmin/utils/field_permission.py | 33 +++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/backend/dvadmin/utils/field_permission.py b/backend/dvadmin/utils/field_permission.py
index 20b4cb9..7c259f6 100644
--- a/backend/dvadmin/utils/field_permission.py
+++ b/backend/dvadmin/utils/field_permission.py
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
+
+from itertools import groupby
+
from django.db.models import F
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
@@ -35,4 +38,34 @@ class FieldPermissionMixin:
data= FieldPermission.objects.filter(
field__model=model['model'],role__in=roles
).values( 'is_create', 'is_query', 'is_update',field_name=F('field__field_name'))
+
+ """
+ 合并权限
+
+ 这段代码首先根据 field_name 对列表进行排序,
+ 然后使用 groupby 按 field_name 进行分组。
+ 对于每个组,它创建一个新的字典 merged,
+ 并遍历组中的每个字典,将布尔值字段使用逻辑或(or)操作符进行合并(如果 merged 中还没有该字段,则默认为 False),
+ 其他字段(如 field_name)则直接取组的关键字(即 key)
+ """
+
+ # 使用field_name对列表进行分组, # groupby 需要先对列表进行排序,因为它只能对连续相同的元素进行分组。
+ grouped = groupby(sorted(list(data), key=lambda x: x['field_name']), key=lambda x: x['field_name'])
+
+ data = []
+
+ # 遍历分组,合并权限
+ for key, group in grouped:
+
+ # 初始化一个空字典来存储合并后的结果
+ merged = {}
+ for item in group:
+ # 合并权限, True值优先
+ merged['is_create'] = merged.get('is_create', False) or item['is_create']
+ merged['is_query'] = merged.get('is_query', False) or item['is_query']
+ merged['is_update'] = merged.get('is_update', False) or item['is_update']
+ merged['field_name'] = key
+
+ data.append(merged)
+
return DetailResponse(data=data)
\ No newline at end of file
From 1942f1af4ef0ae666ef7293996acc2f591108a71 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=80=81=E9=AB=98?= <794071084@qq.com>
Date: Wed, 10 Jul 2024 02:22:37 +0000
Subject: [PATCH 07/12] =?UTF-8?q?=E6=9B=B4=E6=94=B9npm=E9=95=9C=E5=83=8F?=
=?UTF-8?q?=E6=BA=90=E4=B8=BAnpmmirror.com=20=E5=B0=86npm=E5=AE=89?=
=?UTF-8?q?=E8=A3=85=E5=91=BD=E4=BB=A4=E4=B8=AD=E7=9A=84registry=E4=BB=8E?=
=?UTF-8?q?=E6=B7=98=E5=AE=9Dnpm=E6=BA=90=E6=94=B9=E4=B8=BAnpmmirror.com?=
=?UTF-8?q?=EF=BC=8C=E5=8E=9F=E6=9C=89npm=E6=BA=90=E5=B7=B2=E7=BB=8F?=
=?UTF-8?q?=E5=A4=B1=E6=95=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 老高 <794071084@qq.com>
---
README.zh.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.zh.md b/README.zh.md
index dea0106..a95a364 100644
--- a/README.zh.md
+++ b/README.zh.md
@@ -114,7 +114,7 @@ cd web
# 安装依赖
npm install yarn
-yarn install --registry=https://registry.npm.taobao.org
+yarn install --registry=https://registry.npmmirror.com
# 启动服务
yarn build
From 9a8506448f76d1087f3a31c2704c30bf3850bfd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A5=BD=E5=A5=87=E5=AE=9D=E5=AE=9D?=
<11259906+haoqibb@user.noreply.gitee.com>
Date: Thu, 25 Jul 2024 01:56:55 +0000
Subject: [PATCH 08/12] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E6=9C=9F?=
=?UTF-8?q?=E6=9C=9F=E9=97=B4=E6=9D=A1=E4=BB=B6=E8=BF=87=E6=BB=A4=EF=BC=8C?=
=?UTF-8?q?=E5=8C=85=E5=90=AB=E6=88=AA=E6=AD=A2=E6=97=A5=E6=9C=9F=E5=BD=93?=
=?UTF-8?q?=E5=89=8D=E6=95=B0=E6=8D=AE=20=E5=9B=A0=E4=B8=BA=E5=88=9B?=
=?UTF-8?q?=E5=BB=BA=E6=97=A5=E6=9C=9F=E6=98=AF=E4=B8=80=E4=B8=AAdatetime?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=B1=BB=E5=9E=8B=EF=BC=8C=E7=9B=B4=E6=8E=A5?=
=?UTF-8?q?=E4=BD=BF=E7=94=A8lte=E4=B8=8D=E4=BC=9A=E5=8C=85=E5=90=AB?=
=?UTF-8?q?=E6=88=AA=E6=AD=A2=E6=97=A5=E7=9A=84=E6=95=B0=E6=8D=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 好奇宝宝 <11259906+haoqibb@user.noreply.gitee.com>
---
backend/dvadmin/utils/filters.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/dvadmin/utils/filters.py b/backend/dvadmin/utils/filters.py
index 05c0dfb..d09a8a1 100644
--- a/backend/dvadmin/utils/filters.py
+++ b/backend/dvadmin/utils/filters.py
@@ -37,11 +37,11 @@ class CoreModelFilterBankend(BaseFilterBackend):
if any([create_datetime_after, create_datetime_before, update_datetime_after, update_datetime_before]):
create_filter = Q()
if create_datetime_after and create_datetime_before:
- create_filter &= Q(create_datetime__gte=create_datetime_after) & Q(create_datetime__lte=create_datetime_before)
+ create_filter &= Q(create_datetime__gte=create_datetime_after) & Q(create_datetime__lte=f'{create_datetime_before} 23:59:59')
elif create_datetime_after:
create_filter &= Q(create_datetime__gte=create_datetime_after)
elif create_datetime_before:
- create_filter &= Q(create_datetime__lte=create_datetime_before)
+ create_filter &= Q(create_datetime__lte=f'{create_datetime_before} 23:59:59')
# 更新时间范围过滤条件
update_filter = Q()
From 3979628281b89e0b710a88eeaa51fbb57acebf4a Mon Sep 17 00:00:00 2001
From: lxy <46486798@qq.com>
Date: Mon, 29 Jul 2024 03:19:29 +0000
Subject: [PATCH 09/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=99=BB=E5=BD=95?=
=?UTF-8?q?=E6=AC=A1=E6=95=B0=E6=B2=A1=E6=9C=89=E4=BF=9D=E5=AD=98=E9=97=AE?=
=?UTF-8?q?=E9=A2=98=20update=20backend/dvadmin/system/views/login.py.=20?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=99=BB=E5=BD=95=E6=AC=A1=E6=95=B0=E6=B2=A1?=
=?UTF-8?q?=E6=9C=89=E4=BF=9D=E5=AD=98=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: lxy <46486798@qq.com>
---
backend/dvadmin/system/views/login.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/backend/dvadmin/system/views/login.py b/backend/dvadmin/system/views/login.py
index 4023a82..1996906 100644
--- a/backend/dvadmin/system/views/login.py
+++ b/backend/dvadmin/system/views/login.py
@@ -124,6 +124,7 @@ class LoginSerializer(TokenObtainPairSerializer):
user.is_active = False
user.save()
raise CustomValidationError("账号已被锁定,联系管理员解锁")
+ user.save()
count = 5 - user.login_error_count
raise CustomValidationError(f"账号/密码错误;重试{count}次后将被锁定~")
From 1981567c59bfdc01243730bf4b8d5a297966d291 Mon Sep 17 00:00:00 2001
From: lxy <10179281+lxy0722@user.noreply.gitee.com>
Date: Tue, 30 Jul 2024 17:12:54 +0800
Subject: [PATCH 10/12] =?UTF-8?q?fixed=2092a17fa=20from=20https://gitee.co?=
=?UTF-8?q?m/lxy0722/django-vue3-admin/pulls/1=20=E8=A7=A3=E5=86=B3?=
=?UTF-8?q?=E5=9C=A8=E6=B7=B1=E8=89=B2=E6=A8=A1=E5=BC=8F=E4=B8=8B=E7=9A=84?=
=?UTF-8?q?=E8=83=8C=E6=99=AF=E8=89=B2=E6=98=BE=E7=A4=BA=E5=BC=82=E5=B8=B8?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
web/src/views/system/dept/components/DeptUserCom/index.vue | 3 ++-
web/src/views/system/dept/index.vue | 2 +-
web/src/views/system/menu/index.vue | 2 +-
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/web/src/views/system/dept/components/DeptUserCom/index.vue b/web/src/views/system/dept/components/DeptUserCom/index.vue
index fb50ff9..f52db55 100644
--- a/web/src/views/system/dept/components/DeptUserCom/index.vue
+++ b/web/src/views/system/dept/components/DeptUserCom/index.vue
@@ -277,7 +277,8 @@ const { resetCrudOptions } = useCrud({
padding: 0 10px;
border-radius: 8px 0 0 8px;
box-sizing: border-box;
- background-color: #fff;
+ color: var(--next-bg-topBarColor);
+ background-color: var(--el-fill-color-blank);;
}
.dept-user-com-table {
height: calc(100% - 200px);
diff --git a/web/src/views/system/dept/index.vue b/web/src/views/system/dept/index.vue
index 60b803b..52df92b 100644
--- a/web/src/views/system/dept/index.vue
+++ b/web/src/views/system/dept/index.vue
@@ -133,7 +133,7 @@ onMounted(() => {
}
.dept-left {
- background-color: #fff;
+ background-color: var(--el-fill-color-blank);;
border-radius: 0 8px 8px 0;
padding: 10px;
}
diff --git a/web/src/views/system/menu/index.vue b/web/src/views/system/menu/index.vue
index 0637a3f..f7d05bd 100644
--- a/web/src/views/system/menu/index.vue
+++ b/web/src/views/system/menu/index.vue
@@ -138,7 +138,7 @@ onMounted(() => {
.menu-box {
height: 100%;
padding: 10px;
- background-color: #fff;
+ background-color: var(--el-fill-color-blank);;
box-sizing: border-box;
}
From 5cf2eef7adfa5ec0e178005a6be1f5bec5565508 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9C=A8=E5=AD=90-=E6=9D=8E?= <1537080775@qq.com>
Date: Thu, 1 Aug 2024 14:05:49 +0000
Subject: [PATCH 11/12] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dcelery=E6=97=B6?=
=?UTF-8?q?=E5=8C=BA=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: 木子-李 <1537080775@qq.com>
---
backend/application/__init__.py | 5 +++++
backend/application/celery.py | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/backend/application/__init__.py b/backend/application/__init__.py
index e69de29..b7ddb6f 100644
--- a/backend/application/__init__.py
+++ b/backend/application/__init__.py
@@ -0,0 +1,5 @@
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
+from .celery import app as celery_app
+
+__all__ = ('celery_app',)
\ No newline at end of file
diff --git a/backend/application/celery.py b/backend/application/celery.py
index d57b92a..10bce56 100644
--- a/backend/application/celery.py
+++ b/backend/application/celery.py
@@ -15,7 +15,7 @@ else:
from celery import Celery
app = Celery(f"application")
-app.config_from_object('django.conf:settings')
+app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
platforms.C_FORCE_ROOT = True
From b6e05c997d03889966257d0aeaa7924f5cccfffc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9D=8E=E5=B0=8F=E6=B6=9B?= <1537080775@qq.com>
Date: Tue, 27 Aug 2024 17:31:29 +0800
Subject: [PATCH 12/12] =?UTF-8?q?feat(20240827=5FBatchDelete):=20=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
1. 增加表格多选
2. 运行跨页多选
3. 显示多选数据
4. 移除多选数据
5. 增加批量删除
---
web/src/utils/columnPermission.ts | 2 +-
.../menu/components/MenuButtonCom/api.ts | 7 +
.../menu/components/MenuButtonCom/crud.tsx | 52 ++++
.../menu/components/MenuButtonCom/index.vue | 61 ++++-
.../menu/components/MenuFieldCom/api.ts | 7 +
.../menu/components/MenuFieldCom/crud.tsx | 51 +++-
.../menu/components/MenuFieldCom/index.vue | 232 ++++++++++--------
web/src/views/system/menu/index.vue | 4 +-
8 files changed, 312 insertions(+), 104 deletions(-)
diff --git a/web/src/utils/columnPermission.ts b/web/src/utils/columnPermission.ts
index 7ef06de..adff4e1 100644
--- a/web/src/utils/columnPermission.ts
+++ b/web/src/utils/columnPermission.ts
@@ -22,7 +22,7 @@ export const handleColumnPermission = async (func: Function, crudOptions: any,ex
}
}
const columns = crudOptions.columns;
- const excludeColumns = ['_index','id', 'create_datetime', 'update_datetime'].concat(excludeColumn)
+ const excludeColumns = ['checked','_index','id', 'create_datetime', 'update_datetime'].concat(excludeColumn)
for (let col in columns) {
for (let item of res.data) {
if (excludeColumns.includes(item.field_name)) {
diff --git a/web/src/views/system/menu/components/MenuButtonCom/api.ts b/web/src/views/system/menu/components/MenuButtonCom/api.ts
index d91f920..612c49d 100644
--- a/web/src/views/system/menu/components/MenuButtonCom/api.ts
+++ b/web/src/views/system/menu/components/MenuButtonCom/api.ts
@@ -48,3 +48,10 @@ export function BatchAdd(obj: AddReq) {
});
}
+export function BatchDelete(keys: any) {
+ return request({
+ url: apiPrefix + 'multiple_delete/',
+ method: 'delete',
+ data: { keys },
+ });
+}
diff --git a/web/src/views/system/menu/components/MenuButtonCom/crud.tsx b/web/src/views/system/menu/components/MenuButtonCom/crud.tsx
index 908cf2a..79675fb 100644
--- a/web/src/views/system/menu/components/MenuButtonCom/crud.tsx
+++ b/web/src/views/system/menu/components/MenuButtonCom/crud.tsx
@@ -4,6 +4,8 @@ import {auth} from '/@/utils/authFunction'
import {request} from '/@/utils/service';
import { successNotification } from '/@/utils/message';
import { ElMessage } from 'element-plus';
+import { nextTick, ref } from 'vue';
+import XEUtils from 'xe-utils';
//此处为crudOptions配置
export const createCrudOptions = function ({crudExpose, context}: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async () => {
@@ -22,7 +24,42 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti
const addRequest = async ({form}: AddReq) => {
return await api.AddObj({...form, ...{menu: context!.selectOptions.value.id}});
};
+ // 记录选中的行
+ const selectedRows = ref([]);
+
+ const onSelectionChange = (changed: any) => {
+ const tableData = crudExpose.getTableData();
+ const unChanged = tableData.filter((row: any) => !changed.includes(row));
+ // 添加已选择的行
+ XEUtils.arrayEach(changed, (item: any) => {
+ const ids = XEUtils.pluck(selectedRows.value, 'id');
+ if (!ids.includes(item.id)) {
+ selectedRows.value = XEUtils.union(selectedRows.value, [item]);
+ }
+ });
+ // 剔除未选择的行
+ XEUtils.arrayEach(unChanged, (unItem: any) => {
+ selectedRows.value = XEUtils.remove(selectedRows.value, (item: any) => item.id !== unItem.id);
+ });
+ };
+ const toggleRowSelection = () => {
+ // 多选后,回显默认勾选
+ const tableRef = crudExpose.getBaseTableRef();
+ const tableData = crudExpose.getTableData();
+ const selected = XEUtils.filter(tableData, (item: any) => {
+ const ids = XEUtils.pluck(selectedRows.value, 'id');
+ return ids.includes(item.id);
+ });
+
+ nextTick(() => {
+ XEUtils.arrayEach(selected, (item) => {
+ tableRef.toggleRowSelection(item, true);
+ });
+ });
+ };
+
return {
+ selectedRows,
crudOptions: {
pagination:{
show:false
@@ -84,6 +121,11 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti
editRequest,
delRequest,
},
+ table: {
+ rowKey: 'id', //设置你的主键id, 默认rowKey=id
+ onSelectionChange,
+ onRefreshed: () => toggleRowSelection(),
+ },
form: {
col: {span: 24},
labelWidth: '100px',
@@ -93,6 +135,16 @@ export const createCrudOptions = function ({crudExpose, context}: CreateCrudOpti
},
},
columns: {
+ $checked: {
+ title: '选择',
+ form: { show: false },
+ column: {
+ type: 'selection',
+ align: 'center',
+ width: '70px',
+ columnSetDisabled: true, //禁止在列设置中选择
+ },
+ },
_index: {
title: '序号',
form: {show: false},
diff --git a/web/src/views/system/menu/components/MenuButtonCom/index.vue b/web/src/views/system/menu/components/MenuButtonCom/index.vue
index 9dddaf9..ec55e35 100644
--- a/web/src/views/system/menu/components/MenuButtonCom/index.vue
+++ b/web/src/views/system/menu/components/MenuButtonCom/index.vue
@@ -1,19 +1,72 @@
-
+
+
+
+
+
+
+
+
+
+ 已选中{{ selectedRowsCount }}条数据
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/views/system/menu/index.vue b/web/src/views/system/menu/index.vue
index 0637a3f..ca544a1 100644
--- a/web/src/views/system/menu/index.vue
+++ b/web/src/views/system/menu/index.vue
@@ -16,12 +16,12 @@
-