From d660a961fbabd4fbc4b5453f44790a746824b157 Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Sun, 21 Jun 2026 23:24:16 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20AI=20=E5=B7=A5=E4=BD=9C=E5=8F=B0?= =?UTF-8?q?=E9=99=84=E4=BB=B6=E6=94=B9=E4=B8=BA=E5=8D=A1=E7=89=87=E5=8C=96?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=B9=B6=E6=94=AF=E6=8C=81=E5=8D=95=E9=A1=B9?= =?UTF-8?q?=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PersonalWorkbenchAiMode 附件区由计数条改为按类型图标/名称/类型标签的卡片列表,支持单项移除(removeAiModeFile),复用 buildFileIdentity 作为 key - resolveAiComposerFileType 按 pdf/图片/表格/文档/压缩包/文件归类,分别对应图标与色调 - .gitignore 补充忽略 server/storage/receipt_folder/ 运行时票据存储目录 --- .gitignore | 1 + .../business/PersonalWorkbenchAiMode.vue | 93 +++++++++++++++++-- 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 9f392c3..9e9e889 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ server/.secrets/ server/logs/ server/storage/expense_claims/ server/storage/finance_reports/ +server/storage/receipt_folder/ test-results/ .codex-remote-attachments/ tmp-*.png diff --git a/web/src/components/business/PersonalWorkbenchAiMode.vue b/web/src/components/business/PersonalWorkbenchAiMode.vue index d578f16..4fae149 100644 --- a/web/src/components/business/PersonalWorkbenchAiMode.vue +++ b/web/src/components/business/PersonalWorkbenchAiMode.vue @@ -175,9 +175,25 @@ -
- 已选择 {{ selectedFiles.length }} 份附件 - +
+
+ + + {{ file.name }} + {{ file.typeLabel }} + + +
@@ -469,9 +485,25 @@
-
- 已选择 {{ selectedFiles.length }} 份附件 - +
+
+ + + {{ file.name }} + {{ file.typeLabel }} + + +
@@ -742,6 +774,7 @@ import { buildRequiredApplicationSelectionText, filterRequiredApplicationCandidates } from '../../views/scripts/travelReimbursementApplicationLinkModel.js' +import { buildFileIdentity } from '../../views/scripts/travelReimbursementAttachmentModel.js' import { calculateTravelReimbursement, extractExpenseClaimItems, @@ -787,6 +820,14 @@ const INLINE_ANSWER_STREAM_CHUNK_SIZE = 6 const INLINE_ANSWER_STREAM_DELAY_MS = 24 const INLINE_AUTO_SCROLL_THRESHOLD = 96 const INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS = 260 +const AI_COMPOSER_FILE_TYPE_META = { + pdf: { label: 'PDF', icon: 'mdi mdi-file-pdf-box', tone: 'pdf' }, + image: { label: '图片', icon: 'mdi mdi-file-image-outline', tone: 'image' }, + spreadsheet: { label: '表格', icon: 'mdi mdi-file-excel-outline', tone: 'spreadsheet' }, + document: { label: '文档', icon: 'mdi mdi-file-document-outline', tone: 'document' }, + archive: { label: '压缩包', icon: 'mdi mdi-folder-zip-outline', tone: 'archive' }, + file: { label: '文件', icon: 'mdi mdi-file-outline', tone: 'file' } +} const { applicationPreviewEditor, resolveApplicationPreviewEditorControl, @@ -854,6 +895,12 @@ const aiModeActionItems = [ } ] +const selectedFileCards = computed(() => selectedFiles.value.map((file) => ({ + key: buildFileIdentity(file), + name: resolveAiComposerFileName(file), + ...resolveAiComposerFileType(file) +}))) + const displayUserName = computed(() => { const user = currentUser.value || {} return String(user.name || user.username || '同事').trim() || '同事' @@ -2815,6 +2862,32 @@ function markInlineMessageFeedback(message, feedback) { toast(feedback === 'up' ? '已记录有帮助反馈。' : '已记录需要改进反馈。') } +function resolveAiComposerFileName(file) { + return String(file?.name || '未命名附件').trim() || '未命名附件' +} + +function resolveAiComposerFileType(file) { + const fileName = resolveAiComposerFileName(file).toLowerCase() + const mimeType = String(file?.type || '').toLowerCase() + const extension = fileName.includes('.') ? fileName.split('.').pop() : '' + if (extension === 'pdf' || mimeType.includes('pdf')) { + return AI_COMPOSER_FILE_TYPE_META.pdf + } + if (/^(png|jpe?g|gif|webp|bmp|svg|heic)$/.test(extension) || mimeType.startsWith('image/')) { + return AI_COMPOSER_FILE_TYPE_META.image + } + if (/^(xls|xlsx|csv|numbers)$/.test(extension) || mimeType.includes('spreadsheet') || mimeType.includes('excel')) { + return AI_COMPOSER_FILE_TYPE_META.spreadsheet + } + if (/^(doc|docx|txt|md|pages)$/.test(extension) || mimeType.includes('word') || mimeType.includes('text')) { + return AI_COMPOSER_FILE_TYPE_META.document + } + if (/^(zip|rar|7z|tar|gz)$/.test(extension) || mimeType.includes('zip') || mimeType.includes('compressed')) { + return AI_COMPOSER_FILE_TYPE_META.archive + } + return AI_COMPOSER_FILE_TYPE_META.file +} + function triggerAiModeFileUpload() { if (isAiModeInputLocked.value) { toast('请等待费用测算完成后再继续操作。') @@ -2831,6 +2904,14 @@ function handleAiModeFilesChange(event) { focusAiModeInput() } +function removeAiModeFile(fileKey) { + selectedFiles.value = selectedFiles.value.filter((file) => buildFileIdentity(file) !== fileKey) + if (!selectedFiles.value.length && fileInputRef.value) { + fileInputRef.value.value = '' + } + focusAiModeInput() +} + function clearAiModeFiles() { selectedFiles.value = [] if (fileInputRef.value) {