flower_bs 4 месяцев назад
Родитель
Сommit
654b8e9eae
95 измененных файлов с 2745 добавлено и 2256 удалено
  1. 64 0
      .vscode/launch.json
  2. 28 0
      .vscode/task.json
  3. 5 0
      backend_start windows.ps1
  4. 0 4
      company/admin.py
  5. 0 4
      company/apps.py
  6. 0 18
      company/filter.py
  7. 0 19
      company/models.py
  8. 0 54
      company/serializers.py
  9. 0 0
      company/tests.py
  10. 0 12
      company/urls.py
  11. 0 114
      company/views.py
  12. 0 0
      customer/__init__.py
  13. 0 4
      customer/admin.py
  14. 0 5
      customer/apps.py
  15. 0 50
      customer/files.py
  16. 0 20
      customer/filter.py
  17. 0 0
      customer/migrations/__init__.py
  18. 0 20
      customer/models.py
  19. 0 74
      customer/serializers.py
  20. 0 0
      customer/tests.py
  21. 0 13
      customer/urls.py
  22. 0 169
      customer/views.py
  23. 0 0
      cyclecount/__init__.py
  24. 0 4
      cyclecount/admin.py
  25. 0 4
      cyclecount/apps.py
  26. 0 52
      cyclecount/files.py
  27. 0 38
      cyclecount/filter.py
  28. 0 0
      cyclecount/migrations/__init__.py
  29. 0 57
      cyclecount/models.py
  30. 0 9
      cyclecount/page.py
  31. 0 120
      cyclecount/serializers.py
  32. 0 0
      cyclecount/tests.py
  33. 0 15
      cyclecount/urls.py
  34. 0 532
      cyclecount/views.py
  35. 1 0
      delete_database.ps1
  36. 22 30
      greaterwms/settings.py
  37. 3 30
      greaterwms/urls.py
  38. 1 0
      requirements.txt
  39. 5 1
      templates/jsconfig.json
  40. 116 0
      templates/mock copy/article.js
  41. 60 0
      templates/mock copy/index.js
  42. 81 0
      templates/mock copy/mock-server.js
  43. 51 0
      templates/mock copy/remote-search.js
  44. 98 0
      templates/mock copy/role/index.js
  45. 530 0
      templates/mock copy/role/routes.js
  46. 84 0
      templates/mock copy/user.js
  47. 48 0
      templates/mock copy/utils.js
  48. 1 0
      templates/package.json
  49. BIN
      templates/public/favicon.ico
  50. 0 0
      templates/src/boot/.gitkeep
  51. 373 0
      templates/src/boot/axios_request.js
  52. 2 0
      templates/src/boot/bus.js
  53. 25 0
      templates/src/boot/i18n.js
  54. 7 0
      templates/src/boot/notify_default.js
  55. 1 0
      templates/src/components/LottieWeb/Login.json
  56. 51 0
      templates/src/components/LottieWeb/lottie-web-cimo.vue
  57. 1 0
      templates/src/components/LottieWeb/meeting.json
  58. 1 0
      templates/src/components/LottieWeb/warehouse.json
  59. 1 0
      templates/src/components/LottieWeb/welcome.json
  60. 4 9
      templates/src/layout/components/Navbar.vue
  61. 4 4
      templates/src/layout/components/Settings/index.vue
  62. 11 6
      templates/src/layout/components/Sidebar/Logo.vue
  63. 4 4
      templates/src/layout/components/TagsView/index.vue
  64. 7 7
      templates/src/main.js
  65. 25 17
      templates/src/permission.js
  66. 32 92
      templates/src/router/index.js
  67. 5 5
      templates/src/router/modules/charts.js
  68. 16 16
      templates/src/router/modules/components.js
  69. 10 10
      templates/src/router/modules/nested.js
  70. 5 5
      templates/src/router/modules/table.js
  71. 1 1
      templates/src/settings.js
  72. 1 0
      templates/src/store/getters.js
  73. 2 0
      templates/src/store/index.js
  74. 83 54
      templates/src/store/modules/user.js
  75. 1 0
      templates/src/utils/auth.js
  76. 1 1
      templates/src/utils/permission.js
  77. 3 3
      templates/src/utils/request.js
  78. 1 0
      templates/src/views/dashboard/index.vue
  79. 0 57
      templates/src/views/documentation/index.vue
  80. 321 64
      templates/src/views/login/index.vue
  81. 32 0
      throttle/migrations/0001_initial.py
  82. 2 3
      userlogin/views.py
  83. 4 1
      userprofile/admin.py
  84. 0 0
      userprofile/files.py
  85. 54 0
      userprofile/filter.py
  86. 94 0
      userprofile/migrations/0001_initial.py
  87. 27 0
      userprofile/migrations/0002_alter_academicprofile_user_and_more.py
  88. 17 0
      userprofile/migrations/0003_alter_academicprofile_table.py
  89. 180 17
      userprofile/models.py
  90. 18 0
      userprofile/serializers.py
  91. 10 0
      userprofile/urls.py
  92. 90 0
      userprofile/views.py
  93. 17 405
      userregister/views.py
  94. 1 1
      utils/fbmsg.py
  95. 2 2
      utils/websocket.py

+ 64 - 0
.vscode/launch.json

@@ -0,0 +1,64 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        // 迁移命令配置
+        {
+            "name": "Django: Make Migrations",
+            "type": "python",
+            "request": "launch",
+            "program": "${workspaceFolder}/manage.py",
+            "args": ["makemigrations"],
+            "console": "integratedTerminal"
+        },
+        {
+            "name": "Django: Migrate",
+            "type": "python",
+            "request": "launch",
+            "program": "${workspaceFolder}/manage.py",
+            "args": ["migrate"],
+            "console": "integratedTerminal"
+        },
+        
+        // 主服务器配置(带端口绑定)
+        {
+            "name": "Django: Run Server (0.0.0.0:8008)",
+            "type": "python",
+            "request": "launch",
+            "program": "${workspaceFolder}/manage.py",
+            "args": [
+                "runserver", 
+                "--noreload",      // 防止自动重载干扰调试
+                "0.0.0.0:8008"    // 同时设置 IP 和端口
+            ],
+            "django": true,
+            "console": "integratedTerminal"
+        },
+        
+        // 带自动迁移的服务器配置(推荐)
+        {
+            "name": "Django: Auto Migrate & Run (0.0.0.0:8008)",
+            "type": "python",
+            "request": "launch",
+            "program": "${workspaceFolder}/manage.py",
+            "args": [
+                "runserver", 
+                "--noreload",
+                "0.0.0.0:8008"
+            ],
+            "django": true,
+            "preLaunchTask": "django-auto-migrate",
+            "console": "integratedTerminal"
+        }
+    ],
+    "compounds": [
+        // 一键执行迁移+服务器
+        {
+            "name": "Django: Full Setup (0.0.0.0:8008)",
+            "configurations": [
+                "Django: Make Migrations",
+                "Django: Migrate",
+                "Django: Run Server (0.0.0.0:8008)"
+            ]
+        }
+    ]
+}

+ 28 - 0
.vscode/task.json

@@ -0,0 +1,28 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "django-auto-migrate",
+            "type": "shell",
+            "command": "${command:python.interpreterPath}",
+            "args": [
+                "${workspaceFolder}/manage.py",
+                "makemigrations",
+                "--no-input",     
+                "&&",
+                "${command:python.interpreterPath}",
+                "${workspaceFolder}/manage.py",
+                "migrate",
+                "--no-input"
+            ],
+            "options": {
+                "cwd": "${workspaceFolder}"
+            },
+            "problemMatcher": [],
+            "presentation": {
+                "reveal": "always",
+                "panel": "dedicated"
+            }
+        }
+    ]
+}

+ 5 - 0
backend_start windows.ps1

@@ -0,0 +1,5 @@
+#!/bin/bash
+python manage.py makemigrations
+python manage.py migrate
+
+daphne -b 0.0.0.0 -p 8008 greaterwms.asgi:application

+ 0 - 4
company/admin.py

@@ -1,4 +0,0 @@
-from django.contrib import admin
-from .models import ListModel
-
-admin.site.register(ListModel)

+ 0 - 4
company/apps.py

@@ -1,4 +0,0 @@
-from django.apps import AppConfig
-
-class CompanyConfig(AppConfig):
-    name = 'company'

+ 0 - 18
company/filter.py

@@ -1,18 +0,0 @@
-from django_filters import FilterSet
-from .models import ListModel
-
-class Filter(FilterSet):
-    class Meta:
-        model = ListModel
-        fields = {
-            "id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
-            "company_name": ['exact', 'iexact', 'contains', 'icontains'],
-            "company_city": ['exact', 'iexact', 'contains', 'icontains'],
-            "company_address": ['exact', 'iexact', 'contains', 'icontains'],
-            "company_contact": ['exact', 'iexact', 'contains', 'icontains'],
-            "company_manager": ['exact', 'iexact', 'contains', 'icontains'],
-            "creater": ['exact', 'iexact', 'contains', 'icontains'],
-            "is_delete": ['exact', 'iexact'],
-            "create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
-            "update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
-        }

+ 0 - 19
company/models.py

@@ -1,19 +0,0 @@
-from django.db import models
-
-class ListModel(models.Model):
-    company_name = models.CharField(max_length=255, verbose_name="Company Name")
-    company_city = models.CharField(max_length=255, verbose_name="Company City")
-    company_address = models.CharField(max_length=255, verbose_name="Company Address")
-    company_contact = models.CharField(max_length=255, verbose_name="Company Contact")
-    company_manager = models.CharField(max_length=255, verbose_name="Company Manager")
-    creater = models.CharField(max_length=255, verbose_name="Who Created")
-    openid = models.CharField(max_length=255, verbose_name="Openid")
-    is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
-    update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
-
-    class Meta:
-        db_table = 'company'
-        verbose_name = 'Company'
-        verbose_name_plural = "Company"
-        ordering = ['company_name']

+ 0 - 54
company/serializers.py

@@ -1,54 +0,0 @@
-from rest_framework import serializers
-from .models import ListModel
-from utils import datasolve
-
-class CompanyGetSerializer(serializers.ModelSerializer):
-    company_name = serializers.CharField(read_only=True, required=False)
-    company_city = serializers.CharField(read_only=True, required=False)
-    company_address = serializers.CharField(read_only=True, required=False)
-    company_contact = serializers.CharField(read_only=True, required=False)
-    company_manager = serializers.CharField(read_only=True, required=False)
-    creater = serializers.CharField(read_only=True, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    class Meta:
-        model = ListModel
-        exclude = ['openid', 'is_delete', ]
-        read_only_fields = ['id']
-
-class CompanyPostSerializer(serializers.ModelSerializer):
-    openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
-    company_name = serializers.CharField(read_only=False,  required=True, validators=[datasolve.data_validate])
-    company_city = serializers.CharField(read_only=False,  required=True, validators=[datasolve.data_validate])
-    company_address = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    company_contact = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    company_manager = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = ListModel
-        exclude = ['is_delete', ]
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class CompanyUpdateSerializer(serializers.ModelSerializer):
-    company_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    company_city = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    company_address = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    company_contact = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    company_manager = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = ListModel
-        exclude = ['openid', 'is_delete', ]
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class CompanyPartialUpdateSerializer(serializers.ModelSerializer):
-    company_name = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    company_city = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    company_address = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    company_contact = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    company_manager = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    creater = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    class Meta:
-        model = ListModel
-        exclude = ['openid', 'is_delete', ]
-        read_only_fields = ['id', 'create_time', 'update_time', ]

+ 0 - 0
company/tests.py


+ 0 - 12
company/urls.py

@@ -1,12 +0,0 @@
-from django.urls import path, re_path
-from . import views
-
-urlpatterns = [
-path(r'', views.APIViewSet.as_view({"get": "list", "post": "create"}), name="company"),
-re_path(r'^(?P<pk>\d+)/$', views.APIViewSet.as_view({
-    'get': 'retrieve',
-    'put': 'update',
-    'patch': 'partial_update',
-    'delete': 'destroy'
-}), name="company_1")
-]

+ 0 - 114
company/views.py

@@ -1,114 +0,0 @@
-from rest_framework import viewsets
-from .models import ListModel
-from . import serializers
-from utils.page import MyPageNumberPagination
-from rest_framework.filters import OrderingFilter
-from django_filters.rest_framework import DjangoFilterBackend
-from rest_framework.response import Response
-from .filter import Filter
-from rest_framework.exceptions import APIException
-
-class APIViewSet(viewsets.ModelViewSet):
-    """
-        retrieve:
-            Response a data list(get)
-
-        list:
-            Response a data list(all)
-
-        create:
-            Create a data line(post)
-
-        delete:
-            Delete a data line(delete)
-
-        partial_update:
-            Partial_update a data(patch:partial_update)
-
-        update:
-            Update a data(put:update)
-    """
-    pagination_class = MyPageNumberPagination
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            if id is None:
-                return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
-            else:
-                return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
-        else:
-            return ListModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list', 'retrieve', 'destroy']:
-            return serializers.CompanyGetSerializer
-        elif self.action in ['create']:
-            return serializers.CompanyPostSerializer
-        elif self.action in ['update']:
-            return serializers.CompanyUpdateSerializer
-        elif self.action in ['partial_update']:
-            return serializers.CompanyPartialUpdateSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def create(self, request, *args, **kwargs):
-        data = self.request.data
-        data['openid'] = self.request.auth.openid
-        if ListModel.objects.filter(openid=data['openid'], company_name=data['company_name'], is_delete=False).exists():
-            raise APIException({"detail": "Data exists"})
-        else:
-            if ListModel.objects.filter(openid=data['openid'], is_delete=False).count() >= 1:
-                raise APIException({"detail": "You Just Can Create 1 Company"})
-            else:
-                serializer = self.get_serializer(data=data)
-                serializer.is_valid(raise_exception=True)
-                serializer.save()
-                headers = self.get_success_headers(serializer.data)
-                return Response(serializer.data, status=200, headers=headers)
-
-    def update(self, request, pk):
-        qs = self.get_object()
-        if qs.openid != self.request.auth.openid:
-            raise APIException({"detail": "Cannot update data which not yours"})
-        else:
-            data = self.request.data
-            serializer = self.get_serializer(qs, data=data)
-            serializer.is_valid(raise_exception=True)
-            serializer.save()
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-
-    def partial_update(self, request, pk):
-        qs = self.get_object()
-        if qs.openid != self.request.auth.openid:
-            raise APIException({"detail": "Cannot partial_update data which not yours"})
-        else:
-            data = self.request.data
-            serializer = self.get_serializer(qs, data=data, partial=True)
-            serializer.is_valid(raise_exception=True)
-            serializer.save()
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-
-    def destroy(self, request, pk):
-        qs = self.get_object()
-        if qs.openid != self.request.auth.openid:
-            raise APIException({"detail": "Cannot delete data which not yours"})
-        else:
-            qs.is_delete = True
-            qs.save()
-            serializer = self.get_serializer(qs, many=False)
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-

+ 0 - 0
customer/__init__.py


+ 0 - 4
customer/admin.py

@@ -1,4 +0,0 @@
-from django.contrib import admin
-from .models import ListModel
-
-admin.site.register(ListModel)

+ 0 - 5
customer/apps.py

@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class CustomerConfig(AppConfig):
-    name = 'customer'

+ 0 - 50
customer/files.py

@@ -1,50 +0,0 @@
-from rest_framework_csv.renderers import CSVStreamingRenderer
-
-
-def file_headers():
-    return [
-        'customer_name',
-        'customer_city',
-        'customer_address',
-        'customer_contact',
-        'customer_manager',
-        'customer_level',
-        'creater',
-        'create_time',
-        'update_time'
-    ]
-
-def cn_data_header():
-    return dict([
-        ('customer_name', u'客户名称'),
-        ('customer_city', u'客户城市'),
-        ('customer_address', u'详细地址'),
-        ('customer_contact', u'联系电话'),
-        ('customer_manager', u'负责人'),
-        ('customer_level', u'客户等级'),
-        ('creater', u'创建人'),
-        ('create_time', u'创建时间'),
-        ('update_time', u'更新时间'),
-    ])
-
-def en_data_header():
-    return dict([
-        ('customer_name', u'Customer Name'),
-        ('customer_city', u'Customer City'),
-        ('customer_address', u'Customer Address'),
-        ('customer_contact', u'Customer Contact'),
-        ('customer_manager', u'Customer Manager'),
-        ('customer_level', u'Customer Level'),
-        ('creater', u'Creater'),
-        ('create_time', u'Create Time'),
-        ('update_time', u'Update Time'),
-    ])
-
-
-class FileRenderCN(CSVStreamingRenderer):
-    header = file_headers()
-    labels = cn_data_header()
-
-class FileRenderEN(CSVStreamingRenderer):
-    header = file_headers()
-    labels = en_data_header()

+ 0 - 20
customer/filter.py

@@ -1,20 +0,0 @@
-from django_filters import FilterSet
-from .models import ListModel
-
-class Filter(FilterSet):
-    class Meta:
-        model = ListModel
-        fields = {
-            "id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
-            "customer_name": ['exact', 'iexact', 'contains', 'icontains'],
-            "customer_city": ['exact', 'iexact', 'contains', 'icontains'],
-            "customer_address": ['exact', 'iexact', 'contains', 'icontains'],
-            "customer_contact": ['exact', 'iexact', 'contains', 'icontains'],
-            "customer_manager": ['exact', 'iexact', 'contains', 'icontains'],
-            "customer_level": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
-            "creater": ['exact', 'iexact', 'contains', 'icontains'],
-            "is_delete": ['exact', 'iexact'],
-            "create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
-            "update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
-        }
-

+ 0 - 0
customer/migrations/__init__.py


+ 0 - 20
customer/models.py

@@ -1,20 +0,0 @@
-from django.db import models
-
-class ListModel(models.Model):
-    customer_name = models.CharField(max_length=255, verbose_name="Customer Name")
-    customer_city = models.CharField(max_length=255, verbose_name="Customer City")
-    customer_address = models.CharField(max_length=255, verbose_name="Customer Address")
-    customer_contact = models.CharField(max_length=255, verbose_name="Customer Contact")
-    customer_manager = models.CharField(max_length=255, verbose_name="Customer Manager")
-    customer_level = models.BigIntegerField(default=1, verbose_name="Customer Level")
-    creater = models.CharField(max_length=255, verbose_name="Who Created")
-    openid = models.CharField(max_length=255, verbose_name="Openid")
-    is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
-    update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
-
-    class Meta:
-        db_table = 'customer'
-        verbose_name = 'Customer'
-        verbose_name_plural = "Customer"
-        ordering = ['customer_name']

+ 0 - 74
customer/serializers.py

@@ -1,74 +0,0 @@
-from rest_framework import serializers
-from .models import ListModel
-from utils import datasolve
-
-class CustomerGetSerializer(serializers.ModelSerializer):
-    customer_name = serializers.CharField(read_only=True, required=False)
-    customer_city = serializers.CharField(read_only=True, required=False)
-    customer_address = serializers.CharField(read_only=True, required=False)
-    customer_contact = serializers.CharField(read_only=True, required=False)
-    customer_manager = serializers.CharField(read_only=True, required=False)
-    customer_level = serializers.IntegerField(read_only=True, required=False)
-    creater = serializers.CharField(read_only=True, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    class Meta:
-        model = ListModel
-        exclude = ['openid', 'is_delete', ]
-        read_only_fields = ['id', ]
-
-class CustomerPostSerializer(serializers.ModelSerializer):
-    openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
-    customer_name = serializers.CharField(read_only=False,  required=True, validators=[datasolve.data_validate])
-    customer_city = serializers.CharField(read_only=False,  required=True, validators=[datasolve.data_validate])
-    customer_address = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_contact = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_manager = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_level = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.data_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = ListModel
-        exclude = ['is_delete', ]
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class CustomerUpdateSerializer(serializers.ModelSerializer):
-    customer_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_city = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_address = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_contact = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_manager = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    customer_level = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.data_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = ListModel
-        exclude = ['openid', 'is_delete', ]
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class CustomerPartialUpdateSerializer(serializers.ModelSerializer):
-    customer_name = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    customer_city = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    customer_address = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    customer_contact = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    customer_manager = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    customer_level = serializers.IntegerField(read_only=False, required=False, validators=[datasolve.data_validate])
-    creater = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
-    class Meta:
-        model = ListModel
-        exclude = ['openid', 'is_delete', ]
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class FileRenderSerializer(serializers.ModelSerializer):
-    customer_name = serializers.CharField(read_only=False, required=False)
-    customer_city = serializers.CharField(read_only=False, required=False)
-    customer_address = serializers.CharField(read_only=False, required=False)
-    customer_contact = serializers.CharField(read_only=False, required=False)
-    customer_manager = serializers.CharField(read_only=False, required=False)
-    customer_level = serializers.IntegerField(read_only=False, required=False)
-    creater = serializers.CharField(read_only=False, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-
-    class Meta:
-        model = ListModel
-        ref_name = 'CustomereFileRenderSerializer'
-        exclude = ['openid', 'is_delete', ]

+ 0 - 0
customer/tests.py


+ 0 - 13
customer/urls.py

@@ -1,13 +0,0 @@
-from django.urls import path, re_path
-from . import views
-
-urlpatterns = [
-path(r'', views.APIViewSet.as_view({"get": "list", "post": "create"}), name="customer"),
-path(r'file/', views.FileDownloadView.as_view({"get": "list"}), name="customerfiledownload"),
-re_path(r'^(?P<pk>\d+)/$', views.APIViewSet.as_view({
-    'get': 'retrieve',
-    'put': 'update',
-    'patch': 'partial_update',
-    'delete': 'destroy'
-}), name="customer_1")
-]

+ 0 - 169
customer/views.py

@@ -1,169 +0,0 @@
-from django.http import StreamingHttpResponse
-from rest_framework import viewsets
-from rest_framework.settings import api_settings
-from .files import FileRenderCN, FileRenderEN
-from .models import ListModel
-from . import serializers
-from utils.page import MyPageNumberPagination
-from rest_framework.filters import OrderingFilter
-from django_filters.rest_framework import DjangoFilterBackend
-from rest_framework.response import Response
-from .filter import Filter
-from rest_framework.exceptions import APIException
-from .serializers import FileRenderSerializer
-
-class APIViewSet(viewsets.ModelViewSet):
-    """
-        retrieve:
-            Response a data list(get)
-
-        list:
-            Response a data list(all)
-
-        create:
-            Create a data line(post)
-
-        delete:
-            Delete a data line(delete)
-
-        partial_update:
-            Partial_update a data(patch:partial_update)
-
-        update:
-            Update a data(put:update)
-    """
-    pagination_class = MyPageNumberPagination
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            if id is None:
-                return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
-            else:
-                return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
-        else:
-            return ListModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list', 'retrieve', 'destroy']:
-            return serializers.CustomerGetSerializer
-        elif self.action in ['create']:
-            return serializers.CustomerPostSerializer
-        elif self.action in ['update']:
-            return serializers.CustomerUpdateSerializer
-        elif self.action in ['partial_update']:
-            return serializers.CustomerPartialUpdateSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def create(self, request, *args, **kwargs):
-        data = self.request.data
-        data['openid'] = self.request.auth.openid
-        if ListModel.objects.filter(openid=data['openid'], customer_name=data['customer_name'], is_delete=False).exists():
-            raise APIException({"detail": "Data exists"})
-        else:
-            serializer = self.get_serializer(data=data)
-            serializer.is_valid(raise_exception=True)
-            serializer.save()
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-
-    def update(self, request, pk):
-        qs = self.get_object()
-        if qs.openid != self.request.auth.openid:
-            raise APIException({"detail": "Cannot update data which not yours"})
-        else:
-            data = self.request.data
-            serializer = self.get_serializer(qs, data=data)
-            serializer.is_valid(raise_exception=True)
-            serializer.save()
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-
-    def partial_update(self, request, pk):
-        qs = self.get_object()
-        if qs.openid != self.request.auth.openid:
-            raise APIException({"detail": "Cannot partial_update data which not yours"})
-        else:
-            data = self.request.data
-            serializer = self.get_serializer(qs, data=data, partial=True)
-            serializer.is_valid(raise_exception=True)
-            serializer.save()
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-
-    def destroy(self, request, pk):
-        qs = self.get_object()
-        if qs.openid != self.request.auth.openid:
-            raise APIException({"detail": "Cannot delete data which not yours"})
-        else:
-            qs.is_delete = True
-            qs.save()
-            serializer = self.get_serializer(qs, many=False)
-            headers = self.get_success_headers(serializer.data)
-            return Response(serializer.data, status=200, headers=headers)
-
-
-class FileDownloadView(viewsets.ModelViewSet):
-    renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            if id is None:
-                return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
-            else:
-                return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
-        else:
-            return ListModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.FileRenderSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def get_lang(self, data):
-        lang = self.request.META.get('HTTP_LANGUAGE')
-        if lang:
-            if lang == 'zh-hans':
-                return FileRenderCN().render(data)
-            else:
-                return FileRenderEN().render(data)
-        else:
-            return FileRenderEN().render(data)
-
-    def list(self, request, *args, **kwargs):
-        from datetime import datetime
-        dt = datetime.now()
-        data = (
-            FileRenderSerializer(instance).data
-            for instance in self.filter_queryset(self.get_queryset())
-        )
-        renderer = self.get_lang(data)
-        response = StreamingHttpResponse(
-            renderer,
-            content_type="text/csv"
-        )
-        response['Content-Disposition'] = "attachment; filename='customer_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
-        return response

+ 0 - 0
cyclecount/__init__.py


+ 0 - 4
cyclecount/admin.py

@@ -1,4 +0,0 @@
-from django.contrib import admin
-from .models import CyclecountModeDayModel
-
-admin.site.register(CyclecountModeDayModel)

+ 0 - 4
cyclecount/apps.py

@@ -1,4 +0,0 @@
-from django.apps import AppConfig
-
-class CyclecountConfig(AppConfig):
-    name = 'cyclecount'

+ 0 - 52
cyclecount/files.py

@@ -1,52 +0,0 @@
-from rest_framework_csv.renderers import CSVStreamingRenderer
-
-def file_headers():
-    return [
-        "cyclecount_status",
-        "bin_name",
-        "goods_code",
-        "goods_desc",
-        "goods_qty",
-        "physical_inventory",
-        "difference",
-        "creater",
-        "create_time",
-        "update_time"
-    ]
-
-def cn_data_header():
-    return dict([
-        ('cyclecount_status', u'盘点状态'),
-        ('bin_name', u'库位名称'),
-        ('goods_code', u'商品编码'),
-        ('goods_desc', u'商品描述'),
-        ('goods_qty', u'现有数量'),
-        ('physical_inventory', u'盘点数量'),
-        ('difference', u'盘点差异'),
-        ('creater', u'创建人'),
-        ('create_time', u'创建时间'),
-        ('update_time', u'盘点时间')
-    ])
-
-def en_data_header():
-    return dict([
-        ('cyclecount_status', u'Count Status'),
-        ('bin_nam', u'Bin Name'),
-        ('goods_code', u'Goods Code'),
-        ('goods_desc', u'Goods Description'),
-        ('goods_qty', u'On-Hand Stock'),
-        ('physical_inventory', u'Count QTY'),
-        ('difference', u'Count Difference'),
-        ('creater', u'Creater'),
-        ('create_time', u'Create Time'),
-        ('update_time', u'Update Time')
-    ])
-
-
-class FileRenderCN(CSVStreamingRenderer):
-    header = file_headers()
-    labels = cn_data_header()
-
-class FileRenderEN(CSVStreamingRenderer):
-    header = file_headers()
-    labels = en_data_header()

+ 0 - 38
cyclecount/filter.py

@@ -1,38 +0,0 @@
-from django_filters import FilterSet
-from .models import CyclecountModeDayModel
-from .models import QTYRecorder
-from .models import ManualCyclecountModeModel
-
-class Filter(FilterSet):
-    class Meta:
-        model = CyclecountModeDayModel
-        fields = {
-            "id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
-            "create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
-        }
-
-class QTYRecorderListFilter(FilterSet):
-    class Meta:
-        model = QTYRecorder
-        fields = {
-            "id": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
-            "mode_code": ['exact', 'iexact', 'contains', 'icontains'],
-            "bin_name": ['exact', 'iexact', 'contains', 'icontains'],
-            "goods_code": ['exact', 'iexact', 'contains', 'icontains'],
-            "goods_desc": ['exact', 'iexact', 'contains', 'icontains'],
-            "goods_qty": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'range'],
-            "store_code": ['exact', 'iexact', 'contains', 'icontains'],
-            "creater": ['exact', 'iexact', 'contains', 'icontains'],
-            "create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
-            "update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
-        }
-
-class ManualFilter(FilterSet):
-    class Meta:
-        model = ManualCyclecountModeModel
-        fields = {
-            "id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
-            "create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
-            "bin_name": ['exact', 'iexact', 'contains', 'icontains'],
-            "goods_code": ['exact', 'iexact', 'contains', 'icontains'],
-        }

+ 0 - 0
cyclecount/migrations/__init__.py


+ 0 - 57
cyclecount/models.py

@@ -1,57 +0,0 @@
-from django.db import models
-
-class QTYRecorder(models.Model):
-    openid = models.CharField(max_length=255, verbose_name="Openid")
-    mode_code = models.CharField(max_length=255, verbose_name="Transaction Mode")
-    bin_name = models.CharField(max_length=255, verbose_name="Bin Name")
-    goods_code = models.CharField(max_length=255, verbose_name="Goods Code")
-    goods_desc = models.CharField(max_length=255, verbose_name="Goods Description")
-    goods_qty = models.BigIntegerField(default=0, verbose_name="On Hand Stock")
-    store_code = models.CharField(default='', max_length=255, verbose_name="Store Code")
-    creater = models.CharField(max_length=255, verbose_name="Who Create")
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
-    update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
-
-    class Meta:
-        db_table = 'qtyrecorder'
-        verbose_name = 'QTY Recorder'
-        verbose_name_plural = "QTY Recorder"
-        ordering = ['-id']
-
-class CyclecountModeDayModel(models.Model):
-    openid = models.CharField(max_length=255, verbose_name="Openid")
-    cyclecount_status = models.IntegerField(default=0, verbose_name="Cycle Count Status")
-    bin_name = models.CharField(max_length=255, verbose_name="Bin Name")
-    goods_code = models.CharField(max_length=255, verbose_name="Goods Code")
-    goods_qty = models.BigIntegerField(default=0, verbose_name="On Hand Stock")
-    physical_inventory = models.BigIntegerField(default=0, verbose_name="Goods Code")
-    difference = models.BigIntegerField(default=0, verbose_name="Goods Code")
-    creater = models.CharField(max_length=255, verbose_name="Who Create")
-    t_code = models.CharField(max_length=255, verbose_name="Transaction Code")
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
-    update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
-
-    class Meta:
-        db_table = 'cyclecountday'
-        verbose_name = 'Cyclecount Day'
-        verbose_name_plural = "Cyclecount Day"
-        ordering = ['openid']
-
-class ManualCyclecountModeModel(models.Model):
-    openid = models.CharField(max_length=255, verbose_name="Openid")
-    cyclecount_status = models.IntegerField(default=0, verbose_name="Cycle Count Status")
-    bin_name = models.CharField(max_length=255, verbose_name="Bin Name")
-    goods_code = models.CharField(max_length=255, verbose_name="Goods Code")
-    goods_qty = models.BigIntegerField(default=0, verbose_name="On Hand Stock")
-    physical_inventory = models.BigIntegerField(default=0, verbose_name="Goods Code")
-    difference = models.BigIntegerField(default=0, verbose_name="Goods Code")
-    creater = models.CharField(max_length=255, verbose_name="Who Create")
-    t_code = models.CharField(max_length=255, verbose_name="Transaction Code")
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
-    update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
-
-    class Meta:
-        db_table = 'manualcyclecount'
-        verbose_name = 'Manual Cyclecount'
-        verbose_name_plural = "Manual Cyclecount"
-        ordering = ['openid']

+ 0 - 9
cyclecount/page.py

@@ -1,9 +0,0 @@
-from rest_framework.pagination import PageNumberPagination
-from rest_framework.utils.urls import replace_query_param, remove_query_param
-from rest_framework.exceptions import APIException
-
-class CycleCountPageNumberPagination(PageNumberPagination):
-    page_size = 10000
-    page_size_query_param = "max_page"
-    max_page_size = 20000
-    page_query_param = 'page'

+ 0 - 120
cyclecount/serializers.py

@@ -1,120 +0,0 @@
-from rest_framework import serializers
-from .models import CyclecountModeDayModel
-from .models import ManualCyclecountModeModel
-from utils import datasolve
-from .models import QTYRecorder
-class CyclecountGetSerializer(serializers.ModelSerializer):
-    creater = serializers.CharField(read_only=True, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    class Meta:
-        model = CyclecountModeDayModel
-        exclude = ['openid']
-        read_only_fields = ['id', ]
-
-class CyclecountPostSerializer(serializers.ModelSerializer):
-    openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = CyclecountModeDayModel
-        exclude = []
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class CyclecountUpdateSerializer(serializers.ModelSerializer):
-    openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = CyclecountModeDayModel
-        exclude = []
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class FileRenderSerializer(serializers.ModelSerializer):
-    creater = serializers.CharField(read_only=False, required=False)
-    physical_inventory = serializers.SerializerMethodField()
-    difference = serializers.SerializerMethodField()
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-
-    class Meta:
-        model = CyclecountModeDayModel
-        ref_name = 'CyclecountFileRenderSerializer'
-        exclude = ['openid']
-
-    def get_physical_inventory(self, obj):
-        return ''
-
-    def get_difference(self, obj):
-        return ''
-
-class FileRenderAllSerializer(serializers.ModelSerializer):
-    creater = serializers.CharField(read_only=False, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-
-    class Meta:
-        model = CyclecountModeDayModel
-        ref_name = 'CyclecountFileRenderAllSerializer'
-        exclude = ['openid']
-
-
-class QTYRecorderSerializer(serializers.ModelSerializer):
-    mode_code = serializers.CharField(read_only=True, required=False)
-    bin_name = serializers.CharField(read_only=True, required=False)
-    goods_code = serializers.CharField(read_only=True, required=False)
-    goods_desc = serializers.CharField(read_only=True, required=False)
-    goods_qty = serializers.IntegerField(read_only=True, required=False)
-    store_code = serializers.CharField(read_only=True, required=False)
-    creater = serializers.CharField(read_only=True, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-
-    class Meta:
-        model = QTYRecorder
-        ref_name = 'QTYRecorderSerializer'
-        exclude = ['openid']
-
-class ManualCyclecountGetSerializer(serializers.ModelSerializer):
-    creater = serializers.CharField(read_only=True, required=False)
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    class Meta:
-        model = ManualCyclecountModeModel
-        exclude = ['openid']
-        read_only_fields = ['id', ]
-
-
-class ManualCyclecountPostSerializer(serializers.ModelSerializer):
-    openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = ManualCyclecountModeModel
-        exclude = []
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-class ManualCyclecountUpdateSerializer(serializers.ModelSerializer):
-    openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
-    creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
-    class Meta:
-        model = ManualCyclecountModeModel
-        exclude = []
-        read_only_fields = ['id', 'create_time', 'update_time', ]
-
-
-class ManualFileRenderSerializer(serializers.ModelSerializer):
-    creater = serializers.CharField(read_only=False, required=False)
-    physical_inventory = serializers.SerializerMethodField()
-    difference = serializers.SerializerMethodField()
-    create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-    update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
-
-    class Meta:
-        model = ManualCyclecountModeModel
-        ref_name = 'ManualFileRenderSerializer'
-        exclude = ['openid']
-
-    def get_physical_inventory(self, obj):
-        return ''
-
-    def get_difference(self, obj):
-        return ''
-

+ 0 - 0
cyclecount/tests.py


+ 0 - 15
cyclecount/urls.py

@@ -1,15 +0,0 @@
-from django.urls import path
-from . import views
-
-urlpatterns = [
-path(r'', views.CyclecountModeDayViewSet.as_view({"get": "list", "post": "create", 'put': 'update'}), name="cyclecount"),
-path(r'cyclecountrecorder/', views.CyclecountModeAllViewSet.as_view({"get": "list"}), name="cyclecountrecorder"),
-path(r'filecyclecountday/', views.FileDownloadView.as_view({"get": "list"}), name="filecyclecountday"),
-path(r'filecyclecountall/', views.FileDownloadAllView.as_view({"get": "list"}), name="filecyclecountall"),
-
-path(r'qtyrecorviewset/', views.QTYRecorderViewSet.as_view({"get": "list"}), name="qtyrecorviewset"),
-path(r'getgoodscyclecount/', views.GetGoodsCyclecountViewSet.as_view({"get": "list"}), name="getgoodscyclecount"),
-path(r'manualcyclecount/', views.ManualCyclecountViewSet.as_view({"post": "create", "get": "list"}), name="manualcyclecount"),
-path(r'manualfilecyclecount/', views.ManualFileDownloadView.as_view({"get": "list"}), name="manualfilecyclecount"),
-path(r'manualcyclecountrecorder/', views.ManualCyclecountRecorderViewSet.as_view({"get": "list"}), name="manualcyclecountrecorder")
-]

+ 0 - 532
cyclecount/views.py

@@ -1,532 +0,0 @@
-import traceback
-from dateutil.relativedelta import relativedelta
-from django.http import StreamingHttpResponse
-from django.utils import timezone
-from rest_framework import viewsets
-from rest_framework.settings import api_settings
-from .files import FileRenderCN, FileRenderEN
-from .models import CyclecountModeDayModel
-from . import serializers
-from utils.page import MyPageNumberPagination
-from rest_framework.filters import OrderingFilter
-from django_filters.rest_framework import DjangoFilterBackend
-from rest_framework.response import Response
-from .filter import Filter
-from .filter import ManualFilter
-from .filter import QTYRecorderListFilter
-from .serializers import FileRenderSerializer, FileRenderAllSerializer
-from .models import QTYRecorder
-from .models import ManualCyclecountModeModel
-from userprofile.models import Users
-from stock.views import StockBinViewSet
-from utils.md5 import Md5
-from staff.models import ListModel as staff
-
-class QTYRecorderViewSet(viewsets.ModelViewSet):
-    """
-        list:
-            Response a data list(all)
-
-    """
-    pagination_class = MyPageNumberPagination
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = QTYRecorderListFilter
-
-    def get_queryset(self):
-        if self.request.user:
-            return QTYRecorder.objects.filter(openid=self.request.auth.openid)
-        else:
-            return QTYRecorder.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.QTYRecorderSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-class CyclecountModeDayViewSet(viewsets.ModelViewSet):
-    """
-        retrieve:
-            Response a data list(get)
-
-        list:
-            Response a data list(all)
-
-        create:
-            Create a data line(post)
-
-        delete:
-            Delete a data line(delete)
-
-        partial_update:
-            Partial_update a data(patch:partial_update)
-
-        update:
-            Update a data(put:update)
-    """
-    pagination_class = None
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            cur_date = timezone.now()
-            delt_date = relativedelta(days=1)
-            if id is None:
-                return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=0,
-                                                             update_time__gte=str((cur_date -delt_date).date()) + ' 00:00:00',
-                                                             update_time__lte=str((cur_date + delt_date).date()) + ' 00:00:00')
-            else:
-                return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=0,
-                                                             update_time__gte=str((cur_date - delt_date).date()) + ' 00:00:00',
-                                                             update_time__lte=str((cur_date + delt_date).date()) + ' 00:00:00', id=id)
-        else:
-            return CyclecountModeDayModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.CyclecountGetSerializer
-        elif self.action in ['create']:
-            return serializers.CyclecountPostSerializer
-        elif self.action in ['update']:
-            return serializers.CyclecountUpdateSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def create(self, request, *args, **kwargs):
-        data = self.request.data
-        for i in range(len(data)):
-            CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid,
-                                                  t_code=data[i]['t_code']).update(
-                physical_inventory=data[i]['physical_inventory'], cyclecount_status=1,
-                difference=data[i]['physical_inventory'] - data[i]['goods_qty'])
-        return Response({"detail": "success"}, status=200)
-
-    def update(self, request, *args, **kwargs):
-        data = self.request.data
-        for i in range(len(data)):
-            scan_count_data = self.get_queryset().filter(openid=self.request.auth.openid,
-                                                  t_code=data[i]['t_code']).first()
-            scan_count_data.physical_inventory = scan_count_data.physical_inventory + data[i]['physical_inventory']
-            scan_count_data.difference = data[i]['physical_inventory'] - data[i]['goods_qty']
-            scan_count_data.cyclecount_status = 1
-            scan_count_data.save()
-        return Response({"detail": "success"}, status=200)
-
-class CyclecountModeAllViewSet(viewsets.ModelViewSet):
-    """
-        list:
-            Response a data list(get)
-    """
-    pagination_class = MyPageNumberPagination
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            date_choice = self.request.GET.get('create_time', '')
-            cur_time = timezone.now().date()
-            if date_choice:
-                if id is None:
-                    return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=1,
-                                                                 update_time__gte=str(date_choice) + ' 00:00:00',
-                                                                 update_time__lte=str(date_choice) + ' 23:59:59')
-                else:
-                    return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=1,
-                                                                 update_time__gte=str(date_choice) + ' 00:00:00',
-                                                                 update_time__lte=str(date_choice) + ' 23:59:59',
-                                                                 id=id)
-            else:
-                if id is None:
-                    return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=1,
-                                                                 update_time__gte=str(cur_time) + ' 00:00:00',
-                                                                 update_time__lte=str(cur_time) + ' 23:59:59')
-                else:
-                    return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=1,
-                                                                 update_time__gte=str(cur_time) + ' 00:00:00',
-                                                                 update_time__lte=str(cur_time) + ' 23:59:59',
-                                                                 id=id)
-        else:
-            return CyclecountModeDayModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.CyclecountGetSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-
-class FileDownloadView(viewsets.ModelViewSet):
-    renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time"]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            cur_date = timezone.now()
-            delt_date = relativedelta(days=1)
-            if id is None:
-                return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=0,
-                                                             update_time__gte=str((cur_date -delt_date).date()) + ' 00:00:00')
-            else:
-                return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=0,
-                                                             update_time__gte=str((cur_date -delt_date).date()) + ' 00:00:00', id=id)
-        else:
-            return CyclecountModeDayModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.FileRenderSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def get_lang(self, data):
-        lang = self.request.META.get('HTTP_LANGUAGE')
-        if lang:
-            if lang == 'zh-hans':
-                return FileRenderCN().render(data)
-            else:
-                return FileRenderEN().render(data)
-        else:
-            return FileRenderEN().render(data)
-
-    def list(self, request, *args, **kwargs):
-        from datetime import datetime
-        dt = datetime.now()
-        data = (
-            FileRenderSerializer(instance).data
-            for instance in self.filter_queryset(self.get_queryset())
-        )
-        renderer = self.get_lang(data)
-        response = StreamingHttpResponse(
-            renderer,
-            content_type="text/csv"
-        )
-        response['Content-Disposition'] = "attachment; filename='cyclecount_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
-        return response
-
-class FileDownloadAllView(viewsets.ModelViewSet):
-    renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time"]
-    filter_class = Filter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-            id = self.get_project()
-            if self.request.user:
-                cur_date = timezone.now()
-                delt_date = relativedelta(days=1)
-                if id is None:
-                    return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=1,
-                                                                 update_time__gte=str((cur_date -delt_date).date()) + ' 00:00:00',
-                                                                 update_time__lte=str((cur_date + delt_date).date()) + ' 23:59:59')
-                else:
-                    return CyclecountModeDayModel.objects.filter(openid=self.request.auth.openid, cyclecount_status=1,
-                                                                 update_time__gte=str((cur_date - delt_date).date()) + ' 00:00:00',
-                                                                 update_time__lte=str((cur_date + delt_date).date()) + ' 23:59:59', id=id)
-            else:
-                return CyclecountModeDayModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.FileRenderSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def get_lang(self, data):
-        lang = self.request.META.get('HTTP_LANGUAGE')
-        if lang:
-            if lang == 'zh-hans':
-                return FileRenderCN().render(data)
-            else:
-                return FileRenderEN().render(data)
-        else:
-            return FileRenderEN().render(data)
-
-    def list(self, request, *args, **kwargs):
-        from datetime import datetime
-        dt = datetime.now()
-        data = (
-            FileRenderAllSerializer(instance).data
-            for instance in self.filter_queryset(self.get_queryset())
-        )
-        renderer = self.get_lang(data)
-        response = StreamingHttpResponse(
-            renderer,
-            content_type="text/csv"
-        )
-        response['Content-Disposition'] = "attachment; filename='cyclecountall_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
-        return response
-
-class GetGoodsCyclecountViewSet(StockBinViewSet):
-    """
-        list:
-            Response a data list(get)
-    """
-    pagination_class = None
-
-    def list(self, request, *args, **kwargs):
-        staff_name = staff.objects.filter(openid=self.request.auth.openid, id=self.request.META.get('HTTP_OPERATOR')).first().staff_name
-        queryset = self.filter_queryset(self.get_queryset())
-        goods_code = self.request.GET.get('goods_code', '')
-        for i in queryset:
-            if (d:=ManualCyclecountModeModel.objects.filter(cyclecount_status=0, bin_name=i.bin_name, goods_code=goods_code)).exists():
-                d.delete()
-            data = {
-                'openid': self.request.auth.openid,
-                'creater': staff_name,
-                'cyclecount_status': 0,
-                'bin_name': i.bin_name,
-                'goods_code': goods_code,
-                'goods_qty': i.goods_qty,
-                'physical_inventory': 0,
-                'difference': 0,
-                't_code': Md5.md5(goods_code)
-            }
-            serializer = serializers.ManualCyclecountPostSerializer(data=data)
-            serializer.is_valid(raise_exception=True)
-            serializer.save()
-        queryset = ManualCyclecountModeModel.objects.filter(goods_code=goods_code, cyclecount_status=0)
-        page = self.paginate_queryset(queryset)
-        if page is not None:
-            serializer = serializers.ManualCyclecountGetSerializer(instance=page, many=True)
-            return self.get_paginated_response(serializer.data)
-        serializer = serializers.ManualCyclecountGetSerializer(instance=queryset, many=True)
-        return Response(serializer.data)
-
-class ManualCyclecountViewSet(viewsets.ModelViewSet):
-    """
-        retrieve:
-            Response a data list(get)
-
-        list:
-            Response a data list(all)
-
-        create:
-            Create a data line(post)
-
-        delete:
-            Delete a data line(delete)
-
-        partial_update:
-            Partial_update a data(patch:partial_update)
-
-        update:
-            Update a data(put:update)
-    """
-    pagination_class = None
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = ManualFilter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            cur_date = timezone.now()
-            delt_date = relativedelta(days=1)
-            u = Users.objects.filter(vip=9).first()
-            if u is None:
-                superopenid = None
-            else:
-                superopenid = u.openid
-            query_dict = {
-                'cyclecount_status': 0,
-                'update_time__gte': str((cur_date - delt_date).date()) + ' 00:00:00',
-                'update_time__lte': str((cur_date + delt_date).date()) + ' 00:00:00'
-            }
-            if self.request.auth.openid != superopenid:
-                query_dict['openid'] = self.request.auth.openid
-            if id is not None:
-                query_dict['id'] = id
-            return ManualCyclecountModeModel.objects.filter(**query_dict)
-        else:
-            return ManualCyclecountModeModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.ManualCyclecountGetSerializer
-        elif self.action in ['create']:
-            return serializers.ManualCyclecountModeModel
-        elif self.action in ['update']:
-            return serializers.ManualCyclecountUpdateSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def create(self, request, *args, **kwargs):
-        data = self.request.data
-        for i in range(len(data)):
-            ManualCyclecountModeModel.objects.filter(openid=self.request.auth.openid, t_code=data[i]['t_code']).update(
-                physical_inventory=data[i]['physical_inventory'],
-                cyclecount_status=1,
-                difference=data[i]['physical_inventory'] - data[i]['goods_qty']
-            )
-        return Response({"detail": "success"}, status=200)
-
-    def update(self, request, *args, **kwargs):
-        data = self.request.data
-        for i in range(len(data)):
-            scan_count_data = self.get_queryset().filter(openid=self.request.auth.openid,
-                                                  t_code=data[i]['t_code']).first()
-            scan_count_data.physical_inventory = scan_count_data.physical_inventory + data[i]['physical_inventory']
-            scan_count_data.difference = data[i]['physical_inventory'] - data[i]['goods_qty']
-            scan_count_data.cyclecount_status = 1
-            scan_count_data.save()
-        return Response({"detail": "success"}, status=200)
-
-class ManualCyclecountRecorderViewSet(viewsets.ModelViewSet):
-    """
-        list:
-            Response a data list(get)
-    """
-    pagination_class = MyPageNumberPagination
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time", "update_time", ]
-    filter_class = ManualFilter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            date_choice = self.request.GET.get('create_time', '')
-            cur_time = timezone.now().date()
-            u = Users.objects.filter(vip=9).first()
-            if u is None:
-                superopenid = None
-            else:
-                superopenid = u.openid
-            query_dict = {
-                'cyclecount_status': 1
-            }
-            if self.request.auth.openid != superopenid:
-                query_dict['openid'] = self.request.auth.openid
-            if date_choice:
-                query_dict['update_time__gte'] = str(date_choice) + ' 00:00:00'
-                query_dict['update_time__lte'] = str(date_choice) + ' 23:59:59'
-            else:
-                query_dict['update_time__gte'] = str(cur_time) + ' 00:00:00'
-                query_dict['update_time__lte'] = str(cur_time) + ' 23:59:59'
-            if id is not None:
-                query_dict['id'] = id
-            return ManualCyclecountModeModel.objects.filter(**query_dict)
-        else:
-            return ManualCyclecountModeModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.ManualCyclecountGetSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-class ManualFileDownloadView(viewsets.ModelViewSet):
-    renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
-    filter_backends = [DjangoFilterBackend, OrderingFilter, ]
-    ordering_fields = ['id', "create_time"]
-    filter_class = ManualFilter
-
-    def get_project(self):
-        try:
-            id = self.kwargs.get('pk')
-            return id
-        except:
-            return None
-
-    def get_queryset(self):
-        id = self.get_project()
-        if self.request.user:
-            cur_date = timezone.now()
-            delt_date = relativedelta(days=1)
-            u = Users.objects.filter(vip=9).first()
-            if u is None:
-                superopenid = None
-            else:
-                superopenid = u.openid
-            query_dict = {
-                'cyclecount_status': 0,
-                'update_time__gte': str((cur_date - delt_date).date()) + ' 00:00:00'
-            }
-            if self.request.auth.openid != superopenid:
-                query_dict['openid'] = self.request.auth.openid
-            if id is not None:
-                query_dict['id'] = id
-            return ManualCyclecountModeModel.objects.filter(**query_dict)
-        else:
-            return ManualCyclecountModeModel.objects.none()
-
-    def get_serializer_class(self):
-        if self.action in ['list']:
-            return serializers.ManualFileRenderSerializer
-        else:
-            return self.http_method_not_allowed(request=self.request)
-
-    def get_lang(self, data):
-        lang = self.request.META.get('HTTP_LANGUAGE')
-        if lang:
-            if lang == 'zh-hans':
-                return FileRenderCN().render(data)
-            else:
-                return FileRenderEN().render(data)
-        else:
-            return FileRenderEN().render(data)
-
-    def list(self, request, *args, **kwargs):
-        from datetime import datetime
-        dt = datetime.now()
-        data = (
-            serializers.ManualFileRenderSerializer(instance).data
-            for instance in self.filter_queryset(self.get_queryset())
-        )
-        renderer = self.get_lang(data)
-        response = StreamingHttpResponse(
-            renderer,
-            content_type="text/csv"
-        )
-        response['Content-Disposition'] = "attachment; filename='manualcyclecount_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
-        return response

+ 1 - 0
delete_database.ps1

@@ -0,0 +1 @@
+python manage.py flush --no-input

+ 22 - 30
greaterwms/settings.py

@@ -27,36 +27,13 @@ INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
-    'staff.apps.StaffConfig',
+
     'userprofile.apps.UserprofileConfig',
     'userregister.apps.UserregisterConfig',
     'userlogin.apps.UserloginConfig',
-    'company.apps.CompanyConfig',
-    'supplier.apps.SupplierConfig',
-    'asn.apps.AsnConfig',
-    'dn.apps.DnConfig',
-    'binset.apps.BinsetConfig',
-    'binsize.apps.BinsizeConfig',
-    'binproperty.apps.BinpropertyConfig',
-    'customer.apps.CustomerConfig',
-    'capital.apps.CapitalConfig',
-    'cyclecount.apps.CyclecountConfig',
-    'dashboard.apps.DashboardConfig',
-    'warehouse.apps.WarehouseConfig',
-    'goods.apps.GoodsConfig',
-    'goodsunit.apps.GoodsunitConfig',
-    'goodsclass.apps.GoodsclassConfig',
-    'goodscolor.apps.GoodscolorConfig',
-    'goodsbrand.apps.GoodsbrandConfig',
-    'goodsshape.apps.GoodsshapeConfig',
-    'goodsspecs.apps.GoodsspecsConfig',
-    'goodsorigin.apps.GoodsoriginConfig',
-    'payment.apps.PaymentConfig',
-    'driver.apps.DriverConfig',
-    'stock.apps.StockConfig',
     'throttle.apps.ThrottleConfig',
-    'uploadfile.apps.UploadfileConfig',
-    'scanner.apps.ScannerConfig',
+
+
     'rest_framework',
     'django_filters',
     'corsheaders',
@@ -100,13 +77,28 @@ CSRF_COOKIE_SAMESITE = None
 # Database
 # https://docs.djangoproject.com/en/3.1/ref/settings/#databases
 # update
+# DATABASES = {
+#     'default': {
+#         'ENGINE': 'django.db.backends.sqlite3',
+#         'NAME': BASE_DIR / 'db.sqlite3',
+#         'OPTIONS': {
+#             'timeout': 20,
+#         }
+#     }
+# }
 DATABASES = {
     'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': BASE_DIR / 'db.sqlite3',
+        'ENGINE': 'django.db.backends.postgresql',
+        'NAME': 'django_db',        # 你的数据库名
+        'USER': 'django_user',      # 创建的用户名
+        'PASSWORD': 'zhanglei',     # 设置的密码
+        'HOST': 'localhost',
+        'PORT': '5432',
+        # 以下为优化选项(可选但推荐)
         'OPTIONS': {
-            'timeout': 20,
-        }
+            'connect_timeout': 5,   # 连接超时时间(秒)
+        },
+        'CONN_MAX_AGE': 300,       # 连接持久时间(秒)
     }
 }
 

+ 3 - 30
greaterwms/urls.py

@@ -13,38 +13,11 @@ def return_static(request, path, insecure=True, **kwargs):
 
 
 urlpatterns = [
-    path('admin/', admin.site.urls),
-    path('', TemplateView.as_view(template_name='dist/spa/index.html')),
-    path('myip/', views.myip, name='myip'),
-    path('asn/', include('asn.urls')),
-    path('dn/', include('dn.urls')),
-    path('staff/', include('staff.urls')),
-    path('binset/', include('binset.urls')),
-    path('binsize/', include('binsize.urls')),
-    path('binproperty/', include('binproperty.urls')),
-    path('capital/', include('capital.urls')),
-    path('driver/', include('driver.urls')),
-    path('stock/', include('stock.urls')),
-    path('company/', include('company.urls')),
-    path('cyclecount/', include('cyclecount.urls')),
-    path('dashboard/', include('dashboard.urls')),
-    path('supplier/', include('supplier.urls')),
-    path('customer/', include('customer.urls')),
-    path('warehouse/', include('warehouse.urls')),
-    path('goods/', include('goods.urls')),
-    path('goodsunit/', include('goodsunit.urls')),
-    path('goodsclass/', include('goodsclass.urls')),
-    path('goodscolor/', include('goodscolor.urls')),
-    path('goodsbrand/', include('goodsbrand.urls')),
-    path('goodsshape/', include('goodsshape.urls')),
-    path('goodsspecs/', include('goodsspecs.urls')),
-    path('goodsorigin/', include('goodsorigin.urls')),
-    path('scanner/', include('scanner.urls')),
-    path('payment/', include('payment.urls')),
+    
     path('login/', include('userlogin.urls')),
     path('register/', include('userregister.urls')),
-    path('uploadfile/', include('uploadfile.urls')),
-    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
+    path('user/', include('userprofile.urls')),
+
     re_path(r'^favicon\.ico$', views.favicon, name='favicon'),
     re_path('^css/.*$', views.css, name='css'),
     re_path('^js/.*$', views.js, name='js'),

+ 1 - 0
requirements.txt

@@ -50,3 +50,4 @@ unicodecsv==0.14.1
 uritemplate==4.1.1
 urllib3==1.26.12
 zope.interface==6.0
+psycopg2-binary==2.9.3

+ 5 - 1
templates/jsconfig.json

@@ -2,7 +2,11 @@
   "compilerOptions": {
     "baseUrl": "./",
     "paths": {
-        "@/*": ["src/*"]
+        "@/*": ["src/*"],
+        "boot/*": [
+        "src/boot/*"
+      ],
+
     }
   },
   "exclude": ["node_modules", "dist"]

+ 116 - 0
templates/mock copy/article.js

@@ -0,0 +1,116 @@
+const Mock = require('mockjs')
+
+const List = []
+const count = 100
+
+const baseContent = '<p>I am testing data, I am testing data.</p><p><img src="https://wpimg.wallstcn.com/4c69009c-0fd4-4153-b112-6cb53d1cf943"></p>'
+const image_uri = 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3'
+
+for (let i = 0; i < count; i++) {
+  List.push(Mock.mock({
+    id: '@increment',
+    timestamp: +Mock.Random.date('T'),
+    author: '@first',
+    reviewer: '@first',
+    title: '@title(5, 10)',
+    content_short: 'mock data',
+    content: baseContent,
+    forecast: '@float(0, 100, 2, 2)',
+    importance: '@integer(1, 3)',
+    'type|1': ['CN', 'US', 'JP', 'EU'],
+    'status|1': ['published', 'draft'],
+    display_time: '@datetime',
+    comment_disabled: true,
+    pageviews: '@integer(300, 5000)',
+    image_uri,
+    platforms: ['a-platform']
+  }))
+}
+
+module.exports = [
+  {
+    url: '/vue-element-admin/article/list',
+    type: 'get',
+    response: config => {
+      const { importance, type, title, page = 1, limit = 20, sort } = config.query
+
+      let mockList = List.filter(item => {
+        if (importance && item.importance !== +importance) return false
+        if (type && item.type !== type) return false
+        if (title && item.title.indexOf(title) < 0) return false
+        return true
+      })
+
+      if (sort === '-id') {
+        mockList = mockList.reverse()
+      }
+
+      const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
+
+      return {
+        code: 20000,
+        data: {
+          total: mockList.length,
+          items: pageList
+        }
+      }
+    }
+  },
+
+  {
+    url: '/vue-element-admin/article/detail',
+    type: 'get',
+    response: config => {
+      const { id } = config.query
+      for (const article of List) {
+        if (article.id === +id) {
+          return {
+            code: 20000,
+            data: article
+          }
+        }
+      }
+    }
+  },
+
+  {
+    url: '/vue-element-admin/article/pv',
+    type: 'get',
+    response: _ => {
+      return {
+        code: 20000,
+        data: {
+          pvData: [
+            { key: 'PC', pv: 1024 },
+            { key: 'mobile', pv: 1024 },
+            { key: 'ios', pv: 1024 },
+            { key: 'android', pv: 1024 }
+          ]
+        }
+      }
+    }
+  },
+
+  {
+    url: '/vue-element-admin/article/create',
+    type: 'post',
+    response: _ => {
+      return {
+        code: 20000,
+        data: 'success'
+      }
+    }
+  },
+
+  {
+    url: '/vue-element-admin/article/update',
+    type: 'post',
+    response: _ => {
+      return {
+        code: 20000,
+        data: 'success'
+      }
+    }
+  }
+]
+

+ 60 - 0
templates/mock copy/index.js

@@ -0,0 +1,60 @@
+const Mock = require('mockjs')
+const { param2Obj } = require('./utils')
+
+const user = require('./user')
+const role = require('./role')
+const article = require('./article')
+const search = require('./remote-search')
+
+const mocks = [
+  ...user,
+  ...role,
+  ...article,
+  ...search
+]
+
+// for front mock
+// please use it cautiously, it will redefine XMLHttpRequest,
+// which will cause many of your third-party libraries to be invalidated(like progress event).
+function mockXHR() {
+  // mock patch
+  // https://github.com/nuysoft/Mock/issues/300
+  Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send
+  Mock.XHR.prototype.send = function() {
+    if (this.custom.xhr) {
+      this.custom.xhr.withCredentials = this.withCredentials || false
+
+      if (this.responseType) {
+        this.custom.xhr.responseType = this.responseType
+      }
+    }
+    this.proxy_send(...arguments)
+  }
+
+  function XHR2ExpressReqWrap(respond) {
+    return function(options) {
+      let result = null
+      if (respond instanceof Function) {
+        const { body, type, url } = options
+        // https://expressjs.com/en/4x/api.html#req
+        result = respond({
+          method: type,
+          body: JSON.parse(body),
+          query: param2Obj(url)
+        })
+      } else {
+        result = respond
+      }
+      return Mock.mock(result)
+    }
+  }
+
+  for (const i of mocks) {
+    Mock.mock(new RegExp(i.url), i.type || 'get', XHR2ExpressReqWrap(i.response))
+  }
+}
+
+module.exports = {
+  mocks,
+  mockXHR
+}

+ 81 - 0
templates/mock copy/mock-server.js

@@ -0,0 +1,81 @@
+const chokidar = require('chokidar')
+const bodyParser = require('body-parser')
+const chalk = require('chalk')
+const path = require('path')
+const Mock = require('mockjs')
+
+const mockDir = path.join(process.cwd(), 'mock')
+
+function registerRoutes(app) {
+  let mockLastIndex
+  const { mocks } = require('./index.js')
+  const mocksForServer = mocks.map(route => {
+    return responseFake(route.url, route.type, route.response)
+  })
+  for (const mock of mocksForServer) {
+    app[mock.type](mock.url, mock.response)
+    mockLastIndex = app._router.stack.length
+  }
+  const mockRoutesLength = Object.keys(mocksForServer).length
+  return {
+    mockRoutesLength: mockRoutesLength,
+    mockStartIndex: mockLastIndex - mockRoutesLength
+  }
+}
+
+function unregisterRoutes() {
+  Object.keys(require.cache).forEach(i => {
+    if (i.includes(mockDir)) {
+      delete require.cache[require.resolve(i)]
+    }
+  })
+}
+
+// for mock server
+const responseFake = (url, type, respond) => {
+  return {
+    url: new RegExp(`${process.env.VUE_APP_BASE_API}${url}`),
+    type: type || 'get',
+    response(req, res) {
+      console.log('request invoke:' + req.path)
+      res.json(Mock.mock(respond instanceof Function ? respond(req, res) : respond))
+    }
+  }
+}
+
+module.exports = app => {
+  // parse app.body
+  // https://expressjs.com/en/4x/api.html#req.body
+  app.use(bodyParser.json())
+  app.use(bodyParser.urlencoded({
+    extended: true
+  }))
+
+  const mockRoutes = registerRoutes(app)
+  var mockRoutesLength = mockRoutes.mockRoutesLength
+  var mockStartIndex = mockRoutes.mockStartIndex
+
+  // watch files, hot reload mock server
+  chokidar.watch(mockDir, {
+    ignored: /mock-server/,
+    ignoreInitial: true
+  }).on('all', (event, path) => {
+    if (event === 'change' || event === 'add') {
+      try {
+        // remove mock routes stack
+        app._router.stack.splice(mockStartIndex, mockRoutesLength)
+
+        // clear routes cache
+        unregisterRoutes()
+
+        const mockRoutes = registerRoutes(app)
+        mockRoutesLength = mockRoutes.mockRoutesLength
+        mockStartIndex = mockRoutes.mockStartIndex
+
+        console.log(chalk.magentaBright(`\n > Mock Server hot reload success! changed  ${path}`))
+      } catch (error) {
+        console.log(chalk.redBright(error))
+      }
+    }
+  })
+}

+ 51 - 0
templates/mock copy/remote-search.js

@@ -0,0 +1,51 @@
+const Mock = require('mockjs')
+
+const NameList = []
+const count = 100
+
+for (let i = 0; i < count; i++) {
+  NameList.push(Mock.mock({
+    name: '@first'
+  }))
+}
+NameList.push({ name: 'mock-Pan' })
+
+module.exports = [
+  // username search
+  {
+    url: '/vue-element-admin/search/user',
+    type: 'get',
+    response: config => {
+      const { name } = config.query
+      const mockNameList = NameList.filter(item => {
+        const lowerCaseName = item.name.toLowerCase()
+        return !(name && lowerCaseName.indexOf(name.toLowerCase()) < 0)
+      })
+      return {
+        code: 20000,
+        data: { items: mockNameList }
+      }
+    }
+  },
+
+  // transaction list
+  {
+    url: '/vue-element-admin/transaction/list',
+    type: 'get',
+    response: _ => {
+      return {
+        code: 20000,
+        data: {
+          total: 20,
+          'items|20': [{
+            order_no: '@guid()',
+            timestamp: +Mock.Random.date('T'),
+            username: '@name()',
+            price: '@float(1000, 15000, 0, 2)',
+            'status|1': ['success', 'pending']
+          }]
+        }
+      }
+    }
+  }
+]

+ 98 - 0
templates/mock copy/role/index.js

@@ -0,0 +1,98 @@
+const Mock = require('mockjs')
+const { deepClone } = require('../utils')
+const { asyncRoutes, constantRoutes } = require('./routes.js')
+
+const routes = deepClone([...constantRoutes, ...asyncRoutes])
+
+const roles = [
+  {
+    key: 'admin',
+    name: 'admin',
+    description: 'Super Administrator. Have access to view all pages.',
+    routes: routes
+  },
+  {
+    key: 'editor',
+    name: 'editor',
+    description: 'Normal Editor. Can see all pages except permission page',
+    routes: routes.filter(i => i.path !== '/permission')// just a mock
+  },
+  {
+    key: 'visitor',
+    name: 'visitor',
+    description: 'Just a visitor. Can only see the home page and the document page',
+    routes: [{
+      path: '',
+      redirect: 'dashboard',
+      children: [
+        {
+          path: 'dashboard',
+          name: 'Dashboard',
+          meta: { title: 'dashboard', icon: 'dashboard' }
+        }
+      ]
+    }]
+  }
+]
+
+module.exports = [
+  // mock get all routes form server
+  {
+    url: '/vue-element-admin/routes',
+    type: 'get',
+    response: _ => {
+      return {
+        code: 20000,
+        data: routes
+      }
+    }
+  },
+
+  // mock get all roles form server
+  {
+    url: '/vue-element-admin/roles',
+    type: 'get',
+    response: _ => {
+      return {
+        code: 20000,
+        data: roles
+      }
+    }
+  },
+
+  // add role
+  {
+    url: '/vue-element-admin/role',
+    type: 'post',
+    response: {
+      code: 20000,
+      data: {
+        key: Mock.mock('@integer(300, 5000)')
+      }
+    }
+  },
+
+  // update role
+  {
+    url: '/vue-element-admin/role/[A-Za-z0-9]',
+    type: 'put',
+    response: {
+      code: 20000,
+      data: {
+        status: 'success'
+      }
+    }
+  },
+
+  // delete role
+  {
+    url: '/vue-element-admin/role/[A-Za-z0-9]',
+    type: 'delete',
+    response: {
+      code: 20000,
+      data: {
+        status: 'success'
+      }
+    }
+  }
+]

+ 530 - 0
templates/mock copy/role/routes.js

@@ -0,0 +1,530 @@
+// Just a mock data
+
+const constantRoutes = [
+  {
+    path: '/redirect',
+    component: 'layout/Layout',
+    hidden: true,
+    children: [
+      {
+        path: '/redirect/:path*',
+        component: 'views/redirect/index'
+      }
+    ]
+  },
+  {
+    path: '/login',
+    component: 'views/login/index',
+    hidden: true
+  },
+  {
+    path: '/auth-redirect',
+    component: 'views/login/auth-redirect',
+    hidden: true
+  },
+  {
+    path: '/404',
+    component: 'views/error-page/404',
+    hidden: true
+  },
+  {
+    path: '/401',
+    component: 'views/error-page/401',
+    hidden: true
+  },
+  {
+    path: '',
+    component: 'layout/Layout',
+    redirect: 'dashboard',
+    children: [
+      {
+        path: 'dashboard',
+        component: 'views/dashboard/index',
+        name: 'Dashboard',
+        meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
+      }
+    ]
+  },
+  {
+    path: '/documentation',
+    component: 'layout/Layout',
+    children: [
+      {
+        path: 'index',
+        component: 'views/documentation/index',
+        name: 'Documentation',
+        meta: { title: 'Documentation', icon: 'documentation', affix: true }
+      }
+    ]
+  },
+  {
+    path: '/guide',
+    component: 'layout/Layout',
+    redirect: '/guide/index',
+    children: [
+      {
+        path: 'index',
+        component: 'views/guide/index',
+        name: 'Guide',
+        meta: { title: 'Guide', icon: 'guide', noCache: true }
+      }
+    ]
+  }
+]
+
+const asyncRoutes = [
+  {
+    path: '/permission',
+    component: 'layout/Layout',
+    redirect: '/permission/index',
+    alwaysShow: true,
+    meta: {
+      title: 'Permission',
+      icon: 'lock',
+      roles: ['admin', 'editor']
+    },
+    children: [
+      {
+        path: 'page',
+        component: 'views/permission/page',
+        name: 'PagePermission',
+        meta: {
+          title: 'Page Permission',
+          roles: ['admin']
+        }
+      },
+      {
+        path: 'directive',
+        component: 'views/permission/directive',
+        name: 'DirectivePermission',
+        meta: {
+          title: 'Directive Permission'
+        }
+      },
+      {
+        path: 'role',
+        component: 'views/permission/role',
+        name: 'RolePermission',
+        meta: {
+          title: 'Role Permission',
+          roles: ['admin']
+        }
+      }
+    ]
+  },
+
+  {
+    path: '/icon',
+    component: 'layout/Layout',
+    children: [
+      {
+        path: 'index',
+        component: 'views/icons/index',
+        name: 'Icons',
+        meta: { title: 'Icons', icon: 'icon', noCache: true }
+      }
+    ]
+  },
+
+  {
+    path: '/components',
+    component: 'layout/Layout',
+    redirect: 'noRedirect',
+    name: 'ComponentDemo',
+    meta: {
+      title: 'Components',
+      icon: 'component'
+    },
+    children: [
+      {
+        path: 'tinymce',
+        component: 'views/components-demo/tinymce',
+        name: 'TinymceDemo',
+        meta: { title: 'Tinymce' }
+      },
+      {
+        path: 'markdown',
+        component: 'views/components-demo/markdown',
+        name: 'MarkdownDemo',
+        meta: { title: 'Markdown' }
+      },
+      {
+        path: 'json-editor',
+        component: 'views/components-demo/json-editor',
+        name: 'JsonEditorDemo',
+        meta: { title: 'Json Editor' }
+      },
+      {
+        path: 'split-pane',
+        component: 'views/components-demo/split-pane',
+        name: 'SplitpaneDemo',
+        meta: { title: 'SplitPane' }
+      },
+      {
+        path: 'avatar-upload',
+        component: 'views/components-demo/avatar-upload',
+        name: 'AvatarUploadDemo',
+        meta: { title: 'Avatar Upload' }
+      },
+      {
+        path: 'dropzone',
+        component: 'views/components-demo/dropzone',
+        name: 'DropzoneDemo',
+        meta: { title: 'Dropzone' }
+      },
+      {
+        path: 'sticky',
+        component: 'views/components-demo/sticky',
+        name: 'StickyDemo',
+        meta: { title: 'Sticky' }
+      },
+      {
+        path: 'count-to',
+        component: 'views/components-demo/count-to',
+        name: 'CountToDemo',
+        meta: { title: 'Count To' }
+      },
+      {
+        path: 'mixin',
+        component: 'views/components-demo/mixin',
+        name: 'ComponentMixinDemo',
+        meta: { title: 'componentMixin' }
+      },
+      {
+        path: 'back-to-top',
+        component: 'views/components-demo/back-to-top',
+        name: 'BackToTopDemo',
+        meta: { title: 'Back To Top' }
+      },
+      {
+        path: 'drag-dialog',
+        component: 'views/components-demo/drag-dialog',
+        name: 'DragDialogDemo',
+        meta: { title: 'Drag Dialog' }
+      },
+      {
+        path: 'drag-select',
+        component: 'views/components-demo/drag-select',
+        name: 'DragSelectDemo',
+        meta: { title: 'Drag Select' }
+      },
+      {
+        path: 'dnd-list',
+        component: 'views/components-demo/dnd-list',
+        name: 'DndListDemo',
+        meta: { title: 'Dnd List' }
+      },
+      {
+        path: 'drag-kanban',
+        component: 'views/components-demo/drag-kanban',
+        name: 'DragKanbanDemo',
+        meta: { title: 'Drag Kanban' }
+      }
+    ]
+  },
+  {
+    path: '/charts',
+    component: 'layout/Layout',
+    redirect: 'noRedirect',
+    name: 'Charts',
+    meta: {
+      title: 'Charts',
+      icon: 'chart'
+    },
+    children: [
+      {
+        path: 'keyboard',
+        component: 'views/charts/keyboard',
+        name: 'KeyboardChart',
+        meta: { title: 'Keyboard Chart', noCache: true }
+      },
+      {
+        path: 'line',
+        component: 'views/charts/line',
+        name: 'LineChart',
+        meta: { title: 'Line Chart', noCache: true }
+      },
+      {
+        path: 'mixchart',
+        component: 'views/charts/mixChart',
+        name: 'MixChart',
+        meta: { title: 'Mix Chart', noCache: true }
+      }
+    ]
+  },
+  {
+    path: '/nested',
+    component: 'layout/Layout',
+    redirect: '/nested/menu1/menu1-1',
+    name: 'Nested',
+    meta: {
+      title: 'Nested',
+      icon: 'nested'
+    },
+    children: [
+      {
+        path: 'menu1',
+        component: 'views/nested/menu1/index',
+        name: 'Menu1',
+        meta: { title: 'Menu1' },
+        redirect: '/nested/menu1/menu1-1',
+        children: [
+          {
+            path: 'menu1-1',
+            component: 'views/nested/menu1/menu1-1',
+            name: 'Menu1-1',
+            meta: { title: 'Menu1-1' }
+          },
+          {
+            path: 'menu1-2',
+            component: 'views/nested/menu1/menu1-2',
+            name: 'Menu1-2',
+            redirect: '/nested/menu1/menu1-2/menu1-2-1',
+            meta: { title: 'Menu1-2' },
+            children: [
+              {
+                path: 'menu1-2-1',
+                component: 'views/nested/menu1/menu1-2/menu1-2-1',
+                name: 'Menu1-2-1',
+                meta: { title: 'Menu1-2-1' }
+              },
+              {
+                path: 'menu1-2-2',
+                component: 'views/nested/menu1/menu1-2/menu1-2-2',
+                name: 'Menu1-2-2',
+                meta: { title: 'Menu1-2-2' }
+              }
+            ]
+          },
+          {
+            path: 'menu1-3',
+            component: 'views/nested/menu1/menu1-3',
+            name: 'Menu1-3',
+            meta: { title: 'Menu1-3' }
+          }
+        ]
+      },
+      {
+        path: 'menu2',
+        name: 'Menu2',
+        component: 'views/nested/menu2/index',
+        meta: { title: 'Menu2' }
+      }
+    ]
+  },
+
+  {
+    path: '/example',
+    component: 'layout/Layout',
+    redirect: '/example/list',
+    name: 'Example',
+    meta: {
+      title: 'Example',
+      icon: 'example'
+    },
+    children: [
+      {
+        path: 'create',
+        component: 'views/example/create',
+        name: 'CreateArticle',
+        meta: { title: 'Create Article', icon: 'edit' }
+      },
+      {
+        path: 'edit/:id(\\d+)',
+        component: 'views/example/edit',
+        name: 'EditArticle',
+        meta: { title: 'Edit Article', noCache: true },
+        hidden: true
+      },
+      {
+        path: 'list',
+        component: 'views/example/list',
+        name: 'ArticleList',
+        meta: { title: 'Article List', icon: 'list' }
+      }
+    ]
+  },
+
+  {
+    path: '/tab',
+    component: 'layout/Layout',
+    children: [
+      {
+        path: 'index',
+        component: 'views/tab/index',
+        name: 'Tab',
+        meta: { title: 'Tab', icon: 'tab' }
+      }
+    ]
+  },
+
+  {
+    path: '/error',
+    component: 'layout/Layout',
+    redirect: 'noRedirect',
+    name: 'ErrorPages',
+    meta: {
+      title: 'Error Pages',
+      icon: '404'
+    },
+    children: [
+      {
+        path: '401',
+        component: 'views/error-page/401',
+        name: 'Page401',
+        meta: { title: 'Page 401', noCache: true }
+      },
+      {
+        path: '404',
+        component: 'views/error-page/404',
+        name: 'Page404',
+        meta: { title: 'Page 404', noCache: true }
+      }
+    ]
+  },
+
+  {
+    path: '/error-log',
+    component: 'layout/Layout',
+    redirect: 'noRedirect',
+    children: [
+      {
+        path: 'log',
+        component: 'views/error-log/index',
+        name: 'ErrorLog',
+        meta: { title: 'Error Log', icon: 'bug' }
+      }
+    ]
+  },
+
+  {
+    path: '/excel',
+    component: 'layout/Layout',
+    redirect: '/excel/export-excel',
+    name: 'Excel',
+    meta: {
+      title: 'Excel',
+      icon: 'excel'
+    },
+    children: [
+      {
+        path: 'export-excel',
+        component: 'views/excel/export-excel',
+        name: 'ExportExcel',
+        meta: { title: 'Export Excel' }
+      },
+      {
+        path: 'export-selected-excel',
+        component: 'views/excel/select-excel',
+        name: 'SelectExcel',
+        meta: { title: 'Select Excel' }
+      },
+      {
+        path: 'export-merge-header',
+        component: 'views/excel/merge-header',
+        name: 'MergeHeader',
+        meta: { title: 'Merge Header' }
+      },
+      {
+        path: 'upload-excel',
+        component: 'views/excel/upload-excel',
+        name: 'UploadExcel',
+        meta: { title: 'Upload Excel' }
+      }
+    ]
+  },
+
+  {
+    path: '/zip',
+    component: 'layout/Layout',
+    redirect: '/zip/download',
+    alwaysShow: true,
+    meta: { title: 'Zip', icon: 'zip' },
+    children: [
+      {
+        path: 'download',
+        component: 'views/zip/index',
+        name: 'ExportZip',
+        meta: { title: 'Export Zip' }
+      }
+    ]
+  },
+
+  {
+    path: '/pdf',
+    component: 'layout/Layout',
+    redirect: '/pdf/index',
+    children: [
+      {
+        path: 'index',
+        component: 'views/pdf/index',
+        name: 'PDF',
+        meta: { title: 'PDF', icon: 'pdf' }
+      }
+    ]
+  },
+  {
+    path: '/pdf/download',
+    component: 'views/pdf/download',
+    hidden: true
+  },
+
+  {
+    path: '/theme',
+    component: 'layout/Layout',
+    redirect: 'noRedirect',
+    children: [
+      {
+        path: 'index',
+        component: 'views/theme/index',
+        name: 'Theme',
+        meta: { title: 'Theme', icon: 'theme' }
+      }
+    ]
+  },
+
+  {
+    path: '/clipboard',
+    component: 'layout/Layout',
+    redirect: 'noRedirect',
+    children: [
+      {
+        path: 'index',
+        component: 'views/clipboard/index',
+        name: 'ClipboardDemo',
+        meta: { title: 'Clipboard Demo', icon: 'clipboard' }
+      }
+    ]
+  },
+
+  {
+    path: '/i18n',
+    component: 'layout/Layout',
+    children: [
+      {
+        path: 'index',
+        component: 'views/i18n-demo/index',
+        name: 'I18n',
+        meta: { title: 'I18n', icon: 'international' }
+      }
+    ]
+  },
+
+  {
+    path: 'external-link',
+    component: 'layout/Layout',
+    children: [
+      {
+        path: 'https://github.com/PanJiaChen/vue-element-admin',
+        meta: { title: 'External Link', icon: 'link' }
+      }
+    ]
+  },
+
+  { path: '*', redirect: '/404', hidden: true }
+]
+
+module.exports = {
+  constantRoutes,
+  asyncRoutes
+}

+ 84 - 0
templates/mock copy/user.js

@@ -0,0 +1,84 @@
+
+const tokens = {
+  admin: {
+    token: 'admin-token'
+  },
+  editor: {
+    token: 'editor-token'
+  }
+}
+
+const users = {
+  'admin-token': {
+    roles: ['admin'],
+    introduction: 'I am a super administrator',
+    avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
+    name: 'Super Admin'
+  },
+  'editor-token': {
+    roles: ['editor'],
+    introduction: 'I am an editor',
+    avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
+    name: 'Normal Editor'
+  }
+}
+
+module.exports = [
+  // user login
+  {
+    url: '/vue-element-admin/user/login',
+    type: 'post',
+    response: config => {
+      const { username } = config.body
+      const token = tokens[username]
+
+      // mock error
+      if (!token) {
+        return {
+          code: 60204,
+          message: 'Account and password are incorrect.'
+        }
+      }
+
+      return {
+        code: 20000,
+        data: token
+      }
+    }
+  },
+
+  // get user info
+  {
+    url: '/vue-element-admin/user/info\.*',
+    type: 'get',
+    response: config => {
+      const { token } = config.query
+      const info = users[token]
+
+      // mock error
+      if (!info) {
+        return {
+          code: 50008,
+          message: 'Login failed, unable to get user details.'
+        }
+      }
+
+      return {
+        code: 20000,
+        data: info
+      }
+    }
+  },
+
+  // user logout
+  {
+    url: '/vue-element-admin/user/logout',
+    type: 'post',
+    response: _ => {
+      return {
+        code: 20000,
+        data: 'success'
+      }
+    }
+  }
+]

+ 48 - 0
templates/mock copy/utils.js

@@ -0,0 +1,48 @@
+/**
+ * @param {string} url
+ * @returns {Object}
+ */
+function param2Obj(url) {
+  const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
+  if (!search) {
+    return {}
+  }
+  const obj = {}
+  const searchArr = search.split('&')
+  searchArr.forEach(v => {
+    const index = v.indexOf('=')
+    if (index !== -1) {
+      const name = v.substring(0, index)
+      const val = v.substring(index + 1, v.length)
+      obj[name] = val
+    }
+  })
+  return obj
+}
+
+/**
+ * This is just a simple version of deep copy
+ * Has a lot of edge cases bug
+ * If you want to use a perfect deep copy, use lodash's _.cloneDeep
+ * @param {Object} source
+ * @returns {Object}
+ */
+function deepClone(source) {
+  if (!source && typeof source !== 'object') {
+    throw new Error('error arguments', 'deepClone')
+  }
+  const targetObj = source.constructor === Array ? [] : {}
+  Object.keys(source).forEach(keys => {
+    if (source[keys] && typeof source[keys] === 'object') {
+      targetObj[keys] = deepClone(source[keys])
+    } else {
+      targetObj[keys] = source[keys]
+    }
+  })
+  return targetObj
+}
+
+module.exports = {
+  param2Obj,
+  deepClone
+}

+ 1 - 0
templates/package.json

@@ -41,6 +41,7 @@
     "vue-splitpane": "1.0.4",
     "vuedraggable": "2.20.0",
     "vuex": "3.1.0",
+    "lottie-web": "^5.7.11",
     "xlsx": "0.14.1"
   },
   "devDependencies": {

BIN
templates/public/favicon.ico


company/__init__.py → templates/src/boot/.gitkeep


+ 373 - 0
templates/src/boot/axios_request.js

@@ -0,0 +1,373 @@
+import Vue from 'vue'
+import axios from 'axios'
+
+const baseurl = 'http://192.168.31.105:8008'
+
+// 创建事件总线用于全局通信
+const Bus = new Vue()
+
+// 创建 axios 实例
+const createAxiosInstance = (baseURL = baseurl) => {
+  return axios.create({ baseURL })
+}
+
+const axiosInstance = createAxiosInstance()
+const axiosInstanceVersion = createAxiosInstance()
+const axiosInstanceAuth = createAxiosInstance()
+const axiosInstanceAuthScan = createAxiosInstance()
+const axiosFile = createAxiosInstance()
+
+// 获取语言设置
+const lang = localStorage.getItem('lang') || 'zh-hans'
+localStorage.setItem('lang', lang)
+
+// 自定义通知函数
+function showNotification(message, type = 'error') {
+  // 移除现有通知
+  const existing = document.getElementById('custom-notification')
+  if (existing) existing.remove()
+
+  // 创建通知元素
+  const notification = document.createElement('div')
+  notification.id = 'custom-notification'
+  notification.style = `
+    position: fixed;
+    top: 20px;
+    right: 20px;
+    padding: 15px 20px;
+    border-radius: 4px;
+    background-color: ${type === 'error' ? '#f56c6c' : '#67c23a'};
+    color: white;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    z-index: 9999;
+    transition: opacity 0.3s;
+    opacity: 1;
+  `
+
+  notification.textContent = message
+  document.body.appendChild(notification)
+
+  // 3秒后自动消失
+  setTimeout(() => {
+    notification.style.opacity = '0'
+    setTimeout(() => notification.remove(), 300)
+  }, 3000)
+}
+
+// 自定义加载指示器
+const loadingIndicator = {
+  count: 0,
+  element: null,
+
+  show() {
+    this.count++
+    if (this.element) return
+
+    this.element = document.createElement('div')
+    this.element.id = 'custom-loading'
+    this.element.style = `
+      position: fixed;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-color: rgba(255, 255, 255, 0.7);
+      z-index: 9998;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    `
+
+    const spinner = document.createElement('div')
+    spinner.style = `
+      width: 50px;
+      height: 50px;
+      border: 5px solid #f3f3f3;
+      border-top: 5px solid #409eff;
+      border-radius: 50%;
+      animation: spin 1s linear infinite;
+    `
+
+    // 添加旋转动画
+    const style = document.createElement('style')
+    style.textContent = `
+      @keyframes spin {
+        0% { transform: rotate(0deg); }
+        100% { transform: rotate(360deg); }
+      }
+    `
+
+    this.element.appendChild(style)
+    this.element.appendChild(spinner)
+    document.body.appendChild(this.element)
+  },
+
+  hide() {
+    this.count = Math.max(0, this.count - 1)
+    if (this.count === 0 && this.element) {
+      this.element.remove()
+      this.element = null
+    }
+  }
+}
+
+// 统一错误处理函数
+function handleApiError(error) {
+  let message = '未知错误'
+
+  if (error.code === 'ECONNABORTED' ||
+      error.message?.includes('timeout') ||
+      error.message === 'Network Error') {
+    message = '网络连接超时,请检查网络连接'
+  } else if (!error.response) {
+    message = '服务器无响应,请检查网络连接'
+  } else {
+    switch (error.response.status) {
+      case 400: message = '请求错误'; break
+      case 401: message = '未授权,请登录'; break
+      case 403: message = '拒绝访问'; break
+      case 404: message = '请求地址出错'; break
+      case 405: message = '不支持的请求方法'; break
+      case 408: message = '请求超时'; break
+      case 409: message = '请求冲突'; break
+      case 410: message = '资源已被删除'; break
+      case 500: message = '服务器内部错误'; break
+      case 501: message = '服务未实现'; break
+      case 502: message = '网关错误'; break
+      case 503: message = '服务不可用'; break
+      case 504: message = '网关超时'; break
+      case 505: message = 'HTTP版本不受支持'; break
+    }
+  }
+
+  showNotification(message, 'error')
+  loadingIndicator.hide()
+
+  // 确保错误被传播到调用方
+  return Promise.reject(error)
+}
+
+// 通用请求头设置
+function setCommonHeaders(config) {
+  config.headers = {
+    ...config.headers,
+    'Content-Type': 'application/json',
+    // 修改为使用 openid 作为 token
+    token: localStorage.getItem('openid')
+  }
+  return config
+}
+
+// 检查用户认证状态 - 修改为检查 openid
+function checkAuth() {
+  const openid = localStorage.getItem('openid')
+  return openid !== null
+}
+
+// 请求拦截器 - 认证实例
+axiosInstanceAuth.interceptors.request.use(
+  config => {
+    if (!checkAuth()) {
+      loadingIndicator.hide()
+      Bus.$emit('needLogin', true)
+      return Promise.reject(new Error('未登录'))
+    }
+
+    config = setCommonHeaders(config)
+
+    if (['post', 'patch', 'put', 'delete'].includes(config.method)) {
+      loadingIndicator.show()
+    }
+
+    return config
+  },
+  error => {
+    loadingIndicator.hide()
+    return Promise.reject(error)
+  }
+)
+
+// 响应拦截器 - 认证实例
+axiosInstanceAuth.interceptors.response.use(
+  response => {
+    if (response.data.detail && response.data.detail !== 'success') {
+      showNotification(response.data.detail, 'error')
+    }
+
+    loadingIndicator.hide()
+    return response.data
+  },
+  handleApiError
+)
+
+// 请求拦截器 - 认证扫描实例
+axiosInstanceAuthScan.interceptors.request.use(
+  config => {
+    if (!checkAuth()) {
+      loadingIndicator.hide()
+      Bus.$emit('needLogin', true)
+      return Promise.reject(new Error('未登录'))
+    }
+
+    config = setCommonHeaders(config)
+
+    if (['post', 'patch', 'put', 'delete'].includes(config.method)) {
+      loadingIndicator.show()
+    }
+
+    return config
+  },
+  error => {
+    loadingIndicator.hide()
+    return Promise.reject(error)
+  }
+)
+
+// 响应拦截器 - 认证扫描实例
+axiosInstanceAuthScan.interceptors.response.use(
+  response => {
+    loadingIndicator.hide()
+    return response.data
+  },
+  handleApiError
+)
+
+// 请求拦截器 - 基础实例
+axiosInstance.interceptors.request.use(
+  config => {
+    config.headers = {
+      ...config.headers,
+      language: lang
+    }
+
+    if (['post', 'patch', 'put', 'delete'].includes(config.method)) {
+      loadingIndicator.show()
+    }
+
+    return config
+  },
+  error => {
+    loadingIndicator.hide()
+    return Promise.reject(error)
+  }
+)
+
+// 响应拦截器 - 基础实例
+axiosInstance.interceptors.response.use(
+  response => {
+    if (response.data.detail && response.data.detail !== 'success') {
+      showNotification(response.data.detail, 'error')
+    }
+
+    loadingIndicator.hide()
+    return response.data
+  },
+  handleApiError
+)
+
+// 请求拦截器 - 版本检查实例
+axiosInstanceVersion.interceptors.request.use(
+  config => {
+    if (!checkAuth()) {
+      loadingIndicator.hide()
+      Bus.$emit('needLogin', true)
+      return Promise.reject(new Error('未登录'))
+    }
+
+    if (['post', 'patch', 'put', 'delete'].includes(config.method)) {
+      loadingIndicator.show()
+    }
+
+    return config
+  },
+  error => {
+    loadingIndicator.hide()
+    return Promise.reject(error)
+  }
+)
+
+// 响应拦截器 - 版本检查实例
+axiosInstanceVersion.interceptors.response.use(
+  response => {
+    loadingIndicator.hide()
+    return response.data
+  },
+  handleApiError
+)
+
+// 请求拦截器 - 文件实例
+axiosFile.interceptors.request.use(
+  config => {
+    if (!checkAuth()) {
+      loadingIndicator.hide()
+      Bus.$emit('needLogin', true)
+      return Promise.reject(new Error('未登录'))
+    }
+
+    config.headers = {
+      ...config.headers,
+      'Content-Type': 'application/vnd.ms-excel',
+      token: localStorage.getItem('openid'),
+      appid: localStorage.getItem('appid'),
+      operator: localStorage.getItem('login_id'),
+      language: lang
+    }
+
+    if (['post', 'patch', 'put', 'delete'].includes(config.method)) {
+      loadingIndicator.show()
+    }
+
+    return config
+  },
+  error => {
+    loadingIndicator.hide()
+    return Promise.reject(error)
+  }
+)
+
+// 响应拦截器 - 文件实例
+axiosFile.interceptors.response.use(
+  response => {
+    if (response.data.detail && response.data.detail !== 'success') {
+      showNotification(response.data.detail, 'error')
+    }
+
+    loadingIndicator.hide()
+    return response
+  },
+  handleApiError
+)
+
+// API 方法
+export const getauth = (url) => axiosInstanceAuth.get(url)
+export const get = (url) => axiosInstance.get(url)
+export const versioncheck = (url) => axiosInstanceVersion.get(url)
+export const post = (url, data) => axiosInstance.post(url, data)
+export const postauth = (url, data) => axiosInstanceAuth.post(url, data)
+export const putauth = (url, data) => axiosInstanceAuth.put(url, data)
+export const patchauth = (url, data) => axiosInstanceAuth.patch(url, data)
+export const deleteauth = (url) => axiosInstanceAuth.delete(url)
+export const viewPrintAuth = (url) => axiosInstanceAuth.get(url)
+export const scangetauth = (url) => axiosInstanceAuthScan.get(url)
+export const scanpostauth = (url, data) => axiosInstanceAuthScan.post(url, data)
+export const getfile = (url) => axiosFile.get(url)
+
+// 挂载到 Vue 原型
+Vue.prototype.$axios = axios
+Vue.prototype.$bus = Bus // 暴露事件总线
+
+export default {
+  baseurl,
+  get,
+  versioncheck,
+  post,
+  getauth,
+  postauth,
+  putauth,
+  deleteauth,
+  patchauth,
+  viewPrintAuth,
+  getfile,
+  scangetauth,
+  scanpostauth
+}

+ 2 - 0
templates/src/boot/bus.js

@@ -0,0 +1,2 @@
+import Vue from 'vue'
+export default new Vue()

+ 25 - 0
templates/src/boot/i18n.js

@@ -0,0 +1,25 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import messages from 'src/i18n'
+
+Vue.use(VueI18n)
+
+var lang = localStorage.getItem('lang')
+if (localStorage.getItem('lang')) {
+  lang = lang || 'en-US'
+} else {
+  localStorage.setItem('lang', 'en-US')
+  lang = 'en-US'
+}
+
+const i18n = new VueI18n({
+  locale: lang,
+  fallbackLocale: lang,
+  messages
+})
+
+export default ({ app }) => {
+  app.i18n = i18n
+}
+
+export { i18n }

+ 7 - 0
templates/src/boot/notify_default.js

@@ -0,0 +1,7 @@
+import { Notify } from 'quasar'
+
+Notify.setDefaults({
+  position: 'right',
+  timeout: 2500,
+  textColor: 'white'
+})

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
templates/src/components/LottieWeb/Login.json


+ 51 - 0
templates/src/components/LottieWeb/lottie-web-cimo.vue

@@ -0,0 +1,51 @@
+<template>
+  <div ref="lottieBox" />
+</template>
+
+<script>
+import lottie from 'lottie-web'
+
+export default {
+  name: 'LottieWebCimo',
+  props: {
+    animationName: {
+      type: String,
+      required: true,
+      validator: function(value) {
+        // console.log(value)
+        // 确保传入的动画名称是预定义的
+        return ['welcome', 'warehouse', 'meeting', 'login'].indexOf(value) !== -1
+      }
+    }
+  },
+  data() {
+    return {
+      lottie: {},
+      animationData: null,
+      loop: true
+    }
+  },
+  mounted() {
+    this.initLottie()
+  },
+  beforeDestroy() {
+    this.lottie.destroy()
+    this.lottie = null
+  },
+  methods: {
+    initLottie: function() {
+      var _this = this
+      // 根据 animationName 加载不同的动画数据
+      _this.animationData = require(`./${_this.animationName}.json`)
+
+      _this.lottie = lottie.loadAnimation({
+        container: _this.$refs.lottieBox,
+        renderer: 'svg',
+        loop: _this.loop || true,
+        animationData: _this.animationData
+      })
+      _this.lottie.setSpeed(0.5)
+    }
+  }
+}
+</script>

Разница между файлами не показана из-за своего большого размера
+ 1 - 0
templates/src/components/LottieWeb/meeting.json


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
templates/src/components/LottieWeb/warehouse.json


Разница между файлами не показана из-за своего большого размера
+ 1 - 0
templates/src/components/LottieWeb/welcome.json


+ 4 - 9
templates/src/layout/components/Navbar.vue

@@ -1,3 +1,4 @@
+<!-- 顶部窗口导航栏 -->
 <template>
   <div class="navbar">
     <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
@@ -25,19 +26,13 @@
         </div>
         <el-dropdown-menu slot="dropdown">
           <router-link to="/profile/index">
-            <el-dropdown-item>Profile</el-dropdown-item>
+            <el-dropdown-item>个人中心</el-dropdown-item>
           </router-link>
           <router-link to="/">
-            <el-dropdown-item>Dashboard</el-dropdown-item>
+            <el-dropdown-item>首页</el-dropdown-item>
           </router-link>
-          <a target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">
-            <el-dropdown-item>Github</el-dropdown-item>
-          </a>
-          <a target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/#/">
-            <el-dropdown-item>Docs</el-dropdown-item>
-          </a>
           <el-dropdown-item divided @click.native="logout">
-            <span style="display:block;">Log Out</span>
+            <span style="display:block;">退出登录</span>
           </el-dropdown-item>
         </el-dropdown-menu>
       </el-dropdown>

+ 4 - 4
templates/src/layout/components/Settings/index.vue

@@ -4,22 +4,22 @@
       <h3 class="drawer-title">Page style setting</h3>
 
       <div class="drawer-item">
-        <span>Theme Color</span>
+        <span>主题色</span>
         <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
       </div>
 
       <div class="drawer-item">
-        <span>Open Tags-View</span>
+        <span>开启书签</span>
         <el-switch v-model="tagsView" class="drawer-switch" />
       </div>
 
       <div class="drawer-item">
-        <span>Fixed Header</span>
+        <span>固定头部</span>
         <el-switch v-model="fixedHeader" class="drawer-switch" />
       </div>
 
       <div class="drawer-item">
-        <span>Sidebar Logo</span>
+        <span>侧边栏 Logo</span>
         <el-switch v-model="sidebarLogo" class="drawer-switch" />
       </div>
 

+ 11 - 6
templates/src/layout/components/Sidebar/Logo.vue

@@ -1,13 +1,18 @@
 <template>
-  <div class="sidebar-logo-container" :class="{'collapse':collapse}">
+  <div class="sidebar-logo-container" :class="{ collapse: collapse }">
     <transition name="sidebarLogoFade">
-      <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
+      <router-link
+        v-if="collapse"
+        key="collapse"
+        class="sidebar-logo-link"
+        to="/"
+      >
         <img v-if="logo" :src="logo" class="sidebar-logo">
-        <h1 v-else class="sidebar-title">{{ title }} </h1>
+        <h1 v-else class="sidebar-title">{{ title }}</h1>
       </router-link>
       <router-link v-else key="expand" class="sidebar-logo-link" to="/">
         <img v-if="logo" :src="logo" class="sidebar-logo">
-        <h1 class="sidebar-title">{{ title }} </h1>
+        <h1 class="sidebar-title">{{ title }}</h1>
       </router-link>
     </transition>
   </div>
@@ -24,8 +29,8 @@ export default {
   },
   data() {
     return {
-      title: 'Vue Element Admin',
-      logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
+      title: '机器人感知与信息融合小组',
+      logo: '/favicon.ico' // 本地 public 目录下的路径
     }
   }
 }

+ 4 - 4
templates/src/layout/components/TagsView/index.vue

@@ -17,10 +17,10 @@
       </router-link>
     </scroll-pane>
     <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
-      <li @click="refreshSelectedTag(selectedTag)">Refresh</li>
-      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
-      <li @click="closeOthersTags">Close Others</li>
-      <li @click="closeAllTags(selectedTag)">Close All</li>
+      <li @click="refreshSelectedTag(selectedTag)">刷新</li>
+      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li>
+      <li @click="closeOthersTags">关闭其他</li>
+      <li @click="closeAllTags(selectedTag)">关闭全部</li>
     </ul>
   </div>
 </template>

+ 7 - 7
templates/src/main.js

@@ -6,7 +6,7 @@ import 'normalize.css/normalize.css' // a modern alternative to CSS resets
 
 import Element from 'element-ui'
 import './styles/element-variables.scss'
-import enLang from 'element-ui/lib/locale/lang/en'// 如果使用中文语言包请默认支持,无需额外引入,请删除该依赖
+// import enLang from 'element-ui/lib/locale/lang/en'// 如果使用中文语言包请默认支持,无需额外引入,请删除该依赖
 
 import '@/styles/index.scss' // global css
 
@@ -28,14 +28,14 @@ import * as filters from './filters' // global filters
  * Currently MockJs will be used in the production environment,
  * please remove it before going online ! ! !
  */
-if (process.env.NODE_ENV === 'production') {
-  const { mockXHR } = require('../mock')
-  mockXHR()
-}
+// if (process.env.NODE_ENV === 'production') {
+//   const { mockXHR } = require('../mock')
+//   mockXHR()
+// }
 
 Vue.use(Element, {
-  size: Cookies.get('size') || 'medium', // set element-ui default size
-  locale: enLang // 如果使用中文,无需设置,请删除
+  size: Cookies.get('size') || 'medium' // set element-ui default size
+  // locale: enLang // 如果使用中文,无需设置,请删除
 })
 
 // register global utility filters

+ 25 - 17
templates/src/permission.js

@@ -10,43 +10,50 @@ NProgress.configure({ showSpinner: false }) // NProgress Configuration
 
 const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
 
+// 导航守卫,用于在路由跳转前进行权限验证
 router.beforeEach(async(to, from, next) => {
   // start progress bar
   NProgress.start()
 
-  // set page title
+  // 设置页面标题
   document.title = getPageTitle(to.meta.title)
 
-  // determine whether the user has logged in
+  // 确定用户是否已登录
   const hasToken = getToken()
-
+  console.log('[permission.js]当前的用户[token]', hasToken)
   if (hasToken) {
     if (to.path === '/login') {
-      // if is logged in, redirect to the home page
+      // 如果已登录,重定向到主页
       next({ path: '/' })
       NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
     } else {
-      // determine whether the user has obtained his permission roles through getInfo
+      // 确定用户是否已通过 getInfo 获取角色信息
+      console.log('[src/permission.js]当前的用户角色[store.getters.roles]', store.getters.roles)
       const hasRoles = store.getters.roles && store.getters.roles.length > 0
+      console.log('[src/permission.js]当前的用户是否有角色[hasRoles]', hasRoles)
       if (hasRoles) {
         next()
       } else {
         try {
-          // get user info
-          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
+          // 获取用户信息
+          // 注意: roles 必须是对象数组! 例如: ['admin'] 或 ['developer','editor']
           const { roles } = await store.dispatch('user/getInfo')
+          console.log('[src/permission.js]当前的用户角色[roles]', roles)
+          // 根据角色生成可访问的路由映射
+          const accessRoutes = await store.dispatch(
+            'permission/generateRoutes',
+            roles
+          )
+          console.log('[src/permission.js]当前的用户可访问路由', accessRoutes)
 
-          // generate accessible routes map based on roles
-          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
-
-          // dynamically add accessible routes
+          // 动态添加可访问的路由
           router.addRoutes(accessRoutes)
 
-          // hack method to ensure that addRoutes is complete
-          // set the replace: true, so the navigation will not leave a history record
+          // hack 方法确保 addRoutes 完成
+          // 设置 replace: true, 使导航不会留下历史记录
           next({ ...to, replace: true })
         } catch (error) {
-          // remove token and go to login page to re-login
+          // 移除 token 并重定向到登录页面重新登录
           await store.dispatch('user/resetToken')
           Message.error(error || 'Has Error')
           next(`/login?redirect=${to.path}`)
@@ -58,17 +65,18 @@ router.beforeEach(async(to, from, next) => {
     /* has no token*/
 
     if (whiteList.indexOf(to.path) !== -1) {
-      // in the free login whitelist, go directly
+      // 如果在无需登录的白名单中,直接进入
       next()
     } else {
-      // other pages that do not have permission to access are redirected to the login page.
+      // 其他没有权限访问的页面重定向到登录页面
       next(`/login?redirect=${to.path}`)
       NProgress.done()
     }
   }
 })
 
+// 导航守卫,用于在路由跳转后进行处理
 router.afterEach(() => {
-  // finish progress bar
+  // 完成进度条
   NProgress.done()
 })

+ 32 - 92
templates/src/router/index.js

@@ -12,32 +12,6 @@ import chartsRouter from './modules/charts'
 import tableRouter from './modules/table'
 import nestedRouter from './modules/nested'
 
-/**
- * Note: sub-menu only appear when route children.length >= 1
- * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
- *
- * hidden: true                   if set true, item will not show in the sidebar(default is false)
- * alwaysShow: true               if set true, will always show the root menu
- *                                if not set alwaysShow, when item has more than one children route,
- *                                it will becomes nested mode, otherwise not show the root menu
- * redirect: noRedirect           if set noRedirect will no redirect in the breadcrumb
- * name:'router-name'             the name is used by <keep-alive> (must set!!!)
- * meta : {
-    roles: ['admin','editor']    control the page roles (you can set multiple roles)
-    title: 'title'               the name show in sidebar and breadcrumb (recommend set)
-    icon: 'svg-name'/'el-icon-x' the icon show in the sidebar
-    noCache: true                if set true, the page will no be cached(default is false)
-    affix: true                  if set true, the tag will affix in the tags-view
-    breadcrumb: false            if set false, the item will hidden in breadcrumb(default is true)
-    activeMenu: '/example/list'  if set path, the sidebar will highlight the path you set
-  }
- */
-
-/**
- * constantRoutes
- * a base page that does not have permission requirements
- * all roles can be accessed
- */
 export const constantRoutes = [
   {
     path: '/redirect',
@@ -79,19 +53,7 @@ export const constantRoutes = [
         path: 'dashboard',
         component: () => import('@/views/dashboard/index'),
         name: 'Dashboard',
-        meta: { title: 'Dashboard', icon: 'dashboard', affix: true }
-      }
-    ]
-  },
-  {
-    path: '/documentation',
-    component: Layout,
-    children: [
-      {
-        path: 'index',
-        component: () => import('@/views/documentation/index'),
-        name: 'Documentation',
-        meta: { title: 'Documentation', icon: 'documentation', affix: true }
+        meta: { title: '主页', icon: 'dashboard', affix: true }
       }
     ]
   },
@@ -104,7 +66,7 @@ export const constantRoutes = [
         path: 'index',
         component: () => import('@/views/guide/index'),
         name: 'Guide',
-        meta: { title: 'Guide', icon: 'guide', noCache: true }
+        meta: { title: '引导', icon: 'guide', noCache: true }
       }
     ]
   },
@@ -118,27 +80,23 @@ export const constantRoutes = [
         path: 'index',
         component: () => import('@/views/profile/index'),
         name: 'Profile',
-        meta: { title: 'Profile', icon: 'user', noCache: true }
+        meta: { title: '个人中心', icon: 'user', noCache: true }
       }
     ]
   }
 ]
 
-/**
- * asyncRoutes
- * the routes that need to be dynamically loaded based on user roles
- */
 export const asyncRoutes = [
   {
     path: '/permission',
     component: Layout,
     redirect: '/permission/page',
-    alwaysShow: true, // will always show the root menu
+    alwaysShow: true,
     name: 'Permission',
     meta: {
-      title: 'Permission',
+      title: '权限管理',
       icon: 'lock',
-      roles: ['admin', 'editor'] // you can set roles in root nav
+      roles: ['admin', 'editor']
     },
     children: [
       {
@@ -146,8 +104,8 @@ export const asyncRoutes = [
         component: () => import('@/views/permission/page'),
         name: 'PagePermission',
         meta: {
-          title: 'Page Permission',
-          roles: ['admin'] // or you can only set roles in sub nav
+          title: '页面权限',
+          roles: ['admin']
         }
       },
       {
@@ -155,8 +113,7 @@ export const asyncRoutes = [
         component: () => import('@/views/permission/directive'),
         name: 'DirectivePermission',
         meta: {
-          title: 'Directive Permission'
-          // if do not set roles, means: this page does not require permission
+          title: '指令权限'
         }
       },
       {
@@ -164,13 +121,12 @@ export const asyncRoutes = [
         component: () => import('@/views/permission/role'),
         name: 'RolePermission',
         meta: {
-          title: 'Role Permission',
+          title: '角色权限',
           roles: ['admin']
         }
       }
     ]
   },
-
   {
     path: '/icon',
     component: Layout,
@@ -179,24 +135,21 @@ export const asyncRoutes = [
         path: 'index',
         component: () => import('@/views/icons/index'),
         name: 'Icons',
-        meta: { title: 'Icons', icon: 'icon', noCache: true }
+        meta: { title: '图标库', icon: 'icon', noCache: true }
       }
     ]
   },
-
-  /** when your routing map is too long, you can split it into small modules **/
   componentsRouter,
   chartsRouter,
   nestedRouter,
   tableRouter,
-
   {
     path: '/example',
     component: Layout,
     redirect: '/example/list',
     name: 'Example',
     meta: {
-      title: 'Example',
+      title: '示例中心',
       icon: 'el-icon-s-help'
     },
     children: [
@@ -204,24 +157,23 @@ export const asyncRoutes = [
         path: 'create',
         component: () => import('@/views/example/create'),
         name: 'CreateArticle',
-        meta: { title: 'Create Article', icon: 'edit' }
+        meta: { title: '创建文章', icon: 'edit' }
       },
       {
         path: 'edit/:id(\\d+)',
         component: () => import('@/views/example/edit'),
         name: 'EditArticle',
-        meta: { title: 'Edit Article', noCache: true, activeMenu: '/example/list' },
+        meta: { title: '编辑文章', noCache: true, activeMenu: '/example/list' },
         hidden: true
       },
       {
         path: 'list',
         component: () => import('@/views/example/list'),
         name: 'ArticleList',
-        meta: { title: 'Article List', icon: 'list' }
+        meta: { title: '文章列表', icon: 'list' }
       }
     ]
   },
-
   {
     path: '/tab',
     component: Layout,
@@ -230,18 +182,17 @@ export const asyncRoutes = [
         path: 'index',
         component: () => import('@/views/tab/index'),
         name: 'Tab',
-        meta: { title: 'Tab', icon: 'tab' }
+        meta: { title: '标签页管理', icon: 'tab' }
       }
     ]
   },
-
   {
     path: '/error',
     component: Layout,
     redirect: 'noRedirect',
     name: 'ErrorPages',
     meta: {
-      title: 'Error Pages',
+      title: '错误页面',
       icon: '404'
     },
     children: [
@@ -249,17 +200,16 @@ export const asyncRoutes = [
         path: '401',
         component: () => import('@/views/error-page/401'),
         name: 'Page401',
-        meta: { title: '401', noCache: true }
+        meta: { title: '401错误页', noCache: true }
       },
       {
         path: '404',
         component: () => import('@/views/error-page/404'),
         name: 'Page404',
-        meta: { title: '404', noCache: true }
+        meta: { title: '404错误页', noCache: true }
       }
     ]
   },
-
   {
     path: '/error-log',
     component: Layout,
@@ -268,18 +218,17 @@ export const asyncRoutes = [
         path: 'log',
         component: () => import('@/views/error-log/index'),
         name: 'ErrorLog',
-        meta: { title: 'Error Log', icon: 'bug' }
+        meta: { title: '错误日志', icon: 'bug' }
       }
     ]
   },
-
   {
     path: '/excel',
     component: Layout,
     redirect: '/excel/export-excel',
     name: 'Excel',
     meta: {
-      title: 'Excel',
+      title: 'Excel操作',
       icon: 'excel'
     },
     children: [
@@ -287,46 +236,44 @@ export const asyncRoutes = [
         path: 'export-excel',
         component: () => import('@/views/excel/export-excel'),
         name: 'ExportExcel',
-        meta: { title: 'Export Excel' }
+        meta: { title: 'Excel导出' }
       },
       {
         path: 'export-selected-excel',
         component: () => import('@/views/excel/select-excel'),
         name: 'SelectExcel',
-        meta: { title: 'Export Selected' }
+        meta: { title: '选择导出' }
       },
       {
         path: 'export-merge-header',
         component: () => import('@/views/excel/merge-header'),
         name: 'MergeHeader',
-        meta: { title: 'Merge Header' }
+        meta: { title: '合并表头' }
       },
       {
         path: 'upload-excel',
         component: () => import('@/views/excel/upload-excel'),
         name: 'UploadExcel',
-        meta: { title: 'Upload Excel' }
+        meta: { title: 'Excel上传' }
       }
     ]
   },
-
   {
     path: '/zip',
     component: Layout,
     redirect: '/zip/download',
     alwaysShow: true,
     name: 'Zip',
-    meta: { title: 'Zip', icon: 'zip' },
+    meta: { title: '压缩管理', icon: 'zip' },
     children: [
       {
         path: 'download',
         component: () => import('@/views/zip/index'),
         name: 'ExportZip',
-        meta: { title: 'Export Zip' }
+        meta: { title: '导出压缩' }
       }
     ]
   },
-
   {
     path: '/pdf',
     component: Layout,
@@ -336,7 +283,7 @@ export const asyncRoutes = [
         path: 'index',
         component: () => import('@/views/pdf/index'),
         name: 'PDF',
-        meta: { title: 'PDF', icon: 'pdf' }
+        meta: { title: 'PDF操作', icon: 'pdf' }
       }
     ]
   },
@@ -345,7 +292,6 @@ export const asyncRoutes = [
     component: () => import('@/views/pdf/download'),
     hidden: true
   },
-
   {
     path: '/theme',
     component: Layout,
@@ -354,11 +300,10 @@ export const asyncRoutes = [
         path: 'index',
         component: () => import('@/views/theme/index'),
         name: 'Theme',
-        meta: { title: 'Theme', icon: 'theme' }
+        meta: { title: '主题设置', icon: 'theme' }
       }
     ]
   },
-
   {
     path: '/clipboard',
     component: Layout,
@@ -367,38 +312,33 @@ export const asyncRoutes = [
         path: 'index',
         component: () => import('@/views/clipboard/index'),
         name: 'ClipboardDemo',
-        meta: { title: 'Clipboard', icon: 'clipboard' }
+        meta: { title: '剪贴板', icon: 'clipboard' }
       }
     ]
   },
-
   {
     path: 'external-link',
     component: Layout,
     children: [
       {
         path: 'https://github.com/PanJiaChen/vue-element-admin',
-        meta: { title: 'External Link', icon: 'link' }
+        meta: { title: '外部链接', icon: 'link' }
       }
     ]
   },
-
-  // 404 page must be placed at the end !!!
   { path: '*', redirect: '/404', hidden: true }
 ]
 
 const createRouter = () => new Router({
-  // mode: 'history', // require service support
   scrollBehavior: () => ({ y: 0 }),
   routes: constantRoutes
 })
 
 const router = createRouter()
 
-// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
 export function resetRouter() {
   const newRouter = createRouter()
-  router.matcher = newRouter.matcher // reset router
+  router.matcher = newRouter.matcher
 }
 
 export default router

+ 5 - 5
templates/src/router/modules/charts.js

@@ -1,4 +1,4 @@
-/** When your routing table is too long, you can split it into small modules**/
+/** 当路由表过长时,可以拆分成小模块 **/
 
 import Layout from '@/layout'
 
@@ -8,7 +8,7 @@ const chartsRouter = {
   redirect: 'noRedirect',
   name: 'Charts',
   meta: {
-    title: 'Charts',
+    title: '图表管理', // 翻译为中文
     icon: 'chart'
   },
   children: [
@@ -16,19 +16,19 @@ const chartsRouter = {
       path: 'keyboard',
       component: () => import('@/views/charts/keyboard'),
       name: 'KeyboardChart',
-      meta: { title: 'Keyboard Chart', noCache: true }
+      meta: { title: '键盘图表', noCache: true }
     },
     {
       path: 'line',
       component: () => import('@/views/charts/line'),
       name: 'LineChart',
-      meta: { title: 'Line Chart', noCache: true }
+      meta: { title: '折线图', noCache: true }
     },
     {
       path: 'mix-chart',
       component: () => import('@/views/charts/mix-chart'),
       name: 'MixChart',
-      meta: { title: 'Mix Chart', noCache: true }
+      meta: { title: '混合图表', noCache: true }
     }
   ]
 }

+ 16 - 16
templates/src/router/modules/components.js

@@ -1,4 +1,4 @@
-/** When your routing table is too long, you can split it into small modules **/
+/** 当路由表过长时,可以拆分成小模块 **/
 
 import Layout from '@/layout'
 
@@ -8,7 +8,7 @@ const componentsRouter = {
   redirect: 'noRedirect',
   name: 'ComponentDemo',
   meta: {
-    title: 'Components',
+    title: '组件管理',
     icon: 'component'
   },
   children: [
@@ -16,85 +16,85 @@ const componentsRouter = {
       path: 'tinymce',
       component: () => import('@/views/components-demo/tinymce'),
       name: 'TinymceDemo',
-      meta: { title: 'Tinymce' }
+      meta: { title: '富文本编辑器' }
     },
     {
       path: 'markdown',
       component: () => import('@/views/components-demo/markdown'),
       name: 'MarkdownDemo',
-      meta: { title: 'Markdown' }
+      meta: { title: 'Markdown编辑器' }
     },
     {
       path: 'json-editor',
       component: () => import('@/views/components-demo/json-editor'),
       name: 'JsonEditorDemo',
-      meta: { title: 'JSON Editor' }
+      meta: { title: 'JSON编辑器' }
     },
     {
       path: 'split-pane',
       component: () => import('@/views/components-demo/split-pane'),
       name: 'SplitpaneDemo',
-      meta: { title: 'SplitPane' }
+      meta: { title: '分割面板' }
     },
     {
       path: 'avatar-upload',
       component: () => import('@/views/components-demo/avatar-upload'),
       name: 'AvatarUploadDemo',
-      meta: { title: 'Upload' }
+      meta: { title: '头像上传' }
     },
     {
       path: 'dropzone',
       component: () => import('@/views/components-demo/dropzone'),
       name: 'DropzoneDemo',
-      meta: { title: 'Dropzone' }
+      meta: { title: '拖放上传' }
     },
     {
       path: 'sticky',
       component: () => import('@/views/components-demo/sticky'),
       name: 'StickyDemo',
-      meta: { title: 'Sticky' }
+      meta: { title: '粘性定位' }
     },
     {
       path: 'count-to',
       component: () => import('@/views/components-demo/count-to'),
       name: 'CountToDemo',
-      meta: { title: 'Count To' }
+      meta: { title: '数字滚动' }
     },
     {
       path: 'mixin',
       component: () => import('@/views/components-demo/mixin'),
       name: 'ComponentMixinDemo',
-      meta: { title: 'Component Mixin' }
+      meta: { title: '组件混入' }
     },
     {
       path: 'back-to-top',
       component: () => import('@/views/components-demo/back-to-top'),
       name: 'BackToTopDemo',
-      meta: { title: 'Back To Top' }
+      meta: { title: '返回顶部' }
     },
     {
       path: 'drag-dialog',
       component: () => import('@/views/components-demo/drag-dialog'),
       name: 'DragDialogDemo',
-      meta: { title: 'Drag Dialog' }
+      meta: { title: '拖拽对话框' }
     },
     {
       path: 'drag-select',
       component: () => import('@/views/components-demo/drag-select'),
       name: 'DragSelectDemo',
-      meta: { title: 'Drag Select' }
+      meta: { title: '拖拽选择' }
     },
     {
       path: 'dnd-list',
       component: () => import('@/views/components-demo/dnd-list'),
       name: 'DndListDemo',
-      meta: { title: 'Dnd List' }
+      meta: { title: '拖拽列表' }
     },
     {
       path: 'drag-kanban',
       component: () => import('@/views/components-demo/drag-kanban'),
       name: 'DragKanbanDemo',
-      meta: { title: 'Drag Kanban' }
+      meta: { title: '拖拽看板' }
     }
   ]
 }

+ 10 - 10
templates/src/router/modules/nested.js

@@ -1,4 +1,4 @@
-/** When your routing table is too long, you can split it into small modules **/
+/** 当路由表过长时,可以拆分成小模块 **/
 
 import Layout from '@/layout'
 
@@ -8,41 +8,41 @@ const nestedRouter = {
   redirect: '/nested/menu1/menu1-1',
   name: 'Nested',
   meta: {
-    title: 'Nested Routes',
+    title: '嵌套路由',
     icon: 'nested'
   },
   children: [
     {
       path: 'menu1',
-      component: () => import('@/views/nested/menu1/index'), // Parent router-view
+      component: () => import('@/views/nested/menu1/index'), // 父级路由视图
       name: 'Menu1',
-      meta: { title: 'Menu 1' },
+      meta: { title: '菜单1' },
       redirect: '/nested/menu1/menu1-1',
       children: [
         {
           path: 'menu1-1',
           component: () => import('@/views/nested/menu1/menu1-1'),
           name: 'Menu1-1',
-          meta: { title: 'Menu 1-1' }
+          meta: { title: '菜单1-1' }
         },
         {
           path: 'menu1-2',
           component: () => import('@/views/nested/menu1/menu1-2'),
           name: 'Menu1-2',
           redirect: '/nested/menu1/menu1-2/menu1-2-1',
-          meta: { title: 'Menu 1-2' },
+          meta: { title: '菜单1-2' },
           children: [
             {
               path: 'menu1-2-1',
               component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
               name: 'Menu1-2-1',
-              meta: { title: 'Menu 1-2-1' }
+              meta: { title: '菜单1-2-1' }
             },
             {
               path: 'menu1-2-2',
               component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
               name: 'Menu1-2-2',
-              meta: { title: 'Menu 1-2-2' }
+              meta: { title: '菜单1-2-2' }
             }
           ]
         },
@@ -50,7 +50,7 @@ const nestedRouter = {
           path: 'menu1-3',
           component: () => import('@/views/nested/menu1/menu1-3'),
           name: 'Menu1-3',
-          meta: { title: 'Menu 1-3' }
+          meta: { title: '菜单1-3' }
         }
       ]
     },
@@ -58,7 +58,7 @@ const nestedRouter = {
       path: 'menu2',
       name: 'Menu2',
       component: () => import('@/views/nested/menu2/index'),
-      meta: { title: 'Menu 2' }
+      meta: { title: '菜单2' }
     }
   ]
 }

+ 5 - 5
templates/src/router/modules/table.js

@@ -8,7 +8,7 @@ const tableRouter = {
   redirect: '/table/complex-table',
   name: 'Table',
   meta: {
-    title: 'Table',
+    title: '表格',
     icon: 'table'
   },
   children: [
@@ -16,25 +16,25 @@ const tableRouter = {
       path: 'dynamic-table',
       component: () => import('@/views/table/dynamic-table/index'),
       name: 'DynamicTable',
-      meta: { title: 'Dynamic Table' }
+      meta: { title: '动态表格' }
     },
     {
       path: 'drag-table',
       component: () => import('@/views/table/drag-table'),
       name: 'DragTable',
-      meta: { title: 'Drag Table' }
+      meta: { title: '拖拽表格' }
     },
     {
       path: 'inline-edit-table',
       component: () => import('@/views/table/inline-edit-table'),
       name: 'InlineEditTable',
-      meta: { title: 'Inline Edit' }
+      meta: { title: '内联编辑表格' }
     },
     {
       path: 'complex-table',
       component: () => import('@/views/table/complex-table'),
       name: 'ComplexTable',
-      meta: { title: 'Complex Table' }
+      meta: { title: '综合表格' }
     }
   ]
 }

+ 1 - 1
templates/src/settings.js

@@ -1,5 +1,5 @@
 module.exports = {
-  title: 'Vue Element Admin',
+  title: '机器人感知与信息融合小组',
 
   /**
    * @type {boolean} true | false

+ 1 - 0
templates/src/store/getters.js

@@ -1,3 +1,4 @@
+//  存储数据的地方,可以理解为全局变量,可以直接通过 this.$store.getters.xxx 获取数据
 const getters = {
   sidebar: state => state.app.sidebar,
   size: state => state.app.size,

+ 2 - 0
templates/src/store/index.js

@@ -7,6 +7,7 @@ Vue.use(Vuex)
 // https://webpack.js.org/guides/dependency-management/#requirecontext
 const modulesFiles = require.context('./modules', true, /\.js$/)
 
+// 通过 require.context 自动加载所有模块文件
 // you do not need `import app from './modules/app'`
 // it will auto require all vuex module from modules file
 const modules = modulesFiles.keys().reduce((modules, modulePath) => {
@@ -17,6 +18,7 @@ const modules = modulesFiles.keys().reduce((modules, modulePath) => {
   return modules
 }, {})
 
+// 创建 Vuex Store 实例
 const store = new Vuex.Store({
   modules,
   getters

+ 83 - 54
templates/src/store/modules/user.js

@@ -1,6 +1,9 @@
-import { login, logout, getInfo } from '@/api/user'
+// 该文件是用户模块的 store 文件,用于管理用户相关的数据
+import { logout } from '@/api/user'
+
 import { getToken, setToken, removeToken } from '@/utils/auth'
 import router, { resetRouter } from '@/router'
+import { post, getauth } from '@/boot/axios_request'
 
 const state = {
   token: getToken(),
@@ -29,72 +32,99 @@ const mutations = {
 }
 
 const actions = {
-  // user login
+  // 用户登录
   login({ commit }, userInfo) {
     const { username, password } = userInfo
+    console.log('[user.js]当前的用户名', username)
     return new Promise((resolve, reject) => {
-      login({ username: username.trim(), password: password }).then(response => {
-        const { data } = response
-        commit('SET_TOKEN', data.token)
-        setToken(data.token)
-        resolve()
-      }).catch(error => {
-        reject(error)
+      post('login/', {
+        name: username.trim(),
+        password: password
       })
+        .then((response) => {
+          if (response.code === '200') {
+            console.log('[user.js]当前[login]的response', response)
+            // 登录成功处理
+            const token = response.data.openid || 'admin-token'
+            // 存储 token 和用户信息
+            localStorage.setItem('token', token)
+            localStorage.setItem('openid', token)
+
+            // commit('SET_ROLES', roles)
+            commit('SET_TOKEN', token)
+            setToken(token)
+            resolve()
+          } else {
+            // 登录失败处理
+            console.log('[user.js]当前的msg', response.msg)
+            reject(new Error(response.msg || '用户名或密码错误'))
+          }
+        })
+        .catch((error) => {
+          reject(error)
+        })
     })
   },
 
-  // get user info
+  // 获取用户信息
   getInfo({ commit, state }) {
+    console.log('[user.js]当前[getInfo]的token', state.token)
+    const openid = localStorage.getItem('openid')
+    console.log('[user.js]当前[getInfo]的openid', openid)
+
     return new Promise((resolve, reject) => {
-      getInfo(state.token).then(response => {
-        const { data } = response
-
-        if (!data) {
-          reject('Verification failed, please Login again.')
-        }
-
-        const { roles, name, avatar, introduction } = data
-
-        // roles must be a non-empty array
-        if (!roles || roles.length <= 0) {
-          reject('getInfo: roles must be a non-null array!')
-        }
-
-        commit('SET_ROLES', roles)
-        commit('SET_NAME', name)
-        commit('SET_AVATAR', avatar)
-        commit('SET_INTRODUCTION', introduction)
-        resolve(data)
-      }).catch(error => {
-        reject(error)
-      })
+      getauth('user/profile/?openid=' + openid)
+        .then((response) => {
+          console.log('[user.js]当前[getInfo]的response', response.results[0])
+          const data = response.results[0]
+          console.log('[user.js]当前[getInfo]的data', data)
+          if (!data) {
+            reject('Verification failed, please Login again.')
+          }
+
+          const { roles, name, avatar, introduction } = data
+
+          if (!roles || roles.length <= 0) {
+            reject('getInfo: roles must be a non-null array!')
+          }
+
+          commit('SET_ROLES', roles)
+          commit('SET_NAME', name)
+          commit('SET_AVATAR', avatar)
+          commit('SET_INTRODUCTION', introduction)
+          console.log('[user.js]当前的roles', roles)
+          console.log('[user.js]当前的name', name)
+          console.log('[user.js]当前的avatar', avatar)
+          console.log('[user.js]当前的introduction', introduction)
+          resolve(data)
+        })
+        .catch((error) => {
+          reject(error)
+        })
     })
   },
 
-  // user logout
+  // 用户登出
   logout({ commit, state, dispatch }) {
     return new Promise((resolve, reject) => {
-      logout(state.token).then(() => {
-        commit('SET_TOKEN', '')
-        commit('SET_ROLES', [])
-        removeToken()
-        resetRouter()
-
-        // reset visited views and cached views
-        // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
-        dispatch('tagsView/delAllViews', null, { root: true })
-
-        resolve()
-      }).catch(error => {
-        reject(error)
-      })
+      logout(state.token)
+        .then(() => {
+          commit('SET_TOKEN', '')
+          commit('SET_ROLES', [])
+          removeToken()
+          resetRouter()
+          dispatch('tagsView/delAllViews', null, { root: true })
+          resolve()
+        })
+        .catch((error) => {
+          reject(error)
+        })
     })
   },
 
-  // remove token
+  // 重置 token
   resetToken({ commit }) {
-    return new Promise(resolve => {
+    return new Promise((resolve) => {
       commit('SET_TOKEN', '')
       commit('SET_ROLES', [])
       removeToken()
@@ -102,7 +132,7 @@ const actions = {
     })
   },
 
-  // dynamically modify permissions
+  // 动态修改权限
   async changeRoles({ commit, dispatch }, role) {
     const token = role + '-token'
 
@@ -113,12 +143,11 @@ const actions = {
 
     resetRouter()
 
-    // generate accessible routes map based on roles
-    const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
-    // dynamically add accessible routes
+    const accessRoutes = await dispatch('permission/generateRoutes', roles, {
+      root: true
+    })
     router.addRoutes(accessRoutes)
 
-    // reset visited views and cached views
     dispatch('tagsView/delAllViews', null, { root: true })
   }
 }

+ 1 - 0
templates/src/utils/auth.js

@@ -7,6 +7,7 @@ export function getToken() {
 }
 
 export function setToken(token) {
+  console.log('当前的token', token)
   return Cookies.set(TokenKey, token)
 }
 

+ 1 - 1
templates/src/utils/permission.js

@@ -15,7 +15,7 @@ export default function checkPermission(value) {
     })
     return hasPermission
   } else {
-    console.error(`need roles! Like v-permission="['admin','editor']"`)
+    console.error(`[sr/utils/permission.js]need roles! Like v-permission="['admin','editor']"`)
     return false
   }
 }

+ 3 - 3
templates/src/utils/request.js

@@ -56,9 +56,9 @@ service.interceptors.response.use(
       // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
       if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
         // to re-login
-        MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
-          confirmButtonText: 'Re-Login',
-          cancelButtonText: 'Cancel',
+        MessageBox.confirm('您已登出,可以选择取消以停留在此页面,或重新登录。', '确认登出', {
+          confirmButtonText: '重新登录',
+          cancelButtonText: '取消',
           type: 'warning'
         }).then(() => {
           store.dispatch('user/resetToken').then(() => {

+ 1 - 0
templates/src/views/dashboard/index.vue

@@ -23,6 +23,7 @@ export default {
     ])
   },
   created() {
+    console.log('[views/dashboard/index.vue]当前的用户角色[this.roles]', this.roles)
     if (!this.roles.includes('admin')) {
       this.currentRole = 'editorDashboard'
     }

+ 0 - 57
templates/src/views/documentation/index.vue

@@ -1,57 +0,0 @@
-<template>
-  <div class="app-container documentation-container">
-    <a class="document-btn" target="_blank" href="https://store.akveo.com/products/vue-java-admin-dashboard-spring?utm_campaign=akveo_store-Vue-Vue_demo%2Fgithub&utm_source=vue_admin&utm_medium=referral&utm_content=demo_English_button">Java backend integration</a>
-    <a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/">Documentation</a>
-    <a class="document-btn" target="_blank" href="https://github.com/PanJiaChen/vue-element-admin/">Github Repository</a>
-    <a class="document-btn" target="_blank" href="https://panjiachen.gitee.io/vue-element-admin-site/zh/">国内文档</a>
-    <dropdown-menu class="document-btn" :items="articleList" title="系列文章" />
-    <a class="document-btn" target="_blank" href="https://panjiachen.github.io/vue-element-admin-site/zh/job/">内推招聘</a>
-  </div>
-</template>
-
-<script>
-import DropdownMenu from '@/components/Share/DropdownMenu'
-
-export default {
-  name: 'Documentation',
-  components: { DropdownMenu },
-  data() {
-    return {
-      articleList: [
-        { title: '基础篇', href: 'https://juejin.im/post/59097cd7a22b9d0065fb61d2' },
-        { title: '登录权限篇', href: 'https://juejin.im/post/591aa14f570c35006961acac' },
-        { title: '实战篇', href: 'https://juejin.im/post/593121aa0ce4630057f70d35' },
-        { title: 'vue-admin-template 篇', href: 'https://juejin.im/post/595b4d776fb9a06bbe7dba56' },
-        { title: 'v4.0 篇', href: 'https://juejin.im/post/5c92ff94f265da6128275a85' },
-        { title: '自行封装 component', href: 'https://segmentfault.com/a/1190000009090836' },
-        { title: '优雅的使用 icon', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
-        { title: 'webpack4(上)', href: 'https://juejin.im/post/59bb864b5188257e7a427c09' },
-        { title: 'webpack4(下)', href: 'https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc' }
-      ]
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.documentation-container {
-  margin: 50px;
-  display: flex;
-  flex-wrap: wrap;
-  justify-content: flex-start;
-
-  .document-btn {
-    flex-shrink: 0;
-    display: block;
-    cursor: pointer;
-    background: black;
-    color: white;
-    height: 60px;
-    padding: 0 16px;
-    margin: 16px;
-    line-height: 60px;
-    font-size: 20px;
-    text-align: center;
-  }
-}
-</style>

+ 321 - 64
templates/src/views/login/index.vue

@@ -1,9 +1,20 @@
 <template>
   <div class="login-container">
-    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on" label-position="left">
-
+    <el-form
+      ref="loginForm"
+      :model="loginForm"
+      :rules="loginRules"
+      class="login-form"
+      autocomplete="on"
+      label-position="left"
+    >
+      <lottie-web-cimo
+        ref="lottie_web"
+        animation-name="meeting"
+        style="width: 85%; max-width: 95%; height: 12%; margin: auto"
+      />
       <div class="title-container">
-        <h3 class="title">Login Form</h3>
+        <h3 class="title">机器人感知与信息融合小组管理系统</h3>
       </div>
 
       <el-form-item prop="username">
@@ -13,7 +24,7 @@
         <el-input
           ref="username"
           v-model="loginForm.username"
-          placeholder="Username"
+          placeholder=""
           name="username"
           type="text"
           tabindex="1"
@@ -21,7 +32,12 @@
         />
       </el-form-item>
 
-      <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
+      <el-tooltip
+        v-model="capsTooltip"
+        content="Caps lock is On"
+        placement="right"
+        manual
+      >
         <el-form-item prop="password">
           <span class="svg-container">
             <svg-icon icon-class="password" />
@@ -40,70 +56,194 @@
             @keyup.enter.native="handleLogin"
           />
           <span class="show-pwd" @click="showPwd">
-            <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
+            <svg-icon
+              :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
+            />
           </span>
         </el-form-item>
       </el-tooltip>
 
-      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">Login</el-button>
+      <el-button
+        :loading="loading"
+        type="primary"
+        style="width: 100%; margin-bottom: 30px"
+        @click.native.prevent="handleLogin"
+      >登录组内系统</el-button>
 
-      <div style="position:relative">
-        <div class="tips">
-          <span>Username : admin</span>
-          <span>Password : any</span>
-        </div>
-        <div class="tips">
-          <span style="margin-right:18px;">Username : editor</span>
-          <span>Password : any</span>
-        </div>
+      <div style="position: relative">
+        <el-button type="primary" @click="registerDialogVisible = true">
+          没有账号?立即注册
+        </el-button>
 
-        <el-button class="thirdparty-button" type="primary" @click="showDialog=true">
-          Or connect with
+        <el-button type="primary" @click="showDialog = true">
+          使用第三方账号登录
         </el-button>
       </div>
     </el-form>
 
-    <el-dialog title="Or connect with" :visible.sync="showDialog">
-      Can not be simulated on local, so please combine you own business simulation! ! !
+    <!-- 第三方登录弹窗 -->
+    <el-dialog
+      title="或者使用第三方账号登录"
+      :visible.sync="showDialog"
+      width="400px"
+      class="register-dialog"
+    >
+      后续使用微信或者qq扫码登录
       <br>
       <br>
       <br>
       <social-sign />
     </el-dialog>
+
+    <!-- 注册弹窗 -->
+    <el-dialog
+      title="用户注册"
+      :visible.sync="registerDialogVisible"
+      width="400px"
+      class="register-dialog"
+      :before-close="handleClose"
+    >
+      <el-form
+        ref="registerForm"
+        :model="registerForm"
+        :rules="registerRules"
+        label-width="80px"
+      >
+        <el-form-item label="用户名" prop="username">
+          <el-input
+            v-model="registerForm.username"
+            placeholder="请输入用户名"
+          />
+        </el-form-item>
+
+        <el-form-item label="密码" prop="password">
+          <el-input
+            v-model="registerForm.password"
+            :type="registerPasswordType"
+            placeholder="请输入密码"
+          >
+            <i
+              slot="suffix"
+              :class="
+                registerPasswordType === 'password'
+                  ? 'el-icon-view'
+                  : 'el-icon-view'
+              "
+              style="cursor: pointer; padding: 8px"
+              @click="toggleRegisterPasswordVisibility"
+            />
+          </el-input>
+        </el-form-item>
+
+        <el-form-item label="确认密码" prop="confirmPassword">
+          <el-input
+            v-model="registerForm.confirmPassword"
+            :type="confirmPasswordType"
+            placeholder="请再次输入密码"
+          >
+            <i
+              slot="suffix"
+              :class="
+                confirmPasswordType === 'password'
+                  ? 'el-icon-view'
+                  : 'el-icon-view'
+              "
+              style="cursor: pointer; padding: 8px"
+              @click="toggleConfirmPasswordVisibility"
+            />
+          </el-input>
+        </el-form-item>
+      </el-form>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="registerDialogVisible = false">取 消</el-button>
+        <el-button
+          type="primary"
+          :loading="registerLoading"
+          @click="handleRegister"
+        >注 册</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
-import { validUsername } from '@/utils/validate'
 import SocialSign from './components/SocialSignin'
+import LottieWebCimo from '@/components/LottieWeb/lottie-web-cimo'
+import { post } from '@/boot/axios_request'
 
 export default {
   name: 'Login',
-  components: { SocialSign },
+  components: { SocialSign, LottieWebCimo },
   data() {
-    const validateUsername = (rule, value, callback) => {
-      if (!validUsername(value)) {
-        callback(new Error('Please enter the correct user name'))
+    const validatePassword = (rule, value, callback) => {
+      if (value.length < 6) {
+        callback(new Error('密码长度不能少于6位'))
       } else {
         callback()
       }
     }
-    const validatePassword = (rule, value, callback) => {
-      if (value.length < 6) {
-        callback(new Error('The password can not be less than 6 digits'))
+
+    const validateConfirmPassword = (rule, value, callback) => {
+      if (value !== this.registerForm.password) {
+        callback(new Error('两次输入的密码不一致'))
       } else {
         callback()
       }
     }
+
     return {
       loginForm: {
-        username: 'admin',
-        password: '111111'
+        username: '',
+        password: ''
       },
       loginRules: {
-        username: [{ required: true, trigger: 'blur', validator: validateUsername }],
-        password: [{ required: true, trigger: 'blur', validator: validatePassword }]
+        username: [
+          { required: true, message: '请输入用户名', trigger: 'blur' },
+          {
+            min: 3,
+            max: 15,
+            message: '用户名长度在3到15个字符之间',
+            trigger: 'blur'
+          }
+        ],
+        password: [
+          { required: true, message: '请输入密码', trigger: 'blur' },
+          { validator: validatePassword, trigger: 'blur' }
+        ]
+      },
+
+      // 注册相关数据
+      registerDialogVisible: false,
+      registerLoading: false,
+      registerForm: {
+        username: '',
+        password: '',
+        confirmPassword: ''
       },
+      registerRules: {
+        username: [
+          { required: true, message: '请输入用户名', trigger: 'blur' },
+          {
+            min: 3,
+            max: 15,
+            message: '用户名长度在3到15个字符之间',
+            trigger: 'blur'
+          }
+        ],
+        password: [
+          { required: true, message: '请输入密码', trigger: 'blur' },
+          { validator: validatePassword, trigger: 'blur' }
+        ],
+        confirmPassword: [
+          { required: true, message: '请确认密码', trigger: 'blur' },
+          { validator: validateConfirmPassword, trigger: 'blur' }
+        ]
+      },
+      registerPasswordType: 'password',
+      confirmPasswordType: 'password',
+
+      // 其他数据保持不变
       passwordType: 'password',
       capsTooltip: false,
       loading: false,
@@ -138,9 +278,70 @@ export default {
     // window.removeEventListener('storage', this.afterQRScan)
   },
   methods: {
+    // 注册相关方法
+    toggleRegisterPasswordVisibility() {
+      this.registerPasswordType =
+        this.registerPasswordType === 'password' ? 'text' : 'password'
+    },
+    toggleConfirmPasswordVisibility() {
+      this.confirmPasswordType =
+        this.confirmPasswordType === 'password' ? 'text' : 'password'
+    },
+    handleClose(done) {
+      this.$refs.registerForm.resetFields()
+      done()
+    },
+    handleRegister() {
+      this.$refs.registerForm.validate((valid) => {
+        if (valid) {
+          this.registerLoading = true
+
+          // 模拟注册请求
+          post('register/', {
+            name: this.registerForm.username,
+            password1: this.registerForm.password,
+            password2: this.registerForm.confirmPassword
+          })
+            .then((res) => {
+              if (res.code === '200') {
+                this.registerLoading = false
+                this.$notify({
+                  title: '注册成功',
+                  message: '账号已成功创建,请使用新账号登录',
+                  type: 'success'
+                })
+                this.registerDialogVisible = false
+                localStorage.setItem('openid', res.data.openid)
+                // 自动填充注册信息到登录表单
+                this.loginForm.username = this.registerForm.username
+                this.loginForm.password = this.registerForm.password
+                this.$nextTick(() => {
+                  this.$refs.password.focus()
+                })
+              } else {
+                this.registerLoading = false
+                this.$notify({
+                  title: '注册失败',
+                  message: res.msg,
+                  type: 'error'
+                })
+              }
+            })
+            .finally(() => {
+              this.registerLoading = false
+            })
+        } else {
+          this.registerLoading = false
+          console.log('表单验证不通过')
+          return false
+        }
+      })
+    },
+
+    // 原有方法保持不变
     checkCapslock(e) {
       const { key } = e
-      this.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')
+      this.capsTooltip = key && key.length === 1 && key >= 'A' && key <= 'Z'
     },
     showPwd() {
       if (this.passwordType === 'password') {
@@ -153,15 +354,33 @@ export default {
       })
     },
     handleLogin() {
-      this.$refs.loginForm.validate(valid => {
+      this.$refs.loginForm.validate((valid) => {
         if (valid) {
           this.loading = true
-          this.$store.dispatch('user/login', this.loginForm)
+
+          this.$store
+            .dispatch('user/login', this.loginForm)
             .then(() => {
-              this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
-              this.loading = false
+              // 登录成功后获取用户信息
+              return this.$store.dispatch('user/getInfo')
             })
-            .catch(() => {
+            .then(() => {
+              // 跳转到首页
+              this.$router.push({
+                path: this.redirect || '/',
+                query: this.otherQuery
+              })
+            })
+            .catch((error) => {
+              // 显示错误通知
+              this.$notify({
+                title: '登录失败',
+                message: error.message || '用户名或密码错误',
+                type: 'error',
+                duration: 5000
+              })
+            })
+            .finally(() => {
               this.loading = false
             })
         } else {
@@ -178,34 +397,13 @@ export default {
         return acc
       }, {})
     }
-    // afterQRScan() {
-    //   if (e.key === 'x-admin-oauth-code') {
-    //     const code = getQueryObject(e.newValue)
-    //     const codeMap = {
-    //       wechat: 'code',
-    //       tencent: 'code'
-    //     }
-    //     const type = codeMap[this.auth_type]
-    //     const codeName = code[type]
-    //     if (codeName) {
-    //       this.$store.dispatch('LoginByThirdparty', codeName).then(() => {
-    //         this.$router.push({ path: this.redirect || '/' })
-    //       })
-    //     } else {
-    //       alert('第三方登录失败')
-    //     }
-    //   }
-    // }
   }
 }
 </script>
 
 <style lang="scss">
-/* 修复input 背景不协调 和光标变色 */
-/* Detail see https://github.com/PanJiaChen/vue-element-admin/pull/927 */
-
-$bg:#283443;
-$light_gray:#fff;
+$bg: #283443;
+$light_gray: #fff;
 $cursor: #fff;
 
 @supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
@@ -245,12 +443,71 @@ $cursor: #fff;
     color: #454545;
   }
 }
+
+/* 注册弹窗样式 */
+.register-dialog {
+  .el-dialog {
+    border-radius: 8px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
+
+    &__header {
+      background-color: #409eff;
+      padding: 15px 20px;
+      border-top-left-radius: 8px;
+      border-top-right-radius: 8px;
+
+      .el-dialog__title {
+        color: #fff;
+        font-weight: bold;
+      }
+
+      .el-dialog__headerbtn {
+        top: 15px;
+        .el-dialog__close {
+          color: #fff;
+        }
+      }
+    }
+
+    &__body {
+      padding: 20px 25px;
+
+      .el-form-item {
+        margin-bottom: 20px;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        .el-input__inner {
+          height: 38px;
+          line-height: 38px;
+          color: #000; /* 设置输入框文字颜色为黑色 */
+        }
+
+        .el-form-item__error {
+          padding-top: 4px;
+        }
+      }
+    }
+
+    &__footer {
+      padding: 10px 20px 20px;
+      text-align: center;
+
+      .el-button {
+        width: 100px;
+        margin: 0 10px;
+      }
+    }
+  }
+}
 </style>
 
 <style lang="scss" scoped>
-$bg:#2d3a4b;
-$dark_gray:#889aa4;
-$light_gray:#eee;
+$bg: #2d3a4b;
+$dark_gray: #889aa4;
+$light_gray: #eee;
 
 .login-container {
   min-height: 100%;

+ 32 - 0
throttle/migrations/0001_initial.py

@@ -0,0 +1,32 @@
+# Generated by Django 4.1.2 on 2025-08-04 13:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ListModel',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('openid', models.CharField(max_length=255, verbose_name='Openid')),
+                ('appid', models.CharField(max_length=255, verbose_name='Appid')),
+                ('ip', models.CharField(max_length=255, verbose_name='IP')),
+                ('method', models.CharField(max_length=18, verbose_name='Method')),
+                ('t_code', models.CharField(max_length=255, verbose_name='Transaction Code')),
+                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='Create Time')),
+            ],
+            options={
+                'verbose_name': 'Throttle',
+                'verbose_name_plural': 'Throttle',
+                'db_table': 'throttle',
+                'ordering': ['-id'],
+            },
+        ),
+    ]

+ 2 - 3
userlogin/views.py

@@ -4,7 +4,7 @@ from django.contrib import auth
 from django.contrib.auth.models import User
 import json
 from userprofile.models import Users
-from staff.models import ListModel as staff
+
 
 def login(request, *args, **kwargs):
     post_data = json.loads(request.body.decode())
@@ -23,11 +23,10 @@ def login(request, *args, **kwargs):
         else:
             auth.login(request, user)
             user_detail = Users.objects.filter(user_id=user.id).first()
-            staff_id = staff.objects.filter(openid=user_detail.openid, staff_name=str(user_detail.name)).first().id
             data = {
                 "name": data['name'],
                 'openid': user_detail.openid,
-                "user_id": staff_id
+
             }
             ret = FBMsg.ret()
             ret['ip'] = ip

+ 4 - 1
userprofile/admin.py

@@ -1,4 +1,7 @@
 from django.contrib import admin
-from .models import Users
+from .models import Users, AcademicProfile, ResearchGroup, GroupMembership
 
 admin.site.register(Users)
+admin.site.register(AcademicProfile)
+admin.site.register(ResearchGroup)
+admin.site.register(GroupMembership)

company/migrations/__init__.py → userprofile/files.py


+ 54 - 0
userprofile/filter.py

@@ -0,0 +1,54 @@
+from django_filters import FilterSet
+from .models import Users, AcademicProfile, ResearchGroup, GroupMembership
+
+class UsersFilter(FilterSet):
+    class Meta:
+        model = Users
+        fields = {
+            'name': ['icontains', 'exact'],
+            'openid': ['icontains', 'exact'],
+            'appid': ['icontains', 'exact'],
+            'vip': ['exact'],
+            'vip_time': ['exact'],
+            'is_delete': ['exact'],
+            'developer': ['exact'],
+            't_code': ['icontains'],
+            'ip': ['icontains'],
+            'avatar': ['icontains'],
+            'create_time': ['exact', 'lt', 'gt'],
+            'update_time': ['exact', 'lt', 'gt'],
+        }
+
+class AcademicProfileFilter(FilterSet):
+    class Meta:
+        model = AcademicProfile
+        fields = {
+            'role': ['exact', 'icontains'],
+            'enrollment_year': ['exact', 'lt', 'gt'],
+            'graduation_year': ['exact', 'lt', 'gt'],
+            'department': ['icontains'],
+           'major': ['icontains'],
+           'research_tags': ['icontains'],
+           'skill_tags': ['icontains'],
+        }
+
+class ResearchGroupFilter(FilterSet):
+    class Meta:
+        model = ResearchGroup
+        fields = {
+            'name': ['icontains', 'exact'],
+            'description': ['icontains'],
+            'created_by': ['exact'],
+            'created_at': ['exact', 'lt', 'gt'],
+            'is_active': ['exact'],
+        }
+
+class GroupMembershipFilter(FilterSet):
+    class Meta:
+        model = GroupMembership
+        fields = {
+            'role': ['exact'],
+           'status': ['exact'],
+            'joined_at': ['exact', 'lt', 'gt'],
+            'left_at': ['exact', 'lt', 'gt'],
+        }

+ 94 - 0
userprofile/migrations/0001_initial.py

@@ -0,0 +1,94 @@
+# Generated by Django 4.1.2 on 2025-08-05 14:01
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Users',
+            fields=[
+                ('user_id', models.AutoField(primary_key=True, serialize=False, verbose_name='用户ID')),
+                ('name', models.CharField(max_length=80, verbose_name='姓名')),
+                ('openid', models.CharField(max_length=100, unique=True, verbose_name='OPENID')),
+                ('appid', models.CharField(max_length=100, verbose_name='APPID')),
+                ('vip', models.PositiveIntegerField(default=1, verbose_name='VIP等级')),
+                ('vip_time', models.DateTimeField(auto_now_add=True, verbose_name='VIP生效时间')),
+                ('is_delete', models.BooleanField(default=False, verbose_name='删除标记')),
+                ('developer', models.BooleanField(default=False, verbose_name='开发者标记')),
+                ('t_code', models.CharField(max_length=100, unique=True, verbose_name='交易验证码')),
+                ('ip', models.GenericIPAddressField(verbose_name='注册IP')),
+                ('avatar', models.CharField(default='/static/img/user.jpg', max_length=200, verbose_name='用户头像')),
+                ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
+                ('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
+            ],
+            options={
+                'verbose_name': '用户档案',
+                'verbose_name_plural': '用户档案',
+                'db_table': 'user_profile',
+                'ordering': ['-create_time'],
+            },
+        ),
+        migrations.CreateModel(
+            name='AcademicProfile',
+            fields=[
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='academic_profile', serialize=False, to='userprofile.users', verbose_name='关联用户')),
+                ('role', models.CharField(choices=[('UG', '本科生'), ('MS', '硕士生'), ('PhD', '博士生'), ('PD', '博士后'), ('FAC', '教职工'), ('RES', '研究员')], default='MS', max_length=10, verbose_name='学术身份')),
+                ('enrollment_year', models.PositiveIntegerField(help_text='格式:YYYY(如2023)', verbose_name='入学年份')),
+                ('graduation_year', models.PositiveIntegerField(blank=True, null=True, verbose_name='预计毕业年份')),
+                ('department', models.CharField(max_length=100, verbose_name='院系/研究所')),
+                ('major', models.CharField(max_length=100, verbose_name='专业方向')),
+                ('research_tags', models.CharField(blank=True, help_text='用逗号分隔多个方向(如:人工智能,教育技术)', max_length=255, verbose_name='研究方向')),
+                ('skill_tags', models.CharField(blank=True, help_text='用逗号分隔多个技能(如:Python,数据分析)', max_length=255, verbose_name='技能标签')),
+            ],
+            options={
+                'verbose_name': '学术档案',
+                'verbose_name_plural': '学术档案',
+                'db_table': 'academic_profile',
+            },
+        ),
+        migrations.CreateModel(
+            name='ResearchGroup',
+            fields=[
+                ('group_id', models.AutoField(primary_key=True, serialize=False, verbose_name='小组ID')),
+                ('name', models.CharField(max_length=100, unique=True, verbose_name='小组名称')),
+                ('description', models.TextField(blank=True, verbose_name='小组描述')),
+                ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
+                ('is_active', models.BooleanField(default=True, verbose_name='是否活跃')),
+                ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_groups', to='userprofile.users', verbose_name='创建人')),
+            ],
+            options={
+                'verbose_name': '教研小组',
+                'verbose_name_plural': '教研小组',
+                'db_table': 'research_group',
+                'ordering': ['-created_at'],
+            },
+        ),
+        migrations.CreateModel(
+            name='GroupMembership',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('role', models.CharField(choices=[('LEAD', '组长'), ('DEP', '副组长'), ('CORE', '核心成员'), ('MEM', '普通成员'), ('ADV', '指导老师'), ('OBS', '观察员')], default='MEM', max_length=20, verbose_name='小组角色')),
+                ('status', models.CharField(choices=[('ACT', '活跃'), ('LV', '请假'), ('INAC', '不活跃'), ('GRAD', '已毕业'), ('TRF', '已转组')], default='ACT', max_length=10, verbose_name='在组状态')),
+                ('joined_at', models.DateTimeField(auto_now_add=True, verbose_name='加入时间')),
+                ('left_at', models.DateTimeField(blank=True, null=True, verbose_name='离开时间')),
+                ('custom_permissions', models.JSONField(blank=True, default=dict, help_text='JSON格式存储额外权限', null=True, verbose_name='特殊权限')),
+                ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='members', to='userprofile.researchgroup', verbose_name='所属小组')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='group_memberships', to='userprofile.users', verbose_name='成员')),
+            ],
+            options={
+                'verbose_name': '小组成员',
+                'verbose_name_plural': '小组成员',
+                'db_table': 'group_membership',
+                'ordering': ['-joined_at'],
+                'unique_together': {('user', 'group')},
+            },
+        ),
+    ]

+ 27 - 0
userprofile/migrations/0002_alter_academicprofile_user_and_more.py

@@ -0,0 +1,27 @@
+# Generated by Django 4.1.2 on 2025-08-05 16:06
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('userprofile', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='academicprofile',
+            name='user',
+            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='user_academic_profile', serialize=False, to='userprofile.users', verbose_name='关联用户'),
+        ),
+        migrations.AlterModelTable(
+            name='groupmembership',
+            table='user_group_membership',
+        ),
+        migrations.AlterModelTable(
+            name='researchgroup',
+            table='user_research_group',
+        ),
+    ]

+ 17 - 0
userprofile/migrations/0003_alter_academicprofile_table.py

@@ -0,0 +1,17 @@
+# Generated by Django 4.1.2 on 2025-08-05 16:08
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('userprofile', '0002_alter_academicprofile_user_and_more'),
+    ]
+
+    operations = [
+        migrations.AlterModelTable(
+            name='academicprofile',
+            table='user_academic_profile',
+        ),
+    ]

+ 180 - 17
userprofile/models.py

@@ -1,24 +1,187 @@
 from django.db import models
+from django.utils.translation import gettext_lazy as _
+from django.utils import timezone
 
 class Users(models.Model):
-    user_id = models.IntegerField(default=0, verbose_name="Admin ID")
-    name = models.CharField(max_length=80, verbose_name='Staff Name')
-    vip = models.BigIntegerField(default=1, verbose_name='VIP Level')
-    openid = models.CharField(max_length=100, verbose_name='OPENID')
+    """核心用户模型 (继承现有结构)"""
+    user_id = models.AutoField(primary_key=True, verbose_name="用户ID")
+    name = models.CharField(max_length=80, verbose_name='姓名')
+    openid = models.CharField(max_length=100, unique=True, verbose_name='OPENID')
     appid = models.CharField(max_length=100, verbose_name='APPID')
-    is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
-    developer = models.BooleanField(default=True, verbose_name='Developer Label')
-    t_code = models.CharField(max_length=100, verbose_name='Transaction Code')
-    ip = models.CharField(max_length=100, verbose_name='Register IP')
-    vip_time = models.DateTimeField(auto_now_add=True)
-    link_to = models.BooleanField(default=False, verbose_name='Link To')
-    link_to_id = models.BigIntegerField(default=0, verbose_name='Link To ID')
-    avatar = models.CharField(max_length=100, default='/static/img/user.jpg', verbose_name='Staff Avatar')
-    create_time = models.DateTimeField(auto_now_add=True, verbose_name='Create Time')
-    update_time = models.DateTimeField(auto_now=True, verbose_name='Update Time')
+    
+    # 权限系统
+    vip = models.PositiveIntegerField(default=1, verbose_name='VIP等级')
+    vip_time = models.DateTimeField(auto_now_add=True, verbose_name='VIP生效时间')
+    
+    # 账户状态
+    is_delete = models.BooleanField(default=False, verbose_name='删除标记')
+    developer = models.BooleanField(default=False, verbose_name='开发者标记')
+    
+    # 安全信息
+    t_code = models.CharField(max_length=100, unique=True, verbose_name='交易验证码')
+    ip = models.GenericIPAddressField(verbose_name='注册IP')
+    
+    # 基本信息
+    avatar = models.CharField(max_length=200, default='/static/img/user.jpg', verbose_name='用户头像')
+    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
+    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
 
     class Meta:
         db_table = 'user_profile'
-        verbose_name = 'User Profile'
-        verbose_name_plural = "User Profile"
-        ordering = ['-id']
+        verbose_name = '用户档案'
+        verbose_name_plural = "用户档案"
+        ordering = ['-create_time']
+
+    def __str__(self):
+        return f"{self.name}(ID:{self.user_id})"
+
+class AcademicProfile(models.Model):
+    """学术信息扩展模型 (与用户一对一关联)"""
+    class AcademicRole(models.TextChoices):
+        UNDERGRAD = 'UG', _('本科生')
+        MASTER = 'MS', _('硕士生')
+        PHD = 'PhD', _('博士生')
+        POSTDOC = 'PD', _('博士后')
+        FACULTY = 'FAC', _('教职工')
+        RESEARCHER = 'RES', _('研究员')
+    
+    user = models.OneToOneField(
+        Users,
+        on_delete=models.CASCADE,
+        primary_key=True,
+        related_name='user_academic_profile',
+        verbose_name='关联用户'
+    )
+    # 学术身份
+    role = models.CharField(
+        max_length=10,
+        choices=AcademicRole.choices,
+        default=AcademicRole.MASTER,
+        verbose_name='学术身份'
+    )
+    # 年级信息
+    enrollment_year = models.PositiveIntegerField(
+        verbose_name='入学年份',
+        help_text='格式:YYYY(如2023)'
+    )
+    graduation_year = models.PositiveIntegerField(
+        null=True,
+        blank=True,
+        verbose_name='预计毕业年份'
+    )
+    # 学术单位
+    department = models.CharField(max_length=100, verbose_name='院系/研究所')
+    major = models.CharField(max_length=100, verbose_name='专业方向')
+    
+    # 研究标签
+    research_tags = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name='研究方向',
+        help_text='用逗号分隔多个方向(如:人工智能,教育技术)'
+    )
+    skill_tags = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name='技能标签',
+        help_text='用逗号分隔多个技能(如:Python,数据分析)'
+    )
+    
+    class Meta:
+        db_table = 'user_academic_profile'
+        verbose_name = '学术档案'
+        verbose_name_plural = "学术档案"
+    
+    def __str__(self):
+        return f"{self.user.name}的学术档案"
+
+class ResearchGroup(models.Model):
+    """教研小组模型"""
+    group_id = models.AutoField(primary_key=True, verbose_name="小组ID")
+    name = models.CharField(max_length=100, unique=True, verbose_name="小组名称")
+    description = models.TextField(blank=True, verbose_name="小组描述")
+    created_by = models.ForeignKey(
+        Users,
+        on_delete=models.SET_NULL,
+        null=True,
+        related_name='created_groups',
+        verbose_name="创建人"
+    )
+    created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
+    is_active = models.BooleanField(default=True, verbose_name="是否活跃")
+    
+    class Meta:
+        db_table = 'user_research_group'
+        verbose_name = '教研小组'
+        verbose_name_plural = "教研小组"
+        ordering = ['-created_at']
+    
+    def __str__(self):
+        return f"{self.name}(ID:{self.group_id})"
+
+class GroupMembership(models.Model):
+    """小组成员关联模型"""
+    class MemberRole(models.TextChoices):
+        LEADER = 'LEAD', _('组长')
+        DEPUTY = 'DEP', _('副组长')
+        CORE = 'CORE', _('核心成员')
+        MEMBER = 'MEM', _('普通成员')
+        ADVISOR = 'ADV', _('指导老师')
+        OBSERVER = 'OBS', _('观察员')
+    
+    class MemberStatus(models.TextChoices):
+        ACTIVE = 'ACT', _('活跃')
+        LEAVE = 'LV', _('请假')
+        INACTIVE = 'INAC', _('不活跃')
+        GRADUATED = 'GRAD', _('已毕业')
+        TRANSFERRED = 'TRF', _('已转组')
+    
+    user = models.ForeignKey(
+        Users,
+        on_delete=models.CASCADE,
+        related_name='group_memberships',
+        verbose_name="成员"
+    )
+    group = models.ForeignKey(
+        ResearchGroup,
+        on_delete=models.CASCADE,
+        related_name='members',
+        verbose_name="所属小组"
+    )
+    role = models.CharField(
+        max_length=20,
+        choices=MemberRole.choices,
+        default=MemberRole.MEMBER,
+        verbose_name="小组角色"
+    )
+    status = models.CharField(
+        max_length=10,
+        choices=MemberStatus.choices,
+        default=MemberStatus.ACTIVE,
+        verbose_name="在组状态"
+    )
+    joined_at = models.DateTimeField(auto_now_add=True, verbose_name="加入时间")
+    left_at = models.DateTimeField(null=True, blank=True, verbose_name="离开时间")
+    custom_permissions = models.JSONField(
+        null=True,
+        blank=True,
+        default=dict,
+        verbose_name="特殊权限",
+        help_text="JSON格式存储额外权限"
+    )
+    
+    class Meta:
+        db_table = 'user_group_membership'
+        verbose_name = '小组成员'
+        verbose_name_plural = "小组成员"
+        unique_together = ('user', 'group')  # 确保同一用户不会重复加入同一小组
+        ordering = ['-joined_at']
+    
+    def save(self, *args, **kwargs):
+        """自动更新离开时间"""
+        if self.status in [self.MemberStatus.GRADUATED, self.MemberStatus.TRANSFERRED] and not self.left_at:
+            self.left_at = timezone.now()
+        super().save(*args, **kwargs)
+    
+    def __str__(self):
+        return f"{self.user.name}在{self.group.name}({self.get_role_display()})"

+ 18 - 0
userprofile/serializers.py

@@ -0,0 +1,18 @@
+from rest_framework import serializers
+from .models import Users, AcademicProfile, ResearchGroup, GroupMembership
+
+class UsersGetSerializer(serializers.ModelSerializer):
+   
+    roles = serializers.SerializerMethodField()
+    introduction = serializers.SerializerMethodField()
+    class Meta:
+        model = Users
+        fields = ['user_id','name', 'roles','avatar', 'introduction','create_time', 'update_time']
+
+    def get_roles(self, obj):
+
+        # return obj.roles.all()
+        return ['admin']
+
+    def get_introduction(self, obj):
+        return 'I am a super administrator'

+ 10 - 0
userprofile/urls.py

@@ -0,0 +1,10 @@
+from django.urls import path, re_path
+from . import views
+
+urlpatterns=[
+
+path('profile/', views.UserprofileViewSet.as_view({'get': 'list'}), name='groups'),
+
+re_path(r'^profile/(?P<pk>[0-9]+)/$', views.UserprofileViewSet.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}), name='groups')
+
+]

+ 90 - 0
userprofile/views.py

@@ -0,0 +1,90 @@
+from rest_framework import viewsets
+from utils.page import MyPageNumberPagination
+from rest_framework.filters import OrderingFilter
+from django_filters.rest_framework import DjangoFilterBackend
+
+from .models import Users, AcademicProfile, ResearchGroup, GroupMembership
+from .filter import UsersFilter, AcademicProfileFilter, ResearchGroupFilter, GroupMembershipFilter
+from .serializers import UsersGetSerializer
+
+
+class UserprofileViewSet(viewsets.ModelViewSet):
+    """
+    用户档案视图集
+        retrieve:
+            Response a data list (get)
+
+        list:
+            Response a data list (all)
+
+        create:
+            Create a data line (post)
+
+        delete:
+            Delete a data line (delete)
+
+        partial_update:
+            Partial_update a data (patch:partial_update)
+
+        update:
+            Update a data (put:update)
+    """
+    fliter_backends = (DjangoFilterBackend, OrderingFilter)
+    ordering_fields = ('create_time', 'update_time')
+    filterset_class = UsersFilter
+    pagination_class = MyPageNumberPagination
+
+    def get_project(self):
+        try:
+            id = self.kwargs.get('pk')
+            return id
+        except:
+            return None
+
+    def get_queryset(self):
+        project_id = self.get_project()
+        if project_id:
+            return Users.objects.filter(user_id=project_id)
+        else:
+            return Users.objects.all()
+
+    def get_serializer_class(self):
+        if self.action in ['list','retrieve']:
+            return UsersGetSerializer
+        else:
+            return UsersGetSerializer
+        
+    def create(self, request, *args, **kwargs):
+        serializer = self.get_serializer(data=request.data)
+        serializer.is_valid(raise_exception=True)
+        self.perform_create(serializer)
+        headers = self.get_success_headers(serializer.data)
+        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
+    
+    def update(self, request, *args, **kwargs):
+        qs = self.get_object()
+        serializer = self.get_serializer(qs, data=request.data, partial=True)
+        serializer.is_valid(raise_exception=True)
+        self.perform_update(serializer)
+        return Response(serializer.data)    
+    
+    def partial_update(self, request, *args, **kwargs):
+        qs = self.get_object()
+        serializer = self.get_serializer(qs, data=request.data, partial=True)
+        serializer.is_valid(raise_exception=True)
+        self.perform_update(serializer)
+        return Response(serializer.data)
+    
+    def destroy(self, request, *args, **kwargs):
+        qs = self.get_object()
+        qs.is_delete = True
+        qs.save()
+        return Response(status=status.HTTP_204_NO_CONTENT)
+    
+    def perform_create(self, serializer):
+        serializer.save()
+    
+    def perform_update(self, serializer):
+        serializer.save()
+    
+ 

+ 17 - 405
userregister/views.py

@@ -7,48 +7,11 @@ from django.utils.decorators import method_decorator
 from django.contrib import auth
 from django.utils import timezone
 from django.contrib.auth.models import User
-from staff.models import ListModel as staff
+
 import json, random, os
 from django.conf import settings
-from scanner.models import ListModel as scanner
-
-def randomPhone():
-    List = ["130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
-               "147", "150", "151", "152", "153", "155", "156", "157", "158", "159",
-               "186", "187", "188", "189"]
-    return (random.choice(List) + "".join(random.choice("0123456789") for i in range(8)))
-
-randomcity = ["shanghai", "nanjing", "hangzhou", "beijing", "chongqing", "shenzhen", "guangzhou", "suzhou", "hefei",
-                "chengdu", "kunming", "wuhan"]
-
-randomcolor = ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Purple"]
-
-randomclass = ["Electronics", "Computers", "Smart Home", "Arts & Crafts", "Automotive", "Baby", "Health", "Kitchen",
-               "Industrial", "Luggage", "Movies", "Software"]
-
-randomunit = ["Box", "Package", "Piece", "Pallet"]
-
-randomname = ["Aaron", "Abbott", "Abel", "Baird", "Baldwin", "Bancroft", "Caesar", "Calvin", "Camille", "chengdu",
-              "Daisy", "Dale", "Dana", "Earl", "Eartha", "Ed", "Fabian", "Faithe", "Fanny", "Gabriel", "Gabrielle",
-              "Gail", "Hale", "Haley", "Hamiltion", "Ian", "Ida", "Ina", "Jack", "Jacob", "Jacqueline", "Kama",
-              "Karen", "Katherine", "Lambert", "Lance", "Larry", "Mabel", "Madeline", "Madge", "Nancy", "Naomi",
-              "Nat", "Octavia", "Odelette", "Odelia", "Paddy", "Pag", "Page", "Queena", "Quennel", "Quentin",
-              "Rachel", "Rae", "Ralap", "Sabina", "Sabrina", "Sally", "Tab", "Tabitha", "Tammy", "Ula", "Ulysses",
-              "Una", "Valentina", "Valentine", "Valentine", "Wade", "Walker", "Wallis", "Xanthe", "Xavier", "Xaviera",
-              "Yale", "Yedda", "Yehudi", "Zachary", "Zebulon", "Zenobia"
-            ]
-
-randomshape = ["Square", "Rectangle", "Cone", "Cylinder", "Irregular"]
 
-randomspecs = ["1 x 10", "3 x 3", "5 x 5", "6 x 6"]
 
-def randomStaffType():
-    List = ["Manager", "Supervisor", "Inbound", "Outbound", "StockControl"]
-    return (random.choice(List))
-
-randombinproperty = ["Normal", "Holding", "Damage", "Inspection"]
-
-randombinsize = ["Big", "Floor", "Tiny", "Small"]
 
 @method_decorator(csrf_exempt, name='dispatch')
 def register(request, *args, **kwargs):
@@ -96,6 +59,16 @@ def register(request, *args, **kwargs):
                             err_password_not_same['data'] = data['name']
                             return JsonResponse(err_password_not_same)
                         else:
+
+                            # 第一次注册时候,顺便注册开发者用户
+                            # 后续开发者用户需要手动修改用户信息
+                            if Users.objects.filter().count() == 0:
+                                userzl = User.objects.create_user(username='adminzl',password=str(123456))
+                                Users.objects.create(user_id=userzl.id, name='adminzl',
+                                                 openid="adminzl", appid="adminzl",
+                                                 t_code=Md5.md5(str(timezone.now())),
+                                                 developer=1, ip=ip)
+                                
                             transaction_code = Md5.md5(data['name'])
                             user = User.objects.create_user(username=str(data['name']),
                                                             password=str(data['password1']))
@@ -104,13 +77,9 @@ def register(request, *args, **kwargs):
                                                  t_code=Md5.md5(str(timezone.now())),
                                                  developer=1, ip=ip)
                             auth.login(request, user)
-                            check_code = random.randint(1000, 9999)
-                            staff.objects.create(staff_name=str(data['name']),
-                                                 staff_type='Admin',
-                                                 check_code=check_code,
-                                                 openid=transaction_code)
-                            user_id = staff.objects.filter(openid=transaction_code, staff_name=str(data['name']),
-                                                 staff_type='Admin', check_code=check_code).first().id
+                            
+       
+
                             folder = os.path.exists(os.path.join(settings.BASE_DIR, 'media/' + transaction_code))
                             if not folder:
                                 os.makedirs(os.path.join(settings.BASE_DIR, 'media/' + transaction_code))
@@ -121,367 +90,10 @@ def register(request, *args, **kwargs):
                             ret['ip'] = ip
                             data['openid'] = transaction_code
                             data['name'] = str(data['name'])
-                            data['user_id'] = user_id
+  
                             data.pop('password1', '')
                             data.pop('password2', '')
                             ret['data'] = data
-                            from company.models import ListModel as company
-                            company.objects.create(openid=transaction_code,
-                                                   company_name='GreaterWMS',
-                                                   company_city=str(random.choice(randomcity)),
-                                                   company_address='People’s Square # 666 Room 1F',
-                                                   company_contact=str(randomPhone()),
-                                                   company_manager='Elvis.Shi',
-                                                   creater='DemoData'
-                                                   )
-                            from warehouse.models import ListModel as warehouse
-                            warehouse.objects.create(openid=transaction_code,
-                                                     warehouse_name='Center Warehouse',
-                                                     warehouse_city=str(random.choice(randomcity)),
-                                                     warehouse_address='People’s Square # 666 Room 2F',
-                                                     warehouse_contact=str(randomPhone()),
-                                                     warehouse_manager='Tim.Yao',
-                                                     creater='DemoData'
-                                                     )
-                            from supplier.models import ListModel as supplier
-                            supplier_data_list = []
-                            for supplier_data in range(1, 42):
-                                demo_data = supplier(openid=transaction_code,
-                                                     supplier_name='Supplier Name-' + str(supplier_data),
-                                                     supplier_city=str(random.choice(randomcity)),
-                                                     supplier_address='Address-' + str(supplier_data),
-                                                     supplier_contact=str(randomPhone()),
-                                                     supplier_manager=str(random.choice(randomname)),
-                                                     creater='DemoData'
-                                                     )
-                                supplier_data_list.append(demo_data)
-                            supplier.objects.bulk_create(supplier_data_list, batch_size=100)
-                            from customer.models import ListModel as customer
-                            customer_data_list = []
-                            for customer_data in range(1, 42):
-                                demo_data = customer(openid=transaction_code,
-                                                     customer_name='Customer Name-' + str(customer_data),
-                                                     customer_city=str(random.choice(randomcity)),
-                                                     customer_address='Address-' + str(customer_data),
-                                                     customer_contact=str(randomPhone()),
-                                                     customer_manager=str(random.choice(randomname)),
-                                                     creater='DemoData'
-                                                     )
-                                customer_data_list.append(demo_data)
-                            customer.objects.bulk_create(customer_data_list, batch_size=100)
-                            staff_data_list = []
-                            for staff_data in randomname:
-                                demo_data = staff(openid=transaction_code,
-                                                  staff_name=staff_data,
-                                                  staff_type=str(randomStaffType()),
-                                                  check_code=random.randint(1000, 9999)
-                                                  )
-                                staff_data_list.append(demo_data)
-                            staff.objects.bulk_create(staff_data_list, batch_size=100)
-                            from driver.models import ListModel as driver
-                            driver_data_list = []
-                            for driver_data in range(1, 42):
-                                demo_data = driver(openid=transaction_code,
-                                                   driver_name='Driver Name-' + str(driver_data),
-                                                   license_plate="".join(random.choice("0123456789") for i in range(8)),
-                                                   contact=str(randomPhone()),
-                                                   creater='DemoData'
-                                                   )
-                                driver_data_list.append(demo_data)
-                            driver.objects.bulk_create(driver_data_list, batch_size=100)
-                            from capital.models import ListModel as capital
-                            capital_data_list = []
-                            for capital_data in range(1, 42):
-                                demo_data = capital(openid=transaction_code,
-                                                    capital_name='Capital Name-' + str(capital_data),
-                                                    capital_qty=random.randint(1, 100),
-                                                    capital_cost=random.randint(100, 10000),
-                                                    creater='DemoData'
-                                                    )
-                                capital_data_list.append(demo_data)
-                            capital.objects.bulk_create(capital_data_list, batch_size=100)
-                            from binsize.models import ListModel as binsize
-                            binsize_data_list = [
-                                binsize(openid=transaction_code,
-                                        bin_size='Big',
-                                        bin_size_w=1100,
-                                        bin_size_d=1200,
-                                        bin_size_h=1800,
-                                        creater='DemoData'
-                                        ),
-                                binsize(openid=transaction_code,
-                                        bin_size='Floor',
-                                        bin_size_w=10000,
-                                        bin_size_d=10000,
-                                        bin_size_h=10000,
-                                        creater='DemoData'
-                                        ),
-                                binsize(openid=transaction_code,
-                                        bin_size='Small',
-                                        bin_size_w=800,
-                                        bin_size_d=1000,
-                                        bin_size_h=1200,
-                                        creater='DemoData'
-                                        ),
-                                binsize(openid=transaction_code,
-                                        bin_size='Tiny',
-                                        bin_size_w=200,
-                                        bin_size_d=250,
-                                        bin_size_h=300,
-                                        creater='DemoData'
-                                        )
-                            ]
-                            binsize.objects.bulk_create(binsize_data_list, batch_size=100)
-                            from binset.models import ListModel as binset
-                            bar_code1 = Md5.md5('1')
-                            bar_code2 = Md5.md5('2')
-                            bar_code3 = Md5.md5('3')
-                            bar_code4 = Md5.md5('4')
-                            bar_code5 = Md5.md5('5')
-                            bar_code6 = Md5.md5('6')
-                            bar_code7 = Md5.md5('7')
-                            bar_code8 = Md5.md5('8')
-                            bar_code9 = Md5.md5('9')
-                            bar_code10 = Md5.md5('10')
-                            bar_code11 = Md5.md5('11')
-                            bar_code12 = Md5.md5('12')
-                            binset_data_list = [
-                                binset(openid=transaction_code,
-                                       bin_name='A010101',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Normal",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code1
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='A010102',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Normal",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code2
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='A010103',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Normal",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code3
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B010101',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Inspection",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code4
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B010102',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Inspection",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code5
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B010103',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Inspection",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code6
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B020101',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Holding",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code7
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B020102',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Holding",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code8
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B020103',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Holding",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code9
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B030101',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Damage",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code10
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B030102',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Damage",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code11
-                                       ),
-                                binset(openid=transaction_code,
-                                       bin_name='B030103',
-                                       bin_size=str(random.choice(randombinsize)),
-                                       bin_property="Damage",
-                                       empty_label=True,
-                                       creater='DemoData',
-                                       bar_code=bar_code12
-                                       ),
-                            ]
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='A010101',
-                                                   bar_code=bar_code1)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='A010102',
-                                                   bar_code=bar_code2)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='A010103',
-                                                   bar_code=bar_code3)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B010101',
-                                                   bar_code=bar_code4)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B010102',
-                                                   bar_code=bar_code5)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B010103',
-                                                   bar_code=bar_code6)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B020101',
-                                                   bar_code=bar_code7)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B020102',
-                                                   bar_code=bar_code8)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B020103',
-                                                   bar_code=bar_code9)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B030101',
-                                                   bar_code=bar_code10)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B030102',
-                                                   bar_code=bar_code11)
-                            scanner.objects.create(openid=transaction_code, mode="BINSET",
-                                                   code='B030103',
-                                                   bar_code=bar_code12)
-                            binset.objects.bulk_create(binset_data_list, batch_size=100)
-                            from goodsunit.models import ListModel as goodsunit
-                            demo_data = []
-                            for goods_unit in randomunit:
-                                demo_data.append(goodsunit(openid=transaction_code, goods_unit=goods_unit,
-                                                           creater='DemoData'))
-                            goodsunit.objects.bulk_create(demo_data, batch_size=100)
-                            from goodsclass.models import ListModel as goodsclass
-                            demo_data = []
-                            for goods_class in randomclass:
-                                demo_data.append(goodsclass(openid=transaction_code, goods_class=goods_class,
-                                                            creater='DemoData'))
-                            goodsclass.objects.bulk_create(demo_data, batch_size=100)
-                            from goodscolor.models import ListModel as goodscolor
-                            demo_data = []
-                            for goods_color in randomcolor:
-                                demo_data.append(goodscolor(openid=transaction_code, goods_color=goods_color,
-                                                            creater='DemoData'))
-                            goodscolor.objects.bulk_create(demo_data, batch_size=100)
-                            from goodsbrand.models import ListModel as goodsbrand
-                            goodsbrand_data_list = []
-                            for goodsbrand_data in range(1, 42):
-                                demo_data = goodsbrand(openid=transaction_code,
-                                                       goods_brand='Brand Name-' + str(goodsbrand_data),
-                                                       creater='DemoData'
-                                                       )
-                                goodsbrand_data_list.append(demo_data)
-                            goodsbrand.objects.bulk_create(goodsbrand_data_list, batch_size=100)
-                            from goodsshape.models import ListModel as goodsshape
-                            demo_data = []
-                            for goods_shape in randomshape:
-                                demo_data.append(goodsshape(openid=transaction_code, goods_shape=goods_shape,
-                                                            creater='DemoData'))
-                            goodsshape.objects.bulk_create(demo_data, batch_size=100)
-                            from goodsspecs.models import ListModel as goodsspecs
-                            demo_data = []
-                            for goods_specs in randomspecs:
-                                demo_data.append(goodsspecs(openid=transaction_code, goods_specs=goods_specs,
-                                                            creater='DemoData'))
-                            goodsspecs.objects.bulk_create(demo_data, batch_size=100)
-                            from goodsorigin.models import ListModel as goodsorigin
-                            goodsorigin_data_list = []
-                            for city in randomcity:
-                                demo_data = goodsorigin(openid=transaction_code,
-                                                        goods_origin=city,
-                                                        creater='DemoData'
-                                                        )
-                                goodsorigin_data_list.append(demo_data)
-                            goodsorigin.objects.bulk_create(goodsorigin_data_list, batch_size=100)
-                            from goods.models import ListModel as goods
-                            goods_data_list = []
-                            for goods_data in range(1, 42):
-                                bar_code = Md5.md5("A0000" + str(goods_data))
-                                goods_w = round(random.uniform(10, 1000), 2),
-                                goods_d = round(random.uniform(10, 1000), 2),
-                                goods_h = round(random.uniform(10, 1000), 2),
-                                goods_cost = round(random.uniform(10, 1000), 2),
-                                goods_price = round(random.uniform(10, 1000), 2),
-                                while True:
-                                    if goods_cost[0] >= goods_price[0]:
-                                        goods_price = round(random.uniform(10, 1000), 2),
-                                    else:
-                                        break
-                                demo_data = goods(openid=transaction_code,
-                                                  goods_code="A0000" + str(goods_data),
-                                                  goods_desc="Goods Desc-" + str(goods_data),
-                                                  goods_supplier='Supplier Name-' + str(random.randint(1, 42)),
-                                                  goods_weight=random.randint(100, 10000),
-                                                  goods_w=goods_w[0],
-                                                  goods_d=goods_d[0],
-                                                  goods_h=goods_h[0],
-                                                  unit_volume=round((int(goods_w[0]) * int(goods_d[0]) * int(
-                                                      goods_h[0])) / 1000000000, 4),
-                                                  goods_unit=random.choice(randomunit),
-                                                  goods_class=random.choice(randomclass),
-                                                  goods_brand='Brand Name-' + str(random.randint(1, 42)),
-                                                  goods_color=random.choice(randomcolor),
-                                                  goods_shape=random.choice(randomshape),
-                                                  goods_specs=random.choice(randomspecs),
-                                                  goods_origin=random.choice(randomcity),
-                                                  goods_cost=goods_cost[0],
-                                                  goods_price=goods_price[0],
-                                                  bar_code=bar_code,
-                                                  creater='DemoData'
-                                                  )
-                                goods_data_list.append(demo_data)
-                                scanner.objects.create(openid=transaction_code, mode="GOODS",
-                                                       code="A0000" + str(goods_data),
-                                                       bar_code=bar_code)
-                            goods.objects.bulk_create(goods_data_list, batch_size=100)
-                            from payment.models import TransportationFeeListModel as freight
-                            freight_data_list = []
-                            for sender in randomcity:
-                                for receiver in randomcity:
-                                    demo_data = freight(openid=transaction_code,
-                                                        send_city=sender,
-                                                        receiver_city=receiver,
-                                                        weight_fee=random.randint(10, 20),
-                                                        volume_fee=random.randint(100, 200),
-                                                        min_payment=random.randint(250, 300),
-                                                        transportation_supplier="Supplier",
-                                                        creater="DemoData"
-                                                        )
-                                    freight_data_list.append(demo_data)
-                            freight.objects.bulk_create(freight_data_list, batch_size=100)
+
+                           
                             return JsonResponse(ret)

+ 1 - 1
utils/fbmsg.py

@@ -21,7 +21,7 @@ class FBMsg(object):
         err_order_fail = {"code": "1006", "msg": "订单支付失败", "data": None}
         return err_order_fail
     def err_ret():
-        err_ret = {"code": "1011", "msg": "User Name Or Password Error", "data": None}
+        err_ret = {"code": "1011", "msg": "用户不存在或者密码错误", "data": None}
         return err_ret
     def err_data():
         err_data = {"code": "1012", "msg": "数据不可用", "data": None}

+ 2 - 2
utils/websocket.py

@@ -3,7 +3,7 @@ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'greaterwms.settings')
 os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
 django.setup()
 from rest_framework.exceptions import APIException
-from staff.models import ListModel as staff
+from django.contrib.auth.models import User
 
 CONECTINGS = {}
 
@@ -23,7 +23,7 @@ async def websocket_application(scope, receive, send):
             openid = qs.get('openid', [''])[0]
             sender = qs.get('sender', [''])[0]
             receiver = qs.get('receiver', [''])[0]
-            if staff.objects.filter(openid=openid, staff_name=receiver).exists():
+            if User.objects.filter(openid=openid, staff_name=receiver).exists():
                 sender_guy = sender + '-' + openid
                 receiver_guy = receiver + '-' + openid
                 text = {