From 60aaa9fd6d3f3b78a0dc570dba6e06db15883780 Mon Sep 17 00:00:00 2001 From: leiyun Date: Sun, 22 Mar 2026 00:03:27 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E7=B3=BB=E5=88=97BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/guardian_fields.sql | 5 +++ src/controller/admin/patient.js | 27 +++++++++--- src/controller/mp.js | 8 +++- src/middleware/request_log.js | 8 +++- view/admin/patient_detail.html | 44 +++++++++++++++++-- view/admin/patient_index.html | 77 ++++++++++++++++++++++++++++----- 6 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 sql/guardian_fields.sql diff --git a/sql/guardian_fields.sql b/sql/guardian_fields.sql new file mode 100644 index 0000000..4f60320 --- /dev/null +++ b/sql/guardian_fields.sql @@ -0,0 +1,5 @@ +-- 患者表增加监护人信息字段 +ALTER TABLE `patient` + ADD COLUMN `guardian_name` varchar(50) NOT NULL DEFAULT '' COMMENT '监护人姓名' AFTER `sign_privacy_jhr`, + ADD COLUMN `guardian_id_card` varchar(18) NOT NULL DEFAULT '' COMMENT '监护人身份证号' AFTER `guardian_name`, + ADD COLUMN `guardian_relation` varchar(20) NOT NULL DEFAULT '' COMMENT '与患者关系' AFTER `guardian_id_card`; diff --git a/src/controller/admin/patient.js b/src/controller/admin/patient.js index 0d96aca..5b15aa5 100644 --- a/src/controller/admin/patient.js +++ b/src/controller/admin/patient.js @@ -1,4 +1,5 @@ const Base = require('../base.js'); +const dayjs = require('dayjs'); module.exports = class extends Base { // 患者列表页面 @@ -122,7 +123,7 @@ module.exports = class extends Base { // 新增患者 async addAction() { const data = this.post(); - const { name, phone, id_card, gender, birth_date, province_code, city_code, district_code, address, emergency_contact, emergency_phone, tag, documents, sign_income, sign_privacy, sign_promise } = data; + const { name, phone, id_card, gender, birth_date, province_code, city_code, district_code, address, emergency_contact, emergency_phone, tag, documents, sign_income, sign_privacy, sign_promise, sign_privacy_jhr, income_amount, guardian_name, guardian_id_card, guardian_relation } = data; if (!name || !phone || !id_card || !gender || !birth_date) { return this.fail('请填写完整信息'); @@ -165,14 +166,19 @@ module.exports = class extends Base { sign_income: sign_income || '', sign_privacy: sign_privacy || '', sign_promise: sign_promise || '', - status: 0, + sign_privacy_jhr: sign_privacy_jhr || '', + income_amount: income_amount || '', + guardian_name: guardian_name || '', + guardian_id_card: guardian_id_card || '', + guardian_relation: guardian_relation || '', + status: 1, create_by: this.adminUser?.id || 0 }); - // 记录审核日志 + // 记录审核日志(后台新增直接审核通过) await this.model('patient_audit').add({ patient_id: id, - action: 'submit', + action: 'approve', operator_id: this.adminUser?.id || 0, operator_name: this.adminUser?.nickname || this.adminUser?.username || '' }); @@ -272,7 +278,7 @@ module.exports = class extends Base { // 编辑患者 async editAction() { const data = this.post(); - const { id, name, phone, id_card, gender, birth_date, province_code, city_code, district_code, address, emergency_contact, emergency_phone, tag, documents, sign_income, sign_privacy, sign_promise } = data; + const { id, name, phone, id_card, gender, birth_date, province_code, city_code, district_code, address, emergency_contact, emergency_phone, tag, documents, sign_income, sign_privacy, sign_promise, sign_privacy_jhr, income_amount, guardian_name, guardian_id_card, guardian_relation } = data; if (!id) return this.fail('参数错误'); if (!name || !phone || !id_card || !gender || !birth_date) return this.fail('请填写完整信息'); @@ -295,6 +301,11 @@ module.exports = class extends Base { sign_income: sign_income || '', sign_privacy: sign_privacy || '', sign_promise: sign_promise || '', + sign_privacy_jhr: sign_privacy_jhr || '', + income_amount: income_amount || '', + guardian_name: guardian_name || '', + guardian_id_card: guardian_id_card || '', + guardian_relation: guardian_relation || '', update_by: this.adminUser?.id || 0 }); @@ -337,7 +348,7 @@ module.exports = class extends Base { } const statusMap = { '-1': '待提交', 0: '待审核', 1: '审核通过', 2: '已驳回' }; - const header = ['ID', '姓名', '性别', '身份证', '手机号', '省份', '城市', '审核状态', '审核日期', '审核驳回原因', '瘤种']; + const header = ['ID', '姓名', '性别', '身份证', '手机号', '省份', '城市', '提交时间', '审核状态', '审核日期', '审核驳回原因', '瘤种']; const ExcelJS = require('exceljs'); const workbook = new ExcelJS.Workbook(); @@ -379,6 +390,7 @@ module.exports = class extends Base { item.phone, regionMap[item.province_code] || '', regionMap[item.city_code] || '', + item.create_time || '', statusMap[item.status] || '', audit ? (audit.create_time || '') : '', (audit && audit.action === 'reject') ? (audit.reason || '') : '', @@ -401,7 +413,8 @@ module.exports = class extends Base { }); const buffer = await workbook.xlsx.writeBuffer(); - const fileName = encodeURIComponent(`患者信息_${Date.now()}.xlsx`); + const ts = dayjs().format('YYYYMMDDHHmmss'); + const fileName = encodeURIComponent(`患者信息_${ts}.xlsx`); this.ctx.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); this.ctx.set('Content-Disposition', `attachment; filename*=UTF-8''${fileName}`); diff --git a/src/controller/mp.js b/src/controller/mp.js index 0fdf43f..df3be58 100644 --- a/src/controller/mp.js +++ b/src/controller/mp.js @@ -306,6 +306,9 @@ module.exports = class extends Base { sign_privacy_jhr: patient.sign_privacy_jhr || '', sign_promise: patient.sign_promise || '', income_amount: patient.income_amount || '', + guardian_name: patient.guardian_name || '', + guardian_id_card: patient.guardian_id_card || '', + guardian_relation: patient.guardian_relation || '', status: patient.status, auth_status: patient.auth_status || 0, reject_reason: rejectReason }}); @@ -319,7 +322,7 @@ module.exports = class extends Base { async saveMyInfoAction() { const mpUser = this.mpUser; if (!mpUser) return this.json({ code: 1009, msg: '请先登录' }); - const { gender, province_code, city_code, district_code, address, emergency_contact, emergency_phone, documents, sign_income, sign_privacy, sign_privacy_jhr, sign_promise, income_amount, mp_env_version } = this.post(); + const { gender, province_code, city_code, district_code, address, emergency_contact, emergency_phone, documents, sign_income, sign_privacy, sign_privacy_jhr, sign_promise, income_amount, guardian_name, guardian_id_card, guardian_relation, mp_env_version } = this.post(); const user = await this.model('wechat_user').where({ id: mpUser.id, status: 1 }).find(); if (think.isEmpty(user) || !user.patient_id) return this.json({ code: 1, msg: '请先完成实名认证' }); if (!province_code || !city_code || !district_code) return this.json({ code: 1, msg: '请选择省市区' }); @@ -341,6 +344,9 @@ module.exports = class extends Base { sign_privacy_jhr: sign_privacy_jhr || '', sign_promise: sign_promise || '', income_amount: income_amount || null, + guardian_name: guardian_name || '', + guardian_id_card: guardian_id_card || '', + guardian_relation: guardian_relation || '', status: 0, update_time: now }); diff --git a/src/middleware/request_log.js b/src/middleware/request_log.js index 5bcc69d..7c090f9 100644 --- a/src/middleware/request_log.js +++ b/src/middleware/request_log.js @@ -3,9 +3,15 @@ const dayjs = require('dayjs'); module.exports = () => { return async (ctx, next) => { // 跳过静态资源和source map - if (ctx.path.startsWith('/static/') || ctx.path === '/favicon.ico' || ctx.path.endsWith('.map')) { + if (ctx.path.startsWith('/static/') || ctx.path === '/favicon.ico') { return next(); } + // source map / json 静态请求直接返回 404,避免 think-trace 报错 + if (ctx.path.endsWith('.map') || ctx.path.endsWith('.json')) { + ctx.status = 404; + ctx.body = ''; + return; + } const start = Date.now(); const params = { ...ctx.query, ...(ctx.request.body || {}) }; diff --git a/view/admin/patient_detail.html b/view/admin/patient_detail.html index d81e582..22e3b9b 100644 --- a/view/admin/patient_detail.html +++ b/view/admin/patient_detail.html @@ -63,6 +63,36 @@
提交时间:${ patient.create_time }
紧急联系人:${ patient.emergency_contact || '—' }
紧急联系电话:${ patient.emergency_phone || '—' }
+
年可支配收入:${ patient.income_amount } 元
+
监护人姓名:${ patient.guardian_name }
+
监护人身份证:${ patient.guardian_id_card }
+
与患者关系:${ patient.guardian_relation }
+ + + + +
+

实名认证照片

+

+ + +

+
+
+ +
人像面
+
+
+ +
国徽面
+
+
+ +
免冠照片
+
@@ -212,15 +242,23 @@ var app = createApp({ } var signDocs = Vue.computed(function() { - return [ + var docs = [ { key: 'income', label: '个人可支配收入声明', url: patient.sign_income }, { key: 'privacy', label: '个人信息处理同意书', url: patient.sign_privacy }, { key: 'promise', label: '声明与承诺', url: patient.sign_promise } ]; + if (patient.sign_privacy_jhr) { + docs.push({ key: 'privacy_jhr', label: '监护人个人信息处理同意书', url: patient.sign_privacy_jhr }); + } + return docs; }); var signImageList = Vue.computed(function() { - return [patient.sign_income, patient.sign_privacy, patient.sign_promise].filter(function(u) { return u && isImageUrl(u); }); + return [patient.sign_income, patient.sign_privacy, patient.sign_promise, patient.sign_privacy_jhr].filter(function(u) { return u && isImageUrl(u); }); + }); + + var authImageList = Vue.computed(function() { + return [patient.id_card_front, patient.id_card_back, patient.photo].filter(Boolean); }); async function handleApprove() { @@ -283,7 +321,7 @@ var app = createApp({ return { loading, patient, audits, canAudit, rejectVisible, rejectSaving, rejectReason, selectedReasons, commonReasons, - goBack, downloadSign, isImageUrl, signDocs, signImageList, handleApprove, showRejectDialog, toggleReason, doReject + goBack, downloadSign, isImageUrl, signDocs, signImageList, authImageList, handleApprove, showRejectDialog, toggleReason, doReject }; } }); diff --git a/view/admin/patient_index.html b/view/admin/patient_index.html index 8c10272..467c42e 100644 --- a/view/admin/patient_index.html +++ b/view/admin/patient_index.html @@ -170,14 +170,38 @@ + + + + + + + + + + + + + + + + + + + + + + + +
- - × + style="position:relative;width:80px;height:80px;"> + +
×
@@ -208,6 +232,12 @@ ${ addForm.sign_promise ? '✅' : '📄' } 声明与承诺 + + + ${ addForm.sign_privacy_jhr ? '✅' : '📄' } 监护人个人信息处理同意书 + +
@@ -223,7 +253,7 @@ {% block js %}