yuanjing/src/views/login/Login.vue
2026-06-04 11:17:47 +08:00

262 lines
7.1 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">
<!-- 背景视频 -->
<div class="background-video-stack">
<video
class="background-video"
autoplay
muted
loop
playsinline
crossorigin="anonymous"
src="../../assets/images/loginBg.mp4"
></video>
</div>
<!-- 玻璃卡片表单 -->
<div class="form-card">
<div class="form-top">
<h2 class="form-title">欢迎来到元境</h2>
<span class="form-title"> 元启万象,境由心生 </span>
</div>
<el-form
ref="formRef"
style="max-width: 400px"
:model="form"
:rules="rules"
label-width="0px"
class="login-form"
@submit.prevent="submitForm"
>
<!-- 手机号 -->
<el-form-item prop="cellphone">
<el-input
v-model="form.cellphone"
placeholder="请输入手机号"
:prefix-icon="Iphone"
size="large"
@keyup.enter="submitForm"
/>
</el-form-item>
<!-- 验证码 + 获取按钮 -->
<el-form-item prop="captcha">
<div class="captcha-wrapper">
<el-input
v-model="form.captcha"
placeholder="请输入验证码"
:prefix-icon="Key"
size="large"
class="captcha-input"
@keyup.enter="submitForm"
/>
<el-button
:disabled="countingDown || !validPhone"
class="captcha-btn glass-btn"
@click="handleGetCaptcha"
>
{{ countingDown ? `${countdown}秒后重试` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button
type="primary"
size="large"
class="submit-btn glass-btn"
@click="submitForm"
:loading="loading"
>
登 录 / 注 册
</el-button>
</el-form-item>
</el-form>
<div class="form-bottom">
登录即表示您同意遵守《用户协议》和《隐私政策》
</div>
</div>
<!-- 账号选择弹窗 -->
<el-dialog
v-model="loginStore.showAccountSelect"
title="选择账号"
width="400px"
:close-on-click-modal="false"
@close="handleDialogClose"
>
<div class="account-list">
<div
v-for="account in loginStore.accountList"
:key="account.id"
class="account-item"
@click="selectAccount(account.id)"
>
<div class="account-info">
<div class="account-name">{{ account.username }}</div>
<!-- 显示手机号,优先使用 mobile 字段,其次 orgid -->
<div class="account-phone">{{ account.mobile || account.orgid }}</div>
</div>
<el-button type="primary" size="small">选择</el-button>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, computed, onUnmounted } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';
import { Iphone, Key } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { Code } from '@/apis/login';
import type { CodeResult, LoginParams, CaptchaParams } from '@/types/login';
import { useLoginStore } from '@/store/LoginStore';
import { debounce } from '@/utlis/debounce';
// 登录Store
const loginStore = useLoginStore();
// 表单数据类型
interface LoginForm {
cellphone: string;
captcha: string;
}
// 表单数据
const form = reactive<LoginForm>({
cellphone: '',
captcha: '',
});
// 表单实例引用
const formRef = ref<FormInstance>();
// 加载状态
const loading = ref(false);
// 登录参数(用于保存key)
const loginParams = ref<LoginParams>({
cellphone: '',
key: '',
sms_code: '',
});
// 验证规则
const rules = reactive<FormRules<LoginForm>>({
cellphone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' },
],
captcha: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 6, message: '验证码应为6位数字', trigger: 'blur' },
],
});
// 验证码倒计时
const countdown = ref(60);
const countingDown = ref(false);
let timer: number | null = null;
// 手机号是否有效
const validPhone = computed(() => {
return /^1[3-9]\d{9}$/.test(form.cellphone);
});
// 获取验证码
const requestCaptcha = async () => {
if (countingDown.value) return;
try {
const res = await Code<CodeResult, CaptchaParams>({ cellphone: form.cellphone });
if (res.status === 'ok') {
ElMessage.success('发送成功');
// 保存key
loginParams.value.key = res.data?.key || '';
} else {
ElMessage.error('发送失败');
}
} catch (error) {
console.error('发送验证码失败:', error);
ElMessage.error('发送失败,请稍后重试');
}
// 开始倒计时
countingDown.value = true;
countdown.value = 60;
timer = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(timer!);
timer = null;
countingDown.value = false;
}
}, 1000);
};
const handleGetCaptcha = debounce(requestCaptcha, 500);
// 提交表单(登录)
const doSubmitForm = async () => {
if (!formRef.value) return;
if (loading.value) return;
await formRef.value.validate(async (valid) => {
if (valid) {
loading.value = true;
try {
// 准备登录参数
loginParams.value.cellphone = form.cellphone;
loginParams.value.sms_code = form.captcha;
// 调用登录接口(通过store)
await loginStore.Login(loginParams.value);
} catch (error) {
console.error('登录失败:', error);
ElMessage.error('登录失败,请稍后重试');
} finally {
loading.value = false;
}
} else {
ElMessage.error('请正确填写表单');
}
});
};
const submitForm = debounce(doSubmitForm, 500);
// 选择账号
const handleSelectAccount = async (selectedId: string) => {
// 避免重复点击时多次请求
if (loading.value) return;
loading.value = true;
try {
await loginStore.loginWithSelectedAccount(selectedId, loginParams.value);
// 登录成功后会跳转,无需额外关闭弹窗(store 里已经关闭)
} catch (error) {
console.error('选择账号登录失败:', error);
ElMessage.error('登录失败,请稍后重试');
} finally {
loading.value = false;
}
};
const selectAccount = debounce(handleSelectAccount, 500);
// 弹窗关闭时的回调(如果用户直接关闭,清空账号列表,避免下次残留)
const handleDialogClose = () => {
loginStore.accountList = [];
};
// 组件卸载时清除定时器
onUnmounted(() => {
if (timer) clearInterval(timer);
handleGetCaptcha.cancel();
submitForm.cancel();
selectAccount.cancel();
});
</script>
<style scoped lang="less">
@import url('../../assets/less/Login/Login.less');
</style>