Merge remote-tracking branch 'origin/dev' into dev
commit
42372165ac
|
@ -3,6 +3,7 @@ import logging
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from application import settings
|
from application import settings
|
||||||
|
from dvadmin.system import signals
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -29,9 +30,9 @@ class Command(BaseCommand):
|
||||||
reset = True
|
reset = True
|
||||||
if isinstance(options.get("n"), list) or isinstance(options.get("N"), list):
|
if isinstance(options.get("n"), list) or isinstance(options.get("N"), list):
|
||||||
reset = False
|
reset = False
|
||||||
|
signals.pre_init_complete.send(sender=None, msg='开始初始化', data={"reset": reset})
|
||||||
for app in settings.INSTALLED_APPS:
|
for app in settings.INSTALLED_APPS:
|
||||||
|
signals.detail_init_complete.send(sender=None, msg='初始化中', data={"app": app, "reset": reset})
|
||||||
try:
|
try:
|
||||||
exec(
|
exec(
|
||||||
f"""
|
f"""
|
||||||
|
@ -50,4 +51,5 @@ main(reset={reset})
|
||||||
)
|
)
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
signals.post_init_complete.send(sender=None, msg='初始化完成', data={"reset": reset})
|
||||||
print("初始化数据完成!")
|
print("初始化数据完成!")
|
||||||
|
|
|
@ -131,6 +131,7 @@ class Dept(CoreModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="上级部门",
|
help_text="上级部门",
|
||||||
|
db_index=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.dispatch import Signal
|
||||||
|
# 初始化信号
|
||||||
|
pre_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
detail_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
post_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
# 租户初始化信号
|
||||||
|
pre_tenants_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
detail_tenants_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
post_tenants_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
post_tenants_all_init_complete = Signal(providing_args=['msg', 'data'])
|
||||||
|
# 租户创建完成信号
|
||||||
|
tenants_create_complete = Signal(providing_args=['msg', 'data'])
|
|
@ -13,6 +13,7 @@ from dvadmin.utils.json_response import DetailResponse, SuccessResponse
|
||||||
from dvadmin.utils.permission import AnonymousUserPermission
|
from dvadmin.utils.permission import AnonymousUserPermission
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
from dvadmin.utils.filters import LazyLoadFilter
|
||||||
|
|
||||||
|
|
||||||
class DeptSerializer(CustomModelSerializer):
|
class DeptSerializer(CustomModelSerializer):
|
||||||
|
@ -120,6 +121,12 @@ class DeptCreateUpdateSerializer(CustomModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class DeptLazyFilter(LazyLoadFilter):
|
||||||
|
class Meta:
|
||||||
|
model = Dept
|
||||||
|
fields = ['name', 'parent', 'status']
|
||||||
|
|
||||||
|
|
||||||
class DeptViewSet(CustomModelViewSet):
|
class DeptViewSet(CustomModelViewSet):
|
||||||
"""
|
"""
|
||||||
部门管理接口
|
部门管理接口
|
||||||
|
@ -129,11 +136,13 @@ class DeptViewSet(CustomModelViewSet):
|
||||||
retrieve:单例
|
retrieve:单例
|
||||||
destroy:删除
|
destroy:删除
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = Dept.objects.all()
|
queryset = Dept.objects.all()
|
||||||
serializer_class = DeptSerializer
|
serializer_class = DeptSerializer
|
||||||
create_serializer_class = DeptCreateUpdateSerializer
|
create_serializer_class = DeptCreateUpdateSerializer
|
||||||
update_serializer_class = DeptCreateUpdateSerializer
|
update_serializer_class = DeptCreateUpdateSerializer
|
||||||
filter_fields = ['name', 'id', 'parent']
|
# filter_fields = ["name", "id", "parent"]
|
||||||
|
filter_class = DeptLazyFilter
|
||||||
search_fields = []
|
search_fields = []
|
||||||
# extra_filter_backends = []
|
# extra_filter_backends = []
|
||||||
import_serializer_class = DeptImportSerializer
|
import_serializer_class = DeptImportSerializer
|
||||||
|
|
|
@ -114,7 +114,7 @@ class MenuInitSerializer(CustomModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ['name', 'icon', 'sort', 'is_link', 'is_catalog', 'web_path', 'component', 'component_name', 'status',
|
fields = ['name', 'icon', 'sort', 'is_link', 'is_catalog', 'web_path', 'component', 'component_name', 'status',
|
||||||
'cache', 'visible', 'parent', 'children', 'menu_button', 'creator', 'dept_belong_id']
|
'cache', 'visible', 'parent', 'children', 'menu_button', 'frame_out', 'creator', 'dept_belong_id']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'creator': {'write_only': True},
|
'creator': {'write_only': True},
|
||||||
'dept_belong_id': {'write_only': True}
|
'dept_belong_id': {'write_only': True}
|
||||||
|
@ -137,7 +137,8 @@ class WebRouterSerializer(CustomModelSerializer):
|
||||||
else:
|
else:
|
||||||
# 根据当前角色获取权限按钮id集合
|
# 根据当前角色获取权限按钮id集合
|
||||||
permissionIds = self.request.user.role.values_list('permission', flat=True)
|
permissionIds = self.request.user.role.values_list('permission', flat=True)
|
||||||
queryset = instance.menuPermission.filter(id__in=permissionIds, menu=instance.id).values_list('value', flat=True)
|
queryset = instance.menuPermission.filter(id__in=permissionIds, menu=instance.id).values_list('value',
|
||||||
|
flat=True)
|
||||||
if queryset:
|
if queryset:
|
||||||
return queryset
|
return queryset
|
||||||
else:
|
else:
|
||||||
|
@ -145,7 +146,8 @@ class WebRouterSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ('id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
fields = (
|
||||||
|
'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
||||||
'component_name', 'cache', 'visible', 'menuPermission', 'frame_out')
|
'component_name', 'cache', 'visible', 'menuPermission', 'frame_out')
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
@ -165,6 +167,7 @@ class MenuViewSet(CustomModelViewSet):
|
||||||
update_serializer_class = MenuCreateSerializer
|
update_serializer_class = MenuCreateSerializer
|
||||||
search_fields = ['name', 'status']
|
search_fields = ['name', 'status']
|
||||||
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
||||||
|
|
||||||
# extra_filter_backends = []
|
# extra_filter_backends = []
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[])
|
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||||
|
|
|
@ -23,7 +23,7 @@ class MenuButtonSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MenuButton
|
model = MenuButton
|
||||||
fields = ['id', 'name', 'value', 'api', 'method', 'menu']
|
fields = ["id", "name", "value", "api", "method", "menu"]
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class MenuButtonInitSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = MenuButton
|
model = MenuButton
|
||||||
fields = ['id', 'name', 'value', 'api', 'method', 'menu']
|
fields = ["id", "name", "value", "api", "method", "menu"]
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,21 +58,26 @@ class MenuButtonViewSet(CustomModelViewSet):
|
||||||
retrieve:单例
|
retrieve:单例
|
||||||
destroy:删除
|
destroy:删除
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = MenuButton.objects.all()
|
queryset = MenuButton.objects.all()
|
||||||
serializer_class = MenuButtonSerializer
|
serializer_class = MenuButtonSerializer
|
||||||
create_serializer_class = MenuButtonCreateUpdateSerializer
|
create_serializer_class = MenuButtonCreateUpdateSerializer
|
||||||
update_serializer_class = MenuButtonCreateUpdateSerializer
|
update_serializer_class = MenuButtonCreateUpdateSerializer
|
||||||
extra_filter_backends = []
|
extra_filter_backends = []
|
||||||
|
|
||||||
@action(methods=['get'], detail=False)
|
@action(methods=["GET"], detail=False, permission_classes=[])
|
||||||
def get_btn_permission(self, request):
|
def get_btn_permission(self, request):
|
||||||
"""
|
"""
|
||||||
获取当前用户的按钮权限
|
获取当前用户的按钮权限
|
||||||
"""
|
"""
|
||||||
user = request.user
|
user = request.user
|
||||||
if not user.is_superuser:
|
if not user.is_superuser:
|
||||||
menuIds = user.role.values_list('menu__id', flat=True)
|
menuIds = user.role.values_list("menu__id", flat=True)
|
||||||
else:
|
else:
|
||||||
menuIds = Menu.objects.filter(status=1)
|
menuIds = Menu.objects.filter(status=1)
|
||||||
queryset = MenuButton.objects.filter(menu__in=menuIds).annotate(permission=Concat('menu__web_path',Value(':'),'value',output_field=CharField())).values_list('permission',flat=True)
|
queryset = (
|
||||||
|
MenuButton.objects.filter(menu__in=menuIds)
|
||||||
|
.annotate(permission=Concat("menu__web_path", Value(":"), "value", output_field=CharField()))
|
||||||
|
.values_list("permission", flat=True)
|
||||||
|
)
|
||||||
return DetailResponse(data=queryset)
|
return DetailResponse(data=queryset)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
@Remark: 系统配置
|
@Remark: 系统配置
|
||||||
"""
|
"""
|
||||||
import django_filters
|
import django_filters
|
||||||
|
from django.db import connection
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django_filters.rest_framework import BooleanFilter
|
from django_filters.rest_framework import BooleanFilter
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
@ -276,5 +277,6 @@ class InitSettingsViewSet(APIView):
|
||||||
SystemConfig.objects.filter(status=False, parent_id__isnull=False).values('parent__key',
|
SystemConfig.objects.filter(status=False, parent_id__isnull=False).values('parent__key',
|
||||||
'key')]
|
'key')]
|
||||||
data = dict(filter(lambda x: x[0] not in backend_config, data.items()))
|
data = dict(filter(lambda x: x[0] not in backend_config, data.items()))
|
||||||
data = self.filter_system_config_values(data=data)
|
if hasattr(connection, 'tenant'):
|
||||||
|
data['schema_name'] = connection.tenant.schema_name
|
||||||
return DetailResponse(data=data)
|
return DetailResponse(data=data)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import traceback
|
||||||
|
|
||||||
from django.db.models import ProtectedError, RestrictedError
|
from django.db.models import ProtectedError, RestrictedError
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
from rest_framework.exceptions import APIException as DRFAPIException, AuthenticationFailed
|
from rest_framework.exceptions import APIException as DRFAPIException, AuthenticationFailed, PermissionDenied
|
||||||
from rest_framework.status import HTTP_407_PROXY_AUTHENTICATION_REQUIRED, HTTP_401_UNAUTHORIZED
|
from rest_framework.status import HTTP_407_PROXY_AUTHENTICATION_REQUIRED, HTTP_401_UNAUTHORIZED
|
||||||
from rest_framework.views import set_rollback, exception_handler
|
from rest_framework.views import set_rollback, exception_handler
|
||||||
|
|
||||||
|
@ -50,6 +50,8 @@ def CustomExceptionHandler(ex, context):
|
||||||
elif isinstance(ex, DRFAPIException):
|
elif isinstance(ex, DRFAPIException):
|
||||||
set_rollback()
|
set_rollback()
|
||||||
msg = ex.detail
|
msg = ex.detail
|
||||||
|
if isinstance(ex, PermissionDenied):
|
||||||
|
msg = f'{msg} ({context["request"].method}: {context["request"].path})'
|
||||||
if isinstance(msg, dict):
|
if isinstance(msg, dict):
|
||||||
for k, v in msg.items():
|
for k, v in msg.items():
|
||||||
for i in v:
|
for i in v:
|
||||||
|
|
|
@ -12,13 +12,15 @@ from collections import OrderedDict
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
from django import forms
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Q, F
|
from django.db.models import Q, F
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
from django_filters import utils
|
from django_filters import utils
|
||||||
from django_filters.conf import settings
|
from django_filters.conf import settings
|
||||||
from django_filters.constants import ALL_FIELDS
|
from django_filters.constants import ALL_FIELDS
|
||||||
from django_filters.filters import CharFilter
|
from django_filters.filters import CharFilter, BooleanFilter
|
||||||
|
from django_filters.filterset import FilterSet, FilterSetMetaclass
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django_filters.utils import get_model_field
|
from django_filters.utils import get_model_field
|
||||||
from rest_framework.filters import BaseFilterBackend
|
from rest_framework.filters import BaseFilterBackend
|
||||||
|
@ -71,9 +73,7 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
|
||||||
permission__api=F("url"), permission__method=F("method")
|
permission__api=F("url"), permission__method=F("method")
|
||||||
)
|
)
|
||||||
api_white_list = [
|
api_white_list = [
|
||||||
str(item.get("permission__api").replace("{id}", ".*?"))
|
str(item.get("permission__api").replace("{id}", ".*?")) + ":" + str(item.get("permission__method"))
|
||||||
+ ":"
|
|
||||||
+ str(item.get("permission__method"))
|
|
||||||
for item in api_white_list
|
for item in api_white_list
|
||||||
if item.get("permission__api")
|
if item.get("permission__api")
|
||||||
]
|
]
|
||||||
|
@ -119,19 +119,13 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
|
||||||
|
|
||||||
# 4. 只为仅本人数据权限时只返回过滤本人数据,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据)
|
# 4. 只为仅本人数据权限时只返回过滤本人数据,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据)
|
||||||
if 0 in dataScope_list:
|
if 0 in dataScope_list:
|
||||||
return queryset.filter(
|
return queryset.filter(creator=request.user, dept_belong_id=user_dept_id)
|
||||||
creator=request.user, dept_belong_id=user_dept_id
|
|
||||||
)
|
|
||||||
|
|
||||||
# 5. 自定数据权限 获取部门,根据部门过滤
|
# 5. 自定数据权限 获取部门,根据部门过滤
|
||||||
dept_list = []
|
dept_list = []
|
||||||
for ele in dataScope_list:
|
for ele in dataScope_list:
|
||||||
if ele == 4:
|
if ele == 4:
|
||||||
dept_list.extend(
|
dept_list.extend(request.user.role.filter(status=1).values_list("dept__id", flat=True))
|
||||||
request.user.role.filter(status=1).values_list(
|
|
||||||
"dept__id", flat=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif ele == 2:
|
elif ele == 2:
|
||||||
dept_list.append(user_dept_id)
|
dept_list.append(user_dept_id)
|
||||||
elif ele == 1:
|
elif ele == 1:
|
||||||
|
@ -141,7 +135,7 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
|
||||||
user_dept_id,
|
user_dept_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if queryset.model._meta.model_name == 'dept':
|
if queryset.model._meta.model_name == "dept":
|
||||||
return queryset.filter(id__in=list(set(dept_list)))
|
return queryset.filter(id__in=list(set(dept_list)))
|
||||||
return queryset.filter(dept_belong_id__in=list(set(dept_list)))
|
return queryset.filter(dept_belong_id__in=list(set(dept_list)))
|
||||||
else:
|
else:
|
||||||
|
@ -186,16 +180,14 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
# TODO: remove assertion in 2.1
|
# TODO: remove assertion in 2.1
|
||||||
if filterset_class is None and hasattr(view, "filter_class"):
|
if filterset_class is None and hasattr(view, "filter_class"):
|
||||||
utils.deprecate(
|
utils.deprecate(
|
||||||
"`%s.filter_class` attribute should be renamed `filterset_class`."
|
"`%s.filter_class` attribute should be renamed `filterset_class`." % view.__class__.__name__
|
||||||
% view.__class__.__name__
|
|
||||||
)
|
)
|
||||||
filterset_class = getattr(view, "filter_class", None)
|
filterset_class = getattr(view, "filter_class", None)
|
||||||
|
|
||||||
# TODO: remove assertion in 2.1
|
# TODO: remove assertion in 2.1
|
||||||
if filterset_fields is None and hasattr(view, "filter_fields"):
|
if filterset_fields is None and hasattr(view, "filter_fields"):
|
||||||
utils.deprecate(
|
utils.deprecate(
|
||||||
"`%s.filter_fields` attribute should be renamed `filterset_fields`."
|
"`%s.filter_fields` attribute should be renamed `filterset_fields`." % view.__class__.__name__
|
||||||
% view.__class__.__name__
|
|
||||||
)
|
)
|
||||||
filterset_fields = getattr(view, "filter_fields", None)
|
filterset_fields = getattr(view, "filter_fields", None)
|
||||||
|
|
||||||
|
@ -224,7 +216,8 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
return [
|
return [
|
||||||
f.name
|
f.name
|
||||||
for f in sorted(opts.fields + opts.many_to_many)
|
for f in sorted(opts.fields + opts.many_to_many)
|
||||||
if (f.name == 'id') or not isinstance(f, models.AutoField)
|
if (f.name == "id")
|
||||||
|
or not isinstance(f, models.AutoField)
|
||||||
and not (getattr(f.remote_field, "parent_link", False))
|
and not (getattr(f.remote_field, "parent_link", False))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -255,9 +248,7 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
# Remove excluded fields
|
# Remove excluded fields
|
||||||
exclude = exclude or []
|
exclude = exclude or []
|
||||||
if not isinstance(fields, dict):
|
if not isinstance(fields, dict):
|
||||||
fields = [
|
fields = [(f, [settings.DEFAULT_LOOKUP_EXPR]) for f in fields if f not in exclude]
|
||||||
(f, [settings.DEFAULT_LOOKUP_EXPR]) for f in fields if f not in exclude
|
|
||||||
]
|
|
||||||
else:
|
else:
|
||||||
fields = [(f, lookups) for f, lookups in fields.items() if f not in exclude]
|
fields = [(f, lookups) for f, lookups in fields.items() if f not in exclude]
|
||||||
|
|
||||||
|
@ -291,9 +282,12 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
if field is None:
|
if field is None:
|
||||||
undefined.append(field_name)
|
undefined.append(field_name)
|
||||||
# 更新默认字符串搜索为模糊搜索
|
# 更新默认字符串搜索为模糊搜索
|
||||||
if isinstance(field, (models.CharField)) and filterset_fields == '__all__' and lookups == [
|
if (
|
||||||
'exact']:
|
isinstance(field, (models.CharField))
|
||||||
lookups = ['icontains']
|
and filterset_fields == "__all__"
|
||||||
|
and lookups == ["exact"]
|
||||||
|
):
|
||||||
|
lookups = ["icontains"]
|
||||||
for lookup_expr in lookups:
|
for lookup_expr in lookups:
|
||||||
filter_name = cls.get_filter_name(field_name, lookup_expr)
|
filter_name = cls.get_filter_name(field_name, lookup_expr)
|
||||||
|
|
||||||
|
@ -303,20 +297,15 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if field is not None:
|
if field is not None:
|
||||||
filters[filter_name] = cls.filter_for_field(
|
filters[filter_name] = cls.filter_for_field(field, field_name, lookup_expr)
|
||||||
field, field_name, lookup_expr
|
|
||||||
)
|
|
||||||
|
|
||||||
# Allow Meta.fields to contain declared filters *only* when a list/tuple
|
# Allow Meta.fields to contain declared filters *only* when a list/tuple
|
||||||
if isinstance(cls._meta.fields, (list, tuple)):
|
if isinstance(cls._meta.fields, (list, tuple)):
|
||||||
undefined = [
|
undefined = [f for f in undefined if f not in cls.declared_filters]
|
||||||
f for f in undefined if f not in cls.declared_filters
|
|
||||||
]
|
|
||||||
|
|
||||||
if undefined:
|
if undefined:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"'Meta.fields' must not contain non-model field names: %s"
|
"'Meta.fields' must not contain non-model field names: %s" % ", ".join(undefined)
|
||||||
% ", ".join(undefined)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add in declared filters. This is necessary since we don't enforce adding
|
# Add in declared filters. This is necessary since we don't enforce adding
|
||||||
|
@ -364,3 +353,146 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
if not filterset.is_valid() and self.raise_exception:
|
if not filterset.is_valid() and self.raise_exception:
|
||||||
raise utils.translate_validation(filterset.errors)
|
raise utils.translate_validation(filterset.errors)
|
||||||
return filterset.qs
|
return filterset.qs
|
||||||
|
|
||||||
|
|
||||||
|
# ####################### 懒加载FilterSet ####################### #
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_execution_time(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
start_time = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end_time = time.time()
|
||||||
|
execution_time = end_time - start_time
|
||||||
|
print(f"Function {func.__name__} took {execution_time:.6f} seconds to execute.", flush=True)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# def get_children(model: models, obj_id: int, all_qs=None, rec_list=None):
|
||||||
|
# if not all_qs:
|
||||||
|
# all_qs = model.objects.all().values("id", "parent")
|
||||||
|
# if rec_list is None:
|
||||||
|
# rec_list = [obj_id]
|
||||||
|
# for ele in all_qs:
|
||||||
|
# if ele.get("parent") == obj_id:
|
||||||
|
# rec_list.append(ele.get("id"))
|
||||||
|
# get_dept(ele.get("id"), all_qs, rec_list)
|
||||||
|
# return list(set(rec_list))
|
||||||
|
|
||||||
|
|
||||||
|
# @calculate_execution_time
|
||||||
|
# def get_qs_children(model, qs):
|
||||||
|
# dept_ids = []
|
||||||
|
# for d in qs:
|
||||||
|
# r = get_children(model, d.id)
|
||||||
|
# dept_ids.extend(r)
|
||||||
|
# return list(set(dept_ids))
|
||||||
|
|
||||||
|
|
||||||
|
def next_layer_data(qs_filter, qs_node):
|
||||||
|
parent_nodes = set(qs_node.values_list("id", flat=True))
|
||||||
|
if set(qs_filter) == set(qs_node):
|
||||||
|
return parent_nodes
|
||||||
|
# qs_filter内所有父级id 去重
|
||||||
|
parent_ids = set()
|
||||||
|
for node in qs_filter:
|
||||||
|
while node.parent:
|
||||||
|
if node.id in parent_nodes:
|
||||||
|
parent_ids.add(node.id)
|
||||||
|
break
|
||||||
|
if node.parent.id in parent_nodes:
|
||||||
|
parent_ids.add(node.parent.id)
|
||||||
|
break
|
||||||
|
node = node.parent
|
||||||
|
# print(f"过滤查询集 ==> {qs_filter}", flush=True)
|
||||||
|
# print(f"待渲染节点的id ==> {parent_nodes=}", flush=True)
|
||||||
|
# print(f"过滤查询集的父节点id ==> {parent_ids=}", flush=True)
|
||||||
|
return parent_ids
|
||||||
|
|
||||||
|
|
||||||
|
def construct_data(qs_filter, qs_node, is_parent):
|
||||||
|
filter_node_ids = set(qs_filter.values_list("id", flat=True))
|
||||||
|
render_node_ids = set(qs_node.values_list("id", flat=True))
|
||||||
|
|
||||||
|
hidden_node_ids = set()
|
||||||
|
for node in qs_filter:
|
||||||
|
while node.parent:
|
||||||
|
if node.parent in qs_filter:
|
||||||
|
hidden_node_ids.add(node.id)
|
||||||
|
node = node.parent
|
||||||
|
on_show = filter_node_ids.difference(hidden_node_ids)
|
||||||
|
on_expand = hidden_node_ids & render_node_ids
|
||||||
|
# print(f"完整查询结果 {filter_node_ids}")
|
||||||
|
# print(f"待展示的节点(未过滤) {render_node_ids}")
|
||||||
|
# print(f"查询结果中的子节点 {hidden_node_ids}")
|
||||||
|
# print(f"查询后首先渲染的父节点 {on_show}")
|
||||||
|
# print(f"展开父节点时要渲染的节点 {on_expand}")
|
||||||
|
return on_expand if is_parent else on_show
|
||||||
|
|
||||||
|
|
||||||
|
class FilterSetOptions:
|
||||||
|
def __init__(self, options=None):
|
||||||
|
self.model = getattr(options, "model", None)
|
||||||
|
self.fields = getattr(options, "fields", None)
|
||||||
|
self.exclude = getattr(options, "exclude", None)
|
||||||
|
|
||||||
|
# CharField默认模糊查询
|
||||||
|
self.filter_overrides = getattr(
|
||||||
|
options,
|
||||||
|
"filter_overrides",
|
||||||
|
{
|
||||||
|
models.CharField: {
|
||||||
|
"filter_class": CharFilter,
|
||||||
|
"extra": lambda f: {
|
||||||
|
"lookup_expr": "icontains",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
models.BooleanField: {
|
||||||
|
"filter_class": BooleanFilter,
|
||||||
|
"extra": lambda f: {
|
||||||
|
"widget": forms.RadioSelect,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.form = getattr(options, "form", forms.Form)
|
||||||
|
|
||||||
|
|
||||||
|
class LazyLoadFilterSetMetaclass(FilterSetMetaclass):
|
||||||
|
def __new__(cls, name, bases, attrs):
|
||||||
|
attrs["declared_filters"] = cls.get_declared_filters(bases, attrs)
|
||||||
|
|
||||||
|
new_class = super().__new__(cls, name, bases, attrs)
|
||||||
|
new_class._meta = FilterSetOptions(getattr(new_class, "Meta", None))
|
||||||
|
new_class.base_filters = new_class.get_filters()
|
||||||
|
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
|
||||||
|
class LazyLoadFilter(FilterSet, metaclass=LazyLoadFilterSetMetaclass):
|
||||||
|
@property
|
||||||
|
# @calculate_execution_time
|
||||||
|
def qs(self):
|
||||||
|
queryset = self.queryset
|
||||||
|
# print(self.form.cleaned_data, flush=True)
|
||||||
|
filter_params = [k for k, v in self.form.cleaned_data.items() if v in [None, ""]]
|
||||||
|
for field in filter_params:
|
||||||
|
self.form.cleaned_data.pop(field)
|
||||||
|
is_parent = self.form.cleaned_data.pop("parent", None) is not None
|
||||||
|
# print(queryset, flush=True)
|
||||||
|
if self.form.cleaned_data:
|
||||||
|
self.queryset = queryset.model.objects.all()
|
||||||
|
|
||||||
|
# 从根节点开始
|
||||||
|
# node_ids = next_layer_data(super().qs, queryset)
|
||||||
|
|
||||||
|
# 按匹配结果显示
|
||||||
|
node_ids = construct_data(super().qs, queryset, is_parent)
|
||||||
|
|
||||||
|
return queryset.model.objects.filter(id__in=node_ids)
|
||||||
|
return super().qs
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"china-division": "^2.4.0",
|
"china-division": "^2.4.0",
|
||||||
"core-js": "^3.4.3",
|
"core-js": "^3.4.3",
|
||||||
"cropperjs": "^1.5.6",
|
"cropperjs": "^1.5.6",
|
||||||
|
"crypto-js": "^4.1.1",
|
||||||
"d2-crud-plus": "^2.17.9",
|
"d2-crud-plus": "^2.17.9",
|
||||||
"d2-crud-x": "^2.17.9",
|
"d2-crud-x": "^2.17.9",
|
||||||
"d2p-extends": "^2.17.9",
|
"d2p-extends": "^2.17.9",
|
||||||
|
@ -43,7 +44,9 @@
|
||||||
"ua-parser-js": "^0.7.20",
|
"ua-parser-js": "^0.7.20",
|
||||||
"viser-vue": "^2.4.8",
|
"viser-vue": "^2.4.8",
|
||||||
"vue": "2.7.14",
|
"vue": "2.7.14",
|
||||||
|
"vue-clipboard2": "^0.3.3",
|
||||||
"vue-core-video-player": "^0.2.0",
|
"vue-core-video-player": "^0.2.0",
|
||||||
|
"vue-cropper": "^0.6.2",
|
||||||
"vue-echarts": "^6.5.4",
|
"vue-echarts": "^6.5.4",
|
||||||
"vue-grid-layout": "^2.4.0",
|
"vue-grid-layout": "^2.4.0",
|
||||||
"vue-html2pdf": "^1.8.0",
|
"vue-html2pdf": "^1.8.0",
|
||||||
|
@ -51,6 +54,7 @@
|
||||||
"vue-infinite-scroll": "^2.0.2",
|
"vue-infinite-scroll": "^2.0.2",
|
||||||
"vue-router": "^3.6.5",
|
"vue-router": "^3.6.5",
|
||||||
"vue-splitpane": "^1.0.6",
|
"vue-splitpane": "^1.0.6",
|
||||||
|
"vue-swiper-component": "^2.1.3",
|
||||||
"vuex": "^3.1.2",
|
"vuex": "^3.1.2",
|
||||||
"vxe-table": "^3.3.2",
|
"vxe-table": "^3.3.2",
|
||||||
"xe-utils": "^3.2.1"
|
"xe-utils": "^3.2.1"
|
||||||
|
|
|
@ -180,10 +180,10 @@ export default {
|
||||||
if (this.fileList.length > 0) {
|
if (this.fileList.length > 0) {
|
||||||
const file = this.fileList[0]
|
const file = this.fileList[0]
|
||||||
log.debug('file,', file, file.status)
|
log.debug('file,', file, file.status)
|
||||||
if (file.response != null && file.response.url != null) {
|
if (file.url != null) {
|
||||||
return file.response.url
|
|
||||||
} else if (file.url != null) {
|
|
||||||
return file.url
|
return file.url
|
||||||
|
} else if (file.response != null && file.response.url != null) {
|
||||||
|
return file.response.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
:rules="[
|
:rules="[
|
||||||
{ required: elProps.fields[key].required, message: '不能为空', trigger: 'blur' },
|
{ required: elProps.fields[key].required, message: '不能为空', trigger: 'blur' },
|
||||||
]"
|
]"
|
||||||
|
style="text-align: center"
|
||||||
>
|
>
|
||||||
<el-select v-model="field[key]" v-if="elProps.fields[key].type === 'select'" placeholder="请选择">
|
<el-select v-model="field[key]" v-if="elProps.fields[key].type === 'select'" placeholder="请选择">
|
||||||
<el-option
|
<el-option
|
||||||
|
@ -34,13 +35,22 @@
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input-number style="width: 100%" v-else-if="elProps.fields[key].type === 'number'" controls-position="right" v-model="field[key]"></el-input-number>
|
<el-input-number style="width: 100%" v-else-if="elProps.fields[key].type === 'number'" controls-position="right" v-model="field[key]"></el-input-number>
|
||||||
<div v-else-if="elProps.fields[key].type === 'image'" style="height: 50px;width: 50px;">
|
<div v-else-if="elProps.fields[key].type === 'image'" style="height: 30px;width: 30px;">
|
||||||
<d2p-file-uploader v-model="field[key]" :elProps="elProps.fields[key].elProps || { listType: 'picture-card', accept: '.png,.jpeg,.jpg,.ico,.bmp,.gif', limit: 1 }"></d2p-file-uploader>
|
<d2p-file-uploader v-model="field[key]" :elProps="elProps.fields[key].elProps || { listType: 'picture-card', accept: '.png,.jpeg,.jpg,.ico,.bmp,.gif', limit: 1 }"></d2p-file-uploader>
|
||||||
</div>
|
</div>
|
||||||
<!-- 富文本 -->
|
<!-- 富文本 -->
|
||||||
<span v-else-if="elProps.fields[key].type === 'ueditor'">
|
<span v-else-if="elProps.fields[key].type === 'ueditor'">
|
||||||
<values-popover v-model="field[key]" :elProps="{ type: 'ueditor' }" @previewClick="previewClick(index,key)"></values-popover>
|
<values-popover v-model="field[key]" :elProps="{ type: 'ueditor' }" @previewClick="previewClick(index,key)"></values-popover>
|
||||||
</span>
|
</span>
|
||||||
|
<!-- 多对多 -->
|
||||||
|
<span v-else-if="elProps.fields[key].type === 'many_to_many'">
|
||||||
|
<values-popover
|
||||||
|
v-model="field[key]"
|
||||||
|
:dict="elProps.fields[key].dict"
|
||||||
|
:elProps="{ type: elProps.fields[key].value?.type || 'strList', rowKey: elProps.fields[key].value?.rowKey || 'title', label: elProps.value?.title || '答复选项内容' }"
|
||||||
|
@listClick="manyToManyClick(index,key)">
|
||||||
|
</values-popover>
|
||||||
|
</span>
|
||||||
<el-input v-model="field[key]" v-else placeholder="请输入"></el-input>
|
<el-input v-model="field[key]" v-else placeholder="请输入"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
@ -63,11 +73,31 @@
|
||||||
:visible.sync="previewVisible"
|
:visible.sync="previewVisible"
|
||||||
append-to-body
|
append-to-body
|
||||||
width="900">
|
width="900">
|
||||||
<d2p-ueditor v-model="currentForm.data[ueditorIndex][ueditorKey]" :config="ueditorConfig"></d2p-ueditor>
|
<d2p-ueditor
|
||||||
|
v-if="currentForm.data && currentForm.data[ueditorIndex] && ueditorKey"
|
||||||
|
v-model="currentForm.data[ueditorIndex][ueditorKey]"
|
||||||
|
:config="ueditorConfig">
|
||||||
|
</d2p-ueditor>
|
||||||
<span slot="footer" class="dialog-footer">
|
<span slot="footer" class="dialog-footer">
|
||||||
<el-button type="primary" @click="previewVisible = false">完成</el-button>
|
<el-button type="primary" @click="previewVisible = false">完成</el-button>
|
||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<el-dialog
|
||||||
|
title="编辑"
|
||||||
|
:visible.sync="manyToManyVisible"
|
||||||
|
append-to-body
|
||||||
|
v-if="currentForm.data && currentForm.data[manyToManyIndex] && manyToManyKey"
|
||||||
|
:width="elProps.fields[manyToManyKey].dialogWidth">
|
||||||
|
<foreign-key-crud-form
|
||||||
|
v-model="currentForm.data[manyToManyIndex][manyToManyKey]"
|
||||||
|
:isInitRows="elProps.fields[manyToManyKey].isInitRows"
|
||||||
|
:elProps="elProps.fields[manyToManyKey].elProps"
|
||||||
|
@change="foreignChange"
|
||||||
|
></foreign-key-crud-form>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="manyToManyVisible = false">保存</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
@ -124,6 +154,50 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: null
|
max: null
|
||||||
|
},
|
||||||
|
option_data: {
|
||||||
|
name: '选项题目',
|
||||||
|
type: 'many_to_many',
|
||||||
|
span: 2,
|
||||||
|
default: [],
|
||||||
|
required: false,
|
||||||
|
unit: '个',
|
||||||
|
value: {
|
||||||
|
type: 'strList',
|
||||||
|
rowKey: 'name',
|
||||||
|
title: '选项内容'
|
||||||
|
},
|
||||||
|
// 子级多对多
|
||||||
|
isInitRows: true,
|
||||||
|
dialogWidth: '700',
|
||||||
|
dict: {
|
||||||
|
value: 'id', // 数据字典中value字段的属性名
|
||||||
|
label: 'name' // 数据字典中label字段的属性名
|
||||||
|
},
|
||||||
|
elProps: {
|
||||||
|
index: {
|
||||||
|
name: '序号',
|
||||||
|
span: 2
|
||||||
|
},
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
name: '题目选项内容',
|
||||||
|
type: 'input',
|
||||||
|
span: 10,
|
||||||
|
default: null,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
name: '排序',
|
||||||
|
type: 'number',
|
||||||
|
span: 8,
|
||||||
|
default: 0,
|
||||||
|
required: true,
|
||||||
|
min: 0,
|
||||||
|
max: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +234,11 @@ export default {
|
||||||
// 富文本弹窗编辑框
|
// 富文本弹窗编辑框
|
||||||
previewVisible: false,
|
previewVisible: false,
|
||||||
ueditorIndex: 0,
|
ueditorIndex: 0,
|
||||||
ueditorKey: null
|
ueditorKey: null,
|
||||||
|
// 多对多弹窗
|
||||||
|
manyToManyIndex: 0,
|
||||||
|
manyToManyKey: null,
|
||||||
|
manyToManyVisible: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -250,6 +328,20 @@ export default {
|
||||||
this.ueditorKey = key
|
this.ueditorKey = key
|
||||||
this.previewVisible = true
|
this.previewVisible = true
|
||||||
console.log('previewClick', index, key)
|
console.log('previewClick', index, key)
|
||||||
|
},
|
||||||
|
// 多对多点击事件
|
||||||
|
manyToManyClick (index, key) {
|
||||||
|
this.manyToManyIndex = index
|
||||||
|
this.manyToManyKey = key
|
||||||
|
this.manyToManyVisible = true
|
||||||
|
if (!this.currentForm.data[this.manyToManyIndex][this.manyToManyKey]) {
|
||||||
|
this.currentForm.data[this.manyToManyIndex][this.manyToManyKey] = []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
foreignChange (res) {
|
||||||
|
if (this.manyToManyKey) {
|
||||||
|
this.currentForm.data[this.manyToManyIndex][this.manyToManyKey] = res
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div slot="reference" ref="divRef" :style="{'pointerEvents': disabled?'none':''}">
|
<div slot="reference" ref="divRef" :style="{'pointerEvents': disabled?'none':''}">
|
||||||
<div v-if="currentValue" class="div-input el-input__inner" :class="disabled?'div-disabled':''">
|
<div v-if="currentValue" class="div-input el-input__inner" :class="disabled?'div-disabled':''">
|
||||||
<div>
|
<div v-if="currentValue instanceof Array">
|
||||||
<el-tag
|
<el-tag
|
||||||
style="margin-right: 5px"
|
style="margin-right: 5px"
|
||||||
v-for="(item,index) in currentValue"
|
v-for="(item,index) in currentValue"
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-input v-else placeholder="请选择" slot:reference clearable :disabled="disabled"></el-input>
|
<el-input v-else placeholder="请选择" slot:reference clearable :disabled="disabled" :size="size"></el-input>
|
||||||
</div>
|
</div>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
</div>
|
</div>
|
||||||
|
@ -95,6 +95,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
// 数据字典配置
|
// 数据字典配置
|
||||||
dict: {
|
dict: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
{{ item[dict.label] }}
|
{{ item[dict.label] }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<el-button type="primary" plain size="mini" slot="reference"><span> {{ value.length }} {{ elProps.unit }}</span>
|
<el-button type="primary" plain size="mini" slot="reference" @click="listClick"><span> {{ value.length }} {{ elProps.unit }}</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<el-button v-else type="primary" plain size="mini" slot="reference"><span> {{
|
<el-button v-else type="primary" plain size="mini" slot="reference" @click="listClick"><span> {{
|
||||||
value.length
|
value.length
|
||||||
}} {{ elProps.unit }}</span>
|
}} {{ elProps.unit }}</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
@ -46,10 +46,10 @@
|
||||||
@show="showEvents"
|
@show="showEvents"
|
||||||
@hide="show=false">
|
@hide="show=false">
|
||||||
<div v-html="value" v-if="show"></div>
|
<div v-html="value" v-if="show"></div>
|
||||||
<el-button type="primary" plain size="mini" slot="reference"><span>预览</span>
|
<el-button type="primary" plain size="mini" slot="reference" @click="previewClick"><span>预览</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-popover>
|
</el-popover>
|
||||||
<el-button v-else type="primary" plain size="mini" slot="reference"><span>预览</span>
|
<el-button v-else type="primary" plain size="mini" slot="reference" @click="previewClick"><span>预览</span>
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,7 +125,9 @@ export default {
|
||||||
if (this.value.constructor === Array) {
|
if (this.value.constructor === Array) {
|
||||||
const ids = []
|
const ids = []
|
||||||
this.value.map(res => {
|
this.value.map(res => {
|
||||||
|
if (res) {
|
||||||
ids.push(res[this.dict.value])
|
ids.push(res[this.dict.value])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
params[this.dict.value] = ids
|
params[this.dict.value] = ids
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,6 +137,12 @@ export default {
|
||||||
request({ url: this.dict.url, params: params }).then(ret => {
|
request({ url: this.dict.url, params: params }).then(ret => {
|
||||||
this.data = ret.data.data || ret.data
|
this.data = ret.data.data || ret.data
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
previewClick () {
|
||||||
|
this.$emit('previewClick')
|
||||||
|
},
|
||||||
|
listClick () {
|
||||||
|
this.$emit('listClick')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
<!--
|
|
||||||
* @创建文件时间: 2021-06-01 22:41:20
|
|
||||||
* @Auther: 猿小天
|
|
||||||
* @最后修改人: 猿小天
|
|
||||||
* @最后修改时间: 2021-07-27 00:18:52
|
|
||||||
* 联系Qq:1638245306
|
|
||||||
* @文件介绍:
|
|
||||||
-->
|
|
||||||
<template>
|
<template>
|
||||||
<el-dropdown size="small" class="d2-mr">
|
<el-dropdown size="small" class="d2-mr">
|
||||||
<el-link
|
<el-link
|
||||||
|
@ -28,6 +20,9 @@
|
||||||
<el-dropdown-item @click.native="userInfo">
|
<el-dropdown-item @click.native="userInfo">
|
||||||
<d2-icon name="cog" class="d2-mr-5" />个人信息
|
<d2-icon name="cog" class="d2-mr-5" />个人信息
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click.native="clientInfo" v-if="info.tenant_id === 100000">
|
||||||
|
<d2-icon name="cog" class="d2-mr-5" />租户信息
|
||||||
|
</el-dropdown-item>
|
||||||
<el-dropdown-item @click.native="logOff" divided>
|
<el-dropdown-item @click.native="logOff" divided>
|
||||||
<d2-icon name="power-off" class="d2-mr-5" />
|
<d2-icon name="power-off" class="d2-mr-5" />
|
||||||
注销
|
注销
|
||||||
|
@ -61,6 +56,10 @@ export default {
|
||||||
/** 个人信息 */
|
/** 个人信息 */
|
||||||
userInfo () {
|
userInfo () {
|
||||||
this.$router.push({ path: 'userInfo' })
|
this.$router.push({ path: 'userInfo' })
|
||||||
|
},
|
||||||
|
/** 租户信息 */
|
||||||
|
clientInfo () {
|
||||||
|
this.$router.push({ path: 'myClientInfo' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,9 @@ import util from '@/libs/util'
|
||||||
import VueCoreVideoPlayer from 'vue-core-video-player'
|
import VueCoreVideoPlayer from 'vue-core-video-player'
|
||||||
// 引入echarts
|
// 引入echarts
|
||||||
import * as echarts from 'echarts' // 注册echarts组件
|
import * as echarts from 'echarts' // 注册echarts组件
|
||||||
|
// 第三方组件
|
||||||
|
import VueClipboard from 'vue-clipboard2'
|
||||||
|
Vue.use(VueClipboard)
|
||||||
Vue.use(VueCoreVideoPlayer)
|
Vue.use(VueCoreVideoPlayer)
|
||||||
// 核心插件
|
// 核心插件
|
||||||
Vue.use(d2Admin)
|
Vue.use(d2Admin)
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
/*
|
|
||||||
* @创建文件时间: 2021-06-01 22:41:21
|
|
||||||
* @Auther: 猿小天
|
|
||||||
* @最后修改人: 猿小天
|
|
||||||
* @最后修改时间: 2021-11-19 21:35:56
|
|
||||||
* 联系Qq:1638245306
|
|
||||||
* @文件介绍: 菜单获取
|
|
||||||
*/
|
|
||||||
import { uniqueId } from 'lodash'
|
import { uniqueId } from 'lodash'
|
||||||
import { request } from '@/api/service'
|
import { request } from '@/api/service'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
@ -78,7 +70,8 @@ export const handleRouter = function (menuData) {
|
||||||
meta: {
|
meta: {
|
||||||
title: item.name,
|
title: item.name,
|
||||||
auth: true,
|
auth: true,
|
||||||
cache: item.cache
|
cache: item.cache,
|
||||||
|
openInNewWindow: item.frame_out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.frame_out) {
|
if (item.frame_out) {
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
/*
|
|
||||||
* @创建文件时间: 2021-06-01 22:41:21
|
|
||||||
* @Auther: 猿小天
|
|
||||||
* @最后修改人: 猿小天
|
|
||||||
* @最后修改时间: 2021-11-19 21:49:43
|
|
||||||
* 联系Qq:1638245306
|
|
||||||
* @文件介绍:
|
|
||||||
*/
|
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueRouter from 'vue-router'
|
import VueRouter from 'vue-router'
|
||||||
// 进度条
|
// 进度条
|
||||||
|
@ -42,7 +34,7 @@ const router = new VueRouter({
|
||||||
*/
|
*/
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
// 白名单
|
// 白名单
|
||||||
const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/oauth2']
|
const whiteList = ['/login', '/auth-redirect', '/bind', '/register', '/clientRenew', '/oauth2']
|
||||||
// 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
// 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
||||||
await store.dispatch('d2admin/page/isLoaded')
|
await store.dispatch('d2admin/page/isLoaded')
|
||||||
// 确认已经加载组件尺寸设置 https://github.com/d2-projects/d2-admin/issues/198
|
// 确认已经加载组件尺寸设置 https://github.com/d2-projects/d2-admin/issues/198
|
||||||
|
@ -92,11 +84,21 @@ router.beforeEach(async (to, from, next) => {
|
||||||
next({ path: to.fullPath, replace: true, params: to.params })
|
next({ path: to.fullPath, replace: true, params: to.params })
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
next()
|
|
||||||
const childrenPath = window.qiankunActiveRule || []
|
const childrenPath = window.qiankunActiveRule || []
|
||||||
if (to.name) {
|
if (to.name) {
|
||||||
// 有 name 属性,说明是主应用的路由
|
// 有 name 属性,说明是主应用的路由
|
||||||
|
if (to.meta.openInNewWindow && !to.query.newWindow) {
|
||||||
|
// 在新窗口中打开路由
|
||||||
|
const { href } = router.resolve({
|
||||||
|
path: to.path + '?newWindow=1'
|
||||||
|
})
|
||||||
|
window.open(href, '_blank')
|
||||||
|
// 取消当前导航
|
||||||
|
NProgress.done()
|
||||||
|
next(false)
|
||||||
|
} else {
|
||||||
next()
|
next()
|
||||||
|
}
|
||||||
} else if (childrenPath.some((item) => to.path.includes(item))) {
|
} else if (childrenPath.some((item) => to.path.includes(item))) {
|
||||||
next()
|
next()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,6 +13,7 @@ export const crudOptions = (vm) => {
|
||||||
height: '100%', // 表格高度100%, 使用toolbar必须设置
|
height: '100%', // 表格高度100%, 使用toolbar必须设置
|
||||||
highlightCurrentRow: false,
|
highlightCurrentRow: false,
|
||||||
defaultExpandAll: true,
|
defaultExpandAll: true,
|
||||||
|
resizable: true,
|
||||||
treeConfig: {
|
treeConfig: {
|
||||||
transform: true,
|
transform: true,
|
||||||
rowField: 'id',
|
rowField: 'id',
|
||||||
|
@ -20,7 +21,13 @@ export const crudOptions = (vm) => {
|
||||||
hasChild: 'hasChild',
|
hasChild: 'hasChild',
|
||||||
lazy: true,
|
lazy: true,
|
||||||
loadMethod: ({ row }) => {
|
loadMethod: ({ row }) => {
|
||||||
return api.GetList({ parent: row.id }).then(ret => {
|
let query = JSON.parse(JSON.stringify(vm.getSearch().getForm()))
|
||||||
|
query = Object.fromEntries(
|
||||||
|
Object.entries(query).filter(([_, value]) => ![undefined, null, [], '[]', ''].includes(value))
|
||||||
|
)
|
||||||
|
query.parent = row.id
|
||||||
|
// console.log(query)
|
||||||
|
return api.GetList({ ...query }).then(ret => {
|
||||||
return ret.data.data
|
return ret.data.data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -28,6 +35,7 @@ export const crudOptions = (vm) => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
|
fixed: 'right',
|
||||||
width: 140,
|
width: 140,
|
||||||
view: {
|
view: {
|
||||||
thin: true,
|
thin: true,
|
||||||
|
@ -55,7 +63,7 @@ export const crudOptions = (vm) => {
|
||||||
// 或者直接传true,不显示title,不居中
|
// 或者直接传true,不显示title,不居中
|
||||||
title: '序号',
|
title: '序号',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
width: 100
|
width: 70
|
||||||
},
|
},
|
||||||
|
|
||||||
viewOptions: {
|
viewOptions: {
|
||||||
|
@ -71,7 +79,7 @@ export const crudOptions = (vm) => {
|
||||||
show: false,
|
show: false,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
search: {
|
search: {
|
||||||
disabled: false
|
disabled: true
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
|
@ -136,8 +144,8 @@ export const crudOptions = (vm) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
width: 180,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
showOverflow: 'tooltip',
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import { mapActions, mapState } from 'vuex'
|
import { mapActions, mapState } from 'vuex'
|
||||||
import localeMixin from '@/locales/mixin.js'
|
import localeMixin from '@/locales/mixin.js'
|
||||||
import * as api from '@/views/system/login/api'
|
import * as api from '@/views/system/login/api'
|
||||||
|
import { checkPlugins } from '@/views/plugins'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [localeMixin],
|
mixins: [localeMixin],
|
||||||
|
@ -55,7 +56,8 @@ export default {
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
password: 'admin123456'
|
password: 'admin123456'
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
isTenant: checkPlugins('dvadmin-tenants-web')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -68,7 +70,8 @@ export default {
|
||||||
helpUrl: state => state.settings.data['login.help_url'], // 帮助
|
helpUrl: state => state.settings.data['login.help_url'], // 帮助
|
||||||
privacyUrl: state => state.settings.data['login.privacy_url'], // 隐私
|
privacyUrl: state => state.settings.data['login.privacy_url'], // 隐私
|
||||||
clauseUrl: state => state.settings.data['login.clause_url'], // 条款
|
clauseUrl: state => state.settings.data['login.clause_url'], // 条款
|
||||||
captchaState: state => state.settings.data['base.captcha_state'] !== undefined ? state.settings.data['base.captcha_state'] : true // 验证码
|
captchaState: state => state.settings.data['base.captcha_state'] !== undefined ? state.settings.data['base.captcha_state'] : true, // 验证码
|
||||||
|
isPublic: state => state.settings.data.schema_name === 'public' // 是否超级租户
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
|
|
@ -69,9 +69,20 @@
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<button class="btn btn-primary btn-block" style="padding: 10px 10px;" @click="submit">
|
<el-row v-if="isTenant && isPublic">
|
||||||
登录
|
<el-col :span="11">
|
||||||
|
<button class="btn btn-primary btn-block" style="padding: 10px 10px;" @click="submit">登录</button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="11" :offset="2">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-block"
|
||||||
|
style="padding: 10px 10px;background-color: #409eff;color: #fff;"
|
||||||
|
@click="$router.push('/register')">
|
||||||
|
免费试用
|
||||||
</button>
|
</button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<button v-else class="btn btn-primary btn-block" style="padding: 10px 10px;" @click="submit">登录</button>
|
||||||
<component v-if="componentTag" :is="componentTag"></component>
|
<component v-if="componentTag" :is="componentTag"></component>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
|
|
@ -275,7 +275,7 @@ export const crudOptions = (vm) => {
|
||||||
return request({
|
return request({
|
||||||
url: url
|
url: url
|
||||||
}).then(ret => {
|
}).then(ret => {
|
||||||
return ret.data.data
|
return ret.data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue