You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

460 lines
14 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="page-container">
<div class="page-wrapper">
<!-- 头部导航 -->
<div class="header-bar">
<div class="header-logo">
<el-icon><Collection /></el-icon>
<span>培训报名系统</span>
</div>
</div>
<!-- 表单卡片 -->
<div class="custom-card">
<div class="page-header" style="margin-top: 0;">
<h1 class="page-title">填写报名信息</h1>
<p class="page-subtitle">请填写以下报名信息 <span style="color:#f56c6c">*</span> 为必填项</p>
</div>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-position="top"
size="large"
>
<!-- 基本信息 -->
<div class="form-section">
<div class="form-section-title">基本信息</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="姓名" prop="name">
<el-input
v-model="formData.name"
placeholder="请输入您的姓名"
clearable
>
<template #prefix><el-icon><User /></el-icon></template>
</el-input>
</el-form-item>
</div>
<div class="form-item">
<el-form-item label="职务/职称" prop="title">
<el-select
v-model="formData.title"
placeholder="请选择职务/职称"
style="width: 100%"
>
<el-option label="教授" value="教授" />
<el-option label="副教授" value="副教授" />
<el-option label="讲师" value="讲师" />
<el-option label="工程师" value="工程师" />
<el-option label="其他" value="其他" />
</el-select>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="所在单位" prop="organization">
<el-input
v-model="formData.organization"
placeholder="请输入学校/企业全称"
clearable
>
<template #prefix><el-icon><OfficeBuilding /></el-icon></template>
</el-input>
</el-form-item>
</div>
<div class="form-item">
<el-form-item label="所在部门" prop="department">
<el-input
v-model="formData.department"
placeholder="请输入院系/部门名称"
clearable
>
<template #prefix><el-icon><Grid /></el-icon></template>
</el-input>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="推荐人" prop="referrer">
<el-input
v-model="formData.referrer"
placeholder="请输入推荐人的姓名"
clearable
>
<template #prefix><el-icon><User /></el-icon></template>
</el-input>
</el-form-item>
</div>
<div class="form-item">
<el-form-item label="推荐人单位" prop="referrerCompany">
<el-input
v-model="formData.referrerCompany"
placeholder="请输入推荐人单位"
clearable
>
<template #prefix><el-icon><OfficeBuilding /></el-icon></template>
</el-input>
</el-form-item>
</div>
</div>
</div>
<!-- 联系信息 -->
<div class="form-section">
<div class="form-section-title">联系信息</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="手机号码" prop="phone">
<el-input
v-model="formData.phone"
placeholder="请输入手机号码"
clearable
maxlength="11"
>
<template #prefix><el-icon><Phone /></el-icon></template>
</el-input>
</el-form-item>
</div>
<div class="form-item">
<el-form-item label="电子邮箱" prop="email">
<el-input
v-model="formData.email"
placeholder="请输入电子邮箱"
clearable
>
<template #prefix><el-icon><Message /></el-icon></template>
</el-input>
</el-form-item>
</div>
<!-- <div class="form-item">
<el-form-item label="短信验证码" prop="smsCode">
<el-input
v-model="formData.smsCode"
placeholder="请输入验证码"
clearable
maxlength="6"
style="width: 100%"
>
<template #append>
<el-button
@click="sendSmsCodeFun"
:disabled="smsCountdown > 0"
style="min-width: 100px;"
>
{{ smsCountdown > 0 ? `${smsCountdown}s` : '获取验证码' }}
</el-button>
</template>
</el-input>
</el-form-item>
</div> -->
</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="身份证号" prop="idCard">
<el-input
v-model="formData.idCard"
placeholder="请输入身份证号码"
clearable
maxlength="18"
>
<template #prefix><el-icon><Postcard /></el-icon></template>
</el-input>
</el-form-item>
</div>
</div>
</div>
<!-- 培训信息 -->
<div class="form-section">
<div class="form-section-title">培训信息</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="报名期次" prop="periodId">
<el-radio-group v-model="formData.periodId">
<el-radio-button
v-for="item in sessionOptions"
:key="item.id"
:value="item.id"
:disabled="item.status === 'inactive'"
>
{{ formatPeriodLabel(item.periodName, item.startDate, item.endDate) }}
</el-radio-button>
</el-radio-group>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-item">
<el-form-item label="培训费用">
<div class="price-tag">
<span class="price-amount">¥2680</span>
<span class="price-unit">/人</span>
</div>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-item" style="flex: 100%;">
<el-form-item label="备注说明" prop="remark">
<el-input
v-model="formData.remark"
type="textarea"
:rows="3"
placeholder="饮食禁忌、特殊需求等"
maxlength="200"
show-word-limit
/>
</el-form-item>
</div>
</div>
</div>
<!-- 提交按钮 -->
<div class="form-section" style="text-align: center;">
<el-button type="primary" size="large" class="btn-primary" @click="submitForm" :loading="submitLoading" :disabled="submitLoading" style="width: 100%; max-width: 300px;">
<el-icon><Position /></el-icon>
<span style="margin-left: 8px;"></span>
</el-button>
</div>
</el-form>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { submitRegistration, sendSmsCode, getSessionList } from '../../api/registration'
import { formatPeriodLabel } from '../../utils/format'
const router = useRouter()
const formRef = ref(null)
const sessionOptions = ref([])
const smsCountdown = ref(0)
const submitLoading = ref(false)
let smsTimer = null
// 加载期次列表
const loadSessionOptions = async () => {
try {
const res = await getSessionList({ pageNum: 1, pageSize: 9999 })
const list = Array.isArray(res.data) ? res.data : (res.data.records || [])
sessionOptions.value = list
} catch (e) {
console.error('加载期次失败', e)
}
}
// 表单数据
const formData = reactive({
name: '',
organization: '',
department: '',
title: '',
referrer:'',
referrerCompany:'',
phone: '',
smsCode: '',
email: '',
idCard: '',
periodId: '',
remark: ''
})
// 校验规则
const validatePhone = (rule, value, callback) => {
const phoneReg = /^1[3-9]\d{9}$/
if (!value) {
callback(new Error('请输入手机号码'))
} else if (!phoneReg.test(value)) {
callback(new Error('请输入正确的手机号码'))
} else {
callback()
}
}
const validateSmsCode = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入短信验证码'))
} else if (!/^\d{6}$/.test(value)) {
callback(new Error('验证码为6位数字'))
} else {
callback()
}
}
const validateEmail = (rule, value, callback) => {
const emailReg = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/
if (!value) {
callback(new Error('请输入电子邮箱'))
} else if (!emailReg.test(value)) {
callback(new Error('请输入正确的邮箱地址'))
} else {
callback()
}
}
const validateIdCard = (rule, value, callback) => {
const idCardReg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/
if (!value) {
callback(new Error('请输入身份证号码'))
} else if (!idCardReg.test(value)) {
callback(new Error('请输入正确的身份证号码'))
} else {
callback()
}
}
const formRules = {
name: [
{ required: true, message: '请输入姓名', trigger: 'blur' },
{ min: 2, max: 20, message: '姓名长度为2-20个字符', trigger: 'blur' }
],
referrer: [
{ required: true, message: '请输入推荐人姓名', trigger: 'blur' },
{ min: 2, max: 20, message: '姓名长度为2-20个字符', trigger: 'blur' }
],
referrerCompany:[
{ required: true, message: '请输入推荐人单位', trigger: 'blur' }
],
organization: [
{ required: true, message: '请输入所在单位', trigger: 'blur' }
],
title: [
{ required: true, message: '请选择职务/职称', trigger: 'change' }
],
phone: [
{ required: true, validator: validatePhone, trigger: 'blur' }
],
smsCode: [
{ required: true, validator: validateSmsCode, trigger: 'blur' }
],
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
idCard: [
{ required: true, validator: validateIdCard, trigger: 'blur' }
],
periodId: [
{ required: true, message: '请选择报名期次', trigger: 'change' }
]
}
// 发送验证码
const sendSmsCodeFun = async () => {
if (!formData.phone) {
ElMessage.warning('请先输入手机号码')
return
}
const phoneReg = /^1[3-9]\d{9}$/
if (!phoneReg.test(formData.phone)) {
ElMessage.error('请输入正确的手机号码')
return
}
try {
await sendSmsCode(formData.phone)
ElMessage.success('验证码已发送,请注意查收')
smsCountdown.value = 60
smsTimer = setInterval(() => {
smsCountdown.value--
if (smsCountdown.value <= 0) {
clearInterval(smsTimer)
}
}, 1000)
} catch (e) {
console.error('发送验证码失败', e)
}
}
// 提交表单
const submitForm = async () => {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (valid) {
submitLoading.value = true
try {
const res = await submitRegistration({
company: formData.organization,
department: formData.department,
email: formData.email,
smsCode: formData.smsCode,
fee: 2680,
idCard: formData.idCard,
name: formData.name,
referrer: formData.referrer,
referrerCompany: formData.referrerCompany,
periodId: formData.periodId,
phone: formData.phone,
remark: formData.remark,
title: formData.title
})
ElMessage.success('报名信息提交成功!即将跳转至支付页面...')
// 跳转到支付页面
setTimeout(() => {
router.push({
path: '/client/payment',
query: {
bmId: res.data.bmId,
name: res.data.name,
referrer: res.data.referrer,
referrerCompany: res.data.referrerCompany,
company: res.data.company,
periodId: formData.periodId
}
})
}, 1000)
} catch (e) {
console.error('提交失败', e)
} finally {
submitLoading.value = false
}
} else {
ElMessage.error('请检查表单填写是否正确')
return false
}
})
}
// 初始化加载期次
onMounted(() => {
loadSessionOptions()
})
</script>
<style scoped>
:deep(.el-radio-group) {
gap: 10px;
}
:deep(.el-form-item__content){
gap: 10px;
}
@media screen and (max-width: 768px) {
.form-row {
flex-direction: column;
gap: 0;
}
.form-item {
width: 100%;
}
}
</style>