This commit is contained in:
liqiang
2025-09-19 10:49:07 +08:00
parent 2d36b4357e
commit ab1e0268d1
42 changed files with 2520 additions and 2582 deletions

1
backend/.gitignore vendored
View File

@@ -93,7 +93,6 @@ ENV/
*.pyc *.pyc
conf/* conf/*
!conf/env.example.py !conf/env.example.py
db.sqlite3
media/ media/
__pypackages__/ __pypackages__/
package-lock.json package-lock.json

View File

@@ -28,7 +28,7 @@ from conf.env import *
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure--z8%exyzt7e_%i@1+#1mm=%lb5=^fx_57=1@a+_y7bg5-w%)sm" SECRET_KEY = "django-insecure--z8%exyz2sae4e_%i@1+#1mm=%lb5=^fx_57=1@a+_y7bg5-w%)sm"
# 初始化plugins插件路径到环境变量中 # 初始化plugins插件路径到环境变量中
PLUGINS_PATH = os.path.join(BASE_DIR, "plugins") PLUGINS_PATH = os.path.join(BASE_DIR, "plugins")
sys.path.insert(0, os.path.join(PLUGINS_PATH)) sys.path.insert(0, os.path.join(PLUGINS_PATH))
@@ -408,7 +408,8 @@ SHARED_APPS = []
# ********** 一键导入插件配置开始 ********** # ********** 一键导入插件配置开始 **********
# 例如: # 例如:
# from dvadmin_upgrade_center.settings import * # 升级中心 # from dvadmin_upgrade_center.settings import * # 升级中心
from dvadmin3_celery.settings import * # celery 异步任务 from code_info.settings import * # celery 异步任务
# from dvadmin3_celery.settings import * # celery 异步任务
# from dvadmin_third.settings import * # 第三方用户管理 # from dvadmin_third.settings import * # 第三方用户管理
# from dvadmin_ak_sk.settings import * # 秘钥管理管理 # from dvadmin_ak_sk.settings import * # 秘钥管理管理
# from dvadmin_tenants.settings import * # 租户管理 # from dvadmin_tenants.settings import * # 租户管理
@@ -416,3 +417,22 @@ from dvadmin3_celery.settings import * # celery 异步任务
#from dvadmin_uniapp.settings import * #from dvadmin_uniapp.settings import *
# ... # ...
# ********** 一键导入插件配置结束 ********** # ********** 一键导入插件配置结束 **********
if locals().get('ENVIRONMENT') != 'local':
# 1. 定义统一的迁移文件存放目录(例如:`./all_migrations/`
BASE_MIGRATIONS_DIR = "all_migrations"
# 2. 自动生成 MIGRATION_MODULES
# 2. 定义需要排除的 App如 Django 内置 App 或第三方 App
EXCLUDED_APPS = ('django','admin','auth.','contenttypes.','sessions.','psqlextra','rest_framework','drf_yasg',)
MIGRATION_MODULES = {}
for app in INSTALLED_APPS:
if app.startswith(EXCLUDED_APPS):
continue
# 判断是否存在目录并在目录下创建一个__init__.py 文件
app_migrations_dir = os.path.join(BASE_DIR, BASE_MIGRATIONS_DIR, app.split('.')[-1], 'migrations')
if not os.path.exists(app_migrations_dir):
os.makedirs(os.path.join(app_migrations_dir))
open(os.path.join(BASE_DIR, BASE_MIGRATIONS_DIR, app.split('.')[-1],'migrations', '__init__.py'), 'w').close()
MIGRATION_MODULES.update(
{app.split('.')[-1]: f"{BASE_MIGRATIONS_DIR}.{app.split('.')[-1]}.migrations"})

View File

@@ -1,6 +1,6 @@
[ [
{ {
"name": "DVAdmin团队", "name": "总部",
"key": "dvadmin", "key": "dvadmin",
"sort": 1, "sort": 1,
"owner": "", "owner": "",
@@ -11,7 +11,7 @@
"children": [ "children": [
{ {
"name": "运营部", "name": "运营部",
"key": "", "key": "zongbu",
"sort": 2, "sort": 2,
"owner": "", "owner": "",
"phone": "", "phone": "",

File diff suppressed because it is too large Load Diff

View File

@@ -97,7 +97,7 @@
"parent": 1, "parent": 1,
"title": "网站标题", "title": "网站标题",
"key": "site_title", "key": "site_title",
"value": "Dvadmin", "value": "协众防重码",
"sort": 1, "sort": 1,
"status": true, "status": true,
"data_options": null, "data_options": null,
@@ -111,7 +111,7 @@
"parent": 1, "parent": 1,
"title": "网站名称", "title": "网站名称",
"key": "site_name", "key": "site_name",
"value": "企业级后台管理系统", "value": "协众防重码系统",
"sort": 1, "sort": 1,
"status": true, "status": true,
"data_options": null, "data_options": null,
@@ -158,7 +158,7 @@
"parent": 1, "parent": 1,
"title": "版权信息", "title": "版权信息",
"key": "copyright", "key": "copyright",
"value": "2021-2024 django-vue-admin.com 版权所有", "value": "2021-2025 协众防重码系统 版权所有",
"sort": 4, "sort": 4,
"status": true, "status": true,
"data_options": null, "data_options": null,
@@ -177,7 +177,7 @@
"parent": 1, "parent": 1,
"title": "备案信息", "title": "备案信息",
"key": "keep_record", "key": "keep_record",
"value": "晋ICP备18005113号-3", "value": "",
"sort": 5, "sort": 5,
"status": true, "status": true,
"data_options": null, "data_options": null,
@@ -196,7 +196,7 @@
"parent": 1, "parent": 1,
"title": "帮助链接", "title": "帮助链接",
"key": "help_url", "key": "help_url",
"value": "https://django-vue-admin.com", "value": "#",
"sort": 6, "sort": 6,
"status": true, "status": true,
"data_options": null, "data_options": null,

View File

@@ -16,7 +16,8 @@
"last_name": "", "last_name": "",
"is_staff": true, "is_staff": true,
"is_active": true, "is_active": true,
"password": "pbkdf2_sha256$260000$g17x5wlSiW1FZAh1Eudchw$ZeSAqj3Xak0io8v/pmPW0BX9EX5R2zFXDwbbD68oBFk=", "description": "A01",
"password": "pbkdf2_sha256$600000$ITjuHRKiVCgPpiuCYxAaEp$aA9LDAfujtdMJRstK9YcBPz9a9MkfFG5Tsq1NviWxy0=",
"last_login": null, "last_login": null,
"is_superuser": true "is_superuser": true
}, },
@@ -34,7 +35,8 @@
"last_name": "", "last_name": "",
"is_staff": true, "is_staff": true,
"is_active": true, "is_active": true,
"password": "pbkdf2_sha256$260000$g17x5wlSiW1FZAh1Eudchw$ZeSAqj3Xak0io8v/pmPW0BX9EX5R2zFXDwbbD68oBFk=", "description": "A02",
"password": "pbkdf2_sha256$600000$ITjuHRKiVCgPpiuCYxAaEp$aA9LDAfujtdMJRstK9YcBPz9a9MkfFG5Tsq1NviWxy0=",
"last_login": null, "last_login": null,
"is_superuser": false "is_superuser": false
}, },
@@ -49,11 +51,12 @@
"role": [], "role": [],
"role_key": ["public"], "role_key": ["public"],
"dept_key": "technology", "dept_key": "technology",
"description": "A03",
"first_name": "", "first_name": "",
"last_name": "", "last_name": "",
"is_staff": true, "is_staff": true,
"is_active": true, "is_active": true,
"password": "pbkdf2_sha256$260000$g17x5wlSiW1FZAh1Eudchw$ZeSAqj3Xak0io8v/pmPW0BX9EX5R2zFXDwbbD68oBFk=", "password": "pbkdf2_sha256$600000$ITjuHRKiVCgPpiuCYxAaEp$aA9LDAfujtdMJRstK9YcBPz9a9MkfFG5Tsq1NviWxy0=",
"last_login": null, "last_login": null,
"is_superuser": false "is_superuser": false
} }

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class CodeInfoConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'plugins.code_info'

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,49 @@
[ {
"name": "扫码信息",
"icon": "ele-Bell",
"sort": 8,
"is_link": false,
"is_catalog": true,
"web_path": "/releaseInfo",
"component": "",
"component_name": "",
"status": true,
"cache": true,
"visible": true,
"parent": null,
"children": [
{
"name": "扫码数据",
"icon": "ele-Bell",
"sort": 1,
"is_link": false,
"is_catalog": false,
"web_path": "/scanData",
"component": "plugins/scanInfo/src/scanData/index",
"component_name": "scanData",
"status": true,
"cache": false,
"visible": true,
"children": [],
"menu_button": [],
"menu_field": []
},{
"name": "异常扫码记录",
"icon": "ele-Bell",
"sort": 2,
"is_link": false,
"is_catalog": false,
"web_path": "/scanRecord",
"component": "plugins/scanInfo/src/scanRecord/index",
"component_name": "scanRecord",
"status": true,
"cache": false,
"visible": true,
"children": [],
"menu_button": [],
"menu_field": []
}
],
"menu_button": [],
"menu_field": []
}]

View File

@@ -0,0 +1,33 @@
# 初始化
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "application.settings")
django.setup()
from dvadmin.system.fixtures.initSerializer import MenuInitSerializer, SystemConfigInitSerializer, \
DictionaryInitSerializer
from dvadmin.utils.core_initialize import CoreInitialize
class Initialize(CoreInitialize):
def init_menu(self):
"""
初始化菜单信息
"""
self.init_base(MenuInitSerializer, unique_fields=['name', 'web_path', 'component', 'component_name'])
def init_dictionary(self):
"""
初始化字典表
"""
self.init_base(DictionaryInitSerializer, unique_fields=['value', 'parent', ])
def run(self):
self.init_menu()
self.init_dictionary()
print(22)
if __name__ == '__main__':
Initialize(app='release_info').run()

View File

@@ -0,0 +1,29 @@
from django.db import models
from django.db import models
from dvadmin.utils.models import CoreModel
table_prefix = "release_info_"
class ScanData(CoreModel):
# 产品件号、供应商代码、生产批次、产品序列码、版本号、扫码时间、班次和人员信息
code = models.CharField(null=True, blank=True, max_length=255, verbose_name='扫码值', help_text='扫码值')
product_code = models.CharField(null=True, blank=True, max_length=255, verbose_name='产品件号', help_text='产品件号')
supplier_code = models.CharField(null=True, blank=True, max_length=255, verbose_name='供应商代码', help_text='供应商代码')
production_batch = models.CharField(null=True, blank=True, max_length=255, verbose_name='生产批次', help_text='生产批次')
product_serial_number = models.CharField(null=True, blank=True, max_length=255, verbose_name='产品序列码', help_text='产品序列码')
version_number = models.CharField(null=True, blank=True, max_length=255, verbose_name='版本号', help_text='版本号')
shift = models.CharField(null=True, blank=True, max_length=255, verbose_name='班次', help_text='班次')
STATUS_EMU = (
(0, "重复扫码"),
(1, "正常"),
(2, "未识别码"),
)
status = models.IntegerField(default=1, choices=STATUS_EMU, verbose_name='状态', help_text='状态')
class Meta:
db_table = table_prefix + "scan_data"
verbose_name = "扫码数据"
verbose_name_plural = verbose_name
ordering = ('-create_datetime',)

View File

@@ -0,0 +1,22 @@
from application import settings
# ================================================= #
# ***************** 插件配置区开始 *******************
# ================================================= #
# 路由配置
plugins_url_patterns = [
{"re_path": r'api/code_info/', "include": "code_info.urls"},
]
# app 配置
apps = ['code_info']
# 租户模式中public模式共享app配置
tenant_shared_apps = ['code_info']
# ================================================= #
# ******************* 插件配置区结束 *****************
# ================================================= #
# ********** 赋值到 settings 中 **********
settings.INSTALLED_APPS += [app for app in apps if app not in settings.INSTALLED_APPS]
settings.TENANT_SHARED_APPS += tenant_shared_apps
# ********** 注册路由 **********
settings.PLUGINS_URL_PATTERNS += plugins_url_patterns

View File

@@ -0,0 +1,15 @@
from django.urls import path
from rest_framework import routers
from plugins.code_info.views.scan_data import ScanDataViewSet
from plugins.code_info.views.scan_record import ScanRecordViewSet
route_url = routers.SimpleRouter()
route_url.register(r'scan_data', ScanDataViewSet,basename='scan_data')
route_url.register(r'scan_record', ScanRecordViewSet)
urlpatterns = [
]
urlpatterns += route_url.urls

View File

@@ -0,0 +1,67 @@
from dvadmin.utils.field_permission import FieldPermissionMixin
from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.validator import CustomValidationError
from dvadmin.utils.viewset import CustomModelViewSet
from plugins.code_info.models import ScanData
class ScanDataSerializer(CustomModelSerializer):
"""
扫码数据-序列化器
"""
class Meta:
model = ScanData
fields = "__all__"
read_only_fields = ["id"]
class CreateScanDataSerializer(CustomModelSerializer):
"""
扫码数据-序列化器
"""
def create(self, validated_data):
code = validated_data.get("code")
print(code)
code_list = code.split("/")
if len(code_list) == 5:
validated_data["product_code"] = code_list[0]
validated_data["supplier_code"] = code_list[1]
validated_data["production_batch"] = code_list[2]
validated_data["product_serial_number"] = code_list[3]
validated_data["version_number"] = code_list[4]
validated_data["shift"] = self.request.user.description
instance = super().create(validated_data)
# 1.格式错误
if len(code_list) != 5:
instance.status = 2
instance.save()
raise CustomValidationError("数据格式错误")
# 2.查询数据是否已存在数据库
print("ScanData.objects.filter(code=code, status=1)",ScanData.objects.filter(code=code, status=1))
if ScanData.objects.filter(code=code, status=1).exclude(id=instance.id).exists():
instance.status = 0
instance.save()
raise CustomValidationError("重复扫码")
return instance
class Meta:
model = ScanData
fields = "__all__"
read_only_fields = ["id"]
class ScanDataViewSet(CustomModelViewSet, FieldPermissionMixin):
"""
扫码数据接口
list:查询
create:新增
update:修改
retrieve:单例
destroy:删除
"""
queryset = ScanData.objects.filter(status=1)
serializer_class = ScanDataSerializer
create_serializer_class = CreateScanDataSerializer
extra_filter_class = []

View File

@@ -0,0 +1,29 @@
from dvadmin.utils.field_permission import FieldPermissionMixin
from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.viewset import CustomModelViewSet
from plugins.code_info.models import ScanData
class ScanRecordSerializer(CustomModelSerializer):
"""
扫码数据-序列化器
"""
class Meta:
model = ScanData
fields = "__all__"
read_only_fields = ["id"]
class ScanRecordViewSet(CustomModelViewSet, FieldPermissionMixin):
"""
异常扫码数据记录接口
list:查询
create:新增
update:修改
retrieve:单例
destroy:删除
"""
queryset = ScanData.objects.exclude(status=1)
serializer_class = ScanRecordSerializer
extra_filter_class = []

View File

@@ -8,8 +8,7 @@ django-restql==0.15.4
django-simple-captcha==0.6.0 django-simple-captcha==0.6.0
django-timezone-field==7.0 django-timezone-field==7.0
djangorestframework_simplejwt==5.4.0 djangorestframework_simplejwt==5.4.0
drf-yasg==1.21.7 drf-yasg==1.21.10
mysqlclient==2.2.0
pypinyin==0.51.0 pypinyin==0.51.0
ua-parser==0.18.0 ua-parser==0.18.0
pyparsing==3.1.2 pyparsing==3.1.2

View File

@@ -1,168 +0,0 @@
# Django-Vue3-Admin
[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/huge-dream/django-vue3-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/huge-dream/django-vue3-admin/badge/star.svg?theme=dark)](https://gitee.com/huge-dream/django-vue3-admin)
[preview](https://demo.dvadmin.com) | [Official website document](https://www.django-vue-admin.com) | [qq group](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [community](https://bbs.django-vue-admin.com) | [plugins market](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin)
💡 **「About」**
We are a group of young people who love Code. In this hot era, we hope to calm down and bring some of our colors and colors through code.
Because of love, so embrace the future
## framework introduction
💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) Is a set of all open source rapid development platform, no reservation for individuals and enterprises free use.
* 🧑🤝🧑Front-end adoption Vue3+TS+pinia+fastcrud。
* 👭The backend uses the Python language Django framework as well as the powerful[Django REST Framework](https://pypi.org/project/djangorestframework)。
* 👫Permission authentication use[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt)Supports the multi-terminal authentication system.
* 👬Support loading dynamic permission menu, multi - way easy permission control.
* 💏 Special thanks: [vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/).
* 💡 💏 Special thanks:[jetbrains](https://www.jetbrains.com/) To provide a free IntelliJ IDEA license for this open source project.
## Online experience
👩‍👧‍👦👩‍👧‍👦 demo address:[https://demo.dvadmin.com](https://demo.dvadmin.com)
* demo accountsuperadmin
* demo passwordadmin123456
👩👦👦docs:[https://django-vue-admin.com](https://django-vue-admin.com)
## communication
* Communication community:[click here](https://bbs.django-vue-admin.com)👩‍👦‍👦
* plugins market:[click here](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦
## source code url:
gitee(Main push)[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦
githubno data
## core function
1. 👨‍⚕️ Menu management: Configure the system menu, operation permissions, button permissions, back-end interface permissions, etc.
2. 🧑‍⚕️ Department management: Configure the system organization (company, department, role).
3. 👩‍⚕️ Role management: role menu permission allocation, data permission allocation, set roles according to the department for data range permission division.
4. 🧑‍🎓 Rights Specifies the rights of the authorization role.
5. 👨‍🎓 User management: The user is the system operator, this function mainly completes the system user configuration.
6. 👬 Interface whitelist: specifies the interface that does not need permission verification.
7. 🧑‍🔧 Dictionary management: Maintenance of some fixed data frequently used in the system.
8. 🧑‍🔧 Regional management: to manage provinces, cities, counties and regions.
9. 📁 Attachment management: Unified management of all files and pictures on the platform.
10. 🗓 operation logs: log and query the system normal operation; Log and query system exception information.
11.🔌 [plugins market] (<https://bbs.django-vue-admin.com/plugMarket.html>) : based on the Django framework - Vue - Admin application and plug-in development.
## plugins market 🔌
* Celery Asynchronous task[dvadmin-celery](https://gitee.com/huge-dream/dvadmin-celery)
* Upgrade center backend[dvadmin-upgrade-center](https://gitee.com/huge-dream/dvadmin-upgrade-center)
* Upgrade center front[dvadmin-upgrade-center-web](https://gitee.com/huge-dream/dvadmin-upgrade-center-web)
## before start project you need:
~~~
Python >= 3.8.0
nodejs >= 14.0
Mysql >= 5.7.0 (Optional. The default database is sqlite3. 8.0 is recommended)
Redis(Optional, the latest edition)
~~~
## frontend♝
```bash
# clone code
git clone https://gitee.com/huge-dream/django-vue3-admin.git
# enter code dir
cd web
# install dependence
npm install --registry=https://registry.npm.taobao.org
# Start service
npm run dev
# Visit http://localhost:8080 in your browser
# Parameters such as boot port can be configured in the #.env.development file
# Build the production environment
# npm run build
```
## backend💈
~~~bash
1. enter code dir cd backend
2. copy ./conf/env.example.py to ./conf dirrename as env.py
3. in env.py configure database information
mysql database recommended version: 8.0
mysql database character set: utf8mb4
4. install pip dependence
pip3 install -r requirements.txt
5. Execute the migration command:
python3 manage.py makemigrations
python3 manage.py migrate
6. Initialization data
python3 manage.py init
7. Initialize provincial, municipal and county data:
python3 manage.py init_area
8. start backend
python3 manage.py runserver 0.0.0.0:8000
or daphne :
daphne -b 0.0.0.0 -p 8000 application.asgi:application
~~~
### visit backend swagger
* visit url[http://localhost:8080](http://localhost:8080) (The default address is this one. If you want to change it, follow the configuration file)
* account`superadmin` password`admin123456`
### docker-compose
~~~shell
docker-compose up -d
# Initialize backend data (first execution only)
docker exec -ti dvadmin-django bash
python manage.py makemigrations
python manage.py migrate
python manage.py init_area
python manage.py init
exit
frontend urlhttp://127.0.0.1:8080
backend urlhttp://127.0.0.1:8080/api
# Change 127.0.0.1 to your own public ip address on the server
account`superadmin` password`admin123456`
# docker-compose stop
docker-compose down
# docker-compose restart
docker-compose restart
# docker-compose on start build
docker-compose up -d --build
~~~
## Demo screenshot✅
![image-01](https://images.gitee.com/uploads/images/2022/0530/234137_b58c8f98_5074988.png)
![image-02](https://images.gitee.com/uploads/images/2022/0530/234240_39834603_5074988.png)
![image-03](https://images.gitee.com/uploads/images/2022/0530/234339_35e728a0_5074988.png)
![image-04](https://images.gitee.com/uploads/images/2022/0530/234426_957036b0_5074988.png)
![image-05](https://images.gitee.com/uploads/images/2022/0530/234458_898be492_5074988.png)
![image-06](https://images.gitee.com/uploads/images/2022/0530/234521_35b40076_5074988.png)
![image-07](https://images.gitee.com/uploads/images/2022/0530/234615_c2325639_5074988.png)
![image-08](https://images.gitee.com/uploads/images/2022/0530/234639_1ed6cc93_5074988.png)
![image-09](https://images.gitee.com/uploads/images/2022/0530/234815_cea2c53f_5074988.png)
![image-10](https://images.gitee.com/uploads/images/2022/0530/234840_5f3e5f53_5074988.png)

View File

@@ -1,212 +0,0 @@
# Django-Vue3-Admin
[![img](https://img.shields.io/badge/license-MIT-blue.svg)](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [![img](https://img.shields.io/badge/python-%3E=3.7.x-green.svg)](https://python.org/) [![PyPI - Django Version badge](https://img.shields.io/badge/django%20versions-3.2-blue)](https://docs.djangoproject.com/zh-hans/3.2/) [![img](https://img.shields.io/badge/node-%3E%3D%2012.0.0-brightgreen)](https://nodejs.org/zh-cn/) [![img](https://gitee.com/liqianglog/django-vue-admin/badge/star.svg?theme=dark)](https://gitee.com/liqianglog/django-vue-admin)
[预 览](https://demo.dvadmin.com) | [官网文档](https://www.django-vue-admin.com) | [群聊](https://qm.qq.com/cgi-bin/qm/qr?k=fOdnHhC8DJlRHGYSnyhoB8P5rgogA6Vs&jump_from=webapi) | [社区](https://bbs.django-vue-admin.com) | [插件市场](https://bbs.django-vue-admin.com/plugMarket.html) | [Github](https://github.com/liqianglog/django-vue-admin)
💡 **「关于」**
我们是一群热爱代码的青年在这个炙热的时代下我们希望静下心来通过Code带来一点我们的色彩和颜色。
因为热爱,所以拥抱未来!
## 平台简介
💡 [django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin.git) 是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。
django-vue3-admin 基于 vue3 + CompositionAPI + typescript + vite + element plus, 是一款全栈,快速,开源的后台管理系统!
* 🧑‍🤝‍🧑前端采用 Vue3+TS+pinia+fastcrud(感谢[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/))
* 👭后端采用 Python 语言 Django 框架以及强大的 [Django REST Framework](https://pypi.org/project/djangorestframework)。
* 👫权限认证使用[Django REST Framework SimpleJWT](https://pypi.org/project/djangorestframework-simplejwt),支持多终端认证系统。
* 👬支持加载动态权限菜单,多方式轻松权限控制。
* 💏特别鸣谢:[vue-next-admin](https://lyt-top.gitee.io/vue-next-admin-doc-preview/)。
* 💡 特别感谢[jetbrains](https://www.jetbrains.com/) 为本开源项目提供免费的 IntelliJ IDEA 授权。
#### 🏭 环境支持
| Edge | Firefox | Chrome | Safari |
| --------- | ------------ | ----------- | ----------- |
| Edge ≥ 79 | Firefox ≥ 78 | Chrome ≥ 64 | Safari ≥ 12 |
> 由于 Vue3 不再支持 IE11故而 ElementPlus 也不支持 IE11 及之前版本。
## 在线体验
👩‍👧‍👦演示地址:[https://demo.dvadmin.com](https://demo.dvadmin.com)
- 账号superadmin
- 密码admin123456
👩‍👦‍👦文档地址:[coding](https://dvadmin-private.coding.net/share/km/cec69f3d-30fe-47d5-bd97-e9e851f0b776/K-2)
## 给框架点赞
<img src='https://django-vue-admin.com/alipay.jpg' width='200'>
<img src='https://django-vue-admin.com/wechat.jpg' width='200'>
## 交流
- 交流社区:[戳我](https://bbs.django-vue-admin.com)👩‍👦‍👦
- 插件市场:[戳我](https://bbs.django-vue-admin.com/plugMarket.html)👩‍👦‍👦
- django-vue-admin交流01群(已满)812482043 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=aJVwjDvH-Es4MPJQuoO32N0SucK22TE5&jump_from=webapi)
- django-vue-admin交流02群(已满)687252418 [点击链接加入群聊](https://qm.qq.com/cgi-bin/qm/qr?k=4jJN4IjWGfxJ8YJXbb_gTsuWjR34WLdc&jump_from=webapi)
- django-vue-admin交流03群442108213 [点击链接加入群聊](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=wsm5oSz3K8dElBYUDtLTcQSEPhINFkl8&authKey=M6sbER0z59ZakgBr5erFeZyFZU15CI52bErNZa%2FxSvvGIuVAbY0N5866v89hm%2FK4&noverify=0&group_code=442108213)
- 二维码
<img src='https://images.gitee.com/uploads/images/2022/0530/233203_5fb11883_5074988.jpeg' width='200'>
## 源码地址
gitee地址(主推)[https://gitee.com/huge-dream/django-vue3-admin](https://gitee.com/huge-dream/django-vue3-admin)👩‍👦‍👦
github地址暂无
## 内置功能
1. 👨‍⚕️菜单管理:配置系统菜单,操作权限,按钮权限标识、后端接口权限等。
2. 🧑‍⚕️部门管理:配置系统组织机构(公司、部门、角色)。
3. 👩‍⚕️角色管理:角色菜单权限分配、数据权限分配、设置角色按部门进行数据范围权限划分。
4. 🧑‍🎓按钮权限权限:授权角色的按钮权限和接口权限,可做到每一个接口都能授权数据范围。
5. 🧑‍🎓字段权限权限:授权页面的字段显示权限。
5. 👨‍🎓用户管理:用户是系统操作者,该功能主要完成系统用户配置。
6. 👬接口白名单:配置不需要进行权限校验的接口。
7. 🧑‍🔧字典管理:对系统中经常使用的一些较为固定的数据进行维护。
8. 🧑‍🔧地区管理:对省市县区域进行管理。
9. 📁附件管理:对平台上所有文件、图片等进行统一管理。
10. 🗓️操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
11. 🔌[插件市场 ](https://bbs.django-vue-admin.com/plugMarket.html)基于Django-Vue-Admin框架开发的应用和插件。
## 插件市场 🔌
- Celery异步任务[dvadmin-celery](https://gitee.com/huge-dream/dvadmin-celery)
- 升级中心后端:[dvadmin-upgrade-center](https://gitee.com/huge-dream/dvadmin-upgrade-center)
- 升级中心前端:[dvadmin-upgrade-center-web](https://gitee.com/huge-dream/dvadmin-upgrade-center-web)
## 准备工作
~~~
Python >= 3.8.0 (推荐3.8+版本)
nodejs >= 14.0 (推荐最新)
Mysql >= 5.7.0 (可选默认数据库sqlite3推荐8.0版本)
Redis(可选,最新版)
~~~
## 前端♝
```bash
# 克隆项目
git clone https://gitee.com/huge-dream/django-vue3-admin.git
# 进入项目目录
cd web
# 安装依赖
npm install --registry=https://registry.npm.taobao.org
# 启动服务
npm run dev
# 浏览器访问 http://localhost:8080
# .env.development 文件中可配置启动端口等参数
# 构建生产环境
# npm run build
```
## 后端💈
~~~bash
1. 进入项目目录 cd backend
2. 在项目根目录中,复制 ./conf/env.example.py 文件为一份新的到 ./conf 文件夹下,并重命名为 env.py
3. 在 env.py 中配置数据库信息
mysql数据库版本建议8.0
mysql数据库字符集utf8mb4
4. 安装依赖环境
pip3 install -r requirements.txt
5. 执行迁移命令:
python3 manage.py makemigrations
python3 manage.py migrate
6. 初始化数据
python3 manage.py init
7. 初始化省市县数据:
python3 manage.py init_area
8. 启动项目
python3 manage.py runserver 0.0.0.0:8000
或使用 daphne :
daphne -b 0.0.0.0 -p 8000 application.asgi:application
~~~
### 访问项目
- 访问地址:[http://localhost:8080](http://localhost:8080) (默认为此地址,如有修改请按照配置文件)
- 账号:`superadmin` 密码:`admin123456`
### docker-compose 运行
~~~shell
# 先安装docker-compose (自行百度安装),执行此命令等待安装如有使用celery插件请打开docker-compose.yml中celery 部分注释
docker-compose up -d
# 初始化后端数据(第一次执行即可)
docker exec -ti dvadmin-django bash
python manage.py makemigrations
python manage.py migrate
python manage.py init_area
python manage.py init
exit
前端地址http://127.0.0.1:8080
后端地址http://127.0.0.1:8080/api
# 在服务器上请把127.0.0.1 换成自己公网ip
账号superadmin 密码admin123456
# docker-compose 停止
docker-compose down
# docker-compose 重启
docker-compose restart
# docker-compose 启动时重新进行 build
docker-compose up -d --build
~~~
## 演示图✅
![image-01](https://images.gitee.com/uploads/images/2022/0530/234137_b58c8f98_5074988.png)
![image-02](https://images.gitee.com/uploads/images/2022/0530/234240_39834603_5074988.png)
![image-03](https://images.gitee.com/uploads/images/2022/0530/234339_35e728a0_5074988.png)
![image-04](https://images.gitee.com/uploads/images/2022/0530/234426_957036b0_5074988.png)
![image-05](https://images.gitee.com/uploads/images/2022/0530/234458_898be492_5074988.png)
![image-06](https://images.gitee.com/uploads/images/2022/0530/234521_35b40076_5074988.png)
![image-07](https://images.gitee.com/uploads/images/2022/0530/234615_c2325639_5074988.png)
![image-08](https://images.gitee.com/uploads/images/2022/0530/234639_1ed6cc93_5074988.png)
![image-09](https://images.gitee.com/uploads/images/2022/0530/234815_cea2c53f_5074988.png)
![image-10](https://images.gitee.com/uploads/images/2022/0530/234840_5f3e5f53_5074988.png)

View File

@@ -5,6 +5,7 @@
<LockScreen v-if="themeConfig.isLockScreen" /> <LockScreen v-if="themeConfig.isLockScreen" />
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" /> <Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
<CloseFull v-if="!themeConfig.isLockScreen" /> <CloseFull v-if="!themeConfig.isLockScreen" />
<Scanned ref="scannedRef" ></Scanned>
<!-- <Upgrade v-if="getVersion" />--> <!-- <Upgrade v-if="getVersion" />-->
</el-config-provider> </el-config-provider>
</template> </template>
@@ -20,17 +21,22 @@ import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage'; import { Local, Session } from '/@/utils/storage';
import mittBus from '/@/utils/mitt'; import mittBus from '/@/utils/mitt';
import setIntroduction from '/@/utils/setIconfont'; import setIntroduction from '/@/utils/setIconfont';
import Scan from './scan';
const scan = new Scan(200); // 200是扫码枪有效输入间隔毫秒
let removeScanListener: () => void;
// 引入组件 // 引入组件
const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue')); const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
const Setings = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue')); const Setings = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue'));
const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue')); const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue'));
const Upgrade = defineAsyncComponent(() => import('/@/layout/upgrade/index.vue')); const Scanned = defineAsyncComponent(() => import('/@/layout/Scanned/index.vue'));
import { ElMessageBox, ElNotification, NotificationHandle } from 'element-plus'; import { ElMessageBox, ElNotification, NotificationHandle } from 'element-plus';
import { useCore } from '/@/utils/cores'; import { useCore } from '/@/utils/cores';
// 定义变量内容 // 定义变量内容
const { messages, locale } = useI18n(); const { messages, locale } = useI18n();
const setingsRef = ref(); const setingsRef = ref();
const scannedRef = ref();
const route = useRoute(); const route = useRoute();
const stores = useTagsViewRoutes(); const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig(); const storesThemeConfig = useThemeConfig();
@@ -61,8 +67,21 @@ onBeforeMount(() => {
// 设置批量第三方 js // 设置批量第三方 js
setIntroduction.jsCdn(); setIntroduction.jsCdn();
}); });
// 扫码后的 处理
const handleScan = (code: string) => {
console.log('Scanned code:', code);
scannedRef.value.isShow = true;
scannedRef.value.scanCode = code;
scannedRef.value.postData();
// 处理扫描后的逻辑
};
// 页面加载时 // 页面加载时
onMounted(() => { onMounted(() => {
// 开始监听扫码枪扫码并设置回调
removeScanListener = scan.onScan(handleScan);
scan.start();
nextTick(() => { nextTick(() => {
// 监听布局配'置弹窗点击打开 // 监听布局配'置弹窗点击打开
mittBus.on('openSetingsDrawer', () => { mittBus.on('openSetingsDrawer', () => {
@@ -89,7 +108,13 @@ onMounted(() => {
}); });
// 页面销毁时,关闭监听布局配置/i18n监听 // 页面销毁时,关闭监听布局配置/i18n监听
onUnmounted(() => { onUnmounted(() => {
// 结束监听
scan.stop();
// 移除全局事件监听器
if (removeScanListener) removeScanListener();
mittBus.off('openSetingsDrawer', () => {}); mittBus.off('openSetingsDrawer', () => {});
mittBus.off('scanDataDoRefresh', () => {});
}); });
</script> </script>

View File

@@ -0,0 +1,12 @@
import { request } from '/@/utils/service';
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
export const apiPrefix = '/api/code_info/scan_data/';
export function AddObj(obj: AddReq) {
return request({
url: apiPrefix,
method: 'post',
data: obj,
});
}

View File

@@ -0,0 +1,87 @@
<template>
<div class="upgrade-dialog">
<el-dialog
v-model="isShow"
width="500px"
destroy-on-close
:show-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div style="text-align: center">
<div v-if="status===0">
<el-icon size="100" color="rgb(177, 179, 184)"><Loading /></el-icon>
<div style="font-size: 18px">识别中请稍等</div>
</div>
<div v-if="status===1">
<el-icon size="100" color="#57B046"><CircleCheckFilled /></el-icon>
<div style="font-size: 18px">扫码成功</div>
</div>
<div v-if="status===2">
<el-icon size="100" color="#F56C6C"><CircleCloseFilled /></el-icon>
<div style="font-size: 18px">条码重复</div>
</div>
<div v-if="status===3">
<el-icon size="100" color="#F56C6C"><WarningFilled /></el-icon>
<div style="font-size: 18px">条码错误</div>
</div>
<div style="font-size: 16px;color: #979b9c">{{scanCode}}</div>
<div style="width: 100%;margin-top: 50px;">
<el-button type="primary" style="width: 300px;" size="large" @click="onclick" v-if="time !== -1">{{time}}秒后自动关闭</el-button>
<el-button type="primary" style="width: 300px;" size="large" @click="onclick" v-else>关闭</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="layoutUpgrade">
import {reactive, computed, onMounted, ref} from 'vue';
import * as api from './api';
import mittBus from "/@/utils/mitt";
const isShow = ref(false);
const scanCode = ref('');
const status = ref(0);
const time = ref(-1)
const timer = ref()
const postData = () => {
api.AddObj({
code: scanCode.value,
}).then(() => {
time.value = 5;
status.value = 1;
timer.value = setInterval(() => {
if (time.value <= 0){
onclick()
clearTimeout(timer.value)
time.value = 5
} else {
time.value -= 1
}
}, 1000)
}).catch((err: any) => {
status.value = err?.msg === '重复扫码' ? 2 : 3
time.value = -1
})
}
const onclick = () => {
isShow.value = false;
mittBus.emit('scanDataDoRefresh', () => {});
}
// 页面加载时
onMounted(() => {
});
// 暴露变量
defineExpose({
isShow,
onclick,
scanCode,
postData
});
</script>
<style scoped lang="scss">
</style>

View File

@@ -4,7 +4,7 @@
<el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll layout-backtop-header-fixed" <el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll layout-backtop-header-fixed"
wrap-class="layout-main-scroll" view-class="layout-main-scroll"> wrap-class="layout-main-scroll" view-class="layout-main-scroll">
<LayoutParentView /> <LayoutParentView />
<LayoutFooter v-if="isFooter" /> <!-- <LayoutFooter v-if="isFooter" />-->
</el-scrollbar> </el-scrollbar>
<el-backtop :target="setBacktopClass" /> <el-backtop :target="setBacktopClass" />
</el-main> </el-main>
@@ -20,7 +20,7 @@ import { NextLoading } from '/@/utils/loading';
// 引入组件 // 引入组件
const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue')); const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue'));
const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue')); // const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue'));
// 定义变量内容 // 定义变量内容
const layoutMainScrollbarRef = ref(); const layoutMainScrollbarRef = ref();

View File

@@ -1,8 +1,5 @@
<template> <template>
<div class="layout-footer pb5 pt2"> <div class="layout-footer pb5 pt2">
<div class="layout-footer-warp">
<div> Powered by Django-Vue3-Admin Copyright © DVAdmin团队 </div>
</div>
</div> </div>
</template> </template>
@@ -11,14 +8,4 @@
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.layout-footer {
width: 100%;
display: flex;
&-warp {
margin: auto;
color: var(--el-text-color-secondary);
text-align: center;
animation: error-num 0.3s ease;
}
}
</style> </style>

84
web/src/scan.ts Normal file
View File

@@ -0,0 +1,84 @@
// scan.ts
export default class Scan {
private barCode: string = '';
private lastTime: number = 0;
private timeout: number;
private timer: NodeJS.Timeout | null = null;
private eventHandler: (e: KeyboardEvent) => void;
constructor(timeout = 100) {
this.timeout = timeout;
this.eventHandler = this.eventListenerScanCode.bind(this);
}
private eventListenerScanCode(e: KeyboardEvent): void {
const currCode = e.keyCode || e.which || e.charCode;
const currTime = new Date().getTime();
if (this.lastTime > 0) {
if (currTime - this.lastTime <= this.timeout) {
this.barCode += String.fromCharCode(currCode!);
} else {
this.clearBarCode();
}
} else {
this.barCode = String.fromCharCode(currCode!);
}
// console.log(currTime, "监听到的值:", this.barCode);
this.lastTime = currTime;
if (currCode === 13) { // Enter键
if (this.barCode) {
const code = this.barCode.substring(0, this.barCode.length - 1).trim(); // 去除末尾的Enter键
if (code) {
this.emitScan(code);
}
}
this.clearBarCode();
}
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
if (this.lastTime) {
this.clearBarCode();
console.log("执行清空");
}
clearTimeout(this.timer);
}, this.timeout);
}
private clearBarCode(): void {
this.barCode = '';
this.lastTime = 0;
if (this.timer) {
clearTimeout(this.timer);
this.timer = null;
}
}
public start(): void {
window.addEventListener("keypress", this.eventHandler);
}
public stop(): void {
window.removeEventListener("keypress", this.eventHandler);
this.clearBarCode();
}
private emitScan(code: string): void {
this.onScanCallbacks.forEach(callback => callback(code));
}
private onScanCallbacks: ((code: string) => void)[] = [];
public onScan(callback: (code: string) => void): () => void {
this.onScanCallbacks.push(callback);
console.log(9090)
return () => {
this.onScanCallbacks = this.onScanCallbacks.filter(cb => cb !== callback);
};
}
}

View File

@@ -11,140 +11,51 @@ import { defineStore } from 'pinia';
export const useThemeConfig = defineStore('themeConfig', { export const useThemeConfig = defineStore('themeConfig', {
state: (): ThemeConfigState => ({ state: (): ThemeConfigState => ({
themeConfig: { themeConfig: {
// 是否开启布局配置抽屉
isDrawer: false, isDrawer: false,
/**
* 全局主题
*/
// 默认 primary 主题颜色
primary: '#409eff', primary: '#409eff',
// 是否开启深色模式
isIsDark: false, isIsDark: false,
topBar: '#edf1ff',
/**
* 顶栏设置
*/
// 默认顶栏导航背景颜色
topBar: '#ffffff',
// 默认顶栏导航字体颜色
topBarColor: '#606266', topBarColor: '#606266',
// 是否开启顶栏背景颜色渐变
isTopBarColorGradual: false, isTopBarColorGradual: false,
menuBar: '#edf1ff',
/** menuBarColor: '#000000',
* 菜单设置 menuBarActiveColor: 'rgb(237, 241, 255)',
*/
// 默认菜单导航背景颜色
menuBar: '#334054',
// 默认菜单导航字体颜色
menuBarColor: '#eaeaea',
// 默认菜单高亮背景色
menuBarActiveColor: 'rgba(0, 0, 0, 0.2)',
// 是否开启菜单背景颜色渐变
isMenuBarColorGradual: false, isMenuBarColorGradual: false,
/**
* 分栏设置
*/
// 默认分栏菜单背景颜色
columnsMenuBar: '#334054', columnsMenuBar: '#334054',
// 默认分栏菜单字体颜色
columnsMenuBarColor: '#e6e6e6', columnsMenuBarColor: '#e6e6e6',
// 是否开启分栏菜单背景颜色渐变
isColumnsMenuBarColorGradual: false, isColumnsMenuBarColorGradual: false,
// 是否开启分栏菜单鼠标悬停预加载(预览菜单)
isColumnsMenuHoverPreload: false, isColumnsMenuHoverPreload: false,
/**
* 界面设置
*/
// 是否开启菜单水平折叠效果
isCollapse: false, isCollapse: false,
// 是否开启菜单手风琴效果
isUniqueOpened: true, isUniqueOpened: true,
// 是否开启固定 Header isFixedHeader: true,
isFixedHeader: false,
// 初始化变量,用于更新菜单 el-scrollbar 的高度,请勿删除
isFixedHeaderChange: false, isFixedHeaderChange: false,
// 是否开启经典布局分割菜单(仅经典布局生效)
isClassicSplitMenu: false, isClassicSplitMenu: false,
// 是否开启自动锁屏
isLockScreen: false, isLockScreen: false,
// 开启自动锁屏倒计时(s/秒)
lockScreenTime: 30, lockScreenTime: 30,
/**
* 界面显示
*/
// 是否开启侧边栏 Logo
isShowLogo: true, isShowLogo: true,
// 初始化变量,用于 el-scrollbar 的高度更新,请勿删除
isShowLogoChange: false, isShowLogoChange: false,
// 是否开启 Breadcrumb,强制经典、横向布局不显示 isBreadcrumb: false,
isBreadcrumb: true,
// 是否开启 Tagsview
isTagsview: true, isTagsview: true,
// 是否开启 Breadcrumb 图标
isBreadcrumbIcon: true, isBreadcrumbIcon: true,
// 是否开启 Tagsview 图标
isTagsviewIcon: true, isTagsviewIcon: true,
// 是否开启 TagsView 缓存
isCacheTagsView: true, isCacheTagsView: true,
// 是否开启 TagsView 拖拽
isSortableTagsView: true, isSortableTagsView: true,
// 是否开启 TagsView 共用
isShareTagsView: false, isShareTagsView: false,
// 是否开启 Footer 底部版权信息
isFooter: true, isFooter: true,
// 是否开启灰色模式
isGrayscale: false, isGrayscale: false,
// 是否开启色弱模式
isInvert: false, isInvert: false,
// 是否开启水印
isWartermark: false, isWartermark: false,
// 水印文案
wartermarkText: '', wartermarkText: '',
/**
* 其它设置
*/
// Tagsview 风格:可选值"<tags-style-one|tags-style-four|tags-style-five>",默认 tags-style-five
// 定义的值与 `/src/layout/navBars/tagsView/tagsView.vue` 中的 class 同名
tagsStyle: 'tags-style-five', tagsStyle: 'tags-style-five',
// 主页面切换动画:可选值"<slide-right|slide-left|opacitys>",默认 slide-right
animation: 'slide-right', animation: 'slide-right',
// 分栏高亮风格:可选值"<columns-round|columns-card>",默认 columns-round
columnsAsideStyle: 'columns-round', columnsAsideStyle: 'columns-round',
// 分栏布局风格:可选值"<columns-horizontal|columns-vertical>",默认 columns-horizontal
columnsAsideLayout: 'columns-vertical', columnsAsideLayout: 'columns-vertical',
layout: 'classic',
/**
* 布局切换
* 注意:为了演示,切换布局时,颜色会被还原成默认,代码位置:/@/layout/navBars/breadcrumb/setings.vue
* 中的 `initSetLayoutChange(设置布局切换,重置主题样式)` 方法
*/
// 布局切换:可选值"<defaults|classic|transverse|columns>",默认 defaults
layout: 'defaults',
/**
* 后端控制路由
*/
// 是否开启后端控制路由
isRequestRoutes: true, isRequestRoutes: true,
globalTitle: '协众防重码',
/** globalViceTitle: '协众防重码',
* 全局网站标题 / 副标题 globalViceTitleMsg: '',
*/
// 网站主标题(菜单导航、浏览器当前网页标题)
globalTitle: 'DVAdmin',
// 网站副标题(登录页顶部文字)
globalViceTitle: 'DVAdmin',
// 网站副标题(登录页顶部文字)
globalViceTitleMsg: '企业级快速开发平台',
// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
globalI18n: 'zh-cn', globalI18n: 'zh-cn',
// 默认全局组件大小,可选值"<large|'default'|small>",默认 'large'
globalComponentSize: 'default', globalComponentSize: 'default',
}, },
}), }),

View File

@@ -14,6 +14,7 @@
*/ */
declare type MittType<T = any> = { declare type MittType<T = any> = {
openSetingsDrawer?: string; openSetingsDrawer?: string;
scanDataDoRefresh?: string;
restoreDefault?: string; restoreDefault?: string;
setSendColumnsChildren: T; setSendColumnsChildren: T;
setSendClassicChildren: T; setSendClassicChildren: T;

View File

@@ -94,28 +94,6 @@ export const commonCrudConfig = (options: CrudOptions = {}) => {
helper: '默认不填则为当前创建用户的部门ID', helper: '默认不填则为当前创建用户的部门ID',
}, },
}, },
description: {
title: '备注',
search: {
show: merged.description.search,
},
type: 'textarea',
column: {
width: merged.description.width,
show: merged.description.table,
},
form: {
show: merged.description.form,
component: {
placeholder: '请输入内容',
showWordLimit: true,
maxlength: '200',
},
},
viewForm: {
show: true,
},
},
modifier_name: { modifier_name: {
title: '修改人', title: '修改人',

View File

View File

@@ -0,0 +1,41 @@
import { request } from '/@/utils/service';
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
export const apiPrefix = '/api/code_info/scan_data/';
export function GetList(query: UserPageQuery) {
return request({
url: apiPrefix,
method: 'get',
params: query,
});
}
export function GetObj(id: InfoReq) {
return request({
url: apiPrefix + id,
method: 'get',
});
}
export function AddObj(obj: AddReq) {
return request({
url: apiPrefix,
method: 'post',
data: obj,
});
}
export function UpdateObj(obj: EditReq) {
return request({
url: apiPrefix + obj.id + '/',
method: 'put',
data: obj,
});
}
export function DelObj(id: DelReq) {
return request({
url: apiPrefix + id + '/',
method: 'delete',
data: { id },
});
}

View File

@@ -0,0 +1,195 @@
import * as api from './api';
import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict } from '@fast-crud/fast-crud';
import {commonCrudConfig} from "/@/utils/commonCrud";
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery) => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
actionbar: {
buttons: {
add: {
show: false,
},
},
},
rowHandle: {
fixed:'right',
width: 100,
buttons: {
view: {
type: 'text',
},
edit: {
show: false,
},
remove: {
show: false,
},
},
},
columns: {
_index: {
title: '序号',
form: { show: false },
column: {
//type: 'index',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
formatter: (context) => {
//计算序号,你可以自定义计算规则,此处为翻页累加
let index = context.index ?? 1;
let pagination = crudExpose!.crudBinding.value.pagination;
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
},
},
},
search: {
title: '关键词',
column: {
show: false,
},
search: {
show: true,
component: {
props: {
clearable: true,
},
placeholder: '请输入关键词',
},
},
form: {
show: false,
component: {
props: {
clearable: true,
},
},
},
},
product_code: {
title: '产品件号',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入产品件号',
},
},
},
supplier_code: {
title: '供应商代码',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入供应商代码',
},
},
},
production_batch: {
title: '生产批次',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入生产批次',
},
},
},
product_serial_number: {
title: '产品序列码',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入产品序列码',
},
},
},
version_number: {
title: '版本号',
search: {
show: true,
},
disabled: true,
type: 'input',
column:{
minWidth: 120,
},
form: {
component: {
placeholder: '请输入版本号',
},
},
},
shift: {
title: '班次',
type: 'input',
column:{
minWidth: 90,
},
search: {
show: true,
},
form: {
disabled: true,
component: {
placeholder: '请输入班次',
},
},
component: { props: { color: 'auto' } }, // 自动染色
},
...commonCrudConfig({
create_datetime: { form: false, table: true, search: false, width: 160 },
update_datetime: { form: false, table: true, search: false, width: 160 },
creator_name: { form: false, table: true, search: false, width: 100 },
})
},
},
};
};

View File

@@ -0,0 +1,30 @@
<template>
<fs-page>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script lang="ts" setup name="scanData">
import { onMounted,onUnmounted } from 'vue';
import { useFs } from '@fast-crud/fast-crud';
import { createCrudOptions } from './crud';
import mittBus from "/@/utils/mitt";
import { handleColumnPermission } from '/@/utils/columnPermission';
const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
// 页面打开后获取列表数据
onMounted(async () => {
// 刷新
mittBus.on('scanDataDoRefresh', () => {
crudExpose.doRefresh();
});
crudExpose.doRefresh();
});
// 页面销毁时,关闭监听布局配置/i18n监听
onUnmounted(() => {
mittBus.off('scanDataDoRefresh', () => {});
});
</script>

View File

@@ -0,0 +1,41 @@
import { request } from '/@/utils/service';
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
export const apiPrefix = '/api/code_info/scan_record/';
export function GetList(query: UserPageQuery) {
return request({
url: apiPrefix,
method: 'get',
params: query,
});
}
export function GetObj(id: InfoReq) {
return request({
url: apiPrefix + id,
method: 'get',
});
}
export function AddObj(obj: AddReq) {
return request({
url: apiPrefix,
method: 'post',
data: obj,
});
}
export function UpdateObj(obj: EditReq) {
return request({
url: apiPrefix + obj.id + '/',
method: 'put',
data: obj,
});
}
export function DelObj(id: DelReq) {
return request({
url: apiPrefix + id + '/',
method: 'delete',
data: { id },
});
}

View File

@@ -0,0 +1,229 @@
import * as api from './api';
import { UserPageQuery, AddReq, DelReq, EditReq, CreateCrudOptionsProps, CreateCrudOptionsRet, dict } from '@fast-crud/fast-crud';
import {commonCrudConfig} from "/@/utils/commonCrud";
import {dictionary} from "/@/utils/dictionary";
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery) => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
actionbar: {
buttons: {
add: {
show: false,
},
},
},
rowHandle: {
fixed:'right',
width: 100,
buttons: {
view: {
type: 'text',
},
edit: {
show: false,
},
remove: {
show: false,
},
},
},
columns: {
_index: {
title: '序号',
form: { show: false },
column: {
//type: 'index',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
formatter: (context) => {
//计算序号,你可以自定义计算规则,此处为翻页累加
let index = context.index ?? 1;
let pagination = crudExpose!.crudBinding.value.pagination;
return ((pagination!.currentPage ?? 1) - 1) * pagination!.pageSize + index + 1;
},
},
},
search: {
title: '关键词',
column: {
show: false,
},
search: {
show: true,
component: {
props: {
clearable: true,
},
placeholder: '请输入关键词',
},
},
form: {
show: false,
component: {
props: {
clearable: true,
},
},
},
},
code: {
title: '扫码数据',
search: {
show: true,
},
type: 'input',
column:{
width: 200,
},
form: {
disabled: true,
component: {
placeholder: '请输入产品件号',
},
},
},
product_code: {
title: '产品件号',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入产品件号',
},
},
},
supplier_code: {
title: '供应商代码',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入供应商代码',
},
},
},
production_batch: {
title: '生产批次',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入生产批次',
},
},
},
product_serial_number: {
title: '产品序列码',
search: {
show: true,
},
type: 'input',
column:{
minWidth: 120,
},
form: {
disabled: true,
component: {
placeholder: '请输入产品序列码',
},
},
},
version_number: {
title: '版本号',
search: {
show: true,
},
disabled: true,
type: 'input',
column:{
minWidth: 120,
},
form: {
component: {
placeholder: '请输入版本号',
},
},
},
shift: {
title: '班次',
type: 'input',
column:{
minWidth: 90,
},
search: {
show: true,
},
form: {
disabled: true,
component: {
placeholder: '请输入班次',
},
},
component: { props: { color: 'auto' } }, // 自动染色
},
status: {
title: '状态',
type: 'dict-select',
dict: dict({
data: [
{ label: '重复扫码', value: 0 },
{ label: '正常', value: 1 },
{ label: '未识别码', value: 2 },
]
}),
column: {
width: 120
},
search: {
show: true
}
},
...commonCrudConfig({
create_datetime: { form: false, table: true, search: false, width: 160 },
update_datetime: { form: false, table: true, search: false, width: 160 },
creator_name: { form: false, table: true, search: false, width: 100 },
})
},
},
};
};

View File

@@ -0,0 +1,20 @@
<template>
<fs-page>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page>
</template>
<script lang="ts" setup name="scanRecord">
import { onMounted } from 'vue';
import { useFs } from '@fast-crud/fast-crud';
import { createCrudOptions } from './crud';
import { handleColumnPermission } from '/@/utils/columnPermission';
const { crudBinding, crudRef, crudExpose, crudOptions, resetCrudOptions } = useFs({ createCrudOptions });
// 页面打开后获取列表数据
onMounted(async () => {
// 刷新
crudExpose.doRefresh();
});
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

File diff suppressed because one or more lines are too long

View File

@@ -204,6 +204,29 @@ export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProp
}, },
}, },
}, },
description: {
title: '班次',
search: {
show: true,
},
type: 'input',
column: {
minWidth: 100, //最小列宽
},
form: {
rules: [
// 表单校验规则
{
required: true,
message: '班次必填项',
},
],
component: {
span: 12,
placeholder: '请输入班次',
}
},
},
dept: { dept: {
title: '部门', title: '部门',
search: { search: {