feat: 新增归档中心页面并完善知识库与报销查询能力

新增前端归档中心视图及相关工具函数,扩充知识库文档分类和
提取器支持多种格式,增强编排器报销查询的多维度检索,优
化本体规则和用户代理审核消息,前端完善报销创建和审批详
情交互细节,补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-22 16:00:19 +08:00
parent 1f15699013
commit 88ff04bef8
120 changed files with 6236 additions and 643 deletions

View File

@@ -6,26 +6,26 @@ import {
import { normalizeExpenseQueryPayload } from './travelReimbursementExpenseQueryModel.js'
const SCENARIO_LABELS = {
expense: '??',
accounts_receivable: '??',
accounts_payable: '??',
knowledge: '??',
unknown: '??'
expense: '报销',
accounts_receivable: '应收',
accounts_payable: '应付',
knowledge: '知识',
unknown: '通用'
}
const INTENT_LABELS = {
query: '??',
explain: '??',
compare: '??',
risk_check: '????',
draft: '????',
operate: '????'
query: '查询',
explain: '解释',
compare: '对比',
risk_check: '风险检查',
draft: '信息核对',
operate: '动作请求'
}
function resolveStatusLabel(status) {
if (status === 'succeeded') return '???'
if (status === 'blocked') return '???'
return '??'
if (status === 'succeeded') return '已完成'
if (status === 'blocked') return '已阻断'
return '处理中'
}
function resolveStatusTone(status) {
@@ -123,6 +123,12 @@ function buildAssociationDocumentContentLines(document) {
return ['- 识别内容:暂未提取到结构化字段,请以票据原件为准。']
}
function buildAssociationDocumentCard(lines) {
return (Array.isArray(lines) ? lines : [])
.map((line) => String(line || '').trim() ? `> ${line}` : '>')
.join('\n')
}
export function buildAttachmentAssociationConfirmationMessage({
claimNo = '',
claimTitle = '',
@@ -144,13 +150,14 @@ export function buildAttachmentAssociationConfirmationMessage({
const filename = String(document?.filename || '').trim() || `附件 ${index + 1}`
const typeLabel = resolveAssociationDocumentTypeLabel(document)
const contentLines = buildAssociationDocumentContentLines(document)
return [
`附件 ${index + 1}${filename}`,
.map((line) => String(line || '').replace(/^-\s*/, ''))
return buildAssociationDocumentCard([
`**附件 ${index + 1}${filename}**`,
'',
`附件类型:${typeLabel}`,
'',
...contentLines
].join('\n')
])
})
return [
@@ -158,14 +165,17 @@ export function buildAttachmentAssociationConfirmationMessage({
'',
documentBlocks.join('\n\n'),
'',
'',
'请问是否确定将票据信息归集到单据:',
'',
targetLines.join('\n'),
'',
`如果 [确认](${ATTACHMENT_ASSOCIATION_CONFIRM_HREF}) 该信息,我将直接将票据进行归集。`
'',
`如果 **[确认](${ATTACHMENT_ASSOCIATION_CONFIRM_HREF})** 该信息,我将直接将票据进行归集。`
]
.filter((part) => String(part || '').trim())
.join('\n')
.replace(/\n{4,}/g, '\n\n\n')
.trim()
}
export function normalizeReviewDocumentFieldKey(label) {
@@ -235,6 +245,9 @@ export function buildOcrDocumentsFromReviewPayload(reviewPayload) {
document_type_label: resolveDocumentTypeLabel(item?.document_type),
scene_code: resolveExpenseTypeCode(item?.suggested_expense_type),
scene_label: String(item?.scene_label || '').trim(),
preview_kind: String(item?.preview_kind || '').trim(),
preview_data_url: String(item?.preview_data_url || '').trim(),
preview_url: String(item?.preview_url || '').trim(),
document_fields: fields,
warnings: Array.isArray(item?.warnings) ? item.warnings : []
}
@@ -373,12 +386,32 @@ export function mergeFilePreviews(existingPreviews, incomingPreviews) {
return result
}
function inferPreviewKindFromUrl(url) {
const normalized = String(url || '').trim().toLowerCase()
if (!normalized) return ''
if (normalized.startsWith('data:image/') || /\.(png|jpg|jpeg|webp|bmp)(?:[?#].*)?$/i.test(normalized)) {
return 'image'
}
if (normalized.startsWith('data:application/pdf') || /\.pdf(?:[?#].*)?$/i.test(normalized)) {
return 'pdf'
}
return ''
}
function resolveDocumentPreviewKind(item) {
const explicit = String(item?.preview_kind || '').trim()
if (explicit) {
return explicit
}
return inferPreviewKindFromUrl(String(item?.preview_url || item?.preview_data_url || '').trim())
}
export function buildOcrFilePreviews(payload) {
const documents = Array.isArray(payload?.documents) ? payload.documents : []
return documents
.map((item) => ({
filename: String(item?.filename || '').trim(),
kind: String(item?.preview_kind || '').trim(),
kind: resolveDocumentPreviewKind(item),
url: String(item?.preview_url || item?.preview_data_url || '').trim()
}))
.filter((item) => item.filename && item.kind === 'image' && item.url)
@@ -389,7 +422,7 @@ export function buildReviewFilePreviewsFromReviewPayload(reviewPayload) {
return documents
.map((item) => ({
filename: String(item?.filename || '').trim(),
kind: String(item?.preview_kind || '').trim(),
kind: resolveDocumentPreviewKind(item),
url: String(item?.preview_url || item?.preview_data_url || '').trim()
}))
.filter((item) => item.filename && item.kind === 'image' && item.url)