feat: 完成核心页面开发

This commit is contained in:
UGREEN USER
2026-01-28 14:43:18 +08:00
parent b657c3d8aa
commit c635b5e494
19 changed files with 747 additions and 0 deletions

142
src/views/auth/Login.vue Normal file
View File

@@ -0,0 +1,142 @@
<template>
<div class="auth-page">
<div class="auth-container">
<div class="auth-header">
<h1 class="logo">GameGroup</h1>
<p class="subtitle">游戏社群管理平台</p>
</div>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
class="auth-form"
@submit.prevent="handleLogin"
>
<el-form-item prop="account">
<el-input
v-model="formData.account"
placeholder="用户名/邮箱/手机号"
size="large"
prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formData.password"
type="password"
placeholder="密码"
size="large"
prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="large"
:loading="loading"
native-type="submit"
class="submit-btn"
>
登录
</el-button>
</el-form-item>
</el-form>
<div class="auth-footer">
<p>还没有账号?<router-link to="/register" class="link">立即注册</router-link></p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const formRef = ref<FormInstance>()
const loading = ref(false)
const formData = reactive({
account: '',
password: ''
})
const formRules: FormRules = {
account: [
{ required: true, message: '请输入账号', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码至少6位', trigger: 'blur' }
]
}
const handleLogin = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (!valid) return
loading.value = true
try {
await authStore.login(formData.account, formData.password)
ElMessage.success('登录成功')
const redirect = (route.query.redirect as string) || '/'
router.push(redirect)
} catch (error) {
console.error('登录失败:', error)
} finally {
loading.value = false
}
})
}
</script>
<style scoped lang="scss">
.auth-page {
@apply min-h-screen bg-gradient-to-br from-primary-100 to-accent-100 flex items-center justify-center p-4;
}
.auth-container {
@apply bg-white rounded-2xl shadow-2xl p-8 w-full max-w-md;
}
.auth-header {
@apply text-center mb-8;
}
.logo {
@apply text-3xl font-bold bg-gradient-to-r from-primary-500 to-accent-500 bg-clip-text text-transparent;
}
.subtitle {
@apply text-gray-500 mt-2;
}
.auth-form {
@apply space-y-4;
}
.submit-btn {
@apply w-full;
}
.auth-footer {
@apply text-center mt-6 text-sm text-gray-500;
.link {
@apply text-primary-500 hover:text-primary-600 font-medium;
}
}
</style>

196
src/views/auth/Register.vue Normal file
View File

@@ -0,0 +1,196 @@
<template>
<div class="auth-page">
<div class="auth-container">
<div class="auth-header">
<h1 class="logo">GameGroup</h1>
<p class="subtitle">创建新账号</p>
</div>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
class="auth-form"
@submit.prevent="handleRegister"
>
<el-form-item prop="username">
<el-input
v-model="formData.username"
placeholder="用户名"
size="large"
prefix-icon="User"
/>
</el-form-item>
<el-form-item prop="email">
<el-input
v-model="formData.email"
placeholder="邮箱(可选)"
size="large"
prefix-icon="Message"
/>
</el-form-item>
<el-form-item prop="phone">
<el-input
v-model="formData.phone"
placeholder="手机号(可选)"
size="large"
prefix-icon="Phone"
/>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="formData.password"
type="password"
placeholder="密码"
size="large"
prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input
v-model="formData.confirmPassword"
type="password"
placeholder="确认密码"
size="large"
prefix-icon="Lock"
show-password
/>
</el-form-item>
<el-form-item>
<el-button
type="primary"
size="large"
:loading="loading"
native-type="submit"
class="submit-btn"
>
注册
</el-button>
</el-form-item>
</el-form>
<div class="auth-footer">
<p>已有账号?<router-link to="/login" class="link">立即登录</router-link></p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
const router = useRouter()
const authStore = useAuthStore()
const formRef = ref<FormInstance>()
const loading = ref(false)
const formData = reactive({
username: '',
email: '',
phone: '',
password: '',
confirmPassword: ''
})
const validateConfirmPassword = (rule: any, value: string, callback: any) => {
if (value !== formData.password) {
callback(new Error('两次密码不一致'))
} else {
callback()
}
}
const formRules: FormRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度为3-20个字符', trigger: 'blur' }
],
email: [
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
phone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, max: 20, message: '密码长度为6-20个字符', trigger: 'blur' }
],
confirmPassword: [
{ required: true, message: '请确认密码', trigger: 'blur' },
{ validator: validateConfirmPassword, trigger: 'blur' }
]
}
const handleRegister = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (!valid) return
loading.value = true
try {
await authStore.register({
username: formData.username,
email: formData.email || undefined,
phone: formData.phone || undefined,
password: formData.password
})
ElMessage.success('注册成功')
router.push('/')
} catch (error) {
console.error('注册失败:', error)
} finally {
loading.value = false
}
})
}
</script>
<style scoped lang="scss">
// 使用与登录页相同的样式
.auth-page {
@apply min-h-screen bg-gradient-to-br from-primary-100 to-accent-100 flex items-center justify-center p-4;
}
.auth-container {
@apply bg-white rounded-2xl shadow-2xl p-8 w-full max-w-md;
}
.auth-header {
@apply text-center mb-8;
}
.logo {
@apply text-3xl font-bold bg-gradient-to-r from-primary-500 to-accent-500 bg-clip-text text-transparent;
}
.subtitle {
@apply text-gray-500 mt-2;
}
.auth-form {
@apply space-y-4;
}
.submit-btn {
@apply w-full;
}
.auth-footer {
@apply text-center mt-6 text-sm text-gray-500;
.link {
@apply text-primary-500 hover:text-primary-600 font-medium;
}
}
</style>