feat: 报销审批流重构与管家计划全链路贯通

- 重构报销状态注册表、审批流路由与平台风险标记
- 完善管家意图规划器与模型计划构建器全链路
- 新增 OCR Worker 脚本、数据库会话管理与通知状态
- 优化文档中心、日志视图、预算中心与员工管理交互
- 增强工作台摘要、图标资源与全局主题样式
- 补充审批路由、状态注册、OCR 服务与管家规划器测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-06 17:19:07 +08:00
parent f60cebadb8
commit e124e4bbcb
162 changed files with 9161 additions and 1941 deletions

View File

@@ -51,11 +51,6 @@ const chatViewTemplate = readFileSync(
fileURLToPath(new URL('../src/views/ChatView.vue', import.meta.url)),
'utf8'
)
const operationFeedbackInlineTemplate = readFileSync(
fileURLToPath(new URL('../src/components/shared/OperationFeedbackInlineCard.vue', import.meta.url)),
'utf8'
)
test('application and reimbursement entries open the same financial assistant modal', () => {
assert.match(appShellRouteView, /<TravelReimbursementCreateView[\s\S]*:entry-source="smartEntryContext\.source"/)
assert.match(appShellRouteView, /@create-request="openTravelCreate"/)
@@ -134,8 +129,10 @@ test('application edit prefill opens assistant without auto submit', () => {
)
})
test('financial assistant toolbar renders four isolated assistant sessions', () => {
test('financial assistant toolbar renders isolated assistant sessions without steward entry', () => {
assert.match(assistantScript, /filterAssistantSessionModes\(ASSISTANT_SESSION_MODE_OPTIONS, currentUser\.value\)/)
assert.match(assistantScript, /\.filter\(\(mode\) => mode\.key !== SESSION_TYPE_STEWARD\)/)
assert.match(assistantScript, /mode\.key === SESSION_TYPE_BUDGET/)
assert.match(assistantScript, /visibleModes\.map/)
assert.match(assistantScript, /targetSessionType:\s*mode\.key/)
assert.match(assistantScript, /active:\s*mode\.key === activeSessionType\.value/)
@@ -199,38 +196,39 @@ test('assistant message meta hides internal routing and permission chips', () =>
assert.doesNotMatch(chatViewTemplate, /agent-meta-row|agent-meta-chip/)
})
test('assistant operation feedback is inline and persists run context', () => {
test('assistant message action toolbar collects lightweight feedback', () => {
assert.doesNotMatch(appShellRouteView, /<OperationFeedbackDialog/)
assert.doesNotMatch(appShellRouteView, /@operation-completed="handleOperationCompleted"/)
assert.doesNotMatch(appShellComposable, /useOperationFeedback/)
assert.match(messageItemTemplate, /<OperationFeedbackInlineCard/)
assert.match(messageItemTemplate, /class="message-feedback-bubble"/)
assert.match(messageItemTemplate, /:submitted="Boolean\(message\.operationFeedback\?\.submitted\)"/)
assert.match(messageItemTemplate, /:submitted-rating="Number\(message\.operationFeedback\?\.rating \|\| 0\)"/)
assert.doesNotMatch(messageItemTemplate, /<OperationFeedbackInlineCard/)
assert.match(messageItemTemplate, /class="message-action-toolbar"/)
assert.match(messageItemTemplate, /ui\.shouldShowAssistantMessageActions\(message\)/)
assert.match(messageItemTemplate, /ui\.copyAssistantMessage\(message\)/)
assert.match(messageItemTemplate, /ui\.speakAssistantMessage\(message\)/)
assert.match(messageItemTemplate, /rating:\s*5,\s*reason:\s*'thumbs_up'/)
assert.match(messageItemTemplate, /rating:\s*1,\s*reason:\s*'thumbs_down'/)
assert.match(messageItemTemplate, /mdi mdi-content-copy/)
assert.match(messageItemTemplate, /mdi mdi-volume-high/)
assert.match(messageItemTemplate, /mdi mdi-thumb-up-outline/)
assert.match(messageItemTemplate, /mdi mdi-thumb-down-outline/)
assert.match(assistantScript, /emits:\s*\['close', 'draft-saved', 'request-updated'\]/)
assert.match(appShellRouteView, /@request-updated="handleRequestUpdated"/)
assert.match(assistantScript, /function buildMessageOperationFeedbackContext/)
assert.match(assistantScript, /source:\s*'assistant_message_action'/)
assert.match(assistantScript, /operation_type:\s*message\?\.stewardPlan \? 'steward_message' : 'assistant_message'/)
assert.match(assistantScript, /function shouldShowAssistantMessageActions/)
assert.match(assistantScript, /function copyAssistantMessage/)
assert.match(assistantScript, /function speakAssistantMessage/)
assert.match(assistantScript, /function isMessageFeedbackSelected/)
assert.match(assistantScript, /function submitOperationFeedbackForMessage/)
assert.match(assistantScript, /createOperationFeedback/)
assert.match(assistantScript, /normalizeOperationFeedbackContext/)
assert.match(assistantScript, /&& !feedback\.dismissed/)
assert.doesNotMatch(assistantScript, /&& !feedback\.submitted/)
assert.match(assistantScript, /submitted:\s*true/)
assert.match(assistantScript, /dismissed:\s*false/)
assert.doesNotMatch(assistantScript, /emit\('operation-completed'/)
assert.match(assistantSubmitComposerScript, /emitOperationCompleted\?\.\(payload/)
assert.match(assistantSubmitComposerScript, /operationFeedback:\s*buildOperationFeedbackState/)
assert.match(assistantSubmitComposerScript, /rating:\s*0/)
assert.match(operationFeedbackInlineTemplate, /v-for="option in ratingOptions"/)
assert.match(operationFeedbackInlineTemplate, /is-submitted/)
assert.match(operationFeedbackInlineTemplate, /submittedRating/)
assert.match(operationFeedbackInlineTemplate, /感谢您的反馈。谢谢/)
assert.match(operationFeedbackInlineTemplate, /busy \|\| submitted/)
assert.match(operationFeedbackInlineTemplate, /role="radiogroup"/)
assert.match(operationFeedbackInlineTemplate, /handleRatingKeydown/)
assert.match(operationFeedbackInlineTemplate, /operation-feedback-stars/)
assert.match(operationFeedbackInlineTemplate, /score > 3/)
assert.match(operationFeedbackInlineTemplate, /v-if="showReasonInput"/)
assert.match(operationFeedbackInlineTemplate, /稍后/)
const context = normalizeOperationFeedbackContext(
{