提交
This commit is contained in:
1 parent
04d462ad43
commit
7e9155fef1
7 files changed
+1754
-68
No files matched your search
@@ -0,0 +1,137 @@
|
||||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import {
|
||||
buildTaskListDisplayItems,
|
||||
pickSharedRemarkAndAttachments,
|
||||
companyGroupProgress,
|
||||
uniqueAssigneeNames
|
||||
} from './qc-company-task-group'
|
||||
|
||||
type TestQcTask = {
|
||||
id: string
|
||||
taskType: string
|
||||
taskNo?: string
|
||||
itemCode?: string
|
||||
itemName?: string
|
||||
taskStatus?: string
|
||||
workOrderId?: string
|
||||
companyId?: string
|
||||
companyName?: string
|
||||
remark?: string
|
||||
thirdPartyAttachments?: string
|
||||
updateTime?: string
|
||||
assigneeUserNames?: string
|
||||
attachmentFiles?: Array<{ fileUrl: string; fileName?: string; fileType?: string }>
|
||||
}
|
||||
|
||||
function task(partial: Partial<TestQcTask> & Pick<TestQcTask, 'id' | 'taskType'>): TestQcTask {
|
||||
return {
|
||||
taskNo: 'T1',
|
||||
itemCode: 'I',
|
||||
itemName: 'Item',
|
||||
taskStatus: 'dzx',
|
||||
workOrderId: 'wo1',
|
||||
...partial
|
||||
}
|
||||
}
|
||||
|
||||
describe('buildTaskListDisplayItems', () => {
|
||||
it('keeps lab tasks as single cards and groups third-party by companyId', () => {
|
||||
const tasks = [
|
||||
task({ id: 'l1', taskType: 'sysz', itemName: '无菌' }),
|
||||
task({
|
||||
id: 't1',
|
||||
taskType: 'dsfjc',
|
||||
companyId: 'c1',
|
||||
companyName: '金域',
|
||||
itemName: '乙肝'
|
||||
}),
|
||||
task({
|
||||
id: 't2',
|
||||
taskType: 'dsfjc',
|
||||
companyId: 'c1',
|
||||
companyName: '金域',
|
||||
itemName: '丙肝'
|
||||
}),
|
||||
task({
|
||||
id: 't3',
|
||||
taskType: 'dsfjc',
|
||||
companyId: 'c2',
|
||||
companyName: '迪安',
|
||||
itemName: 'HIV'
|
||||
})
|
||||
]
|
||||
const items = buildTaskListDisplayItems(tasks)
|
||||
assert.equal(items.length, 3)
|
||||
assert.equal(items[0].kind, 'lab')
|
||||
assert.equal(items[0].kind === 'lab' && items[0].task.id, 'l1')
|
||||
assert.equal(items[1].kind, 'company')
|
||||
if (items[1].kind === 'company') {
|
||||
assert.equal(items[1].companyId, 'c1')
|
||||
assert.equal(items[1].tasks.length, 2)
|
||||
}
|
||||
assert.equal(items[2].kind, 'company')
|
||||
if (items[2].kind === 'company') {
|
||||
assert.equal(items[2].companyId, 'c2')
|
||||
}
|
||||
})
|
||||
|
||||
it('falls back to single lab-like card when third-party has empty companyId', () => {
|
||||
const tasks = [
|
||||
task({ id: 't0', taskType: 'dsfjc', companyId: '', companyName: '', itemName: '孤儿' })
|
||||
]
|
||||
const items = buildTaskListDisplayItems(tasks)
|
||||
assert.equal(items.length, 1)
|
||||
assert.equal(items[0].kind, 'orphan-third')
|
||||
})
|
||||
})
|
||||
|
||||
describe('companyGroupProgress', () => {
|
||||
it('counts ywc vs total', () => {
|
||||
const p = companyGroupProgress([
|
||||
task({ id: 'a', taskType: 'dsfjc', taskStatus: 'ywc' }),
|
||||
task({ id: 'b', taskType: 'dsfjc', taskStatus: 'dzx' })
|
||||
])
|
||||
assert.deepEqual(p, { completed: 1, total: 2 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('pickSharedRemarkAndAttachments', () => {
|
||||
it('picks latest non-empty remark and attachments by updateTime', () => {
|
||||
const picked = pickSharedRemarkAndAttachments([
|
||||
task({
|
||||
id: 'a',
|
||||
taskType: 'dsfjc',
|
||||
remark: '旧备注',
|
||||
thirdPartyAttachments: 'http://a.pdf',
|
||||
updateTime: '2026-08-01 10:00:00',
|
||||
attachmentFiles: [{ fileUrl: 'http://a.pdf', fileName: 'a.pdf' }]
|
||||
}),
|
||||
task({
|
||||
id: 'b',
|
||||
taskType: 'dsfjc',
|
||||
remark: '新备注',
|
||||
thirdPartyAttachments: '',
|
||||
updateTime: '2026-08-28 12:00:00',
|
||||
attachmentFiles: []
|
||||
})
|
||||
])
|
||||
assert.equal(picked.remark, '新备注')
|
||||
assert.equal(picked.thirdPartyAttachments, 'http://a.pdf')
|
||||
assert.equal(picked.attachmentFiles?.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('uniqueAssigneeNames', () => {
|
||||
it('dedupes assignee names split by comma or顿号', () => {
|
||||
const names = uniqueAssigneeNames([
|
||||
task({ id: 'a', taskType: 'dsfjc', assigneeUserNames: '张三,李四' }),
|
||||
task({ id: 'b', taskType: 'dsfjc', assigneeUserNames: '李四、王五' })
|
||||
])
|
||||
assert.equal(names, '张三、李四、王五')
|
||||
})
|
||||
|
||||
it('returns em dash when no assignees', () => {
|
||||
assert.equal(uniqueAssigneeNames([task({ id: 'a', taskType: 'dsfjc' })]), '—')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,106 @@
|
||||
import type { QcTask } from '../api/qc-task'
|
||||
|
||||
export type QcTaskListLabItem = { kind: 'lab'; task: QcTask }
|
||||
export type QcTaskListOrphanThirdItem = { kind: 'orphan-third'; task: QcTask }
|
||||
export type QcTaskListCompanyItem = {
|
||||
kind: 'company'
|
||||
companyId: string
|
||||
companyName: string
|
||||
tasks: QcTask[]
|
||||
}
|
||||
export type QcTaskListDisplayItem =
|
||||
| QcTaskListLabItem
|
||||
| QcTaskListOrphanThirdItem
|
||||
| QcTaskListCompanyItem
|
||||
|
||||
function trimId(v?: string) {
|
||||
return (v || '').trim()
|
||||
}
|
||||
|
||||
/** 自检卡在前(原序);有 companyId 的第三方按首次出现公司顺序分组;无 companyId 降级为 orphan-third */
|
||||
export function buildTaskListDisplayItems(tasks: QcTask[]): QcTaskListDisplayItem[] {
|
||||
const lab: QcTaskListDisplayItem[] = []
|
||||
const orphans: QcTaskListDisplayItem[] = []
|
||||
const companyOrder: string[] = []
|
||||
const companyMap = new Map<string, QcTaskListCompanyItem>()
|
||||
|
||||
for (const t of tasks || []) {
|
||||
if ((t.taskType || '').trim() === 'sysz') {
|
||||
lab.push({ kind: 'lab', task: t })
|
||||
continue
|
||||
}
|
||||
if ((t.taskType || '').trim() !== 'dsfjc') {
|
||||
lab.push({ kind: 'lab', task: t })
|
||||
continue
|
||||
}
|
||||
const cid = trimId(t.companyId)
|
||||
if (!cid) {
|
||||
orphans.push({ kind: 'orphan-third', task: t })
|
||||
continue
|
||||
}
|
||||
let g = companyMap.get(cid)
|
||||
if (!g) {
|
||||
g = {
|
||||
kind: 'company',
|
||||
companyId: cid,
|
||||
companyName: (t.companyName || '').trim() || '第三方公司',
|
||||
tasks: []
|
||||
}
|
||||
companyMap.set(cid, g)
|
||||
companyOrder.push(cid)
|
||||
}
|
||||
g.tasks.push(t)
|
||||
}
|
||||
|
||||
return [...lab, ...companyOrder.map((id) => companyMap.get(id)!), ...orphans]
|
||||
}
|
||||
|
||||
export function companyGroupProgress(tasks: QcTask[]) {
|
||||
const list = tasks || []
|
||||
const completed = list.filter((t) => (t.taskStatus || '').trim() === 'ywc').length
|
||||
return { completed, total: list.length }
|
||||
}
|
||||
|
||||
function timeMs(v?: string) {
|
||||
if (!v) return 0
|
||||
const n = Date.parse(String(v).replace(/-/g, '/'))
|
||||
return Number.isFinite(n) ? n : 0
|
||||
}
|
||||
|
||||
export function pickSharedRemarkAndAttachments(tasks: QcTask[]): {
|
||||
remark: string
|
||||
thirdPartyAttachments: string
|
||||
attachmentFiles: NonNullable<QcTask['attachmentFiles']>
|
||||
} {
|
||||
const list = [...(tasks || [])].sort((a, b) => timeMs(b.updateTime) - timeMs(a.updateTime))
|
||||
let remark = ''
|
||||
let thirdPartyAttachments = ''
|
||||
let attachmentFiles: NonNullable<QcTask['attachmentFiles']> = []
|
||||
for (const t of list) {
|
||||
if (!remark && (t.remark || '').trim()) remark = (t.remark || '').trim()
|
||||
const urls = (t.thirdPartyAttachments || '').trim()
|
||||
const files = t.attachmentFiles || []
|
||||
if (!thirdPartyAttachments && (urls || files.length)) {
|
||||
thirdPartyAttachments = urls
|
||||
attachmentFiles = files.length ? [...files] : []
|
||||
}
|
||||
if (remark && (thirdPartyAttachments || attachmentFiles.length)) break
|
||||
}
|
||||
return { remark, thirdPartyAttachments, attachmentFiles }
|
||||
}
|
||||
|
||||
export function uniqueAssigneeNames(tasks: QcTask[]): string {
|
||||
const names: string[] = []
|
||||
const seen = new Set<string>()
|
||||
for (const t of tasks || []) {
|
||||
const raw = (t.assigneeUserNames || '').trim()
|
||||
if (!raw) continue
|
||||
for (const part of raw.split(/[,,、]/)) {
|
||||
const n = part.trim()
|
||||
if (!n || seen.has(n)) continue
|
||||
seen.add(n)
|
||||
names.push(n)
|
||||
}
|
||||
}
|
||||
return names.join('、') || '—'
|
||||
}
|
||||
Reference in new issue
Block a user