diff --git a/pages/index/index.vue b/pages/index/index.vue
index 1e98d27..a9f5515 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -106,10 +106,16 @@ const handleJoin = () => {
})
return
}
- // 审核通过 -> 不可点击
- if (patientStatus.value === 1) return
- // 待审核 -> 不可点击
- if (patientStatus.value === 0) return
+ // 审核通过 -> 跳转到个人中心
+ if (patientStatus.value === 1) {
+ uni.switchTab({ url: '/pages/profile/profile' })
+ return
+ }
+ // 待审核 -> 跳转到个人中心
+ if (patientStatus.value === 0) {
+ uni.switchTab({ url: '/pages/profile/profile' })
+ return
+ }
// 已拒绝 -> 跳转资料页重新提交
if (patientStatus.value === 2) {
uni.navigateTo({ url: '/pages/myinfo/myinfo' })
diff --git a/pages/myinfo/myinfo.vue b/pages/myinfo/myinfo.vue
index 9228124..d617a52 100644
--- a/pages/myinfo/myinfo.vue
+++ b/pages/myinfo/myinfo.vue
@@ -104,6 +104,17 @@
去签署
+
+
+ 个人信息处理同意书(监护人)
+ {{ signedPrivacyJhr ? '已签署' : '未签署' }}
+
+
+ 查看
+ 重签
+
+ 去签署
+
声明与承诺
@@ -157,6 +168,7 @@ const form = reactive({
documents: [],
sign_income: '',
sign_privacy: '',
+ sign_privacy_jhr: '',
sign_promise: '',
income_amount: ''
})
@@ -212,8 +224,23 @@ const maskedPhone = computed(() => {
// 签署状态:form 中有新签的 URL 或 info 中有已保存的 URL
const signedIncome = computed(() => form.sign_income || info.value.sign_income)
const signedPrivacy = computed(() => form.sign_privacy || info.value.sign_privacy)
+const signedPrivacyJhr = computed(() => form.sign_privacy_jhr || info.value.sign_privacy_jhr)
const signedPromise = computed(() => form.sign_promise || info.value.sign_promise)
+// 判断是否未成年(从身份证号解析年龄)
+const isMinor = computed(() => {
+ const idCard = info.value.id_card || ''
+ if (idCard.length !== 18) return false
+ const birthYear = parseInt(idCard.substring(6, 10))
+ const birthMonth = parseInt(idCard.substring(10, 12))
+ const birthDay = parseInt(idCard.substring(12, 14))
+ const now = new Date()
+ let age = now.getFullYear() - birthYear
+ const monthDiff = (now.getMonth() + 1) - birthMonth
+ if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < birthDay)) age--
+ return age < 18
+})
+
const regionText = computed(() => {
const parts = []
if (form.province_code) {
@@ -247,6 +274,8 @@ const onSignResult = (data) => {
if (data.amount) form.income_amount = data.amount
} else if (data.type === 'privacy') {
form.sign_privacy = data.url
+ } else if (data.type === 'privacy_jhr') {
+ form.sign_privacy_jhr = data.url
} else if (data.type === 'promise') {
form.sign_promise = data.url
}
@@ -271,6 +300,7 @@ const previewSign = (type) => {
const urlMap = {
income: form.sign_income || info.value.sign_income,
privacy: form.sign_privacy || info.value.sign_privacy,
+ privacy_jhr: form.sign_privacy_jhr || info.value.sign_privacy_jhr,
promise: form.sign_promise || info.value.sign_promise
}
const url = urlMap[type]
@@ -335,6 +365,7 @@ const loadInfo = async () => {
form.documents = res.data.documents || []
form.sign_income = res.data.sign_income || ''
form.sign_privacy = res.data.sign_privacy || ''
+ form.sign_privacy_jhr = res.data.sign_privacy_jhr || ''
form.sign_promise = res.data.sign_promise || ''
form.income_amount = res.data.income_amount || ''
// 设置地区选择器默认索引
@@ -385,6 +416,19 @@ const handleSubmit = async () => {
if (!form.address.trim()) {
return uni.showToast({ title: '请填写详细地址', icon: 'none' })
}
+ // 签名校验:全部必须签
+ if (!signedIncome.value) {
+ return uni.showToast({ title: '请签署个人可支配收入声明', icon: 'none' })
+ }
+ if (!signedPrivacy.value) {
+ return uni.showToast({ title: '请签署个人信息处理同意书', icon: 'none' })
+ }
+ if (isMinor.value && !signedPrivacyJhr.value) {
+ return uni.showToast({ title: '请签署个人信息处理同意书(监护人)', icon: 'none' })
+ }
+ if (!signedPromise.value) {
+ return uni.showToast({ title: '请签署声明与承诺', icon: 'none' })
+ }
// 已通过状态需要二次确认
if (info.value.status === 1) {
showConfirmPopup.value = true
@@ -410,6 +454,7 @@ const doSubmit = async () => {
documents: form.documents,
sign_income: form.sign_income,
sign_privacy: form.sign_privacy,
+ sign_privacy_jhr: form.sign_privacy_jhr,
sign_promise: form.sign_promise,
income_amount: form.income_amount || null,
// #ifdef MP-WEIXIN
diff --git a/pages/profile/profile.vue b/pages/profile/profile.vue
index 9b231be..205be9f 100644
--- a/pages/profile/profile.vue
+++ b/pages/profile/profile.vue
@@ -53,7 +53,7 @@
实名认证
-
+
@@ -180,6 +180,10 @@ const goMessage = () => {
const goVerify = () => {
if (!checkLogin()) return
+ if (isAuthed.value) {
+ uni.showToast({ title: '已认证', icon: 'none' })
+ return
+ }
uni.navigateTo({ url: '/pages/verify/verify' })
}
diff --git a/pages/sign/sign.vue b/pages/sign/sign.vue
index 0bd4fcf..a2af3c1 100644
--- a/pages/sign/sign.vue
+++ b/pages/sign/sign.vue
@@ -11,7 +11,24 @@
请填写您的个人年可支配收入(元)
¥
-
+
+
+
+
+
+
+ 监护人信息
+
+ 监护人姓名
+
+
+
+ 监护人身份证
+
+
+
+ 与患者关系
+
@@ -46,6 +63,9 @@ const signType = ref('')
const docTitle = ref('')
const docContent = ref('')
const incomeAmount = ref('')
+const guardianName = ref('')
+const guardianIdCard = ref('')
+const guardianRelation = ref('')
const signImageUrl = ref('')
const submitting = ref(false)
@@ -83,13 +103,22 @@ const confirmSign = async () => {
if (signType.value === 'income' && (!incomeAmount.value || Number(incomeAmount.value) <= 0)) {
return uni.showToast({ title: '请填写有效的收入金额', icon: 'none' })
}
+ if (signType.value === 'privacy_jhr') {
+ if (!guardianName.value.trim()) return uni.showToast({ title: '请输入监护人姓名', icon: 'none' })
+ if (!guardianIdCard.value.trim()) return uni.showToast({ title: '请输入监护人身份证号', icon: 'none' })
+ if (!/^\d{17}[\dXx]$/.test(guardianIdCard.value)) return uni.showToast({ title: '监护人身份证格式不正确', icon: 'none' })
+ if (!guardianRelation.value.trim()) return uni.showToast({ title: '请输入与患者关系', icon: 'none' })
+ }
submitting.value = true
try {
const params = {
type: signType.value,
signImage: signImageUrl.value,
- amount: signType.value === 'income' ? incomeAmount.value : undefined
+ amount: signType.value === 'income' ? incomeAmount.value : undefined,
+ guardianName: signType.value === 'privacy_jhr' ? guardianName.value.trim() : undefined,
+ guardianIdCard: signType.value === 'privacy_jhr' ? guardianIdCard.value.trim() : undefined,
+ guardianRelation: signType.value === 'privacy_jhr' ? guardianRelation.value.trim() : undefined
}
const res = await post('/api/mp/sign', params)
@@ -162,6 +191,21 @@ const confirmSign = async () => {
background: #f8f8f8;
}
+.form-row {
+ margin-bottom: 20rpx;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+}
+
+.row-label {
+ font-size: 26rpx;
+ color: #555;
+ margin-bottom: 12rpx;
+ display: block;
+}
+
.sign-label {
font-size: 28rpx;
color: #333;
diff --git a/pages/verify/verify.vue b/pages/verify/verify.vue
index fcc840c..f1cb58b 100644
--- a/pages/verify/verify.vue
+++ b/pages/verify/verify.vue
@@ -60,17 +60,17 @@
证件姓名
-
+
证件号码
-
+
-
+
发证机关
-
+
有效期限
@@ -100,6 +100,18 @@
+
+
+
+
+
@@ -116,8 +128,20 @@ const placeholderBack = CDN_BASE + '/images/patient/user/idcard-back.webp?v=1'
const certType = ref('idcard')
const countdown = ref(0)
const submitting = ref(false)
+const showBindPopup = ref(false)
+const bindPatientInfo = ref({ name: '', phone: '' })
let timer = null
+// 身份证号格式校验(18位,含校验位)
+const validateIdCard = (id) => {
+ if (!/^\d{17}[\dXx]$/.test(id)) 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']
+ let sum = 0
+ for (let i = 0; i < 17; i++) sum += parseInt(id[i]) * weights[i]
+ return checkCodes[sum % 11] === id[17].toUpperCase()
+}
+
// 证件类型映射: certType -> idCardType (后端字段)
const certTypeMap = { idcard: 1, child: 2, temp: 3 }
@@ -193,12 +217,6 @@ const doOcrFront = async (imageUrl) => {
const d = res.data
form.name = d.name || ''
form.idNo = d.idNum || ''
- // 从身份证号解析性别和生日
- if (d.idNum && d.idNum.length === 18) {
- const genderNum = parseInt(d.idNum.charAt(16))
- form.gender = genderNum % 2 === 1 ? '男' : '女'
- form.birthday = `${d.idNum.substring(6, 10)}-${d.idNum.substring(10, 12)}-${d.idNum.substring(12, 14)}`
- }
uni.hideLoading()
} catch (e) {
uni.hideLoading()
@@ -281,13 +299,28 @@ const handleSubmit = async () => {
}
if (!form.name) return uni.showToast({ title: '请输入证件姓名', icon: 'none' })
if (!form.idNo) return uni.showToast({ title: '请输入证件号码', icon: 'none' })
+ // 身份证格式校验
+ if (!validateIdCard(form.idNo)) return uni.showToast({ title: '身份证号格式不正确', icon: 'none' })
+
+ // 从身份证号解析性别和生日(提交时统一解析,确保一定有值)
+ if (form.idNo.length === 18) {
+ const genderNum = parseInt(form.idNo.charAt(16))
+ form.gender = genderNum % 2 === 1 ? '男' : '女'
+ form.birthday = `${form.idNo.substring(6, 10)}-${form.idNo.substring(10, 12)}-${form.idNo.substring(12, 14)}`
+ }
+
if (!form.phone || form.phone.length !== 11) return uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
if (!form.smsCode || form.smsCode.length !== 6) return uni.showToast({ title: '请输入6位验证码', icon: 'none' })
+ await doSubmit(false)
+}
+
+const doSubmit = async (confirmBind) => {
+ showBindPopup.value = false
submitting.value = true
try {
await post('/api/mp/authSubmit', {
- idCardType,
+ idCardType: certTypeMap[certType.value],
idCardFront: form.frontImage,
idCardBack: form.backImage,
photo: form.childImage,
@@ -298,7 +331,8 @@ const handleSubmit = async () => {
issuingAuthority: form.authority,
validPeriod: form.validity,
mobile: form.phone,
- code: form.smsCode
+ code: form.smsCode,
+ confirmBind: confirmBind || undefined
})
// 刷新用户信息缓存
@@ -310,7 +344,16 @@ const handleSubmit = async () => {
uni.showToast({ title: '认证成功', icon: 'success' })
setTimeout(() => { uni.navigateBack() }, 1500)
} catch (e) {
- if (e && e.msg) uni.showToast({ title: e.msg, icon: 'none' })
+ if (e && e.code === 1010) {
+ // 患者已存在,弹出确认绑定弹窗
+ bindPatientInfo.value = {
+ name: e.data?.patientName || '',
+ phone: e.data?.patientPhone || ''
+ }
+ showBindPopup.value = true
+ } else if (e && e.msg) {
+ uni.showToast({ title: e.msg, icon: 'none' })
+ }
} finally {
submitting.value = false
}
@@ -611,4 +654,30 @@ const handleSubmit = async () => {
opacity: 0.85;
}
}
+
+.confirm-popup {
+ padding: 48rpx 40rpx 40rpx;
+ width: 560rpx;
+}
+
+.confirm-title {
+ font-size: 34rpx;
+ font-weight: 600;
+ color: #333;
+ text-align: center;
+ margin-bottom: 28rpx;
+}
+
+.confirm-content {
+ font-size: 28rpx;
+ color: #666;
+ line-height: 1.7;
+ text-align: center;
+ margin-bottom: 40rpx;
+}
+
+.confirm-btns {
+ display: flex;
+ gap: 24rpx;
+}
diff --git a/utils/request.js b/utils/request.js
index bc8c3df..491a534 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -49,6 +49,11 @@ export function initRequest(http) {
return Promise.reject({ code, msg: msg || '请先登录' })
}
+ // 1010: 需要前端确认的场景,不自动弹toast,由业务层处理
+ if (code === 1010) {
+ return Promise.reject({ code, data, msg: msg || '' })
+ }
+
uni.showToast({ title: msg || '请求失败', icon: 'none' })
return Promise.reject({ code, msg: msg || '请求失败' })
},