diff --git a/backend/application/settings.py b/backend/application/settings.py index 8b2c8c2d79689f2671f77db328dd5648a41f4360..1fc069dbca6fd08d2a98379dce21162897c58e5c 100644 --- a/backend/application/settings.py +++ b/backend/application/settings.py @@ -60,6 +60,7 @@ INSTALLED_APPS = [ "captcha", "channels", "dvadmin.system", + "asset" ] MIDDLEWARE = [ diff --git a/backend/application/urls.py b/backend/application/urls.py index cb5a89997adb091fb3f8b071a6c7be814583e766..1068457b28d931b2604d15cdb5fcf76927503a8b 100644 --- a/backend/application/urls.py +++ b/backend/application/urls.py @@ -99,6 +99,7 @@ urlpatterns = ( name="schema-redoc", ), path("api/system/", include("dvadmin.system.urls")), + path("api/asset/", include("asset.urls")), path("api/login/", LoginView.as_view(), name="token_obtain_pair"), path("api/logout/", LogoutView.as_view(), name="token_obtain_pair"), path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"), diff --git a/backend/asset/__init__.py b/backend/asset/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/asset/admin.py b/backend/asset/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e --- /dev/null +++ b/backend/asset/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/backend/asset/apps.py b/backend/asset/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..08b6f71957da5bb67fe748652947a2777b3431f1 --- /dev/null +++ b/backend/asset/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AssetConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "asset" diff --git a/backend/asset/migrations/__init__.py b/backend/asset/migrations/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/asset/models.py b/backend/asset/models.py new file mode 100644 index 0000000000000000000000000000000000000000..c35d6f22587c2080aa6f424fb4a7b4ec2c674b6c --- /dev/null +++ b/backend/asset/models.py @@ -0,0 +1,115 @@ +from django.db import models + +from common.common_models import CoreModel, TreeNodeModel +from dvadmin.system.models import Users + + +class AssetCategory(TreeNodeModel): + + """ + @author: 陈仕贤 + @contact: QQ:49933729 + @Created on: 2024/6/29 + @Remark: 资产类别 + """ + + number = models.CharField(max_length=50, unique=True, verbose_name="编码", help_text="编码") + name = models.CharField(max_length=250, verbose_name="名称", help_text="名称") + sort = models.IntegerField(default=1, verbose_name="顺序", help_text="顺序") + status = models.BooleanField(default=True, verbose_name="状态", help_text="状态") + + class Meta: + db_table = "asset_category" + verbose_name = "资产类别表" + verbose_name_plural = verbose_name + ordering = ("sort",) + + +class AssetItem(CoreModel): + + """ + @author: 陈仕贤 + @contact: QQ:49933729 + @Created on: 2024/6/29 + @Remark: 资产清单 + """ + + number = models.CharField(max_length=50, unique=True, verbose_name="编码", help_text="编码") + name = models.CharField(max_length=250, verbose_name="名称", help_text="名称") + category = models.ForeignKey(AssetCategory, models.SET_NULL, related_name='asset_item', verbose_name='类别', + null=True) + rfid = models.CharField(max_length=150, verbose_name="RFID", null=True, blank=True) + brand = models.CharField(max_length=150, verbose_name="品牌", null=True, blank=True) + model = models.CharField(max_length=150, verbose_name="型号", null=True, blank=True) + buy_way = models.CharField(max_length=150, verbose_name="购置方式", null=True, blank=True) + buy_time = models.DateTimeField(help_text="购置时间", verbose_name="购置时间", null=True) + buy_price = models.DecimalField(max_digits=10, decimal_places=2, null=True) + admin = models.ForeignKey(Users, models.SET_NULL, related_name='asset_item_admin', verbose_name='管理员', null=True) + user = models.ForeignKey(Users, models.SET_NULL, related_name='asset_item_user', verbose_name='使用人', null=True) + sort = models.IntegerField(default=1, verbose_name="顺序", help_text="顺序") + status = models.BooleanField(default=True, verbose_name="状态", help_text="状态") + + class Meta: + db_table = "asset_item" + verbose_name = "资产清单表" + verbose_name_plural = verbose_name + ordering = ("sort",) + + + + + +class ApplyAbstract(CoreModel): + + """ + @author: 陈仕贤 + @contact: QQ:49933729 + @Created on: 2024/6/30 + @Remark: 申请 + """ + + number = models.CharField(max_length=50, unique=True, verbose_name="编码", help_text="编码") + title = models.CharField(max_length=250, verbose_name="标题", help_text="标题") + remark = models.TextField() + sort = models.IntegerField(default=1, verbose_name="顺序", help_text="顺序") + status = models.BooleanField(default=True, verbose_name="状态", help_text="状态") + + class Meta: + abstract = True + verbose_name = "申请表抽象" + verbose_name_plural = verbose_name + + +class Apply(ApplyAbstract): + + """ + @author: 陈仕贤 + @contact: QQ:49933729 + @Created on: 2024/6/30 + @Remark: 申请 + """ + + class Meta: + db_table = "apply" + verbose_name = "申请表" + verbose_name_plural = verbose_name + ordering = ("number", "title", "remark", "sort", "status") + + +class AssetInApply(ApplyAbstract): + + """ + @author: 陈仕贤 + @contact: QQ:49933729 + @Created on: 2024/6/30 + @Remark: 资产申请 + """ + + handle_time = models.DateTimeField(help_text="处理时间", verbose_name="处理时间", null=True) + num = models.IntegerField(default=1, verbose_name="数量", help_text="数量") + + class Meta: + db_table = "asset_in_apply" + verbose_name = "资产入库申请表" + verbose_name_plural = verbose_name + ordering = ("number", "title", "handle_time", "num", "remark") diff --git a/backend/asset/tests.py b/backend/asset/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6 --- /dev/null +++ b/backend/asset/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/asset/urls.py b/backend/asset/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..4689765497410d69af1952657b730dd3c85f3928 --- /dev/null +++ b/backend/asset/urls.py @@ -0,0 +1,14 @@ +from django.urls import path +from rest_framework import routers + +from asset.views.item import ItemViewSet +from asset.views.category import CategoryViewSet + +system_url = routers.SimpleRouter() +system_url.register(r'item', ItemViewSet) +system_url.register(r'category', CategoryViewSet) + +urlpatterns = [] + +urlpatterns += system_url.urls + diff --git a/backend/asset/views.py b/backend/asset/views.py new file mode 100644 index 0000000000000000000000000000000000000000..91ea44a218fbd2f408430959283f0419c921093e --- /dev/null +++ b/backend/asset/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/backend/asset/views/__init__.py b/backend/asset/views/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/asset/views/category.py b/backend/asset/views/category.py new file mode 100644 index 0000000000000000000000000000000000000000..1484272e37780aabe019178c42815e94eadc3896 --- /dev/null +++ b/backend/asset/views/category.py @@ -0,0 +1,65 @@ +""" +@author: 陈仕贤 +@contact: QQ:49933729 +@Created on: 2024/7/02 +@Remark: 资产类别管理 +""" + +from rest_framework.response import Response +from rest_framework import serializers + +from dvadmin.utils.serializers import CustomModelSerializer +from dvadmin.utils.validator import CustomUniqueValidator +from dvadmin.utils.viewset import CustomModelViewSet + +from asset.models import AssetCategory +from dvadmin.utils.json_response import SuccessResponse + + +class CategorySerializer(CustomModelSerializer): + """ + 序列化器 + """ + + + + class Meta: + model = AssetCategory + fields = "__all__" + read_only_fields = ["id"] + + +class CategoryCreateUpdateSerializer(CustomModelSerializer): + """ + 增加或更新的序列化器 + """ + + class Meta: + model = AssetCategory + fields = '__all__' + + +class CategoryViewSet(CustomModelViewSet): + """ + 资产清单管理接口 + list:查询 + create:新增 + update:修改 + retrieve:单例 + destroy:删除 + """ + queryset = AssetCategory.objects.all() + serializer_class = CategorySerializer + create_serializer_class = CategoryCreateUpdateSerializer + update_serializer_class = CategoryCreateUpdateSerializer + search_fields = ['name', 'key'] + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + serializer = self.get_serializer(queryset, many=True, request=request) + return SuccessResponse(data=serializer.data, msg="获取成功") + + + + + diff --git a/backend/asset/views/item.py b/backend/asset/views/item.py new file mode 100644 index 0000000000000000000000000000000000000000..5b2d1090ade5513e622d7b41feca3b4bbca06160 --- /dev/null +++ b/backend/asset/views/item.py @@ -0,0 +1,59 @@ +""" +@author: 陈仕贤 +@contact: QQ:49933729 +@Created on: 2024/6/29 +@Remark: 资产清单管理 +""" + +from rest_framework.response import Response +from rest_framework import serializers + +from dvadmin.utils.serializers import CustomModelSerializer +from dvadmin.utils.validator import CustomUniqueValidator +from dvadmin.utils.viewset import CustomModelViewSet + +from asset.models import AssetItem + + +class ItemSerializer(CustomModelSerializer): + """ + 角色-序列化器 + """ + + class Meta: + model = AssetItem + fields = "__all__" + read_only_fields = ["id"] + + +class ItemCreateUpdateSerializer(CustomModelSerializer): + + # def save(self, **kwargs): + # is_superuser = self.request.user.is_superuser + # if not is_superuser: + # self.validated_data.pop('admin') + # data = super().save(**kwargs) + # return data + + class Meta: + model = AssetItem + fields = '__all__' + + +class ItemViewSet(CustomModelViewSet): + """ + 资产清单管理接口 + list:查询 + create:新增 + update:修改 + retrieve:单例 + destroy:删除 + """ + queryset = AssetItem.objects.all() + serializer_class = ItemSerializer + create_serializer_class = ItemCreateUpdateSerializer + update_serializer_class = ItemCreateUpdateSerializer + search_fields = ['name', 'key'] + + + diff --git a/backend/common/__init__.py b/backend/common/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/backend/common/common_models.py b/backend/common/common_models.py new file mode 100644 index 0000000000000000000000000000000000000000..8067962f08956fe7c32ba829a787c572c71d6c59 --- /dev/null +++ b/backend/common/common_models.py @@ -0,0 +1,175 @@ +""" +@author: 陈仕贤 +@contact: QQ:49933729 +@Created on: 2024-06-30 +@Remark: 公共基础model类 +""" + +from importlib import import_module + +from django.apps import apps +from django.db import models +from django.conf import settings + +from application import settings + +table_prefix = settings.TABLE_PREFIX # 数据库表名前缀 + + +class SoftDeleteQuerySet(models.QuerySet): + pass + + +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 SoftDeleteModel(models.Model): + """ + 软删除模型 + 一旦继承,就将开启软删除 + """ + is_deleted = models.BooleanField(verbose_name="是否软删除", help_text='是否软删除', default=False, db_index=True) + objects = SoftDeleteManager() + + class Meta: + abstract = True + verbose_name = '软删除模型' + verbose_name_plural = verbose_name + + def delete(self, using=None, soft_delete=True, *args, **kwargs): + """ + 重写删除方法,直接开启软删除 + """ + self.is_deleted = True + self.save(using=using) + + +class CoreModel(models.Model): + """ + 核心标准抽象模型模型,可直接继承使用 + 增加审计字段, 覆盖字段时, 字段名称请勿修改, 必须统一审计字段名称 + """ + id = models.BigAutoField(primary_key=True, help_text="Id", verbose_name="Id") + description = models.CharField(max_length=255, verbose_name="描述", null=True, blank=True, help_text="描述") + creator = models.ForeignKey(to=settings.AUTH_USER_MODEL, related_query_name='creator_query', null=True, + verbose_name='创建人', help_text="创建人", on_delete=models.SET_NULL, + db_constraint=False) + modifier = models.CharField(max_length=255, null=True, blank=True, help_text="修改人", verbose_name="修改人") + dept_belong_id = models.CharField(max_length=255, help_text="数据归属部门", null=True, blank=True, + verbose_name="数据归属部门") + 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="创建时间") + + class Meta: + abstract = True + verbose_name = '核心模型' + verbose_name_plural = verbose_name + + +class TreeNodeModel(CoreModel): + """ + 树节点的抽象模型模型,可直接继承使用 + 增加父节点ID字段 + """ + parent_id = models.BigIntegerField(help_text="父节点ID", verbose_name="父节点ID") + + class Meta: + abstract = True + verbose_name = '树节点抽象模型' + verbose_name_plural = verbose_name + + + +def get_all_models_objects(model_name=None): + """ + 获取所有 models 对象 + :return: {} + """ + settings.ALL_MODELS_OBJECTS = {} + if not settings.ALL_MODELS_OBJECTS: + all_models = apps.get_models() + for item in list(all_models): + table = { + "tableName": item._meta.verbose_name, + "table": item.__name__, + "tableFields": [] + } + for field in item._meta.fields: + fields = { + "title": field.verbose_name, + "field": field.name + } + table['tableFields'].append(fields) + settings.ALL_MODELS_OBJECTS.setdefault(item.__name__, {"table": table, "object": item}) + if model_name: + return settings.ALL_MODELS_OBJECTS[model_name] or {} + return settings.ALL_MODELS_OBJECTS or {} + + +def get_model_from_app(app_name): + """获取模型里的字段""" + model_module = import_module(app_name + '.models') + filter_model = [ + getattr(model_module, item) for item in dir(model_module) + if item != 'CoreModel' and issubclass(getattr(model_module, item).__class__, models.base.ModelBase) + ] + model_list = [] + for model in filter_model: + if model.__name__ == 'AbstractUser': + continue + fields = [ + {'title': field.verbose_name, 'name': field.name, 'object': field} + for field in model._meta.fields + ] + model_list.append({ + 'app': app_name, + 'verbose': model._meta.verbose_name, + 'model': model.__name__, + 'object': model, + 'fields': fields + }) + return model_list + + +def get_custom_app_models(app_name=None): + """ + 获取所有项目下的app里的models + """ + if app_name: + return get_model_from_app(app_name) + all_apps = apps.get_app_configs() + res = [] + for app in all_apps: + if app.name.startswith('django'): + continue + if app.name in settings.COLUMN_EXCLUDE_APPS: + continue + try: + all_models = get_model_from_app(app.name) + if all_models: + for model in all_models: + res.append(model) + except Exception as e: + pass + return res diff --git a/backend/dvadmin/__init__.py b/backend/dvadmin/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..40a96afc6ff09d58a702b76e3f7dd412fe975e26 --- /dev/null +++ b/backend/dvadmin/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/backend/dvadmin/system/fixtures/init_menu.json b/backend/dvadmin/system/fixtures/init_menu.json index 425887234ac2af9b6d904e786b969173c8e27729..b683f0af73fb39e6746918b0b0f3c8f05ff17a8f 100644 --- a/backend/dvadmin/system/fixtures/init_menu.json +++ b/backend/dvadmin/system/fixtures/init_menu.json @@ -167,19 +167,13 @@ "method": 0 }, { - "name": "查询所有", + "name": "获取所有部门", "value": "dept:SearchAll", "api": "/api/system/dept/all_dept/", "method": 0 }, { - "name": "懒加载查询所有", - "value": "dept:LazySearchAll", - "api": "/api/system/dept/dept_lazy_tree/", - "method": 0 - }, - { - "name": "头信息", + "name": "部门顶部信息", "value": "dept:HeaderInfo", "api": "/api/system/dept/dept_info/", "method": 0 diff --git a/backend/dvadmin/system/views/dept.py b/backend/dvadmin/system/views/dept.py index 5c5105b7fe2dbbae14249d15ce86d6e1005f8c48..de48b0dbba652a91ceeadf47c9738ddc2345f5c0 100644 --- a/backend/dvadmin/system/views/dept.py +++ b/backend/dvadmin/system/views/dept.py @@ -10,6 +10,7 @@ from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from dvadmin.system.models import Dept, RoleMenuButtonPermission, Users +from dvadmin.utils.filters import DataLevelPermissionsFilter from dvadmin.utils.json_response import DetailResponse, SuccessResponse, ErrorResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet @@ -124,33 +125,7 @@ class DeptViewSet(CustomModelViewSet): data = serializer.data return SuccessResponse(data=data) - @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[]) - def dept_lazy_tree(self, request, *args, **kwargs): - parent = self.request.query_params.get('parent') - is_superuser = request.user.is_superuser - if is_superuser: - queryset = Dept.objects.values('id', 'name', 'parent') - else: - role_ids = request.user.role.values_list('id', flat=True) - data_range = RoleMenuButtonPermission.objects.filter(role__in=role_ids).values_list('data_range', flat=True) - user_dept_id = request.user.dept.id - dept_list = [user_dept_id] - data_range_list = list(set(data_range)) - for item in data_range_list: - if item in [0, 2]: - dept_list = [user_dept_id] - elif item == 1: - dept_list = Dept.recursion_all_dept(dept_id=user_dept_id) - elif item == 3: - dept_list = Dept.objects.values_list('id', flat=True) - elif item == 4: - dept_list = request.user.role.values_list('dept', flat=True) - else: - dept_list = [] - queryset = Dept.objects.filter(id__in=dept_list).values('id', 'name', 'parent') - return DetailResponse(data=queryset, msg="获取成功") - - @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[]) + @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated]) def all_dept(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) data = queryset.filter(status=True).order_by('sort').values('name', 'id', 'parent') diff --git a/backend/dvadmin/system/views/menu_button.py b/backend/dvadmin/system/views/menu_button.py index f91839a8d794737af6ea73aef0f8e4b82d124cac..f6cc411f291503204f699a68fa606706f1e5bcd4 100644 --- a/backend/dvadmin/system/views/menu_button.py +++ b/backend/dvadmin/system/views/menu_button.py @@ -10,7 +10,7 @@ from django.db.models import F from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated -from dvadmin.system.models import MenuButton, RoleMenuButtonPermission +from dvadmin.system.models import MenuButton, RoleMenuButtonPermission, Menu from dvadmin.utils.json_response import DetailResponse, SuccessResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.viewset import CustomModelViewSet diff --git a/backend/dvadmin/system/views/role_menu_button_permission.py b/backend/dvadmin/system/views/role_menu_button_permission.py index 604ebe24c0cbfe6cb35f6420a5ad63ef8079de64..d44fab2b547d7e1380003293f521a6a1a3e327c2 100644 --- a/backend/dvadmin/system/views/role_menu_button_permission.py +++ b/backend/dvadmin/system/views/role_menu_button_permission.py @@ -180,51 +180,16 @@ class RoleMenuButtonPermissionViewSet(CustomModelViewSet): :return: menu,btns,columns """ params = request.query_params - role = params.get('role', None) - if role is None: - return ErrorResponse(msg="未获取到角色信息") is_superuser = request.user.is_superuser if is_superuser: queryset = Menu.objects.filter(status=1, is_catalog=True).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) + menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('menu__id', flat=True) queryset = Menu.objects.filter(status=1, is_catalog=True, id__in=menu_list).values('name', 'id').all() serializer = RoleMenuSerializer(queryset, many=True, request=request) data = serializer.data return DetailResponse(data=data) - # data = [] - # 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') - # 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=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) @action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated]) def set_role_premission(self, request, pk): diff --git a/backend/dvadmin/system/views/user.py b/backend/dvadmin/system/views/user.py index c6c002e8817abb75a52e8dea166bbf200f51cfdb..fca29333e793c7d8612107451b84e0a55bb4f2de 100644 --- a/backend/dvadmin/system/views/user.py +++ b/backend/dvadmin/system/views/user.py @@ -10,7 +10,7 @@ from django.db.models import Q from application import dispatch from dvadmin.system.models import Users, Role, Dept from dvadmin.system.views.role import RoleSerializer -from dvadmin.utils.json_response import ErrorResponse, DetailResponse, SuccessResponse +from dvadmin.utils.json_response import ErrorResponse, DetailResponse, SuccessResponse, ListResponse from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.validator import CustomUniqueValidator from dvadmin.utils.viewset import CustomModelViewSet @@ -62,6 +62,19 @@ class UserSerializer(CustomModelSerializer): return serializer.data +class UserListSimpleSerializer(CustomModelSerializer): + """ + 用户管理-序列化器 + """ + dept_name = serializers.CharField(source='dept.name', read_only=True) + + class Meta: + model = Users + read_only_fields = ["id"] + exclude = ["password"] + + + class UserCreateSerializer(CustomModelSerializer): """ 用户新增-序列化器 @@ -119,7 +132,6 @@ class UserUpdateSerializer(CustomModelSerializer): """ 更改激活状态 """ - print(111, value) if value: self.initial_data["login_error_count"] = 0 return value @@ -233,6 +245,7 @@ class UserViewSet(CustomModelViewSet): serializer_class = UserSerializer create_serializer_class = UserCreateSerializer update_serializer_class = UserUpdateSerializer + list_simple_serializer_class = UserListSimpleSerializer filter_fields = ["name", "username", "gender", "is_active", "dept", "user_type"] search_fields = ["username", "name", "dept__name", "role__name"] # 导出 @@ -271,6 +284,13 @@ class UserViewSet(CustomModelViewSet): "role": {"title": "角色", "choices": {"queryset": Role.objects.filter(status=True), "values_name": "name"}}, } + @action(methods=["GET"], detail=False) + def list_simple(self, request): + queryset = self.get_queryset() + serializer_class = self.get_serializer_class() + serializer = serializer_class(queryset, many=True) + return ListResponse(data=serializer.data, msg="获取成功") + @action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated]) def user_info(self, request): """获取当前用户信息""" @@ -407,9 +427,12 @@ class UserViewSet(CustomModelViewSet): queryset = self.filter_queryset(self.get_queryset()) else: queryset = self.filter_queryset(self.get_queryset()) + # print(queryset.values('id','name','dept__id')) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True, request=request) + # print(serializer.data) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(queryset, many=True, request=request) + return SuccessResponse(data=serializer.data, msg="获取成功") diff --git a/backend/dvadmin/utils/json_response.py b/backend/dvadmin/utils/json_response.py index 0d6c363753cd6a4e79668a3658123c7ecc9bd2b8..0a8653e84f9564a7d2f3678b6935a92057dad5a3 100644 --- a/backend/dvadmin/utils/json_response.py +++ b/backend/dvadmin/utils/json_response.py @@ -59,3 +59,20 @@ class ErrorResponse(Response): "msg": msg } super().__init__(std_data, status, template_name, headers, exception, content_type) + + +class ListResponse(Response): + """ + 标准响应成功的返回, SuccessResponse(data)或者SuccessResponse(data=data) + (1)默认code返回2000, 不支持指定其他返回码 + """ + + def __init__(self, data=None, msg='success', status=None, template_name=None, headers=None, exception=False, + content_type=None,page=1,limit=1,total=1): + std_data = { + "code": 2000, + "data": data, + "msg": msg + } + super().__init__(std_data, status, template_name, headers, exception, content_type) + diff --git a/backend/main.py b/backend/main.py index 2408243cdb5fc1c9320565f1d680df38a0c3194e..e0c1aea3cf2efe4e9a0f4d155aa1bae84987e141 100644 --- a/backend/main.py +++ b/backend/main.py @@ -9,5 +9,9 @@ from application.settings import LOGGING if __name__ == '__main__': multiprocessing.freeze_support() - uvicorn.run("application.asgi:application", reload=False, host="0.0.0.0", port=8000, workers=4, + workers = 4 + if os.sys.platform.startswith('win'): + # Windows操作系统 + workers = None + uvicorn.run("application.asgi:application", reload=False, host="0.0.0.0", port=8088, workers=workers, log_config=LOGGING) diff --git a/backend/requirements.txt b/backend/requirements.txt index 6309e7c11080e7c0871da7f9e40d5dd206f9e52e..2e7c6cb6131855497d1be97d0b97f21a4e6eea7c 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ Django==4.2.7 django-comment-migrate==0.1.7 -django-cors-headers==4.3.0 +django-cors-headers==4.4.0 django-filter==23.3 django-ranged-response==0.2.0 djangorestframework==3.14.0 diff --git a/backend/static/logo.icns b/backend/static/logo.icns new file mode 100644 index 0000000000000000000000000000000000000000..99572efcb34019f76a56baed454a7900e63505bf Binary files /dev/null and b/backend/static/logo.icns differ diff --git a/backend/tests/import.xlsx b/backend/tests/import.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3e53acb74ea20ea72e2aaa656f0e6298be9c659e Binary files /dev/null and b/backend/tests/import.xlsx differ diff --git a/backend/tests/import_asset_category.py b/backend/tests/import_asset_category.py new file mode 100644 index 0000000000000000000000000000000000000000..8ebf1f5b5de25ec823eb2dc7bd043729f64e68cb --- /dev/null +++ b/backend/tests/import_asset_category.py @@ -0,0 +1,66 @@ +from conf.env import DATABASE_HOST, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME +import MySQLdb + +import openpyxl + +# 建立连接 +conn = MySQLdb.connect( + host=DATABASE_HOST, # 数据库主机地址 + user=DATABASE_USER, # 数据库用户名 + password=DATABASE_PASSWORD, # 数据库密码 + db=DATABASE_NAME # 数据库名称 +) + +cursor = conn.cursor() + +workbook = openpyxl.load_workbook('./import.xlsx') +table = workbook[workbook.sheetnames[0]] +tableValues = table.values + +def find_id_by_name(name): + cursor.execute('select id from asset_category where name=%s', (name,)) + res = cursor.fetchone() + if not res: + return 0 + return res[0] + + +for index, row in enumerate(tableValues): + if index == 0: + continue + + number = row[0] + name = row[1] + parent_name = row[2] + parent_id = find_id_by_name(parent_name) + sort = row[3] + + param = (number, name, parent_id, sort, 0, 1, ) + + cursor.execute(""" + insert into asset_category(number, name, parent_id, sort, status, creator_id, update_datetime, create_datetime, modifier) + values(%s, %s, %s, %s, %s, %s, now(), now(), 1) + """, param) + + print(row) + +conn.commit() + +# res = find_id_by_name("戴尔台式机") +# print(res) + + + + + + + + + + + + + + + + diff --git "a/backend/\346\225\260\346\215\256\345\272\223\350\277\201\347\247\273.bat" "b/backend/\346\225\260\346\215\256\345\272\223\350\277\201\347\247\273.bat" new file mode 100644 index 0000000000000000000000000000000000000000..3bfa9deec55a857da101e5f10cd2e804d0fb202e --- /dev/null +++ "b/backend/\346\225\260\346\215\256\345\272\223\350\277\201\347\247\273.bat" @@ -0,0 +1,2 @@ +python manage.py makemigrations asset +python manage.py migrate asset \ No newline at end of file diff --git a/docker_env/nginx/my-80.conf b/docker_env/nginx/my-80.conf index e50bdc4697cbe418cc227e1b9e7fe55b334e6322..98d33bd98b9c50792e91a431f43a7ad6996a575d 100644 --- a/docker_env/nginx/my-80.conf +++ b/docker_env/nginx/my-80.conf @@ -7,6 +7,10 @@ server { index index.html index.htm; root /usr/share/nginx/html; try_files $uri $uri/ /index.html; + # 禁止缓存html文件,避免前端页面不及时更新,需要用户手动刷新的情况 + if ($request_uri ~* "^/$|^/index.html|^/index.htm") { + add_header Cache-Control "no-store"; + } } location ~ ^/api/ { diff --git a/docker_env/nginx/my.conf b/docker_env/nginx/my.conf index 178d9793e1db511bc38ee372c104be6ffaacc14e..dd6b3337a19eb93ffdc786635c11995a823a1b06 100644 --- a/docker_env/nginx/my.conf +++ b/docker_env/nginx/my.conf @@ -11,6 +11,10 @@ server { real_ip_header X-Forwarded-For; root /usr/share/nginx/html; index index.html index.php index.htm; + # 禁止缓存html文件,避免前端页面不及时更新,需要用户手动刷新的情况 + if ($request_uri ~* "^/$|^/index.html|^/index.htm") { + add_header Cache-Control "no-store"; + } } location /api/ { diff --git a/sql/asset_category.sql b/sql/asset_category.sql new file mode 100644 index 0000000000000000000000000000000000000000..9e2cc6bb2a1fd7c69fb7e615783d3c6747fb0595 --- /dev/null +++ b/sql/asset_category.sql @@ -0,0 +1,84 @@ +/* + Navicat Premium Data Transfer + + Source Server : 本地 + Source Server Type : MySQL + Source Server Version : 80035 + Source Host : localhost:3306 + Source Schema : asset + + Target Server Type : MySQL + Target Server Version : 80035 + File Encoding : 65001 + + Date: 04/07/2024 09:43:03 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for asset_category +-- ---------------------------- +DROP TABLE IF EXISTS `asset_category`; +CREATE TABLE `asset_category` ( + `id` bigint(0) NOT NULL AUTO_INCREMENT, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `modifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `dept_belong_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `update_datetime` datetime(6) NULL DEFAULT NULL, + `create_datetime` datetime(6) NULL DEFAULT NULL, + `parent_id` bigint(0) NOT NULL, + `number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `name` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `sort` int(0) NULL DEFAULT NULL, + `status` tinyint(1) NOT NULL, + `creator_id` bigint(0) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `number`(`number`) USING BTREE, + INDEX `asset_category_creator_id_f275c846`(`creator_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 155 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of asset_category +-- ---------------------------- +INSERT INTO `asset_category` VALUES (117, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '01', '计算机和网络设备', 1, 0, 1); +INSERT INTO `asset_category` VALUES (118, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0101', '笔记本', 1, 0, 1); +INSERT INTO `asset_category` VALUES (119, NULL, '1', NULL, '2024-07-03 17:45:57.697668', '2024-07-03 09:35:01.000000', 117, '1234', '二手机器', 0, 0, 1); +INSERT INTO `asset_category` VALUES (120, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0102', '台式机', 2, 0, 1); +INSERT INTO `asset_category` VALUES (121, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0103', '一体机', 3, 0, 1); +INSERT INTO `asset_category` VALUES (122, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0104', '主机', 4, 0, 1); +INSERT INTO `asset_category` VALUES (123, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0105', '显示器', 5, 0, 1); +INSERT INTO `asset_category` VALUES (124, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0106', '路由器', 6, 0, 1); +INSERT INTO `asset_category` VALUES (125, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0107', '手机', 7, 0, 1); +INSERT INTO `asset_category` VALUES (126, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0108', '平板', 8, 0, 1); +INSERT INTO `asset_category` VALUES (127, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 117, '0109', '其它网络设备', 9, 0, 1); +INSERT INTO `asset_category` VALUES (128, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '02', '办公设备', 2, 0, 1); +INSERT INTO `asset_category` VALUES (129, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 128, '0201', '打印机', 1, 0, 1); +INSERT INTO `asset_category` VALUES (130, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 128, '0202', '投影仪', 2, 0, 1); +INSERT INTO `asset_category` VALUES (131, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 128, '0203', 'LED显示屏', 3, 0, 1); +INSERT INTO `asset_category` VALUES (132, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 128, '0204', '标签机', 4, 0, 1); +INSERT INTO `asset_category` VALUES (133, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 128, '0205', '会计器具', 5, 0, 1); +INSERT INTO `asset_category` VALUES (134, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 128, '0206', '其它办公设备', 6, 0, 1); +INSERT INTO `asset_category` VALUES (135, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '03', '办公家具', 3, 0, 1); +INSERT INTO `asset_category` VALUES (136, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 135, '0301', '桌子', 1, 0, 1); +INSERT INTO `asset_category` VALUES (137, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 135, '0302', '椅子', 2, 0, 1); +INSERT INTO `asset_category` VALUES (138, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 135, '0303', '沙发', 3, 0, 1); +INSERT INTO `asset_category` VALUES (139, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 135, '0304', '文件柜', 4, 0, 1); +INSERT INTO `asset_category` VALUES (140, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 135, '0305', '保险柜', 5, 0, 1); +INSERT INTO `asset_category` VALUES (141, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 135, '0306', '其它办公家具', 6, 0, 1); +INSERT INTO `asset_category` VALUES (142, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '04', '办公电器', 4, 0, 1); +INSERT INTO `asset_category` VALUES (143, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0401', '空调', 1, 0, 1); +INSERT INTO `asset_category` VALUES (144, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0402', '冰箱冰柜', 2, 0, 1); +INSERT INTO `asset_category` VALUES (145, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0403', '饮水机', 3, 0, 1); +INSERT INTO `asset_category` VALUES (146, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0404', '热水器', 4, 0, 1); +INSERT INTO `asset_category` VALUES (147, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0405', '烹调电器', 5, 0, 1); +INSERT INTO `asset_category` VALUES (148, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0406', '灯具', 6, 0, 1); +INSERT INTO `asset_category` VALUES (149, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 142, '0407', '其它办公电器', 7, 0, 1); +INSERT INTO `asset_category` VALUES (150, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '05', '文艺体育设备', 5, 0, 1); +INSERT INTO `asset_category` VALUES (151, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '06', '仪器仪表', 6, 0, 1); +INSERT INTO `asset_category` VALUES (152, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '07', '车辆', 7, 0, 1); +INSERT INTO `asset_category` VALUES (153, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '08', '图书、档案', 8, 0, 1); +INSERT INTO `asset_category` VALUES (154, NULL, '1', NULL, '2024-07-03 09:35:01.000000', '2024-07-03 09:35:01.000000', 0, '09', '专用设备', 9, 0, 1); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/sql/asset_item.sql b/sql/asset_item.sql new file mode 100644 index 0000000000000000000000000000000000000000..4e77edd5b5660b6f87c5e215d5bf7831f3682abd --- /dev/null +++ b/sql/asset_item.sql @@ -0,0 +1,63 @@ +/* + Navicat Premium Data Transfer + + Source Server : 本地 + Source Server Type : MySQL + Source Server Version : 80035 + Source Host : localhost:3306 + Source Schema : asset + + Target Server Type : MySQL + Target Server Version : 80035 + File Encoding : 65001 + + Date: 04/07/2024 09:42:49 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for asset_item +-- ---------------------------- +DROP TABLE IF EXISTS `asset_item`; +CREATE TABLE `asset_item` ( + `id` bigint(0) NOT NULL AUTO_INCREMENT, + `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `modifier` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `dept_belong_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `update_datetime` datetime(6) NULL DEFAULT NULL, + `create_datetime` datetime(6) NULL DEFAULT NULL, + `number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `name` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `rfid` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `brand` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `model` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `buy_way` varchar(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `buy_time` datetime(6) NULL DEFAULT NULL, + `buy_price` decimal(10, 2) NULL DEFAULT NULL, + `sort` int(0) NOT NULL, + `status` tinyint(1) NOT NULL, + `admin_id` bigint(0) NULL DEFAULT NULL, + `category_id` bigint(0) NULL DEFAULT NULL, + `creator_id` bigint(0) NULL DEFAULT NULL, + `user_id` bigint(0) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `number`(`number`) USING BTREE, + INDEX `asset_item_admin_id_1a6cd17b_fk_dvadmin_system_users_id`(`admin_id`) USING BTREE, + INDEX `asset_item_category_id_1b0617ac_fk_asset_category_id`(`category_id`) USING BTREE, + INDEX `asset_item_user_id_77b9cfa7_fk_dvadmin_system_users_id`(`user_id`) USING BTREE, + INDEX `asset_item_creator_id_9094a9ca`(`creator_id`) USING BTREE, + CONSTRAINT `asset_item_admin_id_1a6cd17b_fk_dvadmin_system_users_id` FOREIGN KEY (`admin_id`) REFERENCES `dvadmin_system_users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `asset_item_category_id_1b0617ac_fk_asset_category_id` FOREIGN KEY (`category_id`) REFERENCES `asset_category` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `asset_item_user_id_77b9cfa7_fk_dvadmin_system_users_id` FOREIGN KEY (`user_id`) REFERENCES `dvadmin_system_users` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of asset_item +-- ---------------------------- +INSERT INTO `asset_item` VALUES (3, NULL, '1', '1', '2024-07-04 09:00:10.679010', '2024-07-04 08:28:01.107255', '4334', '测试资产', NULL, '戴尔', 'X', '采购', '2024-07-25 16:00:00.000000', 100.63, 1, 1, NULL, 135, 1, NULL); +INSERT INTO `asset_item` VALUES (4, NULL, '1', '1', '2024-07-04 09:38:56.559647', '2024-07-04 09:27:42.994190', 'ZC-00020', '实验室专用油性笔', NULL, '', '', '采购', NULL, NULL, 1, 1, NULL, 154, 1, NULL); +INSERT INTO `asset_item` VALUES (5, NULL, '1', '1', '2024-07-04 09:38:18.528205', '2024-07-04 09:38:18.528205', 'ZC-00019', '大功率天线', NULL, '', '', '采购', NULL, NULL, 1, 1, NULL, 154, 1, NULL); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/web/.env.development b/web/.env.development index 1c3ca5db362e1f7935aded834e08f0a46ed640a6..714c5f5c7d4462f0aabb83c6e468f0e31c5a5543 100644 --- a/web/.env.development +++ b/web/.env.development @@ -2,7 +2,7 @@ ENV = 'development' # 本地环境接口地址 -VITE_API_URL = 'http://127.0.0.1:8000' +VITE_API_URL = 'http://127.0.0.1:8088' # 是否启用按钮权限 VITE_PM_ENABLED = true diff --git a/web/.gitignore b/web/.gitignore index aa1baf223b2cc0a3f75b7a01f6792b7c780de240..8cef9186824172b87665ec830e8e4483b0fd0885 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -21,3 +21,5 @@ pnpm-debug.log* *.njsproj *.sln *.sw? +# 构建版本文件,无需上传git +public/version-build diff --git a/web/README.zh.md b/web/README.zh.md new file mode 100644 index 0000000000000000000000000000000000000000..2257d975ea9fd8598387c9af721e837624ca4ae6 --- /dev/null +++ b/web/README.zh.md @@ -0,0 +1,68 @@ +# 资产管理系统 - 前端 + + + +💡 **「关于」** + +这是资产管理系统的前端 + + +## 平台简介 + +💡 资产管理系统是一个物联网平台系统,前后端分离, +后端采用django + django-rest-framework, +前端采用基于 vue3 + CompositionAPI + typescript + vite + element plus + + +#### 🏭 环境支持 + +| Edge | Firefox | Chrome | Safari | +| --------- | ------------ | ----------- | ----------- | +| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 | + +> 不支持 IE11,请升级Edge使用本系统。 + + +## 技术文档 + +- [DVAdmin官网](https://www.django-vue-admin.com) + + +## 源码地址 + +gitee地址(主推):[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦 + + +## 准备工作 +~~~ +Python >= 3.11.0 (最低3.9+版本) +nodejs >= 16.0 +Mysql >= 8.0 (可选,默认数据库sqlite3,支持5.7+,推荐8.0版本) +Redis (可选,最新版) +~~~ + +## 安装♝ + +```bash +# 克隆项目 +git clone https://gitee.com/huge-dream/django-vue3-admin.git + +# 进入项目目录 +cd web + +# 安装依赖 +npm install yarn +yarn install --registry=https://registry.npm.taobao.org + +# 启动服务 +yarn build +# 浏览器访问 http://localhost:8080 +# .env.development 文件中可配置启动端口等参数 +# 构建生产环境 +# yarn run build +``` + +### 访问项目 + +- 访问地址:[http://localhost:8080](http://localhost:8080) (默认为此地址,如有修改请按照配置文件) +- 账号:`superadmin` 密码:`admin123456` diff --git a/web/index.html b/web/index.html index 9515e84b71affad9064130809ddf03581b3cf9cc..3e63bed9b7eada8589c0157f84514e0d92b03f63 100644 --- a/web/index.html +++ b/web/index.html @@ -6,26 +6,27 @@ - - django-vue-admin + + EC-LINK资产管理系统
- + + + + + + + + + + diff --git a/web/package.json b/web/package.json index 49d3d8a211e7e58b56fb97e2baf0697805199239..2a9b2bf80715dc3788f36d8e999daa3601af87d5 100644 --- a/web/package.json +++ b/web/package.json @@ -4,6 +4,7 @@ "description": "是一套全部开源的快速开发平台,毫无保留给个人免费使用、团体授权使用。\n django-vue3-admin 基于RBAC模型的权限控制的一整套基础开发平台,权限粒度达到列级别,前后端分离,后端采用django + django-rest-framework,前端采用基于 vue3 + CompositionAPI + typescript + vite + element plus", "license": "MIT", "scripts": { + "start": "npm run dev", "dev": "vite --force", "build": "vite build", "build:local": "vite build --mode local_prod", diff --git a/web/public/icon.png b/web/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..40987fda5855253bdc12a7b1c9f888b8c9ada88d Binary files /dev/null and b/web/public/icon.png differ diff --git a/web/src/assets/icon.png b/web/src/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..40987fda5855253bdc12a7b1c9f888b8c9ada88d Binary files /dev/null and b/web/src/assets/icon.png differ diff --git a/web/src/assets/loginBg.png b/web/src/assets/loginBg.png new file mode 100644 index 0000000000000000000000000000000000000000..33c5efb625ad10e9c79141ac400edcbf7c08fabd Binary files /dev/null and b/web/src/assets/loginBg.png differ diff --git a/web/src/components/choose/AssetCategoryChoose/api.ts b/web/src/components/choose/AssetCategoryChoose/api.ts new file mode 100644 index 0000000000000000000000000000000000000000..92aebf1e3e5f844eddd6954f1895a12649238cd7 --- /dev/null +++ b/web/src/components/choose/AssetCategoryChoose/api.ts @@ -0,0 +1,42 @@ +import { request } from '/@/utils/service'; +import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud'; + +export const apiPrefix = '/api/asset/category/'; + +export function GetList(query: PageQuery) { + return request({ + url: apiPrefix, + method: 'get', + params: query, + }); +} +export function GetObj(id: InfoReq) { + return request({ + url: apiPrefix + id, + method: 'get', + }); +} + +export function AddObj(obj: AddReq) { + return request({ + url: apiPrefix, + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: EditReq) { + return request({ + url: apiPrefix + obj.id + '/', + method: 'put', + data: obj, + }); +} + +export function DelObj(id: DelReq) { + return request({ + url: apiPrefix + id + '/', + method: 'delete', + data: { id }, + }); +} diff --git a/web/src/components/choose/AssetCategoryChoose/index.vue b/web/src/components/choose/AssetCategoryChoose/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..27491d6c7c65b59b725420965b9077930822ccb1 --- /dev/null +++ b/web/src/components/choose/AssetCategoryChoose/index.vue @@ -0,0 +1,107 @@ + + + + + \ No newline at end of file diff --git a/web/src/layout/routerView/iframes.vue b/web/src/layout/routerView/iframes.vue index 6b8ef27b046bf96a7cd937c2d7d735857df15c90..1a8d7b3d4119a06194f0f501e24a17350be2e114 100644 --- a/web/src/layout/routerView/iframes.vue +++ b/web/src/layout/routerView/iframes.vue @@ -47,7 +47,7 @@ const props = defineProps({ const iframeRef = ref(); const route = useRoute(); -// 处理 list 列表,当打开时,才进行加载 +// 处理 item 列表,当打开时,才进行加载 const setIframeList = computed(() => { return (props.list).filter((v: RouteItem) => v.meta?.isIframeOpen); }); diff --git a/web/src/router/backEnd.ts b/web/src/router/backEnd.ts index 0839c49c1d7a7df2c62680fb0b2e9f1f062ba387..e4e4e46395e75fc87d6f243da56fbb41ca6076e1 100644 --- a/web/src/router/backEnd.ts +++ b/web/src/router/backEnd.ts @@ -52,6 +52,7 @@ export async function initBackEndControlRoutes() { // if (res.data.length <= 0) return Promise.resolve(true); // 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由 const {frameIn,frameOut} = handleMenu(res.data) + dynamicRoutes[0].children = await backEndComponent(frameIn); // 添加动态路由 await setAddRoute(); diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 3f564eb2fc6c389739b2138569914211d774244f..53aa3d962499efb550acd74b1155866c3419fa52 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -13,6 +13,7 @@ import {initBackEndControlRoutes, setRouters} from '/@/router/backEnd'; import {useFrontendMenuStore} from "/@/stores/frontendMenu"; import {useTagsViewRoutes} from "/@/stores/tagsViewRoutes"; import {toRaw} from "vue"; +import {checkVersion} from "/@/utils/upgrade"; /** * 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。 @@ -95,6 +96,8 @@ export function formatTwoStageRoutes(arr: any) { // 路由加载前 router.beforeEach(async (to, from, next) => { + // 检查浏览器本地版本与线上版本是否一致,判断是否需要刷新页面进行更新 + await checkVersion() NProgress.configure({showSpinner: false}); if (to.meta.title) NProgress.start(); const token = Session.get('token'); diff --git a/web/src/router/route.ts b/web/src/router/route.ts index 35df9f69f605fa6b70b244e1c8bbf6770e04007d..39029d353b00f59fd75817c5336d3ed183f45656 100644 --- a/web/src/router/route.ts +++ b/web/src/router/route.ts @@ -85,7 +85,7 @@ export const staticRoutes: Array = [ name: 'login', component: () => import('/@/views/system/login/index.vue'), meta: { - title: '登录', + title: 'EC-LINK资产管理系统 - 登录', }, }, { diff --git a/web/src/utils/loading.ts b/web/src/utils/loading.ts index 5fd020c89f349c651599907f949f50046535c4a0..9dce4bd451213c76817968dd14eb9dc0f09bd224 100644 --- a/web/src/utils/loading.ts +++ b/web/src/utils/loading.ts @@ -1,5 +1,7 @@ import { nextTick } from 'vue'; import '/@/theme/loading.scss'; +import { showUpgrade } from "/@/utils/upgrade"; + /** * 页面全局 Loading @@ -9,6 +11,8 @@ import '/@/theme/loading.scss'; export const NextLoading = { // 创建 loading start: () => { + // 显示升级提示 + showUpgrade() const bodys: Element = document.body; const div = document.createElement('div'); div.setAttribute('class', 'loading-next'); diff --git a/web/src/utils/message.ts b/web/src/utils/message.ts index a022c0ba5b4ff7c06f0804e3632fce85871c0497..4977982d924e6834637ba1640de6afe3d11e7b0f 100644 --- a/web/src/utils/message.ts +++ b/web/src/utils/message.ts @@ -1,4 +1,6 @@ import { ElMessage, ElNotification, MessageOptions } from 'element-plus'; +import { ElMessageBox } from 'element-plus' +import type { Action } from 'element-plus' export function message(message: string, option?: MessageOptions) { ElMessage({ message, ...option }); @@ -31,3 +33,17 @@ export function errorNotification(message: string) { export function infoNotification(message: string) { ElNotification({ message, type: 'info' }); } + +export function alert(message:string, callback: Function | null = null) { + ElMessageBox.alert(message, '提示', { + // if you want to disable its autofocus + // autofocus: false, + confirmButtonText: 'OK', + callback: (action: Action) => { + if (callback) { + callback(action); + } + + }, + }) +} diff --git a/web/src/utils/service.ts b/web/src/utils/service.ts index 0dfe45aa67df7515f662aceb37083018b16a52b6..3775e26612c42553bae8534aacccd3b67b34140b 100644 --- a/web/src/utils/service.ts +++ b/web/src/utils/service.ts @@ -82,7 +82,7 @@ function createService() { ElMessageBox.alert(dataAxios.msg, '提示', { confirmButtonText: 'OK', callback: (action: Action) => { - window.location.reload(); + // window.location.reload(); }, }); errorCreate(`${dataAxios.msg}: ${response.config.url}`); diff --git a/web/src/utils/tools.ts b/web/src/utils/tools.ts index a31df5b9e3c96fe6f7b846ba268c36a1d277a49d..f45084a8f1ac97f8562d1e5a561e7c8d0c31327e 100644 --- a/web/src/utils/tools.ts +++ b/web/src/utils/tools.ts @@ -99,3 +99,55 @@ export function base64ToFile(base64: any, fileName: string) { // 将File文件对象返回给方法的调用者 return file; } + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export function handleTree(data: any[], id: string | null = 'id', parentId: string | null = 'parent_id', children: string | null = 'children') :never[] { + let config = { + id: id, + parentId: parentId, + childrenList: children + }; + + var childrenListMap: {} = {}; + var nodeIds = {}; + var tree: any[] = []; + + for (let d of data) { + let parentId = d[config.parentId]; + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = []; + } + nodeIds[d[config.id]] = d; + childrenListMap[parentId].push(d); + } + + for (let d of data) { + let parentId = d[config.parentId]; + if (nodeIds[parentId] == null) { + tree.push(d); + } + } + + for (let t of tree) { + adaptToChildrenList(t); + } + + function adaptToChildrenList(o:any) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]]; + } + if (o[config.childrenList]) { + for (let c of o[config.childrenList]) { + adaptToChildrenList(c); + } + } + } + + return tree; +} diff --git a/web/src/utils/upgrade.ts b/web/src/utils/upgrade.ts new file mode 100644 index 0000000000000000000000000000000000000000..a571863db809e1fd2110b25400e891117f7995ff --- /dev/null +++ b/web/src/utils/upgrade.ts @@ -0,0 +1,55 @@ +import axios from "axios"; +import * as process from "process"; +import {Local, Session} from '/@/utils/storage'; +import {ElNotification} from "element-plus"; +import fs from "fs"; + +// 是否显示升级提示信息框 +const IS_SHOW_UPGRADE_SESSION_KEY = 'isShowUpgrade'; +const VERSION_KEY = 'DVADMIN3_VERSION' +const VERSION_FILE_NAME = 'version-build' + +export function showUpgrade () { + const isShowUpgrade = Session.get(IS_SHOW_UPGRADE_SESSION_KEY) ?? false + if (isShowUpgrade) { + Session.remove(IS_SHOW_UPGRADE_SESSION_KEY) + ElNotification({ + title: '新版本升级', + message: "检测到系统新版本,正在更新中!不用担心,更新很快的哦!", + type: 'success', + duration: 5000, + }); + } +} + +// 生产环境前端版本校验, +export async function checkVersion(){ + if (process.env.NODE_ENV === 'development') { + // 开发环境无需校验前端版本 + return + } + // 获取线上版本号 t为时间戳,防止缓存 + await axios.get(`/${VERSION_FILE_NAME}?t=${new Date().getTime()}`).then(res => { + const {status, data} = res || {} + if (status === 200) { + // 获取当前版本号 + const localVersion = Local.get(VERSION_KEY) + // 将当前版本号持久缓存至本地 + Local.set(VERSION_KEY, data) + // 当用户本地存在版本号并且和线上版本号不一致时,进行页面刷新操作 + if (localVersion && localVersion !== data) { + // 本地缓存版本号和线上版本号不一致,弹出升级提示框 + // 此处无法直接使用消息框进行提醒,因为 window.location.reload()会导致消息框消失,将在loading页面判断是否需要显示升级提示框 + Session.set(IS_SHOW_UPGRADE_SESSION_KEY, true) + window.location.reload() + + } + } + }) +} + +export function generateVersionFile (){ + // 生成版本文件到public目录下version文件中 + const version = `${process.env.npm_package_version}.${new Date().getTime()}`; + fs.writeFileSync(`public/${VERSION_FILE_NAME}`, version); +} diff --git a/web/src/views/asset/category/api.ts b/web/src/views/asset/category/api.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c3c4fb9d21d642299b75d6c0f6b0a49e6d944ae --- /dev/null +++ b/web/src/views/asset/category/api.ts @@ -0,0 +1,50 @@ +import { request } from '/@/utils/service'; +import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud'; + +export const apiPrefix = '/api/asset/category/'; + +export function GetPermission() { + return request({ + url: apiPrefix + 'field_permission/', + method: 'get', + }); +} + +export function GetList(query: any) { + + return request({ + url: apiPrefix, + method: 'get', + params: query, + }); +} +export function GetObj(id: InfoReq) { + return request({ + url: apiPrefix + id, + method: 'get', + }); +} + +export function AddObj(obj: AddReq) { + return request({ + url: apiPrefix, + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: EditReq) { + return request({ + url: apiPrefix + obj.id + '/', + method: 'put', + data: obj, + }); +} + +export function DelObj(id: DelReq) { + return request({ + url: apiPrefix + id + '/', + method: 'delete', + data: { id }, + }); +} diff --git a/web/src/views/asset/category/edit.vue b/web/src/views/asset/category/edit.vue new file mode 100644 index 0000000000000000000000000000000000000000..a034536db8a89797e3841269db8ca1a75ceaeb02 --- /dev/null +++ b/web/src/views/asset/category/edit.vue @@ -0,0 +1,151 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/asset/category/index.vue b/web/src/views/asset/category/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..315cfe6592f45d41c80caeb5c905e5da5fa42950 --- /dev/null +++ b/web/src/views/asset/category/index.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/web/src/views/asset/category/types.ts b/web/src/views/asset/category/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b0027c5aa1e4c1631e9ef4c691d83878c90686b --- /dev/null +++ b/web/src/views/asset/category/types.ts @@ -0,0 +1,27 @@ +import { CrudOptions, CrudExpose } from '@fast-crud/fast-crud'; + +export interface CreateCrudReturnTypes { + crudOptions: CrudOptions; +} + +export interface CreateCrudOptionsTypes { + crudExpose: CrudExpose; + configPermission: Function; +} + +export interface RoleItemType { + id: string | number; + modifier_name: string; + creator_name: string; + create_datetime: string; + update_datetime: string; + description: string; + modifier: string; + dept_belong_id: string; + name: string; + key: string; + sort: number; + status: boolean; + admin: boolean; + creator: string; +} \ No newline at end of file diff --git a/web/src/views/asset/item/api.ts b/web/src/views/asset/item/api.ts new file mode 100644 index 0000000000000000000000000000000000000000..0cd601d05800ba0823932e59cf177d54239329ec --- /dev/null +++ b/web/src/views/asset/item/api.ts @@ -0,0 +1,66 @@ +import { request } from '/@/utils/service'; +import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud'; + +export const apiPrefix: string = '/api/asset/item/'; +const categoryApiUrl: string = '/api/asset/category/'; +const userApiUrl: string = '/api/system/user/list_simple/'; + +export function GetPermission() { + return request({ + url: apiPrefix + 'field_permission/', + method: 'get', + }); +} + +export function GetList(query: PageQuery) { + return request({ + url: apiPrefix, + method: 'get', + params: query, + }); +} + +export function GetTreeData() { + return request({ + url: categoryApiUrl, + method: 'get' + }); +} + +export function GetUserData() { + return request({ + url: userApiUrl, + method: 'get' + }); +} + +export function GetObj(id: InfoReq) { + return request({ + url: apiPrefix + id, + method: 'get', + }); +} + +export function AddObj(obj: AddReq) { + return request({ + url: apiPrefix, + method: 'post', + data: obj, + }); +} + +export function UpdateObj(obj: EditReq) { + return request({ + url: apiPrefix + obj.id + '/', + method: 'put', + data: obj, + }); +} + +export function DelObj(id: DelReq) { + return request({ + url: apiPrefix + id + '/', + method: 'delete', + data: { id }, + }); +} diff --git a/web/src/views/asset/item/crud.tsx b/web/src/views/asset/item/crud.tsx new file mode 100644 index 0000000000000000000000000000000000000000..92eda852f05cc32bfe3ddfb368f8c08b7600fc95 --- /dev/null +++ b/web/src/views/asset/item/crud.tsx @@ -0,0 +1,226 @@ +import {CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute} from '@fast-crud/fast-crud'; +import * as api from './api'; +import {dictionary} from '/@/utils/dictionary'; +import {successMessage} from '../../../utils/message'; +import {auth} from '/@/utils/authFunction' + +interface CreateCrudOptionsTypes { + output: any; + crudOptions: CrudOptions; +} + +//此处为crudOptions配置 +export default function ({ + crudExpose, + handleDrawerOpen, + }: { + crudExpose: CrudExpose; + handleDrawerOpen: Function; +}): CreateCrudOptionsTypes { + const pageRequest = async (query: any) => { + return await api.GetList(query); + }; + const editRequest = async ({form, row}: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); + }; + const delRequest = async ({row}: DelReq) => { + return await api.DelObj(row.id); + }; + const addRequest = async ({form}: AddReq) => { + return await api.AddObj(form); + }; + + //权限判定 + + // @ts-ignore + // @ts-ignore + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + pagination: { + show: true + }, + actionbar: { + buttons: { + add: { + show: true + } + } + }, + rowHandle: { + //固定右侧 + fixed: 'right', + width: 320, + buttons: { + view: { + show: true, + }, + edit: { + show: true, + }, + remove: { + show: true, + } + }, + }, + form: { + col: {span: 24}, + labelWidth: '100px', + wrapper: { + is: 'el-dialog', + width: '600px', + }, + }, + columns: { + _index: { + title: '序号', + form: {show: false}, + column: { + type: 'index', + align: 'center', + width: '70px', + columnSetDisabled: true, //禁止在列设置中选择 + }, + }, + id: { + title: 'ID', + type: 'text', + column: {show: false}, + search: {show: false}, + form: {show: false}, + }, + number: { + title: '编号', + type: 'text', + search: {show: true}, + column: { + minWidth: 120, + sortable: 'custom', + }, + form: { + rules: [{required: true, message: '必填'}], + component: { + placeholder: '请输入', + }, + }, + }, + name: { + title: '名称', + type: 'text', + search: {show: true}, + column: { + minWidth: 120, + sortable: 'custom', + }, + form: { + rules: [{required: true, message: '名称必填'}], + component: { + placeholder: '请输入', + }, + }, + }, + rfid: { + title: 'RFID', + type: 'text', + search: {show: true}, + column: { + minWidth: 120, + sortable: 'custom', + columnSetDisabled: true, + }, + form: { + component: { + placeholder: '请输入', + }, + } + }, + brand: { + title: '品牌', + search: {show: true}, + type: 'text', + column: { + minWidth: 90, + sortable: 'custom', + }, + form: { + component: { + placeholder: '请输入', + }, + }, + }, + model: { + title: '型号', + search: {show: true}, + type: 'text', + column: { + minWidth: 90, + sortable: 'custom', + }, + form: { + component: { + placeholder: '请输入', + }, + }, + }, + buy_way: { + title: '购置方式', + search: {show: true}, + type: 'text', + column: { + minWidth: 90, + sortable: 'custom', + }, + form: { + component: { + placeholder: '请输入', + }, + }, + }, + buy_time: { + title: '购置时间', + search: {show: true}, + type: 'text', + column: { + minWidth: 90, + sortable: 'custom', + }, + form: { + component: { + placeholder: '请输入', + }, + }, + }, + status: { + title: '状态', + search: {show: true}, + type: 'dict-radio', + column: { + 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); + }); + }; + }), + }, + }, + dict: dict({ + data: dictionary('button_status_bool'), + }), + } + }, + }, + }; +}; diff --git a/web/src/views/asset/item/edit.vue b/web/src/views/asset/item/edit.vue new file mode 100644 index 0000000000000000000000000000000000000000..2a772d1b10fc1cb884b92c63c5e7cf44a71bdd99 --- /dev/null +++ b/web/src/views/asset/item/edit.vue @@ -0,0 +1,221 @@ + + + + + \ No newline at end of file diff --git a/web/src/views/asset/item/index.vue b/web/src/views/asset/item/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..21c0919fde310dd0536df02d7415925b8bd01b47 --- /dev/null +++ b/web/src/views/asset/item/index.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/web/src/views/asset/item/types.ts b/web/src/views/asset/item/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b0027c5aa1e4c1631e9ef4c691d83878c90686b --- /dev/null +++ b/web/src/views/asset/item/types.ts @@ -0,0 +1,27 @@ +import { CrudOptions, CrudExpose } from '@fast-crud/fast-crud'; + +export interface CreateCrudReturnTypes { + crudOptions: CrudOptions; +} + +export interface CreateCrudOptionsTypes { + crudExpose: CrudExpose; + configPermission: Function; +} + +export interface RoleItemType { + id: string | number; + modifier_name: string; + creator_name: string; + create_datetime: string; + update_datetime: string; + description: string; + modifier: string; + dept_belong_id: string; + name: string; + key: string; + sort: number; + status: boolean; + admin: boolean; + creator: string; +} \ No newline at end of file diff --git a/web/src/views/asset/list/index.vue b/web/src/views/asset/list/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..4578b6846fc2536c1a0c4ba9fad9dff186b0fe77 --- /dev/null +++ b/web/src/views/asset/list/index.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/web/src/views/plugins/dvadmin_form_flow/install.js b/web/src/views/plugins/dvadmin_form_flow/install.js deleted file mode 100644 index 02154d40947dfd04d53e9c8099c57c760920cdb5..0000000000000000000000000000000000000000 --- a/web/src/views/plugins/dvadmin_form_flow/install.js +++ /dev/null @@ -1,73 +0,0 @@ -import axios from 'axios' - -import VFormDesigner from '@/components/form-designer/index.vue' -import VFormRender from '@/components/form-render/index.vue' - -import Draggable from '@/../lib/vuedraggable/dist/vuedraggable.umd.js' -import {registerIcon} from '@/utils/el-icons' -import 'virtual:svg-icons-register' -import '@/iconfont/iconfont.css' - -import ContainerWidgets from '@/components/form-designer/form-widget/container-widget/index' -import ContainerItems from '@/components/form-render/container-item/index' - -import { addDirective } from '@/utils/directive' -import { installI18n } from '@/utils/i18n' -import { loadExtension } from '@/extension/extension-loader' - - -VFormDesigner.install = function (app) { - addDirective(app) - installI18n(app) - loadExtension(app) - - app.use(ContainerWidgets) - app.use(ContainerItems) - - registerIcon(app) - app.component('draggable', Draggable) - app.component(VFormDesigner.name, VFormDesigner) -} - -VFormRender.install = function (app) { - installI18n(app) - loadExtension(app) - - app.use(ContainerItems) - - registerIcon(app) - app.component(VFormRender.name, VFormRender) -} - -const components = [ - VFormDesigner, - VFormRender -] - -const install = (app) => { - addDirective(app) - installI18n(app) - loadExtension(app) - - app.use(ContainerWidgets) - app.use(ContainerItems) - - registerIcon(app) - app.component('draggable', Draggable) - - components.forEach(component => { - app.component(component.name, component) - }) - - window.axios = axios -} - -if (typeof window !== 'undefined' && window.Vue) { /* scriptʽʱֵaxios */ - //window.axios = axios -} - -export default { - install, - VFormDesigner, - VFormRender -} diff --git a/web/src/views/plugins/dvadmin_form_flow/src/index.ts b/web/src/views/plugins/dvadmin_form_flow/src/index.ts deleted file mode 100644 index c57cc05a00d2cf0adadfdae3cad6b328ee6bb7de..0000000000000000000000000000000000000000 --- a/web/src/views/plugins/dvadmin_form_flow/src/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -// -import DVAFormDesigner from './components/DVAFormDesigner.vue' - -// 浽һ -const components = [ - DVAFormDesigner -] - -// install -const install = function (Vue) { - - if (install.installed) return - install.installed = true - // бעȫ - components.map(component => { - Vue.component(component.name, component) //component.name ˴ʹõvueļе name - }) -} - -if (typeof window !== 'undefined' && window.Vue) { - install(window.Vue) -} - -export default { - // Ķ߱һ install - install, - // б - ...components -} diff --git a/web/src/views/system/dept/components/DeptUserCom/api.ts b/web/src/views/system/dept/components/DeptUserCom/api.ts index 28602c720f22c51b0c7d4f3405fecb4c51d316cc..b395c39d5634d52c7c11d5c8473bc6cc589a0e49 100644 --- a/web/src/views/system/dept/components/DeptUserCom/api.ts +++ b/web/src/views/system/dept/components/DeptUserCom/api.ts @@ -5,13 +5,13 @@ type GetListType = PageQuery & { show_all: string }; export const apiPrefix = '/api/system/user/'; -export function GetDept(query: PageQuery) { - return request({ - url: '/api/system/dept/dept_lazy_tree/', - method: 'get', - params: query, - }); -} +// export function GetDept(query: PageQuery) { +// return request({ +// url: '/api/system/dept/dept_all/', +// method: 'get', +// params: query, +// }); +// } export function GetList(query: GetListType) { return request({ diff --git a/web/src/views/system/dept/components/DeptUserCom/crud.tsx b/web/src/views/system/dept/components/DeptUserCom/crud.tsx index 8bb2b3cfb0e5fb496ab709bebcb180d884f18ba0..bcee5b09a5fad3f46df39fb01929ae106d585dcc 100644 --- a/web/src/views/system/dept/components/DeptUserCom/crud.tsx +++ b/web/src/views/system/dept/components/DeptUserCom/crud.tsx @@ -220,7 +220,10 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp label: 'name', }), column: { - minWidth: 150, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + return row.dept_name_all + } }, form: { rules: [ @@ -259,7 +262,11 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp label: 'name', }), column: { - minWidth: 100, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + const values = row.role_info.map((item:any) => item.name); + return values.join(',') + } }, form: { rules: [ @@ -382,6 +389,10 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp form: { show: false, }, + column:{ + width:150, + showOverflowTooltip: true, + } }, }, }, diff --git a/web/src/views/system/login/index.vue b/web/src/views/system/login/index.vue index dbaf0611258d11b12efb13f85265818c7bb571f5..32cdacc72fb3aa0ffa6b2e9ccdb1a73ebc651a43 100644 --- a/web/src/views/system/login/index.vue +++ b/web/src/views/system/login/index.vue @@ -1,5 +1,5 @@ @@ -70,8 +69,8 @@ import { defineAsyncComponent, onMounted, reactive, computed } from 'vue'; import { storeToRefs } from 'pinia'; import { useThemeConfig } from '/@/stores/themeConfig'; import { NextLoading } from '/@/utils/loading'; -import logoMini from '/@/assets/logo-mini.svg'; -import loginMain from '/@/assets/login-main.svg'; +import logoMini from '/@/assets/icon.png'; +import loginMain from '/@/assets/loginBg.png'; import loginBg from '/@/assets/login-bg.svg'; import { SystemConfigStore } from '/@/stores/systemConfig' import { getBaseURL } from "/@/utils/baseUrl"; diff --git a/web/src/views/system/messageCenter/crud.tsx b/web/src/views/system/messageCenter/crud.tsx index 57331940ce04fe443ca26de09a27cd37239a2de9..4e819e37f94de929b957b1c4c62a16c3cde02ce8 100644 --- a/web/src/views/system/messageCenter/crud.tsx +++ b/web/src/views/system/messageCenter/crud.tsx @@ -333,43 +333,7 @@ export const createCrudOptions = function ({ }, }, }, - content: { - title: '内容', - column: { - width: 300, - show: false, - }, - type: ['editor-wang5', 'colspan'], - form: { - rules: [ - // 表单校验规则 - { - required: true, - message: '必填项', - }, - ], - component: { - disabled: true, - id: '1', // 当同一个页面有多个editor时,需要配置不同的id - editorConfig: { - // 是否只读 - readOnly: compute((context) => { - const { mode } = context; - if (mode === 'add') { - return false; - } - return true; - }), - }, - uploader: { - type: 'form', - buildUrl(res: any) { - return res.url; - }, - }, - }, - }, - }, + }, }, }; diff --git a/web/src/views/system/role/api.ts b/web/src/views/system/role/api.ts index 2212fbe4f24d8f06260849f5d7d162e690eb5d42..36cb5c3978bbc4e6ee8a45e0cb0c9f75a6f8bc51 100644 --- a/web/src/views/system/role/api.ts +++ b/web/src/views/system/role/api.ts @@ -33,6 +33,7 @@ export function AddObj(obj: AddReq) { } export function UpdateObj(obj: EditReq) { + debugger return request({ url: apiPrefix + obj.id + '/', method: 'put', diff --git a/web/src/views/system/role/components/PermissionComNew/api.ts b/web/src/views/system/role/components/PermissionComNew/api.ts index be37ef5bc7522096cc6a1909c2276ac22c334c71..eee6d2a16de84d1fcb0db5f830c77eaff283f943 100644 --- a/web/src/views/system/role/components/PermissionComNew/api.ts +++ b/web/src/views/system/role/components/PermissionComNew/api.ts @@ -26,7 +26,15 @@ export function setRolePremission(roleId:any,data:object) { }) } -export function getDataPermissionRange() { +export function getDataPermissionRange(query:object) { + return request({ + url: '/api/system/role_menu_button_permission/data_scope/', + method: 'get', + params:query + }) +} + +export function getDataPermissionRangeAll() { return request({ url: '/api/system/role_menu_button_permission/data_scope/', method: 'get', diff --git a/web/src/views/system/role/components/PermissionComNew/index.vue b/web/src/views/system/role/components/PermissionComNew/index.vue index 058f5fc26cd4333b626896f3807b792eb3f030e5..7770b05bc10163511cf773da941bf21fe143711a 100644 --- a/web/src/views/system/role/components/PermissionComNew/index.vue +++ b/web/src/views/system/role/components/PermissionComNew/index.vue @@ -124,7 +124,7 @@ watch( (val) => { drawerVisible.value = val; getMenuBtnPermission() - fetchData() + } ); const handleDrawerClose = () => { @@ -159,9 +159,9 @@ const getMenuBtnPermission = async () => { menuData.value = resMenu.data } -const fetchData = async () => { +const fetchData = async (btnId) => { try { - const resRange = await getDataPermissionRange(); + const resRange = await getDataPermissionRange({menu_button:btnId}); if (resRange?.code === 2000) { dataPermissionRange.value = resRange.data; } @@ -183,6 +183,7 @@ const handleSettingClick = (record: MenusType, btnId: number) => { menuCurrent.value = record; menuBtnCurrent.value = btnId; dialogVisible.value = true; + fetchData(btnId) }; const handleColumnChange = (val: boolean, record: MenusType, btnType: string) => { diff --git a/web/src/views/system/user/api.ts b/web/src/views/system/user/api.ts index 78a15ec98b73aed16403b065a1a9544257b2c918..02a2ac6e9bc7baef18ed80b3e73c4f0bbf22608e 100644 --- a/web/src/views/system/user/api.ts +++ b/web/src/views/system/user/api.ts @@ -5,7 +5,7 @@ export const apiPrefix = '/api/system/user/'; export function GetDept(query: PageQuery) { return request({ - url: "/api/system/dept/dept_lazy_tree/", + url: "/api/system/dept/all_dept/", method: 'get', params: query, }); diff --git a/web/src/views/system/user/crud.tsx b/web/src/views/system/user/crud.tsx index 72815251fc60a5f639118eaa1e472bcab3627215..dd24c406010a65c5b56702ed869b125739e5ce62 100644 --- a/web/src/views/system/user/crud.tsx +++ b/web/src/views/system/user/crud.tsx @@ -218,7 +218,10 @@ export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps) label: 'name' }), column: { - minWidth: 150, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + return row.dept_name_all + } }, form: { rules: [ @@ -253,7 +256,11 @@ export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps) label: 'name', }), column: { - minWidth: 100, //最小列宽 + minWidth: 200, //最小列宽 + formatter({value,row,index}){ + const values = row.role_info.map((item:any) => item.name); + return values.join(',') + } }, form: { rules: [ diff --git a/web/src/views/system/user/index.vue b/web/src/views/system/user/index.vue index 4ea5033d91b0322d68b346654d5270ae15a75edd..71085d1ba30005bbe2e108fa4b125bfe1faf2366 100644 --- a/web/src/views/system/user/index.vue +++ b/web/src/views/system/user/index.vue @@ -98,7 +98,6 @@ const getData = () => { const result = XEUtils.toArrayTree(responseData, { parentKey: 'parent', children: 'children', - strict: true, }); data.value = result; diff --git a/web/vite.config.ts b/web/vite.config.ts index c86cfc48afe8be9b0a9800e4848d13fefede34b4..bc0785da0d4a50b9543e3948e26b1c0e6016c490 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -3,6 +3,7 @@ import { resolve } from 'path'; import { defineConfig, loadEnv, ConfigEnv } from 'vite'; import vueSetupExtend from 'vite-plugin-vue-setup-extend'; import vueJsx from '@vitejs/plugin-vue-jsx' +import { generateVersionFile } from "/@/utils/upgrade"; const pathResolve = (dir: string) => { return resolve(__dirname, '.', dir); @@ -17,6 +18,8 @@ const alias: Record = { const viteConfig = defineConfig((mode: ConfigEnv) => { const env = loadEnv(mode.mode, process.cwd()); + // 当Vite构建时,生成版本文件 + generateVersionFile() return { plugins: [vue(), vueJsx(), vueSetupExtend()], root: process.cwd(), @@ -26,7 +29,7 @@ const viteConfig = defineConfig((mode: ConfigEnv) => { include: ['element-plus/es/locale/lang/zh-cn', 'element-plus/es/locale/lang/en', 'element-plus/es/locale/lang/zh-tw'], }, server: { - host: '0.0.0.0', + host: '127.0.0.1', port: env.VITE_PORT as unknown as number, open: true, hmr: true, @@ -37,6 +40,10 @@ const viteConfig = defineConfig((mode: ConfigEnv) => { changeOrigin: true, rewrite: (path) => path.replace(/^\/gitee/, ''), }, + '/media': { + target: 'http://127.0.0.1:8088', + changeOrigin: true + }, }, }, build: {