feat: 本体字段治理与风险规则模板执行器重构
- 新增本体字段注册表与字段治理审计脚本 - 重构风险规则模板执行器、DSL 验证与清单分类器 - 完善票据夹服务与差旅请求详情页交互 - 优化趋势图表与总览页数据展示 - 增强报销平台风险分级与模拟公司筛选 - 补充本体字段、风险规则生成与票据夹服务测试覆盖
This commit is contained in:
@@ -18,6 +18,7 @@ import { waitForMockApplicationTransportQuote } from '../../utils/expenseApplica
|
||||
import { fetchOntologyParse } from '../../services/ontology.js'
|
||||
import { buildExpenseApplicationOntologyContext } from '../../utils/expenseApplicationOntology.js'
|
||||
import { calculateTravelReimbursement } from '../../services/reimbursements.js'
|
||||
import { fetchReceiptFolderItems } from '../../services/receiptFolder.js'
|
||||
import {
|
||||
handleBudgetCompileReportSubmit,
|
||||
shouldUseBudgetCompileReport
|
||||
@@ -171,6 +172,78 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
function hasReceiptFolderSourceFile(files) {
|
||||
return files.some((file) => String(file?.receiptId || '').trim())
|
||||
}
|
||||
|
||||
async function promptUnlinkedReceiptFolderIfNeeded({
|
||||
detailScopedClaimId,
|
||||
files,
|
||||
fileNames,
|
||||
options,
|
||||
rawText,
|
||||
resolvedUploadDisposition,
|
||||
reviewAction,
|
||||
systemGenerated,
|
||||
userText
|
||||
}) {
|
||||
if (
|
||||
isKnowledgeSession.value ||
|
||||
systemGenerated ||
|
||||
!files.length ||
|
||||
detailScopedClaimId ||
|
||||
resolvedUploadDisposition ||
|
||||
options.skipReceiptFolderUnlinkedPrompt ||
|
||||
options.skipDraftAssociationPrompt ||
|
||||
reviewAction ||
|
||||
hasReceiptFolderSourceFile(files)
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
let unlinkedReceipts = []
|
||||
try {
|
||||
unlinkedReceipts = await fetchReceiptFolderItems('unlinked')
|
||||
} catch (error) {
|
||||
console.warn('Failed to load unlinked receipt folder items before attachment upload:', error)
|
||||
return false
|
||||
}
|
||||
const count = Array.isArray(unlinkedReceipts) ? unlinkedReceipts.length : 0
|
||||
if (!count) {
|
||||
return false
|
||||
}
|
||||
|
||||
resetFlowRun()
|
||||
if (!options.skipUserMessage) {
|
||||
messages.value.push(createMessage('user', userText, fileNames))
|
||||
}
|
||||
messages.value.push(createMessage(
|
||||
'assistant',
|
||||
`票据夹中还有 ${count} 份未关联票据。建议先处理这些票据再上传新附件,避免重复保存或遗漏关联。`,
|
||||
[],
|
||||
{
|
||||
meta: ['票据夹待关联'],
|
||||
suggestedActions: [
|
||||
{
|
||||
action_type: 'open_receipt_folder',
|
||||
label: '去票据夹关联',
|
||||
icon: 'mdi mdi-folder-open-outline',
|
||||
payload: { target_view: 'receiptFolder' }
|
||||
},
|
||||
{
|
||||
action_type: 'continue_upload_with_unlinked_receipts',
|
||||
label: '继续上传新附件',
|
||||
icon: 'mdi mdi-upload-outline',
|
||||
payload: { raw_text: rawText }
|
||||
}
|
||||
]
|
||||
}
|
||||
))
|
||||
nextTick(scrollToBottom)
|
||||
persistSessionState()
|
||||
return true
|
||||
}
|
||||
|
||||
function buildConfirmedAssociationText(message) {
|
||||
return String(message?.text || '')
|
||||
.replace(`[确认](${ATTACHMENT_ASSOCIATION_CONFIRM_HREF})`, '已确认')
|
||||
@@ -653,6 +726,20 @@ export function useTravelReimbursementSubmitComposer(ctx) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (await promptUnlinkedReceiptFolderIfNeeded({
|
||||
detailScopedClaimId,
|
||||
files,
|
||||
fileNames,
|
||||
options,
|
||||
rawText,
|
||||
resolvedUploadDisposition,
|
||||
reviewAction,
|
||||
systemGenerated,
|
||||
userText
|
||||
})) {
|
||||
return null
|
||||
}
|
||||
|
||||
const hasUnsavedReviewDraft = Boolean(
|
||||
!isKnowledgeSession.value &&
|
||||
files.length &&
|
||||
|
||||
Reference in New Issue
Block a user