feat(路由): 添加登录和注册页面路由
在路由配置中添加了登录和注册页面的路由,并删除了旧的AuthContext.jsx文件。同时,新增了LoginPage.vue和RegisterPage.vue页面组件,用于处理用户登录和注册功能。
Showing
4 changed files
with
393 additions
and
69 deletions
src/contexts/AuthContext.jsx
deleted
100644 → 0
| 1 | -import React, { createContext, useContext, useState, useEffect } from 'react'; | ||
| 2 | - | ||
| 3 | -/** | ||
| 4 | - * Authentication Context for user login/logout functionality | ||
| 5 | - */ | ||
| 6 | -const AuthContext = createContext(); | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * AuthProvider component to manage authentication state | ||
| 10 | - * | ||
| 11 | - * @param {Object} props - Component props | ||
| 12 | - * @param {ReactNode} props.children - Child elements | ||
| 13 | - * @returns {JSX.Element} AuthProvider component | ||
| 14 | - */ | ||
| 15 | -export const AuthProvider = ({ children }) => { | ||
| 16 | - const [currentUser, setCurrentUser] = useState(null); | ||
| 17 | - const [loading, setLoading] = useState(true); | ||
| 18 | - | ||
| 19 | - // Check for saved user on mount | ||
| 20 | - useEffect(() => { | ||
| 21 | - const savedUser = localStorage.getItem('currentUser'); | ||
| 22 | - if (savedUser) { | ||
| 23 | - setCurrentUser(JSON.parse(savedUser)); | ||
| 24 | - } | ||
| 25 | - setLoading(false); | ||
| 26 | - }, []); | ||
| 27 | - | ||
| 28 | - // Login function | ||
| 29 | - const login = (userData) => { | ||
| 30 | - setCurrentUser(userData); | ||
| 31 | - localStorage.setItem('currentUser', JSON.stringify(userData)); | ||
| 32 | - return true; | ||
| 33 | - }; | ||
| 34 | - | ||
| 35 | - // Logout function | ||
| 36 | - const logout = () => { | ||
| 37 | - setCurrentUser(null); | ||
| 38 | - localStorage.removeItem('currentUser'); | ||
| 39 | - }; | ||
| 40 | - | ||
| 41 | - // Context value | ||
| 42 | - const value = { | ||
| 43 | - currentUser, | ||
| 44 | - loading, | ||
| 45 | - login, | ||
| 46 | - logout | ||
| 47 | - }; | ||
| 48 | - | ||
| 49 | - return ( | ||
| 50 | - <AuthContext.Provider value={value}> | ||
| 51 | - {!loading && children} | ||
| 52 | - </AuthContext.Provider> | ||
| 53 | - ); | ||
| 54 | -}; | ||
| 55 | - | ||
| 56 | -/** | ||
| 57 | - * Hook to use auth functionality throughout the app | ||
| 58 | - * @returns {Object} Auth context value | ||
| 59 | - */ | ||
| 60 | -export const useAuth = () => { | ||
| 61 | - const context = useContext(AuthContext); | ||
| 62 | - if (!context) { | ||
| 63 | - throw new Error("useAuth must be used within an AuthProvider"); | ||
| 64 | - } | ||
| 65 | - return context; | ||
| 66 | -}; | ||
| 67 | - | ||
| 68 | -export default AuthContext; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-03-20 20:36:36 | 2 | * @Date: 2025-03-20 20:36:36 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-20 21:04:41 | 4 | + * @LastEditTime: 2025-03-20 21:16:30 |
| 5 | * @FilePath: /mlaj/src/router/index.js | 5 | * @FilePath: /mlaj/src/router/index.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -32,6 +32,18 @@ const routes = [ | ... | @@ -32,6 +32,18 @@ const routes = [ |
| 32 | component: () => import('../views/profile/ProfilePage.vue'), | 32 | component: () => import('../views/profile/ProfilePage.vue'), |
| 33 | meta: { title: 'Profile' }, | 33 | meta: { title: 'Profile' }, |
| 34 | }, | 34 | }, |
| 35 | + { | ||
| 36 | + path: '/login', | ||
| 37 | + name: 'Login', | ||
| 38 | + component: () => import('../views/auth/LoginPage.vue'), | ||
| 39 | + meta: { title: 'Login' } | ||
| 40 | + }, | ||
| 41 | + { | ||
| 42 | + path: '/register', | ||
| 43 | + name: 'Register', | ||
| 44 | + component: () => import('../views/auth/RegisterPage.vue'), | ||
| 45 | + meta: { title: 'Register' } | ||
| 46 | + }, | ||
| 35 | ] | 47 | ] |
| 36 | 48 | ||
| 37 | const router = createRouter({ | 49 | const router = createRouter({ | ... | ... |
src/views/auth/LoginPage.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="min-h-screen flex flex-col bg-gradient-to-br from-green-50 via-teal-50 to-blue-50 py-12 px-4 sm:px-6 lg:px-8"> | ||
| 3 | + <div class="sm:mx-auto sm:w-full sm:max-w-md"> | ||
| 4 | + <h1 class="text-center text-3xl font-bold text-gray-800 mb-2">亲子学院</h1> | ||
| 5 | + <h2 class="text-center text-xl font-medium text-gray-600">欢迎回来</h2> | ||
| 6 | + </div> | ||
| 7 | + | ||
| 8 | + <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> | ||
| 9 | + <FrostedGlass class="py-8 px-6 rounded-lg"> | ||
| 10 | + <div v-if="error" class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-md"> | ||
| 11 | + {{ error }} | ||
| 12 | + </div> | ||
| 13 | + | ||
| 14 | + <form class="space-y-6" @submit.prevent="handleSubmit"> | ||
| 15 | + <div> | ||
| 16 | + <label for="email" class="block text-sm font-medium text-gray-700"> | ||
| 17 | + 邮箱 / 手机号 | ||
| 18 | + </label> | ||
| 19 | + <input | ||
| 20 | + id="email" | ||
| 21 | + v-model="email" | ||
| 22 | + name="email" | ||
| 23 | + type="text" | ||
| 24 | + autocomplete="email" | ||
| 25 | + required | ||
| 26 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 27 | + /> | ||
| 28 | + </div> | ||
| 29 | + | ||
| 30 | + <div> | ||
| 31 | + <label for="password" class="block text-sm font-medium text-gray-700"> | ||
| 32 | + 密码 | ||
| 33 | + </label> | ||
| 34 | + <input | ||
| 35 | + id="password" | ||
| 36 | + v-model="password" | ||
| 37 | + name="password" | ||
| 38 | + type="password" | ||
| 39 | + autocomplete="current-password" | ||
| 40 | + required | ||
| 41 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 42 | + /> | ||
| 43 | + </div> | ||
| 44 | + | ||
| 45 | + <div class="flex items-center justify-between"> | ||
| 46 | + <div class="flex items-center"> | ||
| 47 | + <input | ||
| 48 | + id="remember-me" | ||
| 49 | + name="remember-me" | ||
| 50 | + type="checkbox" | ||
| 51 | + class="h-4 w-4 text-green-600 focus:ring-green-500 border-gray-300 rounded" | ||
| 52 | + /> | ||
| 53 | + <label for="remember-me" class="ml-2 block text-sm text-gray-700"> | ||
| 54 | + 记住我 | ||
| 55 | + </label> | ||
| 56 | + </div> | ||
| 57 | + | ||
| 58 | + <div class="text-sm"> | ||
| 59 | + <a href="#" class="font-medium text-green-600 hover:text-green-500"> | ||
| 60 | + 忘记密码? | ||
| 61 | + </a> | ||
| 62 | + </div> | ||
| 63 | + </div> | ||
| 64 | + | ||
| 65 | + <div> | ||
| 66 | + <button | ||
| 67 | + type="submit" | ||
| 68 | + :disabled="loading" | ||
| 69 | + class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500" | ||
| 70 | + :class="{ 'opacity-70 cursor-not-allowed': loading }" | ||
| 71 | + > | ||
| 72 | + {{ loading ? '登录中...' : '登 录' }} | ||
| 73 | + </button> | ||
| 74 | + </div> | ||
| 75 | + </form> | ||
| 76 | + | ||
| 77 | + <div class="mt-6"> | ||
| 78 | + <div class="relative"> | ||
| 79 | + <div class="absolute inset-0 flex items-center"> | ||
| 80 | + <div class="w-full border-t border-gray-300"></div> | ||
| 81 | + </div> | ||
| 82 | + <div class="relative flex justify-center text-sm"> | ||
| 83 | + <span class="px-2 bg-white/30 backdrop-blur-sm text-gray-500"> | ||
| 84 | + 或者 | ||
| 85 | + </span> | ||
| 86 | + </div> | ||
| 87 | + </div> | ||
| 88 | + | ||
| 89 | + <div class="mt-6 grid grid-cols-2 gap-3"> | ||
| 90 | + <button | ||
| 91 | + type="button" | ||
| 92 | + class="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-700 hover:bg-gray-50" | ||
| 93 | + > | ||
| 94 | + <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24"> | ||
| 95 | + <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-2.917 16.083c-2.258 0-4.083-1.825-4.083-4.083s1.825-4.083 4.083-4.083c1.103 0 2.024.402 2.735 1.067l-1.107 1.068c-.304-.292-.834-.63-1.628-.63-1.394 0-2.531 1.155-2.531 2.579 0 1.424 1.138 2.579 2.531 2.579 1.616 0 2.224-1.162 2.316-1.762h-2.316v-1.4h3.855c.036.204.064.408.064.677.001 2.332-1.563 3.988-3.919 3.988zm9.917-3.5h-1.75v1.75h-1.167v-1.75h-1.75v-1.166h1.75v-1.75h1.167v1.75h1.75v1.166z"></path> | ||
| 96 | + </svg> | ||
| 97 | |||
| 98 | + </button> | ||
| 99 | + <button | ||
| 100 | + type="button" | ||
| 101 | + class="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-700 hover:bg-gray-50" | ||
| 102 | + > | ||
| 103 | + <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24"> | ||
| 104 | + <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-2.917 16.083c-2.258 0-4.083-1.825-4.083-4.083s1.825-4.083 4.083-4.083c1.103 0 2.024.402 2.735 1.067l-1.107 1.068c-.304-.292-.834-.63-1.628-.63-1.394 0-2.531 1.155-2.531 2.579 0 1.424 1.138 2.579 2.531 2.579 1.616 0 2.224-1.162 2.316-1.762h-2.316v-1.4h3.855c.036.204.064.408.064.677.001 2.332-1.563 3.988-3.919 3.988zm9.917-3.5h-1.75v1.75h-1.167v-1.75h-1.75v-1.166h1.75v-1.75h1.167v1.75h1.75v1.166z"></path> | ||
| 105 | + </svg> | ||
| 106 | + 微信 | ||
| 107 | + </button> | ||
| 108 | + </div> | ||
| 109 | + </div> | ||
| 110 | + | ||
| 111 | + <div class="text-center mt-6"> | ||
| 112 | + <p class="text-sm text-gray-600"> | ||
| 113 | + 还没有账号? | ||
| 114 | + <router-link to="/register" class="font-medium text-green-600 hover:text-green-500"> | ||
| 115 | + 立即注册 | ||
| 116 | + </router-link> | ||
| 117 | + </p> | ||
| 118 | + </div> | ||
| 119 | + </FrostedGlass> | ||
| 120 | + </div> | ||
| 121 | + </div> | ||
| 122 | +</template> | ||
| 123 | + | ||
| 124 | +<script setup> | ||
| 125 | +import { ref } from 'vue' | ||
| 126 | +import { useRouter } from 'vue-router' | ||
| 127 | +import FrostedGlass from '@/components/ui/FrostedGlass.vue' | ||
| 128 | +import { useAuth } from '@/contexts/auth' | ||
| 129 | + | ||
| 130 | +const router = useRouter() | ||
| 131 | +const { login } = useAuth() | ||
| 132 | + | ||
| 133 | +const email = ref('') | ||
| 134 | +const password = ref('') | ||
| 135 | +const error = ref('') | ||
| 136 | +const loading = ref(false) | ||
| 137 | + | ||
| 138 | +const handleSubmit = async () => { | ||
| 139 | + if (!email.value || !password.value) { | ||
| 140 | + error.value = '请填写所有字段' | ||
| 141 | + return | ||
| 142 | + } | ||
| 143 | + | ||
| 144 | + try { | ||
| 145 | + error.value = '' | ||
| 146 | + loading.value = true | ||
| 147 | + | ||
| 148 | + // 使用auth.js中的login函数 | ||
| 149 | + const success = login({ | ||
| 150 | + email: email.value, | ||
| 151 | + name: '李玉红', | ||
| 152 | + avatar: 'https://cdn.ipadbiz.cn/mlaj/images/user-avatar-1.jpg' | ||
| 153 | + }) | ||
| 154 | + | ||
| 155 | + if (success) { | ||
| 156 | + router.push('/') | ||
| 157 | + } else { | ||
| 158 | + error.value = '登录失败,请检查您的凭据' | ||
| 159 | + } | ||
| 160 | + } catch (err) { | ||
| 161 | + console.error('Login error:', err) | ||
| 162 | + error.value = '登录时发生错误' | ||
| 163 | + } finally { | ||
| 164 | + loading.value = false | ||
| 165 | + } | ||
| 166 | +} | ||
| 167 | +</script> |
src/views/auth/RegisterPage.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="min-h-screen flex flex-col bg-gradient-to-br from-green-50 via-teal-50 to-blue-50 py-12 px-4 sm:px-6 lg:px-8"> | ||
| 3 | + <div class="sm:mx-auto sm:w-full sm:max-w-md"> | ||
| 4 | + <h1 class="text-center text-3xl font-bold text-gray-800 mb-2">亲子学院</h1> | ||
| 5 | + <h2 class="text-center text-xl font-medium text-gray-600">创建账号</h2> | ||
| 6 | + </div> | ||
| 7 | + | ||
| 8 | + <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> | ||
| 9 | + <FrostedGlass class="py-8 px-6 rounded-lg"> | ||
| 10 | + <div v-if="error" class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-md"> | ||
| 11 | + {{ error }} | ||
| 12 | + </div> | ||
| 13 | + | ||
| 14 | + <form class="space-y-6" @submit.prevent="handleSubmit"> | ||
| 15 | + <div> | ||
| 16 | + <label for="name" class="block text-sm font-medium text-gray-700"> | ||
| 17 | + 姓名 <span class="text-red-500">*</span> | ||
| 18 | + </label> | ||
| 19 | + <input | ||
| 20 | + id="name" | ||
| 21 | + v-model="formData.name" | ||
| 22 | + type="text" | ||
| 23 | + required | ||
| 24 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 25 | + /> | ||
| 26 | + </div> | ||
| 27 | + | ||
| 28 | + <div> | ||
| 29 | + <label for="email" class="block text-sm font-medium text-gray-700"> | ||
| 30 | + 邮箱 <span class="text-red-500">*</span> | ||
| 31 | + </label> | ||
| 32 | + <input | ||
| 33 | + id="email" | ||
| 34 | + v-model="formData.email" | ||
| 35 | + type="email" | ||
| 36 | + autocomplete="email" | ||
| 37 | + required | ||
| 38 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 39 | + /> | ||
| 40 | + </div> | ||
| 41 | + | ||
| 42 | + <div> | ||
| 43 | + <label for="phone" class="block text-sm font-medium text-gray-700"> | ||
| 44 | + 手机号 | ||
| 45 | + </label> | ||
| 46 | + <input | ||
| 47 | + id="phone" | ||
| 48 | + v-model="formData.phone" | ||
| 49 | + type="tel" | ||
| 50 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 51 | + /> | ||
| 52 | + </div> | ||
| 53 | + | ||
| 54 | + <div> | ||
| 55 | + <label for="password" class="block text-sm font-medium text-gray-700"> | ||
| 56 | + 密码 <span class="text-red-500">*</span> | ||
| 57 | + </label> | ||
| 58 | + <input | ||
| 59 | + id="password" | ||
| 60 | + v-model="formData.password" | ||
| 61 | + type="password" | ||
| 62 | + autocomplete="new-password" | ||
| 63 | + required | ||
| 64 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 65 | + /> | ||
| 66 | + </div> | ||
| 67 | + | ||
| 68 | + <div> | ||
| 69 | + <label for="confirmPassword" class="block text-sm font-medium text-gray-700"> | ||
| 70 | + 确认密码 <span class="text-red-500">*</span> | ||
| 71 | + </label> | ||
| 72 | + <input | ||
| 73 | + id="confirmPassword" | ||
| 74 | + v-model="formData.confirmPassword" | ||
| 75 | + type="password" | ||
| 76 | + autocomplete="new-password" | ||
| 77 | + required | ||
| 78 | + class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500" | ||
| 79 | + /> | ||
| 80 | + </div> | ||
| 81 | + | ||
| 82 | + <div class="flex items-center"> | ||
| 83 | + <input | ||
| 84 | + id="agreeTerms" | ||
| 85 | + v-model="formData.agreeTerms" | ||
| 86 | + type="checkbox" | ||
| 87 | + class="h-4 w-4 text-green-600 focus:ring-green-500 border-gray-300 rounded" | ||
| 88 | + /> | ||
| 89 | + <label for="agreeTerms" class="ml-2 block text-sm text-gray-700"> | ||
| 90 | + 我已阅读并同意 <a href="#" class="text-green-600 hover:text-green-500">用户协议</a> 和 <a href="#" class="text-green-600 hover:text-green-500">隐私政策</a> | ||
| 91 | + </label> | ||
| 92 | + </div> | ||
| 93 | + | ||
| 94 | + <div> | ||
| 95 | + <button | ||
| 96 | + type="submit" | ||
| 97 | + :disabled="loading" | ||
| 98 | + class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500" | ||
| 99 | + :class="{ 'opacity-70 cursor-not-allowed': loading }" | ||
| 100 | + > | ||
| 101 | + {{ loading ? '注册中...' : '立即注册' }} | ||
| 102 | + </button> | ||
| 103 | + </div> | ||
| 104 | + </form> | ||
| 105 | + | ||
| 106 | + <div class="mt-6"> | ||
| 107 | + <div class="relative"> | ||
| 108 | + <div class="absolute inset-0 flex items-center"> | ||
| 109 | + <div class="w-full border-t border-gray-300"></div> | ||
| 110 | + </div> | ||
| 111 | + <div class="relative flex justify-center text-sm"> | ||
| 112 | + <span class="px-2 bg-white/30 backdrop-blur-sm text-gray-500"> | ||
| 113 | + 或者 | ||
| 114 | + </span> | ||
| 115 | + </div> | ||
| 116 | + </div> | ||
| 117 | + | ||
| 118 | + <div class="mt-6 grid grid-cols-2 gap-3"> | ||
| 119 | + <button | ||
| 120 | + type="button" | ||
| 121 | + class="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-700 hover:bg-gray-50" | ||
| 122 | + > | ||
| 123 | + <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24"> | ||
| 124 | + <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-2.917 16.083c-2.258 0-4.083-1.825-4.083-4.083s1.825-4.083 4.083-4.083c1.103 0 2.024.402 2.735 1.067l-1.107 1.068c-.304-.292-.834-.63-1.628-.63-1.394 0-2.531 1.155-2.531 2.579 0 1.424 1.138 2.579 2.531 2.579 1.616 0 2.224-1.162 2.316-1.762h-2.316v-1.4h3.855c.036.204.064.408.064.677.001 2.332-1.563 3.988-3.919 3.988zm9.917-3.5h-1.75v1.75h-1.167v-1.75h-1.75v-1.166h1.75v-1.75h1.167v1.75h1.75v1.166z"></path> | ||
| 125 | + </svg> | ||
| 126 | |||
| 127 | + </button> | ||
| 128 | + <button | ||
| 129 | + type="button" | ||
| 130 | + class="w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm bg-white text-sm font-medium text-gray-700 hover:bg-gray-50" | ||
| 131 | + > | ||
| 132 | + <svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24"> | ||
| 133 | + <path d="M12 0c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm-2.917 16.083c-2.258 0-4.083-1.825-4.083-4.083s1.825-4.083 4.083-4.083c1.103 0 2.024.402 2.735 1.067l-1.107 1.068c-.304-.292-.834-.63-1.628-.63-1.394 0-2.531 1.155-2.531 2.579 0 1.424 1.138 2.579 2.531 2.579 1.616 0 2.224-1.162 2.316-1.762h-2.316v-1.4h3.855c.036.204.064.408.064.677.001 2.332-1.563 3.988-3.919 3.988zm9.917-3.5h-1.75v1.75h-1.167v-1.75h-1.75v-1.166h1.75v-1.75h1.167v1.75h1.75v1.166z"></path> | ||
| 134 | + </svg> | ||
| 135 | + 微信 | ||
| 136 | + </button> | ||
| 137 | + </div> | ||
| 138 | + </div> | ||
| 139 | + | ||
| 140 | + <div class="text-center mt-6"> | ||
| 141 | + <p class="text-sm text-gray-600"> | ||
| 142 | + 已有账号? | ||
| 143 | + <router-link to="/login" class="font-medium text-green-600 hover:text-green-500"> | ||
| 144 | + 登录 | ||
| 145 | + </router-link> | ||
| 146 | + </p> | ||
| 147 | + </div> | ||
| 148 | + </FrostedGlass> | ||
| 149 | + </div> | ||
| 150 | + </div> | ||
| 151 | +</template> | ||
| 152 | + | ||
| 153 | +<script setup> | ||
| 154 | +import { ref, reactive } from 'vue' | ||
| 155 | +import { useRouter } from 'vue-router' | ||
| 156 | +import FrostedGlass from '@/components/ui/FrostedGlass.vue' | ||
| 157 | +import { useAuth } from '@/contexts/auth' | ||
| 158 | + | ||
| 159 | +const router = useRouter() | ||
| 160 | +const { login } = useAuth() | ||
| 161 | + | ||
| 162 | +const formData = reactive({ | ||
| 163 | + name: '', | ||
| 164 | + email: '', | ||
| 165 | + phone: '', | ||
| 166 | + password: '', | ||
| 167 | + confirmPassword: '', | ||
| 168 | + agreeTerms: false | ||
| 169 | +}) | ||
| 170 | + | ||
| 171 | +const error = ref('') | ||
| 172 | +const loading = ref(false) | ||
| 173 | + | ||
| 174 | +const handleSubmit = async () => { | ||
| 175 | + if (!formData.name || !formData.email || !formData.password) { | ||
| 176 | + error.value = '请填写所有必填字段' | ||
| 177 | + return | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + if (formData.password !== formData.confirmPassword) { | ||
| 181 | + error.value = '两次输入的密码不一致' | ||
| 182 | + return | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + if (!formData.agreeTerms) { | ||
| 186 | + error.value = '请阅读并同意用户协议' | ||
| 187 | + return | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + try { | ||
| 191 | + error.value = '' | ||
| 192 | + loading.value = true | ||
| 193 | + | ||
| 194 | + // 使用auth.js中的login函数 | ||
| 195 | + const success = login({ | ||
| 196 | + email: formData.email, | ||
| 197 | + name: formData.name, | ||
| 198 | + avatar: 'https://cdn.ipadbiz.cn/mlaj/images/user-avatar-3.jpg' | ||
| 199 | + }) | ||
| 200 | + | ||
| 201 | + if (success) { | ||
| 202 | + router.push('/') | ||
| 203 | + } else { | ||
| 204 | + error.value = '注册失败,请稍后再试' | ||
| 205 | + } | ||
| 206 | + } catch (err) { | ||
| 207 | + console.error('Registration error:', err) | ||
| 208 | + error.value = '注册时发生错误' | ||
| 209 | + } finally { | ||
| 210 | + loading.value = false | ||
| 211 | + } | ||
| 212 | +} | ||
| 213 | +</script> |
-
Please register or login to post a comment