优化注册页 添加注册配置拉取

This commit is contained in:
root 2024-12-07 19:55:01 +08:00
parent 4f3622a553
commit 04afcb743e
4 changed files with 344 additions and 55 deletions

View file

@ -0,0 +1,51 @@
{
"message": "success",
"data": {
"username": {
"minLength": 4,
"maxLength": 16,
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]*$",
"description": "用户名只能包含字母、数字、下划线和横杠"
},
"nickname": {
"minLength": 2,
"maxLength": 12,
"pattern": "^[\\u4e00-\\u9fa5a-zA-Z0-9_-]*$",
"description": "昵称可以包含中文、字母、数字、下划线和横杠"
},
"password": {
"minLength": 8,
"maxLength": 20,
"requireNumber": true,
"requireLowercase": true,
"requireUppercase": true,
"requireSpecial": true,
"specialChars": "!@#$%^&*",
"description": "密码必须包含大小写字母、数字和特殊字符"
},
"email": {
"pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
"description": "请输入有效的电子邮件地址",
"allowedDomains": [
"gmail.com",
"outlook.com",
"qq.com",
"163.com",
"126.com"
]
},
"registration": {
"enabled": true,
"requireEmailVerification": false,
"maxAttemptsPerIP": 5,
"cooldownMinutes": 60,
"blockedUsernames": [
"admin",
"root",
"system",
"administrator"
]
}
},
"retcode": 0
}

View file

@ -116,5 +116,12 @@ export const UserAPI = {
*/
addUser(data) {
return request.post('/Management/AddUser', data)
},
/**
* 获取注册配置
*/
getRegisterConfig() {
return request.get('/RegisterConfig.json')
}
}

View file

@ -1,6 +1,6 @@
<template>
<div class="register-container">
<el-card class="register-card">
<el-card class="register-card" v-loading="configLoading">
<template #header>
<div class="register-header">
<img src="../assets/logo.png" alt="logo" class="register-logo">
@ -12,9 +12,18 @@
ref="registerFormRef"
:model="registerForm"
:rules="registerRules"
label-width="100px">
:label-position="isMobile ? 'top' : 'right'"
:label-width="isMobile ? 'auto' : '100px'"
:validate-on-rule-change="false"
:hide-required-asterisk="true">
<el-form-item label="用户名" prop="username">
<el-form-item
label="用户名"
prop="username"
:class="{ 'mobile-form-item': isMobile }">
<div v-if="isMobile" class="input-description">
{{ registerConfig.username?.description }}
</div>
<el-input
v-model="registerForm.username"
placeholder="请输入用户名"
@ -78,7 +87,7 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, onMounted, computed, onUnmounted, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { User, UserFilled, Lock, Message } from '@element-plus/icons-vue'
@ -87,7 +96,9 @@ import { UserAPI } from '../api/user'
const router = useRouter()
const registerFormRef = ref(null)
const loading = ref(false)
const configLoading = ref(true)
//
const registerForm = ref({
username: '',
nickname: '',
@ -96,6 +107,51 @@ const registerForm = ref({
confirmPassword: ''
})
//
const registerConfig = ref({})
//
const registerRules = computed(() => {
const config = registerConfig.value
if (!config.username) return {} //
return {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: config.username.minLength, max: config.username.maxLength,
message: `长度在 ${config.username.minLength}${config.username.maxLength} 个字符`,
trigger: 'blur' },
{ pattern: new RegExp(config.username.pattern),
message: config.username.description,
trigger: 'blur' }
],
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
],
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请再次输入密码', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value !== registerForm.value.password) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
}
},
trigger: 'blur'
}
]
}
})
//
const validateEmail = (rule, value, callback) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
@ -108,37 +164,45 @@ const validateEmail = (rule, value, callback) => {
}
}
const registerRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
],
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请再次输入密码', trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value !== registerForm.value.password) {
callback(new Error('两次输入的密码不一致'))
} else {
callback()
//
const fetchRegisterConfig = async () => {
configLoading.value = true
try {
const response = await UserAPI.getRegisterConfig()
if (response.retcode === 0) {
registerConfig.value = response.data
//
nextTick(() => {
if (registerFormRef.value) {
registerFormRef.value.clearValidate()
}
},
trigger: 'blur'
})
}
]
} catch (error) {
console.error('获取注册配置失败:', error)
ElMessage.error('获取注册配置失败')
} finally {
configLoading.value = false
}
}
//
const isMobile = computed(() => window.innerWidth <= 768)
//
const handleResize = () => {
isMobile.value = window.innerWidth <= 768
}
onMounted(() => {
fetchRegisterConfig()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
window.removeEventListener('resize', handleResize)
})
const handleRegister = async () => {
if (!registerFormRef.value) return
@ -188,6 +252,8 @@ const handleRegister = async () => {
margin: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
background-color: rgba(255, 255, 255, 0.409);
backdrop-filter: blur(8px);
}
.register-header {
@ -198,7 +264,6 @@ const handleRegister = async () => {
.register-logo {
width: 64px;
height: 64px;
margin-bottom: 16px;
}
.register-button {
@ -214,4 +279,79 @@ const handleRegister = async () => {
:deep(.el-form-item__label) {
font-weight: 500;
}
/* 移动端样式优化 */
@media screen and (max-width: 768px) {
.register-card {
width: 100%;
margin: 0;
border-radius: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
}
.register-container {
padding: 0;
}
.register-header {
padding: 20px 0;
.register-logo {
width: 48px;
height: 48px;
margin-bottom: 12px;
}
h2 {
font-size: 20px;
margin: 0;
}
}
:deep(.el-card__body) {
flex: 1;
padding: 16px;
display: flex;
flex-direction: column;
}
:deep(.el-form) {
flex: 1;
display: flex;
flex-direction: column;
}
.mobile-form-item {
margin-bottom: 24px;
}
.input-description {
font-size: 12px;
color: #909399;
margin-bottom: 4px;
line-height: 1.4;
}
:deep(.el-form-item__error) {
position: static;
margin-top: 4px;
}
:deep(.el-form-item__label) {
padding-bottom: 4px;
line-height: 1.4;
font-size: 14px;
}
.register-button {
margin-top: auto;
margin-bottom: 20px;
}
.form-footer {
margin-bottom: 20px;
}
}
</style>

View file

@ -181,11 +181,12 @@
</template>
</el-dialog>
<!-- 添加用户对话框 -->
<!-- 修改添加用户对话框 -->
<el-dialog
v-model="addUserDialogVisible"
title="添加用户"
width="500px">
width="500px"
v-loading="configLoading">
<el-form
ref="addUserFormRef"
:model="addUserForm"
@ -195,24 +196,32 @@
<el-input
v-model="addUserForm.username"
placeholder="请输入用户名" />
<div class="form-tip">{{ registerConfig.username?.description }}</div>
</el-form-item>
<el-form-item label="昵称" prop="nickname">
<el-input
v-model="addUserForm.nickname"
placeholder="请输入昵称" />
<div class="form-tip">{{ registerConfig.nickname?.description }}</div>
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input
v-model="addUserForm.email"
placeholder="请输入邮箱" />
<div class="form-tip">{{ registerConfig.email?.description }}</div>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="addUserForm.password"
type="password"
show-password
placeholder="请输入密码" />
<div class="form-tip">{{ registerConfig.password?.description }}</div>
</el-form-item>
<el-form-item label="权限" prop="mask">
<el-select v-model="addUserForm.mask" placeholder="请选择权限">
<el-option :value="0" label="普通用户" />
@ -454,7 +463,7 @@ const submitResetPassword = async () => {
resetPasswordForm.value.newPassword
)
if (response.retcode === 0) {
ElMessage.success('<EFBFBD><EFBFBD><EFBFBD>码重置成功')
ElMessage.success('码重置成功')
resetPasswordDialogVisible.value = false
}
} catch (error) {
@ -558,28 +567,94 @@ const validateEmail = (rule, value, callback) => {
}
}
const addUserRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
],
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
],
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能小于6位', trigger: 'blur' }
],
mask: [
{ required: true, message: '请选择权限', trigger: 'change' }
]
//
const registerConfig = ref({})
const configLoading = ref(false)
//
const fetchRegisterConfig = async () => {
configLoading.value = true
try {
const response = await UserAPI.getRegisterConfig()
if (response.retcode === 0) {
registerConfig.value = response.data
}
} catch (error) {
console.error('获取注册配置失败:', error)
} finally {
configLoading.value = false
}
}
//
//
const addUserRules = computed(() => {
const config = registerConfig.value
if (!config.username) return {}
return {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: config.username.minLength, max: config.username.maxLength,
message: `长度在 ${config.username.minLength}${config.username.maxLength} 个字符`,
trigger: 'blur' },
{ pattern: new RegExp(config.username.pattern),
message: config.username.description,
trigger: 'blur' }
],
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' },
{ min: config.nickname.minLength, max: config.nickname.maxLength,
message: `长度在 ${config.nickname.minLength}${config.nickname.maxLength} 个字符`,
trigger: 'blur' }
],
email: [
{ required: true, message: '请输入电子邮件', trigger: 'blur' },
{ pattern: new RegExp(config.email.pattern),
message: config.email.description,
trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: config.password.minLength, max: config.password.maxLength,
message: `密码长度在 ${config.password.minLength}${config.password.maxLength} 位之间`,
trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (!value) {
callback()
return
}
const errors = []
if (config.password.requireNumber && !/\d/.test(value)) {
errors.push('需要包含数字')
}
if (config.password.requireLowercase && !/[a-z]/.test(value)) {
errors.push('需要包含小写字母')
}
if (config.password.requireUppercase && !/[A-Z]/.test(value)) {
errors.push('需要包含大写字母')
}
if (config.password.requireSpecial && !/[!@#$%^&*]/.test(value)) {
errors.push('需要包含特殊字符')
}
if (errors.length) {
callback(new Error('密码' + errors.join('、')))
} else {
callback()
}
},
trigger: 'blur'
}
],
mask: [
{ required: true, message: '请选择权限', trigger: 'change' }
]
}
})
//
const showAddUserDialog = () => {
addUserForm.value = {
username: '',
@ -588,9 +663,18 @@ const showAddUserDialog = () => {
password: '',
mask: 0
}
//
if (!registerConfig.value.username) {
fetchRegisterConfig()
}
addUserDialogVisible.value = true
}
//
onMounted(() => {
fetchRegisterConfig()
})
//
const handleAddUser = async () => {
if (!addUserFormRef.value) return
@ -789,4 +873,11 @@ const isSuperAdmin = computed(() => userInfo.value?.mask === 2)
}
}
}
.form-tip {
font-size: 12px;
color: #909399;
margin-top: 4px;
line-height: 1.4;
}
</style>