sh_real_name_system_app/src/components/PersonIdCardForm/index.vue

517 lines
16 KiB
Vue

<template>
<!-- 人员身份证信息表单 -->
<view style="height: 100%">
<up-form
labelWidth="120"
labelPosition="left"
:model="idCardModel"
:rules="idCardRules"
ref="idCardFormRef"
class="id-card-form"
>
<up-cell style="padding: 0 13px">
<template #icon>
<text class="blue-text"> 身份证人面像信息 </text>
</template>
<template #right-icon>
<up-button
icon="photo"
:plain="true"
size="small"
type="primary"
text="识别身份证(人像)"
@tap="onHandleRecognizeIdCard(1)"
/>
</template>
</up-cell>
<up-form-item label="姓名" prop="name" :borderBottom="true">
<up-input
clearable
border="none"
placeholder="请输入姓名"
v-model="idCardModel.name"
/>
</up-form-item>
<up-form-item label="身份证号" prop="idNumber" :borderBottom="true">
<up-input
clearable
border="none"
@blur="onBlurIdNumber"
placeholder="请输入18位身份证号"
v-model="idCardModel.idNumber"
/>
</up-form-item>
<up-form-item label="性别" prop="sex" :borderBottom="true">
<up-radio-group v-model="idCardModel.sex" placement="row" disabled>
<up-radio label="男" name="男" shape="circle" />
<up-radio label="女" name="女" shape="circle" />
</up-radio-group>
</up-form-item>
<up-form-item label="出生日期" prop="birthday" :borderBottom="true">
<up-cell
isLink
disabled
:border="false"
style="width: 100%"
@tap="onOpenPikerDate(1)"
>
<template #icon>
<text :class="{ 'color-text': idCardModel.birthday }" class="time-text">
{{ idCardModel.birthday || '出生日期' }}
</text>
</template>
</up-cell>
</up-form-item>
<up-form-item label="民族" prop="nation" :borderBottom="true">
<up-input
clearable
border="none"
placeholder="请输入民族"
v-model="idCardModel.nation"
/>
</up-form-item>
<up-form-item label="住址" prop="address" :borderBottom="true">
<up-textarea
count
autoHeight
maxlength="50"
placeholder="请输入住址"
v-model="idCardModel.address"
/>
</up-form-item>
<up-cell style="padding: 0 13px">
<template #icon>
<text class="blue-text"> 身份证国徽面信息 </text>
</template>
<template #right-icon>
<up-button
icon="photo"
:plain="true"
size="small"
type="primary"
text="识别身份证(国徽)"
@tap="onHandleRecognizeIdCard(2)"
/>
</template>
</up-cell>
<up-form-item label="生效日期" prop="startTime" :borderBottom="true">
<up-cell
isLink
size="large"
style="width: 100%"
@tap="onOpenPikerDate(2)"
:border="false"
>
<template #icon>
<text :class="{ 'color-text': idCardModel.startTime }" class="time-text">
{{ idCardModel.startTime || '请选择生效日期' }}
</text>
</template>
</up-cell>
</up-form-item>
<up-form-item label="失效日期" prop="birthday" :borderBottom="true">
<up-cell
isLink
size="large"
style="width: 100%"
@tap="onOpenPikerDate(3)"
:border="false"
>
<template #icon>
<text :class="{ 'color-text': idCardModel.endTime }" class="time-text">
{{ idCardModel.endTime || '请选择失效日期' }}
</text>
</template>
</up-cell>
</up-form-item>
<up-form-item label="签发机关" prop="issuingAuthority" :borderBottom="true">
<up-input
clearable
border="none"
placeholder="请输入签发机关"
v-model="idCardModel.issuingAuthority"
/>
</up-form-item>
</up-form>
<up-datetime-picker
mode="date"
ref="pikerBirthdayRef"
:minDate="0"
:show="showPikerBirthday"
@cancel="onCancelBirthday"
@confirm="onConfirmBirthday"
v-model="pikerBirthdayValue"
/>
</view>
</template>
<script setup name="personIdCardForm">
import { ref, watch } from 'vue'
import { pathToBase64 } from 'image-tools'
import { useMemberStore } from '@/stores'
import dayjs from 'dayjs'
const memberStore = useMemberStore()
const idCardFormRef = ref(null) // 身份证表单ref
const idCardModel = ref({
age: '',
sex: '', // 性别
name: '', // 姓名
nation: '', // 民族
startTime: '', // 生效日期
endTime: '', // 失效日期
address: '', // 身份证住址
idNumber: '', // 身份证号
birthday: '', // 出生日期
issuingAuthority: '', // 签发机关
}) // 身份证表单数据
const idCardRules = ref({
name: [
{
type: 'string',
required: true,
message: '请输入姓名',
},
{
max: 20,
message: '姓名长度不能超过20个字符',
},
],
idNumber: [
{
type: 'string',
required: true,
message: '请输入身份证号',
},
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[0-1])\d{3}[0-9Xx]$/,
message: '请输入正确的身份证号',
},
],
sex: [
{
type: 'string',
required: true,
message: '请选择性别',
},
],
birthday: [
{
type: 'string',
required: true,
message: '请选择出生日期',
},
],
nation: [
{
type: 'string',
required: true,
message: '请输入民族',
},
{
max: 20,
message: '民族长度不能超过20个字符',
},
],
address: [
{
type: 'string',
required: true,
message: '请输入住址',
},
{
max: 50,
message: '住址长度不能超过50个字符',
},
],
startTime: [
{
type: 'string',
required: true,
message: '请选择生效日期',
},
],
endTime: [
{
type: 'string',
required: true,
message: '请选择失效日期',
},
],
issuingAuthority: [
{
type: 'string',
required: true,
message: '请输入签发机关',
},
{
max: 20,
message: '签发机关长度不能超过20个字符',
},
],
}) // 身份证表单规则
const showPikerBirthday = ref(false) // 出生日期选择器
const pikerBirthdayRef = ref(null) // 出生日期选择器ref
const pikerBirthdayValue = ref(Date.now()) // 出生日期选择器值
const pikerDateType = ref(1) // 日期选择器类型 1出生日期 2生效日期 3失效日期
const emit = defineEmits(['onInitIdCardForm'])
// 身份证号输入
const onBlurIdNumber = (val) => {
if (!val) {
idCardModel.value.birthday = ''
idCardModel.value.sex = ''
return
}
const birthday = idCardModel.value.idNumber.slice(6, 14)
const sex = idCardModel.value.idNumber.slice(16, 17)
idCardModel.value.birthday =
birthday?.slice(0, 4) + '-' + birthday.slice(4, 6) + '-' + birthday.slice(6, 8)
idCardModel.value.sex = sex % 2 === 0 ? '女' : '男'
}
// 日期格式化
const formatDateFun = (timestamp) => {
// 将时间戳转换为Date对象
const date = new Date(timestamp)
// 格式化日期为"YYYY年MM月DD日"格式
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const formattedValue = `${year}-${month}-${day}`
return formattedValue
}
// 打开日期选择器
const onOpenPikerDate = (type) => {
pikerDateType.value = type
showPikerBirthday.value = true
}
// 日期确认
const onConfirmBirthday = (value) => {
if (value.value) {
const formattedValue = formatDateFun(value.value)
if (pikerDateType.value === 2) {
if (idCardModel.value.endTime && formattedValue > idCardModel.value.endTime) {
uni.$u.toast('生效日期不能大于失效日期')
return
}
idCardModel.value.startTime = formattedValue
} else if (pikerDateType.value === 3) {
if (idCardModel.value.startTime && formattedValue < idCardModel.value.startTime) {
uni.$u.toast('失效日期不能小于生效日期')
return
}
idCardModel.value.endTime = formattedValue
} else {
idCardModel.value.birthday = formattedValue
}
showPikerBirthday.value = false
}
}
// 日期取消
const onCancelBirthday = () => {
pikerBirthdayValue.value = Date.now()
idCardModel.value.birthday = ''
showPikerBirthday.value = false
}
const imgToBase64Fun = async (path) => {
return new Promise((resolve, reject) => {
pathToBase64(path)
.then((base64) => {
resolve(base64)
})
.catch((error) => {
console.error(error)
reject(error)
})
})
}
// 识别身份证(人像面和国徽面)
const onHandleRecognizeIdCard = (type) => {
// 1人像面 2国徽面
// console.log('识别身份证(人像面)')
// uni.$u.toast('功能正在开发中,敬请期待...')
// return
uni.chooseImage({
count: 1, // 最多选择1张图片
sizeType: ['original', 'compressed'],
sourceType: ['camera'], // 选择图片的来源
success: async (res) => {
const base64 = await imgToBase64Fun(res.tempFilePaths[0])
let params = {
recognitionFrontData: '',
recognitionBackData: '',
type: 1,
}
if (type === 1) {
params.recognitionFrontData = base64.split(',')[1]
} else {
params.recognitionBackData = base64.split(',')[1]
}
// 上传后台接口
uni.request({
url: 'http://192.168.0.14:18975/recognition',
method: 'POST',
data: params,
isUploadFile: true,
header: {
// 'Content-Type': 'application/Json',
Authorization: memberStore?.token, // token
},
success: (res) => {
const data = JSON.parse(res.data)
console.log(data, '识别身份证结果')
if (data.code === 20001) {
uni.$u.toast('识别身份证成功')
if (type === 1) {
const { name, gender, ethnicity, dateOfBirth, address, idNumber } =
data.data
const match = dateOfBirth.match(/(\d+)年(\d+)月(\d+)日/)
let birthday = ''
if (match) {
const year = match[1]
// 月和日补零处理
const month = String(match[2]).padStart(2, '0')
const day = String(match[3]).padStart(2, '0')
birthday = `${year}-${month}-${day}`
}
idCardModel.value.name = name
idCardModel.value.sex = gender
idCardModel.value.nation = ethnicity
idCardModel.value.birthday = birthday
idCardModel.value.address = address
idCardModel.value.idNumber = idNumber
} else {
const { issuingAuthority, validTime } = data.data
const validTimeArr = validTime.split('-')
idCardModel.value.issuingAuthority = issuingAuthority
idCardModel.value.startTime = validTimeArr[0].replace(/\./g, '-')
idCardModel.value.endTime = validTimeArr[1].replace(/\./g, '-')
}
} else {
uni.$u.toast('识别失败,请重新识别')
}
// const { data: result } = res
// if (result.code === 200) {
// uni.$u.toast('识别身份证成功')
// } else {
// uni.$u.toast('识别身份证失败,请重新识别')
// }
},
fail: (err) => {
// console.log(err, '----识别身份证失败')
uni.$u.toast('识别失败,请重新识别')
},
})
},
fail: (err) => {
// uni.$u.toast({
// title: '选择图片失败',
// icon: 'none',
// })
},
})
}
// 表单校验
const validateIdCardForm = async () => {
return new Promise((resolve, reject) => {
idCardFormRef.value
.validate()
.then((valid) => {
if (valid) {
// 计算年龄
const age = new Date().getFullYear() - idCardModel.value.birthday.slice(0, 4)
idCardModel.value.age = age + 1
resolve({
isValid: true,
data: idCardModel.value,
})
}
})
.catch((err) => {
reject(false)
})
})
}
const updateIdCardInfo = () => {
Object.assign(idCardModel.value, props.idCardInfo)
}
// 向父组件暴露方法
defineExpose({
validateIdCardForm,
updateIdCardInfo,
})
const props = defineProps({
idCardInfo: {
type: Object,
default: () => {},
},
})
// 增加监听
watch(
props.idCardInfo,
(newVal) => {
if (Object.keys(newVal).length > 0) {
Object.assign(idCardModel.value, newVal)
}
},
{
immediate: true,
},
)
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.blue-text {
font-size: 14px;
color: $uni-color-primary;
}
.u-form-item {
padding: 0 15px;
}
::v-deep .u-cell__body {
padding: 13px 0;
}
.color-text {
color: rgb(48, 49, 51) !important;
}
.time-text {
color: rgb(192, 196, 204);
}
</style>