<template>
|
<view class="profile-page">
|
<guide-nav-bar title="注册导游" @back="goBack" />
|
|
<scroll-view scroll-y class="form-scroll">
|
<view class="form-wrap">
|
<view class="field-block">
|
<view class="field-head" :class="{ 'field-head-error': errors.name }">姓名</view>
|
<input
|
v-model="form.name"
|
class="plain-input"
|
:class="{ 'field-invalid': errors.name }"
|
placeholder="请输入姓名"
|
@input="clearFieldError('name')"
|
/>
|
<text v-if="errors.name" class="field-error">{{ errors.name }}</text>
|
</view>
|
|
<view class="field-block">
|
<view class="field-head" :class="{ 'field-head-error': errors.idNo }">身份证号</view>
|
<input
|
v-model="form.idNo"
|
class="plain-input"
|
:class="{ 'field-invalid': errors.idNo }"
|
type="idcard"
|
maxlength="18"
|
placeholder="请输入身份证号"
|
@input="handleIdCardInput"
|
/>
|
<text v-if="errors.idNo" class="field-error">{{ errors.idNo }}</text>
|
</view>
|
|
<view class="field-block">
|
<view class="field-head" :class="{ 'field-head-error': errors.gender }">性别</view>
|
<picker :range="genderOptions" @change="setGender">
|
<view class="select-row" :class="{ 'field-invalid': errors.gender }">
|
<text :class="{ placeholder: !form.gender }">{{ form.gender || '请选择' }}</text>
|
<image class="select-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
|
</view>
|
</picker>
|
<text v-if="errors.gender" class="field-error">{{ errors.gender }}</text>
|
</view>
|
|
<view class="field-block">
|
<view class="field-head">年龄(非必填)</view>
|
<picker :range="ageOptions" @change="setAge">
|
<view class="select-row">
|
<text :class="{ placeholder: !form.age }">{{ form.age || '请选择' }}</text>
|
<image class="select-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
|
</view>
|
</picker>
|
</view>
|
|
<view class="field-block">
|
<view class="field-head">工作年限(非必填)</view>
|
<picker :range="workYearOptions" @change="setWorkYear">
|
<view class="select-row">
|
<text :class="{ placeholder: !form.workYear }">{{ form.workYear || '请选择' }}</text>
|
<image class="select-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
|
</view>
|
</picker>
|
</view>
|
|
<view class="field-block">
|
<view class="field-head" :class="{ 'field-head-error': errors.guideType }">讲解队/个人</view>
|
<picker :range="teamOptions" range-key="name" @change="setTeam">
|
<view class="select-row" :class="{ 'field-invalid': errors.guideType }">
|
<text :class="{ placeholder: !form.teamName }">{{ form.teamName || '请选择' }}</text>
|
<image class="select-icon" src="/static/icons/chevron-down.svg" mode="aspectFit" />
|
</view>
|
</picker>
|
<text v-if="errors.guideType" class="field-error">{{ errors.guideType }}</text>
|
</view>
|
|
<view class="upload-section">
|
<view class="field-head" :class="{ 'field-head-error': errors.idFront || errors.idBack }">上传身份证</view>
|
<view class="upload-grid two">
|
<view class="upload-item" :class="{ 'field-invalid': errors.idFront }" @tap="chooseImage('idFront')">
|
<image v-if="form.idFront" class="upload-preview" :src="form.idFront" mode="aspectFill" />
|
<image v-else class="plus-icon" src="/static/icons/plus.svg" mode="aspectFit" />
|
</view>
|
<view class="upload-item" :class="{ 'field-invalid': errors.idBack }" @tap="chooseImage('idBack')">
|
<image v-if="form.idBack" class="upload-preview" :src="form.idBack" mode="aspectFill" />
|
<image v-else class="plus-icon" src="/static/icons/plus.svg" mode="aspectFit" />
|
</view>
|
</view>
|
<view class="upload-labels two">
|
<text :class="{ 'upload-label-error': errors.idFront }">正面</text>
|
<text :class="{ 'upload-label-error': errors.idBack }">反面</text>
|
</view>
|
<view class="upload-errors">
|
<text v-if="errors.idFront" class="field-error">{{ errors.idFront }}</text>
|
<text v-if="errors.idBack" class="field-error">{{ errors.idBack }}</text>
|
</view>
|
</view>
|
|
<view class="upload-section certificate-section">
|
<view class="certificate-head">
|
<text :class="{ 'field-head-error': errors.guideLicense }">上传导游证</text>
|
<text>上传导游资格证(非必填)</text>
|
</view>
|
<view class="upload-grid two">
|
<view class="upload-item" :class="{ 'field-invalid': errors.guideLicense }" @tap="chooseImage('guideLicense')">
|
<image v-if="form.guideLicense" class="upload-preview" :src="form.guideLicense" mode="aspectFill" />
|
<image v-else class="plus-icon" src="/static/icons/plus.svg" mode="aspectFit" />
|
</view>
|
<view class="upload-item" @tap="chooseImage('guideCert')">
|
<image v-if="form.guideCert" class="upload-preview" :src="form.guideCert" mode="aspectFill" />
|
<image v-else class="plus-icon" src="/static/icons/plus.svg" mode="aspectFit" />
|
</view>
|
</view>
|
<text v-if="errors.guideLicense" class="field-error">{{ errors.guideLicense }}</text>
|
</view>
|
|
<button class="submit-button" :disabled="submitting" @tap="submit">
|
{{ submitting ? '提交中...' : '提交' }}
|
</button>
|
</view>
|
</scroll-view>
|
|
<guide-dialog
|
v-model:visible="successDialogVisible"
|
title="提交成功"
|
content="资料已提交,平台审核通过后即可接单"
|
:show-cancel="false"
|
@confirm="goLogin"
|
/>
|
</view>
|
</template>
|
|
<script>
|
import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
|
import GuideDialog from '../../components/guide-dialog/guide-dialog.vue'
|
import {
|
getRegistrationTeams,
|
getRegistrationState,
|
submitGuideRegistration
|
} from '../../services/registration'
|
import { routeTo } from '../../utils/router'
|
|
export default {
|
components: {
|
GuideNavBar,
|
GuideDialog
|
},
|
data() {
|
return {
|
phone: '',
|
successDialogVisible: false,
|
submitting: false,
|
errors: {
|
name: '',
|
gender: '',
|
guideType: '',
|
idNo: '',
|
idFront: '',
|
idBack: '',
|
guideLicense: ''
|
},
|
genderOptions: ['男', '女'],
|
ageOptions: ['18-25岁', '26-35岁', '36-45岁', '46岁以上'],
|
workYearOptions: ['1年以内', '1-3年', '3-5年', '5年以上'],
|
teamOptions: [{ id: null, name: '个人', guideType: 'personal' }],
|
form: {
|
name: '',
|
gender: '',
|
age: '',
|
workYear: '',
|
guideType: '',
|
teamId: null,
|
teamName: '',
|
idNo: '',
|
idFront: '',
|
idBack: '',
|
guideLicense: '',
|
guideCert: ''
|
}
|
}
|
},
|
onLoad() {
|
const registration = getRegistrationState()
|
if (!registration || !registration.registrationToken) {
|
uni.showToast({ title: '请先验证手机号', icon: 'none' })
|
routeTo('/pages/register-phone/index', 'redirectTo')
|
return
|
}
|
this.phone = registration.phone
|
getRegistrationTeams()
|
.then((teams) => {
|
if (teams && teams.length) {
|
this.teamOptions = teams
|
}
|
})
|
.catch((error) => uni.showToast({ title: error.message, icon: 'none' }))
|
},
|
methods: {
|
clearFieldError(field) {
|
if (this.errors[field]) {
|
this.errors[field] = ''
|
}
|
},
|
resetFieldErrors() {
|
Object.keys(this.errors).forEach((field) => {
|
this.errors[field] = ''
|
})
|
},
|
validateRequiredFields() {
|
this.resetFieldErrors()
|
const requiredFields = [
|
['name', !this.form.name.trim(), '请输入姓名'],
|
['gender', !this.form.gender, '请选择性别'],
|
['guideType', !this.form.guideType, '请选择讲解队或个人'],
|
['idNo', !this.form.idNo.trim(), '请输入身份证号'],
|
['idFront', !this.form.idFront, '请上传身份证正面'],
|
['idBack', !this.form.idBack, '请上传身份证反面'],
|
['guideLicense', !this.form.guideLicense, '请上传导游证']
|
]
|
let firstError = ''
|
requiredFields.forEach(([field, invalid, message]) => {
|
if (!invalid) return
|
this.errors[field] = message
|
if (!firstError) firstError = message
|
})
|
return firstError
|
},
|
goBack() {
|
uni.navigateBack({
|
fail() {
|
routeTo('/pages/register-phone/index')
|
}
|
})
|
},
|
setGender(event) {
|
this.form.gender = this.genderOptions[event.detail.value]
|
this.clearFieldError('gender')
|
},
|
setAge(event) {
|
this.form.age = this.ageOptions[event.detail.value]
|
},
|
setWorkYear(event) {
|
this.form.workYear = this.workYearOptions[event.detail.value]
|
},
|
setTeam(event) {
|
const team = this.teamOptions[event.detail.value]
|
if (!team) return
|
this.form.guideType = team.guideType
|
this.form.teamId = team.id || null
|
this.form.teamName = team.name
|
this.clearFieldError('guideType')
|
},
|
chooseImage(field) {
|
uni.chooseImage({
|
count: 1,
|
success: (res) => {
|
this.form[field] = res.tempFilePaths[0]
|
this.clearFieldError(field)
|
}
|
})
|
},
|
handleIdCardInput(event) {
|
this.form.idNo = (event.detail.value || '')
|
.toUpperCase()
|
.replace(/[^0-9X]/g, '')
|
.slice(0, 18)
|
this.clearFieldError('idNo')
|
},
|
isValidIdCard(value) {
|
const idCardNo = value.trim().toUpperCase()
|
if (!/^[1-9]\d{16}[0-9X]$/.test(idCardNo)) return false
|
|
const birthday = idCardNo.slice(6, 14)
|
const date = new Date(
|
Number(birthday.slice(0, 4)),
|
Number(birthday.slice(4, 6)) - 1,
|
Number(birthday.slice(6, 8))
|
)
|
if (
|
date.getFullYear() !== Number(birthday.slice(0, 4)) ||
|
date.getMonth() !== Number(birthday.slice(4, 6)) - 1 ||
|
date.getDate() !== Number(birthday.slice(6, 8)) ||
|
date > new Date()
|
) return false
|
|
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
|
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
|
const sum = weights.reduce((total, weight, index) => total + Number(idCardNo[index]) * weight, 0)
|
return idCardNo[17] === checkCodes[sum % 11]
|
},
|
submit() {
|
const requiredError = this.validateRequiredFields()
|
if (requiredError) {
|
uni.showToast({ title: requiredError, icon: 'none' })
|
return
|
}
|
this.form.idNo = this.form.idNo.trim().toUpperCase()
|
if (!this.isValidIdCard(this.form.idNo)) {
|
this.errors.idNo = '身份证号格式不正确,请检查末位校验码'
|
uni.showToast({ title: '身份证号格式不正确,请检查末位校验码', icon: 'none' })
|
return
|
}
|
if (this.submitting) return
|
this.submitting = true
|
submitGuideRegistration(this.form)
|
.then(() => {
|
this.successDialogVisible = true
|
})
|
.catch((error) => uni.showToast({ title: error.message, icon: 'none' }))
|
.finally(() => {
|
this.submitting = false
|
})
|
},
|
goLogin() {
|
routeTo('/pages/login/index', 'reLaunch')
|
}
|
}
|
}
|
</script>
|
|
<style>
|
.profile-page {
|
display: flex;
|
flex-direction: column;
|
min-height: 100vh;
|
background: #ffffff;
|
}
|
|
.form-scroll {
|
flex: 1;
|
min-height: 0;
|
}
|
|
.form-wrap {
|
padding: 40rpx 46rpx 110rpx;
|
}
|
|
.field-block {
|
margin-bottom: 42rpx;
|
}
|
|
.field-head {
|
margin-bottom: 20rpx;
|
color: #1f3b5b;
|
font-size: 25rpx;
|
font-weight: 700;
|
}
|
|
.plain-input,
|
.select-row {
|
display: flex;
|
align-items: center;
|
width: 100%;
|
height: 68rpx;
|
padding: 0 16rpx;
|
border: 1rpx solid #cfd5dd;
|
border-radius: 12rpx;
|
color: #1d2939;
|
background: #ffffff;
|
font-size: 22rpx;
|
}
|
|
.field-head-error,
|
.upload-label-error {
|
color: #e95b55;
|
}
|
|
.field-invalid {
|
border-color: #f0746d;
|
background: #fff9f8;
|
}
|
|
.field-error {
|
display: block;
|
margin-top: 9rpx;
|
color: #e95b55;
|
font-size: 23rpx;
|
}
|
|
.select-row {
|
justify-content: space-between;
|
}
|
|
.placeholder {
|
color: #8b95a5;
|
}
|
|
.select-icon {
|
width: 28rpx;
|
height: 28rpx;
|
}
|
|
.upload-section {
|
margin-bottom: 44rpx;
|
}
|
|
.upload-grid.two,
|
.upload-labels.two,
|
.certificate-head {
|
display: grid;
|
grid-template-columns: 1fr 1fr;
|
column-gap: 70rpx;
|
}
|
|
.upload-item {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
width: 94rpx;
|
height: 94rpx;
|
box-sizing: border-box;
|
border-radius: 12rpx;
|
background: #e6e6e6;
|
overflow: hidden;
|
}
|
|
.upload-item.field-invalid {
|
border: 2rpx solid #f0746d;
|
background: #fff9f8;
|
}
|
|
.plus-icon {
|
width: 36rpx;
|
height: 36rpx;
|
}
|
|
.upload-preview {
|
width: 100%;
|
height: 100%;
|
}
|
|
.upload-labels {
|
margin-top: 16rpx;
|
color: #667085;
|
font-size: 22rpx;
|
text-align: center;
|
}
|
|
.upload-errors {
|
display: flex;
|
flex-direction: column;
|
gap: 8rpx;
|
}
|
|
.certificate-head {
|
margin-bottom: 22rpx;
|
color: #1f3b5b;
|
font-size: 24rpx;
|
font-weight: 700;
|
}
|
|
.submit-button {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
width: 100%;
|
height: 92rpx;
|
margin: 58rpx auto 0;
|
border-radius: 12rpx;
|
color: #ffffff;
|
background: #4d8fe8;
|
font-size: 28rpx;
|
font-weight: 700;
|
}
|
</style>
|