Files
workspace/code/link56/link56-frontend-new/src/views/auth/login.vue
T
2025-12-27 17:21:38 +08:00

410 lines
9.5 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.
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import EmailCodeInput from '@/components/EmailCodeInput.vue'
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: '值得信赖',
company: '恒星科技有限公司',
content: '我们已经连续三年使用该平台,数据处理稳定高效,团队协作体验非常顺畅。',
},
{
title: '安全 · 高效',
company: '博越信息技术',
content: '平台的权限管理做得很好,操作简单清晰,极大提升了内部管理效率。',
},
{
title: '稳定可靠',
company: '远航供应链集团',
content: '系统运行非常稳定,几乎没有出现过宕机情况,客服响应速度也很快。',
},
{
title: '智能化体验',
company: '蓝海数据服务中心',
content: '智能报表和自动化功能非常实用,减少了大量人工整理数据的时间。',
},
{
title: '灵活易用',
company: '中联创投有限公司',
content: 'UI设计简洁友好,新员工上手很快,各项业务流程也能灵活配置。',
},
]
const form = reactive<{
email: string
password: string
code: string
rememberMe: boolean
}>({
email: '',
password: '',
code: '',
rememberMe: true,
})
const formRef = ref<FormInstance>()
const formRules = {
email: [
{ required: true, message: '邮箱号不能为空', trigger: 'blur' },
{ type: 'email', required: true, message: '邮箱格式错误', trigger: 'blur' },
],
password: [
{
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 submitLoading = ref(false)
async function submitForm(formEl: FormInstance | undefined) {
if (!formEl)
return
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.setLoginInfo(res.data)
router.push('/')
}
else {
ElMessage.error(res.message || '登录失败,请稍后再试')
}
}
catch (err) {
console.log(err)
ElMessage.error('登录失败,请稍后再试')
}
finally {
submitLoading.value = false
}
})
}
function navigateTo(path: string) {
router.push(path)
}
</script>
<template>
<div class="auth-wrapper">
<div class="auth-bg">
<img src="/auth-bg.jpg">
<div class="bg-content">
<el-carousel
height="150px"
indicator-position="none"
>
<el-carousel-item v-for="(item, index) in carouselList" :key="index">
<div class="carousel-item">
<h2 class="title">
{{ item.title }}
</h2>
<span class="content">{{ item.content }}</span>
<span class="company"> - {{ item.company }}</span>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
<div class="login-form">
<img src="/logo.png" class="logo" @click="navigateTo('/')">
<div class="main">
<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"
:model="form"
:rules="formRules"
label-width="auto"
label-position="top"
hide-required-asterisk
>
<el-form-item label="邮箱" prop="email">
<el-input v-model="form.email" placeholder="请输入邮箱" size="large" />
</el-form-item>
<el-form-item
v-if="loginMode === '0'"
prop="password"
>
<template #label>
<span class="form-label-flex">
<span>密码</span>
<span class="forgot" @click="navigateTo('/auth/forget')">忘记密码?</span>
</span>
</template>
<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>
<EmailCodeInput
v-model="form.code"
size="large"
:email="form.email"
:channel="AuthCodeChannel.EMAIL"
:purpose="AuthCodePurpose.LOGIN"
placeholder="请输入邮箱验证码"
/>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.rememberMe" label="记住我" class="remeber" />
</el-form-item>
<el-form-item>
<el-button type="primary" class="form-btn" :loading="submitLoading" @click="submitForm(formRef)">
登录
</el-button>
</el-form-item>
</el-form>
</div>
<div class="footer">
没有账号?前去
<span class="back" @click="navigateTo('/auth/register')">注册</span>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.auth-wrapper {
display: flex;
height: 100vh;
overflow: hidden;
background-color: #fff;
.auth-bg {
width: 70%;
height: 100%;
position: relative;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
.bg-content {
position: absolute;
left: 50%;
bottom: 10%;
transform: translateX(-50%);
text-align: center;
color: #fff;
width: 600px;
.carousel-item {
display: flex;
flex-direction: column;
.title {
font-size: 28px;
line-height: 28px;
margin-bottom: 16px;
}
.content {
margin-bottom: 8px;
}
.company {
font-size: 14px;
}
}
}
}
.form-label-flex {
display: flex;
justify-content: space-between;
width: 100%;
align-items: center;
.forgot {
color: #0f6fec;
cursor: pointer;
}
}
.login-form {
flex: 1;
display: flex;
flex-direction: column;
padding: 36px;
.logo {
height: 70px;
width: 160px;
cursor: pointer;
}
.main {
flex: 1;
display: flex;
justify-content: center;
flex-direction: column;
:deep(.el-form-item__label) {
font-size: 16px;
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) {
width: 16px;
height: 16px;
}
:deep(.el-checkbox__label) {
font-size: 16px;
}
}
.title {
font-size: 28px;
font-weight: 600;
margin-bottom: 10px;
}
.title-extra {
font-size: 16px;
color: #666;
margin-bottom: 36px;
}
.form-btn {
width: 100%;
height: 36px;
font-size: 16px;
}
}
.footer {
font-size: 18px;
color: #999;
height: 48px;
text-align: center;
line-height: 48px;
.back {
color: #0f6fec;
cursor: pointer;
}
}
}
}
</style>