1093 lines
25 KiB
Vue
1093 lines
25 KiB
Vue
<template>
|
||
<div class="register-page">
|
||
<div class="register-container">
|
||
<div class="register-card">
|
||
<div class="register-header">
|
||
<h2>用户注册</h2>
|
||
<p>创建你的账号,开始使用炬融圈</p>
|
||
</div>
|
||
|
||
<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>-->
|
||
|
||
<el-form-item prop="phone">
|
||
<el-input
|
||
v-model="registerForm.phone"
|
||
placeholder="请输入手机号"
|
||
size="large"
|
||
:prefix-icon="Message"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
|
||
<el-form-item prop="smsCode">
|
||
<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>
|
||
</el-form-item>
|
||
|
||
<el-form-item prop="region">
|
||
<el-cascader
|
||
v-model="registerForm.region"
|
||
:options="regionOptions"
|
||
placeholder="请选择省市区"
|
||
size="large"
|
||
style="width: 100%"
|
||
: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"
|
||
>
|
||
<template #prefix>
|
||
<el-icon><Location /></el-icon>
|
||
</template>
|
||
</el-cascader>
|
||
</el-form-item>
|
||
|
||
<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>
|
||
|
||
<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>
|
||
|
||
<el-form-item prop="captcha">
|
||
<Captcha
|
||
ref="captchaRef"
|
||
v-model="registerForm.captcha"
|
||
placeholder="请输入验证码"
|
||
size="large"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<el-form-item prop="agreement">
|
||
<el-checkbox
|
||
v-model="registerForm.agreement"
|
||
@click.prevent="showAgreementDialog"
|
||
:disabled="true"
|
||
>
|
||
<span @click="showAgreementDialog" class="agreement-text">
|
||
我已阅读并同意《用户协议》和《隐私政策》
|
||
</span>
|
||
</el-checkbox>
|
||
<div class="agreement-hint">
|
||
<el-icon><InfoFilled /></el-icon>
|
||
<span>请点击查看用户协议和隐私政策</span>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<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>
|
||
|
||
<div class="register-footer">
|
||
<p>
|
||
已有账号?
|
||
<el-link type="primary" @click="$router.push('/mylogin')">
|
||
立即登录
|
||
</el-link>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 背景装饰 -->
|
||
<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>
|
||
|
||
<!-- 协议弹窗 -->
|
||
<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>
|
||
<span class="tab-label">用户协议</span>
|
||
<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>
|
||
<span class="tab-label">隐私政策</span>
|
||
<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>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
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,
|
||
Check,
|
||
} 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();
|
||
|
||
// 表单引用
|
||
const registerFormRef = ref();
|
||
const captchaRef = ref();
|
||
|
||
// 表单数据
|
||
const registerForm = reactive({
|
||
username: '',
|
||
phone: '',
|
||
region: [],
|
||
province: '',
|
||
city: '',
|
||
district: '',
|
||
district_id: '',
|
||
password: '',
|
||
confirmPassword: '',
|
||
captcha: '',
|
||
smsCode: '',
|
||
agreement: false,
|
||
});
|
||
|
||
// 协议查看状态
|
||
const agreementViewed = ref(false);
|
||
const privacyViewed = ref(false);
|
||
|
||
// 协议弹窗相关状态
|
||
const showAgreementDialogVisible = ref(false);
|
||
const activeTab = ref('agreement');
|
||
const agreementContent = ref();
|
||
const privacyContent = ref();
|
||
|
||
// 短信验证码相关状态
|
||
const sendingSMS = ref(false);
|
||
const smsCountdown = ref(0);
|
||
const canSendSMS = computed(() => {
|
||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||
return phoneRegex.test(registerForm.phone);
|
||
});
|
||
|
||
// 地区数据
|
||
const regions = ref([]);
|
||
const regionOptions = ref([]);
|
||
const cities = computed(() => {
|
||
const citySet = new Set();
|
||
regions.value.forEach((region) => {
|
||
if (region.city_name) {
|
||
citySet.add(region.city_name);
|
||
}
|
||
});
|
||
return Array.from(citySet).sort();
|
||
});
|
||
|
||
const filteredDistricts = computed(() => {
|
||
if (!registerForm.city) return [];
|
||
return regions.value.filter(
|
||
(region) => region.city_name === registerForm.city
|
||
);
|
||
});
|
||
|
||
// 自定义验证函数
|
||
const validateUsername = (rule, value, callback) => {
|
||
if (!value) {
|
||
callback(new Error('请输入用户名'));
|
||
} else if (value.length < 3) {
|
||
callback(new Error('用户名至少3个字符'));
|
||
} else if (value.length > 20) {
|
||
callback(new Error('用户名不能超过20个字符'));
|
||
} else if (!/^[a-zA-Z0-9_\u4e00-\u9fa5]+$/.test(value)) {
|
||
callback(new Error('用户名只能包含字母、数字、下划线和中文'));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
|
||
const validatePassword = (rule, value, callback) => {
|
||
if (!value) {
|
||
callback(new Error('请输入密码'));
|
||
} else if (value.length < 6) {
|
||
callback(new Error('密码至少6个字符'));
|
||
} else if (value.length > 20) {
|
||
callback(new Error('密码不能超过20个字符'));
|
||
} else if (!/(?=.*[a-zA-Z])(?=.*\d)/.test(value)) {
|
||
callback(new Error('密码必须包含字母和数字'));
|
||
} else {
|
||
// 如果确认密码已输入,重新验证确认密码
|
||
if (registerForm.confirmPassword) {
|
||
registerFormRef.value?.validateField('confirmPassword');
|
||
}
|
||
callback();
|
||
}
|
||
};
|
||
|
||
const validateConfirmPassword = (rule, value, callback) => {
|
||
if (!value) {
|
||
callback(new Error('请确认密码'));
|
||
} else if (value !== registerForm.password) {
|
||
callback(new Error('两次输入的密码不一致'));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
|
||
const validateAgreement = (rule, value, callback) => {
|
||
if (!value) {
|
||
callback(new Error('请阅读并同意用户协议和隐私政策'));
|
||
} else {
|
||
callback();
|
||
}
|
||
};
|
||
|
||
// 表单验证规则
|
||
const registerRules = {
|
||
username: [{ validator: validateUsername, trigger: 'blur' }],
|
||
phone: [
|
||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||
{
|
||
pattern: /^1[3-9]\d{9}$/,
|
||
message: '请输入正确的手机号',
|
||
trigger: 'blur',
|
||
},
|
||
],
|
||
|
||
smsCode: [
|
||
{ required: true, message: '请输入短信验证码', trigger: 'blur' },
|
||
{ pattern: /^\d{6}$/, message: '短信验证码为6位数字', trigger: 'blur' },
|
||
],
|
||
region: [{ required: true, message: '请选择省市区', trigger: 'change' }],
|
||
password: [{ validator: validatePassword, trigger: 'blur' }],
|
||
confirmPassword: [{ validator: validateConfirmPassword, trigger: 'blur' }],
|
||
captcha: [
|
||
{ required: true, message: '请输入验证码', trigger: 'blur' },
|
||
{ min: 4, max: 4, message: '验证码长度为4位', trigger: 'blur' },
|
||
],
|
||
agreement: [{ validator: validateAgreement, trigger: 'change' }],
|
||
};
|
||
|
||
// 发送短信验证码
|
||
const sendSMSCode = async () => {
|
||
if (!canSendSMS.value || sendingSMS.value || smsCountdown.value > 0) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
sendingSMS.value = true;
|
||
|
||
const response = await fetch('/api/sms/send', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
phone: registerForm.phone,
|
||
}),
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
ElMessage.success('验证码发送成功,请查收短信');
|
||
// 开始倒计时
|
||
startCountdown();
|
||
} else {
|
||
ElMessage.error(result.message || '发送失败,请重试');
|
||
}
|
||
} catch (error) {
|
||
console.error('发送短信验证码失败:', error);
|
||
ElMessage.error('发送失败,请检查网络连接');
|
||
} finally {
|
||
sendingSMS.value = false;
|
||
}
|
||
};
|
||
|
||
// 开始倒计时
|
||
const startCountdown = () => {
|
||
smsCountdown.value = 60;
|
||
const timer = setInterval(() => {
|
||
smsCountdown.value--;
|
||
if (smsCountdown.value <= 0) {
|
||
clearInterval(timer);
|
||
}
|
||
}, 1000);
|
||
};
|
||
|
||
// 加载省市区级联数据
|
||
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];
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 城市变化处理
|
||
*/
|
||
const onCityChange = () => {
|
||
// 清空区域选择
|
||
registerForm.district_id = '';
|
||
};
|
||
|
||
// 处理注册
|
||
const handleRegister = async () => {
|
||
if (!registerFormRef.value || !captchaRef.value) return;
|
||
|
||
try {
|
||
// 先验证表单
|
||
const valid = await registerFormRef.value.validate();
|
||
if (!valid) return;
|
||
|
||
// // 验证验证码
|
||
// const captchaValid = await captchaRef.value.verifyCaptcha(
|
||
// registerForm.captcha
|
||
// );
|
||
// if (!captchaValid) {
|
||
// registerForm.captcha = '';
|
||
// return;
|
||
// }
|
||
|
||
// 获取验证码信息
|
||
const captchaInfo = captchaRef.value.getCaptchaInfo();
|
||
|
||
// 提交注册请求(包含验证码信息)
|
||
const registerData = {
|
||
username: registerForm.phone,
|
||
phone: registerForm.phone,
|
||
city: registerForm.city,
|
||
district_id: registerForm.district_id,
|
||
password: registerForm.password,
|
||
smsCode: registerForm.smsCode,
|
||
captchaId: captchaInfo.captchaId,
|
||
captchaText: captchaInfo.captchaText,
|
||
province: registerForm.province,
|
||
inviter: registerForm.inviter,
|
||
};
|
||
console.log(registerData,'registerData');
|
||
console.log(registerForm,'registerForm')
|
||
const result = await userStore.register(registerData);
|
||
|
||
if (result.success) {
|
||
// 检查是否需要支付
|
||
if (result.needPayment) {
|
||
ElMessage.success('用户信息创建成功,请完成支付以激活账户');
|
||
// 跳转到支付页面
|
||
router.push({
|
||
path: '/payment',
|
||
});
|
||
} else {
|
||
ElMessage.success('注册成功!请登录');
|
||
router.push('/mylogin');
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('注册失败:', error);
|
||
// 注册失败后刷新验证码
|
||
if (captchaRef.value) {
|
||
await captchaRef.value.refreshCaptcha();
|
||
}
|
||
registerForm.captcha = '';
|
||
}
|
||
};
|
||
|
||
// 显示协议弹窗
|
||
const showAgreementDialog = () => {
|
||
showAgreementDialogVisible.value = true;
|
||
activeTab.value = 'agreement';
|
||
// 弹窗打开时默认用户协议已读
|
||
agreementViewed.value = true;
|
||
};
|
||
|
||
// 关闭协议弹窗
|
||
const closeAgreementDialog = () => {
|
||
showAgreementDialogVisible.value = false;
|
||
};
|
||
|
||
// 确认已阅读协议
|
||
const confirmAgreement = () => {
|
||
if (agreementViewed.value && privacyViewed.value) {
|
||
registerForm.agreement = true;
|
||
showAgreementDialogVisible.value = false;
|
||
ElMessage.success('已确认阅读用户协议和隐私政策');
|
||
} else {
|
||
ElMessage.warning('请先查看用户协议和隐私政策');
|
||
}
|
||
};
|
||
|
||
// 处理选项卡切换
|
||
const handleTabChange = (tabName) => {
|
||
if (tabName === 'agreement') {
|
||
// 切换到用户协议时,标记为已读
|
||
agreementViewed.value = true;
|
||
} else if (tabName === 'privacy') {
|
||
// 切换到隐私政策时,标记为已读
|
||
privacyViewed.value = true;
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
|
||
// 图片上传成功处理
|
||
const handleUploadSuccess = (response, field) => {
|
||
ElMessage.success('图片上传成功');
|
||
};
|
||
|
||
// 图片上传失败处理
|
||
const handleUploadError = (error) => {
|
||
ElMessage.error('图片上传失败,请重试');
|
||
};
|
||
|
||
const invite = async () => {
|
||
const inviter = route.query.inviter;
|
||
console.log('邀请人ID:', inviter);
|
||
if (inviter) {
|
||
registerForm.inviter = inviter;
|
||
// try {
|
||
// const response = await api.get(`/user/${inviter}`);
|
||
// if (response.data.success) {
|
||
// registerForm.inviter = inviter;
|
||
// }
|
||
// } catch (error) {
|
||
// console.error('获取邀请人信息失败:', error);
|
||
// }
|
||
}
|
||
}
|
||
|
||
// 组件挂载时的处理
|
||
onMounted(() => {
|
||
// 如果已经登录,直接跳转
|
||
if (userStore.isAuthenticated) {
|
||
const redirectPath = route.query.redirect || '/';
|
||
router.push(redirectPath);
|
||
}
|
||
|
||
|
||
// 加载省市区级联数据
|
||
loadRegionOptions();
|
||
|
||
invite();
|
||
});
|
||
</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;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
.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 {
|
||
0%,
|
||
100% {
|
||
transform: translateY(0px) rotate(0deg);
|
||
opacity: 0.7;
|
||
}
|
||
50% {
|
||
transform: translateY(-15px) rotate(180deg);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 480px) {
|
||
.register-container {
|
||
padding: 15px;
|
||
}
|
||
|
||
.register-card {
|
||
padding: 30px 20px;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
/* 协议相关样式 */
|
||
.agreement-hint {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: #e6a23c;
|
||
}
|
||
|
||
.agreement-hint .el-icon {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.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%;
|
||
}
|
||
|
||
: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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
: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;
|
||
}
|
||
}
|
||
</style>
|