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

@@ -4,6 +4,7 @@ import {
} from './travelReimbursementReviewModel.js'
export const EXPENSE_QUERY_PAGE_SIZE = 5
export const EXPENSE_CENTER_HREF = '/app/requests'
export const ASSOCIATABLE_CLAIM_STATUSES = new Set(['draft', 'supplement', 'returned'])
const EXPENSE_STATUS_LABELS = {
draft: '草稿',
@@ -14,6 +15,36 @@ const EXPENSE_STATUS_LABELS = {
approved: '已审核',
paid: '已入账'
}
const EXPENSE_RISK_LEVEL_LABELS = {
high: '高风险',
medium: '中风险',
warning: '中风险',
low: '低风险',
info: '低风险'
}
export function normalizeExpenseQueryRiskItem(item, index = 0) {
if (!item || typeof item !== 'object') {
return null
}
const rawLevel = String(item.level || item.severity || '').trim().toLowerCase()
const level = EXPENSE_RISK_LEVEL_LABELS[rawLevel] ? rawLevel : 'medium'
const summary = String(item.summary || item.message || item.content || '').trim()
const detail = String(item.detail || item.description || summary).trim()
if (!summary && !detail) {
return null
}
return {
key: String(item.key || `${level}-${index}`).trim() || `${level}-${index}`,
level,
levelLabel: String(item.level_label || item.levelLabel || EXPENSE_RISK_LEVEL_LABELS[level]).trim() || EXPENSE_RISK_LEVEL_LABELS[level],
title: String(item.title || item.label || EXPENSE_RISK_LEVEL_LABELS[level]).trim() || EXPENSE_RISK_LEVEL_LABELS[level],
summary: summary || detail,
detail: detail || summary
}
}
export function normalizeExpenseQueryStatusGroup(item) {
if (!item || typeof item !== 'object') {
@@ -57,6 +88,9 @@ export function normalizeExpenseQueryRecord(item) {
occurredAt,
reason,
location: String(item.location || '').trim(),
riskItems: (Array.isArray(item.risk_flags) ? item.risk_flags : [])
.map((riskItem, index) => normalizeExpenseQueryRiskItem(riskItem, index))
.filter(Boolean),
summary: reason || `${expenseTypeLabel}报销`,
dateDisplay: documentDate || occurredAt || '待补充日期'
}
@@ -164,6 +198,7 @@ export function normalizeExpenseQueryPayload(payload) {
const rawRecordCount = Number(payload.record_count || 0)
const rawPreviewCount = Number(payload.preview_count || records.length)
const rawPreviewLimit = Number(payload.preview_limit || EXPENSE_QUERY_PAGE_SIZE)
const rawOlderRecordCount = Number(payload.older_record_count || 0)
const totalAmount = Number(payload.total_amount || 0)
const rawWindowDays = Number(payload.window_days || 0)
@@ -187,6 +222,7 @@ export function normalizeExpenseQueryPayload(payload) {
windowEndDate: windowEndDate || '',
recordCount: Number.isFinite(rawRecordCount) ? Math.max(0, rawRecordCount) : 0,
previewCount: Number.isFinite(rawPreviewCount) ? Math.max(0, rawPreviewCount) : records.length,
previewLimit: Number.isFinite(rawPreviewLimit) ? Math.max(1, rawPreviewLimit) : EXPENSE_QUERY_PAGE_SIZE,
olderRecordCount: Number.isFinite(rawOlderRecordCount) ? Math.max(0, rawOlderRecordCount) : 0,
hasMoreInWindow: Boolean(payload.has_more_in_window || payload.has_more),
totalAmount: Number.isFinite(totalAmount) ? totalAmount : 0,
@@ -250,18 +286,13 @@ export function buildExpenseQueryHint(queryPayload) {
}
const parts = []
const windowText = buildExpenseQueryWindowLabel(queryPayload)
const previewLimit = Math.max(1, Number(queryPayload.previewLimit || EXPENSE_QUERY_PAGE_SIZE))
const totalCount = Math.max(0, Number(queryPayload.recordCount || 0))
if (Array.isArray(queryPayload.records) && queryPayload.records.length > EXPENSE_QUERY_PAGE_SIZE) {
parts.push(`当前共整理 ${queryPayload.records.length} 笔单据,可左右切换查看`)
}
if (queryPayload.hasMoreInWindow && queryPayload.previewCount < queryPayload.recordCount) {
parts.push(`${windowText}${queryPayload.recordCount} 笔,当前先整理最近 ${queryPayload.previewCount}`)
}
if (queryPayload.olderRecordCount > 0 && queryPayload.windowDays) {
parts.push(`另有 ${queryPayload.olderRecordCount} 笔超过 ${queryPayload.windowDays} 日的单据,请前往个人报销中心查看`)
if (totalCount > previewLimit) {
parts.push(`我只会筛选出最近的 ${previewLimit} 条记录;如果想查询全部的单据,请点击 [**这里**](${EXPENSE_CENTER_HREF}) 跳转到报销中心查看`)
} else if (totalCount > 0) {
parts.push(`当前已展示本次筛选命中的全部记录;如果想进入报销中心继续筛选,请点击 [**这里**](${EXPENSE_CENTER_HREF})。`)
}
return parts.join('。')