feat: 扩展风险规则体系、审批动态路由与预算中心列表化改造
- 新增 25+ 条风险规则(预算/报销/申请/通用类),完善风险规则模拟与反馈发布机制 - 引入费用审批动态路由、平台风险分级、预审与风险阶段管理 - 预算中心列表化改造,优化票据夹仪表盘与数字员工工作看板 - 新增 Hermes 风险线索收集器、Agent 链路追踪中心 - 扩展数字员工能力库(18 个领域 Skill)与交通费用自动预估 - 完善报销申请快速预览、权限控制与前端测试覆盖
This commit is contained in:
124
web/src/views/scripts/receiptFolderDetailDashboard.js
Normal file
124
web/src/views/scripts/receiptFolderDetailDashboard.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
export function createReceiptDetailDashboardModel({
|
||||
detailForm,
|
||||
editableOtherFields,
|
||||
formatDateTime,
|
||||
formatScore,
|
||||
selectedReceipt
|
||||
}) {
|
||||
const previewZoom = ref(1)
|
||||
const previewRotation = ref(0)
|
||||
const previewTransform = computed(() => `scale(${previewZoom.value}) rotate(${previewRotation.value}deg)`)
|
||||
const previewPageLabel = computed(() => {
|
||||
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(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: 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, '待统计') }
|
||||
])
|
||||
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))))
|
||||
}
|
||||
|
||||
function resetPreviewView() {
|
||||
previewZoom.value = 1
|
||||
previewRotation.value = 0
|
||||
}
|
||||
|
||||
function rotatePreview() {
|
||||
previewRotation.value = (previewRotation.value + 90) % 360
|
||||
}
|
||||
|
||||
return {
|
||||
adjustPreviewZoom,
|
||||
archiveInfoItems,
|
||||
basicInfoItems,
|
||||
linkedClaimItems,
|
||||
ocrPreviewFields,
|
||||
operationLogs,
|
||||
previewPageLabel,
|
||||
previewRotation,
|
||||
previewTransform,
|
||||
previewZoom,
|
||||
receiptStatusItems,
|
||||
resetPreviewView,
|
||||
rotatePreview
|
||||
}
|
||||
}
|
||||
|
||||
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) : '待确认'
|
||||
}
|
||||
Reference in New Issue
Block a user