feat(web): AI 工作台文件预览/附件关联任务与草稿分支
- 新增 WorkbenchAiFilePreviewDialog 附件预览对话框及 useWorkbenchAiFilePreview,附件支持点击预览 - 新增 attachmentAssociationJobs/linkedReimbursementDraftJobs 前端服务与对应 composable,接入后台任务轮询与状态展示 - 新增 travelReimbursementDraftBranchModel 草稿分支模型,报销关联门控支持跳过/选择草稿 - PersonalWorkbenchAiMode 及各 composable(expense/document/steward/application-preview/attachment-association)重构适配,WorkbenchAiComposer/FileStrip 样式与交互完善 - DocumentsCenter/ReceiptFolder/TravelReimbursementCreate 等视图及 scripts 重构,风险/差旅规划/审批等工具适配 - 新增/更新前端测试:application-result-card、reimbursement-list-preview-fetch、guided-flow、composer-components 等
This commit is contained in:
@@ -9,9 +9,13 @@ function readSource(path) {
|
||||
|
||||
const aiModeComponent = readSource('../src/components/business/PersonalWorkbenchAiMode.vue')
|
||||
const aiModeTemplate = readSource('../src/components/business/PersonalWorkbenchAiMode.template.html')
|
||||
const aiModeStyles = readSource('../src/assets/styles/components/personal-workbench-ai-mode.css')
|
||||
const composerComponent = readSource('../src/components/business/workbench-ai/WorkbenchAiComposer.vue')
|
||||
const fileStripComponent = readSource('../src/components/business/workbench-ai/WorkbenchAiFileStrip.vue')
|
||||
const filePreviewComponent = readSource('../src/components/business/workbench-ai/WorkbenchAiFilePreviewDialog.vue')
|
||||
const filePreviewStyles = readSource('../src/assets/styles/components/workbench-ai-file-preview-dialog.css')
|
||||
const aiModeRuntime = readSource('../src/composables/workbenchAiMode/usePersonalWorkbenchAiMode.js')
|
||||
const filePreviewRuntime = readSource('../src/composables/workbenchAiMode/useWorkbenchAiFilePreview.js')
|
||||
|
||||
function countOccurrences(source, pattern) {
|
||||
return source.match(pattern)?.length || 0
|
||||
@@ -44,3 +48,57 @@ test('shared workbench file strip preserves OCR status badges', () => {
|
||||
assert.match(fileStripComponent, /mdi mdi-text-recognition/)
|
||||
assert.match(fileStripComponent, /:title="file\.ocrState\.title \|\| file\.ocrState\.label"/)
|
||||
})
|
||||
|
||||
test('AI mode primes attachment OCR synchronously after file selection', () => {
|
||||
assert.match(
|
||||
filePreviewRuntime,
|
||||
/watch\(\s*selectedFiles,\s*\(files(?:,\s*previousFiles\s*=\s*\[\])?\)\s*=>\s*\{[\s\S]*attachmentFlow\.primeAiModeReceiptContext\(files\)[\s\S]*\},\s*\{\s*flush:\s*'sync'\s*\}\s*\)/
|
||||
)
|
||||
})
|
||||
|
||||
test('AI mode keeps conversation anchored above selected attachments', () => {
|
||||
assert.match(
|
||||
filePreviewRuntime,
|
||||
/watch\(\s*selectedFiles,\s*\(files,\s*previousFiles\s*=\s*\[\]\)\s*=>\s*\{[\s\S]*const fileCountChanged = files\.length !== previousFiles\.length[\s\S]*scrollInlineConversationToBottom\(\{\s*force:\s*true\s*\}\)[\s\S]*\},\s*\{\s*flush:\s*'sync'\s*\}\s*\)/
|
||||
)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-conversation-bottom\s*\{[\s\S]*gap:\s*14px;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-thread\s*\{[\s\S]*scroll-padding-bottom:\s*42px;/)
|
||||
})
|
||||
|
||||
test('AI mode lays selected attachments in a horizontal scroll strip', () => {
|
||||
assert.match(aiModeStyles, /\.workbench-ai-file-strip\s*\{[\s\S]*flex-wrap:\s*nowrap;[\s\S]*overflow-x:\s*auto;[\s\S]*overflow-y:\s*hidden;/)
|
||||
assert.match(aiModeStyles, /\.workbench-ai-file-card\s*\{[\s\S]*flex:\s*0 0 312px;/)
|
||||
assert.match(fileStripComponent, /role="button"/)
|
||||
assert.match(fileStripComponent, /@click="runtime\.openAiModeFilePreview\(file\.key\)"/)
|
||||
assert.match(fileStripComponent, /@click\.stop="runtime\.removeAiModeFile\(file\.key\)"/)
|
||||
})
|
||||
|
||||
test('AI mode attachment preview opens a split source and recognition dialog', () => {
|
||||
assert.match(aiModeComponent, /import WorkbenchAiFilePreviewDialog from '\.\/workbench-ai\/WorkbenchAiFilePreviewDialog\.vue'/)
|
||||
assert.match(aiModeTemplate, /<WorkbenchAiFilePreviewDialog :runtime="workbenchAiRuntime" \/>/)
|
||||
assert.match(aiModeRuntime, /useWorkbenchAiFilePreview\(/)
|
||||
assert.match(aiModeRuntime, /\.\.\.filePreview,/)
|
||||
assert.match(filePreviewRuntime, /URL\.createObjectURL\(rawFile\)/)
|
||||
assert.match(filePreviewRuntime, /attachmentFlow\.resolveAiModeReceiptRecognitionState\(rawFile\)/)
|
||||
assert.match(filePreviewComponent, /class="workbench-ai-file-preview-source"/)
|
||||
assert.match(filePreviewComponent, /class="workbench-ai-file-preview-insight"/)
|
||||
assert.match(filePreviewComponent, /<img[\s\S]*v-if="preview\.sourceKind === 'image'"/)
|
||||
assert.match(filePreviewComponent, /<iframe[\s\S]*v-else-if="preview\.sourceKind === 'pdf'"/)
|
||||
assert.match(filePreviewComponent, /v-for="field in preview\.ocrFields"/)
|
||||
})
|
||||
|
||||
test('AI mode attachment preview centers inside the main content area beside the sidebar', () => {
|
||||
assert.match(filePreviewStyles, /--workbench-ai-preview-sidebar-offset:\s*var\(--sidebar-expanded-width,\s*304px\);/)
|
||||
assert.match(
|
||||
filePreviewStyles,
|
||||
/\.workbench-ai-file-preview-mask\s*\{[\s\S]*grid-template-columns:\s*var\(--workbench-ai-preview-sidebar-offset\) minmax\(0,\s*1fr\);/
|
||||
)
|
||||
assert.match(
|
||||
filePreviewStyles,
|
||||
/\.workbench-ai-file-preview-dialog\s*\{[\s\S]*grid-column:\s*2;[\s\S]*justify-self:\s*center;/
|
||||
)
|
||||
assert.match(
|
||||
filePreviewStyles,
|
||||
/@media \(max-width:\s*900px\)\s*\{[\s\S]*--workbench-ai-preview-sidebar-offset:\s*0px;[\s\S]*grid-template-columns:\s*minmax\(0,\s*1fr\);/
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user