This commit is contained in:
Mrx
2026-02-26 14:58:46 +08:00
parent d6e460d7b6
commit 2c97f724f5

View File

@@ -6,12 +6,13 @@ import { useAgentStore } from '@/stores/agentStore'
import { useUserStore } from '@/stores/userStore' import { useUserStore } from '@/stores/userStore'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import useApiFetch from '@/composables/useApiFetch' import useApiFetch from '@/composables/useApiFetch'
import { useAliyunCaptcha } from '@/composables/useAliyunCaptcha'
const router = useRouter() const router = useRouter()
const route = useRoute() const route = useRoute()
const agentStore = useAgentStore() const agentStore = useAgentStore()
const userStore = useUserStore() const userStore = useUserStore()
const appName = import.meta.env.VITE_APP_NAME || '真爱查'; const { runWithCaptcha } = useAliyunCaptcha()
const phoneNumber = ref('') const phoneNumber = ref('')
const verificationCode = ref('') const verificationCode = ref('')
@@ -29,33 +30,31 @@ function fillInviteCode() {
// 从URL参数中读取邀请码并自动填入如果用户已登录且有手机号则自动填充 // 从URL参数中读取邀请码并自动填入如果用户已登录且有手机号则自动填充
onMounted(async () => { onMounted(async () => {
const inviteCodeParam = route.query.invite_code; const inviteCodeParam = route.query.invite_code
if (inviteCodeParam) { if (inviteCodeParam) {
inviteCode.value = inviteCodeParam; inviteCode.value = inviteCodeParam
} }
// 从路由参数获取手机号(已注册用户继续注册成为代理的情况) // 从路由参数获取手机号(已注册用户继续注册成为代理的情况)
const mobileParam = route.query.mobile; const mobileParam = route.query.mobile
if (mobileParam) { if (mobileParam) {
phoneNumber.value = mobileParam; phoneNumber.value = mobileParam
isPhoneDisabled.value = true; isPhoneDisabled.value = true
} else { } else {
// 如果用户已登录且有手机号,自动填充手机号 // 如果用户已登录且有手机号,自动填充手机号
const token = localStorage.getItem("token"); const token = localStorage.getItem('token')
if (token) { if (token) {
// 确保用户信息已加载
if (!userStore.mobile) { if (!userStore.mobile) {
await userStore.fetchUserInfo(); await userStore.fetchUserInfo()
} }
if (userStore.mobile) { if (userStore.mobile) {
phoneNumber.value = userStore.mobile; phoneNumber.value = userStore.mobile
isPhoneDisabled.value = true; isPhoneDisabled.value = true
} }
} }
} }
}); })
// 是否是已注册用户(根据注册接口返回判断)
const isRegisteredUser = ref(false) const isRegisteredUser = ref(false)
const isPhoneNumberValid = computed(() => { const isPhoneNumberValid = computed(() => {
@@ -73,36 +72,61 @@ const canRegister = computed(() => {
isAgreed.value isAgreed.value
}) })
// 发送验证码(参考登录页逻辑,使用阿里云滑块验证码)
async function sendVerificationCode() { async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value) return // 1. 基础校验
if (isCountingDown.value) return
if (!isPhoneNumberValid.value) { if (!isPhoneNumberValid.value) {
showToast({ message: "请输入有效的手机号" }); showToast({ message: '请输入有效的手机号' })
return return
} }
if (!isInviteCodeValid.value) { if (!isInviteCodeValid.value) {
showToast({ message: "请先输入邀请码" }); showToast({ message: '请先输入邀请码' })
return return
} }
const { data, error } = await useApiFetch('auth/sendSms') // 2. 使用阿里云滑块验证码发送短信
.post({ mobile: phoneNumber.value, actionType: 'agentApply' }) try {
.json() const result = await runWithCaptcha(
// 第一个参数:调用发送短信接口的函数,传入滑块验证参数
(captchaVerifyParam) =>
useApiFetch('auth/sendSms')
.post({
mobile: phoneNumber.value,
actionType: 'agentApply', // 根据业务选择 login 或 agentApply
captchaVerifyParam,
inviteCode: inviteCode.value.trim() // 带上邀请码,后端可能需要校验
})
.json(),
if (data.value && !error.value) { // 第二个参数:滑块验证成功后,处理短信发送结果的回调
if (data.value.code === 200) { (res) => {
showToast({ message: "获取成功" }); if (res.code === 200) {
showToast({ message: '验证码发送成功' })
startCountdown() startCountdown()
// 聚焦到验证码输入框 // 聚焦到验证码输入框
nextTick(() => { nextTick(() => {
const verificationCodeInput = document.getElementById('verificationCode'); const verificationCodeInput = document.getElementById('verificationCode')
if (verificationCodeInput) { if (verificationCodeInput) {
verificationCodeInput.focus(); verificationCodeInput.focus()
} }
}); })
return true // 告诉 runWithCaptcha 验证+发送成功
} else { } else {
showToast(data.value.msg) showToast({ message: res.msg || '验证码发送失败' })
return false // 告诉 runWithCaptcha 失败,不再继续
} }
} }
)
// 如果滑块验证或短信发送失败,直接返回
if (!result) {
return
}
} catch (error) {
console.error('发送验证码失败:', error)
showToast({ message: '发送验证码失败,请重试' })
}
} }
function startCountdown() { function startCountdown() {
@@ -120,29 +144,27 @@ function startCountdown() {
async function handleRegister() { async function handleRegister() {
if (!isPhoneNumberValid.value) { if (!isPhoneNumberValid.value) {
showToast({ message: "请输入有效的手机号" }); showToast({ message: '请输入有效的手机号' })
return return
} }
if (!isInviteCodeValid.value) { if (!isInviteCodeValid.value) {
showToast({ message: "请输入邀请码" }); showToast({ message: '请输入邀请码' })
return return
} }
if (verificationCode.value.length !== 6) { if (verificationCode.value.length !== 6) {
showToast({ message: "请输入有效的验证码" }); showToast({ message: '请输入有效的验证码' })
return return
} }
if (!isAgreed.value) { if (!isAgreed.value) {
showToast({ message: "请先同意用户协议、隐私政策和代理管理协议" }); showToast({ message: '请先同意用户协议、隐私政策和代理管理协议' })
return return
} }
// 直接执行注册逻辑
performRegister() performRegister()
} }
// 执行实际的注册逻辑 // 执行实际的注册逻辑
async function performRegister() { async function performRegister() {
try { try {
// 先尝试通过邀请码注册(同时注册用户和代理)
const { data, error } = await registerByInviteCode({ const { data, error } = await registerByInviteCode({
mobile: phoneNumber.value, mobile: phoneNumber.value,
code: verificationCode.value, code: verificationCode.value,
@@ -166,16 +188,17 @@ async function performRegister() {
}) })
} }
showToast({ message: "注册成功!" }); showToast({ message: '注册成功!' })
// 跳转到代理主页
setTimeout(() => { setTimeout(() => {
window.location.href = '/' window.location.href = '/'
}, 500) }, 500)
} else {
showToast(data.value.msg || '注册失败,请重试')
} }
} }
} catch (err) { } catch (err) {
console.error('注册失败:', err) console.error('注册失败:', err)
showToast({ message: "注册失败,请重试" }); showToast({ message: '注册失败,请重试' })
} }
} }
@@ -190,36 +213,34 @@ async function applyForAgentAsRegisteredUser() {
if (data.value && !error.value) { if (data.value && !error.value) {
if (data.value.code === 200) { if (data.value.code === 200) {
// 保存token
localStorage.setItem('token', data.value.data.accessToken) localStorage.setItem('token', data.value.data.accessToken)
localStorage.setItem('refreshAfter', data.value.data.refreshAfter) localStorage.setItem('refreshAfter', data.value.data.refreshAfter)
localStorage.setItem('accessExpire', data.value.data.accessExpire) localStorage.setItem('accessExpire', data.value.data.accessExpire)
showToast({ message: "申请成功!" }); showToast({ message: '申请成功!' })
// 跳转到代理主页
setTimeout(() => { setTimeout(() => {
window.location.href = '/' window.location.href = '/'
}, 500) }, 500)
} else { } else {
showToast(data.value.msg || "申请失败,请重试") showToast(data.value.msg || '申请失败,请重试')
} }
} }
} catch (err) { } catch (err) {
console.error('申请失败:', err) console.error('申请失败:', err)
showToast({ message: "申请失败,请重试" }); showToast({ message: '申请失败,请重试' })
} }
} }
function toUserAgreement() { function toUserAgreement() {
router.push(`/userAgreement`) router.push('/userAgreement')
} }
function toPrivacyPolicy() { function toPrivacyPolicy() {
router.push(`/privacyPolicy`) router.push('/privacyPolicy')
} }
function toAgentManageAgreement() { function toAgentManageAgreement() {
router.push(`/agentManageAgreement`) router.push('/agentManageAgreement')
} }
onUnmounted(() => { onUnmounted(() => {
@@ -234,7 +255,7 @@ const onClickLeft = () => {
</script> </script>
<template> <template>
<div class="login-layout "> <div class="login-layout">
<van-nav-bar fixed placeholder title="注册成为代理" left-text="" left-arrow @click-left="onClickLeft" /> <van-nav-bar fixed placeholder title="注册成为代理" left-text="" left-arrow @click-left="onClickLeft" />
<div class="login px-4 relative z-10"> <div class="login px-4 relative z-10">
<div class="mb-8 pt-20 text-left"> <div class="mb-8 pt-20 text-left">
@@ -309,6 +330,7 @@ const onClickLeft = () => {
</template> </template>
<style scoped> <style scoped>
/* 样式保持不变,和之前一样 */
.login-layout { .login-layout {
background-image: url('@/assets/images/login_bg.png'); background-image: url('@/assets/images/login_bg.png');
background-position: center; background-position: center;
@@ -319,7 +341,6 @@ const onClickLeft = () => {
overflow: hidden; overflow: hidden;
} }
/* 登录表单 */
.login-form { .login-form {
background-color: var(--color-bg-primary); background-color: var(--color-bg-primary);
padding: 2rem; padding: 2rem;
@@ -328,7 +349,6 @@ const onClickLeft = () => {
border-radius: 8px; border-radius: 8px;
} }
/* 表单项 */
.form-item { .form-item {
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
display: flex; display: flex;
@@ -337,7 +357,6 @@ const onClickLeft = () => {
border-bottom: 1px solid var(--color-border-primary); border-bottom: 1px solid var(--color-border-primary);
} }
/* 输入框和按钮组合 */
.input-with-btn { .input-with-btn {
position: relative; position: relative;
display: flex; display: flex;
@@ -374,7 +393,6 @@ const onClickLeft = () => {
.form-input { .form-input {
width: 100%; width: 100%;
padding: 0.875rem 0; padding: 0.875rem 0;
font-size: 0.9375rem; font-size: 0.9375rem;
color: var(--color-text-primary); color: var(--color-text-primary);
outline: none; outline: none;
@@ -395,7 +413,6 @@ const onClickLeft = () => {
background-color: transparent; background-color: transparent;
} }
/* 验证码输入 */
.verification-input-wrapper { .verification-input-wrapper {
position: relative; position: relative;
display: flex; display: flex;
@@ -425,7 +442,6 @@ const onClickLeft = () => {
cursor: not-allowed; cursor: not-allowed;
} }
/* 协议同意 */
.agreement-wrapper { .agreement-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -450,7 +466,6 @@ const onClickLeft = () => {
text-decoration: none; text-decoration: none;
} }
/* 提示文字 */
.notice-text { .notice-text {
font-size: 0.6875rem; font-size: 0.6875rem;
color: var(--color-text-tertiary); color: var(--color-text-tertiary);
@@ -458,7 +473,6 @@ const onClickLeft = () => {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
/* 登录按钮 */
.login-btn { .login-btn {
width: 100%; width: 100%;
padding: 0.875rem; padding: 0.875rem;