Forráskód Böngészése

feat(mobile-oa-h5): align research donation protocol card with excel fields

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 2 hete
szülő
commit
99e71096ac

+ 43 - 42
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'] }
 }

+ 51 - 24
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('储存证 · 未知'))
 })