From 452672dedc1a49897485ecafe01413c8d3472a3f Mon Sep 17 00:00:00 2001 From: KirisameVanilla <118162831+KirisameVanilla@users.noreply.github.com> Date: Tue, 9 Sep 2025 14:45:57 +0800 Subject: [PATCH] feat: login page --- src/App.vue | 2 + src/api/auth.ts | 7 +- src/api/client.ts | 2 +- src/components/Modal.vue | 161 ++++++++++++++++++++++++++++++++++++ src/composables/useModal.ts | 92 +++++++++++++++++++++ src/router/index.ts | 1 + src/views/LoginPage.vue | 64 ++++++++++---- src/views/ModalTestPage.vue | 47 +++++++++++ 8 files changed, 357 insertions(+), 19 deletions(-) create mode 100644 src/components/Modal.vue create mode 100644 src/composables/useModal.ts create mode 100644 src/views/ModalTestPage.vue diff --git a/src/App.vue b/src/App.vue index 00bbf1c..be5eb2d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,8 +1,10 @@ + diff --git a/src/composables/useModal.ts b/src/composables/useModal.ts new file mode 100644 index 0000000..c6fef2e --- /dev/null +++ b/src/composables/useModal.ts @@ -0,0 +1,92 @@ +import { reactive } from 'vue' + +interface ModalConfig { + title?: string + message: string + type?: 'success' | 'error' | 'warning' | 'info' + duration?: number + showCloseButton?: boolean +} + +const modalState = reactive({ + isVisible: false, + title: '', + message: '', + type: 'info' as 'success' | 'error' | 'warning' | 'info', + showCloseButton: true +}) + +let closeTimer: number | null = null + +export const useModal = () => { + const showModal = (config: ModalConfig) => { + // 清除之前的定时器 + if (closeTimer) { + clearTimeout(closeTimer) + closeTimer = null + } + + modalState.isVisible = true + modalState.title = config.title || '' + modalState.message = config.message + modalState.type = config.type || 'info' + modalState.showCloseButton = config.showCloseButton !== false + + // 如果设置了持续时间,自动关闭 + if (config.duration && config.duration > 0) { + closeTimer = setTimeout(() => { + closeModal() + }, config.duration) + } + } + + const closeModal = () => { + modalState.isVisible = false + if (closeTimer) { + clearTimeout(closeTimer) + closeTimer = null + } + } + + const showSuccess = (message: string, duration?: number) => { + showModal({ + type: 'success', + message, + duration + }) + } + + const showError = (message: string, duration?: number) => { + showModal({ + type: 'error', + message, + duration + }) + } + + const showWarning = (message: string, duration?: number) => { + showModal({ + type: 'warning', + message, + duration + }) + } + + const showInfo = (message: string, duration?: number) => { + showModal({ + type: 'info', + message, + duration + }) + } + + return { + modalState, + showModal, + closeModal, + showSuccess, + showError, + showWarning, + showInfo + } +} diff --git a/src/router/index.ts b/src/router/index.ts index b0f4d11..2bd8dad 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -8,6 +8,7 @@ const router = createRouter({ { path: '/trans', name: 'Translation', component: () => import('../views/TranslationPage.vue') }, { path: '/write', name: 'Writing', component: () => import('../views/WritingPage.vue') }, { path: '/login', name: 'Login', component: () => import('../views/LoginPage.vue') }, + { path: '/modal-test', name: 'ModalTest', component: () => import('../views/ModalTestPage.vue') }, ], }) diff --git a/src/views/LoginPage.vue b/src/views/LoginPage.vue index 8ee6a5e..74551f5 100644 --- a/src/views/LoginPage.vue +++ b/src/views/LoginPage.vue @@ -107,9 +107,11 @@ import { ref } from 'vue' import { useRouter } from 'vue-router' import { useAuth } from '../composables/useAuth' +import { useModal } from '../composables/useModal' const router = useRouter() const { login, register } = useAuth() +const { showSuccess, showError } = useModal() const isLogin = ref(true) @@ -121,7 +123,8 @@ const loginForm = ref({ const registerForm = ref({ username: '', password: '', - lang_pref: 'fr' as 'jp' | 'fr' | 'private' + lang_pref: 'fr' as 'jp' | 'fr' | 'private', + portrait: '' }) const confirmPassword = ref('') @@ -159,11 +162,25 @@ const handleLogin = async () => { error.value = '' try { - await login(loginForm.value) - router.push('/dict') + const response = await login(loginForm.value) + + // 检查登录是否成功 + if (response && response.access_token) { + // 显示成功modal + showSuccess('登录成功!正在跳转...', 2000) + + // 2秒后跳转到字典页面 + setTimeout(() => { + router.push('/dict') + }, 2000) + } else { + showError('登录失败,请检查用户名和密码') + } } catch (err: any) { console.error('Login error:', err) - error.value = err.response?.data?.detail || '登录失败,请检查用户名和密码' + const errorMessage = err.response?.data?.detail || err.response?.data?.message || '登录失败,请检查用户名和密码' + showError(errorMessage) + error.value = errorMessage } finally { loading.value = false } @@ -192,24 +209,37 @@ const handleRegister = async () => { try { const username = registerForm.value.username // 保存用户名 - await register(registerForm.value) - successMessage.value = '注册成功!请登录' + const response = await register(registerForm.value) - // 将用户名填入登录表单 - loginForm.value.name = username - - // 切换到登录模式并清空注册表单 - isLogin.value = true - registerForm.value = { - username: '', - password: '', - lang_pref: 'fr' + // 检查注册是否成功 + if (response && response.id) { + // 显示成功modal + showSuccess('注册成功!2秒后将切换到登录页面', 2000) + + // 将用户名填入登录表单 + loginForm.value.name = username + + // 2秒后切换到登录模式并清空注册表单 + setTimeout(() => { + isLogin.value = true + registerForm.value = { + username: '', + password: '', + lang_pref: 'fr', + portrait: '' + } + confirmPassword.value = '' + }, 2000) + } else { + // 注册失败 + showError('注册失败,请检查返回信息') } - confirmPassword.value = '' } catch (err: any) { console.error('Register error:', err) - error.value = err.response?.data?.detail || '注册失败,请稍后重试' + const errorMessage = err.response?.data?.detail || err.response?.data?.message || '注册失败,请稍后重试' + showError(errorMessage) + error.value = errorMessage } finally { loading.value = false } diff --git a/src/views/ModalTestPage.vue b/src/views/ModalTestPage.vue new file mode 100644 index 0000000..9aa8c53 --- /dev/null +++ b/src/views/ModalTestPage.vue @@ -0,0 +1,47 @@ + + + Modal 测试页面 + + + + 测试成功Modal + + + + 测试错误Modal + + + + 测试警告Modal + + + + 测试信息Modal + + + + + + 回到登录页面 + + + + + +