!53 功能重构 & 部分bug修复

后端
refactor: 部分定制化配置迁移至conf.env
feature:验证码启停状态查询接口
feature:重置密码(自动恢复为系统默认密码,原代码块已注释,可以转为更改密码功能)
feature:unique mobile
fixed:修复控制台log重复打印
前端
fixed:更新手机号正则,适配19x/16x等号段
feature:前后端登录验证码启停同步(建议结合system_config集成至后台管理)
refactor: getData请求参数
pull/54/head
dvadmin 2022-04-26 13:14:02 +00:00 committed by Gitee
commit 8509a18816
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
18 changed files with 589 additions and 492 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
/backend/venv /backend/venv
/backend/.idea /backend/.idea
.idea .idea
.history/

4
backend/.gitignore vendored
View File

@ -88,11 +88,11 @@ ENV/
.idea/ .idea/
*.db *.db
.DS_Store .DS_Store
__pycache__ **/migrations/*.py
**/migrations
!**/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__/

View File

@ -27,108 +27,111 @@ from conf.env import *
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure--z8%exyzt7e_%i@1+#1mm=%lb5=^fx_57=1@a+_y7bg5-w%)sm' SECRET_KEY = "django-insecure--z8%exyzt7e_%i@1+#1mm=%lb5=^fx_57=1@a+_y7bg5-w%)sm"
# 初始化plugins插件路径到环境变量中 # 初始化plugins插件路径到环境变量中
PLUGINS_PATH = os.path.join(BASE_DIR, 'plugins') PLUGINS_PATH = os.path.join(BASE_DIR, "plugins")
sys.path.insert(0, os.path.join(PLUGINS_PATH)) sys.path.insert(0, os.path.join(PLUGINS_PATH))
[sys.path.insert(0, os.path.join(PLUGINS_PATH, ele)) for ele in os.listdir(PLUGINS_PATH) if [
os.path.isdir(os.path.join(PLUGINS_PATH, ele)) and not ele.startswith('__')] sys.path.insert(0, os.path.join(PLUGINS_PATH, ele))
for ele in os.listdir(PLUGINS_PATH)
if os.path.isdir(os.path.join(PLUGINS_PATH, ele)) and not ele.startswith("__")
]
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = locals().get('DEBUG', True) DEBUG = locals().get("DEBUG", True)
ALLOWED_HOSTS = locals().get('ALLOWED_HOSTS', ['*']) ALLOWED_HOSTS = locals().get("ALLOWED_HOSTS", ["*"])
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.staticfiles', "django.contrib.staticfiles",
'django_comment_migrate', "django_comment_migrate",
'rest_framework', "rest_framework",
'django_filters', "django_filters",
'corsheaders', # 注册跨域app "corsheaders", # 注册跨域app
'dvadmin.system', "dvadmin.system",
'drf_yasg', "drf_yasg",
'captcha', "captcha",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'whitenoise.middleware.WhiteNoiseMiddleware', "whitenoise.middleware.WhiteNoiseMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'corsheaders.middleware.CorsMiddleware', # 跨域中间件 "corsheaders.middleware.CorsMiddleware", # 跨域中间件
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
'dvadmin.utils.middleware.ApiLoggingMiddleware', "dvadmin.utils.middleware.ApiLoggingMiddleware",
] ]
ROOT_URLCONF = 'application.urls' ROOT_URLCONF = "application.urls"
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [os.path.join(BASE_DIR, 'templates')], "DIRS": [os.path.join(BASE_DIR, "templates")],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'application.wsgi.application' WSGI_APPLICATION = "application.wsgi.application"
# Database # Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases # https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': DATABASE_ENGINE, "ENGINE": DATABASE_ENGINE,
'NAME': DATABASE_NAME, "NAME": DATABASE_NAME,
'USER': DATABASE_USER, "USER": DATABASE_USER,
'PASSWORD': DATABASE_PASSWORD, "PASSWORD": DATABASE_PASSWORD,
'HOST': DATABASE_HOST, "HOST": DATABASE_HOST,
'PORT': DATABASE_PORT, "PORT": DATABASE_PORT,
} }
} }
AUTH_USER_MODEL = 'system.Users' AUTH_USER_MODEL = "system.Users"
USERNAME_FIELD = 'username' USERNAME_FIELD = "username"
# Password validation # Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
}, },
] ]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/ # https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'zh-hans' LANGUAGE_CODE = "zh-hans"
TIME_ZONE = 'Asia/Shanghai' TIME_ZONE = "Asia/Shanghai"
USE_I18N = True USE_I18N = True
@ -139,13 +142,13 @@ USE_TZ = False
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/ # https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = "/static/"
# # 设置django的静态文件目录 # # 设置django的静态文件目录
STATICFILES_DIRS = [ STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"), os.path.join(BASE_DIR, "static"),
] ]
MEDIA_ROOT = 'media' # 项目下的目录 MEDIA_ROOT = "media" # 项目下的目录
MEDIA_URL = "/media/" # 跟STATIC_URL类似指定用户可以通过这个url找到文件 MEDIA_URL = "/media/" # 跟STATIC_URL类似指定用户可以通过这个url找到文件
# 收集静态文件,必须将 MEDIA_ROOT,STATICFILES_DIRS先注释 # 收集静态文件,必须将 MEDIA_ROOT,STATICFILES_DIRS先注释
@ -166,78 +169,82 @@ CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持
# ================================================= # # ================================================= #
# log 配置部分BEGIN # # log 配置部分BEGIN #
SERVER_LOGS_FILE = os.path.join(BASE_DIR, 'logs', 'server.log') SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
ERROR_LOGS_FILE = os.path.join(BASE_DIR, 'logs', 'error.log') ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
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] 这是一条日志: # 格式:[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' STANDARD_LOG_FORMAT = (
CONSOLE_LOG_FORMAT = '[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s' "[%(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": {
'standard': { "standard": {"format": STANDARD_LOG_FORMAT},
'format': STANDARD_LOG_FORMAT "console": {
"format": CONSOLE_LOG_FORMAT,
"datefmt": "%Y-%m-%d %H:%M:%S",
}, },
'console': { "file": {
'format': CONSOLE_LOG_FORMAT, "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": {
'file': { "file": {
'level': 'INFO', "level": "INFO",
'class': 'logging.handlers.RotatingFileHandler', "class": "logging.handlers.RotatingFileHandler",
'filename': SERVER_LOGS_FILE, "filename": SERVER_LOGS_FILE,
'maxBytes': 1024 * 1024 * 100, # 100 MB "maxBytes": 1024 * 1024 * 100, # 100 MB
'backupCount': 5, # 最多备份5个 "backupCount": 5, # 最多备份5个
'formatter': 'standard', "formatter": "standard",
'encoding': 'utf-8', "encoding": "utf-8",
}, },
'error': { "error": {
'level': 'ERROR', "level": "ERROR",
'class': 'logging.handlers.RotatingFileHandler', "class": "logging.handlers.RotatingFileHandler",
'filename': ERROR_LOGS_FILE, "filename": ERROR_LOGS_FILE,
'maxBytes': 1024 * 1024 * 100, # 100 MB "maxBytes": 1024 * 1024 * 100, # 100 MB
'backupCount': 3, # 最多备份3个 "backupCount": 3, # 最多备份3个
'formatter': 'standard', "formatter": "standard",
'encoding': 'utf-8', "encoding": "utf-8",
},
"console": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "console",
}, },
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'console',
}
}, },
'loggers': { "loggers": {
# default日志 # default日志
'': { "": {
'handlers': ['console', 'error', 'file'], "handlers": ["console", "error", "file"],
'level': 'INFO', "level": "INFO",
}, },
'django': { "django": {
'handlers': ['console', 'error', 'file'], "handlers": ["console", "error", "file"],
'level': 'INFO', "level": "INFO",
"propagate": False,
}, },
'scripts': { "scripts": {
'handlers': ['console', 'error', 'file'], "handlers": ["console", "error", "file"],
'level': 'INFO', "level": "INFO",
"propagate": False,
}, },
# 数据库相关日志 # 数据库相关日志
'django.db.backends': { "django.db.backends": {
'handlers': [], "handlers": [],
'propagate': True, "propagate": True,
'level': 'INFO', "level": "INFO",
}, },
} },
} }
# ================================================= # # ================================================= #
@ -245,34 +252,32 @@ LOGGING = {
# ================================================= # # ================================================= #
REST_FRAMEWORK = { REST_FRAMEWORK = {
'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",
'DEFAULT_FILTER_BACKENDS': ( "DEFAULT_FILTER_BACKENDS": (
# 'django_filters.rest_framework.DjangoFilterBackend', # 'django_filters.rest_framework.DjangoFilterBackend',
'dvadmin.utils.filters.CustomDjangoFilterBackend', "dvadmin.utils.filters.CustomDjangoFilterBackend",
'rest_framework.filters.SearchFilter', "rest_framework.filters.SearchFilter",
'rest_framework.filters.OrderingFilter', "rest_framework.filters.OrderingFilter",
), ),
'DEFAULT_PAGINATION_CLASS': 'dvadmin.utils.pagination.CustomPagination', # 自定义分页 "DEFAULT_PAGINATION_CLASS": "dvadmin.utils.pagination.CustomPagination", # 自定义分页
'DEFAULT_AUTHENTICATION_CLASSES': ( "DEFAULT_AUTHENTICATION_CLASSES": (
'rest_framework_simplejwt.authentication.JWTAuthentication', "rest_framework_simplejwt.authentication.JWTAuthentication",
'rest_framework.authentication.SessionAuthentication', "rest_framework.authentication.SessionAuthentication",
), ),
'DEFAULT_PERMISSION_CLASSES': [ "DEFAULT_PERMISSION_CLASSES": [
'rest_framework.permissions.IsAuthenticated', # 只有经过身份认证确定用户身份才能访问 "rest_framework.permissions.IsAuthenticated", # 只有经过身份认证确定用户身份才能访问
# 'rest_framework.permissions.IsAdminUser', # is_staff=True才能访问 —— 管理员(员工)权限 # 'rest_framework.permissions.IsAdminUser', # is_staff=True才能访问 —— 管理员(员工)权限
# 'rest_framework.permissions.AllowAny', # 允许所有 # 'rest_framework.permissions.AllowAny', # 允许所有
# 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 有身份 或者 只读访问(self.list,self.retrieve) # 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 有身份 或者 只读访问(self.list,self.retrieve)
], ],
'EXCEPTION_HANDLER': 'dvadmin.utils.exception.CustomExceptionHandler', # 自定义的异常处理 "EXCEPTION_HANDLER": "dvadmin.utils.exception.CustomExceptionHandler", # 自定义的异常处理
} }
# ================================================= # # ================================================= #
# ******************** 登录方式配置 ******************** # # ******************** 登录方式配置 ******************** #
# ================================================= # # ================================================= #
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = ["dvadmin.utils.backends.CustomBackend"]
'dvadmin.utils.backends.CustomBackend'
]
# ================================================= # # ================================================= #
# ****************** simplejwt配置 ***************** # # ****************** simplejwt配置 ***************** #
# ================================================= # # ================================================= #
@ -280,12 +285,12 @@ from datetime import timedelta
SIMPLE_JWT = { SIMPLE_JWT = {
# token有效时长 # token有效时长
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=120), "ACCESS_TOKEN_LIFETIME": timedelta(minutes=120),
# token刷新后的有效时间 # token刷新后的有效时间
'REFRESH_TOKEN_LIFETIME': timedelta(days=1), "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
# 设置前缀 # 设置前缀
'AUTH_HEADER_TYPES': ('JWT',), "AUTH_HEADER_TYPES": ("JWT",),
'ROTATE_REFRESH_TOKENS': True, "ROTATE_REFRESH_TOKENS": True,
} }
# ====================================# # ====================================#
@ -293,70 +298,63 @@ SIMPLE_JWT = {
# ====================================# # ====================================#
SWAGGER_SETTINGS = { SWAGGER_SETTINGS = {
# 基础样式 # 基础样式
'SECURITY_DEFINITIONS': { "SECURITY_DEFINITIONS": {"basic": {"type": "basic"}},
"basic": {
'type': 'basic'
}
},
# 如果需要登录才能够查看接口文档, 登录的链接使用restframework自带的. # 如果需要登录才能够查看接口文档, 登录的链接使用restframework自带的.
"LOGIN_URL": "apiLogin/",
'LOGIN_URL': 'apiLogin/',
# 'LOGIN_URL': 'rest_framework:login', # 'LOGIN_URL': 'rest_framework:login',
'LOGOUT_URL': 'rest_framework:logout', "LOGOUT_URL": "rest_framework:logout",
# 'DOC_EXPANSION': None, # 'DOC_EXPANSION': None,
# 'SHOW_REQUEST_HEADERS':True, # 'SHOW_REQUEST_HEADERS':True,
# 'USE_SESSION_AUTH': True, # 'USE_SESSION_AUTH': True,
# 'DOC_EXPANSION': 'list', # 'DOC_EXPANSION': 'list',
# 接口文档中方法列表以首字母升序排列 # 接口文档中方法列表以首字母升序排列
'APIS_SORTER': 'alpha', "APIS_SORTER": "alpha",
# 如果支持json提交, 则接口文档中包含json输入框 # 如果支持json提交, 则接口文档中包含json输入框
'JSON_EDITOR': True, "JSON_EDITOR": True,
# 方法列表字母排序 # 方法列表字母排序
'OPERATIONS_SORTER': 'alpha', "OPERATIONS_SORTER": "alpha",
'VALIDATOR_URL': None, "VALIDATOR_URL": None,
'AUTO_SCHEMA_TYPE': 2, # 分组根据url层级分0、1 或 2 层 "AUTO_SCHEMA_TYPE": 2, # 分组根据url层级分0、1 或 2 层
'DEFAULT_AUTO_SCHEMA_CLASS': 'dvadmin.utils.swagger.CustomSwaggerAutoSchema', "DEFAULT_AUTO_SCHEMA_CLASS": "dvadmin.utils.swagger.CustomSwaggerAutoSchema",
} }
# ================================================= # # ================================================= #
# **************** 验证码配置 ******************* # # **************** 验证码配置 ******************* #
# ================================================= # # ================================================= #
CAPTCHA_STATE = True CAPTCHA_STATE = locals().get("CAPTCHA_STATE", False)
CAPTCHA_IMAGE_SIZE = (160, 60) # 设置 captcha 图片大小 CAPTCHA_IMAGE_SIZE = (160, 60) # 设置 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 = 40 # 字体大小
CAPTCHA_FOREGROUND_COLOR = '#0033FF' # 前景色 CAPTCHA_FOREGROUND_COLOR = "#0033FF" # 前景色
CAPTCHA_BACKGROUND_COLOR = '#F5F7F4' # 背景色 CAPTCHA_BACKGROUND_COLOR = "#F5F7F4" # 背景色
CAPTCHA_NOISE_FUNCTIONS = ( CAPTCHA_NOISE_FUNCTIONS = (
'captcha.helpers.noise_arcs', # 线 "captcha.helpers.noise_arcs", # 线
'captcha.helpers.noise_dots', # 点 "captcha.helpers.noise_dots", # 点
) )
# CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' #字母验证码 # CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge' #字母验证码
CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge' # 加减乘除验证码 CAPTCHA_CHALLENGE_FUNCT = "captcha.helpers.math_challenge" # 加减乘除验证码
# ================================================= # # ================================================= #
# ******************** 其他配置 ******************** # # ******************** 其他配置 ******************** #
# ================================================= # # ================================================= #
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
API_LOG_ENABLE = True API_LOG_ENABLE = True
# API_LOG_METHODS = 'ALL' # ['POST', 'DELETE'] # API_LOG_METHODS = 'ALL' # ['POST', 'DELETE']
API_LOG_METHODS = ['POST', 'UPDATE', 'DELETE', 'PUT'] # ['POST', 'DELETE'] API_LOG_METHODS = ["POST", "UPDATE", "DELETE", "PUT"] # ['POST', 'DELETE']
API_MODEL_MAP = { API_MODEL_MAP = {
"/token/": "登录模块", "/token/": "登录模块",
"/api/login/": "登录模块", "/api/login/": "登录模块",
"/api/plugins_market/plugins/": "插件市场", "/api/plugins_market/plugins/": "插件市场",
} }
# 表前缀
TABLE_PREFIX = "dvadmin_"
DJANGO_CELERY_BEAT_TZ_AWARE = False DJANGO_CELERY_BEAT_TZ_AWARE = False
CELERY_TIMEZONE = 'Asia/Shanghai' # celery 时区问题 CELERY_TIMEZONE = "Asia/Shanghai" # celery 时区问题
# 静态页面压缩 # 静态页面压缩
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage' STATICFILES_STORAGE = "whitenoise.storage.CompressedStaticFilesStorage"
# 初始化需要执行的列表,用来初始化后执行
INITIALIZE_RESET_LIST = []
ALL_MODELS_OBJECTS = [] # 所有app models 对象 ALL_MODELS_OBJECTS = [] # 所有app models 对象
# dvadmin 插件 # dvadmin 插件
REGISTER_PLUGINS = ( REGISTER_PLUGINS = (

View File

@ -23,13 +23,19 @@ from rest_framework_simplejwt.views import (
) )
from application import settings from application import settings
from dvadmin.system.views.login import LoginView, CaptchaView, ApiLogin, LogoutView from dvadmin.system.views.login import (
LoginView,
CaptchaStatusView,
CaptchaView,
ApiLogin,
LogoutView,
)
from dvadmin.utils.swagger import CustomOpenAPISchemaGenerator from dvadmin.utils.swagger import CustomOpenAPISchemaGenerator
schema_view = get_schema_view( schema_view = get_schema_view(
openapi.Info( openapi.Info(
title="Snippets API", title="Snippets API",
default_version='v1', default_version="v1",
description="Test description", description="Test description",
terms_of_service="https://www.google.com/policies/terms/", terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact@snippets.local"), contact=openapi.Contact(email="contact@snippets.local"),
@ -38,20 +44,38 @@ schema_view = get_schema_view(
public=True, public=True,
permission_classes=(permissions.AllowAny,), permission_classes=(permissions.AllowAny,),
generator_class=CustomOpenAPISchemaGenerator, generator_class=CustomOpenAPISchemaGenerator,
) )
urlpatterns = [ urlpatterns = (
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), [
name='schema-json'), re_path(
path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), r"^swagger(?P<format>\.json|\.yaml)$",
path(r'redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), schema_view.without_ui(cache_timeout=0),
path('api/system/', include('dvadmin.system.urls')), name="schema-json",
path('api/login/', LoginView.as_view(), name='token_obtain_pair'), ),
path('api/logout/', LogoutView.as_view(), name='token_obtain_pair'), path(
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), "",
re_path(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), schema_view.with_ui("swagger", cache_timeout=0),
path('api/captcha/', CaptchaView.as_view()), name="schema-swagger-ui",
path('apiLogin/', ApiLogin.as_view()), ),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + static(settings.STATIC_URL, path(
document_root=settings.STATIC_URL) r"redoc/",
schema_view.with_ui("redoc", cache_timeout=0),
name="schema-redoc",
),
path("api/system/", include("dvadmin.system.urls")),
path("api/login/", LoginView.as_view(), name="token_obtain_pair"),
path("api/logout/", LogoutView.as_view(), name="token_obtain_pair"),
path("token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
re_path(
r"^api-auth/", include("rest_framework.urls", namespace="rest_framework")
),
path("api/captcha/", CaptchaView.as_view()),
path("api/captcha/status/", CaptchaStatusView.as_view()),
path("apiLogin/", ApiLogin.as_view()),
# 业务路由
# re_path(r'^api/app_route/', include('apps.app_name.urls')),
]
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+ static(settings.STATIC_URL, document_root=settings.STATIC_URL)
)

View File

@ -1,19 +1,8 @@
import os
from application.settings import BASE_DIR
# ================================================= # # ================================================= #
# ************** 数据库 配置 ************** # # *************** mysql数据库 配置 *************** #
# ================================================= # # ================================================= #
# 数据库地址
# 数据库 ENGINE ,默认演示使用 sqlite3 数据库,正式环境建议使用 mysql 数据库 DATABASE_ENGINE = "django.db.backends.mysql"
# sqlite3 设置
DATABASE_ENGINE = "django.db.backends.sqlite3"
DATABASE_NAME = os.path.join(BASE_DIR, 'db.sqlite3')
# 使用mysql时改为此配置
# DATABASE_ENGINE = "django.db.backends.mysql"
# DATABASE_NAME = 'django-vue-admin' # mysql 时使用
# 数据库地址 改为自己数据库地址 # 数据库地址 改为自己数据库地址
DATABASE_HOST = "127.0.0.1" DATABASE_HOST = "127.0.0.1"
# # 数据库端口 # # 数据库端口
@ -22,17 +11,39 @@ DATABASE_PORT = 3306
DATABASE_USER = "root" DATABASE_USER = "root"
# # 数据库密码 # # 数据库密码
DATABASE_PASSWORD = "123456" DATABASE_PASSWORD = "123456"
# 数据库名
DATABASE_NAME = "database_name"
# 表前缀
TABLE_PREFIX = "sys_"
APP_PREFIX = "app_"
# ================================================= # # ================================================= #
# ************** redis配置无redis 可不进行配置 ************** # # ******** redis配置无redis 可不进行配置 ******** #
# ================================================= # # ================================================= #
# REDIS_PASSWORD = '' # REDIS_PASSWORD = ''
# REDIS_HOST = '127.0.0.1' # REDIS_HOST = '127.0.0.1'
# REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6380' # REDIS_URL = f'redis://:{REDIS_PASSWORD or ""}@{REDIS_HOST}:6380'
# ================================================= # # ================================================= #
# ************** 其他 配置 ************** # # ****************** 功能 启停 ******************* #
# ================================================= # # ================================================= #
DEBUG = True # 线上环境请设置为True DEBUG = False
# 是否启用插件不需要可以设置为False
ENABLE_PLUGINS = False
# 启动登录详细概略获取(通过调用api获取ip详细地址)
ENABLE_LOGIN_ANALYSIS_LOG = True
# 是否启用登录验证码不需要可以设置为False
CAPTCHA_STATE = False
# 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
LOGIN_NO_CAPTCHA_AUTH = True
# ================================================= #
# ****************** 其他 配置 ******************* #
# ================================================= #
ALLOWED_HOSTS = ["*"] ALLOWED_HOSTS = ["*"]
LOGIN_NO_CAPTCHA_AUTH = True # 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
ENABLE_LOGIN_ANALYSIS_LOG = True # 启动登录详细概略获取(通过调用api获取ip详细地址) # 默认密码
DEFAULT_PASSWORD = "admin123456"
# 初始化需要执行的列表,用来初始化后执行
INITIALIZE_LIST = []
INITIALIZE_RESET_LIST = []

View File

@ -149,7 +149,7 @@ button_data = [
"update_datetime": datetime.datetime.now(), "update_datetime": datetime.datetime.now(),
"create_datetime": datetime.datetime.now(), "create_datetime": datetime.datetime.now(),
"name": "重置密码", "name": "重置密码",
"value": "ResetPwd", "value": "ResetPassword",
"creator_id": 1, "creator_id": 1,
}, },
] ]
@ -1251,7 +1251,7 @@ menu_button_data = [
"update_datetime": datetime.datetime.now(), "update_datetime": datetime.datetime.now(),
"create_datetime": datetime.datetime.now(), "create_datetime": datetime.datetime.now(),
"name": "重置密码", "name": "重置密码",
"value": "ResetPwd", "value": "ResetPassword",
"api": "/api/system/user/reset_password/{id}/", "api": "/api/system/user/reset_password/{id}/",
"method": 2, "method": 2,
"creator_id": 1, "creator_id": 1,
@ -1362,7 +1362,7 @@ staff_data = [
"create_datetime": datetime.datetime.now(), "create_datetime": datetime.datetime.now(),
"username": "admin", "username": "admin",
"email": "dvadmin@django-vue-admin.com", "email": "dvadmin@django-vue-admin.com",
"mobile": "13333333333", "mobile": "18888888888",
"avatar": "", "avatar": "",
"name": "管理员", "name": "管理员",
"gender": 1, "gender": 1,

View File

@ -1,11 +1,3 @@
# -*- coding: utf-8 -*-
"""
@author: 猿小天
@contact: QQ:1638245306
@Created on: 2021/6/2 002 14:20
@Remark:登录视图
"""
import base64 import base64
import hashlib import hashlib
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -24,7 +16,7 @@ from rest_framework_simplejwt.views import TokenObtainPairView
from application import settings from application import settings
from dvadmin.system.models import Users from dvadmin.system.models import Users
from dvadmin.utils.json_response import SuccessResponse, ErrorResponse, DetailResponse from dvadmin.utils.json_response import ErrorResponse, DetailResponse
from dvadmin.utils.request_util import save_login_log from dvadmin.utils.request_util import save_login_log
from dvadmin.utils.serializers import CustomModelSerializer from dvadmin.utils.serializers import CustomModelSerializer
from dvadmin.utils.validator import CustomValidationError from dvadmin.utils.validator import CustomValidationError
@ -35,21 +27,33 @@ class CaptchaView(APIView):
permission_classes = [] permission_classes = []
@swagger_auto_schema( @swagger_auto_schema(
responses={ responses={"200": openapi.Response("获取成功")},
'200': openapi.Response('获取成功')
},
security=[], security=[],
operation_id='captcha-get', operation_id="captcha-get",
operation_description='验证码获取', operation_description="验证码获取",
) )
def get(self, request): def get(self, request):
hashkey = CaptchaStore.generate_key() data = {}
id = CaptchaStore.objects.filter(hashkey=hashkey).first().id if settings.CAPTCHA_STATE:
imgage = captcha_image(request, hashkey) hashkey = CaptchaStore.generate_key()
# 将图片转换为base64 id = CaptchaStore.objects.filter(hashkey=hashkey).first().id
image_base = base64.b64encode(imgage.content) imgage = captcha_image(request, hashkey)
json_data = {"key": id, "image_base": "data:image/png;base64," + image_base.decode('utf-8')} # 将图片转换为base64
return SuccessResponse(data=json_data) image_base = base64.b64encode(imgage.content)
data = {
"key": id,
"image_base": "data:image/png;base64," + image_base.decode("utf-8"),
}
return DetailResponse(data=data)
class CaptchaStatusView(APIView):
authentication_classes = []
permission_classes = []
def get(self, request):
return DetailResponse(data={"status": settings.CAPTCHA_STATE})
class LoginSerializer(TokenObtainPairSerializer): class LoginSerializer(TokenObtainPairSerializer):
@ -57,53 +61,55 @@ class LoginSerializer(TokenObtainPairSerializer):
登录的序列化器: 登录的序列化器:
重写djangorestframework-simplejwt的序列化器 重写djangorestframework-simplejwt的序列化器
""" """
captcha = serializers.CharField(max_length=6, required=False, allow_null=True)
captcha = serializers.CharField(
max_length=6, required=False, allow_null=True, allow_blank=True
)
class Meta: class Meta:
model = Users model = Users
fields = "__all__" fields = "__all__"
read_only_fields = ["id"] read_only_fields = ["id"]
default_error_messages = { default_error_messages = {"no_active_account": _("账号/密码错误")}
'no_active_account': _('账号/密码不正确')
}
def validate(self, attrs): def validate(self, attrs):
captcha = self.initial_data.get('captcha', None) captcha = self.initial_data.get("captcha", None)
if settings.CAPTCHA_STATE: if settings.CAPTCHA_STATE:
if captcha is None: if captcha is None:
raise CustomValidationError("验证码不能为空") raise CustomValidationError("验证码不能为空")
self.image_code = CaptchaStore.objects.filter( self.image_code = CaptchaStore.objects.filter(
id=self.initial_data['captchaKey']).first() id=self.initial_data["captchaKey"]
).first()
five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0) five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
if self.image_code and five_minute_ago > self.image_code.expiration: if self.image_code and five_minute_ago > self.image_code.expiration:
self.image_code and self.image_code.delete() self.image_code and self.image_code.delete()
raise CustomValidationError('验证码过期') raise CustomValidationError("验证码过期")
else: else:
if self.image_code and (self.image_code.response == captcha or self.image_code.challenge == captcha): if self.image_code and (
self.image_code.response == 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("图片验证码错误")
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
data['avatar'] = self.user.avatar data["avatar"] = self.user.avatar
request = self.context.get('request') request = self.context.get("request")
request.user = self.user request.user = self.user
# 记录登录日志 # 记录登录日志
save_login_log(request=request) save_login_log(request=request)
return { return {"code": 2000, "msg": "请求成功", "data": data}
"code": 2000,
"msg": "请求成功",
"data": data
}
class LoginView(TokenObtainPairView): class LoginView(TokenObtainPairView):
""" """
登录接口 登录接口
""" """
serializer_class = LoginSerializer serializer_class = LoginSerializer
permission_classes = [] permission_classes = []
@ -118,31 +124,22 @@ class LoginTokenSerializer(TokenObtainPairSerializer):
fields = "__all__" fields = "__all__"
read_only_fields = ["id"] read_only_fields = ["id"]
default_error_messages = { default_error_messages = {"no_active_account": _("账号/密码不正确")}
'no_active_account': _('账号/密码不正确')
}
def validate(self, attrs): def validate(self, attrs):
if not getattr(settings, 'LOGIN_NO_CAPTCHA_AUTH', False): if not getattr(settings, "LOGIN_NO_CAPTCHA_AUTH", False):
return { return {"code": 4000, "msg": "该接口暂未开通!", "data": None}
"code": 4000,
"msg": "该接口暂未开通!",
"data": None
}
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
return { return {"code": 2000, "msg": "请求成功", "data": data}
"code": 2000,
"msg": "请求成功",
"data": data
}
class LoginTokenView(TokenObtainPairView): class LoginTokenView(TokenObtainPairView):
""" """
登录获取token接口 登录获取token接口
""" """
serializer_class = LoginTokenSerializer serializer_class = LoginTokenSerializer
permission_classes = [] permission_classes = []
@ -154,27 +151,31 @@ class LogoutView(APIView):
class ApiLoginSerializer(CustomModelSerializer): class ApiLoginSerializer(CustomModelSerializer):
"""接口文档登录-序列化器""" """接口文档登录-序列化器"""
username = serializers.CharField() username = serializers.CharField()
password = serializers.CharField() password = serializers.CharField()
class Meta: class Meta:
model = Users model = Users
fields = ['username', 'password'] fields = ["username", "password"]
class ApiLogin(APIView): class ApiLogin(APIView):
"""接口文档的登录接口""" """接口文档的登录接口"""
serializer_class = ApiLoginSerializer serializer_class = ApiLoginSerializer
authentication_classes = [] authentication_classes = []
permission_classes = [] permission_classes = []
def post(self, request): def post(self, request):
username = request.data.get('username') username = request.data.get("username")
password = request.data.get('password') password = request.data.get("password")
user_obj = auth.authenticate(request, username=username, if user_obj := auth.authenticate(
password=hashlib.md5(password.encode(encoding='UTF-8')).hexdigest()) request,
if user_obj: username=username,
password=hashlib.md5(password.encode(encoding="UTF-8")).hexdigest(),
):
login(request, user_obj) login(request, user_obj)
return redirect('/') return redirect("/")
else: else:
return ErrorResponse(msg="账号/密码错误") return ErrorResponse(msg="账号/密码错误")

View File

@ -1,13 +1,6 @@
# -*- coding: utf-8 -*-
"""
@author: 猿小天
@contact: QQ:1638245306
@Created on: 2021/6/3 003 0:30
@Remark: 用户管理
"""
import hashlib import hashlib
from application import settings
from django.contrib.auth.hashers import make_password from django.contrib.auth.hashers import make_password
from rest_framework import serializers from rest_framework import serializers
from rest_framework.decorators import action from rest_framework.decorators import action
@ -28,9 +21,9 @@ class UserSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Users model = Users
read_only_fields = ["id"] read_only_fields = ["id"]
exclude = ['password'] exclude = ["password"]
extra_kwargs = { extra_kwargs = {
'post': {'required': False}, "post": {"required": False},
} }
@ -38,14 +31,23 @@ class UserCreateSerializer(CustomModelSerializer):
""" """
用户新增-序列化器 用户新增-序列化器
""" """
username = serializers.CharField(max_length=50,
validators=[CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")]) username = serializers.CharField(
password = serializers.CharField(required=False, default=make_password( max_length=50,
hashlib.md5('admin123456'.encode(encoding='UTF-8')).hexdigest())) validators=[
CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")
],
)
password = serializers.CharField(
required=False,
default=make_password(
hashlib.md5(settings.DEFAULT_PASSWORD.encode(encoding="UTF-8")).hexdigest()
),
)
def save(self, **kwargs): def save(self, **kwargs):
data = super().save(**kwargs) data = super().save(**kwargs)
data.post.set(self.initial_data.get('post', [])) data.post.set(self.initial_data.get("post", []))
return data return data
class Meta: class Meta:
@ -53,7 +55,7 @@ class UserCreateSerializer(CustomModelSerializer):
fields = "__all__" fields = "__all__"
read_only_fields = ["id"] read_only_fields = ["id"]
extra_kwargs = { extra_kwargs = {
'post': {'required': False}, "post": {"required": False},
} }
@ -61,13 +63,24 @@ class UserUpdateSerializer(CustomModelSerializer):
""" """
用户修改-序列化器 用户修改-序列化器
""" """
username = serializers.CharField(max_length=50,
validators=[CustomUniqueValidator(queryset=Users.objects.all(), message="账号必须唯一")]) username = serializers.CharField(
max_length=50,
validators=[
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(
max_length=50,
validators=[
CustomUniqueValidator(queryset=Users.objects.all(), message="手机号必须唯一")
],
)
def save(self, **kwargs): def save(self, **kwargs):
data = super().save(**kwargs) data = super().save(**kwargs)
data.post.set(self.initial_data.get('post', [])) data.post.set(self.initial_data.get("post", []))
return data return data
class Meta: class Meta:
@ -75,7 +88,7 @@ class UserUpdateSerializer(CustomModelSerializer):
read_only_fields = ["id"] read_only_fields = ["id"]
fields = "__all__" fields = "__all__"
extra_kwargs = { extra_kwargs = {
'post': {'required': False, 'read_only': True}, "post": {"required": False, "read_only": True},
} }
@ -83,22 +96,35 @@ class ExportUserProfileSerializer(CustomModelSerializer):
""" """
用户导出 序列化器 用户导出 序列化器
""" """
last_login = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
dept__deptName = serializers.CharField(source='dept.deptName', default='') last_login = serializers.DateTimeField(
dept__owner = serializers.CharField(source='dept.owner', default='') format="%Y-%m-%d %H:%M:%S", required=False, read_only=True
gender = serializers.CharField(source='get_gender_display', read_only=True) )
dept__deptName = serializers.CharField(source="dept.deptName", default="")
dept__owner = serializers.CharField(source="dept.owner", default="")
gender = serializers.CharField(source="get_gender_display", read_only=True)
class Meta: class Meta:
model = Users model = Users
fields = ('username', 'name', 'email', 'mobile', 'gender', 'is_active', 'last_login', 'dept__deptName', fields = (
'dept__owner') "username",
"name",
"email",
"mobile",
"gender",
"is_active",
"last_login",
"dept__deptName",
"dept__owner",
)
class UserProfileImportSerializer(CustomModelSerializer): class UserProfileImportSerializer(CustomModelSerializer):
def save(self, **kwargs): def save(self, **kwargs):
data = super().save(**kwargs) data = super().save(**kwargs)
password = hashlib.new('md5', str(self.initial_data.get('password', '')).encode(encoding='UTF-8')).hexdigest() password = hashlib.new(
"md5", str(self.initial_data.get("password", "")).encode(encoding="UTF-8")
).hexdigest()
data.set_password(password) data.set_password(password)
data.save() data.save()
return data return data
@ -106,15 +132,22 @@ class UserProfileImportSerializer(CustomModelSerializer):
def run_validation(self, data={}): def run_validation(self, data={}):
# 把excel 数据进行格式转换 # 把excel 数据进行格式转换
if type(data) is dict: if type(data) is dict:
data['role'] = str(data['role']).split(',') data["role"] = str(data["role"]).split(",")
data['dept_id'] = str(data['dept']).split(',') data["dept_id"] = str(data["dept"]).split(",")
data['gender'] = {'': '1', '': '0', '未知': '2'}.get(data['gender']) data["gender"] = {"": "1", "": "0", "未知": "2"}.get(data["gender"])
data['is_active'] = {'启用': True, '禁用': False}.get(data['is_active']) data["is_active"] = {"启用": True, "禁用": False}.get(data["is_active"])
return super().run_validation(data) return super().run_validation(data)
class Meta: class Meta:
model = Users model = Users
exclude = ('password', 'post', 'user_permissions', 'groups', 'is_superuser', 'date_joined') exclude = (
"password",
"post",
"user_permissions",
"groups",
"is_superuser",
"date_joined",
)
class UserViewSet(CustomModelViewSet): class UserViewSet(CustomModelViewSet):
@ -126,11 +159,12 @@ class UserViewSet(CustomModelViewSet):
retrieve:单例 retrieve:单例
destroy:删除 destroy:删除
""" """
queryset = Users.objects.exclude(is_superuser=1).all() queryset = Users.objects.exclude(is_superuser=1).all()
serializer_class = UserSerializer serializer_class = UserSerializer
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"]
# filter_fields = { # filter_fields = {
# 'name': ['icontains'], # 'name': ['icontains'],
# 'username': ['icontains'], # 'username': ['icontains'],
@ -138,17 +172,35 @@ class UserViewSet(CustomModelViewSet):
# 'is_active': ['icontains'], # 'is_active': ['icontains'],
# 'dept': ['exact'], # 'dept': ['exact'],
# } # }
search_fields = ['username', 'name', 'gender', 'dept__name', 'role__name'] search_fields = ["username", "name", "gender", "dept__name", "role__name"]
# 导出 # 导出
export_field_label = ['用户账号', '用户名称', '用户邮箱', '手机号码', '用户性别', '帐号状态', '最后登录时间', '部门名称', '部门负责人'] export_field_label = [
"用户账号",
"用户名称",
"用户邮箱",
"手机号码",
"用户性别",
"帐号状态",
"最后登录时间",
"部门名称",
"部门负责人",
]
export_serializer_class = ExportUserProfileSerializer export_serializer_class = ExportUserProfileSerializer
# 导入 # 导入
import_serializer_class = UserProfileImportSerializer import_serializer_class = UserProfileImportSerializer
import_field_dict = {'username': '登录账号', 'name': '用户名称', 'email': '用户邮箱', 'mobile': '手机号码', import_field_dict = {
'gender': '用户性别(男/女/未知)', "username": "登录账号",
'is_active': '帐号状态(启用/禁用)', 'password': '登录密码', 'dept': '部门ID', 'role': '角色ID'} "name": "用户名称",
"email": "用户邮箱",
"mobile": "手机号码",
"gender": "用户性别(男/女/未知)",
"is_active": "帐号状态(启用/禁用)",
"password": "登录密码",
"dept": "部门ID",
"role": "角色ID",
}
@action(methods=['GET'], detail=True, permission_classes=[IsAuthenticated]) @action(methods=["GET"], detail=True, permission_classes=[IsAuthenticated])
def user_info(self, request): def user_info(self, request):
"""获取当前用户信息""" """获取当前用户信息"""
user = request.user user = request.user
@ -157,25 +209,25 @@ class UserViewSet(CustomModelViewSet):
"mobile": user.mobile, "mobile": user.mobile,
"gender": user.gender, "gender": user.gender,
"email": user.email, "email": user.email,
'avatar':user.avatar "avatar": user.avatar,
} }
return DetailResponse(data=result, msg="获取成功") return DetailResponse(data=result, msg="获取成功")
@action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated]) @action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated])
def update_user_info(self, request): def update_user_info(self, request):
"""修改当前用户信息""" """修改当前用户信息"""
user = request.user user = request.user
Users.objects.filter(id=user.id).update(**request.data) Users.objects.filter(id=user.id).update(**request.data)
return DetailResponse(data=None, msg="修改成功") return DetailResponse(data=None, msg="修改成功")
@action(methods=['PUT'], detail=True, permission_classes=[IsAuthenticated]) @action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated])
def change_password(self, request, *args, **kwargs): def change_password(self, request, *args, **kwargs):
"""密码修改""" """密码修改"""
instance = Users.objects.filter(id=kwargs.get('pk')).first() instance = Users.objects.filter(id=kwargs.get("pk")).first()
data = request.data data = request.data
old_pwd = data.get('oldPassword') old_pwd = data.get("oldPassword")
new_pwd = data.get('newPassword') new_pwd = data.get("newPassword")
new_pwd2 = data.get('newPassword2') new_pwd2 = data.get("newPassword2")
if instance: if instance:
if new_pwd != new_pwd2: if new_pwd != new_pwd2:
return ErrorResponse(msg="两次密码不匹配") return ErrorResponse(msg="两次密码不匹配")
@ -188,21 +240,32 @@ class UserViewSet(CustomModelViewSet):
else: else:
return ErrorResponse(msg="未获取到用户") return ErrorResponse(msg="未获取到用户")
@action(methods=['PUT'], detail=True) @action(methods=["PUT"], detail=True, permission_classes=[IsAuthenticated])
def reset_password(self, request, pk): def reset_password(self, request, *args, **kwargs):
""" """重置密码"""
密码重置 instance = Users.objects.filter(id=kwargs.get("pk")).first()
"""
instance = Users.objects.filter(id=pk).first()
data = request.data
new_pwd = data.get('newPassword')
new_pwd2 = data.get('newPassword2')
if instance: if instance:
if new_pwd != new_pwd2: instance.set_password(settings.DEFAULT_PASSWORD)
return ErrorResponse(msg="两次密码不匹配") instance.save()
else: return DetailResponse(data=None, msg="密码重置成功")
instance.password = make_password(new_pwd)
instance.save()
return DetailResponse(data=None, msg="修改成功")
else: else:
return ErrorResponse(msg="未获取到用户") return ErrorResponse(msg="未获取到用户")
# @action(methods=['PUT'], detail=True)
# def reset_password(self, request, pk):
# """
# 密码重置
# """
# instance = Users.objects.filter(id=pk).first()
# data = request.data
# new_pwd = data.get('newPassword')
# new_pwd2 = data.get('newPassword2')
# if instance:
# if new_pwd != new_pwd2:
# return ErrorResponse(msg="两次密码不匹配")
# else:
# instance.password = make_password(new_pwd)
# instance.save()
# return DetailResponse(data=None, msg="修改成功")
# else:
# return ErrorResponse(msg="未获取到用户")

View File

@ -273,7 +273,7 @@ Vue.prototype.commonEndColumns = function (param = {}) {
type: 'table-selector', type: 'table-selector',
dict: { dict: {
cache: true, cache: true,
url: deptPrefix + '?limit=999&status=1', url: deptPrefix,
isTree: true, isTree: true,
value: 'id', // 数据字典中value字段的属性名 value: 'id', // 数据字典中value字段的属性名
label: 'name', // 数据字典中label字段的属性名 label: 'name', // 数据字典中label字段的属性名
@ -283,7 +283,8 @@ Vue.prototype.commonEndColumns = function (param = {}) {
component component
}) => { }) => {
return request({ return request({
url: url url: url,
params: { limit: 999, status: 1 }
}).then(ret => { }).then(ret => {
return ret.data.data return ret.data.data
}) })

View File

@ -28,10 +28,21 @@
:action="action" :action="action"
:headers="headers" :headers="headers"
:limit="1" :limit="1"
:disabled="fileList.length===1" :disabled="fileList.length === 1"
:on-success="handleAvatarSuccess"> :on-success="handleAvatarSuccess"
<!-- <el-image v-if="userInfo.avatar" :src="userInfo.avatar" :preview-src-list="[userInfo.avatar]" style="width: 100px;height: 100px" alt="头像"></el-image>--> >
<!-- <i v-else class="el-icon-plus avatar-uploader-icon" style="width: 100px;height: 100px"></i>--> <!-- <el-image
v-if="userInfo.avatar"
:src="userInfo.avatar"
:preview-src-list="[userInfo.avatar]"
style="width: 100px; height: 100px"
alt="头像"
></el-image>
<i
v-else
class="el-icon-plus avatar-uploader-icon"
style="width: 100px; height: 100px"
></i> -->
<i class="el-icon-plus"></i> <i class="el-icon-plus"></i>
</el-upload> </el-upload>
</el-form-item> </el-form-item>
@ -159,9 +170,7 @@ export default {
}, },
userInforules: { userInforules: {
name: [{ required: true, message: '请输入昵称', trigger: 'blur' }], name: [{ required: true, message: '请输入昵称', trigger: 'blur' }],
mobile: [ mobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确手机号' }]
{ pattern: /^1[3|4|5|7|8]\d{9}$/, message: '请输入正确手机号' }
]
}, },
userPasswordInfo: { userPasswordInfo: {
oldPassword: '', oldPassword: '',

View File

@ -64,7 +64,9 @@ router.beforeEach(async (to, from, next) => {
// 处理路由 得到每一级的路由设置 // 处理路由 得到每一级的路由设置
store.commit('d2admin/page/init', routes) store.commit('d2admin/page/init', routes)
router.addRoutes(routes) // router.addRoutes(routes)
routes.forEach(route => router.addRoute(route))
const menu = handleAsideMenu(ret) const menu = handleAsideMenu(ret)
const aside = handleAsideMenu(ret.filter(value => value.visible === true)) const aside = handleAsideMenu(ret.filter(value => value.visible === true))
store.commit('d2admin/menu/asideSet', aside) // 设置侧边栏菜单 store.commit('d2admin/menu/asideSet', aside) // 设置侧边栏菜单

View File

@ -97,13 +97,13 @@ export const crudOptions = (vm) => {
type: 'cascader', type: 'cascader',
dict: { dict: {
cache: false, cache: false,
url: deptPrefix + '?limit=999&status=1', url: deptPrefix,
isTree: true, isTree: true,
value: 'id', // 数据字典中value字段的属性名 value: 'id', // 数据字典中value字段的属性名
label: 'name', // 数据字典中label字段的属性名 label: 'name', // 数据字典中label字段的属性名
children: 'children', // 数据字典中children字段的属性名 children: 'children', // 数据字典中children字段的属性名
getData: (url, dict) => { // 配置此参数会覆盖全局的getRemoteDictFunc getData: (url, dict) => { // 配置此参数会覆盖全局的getRemoteDictFunc
return request({ url: url }).then(ret => { return request({ url: url, params: { limit: 999, status: 1 } }).then(ret => {
const data = XEUtils.toArrayTree(ret.data.data, { parentKey: 'parent', strict: true }) const data = XEUtils.toArrayTree(ret.data.data, { parentKey: 'parent', strict: true })
return [{ id: '0', name: '根节点', children: data }] return [{ id: '0', name: '根节点', children: data }]
}) })

View File

@ -98,13 +98,13 @@ export const crudOptions = (vm) => {
type: 'cascader', type: 'cascader',
dict: { dict: {
cache: false, cache: false,
url: dictionaryPrefix + '?status=1&limit=999', url: dictionaryPrefix,
isTree: true, isTree: true,
value: 'id', // 数据字典中value字段的属性名 value: 'id', // 数据字典中value字段的属性名
label: 'label', // 数据字典中label字段的属性名 label: 'label', // 数据字典中label字段的属性名
children: 'children', // 数据字典中children字段的属性名 children: 'children', // 数据字典中children字段的属性名
getData: (url, dict) => { // 配置此参数会覆盖全局的getRemoteDictFunc getData: (url, dict) => { // 配置此参数会覆盖全局的getRemoteDictFunc
return request({ url: url }).then(ret => { return request({ url: url, params: { limit: 999, status: 1 } }).then(ret => {
return [{ id: null, label: '根节点', children: XEUtils.toArrayTree(ret.data.data, { parentKey: 'parent', strict: true }) }] return [{ id: null, label: '根节点', children: XEUtils.toArrayTree(ret.data.data, { parentKey: 'parent', strict: true }) }]
}) })
} }

View File

@ -1,12 +1,3 @@
/*
* @创建文件时间: 2021-06-02 10:33:33
* @Auther: 猿小天
* @最后修改人: 猿小天
* @最后修改时间: 2021-08-12 22:53:38
* 联系Qq:1638245306
* @文件介绍: 登录的接口
*/
import { request } from '@/api/service' import { request } from '@/api/service'
export function SYS_USER_LOGIN (data) { export function SYS_USER_LOGIN (data) {
@ -31,3 +22,10 @@ export function getCaptcha () {
method: 'get' method: 'get'
}) })
} }
export function getCaptchaStatus () {
return request({
url: 'api/captcha/status/',
method: 'get'
})
}

View File

@ -5,26 +5,13 @@
<li v-for="n in 10" :key="n"></li> <li v-for="n in 10" :key="n"></li>
</ul> </ul>
</div> </div>
<div <div class="page-login--layer page-login--layer-time" flex="main:center cross:center">{{ time }}</div>
class="page-login--layer page-login--layer-time"
flex="main:center cross:center"
>
{{ time }}
</div>
<div class="page-login--layer"> <div class="page-login--layer">
<div <div class="page-login--content" flex="dir:top main:justify cross:stretch box:justify">
class="page-login--content"
flex="dir:top main:justify cross:stretch box:justify"
>
<div class="page-login--content-header"> <div class="page-login--content-header">
<p class="page-login--content-header-motto"> <p class="page-login--content-header-motto">时间是一切财富中最宝贵的财富</p>
时间是一切财富中最宝贵的财富
</p>
</div> </div>
<div <div class="page-login--content-main" flex="dir:top main:center cross:center">
class="page-login--content-main"
flex="dir:top main:center cross:center"
>
<!-- logo --> <!-- logo -->
<img class="page-login--logo" src="./image/dvadmin.png" /> <img class="page-login--logo" src="./image/dvadmin.png" />
<!-- form --> <!-- form -->
@ -38,11 +25,7 @@
size="default" size="default"
> >
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input type="text" v-model="formLogin.username" placeholder="用户名">
type="text"
v-model="formLogin.username"
placeholder="用户名"
>
<i slot="prepend" class="fa fa-user-circle-o"></i> <i slot="prepend" class="fa fa-user-circle-o"></i>
</el-input> </el-input>
</el-form-item> </el-form-item>
@ -56,42 +39,27 @@
<i slot="prepend" class="fa fa-keyboard-o"></i> <i slot="prepend" class="fa fa-keyboard-o"></i>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="captcha"> <el-form-item prop="captcha" v-if="captchaVisible">
<el-input <el-input type="text" v-model="formLogin.captcha" placeholder="验证码">
type="text"
v-model="formLogin.captcha"
placeholder="验证码"
>
<template slot="append"> <template slot="append">
<img <img class="login-code" :src="image_base" @click="getCaptcha" />
class="login-code"
:src="image_base"
@click="getCaptcha"
/>
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-button <el-button size="default" @click="submit" type="primary" class="button-login">登录</el-button>
size="default"
@click="submit"
type="primary"
class="button-login"
>
登录
</el-button>
</el-form> </el-form>
</el-card> </el-card>
<!-- <p class="page-login--options" flex="main:justify cross:center"> <!-- <p class="page-login--options" flex="main:justify cross:center">
<span><d2-icon name="question-circle" /> 忘记密码</span> <span><d2-icon name="question-circle" /> 忘记密码</span>
<span>注册用户</span> <span>注册用户</span>
</p> --> </p>-->
<!-- quick login --> <!-- quick login -->
<el-button <el-button
class="page-login--quick" class="page-login--quick"
size="default" size="default"
type="info" type="info"
@click="dialogVisible = true"
v-if="$env === 'development'" v-if="$env === 'development'"
@click="dialogVisible = true"
> >
快速选择用户限dev环境 快速选择用户限dev环境
</el-button> </el-button>
@ -109,12 +77,9 @@
</p> </p>
<p class="page-login--content-footer-copyright"> <p class="page-login--content-footer-copyright">
Copyright Copyright
<d2-icon name="copyright" /> <d2-icon name="copyright" />Copyright © 2018-2021 pro.django-vue-admin.com All Rights Reserved.
Copyright © 2018-2021 pro.django-vue-admin.com All Rights Reserved.
| |
<a href="https://beian.miit.gov.cn" target="_blank"> <a href="https://beian.miit.gov.cn" target="_blank">晋ICP备18005113号-3</a>
晋ICP备18005113号-3
</a>
</p> </p>
<p class="page-login--content-footer-options"> <p class="page-login--content-footer-options">
<a href="#">帮助</a> <a href="#">帮助</a>
@ -150,6 +115,7 @@ export default {
time: dayjs().format('HH:mm:ss'), time: dayjs().format('HH:mm:ss'),
// //
dialogVisible: false, dialogVisible: false,
captchaVisible: false,
users: [ users: [
{ {
name: '超管', name: '超管',
@ -196,6 +162,10 @@ export default {
image_base: null image_base: null
} }
}, },
created () {
this.$store.dispatch('d2admin/db/databaseClear')
this.getCaptcha()
},
mounted () { mounted () {
this.timeInterval = setInterval(() => { this.timeInterval = setInterval(() => {
this.refreshTime() this.refreshTime()
@ -216,8 +186,10 @@ export default {
handleUserBtnClick (user) { handleUserBtnClick (user) {
this.formLogin.username = user.username this.formLogin.username = user.username
this.formLogin.password = user.password this.formLogin.password = user.password
// this.submit() this.dialogVisible = !this.dialogVisible
this.dialogVisible = false if (!this.captchaVisible) {
this.submit()
}
}, },
/** /**
* @description 提交表单 * @description 提交表单
@ -253,16 +225,19 @@ export default {
* 获取验证码 * 获取验证码
*/ */
getCaptcha () { getCaptcha () {
api.getCaptcha().then((ret) => { api.getCaptchaStatus().then((ret) => {
this.formLogin.captcha = null this.captchaVisible = ret.data.status
this.captchaKey = ret.data.data.key if (this.captchaVisible) {
this.image_base = ret.data.data.image_base api.getCaptcha().then((ret) => {
this.formLogin.captcha = null
if (ret.data) {
this.captchaKey = ret.data.key
this.image_base = ret.data.image_base
}
})
}
}) })
} }
},
created () {
this.$store.dispatch('d2admin/db/databaseClear')
this.getCaptcha()
} }
} }
</script> </script>
@ -393,7 +368,6 @@ export default {
.page-login--content-footer-copyright { .page-login--content-footer-copyright {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
margin-bottom: 10px;
font-size: 12px; font-size: 12px;
line-height: 12px; line-height: 12px;
text-align: center; text-align: center;
@ -405,6 +379,7 @@ export default {
.page-login--content-footer-options { .page-login--content-footer-options {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
margin-bottom: 10px;
font-size: 12px; font-size: 12px;
line-height: 12px; line-height: 12px;
text-align: center; text-align: center;

View File

@ -27,7 +27,8 @@ export const crudOptions = (vm) => {
options: { options: {
rowId: 'id', rowId: 'id',
height: '100%', // 表格高度100%, 使用toolbar必须设置 height: '100%', // 表格高度100%, 使用toolbar必须设置
highlightCurrentRow: false highlightCurrentRow: false,
defaultExpandAll: true
}, },
rowHandle: { rowHandle: {
view: { view: {
@ -129,14 +130,14 @@ export const crudOptions = (vm) => {
}, },
type: 'cascader', type: 'cascader',
dict: { dict: {
url: menuPrefix + '?limit=999&status=1&is_catalog=1', url: menuPrefix,
cache: false, cache: false,
isTree: true, isTree: true,
value: 'id', // 数据字典中value字段的属性名 value: 'id', // 数据字典中value字段的属性名
label: 'name', // 数据字典中label字段的属性名 label: 'name', // 数据字典中label字段的属性名
children: 'children', // 数据字典中children字段的属性名 children: 'children', // 数据字典中children字段的属性名
getData: (url, dict, { form, component }) => { // 配置此参数会覆盖全局的getRemoteDictFunc getData: (url, dict, { form, component }) => { // 配置此参数会覆盖全局的getRemoteDictFunc
return request({ url: url }).then(ret => { return request({ url: url, params: { limit: 999, status: 1, is_catalog: 1 } }).then(ret => {
const responseData = ret.data.data const responseData = ret.data.data
const result = XEUtils.toArrayTree(responseData, { parentKey: 'parent', strict: true }) const result = XEUtils.toArrayTree(responseData, { parentKey: 'parent', strict: true })
return [{ id: null, name: '根节点', children: result }] return [{ id: null, name: '根节点', children: result }]
@ -457,6 +458,8 @@ export const crudOptions = (vm) => {
} }
} }
} }
].concat(vm.commonEndColumns({ update_datetime: { showTable: false } })) ].concat(vm.commonEndColumns({
update_datetime: { showTable: false }
}))
} }
} }

View File

@ -8,11 +8,13 @@ export const crudOptions = (vm) => {
compact: true compact: true
}, },
options: { options: {
height: '100%' height: '100%',
tableType: 'vxe-table',
rowKey: true // 必须设置true or false
}, },
rowHandle: { rowHandle: {
width: 320,
fixed: 'right', fixed: 'right',
width: 180,
view: { view: {
thin: true, thin: true,
text: '', text: '',
@ -36,18 +38,18 @@ export const crudOptions = (vm) => {
}, },
custom: [ custom: [
{ {
thin: true,
text: '',
size: 'small',
type: 'warning',
icon: 'el-icon-key',
show () { show () {
return vm.hasPermissions('ResetPwd') return vm.hasPermissions('ResetPassword')
}, },
emit: 'resetPwd' disabled () {
return !vm.hasPermissions('ResetPassword')
},
text: '重置密码',
type: 'warning',
size: 'small',
emit: 'resetPassword'
} }
] ]
}, },
viewOptions: { viewOptions: {
componentType: 'form' componentType: 'form'
@ -58,7 +60,7 @@ export const crudOptions = (vm) => {
indexRow: { // 或者直接传true,不显示title不居中 indexRow: { // 或者直接传true,不显示title不居中
title: '序号', title: '序号',
align: 'center', align: 'center',
width: 70 width: 60
}, },
columns: [ columns: [
{ {
@ -82,7 +84,6 @@ export const crudOptions = (vm) => {
{ {
title: 'ID', title: 'ID',
key: 'id', key: 'id',
width: 90,
disabled: true, disabled: true,
form: { form: {
disabled: true disabled: true
@ -94,7 +95,7 @@ export const crudOptions = (vm) => {
search: { search: {
disabled: false disabled: false
}, },
width: 140, minWidth: 100,
type: 'input', type: 'input',
form: { form: {
rules: [ // 表单校验规则 rules: [ // 表单校验规则
@ -117,6 +118,7 @@ export const crudOptions = (vm) => {
{ {
title: '姓名', title: '姓名',
key: 'name', key: 'name',
minWidth: 90,
search: { search: {
disabled: false disabled: false
}, },
@ -165,9 +167,9 @@ export const crudOptions = (vm) => {
}, },
component: { component: {
span: 12, span: 12,
pagination: true,
props: { multiple: false }, props: { multiple: false },
elProps: { elProps: {
pagination: true,
columns: [ columns: [
{ {
field: 'name', field: 'name',
@ -197,7 +199,7 @@ export const crudOptions = (vm) => {
form: { form: {
rules: [ rules: [
{ max: 20, message: '请输入正确的手机号码', trigger: 'blur' }, { max: 20, message: '请输入正确的手机号码', trigger: 'blur' },
{ pattern: /^1[3|4|5|7|8]\d{9}$/, message: '请输入正确的手机号码' } { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码' }
], ],
itemProps: { itemProps: {
class: { yxtInput: true } class: { yxtInput: true }
@ -275,7 +277,7 @@ export const crudOptions = (vm) => {
title: '头像', title: '头像',
key: 'avatar', key: 'avatar',
type: 'avatar-cropper', type: 'avatar-cropper',
width: 100, width: 60,
align: 'left', align: 'left',
form: { form: {
component: { component: {
@ -322,9 +324,9 @@ export const crudOptions = (vm) => {
}, },
component: { component: {
span: 12, span: 12,
pagination: true,
props: { multiple: true }, props: { multiple: true },
elProps: { elProps: {
pagination: true,
columns: [ columns: [
{ {
field: 'name', field: 'name',
@ -343,6 +345,9 @@ export const crudOptions = (vm) => {
} }
} }
} }
].concat(vm.commonEndColumns({ update_datetime: { showForm: false, showTable: false }, create_datetime: { showForm: false, showTable: true } })) ].concat(vm.commonEndColumns({
create_datetime: { showTable: false },
update_datetime: { showTable: false }
}))
} }
} }

View File

@ -4,8 +4,7 @@
ref="d2Crud" ref="d2Crud"
v-bind="_crudProps" v-bind="_crudProps"
v-on="_crudListeners" v-on="_crudListeners"
crud.options.tableType="vxe-table" @resetPassword="resetPassword"
@resetPwd="resetPwd"
> >
<div slot="header"> <div slot="header">
<crud-search <crud-search
@ -19,8 +18,9 @@
v-permission="'Create'" v-permission="'Create'"
type="primary" type="primary"
@click="addRow" @click="addRow"
><i class="el-icon-plus" /> 新增</el-button
> >
<i class="el-icon-plus" /> 新增
</el-button>
</el-button-group> </el-button-group>
<crud-toolbar <crud-toolbar
:search.sync="crud.searchOptions.show" :search.sync="crud.searchOptions.show"
@ -31,7 +31,7 @@
/> />
</div> </div>
</d2-crud-x> </d2-crud-x>
<el-dialog title="密码重置" :visible.sync="dialogFormVisible" :close-on-click-modal="false"> <!-- <el-dialog title="密码重置" :visible.sync="dialogFormVisible" :close-on-click-modal="false">
<el-form :model="resetPwdForm" ref="resetPwdForm" :rules="passwordRules"> <el-form :model="resetPwdForm" ref="resetPwdForm" :rules="passwordRules">
<el-form-item label="密码" prop="pwd"> <el-form-item label="密码" prop="pwd">
<el-input v-model="resetPwdForm.pwd" type="password" show-password clearable autocomplete="off"></el-input> <el-input v-model="resetPwdForm.pwd" type="password" show-password clearable autocomplete="off"></el-input>
@ -44,7 +44,7 @@
<el-button @click="dialogFormVisible = false"> </el-button> <el-button @click="dialogFormVisible = false"> </el-button>
<el-button type="primary" @click="resetPwdSubmit"> </el-button> <el-button type="primary" @click="resetPwdSubmit"> </el-button>
</div> </div>
</el-dialog> </el-dialog> -->
</d2-container> </d2-container>
</template> </template>
@ -57,39 +57,39 @@ export default {
name: 'user', name: 'user',
mixins: [d2CrudPlus.crud], mixins: [d2CrudPlus.crud],
data () { data () {
var validatePass = (rule, value, callback) => { // var validatePass = (rule, value, callback) => {
const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}') // const pwdRegex = new RegExp('(?=.*[0-9])(?=.*[a-zA-Z]).{8,30}')
if (value === '') { // if (value === '') {
callback(new Error('请输入密码')) // callback(new Error(''))
} else if (!pwdRegex.test(value)) { // } else if (!pwdRegex.test(value)) {
callback(new Error('您的密码复杂度太低(密码中必须包含字母、数字)')) // callback(new Error('()'))
} else { // } else {
if (this.resetPwdForm.pwd2 !== '') { // if (this.resetPwdForm.pwd2 !== '') {
this.$refs.resetPwdForm.validateField('pwd2') // this.$refs.resetPwdForm.validateField('pwd2')
} // }
callback() // callback()
} // }
} // }
var validatePass2 = (rule, value, callback) => { // var validatePass2 = (rule, value, callback) => {
if (value === '') { // if (value === '') {
callback(new Error('请再次输入密码')) // callback(new Error(''))
} else if (value !== this.resetPwdForm.pwd) { // } else if (value !== this.resetPwdForm.pwd) {
callback(new Error('两次输入密码不一致!')) // callback(new Error('!'))
} else { // } else {
callback() // callback()
} // }
} // }
return { return {
dialogFormVisible: false, // dialogFormVisible: false,
resetPwdForm: { // resetPwdForm: {
id: null, // id: null,
pwd: null, // pwd: null,
pwd2: null // pwd2: null
}, // },
passwordRules: { // passwordRules: {
pwd: [{ required: true, message: '必填项' }, { validator: validatePass, trigger: 'blur' }], // pwd: [{ required: true, message: '' }, { validator: validatePass, trigger: 'blur' }],
pwd2: [{ required: true, message: '必填项' }, { validator: validatePass2, trigger: 'blur' }] // pwd2: [{ required: true, message: '' }, { validator: validatePass2, trigger: 'blur' }]
} // }
} }
}, },
methods: { methods: {
@ -109,35 +109,40 @@ export default {
delRequest (row) { delRequest (row) {
return api.DelObj(row.id) return api.DelObj(row.id)
}, },
// resetPassword (scope) {
resetPwd ({ row }) { api.ResetPwd(scope.row).then((res) => {
this.dialogFormVisible = true this.$message.success('密码重置成功')
this.resetPwdForm.id = row.id
},
//
resetPwdSubmit () {
const that = this
that.$refs.resetPwdForm.validate((valid) => {
if (valid) {
const params = {
id: that.resetPwdForm.id,
newPassword: that.$md5(that.resetPwdForm.pwd),
newPassword2: that.$md5(that.resetPwdForm.pwd2)
}
api.ResetPwd(params).then(res => {
that.dialogFormVisible = false
that.resetPwdForm = {
id: null,
pwd: null,
pwd2: null
}
that.$message.success('修改成功')
})
} else {
that.$message.error('表单校验失败,请检查')
}
}) })
} }
// //
// resetPwd ({ row }) {
// this.dialogFormVisible = true
// this.resetPwdForm.id = row.id
// },
// //
// resetPwdSubmit () {
// const that = this
// that.$refs.resetPwdForm.validate((valid) => {
// if (valid) {
// const params = {
// id: that.resetPwdForm.id,
// newPassword: that.$md5(that.resetPwdForm.pwd),
// newPassword2: that.$md5(that.resetPwdForm.pwd2)
// }
// api.ResetPwd(params).then(res => {
// that.dialogFormVisible = false
// that.resetPwdForm = {
// id: null,
// pwd: null,
// pwd2: null
// }
// that.$message.success('')
// })
// } else {
// that.$message.error('')
// }
// })
// }
} }
} }
</script> </script>