Files
ctms/frontend/src/views/Login.vue
T
2025-12-22 21:19:48 +08:00

227 lines
5.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="login-container">
<div class="login-content">
<div class="login-brand">
<h1 class="brand-title">CTMS</h1>
<p class="brand-subtitle">临床试验管理系统</p>
</div>
<el-card class="login-card">
<div class="login-header">
<h2 class="form-title">用户登录</h2>
<p class="form-desc">请输入邮箱与密码登录未激活账号将无法登录</p>
</div>
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-position="top"
@keyup.enter.native="onSubmit"
>
<el-form-item label="邮箱" prop="email">
<el-input
v-model="form.email"
placeholder="请输入邮箱"
autocomplete="email"
/>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input
v-model="form.password"
type="password"
placeholder="请输入密码"
autocomplete="current-password"
show-password
/>
</el-form-item>
<div class="form-footer">
<el-checkbox v-model="form.remember">记住账号密码</el-checkbox>
<el-button
type="primary"
:loading="loading"
class="submit-btn"
@click="onSubmit"
>
</el-button>
<div class="link-row">
<RouterLink to="/register">没有账号注册</RouterLink>
</div>
</div>
</el-form>
</el-card>
<div class="login-footer">
<p>© 2025 Clinical Trial Management System. All Rights Reserved.</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import { ElCheckbox, ElMessage, type FormInstance, type FormRules } from "element-plus";
import { useAuthStore } from "../store/auth";
import { getCachedCredential, setCachedCredential, clearCachedCredential } from "../utils/auth";
const auth = useAuthStore();
const router = useRouter();
const formRef = ref<FormInstance>();
const form = reactive({
email: "",
password: "",
remember: false,
});
const rules: FormRules<typeof form> = {
email: [
{ required: true, message: "请输入邮箱", trigger: "blur" },
{ type: "email", message: "邮箱格式不正确", trigger: "blur" },
],
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
};
const loading = ref(false);
onMounted(() => {
const cached = getCachedCredential();
if (cached) {
form.email = cached.email;
form.password = cached.password;
form.remember = true;
}
});
const onSubmit = async () => {
if (!formRef.value) return;
const valid = await formRef.value.validate();
if (!valid) return;
loading.value = true;
try {
await auth.login(form.email, form.password);
if (form.remember) {
setCachedCredential(form.email, form.password);
} else {
clearCachedCredential();
}
router.push("/");
} catch (error: any) {
const detail = error?.response?.data?.detail || error?.response?.data?.message;
ElMessage.error(detail || "邮箱或密码错误");
} finally {
loading.value = false;
}
};
</script>
<style scoped>
.login-container {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: radial-gradient(circle at 50% 50%, #f0f2f5 0%, #e6e9f0 100%);
position: relative;
overflow: hidden;
}
/* 装饰性背景元素,不使用外部资源 */
.login-container::before {
content: "";
position: absolute;
width: 600px;
height: 600px;
background: var(--ctms-primary);
opacity: 0.03;
border-radius: 50%;
top: -200px;
right: -200px;
}
.login-content {
width: 400px;
z-index: 1;
}
.login-brand {
text-align: center;
margin-bottom: 40px;
}
.brand-title {
font-size: 36px;
font-weight: 700;
color: var(--ctms-primary);
letter-spacing: 2px;
margin: 0;
}
.brand-subtitle {
font-size: 16px;
color: var(--ctms-text-secondary);
margin-top: 8px;
}
.login-card {
padding: 8px;
}
.login-header {
margin-bottom: 32px;
}
.form-title {
font-size: 20px;
font-weight: 600;
color: var(--ctms-text-main);
margin: 0;
}
.form-desc {
font-size: 14px;
color: var(--ctms-text-secondary);
margin-top: 8px;
}
.submit-btn {
width: 100%;
height: 44px;
font-size: 16px;
margin-top: 16px;
}
.link-row {
margin-top: 12px;
text-align: center;
}
.link-row a {
color: var(--ctms-primary);
font-size: 14px;
}
.login-footer {
text-align: center;
margin-top: 48px;
}
.login-footer p {
font-size: 12px;
color: var(--ctms-text-disabled);
}
:deep(.el-form-item__label) {
padding-bottom: 4px;
}
:deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px #d9d9d9 inset;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: 0 0 0 1px var(--ctms-primary) inset !important;
}
</style>