From e7b4dbf658552ce7a66caa742bd55a75ac4d82e5 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 08 五月 2026 14:54:04 +0800
Subject: [PATCH] refactor(AIChatSidebar): 重构助理配置结构
---
src/components/AIChatSidebar/index.vue | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 106 insertions(+), 12 deletions(-)
diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
index 09632d6..a5dcf0b 100644
--- a/src/components/AIChatSidebar/index.vue
+++ b/src/components/AIChatSidebar/index.vue
@@ -191,7 +191,8 @@
<div
v-for="(file, fileIndex) in message.localUploadFiles"
:key="`${file.previewId || file.name}-${fileIndex}`"
- class="message-local-file-item"
+ :class="['message-local-file-item', { clickable: !!file.accessUrl && !file.isImage }]"
+ @click="handleMessageFileClick(file)"
>
<el-image
v-if="file.isImage && file.previewUrl"
@@ -205,8 +206,14 @@
/>
<el-icon v-else class="message-local-file-icon"><Document /></el-icon>
<div class="message-local-file-meta">
- <span class="message-local-file-name">{{ file.name }}</span>
- <small class="message-local-file-size">{{ formatFileSize(file.size) }}</small>
+ <span
+ :class="['message-local-file-name', { clickable: !!file.accessUrl }]"
+ :title="file.name"
+ @click.stop="openMessageAttachment(file)"
+ >
+ {{ file.name }}
+ </span>
+ <small v-if="Number(file.size) > 0" class="message-local-file-size">{{ formatFileSize(file.size) }}</small>
</div>
</div>
</div>
@@ -773,6 +780,34 @@
const loadingSessions = ref(false)
const isImageFileType = (fileType = '') => String(fileType || '').toLowerCase().startsWith('image/')
+const imageFilePathPattern = /\.(png|jpe?g|gif|webp|bmp|svg)$/i
+
+const getPathnameFromFilePath = (filePath = '') => {
+ const rawPath = String(filePath || '').trim()
+ if (!rawPath) return ''
+ try {
+ const baseOrigin = typeof window !== 'undefined' ? window.location.origin : 'http://localhost'
+ return new URL(rawPath, baseOrigin).pathname || ''
+ } catch (err) {
+ return rawPath.split('?')[0]
+ }
+}
+
+const isImageFilePath = (filePath = '') => {
+ const pathname = getPathnameFromFilePath(filePath).toLowerCase()
+ return imageFilePathPattern.test(pathname)
+}
+
+const getHistoryFileName = (filePath = '', index = 0) => {
+ const pathname = getPathnameFromFilePath(filePath)
+ const fileName = pathname.split('/').filter(Boolean).pop()
+ if (!fileName) return `file-${index + 1}`
+ try {
+ return decodeURIComponent(fileName)
+ } catch (err) {
+ return fileName
+ }
+}
const getImagePreviewList = (files = []) => {
if (!Array.isArray(files)) return []
@@ -800,14 +835,34 @@
const fileType = rawFile?.type || ''
const isImage = isImageFileType(fileType)
const canCreateObjectURL = typeof URL !== 'undefined' && typeof URL.createObjectURL === 'function'
+ const previewUrl = isImage && rawFile && canCreateObjectURL ? URL.createObjectURL(rawFile) : ''
return {
previewId: `${rawFile?.name || 'file'}-${rawFile?.size || 0}-${rawFile?.lastModified || Date.now()}-${index}`,
name: rawFile?.name || `file-${index + 1}`,
size: rawFile?.size || 0,
type: fileType,
isImage,
- previewUrl: isImage && rawFile && canCreateObjectURL ? URL.createObjectURL(rawFile) : '',
- rawFile
+ previewUrl,
+ accessUrl: '',
+ rawFile,
+ isObjectUrl: !!previewUrl
+ }
+}
+
+const createHistoryFileSnapshot = (filePath, memoryId = '', messageIndex = 0, fileIndex = 0) => {
+ const normalizedPath = String(filePath || '').trim()
+ if (!normalizedPath) return null
+ const isImage = isImageFilePath(normalizedPath)
+ return {
+ previewId: `${memoryId || 'history'}-${messageIndex}-${fileIndex}`,
+ name: getHistoryFileName(normalizedPath, fileIndex),
+ size: 0,
+ type: '',
+ isImage,
+ previewUrl: isImage ? normalizedPath : '',
+ accessUrl: normalizedPath,
+ rawFile: null,
+ isObjectUrl: false
}
}
@@ -816,10 +871,29 @@
if (!canRevokeObjectURL) return
if (!Array.isArray(snapshots)) return
snapshots.forEach((snapshot) => {
- if (snapshot?.previewUrl) {
+ if (snapshot?.isObjectUrl && snapshot?.previewUrl) {
URL.revokeObjectURL(snapshot.previewUrl)
}
})
+}
+
+const mapHistoryFilePathsToSnapshots = (filePaths = [], memoryId = '', messageIndex = 0) => {
+ if (!Array.isArray(filePaths)) return []
+ return filePaths
+ .map((filePath, fileIndex) => createHistoryFileSnapshot(filePath, memoryId, messageIndex, fileIndex))
+ .filter(Boolean)
+}
+
+const openMessageAttachment = (file) => {
+ const accessUrl = String(file?.accessUrl || '').trim()
+ if (!accessUrl) return
+ if (typeof window === 'undefined' || typeof window.open !== 'function') return
+ window.open(accessUrl, '_blank', 'noopener,noreferrer')
+}
+
+const handleMessageFileClick = (file) => {
+ if (!file?.accessUrl || file?.isImage) return
+ openMessageAttachment(file)
}
const revokeMessageLocalFileSnapshots = (messageList = []) => {
@@ -904,7 +978,7 @@
const messageObj = {
isUser,
- content: msg.content,
+ content: msg.content || '',
htmlContent: '',
isTyping: false,
chartOptions: null,
@@ -912,7 +986,8 @@
type: '',
tableData: null,
payloadTreeData: null,
- payloadHiddenData: null
+ payloadHiddenData: null,
+ localUploadFiles: isUser ? mapHistoryFilePathsToSnapshots(msg.filePaths, uuid.value, idx) : []
}
messages.value.push(messageObj)
@@ -927,15 +1002,15 @@
}
// 瑙f瀽鍘嗗彶娑堟伅涓殑 JSON
- const extracted = extractEmbeddedSuccessJson(msg.content)
+ const extracted = extractEmbeddedSuccessJson(msg.content || '')
if (extracted) {
applyStructuredMessageData(messageObj, extracted.data, botMsgIndex)
}
- updateOutputState(msg.content, botMsgIndex)
- messageObj.htmlContent = convertStreamOutput(msg.content, botMsgIndex)
+ updateOutputState(msg.content || '', botMsgIndex)
+ messageObj.htmlContent = convertStreamOutput(msg.content || '', botMsgIndex)
} else {
- messageObj.htmlContent = convertTextToHtml(msg.content)
+ messageObj.htmlContent = convertTextToHtml(msg.content || '')
}
})
scrollToBottom()
@@ -3451,6 +3526,16 @@
border: 1px solid rgba(88, 117, 255, 0.2);
background: rgba(255, 255, 255, 0.9);
max-width: 100%;
+
+ &.clickable {
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ border-color: rgba(44, 109, 255, 0.38);
+ background: rgba(243, 247, 255, 0.96);
+ }
+ }
}
.message-local-file-thumb {
@@ -3489,6 +3574,15 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+
+ &.clickable {
+ color: $primary-blue;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
}
.message-local-file-size {
--
Gitblit v1.9.3