quanyoumi/frontend/src/views/Register.vue

371 lines
9.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="register">
<div class="register-header">
<div class="header-back" @click="$router.back()">
<van-icon name="arrow-left" />
</div>
<h1 class="header-title">活动报名</h1>
</div>
<div class="register-content">
<div class="form-card">
<van-form @submit="onSubmit">
<div class="form-section">
<div class="section-label">基本信息</div>
<van-field
v-model="form.name"
label="姓名"
placeholder="请输入您的姓名"
required
:rules="[{ required: true, message: '请输入姓名' }]"
/>
<van-field
v-model="form.phone"
label="手机号"
type="tel"
placeholder="请输入手机号"
maxlength="11"
required
:rules="[{ required: true, message: '请输入手机号' }, { pattern: /^1\d{10}$/, message: '请输入正确的手机号' }]"
/>
<van-field
v-model.number="form.peopleCount"
label="人数"
type="number"
placeholder="请输入参加人数"
required
:rules="[{ required: true, message: '请输入人数' }]"
/>
</div>
<div class="form-section">
<div class="section-label">车辆信息</div>
<van-field
v-model="form.carModel"
label="车型"
placeholder="请选择车型"
readonly
is-link
@click="showCarModelPicker = true"
/>
<van-field
label="车牌号"
:rules="[{ required: true, message: '请输入车牌号' }]"
class="car-number-field"
>
<template #input>
<div class="license-plate-input">
<span class="plate-prefix">闽</span>
<input
ref="letterInput"
v-model="carLetter"
@input="handleLetterInput"
placeholder="_"
maxlength="1"
class="plate-letter"
/>
<span class="plate-dot">·</span>
<input
ref="digitsInput"
v-model="carDigits"
@input="handleDigitsInput"
placeholder="______"
maxlength="6"
class="plate-digits"
/>
</div>
</template>
</van-field>
</div>
<div class="form-section">
<div class="section-label">其他信息</div>
<van-field
v-model="form.note"
label="备注"
type="textarea"
placeholder="有什么想说的吗?"
rows="3"
maxlength="200"
show-word-limit
/>
</div>
<div class="submit-btn">
<van-button round block type="primary" native-type="submit" size="large" color="#FF6700">
提交报名
</van-button>
</div>
</van-form>
</div>
</div>
<!-- 车型选择器 -->
<van-popup v-model:show="showCarModelPicker" position="bottom" round>
<van-picker
:columns="carModels"
@confirm="onCarModelConfirm"
@cancel="showCarModelPicker = false"
/>
</van-popup>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { showToast, showDialog } from 'vant';
import api from '../api';
const route = useRoute();
const router = useRouter();
const showCarModelPicker = ref(false);
const carModels = ref([
{ text: '小米SU7', value: '小米SU7' },
{ text: '小米YU7', value: '小米YU7' },
{ text: '小米SU7 Ultra', value: '小米SU7 Ultra' }
]);
const form = ref({
name: '',
phone: '',
peopleCount: 1,
carModel: '',
carNumber: '',
note: ''
});
const carLetter = ref('');
const carDigits = ref('');
const letterInput = ref(null);
const digitsInput = ref(null);
const handleLetterInput = (e) => {
const value = e.target.value.toUpperCase().replace(/[^A-Z]/g, '');
carLetter.value = value;
form.value.carNumber = value + carDigits.value;
// 输入完1位字母后自动跳到数字输入框
if (value.length === 1) {
setTimeout(() => {
digitsInput.value?.focus();
}, 0);
}
};
const handleDigitsInput = (e) => {
const value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '');
carDigits.value = value;
form.value.carNumber = carLetter.value + value;
};
// 车型选择确认
const onCarModelConfirm = ({ selectedOptions }) => {
form.value.carModel = selectedOptions[0].value;
showCarModelPicker.value = false;
};
// 车牌号格式化已被handleLetterInput和handleDigitsInput替代
// 从缓存加载数据
onMounted(() => {
const cached = localStorage.getItem('userRegistrationInfo');
if (cached) {
try {
const data = JSON.parse(cached);
form.value = { ...form.value, ...data };
} catch (e) {
console.error('加载缓存失败', e);
}
}
});
const onSubmit = async () => {
try {
// 添加"闽"前缀到车牌号
const fullCarNumber = form.value.carNumber ? `${form.value.carNumber}` : '';
// 保存到缓存
localStorage.setItem('userRegistrationInfo', JSON.stringify({
name: form.value.name,
phone: form.value.phone,
carModel: form.value.carModel,
carNumber: fullCarNumber
}));
const { data } = await api.submitRegistration({
activityId: route.params.id,
...form.value,
carNumber: fullCarNumber
});
// 保存报名ID和登录信息
const registrations = JSON.parse(localStorage.getItem('myRegistrations') || '[]');
registrations.push({
activityId: route.params.id,
registrationId: data.id,
date: new Date().toISOString()
});
localStorage.setItem('myRegistrations', JSON.stringify(registrations));
// 自动保存登录状态
localStorage.setItem(`user_reg_${route.params.id}`, JSON.stringify({
id: data.id,
name: form.value.name,
phone: form.value.phone,
carNumber: fullCarNumber,
carModel: form.value.carModel,
status: 'pending',
activityId: route.params.id
}));
await showDialog({
title: '报名成功',
message: `您的报名ID: ${data.id}\n已自动登录请等待管理员审核`
});
router.back();
} catch (error) {
showToast('报名失败');
}
};
</script>
<style scoped>
.register {
min-height: 100vh;
background: linear-gradient(180deg, #FF6700 0%, #FF8533 100%);
}
.register-header {
position: relative;
padding: 16px;
color: #fff;
}
.header-back {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255,255,255,0.2);
border-radius: 50%;
font-size: 20px;
backdrop-filter: blur(10px);
margin-bottom: 16px;
}
.header-title {
font-size: 28px;
font-weight: bold;
margin: 0;
}
.register-content {
padding: 0 16px 16px;
}
.form-card {
background: #fff;
border-radius: 20px;
padding: 24px;
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
}
.form-section {
margin-bottom: 24px;
}
.form-section:last-of-type {
margin-bottom: 32px;
}
.section-label {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 12px;
padding-left: 8px;
border-left: 3px solid #FF6700;
}
/* 车牌号字段特殊样式 - 垂直居中 */
.car-number-field :deep(.van-cell) {
align-items: center !important;
}
.car-number-field :deep(.van-field__label) {
padding-top: 5px !important;
}
.submit-btn {
margin-top: 24px;
}
/* 车牌输入样式 - 橙色主题 */
.license-plate-input {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: linear-gradient(135deg, #FF6700 0%, #FF8533 100%);
border-radius: 6px;
box-shadow: 0 2px 8px rgba(255, 103, 0, 0.3);
width: fit-content;
margin-left: auto;
}
.plate-prefix {
color: #fff;
font-weight: 700;
font-size: 16px;
text-shadow: 0 1px 2px rgba(0,0,0,0.2);
}
.plate-letter {
width: 22px;
height: 28px;
border: 2px solid rgba(255,255,255,0.9);
border-radius: 3px;
background: #fff;
text-align: center;
font-size: 16px;
font-weight: 700;
color: #333;
outline: none;
text-transform: uppercase;
padding: 0;
}
.plate-dot {
color: #fff;
font-size: 18px;
font-weight: 700;
margin: 0 1px;
}
.plate-digits {
width: 95px;
height: 28px;
border: 2px solid rgba(255,255,255,0.9);
border-radius: 3px;
background: #fff;
text-align: center;
font-size: 16px;
font-weight: 700;
color: #333;
outline: none;
letter-spacing: 1px;
text-transform: uppercase;
padding: 0;
}
.plate-letter::placeholder,
.plate-digits::placeholder {
color: #ddd;
font-weight: 400;
}
</style>