feat(web): 工作台 AI 模式报销预审与文档查询模型拆分

- 新增 aiApplicationPrecheckModel/aiDocumentQueryModel/aiApplicationPreviewActions/aiConversationHtmlRenderer 四个独立模型与服务,按职责从主组件拆出
- PersonalWorkbenchAiMode 接入拆分后的预审、文档查询与 HTML 渲染逻辑,配合 markdown 工具增强结构化展示
- 文档中心与归档筛选、风险可见性、申请预览等工具同步适配,补充对应单元测试
- 新增 AI 文档卡片背景资源
This commit is contained in:
caoxiaozhu
2026-06-20 10:17:37 +08:00
parent 3d69f8501f
commit 304bbe1fd4
26 changed files with 3974 additions and 117 deletions

View File

@@ -258,6 +258,7 @@ import EnterprisePagination from '../components/shared/EnterprisePagination.vue'
import TableEmptyState from '../components/shared/TableEmptyState.vue'
import TableLoadingState from '../components/shared/TableLoadingState.vue'
import { useMinimumVisibleState } from '../composables/useMinimumVisibleState.js'
import { useSystemState } from '../composables/useSystemState.js'
import { mapExpenseClaimToRequest } from '../composables/useRequests.js'
import {
extractExpenseClaimItems,
@@ -377,6 +378,8 @@ const emit = defineEmits([
'summary-change'
])
const { currentUser } = useSystemState()
function readDocumentCenterQueryText(key) {
const value = route.query?.[key]
return String(Array.isArray(value) ? value[0] || '' : value || '').trim()
@@ -781,7 +784,12 @@ function resolveDocumentRiskFlags(row) {
function buildDocumentRiskMeta(row) {
const riskFlags = resolveDocumentRiskFlags(row)
const riskSummary = row?.riskSummary || row?.risk
const count = countClaimRisks(riskFlags, riskSummary)
// 列表风险标签按当前查看者可见性过滤,与详情页口径一致:
// 申请人看不到的预算治理等风险不计入列表展示的风险等级。
const viewerOptions = currentUser.value
? { request: row || {}, currentUser: currentUser.value }
: null
const count = countClaimRisks(riskFlags, riskSummary, viewerOptions)
if (!count) {
const meta = RISK_TONE_META.none
return {
@@ -791,7 +799,7 @@ function buildDocumentRiskMeta(row) {
}
}
const tone = resolveArchiveRiskTone(riskFlags, riskSummary)
const tone = resolveArchiveRiskTone(riskFlags, riskSummary, viewerOptions)
const meta = RISK_TONE_META[tone] || RISK_TONE_META.medium
return {
...meta,

View File

@@ -6,6 +6,7 @@
:sidebar-command="aiSidebarCommand"
@conversation-change="emit('ai-conversation-change', $event)"
@conversation-history-change="emit('ai-conversation-history-change', $event)"
@open-document="emit('open-document', $event)"
/>
<PersonalWorkbench
v-else

View File

@@ -1683,7 +1683,7 @@ export default {
)
)
|| (!isEditableRequest.value && canViewApprovalRiskAdvice.value && aiAdvice.value.riskCards.length > 0)
|| (!isEditableRequest.value && isCurrentApplicant.value && !isApplicationDocument.value && hasVisibleRiskCards.value)
|| (!isEditableRequest.value && isCurrentApplicant.value && hasVisibleRiskCards.value)
))
function normalizeRiskDomId(value) {
@@ -1750,21 +1750,24 @@ export default {
}
const aiAdviceTitle = computed(() => {
if (!isEditableRequest.value && isCurrentApplicant.value && !isApplicationDocument.value) {
return '风险提示'
if (!isEditableRequest.value && isCurrentApplicant.value) {
return isApplicationDocument.value ? '申请风险提示' : '风险提示'
}
if (isEditableRequest.value && isApplicationDocument.value) {
return '表单自查提示'
}
return isEditableRequest.value ? 'AI建议' : '风险提示'
})
const aiAdviceHint = computed(() => (
!isEditableRequest.value && isCurrentApplicant.value && !isApplicationDocument.value
? '展示票据、行程、金额等可自行修正的风险点,便于提交人先整改,减少后续退单。'
: isEditableRequest.value
? (isApplicationDocument.value ? '仅提示申请表单本身需要补充的内容,不展示预算治理细节。' : '系统会在草稿保存和附件识别后自动更新检测结果。')
: '展示系统已识别的风险点,便于审批和后续整改。'
))
const aiAdviceHint = computed(() => {
if (!isEditableRequest.value && isCurrentApplicant.value) {
return isApplicationDocument.value
? '展示申请单已识别的风险点及原因,请逐条确认或补充说明后再提交给领导审批。'
: '展示票据、行程、金额等可自行修正的风险点,便于提交人先整改,减少后续退单。'
}
return isEditableRequest.value
? (isApplicationDocument.value ? '仅提示申请表单本身需要补充的内容,不展示预算治理细节。' : '系统会在草稿保存和附件识别后自动更新检测结果。')
: '展示系统已识别的风险点,便于审批和后续整改。'
})
const submitActionLabel = computed(() => {
return resolveSubmitActionLabel({

View File

@@ -252,6 +252,7 @@ export function useApplicationPreviewEditor({
resolveApplicationPreviewRows,
resolveApplicationPreviewEditorControl,
resolveApplicationPreviewEditorOptions,
refreshApplicationPreviewEstimate,
isApplicationPreviewEditing,
isApplicationPreviewDateEditorOpen,
openApplicationPreviewEditor,