Add new components for login and comment functionality
This commit is contained in:
471
components/kl-login/index.vue
Normal file
471
components/kl-login/index.vue
Normal file
@ -0,0 +1,471 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="visible" class="popup-overlay">
|
||||
<div class="popup-content">
|
||||
<div class="login-container relative">
|
||||
<el-icon class="absolute right-0 top-0 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 as REFRESHTOKEN } from '@/utils/axios'
|
||||
import { handleLoginQQ, handleLoginWechat } from '@/utils/login'
|
||||
import useUserStore from '@/store/user'
|
||||
const { $openRegister, $openLogin, $openLoginEmail } = useNuxtApp()
|
||||
const userStore = useUserStore()
|
||||
|
||||
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(() => {
|
||||
$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, msg } = res
|
||||
if (code !== 0) return ElMessage.error(msg)
|
||||
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(() => {
|
||||
$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>
|
||||
Reference in New Issue
Block a user