fix(mobile-oa-h5): finish storage-service tag tone parity for market details

Wire detail header statusTone for protocol/payment/freight/chase from the
registry mirror, stop hardcoding primary on nested tags, and map 已过期 to
warning so title and field colors stay consistent.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-17 11:27:29 +08:00
1 parent d355cd0640
commit 48dcf991dc
11 files changed
+270 -260

No files matched your search

@@ -124,7 +124,7 @@
<van-tag
v-else-if="field.tag"
class="market-tag"
:type="field.tagType || 'primary'"
:type="field.tagType || resolveStatusTone(field.value)"
round
:title="field.value"
>
@@ -145,6 +145,7 @@
<script setup>
import { computed } from 'vue'
import { resolveStatusTone } from '../status-tone.js'
import MarketAttachmentCards from './MarketAttachmentCards.vue'
defineOptions({ name: 'MarketDetailNestedCard' })
@@ -10,7 +10,7 @@
:model-value="field.value"
:editable="false"
/>
<van-tag v-else-if="field.tag" class="market-tag" :type="field.tagType || 'primary'" round :title="field.value">
<van-tag v-else-if="field.tag" class="market-tag" :type="field.tagType || resolveStatusTone(field.value)" round :title="field.value">
{{ field.value }}
</van-tag>
<span v-else>{{ field.value }}</span>
@@ -287,6 +287,7 @@ import {
PROTOCOL_ATTACHMENT_MAX_BYTES
} from '../attachment-upload-policy.js'
import MarketAttachmentCards from './MarketAttachmentCards.vue'
import { resolveStatusTag, payStatus } from '../status-tag-registry.js'
const props = defineProps({
payment: { type: Object, default: () => ({}) },
@@ -360,21 +361,11 @@ function payModeLabel(value) {
}
function statusLabel(value) {
return {
dfj: '已付款',
jfqrz: '付款确认中',
yjf: '付款完成',
tfqrz: '退费确认中',
ytf: '已退费'
}[String(value ?? '')] || '状态异常'
return resolveStatusTag(payStatus, value).label
}
function statusTone(value) {
const status = String(value ?? '')
if (status === 'yjf') return 'success'
if (status === 'jfqrz') return 'warning'
if (status === 'dfj') return 'primary'
return 'danger'
return resolveStatusTag(payStatus, value).tagType
}
function transferTypeLabel(value) {
@@ -576,6 +567,8 @@ function removeAttachment(detailIndex, attachmentIndex) {
.protocol-v1-pay__details-head .van-button,
.protocol-v1-pay__delete {
min-height: 44px;
border-radius: 12px !important;
font-weight: 600 !important;
}
.protocol-v1-pay__detail.is-locked {
@@ -539,7 +539,7 @@ test('nested market detail cards use explicit labels instead of API property nam
const detailSection = source('./components/MarketDetailSection.vue')
assert.match(detailSection, /cards:/)
assert.match(detailSection, /field\.tagType \|\| 'primary'/)
assert.match(detailSection, /field\.tagType \|\| resolveStatusTone\(field\.value\)/)
assert.doesNotMatch(detailSection, /function flattenFields/)
})
@@ -93,6 +93,9 @@ test('protocol summary status resolves real codes without exposing unknown codes
assert.equal(protocol.statusLabel({ source: 'research_donation', basic: { donationStatus: 'yzf' } }), '已作废')
assert.equal(protocol.statusLabel({ source: 'cell_storage', basic: { protocolStatus: 'raw-status' } }), '未知')
assert.equal(protocol.statusLabel({ source: 'cell_storage', basic: { protocolStatus: '' } }), '—')
assert.equal(protocol.statusTone({ source: 'cell_storage', basic: { protocolStatus: 'ygq' } }), 'warning')
assert.equal(protocol.statusTone({ source: 'research_donation', basic: { donationStatus: 'ygq' } }), 'warning')
assert.equal(protocol.statusTone({ source: 'cell_storage', protocol: { protocolStatus: 'sxz' } }), 'primary')
})
test('protocol adapters preserve nested detail sections', () => {
@@ -233,6 +236,34 @@ test('payment detail presents real backend progress and combined pay mode in sum
assert.equal(summary.fields.find((field) => field.label === '缴费进度').value, '部分已缴')
})
test('payment and freight detail headers use registry tagType instead of Chinese heuristics', () => {
const payment = getMarketModule('payment')
assert.equal(payment.statusLabel({ progressStatus: 'dfj' }), '待缴费')
assert.equal(payment.statusTone({ progressStatus: 'dfj' }), 'info')
assert.equal(payment.statusTone({ progressStatus: 'yjq' }), 'success')
const freight = getMarketModule('freight')
assert.equal(
freight.statusTone({ items: [{ sampleSource: 'research_donation' }] }),
'warning'
)
assert.equal(
freight.statusTone({ items: [{ sampleSource: 'cell_storage' }] }),
'primary'
)
const chase = getMarketModule('payment-chase')
assert.equal(chase.statusTone({ progressStatus: 'dfj' }), 'info')
assert.equal(
chase.statusTone({
chaseView: 'protocol',
source: 'cell_storage',
basic: { protocolStatus: 'ygq' }
}),
'warning'
)
})
test('payment progress dfj aligns with web-admin samplePaymentProgress 待缴费', () => {
const payment = getMarketModule('payment')
const byCode = payment.detailSections({ progressStatus: 'dfj' })
+11 -1
View File
@@ -15,6 +15,11 @@ function person(name, id) {
return referenceId ? '未知人员' : '—'
}
function freightHeaderTag(detail) {
const first = (detail?.items || [])[0] || {}
return resolveStatusTag(receptionSource, first.sampleSource || detail?.sampleSource || detail?.source)
}
function money(value) {
const amount = Number(value)
if (!Number.isFinite(amount)) return '—'
@@ -80,7 +85,6 @@ export default {
statusTone: source.tagType,
fields: [
{ label: '来源', value: source.label, tag: true, tagType: source.tagType },
{ label: '协议单号', value: item?.protocolNo || '—' },
{ label: '样本编码', value: item?.sampleCode || '—' },
{ label: '样本类型', value: item?.sampleTypeName || '—' },
{ label: '母亲姓名', value: item?.motherName || '—' },
@@ -91,6 +95,12 @@ export default {
}
]
},
statusLabel(detail) {
return freightHeaderTag(detail).label
},
statusTone(detail) {
return freightHeaderTag(detail).tagType
},
usesPagedSampleOptions(action) {
return ['create', 'link'].includes(action)
},
@@ -32,7 +32,8 @@ export default {
options: [
{ text: '趸交', value: 'dj' },
{ text: '分期', value: 'fq' },
{ text: '分改趸', value: 'fgd' }
{ text: '分改趸', value: 'fgd' },
{ text: '趸交+分期', value: 'djfq' }
]
}
],
@@ -46,5 +47,15 @@ export default {
return detail?.chaseView === 'protocol'
? protocolModule.detailSections(detail)
: paymentModule.detailSections(detail)
},
statusLabel(detail) {
return detail?.chaseView === 'protocol'
? protocolModule.statusLabel(detail)
: paymentModule.statusLabel(detail)
},
statusTone(detail) {
return detail?.chaseView === 'protocol'
? protocolModule.statusTone(detail)
: paymentModule.statusTone(detail)
}
}
@@ -82,6 +82,12 @@ export default {
}
]
},
statusLabel(detail) {
return progressTag(detail?.progressStatus || detail?.paymentProgress).label
},
statusTone(detail) {
return progressTag(detail?.progressStatus || detail?.paymentProgress).tagType
},
initialForm(detail, target = {}) {
const transfer = (detail?.schedules || [])
.flatMap((row) => row.transfers || [])
+195 -242
View File
@@ -22,6 +22,13 @@ const SAMPLE_STATUS_LABELS = Object.freeze({
const VOID_TYPE_LABELS = Object.freeze({
khhc: '客户毁存', jcbhg: '检测不合格'
})
const STATUS_CHANGE_TYPE_LABELS = Object.freeze({
activate: '生效',
expire: '过期',
void: '作废',
to_research: '转科研',
to_storage: '转细胞存储'
})
const TRANSFER_TYPE_LABELS = Object.freeze({
wxzz: '微信转账', wx: '微信转账', wechat: '微信转账',
zzbzz: '支付宝转账', zfb: '支付宝转账', alipay: '支付宝转账',
@@ -69,11 +76,106 @@ function auditTag(code) {
return resolveStatusTag(auditStatus, code)
}
function voidTypeText(source) {
const backendLabel = String(source?.voidTypeLabel || '').trim()
if (backendLabel) return backendLabel
const code = String(source?.voidType || '').trim()
return VOID_TYPE_LABELS[code] || code || '—'
function statusChangeTypeTone(changeType) {
if (changeType === 'void') return 'danger'
if (changeType === 'expire') return 'warning'
if (changeType === 'activate') return 'success'
return 'primary'
}
function statusChangeFlowText(row, statusRegistry) {
const fromLabel = row?.fromStatus
? resolveStatusTag(statusRegistry, row.fromStatus).label
: ''
const toLabel = resolveStatusTag(statusRegistry, row?.toStatus).label
if (fromLabel) return `${fromLabel} → ${toLabel}`
return toLabel || '—'
}
function statusChangeDescText(row) {
const changeType = String(row?.changeType || '')
const desc = String(row?.changeDesc || '').trim()
if (changeType === 'void') {
const voidLabel =
VOID_TYPE_LABELS[String(row?.voidType || '')] ||
String(row?.voidTypeLabel || row?.voidType || '').trim()
if (voidLabel && desc) return `作废类型:${voidLabel};${desc}`
if (voidLabel) return `作废类型:${voidLabel}`
}
return desc || '—'
}
function buildStatusChangeCards(rows, statusRegistry) {
return (rows || []).map((row, index) => {
const changeType = String(row?.changeType || '')
return {
key: String(row?.id || `status-${index}`),
title: `变更 ${index + 1}`,
statusText: STATUS_CHANGE_TYPE_LABELS[changeType] || changeType || '变更',
statusTone: statusChangeTypeTone(changeType),
fields: [
{ label: '变更时间', value: value(row, 'changeTime') },
{ label: '状态流转', value: statusChangeFlowText(row, statusRegistry) },
{ label: '变更说明', value: statusChangeDescText(row) },
{ label: '操作人', value: person(row?.operatorUserName, row?.operatorUserId) }
]
}
})
}
/** 无 statusChanges 时,从主表快照字段合成卡片(对齐方案 A 单行展示) */
function synthesizeStatusChangesFromBasic(basic, source) {
const rows = []
if (basic?.effectiveTime) {
rows.push({
id: 'snap-activate',
changeType: 'activate',
toStatus: 'sxz',
changeTime: basic.effectiveTime,
changeDesc: basic.effectiveDesc || ''
})
}
if (basic?.expireTime) {
rows.push({
id: 'snap-expire',
changeType: 'expire',
toStatus: 'ygq',
changeTime: basic.expireTime,
changeDesc: basic.expireDesc || ''
})
}
if (basic?.voidTime) {
rows.push({
id: 'snap-void',
changeType: 'void',
toStatus: 'yzf',
changeTime: basic.voidTime,
changeDesc: basic.voidDesc || '',
voidType: basic.voidType || '',
voidTypeLabel: basic.voidTypeLabel || ''
})
}
if (source === 'research_donation') {
const convertTime = basic?.convertTime || basic?.toStorageTime
if (convertTime) {
rows.push({
id: 'snap-to-storage',
changeType: 'to_storage',
toStatus: 'zckc',
changeTime: convertTime,
changeDesc: basic?.convertDesc || basic?.toStorageDesc || ''
})
}
} else if (basic?.toResearchTime) {
rows.push({
id: 'snap-to-research',
changeType: 'to_research',
toStatus: 'zky',
changeTime: basic.toResearchTime,
changeDesc: basic.toResearchDesc || ''
})
}
return rows
}
function person(name, id) {
@@ -110,6 +212,26 @@ function transferAttachmentValue(transfer) {
return transfer?.attachments || transfer?.transferAttachmentFiles || transfer?.transferAttachments || ''
}
function refundRecordAttachmentValue(row) {
if (Array.isArray(row?.refundAttachmentFiles) && row.refundAttachmentFiles.length) {
return row.refundAttachmentFiles
}
return row?.refundAttachments || ''
}
function refundRecordCards(records) {
return (records || []).map((row, index) => ({
key: String(row?.id || `refund-${index}`),
title: `退费登记 ${index + 1}`,
fields: [
{ label: '退费金额', value: value(row, 'refundAmount') },
{ label: '退费时间', value: value(row, 'refundTime') },
{ label: '备注', value: value(row, 'refundRemark') },
{ label: '附件', type: 'attachments', value: refundRecordAttachmentValue(row) }
]
}))
}
function transferTypeText(transfer) {
const backendLabel = String(transfer?.transferTypeLabel || '').trim()
if (backendLabel) return backendLabel
@@ -118,115 +240,6 @@ function transferTypeText(transfer) {
return TRANSFER_TYPE_LABELS[code] || (/[㐀-鿿]/u.test(code) ? code : code)
}
function namedItems(items) {
return (items || [])
.map((item) => typeof item === 'string'
? item
: item?.itemName || item?.name || item?.itemCode || item?.id)
.filter(Boolean)
.join('、') || '—'
}
function qcResultCard(result, index, prefix) {
const unit = String(result?.paramUnit || '').trim()
return {
key: String(result?.id || `${prefix}-result-${index}`),
title: `检测结果 ${index + 1}`,
fields: [
{ label: '参数名称', value: value(result, 'paramName') },
{ label: '参数单位', value: unit || '—' },
{ label: '参数值', value: value(result, 'paramValue') }
]
}
}
function qcTaskCard(task, index, prefix) {
return {
key: String(task?.id || `${prefix}-task-${index}`),
title: task?.itemName || task?.taskName || task?.taskNo || `任务 ${index + 1}`,
statusText: value(task, 'taskStatusLabel'),
fields: [
{ label: '任务号', value: value(task, 'taskNo') },
{ label: '任务类型', value: value(task, 'taskTypeLabel') },
{ label: '检测项目', value: value(task, 'itemName') },
{ label: '检测机构', value: value(task, 'companyName') },
{ label: '任务状态', value: value(task, 'taskStatusLabel'), tag: true },
{ label: '结论', value: value(task, 'conclusionLabel'), tag: true },
{ label: '完成时间', value: value(task, 'completedTime') },
{ label: '备注', value: value(task, 'remark') },
{ label: '任务附件', value: task?.attachmentFiles || task?.thirdPartyAttachments || '', type: 'attachments' }
],
cards: (task?.results || []).map((result, resultIndex) =>
qcResultCard(result, resultIndex, `${prefix}-task-${index}`)
)
}
}
function qcBatchCard(batch, index, prefix) {
return {
key: String(batch?.id || `${prefix}-batch-${index}`),
title: `检测批次 ${index + 1}`,
fields: [
{ label: '批次编号', value: value(batch, 'batchNo') },
{ label: '批次名称', value: value(batch, 'batchName') },
{ label: '负责人', value: value(batch, 'assigneeUserNames') },
{ label: '实验室检测', value: batch?.hasLab ? '是' : '否' },
{ label: '第三方检测', value: batch?.hasThirdParty ? '是' : '否' },
{ label: '实验室项目', value: namedItems(batch?.labItems || batch?.labItemIds) },
{ label: '第三方项目', value: namedItems(batch?.thirdPartyItems || batch?.thirdPartyItemIds) },
{ label: '任务总数', value: value(batch, 'taskCount') },
{ label: '已完成任务数', value: value(batch, 'completedTaskCount') }
],
cards: (batch?.tasks || []).map((task, taskIndex) =>
qcTaskCard(task, taskIndex, `${prefix}-batch-${index}`)
)
}
}
function qcCard(info, title, index = 0) {
return {
key: String(info?.id || `${title}-${index}`),
title,
statusText: value(info, 'workOrderStatusLabel'),
fields: [
{ label: '工单号', value: value(info, 'workOrderNo') },
{ label: '业务类型', value: value(info, 'qcBizTypeLabel') },
{ label: '工单状态', value: value(info, 'workOrderStatusLabel'), tag: true },
{ label: '触发来源', value: value(info, 'triggerSourceLabel') },
{ label: '质检对象编码', value: value(info, 'cellProductCode') },
{ label: '质检对象名称', value: value(info, 'cellProductName') },
{ label: '生产工单号', value: value(info, 'productionWorkOrderNo') },
{ label: '包含实验室检测', value: info?.hasLab ? '是' : '否' },
{ label: '包含第三方检测', value: info?.hasThirdParty ? '是' : '否' },
{ label: '实验室检测项目', value: namedItems(info?.labItems) },
{ label: '第三方检测项目', value: namedItems(info?.thirdPartyItems) },
{ label: '配置时间', value: value(info, 'configuredTime') },
{ label: '检测批次', value: (info?.inspectionBatches || []).map((row) => row?.batchNo || row?.batchName).filter(Boolean).join('、') || '—' }
],
cards: [
...(info?.inspectionBatches || []).map((batch, batchIndex) =>
qcBatchCard(batch, batchIndex, `${title}-${index}`)
),
...(info?.tasks || []).map((task, taskIndex) =>
qcTaskCard(task, taskIndex, `${title}-${index}`)
)
]
}
}
function productionMaterialCard(item, index, kind, prefix) {
const input = kind === 'input'
return {
key: String(item?.id || `${prefix}-${kind}-${index}`),
title: `${input ? '投入物' : '产出物'} ${index + 1}`,
fields: [
{ label: input ? '投入物类型' : '产出物类型', value: value(item, input ? 'inputType' : 'outputType') },
{ label: input ? '投入物编码' : '产出物编码', value: value(item, input ? 'inputCode' : 'outputCode') },
{ label: input ? '投入物名称' : '产出物名称', value: value(item, input ? 'inputName' : 'outputName') }
]
}
}
function sampleLifecycleCards(item, source) {
const cards = []
const reception = item?.sampleReception
@@ -270,41 +283,6 @@ function sampleLifecycleCards(item, source) {
]
})
}
if (item?.qcInfo) cards.push(qcCard(item.qcInfo, '门控质检'))
;(item?.processQcInfoList || []).forEach((info, index) => cards.push(qcCard(info, `过程质检 ${index + 1}`, index)))
const production = item?.productionInfo
if (production) {
cards.push({
key: `${item?.id || item?.sampleCode}-production`,
title: '生产信息',
statusText: value(production, 'workOrderStatusLabel'),
fields: [
{ label: '工单号', value: value(production, 'workOrderNo') },
{ label: '工单状态', value: value(production, 'workOrderStatusLabel'), tag: true }
],
cards: (production?.tasks || []).map((task, index) => ({
key: String(task?.id || `production-task-${index}`),
title: `生产任务 ${index + 1}`,
statusText: value(task, 'taskStatusLabel'),
fields: [
{ label: '任务名称', value: value(task, 'taskName') },
{ label: '任务号', value: value(task, 'taskNo') },
{ label: '任务状态', value: value(task, 'taskStatusLabel'), tag: true },
{ label: '开始时间', value: value(task, 'startedTime') },
{ label: '完成时间', value: value(task, 'completedTime') },
{ label: '生产结论', value: value(task, 'conclusionLabel'), tag: true }
],
cards: [
...(task?.inputs || []).map((input, inputIndex) =>
productionMaterialCard(input, inputIndex, 'input', `production-task-${index}`)
),
...(task?.outputs || []).map((output, outputIndex) =>
productionMaterialCard(output, outputIndex, 'output', `production-task-${index}`)
)
]
}))
})
}
const release = item?.storageReleaseInfo
if (release) {
cards.push({
@@ -434,6 +412,13 @@ export default {
: basic?.protocolStatus
return bizStatusTag(status, detail?.source).label
},
statusTone(detail) {
const basic = detail?.protocol || detail?.basic || detail || {}
const status = detail?.source === 'research_donation'
? basic?.donationStatus
: basic?.protocolStatus
return bizStatusTag(status, detail?.source).tagType
},
detailSections(detail) {
if (Array.isArray(detail?.sections)) {
return detail.sections.map((item) => ({ ...item, fields: item?.fields || [], cards: item?.cards || [] }))
@@ -442,26 +427,29 @@ export default {
const basic = detail?.protocol || detail?.basic || detail || {}
const statusKey = source === 'research_donation' ? 'donationStatus' : 'protocolStatus'
const dateKey = source === 'research_donation' ? 'donationDate' : 'registerDate'
const numberKey = source === 'research_donation' ? 'donationNo' : 'protocolNo'
const bizStatus = bizStatusTag(basic?.[statusKey], source)
const isDonation = source === 'research_donation'
const values = [
{
key: 'protocol',
title: source === 'research_donation' ? '捐赠信息' : '协议信息',
fields: [
{ label: source === 'research_donation' ? '捐赠编号' : '协议编号', value: value(basic, numberKey) },
{
label: source === 'research_donation' ? '捐赠状态' : '协议状态',
value: bizStatus.label,
tag: true,
tagType: bizStatus.tagType
},
{ label: source === 'research_donation' ? '捐赠日期' : '登记日期', value: value(basic, dateKey) },
...(source === 'cell_storage' ? [{ label: '协议总金额', value: value(basic, 'protocolTotalAmount') }] : []),
{ label: '备注', value: value(basic, 'remark') },
{ label: '创建时间', value: value(basic, 'createTime') },
{ label: '更新时间', value: value(basic, 'updateTime') }
],
title: isDonation ? '基本信息' : '协议信息',
fields: isDonation
? [
{ label: '状态', value: bizStatus.label, tag: true, tagType: bizStatus.tagType },
{ label: '母亲姓名', value: value(detail?.customer, 'motherName') },
{ label: '捐赠日期', value: value(basic, dateKey) },
{ label: '创建时间', value: value(basic, 'createTime') },
{ label: '备注', value: value(basic, 'remark') }
]
: [
{ label: '协议状态', value: bizStatus.label, tag: true, tagType: bizStatus.tagType },
{ label: '母亲姓名', value: value(detail?.customer, 'motherName') },
{ label: '登记日期', value: value(basic, dateKey) },
{ label: '协议总金额', value: value(basic, 'protocolTotalAmount') },
{ label: '备注', value: value(basic, 'remark') },
{ label: '创建时间', value: value(basic, 'createTime') },
{ label: '更新时间', value: value(basic, 'updateTime') }
],
cards: []
},
{
@@ -492,13 +480,13 @@ export default {
['newbornIdCard', '新生儿身份证'],
['deliveryMode', '分娩方式', { map: (item) => label(DELIVERY_LABELS, item) }],
['birthOrder', '胎次'], ['newbornWeightGram', '新生儿体重(克)'],
['apgar1min', '新生儿评分(1分钟)'], ['apgar5min', '新生儿评分(5分钟)'], ['remark', '备注']
['apgar1min', '新生儿评分(1分钟)'], ['remark', '备注']
]),
cards: []
},
{
key: 'samples',
title: '样本信息',
title: isDonation ? '样本及运输信息' : '样本信息',
fields: [],
cards: (detail?.samples || []).map((item, index) => ({
key: String(item?.id || `sample-${index}`),
@@ -508,7 +496,9 @@ export default {
['sampleTypeName', '样本类型', { map: () => referenceName(item?.sampleTypeName, item?.sampleTypeId) }],
['sampleCode', '样本编码'],
['sampleStatus', '样本状态', { map: () => label(SAMPLE_STATUS_LABELS, item?.sampleStatusLabel || item?.sampleStatus) }],
['price', '优惠价'], ['originalPrice', '原价'], ['storageYears', '存储年限'],
...(source === 'cell_storage'
? [['price', '优惠价'], ['originalPrice', '原价'], ['storageYears', '存储年限']]
: []),
[
'transportMode',
'运输方式',
@@ -573,7 +563,7 @@ export default {
})
})
}
if (detail?.source === 'cell_storage' || detail?.payment || detail?.samplePayments) {
if (detail?.source === 'cell_storage') {
const samplePays = Array.isArray(detail?.samplePayments) ? detail.samplePayments : []
if (samplePays.length) {
values.push({
@@ -623,50 +613,12 @@ export default {
cards: legacyPaymentDetailCards(detail?.payment || {}, legacyDetails)
})
}
}
if (detail?.source === 'cell_storage') {
const cert = detail?.storageCert
const refundRecords = Array.isArray(detail?.refundRecords) ? detail.refundRecords : []
values.push({
key: 'storageCert',
title: '储存证',
fields: cert
? [
{ label: '办理状态', value: Number(cert.makeStatus) === 1 || cert.handled ? '已办理' : '未办理', tag: true },
{ label: '办理日期', value: value(cert, 'makeDate') },
{ label: '办理人', value: person(cert.handlerUserName, cert.handlerUserId) },
{ label: '办理时间', value: value(cert, 'handleTime') },
{ label: '备注', value: value(cert, 'remark') },
{
label: '储存证图片',
value: cert.certImageUrls || '',
type: 'attachments'
}
]
: [{ label: '办理状态', value: '暂无储存证记录' }],
cards: []
})
const insurance = detail?.fullmoonTodo || {}
values.push({
key: 'insurance',
title: '保险 / 满月待办',
fields: [
{ label: '办理状态', value: insurance.handled ? '已办理' : '未办理', tag: true },
{ label: '满月日期', value: value(insurance, 'fullmoonDate') },
{ label: '保险公司', value: value(insurance, 'insuranceCompanyName') },
{ label: '保险类型', value: value(insurance, 'insuranceType') },
{ label: '保单号', value: value(insurance, 'insurancePolicyNo') },
{ label: '生效日', value: value(insurance, 'insuranceEffectiveDate') },
{ label: '到期日', value: value(insurance, 'insuranceExpiryDate') },
{ label: '办理人', value: person(insurance.handlerUserName, insurance.handlerUserId) },
{ label: '办理时间', value: value(insurance, 'handleTime') },
{ label: '办理备注', value: value(insurance, 'handleRemark') },
{
label: '保险附件',
value: insurance.insuranceAttachments || '',
type: 'attachments'
}
],
cards: []
key: 'refundRecords',
title: '退费登记',
fields: refundRecords.length ? [] : [{ label: '记录', value: '暂无退费登记' }],
cards: refundRecordCards(refundRecords)
})
}
const audits = Array.isArray(detail?.audits) ? detail.audits : []
@@ -682,7 +634,6 @@ export default {
statusText: audit.label,
statusTone: audit.tagType,
fields: [
{ label: '审核状态', value: audit.label, tag: true, tagType: audit.tagType },
{ label: '审核人', value: person(row?.auditorUserName, row?.auditorUserId) },
{ label: '审核意见', value: value(row, 'auditRemark') },
{ label: '时间', value: value(row, 'createTime') }
@@ -690,40 +641,42 @@ export default {
}
})
})
const commonStatusFields = [
['effectiveTime', '协议生效时间'],
['effectiveDesc', '协议生效描述'],
['expireTime', '协议过期时间'],
['expireDesc', '协议过期描述'],
['voidTime', '协议作废时间'],
['voidDesc', '协议作废描述'],
['voidType', '作废类型']
]
const sourceStatusFields = source === 'research_donation'
? [['convertTime', '转细胞存储时间'], ['convertDesc', '转细胞存储描述']]
: [['toResearchTime', '转科研时间'], ['toResearchDesc', '转科研描述']]
const statusFields = [...commonStatusFields, ...sourceStatusFields].map(([key, textLabel]) => ({
label: textLabel,
value: key === 'voidType' ? voidTypeText(basic) : value(basic, key)
})).filter((item) => item.value !== '—')
values.push({
key: 'statusChange',
title: '状态变更记录',
fields: statusFields.length ? statusFields : [{ label: '记录', value: '暂无状态变更记录' }],
cards: []
})
values.push({
key: 'attachments',
title: '附件信息',
fields: [
{ label: '协议附件', type: 'attachments', value: detail?.attachments?.protocolAttachments || '' },
{ label: '知情同意书', type: 'attachments', value: detail?.attachments?.consentFormUrls || '' },
{ label: '孕妇健康调查表', type: 'attachments', value: detail?.attachments?.healthSurveyFormUrls || '' },
{ label: '传染病检测报告', type: 'attachments', value: detail?.attachments?.infectiousDiseaseReportUrls || '' },
{ label: '其他附件', type: 'attachments', value: detail?.attachments?.attachmentUrls || '' }
],
fields: isDonation
? [
{ label: '知情同意书', type: 'attachments', value: detail?.attachments?.consentFormUrls || '' },
{ label: '传染病检测报告', type: 'attachments', value: detail?.attachments?.infectiousDiseaseReportUrls || '' },
{ label: '其他附件', type: 'attachments', value: detail?.attachments?.attachmentUrls || '' }
]
: [
{ label: '协议附件', type: 'attachments', value: detail?.attachments?.protocolAttachments || '' },
{ label: '知情同意书', type: 'attachments', value: detail?.attachments?.consentFormUrls || '' },
{ label: '孕妇健康调查表', type: 'attachments', value: detail?.attachments?.healthSurveyFormUrls || '' },
{ label: '传染病检测报告', type: 'attachments', value: detail?.attachments?.infectiousDiseaseReportUrls || '' },
{ label: '其他附件', type: 'attachments', value: detail?.attachments?.attachmentUrls || '' }
],
cards: []
})
const statusRegistry = source === 'research_donation' ? donationStatus : protocolStatus
const statusChangeRows = Array.isArray(detail?.statusChanges) && detail.statusChanges.length
? detail.statusChanges
: synthesizeStatusChangesFromBasic(basic, source)
const statusChangeCards = buildStatusChangeCards(statusChangeRows, statusRegistry)
values.push({
key: 'statusChange',
title: '状态变更记录',
fields: statusChangeCards.length
? []
: [{ label: '记录', value: '暂无状态变更记录' }],
cards: statusChangeCards
})
if (isDonation) {
const order = ['protocol', 'collection', 'samples', 'customer', 'audits', 'attachments', 'statusChange']
const byKey = new Map(values.map((section) => [section.key, section]))
return order.map((key) => byKey.get(key)).filter(Boolean)
}
return values
},
formSections: [
+1 -1
View File
@@ -5,7 +5,7 @@ export function resolveStatusTone(statusText, backendTone) {
if (VALID_TONES.has(preferred)) return preferred
const status = String(statusText || '').trim().toLowerCase()
if (/不通过|异常|拒绝|失败|逾期|作废|danger|error|reject/.test(status)) return 'danger'
if (/待|未|部分|到期|进行|warning|pending|processing/.test(status)) return 'warning'
if (/待|未|部分|到期|过期|进行|warning|pending|processing/.test(status)) return 'warning'
if (/完成|通过|正常|启用|生效|已缴清|已入库|已采集|已接收|已办理|success|completed/.test(status)) return 'success'
if (/停用|默认|info/.test(status)) return 'info'
return 'primary'
@@ -12,3 +12,8 @@ test('matches negative status before positive substrings', () => {
assert.equal(resolveStatusTone('不通过'), 'danger')
assert.equal(resolveStatusTone('通过'), 'success')
})
test('expired status is warning instead of primary green', () => {
assert.equal(resolveStatusTone('已过期'), 'warning')
assert.equal(resolveStatusTone('已过期', 'warning'), 'warning')
})