| @@ -2,7 +2,7 @@ | |||
| CREATE TABLE `message` ( | |||
| `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |||
| `patient_id` INT UNSIGNED NOT NULL COMMENT '患者ID', | |||
| `type` TINYINT NOT NULL DEFAULT 0 COMMENT '类型:1=审核通过 2=审核拒绝', | |||
| `type` TINYINT NOT NULL DEFAULT 0 COMMENT '类型:1=审核通过 2=审核拒绝 3=回寄信息', | |||
| `title` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '标题', | |||
| `content` TEXT COMMENT '详细内容', | |||
| `reason` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '驳回原因', | |||
| @@ -0,0 +1,18 @@ | |||
| -- 送检信息配置与编辑状态 | |||
| ALTER TABLE `patient` | |||
| ADD COLUMN `sample_info_status` tinyint(1) DEFAULT 0 COMMENT '送检信息状态: 0可修改 1已生效' AFTER `sample_photos`; | |||
| INSERT INTO `sys_config` (`config_key`, `config_value`, `remark`) VALUES | |||
| ('sample_receiver_info', '{"address":"武汉东湖新技术开发区花城大道8号武汉软件新城三期D2栋17层001室","receiver":"样本中心","phone":"027-6552 6665","contact_phone":""}', '送检收件信息配置') | |||
| ON DUPLICATE KEY UPDATE `remark` = VALUES(`remark`); | |||
| UPDATE `patient` | |||
| SET `sample_info_status` = CASE | |||
| WHEN COALESCE(`sample_types`, '') <> '' | |||
| OR COALESCE(`report_email`, '') <> '' | |||
| OR COALESCE(`sample_tracking_no`, '') <> '' | |||
| OR COALESCE(JSON_LENGTH(`sample_photos`), 0) > 0 | |||
| THEN 1 | |||
| ELSE 0 | |||
| END | |||
| WHERE `is_deleted` = 0; | |||
| @@ -0,0 +1,5 @@ | |||
| -- 送检样本回寄物流信息 | |||
| ALTER TABLE `patient` | |||
| MODIFY COLUMN `sample_info_status` tinyint(1) DEFAULT 0 COMMENT '送检信息状态: 0可修改 1已生效 2已寄回', | |||
| ADD COLUMN `return_tracking_no` varchar(100) DEFAULT '' COMMENT '回寄物流单号' AFTER `sample_info_status`, | |||
| ADD COLUMN `return_time` datetime DEFAULT NULL COMMENT '回寄时间' AFTER `return_tracking_no`; | |||
| @@ -50,6 +50,10 @@ module.exports = [ | |||
| ['/admin/patient/edit', 'admin/patient/edit', 'post'], | |||
| ['/admin/patient/approve', 'admin/patient/approve', 'post'], | |||
| ['/admin/patient/reject', 'admin/patient/reject', 'post'], | |||
| ['/admin/patient/sampleReceiverConfig', 'admin/patient/sampleReceiverConfig'], | |||
| ['/admin/patient/saveSampleReceiverConfig', 'admin/patient/saveSampleReceiverConfig', 'post'], | |||
| ['/admin/patient/resetSampleInfo', 'admin/patient/resetSampleInfo', 'post'], | |||
| ['/admin/patient/saveReturnTrackingNo', 'admin/patient/saveReturnTrackingNo', 'post'], | |||
| ['/admin/patient/export', 'admin/patient/export'], | |||
| // 公共接口 | |||
| ['/common/regions', 'common/regions'], | |||
| @@ -1,7 +1,13 @@ | |||
| const Base = require('../base.js'); | |||
| const dayjs = require('dayjs'); | |||
| const AUDIT_APPROVED_CONTENT = '您提交的肠愈同行患者关爱项目资料已审核通过,请于7天内在小程序-个人中心-送检信息中提交相关信息。'; | |||
| module.exports = class extends Base { | |||
| _canEditPatient() { | |||
| return this.isSuperAdmin || (this.userPermissions || []).includes('patient:edit'); | |||
| } | |||
| // 患者列表页面 | |||
| async indexAction() { | |||
| this.assign('currentPage', 'patient'); | |||
| @@ -74,6 +80,7 @@ module.exports = class extends Base { | |||
| this.assign('patientId', id); | |||
| this.assign('adminUser', this.adminUser || {}); | |||
| this.assign('canAudit', this.isSuperAdmin || (this.userPermissions || []).includes('patient:audit')); | |||
| this.assign('canEdit', this._canEditPatient()); | |||
| return this.display(); | |||
| } | |||
| @@ -127,6 +134,25 @@ module.exports = class extends Base { | |||
| return this.success({ patient, audits }); | |||
| } | |||
| async sampleReceiverConfigAction() { | |||
| const config = await this.model('sys_config').getSampleReceiverInfo(); | |||
| return this.success(config); | |||
| } | |||
| async saveSampleReceiverConfigAction() { | |||
| if (!this._canEditPatient()) return this.fail('暂无权限'); | |||
| const { address, receiver, phone, contact_phone } = this.post(); | |||
| if (!address || !address.trim()) return this.fail('请填写收件地址'); | |||
| if (!receiver || !receiver.trim()) return this.fail('请填写收件人'); | |||
| if (!phone || !phone.trim()) return this.fail('请填写电话'); | |||
| await this.model('sys_config').setSampleReceiverInfo({ address, receiver, phone, contact_phone }); | |||
| await this.log('edit', '患者管理', '编辑送检信息配置'); | |||
| const config = await this.model('sys_config').getSampleReceiverInfo(); | |||
| return this.success(config); | |||
| } | |||
| // 新增患者 | |||
| async addAction() { | |||
| const data = this.post(); | |||
| @@ -253,7 +279,7 @@ module.exports = class extends Base { | |||
| patient_id: id, | |||
| type: 1, | |||
| title: '审核通过', | |||
| content: '您提交的个人资料已审核通过。', | |||
| content: AUDIT_APPROVED_CONTENT, | |||
| reason: '' | |||
| }); | |||
| @@ -336,6 +362,57 @@ module.exports = class extends Base { | |||
| return this.success(); | |||
| } | |||
| async resetSampleInfoAction() { | |||
| if (!this._canEditPatient()) return this.fail('暂无权限'); | |||
| const { id } = this.post(); | |||
| if (!id) return this.fail('参数错误'); | |||
| const patient = await this.model('patient').where({ id, is_deleted: 0 }).find(); | |||
| if (think.isEmpty(patient)) return this.fail('患者不存在'); | |||
| await this.model('patient').where({ id }).update({ | |||
| sample_info_status: 0, | |||
| update_by: this.adminUser?.id || 0 | |||
| }); | |||
| await this.log('edit', '患者管理', `重置送检信息编辑权限(ID:${id})`); | |||
| return this.success(); | |||
| } | |||
| async saveReturnTrackingNoAction() { | |||
| if (!this._canEditPatient()) return this.fail('暂无权限'); | |||
| const { id, return_tracking_no } = this.post(); | |||
| if (!id) return this.fail('参数错误'); | |||
| if (!return_tracking_no || !return_tracking_no.trim()) return this.fail('请填写回寄物流单号'); | |||
| const patient = await this.model('patient').where({ id, is_deleted: 0 }).find(); | |||
| if (think.isEmpty(patient)) return this.fail('患者不存在'); | |||
| if (!patient.wax_return) return this.fail('该患者未选择样本寄回'); | |||
| if (![1, 2].includes(Number(patient.sample_info_status))) return this.fail('送检信息未生效,暂不能填写回寄物流单号'); | |||
| const trackingNo = return_tracking_no.trim(); | |||
| const now = think.datetime(new Date()); | |||
| await this.model('patient').where({ id }).update({ | |||
| sample_info_status: 2, | |||
| return_tracking_no: trackingNo, | |||
| return_time: now, | |||
| update_by: this.adminUser?.id || 0 | |||
| }); | |||
| await this.model('message').where({ patient_id: id, type: 3 }).delete(); | |||
| await this.model('message').add({ | |||
| patient_id: id, | |||
| type: 3, | |||
| title: '回寄信息', | |||
| content: '样品已经寄回,请注意查收。', | |||
| reason: trackingNo, | |||
| is_read: 0 | |||
| }); | |||
| await this.log('edit', '患者管理', `填写回寄物流单号(ID:${id}):${trackingNo}`); | |||
| return this.success(); | |||
| } | |||
| // 编辑患者 | |||
| async editAction() { | |||
| const data = this.post(); | |||
| @@ -6,6 +6,7 @@ const path = require('path'); | |||
| const cosConfig = require('../config/cos.js'); | |||
| const APP_REMARK = 'pap_mini_cytx'; | |||
| const AUDIT_APPROVED_CONTENT = '您提交的肠愈同行患者关爱项目资料已审核通过,请于7天内在小程序-个人中心-送检信息中提交相关信息。'; | |||
| module.exports = class extends Base { | |||
| // POST /api/mp/login | |||
| @@ -637,6 +638,10 @@ module.exports = class extends Base { | |||
| // 查患者姓名 | |||
| const patient = await this.model('patient').field('name').where({ id: user.patient_id }).find(); | |||
| msg.patient_name = patient ? patient.name : ''; | |||
| if (msg.type === 1) { | |||
| msg.content = AUDIT_APPROVED_CONTENT; | |||
| msg.sample_receiver_info = await this.model('sys_config').getSampleReceiverInfo(); | |||
| } | |||
| return this.json({ code: 0, data: msg }); | |||
| } catch (error) { | |||
| think.logger.error('messageDetail error:', error); | |||
| @@ -698,6 +703,7 @@ module.exports = class extends Base { | |||
| try { sampleTypes = JSON.parse(patient.sample_types || '[]'); } catch (e) { sampleTypes = []; } | |||
| let samplePhotos = []; | |||
| try { samplePhotos = JSON.parse(patient.sample_photos || '[]'); } catch (e) { samplePhotos = []; } | |||
| const sampleReceiverInfo = await this.model('sys_config').getSampleReceiverInfo(); | |||
| return this.json({ code: 0, data: { | |||
| patient: { | |||
| @@ -716,7 +722,11 @@ module.exports = class extends Base { | |||
| return_address: patient.return_address || '', | |||
| report_email: patient.report_email || '', | |||
| sample_tracking_no: patient.sample_tracking_no || '', | |||
| sample_photos: samplePhotos | |||
| sample_photos: samplePhotos, | |||
| sample_info_status: Number(patient.sample_info_status) || 0, | |||
| return_tracking_no: patient.return_tracking_no || '', | |||
| return_time: patient.return_time || '', | |||
| sample_receiver_info: sampleReceiverInfo | |||
| }}); | |||
| } catch (error) { | |||
| think.logger.error('sampleInfo error:', error); | |||
| @@ -739,6 +749,30 @@ module.exports = class extends Base { | |||
| const patient = await this.model('patient').where({ id: user.patient_id, is_deleted: 0 }).find(); | |||
| if (think.isEmpty(patient)) return this.json({ code: 1, msg: '患者信息不存在' }); | |||
| if (patient.status !== 1) return this.json({ code: 1, msg: '请先通过审核' }); | |||
| const sampleReceiverInfo = await this.model('sys_config').getSampleReceiverInfo(); | |||
| const sampleInfoStatus = Number(patient.sample_info_status) || 0; | |||
| if (sampleInfoStatus === 2) { | |||
| return this.json({ | |||
| code: 1010, | |||
| data: { | |||
| sample_info_status: 2, | |||
| return_tracking_no: patient.return_tracking_no || '', | |||
| return_time: patient.return_time || '', | |||
| sample_receiver_info: sampleReceiverInfo | |||
| }, | |||
| msg: `样品已经寄回,回寄物流单号:${patient.return_tracking_no || '—'}。` | |||
| }); | |||
| } | |||
| if (sampleInfoStatus === 1) { | |||
| const contactPhone = sampleReceiverInfo.contact_phone || ''; | |||
| return this.json({ | |||
| code: 1010, | |||
| data: { sample_info_status: 1, sample_receiver_info: sampleReceiverInfo }, | |||
| msg: contactPhone | |||
| ? `送检信息已生效,如需修改请联系平台【${contactPhone}】重置修改权限。` | |||
| : '送检信息已生效,如需修改请联系平台重置修改权限。' | |||
| }); | |||
| } | |||
| const hasSample = sample_types && sample_types.length > 0; | |||
| @@ -764,7 +798,8 @@ module.exports = class extends Base { | |||
| return_address: (hasSample && wax_return) ? (return_address || '') : '', | |||
| report_email: hasSample ? (report_email || '') : '', | |||
| sample_tracking_no: hasSample ? (sample_tracking_no || '') : '', | |||
| sample_photos: hasSample ? JSON.stringify(sample_photos || []) : '[]' | |||
| sample_photos: hasSample ? JSON.stringify(sample_photos || []) : '[]', | |||
| sample_info_status: 1 | |||
| }); | |||
| return this.json({ code: 0, data: {}, msg: '提交成功' }); | |||
| @@ -1,3 +1,10 @@ | |||
| const DEFAULT_SAMPLE_RECEIVER_INFO = { | |||
| address: '武汉东湖新技术开发区花城大道8号武汉软件新城三期D2栋17层001室', | |||
| receiver: '样本中心', | |||
| phone: '027-6552 6665', | |||
| contact_phone: '' | |||
| }; | |||
| module.exports = class extends think.Model { | |||
| get tableName() { | |||
| return 'sys_config'; | |||
| @@ -27,4 +34,22 @@ module.exports = class extends think.Model { | |||
| } | |||
| return this.where({ config_key: key }).update({ config_value: val }); | |||
| } | |||
| async getSampleReceiverInfo() { | |||
| const info = await this.getByKey('sample_receiver_info'); | |||
| return { | |||
| ...DEFAULT_SAMPLE_RECEIVER_INFO, | |||
| ...(info && typeof info === 'object' ? info : {}) | |||
| }; | |||
| } | |||
| async setSampleReceiverInfo(info) { | |||
| const value = { | |||
| address: (info.address || '').trim(), | |||
| receiver: (info.receiver || '').trim(), | |||
| phone: (info.phone || '').trim(), | |||
| contact_phone: (info.contact_phone || '').trim() | |||
| }; | |||
| return this.setByKey('sample_receiver_info', value, '送检收件信息配置'); | |||
| } | |||
| }; | |||
| @@ -147,6 +147,13 @@ | |||
| <div class="detail-panel"> | |||
| <h3>送检信息</h3> | |||
| <div class="info-grid"> | |||
| <div class="info-item"><span class="label">状态:</span> | |||
| <span class="value"> | |||
| <el-tag :type="sampleInfoStatusType(patient.sample_info_status)" size="small"> | |||
| ${ sampleInfoStatusText(patient.sample_info_status) } | |||
| </el-tag> | |||
| </span> | |||
| </div> | |||
| <div class="info-item"><span class="label">送检样本:</span> | |||
| <span class="value"> | |||
| <template v-if="patient.sample_types && patient.sample_types.length"> | |||
| @@ -163,6 +170,8 @@ | |||
| </div> | |||
| <div class="info-item" v-if="patient.report_email"><span class="label">报告邮箱:</span><span class="value">${ patient.report_email }</span></div> | |||
| <div class="info-item" v-if="patient.sample_tracking_no"><span class="label">物流单号:</span><span class="value">${ patient.sample_tracking_no }</span></div> | |||
| <div class="info-item" v-if="patient.sample_info_status == 2 && patient.return_tracking_no"><span class="label">回寄单号:</span><span class="value">${ patient.return_tracking_no }</span></div> | |||
| <div class="info-item" v-if="patient.sample_info_status == 2 && patient.return_time"><span class="label">回寄时间:</span><span class="value">${ patient.return_time }</span></div> | |||
| </div> | |||
| <div v-if="patient.sample_photos && patient.sample_photos.length" style="margin-top:16px;"> | |||
| <div style="font-size:13px;color:#909399;margin-bottom:8px;">送检单照片</div> | |||
| @@ -196,6 +205,10 @@ | |||
| <el-button @click="goBack">返 回</el-button> | |||
| <el-button v-if="(patient.status === 0 || patient.status === 2) && canAudit" type="success" @click="handleApprove">审核通过</el-button> | |||
| <el-button v-if="(patient.status === 0 || patient.status === 1) && canAudit" type="danger" @click="showRejectDialog">驳 回</el-button> | |||
| <el-button v-if="patient.sample_info_status == 1 && canEdit" type="warning" @click="resetSampleInfo">重置送检信息编辑</el-button> | |||
| <el-button v-if="patient.wax_return && (patient.sample_info_status == 1 || patient.sample_info_status == 2) && canEdit" type="primary" @click="showReturnTrackingDialog"> | |||
| ${ patient.sample_info_status == 2 ? '修改回寄物流单号' : '填写回寄物流单号' } | |||
| </el-button> | |||
| </div> | |||
| <!-- 驳回弹窗 --> | |||
| @@ -213,12 +226,26 @@ | |||
| </div> | |||
| </el-dialog> | |||
| <!-- 回寄物流单号弹窗 --> | |||
| <el-dialog v-model="returnTrackingVisible" :title="patient.sample_info_status == 2 ? '修改回寄物流单号' : '填写回寄物流单号'" width="460px" destroy-on-close :close-on-click-modal="false" :z-index="2000"> | |||
| <el-form label-width="110px"> | |||
| <el-form-item label="回寄物流单号" required> | |||
| <el-input v-model="returnTrackingNo" placeholder="请输入回寄物流单号" maxlength="100" clearable></el-input> | |||
| </el-form-item> | |||
| </el-form> | |||
| <div style="text-align:right;margin-top:20px;"> | |||
| <el-button @click="returnTrackingVisible = false">取消</el-button> | |||
| <el-button type="primary" @click="saveReturnTrackingNo" :loading="returnTrackingSaving">保存</el-button> | |||
| </div> | |||
| </el-dialog> | |||
| </div>{% endblock %} | |||
| {% block js %} | |||
| <script> | |||
| var patientId = '{{ patientId }}'; | |||
| var canAuditVal = {{ canAudit | dump | safe }}; | |||
| var canEditVal = {{ canEdit | dump | safe }}; | |||
| var { createApp, ref, reactive, onMounted } = Vue; | |||
| var app = createApp({ | |||
| @@ -226,6 +253,7 @@ var app = createApp({ | |||
| setup() { | |||
| var loading = ref(true); | |||
| var canAudit = ref(canAuditVal); | |||
| var canEdit = ref(canEditVal); | |||
| var patient = reactive({ | |||
| id: '', patient_no: '', name: '', phone: '', id_card: '', gender: '', birth_date: '', | |||
| province_code: '', city_code: '', district_code: '', | |||
| @@ -240,6 +268,7 @@ var app = createApp({ | |||
| return_province_name: '', return_city_name: '', return_district_name: '', | |||
| return_address: '', | |||
| report_email: '', sample_tracking_no: '', sample_photos: [], | |||
| sample_info_status: 0, return_tracking_no: '', return_time: '', | |||
| sign_income: '', sign_privacy: '', sign_promise: '', | |||
| emergency_contact: '', emergency_phone: '', | |||
| status: -1, create_time: '' | |||
| @@ -248,6 +277,9 @@ var app = createApp({ | |||
| var rejectVisible = ref(false); | |||
| var rejectSaving = ref(false); | |||
| var rejectReason = ref(''); | |||
| var returnTrackingVisible = ref(false); | |||
| var returnTrackingSaving = ref(false); | |||
| var returnTrackingNo = ref(''); | |||
| var selectedReasons = ref([]); var commonReasons = [ | |||
| '身份证照片模糊,请重新上传', | |||
| '病历资料不完整,请补充', | |||
| @@ -317,6 +349,18 @@ var app = createApp({ | |||
| return [patient.id_card_front, patient.id_card_back, patient.photo].filter(Boolean); | |||
| }); | |||
| function sampleInfoStatusText(status) { | |||
| if (Number(status) === 2) return '已寄回'; | |||
| if (Number(status) === 1) return '已生效'; | |||
| return '可修改'; | |||
| } | |||
| function sampleInfoStatusType(status) { | |||
| if (Number(status) === 2) return 'primary'; | |||
| if (Number(status) === 1) return 'success'; | |||
| return 'warning'; | |||
| } | |||
| async function handleApprove() { | |||
| try { | |||
| await ElementPlus.ElMessageBox.confirm( | |||
| @@ -372,13 +416,66 @@ var app = createApp({ | |||
| } | |||
| } | |||
| async function resetSampleInfo() { | |||
| try { | |||
| await ElementPlus.ElMessageBox.confirm( | |||
| '确认将该患者送检信息状态重置为可修改吗?重置后患者可在小程序重新编辑并提交送检信息。', | |||
| '重置送检信息编辑', | |||
| { confirmButtonText: '确认重置', cancelButtonText: '取消', type: 'warning' } | |||
| ); | |||
| var res = await fetch('/admin/patient/resetSampleInfo', { | |||
| method: 'POST', | |||
| headers: { 'Content-Type': 'application/json' }, | |||
| body: JSON.stringify({ id: patient.id }) | |||
| }).then(function(r) { return r.json(); }); | |||
| if (res.code === 0) { | |||
| ElementPlus.ElMessage.success('已重置为可修改'); | |||
| loadDetail(); | |||
| } else { | |||
| ElementPlus.ElMessage.error(res.msg || '重置失败'); | |||
| } | |||
| } catch(e) {} | |||
| } | |||
| function showReturnTrackingDialog() { | |||
| returnTrackingNo.value = patient.return_tracking_no || ''; | |||
| returnTrackingVisible.value = true; | |||
| } | |||
| async function saveReturnTrackingNo() { | |||
| if (!returnTrackingNo.value.trim()) { | |||
| ElementPlus.ElMessage.warning('请填写回寄物流单号'); | |||
| return; | |||
| } | |||
| returnTrackingSaving.value = true; | |||
| try { | |||
| var res = await fetch('/admin/patient/saveReturnTrackingNo', { | |||
| method: 'POST', | |||
| headers: { 'Content-Type': 'application/json' }, | |||
| body: JSON.stringify({ id: patient.id, return_tracking_no: returnTrackingNo.value.trim() }) | |||
| }).then(function(r) { return r.json(); }); | |||
| if (res.code === 0) { | |||
| ElementPlus.ElMessage.success('保存成功'); | |||
| returnTrackingVisible.value = false; | |||
| loadDetail(); | |||
| } else { | |||
| ElementPlus.ElMessage.error(res.msg || '保存失败'); | |||
| } | |||
| } finally { | |||
| returnTrackingSaving.value = false; | |||
| } | |||
| } | |||
| onMounted(function() { loadDetail(); }); | |||
| return { | |||
| loading, patient, audits, canAudit, | |||
| loading, patient, audits, canAudit, canEdit, | |||
| returnTrackingVisible, returnTrackingSaving, returnTrackingNo, | |||
| rejectVisible, rejectSaving, rejectReason, selectedReasons, commonReasons, | |||
| hospitalRegionText, goBack, downloadSign, isImageUrl, signDocs, signImageList, | |||
| authImageList, handleApprove, showRejectDialog, toggleReason, doReject | |||
| authImageList, sampleInfoStatusText, sampleInfoStatusType, | |||
| handleApprove, showRejectDialog, toggleReason, doReject, resetSampleInfo, | |||
| showReturnTrackingDialog, saveReturnTrackingNo | |||
| }; | |||
| } | |||
| }); | |||
| @@ -35,6 +35,7 @@ | |||
| <el-button v-if="perms.canAdd" type="success" :icon="Plus" @click="showAddDialog">新增患者</el-button> | |||
| <el-button v-if="perms.canExport" :icon="Download" @click="handleExport" :loading="exporting">导出</el-button> | |||
| <el-button v-if="perms.canExport" type="warning" :icon="Download" @click="showExportFilesDialog">异步下载附件</el-button> | |||
| <el-button v-if="perms.canEdit" type="primary" plain @click="showSampleReceiverDialog">送检信息配置</el-button> | |||
| <el-button v-if="perms.canDelete && selectedIds.length" type="danger" :icon="Delete" @click="handleBatchDelete">批量删除 (${ selectedIds.length })</el-button> | |||
| </div> | |||
| </el-card> | |||
| @@ -265,6 +266,28 @@ | |||
| </template> | |||
| </el-dialog> | |||
| <!-- 送检信息配置弹窗 --> | |||
| <el-dialog v-model="sampleReceiverVisible" title="送检信息配置" width="560px" destroy-on-close :close-on-click-modal="false"> | |||
| <el-form :model="sampleReceiverForm" label-width="90px"> | |||
| <el-form-item label="收件地址" required> | |||
| <el-input v-model="sampleReceiverForm.address" type="textarea" :rows="3" placeholder="请输入收件地址" /> | |||
| </el-form-item> | |||
| <el-form-item label="收件人" required> | |||
| <el-input v-model="sampleReceiverForm.receiver" placeholder="请输入收件人" /> | |||
| </el-form-item> | |||
| <el-form-item label="电话" required> | |||
| <el-input v-model="sampleReceiverForm.phone" placeholder="请输入联系电话" /> | |||
| </el-form-item> | |||
| <el-form-item label="平台电话"> | |||
| <el-input v-model="sampleReceiverForm.contact_phone" placeholder="选填,用于小程序修改权限联系平台" /> | |||
| </el-form-item> | |||
| </el-form> | |||
| <template #footer> | |||
| <el-button @click="sampleReceiverVisible = false">取消</el-button> | |||
| <el-button type="primary" @click="saveSampleReceiverConfig" :loading="sampleReceiverSaving">保存</el-button> | |||
| </template> | |||
| </el-dialog> | |||
| <!-- 导出附件弹窗 --> | |||
| <el-dialog v-model="exportFilesVisible" title="异步下载附件" width="480px" destroy-on-close :close-on-click-modal="false"> | |||
| <p style="color:#606266;margin-bottom:16px;">将按当前筛选条件导出患者附件,打包为 ZIP 文件。请选择需要导出的附件类型:</p> | |||
| @@ -362,6 +385,9 @@ const app = createApp({ | |||
| const regionFilter = ref([]); | |||
| const exporting = ref(false); | |||
| const sampleReceiverVisible = ref(false); | |||
| const sampleReceiverSaving = ref(false); | |||
| const sampleReceiverForm = reactive({ address: '', receiver: '', phone: '', contact_phone: '' }); | |||
| const tabStatusMap = { all: '', draft: '-1', pending: '0', rejected: '2', approved: '1' }; | |||
| @@ -647,6 +673,54 @@ const app = createApp({ | |||
| window.open('/admin/patient/export?' + params.toString(), '_blank'); | |||
| } | |||
| async function showSampleReceiverDialog() { | |||
| try { | |||
| var res = await fetch('/admin/patient/sampleReceiverConfig').then(function(r) { return r.json(); }); | |||
| if (res.code === 0) { | |||
| Object.assign(sampleReceiverForm, { | |||
| address: res.data.address || '', | |||
| receiver: res.data.receiver || '', | |||
| phone: res.data.phone || '', | |||
| contact_phone: res.data.contact_phone || '' | |||
| }); | |||
| sampleReceiverVisible.value = true; | |||
| } else { | |||
| ElementPlus.ElMessage.error(res.msg || '获取配置失败'); | |||
| } | |||
| } catch(e) { | |||
| ElementPlus.ElMessage.error('获取配置失败'); | |||
| } | |||
| } | |||
| async function saveSampleReceiverConfig() { | |||
| if (!sampleReceiverForm.address.trim()) return ElementPlus.ElMessage.warning('请填写收件地址'); | |||
| if (!sampleReceiverForm.receiver.trim()) return ElementPlus.ElMessage.warning('请填写收件人'); | |||
| if (!sampleReceiverForm.phone.trim()) return ElementPlus.ElMessage.warning('请填写电话'); | |||
| sampleReceiverSaving.value = true; | |||
| try { | |||
| var res = await fetch('/admin/patient/saveSampleReceiverConfig', { | |||
| method: 'POST', | |||
| headers: { 'Content-Type': 'application/json' }, | |||
| body: JSON.stringify({ | |||
| address: sampleReceiverForm.address.trim(), | |||
| receiver: sampleReceiverForm.receiver.trim(), | |||
| phone: sampleReceiverForm.phone.trim(), | |||
| contact_phone: sampleReceiverForm.contact_phone.trim() | |||
| }) | |||
| }).then(function(r) { return r.json(); }); | |||
| if (res.code === 0) { | |||
| ElementPlus.ElMessage.success('保存成功'); | |||
| sampleReceiverVisible.value = false; | |||
| } else { | |||
| ElementPlus.ElMessage.error(res.msg || '保存失败'); | |||
| } | |||
| } finally { | |||
| sampleReceiverSaving.value = false; | |||
| } | |||
| } | |||
| // 导出附件 | |||
| const exportFilesVisible = ref(false); | |||
| const exportFilesLoading = ref(false); | |||
| @@ -720,10 +794,11 @@ const app = createApp({ | |||
| keyword, dateRange, tagFilter, regionFilter, activeTab, loading, tableData, pagination, counts, | |||
| uploadHeaders, addVisible, addSaving, addForm, exporting, editingId, perms, selectedIds, | |||
| regionTree, tagOptions, isMinorComputed, Plus, Download, Delete, | |||
| sampleReceiverVisible, sampleReceiverSaving, sampleReceiverForm, | |||
| exportFilesVisible, exportFilesLoading, exportFileTypes, exportSuccessVisible, | |||
| loadList, resetFilter, onTabChange, onSizeChange, onSelectionChange, viewDetail, showAddDialog, showEditDialog, handleExport, | |||
| onIdCardInput, onDocUpload, onSignUpload, submitAdd, handleDelete, handleBatchDelete, | |||
| showExportFilesDialog, submitExportFiles, goExportTask | |||
| showSampleReceiverDialog, saveSampleReceiverConfig, showExportFilesDialog, submitExportFiles, goExportTask | |||
| }; | |||
| } | |||
| }); | |||