Accept Merge Request #5: (develop -> master)
1. 新增: 大数据选择器组件 2. 新增: docker默认一键启动参数 3. 新增: 集成celery和Redis 4. 新增: 封装时间范围的搜索,示例在登录日志页面 5. 优化: 角色授权中的获取授权列表接口 6. 优化: 角色授权中字段权限显示判断 7. 优化: 个人中心修改密码成功后,强制退出登录状态 8. 优化: getBaseURL函数 9. 修复: 页面缓存无效问题 10. 修复: 登录页背景logo设置无效问题 11. 修复: 全局过滤器导致过滤无效问题 12. 修复: 当dept_belong_id为null时,触发接口报错
This commit is contained in:
@@ -409,5 +409,6 @@ PLUGINS_URL_PATTERNS = []
|
|||||||
# from dvadmin_ak_sk.settings import * # 秘钥管理管理
|
# from dvadmin_ak_sk.settings import * # 秘钥管理管理
|
||||||
# from dvadmin_tenants.settings import * # 租户管理
|
# from dvadmin_tenants.settings import * # 租户管理
|
||||||
#from dvadmin_social_auth.settings import *
|
#from dvadmin_social_auth.settings import *
|
||||||
|
#from dvadmin_uniapp.settings import *
|
||||||
# ...
|
# ...
|
||||||
# ********** 一键导入插件配置结束 **********
|
# ********** 一键导入插件配置结束 **********
|
||||||
|
|||||||
@@ -7,30 +7,30 @@ from application.settings import BASE_DIR
|
|||||||
# ================================================= #
|
# ================================================= #
|
||||||
# 数据库 ENGINE ,默认演示使用 sqlite3 数据库,正式环境建议使用 mysql 数据库
|
# 数据库 ENGINE ,默认演示使用 sqlite3 数据库,正式环境建议使用 mysql 数据库
|
||||||
# sqlite3 设置
|
# sqlite3 设置
|
||||||
DATABASE_ENGINE = "django.db.backends.sqlite3"
|
# DATABASE_ENGINE = "django.db.backends.sqlite3"
|
||||||
DATABASE_NAME = os.path.join(BASE_DIR, "db.sqlite3")
|
# DATABASE_NAME = os.path.join(BASE_DIR, "db.sqlite3")
|
||||||
|
|
||||||
# 使用mysql时,改为此配置
|
# 使用mysql时,改为此配置
|
||||||
# DATABASE_ENGINE = "django.db.backends.mysql"
|
DATABASE_ENGINE = "django.db.backends.mysql"
|
||||||
# DATABASE_NAME = 'django-vue-admin' # mysql 时使用
|
DATABASE_NAME = 'django-vue3-admin' # mysql 时使用
|
||||||
|
|
||||||
# 数据库地址 改为自己数据库地址
|
# 数据库地址 改为自己数据库地址
|
||||||
DATABASE_HOST = "127.0.0.1"
|
DATABASE_HOST = '127.0.0.1'
|
||||||
# # 数据库端口
|
# # 数据库端口
|
||||||
DATABASE_PORT = 3306
|
DATABASE_PORT = 3306
|
||||||
# # 数据库用户名
|
# # 数据库用户名
|
||||||
DATABASE_USER = "root"
|
DATABASE_USER = "root"
|
||||||
# # 数据库密码
|
# # 数据库密码
|
||||||
DATABASE_PASSWORD = "123456"
|
DATABASE_PASSWORD = "DVADMIN3"
|
||||||
|
|
||||||
# 表前缀
|
# 表前缀
|
||||||
TABLE_PREFIX = "dvadmin_"
|
TABLE_PREFIX = "dvadmin_"
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ******** redis配置,无redis 可不进行配置 ******** #
|
# ******** redis配置,无redis 可不进行配置 ******** #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# REDIS_PASSWORD = ''
|
REDIS_PASSWORD = 'DVADMIN3'
|
||||||
# REDIS_HOST = '127.0.0.1'
|
REDIS_HOST = '127.0.0.1'
|
||||||
# REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6380'
|
REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6379'
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ****************** 功能 启停 ******************* #
|
# ****************** 功能 启停 ******************* #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
|
|||||||
@@ -1 +1,56 @@
|
|||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
from django.db.models import Func, F, OuterRef, Exists
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
import django
|
||||||
|
import os
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "application.settings")
|
||||||
|
django.setup()
|
||||||
|
from dvadmin.system.models import Menu, RoleMenuPermission, RoleMenuButtonPermission, MenuButton
|
||||||
|
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
def timing_decorator(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
start_time = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end_time = time.time()
|
||||||
|
run_time = end_time - start_time
|
||||||
|
print(f"{func.__name__} ran in {run_time:.6f} seconds")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
@timing_decorator
|
||||||
|
def getMenu():
|
||||||
|
data = []
|
||||||
|
queryset = Menu.objects.filter(status=1, is_catalog=False).values('name', 'id')
|
||||||
|
for item in queryset:
|
||||||
|
parent_list = Menu.get_all_parent(item['id'])
|
||||||
|
names = [d["name"] for d in parent_list]
|
||||||
|
completeName = "/".join(names)
|
||||||
|
isCheck = RoleMenuPermission.objects.filter(
|
||||||
|
menu__id=item['id'],
|
||||||
|
role__id=1,
|
||||||
|
).exists()
|
||||||
|
mbCheck = RoleMenuButtonPermission.objects.filter(
|
||||||
|
menu_button = OuterRef("pk"),
|
||||||
|
role__id=1,
|
||||||
|
)
|
||||||
|
btns = MenuButton.objects.filter(
|
||||||
|
menu__id=item['id'],
|
||||||
|
).annotate(isCheck=Exists(mbCheck)).values('id', 'name', 'value', 'isCheck',data_range=F('menu_button_permission__data_range'))
|
||||||
|
# print(b)
|
||||||
|
dicts = {
|
||||||
|
'name': completeName,
|
||||||
|
'id': item['id'],
|
||||||
|
'isCheck': isCheck,
|
||||||
|
'btns':btns
|
||||||
|
}
|
||||||
|
print(dicts)
|
||||||
|
data.append(dicts)
|
||||||
|
# print(data)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
getMenu()
|
||||||
@@ -60,12 +60,9 @@ class AreaViewSet(CustomModelViewSet):
|
|||||||
del params['page']
|
del params['page']
|
||||||
if limit:
|
if limit:
|
||||||
del params['limit']
|
del params['limit']
|
||||||
if params:
|
if params and pcode:
|
||||||
if pcode:
|
queryset = self.queryset.filter(enable=True, pcode=pcode)
|
||||||
queryset = self.queryset.filter(enable=True, pcode=pcode)
|
|
||||||
else:
|
|
||||||
queryset = self.queryset.filter(enable=True)
|
|
||||||
else:
|
else:
|
||||||
queryset = self.queryset.filter(enable=True, pcode__isnull=True)
|
queryset = self.queryset.filter(enable=True)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ class RoleCreateUpdateSerializer(CustomModelSerializer):
|
|||||||
def validate(self, attrs: dict):
|
def validate(self, attrs: dict):
|
||||||
return super().validate(attrs)
|
return super().validate(attrs)
|
||||||
|
|
||||||
def save(self, **kwargs):
|
# def save(self, **kwargs):
|
||||||
is_superuser = self.request.user.is_superuser
|
# is_superuser = self.request.user.is_superuser
|
||||||
if not is_superuser:
|
# if not is_superuser:
|
||||||
self.validated_data.pop('admin')
|
# self.validated_data.pop('admin')
|
||||||
data = super().save(**kwargs)
|
# data = super().save(**kwargs)
|
||||||
return data
|
# return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Role
|
model = Role
|
||||||
|
|||||||
@@ -171,14 +171,46 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet):
|
|||||||
if role is None:
|
if role is None:
|
||||||
return ErrorResponse(msg="未获取到角色信息")
|
return ErrorResponse(msg="未获取到角色信息")
|
||||||
is_superuser = request.user.is_superuser
|
is_superuser = request.user.is_superuser
|
||||||
|
# if is_superuser:
|
||||||
|
# queryset = Menu.objects.filter(status=1,is_catalog=False).values('name', 'id').all()
|
||||||
|
# else:
|
||||||
|
# role_id = request.user.role.values_list('id', flat=True)
|
||||||
|
# menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id',flat=True)
|
||||||
|
# queryset = Menu.objects.filter(status=1, is_catalog=False,id__in=menu_list).values('name', 'id').all()
|
||||||
|
# serializer = RoleMenuPermissionSerializer(queryset,many=True,request=request)
|
||||||
|
# data = serializer.data
|
||||||
|
# return DetailResponse(data=data)
|
||||||
|
data = []
|
||||||
if is_superuser:
|
if is_superuser:
|
||||||
queryset = Menu.objects.filter(status=1,is_catalog=False).values('name', 'id').all()
|
queryset = Menu.objects.filter(status=1,is_catalog=False).values('name', 'id').all()
|
||||||
else:
|
else:
|
||||||
role_id = request.user.role.values_list('id', flat=True)
|
role_id = request.user.role.values_list('id', flat=True)
|
||||||
menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id',flat=True)
|
menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('id',flat=True)
|
||||||
queryset = Menu.objects.filter(status=1, is_catalog=False,id__in=menu_list).values('name', 'id').all()
|
queryset = Menu.objects.filter(status=1, is_catalog=False,id__in=menu_list).values('name', 'id')
|
||||||
serializer = RoleMenuPermissionSerializer(queryset,many=True,request=request)
|
for item in queryset:
|
||||||
data = serializer.data
|
parent_list = Menu.get_all_parent(item['id'])
|
||||||
|
names = [d["name"] for d in parent_list]
|
||||||
|
completeName = "/".join(names)
|
||||||
|
isCheck = RoleMenuPermission.objects.filter(
|
||||||
|
menu__id=item['id'],
|
||||||
|
role__id=role,
|
||||||
|
).exists()
|
||||||
|
mbCheck = RoleMenuButtonPermission.objects.filter(
|
||||||
|
menu_button=OuterRef("pk"),
|
||||||
|
role__id=role,
|
||||||
|
)
|
||||||
|
btns = MenuButton.objects.filter(
|
||||||
|
menu__id=item['id'],
|
||||||
|
).annotate(isCheck=Exists(mbCheck)).values('id', 'name', 'value', 'isCheck',
|
||||||
|
data_range=F('menu_button_permission__data_range'))
|
||||||
|
dicts = {
|
||||||
|
'name': completeName,
|
||||||
|
'id': item['id'],
|
||||||
|
'isCheck': isCheck,
|
||||||
|
'btns': btns,
|
||||||
|
|
||||||
|
}
|
||||||
|
data.append(dicts)
|
||||||
return DetailResponse(data=data)
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
@action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated])
|
@action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated])
|
||||||
|
|||||||
@@ -12,16 +12,49 @@ from collections import OrderedDict
|
|||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
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, FilterSet
|
||||||
from django_filters.filters import CharFilter
|
from django_filters.constants import ALL_FIELDS
|
||||||
|
from django_filters.filters import CharFilter, DateTimeFromToRangeFilter
|
||||||
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
|
||||||
|
from django_filters.conf import settings
|
||||||
from dvadmin.system.models import Dept, ApiWhiteList, RoleMenuButtonPermission
|
from dvadmin.system.models import Dept, ApiWhiteList, RoleMenuButtonPermission
|
||||||
|
from dvadmin.utils.models import CoreModel
|
||||||
|
|
||||||
|
class CoreModelFilterBankend(BaseFilterBackend):
|
||||||
|
"""
|
||||||
|
自定义时间范围过滤器
|
||||||
|
"""
|
||||||
|
def filter_queryset(self, request, queryset, view):
|
||||||
|
create_datetime_after = request.query_params.get('create_datetime_after', None)
|
||||||
|
create_datetime_before = request.query_params.get('create_datetime_before', None)
|
||||||
|
update_datetime_after = request.query_params.get('update_datetime_after', None)
|
||||||
|
update_datetime_before = request.query_params.get('update_datetime_after', None)
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 更新时间范围过滤条件
|
||||||
|
update_filter = Q()
|
||||||
|
if update_datetime_after and update_datetime_before:
|
||||||
|
update_filter &= Q(update_datetime__gte=update_datetime_after) & Q(update_datetime__lte=update_datetime_before)
|
||||||
|
elif update_datetime_after:
|
||||||
|
update_filter &= Q(update_datetime__gte=update_datetime_after)
|
||||||
|
elif update_datetime_before:
|
||||||
|
update_filter &= Q(update_datetime__lte=update_datetime_before)
|
||||||
|
# 结合两个时间范围过滤条件
|
||||||
|
queryset = queryset.filter(create_filter & update_filter)
|
||||||
|
return queryset
|
||||||
|
return queryset
|
||||||
|
|
||||||
def get_dept(dept_id: int, dept_all_list=None, dept_list=None):
|
def get_dept(dept_id: int, dept_all_list=None, dept_list=None):
|
||||||
"""
|
"""
|
||||||
@@ -172,6 +205,7 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
"$": "iregex",
|
"$": "iregex",
|
||||||
"~": "icontains",
|
"~": "icontains",
|
||||||
}
|
}
|
||||||
|
filter_fields = "__all__"
|
||||||
|
|
||||||
def construct_search(self, field_name, lookup_expr=None):
|
def construct_search(self, field_name, lookup_expr=None):
|
||||||
lookup = self.lookup_prefixes.get(field_name[0])
|
lookup = self.lookup_prefixes.get(field_name[0])
|
||||||
@@ -179,14 +213,16 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
field_name = field_name[1:]
|
field_name = field_name[1:]
|
||||||
else:
|
else:
|
||||||
lookup = lookup_expr
|
lookup = lookup_expr
|
||||||
if field_name.endswith(lookup):
|
if lookup:
|
||||||
return field_name
|
if field_name.endswith(lookup):
|
||||||
return LOOKUP_SEP.join([field_name, lookup])
|
return field_name
|
||||||
|
return LOOKUP_SEP.join([field_name, lookup])
|
||||||
|
return field_name
|
||||||
|
|
||||||
def find_filter_lookups(self, orm_lookups, search_term_key):
|
def find_filter_lookups(self, orm_lookups, search_term_key):
|
||||||
for lookup in orm_lookups:
|
for lookup in orm_lookups:
|
||||||
# if lookup.find(search_term_key) >= 0:
|
# 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
|
# 修复条件搜索错误 bug
|
||||||
if new_lookup == search_term_key:
|
if new_lookup == search_term_key:
|
||||||
return lookup
|
return lookup
|
||||||
@@ -202,18 +238,22 @@ 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)
|
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:
|
if filterset_class:
|
||||||
filterset_model = filterset_class._meta.model
|
filterset_model = filterset_class._meta.model
|
||||||
@@ -233,6 +273,51 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
MetaBase = getattr(self.filterset_base, "Meta", object)
|
MetaBase = getattr(self.filterset_base, "Meta", object)
|
||||||
|
|
||||||
class AutoFilterSet(self.filterset_base):
|
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
|
@classmethod
|
||||||
def get_filters(cls):
|
def get_filters(cls):
|
||||||
"""
|
"""
|
||||||
@@ -261,9 +346,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)
|
||||||
|
|
||||||
@@ -273,20 +361,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
|
||||||
@@ -308,22 +391,31 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
return queryset
|
return queryset
|
||||||
if filterset.__class__.__name__ == "AutoFilterSet":
|
if filterset.__class__.__name__ == "AutoFilterSet":
|
||||||
queryset = filterset.queryset
|
queryset = filterset.queryset
|
||||||
orm_lookups = []
|
filter_fields = filterset.filters if self.filter_fields == "__all__" else self.filter_fields
|
||||||
for search_field in filterset.filters:
|
orm_lookup_dict = dict(
|
||||||
if isinstance(filterset.filters[search_field], CharFilter):
|
zip(
|
||||||
orm_lookups.append(
|
[field for field in filter_fields],
|
||||||
self.construct_search(six.text_type(search_field), filterset.filters[search_field].lookup_expr)
|
[filterset.filters[lookup].lookup_expr for lookup in filterset.filters.keys()],
|
||||||
)
|
)
|
||||||
else:
|
)
|
||||||
orm_lookups.append(search_field)
|
orm_lookups = [
|
||||||
|
self.construct_search(lookup, lookup_expr) for lookup, lookup_expr in orm_lookup_dict.items()
|
||||||
|
]
|
||||||
|
# print(orm_lookups)
|
||||||
conditions = []
|
conditions = []
|
||||||
queries = []
|
queries = []
|
||||||
for search_term_key in filterset.data.keys():
|
for search_term_key in filterset.data.keys():
|
||||||
orm_lookup = self.find_filter_lookups(orm_lookups, search_term_key)
|
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
|
continue
|
||||||
query = Q(**{orm_lookup: filterset.data[search_term_key]})
|
filterset_data_len = len(filterset.data.getlist(search_term_key))
|
||||||
queries.append(query)
|
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:
|
if len(queries) > 0:
|
||||||
conditions.append(reduce(operator.and_, queries))
|
conditions.append(reduce(operator.and_, queries))
|
||||||
queryset = queryset.filter(reduce(operator.and_, conditions))
|
queryset = queryset.filter(reduce(operator.and_, conditions))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class CustomModelSerializer(DynamicFieldsMixin, ModelSerializer):
|
|||||||
# 修改人的审计字段名称, 默认modifier, 继承使用时可自定义覆盖
|
# 修改人的审计字段名称, 默认modifier, 继承使用时可自定义覆盖
|
||||||
modifier_field_id = "modifier"
|
modifier_field_id = "modifier"
|
||||||
modifier_name = serializers.SerializerMethodField(read_only=True)
|
modifier_name = serializers.SerializerMethodField(read_only=True)
|
||||||
dept_belong_id = serializers.IntegerField(required=False, default=None)
|
dept_belong_id = serializers.IntegerField(required=False, allow_null=True)
|
||||||
|
|
||||||
def get_modifier_name(self, instance):
|
def get_modifier_name(self, instance):
|
||||||
if not hasattr(instance, "modifier"):
|
if not hasattr(instance, "modifier"):
|
||||||
|
|||||||
@@ -7,16 +7,18 @@
|
|||||||
@Remark: 自定义视图集
|
@Remark: 自定义视图集
|
||||||
"""
|
"""
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
from django_filters import DateTimeFromToRangeFilter
|
||||||
|
from django_filters.rest_framework import FilterSet
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
|
|
||||||
from dvadmin.utils.filters import DataLevelPermissionsFilter
|
from dvadmin.utils.filters import DataLevelPermissionsFilter, CoreModelFilterBankend
|
||||||
from dvadmin.utils.import_export_mixin import ExportSerializerMixin, ImportSerializerMixin
|
from dvadmin.utils.import_export_mixin import ExportSerializerMixin, ImportSerializerMixin
|
||||||
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse
|
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse
|
||||||
from dvadmin.utils.permission import CustomPermission
|
from dvadmin.utils.permission import CustomPermission
|
||||||
from dvadmin.utils.models import get_custom_app_models
|
from dvadmin.utils.models import get_custom_app_models, CoreModel
|
||||||
from dvadmin.system.models import FieldPermission, MenuField
|
from dvadmin.system.models import FieldPermission, MenuField
|
||||||
from django_restql.mixins import QueryArgumentsMixin
|
from django_restql.mixins import QueryArgumentsMixin
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ class CustomModelViewSet(ModelViewSet, ImportSerializerMixin, ExportSerializerMi
|
|||||||
update_serializer_class = None
|
update_serializer_class = None
|
||||||
filter_fields = '__all__'
|
filter_fields = '__all__'
|
||||||
search_fields = ()
|
search_fields = ()
|
||||||
extra_filter_class = [DataLevelPermissionsFilter]
|
extra_filter_class = [CoreModelFilterBankend,DataLevelPermissionsFilter]
|
||||||
permission_classes = [CustomPermission]
|
permission_classes = [CustomPermission]
|
||||||
import_field_dict = {}
|
import_field_dict = {}
|
||||||
export_field_label = {}
|
export_field_label = {}
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ uvicorn==0.23.2
|
|||||||
gunicorn==21.2.0
|
gunicorn==21.2.0
|
||||||
gevent==23.9.1
|
gevent==23.9.1
|
||||||
Pillow==10.1.0
|
Pillow==10.1.0
|
||||||
|
dvadmin-celery==1.0.5
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ services:
|
|||||||
- ./docker_env/nginx/my.conf:/etc/nginx/conf.d/my.conf
|
- ./docker_env/nginx/my.conf:/etc/nginx/conf.d/my.conf
|
||||||
expose:
|
expose:
|
||||||
- "8080"
|
- "8080"
|
||||||
|
restart: always
|
||||||
networks:
|
networks:
|
||||||
network:
|
network:
|
||||||
ipv4_address: 177.10.0.11
|
ipv4_address: 177.10.0.11
|
||||||
@@ -23,9 +24,8 @@ services:
|
|||||||
dockerfile: ./docker_env/django/Dockerfile
|
dockerfile: ./docker_env/django/Dockerfile
|
||||||
container_name: dvadmin3-django
|
container_name: dvadmin3-django
|
||||||
working_dir: /backend
|
working_dir: /backend
|
||||||
# 打开mysql 时,打开此选项
|
depends_on:
|
||||||
# depends_on:
|
- dvadmin3-mysql
|
||||||
# - dvadmin3-mysql
|
|
||||||
environment:
|
environment:
|
||||||
PYTHONUNBUFFERED: 1
|
PYTHONUNBUFFERED: 1
|
||||||
DATABASE_HOST: dvadmin3-mysql
|
DATABASE_HOST: dvadmin3-mysql
|
||||||
@@ -42,74 +42,70 @@ services:
|
|||||||
network:
|
network:
|
||||||
ipv4_address: 177.10.0.12
|
ipv4_address: 177.10.0.12
|
||||||
|
|
||||||
# dvadmin3-mysql:
|
dvadmin3-mysql:
|
||||||
# image: mysql:5.7
|
image: mysql:8.0
|
||||||
# container_name: dvadmin3-mysql
|
container_name: dvadmin3-mysql
|
||||||
# #使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限
|
privileged: true
|
||||||
# #设置为true,不然数据卷可能挂载不了,启动不起
|
restart: always
|
||||||
## privileged: true
|
ports:
|
||||||
# restart: always
|
- "3306:3306"
|
||||||
# ports:
|
environment:
|
||||||
# - "3306:3306"
|
MYSQL_ROOT_PASSWORD: "DVADMIN3"
|
||||||
# environment:
|
MYSQL_DATABASE: "django-vue3-admin"
|
||||||
# MYSQL_ROOT_PASSWORD: "123456"
|
TZ: Asia/Shanghai
|
||||||
# MYSQL_DATABASE: "dvadmin3_pro"
|
command:
|
||||||
# TZ: Asia/Shanghai
|
--wait_timeout=31536000
|
||||||
# command:
|
--interactive_timeout=31536000
|
||||||
# --wait_timeout=31536000
|
--max_connections=1000
|
||||||
# --interactive_timeout=31536000
|
--default-authentication-plugin=mysql_native_password
|
||||||
# --max_connections=1000
|
volumes:
|
||||||
# --default-authentication-plugin=mysql_native_password
|
- "./docker_env/mysql/data:/var/lib/mysql"
|
||||||
# volumes:
|
- "./docker_env/mysql/conf.d:/etc/mysql/conf.d"
|
||||||
# - "./docker_env/mysql/data:/var/lib/mysql"
|
- "./docker_env/mysql/logs:/logs"
|
||||||
# - "./docker_env/mysql/conf.d:/etc/mysql/conf.d"
|
networks:
|
||||||
# - "./docker_env/mysql/logs:/logs"
|
network:
|
||||||
# networks:
|
ipv4_address: 177.10.0.13
|
||||||
# network:
|
|
||||||
# ipv4_address: 177.10.0.13
|
|
||||||
|
|
||||||
|
|
||||||
# 如果使用celery 插件,请自行打开此注释
|
dvadmin3-celery:
|
||||||
# dvadmin3-celery:
|
build:
|
||||||
# build:
|
context: .
|
||||||
# context: .
|
dockerfile: ./docker_env/celery/Dockerfile
|
||||||
# dockerfile: ./docker_env/celery/Dockerfile
|
container_name: dvadmin3-celery
|
||||||
# # image: django:2.2
|
working_dir: /backend
|
||||||
# container_name: dvadmin3-celery
|
depends_on:
|
||||||
# working_dir: /backend
|
- dvadmin3-mysql
|
||||||
# depends_on:
|
environment:
|
||||||
# - dvadmin3-mysql
|
PYTHONUNBUFFERED: 1
|
||||||
# environment:
|
DATABASE_HOST: dvadmin3-mysql
|
||||||
# PYTHONUNBUFFERED: 1
|
TZ: Asia/Shanghai
|
||||||
# DATABASE_HOST: dvadmin3-mysql
|
volumes:
|
||||||
# TZ: Asia/Shanghai
|
- ./backend:/backend
|
||||||
# volumes:
|
- ./logs/log:/var/log
|
||||||
# - ./backend:/backend
|
restart: always
|
||||||
# - ./logs/log:/var/log
|
networks:
|
||||||
# restart: always
|
network:
|
||||||
# networks:
|
ipv4_address: 177.10.0.14
|
||||||
# network:
|
|
||||||
# ipv4_address: 177.10.0.14
|
|
||||||
|
|
||||||
|
|
||||||
# dvadmin3-redis:
|
dvadmin3-redis:
|
||||||
# image: redis:6.2.6-alpine # 指定服务镜像,最好是与之前下载的redis配置文件保持一致
|
image: redis:6.2.6-alpine # 指定服务镜像,最好是与之前下载的redis配置文件保持一致
|
||||||
# container_name: dvadmin3-redis # 容器名称
|
container_name: dvadmin3-redis # 容器名称
|
||||||
# restart: on-failure # 重启方式
|
restart: always
|
||||||
# environment:
|
environment:
|
||||||
# - TZ=Asia/Shanghai # 设置时区
|
- TZ=Asia/Shanghai # 设置时区
|
||||||
# volumes: # 配置数据卷
|
volumes: # 配置数据卷
|
||||||
# - ./docker_env/redis/data:/data
|
- ./docker_env/redis/data:/data
|
||||||
# - ./docker_env/redis/redis.conf:/etc/redis/redis.conf
|
- ./docker_env/redis/redis.conf:/etc/redis/redis.conf
|
||||||
# ports: # 映射端口
|
ports: # 映射端口
|
||||||
# - "6379:6379"
|
- "6379:6379"
|
||||||
# sysctls: # 设置容器中的内核参数
|
sysctls: # 设置容器中的内核参数
|
||||||
# - net.core.somaxconn=1024
|
- net.core.somaxconn=1024
|
||||||
# command: /bin/sh -c "echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf && redis-server /etc/redis/redis.conf --appendonly yes" # 指定配置文件并开启持久化
|
command: /bin/sh -c "echo 'vm.overcommit_memory = 1' >> /etc/sysctl.conf && redis-server /etc/redis/redis.conf --appendonly yes --requirepass DVADMIN3" # 指定配置文件并开启持久化
|
||||||
# privileged: true # 使用该参数,container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限
|
privileged: true # 使用该参数,container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限
|
||||||
# networks:
|
networks:
|
||||||
# network:
|
network:
|
||||||
# ipv4_address: 177.10.0.15
|
ipv4_address: 177.10.0.15
|
||||||
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-backend:latest
|
FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-backend:latest
|
||||||
WORKDIR /backend
|
WORKDIR /backend
|
||||||
COPY ./backend/ .
|
COPY ./backend/ .
|
||||||
|
RUN ls ./conf/
|
||||||
RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" |cmd; }'
|
RUN awk 'BEGIN { cmd="cp -i ./conf/env.example.py ./conf/env.py "; print "n" |cmd; }'
|
||||||
|
RUN sed -i "s|DATABASE_HOST = "127.0.0.1"|DATABASE_HOST = '177.10.0.1'|g" ./conf/env.py
|
||||||
|
RUN sed -i "s|REDIS_HOST = '127.0.0.1'|REDIS_HOST = '177.10.0.1'|g" ./conf/env.py
|
||||||
RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt
|
RUN python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r requirements.txt
|
||||||
CMD ["/backend/docker_start.sh"]
|
CMD ["/backend/docker_start.sh"]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
# 本地环境接口地址
|
# 本地环境接口地址
|
||||||
VITE_API_URL = 'http://127.0.0.1:8001'
|
VITE_API_URL = 'http://127.0.0.1:8000'
|
||||||
|
|
||||||
# 是否启用按钮权限
|
# 是否启用按钮权限
|
||||||
VITE_PM_ENABLED = true
|
VITE_PM_ENABLED = true
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"@fast-crud/fast-extends": "^1.19.2",
|
"@fast-crud/fast-extends": "^1.19.2",
|
||||||
"@fast-crud/ui-element": "^1.19.2",
|
"@fast-crud/ui-element": "^1.19.2",
|
||||||
"@fast-crud/ui-interface": "^1.19.2",
|
"@fast-crud/ui-interface": "^1.19.2",
|
||||||
|
"@types/lodash": "^4.14.202",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
"echarts": "^5.4.1",
|
"echarts": "^5.4.1",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "^2.3.9",
|
"element-plus": "^2.5.5",
|
||||||
"element-tree-line": "^0.2.1",
|
"element-tree-line": "^0.2.1",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
|
|||||||
141
web/src/components/dvaSelect/index.vue
Normal file
141
web/src/components/dvaSelect/index.vue
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 你的自定义受控组件-->
|
||||||
|
<el-select-v2
|
||||||
|
v-model="data"
|
||||||
|
:options="options"
|
||||||
|
style="width: 100%;"
|
||||||
|
:clearable="true"
|
||||||
|
:props="selectProps"
|
||||||
|
@change="onDataChange"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {ref, defineComponent, watch, computed, toRefs, toRaw, onMounted} from 'vue'
|
||||||
|
import {useUi} from "@fast-crud/fast-crud";
|
||||||
|
import {request} from "/@/utils/service";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
dict: { // 接收来自FastCrud配置中的dict数据
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
modelValue: {}
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
// 获取数据
|
||||||
|
const dataList = ref([])
|
||||||
|
|
||||||
|
function getData(params) {
|
||||||
|
request({
|
||||||
|
url: props.dict.url,
|
||||||
|
params: params
|
||||||
|
}).then(res => {
|
||||||
|
dataList.value = res.data
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// template上使用data
|
||||||
|
const data = ref()
|
||||||
|
// const data = computed({
|
||||||
|
// get: () => {
|
||||||
|
// console.log("有默认值", props.modelValue)
|
||||||
|
// //getData({id:props.modelValue})
|
||||||
|
//
|
||||||
|
// console.log(11, dataList)
|
||||||
|
// // const {data} = res
|
||||||
|
// // console.log("get",data[0][selectProps.value.label])
|
||||||
|
// if (dataList && dataList.length === 1) {
|
||||||
|
// return dataList[0][selectProps.value.value]
|
||||||
|
// } else {
|
||||||
|
// // console.log("aa",res.data)
|
||||||
|
// return props.modelValue
|
||||||
|
// }
|
||||||
|
// // return props.modelValue
|
||||||
|
// },
|
||||||
|
// set: (val) => {
|
||||||
|
// //data.value = val
|
||||||
|
// return val
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
const options = ref([])
|
||||||
|
const selectProps = ref({
|
||||||
|
label: 'label',
|
||||||
|
value: 'value'
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
() => {
|
||||||
|
return props.modelValue
|
||||||
|
}, // 监听modelValue的变化,
|
||||||
|
(value) => {
|
||||||
|
// data.value = value
|
||||||
|
request({
|
||||||
|
url: props.dict.url,
|
||||||
|
params: {
|
||||||
|
id: props.modelValue
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
const dataList = res.data
|
||||||
|
console.log(dataList)
|
||||||
|
if (dataList && dataList.length === 1) {
|
||||||
|
data.value = dataList[0][selectProps.value.label]
|
||||||
|
}else{
|
||||||
|
data.value = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, // 当modelValue值触发后,同步修改data.value的值
|
||||||
|
{immediate: true} // 立即触发一次,给data赋值初始值
|
||||||
|
)
|
||||||
|
//获取表单校验上下文
|
||||||
|
const {ui} = useUi()
|
||||||
|
const formValidator = ui.formItem.injectFormItemContext();
|
||||||
|
// 当data需要变化时,上报给父组件
|
||||||
|
// 父组件监听到update:modelValue事件后,会更新props.modelValue的值
|
||||||
|
// 然后watch会被触发,修改data.value的值。
|
||||||
|
function onDataChange(value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
data.value = value
|
||||||
|
//触发校验
|
||||||
|
formValidator.onChange()
|
||||||
|
formValidator.onBlur()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (props.dict.url instanceof Function) {
|
||||||
|
request(props.dict.url).then((res) => {
|
||||||
|
options.value = res.data
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
selectProps.value.label = props.dict.label
|
||||||
|
selectProps.value.value = props.dict.value
|
||||||
|
request({
|
||||||
|
url: props.dict.url
|
||||||
|
}).then((res) => {
|
||||||
|
options.value = res.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// onMounted(() => {
|
||||||
|
// getData({id: props.modelValue})
|
||||||
|
// })
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.el-select .el-input__wrapper .el-input__inner::placeholder {
|
||||||
|
//color: #a8abb2;
|
||||||
|
color: #0d84ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select-v2 {
|
||||||
|
.el-select-v2__wrapper {
|
||||||
|
.el-select-v2__placeholder.is-transparent {
|
||||||
|
//color: #a8abb2;
|
||||||
|
color: #0d84ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
import {defineProps, onMounted, reactive, ref, toRaw, watch} from 'vue'
|
import {defineProps, onMounted, reactive, ref, toRaw, watch} from 'vue'
|
||||||
import {dict} from '@fast-crud/fast-crud'
|
import {dict} from '@fast-crud/fast-crud'
|
||||||
import XEUtils from 'xe-utils'
|
import XEUtils from 'xe-utils'
|
||||||
|
import {request} from '/@/utils/service'
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
tableConfig: {
|
tableConfig: {
|
||||||
@@ -71,6 +71,7 @@ watch(multipleSelection, // 监听multipleSelection的变化,
|
|||||||
if (!tableConfig.isMultiple) {
|
if (!tableConfig.isMultiple) {
|
||||||
data.value = value ? value[tableConfig.label] : null
|
data.value = value ? value[tableConfig.label] : null
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
const result = value ? value.map((item: any) => {
|
const result = value ? value.map((item: any) => {
|
||||||
return item[tableConfig.label]
|
return item[tableConfig.label]
|
||||||
}) : null
|
}) : null
|
||||||
@@ -123,12 +124,12 @@ const getDict = async () => {
|
|||||||
const params = {
|
const params = {
|
||||||
page: pageConfig.page,
|
page: pageConfig.page,
|
||||||
limit: pageConfig.limit,
|
limit: pageConfig.limit,
|
||||||
search: search
|
search: search.value
|
||||||
}
|
}
|
||||||
const dicts = dict({url: url, params: params})
|
const {data, page, limit, total} = await request({
|
||||||
await dicts.reloadDict()
|
url:url,
|
||||||
const dictData: any = dicts.data
|
params:params
|
||||||
const {data, page, limit, total} = dictData
|
})
|
||||||
pageConfig.page = page
|
pageConfig.page = page
|
||||||
pageConfig.limit = limit
|
pageConfig.limit = limit
|
||||||
pageConfig.total = total
|
pageConfig.total = total
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import { useUserInfo } from '/@/stores/userInfo';
|
|
||||||
import { judementSameArr } from '/@/utils/arrayOperation';
|
import { judementSameArr } from '/@/utils/arrayOperation';
|
||||||
|
import {BtnPermissionStore} from "/@/stores/btnPermission";
|
||||||
/**
|
/**
|
||||||
* 用户权限指令
|
* 用户权限指令
|
||||||
* @directive 单个权限验证(v-auth="xxx")
|
* @directive 单个权限验证(v-auth="xxx")
|
||||||
@@ -12,16 +11,16 @@ export function authDirective(app: App) {
|
|||||||
// 单个权限验证(v-auth="xxx")
|
// 单个权限验证(v-auth="xxx")
|
||||||
app.directive('auth', {
|
app.directive('auth', {
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
const stores = useUserInfo();
|
const stores = BtnPermissionStore();
|
||||||
if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
|
if (!stores.data.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
|
// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
|
||||||
app.directive('auths', {
|
app.directive('auths', {
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
let flag = false;
|
let flag = false;
|
||||||
const stores = useUserInfo();
|
const stores = BtnPermissionStore();
|
||||||
stores.userInfos.authBtnList.map((val: string) => {
|
stores.data.map((val: string) => {
|
||||||
binding.value.map((v: string) => {
|
binding.value.map((v: string) => {
|
||||||
if (val === v) flag = true;
|
if (val === v) flag = true;
|
||||||
});
|
});
|
||||||
@@ -32,8 +31,8 @@ export function authDirective(app: App) {
|
|||||||
// 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
|
// 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
|
||||||
app.directive('auth-all', {
|
app.directive('auth-all', {
|
||||||
mounted(el, binding) {
|
mounted(el, binding) {
|
||||||
const stores = useUserInfo();
|
const stores = BtnPermissionStore();
|
||||||
const flag = judementSameArr(binding.value, stores.userInfos.authBtnList);
|
const flag = judementSameArr(binding.value, stores.data);
|
||||||
if (!flag) el.parentNode.removeChild(el);
|
if (!flag) el.parentNode.removeChild(el);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import { authDirective } from '/@/directive/authDirective';
|
import { authDirective } from '/@/directive/authDirective';
|
||||||
import { wavesDirective, dragDirective } from '/@/directive/customDirective';
|
import { wavesDirective, dragDirective } from '/@/directive/customDirective';
|
||||||
|
import {resizeObDirective} from '/@/directive/sizeDirective'
|
||||||
/**
|
/**
|
||||||
* 导出指令方法:v-xxx
|
* 导出指令方法:v-xxx
|
||||||
* @methods authDirective 用户权限指令,用法:v-auth
|
* @methods authDirective 用户权限指令,用法:v-auth
|
||||||
@@ -15,4 +15,6 @@ export function directive(app: App) {
|
|||||||
wavesDirective(app);
|
wavesDirective(app);
|
||||||
// 自定义拖动指令
|
// 自定义拖动指令
|
||||||
dragDirective(app);
|
dragDirective(app);
|
||||||
|
// 监听窗口大小变化
|
||||||
|
resizeObDirective(app)
|
||||||
}
|
}
|
||||||
|
|||||||
23
web/src/directive/sizeDirective.ts
Normal file
23
web/src/directive/sizeDirective.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import {App} from "vue/dist/vue";
|
||||||
|
|
||||||
|
const map = new WeakMap()
|
||||||
|
const ob = new ResizeObserver((entries) => {
|
||||||
|
for(const entry of entries){
|
||||||
|
const handler = map.get(entry.target);
|
||||||
|
handler && handler({
|
||||||
|
width: entry.borderBoxSize[0].inlineSize,
|
||||||
|
height: entry.borderBoxSize[0].blockSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
export function resizeObDirective(app: App){
|
||||||
|
app.directive('resizeOb', {
|
||||||
|
mounted(el,binding) {
|
||||||
|
map.set(el,binding.value);
|
||||||
|
ob.observe(el); // 监听目标元素
|
||||||
|
},
|
||||||
|
unmounted(el) {
|
||||||
|
ob.unobserve(el); // 停止监听
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
|
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
|
||||||
<img :src="logoMini" class="layout-logo-medium-img" />
|
<img :src="siteLogo" class="layout-logo-medium-img" />
|
||||||
<span style="font-size: x-large">{{ getSystemConfig['login.site_title'] || themeConfig.globalTitle }}</span>
|
<span style="font-size: x-large">{{ getSystemConfig['login.site_title'] || themeConfig.globalTitle }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
||||||
<img :src="logoMini" class="layout-logo-size-img" />
|
<img :src="siteLogo" class="layout-logo-size-img" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ import { computed } from 'vue';
|
|||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
import { useThemeConfig } from '/@/stores/themeConfig';
|
||||||
import logoMini from '/@/assets/logo-mini.svg';
|
import logoMini from '/@/assets/logo-mini.svg';
|
||||||
import {SystemConfigStore} from "/@/stores/systemConfig";
|
import { SystemConfigStore } from "/@/stores/systemConfig";
|
||||||
|
import _ from "lodash";
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const storesThemeConfig = useThemeConfig();
|
const storesThemeConfig = useThemeConfig();
|
||||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||||
@@ -31,11 +31,18 @@ const onThemeConfigChange = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const systemConfigStore = SystemConfigStore()
|
const systemConfigStore = SystemConfigStore()
|
||||||
const {systemConfig} = storeToRefs(systemConfigStore)
|
const { systemConfig } = storeToRefs(systemConfigStore)
|
||||||
const getSystemConfig = computed(()=>{
|
const getSystemConfig = computed(() => {
|
||||||
return systemConfig.value
|
return systemConfig.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const siteLogo = computed(() => {
|
||||||
|
if (!_.isEmpty(getSystemConfig.value['login.site_logo'])) {
|
||||||
|
return getSystemConfig.value['login.site_logo']
|
||||||
|
}
|
||||||
|
return logoMini
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -50,30 +57,36 @@ const getSystemConfig = computed(()=>{
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
animation: logoAnimation 0.3s ease-in-out;
|
animation: logoAnimation 0.3s ease-in-out;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
span {
|
span {
|
||||||
color: var(--color-primary-light-2);
|
color: var(--color-primary-light-2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-medium-img {
|
&-medium-img {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-logo-size {
|
.layout-logo-size {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
animation: logoAnimation 0.3s ease-in-out;
|
animation: logoAnimation 0.3s ease-in-out;
|
||||||
|
|
||||||
&-img {
|
&-img {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
img {
|
img {
|
||||||
animation: logoAnimation 0.3s ease-in-out;
|
animation: logoAnimation 0.3s ease-in-out;
|
||||||
|
|||||||
@@ -3,9 +3,7 @@
|
|||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<transition :name="setTransitionName" mode="out-in">
|
<transition :name="setTransitionName" mode="out-in">
|
||||||
<keep-alive :include="getKeepAliveNames" v-if="showView">
|
<keep-alive :include="getKeepAliveNames" v-if="showView">
|
||||||
<div>
|
|
||||||
<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
|
<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
|
||||||
</div>
|
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
@@ -61,6 +59,7 @@ const setTransitionName = computed(() => {
|
|||||||
});
|
});
|
||||||
// 获取组件缓存列表(name值)
|
// 获取组件缓存列表(name值)
|
||||||
const getKeepAliveNames = computed(() => {
|
const getKeepAliveNames = computed(() => {
|
||||||
|
console.log(cachedViews.value)
|
||||||
return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
|
return themeConfig.value.isTagsview ? cachedViews.value : state.keepAliveNameList;
|
||||||
});
|
});
|
||||||
// 设置 iframe 显示/隐藏
|
// 设置 iframe 显示/隐藏
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import { directive } from '/@/utils/directive';
|
import { directive } from '/@/directive/index';
|
||||||
import { i18n } from '/@/i18n';
|
import { i18n } from '/@/i18n';
|
||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
import '/@/assets/style/tailwind.css'; // 先引入tailwind css, 以免element-plus冲突
|
import '/@/assets/style/tailwind.css'; // 先引入tailwind css, 以免element-plus冲突
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import type { App } from 'vue';
|
|
||||||
import { judementSameArr } from '/@/utils/arrayOperation';
|
|
||||||
import {BtnPermissionStore} from "/@/stores/btnPermission";
|
|
||||||
/**
|
|
||||||
* 用户权限指令
|
|
||||||
* @directive 单个权限验证(v-auth="xxx")
|
|
||||||
* @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
|
|
||||||
* @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
|
|
||||||
*/
|
|
||||||
export function authDirective(app: App) {
|
|
||||||
// 单个权限验证(v-auth="xxx")
|
|
||||||
app.directive('auth', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
const stores = BtnPermissionStore();
|
|
||||||
if (!stores.data.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
|
|
||||||
app.directive('auths', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
let flag = false;
|
|
||||||
const stores = BtnPermissionStore();
|
|
||||||
stores.data.map((val: string) => {
|
|
||||||
binding.value.map((v: string) => {
|
|
||||||
if (val === v) flag = true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (!flag) el.parentNode.removeChild(el);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
|
|
||||||
app.directive('auth-all', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
const stores = BtnPermissionStore();
|
|
||||||
const flag = judementSameArr(binding.value, stores.data);
|
|
||||||
if (!flag) el.parentNode.removeChild(el);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -3,8 +3,12 @@ import { pluginsAll } from '/@/views/plugins/index';
|
|||||||
/**
|
/**
|
||||||
* @description 校验是否为租户模式。租户模式把域名替换成 域名 加端口
|
* @description 校验是否为租户模式。租户模式把域名替换成 域名 加端口
|
||||||
*/
|
*/
|
||||||
export const getBaseURL = function (url:string) {
|
export const getBaseURL = function (url: null | string = null, isHost: null | boolean = null) {
|
||||||
let baseURL = import.meta.env.VITE_API_URL as any;
|
let baseURL = import.meta.env.VITE_API_URL as any;
|
||||||
|
// 如果需要host返回,时,返回地址前缀加http地址
|
||||||
|
if (isHost && !baseURL.startsWith('http')) {
|
||||||
|
baseURL = window.location.protocol + '//' + window.location.host + baseURL
|
||||||
|
}
|
||||||
let param = baseURL.split('/')[3] || '';
|
let param = baseURL.split('/')[3] || '';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (pluginsAll && pluginsAll.indexOf('dvadmin3-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
|
if (pluginsAll && pluginsAll.indexOf('dvadmin3-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
|
||||||
@@ -26,14 +30,13 @@ export const getBaseURL = function (url:string) {
|
|||||||
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL;
|
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(url){
|
if (url) {
|
||||||
const regex = /^(http|https):\/\//;
|
const regex = /^(http|https):\/\//;
|
||||||
if(regex.test(url)){
|
if (regex.test(url)) {
|
||||||
return url
|
return url
|
||||||
}else{
|
} else {
|
||||||
if(url.startsWith('/')){
|
// js判断是否是斜杠结尾
|
||||||
return baseURL + url;
|
return baseURL.replace(/\/$/, '') + '/' + url.replace(/^\//, '');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!baseURL.endsWith('/')) {
|
if (!baseURL.endsWith('/')) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { dict } from "@fast-crud/fast-crud";
|
import {dict} from "@fast-crud/fast-crud";
|
||||||
import {shallowRef} from 'vue'
|
import {shallowRef} from 'vue'
|
||||||
import deptFormat from "/@/components/dept-format/index.vue";
|
import deptFormat from "/@/components/dept-format/index.vue";
|
||||||
|
|
||||||
export const commonCrudConfig = (options = {
|
export const commonCrudConfig = (options = {
|
||||||
create_datetime: {
|
create_datetime: {
|
||||||
form: false,
|
form: false,
|
||||||
@@ -51,7 +52,7 @@ export const commonCrudConfig = (options = {
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
width: 300,
|
width: 300,
|
||||||
show: options.dept_belong_id?.table || false,
|
show: options.dept_belong_id?.table || false,
|
||||||
component:{
|
component: {
|
||||||
name: shallowRef(deptFormat),
|
name: shallowRef(deptFormat),
|
||||||
vModel: "modelValue",
|
vModel: "modelValue",
|
||||||
}
|
}
|
||||||
@@ -62,7 +63,7 @@ export const commonCrudConfig = (options = {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
clearable: true,
|
clearable: true,
|
||||||
props: {
|
props: {
|
||||||
checkStrictly:true,
|
checkStrictly: true,
|
||||||
props: {
|
props: {
|
||||||
// 为什么这里要写两层props
|
// 为什么这里要写两层props
|
||||||
// 因为props属性名与fs的动态渲染的props命名冲突,所以要多写一层
|
// 因为props属性名与fs的动态渲染的props命名冲突,所以要多写一层
|
||||||
@@ -132,7 +133,53 @@ export const commonCrudConfig = (options = {
|
|||||||
title: '更新时间',
|
title: '更新时间',
|
||||||
type: 'datetime',
|
type: 'datetime',
|
||||||
search: {
|
search: {
|
||||||
show: options.update_datetime?.search || false
|
show: options.update_datetime?.search || false,
|
||||||
|
col: {span: 8},
|
||||||
|
component: {
|
||||||
|
type: 'datetimerange',
|
||||||
|
props: {
|
||||||
|
'start-placeholder': '开始时间',
|
||||||
|
'end-placeholder': '结束时间',
|
||||||
|
'value-format': 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
'picker-options': {
|
||||||
|
shortcuts: [{
|
||||||
|
text: '最近一周',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近一个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近三个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valueResolve(context: any) {
|
||||||
|
const {key, value} = context
|
||||||
|
//value解析,就是把组件的值转化为后台所需要的值
|
||||||
|
//在form表单点击保存按钮后,提交到后台之前执行转化
|
||||||
|
if (value) {
|
||||||
|
context.form.update_datetime_after = value[0]
|
||||||
|
context.form.update_datetime_before = value[1]
|
||||||
|
}
|
||||||
|
// ↑↑↑↑↑ 注意这里是form,不是row
|
||||||
|
}
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 160,
|
width: 160,
|
||||||
@@ -149,14 +196,60 @@ export const commonCrudConfig = (options = {
|
|||||||
title: '创建时间',
|
title: '创建时间',
|
||||||
type: 'datetime',
|
type: 'datetime',
|
||||||
search: {
|
search: {
|
||||||
show: options.create_datetime?.search || false
|
show: options.create_datetime?.search || false,
|
||||||
|
col: {span: 8},
|
||||||
|
component: {
|
||||||
|
type: 'datetimerange',
|
||||||
|
props: {
|
||||||
|
'start-placeholder': '开始时间',
|
||||||
|
'end-placeholder': '结束时间',
|
||||||
|
'value-format': 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
'picker-options': {
|
||||||
|
shortcuts: [{
|
||||||
|
text: '最近一周',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近一个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近三个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
valueResolve(context: any) {
|
||||||
|
const {key, value} = context
|
||||||
|
//value解析,就是把组件的值转化为后台所需要的值
|
||||||
|
//在form表单点击保存按钮后,提交到后台之前执行转化
|
||||||
|
if (value) {
|
||||||
|
context.form.create_datetime_after = value[0]
|
||||||
|
context.form.create_datetime_before = value[1]
|
||||||
|
}
|
||||||
|
// ↑↑↑↑↑ 注意这里是form,不是row
|
||||||
|
}
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 160,
|
width: 160,
|
||||||
show: options.create_datetime?.table || false,
|
show: options.create_datetime?.table || false,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false
|
||||||
},
|
},
|
||||||
viewForm: {
|
viewForm: {
|
||||||
show: true
|
show: true
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
import type { App } from 'vue';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 按钮波浪指令
|
|
||||||
* @directive 默认方式:v-waves,如 `<div v-waves></div>`
|
|
||||||
* @directive 参数方式:v-waves=" |light|red|orange|purple|green|teal",如 `<div v-waves="'light'"></div>`
|
|
||||||
*/
|
|
||||||
export function wavesDirective(app: App) {
|
|
||||||
app.directive('waves', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
el.classList.add('waves-effect');
|
|
||||||
binding.value && el.classList.add(`waves-${binding.value}`);
|
|
||||||
function setConvertStyle(obj: { [key: string]: unknown }) {
|
|
||||||
let style: string = '';
|
|
||||||
for (let i in obj) {
|
|
||||||
if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`;
|
|
||||||
}
|
|
||||||
return style;
|
|
||||||
}
|
|
||||||
function onCurrentClick(e: { [key: string]: unknown }) {
|
|
||||||
let elDiv = document.createElement('div');
|
|
||||||
elDiv.classList.add('waves-ripple');
|
|
||||||
el.appendChild(elDiv);
|
|
||||||
let styles = {
|
|
||||||
left: `${e.layerX}px`,
|
|
||||||
top: `${e.layerY}px`,
|
|
||||||
opacity: 1,
|
|
||||||
transform: `scale(${(el.clientWidth / 100) * 10})`,
|
|
||||||
'transition-duration': `750ms`,
|
|
||||||
'transition-timing-function': `cubic-bezier(0.250, 0.460, 0.450, 0.940)`,
|
|
||||||
};
|
|
||||||
elDiv.setAttribute('style', setConvertStyle(styles));
|
|
||||||
setTimeout(() => {
|
|
||||||
elDiv.setAttribute(
|
|
||||||
'style',
|
|
||||||
setConvertStyle({
|
|
||||||
opacity: 0,
|
|
||||||
transform: styles.transform,
|
|
||||||
left: styles.left,
|
|
||||||
top: styles.top,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setTimeout(() => {
|
|
||||||
elDiv && el.removeChild(elDiv);
|
|
||||||
}, 750);
|
|
||||||
}, 450);
|
|
||||||
}
|
|
||||||
el.addEventListener('mousedown', onCurrentClick, false);
|
|
||||||
},
|
|
||||||
unmounted(el) {
|
|
||||||
el.addEventListener('mousedown', () => {});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义拖动指令
|
|
||||||
* @description 使用方式:v-drag="[dragDom,dragHeader]",如 `<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']"></div>`
|
|
||||||
* @description dragDom 要拖动的元素,dragHeader 要拖动的 Header 位置
|
|
||||||
* @link 注意:https://github.com/element-plus/element-plus/issues/522
|
|
||||||
* @lick 参考:https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242
|
|
||||||
*/
|
|
||||||
export function dragDirective(app: App) {
|
|
||||||
app.directive('drag', {
|
|
||||||
mounted(el, binding) {
|
|
||||||
if (!binding.value) return false;
|
|
||||||
|
|
||||||
const dragDom = document.querySelector(binding.value[0]) as HTMLElement;
|
|
||||||
const dragHeader = document.querySelector(binding.value[1]) as HTMLElement;
|
|
||||||
|
|
||||||
dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`);
|
|
||||||
|
|
||||||
function down(e: any, type: string) {
|
|
||||||
// 鼠标按下,计算当前元素距离可视区的距离
|
|
||||||
const disX = type === 'pc' ? e.clientX - dragHeader.offsetLeft : e.touches[0].clientX - dragHeader.offsetLeft;
|
|
||||||
const disY = type === 'pc' ? e.clientY - dragHeader.offsetTop : e.touches[0].clientY - dragHeader.offsetTop;
|
|
||||||
|
|
||||||
// body当前宽度
|
|
||||||
const screenWidth = document.body.clientWidth;
|
|
||||||
// 可见区域高度(应为body高度,可某些环境下无法获取)
|
|
||||||
const screenHeight = document.documentElement.clientHeight;
|
|
||||||
|
|
||||||
// 对话框宽度
|
|
||||||
const dragDomWidth = dragDom.offsetWidth;
|
|
||||||
// 对话框高度
|
|
||||||
const dragDomheight = dragDom.offsetHeight;
|
|
||||||
|
|
||||||
const minDragDomLeft = dragDom.offsetLeft;
|
|
||||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
|
|
||||||
|
|
||||||
const minDragDomTop = dragDom.offsetTop;
|
|
||||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
|
|
||||||
|
|
||||||
// 获取到的值带px 正则匹配替换
|
|
||||||
let styL: any = getComputedStyle(dragDom).left;
|
|
||||||
let styT: any = getComputedStyle(dragDom).top;
|
|
||||||
|
|
||||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
|
||||||
if (styL.includes('%')) {
|
|
||||||
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
|
|
||||||
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
|
|
||||||
} else {
|
|
||||||
styL = +styL.replace(/\px/g, '');
|
|
||||||
styT = +styT.replace(/\px/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
disX,
|
|
||||||
disY,
|
|
||||||
minDragDomLeft,
|
|
||||||
maxDragDomLeft,
|
|
||||||
minDragDomTop,
|
|
||||||
maxDragDomTop,
|
|
||||||
styL,
|
|
||||||
styT,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function move(e: any, type: string, obj: any) {
|
|
||||||
let { disX, disY, minDragDomLeft, maxDragDomLeft, minDragDomTop, maxDragDomTop, styL, styT } = obj;
|
|
||||||
|
|
||||||
// 通过事件委托,计算移动的距离
|
|
||||||
let left = type === 'pc' ? e.clientX - disX : e.touches[0].clientX - disX;
|
|
||||||
let top = type === 'pc' ? e.clientY - disY : e.touches[0].clientY - disY;
|
|
||||||
|
|
||||||
// 边界处理
|
|
||||||
if (-left > minDragDomLeft) {
|
|
||||||
left = -minDragDomLeft;
|
|
||||||
} else if (left > maxDragDomLeft) {
|
|
||||||
left = maxDragDomLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-top > minDragDomTop) {
|
|
||||||
top = -minDragDomTop;
|
|
||||||
} else if (top > maxDragDomTop) {
|
|
||||||
top = maxDragDomTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移动当前元素
|
|
||||||
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* pc端
|
|
||||||
* onmousedown 鼠标按下触发事件
|
|
||||||
* onmousemove 鼠标按下时持续触发事件
|
|
||||||
* onmouseup 鼠标抬起触发事件
|
|
||||||
*/
|
|
||||||
dragHeader.onmousedown = (e) => {
|
|
||||||
const obj = down(e, 'pc');
|
|
||||||
document.onmousemove = (e) => {
|
|
||||||
move(e, 'pc', obj);
|
|
||||||
};
|
|
||||||
document.onmouseup = () => {
|
|
||||||
document.onmousemove = null;
|
|
||||||
document.onmouseup = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 移动端
|
|
||||||
* ontouchstart 当按下手指时,触发ontouchstart
|
|
||||||
* ontouchmove 当移动手指时,触发ontouchmove
|
|
||||||
* ontouchend 当移走手指时,触发ontouchend
|
|
||||||
*/
|
|
||||||
dragHeader.ontouchstart = (e) => {
|
|
||||||
const obj = down(e, 'app');
|
|
||||||
document.ontouchmove = (e) => {
|
|
||||||
move(e, 'app', obj);
|
|
||||||
};
|
|
||||||
document.ontouchend = () => {
|
|
||||||
document.ontouchmove = null;
|
|
||||||
document.ontouchend = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import type { App } from 'vue';
|
|
||||||
import { authDirective } from '/@/utils/authDirective';
|
|
||||||
import { wavesDirective, dragDirective } from '/@/utils/customDirective';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出指令方法:v-xxx
|
|
||||||
* @methods authDirective 用户权限指令,用法:v-auth
|
|
||||||
* @methods wavesDirective 按钮波浪指令,用法:v-waves
|
|
||||||
* @methods dragDirective 自定义拖动指令,用法:v-drag
|
|
||||||
*/
|
|
||||||
export function directive(app: App) {
|
|
||||||
// 用户权限指令
|
|
||||||
authDirective(app);
|
|
||||||
// 按钮波浪指令
|
|
||||||
wavesDirective(app);
|
|
||||||
// 自定义拖动指令
|
|
||||||
dragDirective(app);
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,80 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<fs-page>
|
||||||
测试框架外显示
|
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||||
</div>
|
<template #header-top>
|
||||||
|
<div id="myEcharts" v-show="isEcharts" v-resize-ob="handleResize" :style="{width: '100%', height: '300px'}"></div>
|
||||||
|
</template>
|
||||||
|
</fs-crud>
|
||||||
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="demo">
|
<script lang="ts" setup name="loginLog">
|
||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
|
import { createCrudOptions } from './crud';
|
||||||
|
import * as echarts from "echarts";
|
||||||
|
const isEcharts = ref(true)
|
||||||
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions,isEcharts,initChart });
|
||||||
|
const myEcharts = echarts
|
||||||
|
|
||||||
|
function initChart() {
|
||||||
|
let chart = myEcharts.init(document.getElementById("myEcharts"), "purple-passion");
|
||||||
|
// 在这里请求API,例如:
|
||||||
|
/***
|
||||||
|
* request({url:'xxxx'}).then(res=>{
|
||||||
|
* // 把chart.setOption写在这里面
|
||||||
|
*
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
chart.setOption({
|
||||||
|
title: {
|
||||||
|
text: "2021年各月份销售量(单位:件)",
|
||||||
|
left: "center",
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
data: [
|
||||||
|
"一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis"
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value"
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: [
|
||||||
|
606, 542, 985, 687, 501, 787, 339, 706, 383, 684, 669, 737
|
||||||
|
],
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
itemStyle: {
|
||||||
|
normal: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: "top",
|
||||||
|
formatter: "{c}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
window.onresize = function () {
|
||||||
|
chart.resize();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResize(size) {
|
||||||
|
console.log(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面打开后获取列表数据
|
||||||
|
onMounted(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
initChart()
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict } from '@fast-crud/fast-crud';
|
import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict } from '@fast-crud/fast-crud';
|
||||||
|
import {commonCrudConfig} from "/@/utils/commonCrud";
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: UserPageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
@@ -325,6 +326,11 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
...commonCrudConfig({
|
||||||
|
create_datetime: {
|
||||||
|
search: true
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login-container flex">
|
<div class="login-container flex z-10">
|
||||||
<div class="login-left">
|
<div class="login-left">
|
||||||
<div class="login-left-logo">
|
<div class="login-left-logo">
|
||||||
<img :src="logoMini" />
|
<img :src="siteLogo" />
|
||||||
<div class="login-left-logo-text">
|
<div class="login-left-logo-text">
|
||||||
<span>{{ getSystemConfig['login.site_title']||getThemeConfig.globalViceTitle }}</span>
|
<span>{{ getSystemConfig['login.site_title'] || getThemeConfig.globalViceTitle }}</span>
|
||||||
<span class="login-left-logo-text-msg">{{ getSystemConfig['login.site_name']||getThemeConfig.globalViceTitleMsg }}</span>
|
<span class="login-left-logo-text-msg">{{
|
||||||
|
getSystemConfig['login.site_name'] || getThemeConfig.globalViceTitleMsg }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-left-img">
|
<div class="login-left-img">
|
||||||
@@ -13,12 +14,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<img :src="loginBg" class="login-left-waves" />
|
<img :src="loginBg" class="login-left-waves" />
|
||||||
</div>
|
</div>
|
||||||
<div class="login-right flex">
|
<div class="login-right flex z-10">
|
||||||
<div class="login-right-warp flex-margin">
|
<div class="login-right-warp flex-margin">
|
||||||
<span class="login-right-warp-one"></span>
|
<span class="login-right-warp-one"></span>
|
||||||
<span class="login-right-warp-two"></span>
|
<span class="login-right-warp-two"></span>
|
||||||
<div class="login-right-warp-mian">
|
<div class="login-right-warp-mian">
|
||||||
<div class="login-right-warp-main-title">{{ getSystemConfig['login.site_title'] || getThemeConfig.globalTitle }} 欢迎您!</div>
|
<div class="login-right-warp-main-title">{{ getSystemConfig['login.site_title'] ||
|
||||||
|
getThemeConfig.globalTitle }} 欢迎您!</div>
|
||||||
<div class="login-right-warp-main-form">
|
<div class="login-right-warp-main-form">
|
||||||
<div v-if="!state.isScan">
|
<div v-if="!state.isScan">
|
||||||
<el-tabs v-model="state.tabsActiveName">
|
<el-tabs v-model="state.tabsActiveName">
|
||||||
@@ -41,19 +43,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="login-authorization">
|
<div class="login-authorization z-10">
|
||||||
<p>Copyright © {{getSystemConfig['login.copyright'] || '2021-2024 django-vue-admin.com'}} 版权所有</p>
|
<p>Copyright © {{ getSystemConfig['login.copyright'] || '2021-2024 django-vue-admin.com' }} 版权所有</p>
|
||||||
<p class="la-other">
|
<p class="la-other">
|
||||||
<a href="https://beian.miit.gov.cn" target="_blank">{{getSystemConfig['login.keep_record'] || '晋ICP备18005113号-3'}}</a>
|
<a href="https://beian.miit.gov.cn" target="_blank">{{ getSystemConfig['login.keep_record'] ||
|
||||||
|
'晋ICP备18005113号-3' }}</a>
|
||||||
|
|
|
|
||||||
<a :href="getSystemConfig['login.help_url']?getSystemConfig['login.help_url']:'https://django-vue-admin.com'" target="_blank">帮助</a>
|
<a :href="getSystemConfig['login.help_url'] ? getSystemConfig['login.help_url'] : 'https://django-vue-admin.com'"
|
||||||
|
target="_blank">帮助</a>
|
||||||
|
|
|
|
||||||
<a :href="getSystemConfig['login.privacy_url']?getBaseURL(getSystemConfig['login.privacy_url']):'#'">隐私</a>
|
<a
|
||||||
|
:href="getSystemConfig['login.privacy_url'] ? getBaseURL(getSystemConfig['login.privacy_url']) : '#'">隐私</a>
|
||||||
|
|
|
|
||||||
<a :href="getSystemConfig['login.clause_url']?getBaseURL(getSystemConfig['login.clause_url']):'#'">条款</a>
|
<a
|
||||||
|
:href="getSystemConfig['login.clause_url'] ? getBaseURL(getSystemConfig['login.clause_url']) : '#'">条款</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="siteBg">
|
||||||
|
<img :src="siteBg" class="fixed inset-0 z-1 w-full h-full" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="loginIndex">
|
<script setup lang="ts" name="loginIndex">
|
||||||
@@ -64,12 +73,13 @@ import { NextLoading } from '/@/utils/loading';
|
|||||||
import logoMini from '/@/assets/logo-mini.svg';
|
import logoMini from '/@/assets/logo-mini.svg';
|
||||||
import loginMain from '/@/assets/login-main.svg';
|
import loginMain from '/@/assets/login-main.svg';
|
||||||
import loginBg from '/@/assets/login-bg.svg';
|
import loginBg from '/@/assets/login-bg.svg';
|
||||||
import {SystemConfigStore} from '/@/stores/systemConfig'
|
import { SystemConfigStore } from '/@/stores/systemConfig'
|
||||||
import {getBaseURL} from "/@/utils/baseUrl";
|
import { getBaseURL } from "/@/utils/baseUrl";
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const Account = defineAsyncComponent(() => import('/@/views/system/login/component/account.vue'));
|
const Account = defineAsyncComponent(() => import('/@/views/system/login/component/account.vue'));
|
||||||
const Mobile = defineAsyncComponent(() => import('/@/views/system/login/component/mobile.vue'));
|
const Mobile = defineAsyncComponent(() => import('/@/views/system/login/component/mobile.vue'));
|
||||||
const Scan = defineAsyncComponent(() => import('/@/views/system/login/component/scan.vue'));
|
const Scan = defineAsyncComponent(() => import('/@/views/system/login/component/scan.vue'));
|
||||||
|
import _ from "lodash";
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const storesThemeConfig = useThemeConfig();
|
const storesThemeConfig = useThemeConfig();
|
||||||
@@ -85,11 +95,23 @@ const getThemeConfig = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const systemConfigStore = SystemConfigStore()
|
const systemConfigStore = SystemConfigStore()
|
||||||
const {systemConfig} = storeToRefs(systemConfigStore)
|
const { systemConfig } = storeToRefs(systemConfigStore)
|
||||||
const getSystemConfig = computed(()=>{
|
const getSystemConfig = computed(() => {
|
||||||
return systemConfig.value
|
return systemConfig.value
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const siteLogo = computed(() => {
|
||||||
|
if (!_.isEmpty(getSystemConfig.value['login.site_logo'])) {
|
||||||
|
return getSystemConfig.value['login.site_logo']
|
||||||
|
}
|
||||||
|
return logoMini
|
||||||
|
});
|
||||||
|
|
||||||
|
const siteBg = computed(() => {
|
||||||
|
if (!_.isEmpty(getSystemConfig.value['login.login_background'])) {
|
||||||
|
return getSystemConfig.value['login.login_background']
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 页面加载时
|
// 页面加载时
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,40 +1,45 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, useCompute, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import {dict, useCompute, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions} from '@fast-crud/fast-crud';
|
||||||
import tableSelector from '/@/components/tableSelector/index.vue';
|
import tableSelector from '/@/components/tableSelector/index.vue';
|
||||||
import {shallowRef, computed, ref, inject} from 'vue';
|
import {shallowRef, computed, ref, inject} from 'vue';
|
||||||
import manyToMany from '/@/components/manyToMany/index.vue';
|
import manyToMany from '/@/components/manyToMany/index.vue';
|
||||||
import {auth} from '/@/utils/authFunction'
|
import {auth} from '/@/utils/authFunction'
|
||||||
const { compute } = useCompute();
|
import {createCrudOptions as userCrudOptions } from "/@/views/system/user/crud";
|
||||||
|
import {request} from '/@/utils/service'
|
||||||
|
const {compute} = useCompute();
|
||||||
|
|
||||||
interface CreateCrudOptionsTypes {
|
interface CreateCrudOptionsTypes {
|
||||||
crudOptions: CrudOptions;
|
crudOptions: CrudOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose, tabActivted }: { crudExpose: CrudExpose; tabActivted: any }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({
|
||||||
const pageRequest = async (query: PageQuery) => {
|
crudExpose,
|
||||||
if (tabActivted.value === 'receive') {
|
tabActivted
|
||||||
return await api.GetSelfReceive(query);
|
}: { crudExpose: CrudExpose; tabActivted: any }): CreateCrudOptionsTypes {
|
||||||
}
|
const pageRequest = async (query: PageQuery) => {
|
||||||
return await api.GetList(query);
|
if (tabActivted.value === 'receive') {
|
||||||
};
|
return await api.GetSelfReceive(query);
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
}
|
||||||
form.id = row.id;
|
return await api.GetList(query);
|
||||||
return await api.UpdateObj(form);
|
};
|
||||||
};
|
const editRequest = async ({form, row}: EditReq) => {
|
||||||
const delRequest = async ({ row }: DelReq) => {
|
form.id = row.id;
|
||||||
return await api.DelObj(row.id);
|
return await api.UpdateObj(form);
|
||||||
};
|
};
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const delRequest = async ({row}: DelReq) => {
|
||||||
return await api.AddObj(form);
|
return await api.DelObj(row.id);
|
||||||
};
|
};
|
||||||
|
const addRequest = async ({form}: AddReq) => {
|
||||||
|
return await api.AddObj(form);
|
||||||
|
};
|
||||||
|
|
||||||
const viewRequest = async ({ row }: { row: any }) => {
|
const viewRequest = async ({row}: { row: any }) => {
|
||||||
return await api.GetObj(row.id);
|
return await api.GetObj(row.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const IsReadFunc = computed(() => {
|
const IsReadFunc = computed(() => {
|
||||||
return tabActivted.value === 'receive';
|
return tabActivted.value === 'receive';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -292,6 +297,7 @@ export const createCrudOptions = function ({ crudExpose, tabActivted }: { crudEx
|
|||||||
{
|
{
|
||||||
prop: 'name',
|
prop: 'name',
|
||||||
label: '部门名称',
|
label: '部门名称',
|
||||||
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'status_label',
|
prop: 'status_label',
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ import { useRouter } from 'vue-router';
|
|||||||
import { useUserInfo } from '/@/stores/userInfo';
|
import { useUserInfo } from '/@/stores/userInfo';
|
||||||
import { successMessage } from '/@/utils/message';
|
import { successMessage } from '/@/utils/message';
|
||||||
import {dictionary} from "/@/utils/dictionary";
|
import {dictionary} from "/@/utils/dictionary";
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
// 头像裁剪组件
|
// 头像裁剪组件
|
||||||
const avatarSelector = defineAsyncComponent(() => import('/@/components/avatarSelector/index.vue'));
|
const avatarSelector = defineAsyncComponent(() => import('/@/components/avatarSelector/index.vue'));
|
||||||
@@ -333,6 +334,10 @@ const settingPassword = () => {
|
|||||||
if (valid) {
|
if (valid) {
|
||||||
api.UpdatePassword(userPasswordInfo).then((res: any) => {
|
api.UpdatePassword(userPasswordInfo).then((res: any) => {
|
||||||
ElMessage.success('密码修改成功');
|
ElMessage.success('密码修改成功');
|
||||||
|
setTimeout(() => {
|
||||||
|
Session.remove('token');
|
||||||
|
router.push('/login');
|
||||||
|
}, 1000);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 校验失败
|
// 校验失败
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<div>当前角色:
|
<div>当前授权角色:
|
||||||
<el-tag>{{ props.roleName }}</el-tag>
|
<el-tag>{{ props.roleName }}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pccm-item">
|
<div class="pccm-item" v-if="item.columns&&item.columns.length>0">
|
||||||
<p>对这些数据有以下字段权限</p>
|
<p>对这些数据有以下字段权限</p>
|
||||||
|
|
||||||
<ul class="columns-list">
|
<ul class="columns-list">
|
||||||
|
|||||||
@@ -1,255 +1,189 @@
|
|||||||
import { CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute } from '@fast-crud/fast-crud';
|
import {CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute} from '@fast-crud/fast-crud';
|
||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
import {dictionary} from '/@/utils/dictionary';
|
||||||
import { columnPermission } from '../../../utils/columnPermission';
|
import {columnPermission} from '../../../utils/columnPermission';
|
||||||
import { successMessage } from '../../../utils/message';
|
import {successMessage} from '../../../utils/message';
|
||||||
import {auth} from '/@/utils/authFunction'
|
import {auth} from '/@/utils/authFunction'
|
||||||
|
|
||||||
interface CreateCrudOptionsTypes {
|
interface CreateCrudOptionsTypes {
|
||||||
output: any;
|
output: any;
|
||||||
crudOptions: CrudOptions;
|
crudOptions: CrudOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
//此处为crudOptions配置
|
//此处为crudOptions配置
|
||||||
export const createCrudOptions = function ({
|
export const createCrudOptions = function ({
|
||||||
crudExpose,
|
crudExpose,
|
||||||
rolePermission,
|
rolePermission,
|
||||||
handleDrawerOpen,
|
handleDrawerOpen,
|
||||||
}: {
|
}: {
|
||||||
crudExpose: CrudExpose;
|
crudExpose: CrudExpose;
|
||||||
rolePermission: any;
|
rolePermission: any;
|
||||||
handleDrawerOpen: Function;
|
handleDrawerOpen: Function;
|
||||||
}): CreateCrudOptionsTypes {
|
}): CreateCrudOptionsTypes {
|
||||||
const pageRequest = async (query: any) => {
|
const pageRequest = async (query: any) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({form, row}: EditReq) => {
|
||||||
form.id = row.id;
|
form.id = row.id;
|
||||||
return await api.UpdateObj(form);
|
return await api.UpdateObj(form);
|
||||||
};
|
};
|
||||||
const delRequest = async ({ row }: DelReq) => {
|
const delRequest = async ({row}: DelReq) => {
|
||||||
return await api.DelObj(row.id);
|
return await api.DelObj(row.id);
|
||||||
};
|
};
|
||||||
const addRequest = async ({ form }: AddReq) => {
|
const addRequest = async ({form}: AddReq) => {
|
||||||
return await api.AddObj(form);
|
return await api.AddObj(form);
|
||||||
};
|
};
|
||||||
|
|
||||||
//权限判定
|
//权限判定
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
actionbar: {
|
pagination: {
|
||||||
buttons: {
|
show: true
|
||||||
add: {
|
},
|
||||||
show: auth('role:Create')
|
actionbar: {
|
||||||
}
|
buttons: {
|
||||||
}
|
add: {
|
||||||
},
|
show: auth('role:Create')
|
||||||
rowHandle: {
|
}
|
||||||
//固定右侧
|
}
|
||||||
fixed: 'right',
|
},
|
||||||
width: 320,
|
rowHandle: {
|
||||||
buttons: {
|
//固定右侧
|
||||||
view: {
|
fixed: 'right',
|
||||||
show: true,
|
width: 320,
|
||||||
},
|
buttons: {
|
||||||
edit: {
|
view: {
|
||||||
show: auth('role:Update'),
|
show: true,
|
||||||
},
|
},
|
||||||
remove: {
|
edit: {
|
||||||
show: auth('role:Delete'),
|
show: auth('role:Update'),
|
||||||
},
|
},
|
||||||
permission: {
|
remove: {
|
||||||
type: 'primary',
|
show: auth('role:Delete'),
|
||||||
text: '权限配置',
|
},
|
||||||
show: auth('role:Permission'),
|
permission: {
|
||||||
tooltip: {
|
type: 'primary',
|
||||||
placement: 'top',
|
text: '权限配置',
|
||||||
content: '权限配置',
|
show: auth('role:Permission'),
|
||||||
},
|
tooltip: {
|
||||||
click: (context: any): void => {
|
placement: 'top',
|
||||||
const { row } = context;
|
content: '权限配置',
|
||||||
handleDrawerOpen(row);
|
},
|
||||||
},
|
click: (context: any): void => {
|
||||||
},
|
const {row} = context;
|
||||||
},
|
handleDrawerOpen(row);
|
||||||
},
|
},
|
||||||
form: {
|
},
|
||||||
col: { span: 24 },
|
},
|
||||||
labelWidth: '100px',
|
},
|
||||||
wrapper: {
|
form: {
|
||||||
is: 'el-dialog',
|
col: {span: 24},
|
||||||
width: '600px',
|
labelWidth: '100px',
|
||||||
},
|
wrapper: {
|
||||||
},
|
is: 'el-dialog',
|
||||||
columns: {
|
width: '600px',
|
||||||
_index: {
|
},
|
||||||
title: '序号',
|
},
|
||||||
form: { show: false },
|
columns: {
|
||||||
column: {
|
_index: {
|
||||||
type: 'index',
|
title: '序号',
|
||||||
align: 'center',
|
form: {show: false},
|
||||||
width: '70px',
|
column: {
|
||||||
columnSetDisabled: true, //禁止在列设置中选择
|
type: 'index',
|
||||||
},
|
align: 'center',
|
||||||
},
|
width: '70px',
|
||||||
id: {
|
columnSetDisabled: true, //禁止在列设置中选择
|
||||||
title: 'ID',
|
},
|
||||||
type: 'text',
|
},
|
||||||
column: { show: false },
|
id: {
|
||||||
search: { show: false },
|
title: 'ID',
|
||||||
form: { show: false },
|
type: 'text',
|
||||||
},
|
column: {show: false},
|
||||||
name: {
|
search: {show: false},
|
||||||
title: '角色名称',
|
form: {show: false},
|
||||||
type: 'text',
|
},
|
||||||
search: { show: true },
|
name: {
|
||||||
column: {
|
title: '角色名称',
|
||||||
minWidth: 120,
|
type: 'text',
|
||||||
sortable: 'custom',
|
search: {show: true},
|
||||||
show: columnPermission('name', 'is_query'),
|
column: {
|
||||||
},
|
minWidth: 120,
|
||||||
// addForm: {
|
sortable: 'custom',
|
||||||
// show: columnPermission('name', 'is_create'),
|
},
|
||||||
// },
|
form: {
|
||||||
editForm: {
|
rules: [{required: true, message: '角色名称必填'}],
|
||||||
show: columnPermission('name', 'is_update'),
|
component: {
|
||||||
},
|
placeholder: '请输入角色名称',
|
||||||
form: {
|
},
|
||||||
rules: [{ required: true, message: '角色名称必填' }],
|
},
|
||||||
component: {
|
},
|
||||||
placeholder: '请输入角色名称',
|
key: {
|
||||||
},
|
title: '权限标识',
|
||||||
},
|
type: 'text',
|
||||||
},
|
search: {show: false},
|
||||||
key: {
|
column: {
|
||||||
title: '权限标识',
|
minWidth: 120,
|
||||||
type: 'text',
|
sortable: 'custom',
|
||||||
search: { show: false },
|
columnSetDisabled: true,
|
||||||
column: {
|
},
|
||||||
minWidth: 120,
|
form: {
|
||||||
sortable: 'custom',
|
rules: [{required: true, message: '权限标识必填'}],
|
||||||
show: columnPermission('key', 'is_query'),
|
component: {
|
||||||
columnSetDisabled: true,
|
placeholder: '输入权限标识',
|
||||||
},
|
},
|
||||||
addForm: {
|
},
|
||||||
show: columnPermission('key', 'is_create'),
|
valueBuilder(context) {
|
||||||
},
|
const {row, key} = context
|
||||||
editForm: {
|
return row[key]
|
||||||
show: columnPermission('key', 'is_update'),
|
}
|
||||||
},
|
},
|
||||||
form: {
|
sort: {
|
||||||
rules: [{ required: true, message: '权限标识必填' }],
|
title: '排序',
|
||||||
component: {
|
search: {show: false},
|
||||||
placeholder: '输入权限标识',
|
type: 'number',
|
||||||
},
|
column: {
|
||||||
},
|
minWidth: 90,
|
||||||
valueBuilder(context){
|
sortable: 'custom',
|
||||||
const {row,key} = context
|
},
|
||||||
return row[key]
|
form: {
|
||||||
}
|
rules: [{required: true, message: '排序必填'}],
|
||||||
},
|
value: 1,
|
||||||
sort: {
|
},
|
||||||
title: '排序',
|
},
|
||||||
search: { show: false },
|
status: {
|
||||||
type: 'number',
|
title: '状态',
|
||||||
column: {
|
search: {show: true},
|
||||||
minWidth: 90,
|
type: 'dict-radio',
|
||||||
sortable: 'custom',
|
column: {
|
||||||
},
|
width: 100,
|
||||||
addForm: {
|
component: {
|
||||||
show: columnPermission('sort', 'is_create'),
|
name: 'fs-dict-switch',
|
||||||
},
|
activeText: '',
|
||||||
editForm: {
|
inactiveText: '',
|
||||||
show: columnPermission('sort', 'is_update'),
|
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
|
||||||
},
|
onChange: compute((context) => {
|
||||||
form: {
|
return () => {
|
||||||
rules: [{ required: true, message: '排序必填' }],
|
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||||
value: 1,
|
successMessage(res.msg as string);
|
||||||
},
|
});
|
||||||
},
|
};
|
||||||
status: {
|
}),
|
||||||
title: '状态',
|
},
|
||||||
search: { show: true },
|
},
|
||||||
type: 'dict-radio',
|
dict: dict({
|
||||||
column: {
|
data: dictionary('button_status_bool'),
|
||||||
width: 100,
|
}),
|
||||||
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);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
show: columnPermission('status', 'is_query'),
|
|
||||||
},
|
|
||||||
addForm: {
|
|
||||||
show: columnPermission('status', 'is_create'),
|
|
||||||
},
|
|
||||||
editForm: {
|
|
||||||
show: columnPermission('status', 'is_update'),
|
|
||||||
},
|
|
||||||
dict: dict({
|
|
||||||
data: dictionary('button_status_bool'),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
update_datetime: {
|
|
||||||
title: '更新时间',
|
|
||||||
type: 'text',
|
|
||||||
search: { show: false },
|
|
||||||
column: {
|
|
||||||
minWidth: 170,
|
|
||||||
sortable: 'custom',
|
|
||||||
show: columnPermission('update_datetime', 'is_query'),
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
show: false,
|
|
||||||
component: {
|
|
||||||
placeholder: '输入关键词搜索',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
create_datetime: {
|
|
||||||
title: '创建时间',
|
|
||||||
type: 'text',
|
|
||||||
search: { show: false },
|
|
||||||
column: {
|
|
||||||
sortable: 'custom',
|
|
||||||
minWidth: 170,
|
|
||||||
show: columnPermission('create_datetime', 'is_query'),
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
show: false,
|
|
||||||
component: {
|
|
||||||
placeholder: '输入关键词搜索',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// description: {
|
|
||||||
// title: '备注',
|
|
||||||
// type: 'textarea',
|
|
||||||
// search: {show: false},
|
|
||||||
// form: {
|
|
||||||
// component: {
|
|
||||||
// maxlength: 200,
|
|
||||||
// placeholder: '输入备注',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,21 +47,21 @@ const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|||||||
// 你的crud配置
|
// 你的crud配置
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose, rolePermission, handleDrawerOpen });
|
const { crudOptions } = createCrudOptions({ crudExpose, rolePermission, handleDrawerOpen });
|
||||||
|
|
||||||
|
// 初始化crud配置
|
||||||
|
const { resetCrudOptions } = useCrud({
|
||||||
|
crudExpose,
|
||||||
|
crudOptions,
|
||||||
|
context: {},
|
||||||
|
});
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted( async () => {
|
onMounted( async () => {
|
||||||
|
|
||||||
const newOptions = await handleColumnPermission(GetPermission,crudOptions)
|
const newOptions = await handleColumnPermission(GetPermission,crudOptions)
|
||||||
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({
|
|
||||||
crudExpose,
|
|
||||||
crudOptions,
|
|
||||||
context: {},
|
|
||||||
});
|
|
||||||
//重置crudBinding
|
//重置crudBinding
|
||||||
resetCrudOptions(newOptions);
|
//resetCrudOptions(newOptions);
|
||||||
crudExpose.doRefresh();
|
crudExpose.doRefresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -372,6 +372,9 @@ export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps)
|
|||||||
form: {
|
form: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
|
column: {
|
||||||
|
minWidth: 400, //最小列宽
|
||||||
|
},
|
||||||
},
|
},
|
||||||
...commonCrudConfig({
|
...commonCrudConfig({
|
||||||
dept_belong_id: {
|
dept_belong_id: {
|
||||||
|
|||||||
Reference in New Issue
Block a user