Files
X-Financial/web/src/views/scripts/travelReimbursementReviewDocuments.js
caoxiaozhu 88ff04bef8 feat: 新增归档中心页面并完善知识库与报销查询能力
新增前端归档中心视图及相关工具函数,扩充知识库文档分类和
提取器支持多种格式,增强编排器报销查询的多维度检索,优
化本体规则和用户代理审核消息,前端完善报销创建和审批详
情交互细节,补充单元测试覆盖。
2026-05-22 16:00:19 +08:00

157 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
DOCUMENT_TYPE_LABELS,
EXPENSE_TYPE_LABELS
} from './travelReimbursementReviewConstants.js'
export function cloneReviewDocumentDrafts(items) {
return (Array.isArray(items) ? items : []).map((item) => ({
...item,
warnings: Array.isArray(item?.warnings) ? [...item.warnings] : [],
fields: Array.isArray(item?.fields)
? item.fields.map((field) => ({
label: String(field?.label || '').trim(),
value: String(field?.value || ''),
source: String(field?.source || 'ocr').trim() || 'ocr'
}))
: []
}))
}
export function buildReviewDocumentDrafts(reviewPayload) {
return buildReviewDocumentSummaries(reviewPayload).map((item) => ({
index: Number(item.index || 0),
filename: String(item.filename || '').trim(),
document_type: String(item.document_type || 'other').trim() || 'other',
suggested_expense_type: String(item.suggested_expense_type || 'other').trim() || 'other',
scene_label: String(item.scene_label || '').trim(),
summary: String(item.summary || '').trim(),
confidenceLabel: String(item.confidenceLabel || '').trim(),
documentTypeLabel: String(item.documentTypeLabel || '').trim(),
expenseTypeLabel: String(item.expenseTypeLabel || '').trim(),
preview_kind: String(item.preview_kind || '').trim(),
preview_data_url: String(item.preview_data_url || '').trim(),
preview_url: String(item.preview_url || '').trim(),
warnings: Array.isArray(item.warnings) ? [...item.warnings] : [],
fields: Array.isArray(item.fields)
? item.fields.map((field) => ({
label: String(field?.label || '').trim(),
value: String(field?.value || ''),
source: String(field?.source || 'ocr').trim() || 'ocr'
}))
: []
}))
}
export function normalizeReviewDocumentComparableValue(item) {
return {
index: Number(item?.index || 0),
filename: String(item?.filename || '').trim(),
scene_label: String(item?.scene_label || '').trim(),
summary: String(item?.summary || '').trim(),
fields: (Array.isArray(item?.fields) ? item.fields : []).map((field) => ({
label: String(field?.label || '').trim(),
value: String(field?.value || '').trim()
}))
}
}
export function buildReviewDocumentCorrectionLines(baseDrafts, nextDrafts) {
const baseMap = new Map(
cloneReviewDocumentDrafts(baseDrafts).map((item) => [`${item.index}:${item.filename}`, item])
)
return cloneReviewDocumentDrafts(nextDrafts).reduce((lines, item) => {
const key = `${item.index}:${item.filename}`
const base = baseMap.get(key)
const changes = []
const nextSceneLabel = String(item.scene_label || '').trim()
const baseSceneLabel = String(base?.scene_label || '').trim()
const nextSummary = String(item.summary || '').trim()
const baseSummary = String(base?.summary || '').trim()
if (nextSceneLabel !== baseSceneLabel) {
changes.push(`票据场景:${nextSceneLabel || '待补充'}`)
}
if (nextSummary !== baseSummary) {
changes.push(`识别摘要:${nextSummary || '待补充'}`)
}
const baseFieldMap = new Map(
(Array.isArray(base?.fields) ? base.fields : []).map((field) => [
String(field?.label || '').trim(),
String(field?.value || '').trim()
])
)
for (const field of Array.isArray(item.fields) ? item.fields : []) {
const label = String(field?.label || '').trim()
if (!label) continue
const nextValue = String(field?.value || '').trim()
const baseValue = baseFieldMap.get(label) || ''
if (nextValue !== baseValue) {
changes.push(`${label}${nextValue || '待补充'}`)
}
}
if (changes.length) {
lines.push(`${item.index}张票据(${item.filename}${changes.join('')}`)
}
return lines
}, [])
}
export function buildReviewDocumentCorrectionMessage(baseDrafts, nextDrafts) {
const lines = buildReviewDocumentCorrectionLines(baseDrafts, nextDrafts)
if (!lines.length) {
return ''
}
return `请同步修正逐票据识别结果:\n${lines.join('\n')}`
}
export function buildReviewDocumentCorrectionContext(drafts) {
return cloneReviewDocumentDrafts(drafts).map((item) => ({
index: item.index,
filename: item.filename,
scene_label: String(item.scene_label || '').trim(),
summary: String(item.summary || '').trim(),
fields: item.fields.map((field) => ({
label: String(field.label || '').trim(),
value: String(field.value || '').trim()
}))
}))
}
export function formatConfidenceLabel(value) {
const score = Number(value || 0)
if (!score) return '待补充'
return `${Math.round(score * 100)}%`
}
export function resolveDocumentTypeLabel(type) {
return DOCUMENT_TYPE_LABELS[String(type || '').trim()] || DOCUMENT_TYPE_LABELS.other
}
export function resolveExpenseTypeLabel(type, fallbackLabel = '') {
const normalized = String(type || '').trim()
return EXPENSE_TYPE_LABELS[normalized] || String(fallbackLabel || '').trim() || EXPENSE_TYPE_LABELS.other
}
export function buildReviewDocumentSummaries(reviewPayload) {
const docs = Array.isArray(reviewPayload?.document_cards) ? reviewPayload.document_cards : []
return docs.map((item) => {
const fields = Array.isArray(item.fields) ? item.fields : []
return {
...item,
documentTypeLabel: resolveDocumentTypeLabel(item.document_type),
expenseTypeLabel: resolveExpenseTypeLabel(item.suggested_expense_type, item.scene_label),
confidenceLabel: formatConfidenceLabel(item.avg_score),
lines: fields
.filter((field) => String(field?.value || '').trim())
.map((field) => `${field.label}${field.value}`)
}
})
}