feat: 增强知识库索引与设置页面模块化拆分

扩展知识库索引任务和 RAG 检索支持增量入库和文档去重,优
化本体检测和规则匹配精度,前端设置页面拆分为 LLM、邮件
和 Hermes 员工同步子面板并重构样式,新增日志详情组件和
知识入库日志模型,补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-22 23:47:28 +08:00
parent 88ff04bef8
commit 5b388d08c0
84 changed files with 10170 additions and 2599 deletions

View File

@@ -93,8 +93,11 @@ import {
buildReviewStateLabel,
buildReviewStateTone,
buildReviewPlainFollowupCopy,
buildReviewNextStepRichCopy,
buildReviewRiskLevelCounts,
resolveReviewFooterActions,
resolveReviewSaveDraftAction,
resolveReviewNextStepAction,
buildReviewPrimaryButtonLabel,
buildReviewIntentText,
buildReviewSceneValue,
@@ -129,6 +132,7 @@ import {
buildOcrSummaryFromDocuments,
buildReviewFilePreviewsFromReviewPayload,
extractReviewAttachmentNames,
isTemporaryPreviewUrl,
mergeFilePreviews,
mergeFilesWithLimit,
mergeUploadAttachmentNames,
@@ -169,6 +173,11 @@ const REVIEW_RISK_LEVEL_META = {
icon: 'mdi mdi-alert-circle-outline',
suggestion: '提交前建议核对业务真实性、附件完整性和规则口径。'
},
info: {
label: '提示',
icon: 'mdi mdi-information-outline',
suggestion: '该项主要用于辅助判断,可结合当前单据情况简单核对。'
},
low: {
label: '低风险',
icon: 'mdi mdi-information-outline',
@@ -184,6 +193,9 @@ const REVIEW_DRAWER_MODE_FLOW = 'flow'
const REVIEW_PANEL_SCOPE_OVERVIEW = 'overview'
const REVIEW_PANEL_SCOPE_DOCUMENTS = 'documents'
const REVIEW_PANEL_SCOPE_RISK = 'risk'
const REVIEW_NEXT_STEP_HREF = '#review-next-step'
const REVIEW_RISK_PANEL_HREF_PREFIX = '#review-risk'
const REVIEW_QUICK_EDIT_HREF = '#review-quick-edit'
const FLOW_STEP_STATUS_PENDING = 'pending'
const FLOW_STEP_STATUS_RUNNING = 'running'
const FLOW_STEP_STATUS_COMPLETED = 'completed'
@@ -326,15 +338,6 @@ function buildReviewFactCards(reviewPayload, inlineState = createEmptyInlineRevi
modelKey: 'scene_label',
placeholder: '请选择场景'
},
{
key: 'customer_name',
label: '关联客户',
value: String(inlineState.customer_name || '').trim() || '待补充',
icon: 'mdi mdi-domain',
editor: 'text',
modelKey: 'customer_name',
placeholder: '请输入客户名称'
},
{
key: 'attachments',
label: '票据状态',
@@ -346,8 +349,20 @@ function buildReviewFactCards(reviewPayload, inlineState = createEmptyInlineRevi
}
]
if (shouldShowReviewFactCard(reviewPayload, 'customer_name', inlineState.customer_name)) {
cards.splice(cards.length - 1, 0, {
key: 'customer_name',
label: '关联客户',
value: String(inlineState.customer_name || '').trim() || '待补充',
icon: 'mdi mdi-domain',
editor: 'text',
modelKey: 'customer_name',
placeholder: '请输入客户名称'
})
}
if (shouldShowReviewFactCard(reviewPayload, 'location', inlineState.location)) {
cards.splice(4, 0, {
cards.splice(cards.length - 1, 0, {
key: 'location',
label: '业务地点',
value: String(inlineState.location || '').trim() || '待补充',
@@ -432,18 +447,19 @@ function buildReviewRiskConversationText(item, detailTarget = {}) {
const summary = String(item?.summary || '').trim()
const detail = String(item?.detail || '').trim()
const suggestion = String(item?.suggestion || '').trim()
const isInfo = String(item?.level || '').trim() === 'info'
const detailHref = String(detailTarget?.href || '').trim()
const detailLabel = String(detailTarget?.label || '').trim() || '进入该单据详情重新填写'
const lines = [`${title}`]
if (summary) {
lines.push('', `风险点${summary}`)
lines.push('', `${isInfo ? '提示内容' : '风险点'}${summary}`)
}
if (detail && detail !== summary) {
lines.push('', `规则依据:${detail}`)
}
if (suggestion) {
lines.push('', `修改建议${suggestion}`)
lines.push('', `${isInfo ? '处理建议' : '修改建议'}${suggestion}`)
}
if (detailHref) {
lines.push('', `[${detailLabel}](${detailHref})`)
@@ -539,6 +555,11 @@ export default {
getSessionRuntimeRefs: () => sessionRuntimeRefs
})
const deleteSessionDialogOpen = ref(false)
const nextStepConfirmDialog = ref({
open: false,
message: null,
action: null
})
const reviewActionBusy = ref(false)
const deleteSessionBusy = ref(false)
const reviewDrawerMode = ref(REVIEW_DRAWER_MODE_REVIEW)
@@ -839,6 +860,7 @@ export default {
extractReviewAttachmentNames,
mergeFilesWithLimit,
mergeFilePreviews,
isTemporaryPreviewUrl,
resolveAttachmentPreviewKind,
resolveDocumentPreview,
buildFilePreviews,
@@ -1418,9 +1440,10 @@ export default {
nextTick(scrollToBottom)
}
function resolveReviewRiskDetailTarget() {
function resolveReviewDetailTarget(message = null) {
const latestDraftMessage = [...messages.value].reverse().find((item) => item?.draftPayload)
const candidates = [
message?.draftPayload,
currentInsight.value.agent?.draftPayload,
latestReviewMessage.value?.draftPayload,
latestDraftMessage?.draftPayload,
@@ -1443,6 +1466,74 @@ export default {
}
}
function resolveReviewRiskDetailTarget() {
return resolveReviewDetailTarget()
}
function buildReviewNextStepRichCopyForMessage(message) {
const target = resolveReviewDetailTarget(message)
return buildReviewNextStepRichCopy(message?.reviewPayload, {
detailHref: target.href || ''
})
}
function buildMessageBubbleClass(message) {
if (message?.role !== 'assistant' || !resolveReviewNextStepAction(message?.reviewPayload)) {
return ''
}
const counts = buildReviewRiskLevelCounts(message.reviewPayload)
if (counts.high > 0) {
return 'message-bubble-review-risk-high'
}
if (counts.medium > 0) {
return 'message-bubble-review-risk-medium'
}
if (counts.low > 0) {
return 'message-bubble-review-risk-low'
}
return ''
}
function openReviewNextStepConfirm(message) {
const action = resolveReviewNextStepAction(message?.reviewPayload)
if (!action) {
return
}
nextStepConfirmDialog.value = {
open: true,
message,
action
}
}
function closeReviewNextStepConfirm() {
if (reviewActionBusy.value) {
return
}
nextStepConfirmDialog.value = {
open: false,
message: null,
action: null
}
}
async function confirmReviewNextStepSubmit() {
const message = nextStepConfirmDialog.value.message
const action = nextStepConfirmDialog.value.action
if (!message || !action || reviewActionBusy.value) {
return
}
try {
await handleReviewActionInternal(message, action)
} finally {
nextStepConfirmDialog.value = {
open: false,
message: null,
action: null
}
}
}
function isWorkbenchBusy() {
return submitting.value || reviewActionBusy.value || sessionSwitchBusy.value
}
@@ -1665,6 +1756,31 @@ export default {
}
const href = String(anchor.getAttribute('href') || '').trim()
if (href === REVIEW_NEXT_STEP_HREF) {
event.preventDefault()
openReviewNextStepConfirm(message)
return
}
if (href.startsWith(REVIEW_RISK_PANEL_HREF_PREFIX)) {
event.preventDefault()
if (reviewRiskDrawerAvailable.value) {
switchReviewDrawerMode(REVIEW_DRAWER_MODE_RISK)
} else {
toast('当前没有需要额外处理的风险信息。')
}
return
}
if (href === REVIEW_QUICK_EDIT_HREF) {
event.preventDefault()
if (reviewOverviewDrawerAvailable.value) {
switchReviewDrawerMode(REVIEW_DRAWER_MODE_REVIEW)
toast('已打开右侧核对信息,可以直接修改当前单据。')
}
return
}
if (href.startsWith('/app/')) {
event.preventDefault()
router.push(href)
@@ -1738,12 +1854,12 @@ export default {
reviewDrawerTitle, reviewOverviewDrawerAvailable, reviewDocumentDrawerAvailable, reviewRiskDrawerAvailable, reviewFlowDrawerAvailable, reviewDocumentDrawerLabel, reviewDocumentDrawerIcon, reviewRiskDrawerLabel, reviewRiskDrawerIcon, reviewFlowDrawerLabel, reviewFlowDrawerIcon, activeReviewDocument, activeReviewDocumentIndex, activeReviewDocumentPreview, canPreviewActiveReviewDocument,
reviewIntentText, reviewFactCards, reviewCategoryOptions, reviewOtherCategoryOptions, reviewSelectedOtherCategory, reviewInlineDirty, reviewInlineForm, reviewInlineEditorKey, reviewInlineErrors, reviewOtherCategoryOpen, reviewInlinePendingFiles, DATE_INPUT_FORMAT, REVIEW_SCENE_OTHER_OPTION, REVIEW_SCENE_OPTIONS, REVIEW_OTHER_CATEGORY_OPTIONS,
workbenchVisible, reviewPanelConfidence, reviewRiskSummary, reviewRiskItems, reviewRiskEmpty, recognizedNarratives, reviewRecognitionNotes, reviewDocumentSummaries, reviewDocumentCount, reviewDocumentDirty, reviewHasUnsavedChanges,
travelCalculatorOpen, travelCalculatorBusy, travelCalculatorError, travelCalculatorResult, travelCalculatorForm, travelCalculatorCanSubmit, deleteSessionDialogOpen, reviewActionBusy, deleteSessionBusy, documentPreviewDialog, shortcuts,
resolveReviewMissingSlotCards, resolveReviewRiskBriefs, buildReviewHeadline, buildReviewSubline, buildReviewStateLabel, buildReviewStateTone, buildReviewPlainFollowupCopy, buildReviewPlainFollowupForMessage, resolveReviewFooterActions, resolveReviewSaveDraftAction, buildReviewPrimaryButtonLabel, buildReviewMainMessageText,
travelCalculatorOpen, travelCalculatorBusy, travelCalculatorError, travelCalculatorResult, travelCalculatorForm, travelCalculatorCanSubmit, deleteSessionDialogOpen, nextStepConfirmDialog, reviewActionBusy, deleteSessionBusy, documentPreviewDialog, shortcuts,
resolveReviewMissingSlotCards, resolveReviewRiskBriefs, buildReviewHeadline, buildReviewSubline, buildReviewStateLabel, buildReviewStateTone, buildReviewPlainFollowupCopy, buildReviewPlainFollowupForMessage, buildReviewNextStepRichCopyForMessage, buildMessageBubbleClass, resolveReviewFooterActions, resolveReviewSaveDraftAction, buildReviewPrimaryButtonLabel, buildReviewMainMessageText,
renderMarkdown, buildExpenseQueryWindowLabel, buildExpenseQueryHint, getExpenseQueryActivePage, getExpenseQueryTotalPages, getExpenseQueryVisibleRecords, resolveDocumentPreview, triggerFileUpload, applyComposerDateSelection, handleFilesChange, handleComposerInput, handleComposerEnter, runShortcut, runWelcomeQuickAction: runShortcut, handleSuggestedAction, isSuggestedActionSelected, askHotKnowledgeQuestion, resolveKnowledgeRankLabel, resolveKnowledgeRankTone,
refreshFlowRunDetail, formatFlowStepDuration, resolveFlowStepStatusLabel, resolveFlowStepDetail, toggleInsightPanel, openTravelCalculator, toggleTravelCalculator, closeTravelCalculator, submitTravelCalculator, switchToReviewOverviewDrawer, toggleReviewDocumentDrawer, toggleReviewRiskDrawer, toggleReviewFlowDrawer, toggleAttachedFilesExpanded, removeAttachedFile, clearAttachedFiles,
requestCloseWorkbench, emitCloseAfterLeave, handleAssistantModalAfterEnter, openExpenseQueryRecord, handleExpenseQueryRecordClick, setExpenseQueryPage, shiftExpenseQueryPage, openDeleteSessionDialog, closeDeleteSessionDialog, confirmDeleteCurrentSession, openInlineReviewEditor, closeInlineReviewEditor, commitInlineReviewEditor, clearInlineReviewFieldError, selectInlineScene, selectReviewCategory, selectReviewOtherCategory,
queryDraftByClaimNo, appendReviewRiskBriefToConversation, appendExpenseQueryRiskToConversation, goReviewDocument, openActiveReviewDocumentPreview, closeDocumentPreview, saveInlineReviewChanges, submitComposer, handleAssistantMarkdownClick, handleReviewAction, handleSaveDraftDirectly, isDraftSavedReviewMessage, canUseInlineSaveDraft, handleInlineSaveDraft
queryDraftByClaimNo, appendReviewRiskBriefToConversation, appendExpenseQueryRiskToConversation, goReviewDocument, openActiveReviewDocumentPreview, closeDocumentPreview, saveInlineReviewChanges, submitComposer, handleAssistantMarkdownClick, handleReviewAction, handleSaveDraftDirectly, closeReviewNextStepConfirm, confirmReviewNextStepSubmit, isDraftSavedReviewMessage, canUseInlineSaveDraft, handleInlineSaveDraft
}
}
}