Files
front-pc/components/kl-login/index.vue

480 lines
12 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>
<div v-if="visible" class="popup-overlay">
<div class="popup-content">
<div class="login-container relative">
<el-icon class="absolute! right-[0px] top-[0px] cursor-pointer" @click="onClose()"><Close /></el-icon>
<!-- 左侧插图 -->
<div class="login-left">
<img src="~/assets/images/login-illustration.png" alt="login" class="login-img" />
</div>
<!-- 右侧登录表单 -->
<div class="login-right">
<h2 class="login-title">登录</h2>
<!-- 登录方式切换 -->
<div class="login-tabs">
<span :class="['tab-item', activeTab === 'account' ? 'active' : '']" @click="activeTab = 'account'"> 密码登录 </span>
<span :class="['tab-item', activeTab === 'verify' ? 'active' : '']" @click="activeTab = 'verify'"> 验证码登录 </span>
</div>
<!-- 登录表单 -->
<el-form ref="formRef" :model="loginForm" :rules="rules" class="login-form">
<template v-if="activeTab === 'account'">
<el-form-item prop="mobile">
<el-input v-model="loginForm.mobile" placeholder="请输入用户名" :prefix-icon="User" class="w-[322px]!" />
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" placeholder="请输入密码" :prefix-icon="Lock" type="password" />
</el-form-item>
</template>
<!-- 验证码登录 -->
<template v-else>
<el-form-item prop="phone">
<el-input v-model="loginForm.phone" placeholder="请输入手机号" class="w-[322px]!" />
</el-form-item>
<el-form-item prop="code">
<div class="verify-code-input">
<el-input v-model="loginForm.code" placeholder="请输入验证码" />
<el-button type="primary" class="verify-code-btn" :disabled="counting > 0" @click="handleSendCode">
{{ counting > 0 ? `${counting}s后重新获取` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
</template>
<!-- 登录按钮 -->
<el-button type="primary" class="login-button" :loading="loading" @click="handleLogin"> 登录 </el-button>
<!-- 用户协议 -->
<div class="agreement">
<el-checkbox v-model="loginForm.agreement"> 我已阅读并同意<span class="link">多多用户协议</span> </el-checkbox>
</div>
<!-- 第三方登录 -->
<div class="third-party-login">
<div class="login-icons" @click="handleLoginQQ">
<img src="~/assets/images/qq-v2.png" alt="QQ登录" class="login-icon" />
<div class="icon-text">QQ登录</div>
</div>
<div class="login-icons" @click="handleLoginWechat">
<img src="~/assets/images/weixin-v2.png" alt="微信登录" class="login-icon" />
<div class="icon-text">微信登录</div>
</div>
<div class="login-icons" @click="handleLoginEmail">
<img src="~/assets/images/email-v2.png" alt="邮箱登录" class="login-icon" />
<div class="icon-text">邮箱登录</div>
</div>
</div>
<!-- 注册入口 -->
<div class="register-link"> 没有账号<span class="link" @click="handleRegister">点击注册</span> </div>
</el-form>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, nextTick } from 'vue'
import { User, Lock, Close } from '@element-plus/icons-vue'
import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { login, loginByMobile } from '~/api/login/index'
import { sendSms } from '~/api/common/index'
import REFRESHTOKEN from '~/utils/RefreshToken'
import { handleLoginQQ, handleLoginWechat } from '~/utils/login'
import useUserStore from '~/store/user'
const app = useNuxtApp()
const token = useToken();
const userStore = useUserStore()
const tokenCookie = useCookie<string | undefined>('token');
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
onClose: {
type: Function,
default: () => ({}),
},
active: {
type: String,
default: 'account',
},
/** code */
code: {
type: String,
default: '',
},
/** 区分 */
type: {
type: String,
default: '',
},
state: {
type: String,
default: '',
},
})
const activeTab = ref(props.active)
const counting = ref(0)
const formRef = ref<FormInstance>()
const loginForm = reactive({
mobile: '',
password: '',
phone: '',
code: '',
agreement: false,
})
const rules = {
mobile: {
required: true,
message: '请输入用户名',
trigger: 'blur',
},
password: {
required: true,
message: '请输入密码',
trigger: 'blur',
},
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' },
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 6, message: '验证码长度应为6位', trigger: 'blur' },
],
}
const handleRegister = () => {
props.onClose()
nextTick(() => {
app.$openRegister()
})
}
// 发送验证码
const handleSendCode = async () => {
if (counting.value > 0) return
if (!loginForm.phone) {
ElMessage.warning('请输入手机号')
return
}
// 这里添加手机号验证逻辑
const phoneReg = /^1[3-9]\d{9}$/
if (!phoneReg.test(loginForm.phone)) {
ElMessage.warning('请输入正确的手机号')
return
}
// 这里添加发送验证码的接口调用
// TODO: 调用发送验证码接口
const res = await sendSms({
mobile: loginForm.phone,
scene: 1, // 场景值,根据实际情况设置
})
if (res.code !== 0) return
if (res.code === 0) {
ElMessage.success('验证码发送成功')
}
// 开始倒计时
counting.value = 60
const timer = setInterval(() => {
counting.value--
if (counting.value <= 0) {
clearInterval(timer)
}
}, 1000)
}
const loading = ref(false)
const handleLogin = async () => {
if (!loginForm.agreement) {
ElMessage.warning('请先同意用户协议')
return
}
// 处理登录逻辑
// 根据不同登录方式处理登录逻辑
if (activeTab.value === 'account') {
await formRef.value?.validate()
// 账号密码登录逻辑
loading.value = true
try {
const res = await login(loginForm)
const { code, data} = res
if (code === 0) {
// 设置cookie
tokenCookie.value = data.accessToken;
// 更新state
token.value = data.accessToken;
REFRESHTOKEN.setToken(data.accessToken, data.refreshToken)
REFRESHTOKEN.setUserId(data.userId.toString())
REFRESHTOKEN.setUserName(loginForm.mobile)
userStore.setToken(data.accessToken)
userStore.setUserId(data.userId.toString())
userStore.setUserName(loginForm.mobile)
userStore.setRefreshToken(data.refreshToken)
ElMessage.success('登录成功')
props.onClose()
// 获取用户信息
userStore.getUserInfo()
// 登录成功
}
} finally {
loading.value = false
}
} else {
// 验证码登录逻辑
try {
await formRef.value?.validate()
// TODO: 调用注册接口
loading.value = true
const res = await loginByMobile({
mobile: loginForm.phone,
code: loginForm.code,
socialType: props.type,
socialCode: props.code,
socialState: props.state,
})
if (res.code === 0) {
const data = res.data
REFRESHTOKEN.setToken(data.accessToken, data.refreshToken)
REFRESHTOKEN.setUserId(data.userId.toString())
REFRESHTOKEN.setUserName(loginForm.phone)
userStore.setToken(data.accessToken)
userStore.setUserId(data.userId.toString())
userStore.setUserName(loginForm.phone)
userStore.setRefreshToken(data.refreshToken)
ElMessage.success('登录成功')
props.onClose()
// 获取用户信息
userStore.getUserInfo()
// 登录成功
}
} finally {
loading.value = false
}
}
}
const handleLoginEmail = () => {
props.onClose()
nextTick(() => {
app.$openLoginEmail()
})
}
</script>
<style lang="scss">
.login-dialog {
.el-dialog {
border-radius: 12px;
overflow: hidden;
}
.el-dialog__header {
// margin: 0;
// padding: 0;
}
.el-dialog__body {
padding: 0px !important;
}
}
</style>
<style scoped lang="scss">
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
.popup-content {
background-color: white;
padding: 20px;
border-radius: 5px;
}
}
.login-container {
display: flex;
height: 508px;
}
.login-left {
display: flex;
align-items: center;
justify-content: center;
background: #f9faff;
width: 480px;
.login-img {
width: 438px;
height: 258px;
}
}
.login-right {
padding: 22px 25px;
box-sizing: border-box;
margin-left: auto;
margin-right: auto;
}
.login-title {
font-size: 20px;
color: #333;
text-align: center;
margin-bottom: 20px;
}
.login-tabs {
display: flex;
justify-content: center;
margin-bottom: 24px;
.tab-item {
font-size: 14px;
color: #666;
padding: 0 16px;
cursor: pointer;
position: relative;
&.active {
color: #1677ff;
font-weight: 500;
&::after {
content: '';
position: absolute;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
width: 20px;
height: 2px;
background: #1677ff;
border-radius: 1px;
}
}
// &:first-child::after {
// content: '';
// position: absolute;
// right: 0;
// top: 50%;
// transform: translateY(-50%);
// width: 1px;
// height: 14px;
// background: #ddd;
// }
}
}
.login-form {
.el-input {
height: 40px;
}
:deep(.el-input__wrapper) {
background: #f5f5f5;
border: none;
box-shadow: none;
}
}
.login-button {
width: 100%;
height: 40px;
margin-top: 24px;
border-radius: 4px;
}
.agreement {
margin: 16px 0;
font-size: 14px;
color: #666;
.link {
color: #1677ff;
cursor: pointer;
}
}
.third-party-login {
display: flex;
justify-content: center;
gap: 40px;
margin-top: 30px;
.login-icons {
text-align: center;
cursor: pointer;
.login-icon {
width: 40px;
height: 40px;
margin-bottom: 8px;
}
.icon-text {
font-size: 12px;
color: #666;
}
}
}
.register-link {
text-align: right;
margin-top: 20px;
font-size: 14px;
color: #666;
.link {
color: #1677ff;
cursor: pointer;
}
}
.verify-code-input {
display: flex;
gap: 12px;
.el-input {
flex: 1;
}
.verify-code-btn {
width: 120px;
height: 40px;
padding: 0;
border-radius: 4px;
&:disabled {
color: #999;
background-color: #f5f5f5;
border-color: #dcdfe6;
}
}
}
// 确保验证码按钮文字不换行
:deep(.verify-code-btn) {
white-space: nowrap;
}
</style>