edu/uniapp/src/pages/pre_registration/pre_registration.vue

583 lines
17 KiB
Vue

<template>
<page-meta :page-style="$theme.pageStyle">
<!-- #ifndef H5 -->
<navigation-bar :front-color="$theme.navColor" :background-color="$theme.navBgColor" />
<!-- #endif -->
</page-meta>
<view class="pre-registration min-h-full pb-[env(safe-area-inset-bottom)]">
<!-- 基本信息 -->
<view class="px-[30rpx] py-[30rpx]">
<view class="flex items-center mb-[30rpx]">
<view class="w-[6rpx] h-[32rpx] bg-blue-500 rounded-[3rpx] mr-[16rpx]"></view>
<view class="text-lg font-medium">基本信息</view>
</view>
<view class="form-item">
<view class="label required">姓名</view>
<u-input
v-model="formData.name"
placeholder="请输入真实姓名"
:border="false"
:custom-style="inputStyle"
/>
</view>
<view class="form-item">
<view class="label required">性别</view>
<u-radio-group v-model="formData.gender" direction="row">
<u-radio :name="1" active-color="#3B82F6">男</u-radio>
<u-radio :name="0" class="ml-[60rpx]" active-color="#3B82F6">女</u-radio>
</u-radio-group>
</view>
<view class="form-item">
<view class="label required">身份证号</view>
<u-input
v-model="formData.idCard"
placeholder="请输入18位身份证号"
maxlength="18"
:border="false"
:custom-style="inputStyle"
/>
</view>
</view>
<!-- 学业信息 -->
<view class="px-[30rpx] py-[30rpx]">
<view class="flex items-center mb-[30rpx]">
<view class="w-[6rpx] h-[32rpx] bg-green-500 rounded-[3rpx] mr-[16rpx]"></view>
<view class="text-lg font-medium">学业信息</view>
</view>
<view class="form-item">
<view class="label required">毕业学校</view>
<u-input
v-model="formData.school"
placeholder="请输入毕业学校全称"
:border="false"
:custom-style="inputStyle"
/>
</view>
<view class="form-item">
<view class="label required">中考成绩</view>
<u-input
v-model="formData.score"
placeholder="请输入中考成绩"
type="digit"
maxlength="6"
:border="false"
:custom-style="inputStyle"
/>
</view>
</view>
<!-- 身体信息 -->
<view class="px-[30rpx] py-[30rpx]">
<view class="flex items-center mb-[30rpx]">
<view class="w-[6rpx] h-[32rpx] bg-orange-500 rounded-[3rpx] mr-[16rpx]"></view>
<view class="text-lg font-medium">身体信息</view>
</view>
<view class="form-row">
<view class="form-item flex-1 mr-[20rpx]">
<view class="label required">身高(cm)</view>
<u-input
v-model="formData.height"
placeholder="如: 175"
type="digit"
maxlength="5"
:border="false"
:custom-style="inputStyle"
/>
</view>
<view class="form-item flex-1">
<view class="label required">体重(kg)</view>
<u-input
v-model="formData.weight"
placeholder="如: 60"
type="digit"
maxlength="5"
:border="false"
:custom-style="inputStyle"
/>
</view>
</view>
<view class="form-item">
<view class="label required">鞋码</view>
<u-input
v-model="formData.shoeSize"
placeholder="请输入鞋码,如: 42"
type="number"
maxlength="3"
:border="false"
:custom-style="inputStyle"
/>
</view>
</view>
<!-- 报名信息 -->
<view class="px-[30rpx] py-[30rpx]">
<view class="flex items-center mb-[30rpx]">
<view class="w-[6rpx] h-[32rpx] bg-purple-500 rounded-[3rpx] mr-[16rpx]"></view>
<view class="text-lg font-medium">报名信息</view>
</view>
<view class="form-item">
<view class="label required">报名专业</view>
<view class="dropdown-container">
<view class="dropdown-input" @click="toggleDropdown">
<text :class="formData.major ? '' : 'placeholder'">
{{ formData.major || '请选择报名专业' }}
</text>
<view class="dropdown-icons">
<view
v-if="formData.major && formData.major.trim()"
class="clear-icon"
@click.stop="clearMajor"
>
<u-icon name="close" color="#999" size="24"></u-icon>
</view>
<u-icon
:name="showDropdown ? 'arrow-up' : 'arrow-down'"
color="#999"
size="28"
></u-icon>
</view>
</view>
<view v-if="showDropdown" class="dropdown-options">
<scroll-view
scroll-y
class="dropdown-scroll"
:style="{ maxHeight: dropdownMaxHeight + 'px' }"
>
<view
v-for="(item, index) in majorList"
:key="item.id"
class="dropdown-option"
@click="selectMajor(index)"
>
{{ item.majorName }}
</view>
</scroll-view>
</view>
</view>
</view>
<view class="form-item">
<view class="label required">招生老师</view>
<u-input
v-model="formData.teacher"
:placeholder="formData.teacher ? '' : '请扫描招生老师二维码'"
:border="false"
:custom-style="inputStyle"
disabled
/>
</view>
</view>
<!-- 提交按钮 -->
<view class="px-[30rpx] py-[40rpx]">
<u-button
type="primary"
@click="submit"
:loading="submitting"
:custom-style="submitBtnStyle"
>
{{ submitting ? '提交中...' : '提交报名' }}
</u-button>
<view class="text-center mt-[20rpx] text-xs text-gray-400">
提交即表示您同意我们的服务条款
</view>
</view>
<!-- 点击外部关闭下拉框 -->
<view v-if="showDropdown" class="dropdown-overlay" @click="showDropdown = false"></view>
</view>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, watch, toRaw } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useRouter } from 'uniapp-router-next'
import { getMajorList, addStudent, getTeacherInfo } from '@/api/app'
const router = useRouter()
const formData = reactive({
name: '',
gender: 1,
idCard: '',
school: '',
score: '',
height: '',
weight: '',
shoeSize: '',
major: '',
majorId: 0,
teacher: '',
recruitmentTeacherId: 0
})
const submitting = ref(false)
const showDropdown = ref(false)
const majorList = ref<any[]>([])
const majorIndex = ref(0)
const dropdownMaxHeight = ref(200)
const inputStyle = {
background: '#F5F5F5',
padding: '24rpx',
borderRadius: '12rpx',
fontSize: '28rpx'
}
const submitBtnStyle = {
height: '100rpx',
fontSize: '32rpx',
borderRadius: '50rpx',
background: 'linear-gradient(135deg, #3B82F6 0%, #2563EB 100%)',
boxShadow: '0 8rpx 24rpx rgba(59, 130, 246, 0.3)'
}
const getTeacherData = async (params: { teacherId?: string; invitationCode?: string }) => {
try {
const res = await getTeacherInfo(params)
if (res.code === 1 && res.data) {
formData.teacher = res.data.teacherName
formData.recruitmentTeacherId = res.data.teacherId
}
} catch (error) {
console.error('获取老师信息失败:', error)
}
}
const getMajors = async () => {
try {
console.log('开始获取专业列表...')
const res = await getMajorList()
console.log('接口返回数据:', res)
majorList.value = res.lists || []
console.log('专业列表:', majorList.value)
} catch (error) {
console.error('获取专业列表失败:', error)
}
}
const onMajorChange = (e: any) => {
console.log('选择了专业:', e)
formData.major = majorList.value[e.detail.value].majorName
majorIndex.value = e.detail.value
}
const toggleDropdown = () => {
showDropdown.value = !showDropdown.value
}
const clearMajor = () => {
console.log('清除专业选择,当前值:', formData.major)
formData.major = ''
majorIndex.value = 0
console.log('清除后值:', formData.major)
}
const selectMajor = (index: number) => {
console.log('选择了专业索引:', index)
formData.major = majorList.value[index].majorName
formData.majorId = majorList.value[index].id
majorIndex.value = index
showDropdown.value = false
}
const validateForm = () => {
if (!formData.name.trim()) {
uni.$u.toast('请输入姓名')
return false
}
if (!formData.idCard.trim()) {
uni.$u.toast('请输入身份证号')
return false
}
if (!/^\d{17}[\dXx]$/.test(formData.idCard)) {
uni.$u.toast('请输入正确的18位身份证号')
return false
}
if (!formData.school.trim()) {
uni.$u.toast('请输入毕业学校')
return false
}
if (!formData.score) {
uni.$u.toast('请输入中考成绩')
return false
}
if (Number(formData.score) < 0 || Number(formData.score) > 750) {
uni.$u.toast('请输入正确的中考成绩')
return false
}
if (!formData.height) {
uni.$u.toast('请输入身高')
return false
}
if (Number(formData.height) < 50 || Number(formData.height) > 250) {
uni.$u.toast('请输入正确的身高')
return false
}
if (!formData.weight) {
uni.$u.toast('请输入体重')
return false
}
if (Number(formData.weight) < 20 || Number(formData.weight) > 200) {
uni.$u.toast('请输入正确的体重')
return false
}
if (!formData.shoeSize) {
uni.$u.toast('请输入鞋码')
return false
}
if (Number(formData.shoeSize) < 15 || Number(formData.shoeSize) > 55) {
uni.$u.toast('请输入正确的鞋码')
return false
}
if (!formData.major.trim()) {
uni.$u.toast('请选择报名专业')
return false
}
if (!formData.teacher.trim()) {
uni.$u.toast('请输入招生老师姓名')
return false
}
return true
}
const submit = async () => {
console.log('========== 开始提交 ==========')
console.log('当前表单数据:', JSON.parse(JSON.stringify(formData)))
if (!validateForm()) {
console.log('表单验证失败')
return
}
console.log('表单验证通过')
submitting.value = true
try {
const submitData = {
name: formData.name.trim(),
gender: formData.gender,
idCard: formData.idCard.trim(),
previousSchool: formData.school.trim(),
highSchoolScore: Number(formData.score),
height: Number(formData.height),
weight: Number(formData.weight),
shoeSize: Number(formData.shoeSize),
majorId: formData.majorId,
recruitmentTeacherId: formData.recruitmentTeacherId
}
console.log('========== 准备提交数据 ==========')
console.log('提交数据:', submitData)
console.log('majorId:', submitData.majorId, '类型:', typeof submitData.majorId)
console.log('recruitmentTeacherId:', submitData.recruitmentTeacherId, '类型:', typeof submitData.recruitmentTeacherId)
uni.showLoading({
title: '提交中...',
mask: true
})
console.log('========== 开始调用 addStudent ==========')
const res = await addStudent(submitData)
console.log('========== addStudent 调用完成 ==========')
uni.hideLoading()
console.log('接口返回结果:', res)
console.log('返回code:', res?.code)
console.log('返回msg:', res?.msg)
console.log('返回data:', res?.data)
if (res?.code === 1) {
console.log('提交成功')
uni.showToast({
title: res?.msg || '提交成功',
icon: 'success',
duration: 2000
})
setTimeout(() => {
router.navigateTo('/pages/submit_success/submit_success')
}, 1500)
} else {
console.log('提交失败:', res?.msg || '未知错误')
uni.showToast({
title: res?.msg || '提交失败',
icon: 'none',
duration: 2000
})
}
} catch (error: any) {
uni.hideLoading()
console.error('========== 提交异常 ==========')
console.error('错误对象:', error)
console.error('错误类型:', typeof error)
console.error('错误消息:', error?.message || error)
console.error('错误堆栈:', error?.stack)
uni.showToast({
title: '网络错误,请重试',
icon: 'none',
duration: 2000
})
} finally {
console.log('========== 提交结束 ==========')
submitting.value = false
}
}
onMounted(() => {
getMajors()
})
onLoad((options: any) => {
if (options.scene) {
getTeacherData({ invitationCode: decodeURIComponent(options.scene) })
return
}
if (options.invitationCode) {
getTeacherData({ invitationCode: options.invitationCode })
return
}
if (options.teacherId) {
getTeacherData({ teacherId: options.teacherId })
}
})
</script>
<style lang="scss" scoped>
.pre-registration {
background-color: #ffffff;
}
.form-item {
margin-bottom: 30rpx;
&:last-child {
margin-bottom: 0;
}
}
.form-row {
display: flex;
flex-direction: row;
}
.label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
font-weight: 500;
&.required::before {
content: '*';
color: #ff4d4f;
margin-right: 6rpx;
}
}
.dropdown-container {
position: relative;
z-index: 1000;
}
.dropdown-input {
background: #f5f5f5;
padding: 24rpx;
border-radius: 12rpx;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: space-between;
min-height: 88rpx;
border: 2rpx solid transparent;
transition: all 0.2s ease;
&:active {
background: #e8e8e8;
}
.placeholder {
color: #999;
}
}
.dropdown-icons {
display: flex;
align-items: center;
gap: 16rpx;
}
.clear-icon {
padding: 8rpx;
border-radius: 50%;
background: rgba(0, 0, 0, 0.05);
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
&:active {
background: rgba(0, 0, 0, 0.1);
transform: scale(0.95);
}
}
.dropdown-options {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border-radius: 12rpx;
margin-top: 8rpx;
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.12);
border: 1rpx solid #e8e8e8;
z-index: 1001;
overflow: hidden;
}
.dropdown-scroll {
max-height: 200px;
}
.dropdown-option {
padding: 24rpx;
font-size: 28rpx;
color: #333;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
&:active {
background: #f5f5f5;
}
}
.dropdown-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
}
.bg-blue-500 {
background-color: #3b82f6;
}
.bg-green-500 {
background-color: #22c55e;
}
.bg-orange-500 {
background-color: #f97316;
}
.bg-purple-500 {
background-color: #a855f7;
}
</style>