| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- <template>
- <div class="register-container">
- <el-card class="register-card">
- <template #header>
- <div class="card-header">
- <img src="/logo.png" alt="Logo" class="logo-img" />
- <h2>用户注册</h2>
- <p>注册后即可登录统一认证平台</p>
- </div>
- </template>
- <!-- Steps -->
- <el-steps :active="activeStep" finish-status="success" simple style="margin-bottom: 20px">
- <el-step title="验证手机" />
- <el-step title="设置密码" />
- </el-steps>
- <!-- Step 1: Verify Mobile -->
- <el-form v-if="activeStep === 0" :model="form" :rules="step1Rules" ref="step1FormRef" label-width="0">
- <el-form-item prop="mobile">
- <el-input v-model="form.mobile" placeholder="手机号" prefix-icon="Iphone" />
- </el-form-item>
- <el-form-item prop="captcha_code" class="captcha-item">
- <el-input v-model="form.captcha_code" placeholder="图形验证码" style="width: 60%" prefix-icon="Picture" />
- <div class="captcha-img" @click="fetchCaptcha" v-if="captchaImage">
- <img :src="captchaImage" alt="captcha" />
- </div>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" style="width: 100%" @click="handleSendSms" :loading="loading">
- 发送验证码
- </el-button>
- </el-form-item>
- <div class="login-link">
- <router-link to="/login">已有账号?去登录</router-link>
- </div>
- </el-form>
- <!-- Step 2: Set Password -->
- <el-form v-if="activeStep === 1" :model="form" :rules="step2Rules" ref="step2FormRef" label-width="0">
- <el-form-item>
- <el-input v-model="form.mobile" disabled prefix-icon="Iphone" />
- </el-form-item>
- <el-form-item prop="sms_code">
- <el-input v-model="form.sms_code" placeholder="短信验证码" prefix-icon="Message" />
- </el-form-item>
- <el-form-item prop="name">
- <el-input v-model="form.name" placeholder="真实姓名" prefix-icon="User" />
- </el-form-item>
- <el-form-item prop="password">
- <el-input
- v-model="form.password"
- type="password"
- placeholder="设置密码"
- show-password
- prefix-icon="Lock"
- />
- </el-form-item>
- <el-form-item prop="confirmPassword">
- <el-input
- v-model="form.confirmPassword"
- type="password"
- placeholder="确认密码"
- show-password
- prefix-icon="Lock"
- />
- </el-form-item>
-
- <el-form-item>
- <el-button type="primary" @click="handleSubmit" :loading="loading" style="width: 100%">
- 注册
- </el-button>
- </el-form-item>
- <div class="links">
- <el-button link @click="activeStep = 0">上一步</el-button>
- </div>
- </el-form>
- </el-card>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, reactive, onMounted } from 'vue'
- import { useRouter } from 'vue-router'
- import { ElMessage } from 'element-plus'
- import type { FormInstance, FormRules } from 'element-plus'
- import { registerDeveloper, getCaptcha, sendSms } from '../api/public'
- import { Iphone, Lock, Picture, Message, User } from '@element-plus/icons-vue'
- const router = useRouter()
- const activeStep = ref(0)
- const loading = ref(false)
- const captchaImage = ref('')
- const step1FormRef = ref<FormInstance>()
- const step2FormRef = ref<FormInstance>()
- const form = reactive({
- mobile: '',
- captcha_id: '',
- captcha_code: '',
- sms_code: '',
- name: '',
- password: '',
- confirmPassword: ''
- })
- const fetchCaptcha = async () => {
- try {
- const res = await getCaptcha()
- captchaImage.value = res.data.image
- form.captcha_id = res.data.captcha_id
- form.captcha_code = ''
- } catch (e) {
- console.error(e)
- }
- }
- const validatePass2 = (rule: any, value: any, callback: any) => {
- if (value === '') {
- callback(new Error('请再次输入密码'))
- } else if (value !== form.password) {
- callback(new Error('两次输入密码不一致!'))
- } else {
- callback()
- }
- }
- const step1Rules = reactive<FormRules>({
- mobile: [
- { required: true, message: '请输入手机号', trigger: 'blur' },
- { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
- ],
- captcha_code: [
- { required: true, message: '请输入图形验证码', trigger: 'blur' }
- ]
- })
- const step2Rules = reactive<FormRules>({
- sms_code: [
- { required: true, message: '请输入短信验证码', trigger: 'blur' }
- ],
- name: [
- { required: true, message: '请输入真实姓名', trigger: 'blur' }
- ],
- password: [
- { required: true, message: '请输入密码', trigger: 'blur' },
- { min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
- { pattern: /^(?=.*[a-zA-Z])(?=.*\d).+$/, message: '密码必须包含字母和数字', trigger: 'blur' }
- ],
- confirmPassword: [
- { validator: validatePass2, trigger: 'blur' }
- ]
- })
- const handleSendSms = async () => {
- if (!step1FormRef.value) return
-
- await step1FormRef.value.validate(async (valid) => {
- if (valid) {
- loading.value = true
- try {
- await sendSms({
- mobile: form.mobile,
- captcha_id: form.captcha_id,
- captcha_code: form.captcha_code
- })
- ElMessage.success('验证码已发送 (模拟环境: 请检查后端日志)')
- activeStep.value = 1
- } catch (e) {
- fetchCaptcha()
- } finally {
- loading.value = false
- }
- }
- })
- }
- const handleSubmit = async () => {
- if (!step2FormRef.value) return
-
- await step2FormRef.value.validate(async (valid) => {
- if (valid) {
- loading.value = true
- try {
- await registerDeveloper({
- mobile: form.mobile,
- password: form.password,
- sms_code: form.sms_code,
- name: form.name
- })
- ElMessage.success('注册成功,请登录')
- router.push('/login')
- } catch (error) {
- // error
- } finally {
- loading.value = false
- }
- }
- })
- }
- onMounted(() => {
- fetchCaptcha()
- })
- </script>
- <style scoped>
- .register-container {
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100vh;
- background-color: #f0f2f5;
- }
- .register-card {
- width: 450px;
- }
- .card-header {
- text-align: center;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 10px;
- }
- .logo-img {
- height: 40px;
- object-fit: contain;
- }
- .card-header h2 {
- margin: 0;
- font-size: 20px;
- }
- .card-header p {
- margin: 0;
- color: #909399;
- font-size: 14px;
- }
- .login-link, .links {
- text-align: center;
- margin-top: 10px;
- }
- .captcha-item {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .captcha-img {
- cursor: pointer;
- height: 32px;
- display: flex;
- align-items: center;
- border-radius: 4px;
- overflow: hidden;
- border: 1px solid #dcdfe6;
- }
- .captcha-img img {
- height: 100%;
- }
- </style>
|