diff --git a/backend/application/settings.py b/backend/application/settings.py index 70446c1..3428ae4 100644 --- a/backend/application/settings.py +++ b/backend/application/settings.py @@ -404,10 +404,11 @@ PLUGINS_URL_PATTERNS = [] # ********** 一键导入插件配置开始 ********** # 例如: # from dvadmin_upgrade_center.settings import * # 升级中心 -from dvadmin_celery.settings import * # celery 异步任务 +#from dvadmin_celery.settings import * # celery 异步任务 # from dvadmin_third.settings import * # 第三方用户管理 # from dvadmin_ak_sk.settings import * # 秘钥管理管理 # from dvadmin_tenants.settings import * # 租户管理 #from dvadmin_social_auth.settings import * +#from dvadmin_uniapp.settings import * # ... # ********** 一键导入插件配置结束 ********** diff --git a/backend/dvadmin/system/views/role.py b/backend/dvadmin/system/views/role.py index ccbe6d6..a5b5a6f 100644 --- a/backend/dvadmin/system/views/role.py +++ b/backend/dvadmin/system/views/role.py @@ -47,12 +47,12 @@ class RoleCreateUpdateSerializer(CustomModelSerializer): def validate(self, attrs: dict): return super().validate(attrs) - def save(self, **kwargs): - is_superuser = self.request.user.is_superuser - if not is_superuser: - self.validated_data.pop('admin') - data = super().save(**kwargs) - return data + # def save(self, **kwargs): + # is_superuser = self.request.user.is_superuser + # if not is_superuser: + # self.validated_data.pop('admin') + # data = super().save(**kwargs) + # return data class Meta: model = Role diff --git a/backend/dvadmin/utils/filters.py b/backend/dvadmin/utils/filters.py index cb61630..05c0dfb 100644 --- a/backend/dvadmin/utils/filters.py +++ b/backend/dvadmin/utils/filters.py @@ -12,14 +12,16 @@ from collections import OrderedDict from functools import reduce 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 rest_framework.filters import BaseFilterBackend - +from django_filters.conf import settings from dvadmin.system.models import Dept, ApiWhiteList, RoleMenuButtonPermission from dvadmin.utils.models import CoreModel @@ -203,6 +205,7 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): "$": "iregex", "~": "icontains", } + filter_fields = "__all__" def construct_search(self, field_name, lookup_expr=None): lookup = self.lookup_prefixes.get(field_name[0]) @@ -210,14 +213,16 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): field_name = field_name[1:] else: lookup = lookup_expr - if field_name.endswith(lookup): - return field_name - return LOOKUP_SEP.join([field_name, lookup]) + if lookup: + if field_name.endswith(lookup): + return field_name + return LOOKUP_SEP.join([field_name, lookup]) + return field_name def find_filter_lookups(self, orm_lookups, search_term_key): for lookup in orm_lookups: # if lookup.find(search_term_key) >= 0: - new_lookup = lookup.split("__")[0] + new_lookup = LOOKUP_SEP.join(lookup.split(LOOKUP_SEP)[:-1]) if len(lookup.split(LOOKUP_SEP)) > 1 else lookup # 修复条件搜索错误 bug if new_lookup == search_term_key: return lookup @@ -233,18 +238,22 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): # TODO: remove assertion in 2.1 if filterset_class is None and hasattr(view, "filter_class"): utils.deprecate( - "`%s.filter_class` attribute should be renamed `filterset_class`." - % view.__class__.__name__ + "`%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( - "`%s.filter_fields` attribute should be renamed `filterset_fields`." - % view.__class__.__name__ + "`%s.filter_fields` attribute should be renamed `filterset_fields`." % view.__class__.__name__ ) - filterset_fields = getattr(view, "filter_fields", None) + self.filter_fields = getattr(view, "filter_fields", None) + if isinstance(self.filter_fields, (list, tuple)): + filterset_fields = [ + field[1:] if field[0] in self.lookup_prefixes.keys() else field for field in self.filter_fields + ] + else: + filterset_fields = self.filter_fields if filterset_class: filterset_model = filterset_class._meta.model @@ -264,6 +273,51 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): MetaBase = getattr(self.filterset_base, "Meta", object) class AutoFilterSet(self.filterset_base): + @classmethod + def get_all_model_fields(cls, model): + opts = model._meta + + return [ + f.name + for f in sorted(opts.fields + opts.many_to_many) + if (f.name == "id") + or not isinstance(f, models.AutoField) + and not (getattr(f.remote_field, "parent_link", False)) + ] + + @classmethod + def get_fields(cls): + """ + Resolve the 'fields' argument that should be used for generating filters on the + filterset. This is 'Meta.fields' sans the fields in 'Meta.exclude'. + """ + model = cls._meta.model + fields = cls._meta.fields + exclude = cls._meta.exclude + + assert not (fields is None and exclude is None), ( + "Setting 'Meta.model' without either 'Meta.fields' or 'Meta.exclude' " + "has been deprecated since 0.15.0 and is now disallowed. Add an explicit " + "'Meta.fields' or 'Meta.exclude' to the %s class." % cls.__name__ + ) + + # Setting exclude with no fields implies all other fields. + if exclude is not None and fields is None: + fields = ALL_FIELDS + + # Resolve ALL_FIELDS into all fields for the filterset's model. + if fields == ALL_FIELDS: + fields = cls.get_all_model_fields(model) + + # Remove excluded fields + exclude = exclude or [] + if not isinstance(fields, dict): + fields = [(f, [settings.DEFAULT_LOOKUP_EXPR]) for f in fields if f not in exclude] + else: + fields = [(f, lookups) for f, lookups in fields.items() if f not in exclude] + + return OrderedDict(fields) + @classmethod def get_filters(cls): """ @@ -292,9 +346,12 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): if field is None: undefined.append(field_name) # 更新默认字符串搜索为模糊搜索 - if isinstance(field, (models.CharField)) and filterset_fields == '__all__' and lookups == [ - 'exact']: - lookups = ['icontains'] + if ( + isinstance(field, (models.CharField)) + and filterset_fields == "__all__" + and lookups == ["exact"] + ): + lookups = ["icontains"] for lookup_expr in lookups: filter_name = cls.get_filter_name(field_name, lookup_expr) @@ -304,20 +361,15 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): continue if field is not None: - filters[filter_name] = cls.filter_for_field( - field, field_name, lookup_expr - ) + filters[filter_name] = cls.filter_for_field(field, field_name, lookup_expr) # Allow Meta.fields to contain declared filters *only* when a list/tuple if isinstance(cls._meta.fields, (list, tuple)): - undefined = [ - f for f in undefined if f not in cls.declared_filters - ] + undefined = [f for f in undefined if f not in cls.declared_filters] if undefined: raise TypeError( - "'Meta.fields' must not contain non-model field names: %s" - % ", ".join(undefined) + "'Meta.fields' must not contain non-model field names: %s" % ", ".join(undefined) ) # Add in declared filters. This is necessary since we don't enforce adding @@ -339,22 +391,31 @@ class CustomDjangoFilterBackend(DjangoFilterBackend): return queryset if filterset.__class__.__name__ == "AutoFilterSet": queryset = filterset.queryset - orm_lookups = [] - for search_field in filterset.filters: - if isinstance(filterset.filters[search_field], CharFilter): - orm_lookups.append( - self.construct_search(six.text_type(search_field), filterset.filters[search_field].lookup_expr) - ) - else: - orm_lookups.append(search_field) + filter_fields = filterset.filters if self.filter_fields == "__all__" else self.filter_fields + orm_lookup_dict = dict( + zip( + [field for field in filter_fields], + [filterset.filters[lookup].lookup_expr for lookup in filterset.filters.keys()], + ) + ) + orm_lookups = [ + self.construct_search(lookup, lookup_expr) for lookup, lookup_expr in orm_lookup_dict.items() + ] + # print(orm_lookups) conditions = [] queries = [] for search_term_key in filterset.data.keys(): orm_lookup = self.find_filter_lookups(orm_lookups, search_term_key) - if not orm_lookup: + if not orm_lookup or filterset.data.get(search_term_key) == '': continue - query = Q(**{orm_lookup: filterset.data[search_term_key]}) - queries.append(query) + filterset_data_len = len(filterset.data.getlist(search_term_key)) + if filterset_data_len == 1: + query = Q(**{orm_lookup: filterset.data[search_term_key]}) + queries.append(query) + elif filterset_data_len == 2: + orm_lookup += '__range' + query = Q(**{orm_lookup: filterset.data.getlist(search_term_key)}) + queries.append(query) if len(queries) > 0: conditions.append(reduce(operator.and_, queries)) queryset = queryset.filter(reduce(operator.and_, conditions)) diff --git a/web/package.json b/web/package.json index 28b837f..d4e4679 100644 --- a/web/package.json +++ b/web/package.json @@ -25,7 +25,7 @@ "echarts": "^5.4.1", "echarts-gl": "^2.0.9", "echarts-wordcloud": "^2.1.0", - "element-plus": "^2.3.9", + "element-plus": "^2.5.5", "element-tree-line": "^0.2.1", "font-awesome": "^4.7.0", "js-cookie": "^3.0.1", diff --git a/web/src/components/dvaSelect/index.vue b/web/src/components/dvaSelect/index.vue new file mode 100644 index 0000000..8d58fbe --- /dev/null +++ b/web/src/components/dvaSelect/index.vue @@ -0,0 +1,141 @@ + + + diff --git a/web/src/views/system/demo/index.vue b/web/src/views/system/demo/index.vue index 4f7b7ec..4e71a9c 100644 --- a/web/src/views/system/demo/index.vue +++ b/web/src/views/system/demo/index.vue @@ -1,13 +1,80 @@ - - -