Files
jurong_circle_frontdesk/src/views/Register.vue

1093 lines
25 KiB
Vue
Raw Normal View History

2025-07-26 15:35:53 +08:00
<template>
<div class="register-page">
<div class="register-container">
<div class="register-card">
<div class="register-header">
<h2>用户注册</h2>
2025-09-08 09:43:39 +08:00
<p>创建你的账号开始使用炬融圈</p>
2025-07-26 15:35:53 +08:00
</div>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<el-form
ref="registerFormRef"
:model="registerForm"
:rules="registerRules"
class="register-form"
@submit.prevent="handleRegister"
>
<!-- <el-form-item prop="username">-->
<!-- <el-input-->
<!-- v-model="registerForm.username"-->
<!-- placeholder="请输入用户名"-->
<!-- size="large"-->
<!-- :prefix-icon="User"-->
<!-- clearable-->
<!-- />-->
<!-- </el-form-item>-->
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<el-form-item prop="phone">
<el-input
v-model="registerForm.phone"
placeholder="请输入手机号"
size="large"
:prefix-icon="Message"
clearable
/>
</el-form-item>
2025-09-08 09:43:39 +08:00
2025-07-31 13:54:37 +08:00
<el-form-item prop="smsCode">
2025-07-26 15:35:53 +08:00
<div class="sms-code-group">
<el-input
v-model="registerForm.smsCode"
placeholder="请输入短信验证码"
size="large"
:prefix-icon="ChatDotRound"
clearable
class="sms-input"
/>
<el-button
type="primary"
size="large"
:disabled="!canSendSMS || smsCountdown > 0"
:loading="sendingSMS"
@click="sendSMSCode"
class="sms-button"
>
{{ smsCountdown > 0 ? `${smsCountdown}s后重发` : '发送验证码' }}
</el-button>
</div>
2025-07-31 13:54:37 +08:00
</el-form-item>
2025-09-03 09:13:29 +08:00
2025-09-08 09:43:39 +08:00
<el-form-item prop="region">
<el-cascader
v-model="registerForm.region"
:options="regionOptions"
placeholder="请选择省市区"
2025-08-04 12:32:53 +08:00
size="large"
style="width: 100%"
2025-09-08 09:43:39 +08:00
:props="{ expandTrigger: 'hover', value: 'code' }"
:show-all-levels="true"
:collapse-tags="true"
:max-collapse-tags="1"
:teleported="false"
popper-class="mobile-cascader-popper"
@change="handleRegionChange"
2025-08-04 12:32:53 +08:00
>
<template #prefix>
<el-icon><Location /></el-icon>
</template>
2025-09-08 09:43:39 +08:00
</el-cascader>
2025-08-04 12:32:53 +08:00
</el-form-item>
2025-07-26 15:35:53 +08:00
<el-form-item prop="password">
<el-input
v-model="registerForm.password"
type="password"
placeholder="请输入密码"
size="large"
:prefix-icon="Lock"
show-password
clearable
/>
</el-form-item>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<el-form-item prop="confirmPassword">
<el-input
v-model="registerForm.confirmPassword"
type="password"
placeholder="请确认密码"
size="large"
:prefix-icon="Lock"
show-password
clearable
/>
</el-form-item>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<el-form-item prop="captcha">
<Captcha
ref="captchaRef"
v-model="registerForm.captcha"
placeholder="请输入验证码"
size="large"
/>
</el-form-item>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<el-form-item prop="agreement">
2025-09-08 09:43:39 +08:00
<el-checkbox
2025-09-04 10:27:03 +08:00
v-model="registerForm.agreement"
2025-09-08 16:12:56 +08:00
@click.prevent="showAgreementDialog"
:disabled="true"
2025-09-04 10:27:03 +08:00
>
2025-09-08 16:12:56 +08:00
<span @click="showAgreementDialog" class="agreement-text">
我已阅读并同意用户协议隐私政策
</span>
2025-07-26 15:35:53 +08:00
</el-checkbox>
2025-09-08 16:12:56 +08:00
<div class="agreement-hint">
2025-09-04 10:27:03 +08:00
<el-icon><InfoFilled /></el-icon>
2025-09-08 16:12:56 +08:00
<span>请点击查看用户协议和隐私政策</span>
2025-09-04 10:27:03 +08:00
</div>
2025-07-26 15:35:53 +08:00
</el-form-item>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<el-form-item>
<el-button
type="primary"
size="large"
class="register-button"
:loading="userStore.loading"
@click="handleRegister"
>
{{ userStore.loading ? '注册中...' : '立即注册' }}
</el-button>
</el-form-item>
</el-form>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<div class="register-footer">
<p>
已有账号
2025-07-31 13:54:37 +08:00
<el-link type="primary" @click="$router.push('/mylogin')">
2025-07-26 15:35:53 +08:00
立即登录
</el-link>
</p>
</div>
</div>
</div>
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
<!-- 背景装饰 -->
<div class="background-decoration">
<div class="decoration-shape shape-1"></div>
<div class="decoration-shape shape-2"></div>
<div class="decoration-shape shape-3"></div>
<div class="decoration-shape shape-4"></div>
</div>
2025-09-08 16:12:56 +08:00
<!-- 协议弹窗 -->
<el-dialog
v-model="showAgreementDialogVisible"
title="用户协议和隐私政策"
width="90%"
:style="{ maxWidth: '600px' }"
:show-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
center
class="agreement-dialog"
position="bottom"
>
<div class="agreement-content">
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
<el-tab-pane name="agreement">
<template #label>
2025-09-08 16:48:17 +08:00
<span class="tab-label">用户协议</span>
2025-09-08 16:12:56 +08:00
<el-icon v-if="agreementViewed" class="check-icon"><Check /></el-icon>
</template>
<div class="agreement-text-content" ref="agreementContent">
<h3>用户协议</h3>
<p>1. 用户应当遵守法律法规不得发布违法违规内容</p>
<p>2. 用户对自己发布的内容承担全部责任</p>
<p>3. 平台有权对违规内容进行删除或限制</p>
<p>4. 用户应当保护好自己的账号安全</p>
<p>5. 平台保留修改本协议的权利</p>
</div>
</el-tab-pane>
<el-tab-pane name="privacy">
<template #label>
2025-09-08 16:48:17 +08:00
<span class="tab-label">隐私政策</span>
2025-09-08 16:12:56 +08:00
<el-icon v-if="privacyViewed" class="check-icon"><Check /></el-icon>
</template>
<div class="privacy-text-content" ref="privacyContent">
<h3>隐私政策</h3>
<p>1. 我们重视用户隐私保护</p>
<p>2. 我们只收集必要的用户信息</p>
<p>3. 用户信息仅用于提供服务</p>
<p>4. 我们不会向第三方泄露用户信息</p>
<p>5. 用户有权查看修改或删除个人信息</p>
</div>
</el-tab-pane>
</el-tabs>
</div>
<template #footer>
<div class="dialog-footer">
<div class="read-status">
</div>
<div class="dialog-buttons">
<el-button @click="closeAgreementDialog">取消</el-button>
<el-button type="primary" @click="confirmAgreement" :disabled="!(agreementViewed && privacyViewed)">确认已阅读</el-button>
</div>
</div>
</template>
</el-dialog>
2025-07-26 15:35:53 +08:00
</div>
</template>
<script setup>
2025-09-08 09:43:39 +08:00
import { ref, reactive, computed, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useUserStore } from '@/stores/user';
import { ElMessage, ElMessageBox } from 'element-plus';
import {
User,
Lock,
Message,
Edit,
ChatDotRound,
CreditCard,
Location,
InfoFilled,
2025-09-08 16:12:56 +08:00
Check,
2025-09-08 09:43:39 +08:00
} from '@element-plus/icons-vue';
import Captcha from '@/components/Captcha.vue';
import api from '@/utils/api'
const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
2025-07-26 15:35:53 +08:00
// 表单引用
2025-09-08 09:43:39 +08:00
const registerFormRef = ref();
const captchaRef = ref();
2025-07-26 15:35:53 +08:00
// 表单数据
const registerForm = reactive({
username: '',
phone: '',
2025-09-08 09:43:39 +08:00
region: [],
province: '',
2025-08-04 12:32:53 +08:00
city: '',
2025-09-08 09:43:39 +08:00
district: '',
2025-08-04 12:32:53 +08:00
district_id: '',
2025-07-26 15:35:53 +08:00
password: '',
confirmPassword: '',
captcha: '',
2025-07-31 13:54:37 +08:00
smsCode: '',
2025-09-08 09:43:39 +08:00
agreement: false,
});
2025-07-26 15:35:53 +08:00
2025-09-04 10:27:03 +08:00
// 协议查看状态
2025-09-08 09:43:39 +08:00
const agreementViewed = ref(false);
const privacyViewed = ref(false);
2025-09-08 16:12:56 +08:00
// 协议弹窗相关状态
const showAgreementDialogVisible = ref(false);
const activeTab = ref('agreement');
const agreementContent = ref();
const privacyContent = ref();
2025-09-04 10:27:03 +08:00
2025-07-31 13:54:37 +08:00
// 短信验证码相关状态
2025-09-08 09:43:39 +08:00
const sendingSMS = ref(false);
const smsCountdown = ref(0);
2025-07-31 13:54:37 +08:00
const canSendSMS = computed(() => {
2025-09-08 09:43:39 +08:00
const phoneRegex = /^1[3-9]\d{9}$/;
return phoneRegex.test(registerForm.phone);
});
2025-07-26 15:35:53 +08:00
2025-08-04 12:32:53 +08:00
// 地区数据
2025-09-08 09:43:39 +08:00
const regions = ref([]);
const regionOptions = ref([]);
2025-08-04 12:32:53 +08:00
const cities = computed(() => {
2025-09-08 09:43:39 +08:00
const citySet = new Set();
regions.value.forEach((region) => {
2025-08-04 12:32:53 +08:00
if (region.city_name) {
2025-09-08 09:43:39 +08:00
citySet.add(region.city_name);
2025-08-04 12:32:53 +08:00
}
2025-09-08 09:43:39 +08:00
});
return Array.from(citySet).sort();
});
2025-08-04 12:32:53 +08:00
const filteredDistricts = computed(() => {
2025-09-08 09:43:39 +08:00
if (!registerForm.city) return [];
return regions.value.filter(
(region) => region.city_name === registerForm.city
);
});
2025-08-04 12:32:53 +08:00
2025-07-26 15:35:53 +08:00
// 自定义验证函数
const validateUsername = (rule, value, callback) => {
if (!value) {
2025-09-08 09:43:39 +08:00
callback(new Error('请输入用户名'));
2025-07-26 15:35:53 +08:00
} else if (value.length < 3) {
2025-09-08 09:43:39 +08:00
callback(new Error('用户名至少3个字符'));
2025-07-26 15:35:53 +08:00
} else if (value.length > 20) {
2025-09-08 09:43:39 +08:00
callback(new Error('用户名不能超过20个字符'));
2025-07-26 15:35:53 +08:00
} else if (!/^[a-zA-Z0-9_\u4e00-\u9fa5]+$/.test(value)) {
2025-09-08 09:43:39 +08:00
callback(new Error('用户名只能包含字母、数字、下划线和中文'));
2025-07-26 15:35:53 +08:00
} else {
2025-09-08 09:43:39 +08:00
callback();
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
const validatePassword = (rule, value, callback) => {
if (!value) {
2025-09-08 09:43:39 +08:00
callback(new Error('请输入密码'));
2025-07-26 15:35:53 +08:00
} else if (value.length < 6) {
2025-09-08 09:43:39 +08:00
callback(new Error('密码至少6个字符'));
2025-07-26 15:35:53 +08:00
} else if (value.length > 20) {
2025-09-08 09:43:39 +08:00
callback(new Error('密码不能超过20个字符'));
2025-07-26 15:35:53 +08:00
} else if (!/(?=.*[a-zA-Z])(?=.*\d)/.test(value)) {
2025-09-08 09:43:39 +08:00
callback(new Error('密码必须包含字母和数字'));
2025-07-26 15:35:53 +08:00
} else {
// 如果确认密码已输入,重新验证确认密码
if (registerForm.confirmPassword) {
2025-09-08 09:43:39 +08:00
registerFormRef.value?.validateField('confirmPassword');
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
callback();
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
const validateConfirmPassword = (rule, value, callback) => {
if (!value) {
2025-09-08 09:43:39 +08:00
callback(new Error('请确认密码'));
2025-07-26 15:35:53 +08:00
} else if (value !== registerForm.password) {
2025-09-08 09:43:39 +08:00
callback(new Error('两次输入的密码不一致'));
2025-07-26 15:35:53 +08:00
} else {
2025-09-08 09:43:39 +08:00
callback();
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
const validateAgreement = (rule, value, callback) => {
if (!value) {
2025-09-08 09:43:39 +08:00
callback(new Error('请阅读并同意用户协议和隐私政策'));
2025-07-26 15:35:53 +08:00
} else {
2025-09-08 09:43:39 +08:00
callback();
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
// 表单验证规则
const registerRules = {
username: [{ validator: validateUsername, trigger: 'blur' }],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
2025-09-08 09:43:39 +08:00
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号',
trigger: 'blur',
},
2025-07-26 15:35:53 +08:00
],
2025-09-03 09:13:29 +08:00
2025-07-31 13:54:37 +08:00
smsCode: [
{ required: true, message: '请输入短信验证码', trigger: 'blur' },
2025-09-08 09:43:39 +08:00
{ pattern: /^\d{6}$/, message: '短信验证码为6位数字', trigger: 'blur' },
2025-08-04 12:32:53 +08:00
],
2025-09-08 09:43:39 +08:00
region: [{ required: true, message: '请选择省市区', trigger: 'change' }],
2025-07-26 15:35:53 +08:00
password: [{ validator: validatePassword, trigger: 'blur' }],
confirmPassword: [{ validator: validateConfirmPassword, trigger: 'blur' }],
captcha: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
2025-09-08 09:43:39 +08:00
{ min: 4, max: 4, message: '验证码长度为4位', trigger: 'blur' },
2025-07-26 15:35:53 +08:00
],
2025-09-08 09:43:39 +08:00
agreement: [{ validator: validateAgreement, trigger: 'change' }],
};
2025-07-26 15:35:53 +08:00
2025-07-31 13:54:37 +08:00
// 发送短信验证码
const sendSMSCode = async () => {
if (!canSendSMS.value || sendingSMS.value || smsCountdown.value > 0) {
2025-09-08 09:43:39 +08:00
return;
2025-07-31 13:54:37 +08:00
}
2025-09-08 09:43:39 +08:00
2025-07-31 13:54:37 +08:00
try {
2025-09-08 09:43:39 +08:00
sendingSMS.value = true;
2025-07-31 13:54:37 +08:00
const response = await fetch('/api/sms/send', {
method: 'POST',
headers: {
2025-09-08 09:43:39 +08:00
'Content-Type': 'application/json',
2025-07-31 13:54:37 +08:00
},
body: JSON.stringify({
2025-09-08 09:43:39 +08:00
phone: registerForm.phone,
}),
});
const result = await response.json();
2025-07-31 13:54:37 +08:00
if (result.success) {
2025-09-08 09:43:39 +08:00
ElMessage.success('验证码发送成功,请查收短信');
2025-07-31 13:54:37 +08:00
// 开始倒计时
2025-09-08 09:43:39 +08:00
startCountdown();
2025-07-31 13:54:37 +08:00
} else {
2025-09-08 09:43:39 +08:00
ElMessage.error(result.message || '发送失败,请重试');
2025-07-31 13:54:37 +08:00
}
} catch (error) {
2025-09-08 09:43:39 +08:00
console.error('发送短信验证码失败:', error);
ElMessage.error('发送失败,请检查网络连接');
2025-07-31 13:54:37 +08:00
} finally {
2025-09-08 09:43:39 +08:00
sendingSMS.value = false;
2025-07-31 13:54:37 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
2025-07-31 13:54:37 +08:00
// 开始倒计时
const startCountdown = () => {
2025-09-08 09:43:39 +08:00
smsCountdown.value = 60;
2025-07-31 13:54:37 +08:00
const timer = setInterval(() => {
2025-09-08 09:43:39 +08:00
smsCountdown.value--;
2025-07-31 13:54:37 +08:00
if (smsCountdown.value <= 0) {
2025-09-08 09:43:39 +08:00
clearInterval(timer);
2025-07-31 13:54:37 +08:00
}
2025-09-08 09:43:39 +08:00
}, 1000);
};
2025-07-26 15:35:53 +08:00
2025-09-08 09:43:39 +08:00
// 加载省市区级联数据
const loadRegionOptions = async () => {
const provincesResponse = await api.get('/regions/provinces');
regionOptions.value = provincesResponse.data.data || [];
};
// 处理级联选择器变化
const handleRegionChange = (value) => {
if (value && value.length === 3) {
registerForm.province = value[0];
registerForm.city = value[1];
registerForm.district = value[2];
registerForm.district_id = value[2];
}
};
2025-08-04 12:32:53 +08:00
/**
* 城市变化处理
*/
const onCityChange = () => {
// 清空区域选择
2025-09-08 09:43:39 +08:00
registerForm.district_id = '';
};
2025-08-04 12:32:53 +08:00
2025-07-26 15:35:53 +08:00
// 处理注册
const handleRegister = async () => {
2025-09-08 09:43:39 +08:00
if (!registerFormRef.value || !captchaRef.value) return;
2025-07-26 15:35:53 +08:00
try {
// 先验证表单
2025-09-08 09:43:39 +08:00
const valid = await registerFormRef.value.validate();
if (!valid) return;
2025-07-26 15:35:53 +08:00
// 验证验证码
2025-09-08 09:43:39 +08:00
const captchaValid = await captchaRef.value.verifyCaptcha(
registerForm.captcha
);
2025-07-26 15:35:53 +08:00
if (!captchaValid) {
2025-09-08 09:43:39 +08:00
registerForm.captcha = '';
return;
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
// 获取验证码信息
2025-09-08 09:43:39 +08:00
const captchaInfo = captchaRef.value.getCaptchaInfo();
2025-07-26 15:35:53 +08:00
// 提交注册请求(包含验证码信息)
const registerData = {
username: registerForm.phone,
2025-07-26 15:35:53 +08:00
phone: registerForm.phone,
2025-08-04 12:32:53 +08:00
city: registerForm.city,
district_id: registerForm.district_id,
2025-07-26 15:35:53 +08:00
password: registerForm.password,
2025-07-31 13:54:37 +08:00
smsCode: registerForm.smsCode,
2025-07-26 15:35:53 +08:00
captchaId: captchaInfo.captchaId,
2025-09-08 09:43:39 +08:00
captchaText: captchaInfo.captchaText,
province: registerForm.province,
inviter: registerForm.inviter,
2025-09-08 09:43:39 +08:00
};
console.log(registerData,'registerData');
console.log(registerForm,'registerForm')
const result = await userStore.register(registerData);
2025-07-26 15:35:53 +08:00
if (result.success) {
2025-09-03 09:13:29 +08:00
// 检查是否需要支付
if (result.needPayment) {
2025-09-08 09:43:39 +08:00
ElMessage.success('用户信息创建成功,请完成支付以激活账户');
2025-09-03 09:13:29 +08:00
// 跳转到支付页面
router.push({
path: '/payment',
2025-09-08 09:43:39 +08:00
});
2025-09-03 09:13:29 +08:00
} else {
2025-09-08 09:43:39 +08:00
ElMessage.success('注册成功!请登录');
2025-09-08 09:58:23 +08:00
router.push('/mylogin');
2025-09-03 09:13:29 +08:00
}
2025-07-26 15:35:53 +08:00
}
} catch (error) {
2025-09-08 09:43:39 +08:00
console.error('注册失败:', error);
2025-07-26 15:35:53 +08:00
// 注册失败后刷新验证码
if (captchaRef.value) {
2025-09-08 09:43:39 +08:00
await captchaRef.value.refreshCaptcha();
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
registerForm.captcha = '';
2025-07-26 15:35:53 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
2025-09-08 16:12:56 +08:00
// 显示协议弹窗
const showAgreementDialog = () => {
showAgreementDialogVisible.value = true;
activeTab.value = 'agreement';
// 弹窗打开时默认用户协议已读
agreementViewed.value = true;
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
2025-09-08 16:12:56 +08:00
// 关闭协议弹窗
const closeAgreementDialog = () => {
showAgreementDialogVisible.value = false;
};
// 确认已阅读协议
const confirmAgreement = () => {
if (agreementViewed.value && privacyViewed.value) {
registerForm.agreement = true;
showAgreementDialogVisible.value = false;
ElMessage.success('已确认阅读用户协议和隐私政策');
} else {
ElMessage.warning('请先查看用户协议和隐私政策');
}
2025-09-08 09:43:39 +08:00
};
2025-09-04 10:27:03 +08:00
2025-09-08 16:12:56 +08:00
// 处理选项卡切换
const handleTabChange = (tabName) => {
if (tabName === 'agreement') {
// 切换到用户协议时,标记为已读
agreementViewed.value = true;
} else if (tabName === 'privacy') {
// 切换到隐私政策时,标记为已读
privacyViewed.value = true;
2025-09-04 10:27:03 +08:00
}
2025-09-08 09:43:39 +08:00
};
2025-07-26 15:35:53 +08:00
2025-09-08 16:12:56 +08:00
2025-07-26 15:35:53 +08:00
// 图片上传成功处理
const handleUploadSuccess = (response, field) => {
2025-09-08 09:43:39 +08:00
ElMessage.success('图片上传成功');
};
2025-07-26 15:35:53 +08:00
// 图片上传失败处理
const handleUploadError = (error) => {
2025-09-08 09:43:39 +08:00
ElMessage.error('图片上传失败,请重试');
};
2025-07-26 15:35:53 +08:00
2025-09-08 10:58:06 +08:00
const invite = async () => {
const inviter = route.query.inviter;
console.log('邀请人ID:', inviter);
if (inviter) {
registerForm.inviter = inviter;
2025-09-08 10:58:06 +08:00
// try {
// const response = await api.get(`/user/${inviter}`);
// if (response.data.success) {
// registerForm.inviter = inviter;
// }
// } catch (error) {
// console.error('获取邀请人信息失败:', error);
// }
}
}
2025-07-26 15:35:53 +08:00
// 组件挂载时的处理
onMounted(() => {
// 如果已经登录,直接跳转
if (userStore.isAuthenticated) {
2025-09-08 09:43:39 +08:00
const redirectPath = route.query.redirect || '/';
router.push(redirectPath);
2025-07-26 15:35:53 +08:00
}
2025-09-03 09:13:29 +08:00
2025-09-08 09:43:39 +08:00
// 加载省市区级联数据
loadRegionOptions();
2025-09-08 10:58:06 +08:00
invite();
2025-09-08 09:43:39 +08:00
});
2025-07-26 15:35:53 +08:00
</script>
<style lang="scss" scoped>
.register-page {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
.register-container {
width: 100%;
max-width: 450px;
padding: 20px;
position: relative;
z-index: 10;
}
.register-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 40px 30px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.register-header {
text-align: center;
margin-bottom: 30px;
}
.register-header h2 {
color: #303133;
margin-bottom: 8px;
font-weight: 600;
}
.register-header p {
color: #909399;
font-size: 14px;
}
.register-form {
margin-bottom: 20px;
}
.register-button {
width: 100%;
height: 44px;
font-size: 16px;
font-weight: 600;
}
.register-footer {
text-align: center;
margin-bottom: 20px;
}
.register-footer p {
color: #606266;
font-size: 14px;
}
.document-upload-section {
margin: 20px 0;
}
.document-upload-section .el-divider {
margin: 20px 0;
}
.document-upload-section .el-form-item {
margin-bottom: 20px;
}
.document-upload-section .el-form-item .el-form-item__label {
font-weight: 500;
color: #606266;
}
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
.feature-item {
display: flex;
align-items: center;
gap: 8px;
color: #606266;
font-size: 14px;
}
.feature-item .el-icon {
color: #409eff;
font-size: 16px;
}
.upload-preview {
margin-top: 10px;
}
.upload-preview img {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 8px;
border: 1px solid #dcdfe6;
}
.upload-demo {
margin-bottom: 10px;
}
.background-decoration {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.decoration-shape {
position: absolute;
background: rgba(255, 255, 255, 0.1);
animation: float 8s ease-in-out infinite;
}
.shape-1 {
width: 100px;
height: 100px;
border-radius: 50%;
top: 15%;
left: 15%;
animation-delay: 0s;
}
.shape-2 {
width: 80px;
height: 80px;
border-radius: 20px;
top: 70%;
right: 20%;
animation-delay: 2s;
}
.shape-3 {
width: 60px;
height: 60px;
border-radius: 50%;
bottom: 25%;
left: 25%;
animation-delay: 4s;
}
.shape-4 {
width: 120px;
height: 40px;
border-radius: 20px;
top: 40%;
right: 10%;
animation-delay: 6s;
}
@keyframes float {
2025-09-08 09:43:39 +08:00
0%,
100% {
2025-07-26 15:35:53 +08:00
transform: translateY(0px) rotate(0deg);
opacity: 0.7;
}
50% {
transform: translateY(-15px) rotate(180deg);
opacity: 1;
}
}
/* 响应式设计 */
@media (max-width: 480px) {
.register-container {
padding: 15px;
}
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
.register-card {
padding: 30px 20px;
}
2025-09-08 09:43:39 +08:00
2025-07-26 15:35:53 +08:00
.features-list {
gap: 8px;
}
}
/* Element Plus 组件样式覆盖 */
:deep(.el-input__wrapper) {
border-radius: 8px;
}
:deep(.el-button) {
border-radius: 8px;
}
:deep(.el-divider__text) {
background-color: rgba(255, 255, 255, 0.95);
color: #909399;
}
:deep(.el-checkbox__label) {
font-size: 14px;
color: #606266;
}
/* 输入框聚焦效果 */
:deep(.el-input__wrapper:hover),
:deep(.el-input__wrapper.is-focus) {
box-shadow: 0 0 0 1px #409eff inset;
}
/* 加载状态样式 */
.register-button.is-loading {
pointer-events: none;
}
/* 动画效果 */
.register-card {
animation: slideInUp 0.6s ease-out;
}
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 错误状态样式 */
:deep(.el-form-item.is-error .el-input__wrapper) {
box-shadow: 0 0 0 1px #f56c6c inset;
}
/* 成功状态样式 */
:deep(.el-form-item.is-success .el-input__wrapper) {
box-shadow: 0 0 0 1px #67c23a inset;
}
/* 协议对话框样式 */
:deep(.agreement-dialog),
:deep(.privacy-dialog) {
.el-message-box__content {
max-height: 400px;
overflow-y: auto;
}
}
/* 密码强度指示器 */
.password-strength {
margin-top: 5px;
font-size: 12px;
}
.strength-weak {
color: #f56c6c;
}
.strength-medium {
color: #e6a23c;
}
.strength-strong {
color: #67c23a;
}
/* 短信验证码样式 */
.sms-code-group {
display: flex;
gap: 12px;
align-items: center;
}
.sms-input {
flex: 1;
}
.sms-button {
flex-shrink: 0;
min-width: 120px;
height: 40px;
}
.sms-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
2025-09-04 10:27:03 +08:00
/* 协议相关样式 */
.agreement-hint {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
font-size: 12px;
color: #e6a23c;
}
.agreement-hint .el-icon {
font-size: 14px;
}
2025-09-08 16:12:56 +08:00
.agreement-text {
color: #409eff;
cursor: pointer;
text-decoration: underline;
}
.agreement-text:hover {
color: #66b1ff;
}
/* 协议弹窗样式 */
:deep(.agreement-dialog) {
.el-dialog__body {
padding: 20px;
max-height: 60vh;
overflow: hidden;
}
}
.agreement-content {
height: 100%;
}
2025-09-08 16:48:17 +08:00
:deep(.agreement-dialog .el-tabs__nav) {
display: flex;
width: 100%;
}
:deep(.agreement-dialog .el-tabs__item) {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
}
.tab-label {
margin-right: 4px;
}
2025-09-08 16:12:56 +08:00
.agreement-text-content,
.privacy-text-content {
max-height: 400px;
overflow-y: auto;
padding: 20px;
line-height: 1.6;
font-size: 14px;
color: #606266;
border: 1px solid #e4e7ed;
border-radius: 8px;
background-color: #fafafa;
}
.agreement-text-content h3,
.privacy-text-content h3 {
color: #303133;
margin-bottom: 16px;
font-size: 18px;
font-weight: 600;
}
.agreement-text-content p,
.privacy-text-content p {
margin-bottom: 12px;
}
.agreement-text-content strong,
.privacy-text-content strong {
color: #409eff;
font-weight: 600;
}
.dialog-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
}
.check-icon {
color: #67c23a;
font-size: 16px;
}
/* 滚动条样式 */
.agreement-text-content::-webkit-scrollbar,
.privacy-text-content::-webkit-scrollbar {
width: 6px;
}
.agreement-text-content::-webkit-scrollbar-track,
.privacy-text-content::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.agreement-text-content::-webkit-scrollbar-thumb,
.privacy-text-content::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
}
.agreement-text-content::-webkit-scrollbar-thumb:hover,
.privacy-text-content::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
2025-09-04 10:27:03 +08:00
}
:deep(.el-link.viewed:hover) {
color: #529b2e !important;
}
/* 移动端级联选择器样式优化 */
:deep(.mobile-cascader-popper) {
max-width: calc(100vw - 20px) !important;
left: 10px !important;
right: 10px !important;
transform: none !important;
margin: 0 !important;
}
:deep(.mobile-cascader-popper .el-cascader-panel) {
max-width: 100% !important;
overflow-x: hidden !important;
display: flex !important;
gap: 0 !important;
padding: 0 !important;
}
:deep(.mobile-cascader-popper .el-cascader-menu) {
min-width: 33.33% !important;
max-width: 33.33% !important;
width: 33.33% !important;
flex-shrink: 0 !important;
flex-grow: 0 !important;
overflow-x: hidden !important;
border-right: 1px solid #e4e7ed;
margin: 0 !important;
}
:deep(.mobile-cascader-popper .el-cascader-menu:last-child) {
border-right: none !important;
}
:deep(.mobile-cascader-popper .el-cascader-menu__item) {
padding: 6px 3px !important;
line-height: 1.1 !important;
font-size: 12px !important;
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
margin: 0 !important;
box-sizing: border-box !important;
}
@media (max-width: 480px) {
:deep(.mobile-cascader-popper) {
max-width: calc(100vw - 16px) !important;
left: 8px !important;
right: 8px !important;
}
:deep(.mobile-cascader-popper .el-cascader-menu__item) {
padding: 5px 2px !important;
font-size: 11px !important;
}
}
@media (max-width: 360px) {
:deep(.mobile-cascader-popper) {
max-width: calc(100vw - 12px) !important;
left: 6px !important;
right: 6px !important;
}
:deep(.mobile-cascader-popper .el-cascader-menu__item) {
padding: 4px 1px !important;
font-size: 10px !important;
line-height: 1.0 !important;
}
}
@media (max-width: 320px) {
:deep(.mobile-cascader-popper .el-cascader-menu__item) {
padding: 3px 1px !important;
font-size: 9px !important;
}
}
2025-09-08 09:43:39 +08:00
</style>