hookehuyr

feat(collectionSettings): 添加身份证信息上传和有效期选择功能

新增身份证人像面、国徽面上传功能,包含预览和删除操作
添加身份证有效期开始和结束日期选择器
增加身份证地址输入字段
更新表单验证逻辑以包含新增字段
......@@ -356,3 +356,121 @@
}
}
}
// 身份证图片上传样式
.idcard-img-container {
margin-top: 20rpx;
}
.idcard-img-upload {
width: 100%;
height: 200rpx;
border: 2rpx dashed #ddd;
border-radius: 12rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fafafa;
transition: all 0.3s ease;
&:active {
background-color: #f0f0f0;
border-color: #ccc;
}
.upload-icon {
font-size: 48rpx;
color: #999;
margin-bottom: 10rpx;
}
.upload-text {
font-size: 28rpx;
color: #999;
}
}
.idcard-img-preview {
width: 100%;
position: relative;
.idcard-img-image {
width: 100%;
height: 200rpx;
border-radius: 12rpx;
object-fit: cover;
}
.idcard-img-actions {
display: flex;
justify-content: space-between;
margin-top: 20rpx;
.change-img-btn {
flex: 1;
text-align: center;
padding: 20rpx;
background-color: #ffa500;
color: white;
border-radius: 8rpx;
font-size: 28rpx;
margin-right: 10rpx;
&:active {
background-color: #e6940a;
}
}
.delete-img-btn {
flex: 1;
text-align: center;
padding: 20rpx;
background-color: #ff4757;
color: white;
border-radius: 8rpx;
font-size: 28rpx;
margin-left: 10rpx;
&:active {
background-color: #e63946;
}
}
}
}
// 日期选择器样式
.date-selector {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 0;
border-bottom: 1rpx solid #eee;
min-height: 80rpx;
.date-text {
font-size: 32rpx;
color: #999;
&.date-selected {
color: #333;
}
}
.arrow-down {
font-size: 28rpx;
color: #999;
// transform: rotate(90deg);
}
&:active {
background-color: #f5f5f5;
}
}
// 必填项红色*号标记
.required-mark {
color: #ff4757;
font-size: 28rpx;
margin-right: 8rpx;
}
......
<!--
* @Date: 2022-09-19 14:11:06
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-08-07 14:52:58
* @LastEditTime: 2025-08-07 15:12:29
* @FilePath: /jgdl/src/pages/collectionSettings/index.vue
* @Description: 收款设置
-->
......@@ -55,7 +55,7 @@
<view class="form-content">
<view class="form-item">
<text class="form-label">银行名称</text>
<text class="form-label"><text class="required-mark">*</text>银行名称</text>
<view class="bank-selector" @click="showBankPicker">
<text class="bank-text" :class="{ 'bank-selected': tempAccountInfo.bankName }">
{{ getBankNameById(tempAccountInfo.bankName) || '请选择银行' }}
......@@ -65,7 +65,7 @@
</view>
<view class="form-item">
<text class="form-label">银行账号</text>
<text class="form-label"><text class="required-mark">*</text>银行账号</text>
<input
v-model="tempAccountInfo.bankAccount"
placeholder="请输入银行账号"
......@@ -77,7 +77,7 @@
<!-- 银行卡正面照片上传 -->
<view class="form-item">
<text class="form-label">银行卡正面照</text>
<text class="form-label"><text class="required-mark">*</text>银行卡正面照</text>
<view class="bank-img-container">
<view v-if="tempAccountInfo.bankImg" class="bank-img-preview">
<image
......@@ -111,7 +111,7 @@
type="primary"
@click="saveAccountInfo"
color="#ffa500"
:disabled="!tempAccountInfo.bankName || !tempAccountInfo.bankAccount"
:disabled="!tempAccountInfo.bankName || !tempAccountInfo.bankAccount || !tempAccountInfo.bankImg"
class="footer-btn footer-btn-save"
>
保存
......@@ -152,7 +152,7 @@
<view class="form-content">
<view class="form-item">
<text class="form-label">用户名称</text>
<text class="form-label"><text class="required-mark">*</text>用户名称</text>
<input
v-model="tempIdentityInfo.userName"
placeholder="请输入真实姓名"
......@@ -161,7 +161,7 @@
</view>
<view class="form-item">
<text class="form-label">身份证号码</text>
<text class="form-label"><text class="required-mark">*</text>身份证号码</text>
<input
v-model="tempIdentityInfo.idCard"
placeholder="请输入身份证号码"
......@@ -172,6 +172,85 @@
/>
<text v-if="idCardError" class="error-text">{{ idCardError }}</text>
</view>
<!-- 身份证人像面照片上传 -->
<view class="form-item">
<text class="form-label"><text class="required-mark">*</text>身份证人像面</text>
<view class="idcard-img-container">
<view v-if="tempIdentityInfo.idcard_1_img" class="idcard-img-preview">
<image
:src="tempIdentityInfo.idcard_1_img"
class="idcard-img-image"
mode="aspectFill"
@tap="previewIdcard1Img"
/>
<view class="idcard-img-actions">
<text class="change-img-btn" @click="changeIdcard1Img">重新上传</text>
<text class="delete-img-btn" @click="deleteIdcard1Img">删除</text>
</view>
</view>
<view v-else class="idcard-img-upload" @click="changeIdcard1Img">
<text class="upload-icon">+</text>
<text class="upload-text">上传身份证人像面</text>
</view>
</view>
</view>
<!-- 身份证国徽面照片上传 -->
<view class="form-item">
<text class="form-label"><text class="required-mark">*</text>身份证国徽面</text>
<view class="idcard-img-container">
<view v-if="tempIdentityInfo.idcard_2_img" class="idcard-img-preview">
<image
:src="tempIdentityInfo.idcard_2_img"
class="idcard-img-image"
mode="aspectFill"
@tap="previewIdcard2Img"
/>
<view class="idcard-img-actions">
<text class="change-img-btn" @click="changeIdcard2Img">重新上传</text>
<text class="delete-img-btn" @click="deleteIdcard2Img">删除</text>
</view>
</view>
<view v-else class="idcard-img-upload" @click="changeIdcard2Img">
<text class="upload-icon">+</text>
<text class="upload-text">上传身份证国徽面</text>
</view>
</view>
</view>
<!-- 证件有效期开始日期 -->
<view class="form-item">
<text class="form-label"><text class="required-mark">*</text>证件有效期开始</text>
<view class="date-selector" @click="showIdcardBeginDatePicker">
<text class="date-text" :class="{ 'date-selected': tempIdentityInfo.idcard_effect_begin }">
{{ tempIdentityInfo.idcard_effect_begin || '请选择开始日期' }}
</text>
<text class="arrow-down">></text>
</view>
</view>
<!-- 证件有效期结束日期 -->
<view class="form-item">
<text class="form-label"><text class="required-mark">*</text>证件有效期结束</text>
<view class="date-selector" @click="showIdcardEndDatePicker">
<text class="date-text" :class="{ 'date-selected': tempIdentityInfo.idcard_effect_end }">
{{ tempIdentityInfo.idcard_effect_end || '请选择结束日期' }}
</text>
<text class="arrow-down">></text>
</view>
</view>
<!-- 身份证地址 -->
<view class="form-item">
<text class="form-label"><text class="required-mark">*</text>身份证地址</text>
<textarea
v-model="tempIdentityInfo.idcard_address"
placeholder="请输入身份证地址"
class="form-input native-input"
:cursor-spacing="50"
/>
</view>
</view>
<view class="modal-footer">
......@@ -186,7 +265,7 @@
type="primary"
@click="saveIdentityInfo"
color="#ffa500"
:disabled="!tempIdentityInfo.userName || !tempIdentityInfo.idCard || !!idCardError"
:disabled="!tempIdentityInfo.userName || !tempIdentityInfo.idCard || !tempIdentityInfo.idcard_1_img || !tempIdentityInfo.idcard_2_img || !tempIdentityInfo.idcard_effect_begin || !tempIdentityInfo.idcard_effect_end || !tempIdentityInfo.idcard_address || !!idCardError"
class="footer-btn footer-btn-save"
>
保存
......@@ -195,6 +274,36 @@
</view>
</nut-popup>
<!-- 身份证有效期开始日期选择器 -->
<nut-popup
v-model:visible="showIdcardBeginDate"
position="bottom"
:style="{ height: '50%' }"
>
<nut-date-picker
v-model="idcardBeginDate"
type="date"
title="选择证件有效期开始日期"
@confirm="onIdcardBeginDateConfirm"
@cancel="showIdcardBeginDate = false"
/>
</nut-popup>
<!-- 身份证有效期结束日期选择器 -->
<nut-popup
v-model:visible="showIdcardEndDate"
position="bottom"
:style="{ height: '50%' }"
>
<nut-date-picker
v-model="idcardEndDate"
type="date"
title="选择证件有效期结束日期"
@confirm="onIdcardEndDateConfirm"
@cancel="showIdcardEndDate = false"
/>
</nut-popup>
<!-- 固定返回按钮 - 只有当参数target=sell时才显示 -->
<view v-if="showBackButton" class="fixed-back-btn" @click="goBack">
<text class="back-text">返回</text>
......@@ -244,7 +353,12 @@ const accountInfo = ref({
*/
const identityInfo = ref({
userName: '',
idCard: ''
idCard: '',
idcard_1_img: '',
idcard_2_img: '',
idcard_effect_begin: '',
idcard_effect_end: '',
idcard_address: ''
});
/**
......@@ -261,7 +375,12 @@ const tempAccountInfo = ref({
*/
const tempIdentityInfo = ref({
userName: '',
idCard: ''
idCard: '',
idcard_1_img: '',
idcard_2_img: '',
idcard_effect_begin: '',
idcard_effect_end: '',
idcard_address: ''
});
/**
......@@ -270,12 +389,20 @@ const tempIdentityInfo = ref({
const showAccountModal = ref(false);
const showIdentityModal = ref(false);
const showBankModal = ref(false);
const showIdcardBeginDate = ref(false);
const showIdcardEndDate = ref(false);
/**
* 身份证号码错误信息
*/
const idCardError = ref('');
/**
* 日期选择器相关状态
*/
const idcardBeginDate = ref(new Date());
const idcardEndDate = ref(new Date());
// 银行选择器当前选中的值
const bankPickerValue = ref([]);
......@@ -324,7 +451,12 @@ const getUserInfo = async () => {
if (userInfo.name && userInfo.idcard) {
identityInfo.value = {
userName: userInfo.name,
idCard: userInfo.idcard
idCard: userInfo.idcard,
idcard_1_img: userInfo.idcard_1_img || '',
idcard_2_img: userInfo.idcard_2_img || '',
idcard_effect_begin: userInfo.idcard_effect_begin || '',
idcard_effect_end: userInfo.idcard_effect_end || '',
idcard_address: userInfo.idcard_address || ''
};
}
}
......@@ -500,7 +632,15 @@ const openIdentityModal = () => {
*/
const closeIdentityModal = () => {
showIdentityModal.value = false;
tempIdentityInfo.value = { userName: '', idCard: '' };
tempIdentityInfo.value = {
userName: '',
idCard: '',
idcard_1_img: '',
idcard_2_img: '',
idcard_effect_begin: '',
idcard_effect_end: '',
idcard_address: ''
};
idCardError.value = '';
};
......@@ -610,6 +750,204 @@ const deleteBankImg = () => {
}
/**
* 选择身份证人像面照片
*/
const changeIdcard1Img = () => {
Taro.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
const tempFilePath = res.tempFilePaths[0]
uploadIdcardImage(tempFilePath, 'idcard_1_img')
},
fail: function () {
Taro.showToast({
title: '选择图片失败',
icon: 'none'
})
}
})
}
/**
* 选择身份证国徽面照片
*/
const changeIdcard2Img = () => {
Taro.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: function (res) {
const tempFilePath = res.tempFilePaths[0]
uploadIdcardImage(tempFilePath, 'idcard_2_img')
},
fail: function () {
Taro.showToast({
title: '选择图片失败',
icon: 'none'
})
}
})
}
/**
* 上传身份证图片到服务器
* @param {String} filePath - 图片文件路径
* @param {String} fieldName - 字段名称 (idcard_1_img 或 idcard_2_img)
*/
const uploadIdcardImage = (filePath, fieldName) => {
// 显示上传中提示
Taro.showLoading({
title: '上传中',
mask: true
})
// 上传图片
wx.uploadFile({
url: BASE_URL + '/admin/?m=srv&a=upload',
filePath,
name: 'file',
header: {
'content-type': 'multipart/form-data',
},
success: function (res) {
let upload_data = JSON.parse(res.data);
Taro.hideLoading({
success: () => {
if (res.statusCode === 200) {
tempIdentityInfo.value[fieldName] = upload_data.data.src;
Taro.showToast({
title: '上传成功',
icon: 'success'
})
} else {
Taro.showToast({
icon: 'error',
title: '服务器错误,稍后重试!',
mask: true
})
}
},
});
},
fail: function (res) {
Taro.hideLoading({
success: () => {
Taro.showToast({
icon: 'error',
title: '上传失败,稍后重试!',
mask: true
})
},
});
}
})
}
/**
* 预览身份证人像面图片
*/
const previewIdcard1Img = () => {
if (tempIdentityInfo.value.idcard_1_img) {
Taro.previewImage({
urls: [tempIdentityInfo.value.idcard_1_img],
current: tempIdentityInfo.value.idcard_1_img
})
}
}
/**
* 预览身份证国徽面图片
*/
const previewIdcard2Img = () => {
if (tempIdentityInfo.value.idcard_2_img) {
Taro.previewImage({
urls: [tempIdentityInfo.value.idcard_2_img],
current: tempIdentityInfo.value.idcard_2_img
})
}
}
/**
* 删除身份证人像面图片
*/
const deleteIdcard1Img = () => {
Taro.showModal({
title: '确认删除',
content: '确定要删除身份证人像面照片吗?',
success: function (res) {
if (res.confirm) {
tempIdentityInfo.value.idcard_1_img = ''
Taro.showToast({
title: '删除成功',
icon: 'success'
})
}
}
})
}
/**
* 删除身份证国徽面图片
*/
const deleteIdcard2Img = () => {
Taro.showModal({
title: '确认删除',
content: '确定要删除身份证国徽面照片吗?',
success: function (res) {
if (res.confirm) {
tempIdentityInfo.value.idcard_2_img = ''
Taro.showToast({
title: '删除成功',
icon: 'success'
})
}
}
})
}
/**
* 显示身份证有效期开始日期选择器
*/
const showIdcardBeginDatePicker = () => {
showIdcardBeginDate.value = true
}
/**
* 显示身份证有效期结束日期选择器
*/
const showIdcardEndDatePicker = () => {
showIdcardEndDate.value = true
}
/**
* 确认选择身份证有效期开始日期
* @param {Object} param - 日期选择器返回的参数
*/
const onIdcardBeginDateConfirm = (param) => {
const { selectedValue } = param
const year = selectedValue[0]
const month = selectedValue[1].toString().padStart(2, '0')
const day = selectedValue[2].toString().padStart(2, '0')
tempIdentityInfo.value.idcard_effect_begin = `${year}-${month}-${day}`
showIdcardBeginDate.value = false
}
/**
* 确认选择身份证有效期结束日期
* @param {Object} param - 日期选择器返回的参数
*/
const onIdcardEndDateConfirm = (param) => {
const { selectedValue } = param
const year = selectedValue[0]
const month = selectedValue[1].toString().padStart(2, '0')
const day = selectedValue[2].toString().padStart(2, '0')
tempIdentityInfo.value.idcard_effect_end = `${year}-${month}-${day}`
showIdcardEndDate.value = false
}
/**
* 保存身份信息
*/
const saveIdentityInfo = async () => {
......@@ -634,7 +972,12 @@ const saveIdentityInfo = async () => {
// 调用API保存身份信息
const result = await updateProfileAPI({
name: tempIdentityInfo.value.userName,
idcard: tempIdentityInfo.value.idCard
idcard: tempIdentityInfo.value.idCard,
idcard_1_img: tempIdentityInfo.value.idcard_1_img,
idcard_2_img: tempIdentityInfo.value.idcard_2_img,
idcard_effect_begin: tempIdentityInfo.value.idcard_effect_begin,
idcard_effect_end: tempIdentityInfo.value.idcard_effect_end,
idcard_address: tempIdentityInfo.value.idcard_address
});
if (result.code) {
......@@ -643,7 +986,12 @@ const saveIdentityInfo = async () => {
// 更新全局用户状态
userStore.updateUserInfo({
name: tempIdentityInfo.value.userName,
idcard: tempIdentityInfo.value.idCard
idcard: tempIdentityInfo.value.idCard,
idcard_1_img: tempIdentityInfo.value.idcard_1_img,
idcard_2_img: tempIdentityInfo.value.idcard_2_img,
idcard_effect_begin: tempIdentityInfo.value.idcard_effect_begin,
idcard_effect_end: tempIdentityInfo.value.idcard_effect_end,
idcard_address: tempIdentityInfo.value.idcard_address
});
closeIdentityModal();
......