feat: 适配5.x后端,重构前端架构

主要变更:
1. 重构API目录结构,采用模块化设计,适配5.x后端接口规范
2. 优化布局组件,提升用户界面体验
3. 增强国际化支持,完善多语言配置
4. 新增权限控制功能,支持前端路由权限管理
5. 优化认证流程,支持JWT认证机制
6. 新增中间件和插件,提升系统扩展性
7. 优化状态管理,采用更清晰的模块化设计
8. 新增搜索框、广告等组件,丰富页面功能
9. 更改包管理工具从npm到pnpm,提升依赖管理效率
10. 完善配置系统,支持多环境配置

适配5.x后端特性:
- 采用新的API接口规范
- 支持JWT认证机制
- 适配后端权限控制体系
- 支持后端国际化接口
- 优化数据交互方式
This commit is contained in:
Mr.April
2026-04-07 11:01:11 +08:00
parent 1b83f1890b
commit d84d972e73
127 changed files with 103052 additions and 14661 deletions
+93 -50
View File
@@ -29,7 +29,7 @@
<div class="input-container">
<el-input
v-model="formData.username"
:placeholder="t('username_placeholder')"
:placeholder="t('auth.register.username_placeholder')"
clearable
:inline-message="true"
:readonly="realNameInput"
@@ -39,7 +39,26 @@
>
<template #prefix>
<Icon
name="el-icon-User"
icon="ant-design:user-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
</template>
</el-input>
</div>
</el-form-item>
<el-form-item prop="email" class="form-item">
<div class="input-container">
<el-input
v-model="formData.email"
:placeholder="t('auth.register.email_placeholder')"
clearable
class="register-input"
>
<template #prefix>
<Icon
icon="ant-design:mail-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
@@ -52,7 +71,7 @@
<div class="input-container">
<el-input
v-model="formData.password"
:placeholder="t('password_placeholder')"
:placeholder="t('auth.register.password_placeholder')"
type="password"
clearable
show-password
@@ -60,7 +79,7 @@
>
<template #prefix>
<Icon
name="el-icon-Lock"
icon="ant-design:lock-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
@@ -72,8 +91,8 @@
<el-form-item prop="confirmPassword" class="form-item">
<div class="input-container">
<el-input
v-model="formData.confirmPassword"
:placeholder="t('confirm_password_placeholder')"
v-model="formData.confirm_password"
:placeholder="t('auth.register.confirm_password_placeholder')"
type="password"
clearable
show-password
@@ -81,7 +100,7 @@
>
<template #prefix>
<Icon
name="el-icon-Lock"
icon="ant-design:lock-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
@@ -93,13 +112,13 @@
<el-form-item prop="captchaCode" class="form-item">
<div class="input-container">
<el-input
v-model="formData.captchaCode"
:placeholder="t('captcha_placeholder')"
v-model="formData.captcha_code"
:placeholder="t('auth.register.captcha_placeholder')"
class="register-input"
>
<template #prefix>
<Icon
name="local-verify"
icon="ant-design:safety-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
@@ -124,13 +143,13 @@
<div class="input-container">
<el-input
v-model="formData.mobile"
:placeholder="t('mobile_placeholder')"
:placeholder="t('auth.register.mobile_placeholder')"
clearable
class="register-input"
>
<template #prefix>
<Icon
name="el-icon-Phone"
icon="ant-design:phone-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
@@ -142,13 +161,13 @@
<el-form-item prop="mobileCode" class="form-item">
<div class="input-container">
<el-input
v-model="formData.mobileCode"
:placeholder="t('code_placeholder')"
v-model="formData.mobile_code"
:placeholder="t('auth.register.code_placeholder')"
class="register-input"
>
<template #prefix>
<Icon
name="el-icon-Key"
icon="ant-design:key-outlined"
:color="isDarkMode ? '#ffffff' : '#000000'"
class="input-prefix-icon"
/>
@@ -157,7 +176,7 @@
<sms-code
:mobile="formData.mobile"
type="login"
v-model="formData.mobileKey"
v-model="formData.mobile_key"
@click="sendSmsCode"
ref="smsCodeRef"
></sms-code>
@@ -170,7 +189,7 @@
<!-- 登录链接 -->
<div class="form-actions">
<el-button type="primary" link @click="switchToLogin" class="login-link">
{{ t('have_account') }},{{ t('to_login') }}
{{ t('auth.register.have_account') }},{{ t('auth.register.to_login') }}
</el-button>
</div>
@@ -183,7 +202,7 @@
@click="handleRegister"
:loading="isLoading"
>
{{ isLoading ? t('registering') : t('register') }}
{{ isLoading ? t('auth.register.registering') : t('auth.register.register') }}
</el-button>
</div>
@@ -197,13 +216,13 @@
:class="isAgreeChecked ? 'icon-xuanze1' : 'icon-checkbox_nol'"
@click="toggleAgreement"
></span>
{{ t('register_agree_tips') }}
{{ t('auth.register.register_agree_tips') }}
<NuxtLink :to="protocolUrls.service" target="_blank">
<span class="agreement-link">{{ t('user_agreement') }}</span>
<span class="agreement-link">{{ t('auth.register.user_agreement') }}</span>
</NuxtLink>
{{ t('and') }}
{{ t('auth.register.and') }}
<NuxtLink :to="protocolUrls.privacy" target="_blank">
<span class="agreement-link">{{ t('privacy_agreement') }}</span>
<span class="agreement-link">{{ t('auth.register.privacy_agreement') }}</span>
</NuxtLink>
</div>
</el-form>
@@ -214,8 +233,15 @@
<script lang="ts" setup>
import { ref, reactive, computed } from 'vue'
import { registerUser, registerWithMobile } from '@/api/auth'
import useConfigStore from '@/stores/config'
import { useConfigStore } from '@/stores/config'
import { useMemberStore } from '@/stores/member'
import { useCaptcha } from '~/composables/captcha'
import type { FormInstance } from 'element-plus'
import { Icon } from '~/components/icon'
import {t} from '~/composables/lang'
import validate from '~/utils/validate'
import { ElMessage } from 'element-plus'
// 状态管理
const memberStore = useMemberStore()
@@ -227,7 +253,7 @@ const isDarkMode = computed(() => {
})
// 初始化配置
configStore.getLoginConfig()
// 配置已在 app.vue 中统一加载,这里不再重复请求
// 协议链接配置
const protocolUrls = computed(() => {
@@ -243,10 +269,10 @@ const currentType = ref('')
const registerTypes = computed(() => {
const types = []
if (configStore.login.is_username) {
types.push({ type: 'username', title: t('username_register') })
types.push({ type: 'username', title: t('auth.register.username_register') })
}
if (configStore.login.is_mobile && !configStore.login.is_bind_mobile) {
types.push({ type: 'mobile', title: t('mobile_register') })
types.push({ type: 'mobile', title: t('auth.register.mobile_register') })
}
currentType.value = types[0]?.type || ''
return types
@@ -256,13 +282,14 @@ const registerTypes = computed(() => {
const isLoading = ref(false)
const formData = reactive({
username: '',
email: '',
password: '',
confirmPassword: '',
confirm_password: '',
mobile: '',
mobileCode: '',
mobileKey: '',
captchaKey: '',
captchaCode: ''
mobile_code: '',
mobile_key: '',
captcha_key: '',
captcha_code: ''
})
// 表单验证规则
@@ -271,27 +298,43 @@ const formRules = computed(() => ({
username: {
type: 'string',
required: currentType.value === 'username',
message: t('username_placeholder'),
message: t('auth.register.username_placeholder'),
trigger: ['blur', 'change'],
},
password: {
type: 'string',
required: currentType.value === 'username',
message: t('password_placeholder'),
trigger: ['blur', 'change']
},
confirmPassword: [
email: [
{
type: 'string',
required: currentType.value === 'username',
message: t('confirm_password_placeholder'),
message: t('auth.register.email_required'),
trigger: ['blur', 'change'],
},
{
validator(rule: any, value: string, callback: any) {
if (currentType.value !== 'username') return true
return validate.email(value)
},
message: t('auth.register.email_error'),
trigger: ['change', 'blur'],
}
],
password: {
type: 'string',
required: currentType.value === 'username',
message: t('auth.register.password_placeholder'),
trigger: ['blur', 'change']
},
confirm_password: [
{
type: 'string',
required: currentType.value === 'username',
message: t('auth.register.confirm_password_placeholder'),
trigger: ['blur', 'change']
},
{
validator(rule: any, value: string, callback: any) {
return value === formData.password
},
message: t('confirm_password_error'),
message: t('auth.register.confirm_password_error'),
trigger: ['change', 'blur'],
}
],
@@ -299,7 +342,7 @@ const formRules = computed(() => ({
{
type: 'string',
required: currentType.value === 'mobile' || configStore.login.is_bind_mobile,
message: t('mobile_placeholder'),
message: t('auth.register.mobile_placeholder'),
trigger: ['blur', 'change'],
},
{
@@ -307,20 +350,20 @@ const formRules = computed(() => ({
if (currentType.value !== 'mobile' && !configStore.login.is_bind_mobile) return true
return validate.mobile(value)
},
message: t('mobile_error'),
message: t('auth.register.mobile_error'),
trigger: ['change', 'blur'],
}
],
mobileCode: {
mobile_code: {
type: 'string',
required: currentType.value === 'mobile' || configStore.login.is_bind_mobile,
message: t('code_placeholder'),
message: t('auth.register.code_placeholder'),
trigger: ['blur', 'change']
},
captchaCode: {
captcha_code: {
type: 'string',
required: currentType.value === 'username',
message: t('captcha_placeholder'),
message: t('auth.register.captcha_placeholder'),
trigger: ['blur', 'change'],
}
}))
@@ -338,7 +381,7 @@ const handleRegister = async () => {
// 检查协议同意
if (configStore.login.agreement_show && !isAgreeChecked.value) {
ElMessage.error(t('is_agree_tips'))
ElMessage.error(t('auth.register.is_agree_tips'))
return
}
@@ -347,8 +390,8 @@ const handleRegister = async () => {
try {
const registerApi = currentType.value === 'username' ? registerUser : registerWithMobile
const res = await registerApi(formData)
memberStore.setToken(res.data.token)
const data = await registerApi(formData) as any
memberStore.setToken(data.access_token, data.refresh_token || '')
memberStore.logClose()
} catch (error) {
isLoading.value = false