feat: 本体字段治理与风险规则模板执行器重构

- 新增本体字段注册表与字段治理审计脚本
- 重构风险规则模板执行器、DSL 验证与清单分类器
- 完善票据夹服务与差旅请求详情页交互
- 优化趋势图表与总览页数据展示
- 增强报销平台风险分级与模拟公司筛选
- 补充本体字段、风险规则生成与票据夹服务测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-03 15:46:56 +08:00
parent e12b140508
commit 34457f9c3e
81 changed files with 4858 additions and 1073 deletions

View File

@@ -2,7 +2,6 @@ import { computed, ref } from 'vue'
export function createReceiptDetailDashboardModel({
detailForm,
editableOtherFields,
formatDateTime,
formatScore,
selectedReceipt
@@ -14,74 +13,29 @@ export function createReceiptDetailDashboardModel({
const pageCount = Number(selectedReceipt.value?.page_count || 1)
return `1 / ${Number.isFinite(pageCount) && pageCount > 0 ? pageCount : 1}`
})
const ocrPreviewFields = computed(() => (
editableOtherFields.value
.filter((field) => String(field?.label || field?.key || field?.value || '').trim())
.slice(0, 6)
))
const basicInfoItems = computed(() => [
{ label: '票据类型', value: fallback(detailForm.document_type_label) },
{ label: '票据名称', value: fallback(detailForm.file_name) },
{ label: '金额', value: fallback(detailForm.amount) },
{ label: '票据日期', value: fallback(detailForm.document_date) },
{ label: '提交人', value: fallback(selectedReceipt.value?.owner_name || selectedReceipt.value?.owner || '当前用户') },
{ label: '上传时间', value: formatDateTime(selectedReceipt.value?.uploaded_at) },
{ label: '所属单据编号', value: fallback(selectedReceipt.value?.linked_claim_no, '未关联') },
{ label: 'OCR 置信度', value: formatScore(selectedReceipt.value?.avg_score) }
])
const receiptStatusItems = computed(() => {
const linked = selectedReceipt.value?.status === 'linked'
return [
{ label: '识别状态', value: '识别成功', tone: 'success' },
{ label: '关联状态', value: selectedReceipt.value?.status_label || (linked ? '已关联' : '未关联'), tone: linked ? 'success' : 'warning' },
{ label: '重复报销风险', value: '无风险', tone: 'success' },
{ label: '归档状态', value: linked ? '待归档' : '未归档', tone: 'info' }
]
})
const linkedClaimItems = computed(() => [
{ label: '关联状态', value: selectedReceipt.value?.status === 'linked' ? '已关联' : '未关联' },
{ label: '报销单编号', value: fallback(selectedReceipt.value?.linked_claim_no, '未关联') },
{ label: '报销单名称', value: linkedClaimName.value },
{ label: '费用类型', value: fallback(detailForm.scene_label) },
{ label: '申请日期', value: dateOnly(selectedReceipt.value?.linked_at || selectedReceipt.value?.uploaded_at) },
{ label: '审批状态', value: selectedReceipt.value?.status === 'linked' ? '已关联' : '待关联' },
{ label: '是否已入账', value: '未入账' }
])
const operationLogs = computed(() => [
{
time: formatDateTime(selectedReceipt.value?.uploaded_at),
operator: fallback(selectedReceipt.value?.owner_name || selectedReceipt.value?.owner || '系统'),
label: '上传票据'
},
{
time: formatDateTime(selectedReceipt.value?.uploaded_at),
operator: '系统',
label: `OCR识别提取 ${editableOtherFields.value.length} 项要素`
},
{
time: formatDateTime(selectedReceipt.value?.linked_at || selectedReceipt.value?.uploaded_at),
operator: selectedReceipt.value?.status === 'linked' ? '系统' : '待处理',
label: selectedReceipt.value?.status === 'linked' ? `关联单据 ${selectedReceipt.value?.linked_claim_no || ''}` : '等待关联单据'
}
])
const archiveInfoItems = computed(() => [
{ label: '归档编号', value: archiveNo.value },
{ label: '归档目录', value: `${dateOnly(selectedReceipt.value?.uploaded_at)} / ${fallback(detailForm.scene_label)}` },
{ label: '保管期限', value: '10年' },
{ label: '关联附件数量', value: selectedReceipt.value?.status === 'linked' ? '1' : '0' },
{ label: '文件格式', value: fileFormat.value },
{ label: '文件大小', value: fallback(selectedReceipt.value?.file_size_label || selectedReceipt.value?.size_label, '待统计') }
{ label: '关联时间', value: formatDateTime(selectedReceipt.value?.linked_at) },
{ label: '关联附件数量', value: selectedReceipt.value?.status === 'linked' ? '1' : '0' }
])
const linkedClaimName = computed(() => (
selectedReceipt.value?.linked_claim_no
? `${fallback(detailForm.scene_label)}票据归集`
: '暂未关联报销单'
))
const archiveNo = computed(() => (
selectedReceipt.value?.id ? `DA-${String(selectedReceipt.value.id).slice(0, 8).toUpperCase()}` : '待生成'
))
const fileFormat = computed(() => {
const fileName = String(detailForm.file_name || selectedReceipt.value?.file_name || '').trim()
const suffix = fileName.includes('.') ? fileName.split('.').pop() : ''
return suffix ? suffix.toUpperCase() : fallback(selectedReceipt.value?.preview_kind, '待识别')
})
function adjustPreviewZoom(delta) {
previewZoom.value = Math.min(1.8, Math.max(0.6, Number((previewZoom.value + delta).toFixed(2))))
@@ -98,16 +52,12 @@ export function createReceiptDetailDashboardModel({
return {
adjustPreviewZoom,
archiveInfoItems,
basicInfoItems,
linkedClaimItems,
ocrPreviewFields,
operationLogs,
previewPageLabel,
previewRotation,
previewTransform,
previewZoom,
receiptStatusItems,
resetPreviewView,
rotatePreview
}
@@ -117,8 +67,3 @@ function fallback(value, empty = '待补充') {
const text = String(value || '').trim()
return text || empty
}
function dateOnly(value) {
const text = String(value || '').trim()
return text ? text.slice(0, 10) : '待确认'
}