feat: 患者信息身份证改成非必填

This commit is contained in:
李琦
2026-08-08 18:36:38 +08:00
parent d4a5115a80
commit 53c6f3fcfd
5 changed files with 297 additions and 102 deletions

View File

@@ -337,6 +337,14 @@ function getRes(obj, isDecode = true) {
} else if (obj[key] !== null && typeof obj[key] === 'object') {
obj[key] = getRes(obj[key], isDecode)
} else if (arr.indexOf(key) !== -1) {
// 未填写的敏感字段保持空串,禁止加解密(避免空值被解成密文乱码)
if (obj[key] == null || obj[key] === '') {
obj[key] = ''
if (isDecode === true && (sensitiveData.indexOf(key) !== -1 || displayMaskFields.indexOf(key) !== -1)) {
obj[key + '_tm'] = ''
}
continue
}
let plainValue = ''
if (isDecode === true) {
plainValue = customBase64Decode(obj[key])
@@ -369,17 +377,22 @@ function getRes(obj, isDecode = true) {
}
function customBase64Decode(data) {
// 空值直接返回空串,避免 Base64.decode(null/'' ) 失败后回退成密文原串
if (data == null || data === '') {
return ''
}
// return data
try {
// 第一次Base64解码
const decodedFirst = Base64.decode(data)
// 截取从第30个字符开始往后的内容
const subStr = decodedFirst.slice(30)
// 第二次Base64解码
return Base64.decode(subStr) != '' ? Base64.decode(subStr) : data
// 第二次Base64解码;解出空串说明业务上就是未填写,不要回退成密文
const decodedSecond = Base64.decode(subStr)
return decodedSecond !== '' ? decodedSecond : ''
} catch (error) {
console.error(`解码Base64字符串【${data}】时发生错误`, error)
return data
return ''
}
}

View File

@@ -12,14 +12,24 @@
<u-input v-model="names" input-align="right" placeholder="请输入真实姓名"
placeholder-style="text-align:right;color: #94A3B8;" :type="type" />
</view>
<!-- 身份证号 -->
<!-- 身份证号选填有证时自动带出生日/年龄/性别 -->
<view class="card_name">
<text>身份证号</text>
<u-input v-model="form.identityCardNo" maxlength="18" input-align="right" placeholder="已经填写无法修改,请慎重填写"
<u-input v-model="form.identityCardNo" maxlength="18" input-align="right" placeholder="选填,填写后自动识别"
placeholder-style="text-align:right;color: #94A3B8;padding-right:15px;" @input="inputChange"
:type="type" />
</view>
<!-- 性别 -->
<!-- 生日算年龄优先用生日填身份证时自动回填 -->
<picker mode="date" :value="form.birthday || '2000-01-01'" :end="todayDate" @change="onBirthdayChange">
<view class="card_name">
<text>生日</text>
<view class="birthday-value">
<text :class="{ placeholder: !form.birthday }">{{ form.birthday || '请选择年月日' }}</text>
<u-icon name="arrow-right" color="#A7ABB0" size="26"></u-icon>
</view>
</view>
</picker>
<!-- 性别与单选 name 统一用 1/2填身份证时同步回填 -->
<view class="card_name">
<text>性别</text>
@@ -30,7 +40,7 @@
<!-- <u-input v-model="form.sex" input-align="right" placeholder="-"-->
<!-- placeholder-style="text-align:right;color: #94A3B8;" :type="type" />-->
</view>
<!-- 年龄 -->
<!-- 年龄有证/生日时自动计算也可手填 -->
<view class="card_name">
<text>年龄</text>
<u-input v-model="form.age" input-align="right" placeholder="-"
@@ -384,9 +394,12 @@
text: "", // 添加病情
form: {
identityCardNo: "",
sex: "",
birthday: "",
sex: "1",
age: "",
}, // 身份证
}, // 身份证 / 生日sex 与单选一致 1男2女
// 生日选择器结束日期(今天),避免选到未来
todayDate: '',
sex: "",
// 健康信息
@@ -747,10 +760,36 @@
const n = Number(age);
return age !== '' && !isNaN(n) && n >= 1 && n <= 200;
},
calcAgeFromIdCard(idCard) {
/** 身份证格式正则(与后端 cc_is_id_card 大致对齐) */
isValidIdCard(idCard) {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
if (!reg.test(idCard)) {
return reg.test(idCard || '');
},
/** 按 YYYY-MM-DD 计算周岁,非法返回 false */
calcAgeFromBirthday(birthday) {
if (!birthday || !/^\d{4}-\d{2}-\d{2}$/.test(birthday)) {
return false;
}
const birthdays = new Date(birthday.replace(/-/g, '/'));
if (isNaN(birthdays.getTime())) {
return false;
}
const d = new Date();
if (birthdays > d) {
return false;
}
return (
d.getFullYear() -
birthdays.getFullYear() -
(d.getMonth() < birthdays.getMonth() ||
(d.getMonth() === birthdays.getMonth() && d.getDate() < birthdays.getDate()) ?
1 :
0)
);
},
calcAgeFromIdCard(idCard) {
if (!this.isValidIdCard(idCard)) {
return false;
}
const orgBirthday = idCard.substring(6, 14);
@@ -760,16 +799,39 @@
orgBirthday.substring(4, 6) +
'-' +
orgBirthday.substring(6, 8);
const birthdays = new Date(birthday.replace(/-/g, '/'));
const d = new Date();
return (
d.getFullYear() -
birthdays.getFullYear() -
(d.getMonth() < birthdays.getMonth() ||
(d.getMonth() === birthdays.getMonth() && d.getDate() < birthdays.getDate()) ?
1 :
0)
);
return this.calcAgeFromBirthday(birthday);
},
/** 选择生日后反算年龄(无证时用) */
onBirthdayChange(e) {
const birthday = (e && e.detail && e.detail.value) || '';
this.form.birthday = birthday;
const age = this.calcAgeFromBirthday(birthday);
if (age !== false) {
this.form.age = age;
if (age > 6) {
this.resetGuardian();
}
}
},
/** 提交前:身份证可空,但无证时生日必填;有证需格式正确 */
validateIdentity() {
const idCard = (this.form.identityCardNo || '').trim();
const birthday = (this.form.birthday || '').trim();
if (!idCard && !birthday) {
uni.showToast({
title: '请填写身份证或生日',
icon: 'none',
});
return false;
}
if (idCard && !this.isValidIdCard(idCard)) {
uni.showToast({
title: '身份证号格式不正确',
icon: 'none',
});
return false;
}
return true;
},
validatePatientAge() {
if (!this.isAgeInRange(this.form.age)) {
@@ -859,7 +921,7 @@
},
// 添加就诊人
async getAdduser() {
if (!this.validatePatientAge() || !this.validateGuardian()) {
if (!this.validateIdentity() || !this.validatePatientAge() || !this.validateGuardian()) {
return;
}
try {
@@ -884,7 +946,8 @@
is_default: this.checked ? '1' : '0',
id: this.addid,
name: this.names,
id_card: this.form.identityCardNo,
id_card: (this.form.identityCardNo || '').trim(),
birthday: (this.form.birthday || '').trim(),
sex: (this.form.sex) == '女' ? '2' : (this.form.sex == '男' ? '1' : this.form.sex),
age: this.form.age,
mobile: this.phone,
@@ -1036,43 +1099,38 @@
this.toggleHistoryTag('rSelectPresent', 'present_history', e, item)
},
// 身份证号码
// 身份证号码:合法时自动解析并回填生日/年龄/性别(年龄按生日算)
inputChange() {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
if (reg.test(this.form.identityCardNo)) {
var org_birthday = this.form.identityCardNo.substring(6, 14);
var org_gender = this.form.identityCardNo.substring(16, 17);
var sex = org_gender % 2 == 1 ? "" : "";
var birthday =
org_birthday.substring(0, 4) +
"-" +
org_birthday.substring(4, 6) +
"-" +
org_birthday.substring(6, 8);
var birthdays = new Date(birthday.replace(/-/g, "/"));
let d = new Date();
let age =
d.getFullYear() -
birthdays.getFullYear() -
(d.getMonth() < birthdays.getMonth() ||
(d.getMonth() == birthdays.getMonth() &&
d.getDate() < birthdays.getDate()) ?
1 : 0);
this.form.sex = sex;
if (!this.isValidIdCard(this.form.identityCardNo)) {
return false;
}
var org_birthday = this.form.identityCardNo.substring(6, 14);
var org_gender = this.form.identityCardNo.substring(16, 17);
var sex = org_gender % 2 == 1 ? "1" : "2";
var birthday =
org_birthday.substring(0, 4) +
"-" +
org_birthday.substring(4, 6) +
"-" +
org_birthday.substring(6, 8);
var age = this.calcAgeFromBirthday(birthday);
// 有证必回填生日,供后续优先生日算年龄
this.form.birthday = birthday;
this.form.sex = sex;
if (age !== false) {
this.form.age = age;
this.sex = org_gender% 2 == 1 ? 1 : 2;
if (age > 6) {
this.resetGuardian();
}
} else {
this.form.sex = "";
return false;
}
this.sex = org_gender % 2 == 1 ? 1 : 2;
},
},
mounted() {
const d = new Date();
const m = (d.getMonth() + 1) < 10 ? '0' + (d.getMonth() + 1) : '' + (d.getMonth() + 1);
const day = d.getDate() < 10 ? '0' + d.getDate() : '' + d.getDate();
this.todayDate = d.getFullYear() + '-' + m + '-' + day;
this.getAction();
this.loadPatientList();
}
@@ -1119,6 +1177,16 @@
color: #1E293B;
}
.birthday-value {
display: flex;
align-items: center;
gap: 8rpx;
.placeholder {
color: #94A3B8;
}
}
::v-deep .u-input {
width: 400rpx;
}

View File

@@ -13,20 +13,34 @@
<u-input v-model="names" input-align="right" placeholder="请输入真实姓名"
placeholder-style="text-align:right;color: #94A3B8;" :type="type" />
</view>
<!-- 身份证号 -->
<!-- 身份证号选填复诊等场景需补填 -->
<view class="card_name">
<text>身份证号</text>
<u-input v-model="form.identityCardNo" maxlength="18" input-align="right" placeholder="请输入身份证号"
<u-input v-model="form.identityCardNo" maxlength="18" input-align="right" placeholder="选填,填写后自动识别"
placeholder-style="text-align:right;color: #94A3B8;padding-right:5px;" @input="inputChange"
:type="type" />
</view>
<!-- 性别 -->
<!-- 生日算年龄优先用生日填身份证时自动回填 -->
<picker mode="date" :value="form.birthday || '2000-01-01'" :end="todayDate" @change="onBirthdayChange">
<view class="card_name">
<text>生日</text>
<view class="birthday-value">
<text :class="{ placeholder: !form.birthday }">{{ form.birthday || '请选择年月日' }}</text>
<u-icon name="arrow-right" color="#A7ABB0" size="26"></u-icon>
</view>
</view>
</picker>
<!-- 性别有证由证驱动只读无证可选手动 -->
<view class="card_name">
<text>性别</text>
<u-input disabled v-model="form.sex" input-align="right" placeholder="-"
<u-radio-group v-if="!hasValidIdCard" v-model="form.sex">
<u-radio name="男" style="margin-right: 60rpx;"></u-radio>
<u-radio name="女"></u-radio>
</u-radio-group>
<u-input v-else disabled v-model="form.sex" input-align="right" placeholder="-"
placeholder-style="text-align:right;color: #94A3B8;" :type="type" />
</view>
<!-- 年龄 -->
<!-- 年龄由身份证或生日自动计算 -->
<view class="card_name">
<text>年龄</text>
<u-input disabled v-model="form.age" input-align="right" placeholder="-"
@@ -362,9 +376,12 @@
text: "", // 添加病情
form: {
identityCardNo: "",
birthday: "",
sex: "",
age: "",
}, // 身份证
}, // 身份证 / 生日
// 生日选择器结束日期(今天),避免选到未来
todayDate: '',
sex: "",
// 健康信息
@@ -580,6 +597,12 @@
}
},
computed: {
/** 是否已填写合法身份证(有证时性别只读) */
hasValidIdCard() {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
return reg.test(this.form.identityCardNo || '');
},
showGuardianSection() {
const age = Number(this.form.age);
return this.form.age !== '' && !isNaN(age) && age <= 6;
@@ -605,6 +628,10 @@
},
onLoad(e) {
this.id = e.id
const d = new Date();
const m = (d.getMonth() + 1) < 10 ? '0' + (d.getMonth() + 1) : '' + (d.getMonth() + 1);
const day = d.getDate() < 10 ? '0' + d.getDate() : '' + d.getDate();
this.todayDate = d.getFullYear() + '-' + m + '-' + day;
this.getAction()
},
watch: {
@@ -782,40 +809,31 @@
})
},
// 身份证号码
// 身份证号码:合法时自动解析并回填生日/年龄/性别(年龄按生日算)
inputChange() {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
if (reg.test(this.form.identityCardNo)) {
var org_birthday = this.form.identityCardNo.substring(6, 14);
var org_gender = this.form.identityCardNo.substring(16, 17);
var sex = org_gender % 2 == 1 ? "男" : "女";
var birthday =
org_birthday.substring(0, 4) +
"-" +
org_birthday.substring(4, 6) +
"-" +
org_birthday.substring(6, 8);
var birthdays = new Date(birthday.replace(/-/g, "/"));
let d = new Date();
let age =
d.getFullYear() -
birthdays.getFullYear() -
(d.getMonth() < birthdays.getMonth() ||
(d.getMonth() == birthdays.getMonth() &&
d.getDate() < birthdays.getDate()) ?
1 : 0);
this.form.sex = sex;
if (!this.isValidIdCard(this.form.identityCardNo)) {
return false;
}
var org_birthday = this.form.identityCardNo.substring(6, 14);
var org_gender = this.form.identityCardNo.substring(16, 17);
var sex = org_gender % 2 == 1 ? "男" : "女";
var birthday =
org_birthday.substring(0, 4) +
"-" +
org_birthday.substring(4, 6) +
"-" +
org_birthday.substring(6, 8);
var age = this.calcAgeFromBirthday(birthday);
// 有证必回填生日,供后续优先生日算年龄
this.form.birthday = birthday;
this.form.sex = sex;
if (age !== false) {
this.form.age = age;
this.sex = org_gender % 2 == 1 ? 1 : 2;
if (age > 6) {
this.resetGuardian();
}
} else {
this.form.sex = "";
return false;
}
this.sex = org_gender % 2 == 1 ? 1 : 2;
},
// 就诊人关系
@@ -851,7 +869,13 @@
that.phone = that.infoList[0].mobile
that.form.sex = (that.infoList[0].sex) % 2 == 0 ? '女' : '男'
that.form.age = that.infoList[0].age
that.form.identityCardNo = that.infoList[0].id_card
that.form.identityCardNo = that.infoList[0].id_card || ''
// 回显生日;若有证但库里无生日,再从证号补一次
that.form.birthday = that.infoList[0].birthday || ''
if (!that.form.birthday && that.isValidIdCard(that.form.identityCardNo)) {
const org = that.form.identityCardNo.substring(6, 14)
that.form.birthday = org.substring(0, 4) + '-' + org.substring(4, 6) + '-' + org.substring(6, 8)
}
that.addid = that.infoList[0].id
// 编辑页无「设为默认」开关,保存时原样回传,避免后端缺省写成 0
that.checked = Number(that.infoList[0].is_default) === 1
@@ -933,10 +957,68 @@
const n = Number(age);
return age !== '' && !isNaN(n) && n >= 1 && n <= 200;
},
calcAgeFromIdCard(idCard) {
/** 身份证格式校验 */
isValidIdCard(idCard) {
const reg =
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
if (!reg.test(idCard)) {
return reg.test(idCard || '');
},
/** 按 YYYY-MM-DD 计算周岁,非法返回 false */
calcAgeFromBirthday(birthday) {
if (!birthday || !/^\d{4}-\d{2}-\d{2}$/.test(birthday)) {
return false;
}
const birthdays = new Date(birthday.replace(/-/g, '/'));
if (isNaN(birthdays.getTime())) {
return false;
}
const d = new Date();
if (birthdays > d) {
return false;
}
return (
d.getFullYear() -
birthdays.getFullYear() -
(d.getMonth() < birthdays.getMonth() ||
(d.getMonth() === birthdays.getMonth() && d.getDate() < birthdays.getDate()) ?
1 :
0)
);
},
/** 选择生日后反算年龄(无证时用) */
onBirthdayChange(e) {
const birthday = (e && e.detail && e.detail.value) || '';
this.form.birthday = birthday;
const age = this.calcAgeFromBirthday(birthday);
if (age !== false) {
this.form.age = age;
if (age > 6) {
this.resetGuardian();
}
}
},
/** 提交前:身份证可空,但无证时生日必填;有证需格式正确 */
validateIdentity() {
const idCard = (this.form.identityCardNo || '').trim();
const birthday = (this.form.birthday || '').trim();
if (!idCard && !birthday) {
uni.showToast({
title: '请填写身份证或生日',
icon: 'none',
});
return false;
}
if (idCard && !this.isValidIdCard(idCard)) {
uni.showToast({
title: '身份证号格式不正确',
icon: 'none',
});
return false;
}
return true;
},
calcAgeFromIdCard(idCard) {
if (!this.isValidIdCard(idCard)) {
return false;
}
const orgBirthday = idCard.substring(6, 14);
@@ -946,16 +1028,7 @@
orgBirthday.substring(4, 6) +
'-' +
orgBirthday.substring(6, 8);
const birthdays = new Date(birthday.replace(/-/g, '/'));
const d = new Date();
return (
d.getFullYear() -
birthdays.getFullYear() -
(d.getMonth() < birthdays.getMonth() ||
(d.getMonth() === birthdays.getMonth() && d.getDate() < birthdays.getDate()) ?
1 :
0)
);
return this.calcAgeFromBirthday(birthday);
},
validatePatientAge() {
if (!this.isAgeInRange(this.form.age)) {
@@ -1044,9 +1117,9 @@
return res.data.message || res.data.msg || '保存失败';
},
// 添加就诊人
// 保存就诊人
async getAdduser() {
if (!this.validatePatientAge() || !this.validateGuardian()) {
if (!this.validateIdentity() || !this.validatePatientAge() || !this.validateGuardian()) {
return;
}
try {
@@ -1070,7 +1143,8 @@
store_id: uni.getStorageSync('store_id') || '11001',
id: this.addid,
name: this.names,
id_card: this.form.identityCardNo,
id_card: (this.form.identityCardNo || '').trim(),
birthday: (this.form.birthday || '').trim(),
sex: (this.form.sex) == '女' ? '2' : '1',
age: this.form.age,
mobile: this.phone,
@@ -1240,6 +1314,16 @@
color: #1E293B;
}
.birthday-value {
display: flex;
align-items: center;
gap: 8rpx;
.placeholder {
color: #94A3B8;
}
}
::v-deep .u-input {
width: 380rpx;
}

View File

@@ -30,7 +30,7 @@
</view>
</view>
<view class="info_list_bottom">
<text class="id">{{item.id_card_tm || item.id_card}}</text>
<text class="id">{{ item.id_card_tm || item.id_card || '未填写' }}</text>
<text class="telephone">{{item.mobile_tm || item.mobile}}</text>
</view>
</view>

View File

@@ -376,6 +376,32 @@ export default {
if (this.followUpDrugIdsForApi().length === 0) return '请至少选择一种药品';
return '';
},
/**
* 在线复诊必须有就诊人身份证:列表接口已解密回传 id_card空则引导去编辑页补填
* 普通挂号不强制
*/
ensureFollowUpPatientIdCard() {
if (this.registerType !== '2' && this.registerType !== '3') {
return true;
}
if (!this.actived) {
uni.showToast({ title: '请选择就诊人', icon: 'none' });
return false;
}
const patient = this.infoList.find((p) => p.id === this.actived) || null;
const idCard = patient ? String(patient.id_card || '').trim() : '';
if (!idCard) {
uni.showToast({
title: '复诊需补充就诊人身份证',
icon: 'none',
});
setTimeout(() => {
uni.navigateTo({ url: '/subPackages/my/myinfo-edit?id=' + this.actived });
}, 500);
return false;
}
return true;
},
async prepareFollowUpRegisterInfo() {
let params = typeof this.rInfo === 'string' ? JSON.parse(this.rInfo) : { ...(this.rInfo || {}) };
if (params.info_id) {
@@ -606,6 +632,10 @@ export default {
});
},
subScriptionMessage() {
// 在线复诊:先校验就诊人是否已填身份证
if (!this.ensureFollowUpPatientIdCard()) {
return;
}
if (this.showFollowUpForm) {
const err = this.validateFollowUpFields();
if (err) {