Add new components for login and comment functionality

This commit is contained in:
wangqiao
2025-08-17 20:15:33 +08:00
parent 99df1d1f81
commit 07b4d3de99
37 changed files with 4744 additions and 263 deletions

View File

@ -0,0 +1,333 @@
<template>
<div v-if="visible" class="popup-overlay">
<div class="popup-content">
<div class="register-container relative">
<el-icon class="absolute right-0 top-0 cursor-pointer" @click="onClose()"><Close /></el-icon>
<!-- 左侧插图 -->
<div class="register-left">
<img src="@/assets/images/login-illustration.png" alt="register" class="register-img" />
</div>
<!-- 右侧注册表单 -->
<div class="register-right">
<h2 class="register-title">快速注册</h2>
<!-- 注册表单 -->
<el-form ref="formRef" :model="registerForm" :rules="rules" class="register-form">
<!-- 手机号输入 -->
<el-form-item prop="phone">
<div class="phone-input">
<div class="area-code">+86</div>
<el-input v-model="registerForm.phone" placeholder="请输入注册手机号" class="phone-number w-280px!" />
</div>
</el-form-item>
<!-- 验证码输入 -->
<el-form-item prop="code">
<div class="verify-code-input">
<el-input v-model="registerForm.code" placeholder="请输入短信验证码" />
<el-button type="primary" class="get-code-btn" :disabled="counting > 0" @click="handleSendCode">
{{ counting > 0 ? `${counting}s后重新获取` : '获取验证码' }}
</el-button>
</div>
</el-form-item>
<!-- 注册按钮 -->
<el-button type="primary" class="register-button" @click="handleRegister"> 立即注册 </el-button>
<!-- 用户协议 -->
<div class="agreement">
<el-checkbox v-model="registerForm.agreement"> 我已阅读并同意<span class="link">多多用户协议</span> </el-checkbox>
</div>
<!-- 登录入口 -->
<div class="login-link"> 已有账号<span class="link" @click="goToLogin">点击登录</span> </div>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
import type { FormInstance } from 'element-plus'
import { Close } from '@element-plus/icons-vue'
import { sendSms } from '@/api/common/index'
import { loginByMobile } from '@/api/login/index'
const { $openLogin } = useNuxtApp()
import { refreshToken as REFRESHTOKEN } from '@/utils/axios'
import useUserStore from '@/store/user'
const userStore = useUserStore()
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
onClose: {
type: Function,
default: () => ({}),
},
})
const formRef = ref<FormInstance>()
const counting = ref(0)
const registerForm = reactive({
phone: '',
code: '',
agreement: false,
})
// 表单验证规则
const rules = {
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 handleSendCode = async () => {
if (counting.value > 0) return
// 验证手机号
try {
await formRef.value?.validateField('phone')
} catch (error) {
return
}
// TODO: 调用发送验证码接口
const res = await sendSms({
mobile: registerForm.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 handleRegister = async () => {
if (!registerForm.agreement) {
ElMessage.warning('请先同意用户协议')
return
}
try {
await formRef.value?.validate()
// TODO: 调用注册接口
const res = await loginByMobile({
mobile: registerForm.phone,
code: registerForm.code,
})
if (res.code === 0) {
const data = res.data
REFRESHTOKEN.setToken(data.accessToken, data.refreshToken)
REFRESHTOKEN.setUserId(data.userId.toString())
REFRESHTOKEN.setUserName(registerForm.phone)
userStore.setToken(data.accessToken)
userStore.setUserId(data.userId.toString())
userStore.setUserName(registerForm.phone)
userStore.setRefreshToken(data.refreshToken)
ElMessage.success('注册成功')
props.onClose()
}
} catch (error) {
return
}
}
// 跳转到登录
const goToLogin = () => {
props.onClose()
// TODO: 触发切换到登录页面的事件
// emit('switch-to-login')
$openLogin && $openLogin()
}
</script>
<style lang="scss">
.register-dialog {
.el-dialog {
border-radius: 12px;
overflow: hidden;
}
.el-dialog__header {
margin: 0;
padding: 0;
}
.el-dialog__body {
padding: 0 !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;
}
}
.register-container {
display: flex;
height: 508px;
}
.register-left {
display: flex;
align-items: center;
justify-content: center;
background: #f9faff;
width: 480px;
.register-img {
width: 438px;
height: 258px;
}
}
.register-right {
padding: 22px 25px;
box-sizing: border-box;
margin-left: auto;
margin-right: auto;
}
.register-title {
font-size: 20px;
color: #333;
text-align: center;
margin-bottom: 40px;
font-weight: normal;
}
.register-form {
width: 320px;
margin: 0 auto;
.phone-input {
display: flex;
align-items: center;
background: #f5f5f5;
border-radius: 4px;
.area-code {
padding: 0 12px;
color: #333;
font-size: 14px;
border-right: 1px solid #dcdfe6;
}
.phone-number {
flex: 1;
:deep(.el-input__wrapper) {
background: transparent;
box-shadow: none !important;
}
}
}
.verify-code-input {
display: flex;
gap: 12px;
.el-input {
flex: 1;
:deep(.el-input__wrapper) {
background: #f5f5f5;
box-shadow: none !important;
}
}
.get-code-btn {
width: 120px;
height: 40px;
padding: 0;
border-radius: 4px;
&:disabled {
color: #999;
background-color: #f5f5f5;
border-color: #dcdfe6;
}
}
}
}
.register-button {
width: 100%;
height: 40px;
margin-top: 24px;
border-radius: 4px;
}
.agreement {
margin: 16px 0;
font-size: 14px;
color: #666;
text-align: left;
.link {
color: #1677ff;
cursor: pointer;
}
}
.login-link {
text-align: right;
font-size: 14px;
color: #666;
margin-top: 20px;
.link {
color: #1677ff;
cursor: pointer;
}
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
:deep(.el-input__wrapper) {
background: #f5f5f5;
box-shadow: none !important;
}
:deep(.el-input__inner) {
height: 40px;
}
</style>