371 lines
9.0 KiB
Vue
371 lines
9.0 KiB
Vue
<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>
|