From 99e71096acdf12abdf3ebd4515126d2dbf0fd3c6 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Tue, 13 Oct 2026 13:14:09 +0800 Subject: [PATCH] feat(mobile-oa-h5): align research donation protocol card with excel fields Co-authored-by: Cursor --- mobile-oa-h5/src/market/list-presenter.js | 85 ++++++++++--------- .../src/market/list-presenter.test.js | 75 ++++++++++------ 2 files changed, 94 insertions(+), 66 deletions(-) diff --git a/mobile-oa-h5/src/market/list-presenter.js b/mobile-oa-h5/src/market/list-presenter.js index f08d5024..4c934437 100644 --- a/mobile-oa-h5/src/market/list-presenter.js +++ b/mobile-oa-h5/src/market/list-presenter.js @@ -2,7 +2,6 @@ const SEARCH_PLACEHOLDERS = Object.freeze({ protocol: '搜索母亲姓名 / 样本编码', 'payment-chase': '搜索协议号 / 客户 / 样本编码', payment: '搜索协议号 / 客户 / 样本编码', - sample: '搜索母亲姓名 / 样本编码', freight: '搜索批次号 / 母亲姓名 / 样本编码' }) @@ -19,13 +18,10 @@ const ACTION_LABELS = Object.freeze({ 'view-payment': '查看缴费', 'view-payment-detail': '缴费明细', 'view-protocol': '协议单', - 'view-storage-qc': '储存证质检信息', 'register-transfer': '登记转账', 'edit-transfer': '编辑转账', submit: '提交财务确认', withdraw: '撤回转账', - 'storage-cert': '登记储存证', - insurance: '登记保险', link: '关联样本', unlink: '取消运费' }) @@ -67,7 +63,7 @@ export function money(value) { } export function marketSearchPlaceholder(module, source) { - if (module === 'protocol' || module === 'sample') { + if (module === 'protocol') { return PROTOCOL_SEARCH_PLACEHOLDERS[source] || SEARCH_PLACEHOLDERS[module] } return SEARCH_PLACEHOLDERS[module] || '搜索编号、姓名或关键词' @@ -82,16 +78,55 @@ function allowed(item, ownActions = []) { return ownActions.filter((action) => backend.has(action)) } +function attachmentPresence(urls) { + return String(urls || '').trim() ? '有' : '—' +} + function protocolCard(item) { const actions = allowed(item, ['edit', 'delete']) + const isDonation = item.source === 'research_donation' const status = label(PROTOCOL_STATUS_LABELS, item.protocolStatus || item.donationStatus || item.status) + if (isDonation) { + const collectDate = String(item.sampleCollectTime || '').slice(0, 10) + return { + title: text(item.motherName || item.customerName), + subtitle: `${label(SOURCE_LABELS, item.source)} · ${text(item.labStorageCode || item.sampleCode)}`, + status, + fields: [ + field('采集日期', collectDate || item.donationDate), + field('当月核量号', item.monthlyVerifyNo), + field('实验室流转编号', item.labStorageCode || item.sampleCode, { wide: true }), + field('协议状态', status, { tag: true }), + field('废弃原因', item.voidDesc), + field('母亲姓名', item.motherName || item.customerName), + field('样本类型', item.sampleTypeName), + field('采集地区', referenceName(item.regionName, item.regionId)), + field('采集医院', referenceName(item.hospitalName, item.hospitalId), { wide: true }), + field('分娩时间', item.newbornDeliveryTime, { wide: true }), + field('采集时间', item.sampleCollectTime, { wide: true }), + field('市场专员', personName(item.salesUserName, item.salesUserId)), + field('地区经理', personName(item.regionManagerUserName || item.managerName, item.regionManagerUserId)), + field('运输方式', item.transportModeLabel || item.transportMode), + field('新生儿性别', label(NEWBORN_SEX_LABELS, item.newbornSex)), + field('新生儿体重', item.newbornWeightGram), + field('分娩方式', label(DELIVERY_MODE_LABELS, item.deliveryMode)), + field('胎次', item.birthOrder), + field('母血采集时间', item.maternalBloodCollectTime, { wide: true }), + field('知情同意书', attachmentPresence(item.consentFormUrls)), + field('传染病检测报告', attachmentPresence(item.infectiousDiseaseReportUrls)), + field('协议书', attachmentPresence(item.attachmentUrls)), + field('备注', item.remark, { wide: true }) + ], + actions: ['view', ...actions] + } + } return { title: text(item.motherName || item.customerName), subtitle: `${label(SOURCE_LABELS, item.source)} · ${text(item.sampleCode)}`, status, fields: [ field('状态', status, { tag: true }), - field(item.source === 'research_donation' ? '捐赠日期' : '登记日期', item.registerDate || item.donationDate), + field('登记日期', item.registerDate || item.donationDate), field('母亲姓名', item.motherName || item.customerName), field('母亲电话', item.motherPhone), field('母亲身份证号', item.motherIdCard, { wide: true }), @@ -145,10 +180,10 @@ function paymentCard(item) { ] }) return { - title: text(item.motherName), subtitle: text(item.protocolNo), status: progress, + title: text(item.motherName), subtitle: text(item.sampleCode), status: progress, fields: [ field('姓名', item.motherName), field('手机号', item.motherPhone), - field('合同编号', item.protocolNo, { wide: true }), field('签署日期', item.signDate), + field('样本编码', item.sampleCode, { wide: true }), field('签署日期', item.signDate), field('存储年限', item.storageYears), field('存储项目', item.sampleTypeName, { wide: true }), field('合同总额', money(item.contractAmount), { money: true }), field('缴费方式', label(PAY_MODE_LABELS, item.payMode), { tag: true }), @@ -163,39 +198,6 @@ function paymentCard(item) { } } -function progressLabel(item, key, fallback) { - const value = item.progress?.[key]?.label - return value ? label({}, value) : fallback -} - -function sampleCard(item) { - const status = label(PROTOCOL_STATUS_LABELS, item.protocolStatus || item.donationStatus || item.statusLabel) - return { - title: text(item.motherName || item.customerName), subtitle: text(item.sampleCode), status, - fields: [ - field('协议状态', status, { tag: true }), field('预产期', item.expectedDeliveryDate), - field('母亲姓名', item.motherName || item.customerName), field('新生儿姓名', item.newbornName), - field('样本编码', item.sampleCode, { wide: true }), field('实验室入库编码', item.labStorageCode, { wide: true }), - field('样本类型', referenceName(item.sampleTypeName, item.sampleTypeId), { wide: true }) - ], - progressTags: [ - `采集 · ${progressLabel(item, 'collection', '未采集')}`, - `接收 · ${progressLabel(item, 'reception', '未接收')}`, - `质检 · ${progressLabel(item, 'qc', '未完成')}`, - `入库 · ${progressLabel(item, 'storage', '未入库')}`, - ...(item.source === 'cell_storage' ? [ - `储存证 · ${item.storageCert?.label ? label({}, item.storageCert.label) : '未办理'}`, - `保险 · ${item.insurance?.label ? label({}, item.insurance.label) : '未办理'}` - ] : []) - ], - actions: [ - 'view', - ...(item.source === 'cell_storage' ? ['view-storage-qc'] : []), - ...allowed(item, ['storage-cert', 'insurance']) - ] - } -} - function freightCard(role, item) { const first = item.items?.[0] || {} const transportMode = label({}, item.transportModeLabel || item.transportMode) @@ -223,7 +225,6 @@ export function buildMarketCard(module, role, item = {}) { if (module === 'protocol') return protocolCard(item) if (module === 'payment-chase') return chaseCard(item) if (module === 'payment') return paymentCard(item) - if (module === 'sample') return sampleCard(item) if (module === 'freight') return freightCard(role, item) return { title: text(item.title || item.name || item.code), subtitle: '', status: '', fields: [], actions: ['view'] } } diff --git a/mobile-oa-h5/src/market/list-presenter.test.js b/mobile-oa-h5/src/market/list-presenter.test.js index 44481025..0ca4db3b 100644 --- a/mobile-oa-h5/src/market/list-presenter.test.js +++ b/mobile-oa-h5/src/market/list-presenter.test.js @@ -19,15 +19,6 @@ test('remaining modules use the approved search placeholders', () => { ) assert.equal(marketSearchPlaceholder('payment-chase'), '搜索协议号 / 客户 / 样本编码') assert.equal(marketSearchPlaceholder('payment'), '搜索协议号 / 客户 / 样本编码') - assert.equal(marketSearchPlaceholder('sample'), '搜索母亲姓名 / 样本编码') - assert.equal( - marketSearchPlaceholder('sample', 'cell_storage'), - '搜索母亲姓名 / 样本编码' - ) - assert.equal( - marketSearchPlaceholder('sample', 'research_donation'), - '搜索母亲姓名 / 捐赠编号 / 样本编码' - ) assert.equal(marketSearchPlaceholder('freight'), '搜索批次号 / 母亲姓名 / 样本编码') }) @@ -56,6 +47,52 @@ test('protocol card exposes web parity fields without truncating to three rows', }).status, '转细胞存储') }) +test('research donation protocol card matches excel-parity field set', () => { + const card = buildMarketCard('protocol', 'specialist', { + source: 'research_donation', + motherName: '曹陆双', + sampleCode: 'S-D1', + donationStatus: 'sdfq', + sampleCollectTime: '2025-08-29 12:27:00', + monthlyVerifyNo: '202508001', + labStorageCode: 'A0000001', + voidDesc: '母血乙肝阳性', + sampleTypeName: '围产期造血干细胞', + regionName: '亳州', + hospitalName: '涡阳县人民医院', + newbornDeliveryTime: '2025-08-29 12:26:00', + salesUserName: '杨莉莉', + regionManagerUserName: '张颖', + managerName: '张颖', + transportMode: 'kdys', + transportModeLabel: '快递运输', + newbornSex: '0', + newbornWeightGram: 3000, + deliveryMode: 'sc', + birthOrder: 1, + maternalBloodCollectTime: '2025-08-29 13:30:00', + consentFormUrls: 'https://x/a.pdf', + infectiousDiseaseReportUrls: '', + attachmentUrls: 'https://x/b.pdf', + remark: '备注一', + allowedActions: ['edit'] + }) + const labels = card.fields.map((f) => f.label) + for (const label of [ + '当月核量号', '实验室流转编号', '协议状态', '废弃原因', '样本类型', + '采集地区', '采集医院', '分娩时间', '采集时间', '市场专员', '地区经理', + '运输方式', '新生儿性别', '新生儿体重', '分娩方式', '胎次', '母血采集时间', + '知情同意书', '传染病检测报告', '协议书', '备注' + ]) { + assert.ok(labels.includes(label), `missing ${label}`) + } + assert.equal(card.fields.find((f) => f.label === '当月核量号').value, '202508001') + assert.equal(card.fields.find((f) => f.label === '知情同意书').value, '有') + assert.equal(card.fields.find((f) => f.label === '传染病检测报告').value, '—') + assert.equal(card.fields.find((f) => f.label === '协议书').value, '有') + assert.ok(!labels.includes('是否同存')) +}) + test('payment card formats summary totals and progress', () => { const card = buildMarketCard('payment', 'manager', { protocolNo: 'P-1', motherName: '周雨桐', sampleCode: 'S-1', payMode: 'fq', @@ -67,6 +104,8 @@ test('payment card formats summary totals and progress', () => { allowedActions: ['register-transfer'] }) assert.equal(card.status, '部分缴费') + assert.equal(card.subtitle, 'S-1') + assert.ok(card.fields.some((field) => field.label === '样本编码' && field.value === 'S-1')) assert.ok(card.fields.some((field) => field.label === '签署日期' && field.value === '2026-10-01')) assert.ok(card.fields.some((field) => field.label === '存储项目' && field.value === '脐带血')) assert.ok(card.fields.some((field) => field.label === '第1期·应缴费金额(元)' && field.value === '¥3,000.00')) @@ -101,8 +140,6 @@ test('specialist freight card masks batch-wide counts while manager sees visible test('action labels cover module-specific operations', () => { assert.equal(actionLabel('audit'), '审核') - assert.equal(actionLabel('storage-cert'), '登记储存证') - assert.equal(actionLabel('insurance'), '登记保险') assert.equal(actionLabel('link'), '关联样本') assert.equal(actionLabel('unlink'), '取消运费') assert.equal(actionLabel('internal-action-code'), '操作') @@ -132,19 +169,9 @@ test('list cards hide unresolved reference and dictionary codes while retaining assert.equal(freight.fields.find((field) => field.label === '样本编码').value, 'S-KEEP-2') const payment = buildMarketCard('payment', 'manager', { - protocolNo: 'P-KEEP-2', payMode: 'raw-pay-mode' + protocolNo: 'P-KEEP-2', sampleCode: 'S-KEEP-2', payMode: 'raw-pay-mode' }) - assert.equal(payment.subtitle, 'P-KEEP-2') + assert.equal(payment.subtitle, 'S-KEEP-2') + assert.equal(payment.fields.find((field) => field.label === '样本编码').value, 'S-KEEP-2') assert.equal(payment.fields.find((field) => field.label === '缴费方式').value, '未知') - - const sample = buildMarketCard('sample', 'specialist', { - sampleCode: 'S-KEEP-3', protocolStatus: 'raw-status', sampleTypeId: 'type-raw', - progress: { collection: { label: 'raw-progress' } }, - source: 'cell_storage', storageCert: { label: 'raw-cert-status' } - }) - assert.equal(sample.subtitle, 'S-KEEP-3') - assert.equal(sample.status, '未知') - assert.equal(sample.fields.find((field) => field.label === '样本类型').value, '未知') - assert.ok(sample.progressTags.includes('采集 · 未知')) - assert.ok(sample.progressTags.includes('储存证 · 未知')) })