!66 正式发布v2.0.5版本
1. 新增:数据导出,可根据前端Search进行过滤导出 2. 新增:"软删除"字段,修改删除功能,可以使用软删除功能,修改查询,默认排除已软删除的数据 3. 优化:table-seletor选择器 4. 优化:导出数据自动设置列宽 5. 优化:数据导入导出功能 6. 修复:修改人不会在修改后改变问题pull/67/head v2.0.5
commit
891da887a5
|
@ -9,7 +9,7 @@ def is_tenants_mode():
|
|||
判断是否为租户模式
|
||||
:return:
|
||||
"""
|
||||
return hasattr(connection, 'tenant') and connection.tenant.schema_name
|
||||
return hasattr(connection, "tenant") and connection.tenant.schema_name
|
||||
|
||||
|
||||
# ================================================= #
|
||||
|
@ -17,27 +17,37 @@ def is_tenants_mode():
|
|||
# ================================================= #
|
||||
def _get_all_dictionary():
|
||||
from dvadmin.system.models import Dictionary
|
||||
|
||||
queryset = Dictionary.objects.filter(status=True, is_value=False)
|
||||
data = []
|
||||
for instance in queryset:
|
||||
data.append({
|
||||
"id": instance.id,
|
||||
"value": instance.value,
|
||||
"children": list(Dictionary.objects.filter(parent=instance.id).filter(status=1).
|
||||
values('label', 'value', 'type', 'color'))
|
||||
})
|
||||
data.append(
|
||||
{
|
||||
"id": instance.id,
|
||||
"value": instance.value,
|
||||
"children": list(
|
||||
Dictionary.objects.filter(parent=instance.id)
|
||||
.filter(status=1)
|
||||
.values("label", "value", "type", "color")
|
||||
),
|
||||
}
|
||||
)
|
||||
return {ele.get("value"): ele for ele in data}
|
||||
|
||||
|
||||
def _get_all_system_config():
|
||||
data = {}
|
||||
from dvadmin.system.models import SystemConfig
|
||||
system_config_obj = SystemConfig.objects.filter(status=True, parent_id__isnull=False).values(
|
||||
'parent__key', 'key', 'value', 'form_item_type').order_by('sort')
|
||||
|
||||
system_config_obj = (
|
||||
SystemConfig.objects.filter(status=True, parent_id__isnull=False)
|
||||
.values("parent__key", "key", "value", "form_item_type")
|
||||
.order_by("sort")
|
||||
)
|
||||
for system_config in system_config_obj:
|
||||
value = system_config.get('value', '')
|
||||
if value and system_config.get('form_item_type') == 7:
|
||||
value = value[0].get('url')
|
||||
value = system_config.get("value", "")
|
||||
if value and system_config.get("form_item_type") == 7:
|
||||
value = value[0].get("url")
|
||||
data[f"{system_config.get('parent__key')}.{system_config.get('key')}"] = value
|
||||
return data
|
||||
|
||||
|
@ -50,12 +60,12 @@ def init_dictionary():
|
|||
try:
|
||||
if is_tenants_mode():
|
||||
from django_tenants.utils import tenant_context, get_tenant_model
|
||||
|
||||
for tenant in get_tenant_model().objects.filter():
|
||||
with tenant_context(tenant):
|
||||
settings.DICTIONARY_CONFIG[connection.tenant.schema_name] = _get_all_dictionary()
|
||||
else:
|
||||
settings.DICTIONARY_CONFIG = _get_all_dictionary()
|
||||
print("初始化字典配置完成")
|
||||
except Exception as e:
|
||||
print("请先进行数据库迁移!")
|
||||
return
|
||||
|
@ -71,12 +81,12 @@ def init_system_config():
|
|||
|
||||
if is_tenants_mode():
|
||||
from django_tenants.utils import tenant_context, get_tenant_model
|
||||
|
||||
for tenant in get_tenant_model().objects.filter():
|
||||
with tenant_context(tenant):
|
||||
settings.SYSTEM_CONFIG[connection.tenant.schema_name] = _get_all_system_config()
|
||||
else:
|
||||
settings.SYSTEM_CONFIG = _get_all_system_config()
|
||||
print("初始化系统配置完成")
|
||||
except Exception as e:
|
||||
print("请先进行数据库迁移!")
|
||||
return
|
||||
|
@ -89,6 +99,7 @@ def refresh_dictionary():
|
|||
"""
|
||||
if is_tenants_mode():
|
||||
from django_tenants.utils import tenant_context, get_tenant_model
|
||||
|
||||
for tenant in get_tenant_model().objects.filter():
|
||||
with tenant_context(tenant):
|
||||
settings.DICTIONARY_CONFIG[connection.tenant.schema_name] = _get_all_dictionary()
|
||||
|
@ -103,6 +114,7 @@ def refresh_system_config():
|
|||
"""
|
||||
if is_tenants_mode():
|
||||
from django_tenants.utils import tenant_context, get_tenant_model
|
||||
|
||||
for tenant in get_tenant_model().objects.filter():
|
||||
with tenant_context(tenant):
|
||||
settings.SYSTEM_CONFIG[connection.tenant.schema_name] = _get_all_system_config()
|
||||
|
|
|
@ -13,7 +13,7 @@ STATUS_CHOICES = (
|
|||
)
|
||||
|
||||
|
||||
class Users(AbstractUser, CoreModel):
|
||||
class Users(CoreModel,AbstractUser):
|
||||
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号", help_text="用户账号")
|
||||
email = models.EmailField(max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
|
||||
mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话")
|
||||
|
@ -410,3 +410,22 @@ class LoginLog(CoreModel):
|
|||
verbose_name = "登录日志"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ("-create_datetime",)
|
||||
|
||||
|
||||
class MessageCenter(CoreModel):
|
||||
title = models.CharField(max_length=100,verbose_name="标题",help_text="标题")
|
||||
content = models.TextField(verbose_name="内容",help_text="内容")
|
||||
target_type=models.IntegerField(default=0,verbose_name="目标类型",help_text="目标类型")
|
||||
target_user = models.ForeignKey(to=Users,related_name="target_user",null=True,blank=True,db_constraint=False,on_delete=models.CASCADE,verbose_name="目标用户",help_text="目标用户")
|
||||
target_dept = models.ForeignKey(to=Dept, null=True, blank=True, db_constraint=False, on_delete=models.CASCADE,
|
||||
verbose_name="目标部门", help_text="目标部门")
|
||||
target_role = models.ForeignKey(to=Role, null=True, blank=True, db_constraint=False, on_delete=models.CASCADE,
|
||||
verbose_name="目标角色", help_text="目标角色")
|
||||
is_read=models.BooleanField(default=False,blank=True,verbose_name="是否已读",help_text="是否已读")
|
||||
|
||||
class Meta:
|
||||
db_table = table_prefix + "message_center"
|
||||
verbose_name = "消息中心"
|
||||
verbose_name_plural = verbose_name
|
||||
ordering = ("-create_datetime",)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from dvadmin.system.views.file_list import FileViewSet
|
|||
from dvadmin.system.views.login_log import LoginLogViewSet
|
||||
from dvadmin.system.views.menu import MenuViewSet
|
||||
from dvadmin.system.views.menu_button import MenuButtonViewSet
|
||||
from dvadmin.system.views.message_center import MessageCenterViewSet
|
||||
from dvadmin.system.views.operation_log import OperationLogViewSet
|
||||
from dvadmin.system.views.role import RoleViewSet
|
||||
from dvadmin.system.views.system_config import SystemConfigViewSet
|
||||
|
@ -26,6 +27,7 @@ system_url.register(r'area', AreaViewSet)
|
|||
system_url.register(r'file', FileViewSet)
|
||||
system_url.register(r'api_white_list', ApiWhiteListViewSet)
|
||||
system_url.register(r'system_config', SystemConfigViewSet)
|
||||
system_url.register(r'message_center',MessageCenterViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
path('user/export/', UserViewSet.as_view({'post': 'export_data', })),
|
||||
|
|
|
@ -19,10 +19,17 @@ class DeptSerializer(CustomModelSerializer):
|
|||
"""
|
||||
parent_name = serializers.CharField(read_only=True, source='parent.name')
|
||||
has_children = serializers.SerializerMethodField()
|
||||
status_label = serializers.SerializerMethodField()
|
||||
|
||||
def get_has_children(self, obj: Dept):
|
||||
return Dept.objects.filter(parent_id=obj.id).count()
|
||||
|
||||
def get_status_label(self, instance):
|
||||
status = instance.status
|
||||
if status:
|
||||
return "启用"
|
||||
return "禁用"
|
||||
|
||||
class Meta:
|
||||
model = Dept
|
||||
fields = '__all__'
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import hashlib
|
||||
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django_restql.fields import DynamicSerializerMethodField
|
||||
from rest_framework import serializers
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from application import dispatch
|
||||
from dvadmin.system.models import Users
|
||||
from dvadmin.system.models import Users, Role, Dept
|
||||
from dvadmin.system.views.role import RoleSerializer
|
||||
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
||||
from dvadmin.utils.serializers import CustomModelSerializer
|
||||
from dvadmin.utils.validator import CustomUniqueValidator
|
||||
|
@ -17,6 +19,8 @@ class UserSerializer(CustomModelSerializer):
|
|||
"""
|
||||
用户管理-序列化器
|
||||
"""
|
||||
dept_name = serializers.CharField(source='dept.name', read_only=True)
|
||||
role_info = DynamicSerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Users
|
||||
|
@ -26,6 +30,17 @@ class UserSerializer(CustomModelSerializer):
|
|||
"post": {"required": False},
|
||||
}
|
||||
|
||||
def get_role_info(self, instance, parsed_query):
|
||||
roles = instance.role.all()
|
||||
# You can do what ever you want in here
|
||||
# `parsed_query` param is passed to BookSerializer to allow further querying
|
||||
serializer = RoleSerializer(
|
||||
roles,
|
||||
many=True,
|
||||
parsed_query=parsed_query
|
||||
)
|
||||
return serializer.data
|
||||
|
||||
|
||||
class UsersInitSerializer(CustomModelSerializer):
|
||||
"""
|
||||
|
@ -128,10 +143,14 @@ class ExportUserProfileSerializer(CustomModelSerializer):
|
|||
last_login = serializers.DateTimeField(
|
||||
format="%Y-%m-%d %H:%M:%S", required=False, read_only=True
|
||||
)
|
||||
dept__deptName = serializers.CharField(source="dept.deptName", default="")
|
||||
dept__owner = serializers.CharField(source="dept.owner", default="")
|
||||
is_active = serializers.SerializerMethodField(read_only=True)
|
||||
dept_name = serializers.CharField(source="dept.name", default="")
|
||||
dept_owner = serializers.CharField(source="dept.owner", default="")
|
||||
gender = serializers.CharField(source="get_gender_display", read_only=True)
|
||||
|
||||
def get_is_active(self, instance):
|
||||
return "启用" if instance.is_active else "停用"
|
||||
|
||||
class Meta:
|
||||
model = Users
|
||||
fields = (
|
||||
|
@ -142,8 +161,8 @@ class ExportUserProfileSerializer(CustomModelSerializer):
|
|||
"gender",
|
||||
"is_active",
|
||||
"last_login",
|
||||
"dept__deptName",
|
||||
"dept__owner",
|
||||
"dept_name",
|
||||
"dept_owner",
|
||||
)
|
||||
|
||||
|
||||
|
@ -157,15 +176,6 @@ class UserProfileImportSerializer(CustomModelSerializer):
|
|||
data.save()
|
||||
return data
|
||||
|
||||
def run_validation(self, data={}):
|
||||
# 把excel 数据进行格式转换
|
||||
if type(data) is dict:
|
||||
data["role"] = str(data["role"]).split(",")
|
||||
data["dept_id"] = str(data["dept"]).split(",")
|
||||
data["gender"] = {"男": "1", "女": "0", "未知": "2"}.get(data["gender"])
|
||||
data["is_active"] = {"启用": True, "禁用": False}.get(data["is_active"])
|
||||
return super().run_validation(data)
|
||||
|
||||
class Meta:
|
||||
model = Users
|
||||
exclude = (
|
||||
|
@ -222,11 +232,21 @@ class UserViewSet(CustomModelViewSet):
|
|||
"name": "用户名称",
|
||||
"email": "用户邮箱",
|
||||
"mobile": "手机号码",
|
||||
"gender": "用户性别(男/女/未知)",
|
||||
"is_active": "帐号状态(启用/禁用)",
|
||||
"gender": {
|
||||
"title": "用户性别",
|
||||
"choices": {
|
||||
"data": {"未知": 2, "男": 1, "女": 0},
|
||||
}
|
||||
},
|
||||
"is_active": {
|
||||
"title": "帐号状态",
|
||||
"choices": {
|
||||
"data": {"启用": True, "禁用": False},
|
||||
}
|
||||
},
|
||||
"password": "登录密码",
|
||||
"dept": "部门ID",
|
||||
"role": "角色ID",
|
||||
"dept": {"title": "部门", "choices": {"queryset": Dept.objects.filter(status=True), "values_name": "name"}},
|
||||
"role": {"title": "角色", "choices": {"queryset": Role.objects.filter(status=True), "values_name": "name"}},
|
||||
}
|
||||
|
||||
@action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated])
|
||||
|
|
|
@ -1,37 +1,67 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import re
|
||||
|
||||
import openpyxl
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def import_to_data(file_url, field_data):
|
||||
def import_to_data(file_url, field_data, m2m_fields=None):
|
||||
"""
|
||||
读取导入的excel文件
|
||||
:param request:
|
||||
:param file_url:
|
||||
:param field_data: 首行数据源
|
||||
:param data: 数据源
|
||||
:param FilName: 文件名
|
||||
:param m2m_fields: 多对多字段
|
||||
:return:
|
||||
"""
|
||||
# 读取excel 文件
|
||||
file_path_dir = os.path.join(settings.BASE_DIR, file_url)
|
||||
workbook = openpyxl.load_workbook(file_path_dir)
|
||||
table = workbook[workbook.sheetnames[0]]
|
||||
# 获取参数映射
|
||||
validation_data_dict = {}
|
||||
for key, value in field_data.items():
|
||||
if isinstance(value, dict):
|
||||
choices = value.get("choices", {})
|
||||
data_dict = {}
|
||||
if choices.get("data"):
|
||||
for k, v in choices.get("data").items():
|
||||
data_dict[k] = v
|
||||
elif choices.get("queryset") and choices.get("values_name"):
|
||||
data_list = choices.get("queryset").values(choices.get("values_name"), "id")
|
||||
for ele in data_list:
|
||||
data_dict[ele.get(choices.get("values_name"))] = ele.get("id")
|
||||
else:
|
||||
continue
|
||||
validation_data_dict[key] = data_dict
|
||||
# 创建一个空列表,存储Excel的数据
|
||||
tables = []
|
||||
for i, row in enumerate(range(table.max_row)):
|
||||
if i == 0:
|
||||
continue
|
||||
array = {}
|
||||
for index, ele in enumerate(field_data.keys()):
|
||||
for index, key in enumerate(field_data.keys()):
|
||||
cell_value = table.cell(row=row + 1, column=index + 2).value
|
||||
print(cell_value)
|
||||
# 由于excel导入数字类型后,会出现数字加 .0 的,进行处理
|
||||
if type(cell_value) is float and str(cell_value).split(".")[1] == "0":
|
||||
cell_value = int(str(cell_value).split(".")[0])
|
||||
if type(cell_value) is str:
|
||||
cell_value = cell_value.strip(" \t\n\r")
|
||||
array[ele] = cell_value
|
||||
if cell_value is None:
|
||||
continue
|
||||
if key in validation_data_dict:
|
||||
array[key] = validation_data_dict.get(key, {}).get(cell_value, None)
|
||||
if key in m2m_fields:
|
||||
array[key] = list(
|
||||
filter(
|
||||
lambda x: x,
|
||||
[
|
||||
validation_data_dict.get(key, {}).get(value, None)
|
||||
for value in re.split(r"[,;:|.,;:\s]\s*", cell_value)
|
||||
],
|
||||
)
|
||||
)
|
||||
else:
|
||||
array[key] = cell_value
|
||||
tables.append(array)
|
||||
return tables
|
||||
|
|
|
@ -4,7 +4,8 @@ from urllib.parse import quote
|
|||
from django.db import transaction
|
||||
from django.http import HttpResponse
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.utils import get_column_letter
|
||||
from openpyxl.worksheet.datavalidation import DataValidation
|
||||
from openpyxl.utils import get_column_letter, quote_sheetname
|
||||
from openpyxl.worksheet.table import Table, TableStyleInfo
|
||||
from rest_framework.request import Request
|
||||
|
||||
|
@ -15,13 +16,28 @@ from dvadmin.utils.request_util import get_verbose_name
|
|||
|
||||
class ImportSerializerMixin:
|
||||
"""
|
||||
自定义导出模板、导入功能
|
||||
自定义导入模板、导入功能
|
||||
"""
|
||||
|
||||
# 导入字段
|
||||
import_field_dict = {}
|
||||
# 导入序列化器
|
||||
import_serializer_class = None
|
||||
# 表格表头最大宽度,默认50个字符
|
||||
export_column_width = 50
|
||||
|
||||
def get_string_len(self, string):
|
||||
"""
|
||||
获取字符串最大长度
|
||||
:param string:
|
||||
:return:
|
||||
"""
|
||||
length = 4
|
||||
if string is None:
|
||||
return length
|
||||
for char in string:
|
||||
length += 2.1 if ord(char) > 256 else 1
|
||||
return round(length, 1) if length <= self.export_column_width else self.export_column_width
|
||||
|
||||
@transaction.atomic # Django 事务,防止出错
|
||||
def import_data(self, request: Request, *args, **kwargs):
|
||||
|
@ -44,10 +60,49 @@ class ImportSerializerMixin:
|
|||
"Content-Disposition"
|
||||
] = f'attachment;filename={quote(str(f"导入{get_verbose_name(queryset)}模板.xlsx"))}'
|
||||
wb = Workbook()
|
||||
ws1 = wb.create_sheet("data", 1)
|
||||
ws1.sheet_state = "hidden"
|
||||
ws = wb.active
|
||||
row = get_column_letter(len(self.import_field_dict) + 1)
|
||||
column = 10
|
||||
ws.append(["序号", *self.import_field_dict.values()])
|
||||
header_data = [
|
||||
"序号",
|
||||
]
|
||||
validation_data_dict = {}
|
||||
for index, ele in enumerate(self.import_field_dict.values()):
|
||||
if isinstance(ele, dict):
|
||||
header_data.append(ele.get("title"))
|
||||
choices = ele.get("choices", {})
|
||||
if choices.get("data"):
|
||||
data_list = []
|
||||
data_list.extend(choices.get("data").keys())
|
||||
validation_data_dict[ele.get("title")] = data_list
|
||||
elif choices.get("queryset") and choices.get("values_name"):
|
||||
data_list = choices.get("queryset").values_list(choices.get("values_name"), flat=True)
|
||||
validation_data_dict[ele.get("title")] = list(data_list)
|
||||
else:
|
||||
continue
|
||||
column_letter = get_column_letter(len(validation_data_dict))
|
||||
dv = DataValidation(
|
||||
type="list",
|
||||
formula1=f"{quote_sheetname('data')}!${column_letter}$2:${column_letter}${len(validation_data_dict[ele.get('title')]) + 1}",
|
||||
allow_blank=True,
|
||||
)
|
||||
ws.add_data_validation(dv)
|
||||
dv.add(f"{get_column_letter(index + 2)}2:{get_column_letter(index + 2)}1048576")
|
||||
else:
|
||||
header_data.append(ele)
|
||||
# 添加数据列
|
||||
ws1.append(list(validation_data_dict.keys()))
|
||||
for index, validation_data in enumerate(validation_data_dict.values()):
|
||||
for inx, ele in enumerate(validation_data):
|
||||
ws1[f"{get_column_letter(index + 1)}{inx + 2}"] = ele
|
||||
# 插入导出模板正式数据
|
||||
df_len_max = [self.get_string_len(ele) for ele in header_data]
|
||||
ws.append(header_data)
|
||||
# 更新列宽
|
||||
for index, width in enumerate(df_len_max):
|
||||
ws.column_dimensions[get_column_letter(index + 1)].width = width
|
||||
tab = Table(displayName="Table1", ref=f"A1:{row}{column}") # 名称管理器
|
||||
style = TableStyleInfo(
|
||||
name="TableStyleLight11",
|
||||
|
@ -63,8 +118,14 @@ class ImportSerializerMixin:
|
|||
|
||||
updateSupport = request.data.get("updateSupport")
|
||||
# 从excel中组织对应的数据结构,然后使用序列化器保存
|
||||
data = import_to_data(request.data.get("url"), self.import_field_dict)
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
# 获取多对多字段
|
||||
m2m_fields = [
|
||||
ele.attname
|
||||
for ele in queryset.model._meta.get_fields()
|
||||
if hasattr(ele, "many_to_many") and ele.many_to_many == True
|
||||
]
|
||||
data = import_to_data(request.data.get("url"), self.import_field_dict, m2m_fields)
|
||||
unique_list = [
|
||||
ele.attname for ele in queryset.model._meta.get_fields() if hasattr(ele, "unique") and ele.unique == True
|
||||
]
|
||||
|
@ -91,6 +152,21 @@ class ExportSerializerMixin:
|
|||
export_field_label = []
|
||||
# 导出序列化器
|
||||
export_serializer_class = None
|
||||
# 表格表头最大宽度,默认50个字符
|
||||
export_column_width = 50
|
||||
|
||||
def get_string_len(self, string):
|
||||
"""
|
||||
获取字符串最大长度
|
||||
:param string:
|
||||
:return:
|
||||
"""
|
||||
length = 4
|
||||
if string is None:
|
||||
return length
|
||||
for char in string:
|
||||
length += 2.1 if ord(char) > 256 else 1
|
||||
return round(length, 1) if length <= self.export_column_width else self.export_column_width
|
||||
|
||||
def export_data(self, request: Request, *args, **kwargs):
|
||||
"""
|
||||
|
@ -109,13 +185,33 @@ class ExportSerializerMixin:
|
|||
response["Content-Disposition"] = f'attachment;filename={quote(str(f"导出{get_verbose_name(queryset)}.xlsx"))}'
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
header_data = ["序号", *self.export_field_label]
|
||||
df_len_max = [self.get_string_len(ele) for ele in header_data]
|
||||
row = get_column_letter(len(self.export_field_label) + 1)
|
||||
column = 1
|
||||
ws.append(["序号", *self.export_field_label])
|
||||
ws.append(header_data)
|
||||
for index, results in enumerate(data):
|
||||
ws.append([index + 1, *list(results.values())])
|
||||
results_list = []
|
||||
for inx, result in enumerate(results.values()):
|
||||
# 布尔值进行更新
|
||||
if result is True:
|
||||
result = "是"
|
||||
elif result is False:
|
||||
result = "否"
|
||||
if isinstance(result, int):
|
||||
result = str(result)
|
||||
# 计算最大列宽度
|
||||
result_column_width = self.get_string_len(result)
|
||||
if result_column_width > df_len_max[inx + 1]:
|
||||
df_len_max[inx + 1] = result_column_width
|
||||
|
||||
results_list.append(result)
|
||||
ws.append([index + 1, *results_list])
|
||||
column += 1
|
||||
tab = Table(displayName="Table2", ref=f"A1:{row}{column}") # 名称管理器
|
||||
# 更新列宽
|
||||
for index, width in enumerate(df_len_max):
|
||||
ws.column_dimensions[get_column_letter(index + 1)].width = width
|
||||
tab = Table(displayName="Table", ref=f"A1:{row}{column}") # 名称管理器
|
||||
style = TableStyleInfo(
|
||||
name="TableStyleLight11",
|
||||
showFirstColumn=True,
|
||||
|
|
|
@ -10,12 +10,49 @@ import uuid
|
|||
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from application import settings
|
||||
|
||||
table_prefix = settings.TABLE_PREFIX # 数据库表名前缀
|
||||
|
||||
|
||||
class SoftDeleteQuerySet(QuerySet):
|
||||
def delete(self,soft_delete=True):
|
||||
"""
|
||||
重写删除方法
|
||||
当soft_delete为True时表示软删除,则修改删除时间为当前时间,否则直接删除
|
||||
:param soft: Boolean 是否软删除,默认是
|
||||
:return: Tuple eg.(3, {'lqModel.Test': 3})
|
||||
"""
|
||||
if soft_delete:
|
||||
return self.update(is_deleted=True)
|
||||
else:
|
||||
return super(SoftDeleteQuerySet, self).delete()
|
||||
|
||||
|
||||
|
||||
class SoftDeleteManager(models.Manager):
|
||||
"""支持软删除"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.__add_is_del_filter = False
|
||||
super(SoftDeleteManager, self).__init__(*args, **kwargs)
|
||||
|
||||
def filter(self, *args, **kwargs):
|
||||
# 考虑是否主动传入is_deleted
|
||||
if not kwargs.get('is_deleted') is None:
|
||||
self.__add_is_del_filter = True
|
||||
return super(SoftDeleteManager, self).filter(*args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
if self.__add_is_del_filter:
|
||||
return SoftDeleteQuerySet(self.model, using=self._db).exclude(is_deleted=False)
|
||||
return SoftDeleteQuerySet(self.model).exclude(is_deleted=True)
|
||||
|
||||
def get_by_natural_key(self,name):
|
||||
return SoftDeleteQuerySet(self.model).get(username=name)
|
||||
|
||||
|
||||
class CoreModel(models.Model):
|
||||
"""
|
||||
核心标准抽象模型模型,可直接继承使用
|
||||
|
@ -30,6 +67,9 @@ class CoreModel(models.Model):
|
|||
update_datetime = models.DateTimeField(auto_now=True, null=True, blank=True, help_text="修改时间", verbose_name="修改时间")
|
||||
create_datetime = models.DateTimeField(auto_now_add=True, null=True, blank=True, help_text="创建时间",
|
||||
verbose_name="创建时间")
|
||||
is_deleted = models.BooleanField(verbose_name="是否软删除",help_text='是否软删除', default=False, db_index=True)
|
||||
objects = SoftDeleteManager()
|
||||
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
@ -37,6 +77,8 @@ class CoreModel(models.Model):
|
|||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
|
||||
|
||||
def get_all_models_objects(model_name=None):
|
||||
"""
|
||||
获取所有 models 对象
|
||||
|
|
|
@ -80,6 +80,9 @@ class CustomModelSerializer(DynamicFieldsMixin, ModelSerializer):
|
|||
|
||||
def update(self, instance, validated_data):
|
||||
if self.request:
|
||||
if str(self.request.user) != "AnonymousUser":
|
||||
if self.modifier_field_id in self.fields.fields:
|
||||
validated_data[self.modifier_field_id] = self.get_request_user_id()
|
||||
if hasattr(self.instance, self.modifier_field_id):
|
||||
setattr(
|
||||
self.instance, self.modifier_field_id, self.get_request_user_id()
|
||||
|
|
|
@ -92,7 +92,13 @@ class CustomModelViewSet(ModelViewSet,ImportSerializerMixin,ExportSerializerMixi
|
|||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
self.perform_destroy(instance)
|
||||
request_data = request.data
|
||||
soft_delete = request_data.get('soft_delete',True)
|
||||
if soft_delete:
|
||||
instance.is_deleted = True
|
||||
instance.save()
|
||||
else:
|
||||
self.perform_destroy(instance)
|
||||
return DetailResponse(data=[], msg="删除成功")
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# 构建预览页面
|
||||
|
||||
# 预览环境
|
||||
# 指定构建模式
|
||||
NODE_ENV=production
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# 生产环境
|
||||
# 指定构建模式
|
||||
NODE_ENV=production
|
||||
|
||||
# 标记当前构建方式
|
||||
VUE_APP_BUILD_MODE=PREVIEW
|
||||
# 页面 title 前缀
|
||||
VUE_APP_TITLE=企业级后台管理系统
|
||||
# 显示源码按钮
|
||||
VUE_APP_SCOURCE_LINK=FALSE
|
||||
|
||||
# 部署路径
|
||||
VUE_APP_PUBLIC_PATH=/
|
||||
# 启用权限管理
|
||||
VUE_APP_PM_ENABLED = true
|
|
@ -213,13 +213,14 @@ const refreshTken = function () {
|
|||
* 下载文件
|
||||
* @param url
|
||||
* @param params
|
||||
* @param method
|
||||
* @param filename
|
||||
*/
|
||||
export const downloadFile = function ({ url, data, method, filename }) {
|
||||
export const downloadFile = function ({ url, params, method, filename }) {
|
||||
request({
|
||||
url: url,
|
||||
method: method,
|
||||
data: data,
|
||||
params: params,
|
||||
responseType: 'blob'
|
||||
// headers: {Accept: 'application/vnd.openxmlformats-officedocument'}
|
||||
}).then(res => {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# 一对多表格显示配置说明
|
||||
本组件用于多对多返回数据使用,例如:角色信息
|
||||
```angular2html
|
||||
dept_name = "dvadmin团队"
|
||||
|
||||
#crud的配置
|
||||
component: {
|
||||
name: 'foreignKey',
|
||||
valueBinding: 'dept_name'
|
||||
}
|
||||
```
|
||||
## crud.js
|
||||
```
|
||||
{
|
||||
component: {
|
||||
name: 'foreignKey',
|
||||
valueBinding: 'dept_name',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
|
||||
| Name | Description | Type | Required | Default |
|
||||
| ---------- | ---------------- | ------- | -------- | -------------- |
|
||||
| name | 字段所使用的组件 | String | true | foreignKey |
|
||||
| valueBinding | row中的key | String | true | - |
|
|
@ -0,0 +1,15 @@
|
|||
import { d2CrudPlus } from 'd2-crud-plus'
|
||||
import group from './group'
|
||||
|
||||
function install (Vue, options) {
|
||||
Vue.component('foreign-key', () => import('./index'))
|
||||
if (d2CrudPlus != null) {
|
||||
// 注册字段类型`demo-extend`
|
||||
d2CrudPlus.util.columnResolve.addTypes(group)
|
||||
}
|
||||
}
|
||||
|
||||
// 导出install, 通过`vue.use(D2pDemoExtend)`安装后 ,`demo-extend` 就可以在`crud.js`中使用了
|
||||
export default {
|
||||
install
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<el-tag :type="color">{{ currentValue }}</el-tag>
|
||||
</template>
|
||||
<script>
|
||||
// 行展示组件进阶版
|
||||
// 本示例演示要对传入的值做一些改变,然后再展示
|
||||
export default {
|
||||
name: 'foreign-key',
|
||||
props: {
|
||||
color: {
|
||||
require: false
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentValue: ''
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
value(nv,ov){
|
||||
const { row } = this.$parent.scope
|
||||
const valueBinding = this.$parent.valueBinding
|
||||
this.setValue(row[valueBinding])
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const { row } = this.$parent.scope
|
||||
const valueBinding = this.$parent.valueBinding
|
||||
this.setValue(row[valueBinding])
|
||||
},
|
||||
methods: {
|
||||
setValue (value) {
|
||||
// 在这里对 传入的value值做处理
|
||||
this.currentValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -7,3 +7,5 @@ Vue.component('d2-container', d2Container)
|
|||
Vue.component('d2-icon', () => import('./d2-icon'))
|
||||
Vue.component('d2-icon-svg', () => import('./d2-icon-svg/index.vue'))
|
||||
Vue.component('importExcel', () => import('./importExcel/index.vue'))
|
||||
Vue.component('foreignKey', () => import('./foreign-key/index.vue'))
|
||||
Vue.component('manyToMany', () => import('./many-to-many/index.vue'))
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# 多对多表格显示配置说明
|
||||
本组件用于多对多返回数据使用,例如:角色信息
|
||||
```angular2html
|
||||
role_info = [
|
||||
{"id":1,"name":"普通用户"},
|
||||
{"id":2,"name":"管理员"}
|
||||
]
|
||||
|
||||
#crud的配置
|
||||
component: {
|
||||
name: 'manyToMany',
|
||||
valueBinding: 'role_info',
|
||||
children: 'name'
|
||||
}
|
||||
```
|
||||
## crud.js
|
||||
```
|
||||
{
|
||||
component: {
|
||||
name: 'manyToMany',
|
||||
valueBinding: 'role_name',
|
||||
children: 'name'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 配置说明
|
||||
|
||||
|
||||
| Name | Description | Type | Required | Default |
|
||||
| ---------- | ---------------- | ------- | -------- | -------------- |
|
||||
| name | 字段所使用的组件 | String | true | manyToMany |
|
||||
| valueBinding | row中的key | String | true | - |
|
||||
| children | 数组中的key | String | true | name |
|
|
@ -0,0 +1,15 @@
|
|||
import { d2CrudPlus } from 'd2-crud-plus'
|
||||
import group from './group'
|
||||
|
||||
function install (Vue, options) {
|
||||
Vue.component('many-to-many', () => import('./index'))
|
||||
if (d2CrudPlus != null) {
|
||||
// 注册字段类型`demo-extend`
|
||||
d2CrudPlus.util.columnResolve.addTypes(group)
|
||||
}
|
||||
}
|
||||
|
||||
// 导出install, 通过`vue.use(D2pDemoExtend)`安装后 ,`demo-extend` 就可以在`crud.js`中使用了
|
||||
export default {
|
||||
install
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tag style="margin-right: 10px" :type="color" v-for="(item,index) in currentValue" :key="index">{{
|
||||
item[key]
|
||||
}}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// 行展示组件进阶版
|
||||
// 本示例演示要对传入的值做一些改变,然后再展示
|
||||
export default {
|
||||
name: 'many-to-many',
|
||||
props: {
|
||||
color: {
|
||||
require: false
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentValue: [],
|
||||
key: 'name'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (nv, ov) {
|
||||
const { row } = this.$parent.scope
|
||||
const { children } = this.$parent
|
||||
if (children) {
|
||||
const valueBinding = this.$parent.valueBinding
|
||||
this.setValue(row[valueBinding])
|
||||
this.key = children
|
||||
} else {
|
||||
this.setValue([])
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const { row } = this.$parent.scope
|
||||
const { children } = this.$parent
|
||||
if (children) {
|
||||
const valueBinding = this.$parent.valueBinding
|
||||
this.setValue(row[valueBinding])
|
||||
this.key = children
|
||||
} else {
|
||||
this.setValue([])
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setValue (value) {
|
||||
// 在这里对 传入的value值做处理
|
||||
this.currentValue = value
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -201,6 +201,7 @@ export default {
|
|||
// this.dict = d2CrudPlus.util.dict.mergeDefault(this.dict, true)
|
||||
// }
|
||||
// this.initData()
|
||||
console.log(this)
|
||||
},
|
||||
computed: {
|
||||
_elProps () {
|
||||
|
@ -327,32 +328,23 @@ export default {
|
|||
this.$emit('current-change', event)
|
||||
},
|
||||
openDialog () {
|
||||
if (this.disabled) {
|
||||
const that = this
|
||||
if (that.disabled) {
|
||||
return
|
||||
}
|
||||
this.dialogVisible = true
|
||||
setTimeout(() => {
|
||||
if (this.selected != null) {
|
||||
const ids = this.selected.map(
|
||||
(item) => item[this._elProps.props.value]
|
||||
)
|
||||
ids.forEach((id) => {
|
||||
const current = this.$refs.elTree.store.nodesMap[id]
|
||||
if (current != null) {
|
||||
this.doExpandParent(current)
|
||||
}
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
if (this.multiple) {
|
||||
// this.$refs.elTree.setCheckedKeys(ids, this.leafOnly);
|
||||
this.$refs.elTree.setCheckboxRow(ids)
|
||||
} else if (ids.length > 0) {
|
||||
// this.$refs.elTree.setCurrentKey(ids[0]);
|
||||
this.$refs.elTree.setRadioRow(ids[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 1)
|
||||
that.dialogVisible = true
|
||||
if (that.value != null) {
|
||||
that.$nextTick(() => {
|
||||
const refs = Object.assign({}, that.$refs)
|
||||
const { elTree } = refs
|
||||
console.log(elTree)
|
||||
if (that.multiple) {
|
||||
elTree.setCheckboxRow(that.selected, true)
|
||||
} else {
|
||||
elTree.setRadioRow(that.selected[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
doExpandParent (node) {
|
||||
if (node.parent != null) {
|
||||
|
@ -412,20 +404,6 @@ export default {
|
|||
// 获取选中的行数据
|
||||
refreshSelected () {
|
||||
let nodes = null
|
||||
// if (this.multiple) {
|
||||
// nodes = this.$refs.elTree.getCheckedNodes(
|
||||
// this.leafOnly,
|
||||
// this.includeHalfChecked
|
||||
// );
|
||||
// } else {
|
||||
// const node = this.$refs.elTree.getCurrentNode();
|
||||
// if (node == null) {
|
||||
// nodes = [];
|
||||
// } else {
|
||||
// nodes = [node];
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this.multiple) {
|
||||
nodes = this.$refs.elTree.getCheckboxRecords()
|
||||
} else {
|
||||
|
|
|
@ -256,6 +256,10 @@ Vue.prototype.commonEndColumns = function (param = {}) {
|
|||
create_datetime: {
|
||||
showForm: (param.create_datetime && param.create_datetime.showForm) !== undefined ? param.create_datetime.showForm : false,
|
||||
showTable: (param.create_datetime && param.create_datetime.showTable) !== undefined ? param.create_datetime.showTable : true
|
||||
},
|
||||
is_deleted: {
|
||||
showForm: (param.is_deleted && param.is_deleted.showForm) !== undefined ? param.is_deleted.showForm : false,
|
||||
showTable: (param.is_deleted && param.is_deleted.showTable) !== undefined ? param.is_deleted.showTable : false
|
||||
}
|
||||
}
|
||||
return [
|
||||
|
@ -388,6 +392,23 @@ Vue.prototype.commonEndColumns = function (param = {}) {
|
|||
form: {
|
||||
disabled: !showData.create_datetime.showForm
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '是否软删除',
|
||||
key: 'is_deleted',
|
||||
width: 160,
|
||||
search: {
|
||||
disabled: !showData.is_deleted.showForm,
|
||||
},
|
||||
show: showData.is_deleted.showTable,
|
||||
type: 'radio',
|
||||
dict:{
|
||||
data:[{label:"是",value:true},{label:"否",value:false}]
|
||||
},
|
||||
form: {
|
||||
disabled: !showData.is_deleted.showForm,
|
||||
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -160,8 +160,8 @@ export default {
|
|||
routerViewKey () {
|
||||
// 默认情况下 key 类似 __transition-n-/foo
|
||||
// 这里的字符串操作是为了最终 key 的格式和原来相同 类似 __transition-n-__stamp-time-/foo
|
||||
const stamp = this.$route.meta[`__stamp-${this.$route.path}`] || ''
|
||||
return `${stamp ? `__stamp-${stamp}-` : ''}${this.$route.path}`
|
||||
const stamp = this.$route.meta[`__stamp-${this.$route.fullpath}`] || ''
|
||||
return `${stamp ? `__stamp-${stamp}-` : ''}${this.$route.fullpath}`
|
||||
},
|
||||
/**
|
||||
* @description 最外层容器的背景图片样式
|
||||
|
|
|
@ -2,11 +2,13 @@ import cookies from './util.cookies'
|
|||
import db from './util.db'
|
||||
import log from './util.log'
|
||||
import dayjs from 'dayjs'
|
||||
import filterParams from './util.params'
|
||||
|
||||
const util = {
|
||||
cookies,
|
||||
db,
|
||||
log
|
||||
log,
|
||||
filterParams
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import util from '@/libs/util'
|
||||
|
||||
/**
|
||||
* 对请求参数进行过滤
|
||||
*@param that=>this
|
||||
*@param array:其他字段数组
|
||||
*/
|
||||
const filterParams = function (that, array) {
|
||||
that.$nextTick(()=>{
|
||||
const arr = that.crud.columns
|
||||
const columnKeys = arr.map(item => {
|
||||
return item.key
|
||||
})
|
||||
let newArray = [...columnKeys, array, 'id']
|
||||
newArray = [...new Set(newArray)]
|
||||
that.crud.searchOptions.form.query = '{' + newArray.toString() + '}'
|
||||
})
|
||||
}
|
||||
export default filterParams
|
|
@ -32,10 +32,14 @@ import 'vxe-table/lib/style.css'
|
|||
// md5加密
|
||||
import md5 from 'js-md5'
|
||||
|
||||
//websocket
|
||||
import websocket from '@/api/websocket'
|
||||
|
||||
// 核心插件
|
||||
Vue.use(d2Admin)
|
||||
Vue.use(VXETable)
|
||||
Vue.prototype.$md5 = md5
|
||||
Vue.prototype.$websocket = websocket
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
|
|
|
@ -104,12 +104,13 @@ export const crudOptions = (vm) => {
|
|||
},
|
||||
valueChange (key, value, form, { getColumn, mode, component, immediate, getComponent }) {
|
||||
if (value != null) {
|
||||
console.log('component.dictOptions', component.dictOptions)
|
||||
// console.log('component.dictOptions', component.dictOptions)
|
||||
const obj = component.dictOptions.find(item => {
|
||||
console.log(item.label, value)
|
||||
// console.log(item.label, value)
|
||||
return item.value === value
|
||||
})
|
||||
if (obj && obj.value) {
|
||||
form.name = obj.label
|
||||
form.value = obj.value
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export function GetList (query) {
|
|||
return request({
|
||||
url: urlPrefix,
|
||||
method: 'get',
|
||||
params: query
|
||||
params: {...query}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ export function GetList (query) {
|
|||
return request({
|
||||
url: urlPrefix,
|
||||
method: 'get',
|
||||
params: query
|
||||
params: { ...query }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,15 @@ export function DelObj (id) {
|
|||
return request({
|
||||
url: urlPrefix + id + '/',
|
||||
method: 'delete',
|
||||
data: { id }
|
||||
data: { soft_delete: true }
|
||||
})
|
||||
}
|
||||
|
||||
export function BatchDel (keys) {
|
||||
return request({
|
||||
url: urlPrefix + 'multiple_delete/',
|
||||
method: 'delete',
|
||||
data: { keys }
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { request } from '@/api/service'
|
||||
import { urlPrefix as deptPrefix } from '../dept/api'
|
||||
import util from '@/libs/util'
|
||||
|
||||
export const crudOptions = (vm) => {
|
||||
util.filterParams(vm, ['dept_name','role_info{name}'])
|
||||
return {
|
||||
pageOptions: {
|
||||
compact: true
|
||||
|
@ -9,7 +11,12 @@ export const crudOptions = (vm) => {
|
|||
options: {
|
||||
height: '100%',
|
||||
tableType: 'vxe-table',
|
||||
rowKey: true // 必须设置,true or false
|
||||
rowKey: true,
|
||||
rowId: 'id'
|
||||
},
|
||||
selectionRow: {
|
||||
align: 'center',
|
||||
width: 46
|
||||
},
|
||||
rowHandle: {
|
||||
width: 240,
|
||||
|
@ -128,6 +135,7 @@ export const crudOptions = (vm) => {
|
|||
placeholder: '请输入密码'
|
||||
},
|
||||
value: vm.systemConfig('base.default_password'),
|
||||
editDisabled: true,
|
||||
itemProps: {
|
||||
class: { yxtInput: true }
|
||||
}
|
||||
|
@ -193,6 +201,10 @@ export const crudOptions = (vm) => {
|
|||
pagination: true,
|
||||
props: { multiple: false }
|
||||
}
|
||||
},
|
||||
component: {
|
||||
name: 'foreignKey',
|
||||
valueBinding: 'dept_name'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -368,6 +380,11 @@ export const crudOptions = (vm) => {
|
|||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
component: {
|
||||
name: 'manyToMany',
|
||||
valueBinding: 'role_info',
|
||||
children: 'name'
|
||||
}
|
||||
}
|
||||
].concat(vm.commonEndColumns({
|
||||
|
|
|
@ -21,9 +21,12 @@
|
|||
>
|
||||
<i class="el-icon-plus" /> 新增
|
||||
</el-button>
|
||||
<el-button size="small" type="danger" @click="batchDelete">
|
||||
<i class="el-icon-delete"></i> 批量删除
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="danger"
|
||||
type="warning"
|
||||
@click="onExport"
|
||||
v-permission="'Export'"
|
||||
><i class="el-icon-download" /> 导出
|
||||
|
@ -42,6 +45,16 @@
|
|||
@columns-filter-changed="handleColumnsFilterChanged"
|
||||
/>
|
||||
</div>
|
||||
<span slot="PaginationPrefixSlot" class="prefix">
|
||||
<el-button
|
||||
class="square"
|
||||
size="mini"
|
||||
title="批量删除"
|
||||
@click="batchDelete"
|
||||
icon="el-icon-delete"
|
||||
:disabled="!multipleSelection || multipleSelection.length == 0"
|
||||
/>
|
||||
</span>
|
||||
</d2-crud-x>
|
||||
<el-dialog
|
||||
title="密码重置"
|
||||
|
@ -81,7 +94,6 @@
|
|||
import * as api from './api'
|
||||
import { crudOptions } from './crud'
|
||||
import { d2CrudPlus } from 'd2-crud-plus'
|
||||
|
||||
export default {
|
||||
name: 'user',
|
||||
mixins: [d2CrudPlus.crud],
|
||||
|
@ -144,13 +156,18 @@ export default {
|
|||
delRequest (row) {
|
||||
return api.DelObj(row.id)
|
||||
},
|
||||
batchDelRequest (ids) {
|
||||
return api.BatchDel(ids)
|
||||
},
|
||||
onExport () {
|
||||
const that = this
|
||||
this.$confirm('是否确认导出所有数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(function () {
|
||||
return api.exportData()
|
||||
const query = that.getSearch().getForm()
|
||||
return api.exportData({ ...query })
|
||||
})
|
||||
},
|
||||
// 重置密码弹框
|
||||
|
|
Loading…
Reference in New Issue