163 lines
5.7 KiB
TypeScript
163 lines
5.7 KiB
TypeScript
import type { QcProtocolSiblingSample, QcWorkOrder } from '@/api/qc-work-order'
|
|
import {
|
|
compareLabStorageThenCreateTimeDesc,
|
|
resolveGroupLabStorageCode,
|
|
resolveGroupMaxCreateTime
|
|
} from '@/utils/qc-inbound-lab-storage-sort'
|
|
import {
|
|
groupInboundQcWorkOrders,
|
|
resolveInboundProtocolGroupKey,
|
|
resolveGateWorkOrderForSampleGroup,
|
|
type QcWorkOrderSampleGroupModel
|
|
} from '@/utils/qc-work-order-groups'
|
|
import { resolveInboundSampleGroupCardTitle } from '@/utils/qc-work-order-card-meta'
|
|
|
|
export type QcInboundProtocolSiblingLink = {
|
|
sampleCode: string
|
|
anchorId: string
|
|
labStorageCode: string
|
|
sampleTypeName: string
|
|
}
|
|
|
|
export type QcInboundSampleListItem = {
|
|
group: QcWorkOrderSampleGroupModel
|
|
cardAnchorId: string
|
|
protocolSiblingLinks: QcInboundProtocolSiblingLink[]
|
|
showProtocolPaymentAtGroup: boolean
|
|
}
|
|
|
|
export function buildInboundSampleCardAnchorId(groupKey: string) {
|
|
const safe = String(groupKey || 'sample')
|
|
.trim()
|
|
.replace(/[^a-zA-Z0-9_-]+/g, '_')
|
|
return `qc-inbound-sample-${safe}`
|
|
}
|
|
|
|
function sampleTypeNameForGroup(group: QcWorkOrderSampleGroupModel) {
|
|
const gate = resolveGateWorkOrderForSampleGroup(group.workOrders)
|
|
return (gate?.sampleTypeName || group.workOrders[0]?.sampleTypeName || '').trim()
|
|
}
|
|
|
|
function resolveProtocolSiblingSamplesForGroup(
|
|
group: QcWorkOrderSampleGroupModel
|
|
): QcProtocolSiblingSample[] {
|
|
for (const row of group.workOrders) {
|
|
const samples = row.protocolSiblingSamples
|
|
if (samples?.length) return samples
|
|
}
|
|
const gate = resolveGateWorkOrderForSampleGroup(group.workOrders)
|
|
return gate?.protocolSiblingSamples || []
|
|
}
|
|
|
|
export function buildProtocolSiblingLinksFromSamples(
|
|
group: QcWorkOrderSampleGroupModel,
|
|
samples: QcProtocolSiblingSample[]
|
|
): QcInboundProtocolSiblingLink[] {
|
|
const sorted = [...samples].sort((a, b) => (a.sampleCode || '').localeCompare(b.sampleCode || ''))
|
|
return sorted.map((sample) => {
|
|
const sampleCode = (sample.sampleCode || '').trim() || group.groupKey
|
|
const labStorageCode = (sample.labStorageCode || '').trim()
|
|
return {
|
|
sampleCode,
|
|
anchorId: buildInboundSampleCardAnchorId(sampleCode),
|
|
labStorageCode: labStorageCode || '—',
|
|
sampleTypeName: (sample.sampleTypeName || '').trim()
|
|
}
|
|
})
|
|
}
|
|
|
|
function buildLinksFromVisibleGroups(
|
|
protocolSampleGroups: QcWorkOrderSampleGroupModel[]
|
|
): QcInboundProtocolSiblingLink[] {
|
|
const sorted = [...protocolSampleGroups].sort((a, b) =>
|
|
(a.sampleCode || '').localeCompare(b.sampleCode || '')
|
|
)
|
|
return sorted.map((group) => ({
|
|
sampleCode: (group.sampleCode || group.groupKey || '').trim(),
|
|
anchorId: buildInboundSampleCardAnchorId(group.groupKey),
|
|
labStorageCode: resolveInboundSampleGroupCardTitle(group.workOrders),
|
|
sampleTypeName: sampleTypeNameForGroup(group)
|
|
}))
|
|
}
|
|
|
|
/** 样本组标题栏「同协议样本」胶囊(优先 API enrich,回退当前页多样本互链) */
|
|
export function resolveProtocolSiblingLinksForGroup(
|
|
group: QcWorkOrderSampleGroupModel
|
|
): QcInboundProtocolSiblingLink[] {
|
|
const apiSamples = resolveProtocolSiblingSamplesForGroup(group)
|
|
if (apiSamples.length > 1) {
|
|
return buildProtocolSiblingLinksFromSamples(group, apiSamples)
|
|
}
|
|
return []
|
|
}
|
|
|
|
/** 入库 Tab:按样本独立卡片,并生成同协议互链与协议费用展示位 */
|
|
export function buildInboundSampleListItems(rows: QcWorkOrder[]): QcInboundSampleListItem[] {
|
|
const groups = groupInboundQcWorkOrders(rows)
|
|
const protocolGroups = new Map<string, QcWorkOrderSampleGroupModel[]>()
|
|
|
|
for (const group of groups) {
|
|
const head = group.workOrders[0]
|
|
if (!head) continue
|
|
const protocolKey = resolveInboundProtocolGroupKey(head)
|
|
const list = protocolGroups.get(protocolKey)
|
|
if (list) list.push(group)
|
|
else protocolGroups.set(protocolKey, [group])
|
|
}
|
|
|
|
const siblingLinksByGroupKey = new Map<string, QcInboundProtocolSiblingLink[]>()
|
|
const protocolPaymentHostGroupKey = new Set<string>()
|
|
|
|
for (const [, protocolSampleGroups] of protocolGroups) {
|
|
const sorted = [...protocolSampleGroups].sort((a, b) =>
|
|
(a.sampleCode || '').localeCompare(b.sampleCode || '')
|
|
)
|
|
let links: QcInboundProtocolSiblingLink[] = []
|
|
for (const group of sorted) {
|
|
const apiSamples = resolveProtocolSiblingSamplesForGroup(group)
|
|
if (apiSamples.length > 1) {
|
|
links = buildProtocolSiblingLinksFromSamples(group, apiSamples)
|
|
break
|
|
}
|
|
}
|
|
if (links.length <= 1 && sorted.length > 1) {
|
|
links = buildLinksFromVisibleGroups(sorted)
|
|
}
|
|
if (links.length > 1) {
|
|
for (const group of sorted) {
|
|
siblingLinksByGroupKey.set(group.groupKey, links)
|
|
}
|
|
}
|
|
const paymentHost = sorted.find((group) =>
|
|
group.workOrders.some((wo) => wo.receptionSource === 'cell_storage' && !!wo.paymentSummary)
|
|
)
|
|
if (paymentHost) {
|
|
protocolPaymentHostGroupKey.add(paymentHost.groupKey)
|
|
}
|
|
}
|
|
|
|
const sortedGroups = [...groups].sort((a, b) =>
|
|
compareLabStorageThenCreateTimeDesc(
|
|
resolveGroupLabStorageCode(a.workOrders),
|
|
resolveGroupMaxCreateTime(a.workOrders),
|
|
resolveGroupLabStorageCode(b.workOrders),
|
|
resolveGroupMaxCreateTime(b.workOrders)
|
|
)
|
|
)
|
|
|
|
return sortedGroups.map((group) => ({
|
|
group,
|
|
cardAnchorId: buildInboundSampleCardAnchorId(group.groupKey),
|
|
protocolSiblingLinks: siblingLinksByGroupKey.get(group.groupKey) || [],
|
|
showProtocolPaymentAtGroup: protocolPaymentHostGroupKey.has(group.groupKey)
|
|
}))
|
|
}
|
|
|
|
export function scrollToInboundSampleCard(anchorId: string) {
|
|
const el = document.getElementById(anchorId)
|
|
if (!el) return
|
|
el.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
el.classList.add('qc-sample-row--highlight')
|
|
window.setTimeout(() => el.classList.remove('qc-sample-row--highlight'), 1600)
|
|
}
|