import { computed } from 'vue' const TRAIN_KEY_FIELD_DEFINITIONS = [ { id: 'invoice_number', label: '发票号码', placeholder: '待识别', keys: ['invoice_number', 'ticket_number'], labels: ['发票号码', '票据号码', '票号'] }, { id: 'invoice_date', label: '开票日期', placeholder: 'YYYY-MM-DD', keys: ['invoice_date', 'issue_date'], labels: ['开票日期', '发票日期'] }, { id: 'fare', label: '票价', placeholder: '待识别', keys: ['fare', 'amount'], labels: ['票价', '金额'] }, { id: 'passenger_name', label: '姓名', placeholder: '待识别', keys: ['passenger_name'], labels: ['乘车人', '旅客姓名', '姓名'] } ] const DEFAULT_KEY_FIELD_DEFINITIONS = [ { id: 'invoice_number', label: '发票号码', placeholder: '待识别', keys: ['invoice_number', 'ticket_number'], labels: ['发票号码', '票据号码', '票号'] }, { id: 'invoice_date', label: '开票日期', placeholder: 'YYYY-MM-DD', keys: ['invoice_date', 'issue_date'], labels: ['开票日期', '发票日期'] }, { id: 'amount', label: '金额', placeholder: '待识别', keys: ['amount', 'fare'], labels: ['金额', '价税合计', '合计金额', '票价'] }, { id: 'merchant_name', label: '商户', placeholder: '待识别', keys: ['merchant_name'], labels: ['商户', '销售方', '开票方'] } ] const RECEIPT_META_FIELD_DEFINITIONS = [ { id: 'document_type_label', label: '票据类型', placeholder: '待识别', keys: ['document_type_label'], labels: ['票据类型', '识别类型'] }, { id: 'scene_label', label: '费用场景', placeholder: '待识别', keys: ['scene_label'], labels: ['费用场景', '场景'] }, { id: 'merchant_name', label: '商户', placeholder: '待识别', keys: ['merchant_name'], labels: ['商户', '销售方', '开票方'] } ] export function createReceiptDetailFieldModel({ detailForm, isTrainTicket }) { const activeKeyFieldDefinitions = computed(() => ( isTrainTicket.value ? TRAIN_KEY_FIELD_DEFINITIONS : DEFAULT_KEY_FIELD_DEFINITIONS )) const keyReceiptFields = computed(() => ( activeKeyFieldDefinitions.value.map((definition) => ({ ...definition, value: getReceiptFieldValue(definition) })) )) const keyReceiptFieldTokens = computed(() => { const tokens = new Set() activeKeyFieldDefinitions.value.forEach((definition) => { for (const token of [definition.id, ...(definition.keys || []), ...(definition.labels || [])]) { const normalized = normalizeReceiptFieldToken(token) if (normalized) tokens.add(normalized) } }) return tokens }) const editableOtherFields = computed(() => ( detailForm.fields.filter((field) => { const key = normalizeReceiptFieldToken(field?.key) const label = normalizeReceiptFieldToken(field?.label) return !keyReceiptFieldTokens.value.has(key) && !keyReceiptFieldTokens.value.has(label) }) )) function findReceiptFieldForDefinition(definition) { const keys = (definition.keys || []).map(normalizeReceiptFieldToken).filter(Boolean) const labels = (definition.labels || []).map(normalizeReceiptFieldToken).filter(Boolean) return detailForm.fields.find((field) => keys.includes(normalizeReceiptFieldToken(field?.key))) || detailForm.fields.find((field) => labels.includes(normalizeReceiptFieldToken(field?.label))) || null } function getReceiptFieldFallback(definition) { if (definition.id === 'invoice_date') return detailForm.document_date if (definition.id === 'fare' || definition.id === 'amount') return detailForm.amount if (definition.id === 'merchant_name') return detailForm.merchant_name if (definition.id === 'document_type_label') return detailForm.document_type_label if (definition.id === 'scene_label') return detailForm.scene_label return '' } function getReceiptFieldValue(definition) { const field = findReceiptFieldForDefinition(definition) return String(field?.value || getReceiptFieldFallback(definition) || '') } function ensureReceiptField(definition) { const field = findReceiptFieldForDefinition(definition) if (field) { field.key = field.key || definition.keys?.[0] || definition.id field.label = field.label || definition.label return field } const created = { key: definition.keys?.[0] || definition.id, label: definition.label, value: getReceiptFieldFallback(definition) } detailForm.fields.push(created) return created } function ensureEditableReceiptFields() { for (const definition of [...activeKeyFieldDefinitions.value, ...RECEIPT_META_FIELD_DEFINITIONS]) { const field = ensureReceiptField(definition) const fallback = getReceiptFieldFallback(definition) if (!String(field.value || '').trim() && fallback) { field.value = fallback } } } function updateReceiptField(definition, value) { const field = ensureReceiptField(definition) field.value = value syncEditableFieldsToTopLevel() } function readFieldValue(definition) { return String(findReceiptFieldForDefinition(definition)?.value || '').trim() } function syncEditableFieldsToTopLevel() { const invoiceDate = readFieldValue(TRAIN_KEY_FIELD_DEFINITIONS[1]) || readFieldValue(DEFAULT_KEY_FIELD_DEFINITIONS[1]) const amount = readFieldValue(TRAIN_KEY_FIELD_DEFINITIONS[2]) || readFieldValue(DEFAULT_KEY_FIELD_DEFINITIONS[2]) const merchant = readFieldValue(RECEIPT_META_FIELD_DEFINITIONS[2]) || readFieldValue(DEFAULT_KEY_FIELD_DEFINITIONS[3]) const documentTypeLabel = readFieldValue(RECEIPT_META_FIELD_DEFINITIONS[0]) const sceneLabel = readFieldValue(RECEIPT_META_FIELD_DEFINITIONS[1]) if (invoiceDate) detailForm.document_date = invoiceDate if (amount) detailForm.amount = amount if (merchant) detailForm.merchant_name = merchant if (documentTypeLabel) detailForm.document_type_label = documentTypeLabel if (sceneLabel) detailForm.scene_label = sceneLabel } function buildDetailPayload() { syncEditableFieldsToTopLevel() return { document_type: detailForm.document_type, document_type_label: detailForm.document_type_label, scene_code: detailForm.scene_code, scene_label: detailForm.scene_label, summary: detailForm.summary, amount: detailForm.amount, document_date: detailForm.document_date, merchant_name: detailForm.merchant_name, fields: detailForm.fields .map((field) => ({ key: String(field?.key || '').trim(), label: String(field?.label || '').trim(), value: String(field?.value || '').trim() })) .filter((field) => field.key || field.label || field.value) } } return { buildDetailPayload, editableOtherFields, ensureEditableReceiptFields, keyReceiptFields, syncEditableFieldsToTopLevel, updateReceiptField } } function normalizeReceiptFieldToken(value) { return String(value || '').trim().toLowerCase().replace(/\s+/g, '') }