fix(mobile-oa-h5): align presign and freight tags with registry mirror

Use info for draft; full payMode codes; receptionSource/transportType colors.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-10-15 10:06:07 +08:00
1 parent 7ba336169a
commit 2b5f2f939c
7 files changed
+97 -74

No files matched your search

@@ -75,7 +75,6 @@
v-if="can('submit')"
type="primary"
size="small"
icon="passed"
:loading="busyAction === 'submit-transfer'"
:disabled="busy"
@click="$emit('submit-transfer', modelValue)"
@@ -85,7 +84,6 @@
type="warning"
plain
size="small"
icon="revoke"
:loading="busyAction === 'withdraw-transfer'"
:disabled="busy"
@click="$emit('withdraw-transfer', modelValue)"
@@ -97,7 +95,6 @@
type="danger"
plain
size="small"
icon="delete-o"
:disabled="busy"
@click="$emit('remove', modelValue)"
>删除</van-button>
@@ -110,6 +107,7 @@ import { computed } from 'vue'
import OaDatePicker from '../../oa/components/OaDatePicker.vue'
import OaMoneyField from '../../oa/components/OaMoneyField.vue'
import { removeMarketAttachmentAt } from '../attachment-upload-state.js'
import { resolveStatusTag, payStatus } from '../status-tag-registry.js'
import PresignAttachmentUploader from './PresignAttachmentUploader.vue'
import PresignSelectField from './PresignSelectField.vue'
@@ -142,21 +140,11 @@ const canDelete = computed(() => !props.disabled && (newRow.value || actionSet.v
const busy = computed(() => Boolean(props.busyAction) || props.uploading)
const showActions = computed(() => canDelete.value || can('submit') || can('withdraw'))
const statusLabel = computed(() => ({
dfj: '已付款',
jfqrz: '付款确认中',
yjf: '付款完成',
tfqrz: '退费确认中',
ytf: '已退费'
}[String(props.modelValue.payStatus || 'dfj')] || '已付款'))
const statusTone = computed(() => ({
dfj: 'default',
jfqrz: 'warning',
yjf: 'success',
tfqrz: 'warning',
ytf: 'default'
}[String(props.modelValue.payStatus || 'dfj')] || 'default'))
const statusTag = computed(() =>
resolveStatusTag(payStatus, props.modelValue.payStatus || 'dfj')
)
const statusLabel = computed(() => statusTag.value.label)
const statusTone = computed(() => statusTag.value.tagType)
function can(action) {
return !props.disabled && actionSet.value.has(action)
+22 -6
View File
@@ -5,7 +5,9 @@ import {
payMode,
protocolStatus,
donationStatus,
paymentChaseStatus
paymentChaseStatus,
receptionSource,
transportType
} from './status-tag-registry.js'
const SEARCH_PLACEHOLDERS = Object.freeze({
@@ -298,10 +300,12 @@ function freightSampleBlock(row, index) {
const title = sampleCode
? `样本 ${index + 1} · ${sampleCode}`
: `样本 ${index + 1}`
const source = resolveStatusTag(receptionSource, row?.sampleSource)
return {
key: String(row?.id || row?.sampleId || `${sampleCode || 'sample'}-${index}`),
title,
status: label(SOURCE_LABELS, row?.sampleSource),
status: source.label,
statusTone: source.tagType,
fields: [
field('协议号', row?.protocolNo, { wide: true }),
field('母亲姓名', row?.motherName),
@@ -312,18 +316,30 @@ function freightSampleBlock(row, index) {
}
}
function freightTransportDisplay(item) {
const code = String(item?.transportMode || '').trim()
const provided = String(item?.transportModeLabel || '').trim()
if (provided && provided !== code) return provided
if (!code && !provided) return '—'
if (!code) return provided
return resolveStatusTag(transportType, code).label
}
function freightCard(role, item) {
const rows = Array.isArray(item.items) ? item.items : []
const first = rows[0] || {}
const transportMode = label({}, item.transportModeLabel || item.transportMode)
const source = label(SOURCE_LABELS, first.sampleSource || item.sourceLabel)
const transportCode = String(item.transportMode || '').trim()
const transportTag = resolveStatusTag(transportType, transportCode)
const transportMode = freightTransportDisplay(item)
const source = resolveStatusTag(receptionSource, first.sampleSource || item.sourceLabel)
const sampleCount = Number(item.visibleItemCount ?? rows.length) || 0
return {
title: text(item.freightNo),
subtitle: `${transportMode} · ${money(item.freightAmount)}`,
status: source,
status: source.label,
statusTone: source.tagType,
fields: [
field('运输方式', transportMode, { tag: true }),
field('运输方式', transportMode, { tag: true, tagType: transportTag.tagType }),
field('运费金额', money(item.freightAmount), { money: true }),
field('关联数', String(sampleCount)),
field('创建人', personName(item.creatorName || item.createByName, item.createBy)),
+49 -29
View File
@@ -1,10 +1,11 @@
const SOURCE_LABELS = Object.freeze({ cell_storage: '细胞存储', research_donation: '科研捐赠' })
import { resolveStatusTag, receptionSource, transportType } from '../status-tag-registry.js'
function dictionaryLabel(map, code, providedLabel) {
const label = String(providedLabel || '').trim()
function transportDisplay(code, providedLabel) {
const key = String(code || '').trim()
const label = String(providedLabel || '').trim()
if (label && label !== key) return label
return map[key] || (key ? '未知' : '—')
if (!key) return '—'
return resolveStatusTag(transportType, key).label
}
function person(name, id) {
@@ -24,29 +25,39 @@ export default {
key: 'freight',
resource: 'freights',
actions: {
specialist: ['view', 'create', 'edit', 'link', 'unlink'],
manager: ['view', 'create', 'edit', 'link', 'unlink']
specialist: ['view', 'create', 'edit', 'delete', 'link', 'unlink'],
manager: ['view', 'create', 'edit', 'delete', 'link', 'unlink']
},
listParams: ({ status } = {}) => ({ transportMode: status || '', includeItems: true }),
statusOptions: [{ text: '全部运输方式', value: '' }],
filterFields: [
{ key: 'transportMode', label: '运输方式', type: 'select', optionKey: 'transportModes' },
// 运费列表用搜索下方 van-dropdown-menu(对齐预签约),不走筛选弹层
filterFields: [],
inlineDropdownFilters: [
{ key: 'transportMode', allText: '全部运输方式', optionKey: 'transportModes' },
{
key: 'sampleSource', label: '样本来源', type: 'select', options: [
key: 'sampleSource',
allText: '全部样本来源',
options: [
{ text: '细胞存储', value: 'cell_storage' },
{ text: '科研捐赠', value: 'research_donation' }
]
},
{ key: 'createTimeRange', label: '创建时间', type: 'date-range' }
}
],
detailSections(detail) {
const transportCode = String(detail?.transportMode || '').trim()
const transportTag = resolveStatusTag(transportType, transportCode)
return [
{
key: 'base',
title: '运费信息',
fields: [
{ label: '批次号', value: detail?.freightNo || '—' },
{ label: '运输方式', value: dictionaryLabel({}, detail?.transportMode, detail?.transportModeLabel), tag: true, tagType: 'primary' },
{
label: '运输方式',
value: transportDisplay(transportCode, detail?.transportModeLabel),
tag: true,
tagType: transportTag.tagType
},
{ label: '运费金额', value: money(detail?.freightAmount) },
{ label: '关联数', value: String(detail?.visibleItemCount ?? detail?.items?.length ?? 0) },
{ label: '创建人', value: person(detail?.creatorName || detail?.createByName, detail?.createBy) },
@@ -60,19 +71,23 @@ export default {
key: 'items',
title: '关联样本',
fields: [],
cards: (detail?.items || []).map((item, index) => ({
key: String(item?.id || `item-${index}`),
title: item?.sampleCode || `样本 ${index + 1}`,
statusText: dictionaryLabel(SOURCE_LABELS, item?.sampleSource),
fields: [
{ label: '来源', value: dictionaryLabel(SOURCE_LABELS, item?.sampleSource) },
{ label: '协议单号', value: item?.protocolNo || '—' },
{ label: '样本编码', value: item?.sampleCode || '—' },
{ label: '样本类型', value: item?.sampleTypeName || '—' },
{ label: '母亲姓名', value: item?.motherName || '—' },
{ label: '子女姓名', value: item?.newbornName || '—' }
]
}))
cards: (detail?.items || []).map((item, index) => {
const source = resolveStatusTag(receptionSource, item?.sampleSource)
return {
key: String(item?.id || `item-${index}`),
title: item?.sampleCode || `样本 ${index + 1}`,
statusText: source.label,
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 || '—' },
{ label: '子女姓名', value: item?.newbornName || '—' }
]
}
})
}
]
},
@@ -83,8 +98,13 @@ export default {
if (action === 'unlink') return Array.isArray(detailItems) ? detailItems : []
return Array.isArray(optionRows) ? optionRows : []
},
formSections: [{ key: 'base', title: '运费批次', fields: [
{ key: 'transportMode', label: '运输方式', type: 'select', optionKey: 'transportModes', required: true },
{ key: 'freightAmount', label: '运费金额', type: 'number', required: true }
] }]
formSections: [{
key: 'base',
title: '运费批次',
fieldsLayout: 'grid-2',
fields: [
{ key: 'transportMode', label: '运输方式', type: 'select', optionKey: 'transportModes', required: true },
{ key: 'freightAmount', label: '运费金额', type: 'number', required: true }
]
}]
}
+10 -11
View File
@@ -6,14 +6,8 @@ import {
presignStatusLabel,
presignStatusTone
} from '../presign/presign-presenter.js'
import { resolveStatusTag, payMode, payStatus } from '../status-tag-registry.js'
const paymentStatus = Object.freeze({
dfj: { label: '已付款', tone: 'default' },
jfqrz: { label: '付款确认中', tone: 'warning' },
yjf: { label: '付款完成', tone: 'success' },
tfqrz: { label: '退费确认中', tone: 'warning' },
ytf: { label: '已退费', tone: 'default' }
})
const transferTypeLabels = Object.freeze({
wxzz: '微信转账', wx: '微信转账', wechat: '微信转账',
zzbzz: '支付宝转账', zfb: '支付宝转账', alipay: '支付宝转账',
@@ -51,6 +45,12 @@ function displayValue(value) {
return value && value !== '-' ? value : '—'
}
function payModeLabel(value) {
const key = String(value || '').trim()
if (!key) return '—'
return resolveStatusTag(payMode, key).label
}
export default {
key: 'presign',
resource: 'presign-orders',
@@ -85,13 +85,12 @@ export default {
detailSections(detail) {
const status = String(detail?.orderStatus || '')
const transfers = (detail?.transfers || []).map((transfer, index) => {
const paymentCode = String(transfer?.payStatus || 'dfj')
const payment = paymentStatus[paymentCode] || { label: '未知', tone: 'default' }
const payment = resolveStatusTag(payStatus, transfer?.payStatus || 'dfj')
return {
...transfer,
title: `意向金转账 ${index + 1}`,
statusLabel: payment.label,
statusTone: payment.tone,
statusTone: payment.tagType,
transferAmount: formatPresignMoney(transfer?.transferAmount),
transferTime: formatPresignDateTime(transfer?.transferTime)
}
@@ -110,7 +109,7 @@ export default {
{ label: '采集专员', value: person(detail?.collectorUserName, detail?.collectorUserId) },
{ label: '采集经理', value: person(detail?.regionManagerUserName, detail?.regionManagerUserId) },
{ label: '采集时间', value: displayValue(formatPresignDateTime(detail?.sampleCollectTime)) },
{ label: '缴费方式', value: dictionaryLabel({ fq: '分期', dj: '趸交' }, detail?.payMode) },
{ label: '缴费方式', value: payModeLabel(detail?.payMode) },
{
label: '样本类型',
value: Array.isArray(detail?.sampleTypeNames) && detail.sampleTypeNames.length
@@ -1,4 +1,5 @@
import { intersectAllowedActions } from '../action-policy.js'
import { resolveStatusTag, payMode } from '../status-tag-registry.js'
import {
formatPresignDate,
formatPresignDateTime,
@@ -9,7 +10,6 @@ import {
} from './presign-presenter.js'
const CARD_ACTIONS = new Set(['edit', 'convert', 'delete'])
const PAY_MODE_LABELS = Object.freeze({ dj: '趸交', fq: '分期', fgd: '分改趸', djfq: '趸交+分期' })
function displayText(value) {
return String(value ?? '').trim() || '-'
@@ -21,7 +21,8 @@ function actionCode(action) {
function payModeLabel(value) {
const key = String(value || '').trim()
return PAY_MODE_LABELS[key] || (key ? '未知' : '-')
if (!key) return '-'
return resolveStatusTag(payMode, key).label
}
export function buildPresignListParams({ keyword = '', orderStatus = '', page = 1, pageSize = 10 } = {}) {
@@ -1,9 +1,4 @@
const PRESIGN_STATUS = Object.freeze({
draft: Object.freeze({ label: '草稿', tone: 'default' }),
active: Object.freeze({ label: '进行中', tone: 'warning' }),
converted: Object.freeze({ label: '已转正式', tone: 'success' }),
void: Object.freeze({ label: '已作废', tone: 'danger' })
})
import { resolveStatusTag, presignOrderStatus } from '../status-tag-registry.js'
function validDateParts(year, month, day) {
const date = new Date(Date.UTC(year, month - 1, day))
@@ -83,11 +78,13 @@ export function formatPresignMoney(value) {
export function presignStatusLabel(value) {
const code = String(value ?? '').trim()
if (!code) return '-'
return PRESIGN_STATUS[code]?.label || '未知'
return resolveStatusTag(presignOrderStatus, code).label
}
export function presignStatusTone(value) {
return PRESIGN_STATUS[String(value ?? '').trim()]?.tone || 'default'
const code = String(value ?? '').trim()
if (!code) return 'info'
return resolveStatusTag(presignOrderStatus, code).tagType
}
export function presignCardIndex(page, pageSize, position) {
@@ -40,6 +40,8 @@ test('uses the Web presign status labels and mobile semantic tones', () => {
assert.equal(presignStatusLabel('active'), '进行中')
assert.equal(presignStatusLabel('converted'), '已转正式')
assert.equal(presignStatusLabel('void'), '已作废')
assert.equal(presignStatusTone('draft'), 'info')
assert.notEqual(presignStatusTone('draft'), 'default')
assert.equal(presignStatusTone('converted'), 'success')
assert.equal(presignStatusTone('void'), 'danger')
assert.equal(presignStatusLabel('custom'), '未知')