From a4425addb01832a19ad835f19c31447d935442cf Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 6 Nov 2026 12:11:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(web-admin):=20=E7=BB=86=E8=83=9E=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E5=AF=BC=E5=87=BA=E5=8D=8F=E8=AE=AE=E7=BC=96=E5=8F=B7?= =?UTF-8?q?=E6=94=B9=E6=A0=B7=E6=9C=AC=E7=BC=96=E7=A0=81=E5=B9=B6=E8=A1=A5?= =?UTF-8?q?=E9=BD=90=E8=BF=90=E8=B4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 自存表导出列值改为样本编码;列表补运费后写入运输方式/运费列,并按同批次合并单元格。 --- backend/app/crud/collection_protocol.py | 15 +++++ .../utils/collection-ledger-export.test.ts | 47 +++++++++++++- .../src/utils/collection-ledger-export.ts | 61 +++++++++++++------ .../protocol-audit/ProtocolAuditTab.vue | 6 +- 4 files changed, 105 insertions(+), 24 deletions(-) diff --git a/backend/app/crud/collection_protocol.py b/backend/app/crud/collection_protocol.py index 63dd0e89..08a3f0b5 100644 --- a/backend/app/crud/collection_protocol.py +++ b/backend/app/crud/collection_protocol.py @@ -1291,10 +1291,23 @@ def list_collection_protocols( ) from app.services.qc_work_order_fee_summary import build_protocol_payment_summaries + from app.crud import sample_freight as sample_freight_svc payment_summary_map = build_protocol_payment_summaries( db, protocol_ids, tenant_no, include_sample_payment_modes=True ) + all_list_sample_ids = [ + s.id + for samples in sample_map.values() + for s in samples + if getattr(s, "id", None) + ] + freight_by_sample = sample_freight_svc.freight_map_for_samples( + db, + tenant_no=tenant_no, + sample_source=sample_freight_svc.SAMPLE_SOURCE_CELL, + sample_ids=all_list_sample_ids, + ) # 合并主表 + 子表数据 result = [] @@ -1376,11 +1389,13 @@ def list_collection_protocols( } sample_items.append( { + "id": s.id, "sampleTypeName": type_name, "sampleCode": code, "sampleStatus": (s.sample_status or "").strip(), "labStorageCode": lab_code_map.get(code, "") if code else "", "transport": transport_dict, + "freight": freight_by_sample.get(s.id), **sample_special_to_dict(s), **build_sample_item_reception_qc_fields( reception_summary_map.get(code) if code else None, diff --git a/web-admin/src/utils/collection-ledger-export.test.ts b/web-admin/src/utils/collection-ledger-export.test.ts index fd99040b..ba51f63a 100644 --- a/web-admin/src/utils/collection-ledger-export.test.ts +++ b/web-admin/src/utils/collection-ledger-export.test.ts @@ -65,7 +65,8 @@ describe('buildCellStorageLedgerRows', () => { isCoStored: false, sampleCollectTime: '2025-09-15 08:51:00', motherName: '郭梦朵', - protocolNo: 'ZKZX34000849', + protocolNo: 'PROTO-SHOULD-NOT-EXPORT', + sampleCode: 'ZKZX34000849', sampleTypeName: '围产期造血干细胞', regionName: '亳州区域', hospitalName: '涡阳县人民医院', @@ -73,6 +74,9 @@ describe('buildCellStorageLedgerRows', () => { salesUserName: '马丽', regionManagerUserName: '张颖', transportType: 'kdys', + freightId: 'fr-cell-1', + freightTransportLabel: '专线车', + freightAmountLabel: '120', paymentSummary: { payMode: 'fq', contractAmount: 22880, @@ -97,11 +101,13 @@ describe('buildCellStorageLedgerRows', () => { ] } } as unknown as ProtocolAuditFlatRow - const [row] = buildCellStorageLedgerRows([flat]) + const { rows } = buildCellStorageLedgerRows([flat]) + const row = rows[0] assert.equal(row.length, CELL_STORAGE_HEADERS.length) assert.equal(CELL_STORAGE_HEADERS[13], '涡阳县人民医院') assert.equal(row[5], 'B0000004') assert.equal(row[6], '生效中') + assert.equal(row[9], 'ZKZX34000849') assert.equal(row[11], '否') assert.equal(row[13], '涡阳县人民医院') assert.equal(row[14], '分期') @@ -112,7 +118,42 @@ describe('buildCellStorageLedgerRows', () => { assert.equal(row[25], '16400') assert.equal(row[26], '2025年12月15日') assert.equal(row[50], '马丽') - assert.equal(row[52], '快递运输') + assert.equal(row[52], '专线车') + assert.equal(row[53], '120') + }) + + it('merges transport and freight columns for the same freight batch', () => { + const base = { + protocol: { protocolStatus: 'sxz', remark: '', voidType: '', voidDesc: '' }, + sampleCollectTime: '2025-09-11 18:13:00', + motherName: '甲', + sampleCode: 'S1', + sampleTypeName: 'NK', + regionName: '常州', + hospitalName: '溧阳市人民医院', + labStorageCode: 'UBMC1', + freightId: 'batch-cell', + freightTransportLabel: '专线车', + freightAmountLabel: '100' + } + const { rows, merges } = buildCellStorageLedgerRows([ + { ...base, sampleIndex: 0 } as unknown as ProtocolAuditFlatRow, + { + ...base, + sampleIndex: 1, + motherName: '乙', + sampleCode: 'S2', + labStorageCode: 'UBMC2' + } as unknown as ProtocolAuditFlatRow + ]) + assert.equal(rows[0][52], '专线车') + assert.equal(rows[0][53], '100') + assert.equal(rows[1][52], '') + assert.equal(rows[1][53], '') + assert.deepEqual(merges, [ + { startRow: 2, endRow: 3, startCol: 53, endCol: 53 }, + { startRow: 2, endRow: 3, startCol: 54, endCol: 54 } + ]) }) }) diff --git a/web-admin/src/utils/collection-ledger-export.ts b/web-admin/src/utils/collection-ledger-export.ts index 94afa812..edf670a2 100644 --- a/web-admin/src/utils/collection-ledger-export.ts +++ b/web-admin/src/utils/collection-ledger-export.ts @@ -338,8 +338,16 @@ function researchMaternalBloodText(value: unknown): string { return text(value) ? text(value) : '无母血' } -/** 相邻且 freightId 相同的导出行,合并运输方式/运费列(Excel 行号含表头)。 */ -export function buildFreightMergeRanges(freightIds: string[]): LedgerMergeRange[] { +/** + * 相邻且 freightId 相同的导出行,合并运输方式/运费列(Excel 行号含表头)。 + * @param transportCol 运输方式列(1-based) + * @param freightCol 运费列(1-based) + */ +export function buildFreightMergeRanges( + freightIds: string[], + transportCol = 17, + freightCol = 18 +): LedgerMergeRange[] { const merges: LedgerMergeRange[] = [] let index = 0 while (index < freightIds.length) { @@ -353,14 +361,31 @@ export function buildFreightMergeRanges(freightIds: string[]): LedgerMergeRange[ if (end - index >= 2) { const startRow = index + 2 const endRow = end + 1 - merges.push({ startRow, endRow, startCol: 17, endCol: 17 }) - merges.push({ startRow, endRow, startCol: 18, endCol: 18 }) + merges.push({ startRow, endRow, startCol: transportCol, endCol: transportCol }) + merges.push({ startRow, endRow, startCol: freightCol, endCol: freightCol }) } index = end } return merges } +function applyFreightMerges( + built: string[][], + freightIds: string[], + transportCol: number, + freightCol: number +): LedgerMergeRange[] { + const merges = buildFreightMergeRanges(freightIds, transportCol, freightCol) + for (const merge of merges) { + const colIndex = merge.startCol - 1 + for (let excelRow = merge.startRow + 1; excelRow <= merge.endRow; excelRow += 1) { + const dataIndex = excelRow - 2 + if (dataIndex >= 0 && dataIndex < built.length) built[dataIndex][colIndex] = '' + } + } + return merges +} + /** 导出前加载作废类型字典,避免列表外的编码原样写入 Excel。 */ export async function prepareCollectionLedgerExport(): Promise { const { loadVoidTypeDict, getVoidTypeOptions } = await import('./void-type-dict') @@ -526,7 +551,7 @@ function storageLedgerRow(row: ProtocolAuditFlatRow, index: number, research: bo cells[6] = statusText(status, protocol.voidType) cells[7] = voidReason(protocol) cells[8] = text(row.motherName) - cells[9] = text(row.protocolNo) + cells[9] = text(row.sampleCode) cells[10] = text(row.sampleTypeName) cells[11] = row.isCoStored ? '是' : '否' cells[12] = text(row.regionName) @@ -557,12 +582,19 @@ function storageLedgerRow(row: ProtocolAuditFlatRow, index: number, research: bo } cells[50] = text(row.salesUserName) cells[51] = text(row.regionManagerUserName) - cells[52] = transportText(row) + cells[52] = researchTransportText(row) + cells[53] = researchFreightText(row) return cells } -export function buildCellStorageLedgerRows(rows: ProtocolAuditFlatRow[]): string[][] { - return rows.map((row, index) => storageLedgerRow(row, index, false)) +export function buildCellStorageLedgerRows(rows: ProtocolAuditFlatRow[]): { + rows: string[][] + merges: LedgerMergeRange[] +} { + const freightIds = rows.map((row) => text(row.freightId)) + const built = rows.map((row, index) => storageLedgerRow(row, index, false)) + // 自存表:运输方式=53,运费=54 + return { rows: built, merges: applyFreightMerges(built, freightIds, 53, 54) } } export function buildResearchLedgerRows(rows: ProtocolAuditFlatRow[]): { @@ -571,17 +603,8 @@ export function buildResearchLedgerRows(rows: ProtocolAuditFlatRow[]): { } { const freightIds = rows.map((row) => text(row.freightId)) const built = rows.map((row, index) => storageLedgerRow(row, index, true)) - const merges = buildFreightMergeRanges(freightIds) - // 合并区域内仅保留首行运输/运费,其余清空,避免合并后重复显示 - for (const merge of merges) { - if (merge.startCol !== 17 && merge.startCol !== 18) continue - const colIndex = merge.startCol - 1 - for (let excelRow = merge.startRow + 1; excelRow <= merge.endRow; excelRow += 1) { - const dataIndex = excelRow - 2 - if (dataIndex >= 0 && dataIndex < built.length) built[dataIndex][colIndex] = '' - } - } - return { rows: built, merges } + // 科研表:运输方式=17,运费=18 + return { rows: built, merges: applyFreightMerges(built, freightIds, 17, 18) } } export function buildCustomerServiceLedgerRows(rows: Row[]): string[][] { diff --git a/web-admin/src/views/cell-storage/protocol-audit/ProtocolAuditTab.vue b/web-admin/src/views/cell-storage/protocol-audit/ProtocolAuditTab.vue index 9e121d22..78a3a4cc 100644 --- a/web-admin/src/views/cell-storage/protocol-audit/ProtocolAuditTab.vue +++ b/web-admin/src/views/cell-storage/protocol-audit/ProtocolAuditTab.vue @@ -534,11 +534,13 @@ ElMessage.warning('没有可导出的数据') return } + const ledger = buildCellStorageLedgerRows(rows) await saveCollectionLedgerWorkbook( '自存表', CELL_STORAGE_HEADERS, - buildCellStorageLedgerRows(rows), - '自存表' + ledger.rows, + '自存表', + ledger.merges ) ElMessage.success('导出成功') } catch { -- 2.54.0