mirror of
https://github.com/jiangrui1994/CloudSaver.git
synced 2026-01-13 00:28:45 +08:00
refactor:pc views
This commit is contained in:
354
frontend/src/views/pc/Login.vue
Normal file
354
frontend/src/views/pc/Login.vue
Normal file
@@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<div class="login-bg"></div>
|
||||
<div class="login-card">
|
||||
<div class="card-header">
|
||||
<img src="@/assets/images/logo.png" alt="Logo" class="logo" />
|
||||
<h1 class="title">欢迎回来</h1>
|
||||
<p class="subtitle">登录您的账户以继续</p>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeTab" class="login-tabs">
|
||||
<el-tab-pane label="登录" name="login">
|
||||
<el-form
|
||||
ref="loginFormRef"
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
@keyup.enter="handleLogin"
|
||||
>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
placeholder="用户名"
|
||||
:prefix-icon="User"
|
||||
autocomplete="username"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
:prefix-icon="Lock"
|
||||
show-password
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<div class="form-options">
|
||||
<el-checkbox v-model="rememberPassword">记住密码</el-checkbox>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" class="submit-btn" :loading="loading" @click="handleLogin">
|
||||
登录
|
||||
</el-button>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="注册" name="register">
|
||||
<el-form ref="registerFormRef" :model="registerForm" :rules="registerRules">
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="registerForm.username" placeholder="用户名" :prefix-icon="User" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="registerForm.password"
|
||||
type="password"
|
||||
placeholder="密码"
|
||||
:prefix-icon="Lock"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="confirmPassword">
|
||||
<el-input
|
||||
v-model="registerForm.confirmPassword"
|
||||
type="password"
|
||||
placeholder="确认密码"
|
||||
:prefix-icon="Lock"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="registerCode">
|
||||
<el-input
|
||||
v-model="registerForm.registerCode"
|
||||
placeholder="注册码"
|
||||
:prefix-icon="Key"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-button type="primary" class="submit-btn" :loading="loading" @click="handleRegister">
|
||||
注册
|
||||
</el-button>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { User, Lock, Key } from "@element-plus/icons-vue";
|
||||
import { userApi } from "@/api/user";
|
||||
import "@/styles/common.scss";
|
||||
import { STORAGE_KEYS } from "@/constants/storage";
|
||||
import type { FormItemRule } from "element-plus";
|
||||
|
||||
// 状态
|
||||
const activeTab = ref("login");
|
||||
const loading = ref(false);
|
||||
const rememberPassword = ref(false);
|
||||
|
||||
const loginForm = ref({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const registerForm = ref({
|
||||
username: "",
|
||||
password: "",
|
||||
confirmPassword: "",
|
||||
registerCode: "",
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const loginRules = {
|
||||
username: [
|
||||
{ required: true, message: "请输入用户名", trigger: "blur" },
|
||||
{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
{ min: 6, max: 20, message: "长度在 6 到 20 个字符", trigger: "blur" },
|
||||
],
|
||||
};
|
||||
|
||||
const registerRules = {
|
||||
...loginRules,
|
||||
confirmPassword: [
|
||||
{ required: true, message: "请确认密码", trigger: "blur" },
|
||||
{
|
||||
validator: (_rule: FormItemRule, value: string, callback: (error?: Error) => void) => {
|
||||
if (value !== registerForm.value.password) {
|
||||
callback(new Error("两次输入密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
registerCode: [{ required: true, message: "请输入注册码", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
const loginFormRef = ref();
|
||||
const registerFormRef = ref();
|
||||
|
||||
// 记住密码相关
|
||||
onMounted(() => {
|
||||
const savedUsername = localStorage.getItem(STORAGE_KEYS.USERNAME);
|
||||
const savedPassword = localStorage.getItem(STORAGE_KEYS.PASSWORD);
|
||||
if (savedUsername && savedPassword) {
|
||||
loginForm.value.username = savedUsername;
|
||||
loginForm.value.password = savedPassword;
|
||||
rememberPassword.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 登录处理
|
||||
const handleLogin = async () => {
|
||||
if (!loginFormRef.value) return;
|
||||
|
||||
await loginFormRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await userApi.login(loginForm.value);
|
||||
if (res.code === 0) {
|
||||
// 记住密码
|
||||
if (rememberPassword.value) {
|
||||
localStorage.setItem(STORAGE_KEYS.USERNAME, loginForm.value.username);
|
||||
localStorage.setItem(STORAGE_KEYS.PASSWORD, loginForm.value.password);
|
||||
} else {
|
||||
localStorage.removeItem(STORAGE_KEYS.USERNAME);
|
||||
localStorage.removeItem(STORAGE_KEYS.PASSWORD);
|
||||
}
|
||||
|
||||
localStorage.setItem(STORAGE_KEYS.TOKEN, res.data.token);
|
||||
ElMessage.success("登录成功");
|
||||
router.push("/");
|
||||
} else {
|
||||
ElMessage.error(res.message || "登录失败");
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
ElMessage.error(error instanceof Error ? error.message : "登录失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 注册处理
|
||||
const handleRegister = async () => {
|
||||
if (!registerFormRef.value) return;
|
||||
|
||||
await registerFormRef.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await userApi.register({
|
||||
username: registerForm.value.username,
|
||||
password: registerForm.value.password,
|
||||
registerCode: registerForm.value.registerCode,
|
||||
});
|
||||
|
||||
if (res.code === 0) {
|
||||
ElMessage.success("注册成功");
|
||||
// 自动填充登录表单
|
||||
loginForm.value.username = registerForm.value.username;
|
||||
loginForm.value.password = registerForm.value.password;
|
||||
activeTab.value = "login";
|
||||
// 自动登录
|
||||
handleLogin();
|
||||
} else {
|
||||
ElMessage.error(res.message || "注册失败");
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
ElMessage.error(error instanceof Error ? error.message : "注册失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/styles/common.scss";
|
||||
|
||||
.login-page {
|
||||
@include flex-center;
|
||||
min-height: 100vh;
|
||||
background: var(--theme-bg);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-bg {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-image: url("@/assets/images/login-bg.jpg");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
filter: blur(3px);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
@include glass-effect;
|
||||
position: relative;
|
||||
width: 420px;
|
||||
padding: 32px;
|
||||
border-radius: var(--theme-radius);
|
||||
box-shadow: var(--theme-shadow);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
|
||||
.logo {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: var(--theme-text-primary);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
color: var(--theme-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.login-tabs {
|
||||
:deep(.el-tabs__nav-wrap::after) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__active-bar) {
|
||||
background-color: var(--theme-primary);
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
color: var(--theme-text-secondary);
|
||||
font-size: 16px;
|
||||
|
||||
&.is-active {
|
||||
color: var(--theme-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.el-input__wrapper {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
box-shadow: none;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
transition: var(--theme-transition);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--theme-primary);
|
||||
}
|
||||
|
||||
&.is-focus {
|
||||
border-color: var(--theme-primary);
|
||||
box-shadow: 0 0 0 1px var(--theme-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.el-input__inner {
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-options {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
font-size: 16px;
|
||||
border-radius: var(--theme-radius-sm);
|
||||
background: var(--theme-primary);
|
||||
transition: var(--theme-transition);
|
||||
|
||||
&:hover {
|
||||
background: var(--theme-primary-hover);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user