ts转js
This commit is contained in:
@@ -1,3 +1,2 @@
|
|||||||
/node_modules/*
|
/node_modules/*
|
||||||
/dist/*
|
/dist/*
|
||||||
/src/main.ts
|
|
||||||
@@ -42,6 +42,6 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
46
src/App.vue
46
src/App.vue
@@ -4,31 +4,31 @@
|
|||||||
</ElConfigProvider>
|
</ElConfigProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import { useUserStore } from './store/modules/user'
|
import { useUserStore } from './store/modules/user'
|
||||||
import zh from 'element-plus/es/locale/lang/zh-cn'
|
import zh from 'element-plus/es/locale/lang/zh-cn'
|
||||||
import en from 'element-plus/es/locale/lang/en'
|
import en from 'element-plus/es/locale/lang/en'
|
||||||
import { systemUpgrade } from './utils/sys'
|
import { systemUpgrade } from './utils/sys'
|
||||||
import { toggleTransition } from './utils/ui/animation'
|
import { toggleTransition } from './utils/ui/animation'
|
||||||
import { checkStorageCompatibility } from './utils/storage'
|
import { checkStorageCompatibility } from './utils/storage'
|
||||||
import { initializeTheme } from './hooks/core/useTheme'
|
import { initializeTheme } from './hooks/core/useTheme'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const { language } = storeToRefs(userStore)
|
const { language } = storeToRefs(userStore)
|
||||||
|
|
||||||
const locales = {
|
const locales = {
|
||||||
zh: zh,
|
zh: zh,
|
||||||
en: en
|
en: en
|
||||||
}
|
}
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
toggleTransition(true)
|
toggleTransition(true)
|
||||||
initializeTheme()
|
initializeTheme()
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
checkStorageCompatibility()
|
checkStorageCompatibility()
|
||||||
toggleTransition(false)
|
toggleTransition(false)
|
||||||
systemUpgrade()
|
systemUpgrade()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import request from '@/utils/http'
|
|||||||
* @param params 登录参数
|
* @param params 登录参数
|
||||||
* @returns 登录响应
|
* @returns 登录响应
|
||||||
*/
|
*/
|
||||||
export function fetchLogin(params: Api.Auth.LoginParams) {
|
export function fetchLogin(params) {
|
||||||
return request.post<Api.Auth.LoginResponse>({
|
return request.post({
|
||||||
url: '/api/auth/login',
|
url: '/api/auth/login',
|
||||||
params
|
params
|
||||||
// showSuccessMessage: true // 显示成功消息
|
// showSuccessMessage: true // 显示成功消息
|
||||||
@@ -19,7 +19,7 @@ export function fetchLogin(params: Api.Auth.LoginParams) {
|
|||||||
* @returns 用户信息
|
* @returns 用户信息
|
||||||
*/
|
*/
|
||||||
export function fetchGetUserInfo() {
|
export function fetchGetUserInfo() {
|
||||||
return request.get<Api.Auth.UserInfo>({
|
return request.get({
|
||||||
url: '/api/user/info'
|
url: '/api/user/info'
|
||||||
// 自定义请求头
|
// 自定义请求头
|
||||||
// headers: {
|
// headers: {
|
||||||
24
src/api/system-manage.js
Normal file
24
src/api/system-manage.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import request from '@/utils/http'
|
||||||
|
|
||||||
|
// 获取用户列表
|
||||||
|
export function fetchGetUserList(params) {
|
||||||
|
return request.get({
|
||||||
|
url: '/api/user/list',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取角色列表
|
||||||
|
export function fetchGetRoleList(params) {
|
||||||
|
return request.get({
|
||||||
|
url: '/api/role/list',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取菜单列表
|
||||||
|
export function fetchGetMenuList() {
|
||||||
|
return request.get({
|
||||||
|
url: '/api/v3/system/menus/simple'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import request from '@/utils/http'
|
|
||||||
import { AppRouteRecord } from '@/types/router'
|
|
||||||
|
|
||||||
// 获取用户列表
|
|
||||||
export function fetchGetUserList(params: Api.SystemManage.UserSearchParams) {
|
|
||||||
return request.get<Api.SystemManage.UserList>({
|
|
||||||
url: '/api/user/list',
|
|
||||||
params
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取角色列表
|
|
||||||
export function fetchGetRoleList(params: Api.SystemManage.RoleSearchParams) {
|
|
||||||
return request.get<Api.SystemManage.RoleList>({
|
|
||||||
url: '/api/role/list',
|
|
||||||
params
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取菜单列表
|
|
||||||
export function fetchGetMenuList() {
|
|
||||||
return request.get<AppRouteRecord[]>({
|
|
||||||
url: '/api/v3/system/menus/simple'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -22,8 +22,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
import type { I18n, I18nOptions } from 'vue-i18n'
|
|
||||||
import { LanguageEnum } from '@/enums/appEnum'
|
|
||||||
import { getSystemStorage } from '@/utils/storage'
|
import { getSystemStorage } from '@/utils/storage'
|
||||||
import { StorageKeyManager } from '@/utils/storage/storage-key-manager'
|
import { StorageKeyManager } from '@/utils/storage/storage-key-manager'
|
||||||
|
|
||||||
@@ -40,8 +38,8 @@ const storageKeyManager = new StorageKeyManager()
|
|||||||
* 语言消息对象
|
* 语言消息对象
|
||||||
*/
|
*/
|
||||||
const messages = {
|
const messages = {
|
||||||
[LanguageEnum.EN]: enMessages,
|
'en': enMessages,
|
||||||
[LanguageEnum.ZH]: zhMessages
|
'zh': zhMessages
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,15 +47,15 @@ const messages = {
|
|||||||
* 用于语言切换下拉框
|
* 用于语言切换下拉框
|
||||||
*/
|
*/
|
||||||
export const languageOptions = [
|
export const languageOptions = [
|
||||||
{ value: LanguageEnum.ZH, label: '简体中文' },
|
{ value: 'zh', label: '简体中文' },
|
||||||
{ value: LanguageEnum.EN, label: 'English' }
|
{ value: 'en', label: 'English' }
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从存储中获取语言设置
|
* 从存储中获取语言设置
|
||||||
* @returns 语言设置,如果获取失败则返回默认语言
|
* @returns 语言设置,如果获取失败则返回默认语言
|
||||||
*/
|
*/
|
||||||
const getDefaultLanguage = (): LanguageEnum => {
|
const getDefaultLanguage = () => {
|
||||||
// 尝试从版本化的存储中获取语言设置
|
// 尝试从版本化的存储中获取语言设置
|
||||||
try {
|
try {
|
||||||
const storageKey = storageKeyManager.getStorageKey('user')
|
const storageKey = storageKeyManager.getStorageKey('user')
|
||||||
@@ -65,7 +63,7 @@ const getDefaultLanguage = (): LanguageEnum => {
|
|||||||
|
|
||||||
if (userStore) {
|
if (userStore) {
|
||||||
const { language } = JSON.parse(userStore)
|
const { language } = JSON.parse(userStore)
|
||||||
if (language && Object.values(LanguageEnum).includes(language)) {
|
if (language && Object.values(['zh', 'en']).includes(language)) {
|
||||||
return language
|
return language
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +76,7 @@ const getDefaultLanguage = (): LanguageEnum => {
|
|||||||
const sys = getSystemStorage()
|
const sys = getSystemStorage()
|
||||||
if (sys) {
|
if (sys) {
|
||||||
const { user } = JSON.parse(sys)
|
const { user } = JSON.parse(sys)
|
||||||
if (user?.language && Object.values(LanguageEnum).includes(user.language)) {
|
if (user?.language && Object.values(['zh', 'en']).includes(user.language)) {
|
||||||
return user.language
|
return user.language
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,37 +85,30 @@ const getDefaultLanguage = (): LanguageEnum => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 返回默认语言
|
// 返回默认语言
|
||||||
console.debug('[i18n] 使用默认语言:', LanguageEnum.ZH)
|
console.debug('[i18n] 使用默认语言:', 'zh')
|
||||||
return LanguageEnum.ZH
|
return 'zh'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i18n 配置选项
|
* i18n 配置选项
|
||||||
*/
|
*/
|
||||||
const i18nOptions: I18nOptions = {
|
const i18nOptions = {
|
||||||
locale: getDefaultLanguage(),
|
locale: getDefaultLanguage(),
|
||||||
legacy: false,
|
legacy: false,
|
||||||
globalInjection: true,
|
globalInjection: true,
|
||||||
fallbackLocale: LanguageEnum.ZH,
|
fallbackLocale: 'zh',
|
||||||
messages
|
messages
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i18n 实例
|
* i18n 实例
|
||||||
*/
|
*/
|
||||||
const i18n: I18n = createI18n(i18nOptions)
|
const i18n = createI18n(i18nOptions)
|
||||||
|
|
||||||
/**
|
|
||||||
* 翻译函数类型
|
|
||||||
*/
|
|
||||||
interface Translation {
|
|
||||||
(key: string): string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 全局翻译函数
|
* 全局翻译函数
|
||||||
* 可在任何地方使用,无需导入 useI18n
|
* 可在任何地方使用,无需导入 useI18n
|
||||||
*/
|
*/
|
||||||
export const $t = i18n.global.t as Translation
|
export const $t = i18n.global.t
|
||||||
|
|
||||||
export default i18n
|
export default i18n
|
||||||
20
src/main.js
Normal file
20
src/main.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import App from './App.vue'
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
import { initStore } from './store' // Store
|
||||||
|
import { initRouter } from './router' // Router
|
||||||
|
import language from './locales' // 国际化
|
||||||
|
import '@styles/core/tailwind.css' // tailwind
|
||||||
|
import '@styles/index.scss' // 样式
|
||||||
|
import { setupGlobDirectives } from './directives'
|
||||||
|
import { setupErrorHandle } from './utils/sys/error-handle'
|
||||||
|
|
||||||
|
document.addEventListener('touchstart', function () {}, { passive: false })
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
initStore(app)
|
||||||
|
initRouter(app)
|
||||||
|
setupGlobDirectives(app)
|
||||||
|
setupErrorHandle(app)
|
||||||
|
|
||||||
|
app.use(language)
|
||||||
|
app.mount('#app')
|
||||||
25
src/main.ts
25
src/main.ts
@@ -1,25 +0,0 @@
|
|||||||
import App from './App.vue'
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
import { initStore } from './store' // Store
|
|
||||||
import { initRouter } from './router' // Router
|
|
||||||
import language from './locales' // 国际化
|
|
||||||
import '@styles/core/tailwind.css' // tailwind
|
|
||||||
import '@styles/index.scss' // 样式
|
|
||||||
import '@utils/sys/console.ts' // 控制台输出内容
|
|
||||||
import { setupGlobDirectives } from './directives'
|
|
||||||
import { setupErrorHandle } from './utils/sys/error-handle'
|
|
||||||
|
|
||||||
document.addEventListener(
|
|
||||||
'touchstart',
|
|
||||||
function () {},
|
|
||||||
{ passive: false }
|
|
||||||
)
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
initStore(app)
|
|
||||||
initRouter(app)
|
|
||||||
setupGlobDirectives(app)
|
|
||||||
setupErrorHandle(app)
|
|
||||||
|
|
||||||
app.use(language)
|
|
||||||
app.mount('#app')
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// ANSI 转义码生成网站 https://patorjk.com/software/taag/#p=display&f=Big&t=ABB%0A
|
|
||||||
const asciiArt = `
|
|
||||||
\x1b[32m欢迎使用 Art Design Pro!
|
|
||||||
\x1b[0m
|
|
||||||
\x1b[36m哇!你居然在用我的项目~ 好用的话别忘了去 GitHub 点个 ★Star 呀,你的支持就是我更新的超强动力!祝使用体验满分💯
|
|
||||||
\x1b[0m
|
|
||||||
\x1b[33mGitHub: https://github.com/Daymychen/art-design-pro
|
|
||||||
\x1b[0m
|
|
||||||
\x1b[31m技术支持(QQ群): 1038930070,和开发者一起交流~ 群里有小伙伴实时答疑,遇到问题不用慌!
|
|
||||||
\x1b[0m
|
|
||||||
`
|
|
||||||
|
|
||||||
console.log(asciiArt)
|
|
||||||
@@ -11,21 +11,13 @@
|
|||||||
<p class="sub-title">{{ $t('forgetPassword.subTitle') }}</p>
|
<p class="sub-title">{{ $t('forgetPassword.subTitle') }}</p>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<span class="input-label" v-if="showInputLabel">账号</span>
|
<span class="input-label" v-if="showInputLabel">账号</span>
|
||||||
<ElInput
|
<ElInput class="custom-height" :placeholder="$t('forgetPassword.placeholder')"
|
||||||
class="custom-height"
|
v-model.trim="username" />
|
||||||
:placeholder="$t('forgetPassword.placeholder')"
|
|
||||||
v-model.trim="username"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 15px">
|
<div style="margin-top: 15px">
|
||||||
<ElButton
|
<ElButton class="w-full custom-height" type="primary" @click="register" :loading="loading"
|
||||||
class="w-full custom-height"
|
v-ripple>
|
||||||
type="primary"
|
|
||||||
@click="register"
|
|
||||||
:loading="loading"
|
|
||||||
v-ripple
|
|
||||||
>
|
|
||||||
{{ $t('forgetPassword.submitBtnText') }}
|
{{ $t('forgetPassword.submitBtnText') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,22 +33,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
defineOptions({ name: 'ForgetPassword' })
|
defineOptions({ name: 'ForgetPassword' })
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const showInputLabel = ref(false)
|
const showInputLabel = ref(false)
|
||||||
|
|
||||||
const username = ref('')
|
const username = ref('')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const register = async () => {}
|
const register = async () => { }
|
||||||
|
|
||||||
const toLogin = () => {
|
const toLogin = () => {
|
||||||
router.push({ name: 'Login' })
|
router.push({ name: 'Login' })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import '../login/style.css';
|
@import '../login/style.css';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,65 +10,36 @@
|
|||||||
<div class="form">
|
<div class="form">
|
||||||
<h3 class="title">{{ $t('login.title') }}</h3>
|
<h3 class="title">{{ $t('login.title') }}</h3>
|
||||||
<p class="sub-title">{{ $t('login.subTitle') }}</p>
|
<p class="sub-title">{{ $t('login.subTitle') }}</p>
|
||||||
<ElForm
|
<ElForm ref="formRef" :model="formData" :rules="rules" :key="formKey" @keyup.enter="handleSubmit"
|
||||||
ref="formRef"
|
style="margin-top: 25px">
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
:key="formKey"
|
|
||||||
@keyup.enter="handleSubmit"
|
|
||||||
style="margin-top: 25px"
|
|
||||||
>
|
|
||||||
<ElFormItem prop="account">
|
<ElFormItem prop="account">
|
||||||
<ElSelect v-model="formData.account" @change="setupAccount">
|
<ElSelect v-model="formData.account" @change="setupAccount">
|
||||||
<ElOption
|
<ElOption v-for="account in accounts" :key="account.key" :label="account.label"
|
||||||
v-for="account in accounts"
|
:value="account.key">
|
||||||
:key="account.key"
|
|
||||||
:label="account.label"
|
|
||||||
:value="account.key"
|
|
||||||
>
|
|
||||||
<span>{{ account.label }}</span>
|
<span>{{ account.label }}</span>
|
||||||
</ElOption>
|
</ElOption>
|
||||||
</ElSelect>
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem prop="username">
|
<ElFormItem prop="username">
|
||||||
<ElInput
|
<ElInput class="custom-height" :placeholder="$t('login.placeholder.username')"
|
||||||
class="custom-height"
|
v-model.trim="formData.username" />
|
||||||
:placeholder="$t('login.placeholder.username')"
|
|
||||||
v-model.trim="formData.username"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem prop="password">
|
<ElFormItem prop="password">
|
||||||
<ElInput
|
<ElInput class="custom-height" :placeholder="$t('login.placeholder.password')"
|
||||||
class="custom-height"
|
v-model.trim="formData.password" type="password" autocomplete="off" show-password />
|
||||||
:placeholder="$t('login.placeholder.password')"
|
|
||||||
v-model.trim="formData.password"
|
|
||||||
type="password"
|
|
||||||
autocomplete="off"
|
|
||||||
show-password
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<!-- 推拽验证 -->
|
<!-- 推拽验证 -->
|
||||||
<div class="relative pb-5 mt-6">
|
<div class="relative pb-5 mt-6">
|
||||||
<div
|
<div class="relative z-[2] overflow-hidden select-none rounded-lg border border-transparent tad-300"
|
||||||
class="relative z-[2] overflow-hidden select-none rounded-lg border border-transparent tad-300"
|
:class="{ '!border-[#FF4E4F]': !isPassing && isClickPass }">
|
||||||
:class="{ '!border-[#FF4E4F]': !isPassing && isClickPass }"
|
<ArtDragVerify ref="dragVerify" v-model:value="isPassing" :text="$t('login.sliderText')"
|
||||||
>
|
textColor="var(--art-gray-700)" :successText="$t('login.sliderSuccessText')"
|
||||||
<ArtDragVerify
|
progressBarBg="var(--main-color)" :background="isDark ? '#26272F' : '#F1F1F4'"
|
||||||
ref="dragVerify"
|
handlerBg="var(--default-box-color)" />
|
||||||
v-model:value="isPassing"
|
|
||||||
:text="$t('login.sliderText')"
|
|
||||||
textColor="var(--art-gray-700)"
|
|
||||||
:successText="$t('login.sliderSuccessText')"
|
|
||||||
progressBarBg="var(--main-color)"
|
|
||||||
:background="isDark ? '#26272F' : '#F1F1F4'"
|
|
||||||
handlerBg="var(--default-box-color)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p class="absolute top-0 z-[1] px-px mt-2 text-xs text-[#f56c6c] tad-300"
|
||||||
class="absolute top-0 z-[1] px-px mt-2 text-xs text-[#f56c6c] tad-300"
|
:class="{ 'translate-y-10': !isPassing && isClickPass }">
|
||||||
:class="{ 'translate-y-10': !isPassing && isClickPass }"
|
|
||||||
>
|
|
||||||
{{ $t('login.placeholder.slider') }}
|
{{ $t('login.placeholder.slider') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,13 +54,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 30px">
|
<div style="margin-top: 30px">
|
||||||
<ElButton
|
<ElButton class="w-full custom-height" type="primary" @click="handleSubmit"
|
||||||
class="w-full custom-height"
|
:loading="loading" v-ripple>
|
||||||
type="primary"
|
|
||||||
@click="handleSubmit"
|
|
||||||
:loading="loading"
|
|
||||||
v-ripple
|
|
||||||
>
|
|
||||||
{{ $t('login.btnText') }}
|
{{ $t('login.btnText') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,178 +73,168 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import AppConfig from '@/config'
|
import AppConfig from '@/config'
|
||||||
import { useUserStore } from '@/store/modules/user'
|
import { useUserStore } from '@/store/modules/user'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { HttpError } from '@/utils/http/error'
|
import { HttpError } from '@/utils/http/error'
|
||||||
import { fetchLogin } from '@/api/auth'
|
import { fetchLogin } from '@/api/auth'
|
||||||
import { ElNotification, type FormInstance, type FormRules } from 'element-plus'
|
import { ElNotification } from 'element-plus'
|
||||||
import { useSettingStore } from '@/store/modules/setting'
|
import { useSettingStore } from '@/store/modules/setting'
|
||||||
|
|
||||||
defineOptions({ name: 'Login' })
|
defineOptions({ name: 'Login' })
|
||||||
|
|
||||||
const settingStore = useSettingStore()
|
const settingStore = useSettingStore()
|
||||||
const { isDark } = storeToRefs(settingStore)
|
const { isDark } = storeToRefs(settingStore)
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n()
|
||||||
const formKey = ref(0)
|
const formKey = ref(0)
|
||||||
|
|
||||||
// 监听语言切换,重置表单
|
// 监听语言切换,重置表单
|
||||||
watch(locale, () => {
|
watch(locale, () => {
|
||||||
formKey.value++
|
formKey.value++
|
||||||
})
|
})
|
||||||
|
|
||||||
type AccountKey = 'super' | 'admin' | 'user'
|
const accounts = computed(() => [
|
||||||
|
{
|
||||||
export interface Account {
|
key: 'super',
|
||||||
key: AccountKey
|
label: t('login.roles.super'),
|
||||||
label: string
|
userName: 'Super',
|
||||||
userName: string
|
password: '123456',
|
||||||
password: string
|
roles: ['R_SUPER']
|
||||||
roles: string[]
|
},
|
||||||
|
{
|
||||||
|
key: 'admin',
|
||||||
|
label: t('login.roles.admin'),
|
||||||
|
userName: 'Admin',
|
||||||
|
password: '123456',
|
||||||
|
roles: ['R_ADMIN']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'user',
|
||||||
|
label: t('login.roles.user'),
|
||||||
|
userName: 'User',
|
||||||
|
password: '123456',
|
||||||
|
roles: ['R_USER']
|
||||||
}
|
}
|
||||||
|
])
|
||||||
|
|
||||||
const accounts = computed<Account[]>(() => [
|
const dragVerify = ref()
|
||||||
{
|
|
||||||
key: 'super',
|
const userStore = useUserStore()
|
||||||
label: t('login.roles.super'),
|
const router = useRouter()
|
||||||
userName: 'Super',
|
const route = useRoute()
|
||||||
password: '123456',
|
const isPassing = ref(false)
|
||||||
roles: ['R_SUPER']
|
const isClickPass = ref(false)
|
||||||
},
|
|
||||||
{
|
const systemName = AppConfig.systemInfo.name
|
||||||
key: 'admin',
|
const formRef = ref()
|
||||||
label: t('login.roles.admin'),
|
|
||||||
userName: 'Admin',
|
const formData = reactive({
|
||||||
password: '123456',
|
account: '',
|
||||||
roles: ['R_ADMIN']
|
username: '',
|
||||||
},
|
password: '',
|
||||||
{
|
rememberPassword: true
|
||||||
key: 'user',
|
})
|
||||||
label: t('login.roles.user'),
|
|
||||||
userName: 'User',
|
const rules = computed(() => ({
|
||||||
password: '123456',
|
username: [{ required: true, message: t('login.placeholder.username'), trigger: 'blur' }],
|
||||||
roles: ['R_USER']
|
password: [{ required: true, message: t('login.placeholder.password'), trigger: 'blur' }]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setupAccount('super')
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置账号
|
||||||
|
const setupAccount = (key) => {
|
||||||
|
const selectedAccount = accounts.value.find((account) => account.key === key)
|
||||||
|
formData.account = key
|
||||||
|
formData.username = selectedAccount?.userName ?? ''
|
||||||
|
formData.password = selectedAccount?.password ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 表单验证
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
// 拖拽验证
|
||||||
|
if (!isPassing.value) {
|
||||||
|
isClickPass.value = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
])
|
|
||||||
|
|
||||||
const dragVerify = ref()
|
loading.value = true
|
||||||
|
|
||||||
const userStore = useUserStore()
|
// 登录请求
|
||||||
const router = useRouter()
|
const { username, password } = formData
|
||||||
const route = useRoute()
|
|
||||||
const isPassing = ref(false)
|
|
||||||
const isClickPass = ref(false)
|
|
||||||
|
|
||||||
const systemName = AppConfig.systemInfo.name
|
const { token, refreshToken } = await fetchLogin({
|
||||||
const formRef = ref<FormInstance>()
|
userName: username,
|
||||||
|
password
|
||||||
|
})
|
||||||
|
|
||||||
const formData = reactive({
|
// 验证token
|
||||||
account: '',
|
if (!token) {
|
||||||
username: '',
|
throw new Error('Login failed - no token received')
|
||||||
password: '',
|
|
||||||
rememberPassword: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const rules = computed<FormRules>(() => ({
|
|
||||||
username: [{ required: true, message: t('login.placeholder.username'), trigger: 'blur' }],
|
|
||||||
password: [{ required: true, message: t('login.placeholder.password'), trigger: 'blur' }]
|
|
||||||
}))
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
setupAccount('super')
|
|
||||||
})
|
|
||||||
|
|
||||||
// 设置账号
|
|
||||||
const setupAccount = (key: AccountKey) => {
|
|
||||||
const selectedAccount = accounts.value.find((account: Account) => account.key === key)
|
|
||||||
formData.account = key
|
|
||||||
formData.username = selectedAccount?.userName ?? ''
|
|
||||||
formData.password = selectedAccount?.password ?? ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登录
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
if (!formRef.value) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 表单验证
|
|
||||||
const valid = await formRef.value.validate()
|
|
||||||
if (!valid) return
|
|
||||||
|
|
||||||
// 拖拽验证
|
|
||||||
if (!isPassing.value) {
|
|
||||||
isClickPass.value = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
loading.value = true
|
|
||||||
|
|
||||||
// 登录请求
|
|
||||||
const { username, password } = formData
|
|
||||||
|
|
||||||
const { token, refreshToken } = await fetchLogin({
|
|
||||||
userName: username,
|
|
||||||
password
|
|
||||||
})
|
|
||||||
|
|
||||||
// 验证token
|
|
||||||
if (!token) {
|
|
||||||
throw new Error('Login failed - no token received')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存储 token 和登录状态
|
|
||||||
userStore.setToken(token, refreshToken)
|
|
||||||
userStore.setLoginStatus(true)
|
|
||||||
|
|
||||||
// 登录成功处理
|
|
||||||
showLoginSuccessNotice()
|
|
||||||
|
|
||||||
// 获取 redirect 参数,如果存在则跳转到指定页面,否则跳转到首页
|
|
||||||
const redirect = route.query.redirect as string
|
|
||||||
router.push(redirect || '/')
|
|
||||||
} catch (error) {
|
|
||||||
// 处理 HttpError
|
|
||||||
if (error instanceof HttpError) {
|
|
||||||
// console.log(error.code)
|
|
||||||
} else {
|
|
||||||
// 处理非 HttpError
|
|
||||||
// ElMessage.error('登录失败,请稍后重试')
|
|
||||||
console.error('[Login] Unexpected error:', error)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
resetDragVerify()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 重置拖拽验证
|
// 存储 token 和登录状态
|
||||||
const resetDragVerify = () => {
|
userStore.setToken(token, refreshToken)
|
||||||
dragVerify.value.reset()
|
userStore.setLoginStatus(true)
|
||||||
}
|
|
||||||
|
|
||||||
// 登录成功提示
|
// 登录成功处理
|
||||||
const showLoginSuccessNotice = () => {
|
showLoginSuccessNotice()
|
||||||
setTimeout(() => {
|
|
||||||
ElNotification({
|
// 获取 redirect 参数,如果存在则跳转到指定页面,否则跳转到首页
|
||||||
title: t('login.success.title'),
|
const redirect = route.query.redirect
|
||||||
type: 'success',
|
router.push(redirect || '/')
|
||||||
duration: 2500,
|
} catch (error) {
|
||||||
zIndex: 10000,
|
// 处理 HttpError
|
||||||
message: `${t('login.success.message')}, ${systemName}!`
|
if (error instanceof HttpError) {
|
||||||
})
|
// console.log(error.code)
|
||||||
}, 1000)
|
} else {
|
||||||
|
// 处理非 HttpError
|
||||||
|
// ElMessage.error('登录失败,请稍后重试')
|
||||||
|
console.error('[Login] Unexpected error:', error)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
resetDragVerify()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置拖拽验证
|
||||||
|
const resetDragVerify = () => {
|
||||||
|
dragVerify.value.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 登录成功提示
|
||||||
|
const showLoginSuccessNotice = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
ElNotification({
|
||||||
|
title: t('login.success.title'),
|
||||||
|
type: 'success',
|
||||||
|
duration: 2500,
|
||||||
|
zIndex: 10000,
|
||||||
|
message: `${t('login.success.message')}, ${systemName}!`
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import './style.css';
|
@import './style.css';
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:deep(.el-select__wrapper) {
|
:deep(.el-select__wrapper) {
|
||||||
height: 40px !important;
|
height: 40px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,64 +10,36 @@
|
|||||||
<div class="form">
|
<div class="form">
|
||||||
<h3 class="title">{{ $t('register.title') }}</h3>
|
<h3 class="title">{{ $t('register.title') }}</h3>
|
||||||
<p class="sub-title">{{ $t('register.subTitle') }}</p>
|
<p class="sub-title">{{ $t('register.subTitle') }}</p>
|
||||||
<ElForm
|
<ElForm class="mt-7.5" ref="formRef" :model="formData" :rules="rules" label-position="top"
|
||||||
class="mt-7.5"
|
:key="formKey">
|
||||||
ref="formRef"
|
|
||||||
:model="formData"
|
|
||||||
:rules="rules"
|
|
||||||
label-position="top"
|
|
||||||
:key="formKey"
|
|
||||||
>
|
|
||||||
<ElFormItem prop="username">
|
<ElFormItem prop="username">
|
||||||
<ElInput
|
<ElInput class="custom-height" v-model.trim="formData.username"
|
||||||
class="custom-height"
|
:placeholder="$t('register.placeholder.username')" />
|
||||||
v-model.trim="formData.username"
|
|
||||||
:placeholder="$t('register.placeholder.username')"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElFormItem prop="password">
|
<ElFormItem prop="password">
|
||||||
<ElInput
|
<ElInput class="custom-height" v-model.trim="formData.password"
|
||||||
class="custom-height"
|
:placeholder="$t('register.placeholder.password')" type="password" autocomplete="off"
|
||||||
v-model.trim="formData.password"
|
show-password />
|
||||||
:placeholder="$t('register.placeholder.password')"
|
|
||||||
type="password"
|
|
||||||
autocomplete="off"
|
|
||||||
show-password
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElFormItem prop="confirmPassword">
|
<ElFormItem prop="confirmPassword">
|
||||||
<ElInput
|
<ElInput class="custom-height" v-model.trim="formData.confirmPassword"
|
||||||
class="custom-height"
|
:placeholder="$t('register.placeholder.confirmPassword')" type="password"
|
||||||
v-model.trim="formData.confirmPassword"
|
autocomplete="off" @keyup.enter="register" show-password />
|
||||||
:placeholder="$t('register.placeholder.confirmPassword')"
|
|
||||||
type="password"
|
|
||||||
autocomplete="off"
|
|
||||||
@keyup.enter="register"
|
|
||||||
show-password
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElFormItem prop="agreement">
|
<ElFormItem prop="agreement">
|
||||||
<ElCheckbox v-model="formData.agreement">
|
<ElCheckbox v-model="formData.agreement">
|
||||||
{{ $t('register.agreeText') }}
|
{{ $t('register.agreeText') }}
|
||||||
<RouterLink
|
<RouterLink style="color: var(--theme-color); text-decoration: none"
|
||||||
style="color: var(--theme-color); text-decoration: none"
|
to="/privacy-policy">{{ $t('register.privacyPolicy') }}</RouterLink>
|
||||||
to="/privacy-policy"
|
|
||||||
>{{ $t('register.privacyPolicy') }}</RouterLink
|
|
||||||
>
|
|
||||||
</ElCheckbox>
|
</ElCheckbox>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<div style="margin-top: 15px">
|
<div style="margin-top: 15px">
|
||||||
<ElButton
|
<ElButton class="w-full custom-height" type="primary" @click="register" :loading="loading"
|
||||||
class="w-full custom-height"
|
v-ripple>
|
||||||
type="primary"
|
|
||||||
@click="register"
|
|
||||||
:loading="loading"
|
|
||||||
v-ripple
|
|
||||||
>
|
|
||||||
{{ $t('register.submitBtnText') }}
|
{{ $t('register.submitBtnText') }}
|
||||||
</ElButton>
|
</ElButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,160 +57,152 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
|
||||||
|
|
||||||
defineOptions({ name: 'Register' })
|
defineOptions({ name: 'Register' })
|
||||||
|
|
||||||
interface RegisterForm {
|
const USERNAME_MIN_LENGTH = 3
|
||||||
username: string
|
const USERNAME_MAX_LENGTH = 20
|
||||||
password: string
|
const PASSWORD_MIN_LENGTH = 6
|
||||||
confirmPassword: string
|
const REDIRECT_DELAY = 1000
|
||||||
agreement: boolean
|
|
||||||
|
const { t, locale } = useI18n()
|
||||||
|
const router = useRouter()
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const formKey = ref(0)
|
||||||
|
|
||||||
|
// 监听语言切换,重置表单
|
||||||
|
watch(locale, () => {
|
||||||
|
formKey.value++
|
||||||
|
})
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
confirmPassword: '',
|
||||||
|
agreement: false
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证密码
|
||||||
|
* 当密码输入后,如果确认密码已填写,则触发确认密码的验证
|
||||||
|
*/
|
||||||
|
const validatePassword = (_rule, value, callback) => {
|
||||||
|
if (!value) {
|
||||||
|
callback(new Error(t('register.placeholder.password')))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const USERNAME_MIN_LENGTH = 3
|
if (formData.confirmPassword) {
|
||||||
const USERNAME_MAX_LENGTH = 20
|
formRef.value?.validateField('confirmPassword')
|
||||||
const PASSWORD_MIN_LENGTH = 6
|
|
||||||
const REDIRECT_DELAY = 1000
|
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
|
||||||
const router = useRouter()
|
|
||||||
const formRef = ref<FormInstance>()
|
|
||||||
|
|
||||||
const loading = ref(false)
|
|
||||||
const formKey = ref(0)
|
|
||||||
|
|
||||||
// 监听语言切换,重置表单
|
|
||||||
watch(locale, () => {
|
|
||||||
formKey.value++
|
|
||||||
})
|
|
||||||
|
|
||||||
const formData = reactive<RegisterForm>({
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
confirmPassword: '',
|
|
||||||
agreement: false
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 验证密码
|
|
||||||
* 当密码输入后,如果确认密码已填写,则触发确认密码的验证
|
|
||||||
*/
|
|
||||||
const validatePassword = (_rule: any, value: string, callback: (error?: Error) => void) => {
|
|
||||||
if (!value) {
|
|
||||||
callback(new Error(t('register.placeholder.password')))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formData.confirmPassword) {
|
|
||||||
formRef.value?.validateField('confirmPassword')
|
|
||||||
}
|
|
||||||
|
|
||||||
callback()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
callback()
|
||||||
* 验证确认密码
|
}
|
||||||
* 检查确认密码是否与密码一致
|
|
||||||
*/
|
|
||||||
const validateConfirmPassword = (
|
|
||||||
_rule: any,
|
|
||||||
value: string,
|
|
||||||
callback: (error?: Error) => void
|
|
||||||
) => {
|
|
||||||
if (!value) {
|
|
||||||
callback(new Error(t('register.rule.confirmPasswordRequired')))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value !== formData.password) {
|
/**
|
||||||
callback(new Error(t('register.rule.passwordMismatch')))
|
* 验证确认密码
|
||||||
return
|
* 检查确认密码是否与密码一致
|
||||||
}
|
*/
|
||||||
|
const validateConfirmPassword = (
|
||||||
callback()
|
_rule,
|
||||||
|
value,
|
||||||
|
callback
|
||||||
|
) => {
|
||||||
|
if (!value) {
|
||||||
|
callback(new Error(t('register.rule.confirmPasswordRequired')))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (value !== formData.password) {
|
||||||
* 验证用户协议
|
callback(new Error(t('register.rule.passwordMismatch')))
|
||||||
* 确保用户已勾选同意协议
|
return
|
||||||
*/
|
|
||||||
const validateAgreement = (_rule: any, value: boolean, callback: (error?: Error) => void) => {
|
|
||||||
if (!value) {
|
|
||||||
callback(new Error(t('register.rule.agreementRequired')))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
callback()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rules = computed<FormRules<RegisterForm>>(() => ({
|
callback()
|
||||||
username: [
|
}
|
||||||
{ required: true, message: t('register.placeholder.username'), trigger: 'blur' },
|
|
||||||
{
|
|
||||||
min: USERNAME_MIN_LENGTH,
|
|
||||||
max: USERNAME_MAX_LENGTH,
|
|
||||||
message: t('register.rule.usernameLength'),
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
password: [
|
|
||||||
{ required: true, validator: validatePassword, trigger: 'blur' },
|
|
||||||
{
|
|
||||||
min: PASSWORD_MIN_LENGTH,
|
|
||||||
message: t('register.rule.passwordLength'),
|
|
||||||
trigger: 'blur'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
confirmPassword: [{ required: true, validator: validateConfirmPassword, trigger: 'blur' }],
|
|
||||||
agreement: [{ validator: validateAgreement, trigger: 'change' }]
|
|
||||||
}))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册用户
|
* 验证用户协议
|
||||||
* 验证表单后提交注册请求
|
* 确保用户已勾选同意协议
|
||||||
*/
|
*/
|
||||||
const register = async () => {
|
const validateAgreement = (_rule, value, callback) => {
|
||||||
if (!formRef.value) return
|
if (!value) {
|
||||||
|
callback(new Error(t('register.rule.agreementRequired')))
|
||||||
try {
|
return
|
||||||
await formRef.value.validate()
|
|
||||||
loading.value = true
|
|
||||||
|
|
||||||
// TODO: 替换为真实 API 调用
|
|
||||||
// const params = {
|
|
||||||
// username: formData.username,
|
|
||||||
// password: formData.password
|
|
||||||
// }
|
|
||||||
// const res = await AuthService.register(params)
|
|
||||||
// if (res.code === ApiStatus.success) {
|
|
||||||
// ElMessage.success('注册成功')
|
|
||||||
// toLogin()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 模拟注册请求
|
|
||||||
setTimeout(() => {
|
|
||||||
loading.value = false
|
|
||||||
ElMessage.success('注册成功')
|
|
||||||
toLogin()
|
|
||||||
}, REDIRECT_DELAY)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('表单验证失败:', error)
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
const rules = computed(() => ({
|
||||||
* 跳转到登录页面
|
username: [
|
||||||
*/
|
{ required: true, message: t('register.placeholder.username'), trigger: 'blur' },
|
||||||
const toLogin = () => {
|
{
|
||||||
|
min: USERNAME_MIN_LENGTH,
|
||||||
|
max: USERNAME_MAX_LENGTH,
|
||||||
|
message: t('register.rule.usernameLength'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
{ required: true, validator: validatePassword, trigger: 'blur' },
|
||||||
|
{
|
||||||
|
min: PASSWORD_MIN_LENGTH,
|
||||||
|
message: t('register.rule.passwordLength'),
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
confirmPassword: [{ required: true, validator: validateConfirmPassword, trigger: 'blur' }],
|
||||||
|
agreement: [{ validator: validateAgreement, trigger: 'change' }]
|
||||||
|
}))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册用户
|
||||||
|
* 验证表单后提交注册请求
|
||||||
|
*/
|
||||||
|
const register = async () => {
|
||||||
|
if (!formRef.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await formRef.value.validate()
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
// TODO: 替换为真实 API 调用
|
||||||
|
// const params = {
|
||||||
|
// username: formData.username,
|
||||||
|
// password: formData.password
|
||||||
|
// }
|
||||||
|
// const res = await AuthService.register(params)
|
||||||
|
// if (res.code === ApiStatus.success) {
|
||||||
|
// ElMessage.success('注册成功')
|
||||||
|
// toLogin()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 模拟注册请求
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
router.push({ name: 'Login' })
|
loading.value = false
|
||||||
|
ElMessage.success('注册成功')
|
||||||
|
toLogin()
|
||||||
}, REDIRECT_DELAY)
|
}, REDIRECT_DELAY)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('表单验证失败:', error)
|
||||||
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转到登录页面
|
||||||
|
*/
|
||||||
|
const toLogin = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push({ name: 'Login' })
|
||||||
|
}, REDIRECT_DELAY)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import '../login/style.css';
|
@import '../login/style.css';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -28,14 +28,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import CardList from './modules/card-list.vue'
|
import CardList from './modules/card-list.vue'
|
||||||
import ActiveUser from './modules/active-user.vue'
|
import ActiveUser from './modules/active-user.vue'
|
||||||
import SalesOverview from './modules/sales-overview.vue'
|
import SalesOverview from './modules/sales-overview.vue'
|
||||||
import NewUser from './modules/new-user.vue'
|
import NewUser from './modules/new-user.vue'
|
||||||
import Dynamic from './modules/dynamic-stats.vue'
|
import Dynamic from './modules/dynamic-stats.vue'
|
||||||
import TodoList from './modules/todo-list.vue'
|
import TodoList from './modules/todo-list.vue'
|
||||||
import AboutProject from './modules/about-project.vue'
|
import AboutProject from './modules/about-project.vue'
|
||||||
|
|
||||||
defineOptions({ name: 'Console' })
|
defineOptions({ name: 'Console' })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import AppConfig from '@/config'
|
import AppConfig from '@/config'
|
||||||
import { WEB_LINKS } from '@/utils/constants'
|
import { WEB_LINKS } from '@/utils/constants'
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
* 在新标签页中打开指定 URL
|
* 在新标签页中打开指定 URL
|
||||||
* @param url 要打开的网页地址
|
* @param url 要打开的网页地址
|
||||||
*/
|
*/
|
||||||
const goPage = (url: string): void => {
|
const goPage = (url) => {
|
||||||
window.open(url, '_blank', 'noopener,noreferrer')
|
window.open(url, '_blank', 'noopener,noreferrer')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -24,11 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
interface UserStatItem {
|
|
||||||
name: string
|
|
||||||
num: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最近9个月
|
// 最近9个月
|
||||||
const xAxisLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月']
|
const xAxisLabels = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月']
|
||||||
@@ -40,7 +36,7 @@
|
|||||||
* 用户统计数据列表
|
* 用户统计数据列表
|
||||||
* 包含总用户量、总访问量、日访问量和周同比等关键指标
|
* 包含总用户量、总访问量、日访问量和周同比等关键指标
|
||||||
*/
|
*/
|
||||||
const list: UserStatItem[] = [
|
const list = [
|
||||||
{ name: '总用户量', num: '32k' },
|
{ name: '总用户量', num: '32k' },
|
||||||
{ name: '总访问量', num: '128k' },
|
{ name: '总访问量', num: '128k' },
|
||||||
{ name: '日访问量', num: '1.2k' },
|
{ name: '日访问量', num: '1.2k' },
|
||||||
|
|||||||
@@ -27,21 +27,13 @@
|
|||||||
</ElRow>
|
</ElRow>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
interface CardDataItem {
|
|
||||||
des: string
|
|
||||||
icon: string
|
|
||||||
startVal: number
|
|
||||||
duration: number
|
|
||||||
num: number
|
|
||||||
change: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 卡片统计数据列表
|
* 卡片统计数据列表
|
||||||
* 展示总访问次数、在线访客数、点击量和新用户等核心数据指标
|
* 展示总访问次数、在线访客数、点击量和新用户等核心数据指标
|
||||||
*/
|
*/
|
||||||
const dataList = reactive<CardDataItem[]>([
|
const dataList = reactive([
|
||||||
{
|
{
|
||||||
des: '总访问次数',
|
des: '总访问次数',
|
||||||
icon: 'ri:pie-chart-line',
|
icon: 'ri:pie-chart-line',
|
||||||
|
|||||||
@@ -23,18 +23,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
interface DynamicItem {
|
|
||||||
username: string
|
|
||||||
type: string
|
|
||||||
target: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户动态列表
|
* 用户动态列表
|
||||||
* 记录用户的关注、发文、提问、兑换等各类活动
|
* 记录用户的关注、发文、提问、兑换等各类活动
|
||||||
*/
|
*/
|
||||||
const list = reactive<DynamicItem[]>([
|
const list = reactive([
|
||||||
{
|
{
|
||||||
username: '中小鱼',
|
username: '中小鱼',
|
||||||
type: '关注了',
|
type: '关注了',
|
||||||
|
|||||||
@@ -54,7 +54,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import avatar1 from '@/assets/images/avatar/avatar1.webp'
|
import avatar1 from '@/assets/images/avatar/avatar1.webp'
|
||||||
import avatar2 from '@/assets/images/avatar/avatar2.webp'
|
import avatar2 from '@/assets/images/avatar/avatar2.webp'
|
||||||
import avatar3 from '@/assets/images/avatar/avatar3.webp'
|
import avatar3 from '@/assets/images/avatar/avatar3.webp'
|
||||||
@@ -62,16 +62,6 @@
|
|||||||
import avatar5 from '@/assets/images/avatar/avatar5.webp'
|
import avatar5 from '@/assets/images/avatar/avatar5.webp'
|
||||||
import avatar6 from '@/assets/images/avatar/avatar6.webp'
|
import avatar6 from '@/assets/images/avatar/avatar6.webp'
|
||||||
|
|
||||||
interface UserTableItem {
|
|
||||||
username: string
|
|
||||||
province: string
|
|
||||||
sex: 0 | 1
|
|
||||||
age: number
|
|
||||||
percentage: number
|
|
||||||
pro: number
|
|
||||||
color: string
|
|
||||||
avatar: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const ANIMATION_DELAY = 100
|
const ANIMATION_DELAY = 100
|
||||||
|
|
||||||
@@ -81,7 +71,7 @@
|
|||||||
* 新用户表格数据
|
* 新用户表格数据
|
||||||
* 包含用户基本信息和完成进度
|
* 包含用户基本信息和完成进度
|
||||||
*/
|
*/
|
||||||
const tableData = reactive<UserTableItem[]>([
|
const tableData = reactive([
|
||||||
{
|
{
|
||||||
username: '中小鱼',
|
username: '中小鱼',
|
||||||
province: '北京',
|
province: '北京',
|
||||||
@@ -148,7 +138,7 @@
|
|||||||
* 添加进度条动画效果
|
* 添加进度条动画效果
|
||||||
* 延迟后将进度值从 0 更新到目标百分比,触发动画
|
* 延迟后将进度值从 0 更新到目标百分比,触发动画
|
||||||
*/
|
*/
|
||||||
const addAnimation = (): void => {
|
const addAnimation = () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tableData.forEach((item) => {
|
tableData.forEach((item) => {
|
||||||
item.pro = item.percentage
|
item.pro = item.percentage
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
/**
|
/**
|
||||||
* 全年访问量数据
|
* 全年访问量数据
|
||||||
* 记录每月的访问量统计
|
* 记录每月的访问量统计
|
||||||
|
|||||||
@@ -25,18 +25,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
interface TodoItem {
|
|
||||||
username: string
|
|
||||||
date: string
|
|
||||||
complate: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待办事项列表
|
* 待办事项列表
|
||||||
* 记录每日工作任务及完成状态
|
* 记录每日工作任务及完成状态
|
||||||
*/
|
*/
|
||||||
const list = reactive<TodoItem[]>([
|
const list = reactive([
|
||||||
{
|
{
|
||||||
username: '查看今天工作内容',
|
username: '查看今天工作内容',
|
||||||
date: '上午 09:30',
|
date: '上午 09:30',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import imgUrl from '@imgs/svg/403.svg'
|
import imgUrl from '@imgs/svg/403.svg'
|
||||||
defineOptions({ name: 'Exception403' })
|
defineOptions({ name: 'Exception403' })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import imgUrl from '@imgs/svg/404.svg'
|
import imgUrl from '@imgs/svg/404.svg'
|
||||||
defineOptions({ name: 'Exception404' })
|
defineOptions({ name: 'Exception404' })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import imgUrl from '@imgs/svg/500.svg'
|
import imgUrl from '@imgs/svg/500.svg'
|
||||||
defineOptions({ name: 'Exception500' })
|
defineOptions({ name: 'Exception500' })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -20,10 +20,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
defineOptions({ name: 'AppLayout' })
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use './style';
|
@use './style';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
import { IframeRouteManager } from '@/router/core'
|
import { IframeRouteManager } from '@/router/core'
|
||||||
|
|
||||||
defineOptions({ name: 'IframeView' })
|
defineOptions({ name: 'IframeView' })
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
const iframeUrl = ref('')
|
const iframeUrl = ref('')
|
||||||
const iframeRef = ref<HTMLIFrameElement | null>(null)
|
const iframeRef = ref(null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化 iframe URL
|
* 初始化 iframe URL
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
* 处理 iframe 加载完成事件
|
* 处理 iframe 加载完成事件
|
||||||
* 隐藏加载状态
|
* 隐藏加载状态
|
||||||
*/
|
*/
|
||||||
const handleIframeLoad = (): void => {
|
const handleIframeLoad = () => {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -23,6 +23,6 @@
|
|||||||
</ArtResultPage>
|
</ArtResultPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
defineOptions({ name: 'ResultFail' })
|
defineOptions({ name: 'ResultFail' })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -16,6 +16,6 @@
|
|||||||
</ArtResultPage>
|
</ArtResultPage>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup>
|
||||||
defineOptions({ name: 'ResultSuccess' })
|
defineOptions({ name: 'ResultSuccess' })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user