From f81567133f9d558a5d977eeba92af7cf1e23344c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=A8=9C?= Date: Sat, 26 Jul 2025 11:32:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E6=B3=A8=E8=A7=92=E6=A0=87=E5=92=8C?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E7=AE=80=E4=BB=8B=E3=80=81=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=88=97=E8=A1=A8=E5=92=8C=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy/nginx.conf.tmpl | 16 + src/apis/paths/type.ts | 7 + src/assets/svgs/file_download.svg | 13 + src/assets/svgs/file_download_active.svg | 13 + src/assets/svgs/file_download_hover.svg | 13 + src/assets/svgs/html.svg | 28 ++ src/assets/svgs/jpeg.svg | 27 ++ src/assets/svgs/json.svg | 28 ++ src/assets/svgs/png.svg | 28 ++ src/assets/svgs/pptx.svg | 28 ++ src/assets/svgs/txt_green.svg | 28 ++ src/assets/svgs/yaml.svg | 28 ++ src/assets/svgs/zip.svg | 28 ++ .../dialoguePanel/DialoguePanel.vue | 233 +++++++++++++- src/components/sessionCard/SessionCard.vue | 16 +- src/store/conversation.ts | 24 +- .../dialogue/components/DialogueSession.vue | 288 +++++++++++++++++- src/views/dialogue/types.ts | 1 + vite.config.ts | 8 + 19 files changed, 832 insertions(+), 23 deletions(-) create mode 100644 src/assets/svgs/file_download.svg create mode 100644 src/assets/svgs/file_download_active.svg create mode 100644 src/assets/svgs/file_download_hover.svg create mode 100644 src/assets/svgs/html.svg create mode 100644 src/assets/svgs/jpeg.svg create mode 100644 src/assets/svgs/json.svg create mode 100644 src/assets/svgs/png.svg create mode 100644 src/assets/svgs/pptx.svg create mode 100644 src/assets/svgs/txt_green.svg create mode 100644 src/assets/svgs/yaml.svg create mode 100644 src/assets/svgs/zip.svg diff --git a/deploy/nginx.conf.tmpl b/deploy/nginx.conf.tmpl index c2ef1a6..7aefa26 100644 --- a/deploy/nginx.conf.tmpl +++ b/deploy/nginx.conf.tmpl @@ -117,6 +117,22 @@ http { proxy_pass ${FRAMEWORK_URL}/api/; } + + + location /wtd/ { + proxy_set_header X-Forwarded-For $http_x_real_ip; + add_header Cache-Control "no-cache,no-store,must-revalidate"; + add_header X-Accel-Buffering no; + proxy_buffering off; + proxy_intercept_errors on; + + error_page 404 /404.html; + limit_req zone=ratelimit burst=15 nodelay; + proxy_read_timeout 500s; + proxy_connect_timeout 500s; + + proxy_pass ${FRAMEWORK_URL}/witchaind/; + } location /witchaind/ { proxy_set_header X-Forwarded-For $http_x_real_ip; diff --git a/src/apis/paths/type.ts b/src/apis/paths/type.ts index d610be2..56975dd 100644 --- a/src/apis/paths/type.ts +++ b/src/apis/paths/type.ts @@ -45,6 +45,12 @@ export interface Metadata { inputTokens: number; outputTokens: number; timeCost: number; + footNoteMetadataList?:Array<{ + footSource: string; + footType: string; + insertPosition: number; + releatedId: string; + }> } // 定义问答对数据结构 @@ -58,6 +64,7 @@ export interface ConversationRecord { metadata: Metadata; comment: string; created_at: string; + document?: any[]; } // 定义对话内问答列表数据结构 diff --git a/src/assets/svgs/file_download.svg b/src/assets/svgs/file_download.svg new file mode 100644 index 0000000..283d57e --- /dev/null +++ b/src/assets/svgs/file_download.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/file_download_active.svg b/src/assets/svgs/file_download_active.svg new file mode 100644 index 0000000..c072db4 --- /dev/null +++ b/src/assets/svgs/file_download_active.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/file_download_hover.svg b/src/assets/svgs/file_download_hover.svg new file mode 100644 index 0000000..09d8b74 --- /dev/null +++ b/src/assets/svgs/file_download_hover.svg @@ -0,0 +1,13 @@ + + + Created with Pixso. + + + + + + + + + + diff --git a/src/assets/svgs/html.svg b/src/assets/svgs/html.svg new file mode 100644 index 0000000..80482d0 --- /dev/null +++ b/src/assets/svgs/html.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/jpeg.svg b/src/assets/svgs/jpeg.svg new file mode 100644 index 0000000..64c1c5a --- /dev/null +++ b/src/assets/svgs/jpeg.svg @@ -0,0 +1,27 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/json.svg b/src/assets/svgs/json.svg new file mode 100644 index 0000000..0bbf3ab --- /dev/null +++ b/src/assets/svgs/json.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/png.svg b/src/assets/svgs/png.svg new file mode 100644 index 0000000..f3cf7bc --- /dev/null +++ b/src/assets/svgs/png.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/pptx.svg b/src/assets/svgs/pptx.svg new file mode 100644 index 0000000..76dbe7b --- /dev/null +++ b/src/assets/svgs/pptx.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/txt_green.svg b/src/assets/svgs/txt_green.svg new file mode 100644 index 0000000..6256758 --- /dev/null +++ b/src/assets/svgs/txt_green.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/yaml.svg b/src/assets/svgs/yaml.svg new file mode 100644 index 0000000..5724760 --- /dev/null +++ b/src/assets/svgs/yaml.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svgs/zip.svg b/src/assets/svgs/zip.svg new file mode 100644 index 0000000..a971b21 --- /dev/null +++ b/src/assets/svgs/zip.svg @@ -0,0 +1,28 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/dialoguePanel/DialoguePanel.vue b/src/components/dialoguePanel/DialoguePanel.vue index 6bf185a..9b52a5d 100644 --- a/src/components/dialoguePanel/DialoguePanel.vue +++ b/src/components/dialoguePanel/DialoguePanel.vue @@ -1,7 +1,8 @@ - diff --git a/src/components/sessionCard/SessionCard.vue b/src/components/sessionCard/SessionCard.vue index 9384c13..7eb6f10 100644 --- a/src/components/sessionCard/SessionCard.vue +++ b/src/components/sessionCard/SessionCard.vue @@ -288,9 +288,8 @@ const deleteOne = (name: string, list: string[]) => { flex: 1; border-radius: 8px; width: 264px; - height: 66px; background-color: var(--o-bg-color-light); - padding: 0px 5px 12px 15px; + padding: 12px 15px; display: flex; justify-content: center; flex-direction: column; @@ -319,7 +318,6 @@ const deleteOne = (name: string, list: string[]) => { .conversation-title { display: flex; justify-content: space-between; - margin-top: 12px; &__text { display: flex; font-weight: 500; @@ -332,6 +330,10 @@ const deleteOne = (name: string, list: string[]) => { white-space: nowrap; height: 22px; align-self: center; + &-span { + overflow: hidden; + text-overflow: ellipsis; + } &-input { width: 100%; height: 22px; @@ -372,15 +374,15 @@ const deleteOne = (name: string, list: string[]) => { align-items: center; margin-top: 4px; img { - width: 18px; - height: 18px; + width: 16px; + height: 16px; margin-right: 4px; display: inline-block; cursor: pointer; } div { - height: 18px; - line-height: 18px; + height: 16px; + line-height: 16px; color: var(--o-text-color-secondary); } span { diff --git a/src/store/conversation.ts b/src/store/conversation.ts index 4dcfa12..41dc2f8 100644 --- a/src/store/conversation.ts +++ b/src/store/conversation.ts @@ -85,7 +85,9 @@ export const useSessionStore = defineStore('conversation', () => { conversationItem: RobotConversationItem, message: Record, ) => { - conversationItem.message[conversationItem.currentInd] += message.content; + if (!conversationItem.files) { + conversationItem.files = []; + } conversationItem.files = [...conversationItem.files, message.content]; }, suggestionFunc: ( @@ -243,7 +245,7 @@ export const useSessionStore = defineStore('conversation', () => { break; case 'document.add': // 遇到文档添加事件,先省略 - // dataTransfers.documentAdd(conversationItem, message); + dataTransfers.documentAdd(conversationItem, message); break; case 'Suggestion': dataTransfers.suggestionFunc(conversationItem, message); @@ -678,6 +680,21 @@ export const useSessionStore = defineStore('conversation', () => { }; // #endregion + /** + * 处理历史对话数据中尾注位置 + */ + const handleMessage = (record: any): string => { + let message = record.content.answer; + record.metadata.footNoteMetadataList?.reverse().forEach((footNoteMetadata: any)=>{ + const insertFile = record.document?.filter((file: any)=>file._id === footNoteMetadata.releatedId); + const insertNumber = insertFile[0]?.order; + if(!insertNumber)return; + const pos = footNoteMetadata.insertPosition; + message = message.slice(0, pos) + `[[${insertNumber}]]` + message.slice(pos) + }); + return message; + } + /** * 获取历史对话数据 * @param conversationId @@ -720,7 +737,7 @@ export const useSessionStore = defineStore('conversation', () => { { cid: conversationList.value.length + 2, belong: 'robot', - message: [record.content.answer], + message: [handleMessage(record)], messageList, currentInd: 0, isAgainst: false, @@ -730,6 +747,7 @@ export const useSessionStore = defineStore('conversation', () => { conversationId: record.conversationId, groupId: record.groupId, metadata: record.metadata, + document: record.document, flowdata: record?.flow ? (generateFlowData(record.flow) as FlowType) : undefined, diff --git a/src/views/dialogue/components/DialogueSession.vue b/src/views/dialogue/components/DialogueSession.vue index 88ae041..4fd354b 100644 --- a/src/views/dialogue/components/DialogueSession.vue +++ b/src/views/dialogue/components/DialogueSession.vue @@ -6,7 +6,7 @@ import InitalPanel from 'src/views/dialogue/components/InitalPanel.vue'; import InterPreview from 'src/views/dialogue/components/InterPreview.vue'; import MultiSelectTags from 'src/views/dialogue/components/MultiSelectTags.vue'; import { storeToRefs } from 'pinia'; -import { IconCaretRight } from '@computing/opendesign-icons'; +import { IconCaretRight, IconX } from '@computing/opendesign-icons'; import { useSessionStore, useChangeThemeStore } from 'src/store'; import type { ConversationItem, RobotConversationItem } from '../types'; import type { UploadFileCard } from 'src/components/uploadFile/type.ts'; @@ -16,6 +16,37 @@ import { api } from 'src/apis'; import { useHistorySessionStore } from 'src/store/historySession'; import { successMsg, errorMsg } from 'src/components/Message'; import i18n from 'src/i18n'; +import docSvg from '@/assets/svgs/doc.svg'; +import docxSvg from '@/assets/svgs/docx.svg'; +import mdSvg from '@/assets/svgs/md.svg'; +import pdfSvg from '@/assets/svgs/pdf.svg'; +import txtSvg from '@/assets/svgs/txt_green.svg'; +import xlsxSvg from '@/assets/svgs/xlsx.svg'; +import htmlSvg from '@/assets/svgs/html.svg'; +import jpegSvg from '@/assets/svgs/jpeg.svg'; +import pngSvg from '@/assets/svgs/png.svg'; +import jsonSvg from '@/assets/svgs/json.svg'; +import pptxSvg from '@/assets/svgs/pptx.svg'; +import yamlSvg from '@/assets/svgs/yaml.svg'; +import zipSvg from '@/assets/svgs/zip.svg'; +import { ElMessage } from 'element-plus'; + +const typeSvgMap = { + doc: docSvg, + docx: docxSvg, + md: mdSvg, + pdf: pdfSvg, + txt: txtSvg, + xlsx: xlsxSvg, + html: htmlSvg, + jpeg: jpegSvg, + png: pngSvg, + json: jsonSvg, + pptx: pptxSvg, + yaml: yamlSvg, + zip: zipSvg, +}; + const isDropdownOpen = ref(false); const { appList } = storeToRefs(useSessionStore()); const { user_selected_app, selectLLM } = storeToRefs(useHistorySessionStore()); @@ -34,11 +65,21 @@ const { pausedStream } = useSessionStore(); const themeStore = useChangeThemeStore(); const isCreateApp = ref(props?.isCreateApp); const selectedLLM = ref({}); +const curFileList = ref>([]); const handleChangeMode = (val: string) => { selectedLLM.value = val; }; const llmOptions = ref([]); const { app } = storeToRefs(useSessionStore()); +const showFileSource = ref(false); + +const closeShowFileSource = () => { + showFileSource.value = false; +}; +const openShowFileSource = (fileList: Array) => { + showFileSource.value = true; + curFileList.value = fileList; +}; // 对话输入内容 const dialogueInput = ref(''); @@ -194,6 +235,7 @@ const isAllowToSend = computed(() => { // 会话切换时 watch(currentSelectedSession, async (newVal) => { if (!newVal) return; + closeShowFileSource(); const newExistList = existUploadMap.get(newVal); const newFileView = uploadViewsMap.get(newVal); let curPolling = pollingMap.get(newVal); @@ -617,6 +659,85 @@ watch( deep: true, }, ); +function downloadFun(url: string) { + const token = localStorage.getItem('ECSESSION'); + if (!token) { + ElMessage.error(`Token is not available yet`); + return; + } + fetch(url, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((response) => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + // 提取 Content-Disposition 头部 + const contentDisposition = response.headers.get('Content-Disposition'); + let fileName = 'default-filename'; + // 解析文件名(处理编码及格式) + if (contentDisposition) { + const fileNameMatch = contentDisposition.match(/filename\*?=(?:UTF-8'')?"?([^";]+)"?/i); + if (fileNameMatch && fileNameMatch[1]) { + fileName = decodeURIComponent(fileNameMatch[1].replace(/"/g, '')); + } + } + return response.blob().then((blob) => ({ blob,fileName })); + }) + .then(({ blob,fileName }) => { + const a = document.createElement('a'); + a.href = URL.createObjectURL(blob); + a.download = fileName; + a.style.display = 'none'; + a.click(); + URL.revokeObjectURL(a.href); + }) + .catch((error) => { + ElMessage.error(`下载失败: ${error}`); + }); +} +const downLoadSourceFile = (file: any) => { + if (!file) return; + const url = `${window.origin}/wtd/api/doc/download?docId=${file.documentId}`; + downloadFun(url); +}; + +const formatDate = (timestamp: number) => { + const date = new Date(timestamp * 1000); // 秒转毫秒 + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; +} +/** + * @description 处理并过滤文件列表,将文件列表中的字段名统一为指定格式 + * @param {ConversationItem} ConversationItem - 对话项对象 + * @param {string} str - 字段名 + * @returns {Array} 格式化后的文件列表 + */ +const getFormatFileList = (ConversationItem,str)=>{ + let fileList: any= getItem(ConversationItem,str); + if (!fileList || fileList?.length === 0) return; + let newFileList:any = []; + fileList?.forEach((file) => { + if(file.associated === 'answer'){ + newFileList.push({ + documentId: file._id, + documentName: file.name, + documentAbstract: file.abstract, + documentType: file.type, + documentSize: file.size, + sourceUrl: file.sourceUrl, + documentOrder: file.order, + createdAt: file.created_at, + documentAuthor: file.author, + }) + } + }) + return newFileList; +}