fix(web-admin): 恢复采集大表导出并按运费批次归拢合并

去掉临时隐藏开关,导出自存/科研表时先按运费批次聚拢再合并单元格。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-11-11 14:56:45 +08:00
1 parent 95d6b6025f
commit 3ac30a6c2c
5 files changed
+152 -26

No files matched your search

@@ -10,7 +10,8 @@ import {
buildFreightMergeRanges,
buildPresignLedgerRows,
buildResearchLedgerRows,
formatLedgerDateTime
formatLedgerDateTime,
groupFlatRowsByFreightBatch
} from './collection-ledger-export'
import type { ProtocolAuditFlatRow } from './protocol-audit-flatten'
@@ -194,6 +195,71 @@ describe('buildCellStorageLedgerRows', () => {
{ startRow: 2, endRow: 3, startCol: 54, endCol: 54 }
])
})
it('groups non-adjacent same freight batch before merging cells', () => {
const shared = {
protocol: { protocolStatus: 'sxz', remark: '', voidType: '', voidDesc: '' },
sampleCollectTime: '2025-09-11 18:13:00',
sampleTypeName: 'NK',
regionName: '常州',
hospitalName: '溧阳市人民医院',
freightTransportLabel: '专线车',
freightAmountLabel: '100'
}
const { rows, merges } = buildCellStorageLedgerRows([
{
...shared,
motherName: '甲',
sampleCode: 'S1',
labStorageCode: 'UBMC1',
freightId: 'batch-cell'
} as unknown as ProtocolAuditFlatRow,
{
...shared,
motherName: '丙',
sampleCode: 'S3',
labStorageCode: 'UBMC3',
freightId: 'other',
freightTransportLabel: '快递',
freightAmountLabel: '25'
} as unknown as ProtocolAuditFlatRow,
{
...shared,
motherName: '乙',
sampleCode: 'S2',
labStorageCode: 'UBMC2',
freightId: 'batch-cell'
} as unknown as ProtocolAuditFlatRow
])
assert.equal(rows[0][8], '甲')
assert.equal(rows[1][8], '乙')
assert.equal(rows[2][8], '丙')
assert.equal(rows[0][52], '专线车')
assert.equal(rows[0][53], '100')
assert.equal(rows[1][52], '')
assert.equal(rows[1][53], '')
assert.equal(rows[2][52], '快递')
assert.equal(rows[2][53], '25')
assert.deepEqual(merges, [
{ startRow: 2, endRow: 3, startCol: 53, endCol: 53 },
{ startRow: 2, endRow: 3, startCol: 54, endCol: 54 }
])
})
})
describe('groupFlatRowsByFreightBatch', () => {
it('keeps first-seen batch order and leaves empty freight rows in place', () => {
const arranged = groupFlatRowsByFreightBatch([
{ motherName: 'A', freightId: 'f1' },
{ motherName: 'B', freightId: '' },
{ motherName: 'C', freightId: 'f1' },
{ motherName: 'D', freightId: 'f2' }
] as unknown as ProtocolAuditFlatRow[])
assert.deepEqual(
arranged.map((row) => row.motherName),
['A', 'C', 'B', 'D']
)
})
})
describe('buildResearchLedgerRows', () => {
@@ -335,6 +401,50 @@ describe('buildResearchLedgerRows', () => {
{ startRow: 3, endRow: 4, startCol: 18, endCol: 18 }
])
})
it('groups non-adjacent same freight batch before merging research cells', () => {
const shared = {
protocol: { donationStatus: 'sxz', voidType: '', voidDesc: '', monthlyVerifyNo: '' },
sampleCollectTime: '2025-09-11 18:13:00',
sampleTypeName: 'NK',
regionName: '常州',
hospitalName: '溧阳市人民医院',
freightTransportLabel: '专线车',
freightAmountLabel: '100'
}
const { rows, merges } = buildResearchLedgerRows([
{
...shared,
motherName: '甲',
labStorageCode: 'A1',
freightId: 'batch-a'
} as unknown as ProtocolAuditFlatRow,
{
...shared,
motherName: '散',
labStorageCode: 'A9',
freightId: '',
freightTransportLabel: '',
freightAmountLabel: ''
} as unknown as ProtocolAuditFlatRow,
{
...shared,
motherName: '乙',
labStorageCode: 'A2',
freightId: 'batch-a'
} as unknown as ProtocolAuditFlatRow
])
assert.equal(rows[0][8], '甲')
assert.equal(rows[1][8], '乙')
assert.equal(rows[2][8], '散')
assert.equal(rows[0][16], '专线车')
assert.equal(rows[1][16], '')
assert.equal(rows[1][17], '')
assert.deepEqual(merges, [
{ startRow: 2, endRow: 3, startCol: 17, endCol: 17 },
{ startRow: 2, endRow: 3, startCol: 18, endCol: 18 }
])
})
})
describe('buildCustomerServiceLedgerRows', () => {
@@ -1,8 +1,5 @@
import type { ProtocolAuditFlatRow } from './protocol-audit-flatten'
/** 采集大表导出入口开关;验收未完成前隐藏按钮,改 true 即可恢复。 */
export const COLLECTION_LEDGER_EXPORT_ENABLED = false
type Row = Record<string, unknown>
type PayDetail = {
installmentNo?: number | null
@@ -341,6 +338,36 @@ function researchMaternalBloodText(value: unknown): string {
return text(value) ? text(value) : '无母血'
}
/**
* 将同一运费批次的行聚拢到一起(按首次出现顺序),无批次行保持相对位置。
* 模版要求同批次运输方式/运费合并单元格,故导出前先归拢再合并。
*/
export function groupFlatRowsByFreightBatch(
rows: ProtocolAuditFlatRow[]
): ProtocolAuditFlatRow[] {
const groups = new Map<string, ProtocolAuditFlatRow[]>()
const sequence: Array<{ kind: 'batch'; id: string } | { kind: 'single'; row: ProtocolAuditFlatRow }> =
[]
for (const row of rows) {
const freightId = text(row.freightId)
if (!freightId) {
sequence.push({ kind: 'single', row })
continue
}
if (!groups.has(freightId)) {
groups.set(freightId, [])
sequence.push({ kind: 'batch', id: freightId })
}
groups.get(freightId)!.push(row)
}
const arranged: ProtocolAuditFlatRow[] = []
for (const item of sequence) {
if (item.kind === 'single') arranged.push(item.row)
else arranged.push(...(groups.get(item.id) || []))
}
return arranged
}
/**
* 相邻且 freightId 相同的导出行,合并运输方式/运费列(Excel 行号含表头)。
* @param transportCol 运输方式列(1-based)
@@ -626,9 +653,10 @@ 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
const arranged = groupFlatRowsByFreightBatch(rows)
const freightIds = arranged.map((row) => text(row.freightId))
const built = arranged.map((row, index) => storageLedgerRow(row, index, false))
// 自存表:运输方式=53,运费=54(对齐模版《自存表》)
return { rows: built, merges: applyFreightMerges(built, freightIds, 53, 54) }
}
@@ -636,9 +664,10 @@ export function buildResearchLedgerRows(rows: ProtocolAuditFlatRow[]): {
rows: string[][]
merges: LedgerMergeRange[]
} {
const freightIds = rows.map((row) => text(row.freightId))
const built = rows.map((row, index) => storageLedgerRow(row, index, true))
// 科研表:运输方式=17,运费=18
const arranged = groupFlatRowsByFreightBatch(rows)
const freightIds = arranged.map((row) => text(row.freightId))
const built = arranged.map((row, index) => storageLedgerRow(row, index, true))
// 科研表:运输方式=17,运费=18(对齐模版《科研表》)
return { rows: built, merges: applyFreightMerges(built, freightIds, 17, 18) }
}
@@ -75,12 +75,7 @@
<el-icon><Plus /></el-icon>
新增
</el-button>
<el-button
v-if="COLLECTION_LEDGER_EXPORT_ENABLED"
class="btn-ghost"
:loading="exporting"
@click="handleExport"
>
<el-button class="btn-ghost" :loading="exporting" @click="handleExport">
<el-icon><Download /></el-icon>
导出 Excel
</el-button>
@@ -467,7 +462,6 @@
} from '@/utils/protocol-doc-label'
import { loadVoidTypeDict } from '@/utils/void-type-dict'
import {
COLLECTION_LEDGER_EXPORT_ENABLED,
RESEARCH_HEADERS,
buildResearchLedgerRows,
fetchAllPages,
@@ -79,12 +79,7 @@
<el-icon><Plus /></el-icon>
新增
</el-button>
<el-button
v-if="COLLECTION_LEDGER_EXPORT_ENABLED"
class="btn-ghost"
:loading="exporting"
@click="handleExport"
>
<el-button class="btn-ghost" :loading="exporting" @click="handleExport">
<el-icon><Download /></el-icon>
导出 Excel
</el-button>
@@ -435,7 +430,6 @@
} from '@/utils/status-tag-registry'
import { loadVoidTypeDict } from '@/utils/void-type-dict'
import {
COLLECTION_LEDGER_EXPORT_ENABLED,
CELL_STORAGE_HEADERS,
buildCellStorageLedgerRows,
fetchAllPages,
@@ -93,7 +93,7 @@
登记入库
</el-button>
<el-button
v-if="isCellStorage && COLLECTION_LEDGER_EXPORT_ENABLED"
v-if="isCellStorage"
class="btn-ghost"
:loading="exporting"
@click="handleExport"
@@ -522,7 +522,6 @@
sampleStorageFlagTagType
} from '@/utils/status-tag-registry'
import {
COLLECTION_LEDGER_EXPORT_ENABLED,
CUSTOMER_SERVICE_HEADERS,
buildCustomerServiceLedgerRows,
fetchAllPages,