mirror of
https://github.com/huge-dream/django-vue3-admin.git
synced 2026-09-21 20:55:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff7333d882 | ||
|
|
52de562fac | ||
|
|
9cb3570f5a | ||
|
|
75670f9a5e | ||
|
|
212af88409 | ||
|
|
1b71ba156b | ||
|
|
10b159aa15 | ||
|
|
b7b589176a | ||
|
|
bd8fef9a04 | ||
|
|
dd98b98775 | ||
|
|
8e107d9944 | ||
|
|
7159253c6b | ||
|
|
075d457dc7 | ||
|
|
564e9a112b | ||
|
|
3b84773a7d | ||
|
|
996d644d9b | ||
|
|
97f7bfd3f0 | ||
|
|
d1fdbd7e51 | ||
|
|
802ee7f50e | ||
|
|
ddf9fcd8e8 | ||
|
|
6f15c2f669 | ||
|
|
084d1c0c3e | ||
|
|
6ded198206 | ||
|
|
e549d33a48 | ||
|
|
cbba5a60f4 | ||
|
|
773b56a5e6 | ||
|
|
fb29929f55 | ||
|
|
0772a605bf | ||
|
|
496c453615 | ||
|
|
58b59c2ed0 | ||
|
|
94815f4b20 | ||
|
|
ad6ec5ca58 | ||
|
|
4bcaf8d45f | ||
|
|
be5db33a55 | ||
|
|
cfc5f8597a | ||
|
|
209e2ecced | ||
|
|
4b948fe5c3 | ||
|
|
ea335922b2 | ||
|
|
d290836f4f | ||
|
|
9a426dbd72 | ||
|
|
a0b6e57a3c | ||
|
|
5a89cdf889 | ||
|
|
b6b970f7a2 | ||
|
|
2722631276 | ||
|
|
478aea32f1 | ||
|
|
cf55897552 | ||
|
|
8020da03e2 | ||
|
|
c646563d26 | ||
|
|
43a4572a71 | ||
|
|
1582353279 | ||
|
|
1df2101658 | ||
|
|
0679198d0f | ||
|
|
e3c478a263 | ||
|
|
07cf81c9fe | ||
|
|
1b98fbc982 | ||
|
|
85040c808f | ||
|
|
00b2cac7a5 | ||
|
|
7c27f95353 | ||
|
|
d59df6db7c | ||
|
|
34c55cfd7e | ||
|
|
191e57dcd9 | ||
|
|
9be50863d9 | ||
|
|
9c218a856c | ||
|
|
b4ebb14843 | ||
|
|
17d80b0b00 | ||
|
|
01394c6904 | ||
|
|
59b1b54da1 | ||
|
|
8084ca7b0b | ||
|
|
427b6f8478 | ||
|
|
41a9888441 | ||
|
|
5139d37007 | ||
|
|
44bceb6e22 | ||
|
|
c2f8c43905 | ||
|
|
9f479e476d | ||
|
|
f5a7b157f9 | ||
|
|
7110d10432 | ||
|
|
05f4e2970d | ||
|
|
5353a27187 | ||
|
|
f4ff5d87af | ||
|
|
ceac44813f | ||
|
|
a490ced370 | ||
|
|
20188261c5 | ||
|
|
15ef01aed9 | ||
|
|
1dea6ed29c | ||
|
|
2bad36c982 | ||
|
|
941dbbc783 | ||
|
|
14881707e2 | ||
|
|
d60c795ea2 | ||
|
|
9cf5d13f1c | ||
|
|
e1618359d7 | ||
|
|
abe7972ab4 | ||
|
|
0709e72ae1 | ||
|
|
2e676d64bb | ||
|
|
98e974b55e | ||
|
|
e7f78fbae0 | ||
|
|
111edb3f4a | ||
|
|
86f539a193 | ||
|
|
3b9dc7c171 | ||
|
|
8175c620ed | ||
|
|
de5884d28d | ||
|
|
e63a7cab6f | ||
|
|
cdfdf0a483 | ||
|
|
515f720a59 | ||
|
|
5524446dbc | ||
|
|
9c32c7f60b | ||
|
|
6eed2814f8 | ||
|
|
f03e61ce63 | ||
|
|
5450102275 | ||
|
|
689ce4b43b | ||
|
|
65af240757 | ||
|
|
f61025c6db | ||
|
|
516f0ac675 | ||
|
|
b1b5ae5fcd | ||
|
|
9a847d078f | ||
|
|
dcac5c759a | ||
|
|
13999142aa | ||
|
|
cd6318c6fd | ||
|
|
3c58bcf467 | ||
|
|
b9bdf878de | ||
|
|
db989671c0 | ||
|
|
f67869d428 | ||
|
|
78c693718b | ||
|
|
653483e5de | ||
|
|
89ba768932 | ||
|
|
e4326b6d4e | ||
|
|
97020ba4e8 | ||
|
|
945db3344c | ||
|
|
19df23ce5f | ||
|
|
97c76b6afe | ||
|
|
6597826fdd | ||
|
|
ee419e282e | ||
|
|
3d12d7a5c5 | ||
|
|
b2aa22ce84 | ||
|
|
06d7d718e1 | ||
|
|
6067dfac2f | ||
|
|
637e45ef83 | ||
|
|
202f67460e | ||
|
|
7da23ab6a6 | ||
|
|
be51124bb6 | ||
|
|
7e7637f10b | ||
|
|
dbb94fb9ff | ||
|
|
fea61e35ef | ||
|
|
8ecfc1143b | ||
|
|
7c72cc74df | ||
|
|
12f5125207 | ||
|
|
770c5f73c0 | ||
|
|
98b81a789d | ||
|
|
c13a5f0dda | ||
|
|
a524137e18 | ||
|
|
2c9d8766e3 | ||
|
|
8a0f18eab1 | ||
|
|
d8511eeb7b | ||
|
|
52a91f1703 | ||
|
|
14395621c0 | ||
|
|
c4e2451ac8 | ||
|
|
936928e854 | ||
|
|
619ebeacf1 | ||
|
|
b95679a986 | ||
|
|
8e930c6a10 | ||
|
|
d2811a1f87 | ||
|
|
7ac5c86ed3 | ||
|
|
eea5cdadfe | ||
|
|
4e5f9ce946 | ||
|
|
e790882ab6 | ||
|
|
0f86f5c6fc | ||
|
|
4018113161 | ||
|
|
252b6a8328 | ||
|
|
c26abef364 | ||
|
|
a40dc9afb1 | ||
|
|
9114c6bf81 | ||
|
|
7b0a6bc2be | ||
|
|
e4ca462153 | ||
|
|
840323ff07 | ||
|
|
ed0dca7346 | ||
|
|
2b63966ee2 | ||
|
|
55a2f73420 | ||
|
|
7ccbcebe77 | ||
|
|
8f57a240db | ||
|
|
1ccfd619f3 | ||
|
|
dcdc7bbe3b | ||
|
|
90557ea039 | ||
|
|
a462e15643 | ||
|
|
6a24683769 | ||
|
|
9fa1775b77 | ||
|
|
16605779ed | ||
|
|
186658858d | ||
|
|
996cb1447d | ||
|
|
e8b84ad71f | ||
|
|
e7065ab89e | ||
|
|
e962ca6559 | ||
|
|
4bdc256311 | ||
|
|
f7b94e496c | ||
|
|
8c0a04ce61 | ||
|
|
31f20a766f | ||
|
|
65a34a683c | ||
|
|
66effde1d9 | ||
|
|
f4f958d2e9 | ||
|
|
d10127eafc | ||
|
|
60035767ad | ||
|
|
adc522c180 | ||
|
|
17b67e43c9 | ||
|
|
3cccfaa7f4 | ||
|
|
ce4d410e53 | ||
|
|
7b0341b527 | ||
|
|
31f7175186 | ||
|
|
2c22ecbac5 | ||
|
|
ad59bc68aa | ||
|
|
8d7d775164 | ||
|
|
9c2dea9db5 | ||
|
|
8c31bda3da | ||
|
|
7f7d88fe5b | ||
|
|
19e18a7b6f | ||
|
|
cd6cd775e7 | ||
|
|
111ca9554c | ||
|
|
c9fddbd4a7 | ||
|
|
d3b057f75a | ||
|
|
096f5919af | ||
|
|
25cc2c83c1 | ||
|
|
054bcf3eea | ||
|
|
9c24e61123 | ||
|
|
cc36ff3d8f | ||
|
|
93e74dbaa2 | ||
|
|
5ab3ad4ed9 | ||
|
|
9468abd4a2 | ||
|
|
1080af6413 | ||
|
|
967e4e76c2 | ||
|
|
f7b93c349e | ||
|
|
72f1d2eae7 | ||
|
|
592bc947a0 | ||
|
|
953fcc3437 | ||
|
|
13aa6dbb99 | ||
|
|
460ae171dd | ||
|
|
0d556bfb2b | ||
|
|
61ae6e8eb3 | ||
|
|
175b151f7f | ||
|
|
a50f73d466 | ||
|
|
eea9d320af | ||
|
|
15c4808bbb | ||
|
|
d2d6ba3460 | ||
|
|
4f0295acb1 | ||
|
|
255c405e59 | ||
|
|
96ad956efd | ||
|
|
b586b46016 | ||
|
|
875146e588 | ||
|
|
1ee709b9eb | ||
|
|
f89a5228cd | ||
|
|
795f621637 | ||
|
|
c5d7a70f46 | ||
|
|
8710b047b1 | ||
|
|
300b6c6bb8 | ||
|
|
296640cb2a | ||
|
|
24861fda42 | ||
|
|
0e3fac37e9 | ||
|
|
93d8d94049 | ||
|
|
9d4f007d48 | ||
|
|
4d17a7d9df | ||
|
|
c4c9a81ac8 | ||
|
|
d1223dddd3 | ||
|
|
3dd68e5d21 | ||
|
|
0668cb087f | ||
|
|
38494edea5 | ||
|
|
2f04f22904 | ||
|
|
0ccf2e3725 | ||
|
|
68cd42246d | ||
|
|
dc6ddda4ab | ||
|
|
08b56ee663 | ||
|
|
82d7193dcb | ||
|
|
4b609ed7e4 | ||
|
|
61066b8caa | ||
|
|
160c376f81 | ||
|
|
b96c220f1d | ||
|
|
42884685ff | ||
|
|
c126c704a4 | ||
|
|
662c314518 | ||
|
|
1715fcb4d8 | ||
|
|
58e611cb22 | ||
|
|
0b2fa1e92c | ||
|
|
125dfd4dd5 | ||
|
|
af7bfcd215 | ||
|
|
6917786864 | ||
|
|
cd60b3d9be | ||
|
|
cfae363f53 | ||
|
|
4f8686b35e | ||
|
|
1407a25b6a | ||
|
|
7c78c7e53f | ||
|
|
f1da0507dd | ||
|
|
df74c792f6 | ||
|
|
4c697ef9d4 | ||
|
|
ab1d1c87e2 | ||
|
|
b6b3b4a39a | ||
|
|
581e4cd92f | ||
|
|
fb76d4271b | ||
|
|
a304a49107 | ||
|
|
fc1ab98b2b | ||
|
|
4f176cf88a | ||
|
|
af5f3b7137 | ||
|
|
b03037b866 | ||
|
|
92f57f8608 | ||
|
|
5ab2aaa066 | ||
|
|
b5f50bdf30 | ||
|
|
256c6e4ab9 |
+2
-1
@@ -3,4 +3,5 @@
|
|||||||
.idea
|
.idea
|
||||||
|
|
||||||
.history/
|
.history/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
web/package-lock.json
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/3.2/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/liqianglog/django-vue-admin)
|
[](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/3.2/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/liqianglog/django-vue-admin)
|
||||||
|
|
||||||
[preview](https://demo.django-vue-admin.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)
|
[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」**
|
💡 **「About」**
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/3.2/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/liqianglog/django-vue-admin)
|
[](https://gitee.com/liqianglog/django-vue-admin/blob/master/LICENSE) [](https://python.org/) [](https://docs.djangoproject.com/zh-hans/3.2/) [](https://nodejs.org/zh-cn/) [](https://gitee.com/liqianglog/django-vue-admin)
|
||||||
|
|
||||||
[预 览](https://demo.django-vue-admin.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)
|
[预 览](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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -91,9 +91,10 @@ ENV/
|
|||||||
**/migrations/*.py
|
**/migrations/*.py
|
||||||
!**/migrations/__init__.py
|
!**/migrations/__init__.py
|
||||||
*.pyc
|
*.pyc
|
||||||
conf/
|
conf/*
|
||||||
!conf/env.example.py
|
!conf/env.example.py
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
media/
|
media/
|
||||||
__pypackages__/
|
__pypackages__/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
gunicorn.pid
|
||||||
|
|||||||
@@ -3,6 +3,5 @@ from django.urls import path
|
|||||||
from application.websocketConfig import MegCenter
|
from application.websocketConfig import MegCenter
|
||||||
|
|
||||||
websocket_urlpatterns = [
|
websocket_urlpatterns = [
|
||||||
path('ws/<str:service_uid>/', MegCenter.as_asgi()), #consumers.DvadminWebSocket 是该路由的消费者
|
path('ws/<str:service_uid>/', MegCenter.as_asgi()), # consumers.DvadminWebSocket 是该路由的消费者
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ https://docs.djangoproject.com/en/4.1/ref/settings/
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
@@ -43,6 +44,9 @@ DEBUG = locals().get("DEBUG", True)
|
|||||||
ALLOWED_HOSTS = locals().get("ALLOWED_HOSTS", ["*"])
|
ALLOWED_HOSTS = locals().get("ALLOWED_HOSTS", ["*"])
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
CUSTOM_APPS = [
|
||||||
|
"dvadmin.system",
|
||||||
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
@@ -54,10 +58,10 @@ INSTALLED_APPS = [
|
|||||||
"rest_framework",
|
"rest_framework",
|
||||||
"django_filters",
|
"django_filters",
|
||||||
"corsheaders", # 注册跨域app
|
"corsheaders", # 注册跨域app
|
||||||
"dvadmin.system",
|
|
||||||
"drf_yasg",
|
"drf_yasg",
|
||||||
"captcha",
|
"captcha",
|
||||||
'channels',
|
'channels',
|
||||||
|
*locals().get("CUSTOM_APPS", []), # 所有项目里写的app需要在env.py文件里的CUSTOM_APPS中
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -165,15 +169,16 @@ CORS_ORIGIN_ALLOW_ALL = True
|
|||||||
# 允许cookie
|
# 允许cookie
|
||||||
CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持对cookie的操作
|
CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持对cookie的操作
|
||||||
|
|
||||||
# ================================================= #
|
# ===================================================== #
|
||||||
# ********************* channels配置 ******************* #
|
# ********************* channels配置 ******************* #
|
||||||
# ================================================= #
|
# ===================================================== #
|
||||||
ASGI_APPLICATION = 'application.asgi.application'
|
ASGI_APPLICATION = 'application.asgi.application'
|
||||||
CHANNEL_LAYERS = {
|
CHANNEL_LAYERS = {
|
||||||
"default": {
|
"default": {
|
||||||
"BACKEND": "channels.layers.InMemoryChannelLayer"
|
"BACKEND": "channels.layers.InMemoryChannelLayer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
REDIS_URL = locals().get('REDIS_URL', "")
|
||||||
# CHANNEL_LAYERS = {
|
# CHANNEL_LAYERS = {
|
||||||
# 'default': {
|
# 'default': {
|
||||||
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
# 'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||||||
@@ -187,69 +192,83 @@ CHANNEL_LAYERS = {
|
|||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ********************* 日志配置 ******************* #
|
# ********************* 日志配置 ******************* #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
|
# # log 配置部分BEGIN #
|
||||||
# log 配置部分BEGIN #
|
SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
|
||||||
|
ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
|
||||||
LOGS_FILE = os.path.join(BASE_DIR, "logs")
|
LOGS_FILE = os.path.join(BASE_DIR, "logs")
|
||||||
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
|
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
|
||||||
os.makedirs(os.path.join(BASE_DIR, "logs"))
|
os.makedirs(os.path.join(BASE_DIR, "logs"))
|
||||||
|
|
||||||
|
# 格式:[2020-04-22 23:33:01][micoservice.apps.ready():16] [INFO] 这是一条日志:
|
||||||
|
# 格式:[日期][模块.函数名称():行号] [级别] 信息
|
||||||
|
STANDARD_LOG_FORMAT = (
|
||||||
|
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||||
|
)
|
||||||
|
CONSOLE_LOG_FORMAT = (
|
||||||
|
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||||
|
)
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"disable_existing_loggers": False,
|
"disable_existing_loggers": False,
|
||||||
"formatters": {
|
"formatters": {
|
||||||
"servers": {
|
"standard": {"format": STANDARD_LOG_FORMAT},
|
||||||
"format": "%(message)s",
|
"console": {
|
||||||
|
"format": CONSOLE_LOG_FORMAT,
|
||||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||||
}
|
},
|
||||||
|
"file": {
|
||||||
|
"format": CONSOLE_LOG_FORMAT,
|
||||||
|
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"handlers": {
|
"handlers": {
|
||||||
"servers": {
|
"file": {
|
||||||
"logging_levels": ["info", "error", "warning"],
|
"level": "INFO",
|
||||||
"class": "dvadmin.utils.log.InterceptTimedRotatingFileHandler", # 这个路径看你本地放在哪里(下面的log文件)
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
"filename": os.path.join(LOGS_FILE, "server.log"),
|
"filename": SERVER_LOGS_FILE,
|
||||||
"when": "D",
|
|
||||||
"interval": 1,
|
|
||||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||||
"backupCount": 1,
|
"backupCount": 5, # 最多备份5个
|
||||||
"formatter": "servers",
|
"formatter": "standard",
|
||||||
"encoding": "utf-8",
|
"encoding": "utf-8",
|
||||||
}
|
},
|
||||||
|
"error": {
|
||||||
|
"level": "ERROR",
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"filename": ERROR_LOGS_FILE,
|
||||||
|
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||||
|
"backupCount": 3, # 最多备份3个
|
||||||
|
"formatter": "standard",
|
||||||
|
"encoding": "utf-8",
|
||||||
|
},
|
||||||
|
"console": {
|
||||||
|
"level": "INFO",
|
||||||
|
"class": "logging.StreamHandler",
|
||||||
|
"formatter": "console",
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
# default日志
|
"": {
|
||||||
'django': {
|
"handlers": ["console", "error", "file"],
|
||||||
'handlers': ['servers'],
|
"level": "INFO",
|
||||||
'propagate': False,
|
|
||||||
'level': "INFO"
|
|
||||||
},
|
},
|
||||||
'': {
|
"django": {
|
||||||
'handlers': ['servers'],
|
"handlers": ["console", "error", "file"],
|
||||||
'propagate': False,
|
"level": "INFO",
|
||||||
'level': "ERROR"
|
"propagate": False,
|
||||||
},
|
|
||||||
'celery': {
|
|
||||||
'handlers': ['servers'],
|
|
||||||
'propagate': False,
|
|
||||||
'level': "INFO"
|
|
||||||
},
|
},
|
||||||
'django.db.backends': {
|
'django.db.backends': {
|
||||||
'handlers': ['servers'],
|
'handlers': ["console", "error", "file"],
|
||||||
'propagate': False,
|
'propagate': False,
|
||||||
'level': "INFO"
|
'level': "INFO"
|
||||||
},
|
},
|
||||||
'django.request': {
|
|
||||||
'handlers': ['servers'],
|
|
||||||
'propagate': False,
|
|
||||||
'level': "DEBUG"
|
|
||||||
},
|
|
||||||
"uvicorn.error": {
|
"uvicorn.error": {
|
||||||
"level": "INFO",
|
"level": "INFO",
|
||||||
"handlers": ["servers"],
|
"handlers": ["console", "error", "file"],
|
||||||
},
|
},
|
||||||
"uvicorn.access": {
|
"uvicorn.access": {
|
||||||
"handlers": ["servers"],
|
"handlers": ["console", "error", "file"],
|
||||||
"level": "INFO",
|
"level": "INFO"
|
||||||
"propagate": False
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -261,6 +280,7 @@ LOGGING = {
|
|||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_PARSER_CLASSES': (
|
'DEFAULT_PARSER_CLASSES': (
|
||||||
'rest_framework.parsers.JSONParser',
|
'rest_framework.parsers.JSONParser',
|
||||||
|
'rest_framework.parsers.MultiPartParser',
|
||||||
),
|
),
|
||||||
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
|
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
|
||||||
"DATE_FORMAT": "%Y-%m-%d",
|
"DATE_FORMAT": "%Y-%m-%d",
|
||||||
@@ -288,11 +308,9 @@ AUTHENTICATION_BACKENDS = ["dvadmin.utils.backends.CustomBackend"]
|
|||||||
# ================================================= #
|
# ================================================= #
|
||||||
# ****************** simplejwt配置 ***************** #
|
# ****************** simplejwt配置 ***************** #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
SIMPLE_JWT = {
|
SIMPLE_JWT = {
|
||||||
# token有效时长
|
# token有效时长
|
||||||
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=120),
|
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=1440),
|
||||||
# token刷新后的有效时间
|
# token刷新后的有效时间
|
||||||
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
|
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
|
||||||
# 设置前缀
|
# 设置前缀
|
||||||
@@ -328,11 +346,11 @@ SWAGGER_SETTINGS = {
|
|||||||
# ================================================= #
|
# ================================================= #
|
||||||
# **************** 验证码配置 ******************* #
|
# **************** 验证码配置 ******************* #
|
||||||
# ================================================= #
|
# ================================================= #
|
||||||
CAPTCHA_IMAGE_SIZE = (160, 60) # 设置 captcha 图片大小
|
CAPTCHA_IMAGE_SIZE = (160, 46) # 设置 captcha 图片大小
|
||||||
CAPTCHA_LENGTH = 4 # 字符个数
|
CAPTCHA_LENGTH = 4 # 字符个数
|
||||||
CAPTCHA_TIMEOUT = 1 # 超时(minutes)
|
CAPTCHA_TIMEOUT = 1 # 超时(minutes)
|
||||||
CAPTCHA_OUTPUT_FORMAT = "%(image)s %(text_field)s %(hidden_field)s "
|
CAPTCHA_OUTPUT_FORMAT = "%(image)s %(text_field)s %(hidden_field)s "
|
||||||
CAPTCHA_FONT_SIZE = 40 # 字体大小
|
CAPTCHA_FONT_SIZE = 36 # 字体大小
|
||||||
CAPTCHA_FOREGROUND_COLOR = "#64DAAA" # 前景色
|
CAPTCHA_FOREGROUND_COLOR = "#64DAAA" # 前景色
|
||||||
CAPTCHA_BACKGROUND_COLOR = "#F5F7F4" # 背景色
|
CAPTCHA_BACKGROUND_COLOR = "#F5F7F4" # 背景色
|
||||||
CAPTCHA_NOISE_FUNCTIONS = (
|
CAPTCHA_NOISE_FUNCTIONS = (
|
||||||
@@ -382,10 +400,11 @@ TENANT_SHARED_APPS = []
|
|||||||
PLUGINS_URL_PATTERNS = []
|
PLUGINS_URL_PATTERNS = []
|
||||||
# ********** 一键导入插件配置开始 **********
|
# ********** 一键导入插件配置开始 **********
|
||||||
# 例如:
|
# 例如:
|
||||||
# from dvadmin_upgrade_center.settings import * # 升级中心
|
#from dvadmin3_upgrade_center.settings import * # 升级中心
|
||||||
# from dvadmin_celery.settings import * # celery 异步任务
|
# from dvadmin_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 * # 租户管理
|
||||||
|
# from dvadmin_uniapp.settings import *
|
||||||
# ...
|
# ...
|
||||||
# ********** 一键导入插件配置结束 **********
|
# ********** 一键导入插件配置结束 **********
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from dvadmin.system.views.login import (
|
|||||||
CaptchaView,
|
CaptchaView,
|
||||||
ApiLogin,
|
ApiLogin,
|
||||||
LogoutView,
|
LogoutView,
|
||||||
|
LoginTokenView
|
||||||
)
|
)
|
||||||
from dvadmin.system.views.system_config import InitSettingsViewSet
|
from dvadmin.system.views.system_config import InitSettingsViewSet
|
||||||
from dvadmin.utils.swagger import CustomOpenAPISchemaGenerator
|
from dvadmin.utils.swagger import CustomOpenAPISchemaGenerator
|
||||||
@@ -81,6 +82,9 @@ urlpatterns = (
|
|||||||
path("api/init/dictionary/", InitDictionaryViewSet.as_view()),
|
path("api/init/dictionary/", InitDictionaryViewSet.as_view()),
|
||||||
path("api/init/settings/", InitSettingsViewSet.as_view()),
|
path("api/init/settings/", InitSettingsViewSet.as_view()),
|
||||||
path("apiLogin/", ApiLogin.as_view()),
|
path("apiLogin/", ApiLogin.as_view()),
|
||||||
|
|
||||||
|
# 仅用于开发,上线需关闭
|
||||||
|
path("api/token/", LoginTokenView.as_view()),
|
||||||
]
|
]
|
||||||
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
+ static(settings.STATIC_URL, document_root=settings.STATIC_URL)
|
+ static(settings.STATIC_URL, document_root=settings.STATIC_URL)
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ class MessageCreateSerializer(CustomModelSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
def websocket_push(user_id,message):
|
|
||||||
|
def websocket_push(user_id, message):
|
||||||
username = "user_" + str(user_id)
|
username = "user_" + str(user_id)
|
||||||
channel_layer = get_channel_layer()
|
channel_layer = get_channel_layer()
|
||||||
async_to_sync(channel_layer.group_send)(
|
async_to_sync(channel_layer.group_send)(
|
||||||
@@ -133,8 +134,9 @@ def websocket_push(user_id,message):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_message_push(title: str, content: str, target_type: int=0, target_user: list=[], target_dept=None, target_role=None,
|
|
||||||
message: dict = {'contentType': 'INFO', 'content': '测试~'}, request= Request):
|
def create_message_push(title: str, content: str, target_type: int = 0, target_user: list = None, target_dept=None,
|
||||||
|
target_role=None, message: dict = None, request=Request):
|
||||||
if message is None:
|
if message is None:
|
||||||
message = {"contentType": "INFO", "content": None}
|
message = {"contentType": "INFO", "content": None}
|
||||||
if target_role is None:
|
if target_role is None:
|
||||||
@@ -145,11 +147,11 @@ def create_message_push(title: str, content: str, target_type: int=0, target_use
|
|||||||
"title": title,
|
"title": title,
|
||||||
"content": content,
|
"content": content,
|
||||||
"target_type": target_type,
|
"target_type": target_type,
|
||||||
"target_user":target_user,
|
"target_user": target_user,
|
||||||
"target_dept":target_dept,
|
"target_dept": target_dept,
|
||||||
"target_role":target_role
|
"target_role": target_role
|
||||||
}
|
}
|
||||||
message_center_instance = MessageCreateSerializer(data=data,request=request)
|
message_center_instance = MessageCreateSerializer(data=data, request=request)
|
||||||
message_center_instance.is_valid(raise_exception=True)
|
message_center_instance.is_valid(raise_exception=True)
|
||||||
message_center_instance.save()
|
message_center_instance.save()
|
||||||
users = target_user or []
|
users = target_user or []
|
||||||
@@ -176,6 +178,6 @@ def create_message_push(title: str, content: str, target_type: int=0, target_use
|
|||||||
username,
|
username,
|
||||||
{
|
{
|
||||||
"type": "push.message",
|
"type": "push.message",
|
||||||
"json": {**message,'unread':unread_count}
|
"json": {**message, 'unread': unread_count}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,6 +44,6 @@ LOGIN_NO_CAPTCHA_AUTH = True
|
|||||||
# ================================================= #
|
# ================================================= #
|
||||||
|
|
||||||
ALLOWED_HOSTS = ["*"]
|
ALLOWED_HOSTS = ["*"]
|
||||||
|
CUSTOM_APPS = [
|
||||||
# daphne启动命令
|
"dvadmin.system",
|
||||||
#daphne application.asgi:application -b 0.0.0.0 -p 8000
|
]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import os
|
|||||||
|
|
||||||
exclude = ["venv"] # 需要排除的文件目录
|
exclude = ["venv"] # 需要排除的文件目录
|
||||||
for root, dirs, files in os.walk('.'):
|
for root, dirs, files in os.walk('.'):
|
||||||
dirs[:] = [d for d in set(dirs) - set(exclude)]
|
dirs[:] = list(set(dirs) - set(exclude))
|
||||||
if 'migrations' in dirs:
|
if 'migrations' in dirs:
|
||||||
dir = dirs[dirs.index('migrations')]
|
dir = dirs[dirs.index('migrations')]
|
||||||
for root_j, dirs_j, files_j in os.walk(os.path.join(root, dir)):
|
for root_j, dirs_j, files_j in os.walk(os.path.join(root, dir)):
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
# python manage.py makemigrations
|
# python manage.py makemigrations
|
||||||
# python manage.py migrate
|
# python manage.py migrate
|
||||||
# python manage.py init -y
|
# python manage.py init -y
|
||||||
gunicorn -c gunicorn_conf.py application.asgi:application
|
uvicorn application.asgi:application --port 8000 --host 0.0.0.0 --workers 4
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ from rest_framework import serializers
|
|||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'application.settings')
|
||||||
import django
|
import django
|
||||||
django.setup()
|
django.setup()
|
||||||
from dvadmin.system.models import Role, Dept, Users, Menu, MenuButton, ApiWhiteList, Dictionary, SystemConfig, \
|
from dvadmin.system.models import (
|
||||||
RoleMenuPermission, RoleMenuButtonPermission
|
Role, Dept, Users, Menu, MenuButton,
|
||||||
|
ApiWhiteList, Dictionary, SystemConfig,
|
||||||
|
RoleMenuPermission, RoleApiPermission, Columns
|
||||||
|
)
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
|
|
||||||
|
|
||||||
@@ -39,25 +42,13 @@ class UsersInitSerializer(CustomModelSerializer):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class MenuButtonInitSerializer(CustomModelSerializer):
|
|
||||||
"""
|
|
||||||
初始化菜单按钮-序列化器
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = MenuButton
|
|
||||||
fields = ['id', 'name', 'value', 'api', 'method', 'menu']
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MenuInitSerializer(CustomModelSerializer):
|
class MenuInitSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
递归深度获取数信息(用于生成初始化json文件)
|
递归深度获取数信息(用于生成初始化json文件)
|
||||||
"""
|
"""
|
||||||
name = serializers.CharField(required=False)
|
name = serializers.CharField(required=True)
|
||||||
|
menu_type = serializers.IntegerField(required=True)
|
||||||
children = serializers.SerializerMethodField()
|
children = serializers.SerializerMethodField()
|
||||||
menu_button = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
def get_children(self, obj: Menu):
|
def get_children(self, obj: Menu):
|
||||||
data = []
|
data = []
|
||||||
@@ -67,26 +58,18 @@ class MenuInitSerializer(CustomModelSerializer):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_menu_button(self, obj: Menu):
|
|
||||||
data = []
|
|
||||||
instance = obj.menuPermission.order_by('method')
|
|
||||||
if instance:
|
|
||||||
data = list(instance.values('name', 'value', 'api', 'method'))
|
|
||||||
return data
|
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
instance = super().save(**kwargs)
|
instance = super().save(**kwargs)
|
||||||
children = self.initial_data.get('children')
|
children = self.initial_data.get('children')
|
||||||
menu_button = self.initial_data.get('menu_button')
|
|
||||||
# 菜单表
|
# 菜单表
|
||||||
if children:
|
if children:
|
||||||
for menu_data in children:
|
for menu_data in children:
|
||||||
menu_data['parent'] = instance.id
|
menu_data['parent'] = instance.id
|
||||||
filter_data = {
|
filter_data = {
|
||||||
"name": menu_data['name'],
|
"name": menu_data['name'],
|
||||||
"web_path": menu_data['web_path'],
|
|
||||||
"component": menu_data['component'],
|
"component": menu_data['component'],
|
||||||
"component_name": menu_data['component_name'],
|
"menu_type": menu_data['menu_type'],
|
||||||
}
|
}
|
||||||
instance_obj = Menu.objects.filter(**filter_data).first()
|
instance_obj = Menu.objects.filter(**filter_data).first()
|
||||||
if instance_obj and not self.initial_data.get('reset'):
|
if instance_obj and not self.initial_data.get('reset'):
|
||||||
@@ -94,24 +77,12 @@ class MenuInitSerializer(CustomModelSerializer):
|
|||||||
serializer = MenuInitSerializer(instance_obj, data=menu_data, request=self.request)
|
serializer = MenuInitSerializer(instance_obj, data=menu_data, request=self.request)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
serializer.save()
|
serializer.save()
|
||||||
# 菜单按钮
|
|
||||||
if menu_button:
|
|
||||||
for menu_button_data in menu_button:
|
|
||||||
menu_button_data['menu'] = instance.id
|
|
||||||
filter_data = {
|
|
||||||
"menu": menu_button_data['menu'],
|
|
||||||
"value": menu_button_data['value']
|
|
||||||
}
|
|
||||||
instance_obj = MenuButton.objects.filter(**filter_data).first()
|
|
||||||
serializer = MenuButtonInitSerializer(instance_obj, data=menu_button_data, request=self.request)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
serializer.save()
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ['name', 'icon', 'sort', 'is_link', 'is_catalog', 'web_path', 'component', 'component_name', 'status',
|
fields = ['name', 'icon', 'sort', 'is_link', 'menu_type', 'web_path', 'component', 'component_name', 'status',
|
||||||
'cache', 'visible', 'parent', 'children', 'menu_button', 'creator', 'dept_belong_id']
|
'cache', 'visible', 'parent', 'children', 'creator', 'dept_belong_id']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'creator': {'write_only': True},
|
'creator': {'write_only': True},
|
||||||
'dept_belong_id': {'write_only': True}
|
'dept_belong_id': {'write_only': True}
|
||||||
@@ -126,7 +97,7 @@ class RoleInitSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Role
|
model = Role
|
||||||
fields = ['name', 'key', 'sort', 'status', 'admin',
|
fields = ['name', 'key', 'sort', 'status',
|
||||||
'creator', 'dept_belong_id']
|
'creator', 'dept_belong_id']
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
@@ -139,8 +110,8 @@ class RoleMenuInitSerializer(CustomModelSerializer):
|
|||||||
"""
|
"""
|
||||||
初始化角色菜单(用于生成初始化json文件)
|
初始化角色菜单(用于生成初始化json文件)
|
||||||
"""
|
"""
|
||||||
role_key = serializers.CharField(max_length=100,required=True)
|
role_key = serializers.CharField(max_length=100, required=True)
|
||||||
menu_component_name = serializers.CharField(max_length=100,required=True)
|
menu_component_name = serializers.CharField(max_length=100, required=True)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
init_data = self.initial_data
|
init_data = self.initial_data
|
||||||
@@ -154,7 +125,7 @@ class RoleMenuInitSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RoleMenuPermission
|
model = RoleMenuPermission
|
||||||
fields = ['role_key','menu_component_name','creator', 'dept_belong_id']
|
fields = ['role_key', 'menu_component_name', 'creator', 'dept_belong_id']
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'role': {'required': False},
|
'role': {'required': False},
|
||||||
@@ -164,38 +135,62 @@ class RoleMenuInitSerializer(CustomModelSerializer):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RoleMenuButtonInitSerializer(CustomModelSerializer):
|
class RoleApiPermissionInitSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
初始化角色菜单按钮(用于生成初始化json文件)
|
初始化角色接口权限(用于生成初始化json文件)
|
||||||
"""
|
"""
|
||||||
role_key = serializers.CharField(max_length=100,required=True)
|
role_key = serializers.CharField(max_length=100, required=True)
|
||||||
menu_button_value = serializers.CharField(max_length=100,required=True)
|
name = serializers.CharField(max_length=255, required=True)
|
||||||
|
api = serializers.CharField(max_length=255, required=True)
|
||||||
|
method = serializers.IntegerField(required=True)
|
||||||
data_range = serializers.CharField(max_length=100, required=False)
|
data_range = serializers.CharField(max_length=100, required=False)
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
init_data = self.initial_data
|
init_data = self.initial_data
|
||||||
validated_data.pop('menu_button_value')
|
|
||||||
validated_data.pop('role_key')
|
validated_data.pop('role_key')
|
||||||
role_id = Role.objects.filter(key=init_data['role_key']).first()
|
role_id = Role.objects.filter(key=init_data['role_key']).first()
|
||||||
menu_button_id = MenuButton.objects.filter(value=init_data['menu_button_value']).first()
|
|
||||||
validated_data['role'] = role_id
|
validated_data['role'] = role_id
|
||||||
validated_data['menu_button'] = menu_button_id
|
|
||||||
instance = super().create(validated_data)
|
instance = super().create(validated_data)
|
||||||
instance.dept.set([])
|
instance.dept.set([])
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RoleMenuButtonPermission
|
model = RoleApiPermission
|
||||||
fields = ['role_key','menu_button_value','data_range','dept','creator', 'dept_belong_id']
|
fields = ['role_key', 'name','api','method', 'data_range', 'dept', 'creator', 'dept_belong_id']
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'role': {'required': False},
|
'role': {'required': False},
|
||||||
'menu': {'required': False},
|
|
||||||
'creator': {'write_only': True},
|
'creator': {'write_only': True},
|
||||||
'dept_belong_id': {'write_only': True}
|
'dept_belong_id': {'write_only': True}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RoleColumnInitSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
初始化角色字段权限(用于生成初始化json文件)
|
||||||
|
"""
|
||||||
|
role_key = serializers.CharField(max_length=100, required=True)
|
||||||
|
app = serializers.CharField(max_length=255, required=True)
|
||||||
|
model = serializers.CharField(max_length=255, required=True)
|
||||||
|
field_name = serializers.CharField(max_length=255, required=True)
|
||||||
|
title = serializers.CharField(max_length=255, required=True)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
init_data = self.initial_data
|
||||||
|
validated_data.pop('role_key')
|
||||||
|
role_id = Role.objects.filter(key=init_data['role_key']).first()
|
||||||
|
validated_data['role'] = role_id
|
||||||
|
instance = super().create(validated_data)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Columns
|
||||||
|
fields = ['role_key', 'app','model','field_name', 'title']
|
||||||
|
read_only_fields = ["id"]
|
||||||
|
extra_kwargs = {
|
||||||
|
'role': {'required': False},
|
||||||
|
'creator': {'write_only': True},
|
||||||
|
'dept_belong_id': {'write_only': True}
|
||||||
|
}
|
||||||
|
|
||||||
class ApiWhiteListInitSerializer(CustomModelSerializer):
|
class ApiWhiteListInitSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "id",
|
||||||
|
"title": "Id",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "description",
|
||||||
|
"title": "描述",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "creator",
|
||||||
|
"title": "创建人",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "modifier",
|
||||||
|
"title": "修改人",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "dept_belong_id",
|
||||||
|
"title": "数据归属部门",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "update_datetime",
|
||||||
|
"title": "修改时间",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "create_datetime",
|
||||||
|
"title": "创建时间",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "name",
|
||||||
|
"title": "角色名称",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "key",
|
||||||
|
"title": "权限字符",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "sort",
|
||||||
|
"title": "角色顺序",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dept_belong_id": "1",
|
||||||
|
"app": "dvadmin.system",
|
||||||
|
"model": "Role",
|
||||||
|
"field_name": "status",
|
||||||
|
"title": "角色状态",
|
||||||
|
"is_query": 1,
|
||||||
|
"is_create": 1,
|
||||||
|
"is_update": 1,
|
||||||
|
"role_key": "admin"
|
||||||
|
}
|
||||||
|
]
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@
|
|||||||
"key": "admin",
|
"key": "admin",
|
||||||
"sort": 1,
|
"sort": 1,
|
||||||
"status": true,
|
"status": true,
|
||||||
"admin": true,
|
|
||||||
"remark": null
|
"remark": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -12,7 +11,6 @@
|
|||||||
"key": "public",
|
"key": "public",
|
||||||
"sort": 2,
|
"sort": 2,
|
||||||
"status": true,
|
"status": true,
|
||||||
"admin": true,
|
|
||||||
"remark": null
|
"remark": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"role_key": "admin",
|
||||||
|
"name": "菜单列表查询",
|
||||||
|
"api": "/api/system/menu/",
|
||||||
|
"method": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role_key": "public",
|
||||||
|
"name": "菜单列表查询",
|
||||||
|
"api": "/api/system/menu/",
|
||||||
|
"method": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"role_key": "admin",
|
|
||||||
"menu_button_value": "menu:Search",
|
|
||||||
"data_range": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role_key": "public",
|
|
||||||
"menu_button_value":"menu:Search",
|
|
||||||
"data_range": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
"gender": 1,
|
"gender": 1,
|
||||||
"user_type": 0,
|
"user_type": 0,
|
||||||
"role": [],
|
"role": [],
|
||||||
|
"dept_key": "dvadmin",
|
||||||
"first_name": "",
|
"first_name": "",
|
||||||
"last_name": "",
|
"last_name": "",
|
||||||
"is_staff": true,
|
"is_staff": true,
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "application.settings")
|
|||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
from dvadmin.utils.core_initialize import CoreInitialize
|
from dvadmin.utils.core_initialize import CoreInitialize
|
||||||
from dvadmin.system.fixtures.initSerializer import UsersInitSerializer, DeptInitSerializer, RoleInitSerializer, \
|
from dvadmin.system.fixtures.initSerializer import (
|
||||||
MenuInitSerializer, ApiWhiteListInitSerializer, DictionaryInitSerializer, SystemConfigInitSerializer, \
|
UsersInitSerializer, DeptInitSerializer, RoleInitSerializer,
|
||||||
RoleMenuInitSerializer, RoleMenuButtonInitSerializer
|
MenuInitSerializer, ApiWhiteListInitSerializer, DictionaryInitSerializer,
|
||||||
|
SystemConfigInitSerializer, RoleMenuInitSerializer, RoleApiPermissionInitSerializer, RoleColumnInitSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Initialize(CoreInitialize):
|
class Initialize(CoreInitialize):
|
||||||
@@ -37,7 +39,7 @@ class Initialize(CoreInitialize):
|
|||||||
"""
|
"""
|
||||||
初始化菜单信息
|
初始化菜单信息
|
||||||
"""
|
"""
|
||||||
self.init_base(MenuInitSerializer, unique_fields=['name', 'web_path', 'component', 'component_name'])
|
self.init_base(MenuInitSerializer, unique_fields=['name'])
|
||||||
|
|
||||||
def init_role_menu(self):
|
def init_role_menu(self):
|
||||||
"""
|
"""
|
||||||
@@ -45,12 +47,17 @@ class Initialize(CoreInitialize):
|
|||||||
"""
|
"""
|
||||||
self.init_base(RoleMenuInitSerializer, unique_fields=['role', 'menu'])
|
self.init_base(RoleMenuInitSerializer, unique_fields=['role', 'menu'])
|
||||||
|
|
||||||
def init_role_menu_button(self):
|
def init_role_api_permission(self):
|
||||||
"""
|
"""
|
||||||
初始化角色菜单按钮信息
|
初始化角色菜单按钮信息
|
||||||
"""
|
"""
|
||||||
self.init_base(RoleMenuButtonInitSerializer, unique_fields=['role', 'menu_button'])
|
self.init_base(RoleApiPermissionInitSerializer, unique_fields=['role', 'api','name'])
|
||||||
|
|
||||||
|
def init_role_column(self):
|
||||||
|
"""
|
||||||
|
初始化角色字段权限
|
||||||
|
"""
|
||||||
|
self.init_base(RoleColumnInitSerializer, unique_fields=['app','model','field_name'])
|
||||||
|
|
||||||
def init_api_white_list(self):
|
def init_api_white_list(self):
|
||||||
"""
|
"""
|
||||||
@@ -76,7 +83,8 @@ class Initialize(CoreInitialize):
|
|||||||
self.init_users()
|
self.init_users()
|
||||||
self.init_menu()
|
self.init_menu()
|
||||||
self.init_role_menu()
|
self.init_role_menu()
|
||||||
self.init_role_menu_button()
|
self.init_role_api_permission()
|
||||||
|
self.init_role_column()
|
||||||
self.init_api_white_list()
|
self.init_api_white_list()
|
||||||
self.init_dictionary()
|
self.init_dictionary()
|
||||||
self.init_system_config()
|
self.init_system_config()
|
||||||
|
|||||||
@@ -1,16 +1,38 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser, UserManager
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||||
from application import dispatch
|
from application import dispatch
|
||||||
from dvadmin.utils.models import CoreModel, table_prefix
|
from dvadmin.utils.models import CoreModel, table_prefix, get_custom_app_models
|
||||||
|
|
||||||
STATUS_CHOICES = (
|
|
||||||
(0, "禁用"),
|
class Role(CoreModel):
|
||||||
(1, "启用"),
|
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
|
||||||
)
|
key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
|
||||||
|
sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
|
||||||
|
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = table_prefix + "system_role"
|
||||||
|
verbose_name = "角色表"
|
||||||
|
verbose_name_plural = verbose_name
|
||||||
|
ordering = ("sort",)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomUserManager(UserManager):
|
||||||
|
|
||||||
|
def create_superuser(self, username, email=None, password=None, **extra_fields):
|
||||||
|
user = super(CustomUserManager, self).create_superuser(username, email, password, **extra_fields)
|
||||||
|
user.set_password(password)
|
||||||
|
try:
|
||||||
|
user.role.add(Role.objects.get(name="管理员"))
|
||||||
|
user.save(using=self._db)
|
||||||
|
return user
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
user.delete()
|
||||||
|
raise ValidationError("角色`管理员`不存在, 创建失败, 请先执行python manage.py init")
|
||||||
|
|
||||||
|
|
||||||
class Users(CoreModel, AbstractUser):
|
class Users(CoreModel, AbstractUser):
|
||||||
@@ -48,6 +70,7 @@ class Users(CoreModel, AbstractUser):
|
|||||||
blank=True,
|
blank=True,
|
||||||
help_text="关联部门",
|
help_text="关联部门",
|
||||||
)
|
)
|
||||||
|
objects = CustomUserManager()
|
||||||
|
|
||||||
def set_password(self, raw_password):
|
def set_password(self, raw_password):
|
||||||
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
|
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
|
||||||
@@ -76,24 +99,9 @@ class Post(CoreModel):
|
|||||||
ordering = ("sort",)
|
ordering = ("sort",)
|
||||||
|
|
||||||
|
|
||||||
class Role(CoreModel):
|
|
||||||
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
|
|
||||||
key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
|
|
||||||
sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
|
|
||||||
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
|
|
||||||
admin = models.BooleanField(default=False, verbose_name="是否为admin", help_text="是否为admin")
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
db_table = table_prefix + "system_role"
|
|
||||||
verbose_name = "角色表"
|
|
||||||
verbose_name_plural = verbose_name
|
|
||||||
ordering = ("sort",)
|
|
||||||
|
|
||||||
|
|
||||||
class Dept(CoreModel):
|
class Dept(CoreModel):
|
||||||
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
|
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
|
||||||
key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符",
|
key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符", help_text="关联字符")
|
||||||
help_text="关联字符")
|
|
||||||
sort = models.IntegerField(default=1, verbose_name="显示排序", help_text="显示排序")
|
sort = models.IntegerField(default=1, verbose_name="显示排序", help_text="显示排序")
|
||||||
owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人")
|
owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人")
|
||||||
phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话")
|
phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话")
|
||||||
@@ -147,22 +155,24 @@ class Menu(CoreModel):
|
|||||||
help_text="上级菜单",
|
help_text="上级菜单",
|
||||||
)
|
)
|
||||||
icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
|
icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
|
||||||
name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称")
|
name = models.CharField(max_length=64, verbose_name="目录名称/菜单名称/按钮名称", help_text="目录名称/菜单名称/按钮名称")
|
||||||
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
|
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
|
||||||
ISLINK_CHOICES = (
|
|
||||||
(0, "否"),
|
|
||||||
(1, "是"),
|
|
||||||
)
|
|
||||||
is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
|
is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
|
||||||
is_catalog = models.BooleanField(default=False, verbose_name="是否目录", help_text="是否目录")
|
MENU_TYPE_CHOICES =(
|
||||||
web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
|
(0, "目录"),
|
||||||
component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
|
(1, "菜单"),
|
||||||
|
(2, "按钮"),
|
||||||
|
)
|
||||||
|
menu_type = models.IntegerField(default=0, verbose_name="菜单类型", help_text="菜单类型")
|
||||||
|
web_path = models.CharField(max_length=128,default="/", verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
|
||||||
|
component = models.CharField(max_length=200, verbose_name="组件地址/按钮权限值", null=True, blank=True, help_text="组件地址/按钮权限值")
|
||||||
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
|
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
|
||||||
help_text="组件名称")
|
help_text="组件名称")
|
||||||
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
|
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
|
||||||
cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
|
cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
|
||||||
visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
|
visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
|
||||||
help_text="侧边栏中是否显示")
|
help_text="侧边栏中是否显示")
|
||||||
|
frame_out = models.BooleanField(default=False, blank=True, verbose_name="是否主框架外", help_text="是否主框架外")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = table_prefix + "system_menu"
|
db_table = table_prefix + "system_menu"
|
||||||
@@ -171,6 +181,23 @@ class Menu(CoreModel):
|
|||||||
ordering = ("sort",)
|
ordering = ("sort",)
|
||||||
|
|
||||||
|
|
||||||
|
class Columns(CoreModel):
|
||||||
|
role = models.ForeignKey(to='Role', on_delete=models.CASCADE, verbose_name='角色', db_constraint=False)
|
||||||
|
app = models.CharField(max_length=64, verbose_name='应用名')
|
||||||
|
model = models.CharField(max_length=64, verbose_name='表名')
|
||||||
|
field_name = models.CharField(max_length=64, verbose_name='模型表字段名')
|
||||||
|
title = models.CharField(max_length=64, verbose_name='字段显示名')
|
||||||
|
is_query = models.BooleanField(default=1, verbose_name='是否可查询')
|
||||||
|
is_create = models.BooleanField(default=1, verbose_name='是否可创建')
|
||||||
|
is_update = models.BooleanField(default=1, verbose_name='是否可更新')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = table_prefix + "system_columns"
|
||||||
|
verbose_name = "列权限表"
|
||||||
|
verbose_name_plural = verbose_name
|
||||||
|
ordering = ("id",)
|
||||||
|
|
||||||
|
|
||||||
class MenuButton(CoreModel):
|
class MenuButton(CoreModel):
|
||||||
menu = models.ForeignKey(
|
menu = models.ForeignKey(
|
||||||
to="Menu",
|
to="Menu",
|
||||||
@@ -221,28 +248,28 @@ class RoleMenuPermission(CoreModel):
|
|||||||
db_table = table_prefix + "role_menu_permission"
|
db_table = table_prefix + "role_menu_permission"
|
||||||
verbose_name = "角色菜单权限表"
|
verbose_name = "角色菜单权限表"
|
||||||
verbose_name_plural = verbose_name
|
verbose_name_plural = verbose_name
|
||||||
ordering = ("-create_datetime",)
|
# ordering = ("-create_datetime",)
|
||||||
|
|
||||||
|
|
||||||
class RoleMenuButtonPermission(CoreModel):
|
class RoleApiPermission(CoreModel):
|
||||||
role = models.ForeignKey(
|
role = models.ForeignKey(
|
||||||
to="Role",
|
to="Role",
|
||||||
db_constraint=False,
|
db_constraint=False,
|
||||||
related_name="role_menu_button",
|
related_name="role_api",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name="关联角色",
|
verbose_name="关联角色",
|
||||||
help_text="关联角色",
|
help_text="关联角色",
|
||||||
)
|
)
|
||||||
menu_button = models.ForeignKey(
|
name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
|
||||||
to="MenuButton",
|
api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址")
|
||||||
db_constraint=False,
|
METHOD_CHOICES = (
|
||||||
related_name="menu_button_permission",
|
(0, "GET"),
|
||||||
on_delete=models.CASCADE,
|
(1, "POST"),
|
||||||
verbose_name="关联菜单按钮",
|
(2, "PUT"),
|
||||||
help_text="关联菜单按钮",
|
(3, "DELETE"),
|
||||||
null=True,
|
|
||||||
blank=True
|
|
||||||
)
|
)
|
||||||
|
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
|
||||||
|
help_text="接口请求方法")
|
||||||
DATASCOPE_CHOICES = (
|
DATASCOPE_CHOICES = (
|
||||||
(0, "仅本人数据权限"),
|
(0, "仅本人数据权限"),
|
||||||
(1, "本部门及以下数据权限"),
|
(1, "本部门及以下数据权限"),
|
||||||
@@ -256,8 +283,8 @@ class RoleMenuButtonPermission(CoreModel):
|
|||||||
help_text="数据权限-关联部门")
|
help_text="数据权限-关联部门")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = table_prefix + "role_menu_button_permission"
|
db_table = table_prefix + "role_api_permission"
|
||||||
verbose_name = "角色按钮权限表"
|
verbose_name = "角色接口权限表"
|
||||||
verbose_name_plural = verbose_name
|
verbose_name_plural = verbose_name
|
||||||
ordering = ("-create_datetime",)
|
ordering = ("-create_datetime",)
|
||||||
|
|
||||||
@@ -274,8 +301,7 @@ class Dictionary(CoreModel):
|
|||||||
(7, "images"),
|
(7, "images"),
|
||||||
)
|
)
|
||||||
label = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称")
|
label = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称")
|
||||||
value = models.CharField(max_length=200, blank=True, null=True, verbose_name="字典编号",
|
value = models.CharField(max_length=200, blank=True, null=True, verbose_name="字典编号", help_text="字典编号/实际值")
|
||||||
help_text="字典编号/实际值")
|
|
||||||
parent = models.ForeignKey(
|
parent = models.ForeignKey(
|
||||||
to="self",
|
to="self",
|
||||||
related_name="sublist",
|
related_name="sublist",
|
||||||
@@ -339,12 +365,16 @@ class OperationLog(CoreModel):
|
|||||||
def media_file_name(instance, filename):
|
def media_file_name(instance, filename):
|
||||||
h = instance.md5sum
|
h = instance.md5sum
|
||||||
basename, ext = os.path.splitext(filename)
|
basename, ext = os.path.splitext(filename)
|
||||||
return os.path.join("files", h[0:1], h[1:2], h + ext.lower())
|
return os.path.join("files", h[:1], h[1:2], h + ext.lower())
|
||||||
|
|
||||||
|
|
||||||
class FileList(CoreModel):
|
class FileList(CoreModel):
|
||||||
name = models.CharField(max_length=200, null=True, blank=True, verbose_name="名称", help_text="名称")
|
name = models.CharField(max_length=200, null=True, blank=True, verbose_name="名称", help_text="名称")
|
||||||
url = models.FileField(upload_to=media_file_name)
|
url = models.FileField(upload_to=media_file_name, null=True, blank=True,)
|
||||||
|
file_url = models.CharField(max_length=255, blank=True, verbose_name="文件地址", help_text="文件地址")
|
||||||
|
engine = models.CharField(max_length=100, default='local', blank=True, verbose_name="引擎", help_text="引擎")
|
||||||
|
mime_type = models.CharField(max_length=100, blank=True, verbose_name="Mime类型", help_text="Mime类型")
|
||||||
|
size = models.CharField(max_length=36, blank=True, verbose_name="文件大小", help_text="文件大小")
|
||||||
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
|
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
@@ -353,6 +383,11 @@ class FileList(CoreModel):
|
|||||||
for chunk in self.url.chunks():
|
for chunk in self.url.chunks():
|
||||||
md5.update(chunk)
|
md5.update(chunk)
|
||||||
self.md5sum = md5.hexdigest()
|
self.md5sum = md5.hexdigest()
|
||||||
|
if not self.size:
|
||||||
|
self.size = self.url.size
|
||||||
|
if not self.file_url:
|
||||||
|
url = media_file_name(self, self.name)
|
||||||
|
self.file_url = f'media/{url}'
|
||||||
super(FileList, self).save(*args, **kwargs)
|
super(FileList, self).save(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -422,7 +457,7 @@ class SystemConfig(CoreModel):
|
|||||||
help_text="父级",
|
help_text="父级",
|
||||||
)
|
)
|
||||||
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
|
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
|
||||||
key = models.CharField(max_length=20, verbose_name="键", help_text="键", db_index=True)
|
key = models.CharField(max_length=50, verbose_name="键", help_text="键", db_index=True)
|
||||||
value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True)
|
value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True)
|
||||||
sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
|
sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
|
||||||
status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
|
status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
|
||||||
|
|||||||
@@ -8,18 +8,17 @@ from dvadmin.system.views.dictionary import DictionaryViewSet
|
|||||||
from dvadmin.system.views.file_list import FileViewSet
|
from dvadmin.system.views.file_list import FileViewSet
|
||||||
from dvadmin.system.views.login_log import LoginLogViewSet
|
from dvadmin.system.views.login_log import LoginLogViewSet
|
||||||
from dvadmin.system.views.menu import MenuViewSet
|
from dvadmin.system.views.menu import MenuViewSet
|
||||||
from dvadmin.system.views.menu_button import MenuButtonViewSet
|
|
||||||
from dvadmin.system.views.message_center import MessageCenterViewSet
|
from dvadmin.system.views.message_center import MessageCenterViewSet
|
||||||
from dvadmin.system.views.operation_log import OperationLogViewSet
|
from dvadmin.system.views.operation_log import OperationLogViewSet
|
||||||
from dvadmin.system.views.role import RoleViewSet
|
from dvadmin.system.views.role import RoleViewSet
|
||||||
from dvadmin.system.views.role_menu import RoleMenuPermissionViewSet
|
from dvadmin.system.views.role_menu import RoleMenuPermissionViewSet
|
||||||
from dvadmin.system.views.role_menu_button_permission import RoleMenuButtonPermissionViewSet
|
from dvadmin.system.views.role_api_permission import RoleApiPermissionViewSet
|
||||||
from dvadmin.system.views.system_config import SystemConfigViewSet
|
from dvadmin.system.views.system_config import SystemConfigViewSet
|
||||||
from dvadmin.system.views.user import UserViewSet
|
from dvadmin.system.views.user import UserViewSet
|
||||||
|
from dvadmin.system.views.column import ColumnViewSet
|
||||||
|
|
||||||
system_url = routers.SimpleRouter()
|
system_url = routers.SimpleRouter()
|
||||||
system_url.register(r'menu', MenuViewSet)
|
system_url.register(r'menu', MenuViewSet)
|
||||||
system_url.register(r'menu_button', MenuButtonViewSet)
|
|
||||||
system_url.register(r'role', RoleViewSet)
|
system_url.register(r'role', RoleViewSet)
|
||||||
system_url.register(r'dept', DeptViewSet)
|
system_url.register(r'dept', DeptViewSet)
|
||||||
system_url.register(r'user', UserViewSet)
|
system_url.register(r'user', UserViewSet)
|
||||||
@@ -29,11 +28,10 @@ system_url.register(r'area', AreaViewSet)
|
|||||||
system_url.register(r'file', FileViewSet)
|
system_url.register(r'file', FileViewSet)
|
||||||
system_url.register(r'api_white_list', ApiWhiteListViewSet)
|
system_url.register(r'api_white_list', ApiWhiteListViewSet)
|
||||||
system_url.register(r'system_config', SystemConfigViewSet)
|
system_url.register(r'system_config', SystemConfigViewSet)
|
||||||
system_url.register(r'message_center',MessageCenterViewSet)
|
system_url.register(r'message_center', MessageCenterViewSet)
|
||||||
system_url.register(r'role_menu_button_permission', RoleMenuButtonPermissionViewSet)
|
system_url.register(r'role_api_permission', RoleApiPermissionViewSet)
|
||||||
system_url.register(r'role_menu_permission', RoleMenuPermissionViewSet)
|
system_url.register(r'role_menu_permission', RoleMenuPermissionViewSet)
|
||||||
|
system_url.register(r'column', ColumnViewSet)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
|
from dvadmin.system.models import Columns, Role
|
||||||
|
from dvadmin.utils.models import get_custom_app_models
|
||||||
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
|
from dvadmin.utils.json_response import DetailResponse, ErrorResponse, SuccessResponse
|
||||||
|
|
||||||
|
|
||||||
|
class ColumnSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
列权限序列化器
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Columns
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = ['id']
|
||||||
|
|
||||||
|
|
||||||
|
class ColumnViewSet(CustomModelViewSet):
|
||||||
|
"""
|
||||||
|
列权限视图集
|
||||||
|
"""
|
||||||
|
queryset = Columns.objects.all()
|
||||||
|
serializer_class = ColumnSerializer
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
role_id = request.query_params.get('role')
|
||||||
|
app_name = request.query_params.get('app')
|
||||||
|
model_name = request.query_params.get('model')
|
||||||
|
if not role_id or not model_name or not app_name:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
queryset = Columns.objects.filter(role_id=role_id, model=model_name, app=app_name)
|
||||||
|
serializer = ColumnSerializer(queryset, many=True, request=request)
|
||||||
|
return SuccessResponse(data=serializer.data, msg="获取成功")
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
payload = request.data
|
||||||
|
for model in get_custom_app_models(payload.get('app')):
|
||||||
|
if payload.get('model') == model['model']:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return ErrorResponse(msg='模型表不存在')
|
||||||
|
|
||||||
|
if Columns.objects.filter(app=model['app'], model=model['model'], field_name=payload.get('field_name')).exists():
|
||||||
|
return ErrorResponse(msg='‘%s’ 字段权限已有,不可重复创建' % payload.get('title'))
|
||||||
|
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def get_models(self, request):
|
||||||
|
"""获取所有项目app下的model"""
|
||||||
|
res = []
|
||||||
|
for app in get_custom_app_models():
|
||||||
|
for model in app:
|
||||||
|
res.append({
|
||||||
|
'app': model['app'],
|
||||||
|
'title': model['verbose'],
|
||||||
|
'key': model['model']
|
||||||
|
})
|
||||||
|
return DetailResponse(res)
|
||||||
|
|
||||||
|
@action(methods=['POST'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def auto_match_fields(self, request):
|
||||||
|
"""自动匹配已有的字段"""
|
||||||
|
role_id = request.data.get('role')
|
||||||
|
app_name = request.data.get('app')
|
||||||
|
model_name = request.data.get('model')
|
||||||
|
if not role_id or not model_name or not app_name:
|
||||||
|
return DetailResponse([], msg='无操作')
|
||||||
|
for model in get_custom_app_models(app_name):
|
||||||
|
if model['model'] != model_name:
|
||||||
|
continue
|
||||||
|
for field in model['fields']:
|
||||||
|
if Columns.objects.filter(
|
||||||
|
role_id=role_id, app=app_name, model=model_name, field_name=field['name']
|
||||||
|
).exists():
|
||||||
|
continue
|
||||||
|
data = {
|
||||||
|
'role': role_id,
|
||||||
|
'app': app_name,
|
||||||
|
'model': model_name,
|
||||||
|
'field_name': field['name'],
|
||||||
|
'title': str(field['title']),
|
||||||
|
}
|
||||||
|
serializer = self.get_serializer(data=data, request=request)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
return DetailResponse(msg='匹配成功')
|
||||||
@@ -9,9 +9,8 @@ from rest_framework import serializers
|
|||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
from dvadmin.system.models import Dept
|
from dvadmin.system.models import Dept, RoleApiPermission, Users
|
||||||
from dvadmin.utils.json_response import DetailResponse, SuccessResponse
|
from dvadmin.utils.json_response import DetailResponse, SuccessResponse, ErrorResponse
|
||||||
from dvadmin.utils.permission import AnonymousUserPermission
|
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
|
||||||
@@ -25,6 +24,11 @@ class DeptSerializer(CustomModelSerializer):
|
|||||||
has_children = serializers.SerializerMethodField()
|
has_children = serializers.SerializerMethodField()
|
||||||
hasChild = serializers.SerializerMethodField()
|
hasChild = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
dept_user_count = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_dept_user_count(self, obj: Dept):
|
||||||
|
return Users.objects.filter(dept=obj).count()
|
||||||
|
|
||||||
def get_hasChild(self, instance):
|
def get_hasChild(self, instance):
|
||||||
hasChild = Dept.objects.filter(parent=instance.id)
|
hasChild = Dept.objects.filter(parent=instance.id)
|
||||||
if hasChild:
|
if hasChild:
|
||||||
@@ -56,18 +60,18 @@ class DeptImportSerializer(CustomModelSerializer):
|
|||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DeptCreateUpdateSerializer(CustomModelSerializer):
|
class DeptCreateUpdateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
部门管理 创建/更新时的列化器
|
部门管理 创建/更新时的列化器
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
value = validated_data.get('parent',None)
|
value = validated_data.get('parent', None)
|
||||||
if value is None:
|
if value is None:
|
||||||
validated_data['parent'] = self.request.user.dept
|
validated_data['parent'] = self.request.user.dept
|
||||||
|
dept_obj = Dept.objects.filter(parent=self.request.user.dept).order_by('-sort').first()
|
||||||
|
last_sort = dept_obj.sort if dept_obj else 0
|
||||||
|
validated_data['sort'] = last_sort + 1
|
||||||
instance = super().create(validated_data)
|
instance = super().create(validated_data)
|
||||||
instance.dept_belong_id = instance.id
|
instance.dept_belong_id = instance.id
|
||||||
instance.save()
|
instance.save()
|
||||||
@@ -123,33 +127,114 @@ class DeptViewSet(CustomModelViewSet):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
return SuccessResponse(data=data)
|
return SuccessResponse(data=data)
|
||||||
|
|
||||||
|
@action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[])
|
||||||
def dept_lazy_tree(self, request, *args, **kwargs):
|
def dept_lazy_tree(self, request, *args, **kwargs):
|
||||||
parent = self.request.query_params.get('parent')
|
parent = self.request.query_params.get('parent')
|
||||||
is_superuser = request.user.is_superuser
|
is_superuser = request.user.is_superuser
|
||||||
if is_superuser:
|
if is_superuser:
|
||||||
queryset = Dept.objects.values('id', 'name', 'parent')
|
queryset = Dept.objects.values('id', 'name', 'parent')
|
||||||
else:
|
else:
|
||||||
data_range = request.user.role.values_list('data_range', flat=True)
|
role_ids = request.user.role.values_list('id', flat=True)
|
||||||
|
data_range = RoleApiPermission.objects.filter(role__in=role_ids).values_list('data_range', flat=True)
|
||||||
user_dept_id = request.user.dept.id
|
user_dept_id = request.user.dept.id
|
||||||
dept_list = [user_dept_id]
|
dept_list = [user_dept_id]
|
||||||
data_range_list = list(set(data_range))
|
data_range_list = list(set(data_range))
|
||||||
for item in data_range_list:
|
for item in data_range_list:
|
||||||
if item in [0,2]:
|
if item in [0, 2]:
|
||||||
dept_list = [user_dept_id]
|
dept_list = [user_dept_id]
|
||||||
elif item == 1:
|
elif item == 1:
|
||||||
dept_list = Dept.recursion_dept_info(dept_id=user_dept_id)
|
dept_list = Dept.recursion_dept_info(dept_id=user_dept_id)
|
||||||
elif item == 3:
|
elif item == 3:
|
||||||
dept_list = Dept.objects.values_list('id',flat=True)
|
dept_list = Dept.objects.values_list('id', flat=True)
|
||||||
elif item == 4:
|
elif item == 4:
|
||||||
dept_list = request.user.role.values_list('dept',flat=True)
|
dept_list = request.user.role.values_list('dept', flat=True)
|
||||||
else:
|
else:
|
||||||
dept_list = []
|
dept_list = []
|
||||||
queryset = Dept.objects.filter(id__in=dept_list).values('id', 'name', 'parent')
|
queryset = Dept.objects.filter(id__in=dept_list).values('id', 'name', 'parent')
|
||||||
return DetailResponse(data=queryset, msg="获取成功")
|
return DetailResponse(data=queryset, msg="获取成功")
|
||||||
|
|
||||||
|
@action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated], extra_filter_class=[])
|
||||||
@action(methods=["GET"], detail=False, permission_classes=[IsAuthenticated],extra_filter_class=[])
|
|
||||||
def all_dept(self, request, *args, **kwargs):
|
def all_dept(self, request, *args, **kwargs):
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
data = queryset.filter(status=True).order_by('sort').values('name', 'id', 'parent')
|
data = queryset.filter(status=True).order_by('sort').values('name', 'id', 'parent')
|
||||||
return DetailResponse(data=data, msg="获取成功")
|
return DetailResponse(data=data, msg="获取成功")
|
||||||
|
|
||||||
|
@action(methods=['POST'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def move_up(self, request):
|
||||||
|
"""部门上移"""
|
||||||
|
dept_id = request.data.get('dept_id')
|
||||||
|
try:
|
||||||
|
dept = Dept.objects.get(id=dept_id)
|
||||||
|
except Dept.DoesNotExist:
|
||||||
|
return ErrorResponse(msg="部门不存在")
|
||||||
|
previous_menu = Dept.objects.filter(sort__lt=dept.sort, parent=dept.parent).order_by('-sort').first()
|
||||||
|
if previous_menu:
|
||||||
|
previous_menu.sort, dept.sort = dept.sort, previous_menu.sort
|
||||||
|
previous_menu.save()
|
||||||
|
dept.save()
|
||||||
|
return SuccessResponse(data=[], msg="上移成功")
|
||||||
|
|
||||||
|
@action(methods=['POST'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def move_down(self, request):
|
||||||
|
"""部门下移"""
|
||||||
|
dept_id = request.data['dept_id']
|
||||||
|
try:
|
||||||
|
dept = Dept.objects.get(id=dept_id)
|
||||||
|
except Dept.DoesNotExist:
|
||||||
|
return ErrorResponse(msg="部门不存在")
|
||||||
|
next_menu = Dept.objects.filter(sort__gt=dept.sort, parent=dept.parent).order_by('sort').first()
|
||||||
|
if next_menu:
|
||||||
|
next_menu.sort, dept.sort = dept.sort, next_menu.sort
|
||||||
|
next_menu.save()
|
||||||
|
dept.save()
|
||||||
|
return SuccessResponse(data=[], msg="下移成功")
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||||
|
def dept_info(self, request):
|
||||||
|
"""部门信息"""
|
||||||
|
def inner(did, li):
|
||||||
|
sub = Dept.objects.filter(parent_id=did)
|
||||||
|
if not sub.exists():
|
||||||
|
return li
|
||||||
|
for i in sub:
|
||||||
|
li.append(i.pk)
|
||||||
|
inner(i, li)
|
||||||
|
return li
|
||||||
|
dept_id = request.query_params.get('dept_id')
|
||||||
|
show_all = request.query_params.get('show_all')
|
||||||
|
if dept_id is None:
|
||||||
|
return ErrorResponse(msg="部门不存在")
|
||||||
|
if not show_all:
|
||||||
|
show_all = 0
|
||||||
|
if int(show_all): # 递归当前部门下的所有部门,查询用户
|
||||||
|
all_did = [dept_id]
|
||||||
|
inner(dept_id, all_did)
|
||||||
|
users = Users.objects.filter(dept_id__in=all_did)
|
||||||
|
else:
|
||||||
|
if dept_id != '':
|
||||||
|
users = Users.objects.filter(dept_id=dept_id)
|
||||||
|
else:
|
||||||
|
users = Users.objects.none()
|
||||||
|
dept_obj = Dept.objects.get(id=dept_id) if dept_id != '' else None
|
||||||
|
sub_dept = Dept.objects.filter(parent_id=dept_obj.pk) if dept_id != '' else []
|
||||||
|
data = {
|
||||||
|
'dept_name': dept_obj and dept_obj.name,
|
||||||
|
'dept_user': users.count(),
|
||||||
|
'owner': dept_obj and dept_obj.owner,
|
||||||
|
'description': dept_obj and dept_obj.description,
|
||||||
|
'gender': {
|
||||||
|
'male': users.filter(gender=1).count(),
|
||||||
|
'female': users.filter(gender=2).count(),
|
||||||
|
'unknown': users.filter(gender=0).count(),
|
||||||
|
},
|
||||||
|
'sub_dept_map': []
|
||||||
|
}
|
||||||
|
for dept in sub_dept:
|
||||||
|
all_did = [dept.pk]
|
||||||
|
inner(dept.pk, all_did)
|
||||||
|
sub_data = {
|
||||||
|
'name': dept.name,
|
||||||
|
'count': Users.objects.filter(dept_id__in=all_did).count()
|
||||||
|
}
|
||||||
|
data['sub_dept_map'].append(sub_data)
|
||||||
|
return SuccessResponse(data)
|
||||||
|
|||||||
@@ -34,6 +34,19 @@ class DictionaryCreateUpdateSerializer(CustomModelSerializer):
|
|||||||
"""
|
"""
|
||||||
字典管理 创建/更新时的列化器
|
字典管理 创建/更新时的列化器
|
||||||
"""
|
"""
|
||||||
|
value = serializers.CharField(max_length=100)
|
||||||
|
|
||||||
|
def validate_value(self, value):
|
||||||
|
"""
|
||||||
|
在父级的字典编号验证重复性
|
||||||
|
"""
|
||||||
|
initial_data = self.initial_data
|
||||||
|
parent = initial_data.get('parent',None)
|
||||||
|
if parent is None:
|
||||||
|
unique = Dictionary.objects.filter(value=value).exists()
|
||||||
|
if unique:
|
||||||
|
raise serializers.ValidationError("字典编号不能重复")
|
||||||
|
return value
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Dictionary
|
model = Dictionary
|
||||||
@@ -51,20 +64,24 @@ class DictionaryViewSet(CustomModelViewSet):
|
|||||||
"""
|
"""
|
||||||
queryset = Dictionary.objects.all()
|
queryset = Dictionary.objects.all()
|
||||||
serializer_class = DictionarySerializer
|
serializer_class = DictionarySerializer
|
||||||
|
create_serializer_class = DictionaryCreateUpdateSerializer
|
||||||
extra_filter_class = []
|
extra_filter_class = []
|
||||||
search_fields = ['label']
|
search_fields = ['label']
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
params = self.request.query_params
|
if self.action =='list':
|
||||||
parent = params.get('parent', None)
|
params = self.request.query_params
|
||||||
if params:
|
parent = params.get('parent', None)
|
||||||
if parent:
|
if params:
|
||||||
queryset = self.queryset.filter(status=1, parent=parent)
|
if parent:
|
||||||
|
queryset = self.queryset.filter(parent=parent)
|
||||||
|
else:
|
||||||
|
queryset = self.queryset.filter(parent__isnull=True)
|
||||||
else:
|
else:
|
||||||
queryset = self.queryset.filter(status=1, parent__isnull=True)
|
queryset = self.queryset.filter(parent__isnull=True)
|
||||||
|
return queryset
|
||||||
else:
|
else:
|
||||||
queryset = self.queryset.filter(status=1, parent__isnull=True)
|
return self.queryset
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class InitDictionaryViewSet(APIView):
|
class InitDictionaryViewSet(APIView):
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
from rest_framework import serializers
|
import hashlib
|
||||||
|
import mimetypes
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
|
||||||
|
from application import dispatch
|
||||||
from dvadmin.system.models import FileList
|
from dvadmin.system.models import FileList
|
||||||
|
from dvadmin.utils.json_response import DetailResponse
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
|
||||||
@@ -9,15 +15,52 @@ class FileSerializer(CustomModelSerializer):
|
|||||||
url = serializers.SerializerMethodField(read_only=True)
|
url = serializers.SerializerMethodField(read_only=True)
|
||||||
|
|
||||||
def get_url(self, instance):
|
def get_url(self, instance):
|
||||||
return 'media/' + str(instance.url)
|
# return 'media/' + str(instance.url)
|
||||||
|
return instance.file_url or (f'media/{str(instance.url)}')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FileList
|
model = FileList
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
validated_data['name'] = str(self.initial_data.get('file'))
|
file_engine = dispatch.get_system_config_values("fileStorageConfig.file_engine") or 'local'
|
||||||
validated_data['url'] = self.initial_data.get('file')
|
file_backup = dispatch.get_system_config_values("fileStorageConfig.file_backup")
|
||||||
|
file = self.initial_data.get('file')
|
||||||
|
file_size = file.size
|
||||||
|
validated_data['name'] = str(file)
|
||||||
|
validated_data['size'] = file_size
|
||||||
|
md5 = hashlib.md5()
|
||||||
|
for chunk in file.chunks():
|
||||||
|
md5.update(chunk)
|
||||||
|
validated_data['md5sum'] = md5.hexdigest()
|
||||||
|
validated_data['engine'] = file_engine
|
||||||
|
validated_data['mime_type'] = file.content_type
|
||||||
|
if file_backup:
|
||||||
|
validated_data['url'] = file
|
||||||
|
if file_engine == 'oss':
|
||||||
|
from dvadmin_cloud_storage.views.aliyun import ali_oss_upload
|
||||||
|
file_path = ali_oss_upload(file)
|
||||||
|
if file_path:
|
||||||
|
validated_data['file_url'] = file_path
|
||||||
|
else:
|
||||||
|
raise ValueError("上传失败")
|
||||||
|
elif file_engine == 'cos':
|
||||||
|
from dvadmin_cloud_storage.views.tencent import tencent_cos_upload
|
||||||
|
file_path = tencent_cos_upload(file)
|
||||||
|
if file_path:
|
||||||
|
validated_data['file_url'] = file_path
|
||||||
|
else:
|
||||||
|
raise ValueError("上传失败")
|
||||||
|
else:
|
||||||
|
validated_data['url'] = file
|
||||||
|
# 审计字段
|
||||||
|
try:
|
||||||
|
request_user = self.request.user
|
||||||
|
validated_data['dept_belong_id'] = request_user.dept.id
|
||||||
|
validated_data['creator'] = request_user.id
|
||||||
|
validated_data['modifier'] = request_user.id
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return super().create(validated_data)
|
return super().create(validated_data)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,4 +76,4 @@ class FileViewSet(CustomModelViewSet):
|
|||||||
queryset = FileList.objects.all()
|
queryset = FileList.objects.all()
|
||||||
serializer_class = FileSerializer
|
serializer_class = FileSerializer
|
||||||
filter_fields = ['name', ]
|
filter_fields = ['name', ]
|
||||||
permission_classes = []
|
permission_classes = []
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from captcha.views import CaptchaStore, captcha_image
|
from captcha.views import CaptchaStore, captcha_image
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
from django.contrib.auth.hashers import make_password, check_password
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
@@ -14,9 +12,7 @@ from rest_framework import serializers
|
|||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
|
||||||
from rest_framework_simplejwt.views import TokenObtainPairView
|
from rest_framework_simplejwt.views import TokenObtainPairView
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from application import dispatch
|
from application import dispatch
|
||||||
from dvadmin.system.models import Users
|
from dvadmin.system.models import Users
|
||||||
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
||||||
@@ -58,6 +54,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
captcha = serializers.CharField(
|
captcha = serializers.CharField(
|
||||||
max_length=6, required=False, allow_null=True, allow_blank=True
|
max_length=6, required=False, allow_null=True, allow_blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Users
|
model = Users
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
@@ -79,13 +76,18 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
raise CustomValidationError("验证码过期")
|
raise CustomValidationError("验证码过期")
|
||||||
else:
|
else:
|
||||||
if self.image_code and (
|
if self.image_code and (
|
||||||
self.image_code.response == captcha
|
self.image_code.response == captcha
|
||||||
or self.image_code.challenge == captcha
|
or self.image_code.challenge == captcha
|
||||||
):
|
):
|
||||||
self.image_code and self.image_code.delete()
|
self.image_code and self.image_code.delete()
|
||||||
else:
|
else:
|
||||||
self.image_code and self.image_code.delete()
|
self.image_code and self.image_code.delete()
|
||||||
raise CustomValidationError("图片验证码错误")
|
raise CustomValidationError("图片验证码错误")
|
||||||
|
|
||||||
|
user = Users.objects.get(username=attrs['username'])
|
||||||
|
if not user.is_active:
|
||||||
|
raise CustomValidationError("账号被锁定")
|
||||||
|
|
||||||
data = super().validate(attrs)
|
data = super().validate(attrs)
|
||||||
data["name"] = self.user.name
|
data["name"] = self.user.name
|
||||||
data["userId"] = self.user.id
|
data["userId"] = self.user.id
|
||||||
@@ -107,6 +109,7 @@ class LoginSerializer(TokenObtainPairSerializer):
|
|||||||
save_login_log(request=request)
|
save_login_log(request=request)
|
||||||
return {"code": 2000, "msg": "请求成功", "data": data}
|
return {"code": 2000, "msg": "请求成功", "data": data}
|
||||||
|
|
||||||
|
|
||||||
class LoginView(TokenObtainPairView):
|
class LoginView(TokenObtainPairView):
|
||||||
"""
|
"""
|
||||||
登录接口
|
登录接口
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
"""
|
"""
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
from dvadmin.system.models import Menu, MenuButton, RoleMenuPermission
|
from dvadmin.system.models import Menu, RoleMenuPermission
|
||||||
from dvadmin.utils.json_response import SuccessResponse
|
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
|
||||||
@@ -20,16 +21,17 @@ class MenuSerializer(CustomModelSerializer):
|
|||||||
菜单表的简单序列化器
|
菜单表的简单序列化器
|
||||||
"""
|
"""
|
||||||
menuPermission = serializers.SerializerMethodField(read_only=True)
|
menuPermission = serializers.SerializerMethodField(read_only=True)
|
||||||
hasChild = serializers.SerializerMethodField()
|
hasChildren = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def get_menuPermission(self, instance):
|
def get_menuPermission(self, instance):
|
||||||
queryset = instance.menuPermission.order_by('-name').values_list('name', flat=True)
|
queryset = instance.menuPermission.order_by('-name').values('id', 'name', 'value')
|
||||||
|
# MenuButtonSerializer(instance.menuPermission.all(), many=True)
|
||||||
if queryset:
|
if queryset:
|
||||||
return queryset
|
return queryset
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_hasChild(self, instance):
|
def get_hasChildren(self, instance):
|
||||||
hasChild = Menu.objects.filter(parent=instance.id)
|
hasChild = Menu.objects.filter(parent=instance.id)
|
||||||
if hasChild:
|
if hasChild:
|
||||||
return True
|
return True
|
||||||
@@ -47,15 +49,18 @@ class MenuCreateSerializer(CustomModelSerializer):
|
|||||||
"""
|
"""
|
||||||
name = serializers.CharField(required=False)
|
name = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
menu_obj = Menu.objects.filter(parent_id=validated_data.get('parent', None)).order_by('-sort').first()
|
||||||
|
last_sort = menu_obj.sort if menu_obj else 0
|
||||||
|
validated_data['sort'] = last_sort + 1
|
||||||
|
return super().create(validated_data)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WebRouterSerializer(CustomModelSerializer):
|
class WebRouterSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
前端菜单路由的简单序列化器
|
前端菜单路由的简单序列化器
|
||||||
@@ -63,11 +68,11 @@ class WebRouterSerializer(CustomModelSerializer):
|
|||||||
path = serializers.CharField(source="web_path")
|
path = serializers.CharField(source="web_path")
|
||||||
title = serializers.CharField(source="name")
|
title = serializers.CharField(source="name")
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ('id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'is_catalog', 'web_path', 'component',
|
fields = (
|
||||||
'component_name', 'cache', 'visible')
|
'id', 'parent', 'icon', 'sort', 'path', 'name', 'title', 'is_link', 'menu_type', 'web_path', 'component',
|
||||||
|
'component_name', 'cache', 'visible', 'status')
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
@@ -85,14 +90,62 @@ class MenuViewSet(CustomModelViewSet):
|
|||||||
create_serializer_class = MenuCreateSerializer
|
create_serializer_class = MenuCreateSerializer
|
||||||
update_serializer_class = MenuCreateSerializer
|
update_serializer_class = MenuCreateSerializer
|
||||||
search_fields = ['name', 'status']
|
search_fields = ['name', 'status']
|
||||||
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'is_catalog']
|
filter_fields = ['parent', 'name', 'status', 'is_link', 'visible', 'cache', 'menu_type']
|
||||||
# extra_filter_class = []
|
|
||||||
|
@action(methods=['get'], detail=False)
|
||||||
|
def tree(self, request):
|
||||||
|
"""懒加载"""
|
||||||
|
params = request.query_params
|
||||||
|
menu_type = params.get('menu_type', 0)
|
||||||
|
queryset = Menu.objects.filter(menu_type=menu_type).order_by('sort')
|
||||||
|
serializer = MenuSerializer(queryset, many=True, request=request)
|
||||||
|
data = serializer.data
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=True)
|
||||||
|
def getChildren(self,request,pk):
|
||||||
|
"""
|
||||||
|
获取子菜单,用于菜单页面的懒加载
|
||||||
|
"""
|
||||||
|
queryset = Menu.objects.filter(parent=pk)
|
||||||
|
serializer = MenuSerializer(queryset, many=True, request=request)
|
||||||
|
data = serializer.data
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[])
|
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||||
def web_router(self, request):
|
def web_router(self, request):
|
||||||
"""用于前端获取当前角色的路由"""
|
"""用于前端获取当前角色的路由"""
|
||||||
user = request.user
|
user = request.user
|
||||||
queryset = self.queryset.filter(status=1)
|
if user.is_superuser:
|
||||||
|
queryset = self.queryset.filter(status=1,menu_type__in=[0,1])
|
||||||
|
else:
|
||||||
|
role_list = user.role.values_list('id', flat=True)
|
||||||
|
menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id', flat=True)
|
||||||
|
queryset = Menu.objects.filter(id__in=menu_list,menu_type__in=[0,1])
|
||||||
|
serializer = WebRouterSerializer(queryset, many=True, request=request)
|
||||||
|
data = serializer.data
|
||||||
|
return SuccessResponse(data=data, total=len(data), msg="获取成功")
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def menu_button_all_permission(self, request):
|
||||||
|
"""
|
||||||
|
获取所有的按钮权限
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
is_superuser = request.user.is_superuser
|
||||||
|
if is_superuser:
|
||||||
|
queryset = Menu.objects.filter(menu_type=2).values_list('component', flat=True)
|
||||||
|
else:
|
||||||
|
role_id = request.user.role.values_list('id', flat=True)
|
||||||
|
queryset = Menu.objects.filter(role__in=role_id,menu_type=2).values_list('component',flat=True).distinct()
|
||||||
|
return DetailResponse(data=queryset)
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False, permission_classes=[])
|
||||||
|
def get_all_menu(self, request):
|
||||||
|
"""用于菜单管理获取所有的菜单"""
|
||||||
|
user = request.user
|
||||||
|
queryset = self.queryset.all()
|
||||||
if not user.is_superuser:
|
if not user.is_superuser:
|
||||||
role_list = user.role.values_list('id', flat=True)
|
role_list = user.role.values_list('id', flat=True)
|
||||||
menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id')
|
menu_list = RoleMenuPermission.objects.filter(role__in=role_list).values_list('menu_id')
|
||||||
@@ -101,25 +154,32 @@ class MenuViewSet(CustomModelViewSet):
|
|||||||
data = serializer.data
|
data = serializer.data
|
||||||
return SuccessResponse(data=data, total=len(data), msg="获取成功")
|
return SuccessResponse(data=data, total=len(data), msg="获取成功")
|
||||||
|
|
||||||
def list(self,request):
|
@action(methods=['POST'], detail=False, permission_classes=[])
|
||||||
"""懒加载"""
|
def move_up(self, request):
|
||||||
request.query_params._mutable = True
|
"""菜单上移"""
|
||||||
params = request.query_params
|
menu_id = request.data.get('menu_id')
|
||||||
parent = params.get('parent', None)
|
try:
|
||||||
page = params.get('page',None)
|
menu = Menu.objects.get(id=menu_id)
|
||||||
limit = params.get('limit', None)
|
except Menu.DoesNotExist:
|
||||||
if page:
|
return ErrorResponse(msg="菜单不存在")
|
||||||
del params['page']
|
previous_menu = Menu.objects.filter(sort__lt=menu.sort, parent=menu.parent).order_by('-sort').first()
|
||||||
if limit:
|
if previous_menu:
|
||||||
del params['limit']
|
previous_menu.sort, menu.sort = menu.sort, previous_menu.sort
|
||||||
if params:
|
previous_menu.save()
|
||||||
if parent:
|
menu.save()
|
||||||
queryset = self.queryset.filter(status=1, parent=parent)
|
return SuccessResponse(data=[], msg="上移成功")
|
||||||
else:
|
|
||||||
queryset = self.queryset.filter(status=1)
|
@action(methods=['POST'], detail=False, permission_classes=[])
|
||||||
else:
|
def move_down(self, request):
|
||||||
queryset = self.queryset.filter(status=1, parent__isnull=True)
|
"""菜单下移"""
|
||||||
queryset = self.filter_queryset(queryset)
|
menu_id = request.data['menu_id']
|
||||||
serializer = MenuSerializer(queryset, many=True, request=request)
|
try:
|
||||||
data = serializer.data
|
menu = Menu.objects.get(id=menu_id)
|
||||||
return SuccessResponse(data=data)
|
except Menu.DoesNotExist:
|
||||||
|
return ErrorResponse(msg="菜单不存在")
|
||||||
|
next_menu = Menu.objects.filter(sort__gt=menu.sort, parent=menu.parent).order_by('sort').first()
|
||||||
|
if next_menu:
|
||||||
|
next_menu.sort, menu.sort = menu.sort, next_menu.sort
|
||||||
|
next_menu.save()
|
||||||
|
menu.save()
|
||||||
|
return SuccessResponse(data=[], msg="下移成功")
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
@author: 猿小天
|
|
||||||
@contact: QQ:1638245306
|
|
||||||
@Created on: 2021/6/3 003 0:30
|
|
||||||
@Remark: 菜单按钮管理
|
|
||||||
"""
|
|
||||||
from django.db.models import F
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
from rest_framework.permissions import IsAuthenticated
|
|
||||||
|
|
||||||
from dvadmin.system.models import MenuButton, RoleMenuButtonPermission
|
|
||||||
from dvadmin.utils.json_response import DetailResponse
|
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
|
||||||
|
|
||||||
|
|
||||||
class MenuButtonSerializer(CustomModelSerializer):
|
|
||||||
"""
|
|
||||||
菜单按钮-序列化器
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = MenuButton
|
|
||||||
fields = ['id', 'name', 'value', 'api', 'method']
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MenuButtonCreateUpdateSerializer(CustomModelSerializer):
|
|
||||||
"""
|
|
||||||
初始化菜单按钮-序列化器
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = MenuButton
|
|
||||||
fields = "__all__"
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
|
|
||||||
class MenuButtonViewSet(CustomModelViewSet):
|
|
||||||
"""
|
|
||||||
菜单按钮接口
|
|
||||||
list:查询
|
|
||||||
create:新增
|
|
||||||
update:修改
|
|
||||||
retrieve:单例
|
|
||||||
destroy:删除
|
|
||||||
"""
|
|
||||||
queryset = MenuButton.objects.all()
|
|
||||||
serializer_class = MenuButtonSerializer
|
|
||||||
create_serializer_class = MenuButtonCreateUpdateSerializer
|
|
||||||
update_serializer_class = MenuButtonCreateUpdateSerializer
|
|
||||||
extra_filter_class = []
|
|
||||||
|
|
||||||
@action(methods=['get'],detail=False,permission_classes=[IsAuthenticated])
|
|
||||||
def menu_button_all_permission(self,request):
|
|
||||||
"""
|
|
||||||
获取所有的按钮权限
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
is_superuser = request.user.is_superuser
|
|
||||||
is_admin = request.user.role.values_list('admin', flat=True)
|
|
||||||
if is_superuser or True in is_admin:
|
|
||||||
queryset = MenuButton.objects.values_list('value',flat=True)
|
|
||||||
else:
|
|
||||||
role_id = request.user.role.values_list('id', flat=True)
|
|
||||||
queryset = RoleMenuButtonPermission.objects.filter(role__in=role_id).values_list('menu_button__value',flat=True).distinct()
|
|
||||||
return DetailResponse(data=queryset)
|
|
||||||
@@ -85,7 +85,7 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer):
|
|||||||
def get_is_read(self, instance):
|
def get_is_read(self, instance):
|
||||||
user_id = self.request.user.id
|
user_id = self.request.user.id
|
||||||
message_center_id = instance.id
|
message_center_id = instance.id
|
||||||
queryset = MessageCenterTargetUser.objects.filter(messagecenter__id=message_center_id,users_id=user_id).first()
|
queryset = MessageCenterTargetUser.objects.filter(messagecenter__id=message_center_id, users_id=user_id).first()
|
||||||
if queryset:
|
if queryset:
|
||||||
return queryset.is_read
|
return queryset.is_read
|
||||||
return False
|
return False
|
||||||
@@ -95,21 +95,22 @@ class MessageCenterTargetUserListSerializer(CustomModelSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
def websocket_push(user_id, message):
|
def websocket_push(user_id, message):
|
||||||
"""
|
"""
|
||||||
主动推送消息
|
主动推送消息
|
||||||
"""
|
"""
|
||||||
username = "user_"+str(user_id)
|
username = "user_" + str(user_id)
|
||||||
print(103,message)
|
|
||||||
channel_layer = get_channel_layer()
|
channel_layer = get_channel_layer()
|
||||||
async_to_sync(channel_layer.group_send)(
|
async_to_sync(channel_layer.group_send)(
|
||||||
username,
|
username,
|
||||||
{
|
{
|
||||||
"type": "push.message",
|
"type": "push.message",
|
||||||
"json": message
|
"json": message
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class MessageCenterCreateSerializer(CustomModelSerializer):
|
class MessageCenterCreateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
消息中心-新增-序列化器
|
消息中心-新增-序列化器
|
||||||
@@ -122,10 +123,10 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
|
|||||||
# 在保存之前,根据目标类型,把目标用户查询出来并保存
|
# 在保存之前,根据目标类型,把目标用户查询出来并保存
|
||||||
users = initial_data.get('target_user', [])
|
users = initial_data.get('target_user', [])
|
||||||
if target_type in [1]: # 按角色
|
if target_type in [1]: # 按角色
|
||||||
target_role = initial_data.get('target_role',[])
|
target_role = initial_data.get('target_role', [])
|
||||||
users = Users.objects.filter(role__id__in=target_role).values_list('id', flat=True)
|
users = Users.objects.filter(role__id__in=target_role).values_list('id', flat=True)
|
||||||
if target_type in [2]: # 按部门
|
if target_type in [2]: # 按部门
|
||||||
target_dept = initial_data.get('target_dept',[])
|
target_dept = initial_data.get('target_dept', [])
|
||||||
users = Users.objects.filter(dept__id__in=target_dept).values_list('id', flat=True)
|
users = Users.objects.filter(dept__id__in=target_dept).values_list('id', flat=True)
|
||||||
if target_type in [3]: # 系统通知
|
if target_type in [3]: # 系统通知
|
||||||
users = Users.objects.values_list('id', flat=True)
|
users = Users.objects.values_list('id', flat=True)
|
||||||
@@ -141,7 +142,7 @@ class MessageCenterCreateSerializer(CustomModelSerializer):
|
|||||||
for user in users:
|
for user in users:
|
||||||
unread_count = MessageCenterTargetUser.objects.filter(users__id=user, is_read=False).count()
|
unread_count = MessageCenterTargetUser.objects.filter(users__id=user, is_read=False).count()
|
||||||
websocket_push(user, message={"sender": 'system', "contentType": 'SYSTEM',
|
websocket_push(user, message={"sender": 'system', "contentType": 'SYSTEM',
|
||||||
"content": '您有一条新消息~', "unread": unread_count})
|
"content": '您有一条新消息~', "unread": unread_count})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -184,7 +185,7 @@ class MessageCenterViewSet(CustomModelViewSet):
|
|||||||
# 主动推送消息
|
# 主动推送消息
|
||||||
unread_count = MessageCenterTargetUser.objects.filter(users__id=user_id, is_read=False).count()
|
unread_count = MessageCenterTargetUser.objects.filter(users__id=user_id, is_read=False).count()
|
||||||
websocket_push(user_id, message={"sender": 'system', "contentType": 'TEXT',
|
websocket_push(user_id, message={"sender": 'system', "contentType": 'TEXT',
|
||||||
"content": '您查看了一条消息~', "unread": unread_count})
|
"content": '您查看了一条消息~', "unread": unread_count})
|
||||||
return DetailResponse(data=serializer.data, msg="获取成功")
|
return DetailResponse(data=serializer.data, msg="获取成功")
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
@@ -195,7 +196,6 @@ class MessageCenterViewSet(CustomModelViewSet):
|
|||||||
self_user_id = self.request.user.id
|
self_user_id = self.request.user.id
|
||||||
# queryset = MessageCenterTargetUser.objects.filter(users__id=self_user_id).order_by('-create_datetime')
|
# queryset = MessageCenterTargetUser.objects.filter(users__id=self_user_id).order_by('-create_datetime')
|
||||||
queryset = MessageCenter.objects.filter(target_user__id=self_user_id)
|
queryset = MessageCenter.objects.filter(target_user__id=self_user_id)
|
||||||
print(queryset)
|
|
||||||
# queryset = self.filter_queryset(queryset)
|
# queryset = self.filter_queryset(queryset)
|
||||||
page = self.paginate_queryset(queryset)
|
page = self.paginate_queryset(queryset)
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ from rest_framework.permissions import IsAuthenticated
|
|||||||
from dvadmin.system.models import Role, Menu, MenuButton, Dept
|
from dvadmin.system.models import Role, Menu, MenuButton, Dept
|
||||||
from dvadmin.system.views.dept import DeptSerializer
|
from dvadmin.system.views.dept import DeptSerializer
|
||||||
from dvadmin.system.views.menu import MenuSerializer
|
from dvadmin.system.views.menu import MenuSerializer
|
||||||
from dvadmin.system.views.menu_button import MenuButtonSerializer
|
|
||||||
from dvadmin.utils.crud_mixin import FastCrudMixin
|
from dvadmin.utils.crud_mixin import FastCrudMixin
|
||||||
|
from dvadmin.utils.field_permission import FieldPermissionMixin
|
||||||
from dvadmin.utils.json_response import SuccessResponse, DetailResponse
|
from dvadmin.utils.json_response import SuccessResponse, DetailResponse
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.validator import CustomUniqueValidator
|
from dvadmin.utils.validator import CustomUniqueValidator
|
||||||
@@ -32,16 +32,12 @@ class RoleSerializer(CustomModelSerializer):
|
|||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RoleCreateUpdateSerializer(CustomModelSerializer):
|
class RoleCreateUpdateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
角色管理 创建/更新时的列化器
|
角色管理 创建/更新时的列化器
|
||||||
"""
|
"""
|
||||||
menu = MenuSerializer(many=True, read_only=True)
|
menu = MenuSerializer(many=True, read_only=True)
|
||||||
dept = DeptSerializer(many=True, read_only=True)
|
dept = DeptSerializer(many=True, read_only=True)
|
||||||
permission = MenuButtonSerializer(many=True, read_only=True)
|
|
||||||
key = serializers.CharField(max_length=50,
|
key = serializers.CharField(max_length=50,
|
||||||
validators=[CustomUniqueValidator(queryset=Role.objects.all(), message="权限字符必须唯一")])
|
validators=[CustomUniqueValidator(queryset=Role.objects.all(), message="权限字符必须唯一")])
|
||||||
name = serializers.CharField(max_length=50, validators=[CustomUniqueValidator(queryset=Role.objects.all())])
|
name = serializers.CharField(max_length=50, validators=[CustomUniqueValidator(queryset=Role.objects.all())])
|
||||||
@@ -61,28 +57,40 @@ class RoleCreateUpdateSerializer(CustomModelSerializer):
|
|||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class MenuPermissonSerializer(CustomModelSerializer):
|
class MenuPermissionSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
菜单的按钮权限
|
菜单的按钮权限
|
||||||
"""
|
"""
|
||||||
menuPermission = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
def get_menuPermission(self, instance):
|
|
||||||
is_superuser = self.request.user.is_superuser
|
|
||||||
if is_superuser:
|
|
||||||
queryset = MenuButton.objects.filter(menu__id=instance.id)
|
|
||||||
else:
|
|
||||||
menu_permission_id_list = self.request.user.role.values_list('permission',flat=True)
|
|
||||||
queryset = MenuButton.objects.filter(id__in=menu_permission_id_list,menu__id=instance.id)
|
|
||||||
serializer = MenuButtonSerializer(queryset,many=True, read_only=True)
|
|
||||||
return serializer.data
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Menu
|
model = Menu
|
||||||
fields = ['id', 'parent', 'name', 'menuPermission']
|
fields = ['id', 'parent', 'name']
|
||||||
|
|
||||||
|
|
||||||
class RoleViewSet(CustomModelViewSet,FastCrudMixin):
|
class MenuButtonPermissionSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
菜单和按钮权限
|
||||||
|
"""
|
||||||
|
isCheck = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_isCheck(self, instance):
|
||||||
|
is_superuser = self.request.user.is_superuser
|
||||||
|
if is_superuser:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return MenuButton.objects.filter(
|
||||||
|
menu__id=instance.id,
|
||||||
|
role__id__in=self.request.user.role.values_list('id', flat=True),
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Menu
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RoleViewSet(CustomModelViewSet, FastCrudMixin,FieldPermissionMixin):
|
||||||
"""
|
"""
|
||||||
角色管理接口
|
角色管理接口
|
||||||
list:查询
|
list:查询
|
||||||
|
|||||||
@@ -0,0 +1,245 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""
|
||||||
|
@author: 猿小天
|
||||||
|
@contact: QQ:1638245306
|
||||||
|
@Created on: 2021/6/3 003 0:30
|
||||||
|
@Remark: 接口权限管理
|
||||||
|
"""
|
||||||
|
from django.db.models import F, Subquery, OuterRef, Exists
|
||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
|
from dvadmin.system.models import RoleApiPermission, Menu, MenuButton, Dept, RoleMenuPermission, Columns
|
||||||
|
from dvadmin.system.views.menu import MenuSerializer
|
||||||
|
from dvadmin.utils.json_response import DetailResponse, ErrorResponse
|
||||||
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
|
|
||||||
|
|
||||||
|
class RoleApiPermissionSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
接口权限-序列化器
|
||||||
|
"""
|
||||||
|
dept_name = serializers.SerializerMethodField(help_text="部门名称")
|
||||||
|
|
||||||
|
def get_dept_name(self, instance):
|
||||||
|
dept_name_list = instance.dept.values_list("name",flat=True)
|
||||||
|
return ",".join(dept_name_list)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RoleApiPermission
|
||||||
|
fields = "__all__"
|
||||||
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RoleApiPermissionCreateUpdateSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
初始化接口权限-序列化器
|
||||||
|
"""
|
||||||
|
menu_button__name = serializers.CharField(source='menu_button.name', read_only=True)
|
||||||
|
menu_button__value = serializers.CharField(source='menu_button.value', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = RoleApiPermission
|
||||||
|
fields = "__all__"
|
||||||
|
read_only_fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
class RoleButtonPermissionSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
角色按钮权限
|
||||||
|
"""
|
||||||
|
isCheck = serializers.SerializerMethodField()
|
||||||
|
data_range = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_isCheck(self, instance):
|
||||||
|
params = self.request.query_params
|
||||||
|
return RoleApiPermission.objects.filter(
|
||||||
|
menu_button__id=instance['id'],
|
||||||
|
role__id=params.get('role'),
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
def get_data_range(self, instance):
|
||||||
|
params = self.request.query_params
|
||||||
|
obj = RoleApiPermission.objects.filter(
|
||||||
|
menu_button__id=instance['id'],
|
||||||
|
role__id=params.get('role'),
|
||||||
|
).first()
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
return obj.data_range
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MenuButton
|
||||||
|
fields = ['id','name','value','isCheck','data_range']
|
||||||
|
|
||||||
|
class RoleColumnsSerializer(CustomModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Columns
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class RoleMenuPermissionSerializer(CustomModelSerializer):
|
||||||
|
"""
|
||||||
|
菜单和按钮权限
|
||||||
|
"""
|
||||||
|
isCheck = serializers.SerializerMethodField()
|
||||||
|
btns = serializers.SerializerMethodField()
|
||||||
|
columns = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_isCheck(self, instance):
|
||||||
|
params = self.request.query_params
|
||||||
|
return RoleMenuPermission.objects.filter(
|
||||||
|
menu__id=instance['id'],
|
||||||
|
role__id=params.get('role'),
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
def get_btns(self, instance):
|
||||||
|
btn_list = MenuButton.objects.filter(menu__id=instance['id']).values('id', 'name', 'value')
|
||||||
|
serializer = RoleButtonPermissionSerializer(btn_list,many=True,request=self.request)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
def get_columns(self, instance):
|
||||||
|
params = self.request.query_params
|
||||||
|
col_list = Columns.objects.filter(role__id=params.get('role'))
|
||||||
|
serializer = RoleColumnsSerializer(col_list,many=True,request=self.request)
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Menu
|
||||||
|
fields = ['id','name','isCheck','btns','columns']
|
||||||
|
|
||||||
|
class RoleApiPermissionViewSet(CustomModelViewSet):
|
||||||
|
"""
|
||||||
|
接口权限接口
|
||||||
|
list:查询
|
||||||
|
create:新增
|
||||||
|
update:修改
|
||||||
|
retrieve:单例
|
||||||
|
destroy:删除
|
||||||
|
"""
|
||||||
|
queryset = RoleApiPermission.objects.all()
|
||||||
|
serializer_class = RoleApiPermissionSerializer
|
||||||
|
create_serializer_class = RoleApiPermissionCreateUpdateSerializer
|
||||||
|
update_serializer_class = RoleApiPermissionCreateUpdateSerializer
|
||||||
|
extra_filter_class = []
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def data_scope(self, request):
|
||||||
|
"""
|
||||||
|
获取数据权限范围:角色授权页面使用
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
is_superuser = request.user.is_superuser
|
||||||
|
if is_superuser:
|
||||||
|
data = [
|
||||||
|
{
|
||||||
|
"value": 0,
|
||||||
|
"label": '仅本人数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 1,
|
||||||
|
"label": '本部门及以下数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 2,
|
||||||
|
"label": '本部门数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 3,
|
||||||
|
"label": '全部数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 4,
|
||||||
|
"label": '自定义数据权限'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
role_list = request.user.role.values_list('id', flat=True)
|
||||||
|
if params := request.query_params:
|
||||||
|
if menu_button_id := params.get('menu_button', None):
|
||||||
|
role_queryset = RoleApiPermission.objects.filter(
|
||||||
|
role__in=role_list, menu_button__id=menu_button_id
|
||||||
|
).values_list('data_range', flat=True)
|
||||||
|
data_range_list = list(set(role_queryset))
|
||||||
|
for item in data_range_list:
|
||||||
|
if item == 0:
|
||||||
|
data = [{
|
||||||
|
"value": 0,
|
||||||
|
"label": '仅本人数据权限'
|
||||||
|
}]
|
||||||
|
elif item == 1:
|
||||||
|
data = [{
|
||||||
|
"value": 0,
|
||||||
|
"label": '仅本人数据权限'
|
||||||
|
}, {
|
||||||
|
"value": 1,
|
||||||
|
"label": '本部门及以下数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 2,
|
||||||
|
"label": '本部门数据权限'
|
||||||
|
}]
|
||||||
|
elif item == 2:
|
||||||
|
data = [{
|
||||||
|
"value": 0,
|
||||||
|
"label": '仅本人数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 2,
|
||||||
|
"label": '本部门数据权限'
|
||||||
|
}]
|
||||||
|
elif item == 3:
|
||||||
|
data = [{
|
||||||
|
"value": 0,
|
||||||
|
"label": '仅本人数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 3,
|
||||||
|
"label": '全部数据权限'
|
||||||
|
}, ]
|
||||||
|
elif item == 4:
|
||||||
|
data = [{
|
||||||
|
"value": 0,
|
||||||
|
"label": '仅本人数据权限'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": 4,
|
||||||
|
"label": '自定义数据权限'
|
||||||
|
}]
|
||||||
|
else:
|
||||||
|
data = []
|
||||||
|
return DetailResponse(data=data)
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
|
||||||
|
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
|
||||||
|
def role_to_dept_all(self, request):
|
||||||
|
"""
|
||||||
|
当前用户角色下所能授权的部门:角色授权页面使用
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
params = request.query_params
|
||||||
|
is_superuser = request.user.is_superuser
|
||||||
|
if is_superuser:
|
||||||
|
queryset = Dept.objects.values('id', 'name', 'parent')
|
||||||
|
else:
|
||||||
|
if not params:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
menu_button = params.get('menu_button')
|
||||||
|
if menu_button is None:
|
||||||
|
return ErrorResponse(msg="参数错误")
|
||||||
|
role_list = request.user.role.values_list('id', flat=True)
|
||||||
|
dept_ids = RoleApiPermission.objects.filter(role__in=role_list).values_list('dept__id',flat=True)
|
||||||
|
queryset = Dept.objects.filter(id__in=dept_ids).values('id', 'name', 'parent')
|
||||||
|
return DetailResponse(data=queryset)
|
||||||
@@ -58,8 +58,38 @@ class RoleMenuPermissionViewSet(CustomModelViewSet):
|
|||||||
update_serializer_class = RoleMenuPermissionCreateUpdateSerializer
|
update_serializer_class = RoleMenuPermissionCreateUpdateSerializer
|
||||||
extra_filter_class = []
|
extra_filter_class = []
|
||||||
|
|
||||||
|
@action(methods=['get'],detail=False)
|
||||||
|
def menu_permission_tree(self,request):
|
||||||
|
"""
|
||||||
|
获取菜单按钮树
|
||||||
|
"""
|
||||||
|
# params = request.query_params
|
||||||
|
# role_id = params.get('role',None)
|
||||||
|
# if role_id is None:
|
||||||
|
# return ErrorResponse(msg="未获取到角色")
|
||||||
|
if request.user.is_superuser:
|
||||||
|
queryset = Menu.objects.filter(status=1).values("id", "name", "parent_id")
|
||||||
|
else:
|
||||||
|
role_id = request.user.role.values_list('id', flat=True)
|
||||||
|
menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('menu_id', flat=True)
|
||||||
|
queryset = Menu.objects.filter(status=1, id__in=menu_list).values('id','name', "parent_id").all()
|
||||||
|
return DetailResponse(data=queryset)
|
||||||
|
|
||||||
|
@action(methods=['get'],detail=False)
|
||||||
|
def get_menu_permission_checked(self,request):
|
||||||
|
"""
|
||||||
|
获取已授权的菜单ID
|
||||||
|
"""
|
||||||
|
params = request.query_params
|
||||||
|
role_id = params.get('role',None)
|
||||||
|
if role_id is None:
|
||||||
|
return ErrorResponse(msg="未获取到角色")
|
||||||
|
menu_list = RoleMenuPermission.objects.filter(role__in=role_id).values_list('menu_id', flat=True)
|
||||||
|
queryset = Menu.objects.filter(status=1, id__in=menu_list).values_list('id',flat=True)
|
||||||
|
return DetailResponse(data=queryset)
|
||||||
|
|
||||||
@action(methods=['post'],detail=False)
|
@action(methods=['post'],detail=False)
|
||||||
def save_auth(self,request):
|
def save_menu_permission(self,request):
|
||||||
"""
|
"""
|
||||||
保存页面菜单授权
|
保存页面菜单授权
|
||||||
:param request:
|
:param request:
|
||||||
@@ -72,7 +102,14 @@ class RoleMenuPermissionViewSet(CustomModelViewSet):
|
|||||||
menu_list = body.get('menu',None)
|
menu_list = body.get('menu',None)
|
||||||
if menu_list is None:
|
if menu_list is None:
|
||||||
return ErrorResponse(msg="未获取到菜单参数")
|
return ErrorResponse(msg="未获取到菜单参数")
|
||||||
data = [{"role":role_id,"menu":item} for item in menu_list]
|
obj_list = RoleMenuPermission.objects.filter(role__id=role_id).values_list('menu__id',flat=True)
|
||||||
|
old_set = set(obj_list)
|
||||||
|
new_set = set(menu_list)
|
||||||
|
#need_update = old_set.intersection(new_set) # 需要更新的
|
||||||
|
need_del = old_set.difference(new_set) # 需要删除的
|
||||||
|
need_add = new_set.difference(old_set) # 需要新增的
|
||||||
|
RoleMenuPermission.objects.filter(role__id=role_id,menu__in=list(need_del)).delete()
|
||||||
|
data = [{"role": role_id, "menu": item} for item in list(need_add)]
|
||||||
serializer = RoleMenuPermissionSerializer(data=data,many=True,request=request)
|
serializer = RoleMenuPermissionSerializer(data=data,many=True,request=request)
|
||||||
if serializer.is_valid(raise_exception=True):
|
if serializer.is_valid(raise_exception=True):
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
"""
|
|
||||||
@author: 猿小天
|
|
||||||
@contact: QQ:1638245306
|
|
||||||
@Created on: 2021/6/3 003 0:30
|
|
||||||
@Remark: 菜单按钮管理
|
|
||||||
"""
|
|
||||||
from django.db.models import F
|
|
||||||
from rest_framework.decorators import action
|
|
||||||
from rest_framework.permissions import IsAuthenticated
|
|
||||||
|
|
||||||
from dvadmin.system.models import RoleMenuButtonPermission, Menu, MenuButton, Dept
|
|
||||||
from dvadmin.utils.json_response import DetailResponse, ErrorResponse
|
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
|
||||||
|
|
||||||
|
|
||||||
class RoleMenuButtonPermissionSerializer(CustomModelSerializer):
|
|
||||||
"""
|
|
||||||
菜单按钮-序列化器
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RoleMenuButtonPermission
|
|
||||||
fields = "__all__"
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
|
|
||||||
class RoleMenuButtonPermissionInitSerializer(CustomModelSerializer):
|
|
||||||
"""
|
|
||||||
初始化菜单按钮-序列化器
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RoleMenuButtonPermission
|
|
||||||
fields = "__all__"
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
class RoleMenuButtonPermissionCreateUpdateSerializer(CustomModelSerializer):
|
|
||||||
"""
|
|
||||||
初始化菜单按钮-序列化器
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = RoleMenuButtonPermission
|
|
||||||
fields = "__all__"
|
|
||||||
read_only_fields = ["id"]
|
|
||||||
|
|
||||||
|
|
||||||
class RoleMenuButtonPermissionViewSet(CustomModelViewSet):
|
|
||||||
"""
|
|
||||||
菜单按钮接口
|
|
||||||
list:查询
|
|
||||||
create:新增
|
|
||||||
update:修改
|
|
||||||
retrieve:单例
|
|
||||||
destroy:删除
|
|
||||||
"""
|
|
||||||
queryset = RoleMenuButtonPermission.objects.all()
|
|
||||||
serializer_class = RoleMenuButtonPermissionSerializer
|
|
||||||
create_serializer_class = RoleMenuButtonPermissionCreateUpdateSerializer
|
|
||||||
update_serializer_class = RoleMenuButtonPermissionCreateUpdateSerializer
|
|
||||||
extra_filter_class = []
|
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
|
||||||
def role_get_menu(self, request):
|
|
||||||
"""根据当前用户的角色返回角色拥有的菜单"""
|
|
||||||
is_superuser = request.user.is_superuser
|
|
||||||
is_admin = request.user.role.values_list('admin', flat=True)
|
|
||||||
if is_superuser or True in is_admin:
|
|
||||||
queryset = Menu.objects.filter(status=1).values('id','name','parent','is_catalog')
|
|
||||||
else:
|
|
||||||
role_id = request.user.role.values_list('id',flat=True)
|
|
||||||
queryset = RoleMenuButtonPermission.objects.filter(role__in=role_id).values(id=F('menu__id'),name=F('menu__name'),parent=F('menu__parent'),is_catalog=F('menu__is_catalog'))
|
|
||||||
return DetailResponse(data=queryset)
|
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
|
||||||
def role_menu_get_button(self,request):
|
|
||||||
"""
|
|
||||||
当前用户角色和菜单获取可下拉选项的按钮:角色授权页面使用
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
params = request.query_params
|
|
||||||
if params:
|
|
||||||
menu_id = params.get('menu',None)
|
|
||||||
if menu_id:
|
|
||||||
is_superuser = request.user.is_superuser
|
|
||||||
is_admin = request.user.role.values_list('admin', flat=True)
|
|
||||||
if is_superuser or True in is_admin:
|
|
||||||
queryset = MenuButton.objects.filter(menu=menu_id).values('id', 'name')
|
|
||||||
else:
|
|
||||||
role_list = request.user.role.values_list('id',flat=True)
|
|
||||||
queryset = RoleMenuButtonPermission.objects.filter(role_in=role_list,menu_button__menu=menu_id).values(
|
|
||||||
id=F('menu_button__id'),
|
|
||||||
name=F('menu_button__name')
|
|
||||||
)
|
|
||||||
return DetailResponse(data=queryset)
|
|
||||||
return ErrorResponse(msg="参数错误")
|
|
||||||
|
|
||||||
@action(methods=['GET'], detail=False, permission_classes=[IsAuthenticated])
|
|
||||||
def data_scope(self, request):
|
|
||||||
"""
|
|
||||||
获取数据权限范围:角色授权页面使用
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
is_superuser = request.user.is_superuser
|
|
||||||
if is_superuser:
|
|
||||||
data = [
|
|
||||||
{
|
|
||||||
"value": 0,
|
|
||||||
"label": '仅本人数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 1,
|
|
||||||
"label": '本部门及以下数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 2,
|
|
||||||
"label": '本部门数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 3,
|
|
||||||
"label": '全部数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 4,
|
|
||||||
"label": '自定义数据权限'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
return DetailResponse(data=data)
|
|
||||||
else:
|
|
||||||
data = []
|
|
||||||
role_id = request.user.role.id
|
|
||||||
params = request.query_params
|
|
||||||
if params:
|
|
||||||
menu_button_id = params.get('menu_button', None)
|
|
||||||
if menu_button_id:
|
|
||||||
role_queryset = RoleMenuButtonPermission.objects.filter(role=role_id,menu_button=menu_button_id).values_list('data_range',flat=True)
|
|
||||||
data_range_list = list(set(role_queryset))
|
|
||||||
for item in data_range_list:
|
|
||||||
if item == 0:
|
|
||||||
data = [{
|
|
||||||
"value": 0,
|
|
||||||
"label": '仅本人数据权限'
|
|
||||||
}]
|
|
||||||
elif item == 1:
|
|
||||||
data = [{
|
|
||||||
"value": 0,
|
|
||||||
"label": '仅本人数据权限'
|
|
||||||
}, {
|
|
||||||
"value": 1,
|
|
||||||
"label": '本部门及以下数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 2,
|
|
||||||
"label": '本部门数据权限'
|
|
||||||
}]
|
|
||||||
elif item == 2:
|
|
||||||
data = [{
|
|
||||||
"value": 0,
|
|
||||||
"label": '仅本人数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 2,
|
|
||||||
"label": '本部门数据权限'
|
|
||||||
}]
|
|
||||||
elif item == 3:
|
|
||||||
data = [{
|
|
||||||
"value": 0,
|
|
||||||
"label": '仅本人数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 3,
|
|
||||||
"label": '全部数据权限'
|
|
||||||
}, ]
|
|
||||||
elif item == 4:
|
|
||||||
data = [{
|
|
||||||
"value": 0,
|
|
||||||
"label": '仅本人数据权限'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"value": 4,
|
|
||||||
"label": '自定义数据权限'
|
|
||||||
}]
|
|
||||||
else:
|
|
||||||
data = []
|
|
||||||
return DetailResponse(data=data)
|
|
||||||
return ErrorResponse(msg="参数错误")
|
|
||||||
|
|
||||||
@action(methods=['get'], detail=False, permission_classes=[IsAuthenticated])
|
|
||||||
def role_to_dept_all(self, request):
|
|
||||||
"""
|
|
||||||
当前用户角色下所能授权的部门:角色授权页面使用
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
params = request.query_params
|
|
||||||
is_superuser = request.user.is_superuser
|
|
||||||
is_admin = request.user.role.values_list('admin', flat=True)
|
|
||||||
if is_superuser or True in is_admin:
|
|
||||||
queryset = Dept.objects.values('id','name','parent')
|
|
||||||
return DetailResponse(data=queryset)
|
|
||||||
else:
|
|
||||||
if params:
|
|
||||||
menu_button = params.get('menu_button')
|
|
||||||
if menu_button is None:
|
|
||||||
return ErrorResponse(msg="参数错误")
|
|
||||||
role_list = request.user.role.values_list('id', flat=True)
|
|
||||||
queryset = RoleMenuButtonPermission.objects.filter(role_in=role_list,menu_button=None).values(
|
|
||||||
id=F('dept__id'),
|
|
||||||
name=F('dept__name'),
|
|
||||||
parent=F('dept__parent')
|
|
||||||
)
|
|
||||||
return DetailResponse(data=queryset)
|
|
||||||
else:
|
|
||||||
return ErrorResponse(msg="参数错误")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@action(methods=['get'],detail=False,permission_classes=[IsAuthenticated])
|
|
||||||
def menu_to_button(self,request):
|
|
||||||
"""
|
|
||||||
根据所选择菜单获取已配置的按钮/接口权限:角色授权页面使用
|
|
||||||
:param request:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
params = request.query_params
|
|
||||||
if params:
|
|
||||||
menu_id = params.get('menu',None)
|
|
||||||
if menu_id is None:
|
|
||||||
return ErrorResponse(msg="未获取到参数")
|
|
||||||
role_id = params.get('role', None)
|
|
||||||
if role_id is None:
|
|
||||||
return ErrorResponse(msg="未获取到参数")
|
|
||||||
queryset = RoleMenuButtonPermission.objects.filter(role=role_id,menu_button__menu=menu_id).values(
|
|
||||||
'id',
|
|
||||||
'data_range',
|
|
||||||
'menu_button'
|
|
||||||
)
|
|
||||||
return DetailResponse(data=queryset)
|
|
||||||
return ErrorResponse(msg="未获取到参数")
|
|
||||||
@@ -61,10 +61,10 @@ class SystemConfigChinldernSerializer(CustomModelSerializer):
|
|||||||
"""
|
"""
|
||||||
系统配置子级-序列化器
|
系统配置子级-序列化器
|
||||||
"""
|
"""
|
||||||
chinldern = serializers.SerializerMethodField()
|
children = serializers.SerializerMethodField()
|
||||||
form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True)
|
form_item_type_label = serializers.CharField(source='get_form_item_type_display', read_only=True)
|
||||||
|
|
||||||
def get_chinldern(self, instance):
|
def get_children(self, instance):
|
||||||
queryset = SystemConfig.objects.filter(parent=instance)
|
queryset = SystemConfig.objects.filter(parent=instance)
|
||||||
serializer = SystemConfigSerializer(queryset, many=True)
|
serializer = SystemConfigSerializer(queryset, many=True)
|
||||||
return serializer.data
|
return serializer.data
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from django.contrib.auth.hashers import make_password
|
from django.contrib.auth.hashers import make_password, check_password
|
||||||
from django_restql.fields import DynamicSerializerMethodField
|
from django_restql.fields import DynamicSerializerMethodField
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from rest_framework.decorators import action, permission_classes
|
from rest_framework.decorators import action
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
from django.db.models import Q
|
||||||
from application import dispatch
|
from application import dispatch
|
||||||
from dvadmin.system.models import Users, Role, Dept
|
from dvadmin.system.models import Users, Role, Dept
|
||||||
from dvadmin.system.views.role import RoleSerializer
|
from dvadmin.system.views.role import RoleSerializer
|
||||||
from dvadmin.utils.json_response import ErrorResponse, DetailResponse
|
from dvadmin.utils.json_response import ErrorResponse, DetailResponse, SuccessResponse
|
||||||
from dvadmin.utils.serializers import CustomModelSerializer
|
from dvadmin.utils.serializers import CustomModelSerializer
|
||||||
from dvadmin.utils.validator import CustomUniqueValidator
|
from dvadmin.utils.validator import CustomUniqueValidator
|
||||||
from dvadmin.utils.viewset import CustomModelViewSet
|
from dvadmin.utils.viewset import CustomModelViewSet
|
||||||
@@ -23,7 +24,7 @@ def recursion(instance, parent, result):
|
|||||||
res.append(data)
|
res.append(data)
|
||||||
if new_instance:
|
if new_instance:
|
||||||
array = recursion(new_instance, parent, result)
|
array = recursion(new_instance, parent, result)
|
||||||
res += (array)
|
res += array
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ class UserSerializer(CustomModelSerializer):
|
|||||||
exclude = ["password"]
|
exclude = ["password"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False},
|
"post": {"required": False},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_dept_name_all(self, instance):
|
def get_dept_name_all(self, instance):
|
||||||
@@ -60,9 +62,6 @@ class UserSerializer(CustomModelSerializer):
|
|||||||
return serializer.data
|
return serializer.data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserCreateSerializer(CustomModelSerializer):
|
class UserCreateSerializer(CustomModelSerializer):
|
||||||
"""
|
"""
|
||||||
用户新增-序列化器
|
用户新增-序列化器
|
||||||
@@ -82,10 +81,10 @@ class UserCreateSerializer(CustomModelSerializer):
|
|||||||
"""
|
"""
|
||||||
对密码进行验证
|
对密码进行验证
|
||||||
"""
|
"""
|
||||||
password = self.initial_data.get("password")
|
md5 = hashlib.md5()
|
||||||
if password:
|
md5.update(value.encode('utf-8'))
|
||||||
return make_password(value)
|
md5_password = md5.hexdigest()
|
||||||
return value
|
return make_password(md5_password)
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
data = super().save(**kwargs)
|
data = super().save(**kwargs)
|
||||||
@@ -100,6 +99,7 @@ class UserCreateSerializer(CustomModelSerializer):
|
|||||||
read_only_fields = ["id"]
|
read_only_fields = ["id"]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False},
|
"post": {"required": False},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -114,14 +114,15 @@ class UserUpdateSerializer(CustomModelSerializer):
|
|||||||
CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")
|
CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# password = serializers.CharField(required=False, allow_blank=True)
|
# password = serializers.CharField(required=False, allow_blank=True)
|
||||||
mobile = serializers.CharField(
|
# mobile = serializers.CharField(
|
||||||
max_length=50,
|
# max_length=50,
|
||||||
validators=[
|
# validators=[
|
||||||
CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
|
# CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
|
||||||
],
|
# ],
|
||||||
allow_blank=True
|
# allow_blank=True
|
||||||
)
|
# )
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
data = super().save(**kwargs)
|
data = super().save(**kwargs)
|
||||||
@@ -136,6 +137,7 @@ class UserUpdateSerializer(CustomModelSerializer):
|
|||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False, "read_only": True},
|
"post": {"required": False, "read_only": True},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -159,6 +161,7 @@ class UserInfoUpdateSerializer(CustomModelSerializer):
|
|||||||
fields = ['email', 'mobile', 'avatar', 'name', 'gender']
|
fields = ['email', 'mobile', 'avatar', 'name', 'gender']
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"post": {"required": False, "read_only": True},
|
"post": {"required": False, "read_only": True},
|
||||||
|
"mobile": {"required": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -194,10 +197,12 @@ class ExportUserProfileSerializer(CustomModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class UserProfileImportSerializer(CustomModelSerializer):
|
class UserProfileImportSerializer(CustomModelSerializer):
|
||||||
|
password = serializers.CharField(read_only=True, required=False)
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
data = super().save(**kwargs)
|
data = super().save(**kwargs)
|
||||||
password = hashlib.new(
|
password = hashlib.new(
|
||||||
"md5", str(self.initial_data.get("password", "")).encode(encoding="UTF-8")
|
"md5", str(self.initial_data.get("password", "admin123456")).encode(encoding="UTF-8")
|
||||||
).hexdigest()
|
).hexdigest()
|
||||||
data.set_password(password)
|
data.set_password(password)
|
||||||
data.save()
|
data.save()
|
||||||
@@ -229,7 +234,7 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
create_serializer_class = UserCreateSerializer
|
create_serializer_class = UserCreateSerializer
|
||||||
update_serializer_class = UserUpdateSerializer
|
update_serializer_class = UserUpdateSerializer
|
||||||
filter_fields = ["name", "username", "gender", "is_active", "dept", "user_type"]
|
filter_fields = ["name", "username", "gender", "is_active", "dept", "user_type"]
|
||||||
search_fields = ["username", "name", "gender", "dept__name", "role__name"]
|
search_fields = ["username", "name", "dept__name", "role__name"]
|
||||||
# 导出
|
# 导出
|
||||||
export_field_label = {
|
export_field_label = {
|
||||||
"username": "用户账号",
|
"username": "用户账号",
|
||||||
@@ -262,7 +267,6 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
"data": {"启用": True, "禁用": False},
|
"data": {"启用": True, "禁用": False},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"password": "登录密码",
|
|
||||||
"dept": {"title": "部门", "choices": {"queryset": Dept.objects.filter(status=True), "values_name": "name"}},
|
"dept": {"title": "部门", "choices": {"queryset": Dept.objects.filter(status=True), "values_name": "name"}},
|
||||||
"role": {"title": "角色", "choices": {"queryset": Role.objects.filter(status=True), "values_name": "name"}},
|
"role": {"title": "角色", "choices": {"queryset": Role.objects.filter(status=True), "values_name": "name"}},
|
||||||
}
|
}
|
||||||
@@ -293,6 +297,11 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
'dept_id': dept.id,
|
'dept_id': dept.id,
|
||||||
'dept_name': dept.name
|
'dept_name': dept.name
|
||||||
}
|
}
|
||||||
|
else:
|
||||||
|
result['dept_info'] = {
|
||||||
|
'dept_id': None,
|
||||||
|
'dept_name': "暂无部门"
|
||||||
|
}
|
||||||
role = getattr(user, 'role', None)
|
role = getattr(user, 'role', None)
|
||||||
if role:
|
if role:
|
||||||
result['role_info'] = role.values('id', 'name', 'key')
|
result['role_info'] = role.values('id', 'name', 'key')
|
||||||
@@ -317,11 +326,10 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
return ErrorResponse(msg="参数不能为空")
|
return ErrorResponse(msg="参数不能为空")
|
||||||
if new_pwd != new_pwd2:
|
if new_pwd != new_pwd2:
|
||||||
return ErrorResponse(msg="两次密码不匹配")
|
return ErrorResponse(msg="两次密码不匹配")
|
||||||
check_password = request.user.check_password(old_pwd)
|
verify_password = check_password(old_pwd, self.request.user.password)
|
||||||
if not check_password:
|
if not verify_password:
|
||||||
check_password = request.user.check_password(hashlib.md5(old_pwd.encode(encoding='UTF-8')).hexdigest())
|
verify_password = check_password(hashlib.md5(old_pwd.encode(encoding='UTF-8')).hexdigest(), self.request.user.password)
|
||||||
if check_password:
|
if verify_password:
|
||||||
new_pwd = hashlib.md5(new_pwd.encode(encoding='UTF-8')).hexdigest()
|
|
||||||
request.user.password = make_password(new_pwd)
|
request.user.password = make_password(new_pwd)
|
||||||
request.user.save()
|
request.user.save()
|
||||||
return DetailResponse(data=None, msg="修改成功")
|
return DetailResponse(data=None, msg="修改成功")
|
||||||
@@ -344,6 +352,8 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
"""
|
"""
|
||||||
密码重置
|
密码重置
|
||||||
"""
|
"""
|
||||||
|
if not self.request.user.is_superuser:
|
||||||
|
return ErrorResponse(msg="只允许超级管理员对其进行密码重置")
|
||||||
instance = Users.objects.filter(id=pk).first()
|
instance = Users.objects.filter(id=pk).first()
|
||||||
data = request.data
|
data = request.data
|
||||||
new_pwd = data.get("newPassword")
|
new_pwd = data.get("newPassword")
|
||||||
@@ -357,3 +367,43 @@ class UserViewSet(CustomModelViewSet):
|
|||||||
return DetailResponse(data=None, msg="修改成功")
|
return DetailResponse(data=None, msg="修改成功")
|
||||||
else:
|
else:
|
||||||
return ErrorResponse(msg="未获取到用户")
|
return ErrorResponse(msg="未获取到用户")
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
dept_id = request.query_params.get('dept')
|
||||||
|
show_all = request.query_params.get('show_all')
|
||||||
|
if not dept_id:
|
||||||
|
dept_id = ''
|
||||||
|
if not show_all:
|
||||||
|
show_all = 0
|
||||||
|
if int(show_all):
|
||||||
|
all_did = [dept_id]
|
||||||
|
def inner(did):
|
||||||
|
sub = Dept.objects.filter(parent_id=did)
|
||||||
|
if not sub.exists():
|
||||||
|
return
|
||||||
|
for i in sub:
|
||||||
|
all_did.append(i.pk)
|
||||||
|
inner(i)
|
||||||
|
if dept_id != '':
|
||||||
|
inner(dept_id)
|
||||||
|
searchs = [
|
||||||
|
Q(**{f+'__icontains':i})
|
||||||
|
for f in self.search_fields
|
||||||
|
] if (i:=request.query_params.get('search')) else []
|
||||||
|
q_obj = []
|
||||||
|
if searchs:
|
||||||
|
q = searchs[0]
|
||||||
|
for i in searchs[1:]:
|
||||||
|
q |= i
|
||||||
|
q_obj.append(Q(q))
|
||||||
|
queryset = Users.objects.filter(*q_obj, dept_id__in=all_did)
|
||||||
|
else:
|
||||||
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
else:
|
||||||
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
page = self.paginate_queryset(queryset)
|
||||||
|
if page is not None:
|
||||||
|
serializer = self.get_serializer(page, many=True, request=request)
|
||||||
|
return self.get_paginated_response(serializer.data)
|
||||||
|
serializer = self.get_serializer(queryset, many=True, request=request)
|
||||||
|
return SuccessResponse(data=serializer.data, msg="获取成功")
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import logging
|
|||||||
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
|
from django.contrib.auth.hashers import check_password
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from dvadmin.utils.validator import CustomValidationError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
UserModel = get_user_model()
|
UserModel = get_user_model()
|
||||||
|
|
||||||
@@ -24,10 +27,13 @@ class CustomBackend(ModelBackend):
|
|||||||
except UserModel.DoesNotExist:
|
except UserModel.DoesNotExist:
|
||||||
UserModel().set_password(password)
|
UserModel().set_password(password)
|
||||||
else:
|
else:
|
||||||
check_password = user.check_password(password)
|
verify_password = check_password(password, user.password)
|
||||||
if not check_password:
|
if not verify_password:
|
||||||
check_password = user.check_password(hashlib.md5(password.encode(encoding='UTF-8')).hexdigest())
|
password = hashlib.md5(password.encode(encoding='UTF-8')).hexdigest()
|
||||||
if check_password and self.user_can_authenticate(user):
|
verify_password = check_password(password, user.password)
|
||||||
user.last_login = timezone.now()
|
if verify_password:
|
||||||
user.save()
|
if self.user_can_authenticate(user):
|
||||||
return user
|
user.last_login = timezone.now()
|
||||||
|
user.save()
|
||||||
|
return user
|
||||||
|
raise CustomValidationError("当前用户已被禁用,请联系管理员!")
|
||||||
|
|||||||
@@ -38,12 +38,16 @@ def CustomExceptionHandler(ex, context):
|
|||||||
# 调用默认的异常处理函数
|
# 调用默认的异常处理函数
|
||||||
response = exception_handler(ex, context)
|
response = exception_handler(ex, context)
|
||||||
if isinstance(ex, AuthenticationFailed):
|
if isinstance(ex, AuthenticationFailed):
|
||||||
code = 401
|
# 如果是身份验证错误
|
||||||
code_type = response.data.get('detail').code
|
if response and response.data.get('detail') == "Given token not valid for any token type":
|
||||||
if code_type == 'no_active_account':
|
code = 401
|
||||||
code=400
|
msg = ex.detail
|
||||||
|
elif response and response.data.get('detail') == "Token is blacklisted":
|
||||||
|
# token在黑名单
|
||||||
return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
|
return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
|
||||||
msg = ex.detail
|
else:
|
||||||
|
code = 401
|
||||||
|
msg = ex.detail
|
||||||
elif isinstance(ex,Http404):
|
elif isinstance(ex,Http404):
|
||||||
code = 400
|
code = 400
|
||||||
msg = "接口地址不正确"
|
msg = "接口地址不正确"
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
|
||||||
|
from dvadmin.system.models import Columns
|
||||||
|
from dvadmin.utils.json_response import DetailResponse
|
||||||
|
from dvadmin.utils.models import get_custom_app_models
|
||||||
|
|
||||||
|
|
||||||
|
class FieldPermissionMixin:
|
||||||
|
@action(methods=['get'], detail=False,permission_classes=[IsAuthenticated])
|
||||||
|
def field_permission(self, request):
|
||||||
|
"""
|
||||||
|
获取字段权限
|
||||||
|
"""
|
||||||
|
finded = False
|
||||||
|
for app in get_custom_app_models():
|
||||||
|
for model in app:
|
||||||
|
if model['object'] is self.serializer_class.Meta.model:
|
||||||
|
finded = True
|
||||||
|
break
|
||||||
|
if finded:
|
||||||
|
break
|
||||||
|
if finded is False:
|
||||||
|
return []
|
||||||
|
roles = request.user.role.values_list('id', flat=True)
|
||||||
|
user = request.user
|
||||||
|
if user.is_superuser==1:
|
||||||
|
data = Columns.objects.filter(app=model['app'], model=model['model']).values('field_name', 'is_create', 'is_query', 'is_update')
|
||||||
|
for item in data:
|
||||||
|
item['is_create'] = True
|
||||||
|
item['is_query'] = True
|
||||||
|
item['is_update'] = True
|
||||||
|
else:
|
||||||
|
data= Columns.objects.filter(
|
||||||
|
app=model['app'], model=model['model'],role__in=roles
|
||||||
|
).values('field_name', 'is_create', 'is_query', 'is_update')
|
||||||
|
return DetailResponse(data=data)
|
||||||
@@ -20,7 +20,7 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||||||
from django_filters.utils import get_model_field
|
from django_filters.utils import get_model_field
|
||||||
from rest_framework.filters import BaseFilterBackend
|
from rest_framework.filters import BaseFilterBackend
|
||||||
|
|
||||||
from dvadmin.system.models import Dept, ApiWhiteList, RoleMenuButtonPermission
|
from dvadmin.system.models import Dept, ApiWhiteList, RoleApiPermission
|
||||||
|
|
||||||
|
|
||||||
def get_dept(dept_id: int, dept_all_list=None, dept_list=None):
|
def get_dept(dept_id: int, dept_all_list=None, dept_list=None):
|
||||||
@@ -75,7 +75,7 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
|
|||||||
if item.get("permission__api")
|
if item.get("permission__api")
|
||||||
]
|
]
|
||||||
for item in api_white_list:
|
for item in api_white_list:
|
||||||
new_api = api + ":" + str(method)
|
new_api = f"{api}:{method}"
|
||||||
matchObj = re.match(item, new_api, re.M | re.I)
|
matchObj = re.match(item, new_api, re.M | re.I)
|
||||||
if matchObj is None:
|
if matchObj is None:
|
||||||
continue
|
continue
|
||||||
@@ -86,74 +86,79 @@ class DataLevelPermissionsFilter(BaseFilterBackend):
|
|||||||
如果不是超级管理员,则进入下一步权限判断
|
如果不是超级管理员,则进入下一步权限判断
|
||||||
"""
|
"""
|
||||||
if request.user.is_superuser == 0:
|
if request.user.is_superuser == 0:
|
||||||
# 0. 获取用户的部门id,没有部门则返回空
|
return self._extracted_from_filter_queryset_33(request, queryset, api, method)
|
||||||
user_dept_id = getattr(request.user, "dept_id", None)
|
|
||||||
if not user_dept_id:
|
|
||||||
return queryset.none()
|
|
||||||
|
|
||||||
# 1. 判断过滤的数据是否有创建人所在部门 "dept_belong_id" 字段
|
|
||||||
if not getattr(queryset.model, "dept_belong_id", None):
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
# 2. 如果用户没有关联角色则返回本部门数据
|
|
||||||
if not hasattr(request.user, "role"):
|
|
||||||
return queryset.filter(dept_belong_id=user_dept_id)
|
|
||||||
|
|
||||||
# 3. 根据所有角色 获取所有权限范围
|
|
||||||
# (0, "仅本人数据权限"),
|
|
||||||
# (1, "本部门及以下数据权限"),
|
|
||||||
# (2, "本部门数据权限"),
|
|
||||||
# (3, "全部数据权限"),
|
|
||||||
# (4, "自定数据权限")
|
|
||||||
replace_str = re.compile('\d')
|
|
||||||
re_api = replace_str.sub('{id}', api)
|
|
||||||
role_id_list = request.user.role.values_list('id', flat=True)
|
|
||||||
role_permission_list=RoleMenuButtonPermission.objects.filter(
|
|
||||||
role__in=role_id_list,
|
|
||||||
role__status=1,
|
|
||||||
menu_button__api=re_api,
|
|
||||||
menu_button__method=method).values(
|
|
||||||
'data_range',
|
|
||||||
role_admin=F('role__admin')
|
|
||||||
)
|
|
||||||
dataScope_list = [] # 权限范围列表
|
|
||||||
for ele in role_permission_list:
|
|
||||||
# 判断用户是否为超级管理员角色/如果拥有[全部数据权限]则返回所有数据
|
|
||||||
if 3 == ele.get("data_range") or ele.get("role_admin") == True:
|
|
||||||
return queryset
|
|
||||||
dataScope_list.append(ele.get("data_range"))
|
|
||||||
dataScope_list = list(set(dataScope_list))
|
|
||||||
|
|
||||||
# 4. 只为仅本人数据权限时只返回过滤本人数据,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据)
|
|
||||||
if 0 in dataScope_list:
|
|
||||||
return queryset.filter(
|
|
||||||
creator=request.user, dept_belong_id=user_dept_id
|
|
||||||
)
|
|
||||||
|
|
||||||
# 5. 自定数据权限 获取部门,根据部门过滤
|
|
||||||
dept_list = []
|
|
||||||
for ele in dataScope_list:
|
|
||||||
if ele == 4:
|
|
||||||
dept_list.extend(
|
|
||||||
request.user.role.filter(status=1).values_list(
|
|
||||||
"dept__id", flat=True
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif ele == 2:
|
|
||||||
dept_list.append(user_dept_id)
|
|
||||||
elif ele == 1:
|
|
||||||
dept_list.append(user_dept_id)
|
|
||||||
dept_list.extend(
|
|
||||||
get_dept(
|
|
||||||
user_dept_id,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if queryset.model._meta.model_name == 'dept':
|
|
||||||
return queryset.filter(id__in=list(set(dept_list)))
|
|
||||||
return queryset.filter(dept_belong_id__in=list(set(dept_list)))
|
|
||||||
else:
|
else:
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
# TODO Rename this here and in `filter_queryset`
|
||||||
|
def _extracted_from_filter_queryset_33(self, request, queryset, api, method):
|
||||||
|
# 0. 获取用户的部门id,没有部门则返回空
|
||||||
|
user_dept_id = getattr(request.user, "dept_id", None)
|
||||||
|
if not user_dept_id:
|
||||||
|
return queryset.none()
|
||||||
|
|
||||||
|
# 1. 判断过滤的数据是否有创建人所在部门 "dept_belong_id" 字段
|
||||||
|
if not getattr(queryset.model, "dept_belong_id", None):
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
# 2. 如果用户没有关联角色则返回本部门数据
|
||||||
|
if not hasattr(request.user, "role"):
|
||||||
|
return queryset.filter(dept_belong_id=user_dept_id)
|
||||||
|
|
||||||
|
# 3. 根据所有角色 获取所有权限范围
|
||||||
|
# (0, "仅本人数据权限"),
|
||||||
|
# (1, "本部门及以下数据权限"),
|
||||||
|
# (2, "本部门数据权限"),
|
||||||
|
# (3, "全部数据权限"),
|
||||||
|
# (4, "自定数据权限")
|
||||||
|
re_api = api
|
||||||
|
_pk = request.parser_context["kwargs"].get('pk')
|
||||||
|
if _pk: # 判断是否是单例查询
|
||||||
|
re_api = re.sub(_pk,'{id}', api)
|
||||||
|
role_id_list = request.user.role.values_list('id', flat=True)
|
||||||
|
role_permission_list=RoleApiPermission.objects.filter(
|
||||||
|
role__in=role_id_list,
|
||||||
|
role__status=1,
|
||||||
|
api=re_api,
|
||||||
|
method=method).values(
|
||||||
|
'data_range',
|
||||||
|
)
|
||||||
|
dataScope_list = [] # 权限范围列表
|
||||||
|
for ele in role_permission_list:
|
||||||
|
# 判断用户是否为超级管理员角色/如果拥有[全部数据权限]则返回所有数据
|
||||||
|
if ele.get("data_range") == 3:
|
||||||
|
return queryset
|
||||||
|
dataScope_list.append(ele.get("data_range"))
|
||||||
|
dataScope_list = list(set(dataScope_list))
|
||||||
|
|
||||||
|
# 4. 只为仅本人数据权限时只返回过滤本人数据,并且部门为自己本部门(考虑到用户会变部门,只能看当前用户所在的部门数据)
|
||||||
|
if 0 in dataScope_list:
|
||||||
|
return queryset.filter(
|
||||||
|
creator=request.user, dept_belong_id=user_dept_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 5. 自定数据权限 获取部门,根据部门过滤
|
||||||
|
dept_list = []
|
||||||
|
for ele in dataScope_list:
|
||||||
|
if ele == 1:
|
||||||
|
dept_list.append(user_dept_id)
|
||||||
|
dept_list.extend(
|
||||||
|
get_dept(
|
||||||
|
user_dept_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif ele == 2:
|
||||||
|
dept_list.append(user_dept_id)
|
||||||
|
elif ele == 4:
|
||||||
|
dept_list.extend(
|
||||||
|
request.user.role.filter(status=1).values_list(
|
||||||
|
"dept__id", flat=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if queryset.model._meta.model_name == 'dept':
|
||||||
|
return queryset.filter(id__in=list(set(dept_list)))
|
||||||
|
return queryset.filter(dept_belong_id__in=list(set(dept_list)))
|
||||||
|
|
||||||
|
|
||||||
class CustomDjangoFilterBackend(DjangoFilterBackend):
|
class CustomDjangoFilterBackend(DjangoFilterBackend):
|
||||||
lookup_prefixes = {
|
lookup_prefixes = {
|
||||||
@@ -164,12 +169,14 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
"~": "icontains",
|
"~": "icontains",
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct_search(self, field_name):
|
def construct_search(self, field_name, lookup_expr=None):
|
||||||
lookup = self.lookup_prefixes.get(field_name[0])
|
lookup = self.lookup_prefixes.get(field_name[0])
|
||||||
if lookup:
|
if lookup:
|
||||||
field_name = field_name[1:]
|
field_name = field_name[1:]
|
||||||
else:
|
else:
|
||||||
lookup = "icontains"
|
lookup = lookup_expr
|
||||||
|
if field_name.endswith(lookup):
|
||||||
|
return field_name
|
||||||
return LOOKUP_SEP.join([field_name, lookup])
|
return LOOKUP_SEP.join([field_name, lookup])
|
||||||
|
|
||||||
def find_filter_lookups(self, orm_lookups, search_term_key):
|
def find_filter_lookups(self, orm_lookups, search_term_key):
|
||||||
@@ -249,7 +256,10 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
# warn if the field doesn't exist.
|
# warn if the field doesn't exist.
|
||||||
if field is None:
|
if field is None:
|
||||||
undefined.append(field_name)
|
undefined.append(field_name)
|
||||||
|
# 更新默认字符串搜索为模糊搜索
|
||||||
|
if isinstance(field, (models.CharField)) and filterset_fields == '__all__' and lookups == [
|
||||||
|
'exact']:
|
||||||
|
lookups = ['icontains']
|
||||||
for lookup_expr in lookups:
|
for lookup_expr in lookups:
|
||||||
filter_name = cls.get_filter_name(field_name, lookup_expr)
|
filter_name = cls.get_filter_name(field_name, lookup_expr)
|
||||||
|
|
||||||
@@ -298,7 +308,7 @@ class CustomDjangoFilterBackend(DjangoFilterBackend):
|
|||||||
for search_field in filterset.filters:
|
for search_field in filterset.filters:
|
||||||
if isinstance(filterset.filters[search_field], CharFilter):
|
if isinstance(filterset.filters[search_field], CharFilter):
|
||||||
orm_lookups.append(
|
orm_lookups.append(
|
||||||
self.construct_search(six.text_type(search_field))
|
self.construct_search(six.text_type(search_field), filterset.filters[search_field].lookup_expr)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
orm_lookups.append(search_field)
|
orm_lookups.append(search_field)
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
import logging
|
|
||||||
import os.path
|
|
||||||
from logging import LogRecord
|
|
||||||
|
|
||||||
from django.core.servers.basehttp import WSGIRequestHandler
|
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
from logging.handlers import RotatingFileHandler
|
|
||||||
|
|
||||||
# 1.🎖️先声明一个类继承logging.Handler(制作一件品如的衣服)
|
|
||||||
from loguru._defaults import LOGURU_FORMAT
|
|
||||||
|
|
||||||
|
|
||||||
class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
|
||||||
"""
|
|
||||||
自定义反射时间回滚日志记录器
|
|
||||||
缺少命名空间
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, filename, when='d', interval=1, backupCount=5, encoding="utf-8", delay=False, utc=False,
|
|
||||||
maxBytes=1024 * 1024 * 100, atTime=None, logging_levels="all"):
|
|
||||||
super(InterceptTimedRotatingFileHandler, self).__init__(filename)
|
|
||||||
filename = os.path.abspath(filename)
|
|
||||||
when = when.lower()
|
|
||||||
# 2.🎖️需要本地用不同的文件名做为不同日志的筛选器
|
|
||||||
self.logger_ = logger.bind(sime=filename, ip="-", port="-", username="张三")
|
|
||||||
self.filename = filename
|
|
||||||
key_map = {
|
|
||||||
'h': 'hour',
|
|
||||||
'w': 'week',
|
|
||||||
's': 'second',
|
|
||||||
'm': 'minute',
|
|
||||||
'd': 'day',
|
|
||||||
}
|
|
||||||
# 根据输入文件格式及时间回滚设立文件名称
|
|
||||||
rotation = f"{maxBytes / 1024 / 1024}MB"
|
|
||||||
retention = "%d %ss" % (backupCount, key_map[when])
|
|
||||||
time_format = "{time:%Y-%m-%d_%H-%M-%S}"
|
|
||||||
if when == "s":
|
|
||||||
time_format = "{time:%Y-%m-%d_%H-%M-%S}"
|
|
||||||
elif when == "m":
|
|
||||||
time_format = "{time:%Y-%m-%d_%H-%M}"
|
|
||||||
elif when == "h":
|
|
||||||
time_format = "{time:%Y-%m-%d_%H}"
|
|
||||||
elif when == "d":
|
|
||||||
time_format = "{time:%Y-%m-%d}"
|
|
||||||
elif when == "w":
|
|
||||||
time_format = "{time:%Y-%m-%d}"
|
|
||||||
level_keys = ["info"]
|
|
||||||
# 3.🎖️构建一个筛选器
|
|
||||||
levels = {
|
|
||||||
"debug": lambda x: "DEBUG" == x['level'].name.upper() and x['extra'].get('sime') == filename,
|
|
||||||
"error": lambda x: "ERROR" == x['level'].name.upper() and x['extra'].get('sime') == filename,
|
|
||||||
"info": lambda x: "INFO" == x['level'].name.upper() and x['extra'].get('sime') == filename,
|
|
||||||
"warning": lambda x: "WARNING" == x['level'].name.upper() and x['extra'].get('sime') == filename
|
|
||||||
}
|
|
||||||
# 4. 🎖️根据输出构建筛选器
|
|
||||||
if isinstance(logging_levels, str):
|
|
||||||
if logging_levels.lower() == "all":
|
|
||||||
level_keys = levels.keys()
|
|
||||||
elif logging_levels.lower() in levels:
|
|
||||||
level_keys = [logging_levels]
|
|
||||||
elif isinstance(logging_levels, (list, tuple)):
|
|
||||||
level_keys = logging_levels
|
|
||||||
for k, f in {_: levels[_] for _ in level_keys}.items():
|
|
||||||
|
|
||||||
# 5.🎖️为防止重复添加sink,而重复写入日志,需要判断是否已经装载了对应sink,防止其使用秘技:反复横跳。
|
|
||||||
filename_fmt = filename.replace(".log", "_%s_%s.log" % (time_format, k))
|
|
||||||
# noinspection PyUnresolvedReferences,PyProtectedMember
|
|
||||||
file_key = {_._name: han_id for han_id, _ in self.logger_._core.handlers.items()}
|
|
||||||
filename_fmt_key = "'{}'".format(filename_fmt)
|
|
||||||
if filename_fmt_key in file_key:
|
|
||||||
continue
|
|
||||||
# self.logger_.remove(file_key[filename_fmt_key])
|
|
||||||
self.logger_.add(
|
|
||||||
filename_fmt,
|
|
||||||
# format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <green>{extra[ip]}:{extra[port]}</green> | <level>{level: <8}</level>| <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
|
||||||
retention=retention,
|
|
||||||
encoding=encoding,
|
|
||||||
level=self.level,
|
|
||||||
rotation=rotation,
|
|
||||||
compression="zip", # 日志归档自行压缩文件
|
|
||||||
delay=delay,
|
|
||||||
enqueue=True,
|
|
||||||
backtrace=True,
|
|
||||||
filter=f
|
|
||||||
)
|
|
||||||
|
|
||||||
def emit(self, record):
|
|
||||||
try:
|
|
||||||
level = self.logger_.level(record.levelname).name
|
|
||||||
except ValueError:
|
|
||||||
level = record.levelno
|
|
||||||
|
|
||||||
frame, depth = logging.currentframe(), 2
|
|
||||||
# 6.🎖️把当前帧的栈深度回到发生异常的堆栈深度,不然就是当前帧发生异常而无法回溯
|
|
||||||
while frame.f_code.co_filename == logging.__file__:
|
|
||||||
frame = frame.f_back
|
|
||||||
depth += 1
|
|
||||||
# 设置自定义属性
|
|
||||||
port = "-"
|
|
||||||
ip = "-"
|
|
||||||
locals_self = frame.f_locals.get('self', None)
|
|
||||||
msg = self.format(record)
|
|
||||||
if locals_self and hasattr(locals_self, 'client_address'):
|
|
||||||
ip, port = locals_self.client_address
|
|
||||||
# - 127.0.0.1:56525 -
|
|
||||||
msg = f"{ip}:{port} - {msg}"
|
|
||||||
self.logger_ \
|
|
||||||
.opt(depth=depth, exception=record.exc_info, colors=True) \
|
|
||||||
.bind(ip=ip, port=port) \
|
|
||||||
.log(level, msg)
|
|
||||||
@@ -6,22 +6,20 @@
|
|||||||
@Created on: 2021/5/31 031 22:08
|
@Created on: 2021/5/31 031 22:08
|
||||||
@Remark: 公共基础model类
|
@Remark: 公共基础model类
|
||||||
"""
|
"""
|
||||||
import uuid
|
from importlib import import_module
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import QuerySet
|
from django.conf import settings
|
||||||
|
|
||||||
from application import settings
|
from application import settings
|
||||||
table_prefix = settings.TABLE_PREFIX # 数据库表名前缀
|
table_prefix = settings.TABLE_PREFIX # 数据库表名前缀
|
||||||
|
|
||||||
|
|
||||||
class SoftDeleteQuerySet(QuerySet):
|
class SoftDeleteQuerySet(models.QuerySet):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SoftDeleteManager(models.Manager):
|
class SoftDeleteManager(models.Manager):
|
||||||
"""支持软删除"""
|
"""支持软删除"""
|
||||||
|
|
||||||
@@ -40,7 +38,7 @@ class SoftDeleteManager(models.Manager):
|
|||||||
return SoftDeleteQuerySet(self.model, using=self._db).exclude(is_deleted=False)
|
return SoftDeleteQuerySet(self.model, using=self._db).exclude(is_deleted=False)
|
||||||
return SoftDeleteQuerySet(self.model).exclude(is_deleted=True)
|
return SoftDeleteQuerySet(self.model).exclude(is_deleted=True)
|
||||||
|
|
||||||
def get_by_natural_key(self,name):
|
def get_by_natural_key(self, name):
|
||||||
return SoftDeleteQuerySet(self.model).get(username=name)
|
return SoftDeleteQuerySet(self.model).get(username=name)
|
||||||
|
|
||||||
|
|
||||||
@@ -86,8 +84,6 @@ class CoreModel(models.Model):
|
|||||||
verbose_name_plural = verbose_name
|
verbose_name_plural = verbose_name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_models_objects(model_name=None):
|
def get_all_models_objects(model_name=None):
|
||||||
"""
|
"""
|
||||||
获取所有 models 对象
|
获取所有 models 对象
|
||||||
@@ -111,4 +107,39 @@ def get_all_models_objects(model_name=None):
|
|||||||
settings.ALL_MODELS_OBJECTS.setdefault(item.__name__, {"table": table, "object": item})
|
settings.ALL_MODELS_OBJECTS.setdefault(item.__name__, {"table": table, "object": item})
|
||||||
if model_name:
|
if model_name:
|
||||||
return settings.ALL_MODELS_OBJECTS[model_name] or {}
|
return settings.ALL_MODELS_OBJECTS[model_name] or {}
|
||||||
return settings.ALL_MODELS_OBJECTS or {}
|
return settings.ALL_MODELS_OBJECTS or {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_model_from_app(app_name):
|
||||||
|
"""获取模型里的字段"""
|
||||||
|
model_module = import_module(app_name + '.models')
|
||||||
|
filter_model = [
|
||||||
|
getattr(model_module, item) for item in dir(model_module)
|
||||||
|
if item != 'CoreModel' and issubclass(getattr(model_module, item).__class__, models.base.ModelBase)
|
||||||
|
]
|
||||||
|
model_list = []
|
||||||
|
for model in filter_model:
|
||||||
|
if model.__name__ == 'AbstractUser':
|
||||||
|
continue
|
||||||
|
fields = [
|
||||||
|
{'title': field.verbose_name, 'name': field.name, 'object': field}
|
||||||
|
for field in model._meta.fields
|
||||||
|
]
|
||||||
|
model_list.append({
|
||||||
|
'app': app_name,
|
||||||
|
'verbose': model._meta.verbose_name,
|
||||||
|
'model': model.__name__,
|
||||||
|
'object': model,
|
||||||
|
'fields': fields
|
||||||
|
})
|
||||||
|
return model_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_custom_app_models(app_name=None):
|
||||||
|
"""获取所有项目写的app里的models"""
|
||||||
|
if app_name:
|
||||||
|
return get_model_from_app(app_name)
|
||||||
|
res = []
|
||||||
|
for app in settings.CUSTOM_APPS:
|
||||||
|
res.append(get_model_from_app(app))
|
||||||
|
return res
|
||||||
|
|||||||
@@ -40,13 +40,11 @@ class CustomPagination(PageNumberPagination):
|
|||||||
try:
|
try:
|
||||||
self.page = paginator.page(page_number)
|
self.page = paginator.page(page_number)
|
||||||
except InvalidPage as exc:
|
except InvalidPage as exc:
|
||||||
|
|
||||||
# msg = self.invalid_page_message.format(
|
# msg = self.invalid_page_message.format(
|
||||||
# page_number=page_number, message=str(exc)
|
# page_number=page_number, message=str(exc)
|
||||||
# )
|
# )
|
||||||
# raise NotFound(msg)
|
# raise NotFound(msg)
|
||||||
empty = False
|
empty = False
|
||||||
pass
|
|
||||||
|
|
||||||
if paginator.num_pages > 1 and self.template is not None:
|
if paginator.num_pages > 1 and self.template is not None:
|
||||||
# The browsable API should display pagination controls.
|
# The browsable API should display pagination controls.
|
||||||
@@ -58,15 +56,15 @@ class CustomPagination(PageNumberPagination):
|
|||||||
self.page = []
|
self.page = []
|
||||||
|
|
||||||
return list(self.page)
|
return list(self.page)
|
||||||
|
|
||||||
def get_paginated_response(self, data):
|
def get_paginated_response(self, data):
|
||||||
code = 2000
|
code = 2000
|
||||||
msg = 'success'
|
msg = 'success'
|
||||||
page =int(self.get_page_number(self.request, paginator)) or 1
|
page = int(self.get_page_number(self.request, paginator)) or 1
|
||||||
total=self.page.paginator.count if self.page else 0
|
total = self.page.paginator.count if self.page else 0
|
||||||
limit= int(self.get_page_size(self.request)) or 10
|
limit = int(self.get_page_size(self.request)) or 10
|
||||||
is_next= self.page.has_next()
|
is_next = self.page.has_next() if self.page else False
|
||||||
is_previous= self.page.has_previous()
|
is_previous = self.page.has_previous() if self.page else False
|
||||||
data=data
|
|
||||||
|
|
||||||
if not data:
|
if not data:
|
||||||
code = 2000
|
code = 2000
|
||||||
@@ -78,8 +76,8 @@ class CustomPagination(PageNumberPagination):
|
|||||||
('msg', msg),
|
('msg', msg),
|
||||||
('page', page),
|
('page', page),
|
||||||
('limit', limit),
|
('limit', limit),
|
||||||
('total',total),
|
('total', total),
|
||||||
('is_next',is_next),
|
('is_next', is_next),
|
||||||
('is_previous', is_previous),
|
('is_previous', is_previous),
|
||||||
('data', data)
|
('data', data)
|
||||||
]))
|
]))
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from django.contrib.auth.models import AnonymousUser
|
|||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from rest_framework.permissions import BasePermission
|
from rest_framework.permissions import BasePermission
|
||||||
|
|
||||||
from dvadmin.system.models import ApiWhiteList, RoleMenuButtonPermission
|
from dvadmin.system.models import ApiWhiteList, RoleApiPermission
|
||||||
|
|
||||||
|
|
||||||
def ValidationApi(reqApi, validApi):
|
def ValidationApi(reqApi, validApi):
|
||||||
@@ -74,18 +74,18 @@ class CustomPermission(BasePermission):
|
|||||||
methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH']
|
methodList = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH']
|
||||||
method = methodList.index(method)
|
method = methodList.index(method)
|
||||||
# ***接口白名单***
|
# ***接口白名单***
|
||||||
api_white_list = ApiWhiteList.objects.values(permission__api=F('url'), permission__method=F('method'))
|
api_white_list = ApiWhiteList.objects.values('method',api=F('url'))
|
||||||
api_white_list = [
|
api_white_list = [
|
||||||
str(item.get('permission__api').replace('{id}', '([a-zA-Z0-9-]+)')) + ":" + str(
|
str(item.get('api').replace('{id}', '([a-zA-Z0-9-]+)')) + ":" + str(
|
||||||
item.get('permission__method')) + '$' for item in api_white_list if item.get('permission__api')]
|
item.get('method')) + '$' for item in api_white_list if item.get('api')]
|
||||||
# ********#
|
# ********#
|
||||||
if not hasattr(request.user, "role"):
|
if not hasattr(request.user, "role"):
|
||||||
return False
|
return False
|
||||||
role_id_list = request.user.role.values_list('id',flat=True)
|
role_id_list = request.user.role.values_list('id',flat=True)
|
||||||
userApiList = RoleMenuButtonPermission.objects.filter(role__in=role_id_list).values(permission__api=F('menu_button__api'), permission__method=F('menu_button__method')) # 获取当前用户的角色拥有的所有接口
|
userApiList = RoleApiPermission.objects.filter(role__in=role_id_list).values('api','method') # 获取当前用户的角色拥有的所有接口
|
||||||
ApiList = [
|
ApiList = [
|
||||||
str(item.get('permission__api').replace('{id}', '([a-zA-Z0-9-]+)')) + ":" + str(
|
str(item.get('api').replace('{id}', '([a-zA-Z0-9-]+)')) + ":" + str(
|
||||||
item.get('permission__method')) + '$' for item in userApiList if item.get('permission__api')]
|
item.get('method')) + '$' for item in userApiList if item.get('api')]
|
||||||
new_api_ist = api_white_list + ApiList
|
new_api_ist = api_white_list + ApiList
|
||||||
new_api = api + ":" + str(method)
|
new_api = api + ":" + str(method)
|
||||||
for item in new_api_ist:
|
for item in new_api_ist:
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
@Created on: 2021/6/1 001 22:57
|
@Created on: 2021/6/1 001 22:57
|
||||||
@Remark: 自定义视图集
|
@Remark: 自定义视图集
|
||||||
"""
|
"""
|
||||||
import uuid
|
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
@@ -18,6 +16,8 @@ from dvadmin.utils.filters import DataLevelPermissionsFilter
|
|||||||
from dvadmin.utils.import_export_mixin import ExportSerializerMixin, ImportSerializerMixin
|
from dvadmin.utils.import_export_mixin import ExportSerializerMixin, ImportSerializerMixin
|
||||||
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse
|
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse
|
||||||
from dvadmin.utils.permission import CustomPermission
|
from dvadmin.utils.permission import CustomPermission
|
||||||
|
from dvadmin.utils.models import get_custom_app_models
|
||||||
|
from dvadmin.system.models import Columns
|
||||||
from django_restql.mixins import QueryArgumentsMixin
|
from django_restql.mixins import QueryArgumentsMixin
|
||||||
|
|
||||||
|
|
||||||
@@ -63,12 +63,38 @@ class CustomModelViewSet(ModelViewSet, ImportSerializerMixin, ExportSerializerMi
|
|||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
serializer_class = self.get_serializer_class()
|
serializer_class = self.get_serializer_class()
|
||||||
kwargs.setdefault('context', self.get_serializer_context())
|
kwargs.setdefault('context', self.get_serializer_context())
|
||||||
|
# 全部以可见字段为准
|
||||||
|
can_see = self.get_column_permission(serializer_class)
|
||||||
|
# 排除掉序列化器级的字段
|
||||||
|
# sub_set = set(serializer_class._declared_fields.keys()) - set(can_see)
|
||||||
|
# for field in sub_set:
|
||||||
|
# serializer_class._declared_fields.pop(field)
|
||||||
|
# if not self.request.user.is_superuser:
|
||||||
|
# serializer_class.Meta.fields = can_see
|
||||||
|
# 在分页器中使用
|
||||||
|
self.request.permission_fields = can_see
|
||||||
if isinstance(self.request.data, list):
|
if isinstance(self.request.data, list):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
return serializer_class(many=True, *args, **kwargs)
|
return serializer_class(many=True, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return serializer_class(*args, **kwargs)
|
return serializer_class(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_column_permission(self, serializer_class):
|
||||||
|
"""获取列权限"""
|
||||||
|
finded = False
|
||||||
|
for app in get_custom_app_models():
|
||||||
|
for model in app:
|
||||||
|
if model['object'] is serializer_class.Meta.model:
|
||||||
|
finded = True
|
||||||
|
break
|
||||||
|
if finded:
|
||||||
|
break
|
||||||
|
if finded is False:
|
||||||
|
return []
|
||||||
|
return Columns.objects.filter(
|
||||||
|
app=model['app'], model=model['model']
|
||||||
|
).values('field_name', 'is_create', 'is_query', 'is_update')
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
serializer = self.get_serializer(data=request.data, request=request)
|
serializer = self.get_serializer(data=request.data, request=request)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
7
|
|
||||||
+23
-24
@@ -1,31 +1,30 @@
|
|||||||
Django==4.1.5
|
Django==4.2.6
|
||||||
django-comment-migrate==0.1.7
|
django-comment-migrate==0.1.7
|
||||||
django-cors-headers==3.13.0
|
django-cors-headers==4.3.0
|
||||||
django-filter==21.1
|
django-filter==23.3
|
||||||
django-ranged-response==0.2.0
|
django-ranged-response==0.2.0
|
||||||
djangorestframework==3.14.0
|
djangorestframework==3.14.0
|
||||||
django-restql==0.15.3
|
django-restql==0.15.3
|
||||||
django-simple-captcha==0.5.17
|
django-simple-captcha==0.5.20
|
||||||
django-timezone-field==5.0
|
django-timezone-field==6.0.1
|
||||||
djangorestframework-simplejwt==5.2.2
|
djangorestframework-simplejwt==5.3.0
|
||||||
drf-yasg==1.21.4
|
drf-yasg==1.21.7
|
||||||
mysqlclient==2.1.1
|
mysqlclient==2.2.0
|
||||||
pypinyin==0.48.0
|
pypinyin==0.49.0
|
||||||
ua-parser==0.16.1
|
ua-parser==0.18.0
|
||||||
pyparsing==3.0.9
|
pyparsing==3.1.1
|
||||||
openpyxl==3.0.10
|
openpyxl==3.1.2
|
||||||
requests==2.28.2
|
requests==2.31.0
|
||||||
loguru==0.6.0
|
typing-extensions==4.8.0
|
||||||
typing-extensions==4.4.0
|
tzlocal==5.1
|
||||||
smmap==5.0.0
|
|
||||||
tzlocal==4.1
|
|
||||||
channels==4.0.0
|
channels==4.0.0
|
||||||
channels-redis==4.0.0
|
channels-redis==4.1.0
|
||||||
websockets==10.4
|
websockets==11.0.3
|
||||||
user-agents==2.2.0
|
user-agents==2.2.0
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
whitenoise==6.3.0
|
whitenoise==6.6.0
|
||||||
psycopg2==2.9.5
|
psycopg2==2.9.9
|
||||||
uvicorn==0.20.0
|
uvicorn==0.23.2
|
||||||
gunicorn==20.1.0
|
gunicorn==21.2.0
|
||||||
gevent==22.10.2
|
gevent==23.9.1
|
||||||
|
Pillow==10.1.0
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
~~~sh
|
~~~sh
|
||||||
# 编译打包到本地
|
# 编译打包到本地
|
||||||
docker build -f ./docker_env/web/DockerfileBuild -t registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:latest .
|
docker build -f ./docker_env/web/DockerfileBuild -t registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:16.19-alpine .
|
||||||
# 上传到阿里云仓库
|
# 上传到阿里云仓库
|
||||||
docker push registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:latest
|
docker push registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:16.19-alpine
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:16.19-alpine
|
FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:16.19-alpine
|
||||||
WORKDIR /web/
|
WORKDIR /web/
|
||||||
COPY web/. .
|
COPY web/. .
|
||||||
RUN yarn install
|
RUN yarn install
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM node:16.19-alpine
|
FROM node:16.19-alpine
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
COPY ./web/package.json .
|
COPY ./web/package.json .
|
||||||
RUN npm install -g yarn
|
RUN yarn install --registry=https://registry.npm.taobao.org
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
# 本地环境接口地址
|
# 本地环境接口地址
|
||||||
VITE_API_URL = 'http://127.0.0.1:8000/'
|
VITE_API_URL = 'http://127.0.0.1:8000'
|
||||||
|
|
||||||
# 是否启用按钮权限
|
# 是否启用按钮权限
|
||||||
VITE_PM_ENABLED = true
|
VITE_PM_ENABLED = true
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
ENV = 'production'
|
ENV = 'production'
|
||||||
|
|
||||||
# 线上环境接口地址
|
# 线上环境接口地址
|
||||||
VITE_API_URL = ''
|
VITE_API_URL = '/api' # docker-compose部署不需要修改,nginx容器自动代理了这个地址
|
||||||
|
|
||||||
# 是否启用按钮权限
|
# 是否启用按钮权限
|
||||||
VITE_PM_ENABLED = true
|
VITE_PM_ENABLED = true
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
node_modules
|
node_modules
|
||||||
/dist
|
/dist
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
|
|||||||
Generated
-6818
File diff suppressed because it is too large
Load Diff
+12
-5
@@ -10,12 +10,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.0.10",
|
"@element-plus/icons-vue": "^2.0.10",
|
||||||
"@fast-crud/fast-crud": "^1.9.0",
|
"@fast-crud/fast-crud": "^1.18.3",
|
||||||
"@fast-crud/fast-extends": "^1.9.0",
|
"@fast-crud/fast-extends": "^1.18.3",
|
||||||
"@fast-crud/ui-element": "^1.9.0",
|
"@fast-crud/ui-element": "^1.18.3",
|
||||||
|
"@fast-crud/ui-interface": "^1.18.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
|
"autoprefixer": "^10.4.14",
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"countup.js": "^2.3.2",
|
"countup.js": "^2.3.2",
|
||||||
"cropperjs": "^1.5.13",
|
"cropperjs": "^1.5.13",
|
||||||
@@ -23,7 +25,8 @@
|
|||||||
"echarts": "^5.4.1",
|
"echarts": "^5.4.1",
|
||||||
"echarts-gl": "^2.0.9",
|
"echarts-gl": "^2.0.9",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "^2.2.26",
|
"element-plus": "^2.3.9",
|
||||||
|
"element-tree-line": "^0.2.1",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"js-table2excel": "^1.0.3",
|
"js-table2excel": "^1.0.3",
|
||||||
@@ -32,19 +35,23 @@
|
|||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"pinia-plugin-persist": "^1.0.0",
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
|
"postcss": "^8.4.21",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"qrcodejs2-fixes": "^0.0.2",
|
"qrcodejs2-fixes": "^0.0.2",
|
||||||
"qs": "^6.11.0",
|
"qs": "^6.11.0",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"splitpanes": "^3.1.5",
|
"splitpanes": "^3.1.5",
|
||||||
|
"tailwindcss": "^3.2.7",
|
||||||
"ts-md5": "^1.3.1",
|
"ts-md5": "^1.3.1",
|
||||||
|
"upgrade": "^1.1.0",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
"vue-clipboard3": "^2.0.0",
|
"vue-clipboard3": "^2.0.0",
|
||||||
|
"vue-cropper": "^1.0.8",
|
||||||
"vue-grid-layout": "^3.0.0-beta1",
|
"vue-grid-layout": "^3.0.0-beta1",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vxe-table": "^4.3.10",
|
"vxe-table": "^4.4.1",
|
||||||
"xe-utils": "^3.5.7"
|
"xe-utils": "^3.5.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
+28
-25
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
|
<el-config-provider :size="getGlobalComponentSize" :locale="getGlobalI18n">
|
||||||
|
<!-- v-show="themeConfig.lockScreenTime > 1" -->
|
||||||
<router-view v-show="themeConfig.lockScreenTime > 1" />
|
<router-view v-show="themeConfig.lockScreenTime > 1" />
|
||||||
<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" />
|
||||||
@@ -9,7 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="app">
|
<script setup lang="ts" name="app">
|
||||||
import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, watch,onBeforeUnmount } from 'vue';
|
import { defineAsyncComponent, computed, ref, onBeforeMount, onMounted, onUnmounted, nextTick, watch, onBeforeUnmount } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
@@ -33,8 +34,8 @@ const route = useRoute();
|
|||||||
const stores = useTagsViewRoutes();
|
const stores = useTagsViewRoutes();
|
||||||
const storesThemeConfig = useThemeConfig();
|
const storesThemeConfig = useThemeConfig();
|
||||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||||
import websocket from "/@/utils/websocket";
|
import websocket from '/@/utils/websocket';
|
||||||
import {ElNotification} from "element-plus";
|
import { ElNotification } from 'element-plus';
|
||||||
// 获取版本号
|
// 获取版本号
|
||||||
const getVersion = computed(() => {
|
const getVersion = computed(() => {
|
||||||
let isVersion = false;
|
let isVersion = false;
|
||||||
@@ -58,8 +59,12 @@ onBeforeMount(() => {
|
|||||||
setIntroduction.cssCdn();
|
setIntroduction.cssCdn();
|
||||||
// 设置批量第三方 js
|
// 设置批量第三方 js
|
||||||
setIntroduction.jsCdn();
|
setIntroduction.jsCdn();
|
||||||
//websockt 模块
|
//websockt 模块
|
||||||
websocket.init(wsReceive)
|
try {
|
||||||
|
//websocket.init(wsReceive)
|
||||||
|
} catch (e) {
|
||||||
|
console.log('websocket错误');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// 页面加载时
|
// 页面加载时
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -95,26 +100,24 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// websocket相关代码
|
// websocket相关代码
|
||||||
import {messageCenterStore} from "/@/stores/messageCenter";
|
import { messageCenterStore } from '/@/stores/messageCenter';
|
||||||
const wsReceive = (message: any) => {
|
const wsReceive = (message: any) => {
|
||||||
const data = JSON.parse(message.data)
|
const data = JSON.parse(message.data);
|
||||||
const { unread } = data
|
const { unread } = data;
|
||||||
const messageCenter = messageCenterStore()
|
const messageCenter = messageCenterStore();
|
||||||
messageCenter.setUnread(unread);
|
messageCenter.setUnread(unread);
|
||||||
if (data.contentType === 'SYSTEM') {
|
if (data.contentType === 'SYSTEM') {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '系统消息',
|
title: '系统消息',
|
||||||
message: data.content,
|
message: data.content,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
position: 'bottom-right',
|
position: 'bottom-right',
|
||||||
duration: 5000
|
duration: 5000,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
// 关闭连接
|
// 关闭连接
|
||||||
websocket.close()
|
websocket.close();
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 698 B |
Binary file not shown.
|
After Width: | Height: | Size: 663 B |
Binary file not shown.
|
After Width: | Height: | Size: 737 B |
@@ -0,0 +1,9 @@
|
|||||||
|
.fs-crud-container {
|
||||||
|
.el-table thead {
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
.el-input__inner::placeholder {
|
||||||
|
color: #dcdfe6;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
<template>
|
||||||
|
<div class="user-info-head" @click="editCropper()">
|
||||||
|
<el-avatar :size="100" :src="options.img" />
|
||||||
|
<el-dialog :title="title" v-model="dialogVisiable" width="600px" append-to-body @opened="modalOpened" @close="closeDialog">
|
||||||
|
<el-row>
|
||||||
|
<el-col class="flex justify-center">
|
||||||
|
<vue-cropper
|
||||||
|
ref="cropper"
|
||||||
|
:img="options.img"
|
||||||
|
:info="true"
|
||||||
|
:autoCrop="options.autoCrop"
|
||||||
|
:autoCropWidth="options.autoCropWidth"
|
||||||
|
:autoCropHeight="options.autoCropHeight"
|
||||||
|
:fixedBox="options.fixedBox"
|
||||||
|
:outputType="options.outputType"
|
||||||
|
@realTime="realTime"
|
||||||
|
:centerBox="true"
|
||||||
|
v-if="visible"
|
||||||
|
class="cropper"
|
||||||
|
/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<br />
|
||||||
|
<el-row class="flex justify-center">
|
||||||
|
<el-col :lg="2" :md="2">
|
||||||
|
<el-upload action="#" :http-request="requestUpload" :show-file-list="false" :before-upload="beforeUpload">
|
||||||
|
<el-button type="success">
|
||||||
|
选择
|
||||||
|
<el-icon class="el-icon--right"><Plus /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-upload>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||||
|
<el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{ span: 1, offset: 2 }" :md="2">
|
||||||
|
<el-button icon="RefreshRight" @click="rotateRight()"></el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="{ span: 2, offset: 2 }" :md="2">
|
||||||
|
<el-button type="primary" @click="uploadImg()">更新头像</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import 'vue-cropper/dist/index.css';
|
||||||
|
import { VueCropper } from 'vue-cropper';
|
||||||
|
import { useUserInfo } from '/@/stores/userInfo';
|
||||||
|
import { getCurrentInstance, nextTick, reactive, ref, computed, onMounted, defineExpose } from 'vue';
|
||||||
|
import { base64ToFile } from '/@/utils/tools';
|
||||||
|
const userStore = useUserInfo();
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
|
const open = ref(false);
|
||||||
|
const visible = ref(false);
|
||||||
|
const title = ref('修改头像');
|
||||||
|
const emit = defineEmits(['uploadImg']);
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const dialogVisiable = computed({
|
||||||
|
get() {
|
||||||
|
return props.modelValue;
|
||||||
|
},
|
||||||
|
set(newVal) {
|
||||||
|
emit('update:modelValue', newVal);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//图片裁剪数据
|
||||||
|
const options = reactive({
|
||||||
|
img: userStore.userInfos.avatar, // 裁剪图片的地址
|
||||||
|
fileName: '',
|
||||||
|
autoCrop: true, // 是否默认生成截图框
|
||||||
|
autoCropWidth: 200, // 默认生成截图框宽度
|
||||||
|
autoCropHeight: 200, // 默认生成截图框高度
|
||||||
|
fixedBox: true, // 固定截图框大小 不允许改变
|
||||||
|
outputType: 'png', // 默认生成截图为PNG格式
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 编辑头像 */
|
||||||
|
function editCropper() {
|
||||||
|
dialogVisiable.value = true;
|
||||||
|
}
|
||||||
|
/** 打开弹出层结束时的回调 */
|
||||||
|
function modalOpened() {
|
||||||
|
nextTick(() => {
|
||||||
|
visible.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** 覆盖默认上传行为 */
|
||||||
|
function requestUpload() {}
|
||||||
|
/** 向左旋转 */
|
||||||
|
function rotateLeft() {
|
||||||
|
proxy.$refs.cropper.rotateLeft();
|
||||||
|
}
|
||||||
|
/** 向右旋转 */
|
||||||
|
function rotateRight() {
|
||||||
|
proxy.$refs.cropper.rotateRight();
|
||||||
|
}
|
||||||
|
/** 图片缩放 */
|
||||||
|
function changeScale(num) {
|
||||||
|
num = num || 1;
|
||||||
|
proxy.$refs.cropper.changeScale(num);
|
||||||
|
}
|
||||||
|
/** 上传预处理 */
|
||||||
|
function beforeUpload(file) {
|
||||||
|
if (file.type.indexOf('image/') == -1) {
|
||||||
|
proxy.$modal.msgError('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。');
|
||||||
|
} else {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => {
|
||||||
|
options.img = reader.result;
|
||||||
|
options.fileName = file.name;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 上传图片 */
|
||||||
|
function uploadImg() {
|
||||||
|
// 获取截图的 base64 数据
|
||||||
|
proxy.$refs.cropper.getCropData((data) => {
|
||||||
|
let img = new Image();
|
||||||
|
img.src = data;
|
||||||
|
img.onload = async () => {
|
||||||
|
let _data = compress(img);
|
||||||
|
const imgFile = base64ToFile(_data, options.fileName);
|
||||||
|
emit('uploadImg', imgFile);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 压缩图片
|
||||||
|
function compress(img) {
|
||||||
|
let canvas = document.createElement('canvas');
|
||||||
|
let ctx = canvas.getContext('2d');
|
||||||
|
// let initSize = img.src.length;
|
||||||
|
let width = img.width;
|
||||||
|
let height = img.height;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
// 铺底色
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
// 进行压缩
|
||||||
|
let ndata = canvas.toDataURL('image/jpeg', 0.8);
|
||||||
|
return ndata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭窗口 */
|
||||||
|
function closeDialog() {
|
||||||
|
options.visible = false;
|
||||||
|
options.img = userStore.userInfos.avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAvatar = (img) => {
|
||||||
|
options.img = img;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
updateAvatar,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.user-info-head {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info-head:hover:after {
|
||||||
|
content: '修改头像';
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
color: #000000;
|
||||||
|
font-size: 20px;
|
||||||
|
font-style: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 110px;
|
||||||
|
}
|
||||||
|
.cropper {
|
||||||
|
height: 400px;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -159,11 +159,14 @@ const initModeValueEcho = () => {
|
|||||||
// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
|
// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
|
||||||
const initFontIconName = () => {
|
const initFontIconName = () => {
|
||||||
let name = 'ali';
|
let name = 'ali';
|
||||||
if (props.modelValue!.indexOf('iconfont') > -1) name = 'ali';
|
if(props.modelValue){
|
||||||
else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele';
|
if (props.modelValue!.indexOf('iconfont') > -1) name = 'ali';
|
||||||
else if (props.modelValue!.indexOf('fa') > -1) name = 'awe';
|
else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele';
|
||||||
// 初始化 tab 高亮回显
|
else if (props.modelValue!.indexOf('fa') > -1) name = 'awe';
|
||||||
state.fontIconTabActive = name;
|
// 初始化 tab 高亮回显
|
||||||
|
state.fontIconTabActive = name;
|
||||||
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
// 初始化数据
|
// 初始化数据
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<template>
|
||||||
|
<div style="display: inline-block">
|
||||||
|
<el-button size="default" type="success" @click="handleImport()">
|
||||||
|
<slot>导入</slot>
|
||||||
|
</el-button>
|
||||||
|
<el-dialog :title="props.upload.title" v-model="uploadShow" width="400px" append-to-body>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<el-upload
|
||||||
|
ref="uploadRef"
|
||||||
|
:limit="1"
|
||||||
|
accept=".xlsx, .xls"
|
||||||
|
:headers="props.upload.headers"
|
||||||
|
:action="props.upload.url"
|
||||||
|
:disabled="isUploading"
|
||||||
|
:on-progress="handleFileUploadProgress"
|
||||||
|
:on-success="handleFileSuccess"
|
||||||
|
:auto-upload="false"
|
||||||
|
drag
|
||||||
|
>
|
||||||
|
<i class="el-icon-upload"/>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
将文件拖到此处,或
|
||||||
|
<em>点击上传</em>
|
||||||
|
</div>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip" style="color:red">提示:仅允许导入“xls”或“xlsx”格式文件!</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
<div>
|
||||||
|
<el-button type="warning" style="font-size:14px;margin-top: 20px" @click="importTemplate">下载导入模板</el-button>
|
||||||
|
<el-button type="warning" style="font-size:14px;margin-top: 20px" @click="updateTemplate">批量更新模板</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" :disabled="loading" @click="submitFileForm">确 定</el-button>
|
||||||
|
<el-button :disabled="loading" @click="uploadShow = false">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="importExcel">
|
||||||
|
import { request, downloadFile } from '/@/utils/service';
|
||||||
|
import {inject,ref} from "vue";
|
||||||
|
import { getBaseURL } from '/@/utils/baseUrl';
|
||||||
|
import { Session } from '/@/utils/storage';
|
||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import type { Action } from 'element-plus'
|
||||||
|
const refreshView = inject('refreshView')
|
||||||
|
|
||||||
|
let props = defineProps({
|
||||||
|
upload: {
|
||||||
|
type: Object,
|
||||||
|
default () {
|
||||||
|
return {
|
||||||
|
// 是否显示弹出层
|
||||||
|
open: true,
|
||||||
|
// 弹出层标题
|
||||||
|
title: '',
|
||||||
|
// 是否禁用上传
|
||||||
|
isUploading: false,
|
||||||
|
// 是否更新已经存在的用户数据
|
||||||
|
updateSupport: 0,
|
||||||
|
// 设置上传的请求头部
|
||||||
|
headers: { Authorization: 'JWT ' + Session.get('token') },
|
||||||
|
// 上传的地址
|
||||||
|
url: getBaseURL() + 'api/system/file/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
api: { // 导入接口地址
|
||||||
|
type: String,
|
||||||
|
default () {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let loading = ref(false)
|
||||||
|
const uploadRef = ref()
|
||||||
|
const uploadShow = ref(false)
|
||||||
|
const isUploading = ref(false)
|
||||||
|
/** 导入按钮操作 */
|
||||||
|
const handleImport = function () {
|
||||||
|
uploadShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 下载模板操作 */
|
||||||
|
const importTemplate=function () {
|
||||||
|
downloadFile({
|
||||||
|
url: props.api + 'import_data/',
|
||||||
|
params: {},
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/***
|
||||||
|
* 批量更新模板
|
||||||
|
*/
|
||||||
|
const updateTemplate=function () {
|
||||||
|
downloadFile({
|
||||||
|
url: props.api + 'update_template/',
|
||||||
|
params: {},
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 文件上传中处理
|
||||||
|
const handleFileUploadProgress=function (event:any, file:any, fileList:any) {
|
||||||
|
isUploading.value = true
|
||||||
|
}
|
||||||
|
// 文件上传成功处理
|
||||||
|
const handleFileSuccess=function (response:any, file:any, fileList:any) {
|
||||||
|
isUploading.value = false
|
||||||
|
loading.value = true
|
||||||
|
uploadRef.value.clearFiles()
|
||||||
|
// 是否更新已经存在的用户数据
|
||||||
|
return request({
|
||||||
|
url: props.api + 'import_data/',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
url: response.data.url
|
||||||
|
}
|
||||||
|
}).then((response:any) => {
|
||||||
|
loading.value = false
|
||||||
|
ElMessageBox.alert('导入成功', '导入完成', {
|
||||||
|
confirmButtonText: 'OK',
|
||||||
|
callback: (action: Action) => {
|
||||||
|
refreshView()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}).catch(()=>{
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
// 提交上传文件
|
||||||
|
const submitFileForm=function () {
|
||||||
|
uploadRef.value.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -14,9 +14,9 @@ import { useThemeConfig } from '/@/stores/themeConfig';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// element plus 自带国际化
|
// element plus 自带国际化
|
||||||
import enLocale from 'element-plus/lib/locale/lang/en';
|
import enLocale from 'element-plus/es/locale/lang/en';
|
||||||
import zhcnLocale from 'element-plus/lib/locale/lang/zh-cn';
|
import zhcnLocale from 'element-plus/es/locale/lang/zh-cn';
|
||||||
import zhtwLocale from 'element-plus/lib/locale/lang/zh-tw';
|
import zhtwLocale from 'element-plus/es/locale/lang/zh-tw';
|
||||||
|
|
||||||
// 定义变量内容
|
// 定义变量内容
|
||||||
const messages = {};
|
const messages = {};
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export default {
|
|||||||
two4: 'Links',
|
two4: 'Links',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
accountPlaceholder1: 'The user name admin or not is common',
|
accountPlaceholder1: 'Please enter your login account',
|
||||||
accountPlaceholder2: 'Password: 123456',
|
accountPlaceholder2: 'Please enter your login password',
|
||||||
accountPlaceholder3: 'Please enter the verification code',
|
accountPlaceholder3: 'Please enter the verification code',
|
||||||
accountBtnText: 'Sign in',
|
accountBtnText: 'Sign in',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export default {
|
|||||||
two4: '友情链接',
|
two4: '友情链接',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
accountPlaceholder1: '用户名 admin 或不输均为 common',
|
accountPlaceholder1: '请输入登录账号',
|
||||||
accountPlaceholder2: '密码:123456',
|
accountPlaceholder2: '请输入登录密码',
|
||||||
accountPlaceholder3: '请输入验证码',
|
accountPlaceholder3: '请输入验证码',
|
||||||
accountBtnText: '登 录',
|
accountBtnText: '登 录',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export default {
|
|||||||
two4: '友情連結',
|
two4: '友情連結',
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
accountPlaceholder1: '用戶名admin或不輸均為common',
|
accountPlaceholder1: '請輸入登入賬號',
|
||||||
accountPlaceholder2: '密碼:123456',
|
accountPlaceholder2: '請輸入登入密碼',
|
||||||
accountPlaceholder3: '請輸入驗證碼',
|
accountPlaceholder3: '請輸入驗證碼',
|
||||||
accountBtnText: '登入',
|
accountBtnText: '登入',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
|
<el-main class="layout-main"
|
||||||
<el-scrollbar
|
:style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
|
||||||
ref="layoutMainScrollbarRef"
|
<el-scrollbar ref="layoutMainScrollbarRef" class="layout-main-scroll layout-backtop-header-fixed"
|
||||||
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>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<div class="layout-navbars-breadcrumb-user-icon">
|
<div class="layout-navbars-breadcrumb-user-icon">
|
||||||
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
<el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<el-badge :value="messageCenter.unread" :hidden="messageCenter.unread===0">
|
<el-badge :value="messageCenter.unread" :hidden="messageCenter.unread === 0">
|
||||||
<el-icon :title="$t('message.user.title4')">
|
<el-icon :title="$t('message.user.title4')">
|
||||||
<ele-Bell />
|
<ele-Bell />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@@ -59,8 +59,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
<el-dropdown :show-timeout="70" :hide-timeout="50" @command="onHandleCommandClick">
|
||||||
<span class="layout-navbars-breadcrumb-user-link">
|
<span class="layout-navbars-breadcrumb-user-link">
|
||||||
<img :src="userInfos.photo" class="layout-navbars-breadcrumb-user-link-photo mr5" />
|
<img :src="userInfos.avatar || headerImage" class="layout-navbars-breadcrumb-user-link-photo mr5" />
|
||||||
{{ userInfos.userName === '' ? 'common' : userInfos.userName }}
|
{{ userInfos.username === '' ? 'common' : userInfos.username }}
|
||||||
<el-icon class="el-icon--right">
|
<el-icon class="el-icon--right">
|
||||||
<ele-ArrowDown />
|
<ele-ArrowDown />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
|
<el-dropdown-item command="/home">{{ $t('message.user.dropdown1') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
|
<el-dropdown-item command="/personal">{{ $t('message.user.dropdown2') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>
|
<el-dropdown-item command="wareHouse">{{ $t('message.user.dropdown6') }}</el-dropdown-item>
|
||||||
<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
|
<el-dropdown-item divided command="logOut">{{ $t('message.user.dropdown5') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
@@ -90,7 +90,7 @@ import { useThemeConfig } from '/@/stores/themeConfig';
|
|||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
import mittBus from '/@/utils/mitt';
|
import mittBus from '/@/utils/mitt';
|
||||||
import { Session, Local } from '/@/utils/storage';
|
import { Session, Local } from '/@/utils/storage';
|
||||||
|
import headerImage from '/@/assets/img/headerImage.png';
|
||||||
// 引入组件
|
// 引入组件
|
||||||
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
|
const UserNews = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/userNews.vue'));
|
||||||
const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue'));
|
const Search = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/search.vue'));
|
||||||
@@ -208,8 +208,8 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//消息中心的未读数量
|
//消息中心的未读数量
|
||||||
import {messageCenterStore} from "/@/stores/messageCenter";
|
import { messageCenterStore } from '/@/stores/messageCenter';
|
||||||
const messageCenter = messageCenterStore()
|
const messageCenter = messageCenterStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -3,18 +3,21 @@
|
|||||||
<router-view v-slot="{ Component }">
|
<router-view v-slot="{ Component }">
|
||||||
<transition :name="setTransitionName" mode="out-in">
|
<transition :name="setTransitionName" mode="out-in">
|
||||||
<keep-alive :include="getKeepAliveNames" v-if="showView">
|
<keep-alive :include="getKeepAliveNames" v-if="showView">
|
||||||
<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
|
<div>
|
||||||
|
<component :is="Component" :key="state.refreshRouterViewKey" class="w100" v-show="!isIframePage" />
|
||||||
|
</div>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
<transition :name="setTransitionName" mode="out-in">
|
<transition :name="setTransitionName" mode="out-in">
|
||||||
<Iframes class="w100" v-show="isIframePage" :refreshKey="state.iframeRefreshKey" :name="setTransitionName" :list="state.iframeList" />
|
<Iframes class="w100" v-show="isIframePage" :refreshKey="state.iframeRefreshKey" :name="setTransitionName"
|
||||||
|
:list="state.iframeList" />
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="layoutParentView">
|
<script setup lang="ts" name="layoutParentView">
|
||||||
import { defineAsyncComponent, computed, reactive, onBeforeMount, onUnmounted, nextTick, watch, onMounted,ref,provide } from 'vue';
|
import { defineAsyncComponent, computed, reactive, onBeforeMount, onUnmounted, nextTick, watch, onMounted, ref, provide } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useKeepALiveNames } from '/@/stores/keepAliveNames';
|
import { useKeepALiveNames } from '/@/stores/keepAliveNames';
|
||||||
@@ -44,13 +47,13 @@ const showView = ref(true)
|
|||||||
/**
|
/**
|
||||||
* 刷新页面
|
* 刷新页面
|
||||||
*/
|
*/
|
||||||
const refreshView=function () {
|
const refreshView = function () {
|
||||||
showView.value = false // 通过v-if移除router-view节点
|
showView.value = false // 通过v-if移除router-view节点
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
showView.value = true // DOM更新后再通过v-if添加router-view节点
|
showView.value = true // DOM更新后再通过v-if添加router-view节点
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
provide('refreshView',refreshView)
|
provide('refreshView', refreshView)
|
||||||
|
|
||||||
// 设置主界面切换动画
|
// 设置主界面切换动画
|
||||||
const setTransitionName = computed(() => {
|
const setTransitionName = computed(() => {
|
||||||
@@ -105,7 +108,7 @@ onMounted(() => {
|
|||||||
});
|
});
|
||||||
// 页面卸载时
|
// 页面卸载时
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
mittBus.off('onTagsViewRefreshRouterView', () => {});
|
mittBus.off('onTagsViewRefreshRouterView', () => { });
|
||||||
});
|
});
|
||||||
// 监听路由变化,防止 tagsView 多标签时,切换动画消失
|
// 监听路由变化,防止 tagsView 多标签时,切换动画消失
|
||||||
// https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files
|
// https://toscode.gitee.com/lyt-top/vue-next-admin/pulls/38/files
|
||||||
|
|||||||
+21
-8
@@ -1,9 +1,10 @@
|
|||||||
import {createApp} from 'vue';
|
import { createApp } from 'vue';
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
import router from './router';
|
import router from './router';
|
||||||
import {directive} from '/@/utils/directive';
|
import { directive } from '/@/utils/directive';
|
||||||
import {i18n} from '/@/i18n/index';
|
import { i18n } from '/@/i18n';
|
||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
|
import '/@/assets/style/tailwind.css'; // 先引入tailwind css, 以免element-plus冲突
|
||||||
import ElementPlus from 'element-plus';
|
import ElementPlus from 'element-plus';
|
||||||
import 'element-plus/dist/index.css';
|
import 'element-plus/dist/index.css';
|
||||||
import '/@/theme/index.scss';
|
import '/@/theme/index.scss';
|
||||||
@@ -15,7 +16,7 @@ import fastCrud from './settings.ts';
|
|||||||
import pinia from './stores';
|
import pinia from './stores';
|
||||||
import permission from '/@/plugin/permission/index';
|
import permission from '/@/plugin/permission/index';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import eIconPicker, { iconList,analyzingIconForIconfont } from 'e-icon-picker';
|
import eIconPicker, { iconList, analyzingIconForIconfont } from 'e-icon-picker';
|
||||||
import 'e-icon-picker/icon/default-icon/symbol.js'; //基本彩色图标库
|
import 'e-icon-picker/icon/default-icon/symbol.js'; //基本彩色图标库
|
||||||
import 'e-icon-picker/index.css'; // 基本样式,包含基本图标
|
import 'e-icon-picker/index.css'; // 基本样式,包含基本图标
|
||||||
import 'font-awesome/css/font-awesome.min.css';
|
import 'font-awesome/css/font-awesome.min.css';
|
||||||
@@ -24,6 +25,14 @@ import fontAwesome470 from 'e-icon-picker/icon/fontawesome/font-awesome.v4.7.0.j
|
|||||||
import eIconList from 'e-icon-picker/icon/default-icon/eIconList.js';
|
import eIconList from 'e-icon-picker/icon/default-icon/eIconList.js';
|
||||||
import iconfont from '/@/assets/iconfont/iconfont.json'; //引入json文件
|
import iconfont from '/@/assets/iconfont/iconfont.json'; //引入json文件
|
||||||
import '/@/assets/iconfont/iconfont.css'; //引入css
|
import '/@/assets/iconfont/iconfont.css'; //引入css
|
||||||
|
// 自动注册插件
|
||||||
|
import { scanAndInstallPlugins } from '/@/views/plugins/index';
|
||||||
|
import VXETable from 'vxe-table'
|
||||||
|
import 'vxe-table/lib/style.css'
|
||||||
|
|
||||||
|
import '/@/assets/style/reset.scss';
|
||||||
|
import 'element-tree-line/dist/style.css'
|
||||||
|
|
||||||
let forIconfont = analyzingIconForIconfont(iconfont); //解析class
|
let forIconfont = analyzingIconForIconfont(iconfont); //解析class
|
||||||
iconList.addIcon(forIconfont.list); // 添加iconfont dvadmin3的icon
|
iconList.addIcon(forIconfont.list); // 添加iconfont dvadmin3的icon
|
||||||
iconList.addIcon(elementPlus); // 添加element plus的图标
|
iconList.addIcon(elementPlus); // 添加element plus的图标
|
||||||
@@ -31,17 +40,21 @@ iconList.addIcon(fontAwesome470); // 添加fontAwesome 470版本的图标
|
|||||||
|
|
||||||
let app = createApp(App);
|
let app = createApp(App);
|
||||||
|
|
||||||
|
scanAndInstallPlugins(app);
|
||||||
|
|
||||||
app.use(eIconPicker, {
|
app.use(eIconPicker, {
|
||||||
addIconList: eIconList, //全局添加图标
|
addIconList: eIconList, //全局添加图标
|
||||||
removeIconList: [], //全局删除图标
|
removeIconList: [], //全局删除图标
|
||||||
zIndex: 3100, //选择器弹层的最低层,全局配置
|
zIndex: 3100, //选择器弹层的最低层,全局配置
|
||||||
});
|
});
|
||||||
|
|
||||||
pinia.use(piniaPersist);
|
pinia.use(piniaPersist);
|
||||||
directive(app);
|
directive(app);
|
||||||
other.elSvg(app);
|
other.elSvg(app);
|
||||||
|
|
||||||
|
|
||||||
|
app.use(VXETable)
|
||||||
app.use(permission);
|
app.use(permission);
|
||||||
app.use(pinia).use(router).use(ElementPlus, {i18n: i18n.global.t}).use(i18n).use(VueGridLayout).use(fastCrud).mount('#app');
|
app.use(pinia).use(router).use(ElementPlus, { i18n: i18n.global.t }).use(i18n).use(VueGridLayout).use(fastCrud).mount('#app');
|
||||||
|
|
||||||
app.config.globalProperties.mittBus = mitt();
|
app.config.globalProperties.mittBus = mitt();
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default {
|
|||||||
return XEUtils.includeArrays(BtnPermission, value)
|
return XEUtils.includeArrays(BtnPermission, value)
|
||||||
}else if(typeof value === 'string'){
|
}else if(typeof value === 'string'){
|
||||||
const index = XEUtils.arrayIndexOf(BtnPermission, value)
|
const index = XEUtils.arrayIndexOf(BtnPermission, value)
|
||||||
return index>0?true:false
|
return index>-1?true:false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const BtnPermissionStore = defineStore('BtnPermission', {
|
|||||||
actions: {
|
actions: {
|
||||||
async getBtnPermissionStore() {
|
async getBtnPermissionStore() {
|
||||||
request({
|
request({
|
||||||
url: '/api/system/menu_button/menu_button_all_permission/',
|
url: '/api/system/menu/menu_button_all_permission/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
}).then((ret: {
|
}).then((ret: {
|
||||||
data: []
|
data: []
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useRoutesList } from '/@/stores/routesList';
|
|||||||
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
||||||
import { useMenuApi } from '/@/api/menu/index';
|
import { useMenuApi } from '/@/api/menu/index';
|
||||||
import { handleMenu } from '../utils/menu';
|
import { handleMenu } from '../utils/menu';
|
||||||
import {BtnPermissionStore} from "/@/plugin/permission/store.permission";
|
import { BtnPermissionStore } from '/@/plugin/permission/store.permission';
|
||||||
|
|
||||||
const menuApi = useMenuApi();
|
const menuApi = useMenuApi();
|
||||||
|
|
||||||
|
|||||||
+86
-79
@@ -1,15 +1,15 @@
|
|||||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
import {createRouter, createWebHashHistory} from 'vue-router';
|
||||||
import NProgress from 'nprogress';
|
import NProgress from 'nprogress';
|
||||||
import 'nprogress/nprogress.css';
|
import 'nprogress/nprogress.css';
|
||||||
import pinia from '/@/stores/index';
|
import pinia from '/@/stores/index';
|
||||||
import { storeToRefs } from 'pinia';
|
import {storeToRefs} from 'pinia';
|
||||||
import { useKeepALiveNames } from '/@/stores/keepAliveNames';
|
import {useKeepALiveNames} from '/@/stores/keepAliveNames';
|
||||||
import { useRoutesList } from '/@/stores/routesList';
|
import {useRoutesList} from '/@/stores/routesList';
|
||||||
import { useThemeConfig } from '/@/stores/themeConfig';
|
import {useThemeConfig} from '/@/stores/themeConfig';
|
||||||
import { Session } from '/@/utils/storage';
|
import {Session} from '/@/utils/storage';
|
||||||
import { staticRoutes } from '/@/router/route';
|
import {staticRoutes} from '/@/router/route';
|
||||||
import { initFrontEndControlRoutes } from '/@/router/frontEnd';
|
import {initFrontEndControlRoutes} from '/@/router/frontEnd';
|
||||||
import { initBackEndControlRoutes } from '/@/router/backEnd';
|
import {initBackEndControlRoutes} from '/@/router/backEnd';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。
|
* 1、前端控制路由时:isRequestRoutes 为 false,需要写 roles,需要走 setFilterRoute 方法。
|
||||||
@@ -22,8 +22,8 @@ import { initBackEndControlRoutes } from '/@/router/backEnd';
|
|||||||
|
|
||||||
// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置
|
// 读取 `/src/stores/themeConfig.ts` 是否开启后端控制路由配置
|
||||||
const storesThemeConfig = useThemeConfig(pinia);
|
const storesThemeConfig = useThemeConfig(pinia);
|
||||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
const {themeConfig} = storeToRefs(storesThemeConfig);
|
||||||
const { isRequestRoutes } = themeConfig.value;
|
const {isRequestRoutes} = themeConfig.value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建一个可以被 Vue 应用程序使用的路由实例
|
* 创建一个可以被 Vue 应用程序使用的路由实例
|
||||||
@@ -31,8 +31,8 @@ const { isRequestRoutes } = themeConfig.value;
|
|||||||
* @link 参考:https://next.router.vuejs.org/zh/api/#createrouter
|
* @link 参考:https://next.router.vuejs.org/zh/api/#createrouter
|
||||||
*/
|
*/
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: staticRoutes,
|
routes: staticRoutes,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,13 +41,13 @@ export const router = createRouter({
|
|||||||
* @returns 返回处理后的一维路由菜单数组
|
* @returns 返回处理后的一维路由菜单数组
|
||||||
*/
|
*/
|
||||||
export function formatFlatteningRoutes(arr: any) {
|
export function formatFlatteningRoutes(arr: any) {
|
||||||
if (arr.length <= 0) return false;
|
if (arr.length <= 0) return false;
|
||||||
for (let i = 0; i < arr.length; i++) {
|
for (let i = 0; i < arr.length; i++) {
|
||||||
if (arr[i].children) {
|
if (arr[i].children) {
|
||||||
arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
|
arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,73 +58,80 @@ export function formatFlatteningRoutes(arr: any) {
|
|||||||
* @returns 返回将一维数组重新处理成 `定义动态路由(dynamicRoutes)` 的格式
|
* @returns 返回将一维数组重新处理成 `定义动态路由(dynamicRoutes)` 的格式
|
||||||
*/
|
*/
|
||||||
export function formatTwoStageRoutes(arr: any) {
|
export function formatTwoStageRoutes(arr: any) {
|
||||||
if (arr.length <= 0) return false;
|
if (arr.length <= 0) return false;
|
||||||
const newArr: any = [];
|
const newArr: any = [];
|
||||||
const cacheList: Array<string> = [];
|
const cacheList: Array<string> = [];
|
||||||
arr.forEach((v: any) => {
|
arr.forEach((v: any) => {
|
||||||
if (v.path === '/') {
|
if (v.path === '/') {
|
||||||
newArr.push({ component: v.component, name: v.name, path: v.path, redirect: v.redirect, meta: v.meta, children: [] });
|
newArr.push({
|
||||||
} else {
|
component: v.component,
|
||||||
// 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用
|
name: v.name,
|
||||||
// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
|
path: v.path,
|
||||||
if (v.path.indexOf('/:') > -1) {
|
redirect: v.redirect,
|
||||||
v.meta['isDynamic'] = true;
|
meta: v.meta,
|
||||||
v.meta['isDynamicPath'] = v.path;
|
children: []
|
||||||
}
|
});
|
||||||
newArr[0].children.push({ ...v });
|
} else {
|
||||||
// 存 name 值,keep-alive 中 include 使用,实现路由的缓存
|
// 判断是否是动态路由(xx/:id/:name),用于 tagsView 等中使用
|
||||||
// 路径:/@/layout/routerView/parent.vue
|
// 修复:https://gitee.com/lyt-top/vue-next-admin/issues/I3YX6G
|
||||||
if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive) {
|
if (v.path.indexOf('/:') > -1) {
|
||||||
cacheList.push(v.name);
|
v.meta['isDynamic'] = true;
|
||||||
const stores = useKeepALiveNames(pinia);
|
v.meta['isDynamicPath'] = v.path;
|
||||||
stores.setCacheKeepAlive(cacheList);
|
}
|
||||||
}
|
newArr[0].children.push({...v});
|
||||||
}
|
// 存 name 值,keep-alive 中 include 使用,实现路由的缓存
|
||||||
});
|
// 路径:/@/layout/routerView/parent.vue
|
||||||
return newArr;
|
if (newArr[0].meta.isKeepAlive && v.meta.isKeepAlive && v.component_name != "") {
|
||||||
|
cacheList.push(v.name);
|
||||||
|
const stores = useKeepALiveNames(pinia);
|
||||||
|
stores.setCacheKeepAlive(cacheList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newArr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 路由加载前
|
// 路由加载前
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
NProgress.configure({ showSpinner: false });
|
NProgress.configure({showSpinner: false});
|
||||||
if (to.meta.title) NProgress.start();
|
if (to.meta.title) NProgress.start();
|
||||||
const token = Session.get('token');
|
const token = Session.get('token');
|
||||||
if (to.path === '/login' && !token) {
|
if (to.path === '/login' && !token) {
|
||||||
next();
|
next();
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
} else {
|
} else {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
next(`/login?redirect=${to.path}¶ms=${JSON.stringify(to.query ? to.query : to.params)}`);
|
next(`/login?redirect=${to.path}¶ms=${JSON.stringify(to.query ? to.query : to.params)}`);
|
||||||
Session.clear();
|
Session.clear();
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
} else if (token && to.path === '/login') {
|
} else if (token && to.path === '/login') {
|
||||||
next('/home');
|
next('/home');
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
} else {
|
} else {
|
||||||
const storesRoutesList = useRoutesList(pinia);
|
const storesRoutesList = useRoutesList(pinia);
|
||||||
const { routesList } = storeToRefs(storesRoutesList);
|
const {routesList} = storeToRefs(storesRoutesList);
|
||||||
if (routesList.value.length === 0) {
|
if (routesList.value.length === 0) {
|
||||||
if (isRequestRoutes) {
|
if (isRequestRoutes) {
|
||||||
// 后端控制路由:路由数据初始化,防止刷新时丢失
|
// 后端控制路由:路由数据初始化,防止刷新时丢失
|
||||||
await initBackEndControlRoutes();
|
await initBackEndControlRoutes();
|
||||||
// 动态添加路由:防止非首页刷新时跳转回首页的问题
|
// 动态添加路由:防止非首页刷新时跳转回首页的问题
|
||||||
// 确保 addRoute() 时动态添加的路由已经被完全加载上去
|
// 确保 addRoute() 时动态添加的路由已经被完全加载上去
|
||||||
next({ ...to, replace: true });
|
next({...to, replace: true});
|
||||||
} else {
|
} else {
|
||||||
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
|
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
|
||||||
await initFrontEndControlRoutes();
|
await initFrontEndControlRoutes();
|
||||||
next({ ...to, replace: true });
|
next({...to, replace: true});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 路由加载后
|
// 路由加载后
|
||||||
router.afterEach(() => {
|
router.afterEach(() => {
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 导出路由
|
// 导出路由
|
||||||
|
|||||||
+65
-8
@@ -7,8 +7,9 @@ import { setLogger } from '@fast-crud/fast-crud';
|
|||||||
import ui from '@fast-crud/ui-element';
|
import ui from '@fast-crud/ui-element';
|
||||||
import { request } from '/@/utils/service';
|
import { request } from '/@/utils/service';
|
||||||
//扩展包
|
//扩展包
|
||||||
import {FsExtendsEditor} from "@fast-crud/fast-extends";
|
import { FsExtendsEditor,FsExtendsUploader } from '@fast-crud/fast-extends';
|
||||||
import "@fast-crud/fast-extends/dist/style.css";
|
import '@fast-crud/fast-extends/dist/style.css';
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
export default {
|
export default {
|
||||||
async install(app: any, options: any) {
|
async install(app: any, options: any) {
|
||||||
// 先安装ui
|
// 先安装ui
|
||||||
@@ -17,8 +18,11 @@ export default {
|
|||||||
app.use(FastCrud, {
|
app.use(FastCrud, {
|
||||||
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
//i18n, //i18n配置,可选,默认使用中文,具体用法请看demo里的 src/i18n/index.js 文件
|
||||||
// 此处配置公共的dictRequest(字典请求)
|
// 此处配置公共的dictRequest(字典请求)
|
||||||
async dictRequest({ dict }: any) {
|
async dictRequest({ url }: any) {
|
||||||
return await request({ url: dict.url,params:dict.params || {} }); //根据dict的url,异步返回一个字典数组
|
//根据dict的url,异步返回一个字典数组
|
||||||
|
return await request({ url: url, }).then((res:any)=>{
|
||||||
|
return res.data
|
||||||
|
});
|
||||||
},
|
},
|
||||||
//公共crud配置
|
//公共crud配置
|
||||||
commonOptions() {
|
commonOptions() {
|
||||||
@@ -28,13 +32,31 @@ export default {
|
|||||||
//你项目后台接口大概率与fast-crud所需要的返回结构不一致,所以需要配置此项
|
//你项目后台接口大概率与fast-crud所需要的返回结构不一致,所以需要配置此项
|
||||||
//请参考文档http://fast-crud.docmirror.cn/api/crud-options/request.html
|
//请参考文档http://fast-crud.docmirror.cn/api/crud-options/request.html
|
||||||
transformQuery: ({ page, form, sort }: any) => {
|
transformQuery: ({ page, form, sort }: any) => {
|
||||||
|
if (sort.asc !== undefined) {
|
||||||
|
form['ordering'] = `${sort.asc ? '' : '-'}${sort.prop}`;
|
||||||
|
}
|
||||||
//转换为你pageRequest所需要的请求参数结构
|
//转换为你pageRequest所需要的请求参数结构
|
||||||
return { page: page.currentPage, limit: page.pageSize, ...form };
|
return { page: page.currentPage, limit: page.pageSize, ...form };
|
||||||
},
|
},
|
||||||
transformRes: ({ res }: any) => {
|
transformRes: ({ res }: any) => {
|
||||||
//将pageRequest的返回数据,转换为fast-crud所需要的格式
|
//将pageRequest的返回数据,转换为fast-crud所需要的格式
|
||||||
//return {records,currentPage,pageSize,total};
|
//return {records,currentPage,pageSize,total};
|
||||||
return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total };
|
if(res.page){
|
||||||
|
return { records: res.data, currentPage: res.page, pageSize: res.limit, total: res.total };
|
||||||
|
}else{
|
||||||
|
return { records: res.data,currentPage: 1, pageSize: res.data.length, total: res.data.length };
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
afterSubmit(ctx:any ) {
|
||||||
|
const {mode} = ctx
|
||||||
|
if (mode === "add") {
|
||||||
|
ElMessage.success({ message: "添加成功" });
|
||||||
|
} else if (mode === "edit") {
|
||||||
|
ElMessage.success({ message: "保存成功" });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/* search: {
|
/* search: {
|
||||||
@@ -55,9 +77,44 @@ export default {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
//富文本
|
//富文本
|
||||||
app.use(FsExtendsEditor,{
|
app.use(FsExtendsEditor, {
|
||||||
wangEditor:{
|
wangEditor: {
|
||||||
width:300,
|
width: 300,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 文件上传
|
||||||
|
app.use(FsExtendsUploader, {
|
||||||
|
defaultType: "form",
|
||||||
|
form: {
|
||||||
|
action: `/api/system/file/`,
|
||||||
|
name: "file",
|
||||||
|
withCredentials: false,
|
||||||
|
uploadRequest: async ({ action, file, onProgress }) => {
|
||||||
|
// @ts-ignore
|
||||||
|
const data = new FormData();
|
||||||
|
data.append("file", file);
|
||||||
|
return await request({
|
||||||
|
url: action,
|
||||||
|
method: "post",
|
||||||
|
timeout: 60000,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data"
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
onUploadProgress: (p) => {
|
||||||
|
onProgress({ percent: Math.round((p.loaded / p.total) * 100) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
successHandle(ret) {
|
||||||
|
console.log(111,ret)
|
||||||
|
// 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx}
|
||||||
|
return {
|
||||||
|
url: ret.data.url,
|
||||||
|
key: ret.data.id,
|
||||||
|
...ret.data
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setLogger({ level: 'error' });
|
setLogger({ level: 'error' });
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
export interface DataItemType {
|
||||||
|
field_name: string;
|
||||||
|
is_create: boolean;
|
||||||
|
is_query: boolean;
|
||||||
|
is_update: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useColumnPermission = defineStore('columnPermission', {
|
||||||
|
state: () => ({
|
||||||
|
permission: [] as DataItemType[],
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
setPermissionData(data: DataItemType[]) {
|
||||||
|
this.permission = data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -5,11 +5,17 @@
|
|||||||
|
|
||||||
// 用户信息
|
// 用户信息
|
||||||
export interface UserInfosState {
|
export interface UserInfosState {
|
||||||
authBtnList: string[];
|
avatar: string;
|
||||||
photo: string;
|
username: string;
|
||||||
roles: string[];
|
name: string;
|
||||||
time: number;
|
email: string;
|
||||||
userName: string;
|
mobile: string;
|
||||||
|
gender: string;
|
||||||
|
dept_info: {
|
||||||
|
dept_id: number;
|
||||||
|
dept_name: string;
|
||||||
|
};
|
||||||
|
role_info: any[];
|
||||||
}
|
}
|
||||||
export interface UserInfosStates {
|
export interface UserInfosStates {
|
||||||
userInfos: UserInfosState;
|
userInfos: UserInfosState;
|
||||||
@@ -91,4 +97,7 @@ export interface ThemeConfigStates {
|
|||||||
|
|
||||||
export interface DictionaryStates {
|
export interface DictionaryStates {
|
||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
export interface ConfigStates {
|
||||||
|
systemConfig: any;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ConfigStates } from './interface';
|
||||||
|
import { request } from '../utils/service';
|
||||||
|
export const urlPrefix = '/api/init/settings/';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统配置数据
|
||||||
|
* @methods getSystemConfig 获取系统配置数据
|
||||||
|
*/
|
||||||
|
export const SystemConfigStore = defineStore('SystemConfig', {
|
||||||
|
state: (): ConfigStates => ({
|
||||||
|
systemConfig: {},
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async getSystemConfigs() {
|
||||||
|
request({
|
||||||
|
url: urlPrefix,
|
||||||
|
method: 'get',
|
||||||
|
}).then((ret: { data: [] }) => {
|
||||||
|
// 转换数据格式并保存到pinia
|
||||||
|
this.systemConfig = JSON.parse(JSON.stringify(ret.data));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
persist: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
+39
-14
@@ -1,9 +1,7 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import Cookies from 'js-cookie';
|
|
||||||
import { UserInfosStates } from './interface';
|
import { UserInfosStates } from './interface';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
import { request } from '../utils/service';
|
import { request } from '../utils/service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户信息
|
* 用户信息
|
||||||
* @methods setUserInfos 设置用户信息
|
* @methods setUserInfos 设置用户信息
|
||||||
@@ -11,32 +9,59 @@ import { request } from '../utils/service';
|
|||||||
export const useUserInfo = defineStore('userInfo', {
|
export const useUserInfo = defineStore('userInfo', {
|
||||||
state: (): UserInfosStates => ({
|
state: (): UserInfosStates => ({
|
||||||
userInfos: {
|
userInfos: {
|
||||||
userName: '',
|
avatar: '',
|
||||||
photo: '',
|
username: '',
|
||||||
time: 0,
|
name: '',
|
||||||
roles: [],
|
email: '',
|
||||||
authBtnList: [],
|
mobile: '',
|
||||||
|
gender: '',
|
||||||
|
dept_info: {
|
||||||
|
dept_id: 0,
|
||||||
|
dept_name: '',
|
||||||
|
},
|
||||||
|
role_info: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
async updateUserInfos() {
|
||||||
|
let userInfos: any = await this.getApiUserInfo();
|
||||||
|
this.userInfos.username = userInfos.data.name;
|
||||||
|
this.userInfos.avatar = userInfos.data.avatar;
|
||||||
|
this.userInfos.name = userInfos.data.name;
|
||||||
|
this.userInfos.email = userInfos.data.email;
|
||||||
|
this.userInfos.mobile = userInfos.data.mobile;
|
||||||
|
this.userInfos.gender = userInfos.data.gender;
|
||||||
|
this.userInfos.dept_info = userInfos.data.dept_info;
|
||||||
|
this.userInfos.role_info = userInfos.data.role_info;
|
||||||
|
Session.set('userInfo', this.userInfos);
|
||||||
|
},
|
||||||
async setUserInfos() {
|
async setUserInfos() {
|
||||||
// 存储用户信息到浏览器缓存
|
// 存储用户信息到浏览器缓存
|
||||||
if (Session.get('userInfo')) {
|
if (Session.get('userInfo')) {
|
||||||
this.userInfos = Session.get('userInfo');
|
this.userInfos = Session.get('userInfo');
|
||||||
} else {
|
} else {
|
||||||
let userInfos: any = await this.getApiUserInfo();
|
let userInfos: any = await this.getApiUserInfo();
|
||||||
this.userInfos.userName = userInfos.data.name;
|
this.userInfos.username = userInfos.data.name;
|
||||||
this.userInfos.photo = userInfos.data.avatar || 'https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500'
|
this.userInfos.avatar = userInfos.data.avatar;
|
||||||
this.userInfos.time = new Date().getTime()
|
this.userInfos.name = userInfos.data.name;
|
||||||
this.userInfos.roles = ['admin']
|
this.userInfos.email = userInfos.data.email;
|
||||||
Session.set('userInfo', this.userInfos)
|
this.userInfos.mobile = userInfos.data.mobile;
|
||||||
|
this.userInfos.gender = userInfos.data.gender;
|
||||||
|
this.userInfos.dept_info = userInfos.data.dept_info;
|
||||||
|
this.userInfos.role_info = userInfos.data.role_info;
|
||||||
|
Session.set('userInfo', this.userInfos);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getApiUserInfo() {
|
async getApiUserInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/system/user/user_info/',
|
url: '/api/system/user/user_info/',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ declare interface UserInfosState<T = any> {
|
|||||||
photo: string;
|
photo: string;
|
||||||
roles: string[];
|
roles: string[];
|
||||||
time: number;
|
time: number;
|
||||||
userName: string;
|
username: string;
|
||||||
[key: string]: T;
|
[key: string]: T;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -90,7 +90,7 @@ declare type TreeType = {
|
|||||||
|
|
||||||
// user
|
// user
|
||||||
declare type RowUserType<T = any> = {
|
declare type RowUserType<T = any> = {
|
||||||
userName: string;
|
username: string;
|
||||||
userNickname: string;
|
userNickname: string;
|
||||||
roleSign: string;
|
roleSign: string;
|
||||||
department: string[];
|
department: string[];
|
||||||
|
|||||||
+61
-55
@@ -1,61 +1,67 @@
|
|||||||
|
import { pluginsAll } from '/@/views/plugins/index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 校验是否为租户模式。租户模式把域名替换成 域名 加端口
|
* @description 校验是否为租户模式。租户模式把域名替换成 域名 加端口
|
||||||
*/
|
*/
|
||||||
export const getBaseURL = function () {
|
export const getBaseURL = function () {
|
||||||
var baseURL = import.meta.env.VITE_API_URL as any
|
var baseURL = import.meta.env.VITE_API_URL as any;
|
||||||
var param = baseURL.split('/')[3] || ''
|
var param = baseURL.split('/')[3] || '';
|
||||||
if (window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
|
// @ts-ignore
|
||||||
// 1.把127.0.0.1 替换成和前端一样域名
|
if (pluginsAll && pluginsAll.indexOf('dvadmin3-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
|
||||||
// 2.把 ip 地址替换成和前端一样域名
|
// 1.把127.0.0.1 替换成和前端一样域名
|
||||||
// 3.把 /api 或其他类似的替换成和前端一样域名
|
// 2.把 ip 地址替换成和前端一样域名
|
||||||
// document.domain
|
// 3.把 /api 或其他类似的替换成和前端一样域名
|
||||||
var host = baseURL.split('/')[2]
|
// document.domain
|
||||||
if (host) {
|
|
||||||
var prot = baseURL.split(':')[2] || 80
|
var host = baseURL.split('/')[2];
|
||||||
if (prot === 80 || prot === 443) {
|
if (host) {
|
||||||
host = document.domain
|
var port = baseURL.split(':')[2] || 80;
|
||||||
} else {
|
if (port === 80 || port === 443) {
|
||||||
host = document.domain + ':' + prot
|
host = document.domain;
|
||||||
}
|
} else {
|
||||||
baseURL = baseURL.split('/')[0] + '//' + baseURL.split('/')[1] + host + '/' + param
|
host = document.domain + ':' + port;
|
||||||
} else {
|
}
|
||||||
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL
|
baseURL = baseURL.split('/')[0] + '//' + baseURL.split('/')[1] + host + '/' + param;
|
||||||
}
|
} else {
|
||||||
}
|
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL;
|
||||||
if (!baseURL.endsWith('/')) {
|
}
|
||||||
baseURL += '/'
|
}
|
||||||
}
|
if (!baseURL.endsWith('/')) {
|
||||||
return baseURL
|
baseURL += '/';
|
||||||
}
|
}
|
||||||
|
return baseURL;
|
||||||
|
};
|
||||||
|
|
||||||
export const getWsBaseURL = function () {
|
export const getWsBaseURL = function () {
|
||||||
let baseURL = import.meta.env.VITE_API_URL as any
|
let baseURL = import.meta.env.VITE_API_URL as any;
|
||||||
let param = baseURL.split('/')[3] || ''
|
let param = baseURL.split('/')[3] || '';
|
||||||
if (window.pluginsAll && window.pluginsAll.indexOf('dvadmin-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
|
// @ts-ignore
|
||||||
// 1.把127.0.0.1 替换成和前端一样域名
|
if (pluginsAll && pluginsAll.indexOf('dvadmin3-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
|
||||||
// 2.把 ip 地址替换成和前端一样域名
|
// 1.把127.0.0.1 替换成和前端一样域名
|
||||||
// 3.把 /api 或其他类似的替换成和前端一样域名
|
// 2.把 ip 地址替换成和前端一样域名
|
||||||
// document.domain
|
// 3.把 /api 或其他类似的替换成和前端一样域名
|
||||||
var host = baseURL.split('/')[2]
|
// document.domain
|
||||||
if (host) {
|
var host = baseURL.split('/')[2];
|
||||||
var prot = baseURL.split(':')[2] || 80
|
if (host) {
|
||||||
if (prot === 80 || prot === 443) {
|
var port = baseURL.split(':')[2] || 80;
|
||||||
host = document.domain
|
if (port === 80 || port === 443) {
|
||||||
} else {
|
host = document.domain;
|
||||||
host = document.domain + ':' + prot
|
} else {
|
||||||
}
|
host = document.domain + ':' + port;
|
||||||
baseURL = baseURL.split('/')[0] + '//' + baseURL.split('/')[1] + host + '/' + param
|
}
|
||||||
} else {
|
baseURL = baseURL.split('/')[0] + '//' + baseURL.split('/')[1] + host + '/' + param;
|
||||||
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL
|
} else {
|
||||||
}
|
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL;
|
||||||
} else if (param !== '' || baseURL.startsWith('/')) {
|
}
|
||||||
baseURL = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.hostname + (location.port ? ':' : '') + location.port + baseURL
|
} else if (param !== '' || baseURL.startsWith('/')) {
|
||||||
}
|
baseURL = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.hostname + (location.port ? ':' : '') + location.port + baseURL;
|
||||||
if (!baseURL.endsWith('/')) {
|
}
|
||||||
baseURL += '/'
|
if (!baseURL.endsWith('/')) {
|
||||||
}
|
baseURL += '/';
|
||||||
if (baseURL.startsWith('http')) { // https 也默认会被替换成 wss
|
}
|
||||||
baseURL = baseURL.replace('http', 'ws')
|
if (baseURL.startsWith('http')) {
|
||||||
}
|
// https 也默认会被替换成 wss
|
||||||
return baseURL
|
baseURL = baseURL.replace('http', 'ws');
|
||||||
}
|
}
|
||||||
|
return baseURL;
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { useColumnPermission } from '/@/stores/columnPermission';
|
||||||
|
|
||||||
|
type permissionType = 'is_create' | 'is_query' | 'is_update';
|
||||||
|
|
||||||
|
export const columnPermission = (key: string, type: permissionType): boolean => {
|
||||||
|
const permissions = useColumnPermission().permission || [];
|
||||||
|
|
||||||
|
return !!permissions.some((i) => i.field_name === key && i[type]);
|
||||||
|
};
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
import { toRaw } from 'vue';
|
import { toRaw } from 'vue';
|
||||||
import { DictionaryStore } from '/@/stores/dictionary';
|
import { DictionaryStore } from '/@/stores/dictionary';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method 获取指定name字典
|
* @method 获取指定name字典
|
||||||
*/
|
*/
|
||||||
export const dictionary = (name: string) => {
|
export const dictionary = (key: string,value:string|number|null=null) => {
|
||||||
const dict = DictionaryStore()
|
const dict = DictionaryStore()
|
||||||
const dictionary = toRaw(dict.data)
|
const dictionary = toRaw(dict.data)
|
||||||
return dictionary[name]
|
if(value!==null){
|
||||||
}
|
for (let item of dictionary[key]) {
|
||||||
|
if (item.value === value) {
|
||||||
|
return item.label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return dictionary[key]
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ export const handleMenu = (menuData: Array<any>) => {
|
|||||||
title: item.title,
|
title: item.title,
|
||||||
isLink: item.is_link,
|
isLink: item.is_link,
|
||||||
isHide: !item.visible,
|
isHide: !item.visible,
|
||||||
isKeepAlive: true,
|
isKeepAlive: item.cache,
|
||||||
isAffix: false,
|
isAffix: false,
|
||||||
isIframe: false,
|
isIframe: false,
|
||||||
roles: ['admin'],
|
roles: ['admin'],
|
||||||
icon: item.icon
|
icon: item.icon
|
||||||
}
|
}
|
||||||
|
item.name = item.component_name
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
menuData.forEach((val) => {
|
menuData.forEach((val) => {
|
||||||
@@ -46,4 +46,4 @@ export const handleMenu = (menuData: Array<any>) => {
|
|||||||
...data
|
...data
|
||||||
]
|
]
|
||||||
return menu
|
return menu
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ElMessage, MessageOptions } from 'element-plus';
|
import { ElMessage, ElNotification, MessageOptions } from 'element-plus';
|
||||||
|
|
||||||
export function message(message: string, option?: MessageOptions) {
|
export function message(message: string, option?: MessageOptions) {
|
||||||
ElMessage({ message, ...option });
|
ElMessage({ message, ...option });
|
||||||
}
|
}
|
||||||
export function successMessage(message: string, option?: MessageOptions) {
|
export function successMessage(message: string, option?: MessageOptions) {
|
||||||
ElMessage({ message, ...option, type: 'success' });
|
ElMessage({ message, type: 'success' });
|
||||||
}
|
}
|
||||||
export function warningMessage(message: string, option?: MessageOptions) {
|
export function warningMessage(message: string, option?: MessageOptions) {
|
||||||
ElMessage({ message, ...option, type: 'warning' });
|
ElMessage({ message, ...option, type: 'warning' });
|
||||||
@@ -15,3 +15,19 @@ export function errorMessage(message: string, option?: MessageOptions) {
|
|||||||
export function infoMessage(message: string, option?: MessageOptions) {
|
export function infoMessage(message: string, option?: MessageOptions) {
|
||||||
ElMessage({ message, ...option, type: 'info' });
|
ElMessage({ message, ...option, type: 'info' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function notification(message: string) {
|
||||||
|
ElNotification({ message });
|
||||||
|
}
|
||||||
|
export function successNotification(message: string) {
|
||||||
|
ElNotification({ message, type: 'success' });
|
||||||
|
}
|
||||||
|
export function warningNotification(message: string) {
|
||||||
|
ElNotification({ message, type: 'warning' });
|
||||||
|
}
|
||||||
|
export function errorNotification(message: string) {
|
||||||
|
ElNotification({ message, type: 'error' });
|
||||||
|
}
|
||||||
|
export function infoNotification(message: string) {
|
||||||
|
ElNotification({ message, type: 'info' });
|
||||||
|
}
|
||||||
|
|||||||
+66
-16
@@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { get } from 'lodash-es';
|
import { get } from 'lodash-es';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import type { Action } from 'element-plus'
|
import type { Action } from 'element-plus';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { errorLog, errorCreate } from './tools.ts';
|
import { errorLog, errorCreate } from './tools.ts';
|
||||||
@@ -9,6 +9,7 @@ import { errorLog, errorCreate } from './tools.ts';
|
|||||||
// import { useUserStore } from "../store/modules/user";
|
// import { useUserStore } from "../store/modules/user";
|
||||||
import { Local, Session } from '/@/utils/storage';
|
import { Local, Session } from '/@/utils/storage';
|
||||||
import qs from 'qs';
|
import qs from 'qs';
|
||||||
|
import { getBaseURL } from './baseUrl';
|
||||||
/**
|
/**
|
||||||
* @description 创建请求实例
|
* @description 创建请求实例
|
||||||
*/
|
*/
|
||||||
@@ -17,16 +18,23 @@ function createService() {
|
|||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
timeout: 20000,
|
timeout: 20000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json;charset=utf-8'
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
},
|
},
|
||||||
paramsSerializer: {
|
paramsSerializer: {
|
||||||
serialize(params) {
|
serialize(params) {
|
||||||
return qs.stringify(params, { indices: false,encoder: (val:string) => {
|
interface paramsObj {
|
||||||
if (typeof val === 'boolean') {
|
[key: string]: any;
|
||||||
return val ? 1 : 0;
|
}
|
||||||
}
|
let result: paramsObj = {};
|
||||||
return val;
|
for (const [key, value] of Object.entries(params)) {
|
||||||
} });
|
if (value !== '') {
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
result[key] = value ? 'True' : 'False';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qs.stringify(result);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -62,21 +70,21 @@ function createService() {
|
|||||||
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 400:
|
case 400:
|
||||||
Local.clear();
|
// Local.clear();
|
||||||
Session.clear();
|
// Session.clear();
|
||||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||||
window.location.reload();
|
// window.location.reload();
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
Local.clear();
|
Local.clear();
|
||||||
Session.clear();
|
Session.clear();
|
||||||
dataAxios.msg = '登录授权过期,请重新登录';
|
dataAxios.msg = '登录认证失败,请重新登录';
|
||||||
ElMessageBox.alert(dataAxios.msg, '提示', {
|
ElMessageBox.alert(dataAxios.msg, '提示', {
|
||||||
confirmButtonText: 'OK',
|
confirmButtonText: 'OK',
|
||||||
callback: (action: Action) => {
|
callback: (action: Action) => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||||
break;
|
break;
|
||||||
case 2000:
|
case 2000:
|
||||||
@@ -86,6 +94,9 @@ function createService() {
|
|||||||
return dataAxios;
|
return dataAxios;
|
||||||
}
|
}
|
||||||
return dataAxios;
|
return dataAxios;
|
||||||
|
case 4000:
|
||||||
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||||
|
return dataAxios;
|
||||||
default:
|
default:
|
||||||
// 不是正确的 code
|
// 不是正确的 code
|
||||||
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
|
||||||
@@ -100,7 +111,15 @@ function createService() {
|
|||||||
error.message = '请求错误';
|
error.message = '请求错误';
|
||||||
break;
|
break;
|
||||||
case 401:
|
case 401:
|
||||||
error.message = '未授权,请登录';
|
Local.clear();
|
||||||
|
Session.clear();
|
||||||
|
error.message = '登录授权过期,请重新登录';
|
||||||
|
ElMessageBox.alert(error.message, '提示', {
|
||||||
|
confirmButtonText: 'OK',
|
||||||
|
callback: (action: Action) => {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case 403:
|
case 403:
|
||||||
error.message = '拒绝访问';
|
error.message = '拒绝访问';
|
||||||
@@ -154,7 +173,7 @@ function createRequestFunction(service: any) {
|
|||||||
'Content-Type': get(config, 'headers.Content-Type', 'application/json'),
|
'Content-Type': get(config, 'headers.Content-Type', 'application/json'),
|
||||||
},
|
},
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
baseURL: import.meta.env.VITE_API_URL as any,
|
baseURL: getBaseURL(),
|
||||||
data: {},
|
data: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,3 +194,34 @@ export const request = createRequestFunction(service);
|
|||||||
// 用于模拟网络请求的实例和请求方法
|
// 用于模拟网络请求的实例和请求方法
|
||||||
export const serviceForMock = createService();
|
export const serviceForMock = createService();
|
||||||
export const requestForMock = createRequestFunction(serviceForMock);
|
export const requestForMock = createRequestFunction(serviceForMock);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
* @param url
|
||||||
|
* @param params
|
||||||
|
* @param method
|
||||||
|
* @param filename
|
||||||
|
*/
|
||||||
|
export const downloadFile = function ({ url, params, method, filename = '文件导出' }: any) {
|
||||||
|
request({
|
||||||
|
url: url,
|
||||||
|
method: method,
|
||||||
|
params: params,
|
||||||
|
responseType: 'blob'
|
||||||
|
// headers: {Accept: 'application/vnd.openxmlformats-officedocument'}
|
||||||
|
}).then((res: any) => {
|
||||||
|
const xlsxName = window.decodeURI(res.headers['content-disposition'].split('=')[1])
|
||||||
|
const fileName = xlsxName || `${filename}.xlsx`
|
||||||
|
if (res) {
|
||||||
|
const blob = new Blob([res.data], { type: 'charset=utf-8' })
|
||||||
|
const elink = document.createElement('a')
|
||||||
|
elink.download = fileName
|
||||||
|
elink.style.display = 'none'
|
||||||
|
elink.href = URL.createObjectURL(blob)
|
||||||
|
document.body.appendChild(elink)
|
||||||
|
elink.click()
|
||||||
|
URL.revokeObjectURL(elink.href) // 释放URL 对象0
|
||||||
|
document.body.removeChild(elink)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
const cssCdnUrlList: Array<string> = [
|
const cssCdnUrlList: Array<string> = [
|
||||||
'//at.alicdn.com/t/font_2298093_y6u00apwst.css',
|
'//at.alicdn.com/t/font_2298093_y6u00apwst.css',
|
||||||
'//at.alicdn.com/t/c/font_3882322_9ah7y8m9175.css', //dvadmin3项目用icon
|
'//at.alicdn.com/t/c/font_3882322_9ah7y8m9175.css', //dvadmin3项目用icon
|
||||||
'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
|
|
||||||
];
|
];
|
||||||
// 第三方 js url
|
// 第三方 js url
|
||||||
const jsCdnUrlList: Array<string> = [];
|
const jsCdnUrlList: Array<string> = [];
|
||||||
|
|||||||
+59
-27
@@ -3,16 +3,16 @@
|
|||||||
* @param {String} jsonString 需要解析的 json 字符串
|
* @param {String} jsonString 需要解析的 json 字符串
|
||||||
* @param {String} defaultValue 默认值
|
* @param {String} defaultValue 默认值
|
||||||
*/
|
*/
|
||||||
import { uiContext } from "@fast-crud/fast-crud";
|
import { uiContext } from '@fast-crud/fast-crud';
|
||||||
|
|
||||||
export function parse(jsonString = "{}", defaultValue = {}) {
|
export function parse(jsonString = '{}', defaultValue = {}) {
|
||||||
let result = defaultValue;
|
let result = defaultValue;
|
||||||
try {
|
try {
|
||||||
result = JSON.parse(jsonString);
|
result = JSON.parse(jsonString);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,8 +21,8 @@ export function parse(jsonString = "{}", defaultValue = {}) {
|
|||||||
* @param {String} msg 状态信息
|
* @param {String} msg 状态信息
|
||||||
* @param {Number} code 状态码
|
* @param {Number} code 状态码
|
||||||
*/
|
*/
|
||||||
export function response(data = {}, msg = "", code = 0) {
|
export function response(data = {}, msg = '', code = 0) {
|
||||||
return [200, { code, msg, data }];
|
return [200, { code, msg, data }];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,8 +30,8 @@ export function response(data = {}, msg = "", code = 0) {
|
|||||||
* @param {Any} data 返回值
|
* @param {Any} data 返回值
|
||||||
* @param {String} msg 状态信息
|
* @param {String} msg 状态信息
|
||||||
*/
|
*/
|
||||||
export function responseSuccess(data = {}, msg = "成功") {
|
export function responseSuccess(data = {}, msg = '成功') {
|
||||||
return response(data, msg);
|
return response(data, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,30 +40,62 @@ export function responseSuccess(data = {}, msg = "成功") {
|
|||||||
* @param {String} msg 状态信息
|
* @param {String} msg 状态信息
|
||||||
* @param {Number} code 状态码
|
* @param {Number} code 状态码
|
||||||
*/
|
*/
|
||||||
export function responseError(data = {}, msg = "请求失败", code = 500) {
|
export function responseError(data = {}, msg = '请求失败', code = 500) {
|
||||||
return response(data, msg, code);
|
return response(data, msg, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 记录和显示错误
|
* @description 记录和显示错误
|
||||||
* @param {Error} error 错误对象
|
* @param {Error} error 错误对象
|
||||||
*/
|
*/
|
||||||
export function errorLog(error:any,notification=true) {
|
export function errorLog(error: any, notification = true) {
|
||||||
// 打印到控制台
|
// 打印到控制台
|
||||||
console.error(error);
|
console.error(error);
|
||||||
// 显示提示
|
// 显示提示
|
||||||
if(notification){
|
if (notification) {
|
||||||
uiContext.get().notification.error({ message: error.message });
|
uiContext.get().notification.error({ message: error.message });
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 创建一个错误
|
* @description 创建一个错误
|
||||||
* @param {String} msg 错误信息
|
* @param {String} msg 错误信息
|
||||||
*/
|
*/
|
||||||
export function errorCreate(msg:any,notification=true) {
|
export function errorCreate(msg: any, notification = true) {
|
||||||
const error = new Error(msg);
|
const error = new Error(msg);
|
||||||
errorLog(error,notification);
|
errorLog(error, notification);
|
||||||
// throw error;
|
// throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description base64转file
|
||||||
|
* @param {String} base64 base64字符串
|
||||||
|
* @param {String} fileName 文件名
|
||||||
|
*/
|
||||||
|
export function base64ToFile(base64: any, fileName: string) {
|
||||||
|
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
|
||||||
|
let data = base64.split(',');
|
||||||
|
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
|
||||||
|
let type = data[0].match(/:(.*?);/)[1];
|
||||||
|
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
|
||||||
|
let suffix = type.split('/')[1];
|
||||||
|
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
|
||||||
|
const bstr = window.atob(data[1]);
|
||||||
|
// 获取解码结果字符串的长度
|
||||||
|
let n = bstr.length;
|
||||||
|
// 根据解码结果字符串的长度创建一个等长的整形数字数组
|
||||||
|
// 但在创建时 所有元素初始值都为 0
|
||||||
|
const u8arr = new Uint8Array(n);
|
||||||
|
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
|
||||||
|
while (n--) {
|
||||||
|
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
|
||||||
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
|
}
|
||||||
|
// 利用构造函数创建File文件对象
|
||||||
|
// new File(bits, name, options)
|
||||||
|
const file = new File([u8arr], `${fileName}.${suffix}`, {
|
||||||
|
type: type,
|
||||||
|
});
|
||||||
|
// 将File文件对象返回给方法的调用者
|
||||||
|
return file;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const websocket: socket = {
|
|||||||
}
|
}
|
||||||
const token = Session.get('token')
|
const token = Session.get('token')
|
||||||
if(!token){
|
if(!token){
|
||||||
message.warning('websocket认证失败')
|
// message.warning('websocket认证失败')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const wsUrl = `${getWsBaseURL()}ws/${token}/`
|
const wsUrl = `${getWsBaseURL()}ws/${token}/`
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* 后端API接口响应数据
|
||||||
|
*/
|
||||||
|
interface APIResponseData {
|
||||||
|
code?: number;
|
||||||
|
data: [];
|
||||||
|
msg?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { defineAsyncComponent, AsyncComponentLoader } from 'vue';
|
||||||
|
export let pluginsAll: any = [];
|
||||||
|
// 扫描插件目录并注册插件
|
||||||
|
export const scanAndInstallPlugins = (app: any) => {
|
||||||
|
const components = import.meta.glob('./**/*.vue');
|
||||||
|
const pluginNames = new Set();
|
||||||
|
// 遍历对象并注册异步组件
|
||||||
|
for (const [key, value] of Object.entries(components)) {
|
||||||
|
const name = key.slice(key.lastIndexOf('/') + 1, key.lastIndexOf('.'));
|
||||||
|
app.component(name, defineAsyncComponent(value as AsyncComponentLoader));
|
||||||
|
const pluginsName = key.match(/\/([^\/]*)\//)?.[1];
|
||||||
|
pluginNames.add(pluginsName);
|
||||||
|
}
|
||||||
|
pluginsAll = Array.from(pluginNames);
|
||||||
|
console.table('已注册插件:', pluginsAll);
|
||||||
|
};
|
||||||
@@ -1,41 +1,41 @@
|
|||||||
import { request } from '/@/utils/service';
|
import { request } from '/@/utils/service';
|
||||||
import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
|
||||||
|
|
||||||
export const apiPrefix = '/api/system/area/';
|
export const apiPrefix = '/api/system/area/';
|
||||||
export function GetList(query: PageQuery) {
|
export function GetList(query: UserPageQuery) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query,
|
params: query,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
export function GetObj(id: InfoReq) {
|
export function GetObj(id: InfoReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id,
|
url: apiPrefix + id,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AddObj(obj: AddReq) {
|
export function AddObj(obj: AddReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix,
|
url: apiPrefix,
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UpdateObj(obj: EditReq) {
|
export function UpdateObj(obj: EditReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + obj.id + '/',
|
url: apiPrefix + obj.id + '/',
|
||||||
method: 'put',
|
method: 'put',
|
||||||
data: obj,
|
data: obj,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DelObj(id: DelReq) {
|
export function DelObj(id: DelReq) {
|
||||||
return request({
|
return request({
|
||||||
url: apiPrefix + id + '/',
|
url: apiPrefix + id + '/',
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
data: { id },
|
data: { id },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import * as api from './api';
|
import * as api from './api';
|
||||||
import { dict, PageQuery, AddReq, DelReq, EditReq, CrudExpose, CrudOptions } from '@fast-crud/fast-crud';
|
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
|
||||||
import { request } from '/@/utils/service';
|
|
||||||
import { dictionary } from '/@/utils/dictionary';
|
import { dictionary } from '/@/utils/dictionary';
|
||||||
interface CreateCrudOptionsTypes {
|
import { successMessage } from '/@/utils/message';
|
||||||
crudOptions: CrudOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExpose }): CreateCrudOptionsTypes {
|
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const pageRequest = async (query: PageQuery) => {
|
const pageRequest = async (query: UserPageQuery) => {
|
||||||
return await api.GetList(query);
|
return await api.GetList(query);
|
||||||
};
|
};
|
||||||
const editRequest = async ({ form, row }: EditReq) => {
|
const editRequest = async ({ form, row }: EditReq) => {
|
||||||
@@ -26,8 +23,8 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
* @param row
|
* @param row
|
||||||
* @returns {Promise<unknown>}
|
* @returns {Promise<unknown>}
|
||||||
*/
|
*/
|
||||||
const loadContentMethod = (tree: any, treeNode: any, resolve: any) => {
|
const loadContentMethod = (tree: any, treeNode: any, resolve: Function) => {
|
||||||
api.GetList({ pcode: tree.code }).then((res: any) => {
|
pageRequest({ pcode: tree.code }).then((res: APIResponseData) => {
|
||||||
resolve(res.data);
|
resolve(res.data);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -40,6 +37,24 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
editRequest,
|
editRequest,
|
||||||
delRequest,
|
delRequest,
|
||||||
},
|
},
|
||||||
|
rowHandle: {
|
||||||
|
//固定右侧
|
||||||
|
fixed: 'right',
|
||||||
|
width: 200,
|
||||||
|
buttons: {
|
||||||
|
view: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
iconRight: 'Edit',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
iconRight: 'Delete',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
@@ -90,8 +105,10 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
treeNode: true,
|
treeNode: true,
|
||||||
width: 160,
|
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -108,6 +125,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 90,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -124,6 +144,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 120,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -140,6 +163,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
disabled: true,
|
disabled: true,
|
||||||
},
|
},
|
||||||
type: 'input',
|
type: 'input',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
rules: [
|
rules: [
|
||||||
@@ -153,6 +179,9 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
},
|
},
|
||||||
initials: {
|
initials: {
|
||||||
title: '首字母',
|
title: '首字母',
|
||||||
|
column:{
|
||||||
|
minWidth: 100,
|
||||||
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [
|
rules: [
|
||||||
// 表单校验规则
|
// 表单校验规则
|
||||||
@@ -169,14 +198,26 @@ export const createCrudOptions = function ({ crudExpose }: { crudExpose: CrudExp
|
|||||||
search: {
|
search: {
|
||||||
show: true,
|
show: true,
|
||||||
},
|
},
|
||||||
width: 90,
|
|
||||||
type: 'dict-radio',
|
type: 'dict-radio',
|
||||||
|
column: {
|
||||||
|
minWidth:90,
|
||||||
|
component: {
|
||||||
|
name: 'fs-dict-switch',
|
||||||
|
activeText: '',
|
||||||
|
inactiveText: '',
|
||||||
|
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
|
||||||
|
onChange: compute((context) => {
|
||||||
|
return () => {
|
||||||
|
api.UpdateObj(context.row).then((res: APIResponseData) => {
|
||||||
|
successMessage(res.msg as string);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
dict: dict({
|
dict: dict({
|
||||||
data: dictionary('button_status_bool'),
|
data: dictionary('button_status_bool'),
|
||||||
}),
|
}),
|
||||||
form: {
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,20 +4,12 @@
|
|||||||
</fs-page>
|
</fs-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup name="areas">
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useExpose, useCrud } from '@fast-crud/fast-crud';
|
import { useFs } from '@fast-crud/fast-crud';
|
||||||
import { createCrudOptions } from './crud';
|
import { createCrudOptions } from './crud';
|
||||||
// crud组件的ref
|
|
||||||
const crudRef = ref();
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||||
// crud 配置的ref
|
|
||||||
const crudBinding = ref();
|
|
||||||
// 暴露的方法
|
|
||||||
const { crudExpose } = useExpose({ crudRef, crudBinding });
|
|
||||||
// 你的crud配置
|
|
||||||
const { crudOptions } = createCrudOptions({ crudExpose });
|
|
||||||
// 初始化crud配置
|
|
||||||
const { resetCrudOptions } = useCrud({ crudExpose, crudOptions });
|
|
||||||
|
|
||||||
// 页面打开后获取列表数据
|
// 页面打开后获取列表数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { request } from '/@/utils/service';
|
||||||
|
import { PageQuery } from './types'
|
||||||
|
|
||||||
|
export function getRoleList(query: PageQuery) {
|
||||||
|
return request({
|
||||||
|
url: '/api/system/role/',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMenuList(query: PageQuery) {
|
||||||
|
return request({
|
||||||
|
url: '/api/system/menu/',
|
||||||
|
method: 'get',
|
||||||
|
params: {is_catalog:0,...query},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getModelList() {
|
||||||
|
return request({
|
||||||
|
url: '/api/system/column/get_models/',
|
||||||
|
method: 'get',
|
||||||
|
});
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user