Files
delivery-uniapp/pages/registered/registerRiders.vue

1058 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="">
<up-navbar title="骑手注册" leftIcon="" placeholder>
</up-navbar>
<view class="page">
<up-form ref="riderForm" :model="form" :rules="rules" labelPosition="left" labelWidth="90">
<view class="section-title">基础信息</view>
<up-form-item label="真实姓名" prop="username" :required="true">
<up-input v-model="form.username" placeholder="请输入您的姓名" />
</up-form-item>
<up-form-item label="身份证号" prop="idNumber" :required="true">
<up-input v-model="form.idNumber" placeholder="数字开头18位号码" maxlength="18" />
</up-form-item>
<up-form-item label="生效日期" prop="idStartTime" :required="true">
<view class="expiry-row">
<up-input :disabled="true" v-model="form.idStartTime" placeholder="选择生效日期" maxlength="18" @click="startTimeShow = true" />
<up-datetime-picker v-model:show="startTimeShow" v-model="idStartTime" mode="date" placeholder="请选择身份证生效日期" @confirm="changeData($event, 'start')" />
</view>
</up-form-item>
<up-form-item label="失效日期" prop="idEndTime" :required="true">
<view class="expiry-row">
<up-radio-group v-model="form.idValidType" direction="horizontal">
<up-radio :name="1" label="长期有效"></up-radio>
<up-radio :name="2" label="选择失效日期"></up-radio>
</up-radio-group>
<up-input v-if="form.idValidType === 2" :disabled="true" v-model="form.idEndTime" placeholder="选择失效日期" maxlength="18" @click="endTimeShow = true" />
<up-datetime-picker v-model:show="endTimeShow" v-model="idEndTime" mode="date"
placeholder="选择失效日期" @confirm="changeData($event, 'end')" />
</view>
</up-form-item>
<up-form-item label="性别" prop="gender" :required="true">
<up-radio-group v-model="form.gender" direction="horizontal">
<up-radio :name="1" label="男"></up-radio>
<up-radio :name="2" label="女"></up-radio>
</up-radio-group>
</up-form-item>
<up-form-item label="紧急联系人姓名" prop="emergencyContactName" :required="true">
<up-input v-model="form.emergencyContactName" placeholder="请输入" />
</up-form-item>
<up-form-item label="紧急联系人手机" prop="emergencyContactPhone" :required="true">
<up-input v-model="form.emergencyContactPhone" placeholder="请输入" type="tel" maxlength="11" />
</up-form-item>
<up-form-item label="上传身份证正反面" prop="idImages" :required="true">
<view class="upload-row">
<view class="upload-box">
<up-upload :max-count="1" :show-file-list="false" @afterRead="onUploadFront">
<view class="upload-placeholder" v-if="!frontImage">
<up-icon name="+" />
</view>
<up-image v-else width="120" height="80" :src="frontImage" mode="aspectFill" />
</up-upload>
<text class="hint">人像面</text>
</view>
<view class="upload-box">
<up-upload :max-count="1" :show-file-list="false" @afterRead="onUploadBack">
<view class="upload-placeholder" v-if="!backImage">
<up-icon name="+" />
</view>
<up-image v-else width="120" height="80" :src="backImage" mode="aspectFill" />
</up-upload>
<text class="hint">国徽面</text>
</view>
</view>
</up-form-item>
<view class="section-title">接单选择</view>
<up-form-item label="职业身份" prop="occupationType" :required="true">
<up-radio-group v-model="form.occupationType" direction="horizontal">
<up-radio :name="1" label="在校学生"></up-radio>
<up-radio :name="2" label="社会人员/职工"></up-radio>
</up-radio-group>
</up-form-item>
<!-- 学生视图兼职意愿可兼职时段健康证 -->
<template v-if="form.occupationType === 1">
<up-form-item label="兼职意愿" prop="employmentType" :required="true">
<up-input :disabled="true" v-model="partTimeLabel" placeholder="选择兼职意愿" maxlength="18" @click="employmentTypeShow = true" />
<up-picker v-model:show="employmentTypeShow" :columns="partTimeOptions" v-model="partTimeLabel"
@confirm="onPartTimeConfirm"></up-picker>
</up-form-item>
<up-form-item label="请选择可兼职时段" prop="partTimePeriods">
<view class="choose-row" @click="timeShow1 = true">
<text class="muted">{{ partTimePeriodsLabel }}</text>
<text class="status"
:class="{empty: form.partTimePeriods.length===0}">{{ form.partTimePeriods.length===0 ? '待完善 >' : '已完善' }}</text>
</view>
</up-form-item>
<up-popup :show="timeShow1" mode="bottom" @close="timeShow1 = false">
<view class="popup-content">
<view class="popup-header">
<text>选择可兼职时段</text>
</view>
<view class="popup-body">
<!-- 网格每行为一天三段时段按钮可切换选中 -->
<view class="time-grid">
<view class="time-row" v-for="(day, dayIndex) in days" :key="dayIndex">
<view class="day-label">{{ day }}</view>
<view class="slots">
<view class="slot-btn" :class="{selected: isSlotSelected(dayIndex, 0)}"
@click="toggleSlot(dayIndex, 0)" key="m">08:00~13:00</view>
<view class="slot-btn" :class="{selected: isSlotSelected(dayIndex, 1)}"
@click="toggleSlot(dayIndex, 1)" key="a">13:00~17:30</view>
<view class="slot-btn" :class="{selected: isSlotSelected(dayIndex, 2)}"
@click="toggleSlot(dayIndex, 2)" key="e">17:00~22:30</view>
</view>
</view>
</view>
</view>
<view class="popup-footer">
<up-button plain @click="onCancelTime">取消</up-button>
<up-button type="primary" @click="onSaveTime">保存</up-button>
</view>
</view>
</up-popup>
</template>
<!-- 社会人员/职工视图类别意向城市健康证 -->
<template v-else>
<up-form-item label="类别" prop="category" :required="true">
<up-picker hasInput :columns="categoryOptions" @confirm="onCategoryConfirm">
<template #trigger>
<up-input readonly v-model="categoryLabel" placeholder="请选择类别" />
</template>
</up-picker>
</up-form-item>
<up-form-item label="意向城市" prop="city" :required="true">
<up-input readonly v-model="cityLabel" placeholder="请选择" @tap="regionShow = true" />
</up-form-item>
</template>
<!-- 健康证学生/社会人员皆可上传 -->
<up-form-item label="健康证" prop="healthCert">
<view class="health-row" @click="openHealthPopup">
<view class="health-left">上传健康证</view>
<view class="health-right">
<up-icon name="arrow-right" color="#6c6c6c" size="21"></up-icon>
</view>
</view>
</up-form-item>
<view class="submit-row">
<up-button type="primary" @click="onSubmit">保存并下一步完善工资结算信息</up-button>
</view>
</up-form>
</view>
<!-- 健康证信息弹框 -->
<up-popup :show="popupShow" mode="bottom" @close="onCancelHealth" :round="16" safeAreaInsetBottom>
<view class="health-popup">
<view class="health-popup-header">
<text class="title">上传健康证</text>
</view>
<view class="health-popup-body">
<view class="field-row">
<text class="label">编号</text>
<up-input v-model="healthNumber" placeholder="请输入健康证编号" class="field-input" />
</view>
<view class="field-row">
<text class="label">类别</text>
<up-input v-model="healthCategory" placeholder="请输入健康证类别" class="field-input" />
</view>
<view class="field-row date-row">
<text class="label">有效日期</text>
<up-datetime-picker hasInput v-model="healthValidStart" mode="date" placeholder="年/月/日"
class="date-input" />
<text class="dash"> </text>
<up-datetime-picker hasInput v-model="healthValidEnd" mode="date" placeholder="年/月/日" class="date-input" />
</view>
<view class="upload-list">
<text class="upload-title">上传健康证</text>
<view class="upload-row-inner">
<view v-for="(img, idx) in healthImages" :key="idx" class="upload-item">
<up-image :src="img" class="upload-thumb" mode="aspectFill" />
<view class="remove-btn" @click.stop="removeHealthImage(idx)">×</view>
</view>
<up-upload :max-count="1" :show-file-list="false" @afterRead="onUploadHealth">
<view class="upload-add" v-if="healthImages.length < 4">
<up-icon name="+" />
</view>
</up-upload>
</view>
</view>
</view>
<view class="health-popup-footer">
<up-button plain @click="onCancelHealth">取消</up-button>
<up-button type="primary" class="save-btn" @click="onSaveHealth">保存</up-button>
</view>
</view>
</up-popup>
<su-region-picker :show="regionShow" @confirm="onRegionConfirm" @cancel="regionShow = false" />
</view>
</template>
<script setup>
import {
reactive,
ref,
computed,
watch,
onBeforeMount
} from 'vue'
import AreaApi from '@/sheep/api/system/area';
import { baseUrl, apiPath } from '@/sheep/config';
// import SuRegionPicker from 'sheep/ui/su-region-picker/su-region-picker.vue'
import dayjs from 'dayjs';
const form = reactive({
username: '',
idNumber: '',
idStartTime: '',
idEndTime: '',
idValidType: 2, // 1=长期 2=非长期
gender: 1, // 1=男 2=女
emergencyContactName: '',
emergencyContactPhone: '',
idImages: [],
// 接单选择相关字段
occupationType: 1, // 1=在校学生 2=社会人员/职工
employmentType: 1, // 1=全职 2=兼职
partTimePeriods: [],
healthCertificateUrl: '',
// 健康证详情字段(弹窗保存到这里)
healthCertificateNo: '',
healthCertificateType: 1,
healthCertificateStartTime: '',
healthCertificateEndTime: '',
// 社会人员/职工视图字段
category: '',
city: '',
// 银行相关字段(用于下一个页面)
bankProvince: '',
bankProvinceCode: '',
bankCity: '',
bankCityCode: '',
bankDistrict: '',
bankDistrictCode: '',
bankCardNumber: '',
cardholderName: '',
bankAccountPhone: '',
code: '',
})
const startTimeShow = ref(false)
const endTimeShow = ref(false)
const employmentTypeShow = ref(false)
const idStartTime = ref('')
const idEndTime = ref('')
const timeShow1 = ref(false)
const partTimeOptions = reactive([
['长期至少1学期', '非长期(临时/偶尔兼职)']
])
const partTimeLabel = ref([])
const categoryOptions = [
['全职', '兼职']
]
const categoryLabel = ref([])
const cityOptions = [{
text: '请选择城市',
value: ''
},
{
text: '北京市',
value: 'beijing'
},
{
text: '上海市',
value: 'shanghai'
},
]
const cityLabel = ref('')
const regionShow = ref(false)
// 可选时段网格数据与选择状态
const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
const timeSlots = ['08:00~13:00', '13:00~17:30', '17:00~22:30']
// 使用二维布尔数组表示选择状态selectedGrid[dayIndex][slotIndex] = true/false
const selectedGrid = reactive(Array.from({
length: days.length
}, () => [false, false, false]))
// 弹窗临时副本,打开时拷贝 selectedGrid 到 tempSelected用于取消恢复
const tempSelected = reactive(Array.from({
length: days.length
}, () => [false, false, false]))
const popupShow = ref(false)
const changeData = (data, type) => {
if (type == 'start') {
form.idStartTime = dayjs(data.value).format('YYYY-MM-DD');
startTimeShow.value = false;
}else {
form.idEndTime = dayjs(data.value).format('YYYY-MM-DD');
endTimeShow.value = false;
}
console.log("this.form", form);
}
function isSlotSelected(dayIndex, slotIndex) {
return tempSelected[dayIndex][slotIndex]
}
function toggleSlot(dayIndex, slotIndex) {
tempSelected[dayIndex][slotIndex] = !tempSelected[dayIndex][slotIndex]
}
function onCancelTime() {
// 恢复原选中状态并关闭弹窗
for (let i = 0; i < days.length; i++) {
for (let j = 0; j < timeSlots.length; j++) {
tempSelected[i][j] = selectedGrid[i][j]
}
}
timeShow1.value = false
}
function onSaveTime() {
// 将 tempSelected 同步到 selectedGrid 和 form.partTimePeriods保存为可读文本
const selections = []
for (let i = 0; i < days.length; i++) {
for (let j = 0; j < timeSlots.length; j++) {
selectedGrid[i][j] = tempSelected[i][j]
if (tempSelected[i][j]) {
selections.push(`${days[i]} ${timeSlots[j]}`)
}
}
}
form.partTimePeriods = selections
timeShow1.value = false
}
// 打开弹窗时将当前选择拷贝到 tempSelected
watch(timeShow1, (val) => {
if (val) {
for (let i = 0; i < days.length; i++) {
for (let j = 0; j < timeSlots.length; j++) {
tempSelected[i][j] = selectedGrid[i][j]
}
}
}
})
const rules = {
username: [{
required: true,
message: '请输入真实姓名'
}],
idNumber: [{
required: true,
message: '请输入身份证号'
},
{
pattern: /^[0-9A-Za-z]{15,18}$/,
message: '请输入正确的身份证号'
},
],
idStartTime: [{
required: true,
message: '请选择生效日期'
}],
idEndTime: [{
validator(value) {
if (form.idValidType === 2 && !value) {
return false
}
return true
},
message: '请选择失效日期',
}, ],
gender: [{
required: true,
message: '请选择性别'
}],
emergencyContactName: [{
required: true,
message: '请输入紧急联系人姓名'
}],
emergencyContactPhone: [{
required: true,
message: '请输入紧急联系人手机'
},
{
pattern: /^1\d{10}$/,
message: '请输入正确的手机号码'
},
],
idImages: [{
required: true,
validator(value) {
console.log("form.idImages身份证", form.idImages);
// 校验是否上传了正反面两张图片
if (!Array.isArray(form.idImages)) {
return false;
}
const frontUrl = form.idImages[0] || '';
const backUrl = form.idImages[1] || '';
if (!frontUrl || !backUrl) {
return false;
}
return true;
},
message: '请上传身份证正面和背面照片',
}],
}
const riderForm = ref(null)
const frontImage = ref('')
const backImage = ref('')
const healthCert = ref('')
// 健康证弹窗临时状态
const healthImages = reactive([])
const healthNumber = ref('')
const healthCategory = ref('')
const healthValidStart = ref('')
const healthValidEnd = ref('')
const partTimePeriodsLabel = computed(() => {
return form.partTimePeriods.length ? form.partTimePeriods.join('、') : ''
})
// 上传单个文件到 OSS
async function uploadFile(filePath) {
const token = uni.getStorageSync('token');
return new Promise((resolve, reject) => {
uni.uploadFile({
url: baseUrl + apiPath + '/app/file/uploadOss', // 上传接口地址
header: {
'Authorization': token
},
filePath: filePath,
name: 'file',
success: (res) => {
if (res.statusCode === 200) {
try {
const data = JSON.parse(res.data);
if (data.code === 0 && data.data) {
resolve(data.data);
} else {
console.error('上传失败:', data.msg);
resolve(null);
}
} catch (e) {
console.error('解析上传响应失败:', e);
resolve(null);
}
} else {
console.error('上传失败:', res.statusCode);
resolve(null);
}
},
fail: (err) => {
console.error('上传请求失败:', err);
reject(err);
}
});
});
}
// 上传图片处理
async function handleUpload(file, isFront = true) {
const url = file?.url || file?.path || file?.thumb || '';
if (!url) {
console.error('未找到图片路径');
return;
}
try {
uni.showLoading({ title: '上传中...' });
// 上传到 OSS
const uploadedUrl = await uploadFile(url);
uni.hideLoading();
if (uploadedUrl) {
if (isFront) {
frontImage.value = uploadedUrl;
form.idImages = [uploadedUrl, form.idImages[1] || ''];
} else {
backImage.value = uploadedUrl;
form.idImages = [form.idImages[0] || '', uploadedUrl];
}
} else {
uni.showToast({
title: '图片上传失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('上传图片异常:', error);
uni.showToast({
title: '上传失败,请重试',
icon: 'none'
});
}
}
function onUploadFront(event) {
const { file } = Array.isArray(event) ? event[0] : (event.detail || event);
console.log("文件:", file);
handleUpload(file, true);
}
function onUploadBack(event) {
const { file } = Array.isArray(event) ? event[0] : (event.detail || event);
handleUpload(file, false);
}
// 健康证图片处理(支持多图)
async function handleUploadHealth(file) {
const url = file?.url || file?.path || file?.thumb || '';
if (!url) {
console.error('未找到图片路径');
return;
}
try {
uni.showLoading({ title: '上传中...' });
// 上传到 OSS
const uploadedUrl = await uploadFile(url);
uni.hideLoading();
if (uploadedUrl) {
// 限制最多 4 张预览图
if (healthImages.length < 4) {
healthImages.push(uploadedUrl);
}
healthCert.value = uploadedUrl;
// 保存到表单字段
form.healthCertificateUrl = uploadedUrl;
} else {
uni.showToast({
title: '图片上传失败',
icon: 'none'
});
}
} catch (error) {
uni.hideLoading();
console.error('上传图片异常:', error);
uni.showToast({
title: '上传失败,请重试',
icon: 'none'
});
}
}
function onUploadHealth(event) {
const { file } = Array.isArray(event) ? event[0] : (event.detail || event);
handleUploadHealth(file);
}
function onPartTimeConfirm(selected) {
const first = Array.isArray(selected) ? selected[0] : selected
if (first && (first.value || first.text)) {
const text = first.text || first.value
// 兼职意愿转换:全职->1, 兼职->2
const intentMap = {
'长期至少1学期': 1,
'非长期(临时/偶尔兼职)': 2
}
form.employmentType = intentMap[text] || 1
partTimeLabel.value = text
employmentTypeShow.value = false
}
}
function onCategoryConfirm(selected) {
const first = Array.isArray(selected) ? selected[0] : selected
if (first && (first.value || first.text)) {
form.category = first.value || first.text
categoryLabel.value = first.text || first.value
}
}
function onCityConfirm(selected) {
const first = Array.isArray(selected) ? selected[0] : selected
if (first && (first.value || first.text)) {
form.city = first.value || first.text
cityLabel.value = first.text || first.value
}
}
function onRegionConfirm(result) {
// result: { province_name, province_id, city_name, city_id, district_name, district_id }
form.city = result
cityLabel.value = `${result.province_name || ''} ${result.city_name || ''} ${result.district_name || ''}`.trim()
regionShow.value = false
}
function openHealthPopup() {
// 从表单恢复到弹窗临时状态
healthImages.splice(0, healthImages.length)
if (form.healthCertificateUrl) {
healthImages.push(form.healthCertificateUrl)
}
healthNumber.value = form.healthCertificateNo || ''
healthCategory.value = form.healthCertificateType?.toString() || ''
healthValidStart.value = form.healthCertificateStartTime || ''
healthValidEnd.value = form.healthCertificateEndTime || ''
popupShow.value = true
}
function removeHealthImage(index) {
if (index >= 0 && index < healthImages.length) {
healthImages.splice(index, 1)
}
}
function onCancelHealth() {
// 直接关闭弹窗,放弃临时更改
popupShow.value = false
}
function onSaveHealth() {
// 保存弹窗数据回表单
form.healthCertificateUrl = healthImages.length ? healthImages[0] : ''
healthCert.value = form.healthCertificateUrl
form.healthCertificateNo = healthNumber.value
// 类别转换:食品卫生类->1, 医疗卫生类->2, 教育类->3
const categoryMap = {
'食品卫生类': 1,
'医疗卫生类': 2,
'教育类': 3
}
const categoryText = healthCategory.value.trim()
form.healthCertificateType = categoryMap[categoryText] || 1
form.healthCertificateStartTime = healthValidStart.value
form.healthCertificateEndTime = healthValidEnd.value
popupShow.value = false
}
async function onSubmit() {
console.log("form.idStartTime", form.idStartTime);
try {
await riderForm.value.validate()
console.log('提交表单', JSON.parse(JSON.stringify(form)))
// 构建提交给后端的数据(包含两个页面的字段)
const submitData = {
// 第一个页面的字段
username: form.username,
idNumber: form.idNumber,
idStartTime: form.idStartTime,
idEndTime: form.idEndTime,
idValidType: form.idValidType,
gender: form.gender,
emergencyContactName: form.emergencyContactName,
emergencyContactPhone: form.emergencyContactPhone,
idFrontUrl: form.idImages[0] || '',
idBackUrl: form.idImages[1] || '',
occupationType: form.occupationType,
employmentType: form.employmentType,
partTimePeriods: form.partTimePeriods,
healthCertificateUrl: form.healthCertificateUrl,
healthCertificateNo: form.healthCertificateNo,
healthCertificateType: form.healthCertificateType,
healthCertificateStartTime: form.healthCertificateStartTime,
healthCertificateEndTime: form.healthCertificateEndTime,
// 第二个页面的字段(先缓存空值,会在下个页面填充)
bankAccountPhone: form.bankAccountPhone || '',
bankCardNumber: form.bankCardNumber || '',
bankCity: form.bankCity || '',
bankCityCode: form.bankCityCode || '',
bankDistrict: form.bankDistrict || '',
bankDistrictCode: form.bankDistrictCode || '',
bankProvince: form.bankProvince || '',
bankProvinceCode: form.bankProvinceCode || '',
cardholderName: form.cardholderName || '',
code: form.code || '',
}
// 将注册信息临时存储,供结算页继续使用
try {
uni.setStorageSync('riderRegisterData', JSON.stringify(submitData))
} catch (err) {
console.warn('存储注册信息失败', err)
}
// 跳转到工资结算账户信息页面
uni.navigateTo({
url: '/pages/registered/accountInfo'
})
} catch (e) {
console.warn('表单校验未通过', e)
}
}
const rightClick = () => {
console.log('rightClick');
};
onBeforeMount(() => {
// 尝试从缓存恢复数据
try {
const saved = uni.getStorageSync('riderRegisterData') || null
if (saved) {
// 恢复表单数据
form.username = saved.username || ''
form.idNumber = saved.idNumber || ''
form.idStartTime = saved.idStartTime || ''
form.idEndTime = saved.idEndTime || ''
form.idValidType = saved.idValidType || 2
form.gender = saved.gender || 1
form.emergencyContactName = saved.emergencyContactName || ''
form.emergencyContactPhone = saved.emergencyContactPhone || ''
// 恢复身份证图片
if (saved.idFrontUrl) {
form.idImages[0] = saved.idFrontUrl
frontImage.value = saved.idFrontUrl
}
if (saved.idBackUrl) {
form.idImages[1] = saved.idBackUrl
backImage.value = saved.idBackUrl
}
form.occupationType = saved.occupationType || 1
form.employmentType = saved.employmentType || 1
form.partTimePeriods = saved.partTimePeriods || []
// 恢复健康证数据
form.healthCertificateUrl = saved.healthCertificateUrl || ''
form.healthCertificateNo = saved.healthCertificateNo || ''
form.healthCertificateType = saved.healthCertificateType || 1
form.healthCertificateStartTime = saved.healthCertificateStartTime || ''
form.healthCertificateEndTime = saved.healthCertificateEndTime || ''
}
} catch (err) {
console.warn('读取缓存数据失败', err)
}
if (!!uni.getStorageSync('areaData')) {
return;
}
// 提前加载省市区数据
AreaApi.getAreaTree().then((res) => {
if (res.code === 0) {
uni.setStorageSync('areaData', res.data);
}
});
});
// 监听表单数据变化,自动保存到缓存
watch(form, () => {
try {
uni.setStorageSync('riderRegisterData', {
username: form.username,
idNumber: form.idNumber,
idStartTime: form.idStartTime,
idEndTime: form.idEndTime,
idValidType: form.idValidType,
gender: form.gender,
emergencyContactName: form.emergencyContactName,
emergencyContactPhone: form.emergencyContactPhone,
idFrontUrl: form.idImages[0] || '',
idBackUrl: form.idImages[1] || '',
occupationType: form.occupationType,
employmentType: form.employmentType,
partTimePeriods: form.partTimePeriods,
healthCertificateUrl: form.healthCertificateUrl,
healthCertificateNo: form.healthCertificateNo,
healthCertificateType: form.healthCertificateType,
healthCertificateStartTime: form.healthCertificateStartTime,
healthCertificateEndTime: form.healthCertificateEndTime,
// 银行信息(如果已经在第二个页面填写了)
bankAccountPhone: form.bankAccountPhone || '',
bankCardNumber: form.bankCardNumber || '',
bankCity: form.bankCity || '',
bankCityCode: form.bankCityCode || '',
bankDistrict: form.bankDistrict || '',
bankDistrictCode: form.bankDistrictCode || '',
bankProvince: form.bankProvince || '',
bankProvinceCode: form.bankProvinceCode || '',
cardholderName: form.cardholderName || '',
code: form.code || '',
})
} catch (err) {
console.warn('保存缓存数据失败', err)
}
}, { deep: true })
</script>
<style lang="scss" scoped>
.page {
padding: 38rpx;
background: #fff;
}
.section-title {
padding: 10px 0;
color: #666;
font-weight: 600;
}
.expiry-row {
display: flex;
flex-direction: column;
gap: 8px;
}
.upload-row {
display: flex;
gap: 12px;
}
.upload-box {
display: flex;
flex-direction: column;
align-items: center;
}
.upload-placeholder {
width: 88px;
height: 60px;
border: 1px dashed #ddd;
display: flex;
align-items: center;
justify-content: center;
}
.thumb {
width: 160rpx;
height: 120rpx;
border-radius: 5rpx;
}
.hint {
margin-top: 6px;
color: #999;
font-size: 12px;
}
.submit-row {
margin-top: 20px;
display: flex;
justify-content: center;
}
.time-grid {
padding: 10px 0;
}
.time-row {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.day-label {
width: 90rpx;
color: #333;
}
.slots {
display: flex;
gap: 10px;
flex: 1;
}
.slot-btn {
padding: 8px 12px;
border-radius: 6px;
background: #f5f5f5;
color: #333;
font-size: 24rpx;
}
.slot-btn.selected {
background: #09aaff;
color: #fff;
}
.popup-footer {
display: flex;
gap: 10px;
padding: 12px;
justify-content: space-between;
}
.popup-content {
padding: 50rpx 20rpx 20rpx;
}
.popup-body {
padding: 10rpx 15rpx 20rpx;
}
/* 健康证相关样式 */
.health-row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-right: 10rpx;
}
.health-left {
color: #333;
}
.health-right {
display: flex;
align-items: center;
gap: 10rpx;
}
.health-thumbs {
display: flex;
gap: 10rpx;
align-items: center;
}
.health-thumb-wrap {
width: 88px;
height: 88px;
}
.health-thumb {
width: 88px;
height: 88px;
border-radius: 6px;
}
.health-add {
width: 88px;
height: 88px;
border: 1px dashed #ddd;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
color: #999;
}
.health-popup {
padding: 30rpx 30rpx 30rpx;
background: #fff;
}
.health-popup-header .title {
font-weight: 700;
font-size: 32rpx;
text-align: center;
margin-bottom: 10px;
}
.health-popup-body {
padding: 10rpx 0 20rpx;
}
.field-row {
display: flex;
align-items: center;
gap: 38rpx;
margin-bottom: 12px;
}
.label {
width: 90rpx;
color: #666;
}
.field-input {
flex: 1;
}
.date-row .date-input {
width: 35%;
}
.dash {
width: 10rpx;
text-align: center;
color: #999;
}
.upload-list {
margin-top: 10px;
}
.upload-title {
color: #333;
margin-bottom: 8px;
display: block;
}
.upload-row-inner {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.upload-item {
position: relative;
width: 88px;
height: 88px;
}
.upload-thumb {
width: 88px;
height: 88px;
border-radius: 6px;
}
.remove-btn {
position: absolute;
top: -6px;
right: -6px;
background: rgba(0, 0, 0, 0.6);
color: #fff;
width: 24px;
height: 24px;
border-radius: 12px;
text-align: center;
line-height: 24px;
font-size: 18px;
}
.upload-add {
width: 88px;
height: 88px;
border: 1px dashed #ddd;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
color: #999;
}
.health-popup-footer {
display: flex;
justify-content: space-between;
padding-top: 16px;
gap: 12px;
}
.save-btn {
width: 240rpx;
align-self: center;
}
</style>