This commit is contained in:
oneao committed 2025-12-16 17:21:56 +08:00
1 parent 1da52dc4f4
commit 276644f473
223 files changed
+9609 -262

No files matched your search

@@ -1,14 +1,29 @@
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { reactive, ref } from 'vue'
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { loginApi } from '@/request/api/auth'
import { loginByEmailCodeApi, loginByPasswordApi } from '@/request/api/auth'
import { authCodeSendApi } from '@/request/api/auth/code'
import { useUserStore } from '@/store/user'
import { AuthCodeChannel, AuthCodePurpose } from '@/types/auth/code'
import { validateEmail } from '@/utils/validate'
const userStore = useUserStore()
const router = useRouter()
const loginOptions = [
{
label: '密码登录',
value: '0',
},
{
label: '验证码登录',
value: '1',
},
]
const loginMode = ref('0')
const carouselList = [
{
title: '值得信赖',
@@ -37,59 +52,160 @@ const carouselList = [
},
]
// --- 发送验证码状态 & 倒计时 ---
const sending = ref(false)
const countdown = ref(0)
let timer: number | undefined
function startCountdown(seconds = 60) {
countdown.value = seconds
timer = window.setInterval(() => {
countdown.value -= 1
if (countdown.value <= 0) {
clearInterval(timer)
timer = undefined
}
}, 1000)
}
const form = reactive<{
loginCredential: string
email: string
password: string
code: string
rememberMe: boolean
}>({
loginCredential: '',
email: '',
password: '',
code: '',
rememberMe: true,
})
const formRef = ref<FormInstance>()
const formRules = {
loginCredential: [
email: [
{ required: true, message: '邮箱号不能为空', trigger: 'blur' },
{ type: 'email', required: true, message: '邮箱格式错误', trigger: 'blur' },
],
password: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
if (loginMode.value === '0') {
if (!value) {
callback(new Error('密码不能为空'))
return
}
}
callback() // 校验通过
},
trigger: 'blur',
},
],
code: [
{
validator: (rule: any, value: any, callback: any) => {
if (loginMode.value === '1') {
if (!value) {
callback(new Error('验证码不能为空'))
return
}
}
callback() // 校验通过
},
trigger: 'blur',
},
],
}
const isEmailValid = computed(() => validateEmail(form.email))
const submitLoading = ref(false)
function handleSendEmailCode() {
if (sending.value || countdown.value > 0 || !isEmailValid.value)
return
sending.value = true
authCodeSendApi({
channel: AuthCodeChannel.EMAIL,
purpose: AuthCodePurpose.LOGIN,
target: form.email,
}).then((res) => {
if (res.isSuccess) {
ElMessage.success('发送成功')
startCountdown()
}
else {
sending.value = false
ElMessage.error(res.message || '发送失败,请检查邮箱是否正确或稍后再试!')
}
}).catch((err) => {
console.log(err)
ElMessage.error('发送失败,请稍后再试')
sending.value = false
}).finally(() => {
sending.value = false
})
}
async function submitForm(formEl: FormInstance | undefined) {
if (!formEl)
return
await formEl.validate((valid) => {
if (valid) {
submitLoading.value = true
loginApi({
loginCredential: form.loginCredential,
password: form.password,
}).then((res) => {
if (res.isSuccess) {
userStore.setUserInfo({
id: res.data.id,
account: res.data.account,
email: res.data.email,
name: res.data.name,
token: res.data.token,
avatar: res.data.avatar || '/no-avatar.png',
})
ElMessage.success('登录成功')
router.push('/')
}
else {
ElMessage.error(res.message || '登录失败,请稍后再试!')
}
}).catch((err) => {
console.log(err)
}).finally(() => {
submitLoading.value = false
})
await formEl.validate(async (valid) => {
if (!valid)
return
submitLoading.value = true
try {
let res
if (loginMode.value === '0') {
// 密码登录
res = await loginByPasswordApi({
email: form.email,
password: form.password,
})
}
else {
// 验证码登录
res = await loginByEmailCodeApi({
email: form.email,
code: form.code,
})
}
if (res.isSuccess) {
ElMessage.success('登录成功')
userStore.setUserInfo({
user: {
id: res.data.userId,
name: res.data.userName,
position: res.data.userPosition,
role: res.data.userRole,
},
company: {
id: res.data.companyId,
logo: res.data.companyLogo,
name: res.data.companyName,
},
token: res.data.token,
})
router.push('/')
}
else {
ElMessage.error(res.message || '登录失败,请稍后再试')
}
}
catch (err) {
console.log(err)
ElMessage.error('登录失败,请稍后再试')
}
finally {
submitLoading.value = false
}
})
}
@@ -130,8 +246,19 @@ function navigateTo(path: string) {
<h4 class="title">
登录
</h4>
<div style="padding: 8px 0px;">
<el-segmented
v-model="loginMode" :options="loginOptions" size="large"
/>
</div>
<p class="title-extra">
输入你的 邮箱 和 密码 登录账户.
<template v-if="loginMode === '0'">
输入你的 邮箱 和 密码 登录系统
</template>
<template v-else-if="loginMode === '1'">
输入你的 邮箱 和 验证码 登录系统
</template>
</p>
<el-form
ref="formRef"
@@ -141,11 +268,14 @@ function navigateTo(path: string) {
label-position="top"
hide-required-asterisk
>
<el-form-item label="邮箱" prop="loginCredential">
<el-input v-model="form.loginCredential" placeholder="请输入邮箱" size="large" />
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" size="large" />
</el-form-item>
<el-form-item prop="password">
<el-form-item
v-if="loginMode === '0'"
prop="password"
>
<template #label>
<span class="form-label-flex">
<span>密码</span>
@@ -155,11 +285,47 @@ function navigateTo(path: string) {
<el-input
v-model="form.password"
placeholder="请输入密码"
show-password
size="large"
@keyup.enter="submitForm(formRef)"
/>
</el-form-item>
<el-form-item
v-else
prop="code"
>
<template #label>
<span class="form-label-flex">
<span>密码</span>
<span class="forgot" @click="navigateTo('/auth/forget')">忘记密码?</span>
</span>
</template>
<el-input
v-model="form.code"
placeholder="请输入邮箱验证码"
class="email-code"
size="large"
:class="(sending || countdown > 0 || !isEmailValid) ? 'disabled' : ''"
>
<template #append>
<el-button
:disabled="sending || countdown > 0 || !isEmailValid"
@click="handleSendEmailCode"
>
<template v-if="sending">
发送中...
</template>
<template v-else-if="countdown > 0">
{{ countdown }}s
</template>
<template v-else>
发送验证码
</template>
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.rememberMe" label="记住我" class="remeber" />
</el-form-item>
@@ -257,6 +423,20 @@ function navigateTo(path: string) {
width: 100%;
margin-bottom: 12px;
}
.email-code {
:deep(.el-input-group__append) {
background-color: #409eff;
color: #fff;
cursor: pointer;
}
&.disabled {
:deep(.el-input-group__append) {
background-color: #f5f7fa;
color: #c0c4cc;
}
}
}
.remeber {
:deep(.el-checkbox__inner) {
@@ -267,6 +447,7 @@ function navigateTo(path: string) {
font-size: 16px;
}
}
.title {
font-size: 28px;
font-weight: 600;