|
|
<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>
|