|
|
@@ -149,7 +149,7 @@
|
|
|
accept=".pdf,.docx,.xlsx,.xls"
|
|
|
>
|
|
|
<a-button class="upload-btn">
|
|
|
- <UploadOutlined></UploadOutlined>
|
|
|
+ <UploadOutlined />
|
|
|
从本地上传
|
|
|
</a-button>
|
|
|
</a-upload>
|
|
|
@@ -172,1345 +172,1341 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<!-- 预览内容 -->
|
|
|
- <div class="pre-container">
|
|
|
- <!-- PDF预览 -->
|
|
|
- <VueOfficePdf v-if="fileType === 'pdf'" :src="fileUrl" @rendered="handleRendered" @error="handleError" />
|
|
|
- <!-- Word文档预览 -->
|
|
|
- <VueOfficeDocx v-else-if="fileType === 'docx'" :src="fileUrl" @rendered="handleRendered" @error="handleError" />
|
|
|
- <!-- Excel预览 -->
|
|
|
- <VueOfficeExcel
|
|
|
- v-else-if="fileType === 'xlsx' || fileType === 'xls'"
|
|
|
- :options="{ xls: true }"
|
|
|
- :src="fileUrl"
|
|
|
- @rendered="handleRendered"
|
|
|
- @error="handleError"
|
|
|
- />
|
|
|
- <!-- 不支持的文件类型 -->
|
|
|
- <div v-else class="unsupported">
|
|
|
- <p>不支持预览该文件类型: {{ fileType }}</p>
|
|
|
- </div>
|
|
|
- <!-- 加载状态 -->
|
|
|
- <div v-if="loading" class="loading">
|
|
|
- <p>文件加载中...</p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import VueOfficePdf from '@vue-office/pdf';
|
|
|
-import VueOfficeDocx from '@vue-office/docx';
|
|
|
-import VueOfficeExcel from '@vue-office/excel';
|
|
|
-import { ref, onMounted, nextTick } from 'vue';
|
|
|
-import { SvgIcon } from '../Icon';
|
|
|
-import { Space, Button, Modal, Input, message } from 'ant-design-vue';
|
|
|
-// import AIChat from './index.vue';
|
|
|
-import { useUserStore } from '/@/store/modules/user';
|
|
|
-import {
|
|
|
- EditOutlined,
|
|
|
- DeleteOutlined,
|
|
|
- UploadOutlined,
|
|
|
- CopyOutlined,
|
|
|
- RightOutlined,
|
|
|
- DownOutlined,
|
|
|
- FastForwardFilled,
|
|
|
- RedoOutlined,
|
|
|
-} from '@ant-design/icons-vue';
|
|
|
-import { createVNode } from 'vue';
|
|
|
-import { marked } from 'marked';
|
|
|
-import katex from 'katex';
|
|
|
-import 'katex/dist/katex.min.css';
|
|
|
-import '@vue-office/excel/lib/index.css';
|
|
|
-import '@vue-office/docx/lib/index.css';
|
|
|
-const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
|
|
|
-const inputText = ref(''); // 输入框内容
|
|
|
-const refreshText = ref(''); //重新生成文本
|
|
|
-const sessionHistory = ref([]);
|
|
|
-const isShowChatBroad = ref(false);
|
|
|
-const editingId = ref<number | null>(null);
|
|
|
-const editText = ref('');
|
|
|
-const currentSessionID = ref('');
|
|
|
-const taskID = ref('');
|
|
|
-const messageID = ref('');
|
|
|
-const open = ref<boolean>(false);
|
|
|
-const isThinking = ref(false); //深度思考是否开启
|
|
|
-const Thinking = ref(false);
|
|
|
-const isShowDoc = ref(false);
|
|
|
-const fileType = ref('');
|
|
|
-const fileUrl = ref('');
|
|
|
-const APIKEY = ref('Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd');
|
|
|
-interface ListItem {
|
|
|
- id: number;
|
|
|
- name?: string;
|
|
|
-}
|
|
|
-interface Message {
|
|
|
- id: string; // 唯一标识(可用时间戳生成)
|
|
|
- type: 'user' | 'system' | 'response';
|
|
|
- content: String; // 原始 Markdown 字符串(用于拼接)
|
|
|
- parsedContent: String; // 解析后的 HTML(用于渲染)
|
|
|
- contentR1: String; // 原始思考过程 Markdown
|
|
|
- parsedContentR1: String; // 解析后的思考过程 HTML
|
|
|
- timestamp: number; // 排序依据
|
|
|
- isShowThink: boolean; //深度思考展示
|
|
|
-}
|
|
|
-// 定义消息历史数组类型
|
|
|
-const messageHistory = ref<Message[]>([]);
|
|
|
-const isFold = ref(true); // 是否折叠
|
|
|
-const userid = useUserStore().getUserInfo.id as string;
|
|
|
-const filePath = ref(''); // 绑定输入框值
|
|
|
-const uploadedFiles = ref([]);
|
|
|
-const showConfirmBtn = ref(false); // 控制确认按钮显示状态
|
|
|
-const fileList = ref([]);
|
|
|
-const suggestList = ref([]); //建议列表
|
|
|
-const loading = ref(false);
|
|
|
-// 文件预览
|
|
|
-const handleRendered = () => {
|
|
|
- loading.value = false;
|
|
|
- console.log('文件渲染完成');
|
|
|
-};
|
|
|
-const handleError = (error) => {
|
|
|
- loading.value = false;
|
|
|
- console.error('文件预览错误:', error);
|
|
|
-};
|
|
|
-function previewFile(data) {
|
|
|
- fileType.value = data.name.split('.').pop().toLowerCase();
|
|
|
- fileUrl.value = data.source_url;
|
|
|
-}
|
|
|
-function deleteFile(fileId) {
|
|
|
- // 确认删除
|
|
|
- if (confirm('确定要删除这个文件吗?')) {
|
|
|
- uploadedFiles.value = uploadedFiles.value.filter((file) => file.id !== fileId);
|
|
|
- }
|
|
|
-}
|
|
|
-//启用深度思考
|
|
|
-const toggleThinking = () => {
|
|
|
- isThinking.value = !isThinking.value;
|
|
|
- if (isThinking.value) {
|
|
|
- APIKEY.value = 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN';
|
|
|
- } else {
|
|
|
- APIKEY.value = 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd';
|
|
|
- }
|
|
|
-};
|
|
|
-// 折叠思考过程
|
|
|
-const isShow = (message) => {
|
|
|
- message.isShowThink = !message.isShowThink;
|
|
|
-};
|
|
|
-const dialogAreaRef = ref(null);
|
|
|
-// 滚动到底部的方法
|
|
|
-const scrollToBottom = async () => {
|
|
|
- // 等待 DOM 更新(如消息渲染完成)
|
|
|
- await nextTick();
|
|
|
- if (dialogAreaRef.value) {
|
|
|
- const el = dialogAreaRef.value;
|
|
|
- // 关键:scrollTop = scrollHeight(滚动内容总高度)
|
|
|
- el.scrollTop = el.scrollHeight;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 点击建议项时的处理函数
|
|
|
-const handleSuggestClick = (text) => {
|
|
|
- // 将选中的建议填充到文本框
|
|
|
- inputText.value = text;
|
|
|
-};
|
|
|
-function showAIChat() {
|
|
|
- isShowChatBroad.value = !isShowChatBroad.value;
|
|
|
- if (isShowChatBroad) {
|
|
|
- isShowDoc.value = false;
|
|
|
+ // import VueOfficePdf from '@vue-office/pdf';
|
|
|
+ // import VueOfficeDocx from '@vue-office/docx';
|
|
|
+ // import VueOfficeExcel from '@vue-office/excel';
|
|
|
+ import { ref, onMounted, nextTick } from 'vue';
|
|
|
+ import { SvgIcon } from '../Icon';
|
|
|
+ import { Space, Button, Modal, Input, message } from 'ant-design-vue';
|
|
|
+ // import AIChat from './index.vue';
|
|
|
+ import { useUserStore } from '/@/store/modules/user';
|
|
|
+ import {
|
|
|
+ EditOutlined,
|
|
|
+ DeleteOutlined,
|
|
|
+ UploadOutlined,
|
|
|
+ CopyOutlined,
|
|
|
+ RightOutlined,
|
|
|
+ DownOutlined,
|
|
|
+ FastForwardFilled,
|
|
|
+ RedoOutlined,
|
|
|
+ } from '@ant-design/icons-vue';
|
|
|
+ import { createVNode } from 'vue';
|
|
|
+ import { marked } from 'marked';
|
|
|
+ import katex from 'katex';
|
|
|
+ import 'katex/dist/katex.min.css';
|
|
|
+ // import '@vue-office/excel/lib/index.css';
|
|
|
+ // import '@vue-office/docx/lib/index.css';
|
|
|
+ const TextArea = Input.TextArea; // 直接导入TextArea组件使用时打包报错
|
|
|
+ const inputText = ref(''); // 输入框内容
|
|
|
+ const refreshText = ref(''); //重新生成文本
|
|
|
+ const sessionHistory = ref([]);
|
|
|
+ const isShowChatBroad = ref(false);
|
|
|
+ const editingId = ref<number | null>(null);
|
|
|
+ const editText = ref('');
|
|
|
+ const currentSessionID = ref('');
|
|
|
+ const taskID = ref('');
|
|
|
+ const messageID = ref('');
|
|
|
+ const open = ref<boolean>(false);
|
|
|
+ const isThinking = ref(false); //深度思考是否开启
|
|
|
+ const Thinking = ref(false);
|
|
|
+ const isShowDoc = ref(false);
|
|
|
+ const fileType = ref('');
|
|
|
+ const fileUrl = ref('');
|
|
|
+ const APIKEY = ref('Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd');
|
|
|
+ interface ListItem {
|
|
|
+ id: number;
|
|
|
+ name?: string;
|
|
|
+ }
|
|
|
+ interface Message {
|
|
|
+ id: string; // 唯一标识(可用时间戳生成)
|
|
|
+ type: 'user' | 'system' | 'response';
|
|
|
+ content: String; // 原始 Markdown 字符串(用于拼接)
|
|
|
+ parsedContent: String; // 解析后的 HTML(用于渲染)
|
|
|
+ contentR1: String; // 原始思考过程 Markdown
|
|
|
+ parsedContentR1: String; // 解析后的思考过程 HTML
|
|
|
+ timestamp: number; // 排序依据
|
|
|
+ isShowThink: boolean; //深度思考展示
|
|
|
+ }
|
|
|
+ // 定义消息历史数组类型
|
|
|
+ const messageHistory = ref<Message[]>([]);
|
|
|
+ const isFold = ref(true); // 是否折叠
|
|
|
+ const userid = useUserStore().getUserInfo.id as string;
|
|
|
+ const filePath = ref(''); // 绑定输入框值
|
|
|
+ const uploadedFiles = ref([]);
|
|
|
+ const showConfirmBtn = ref(false); // 控制确认按钮显示状态
|
|
|
+ const fileList = ref([]);
|
|
|
+ const suggestList = ref([]); //建议列表
|
|
|
+ const loading = ref(false);
|
|
|
+ // 文件预览
|
|
|
+ const handleRendered = () => {
|
|
|
+ loading.value = false;
|
|
|
+ console.log('文件渲染完成');
|
|
|
+ };
|
|
|
+ const handleError = (error) => {
|
|
|
+ loading.value = false;
|
|
|
+ console.error('文件预览错误:', error);
|
|
|
+ };
|
|
|
+ function previewFile(data) {
|
|
|
+ fileType.value = data.name.split('.').pop().toLowerCase();
|
|
|
+ fileUrl.value = data.source_url;
|
|
|
}
|
|
|
-}
|
|
|
-//复制消息
|
|
|
-function copyToClipboard(text) {
|
|
|
- if (!text || text.trim() === '') {
|
|
|
- message.warn('没有可复制的内容');
|
|
|
- return;
|
|
|
- }
|
|
|
- // 2. 创建临时textarea 元素
|
|
|
- const textarea = document.createElement('textarea');
|
|
|
- textarea.value = text;
|
|
|
- textarea.style.position = 'fixed';
|
|
|
- textarea.style.top = '-999px';
|
|
|
- textarea.style.left = '-999px';
|
|
|
- textarea.style.width = '200px';
|
|
|
- textarea.style.height = '200px';
|
|
|
- document.body.appendChild(textarea);
|
|
|
- try {
|
|
|
- textarea.select();
|
|
|
- textarea.setSelectionRange(0, text.length);
|
|
|
- const isSuccessful = document.execCommand('copy');
|
|
|
- if (isSuccessful) {
|
|
|
- message.success('复制成功!');
|
|
|
+ function deleteFile(fileId) {
|
|
|
+ // 确认删除
|
|
|
+ if (confirm('确定要删除这个文件吗?')) {
|
|
|
+ uploadedFiles.value = uploadedFiles.value.filter((file) => file.id !== fileId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //启用深度思考
|
|
|
+ const toggleThinking = () => {
|
|
|
+ isThinking.value = !isThinking.value;
|
|
|
+ if (isThinking.value) {
|
|
|
+ APIKEY.value = 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN';
|
|
|
} else {
|
|
|
- throw new Error('复制命令执行失败');
|
|
|
+ APIKEY.value = 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd';
|
|
|
}
|
|
|
- } catch (err) {
|
|
|
- console.error('复制失败:', err);
|
|
|
- message.error('复制失败,请手动复制');
|
|
|
- } finally {
|
|
|
- document.body.removeChild(textarea);
|
|
|
- }
|
|
|
-}
|
|
|
-const initMarked = () => {
|
|
|
- marked.setOptions({
|
|
|
- gfm: true, // 启用GitHub风格的Markdown,包含表格
|
|
|
- breaks: false, // 禁用换行符转换
|
|
|
- });
|
|
|
-};
|
|
|
-// LaTeX 公式渲染(内部使用)
|
|
|
-const renderLatexInHtml = (html) => {
|
|
|
- // 匹配块级公式($$...$$)
|
|
|
- const blockRegex = /\$\$(.*?)\$\$/gs;
|
|
|
- // 匹配行内公式($...$)
|
|
|
- const inlineRegex = /\$(.*?)\$/g;
|
|
|
- // // 替换块级公式(居中显示)
|
|
|
- html = html.replace(blockRegex, (match, formula) => {
|
|
|
- matchCount++;
|
|
|
- console.log(`块级公式匹配第 ${matchCount} 次:`, formula); // 查看匹配次数和内容
|
|
|
- return katex.renderToString(formula.trim(), {
|
|
|
- displayMode: true,
|
|
|
- throwOnError: false,
|
|
|
- strict: false,
|
|
|
- trust: true,
|
|
|
- });
|
|
|
- });
|
|
|
- // 替换行内公式(行内显示)
|
|
|
- html = html.replace(inlineRegex, (match, formula) => {
|
|
|
- return katex.renderToString(formula.trim(), {
|
|
|
- displayMode: false,
|
|
|
- throwOnError: false,
|
|
|
- strict: false,
|
|
|
- });
|
|
|
- });
|
|
|
- return html;
|
|
|
-};
|
|
|
-// Markdown + LaTeX 解析
|
|
|
-const parseMarkdownWithLatex = (mdStr) => {
|
|
|
- if (!mdStr) return '';
|
|
|
- try {
|
|
|
- let html = marked(mdStr); // Markdown → HTML
|
|
|
- html = renderLatexInHtml(html); // 替换公式
|
|
|
- return html;
|
|
|
- } catch (error) {
|
|
|
- console.error('解析失败:', error);
|
|
|
- return mdStr; // 降级显示原始字符串
|
|
|
- }
|
|
|
-};
|
|
|
-//重新生成
|
|
|
-const refresh = () => {
|
|
|
- handleSend(refreshText.value);
|
|
|
-};
|
|
|
-//重新编辑
|
|
|
-const editAsk = (data) => {
|
|
|
- inputText.value = data;
|
|
|
-};
|
|
|
-//获取消息列表
|
|
|
-async function handleSend(data) {
|
|
|
- refreshText.value = data;
|
|
|
- inputText.value = '';
|
|
|
- if (isThinking) {
|
|
|
- messageHistory.value.push({
|
|
|
- id: `user_${Date.now()}`,
|
|
|
- type: 'user',
|
|
|
- content: '',
|
|
|
- parsedContent: data,
|
|
|
- contentR1: '',
|
|
|
- parsedContentR1: '',
|
|
|
- timestamp: Date.now(),
|
|
|
- isShowThink: true,
|
|
|
+ };
|
|
|
+ // 折叠思考过程
|
|
|
+ const isShow = (message) => {
|
|
|
+ message.isShowThink = !message.isShowThink;
|
|
|
+ };
|
|
|
+ const dialogAreaRef = ref(null);
|
|
|
+ // 滚动到底部的方法
|
|
|
+ const scrollToBottom = async () => {
|
|
|
+ // 等待 DOM 更新(如消息渲染完成)
|
|
|
+ await nextTick();
|
|
|
+ if (dialogAreaRef.value) {
|
|
|
+ const el = dialogAreaRef.value;
|
|
|
+ // 关键:scrollTop = scrollHeight(滚动内容总高度)
|
|
|
+ el.scrollTop = el.scrollHeight;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 点击建议项时的处理函数
|
|
|
+ const handleSuggestClick = (text) => {
|
|
|
+ // 将选中的建议填充到文本框
|
|
|
+ inputText.value = text;
|
|
|
+ };
|
|
|
+ function showAIChat() {
|
|
|
+ isShowChatBroad.value = !isShowChatBroad.value;
|
|
|
+ if (isShowChatBroad.value) {
|
|
|
+ isShowDoc.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //复制消息
|
|
|
+ function copyToClipboard(text) {
|
|
|
+ if (!text || text.trim() === '') {
|
|
|
+ message.warn('没有可复制的内容');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 2. 创建临时textarea 元素
|
|
|
+ const textarea = document.createElement('textarea');
|
|
|
+ textarea.value = text;
|
|
|
+ textarea.style.position = 'fixed';
|
|
|
+ textarea.style.top = '-999px';
|
|
|
+ textarea.style.left = '-999px';
|
|
|
+ textarea.style.width = '200px';
|
|
|
+ textarea.style.height = '200px';
|
|
|
+ document.body.appendChild(textarea);
|
|
|
+ try {
|
|
|
+ textarea.select();
|
|
|
+ textarea.setSelectionRange(0, text.length);
|
|
|
+ const isSuccessful = document.execCommand('copy');
|
|
|
+ if (isSuccessful) {
|
|
|
+ message.success('复制成功!');
|
|
|
+ } else {
|
|
|
+ throw new Error('复制命令执行失败');
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error('复制失败:', err);
|
|
|
+ message.error('复制失败,请手动复制');
|
|
|
+ } finally {
|
|
|
+ document.body.removeChild(textarea);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const initMarked = () => {
|
|
|
+ marked.setOptions({
|
|
|
+ gfm: true, // 启用GitHub风格的Markdown,包含表格
|
|
|
+ breaks: false, // 禁用换行符转换
|
|
|
});
|
|
|
- } else {
|
|
|
- messageHistory.value.push({
|
|
|
- id: `user_${Date.now()}`,
|
|
|
- type: 'user',
|
|
|
- content: '',
|
|
|
- parsedContent: data,
|
|
|
- contentR1: '',
|
|
|
- parsedContentR1: '',
|
|
|
- timestamp: Date.now(),
|
|
|
- isShowThink: false,
|
|
|
+ };
|
|
|
+ // LaTeX 公式渲染(内部使用)
|
|
|
+ const renderLatexInHtml = (html) => {
|
|
|
+ // 匹配块级公式($$...$$)
|
|
|
+ const blockRegex = /\$\$(.*?)\$\$/gs;
|
|
|
+ // 匹配行内公式($...$)
|
|
|
+ const inlineRegex = /\$(.*?)\$/g;
|
|
|
+ // // 替换块级公式(居中显示)
|
|
|
+ html = html.replace(blockRegex, (match, formula) => {
|
|
|
+ matchCount++;
|
|
|
+ console.log(`块级公式匹配第 ${matchCount} 次:`, formula); // 查看匹配次数和内容
|
|
|
+ return katex.renderToString(formula.trim(), {
|
|
|
+ displayMode: true,
|
|
|
+ throwOnError: false,
|
|
|
+ strict: false,
|
|
|
+ trust: true,
|
|
|
+ });
|
|
|
});
|
|
|
- }
|
|
|
- try {
|
|
|
- const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
|
|
|
- method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: APIKEY.value,
|
|
|
- },
|
|
|
- body: JSON.stringify({
|
|
|
- conversation_id: currentSessionID.value ? currentSessionID.value : '',
|
|
|
- query: data,
|
|
|
- response_mode: 'streaming',
|
|
|
- user: userid,
|
|
|
- inputs: {},
|
|
|
- }),
|
|
|
+ // 替换行内公式(行内显示)
|
|
|
+ html = html.replace(inlineRegex, (match, formula) => {
|
|
|
+ return katex.renderToString(formula.trim(), {
|
|
|
+ displayMode: false,
|
|
|
+ throwOnError: false,
|
|
|
+ strict: false,
|
|
|
+ });
|
|
|
});
|
|
|
-
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
+ return html;
|
|
|
+ };
|
|
|
+ // Markdown + LaTeX 解析
|
|
|
+ const parseMarkdownWithLatex = (mdStr) => {
|
|
|
+ if (!mdStr) return '';
|
|
|
+ try {
|
|
|
+ let html = marked(mdStr); // Markdown → HTML
|
|
|
+ html = renderLatexInHtml(html); // 替换公式
|
|
|
+ return html;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('解析失败:', error);
|
|
|
+ return mdStr; // 降级显示原始字符串
|
|
|
}
|
|
|
-
|
|
|
- const decoder = new TextDecoder('utf-8');
|
|
|
- const reader = response.body.getReader();
|
|
|
- let textBuffer = ''; // 使用字符串缓冲区来累积数据
|
|
|
- // 在组件中定义
|
|
|
- const currentProcessingMessage = ref(null);
|
|
|
- if (isThinking) {
|
|
|
- const newMessage = {
|
|
|
- id: `response_${Date.now()}`,
|
|
|
- type: 'response',
|
|
|
+ };
|
|
|
+ //重新生成
|
|
|
+ const refresh = () => {
|
|
|
+ handleSend(refreshText.value);
|
|
|
+ };
|
|
|
+ //重新编辑
|
|
|
+ const editAsk = (data) => {
|
|
|
+ inputText.value = data;
|
|
|
+ };
|
|
|
+ //获取消息列表
|
|
|
+ async function handleSend(data) {
|
|
|
+ refreshText.value = data;
|
|
|
+ inputText.value = '';
|
|
|
+ if (isThinking.value) {
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
content: '',
|
|
|
- parsedContent: '',
|
|
|
+ parsedContent: data,
|
|
|
contentR1: '',
|
|
|
parsedContentR1: '',
|
|
|
timestamp: Date.now(),
|
|
|
isShowThink: true,
|
|
|
- };
|
|
|
- messageHistory.value.push(newMessage);
|
|
|
- currentProcessingMessage.value = newMessage; // 保存引用
|
|
|
+ });
|
|
|
} else {
|
|
|
- const newMessage = {
|
|
|
- id: `response_${Date.now()}`,
|
|
|
- type: 'response',
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
content: '',
|
|
|
- parsedContent: '',
|
|
|
+ parsedContent: data,
|
|
|
contentR1: '',
|
|
|
parsedContentR1: '',
|
|
|
timestamp: Date.now(),
|
|
|
isShowThink: false,
|
|
|
- };
|
|
|
- messageHistory.value.push(newMessage);
|
|
|
- currentProcessingMessage.value = newMessage; // 保存引用
|
|
|
+ });
|
|
|
}
|
|
|
- while (true) {
|
|
|
- const { done, value } = await reader.read();
|
|
|
- if (done) {
|
|
|
- if (textBuffer) {
|
|
|
- processLine(textBuffer);
|
|
|
- }
|
|
|
- break;
|
|
|
+ try {
|
|
|
+ const response = await fetch('http://39.97.59.228:8000/v1/chat-messages', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ conversation_id: currentSessionID.value ? currentSessionID.value : '',
|
|
|
+ query: data,
|
|
|
+ response_mode: 'streaming',
|
|
|
+ user: userid,
|
|
|
+ inputs: {},
|
|
|
+ }),
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
}
|
|
|
- textBuffer += decoder.decode(value, { stream: true });
|
|
|
- // 处理每一行数据
|
|
|
- let lineIndex;
|
|
|
- while ((lineIndex = textBuffer.indexOf('\n')) !== -1) {
|
|
|
- const line = textBuffer.substring(0, lineIndex).trim();
|
|
|
- textBuffer = textBuffer.substring(lineIndex + 1);
|
|
|
|
|
|
- if (line) {
|
|
|
- processLine(line);
|
|
|
+ const decoder = new TextDecoder('utf-8');
|
|
|
+ const reader = response.body.getReader();
|
|
|
+ let textBuffer = ''; // 使用字符串缓冲区来累积数据
|
|
|
+ // 在组件中定义
|
|
|
+ const currentProcessingMessage = ref(null);
|
|
|
+ if (isThinking.value) {
|
|
|
+ const newMessage = {
|
|
|
+ id: `response_${Date.now()}`,
|
|
|
+ type: 'response',
|
|
|
+ content: '',
|
|
|
+ parsedContent: '',
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: true,
|
|
|
+ };
|
|
|
+ messageHistory.value.push(newMessage);
|
|
|
+ currentProcessingMessage.value = newMessage; // 保存引用
|
|
|
+ } else {
|
|
|
+ const newMessage = {
|
|
|
+ id: `response_${Date.now()}`,
|
|
|
+ type: 'response',
|
|
|
+ content: '',
|
|
|
+ parsedContent: '',
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
+ };
|
|
|
+ messageHistory.value.push(newMessage);
|
|
|
+ currentProcessingMessage.value = newMessage; // 保存引用
|
|
|
+ }
|
|
|
+ while (true) {
|
|
|
+ const { done, value } = await reader.read();
|
|
|
+ if (done) {
|
|
|
+ if (textBuffer) {
|
|
|
+ processLine(textBuffer);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ textBuffer += decoder.decode(value, { stream: true });
|
|
|
+ // 处理每一行数据
|
|
|
+ let lineIndex;
|
|
|
+ while ((lineIndex = textBuffer.indexOf('\n')) !== -1) {
|
|
|
+ const line = textBuffer.substring(0, lineIndex).trim();
|
|
|
+ textBuffer = textBuffer.substring(lineIndex + 1);
|
|
|
+
|
|
|
+ if (line) {
|
|
|
+ processLine(line);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- function processLine(line) {
|
|
|
- if (line.startsWith('data: ')) {
|
|
|
- try {
|
|
|
- const jsonStr = line.substring('data: '.length);
|
|
|
- const data = JSON.parse(jsonStr);
|
|
|
- switch (data.event) {
|
|
|
- case 'message':
|
|
|
- if (data.answer) {
|
|
|
- const targetMessage = messageHistory.value.find((msg) => msg.id === currentProcessingMessage.value.id);
|
|
|
+ function processLine(line) {
|
|
|
+ if (line.startsWith('data: ')) {
|
|
|
+ try {
|
|
|
+ const jsonStr = line.substring('data: '.length);
|
|
|
+ const data = JSON.parse(jsonStr);
|
|
|
+ switch (data.event) {
|
|
|
+ case 'message':
|
|
|
+ if (data.answer) {
|
|
|
+ const targetMessage = messageHistory.value.find((msg) => msg.id === currentProcessingMessage.value.id);
|
|
|
|
|
|
- if (!targetMessage) break;
|
|
|
- let currentChunk = data.answer;
|
|
|
+ if (!targetMessage) break;
|
|
|
+ let currentChunk = data.answer;
|
|
|
|
|
|
- // 检查是否包含起始标签
|
|
|
- const startIndex = currentChunk.indexOf('<think>');
|
|
|
- if (startIndex !== -1) {
|
|
|
- // 找到起始标签:将标签前的内容作为正文
|
|
|
- if (startIndex > 0) {
|
|
|
- targetMessage.content += currentChunk.substring(0, startIndex);
|
|
|
- targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
|
|
|
- }
|
|
|
- // 进入思考模式,将标签后的内容追加到contentR1
|
|
|
- Thinking.value = true;
|
|
|
- const remainingContent = currentChunk.substring(startIndex + '<think>'.length);
|
|
|
- if (remainingContent) {
|
|
|
- targetMessage.contentR1 += remainingContent;
|
|
|
- targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
|
|
|
- }
|
|
|
- } else if (Thinking.value) {
|
|
|
- // 结束标签
|
|
|
- const endIndex = currentChunk.indexOf('</think>');
|
|
|
- if (endIndex !== -1) {
|
|
|
- // 找到结束标签:标签前的内容追加到contentR1
|
|
|
- if (endIndex > 0) {
|
|
|
- targetMessage.contentR1 += currentChunk.substring(0, endIndex);
|
|
|
- targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
|
|
|
+ // 检查是否包含起始标签
|
|
|
+ const startIndex = currentChunk.indexOf('<think>');
|
|
|
+ if (startIndex !== -1) {
|
|
|
+ // 找到起始标签:将标签前的内容作为正文
|
|
|
+ if (startIndex > 0) {
|
|
|
+ targetMessage.content += currentChunk.substring(0, startIndex);
|
|
|
+ targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
|
|
|
}
|
|
|
- // 将标签后的内容作为正文
|
|
|
- const remainingContent = currentChunk.substring(endIndex + '</think>'.length);
|
|
|
+ // 进入思考模式,将标签后的内容追加到contentR1
|
|
|
+ Thinking.value = true;
|
|
|
+ const remainingContent = currentChunk.substring(startIndex + '<think>'.length);
|
|
|
if (remainingContent) {
|
|
|
- targetMessage.content += remainingContent;
|
|
|
- targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
|
|
|
+ targetMessage.contentR1 += remainingContent;
|
|
|
+ targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
|
|
|
+ }
|
|
|
+ } else if (Thinking.value) {
|
|
|
+ // 结束标签
|
|
|
+ const endIndex = currentChunk.indexOf('</think>');
|
|
|
+ if (endIndex !== -1) {
|
|
|
+ // 找到结束标签:标签前的内容追加到contentR1
|
|
|
+ if (endIndex > 0) {
|
|
|
+ targetMessage.contentR1 += currentChunk.substring(0, endIndex);
|
|
|
+ targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
|
|
|
+ }
|
|
|
+ // 将标签后的内容作为正文
|
|
|
+ const remainingContent = currentChunk.substring(endIndex + '</think>'.length);
|
|
|
+ if (remainingContent) {
|
|
|
+ targetMessage.content += remainingContent;
|
|
|
+ targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
|
|
|
+ }
|
|
|
+ targetMessage.content += currentChunk;
|
|
|
+ Thinking.value = false;
|
|
|
+ targetMessage.isShowThink = false;
|
|
|
+ } else {
|
|
|
+ // 没有结束标签,继续追加到contentR1
|
|
|
+ targetMessage.contentR1 += currentChunk;
|
|
|
+ targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
|
|
|
}
|
|
|
- targetMessage.content += currentChunk;
|
|
|
- Thinking.value = false;
|
|
|
- targetMessage.isShowThink = false;
|
|
|
} else {
|
|
|
- // 没有结束标签,继续追加到contentR1
|
|
|
- targetMessage.contentR1 += currentChunk;
|
|
|
- targetMessage.parsedContentR1 = parseMarkdownWithLatex(targetMessage.contentR1);
|
|
|
+ targetMessage.content += currentChunk;
|
|
|
+ targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
|
|
|
}
|
|
|
- } else {
|
|
|
- targetMessage.content += currentChunk;
|
|
|
- targetMessage.parsedContent = parseMarkdownWithLatex(targetMessage.content);
|
|
|
+ scrollToBottom(); // 每次接收消息都滚动
|
|
|
+ }
|
|
|
+ if (data.task_id && !taskID.value) taskID.value = data.task_id;
|
|
|
+ if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
|
|
|
+ if (data.message_id && !messageID.value) {
|
|
|
+ messageID.value = data.message_id;
|
|
|
}
|
|
|
- scrollToBottom(); // 每次接收消息都滚动
|
|
|
- }
|
|
|
- if (data.task_id && !taskID.value) taskID.value = data.task_id;
|
|
|
- if (data.conversation_id && !currentSessionID.value) currentSessionID.value = data.conversation_id;
|
|
|
- if (data.message_id && !messageID.value) {
|
|
|
- messageID.value = data.message_id;
|
|
|
- }
|
|
|
- break;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('Error parsing stream chunk:', error, 'Chunk:', line);
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
- console.warn('Error parsing stream chunk:', error, 'Chunk:', line);
|
|
|
}
|
|
|
}
|
|
|
+ getNextSuggest();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error in handleSend:', error);
|
|
|
+ // 在 UI 上显示错误信息
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: '',
|
|
|
+ parsedContent: '请求错误',
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
+ });
|
|
|
}
|
|
|
- getNextSuggest();
|
|
|
- } catch (error) {
|
|
|
- console.error('Error in handleSend:', error);
|
|
|
- // 在 UI 上显示错误信息
|
|
|
- messageHistory.value.push({
|
|
|
- id: `system_${Date.now()}`,
|
|
|
- type: 'system',
|
|
|
- content: '',
|
|
|
- parsedContent: '请求错误',
|
|
|
- contentR1: '',
|
|
|
- parsedContentR1: '',
|
|
|
- timestamp: Date.now(),
|
|
|
- isShowThink: false,
|
|
|
- });
|
|
|
}
|
|
|
-}
|
|
|
-//创建新对话
|
|
|
-async function addNew() {
|
|
|
- messageHistory.value = [];
|
|
|
- currentSessionID.value = '';
|
|
|
- taskID.value = '';
|
|
|
- messageID.value = '';
|
|
|
-}
|
|
|
-// 上传文件
|
|
|
-const showModal = (data) => {
|
|
|
- open.value = data;
|
|
|
-};
|
|
|
-const close = () => {
|
|
|
- isShowDoc.value = false;
|
|
|
-};
|
|
|
-// 上传文件
|
|
|
-const handleBeforeUpload = async (file) => {
|
|
|
- const formData = new FormData();
|
|
|
- formData.append('file', file);
|
|
|
- formData.append('user', userid);
|
|
|
+ //创建新对话
|
|
|
+ async function addNew() {
|
|
|
+ messageHistory.value = [];
|
|
|
+ currentSessionID.value = '';
|
|
|
+ taskID.value = '';
|
|
|
+ messageID.value = '';
|
|
|
+ }
|
|
|
+ // 上传文件
|
|
|
+ const showModal = (data) => {
|
|
|
+ open.value = data;
|
|
|
+ };
|
|
|
+ const close = () => {
|
|
|
+ isShowDoc.value = false;
|
|
|
+ };
|
|
|
+ // 上传文件
|
|
|
+ const handleBeforeUpload = async (file) => {
|
|
|
+ const formData = new FormData();
|
|
|
+ formData.append('file', file);
|
|
|
+ formData.append('user', userid);
|
|
|
|
|
|
- try {
|
|
|
- const response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
|
|
|
- method: 'POST',
|
|
|
- headers: {
|
|
|
- Authorization: APIKEY.value,
|
|
|
- },
|
|
|
- body: formData,
|
|
|
- });
|
|
|
+ try {
|
|
|
+ const response = await fetch(`http://39.97.59.228:8000/v1/files/upload`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ Authorization: APIKEY.value,
|
|
|
+ },
|
|
|
+ body: formData,
|
|
|
+ });
|
|
|
|
|
|
- const result = await response.json();
|
|
|
- if (response.ok) {
|
|
|
- const linkText = `[${result.name}](${result.source_url})`;
|
|
|
- // inputText.value += `\n${linkText}`;
|
|
|
- uploadedFiles.value.push(result);
|
|
|
- isShowDoc.value = true;
|
|
|
- previewFile(result);
|
|
|
- } else {
|
|
|
- message.error(`上传失败: ${result.message || '网络错误'}`);
|
|
|
+ const result = await response.json();
|
|
|
+ if (response.ok) {
|
|
|
+ const linkText = `[${result.name}](${result.source_url})`;
|
|
|
+ // inputText.value += `\n${linkText}`;
|
|
|
+ uploadedFiles.value.push(result);
|
|
|
+ isShowDoc.value = true;
|
|
|
+ previewFile(result);
|
|
|
+ } else {
|
|
|
+ message.error(`上传失败: ${result.message || '网络错误'}`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存失败:', error);
|
|
|
+ message.error('上传失败,请重试');
|
|
|
}
|
|
|
- } catch (error) {
|
|
|
- console.error('保存失败:', error);
|
|
|
- message.error('上传失败,请重试');
|
|
|
- }
|
|
|
- return false;
|
|
|
-};
|
|
|
+ return false;
|
|
|
+ };
|
|
|
|
|
|
-const handleRemove = (file) => {
|
|
|
- const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
|
|
- if (index !== -1) {
|
|
|
- fileList.value.splice(index, 1);
|
|
|
- }
|
|
|
-};
|
|
|
-//停止响应
|
|
|
-async function stopReq() {
|
|
|
- try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/chat-messages/${taskID.value}/stop`, {
|
|
|
- method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: APIKEY.value,
|
|
|
- },
|
|
|
- body: JSON.stringify({
|
|
|
- user: userid,
|
|
|
- }),
|
|
|
- });
|
|
|
- if (!response) {
|
|
|
- throw new Error('Network response was not ok');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('保存失败:', error);
|
|
|
- }
|
|
|
-}
|
|
|
-//获取具体会话记录
|
|
|
-async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
- const API_KEYS = ['Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd', 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN'];
|
|
|
- // 最大重试次数
|
|
|
- const maxRetries = API_KEYS.length - 1;
|
|
|
- try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/messages?conversation_id=${id}&user=${userid}`, {
|
|
|
- method: 'GET',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: API_KEYS[retryCount],
|
|
|
- },
|
|
|
- });
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
|
+ const handleRemove = (file) => {
|
|
|
+ const index = fileList.value.findIndex((item) => item.uid === file.uid);
|
|
|
+ if (index !== -1) {
|
|
|
+ fileList.value.splice(index, 1);
|
|
|
}
|
|
|
- const data = await response.json();
|
|
|
- if (data.data.length > 0) {
|
|
|
- messageHistory.value = [];
|
|
|
- data.data.forEach((item: any) => {
|
|
|
- currentSessionID.value = item.conversation_id;
|
|
|
- messageHistory.value.push({
|
|
|
- id: `user_${Date.now()}`,
|
|
|
- type: 'user',
|
|
|
- content: '',
|
|
|
- parsedContent: item.query,
|
|
|
- contentR1: '',
|
|
|
- parsedContentR1: '',
|
|
|
- timestamp: Date.now(),
|
|
|
- isShowThink: false,
|
|
|
- });
|
|
|
- const answer = item.answer;
|
|
|
- // 查找<think>
|
|
|
- const startIndex = answer.indexOf('<think>');
|
|
|
- const endIndex = answer.indexOf('</think>');
|
|
|
- let content = '';
|
|
|
- let contentR1 = '';
|
|
|
- // 根据标签判断
|
|
|
- if (startIndex !== -1 && endIndex !== -1) {
|
|
|
- content = answer.substring(0, startIndex) + answer.substring(endIndex + '</think>'.length);
|
|
|
- contentR1 = answer.substring(startIndex + '<think>'.length, endIndex);
|
|
|
- } else if (startIndex !== -1) {
|
|
|
- // 只有起始标签
|
|
|
- content = answer.substring(0, startIndex);
|
|
|
- contentR1 = answer.substring(startIndex + '<think>'.length);
|
|
|
- } else if (endIndex !== -1) {
|
|
|
- // 只有结束标签
|
|
|
- content = answer.substring(endIndex + '</think>'.length);
|
|
|
- contentR1 = answer.substring(0, endIndex);
|
|
|
- } else {
|
|
|
- // 没有标签
|
|
|
- content = answer;
|
|
|
- }
|
|
|
- // 添加到消息历史
|
|
|
- messageHistory.value.push({
|
|
|
- id: `system_${Date.now()}`,
|
|
|
- type: 'system',
|
|
|
- content: '',
|
|
|
- parsedContent: parseMarkdownWithLatex(content.trim()),
|
|
|
- contentR1: '',
|
|
|
- parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
|
|
|
- timestamp: Date.now(),
|
|
|
- isShowThink: contentR1.length > 0, // 如果有思考内容则显示
|
|
|
- });
|
|
|
+ };
|
|
|
+ //停止响应
|
|
|
+ async function stopReq() {
|
|
|
+ try {
|
|
|
+ let response = await fetch(`http://39.97.59.228:8000/v1/chat-messages/${taskID.value}/stop`, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ user: userid,
|
|
|
+ }),
|
|
|
});
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- // 判断是否达到最大重试次数
|
|
|
- if (retryCount < maxRetries) {
|
|
|
- console.warn(`请求失败,正在尝试第 ${retryCount + 2} 个 API Key...`);
|
|
|
- return sessionsHistory(id, retryCount + 1);
|
|
|
- } else {
|
|
|
- console.error('所有 API Key 均尝试失败:', error);
|
|
|
- throw error;
|
|
|
+ if (!response) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存失败:', error);
|
|
|
}
|
|
|
}
|
|
|
- editingId.value = null;
|
|
|
-}
|
|
|
-//获取下一轮建议问题列表
|
|
|
-//停止响应
|
|
|
-async function getNextSuggest() {
|
|
|
- console.log(messageID.value, '123');
|
|
|
- try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/messages/${messageID.value}/suggested?user=${userid}`, {
|
|
|
- method: 'GET',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: APIKEY.value,
|
|
|
- },
|
|
|
- });
|
|
|
- const data = await response.json();
|
|
|
- suggestList.value = data.data;
|
|
|
- if (!response) {
|
|
|
- throw new Error('Network response was not ok');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('保存失败:', error);
|
|
|
- }
|
|
|
-}
|
|
|
-//编辑标题
|
|
|
-const startEditing = (item: ListItem) => {
|
|
|
- editingId.value = item.id;
|
|
|
- editText.value = item.name || '';
|
|
|
-};
|
|
|
-// 保存修改
|
|
|
-const handleSave = async (item: ListItem) => {
|
|
|
- try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}/name`, {
|
|
|
- method: 'POST',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: APIKEY.value,
|
|
|
- },
|
|
|
- body: JSON.stringify({
|
|
|
- name: editText.value,
|
|
|
- user: userid,
|
|
|
- }),
|
|
|
- });
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error('Network response was not ok');
|
|
|
- }
|
|
|
- item.name = editText.value;
|
|
|
- } catch (error) {
|
|
|
- console.error('保存失败:', error);
|
|
|
- }
|
|
|
- editingId.value = null;
|
|
|
-};
|
|
|
-// 删除会话
|
|
|
-const startDelete = async (item: ListItem) => {
|
|
|
- Modal.confirm({
|
|
|
- title: '确认删除',
|
|
|
- content: `确定要删除会话 "${item.name || '新会话'}" 吗?此操作不可撤销。`,
|
|
|
- okText: '确认',
|
|
|
- cancelText: '取消',
|
|
|
- onOk: async () => {
|
|
|
- // 原有删除逻辑不变
|
|
|
- try {
|
|
|
- let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}`, {
|
|
|
- method: 'DELETE',
|
|
|
- headers: {
|
|
|
- 'Content-Type': 'application/json',
|
|
|
- Authorization: APIKEY.value,
|
|
|
- },
|
|
|
- body: JSON.stringify({
|
|
|
- user: userid,
|
|
|
- }),
|
|
|
- });
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error('Network response was not ok');
|
|
|
- }
|
|
|
- getHistoryList();
|
|
|
- } catch (error) {
|
|
|
- console.error('删除失败:', error);
|
|
|
- Modal.error({
|
|
|
- title: '删除失败',
|
|
|
- content: '删除会话时出现错误,请稍后重试。',
|
|
|
+ //获取具体会话记录
|
|
|
+ async function sessionsHistory(id: string, retryCount = 0) {
|
|
|
+ const API_KEYS = ['Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd', 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN'];
|
|
|
+ // 最大重试次数
|
|
|
+ const maxRetries = API_KEYS.length - 1;
|
|
|
+ try {
|
|
|
+ let response = await fetch(`http://39.97.59.228:8000/v1/messages?conversation_id=${id}&user=${userid}`, {
|
|
|
+ method: 'GET',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: API_KEYS[retryCount],
|
|
|
+ },
|
|
|
+ });
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
|
+ }
|
|
|
+ const data = await response.json();
|
|
|
+ if (data.data.length > 0) {
|
|
|
+ messageHistory.value = [];
|
|
|
+ data.data.forEach((item: any) => {
|
|
|
+ currentSessionID.value = item.conversation_id;
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
+ content: '',
|
|
|
+ parsedContent: item.query,
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: '',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: false,
|
|
|
+ });
|
|
|
+ const answer = item.answer;
|
|
|
+ // 查找<think>
|
|
|
+ const startIndex = answer.indexOf('<think>');
|
|
|
+ const endIndex = answer.indexOf('</think>');
|
|
|
+ let content = '';
|
|
|
+ let contentR1 = '';
|
|
|
+ // 根据标签判断
|
|
|
+ if (startIndex !== -1 && endIndex !== -1) {
|
|
|
+ content = answer.substring(0, startIndex) + answer.substring(endIndex + '</think>'.length);
|
|
|
+ contentR1 = answer.substring(startIndex + '<think>'.length, endIndex);
|
|
|
+ } else if (startIndex !== -1) {
|
|
|
+ // 只有起始标签
|
|
|
+ content = answer.substring(0, startIndex);
|
|
|
+ contentR1 = answer.substring(startIndex + '<think>'.length);
|
|
|
+ } else if (endIndex !== -1) {
|
|
|
+ // 只有结束标签
|
|
|
+ content = answer.substring(endIndex + '</think>'.length);
|
|
|
+ contentR1 = answer.substring(0, endIndex);
|
|
|
+ } else {
|
|
|
+ // 没有标签
|
|
|
+ content = answer;
|
|
|
+ }
|
|
|
+ // 添加到消息历史
|
|
|
+ messageHistory.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: '',
|
|
|
+ parsedContent: parseMarkdownWithLatex(content.trim()),
|
|
|
+ contentR1: '',
|
|
|
+ parsedContentR1: parseMarkdownWithLatex(contentR1.trim()),
|
|
|
+ timestamp: Date.now(),
|
|
|
+ isShowThink: contentR1.length > 0, // 如果有思考内容则显示
|
|
|
+ });
|
|
|
});
|
|
|
}
|
|
|
- },
|
|
|
- });
|
|
|
-};
|
|
|
-const fold = () => {
|
|
|
- isFold.value = !isFold.value;
|
|
|
- if (!isFold.value) {
|
|
|
- getHistoryList();
|
|
|
+ } catch (error) {
|
|
|
+ // 判断是否达到最大重试次数
|
|
|
+ if (retryCount < maxRetries) {
|
|
|
+ console.warn(`请求失败,正在尝试第 ${retryCount + 2} 个 API Key...`);
|
|
|
+ return sessionsHistory(id, retryCount + 1);
|
|
|
+ } else {
|
|
|
+ console.error('所有 API Key 均尝试失败:', error);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ editingId.value = null;
|
|
|
}
|
|
|
-};
|
|
|
-// 获取历史会话列表
|
|
|
-async function getHistoryList() {
|
|
|
- sessionHistory.value = [];
|
|
|
- try {
|
|
|
- // 并行请求两个接口,提升效率
|
|
|
- const [response1, response2] = await Promise.all([
|
|
|
- // 第一个请求
|
|
|
- fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
- method: 'get',
|
|
|
+ //获取下一轮建议问题列表
|
|
|
+ //停止响应
|
|
|
+ async function getNextSuggest() {
|
|
|
+ console.log(messageID.value, '123');
|
|
|
+ try {
|
|
|
+ let response = await fetch(`http://39.97.59.228:8000/v1/messages/${messageID.value}/suggested?user=${userid}`, {
|
|
|
+ method: 'GET',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
- }),
|
|
|
- // 第二个请求
|
|
|
- fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
- method: 'get',
|
|
|
+ });
|
|
|
+ const data = await response.json();
|
|
|
+ suggestList.value = data.data;
|
|
|
+ if (!response) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存失败:', error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //编辑标题
|
|
|
+ const startEditing = (item: ListItem) => {
|
|
|
+ editingId.value = item.id;
|
|
|
+ editText.value = item.name || '';
|
|
|
+ };
|
|
|
+ // 保存修改
|
|
|
+ const handleSave = async (item: ListItem) => {
|
|
|
+ try {
|
|
|
+ let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}/name`, {
|
|
|
+ method: 'POST',
|
|
|
headers: {
|
|
|
'Content-Type': 'application/json',
|
|
|
- Authorization: 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
},
|
|
|
- }),
|
|
|
- ]);
|
|
|
-
|
|
|
- // 检查响应是否成功
|
|
|
- if (!response1.ok || !response2.ok) {
|
|
|
- throw new Error('接口请求失败');
|
|
|
+ body: JSON.stringify({
|
|
|
+ name: editText.value,
|
|
|
+ user: userid,
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+ item.name = editText.value;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存失败:', error);
|
|
|
}
|
|
|
-
|
|
|
- // 解析两个响应的JSON数据
|
|
|
- const [data1, data2] = await Promise.all([response1.json(), response2.json()]);
|
|
|
-
|
|
|
- // 合并两个数组(核心修改:用扩展运算符合并)
|
|
|
- // 确保 data1.data 和 data2.data 都是数组(防止接口返回异常)
|
|
|
- const list1 = Array.isArray(data1.data) ? data1.data : [];
|
|
|
- const list2 = Array.isArray(data2.data) ? data2.data : [];
|
|
|
-
|
|
|
- // 合并数组并赋值给 sessionHistory
|
|
|
- sessionHistory.value = [...list1, ...list2];
|
|
|
- sessionHistory.value.forEach((item) => {
|
|
|
- currentSessionID.value = item.id;
|
|
|
+ editingId.value = null;
|
|
|
+ };
|
|
|
+ // 删除会话
|
|
|
+ const startDelete = async (item: ListItem) => {
|
|
|
+ Modal.confirm({
|
|
|
+ title: '确认删除',
|
|
|
+ content: `确定要删除会话 "${item.name || '新会话'}" 吗?此操作不可撤销。`,
|
|
|
+ okText: '确认',
|
|
|
+ cancelText: '取消',
|
|
|
+ onOk: async () => {
|
|
|
+ // 原有删除逻辑不变
|
|
|
+ try {
|
|
|
+ let response = await fetch(`http://39.97.59.228:8000/v1/conversations/${item.id}`, {
|
|
|
+ method: 'DELETE',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: APIKEY.value,
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ user: userid,
|
|
|
+ }),
|
|
|
+ });
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+ getHistoryList();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除失败:', error);
|
|
|
+ Modal.error({
|
|
|
+ title: '删除失败',
|
|
|
+ content: '删除会话时出现错误,请稍后重试。',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
});
|
|
|
- console.log(sessionHistory.value, '历史数据');
|
|
|
- } catch (error) {
|
|
|
- console.error('获取历史数据失败:', error);
|
|
|
- // 错误处理(比如提示用户)
|
|
|
- }
|
|
|
-}
|
|
|
-//格式化消息
|
|
|
-const formatMessage = (text) => {
|
|
|
- if (!text) return '';
|
|
|
- let formatted = text
|
|
|
- // 处理换行
|
|
|
- .replace(/\n\n/g, '<br>')
|
|
|
- .replace(/\n###/g, '<br>')
|
|
|
- .replace(/###/g, '')
|
|
|
- .replace(/---/g, '')
|
|
|
- .replace(/^- /gm, '<br> - ')
|
|
|
- // 处理粗体
|
|
|
- .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
|
- // 处理斜体
|
|
|
- .replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
|
- // 处理行内代码
|
|
|
- .replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
|
- return formatted;
|
|
|
-};
|
|
|
-// 初始化按钮定位
|
|
|
-onMounted(() => {
|
|
|
- getHistoryList();
|
|
|
- // 初始化(仅执行一次)
|
|
|
- initMarked();
|
|
|
-});
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="less" scoped>
|
|
|
-.btn-header {
|
|
|
- width: 40px;
|
|
|
- height: 40px;
|
|
|
- margin-right: 5px;
|
|
|
- margin-bottom: 5px;
|
|
|
-}
|
|
|
-.container {
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
-}
|
|
|
-.mini-chat {
|
|
|
- display: flex;
|
|
|
- min-width: none;
|
|
|
- width: 950px;
|
|
|
- height: 85%;
|
|
|
- border-radius: 4px;
|
|
|
- position: fixed;
|
|
|
- top: 50px;
|
|
|
- right: 20px;
|
|
|
- padding: 10px;
|
|
|
- background-color: #0a1a2f;
|
|
|
- background-image: linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f),
|
|
|
- linear-gradient(to top, #0b69b6, #0a1a2f), linear-gradient(to right, #0b69b6, #0a1a2f);
|
|
|
- background-size: 100% 5px, 5px 100%, 100% 5px, 5px 100%;
|
|
|
- background-position: top left, top right, bottom right, bottom left;
|
|
|
- background-repeat: no-repeat;
|
|
|
- z-index: 9999999;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-.doc {
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- min-width: 600px;
|
|
|
- height: 85%;
|
|
|
- border-radius: 4px;
|
|
|
- position: fixed;
|
|
|
- top: 50px;
|
|
|
- right: 51%;
|
|
|
- padding: 10px;
|
|
|
- background-color: #0a1a2f;
|
|
|
- background-image: linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f),
|
|
|
- linear-gradient(to top, #0b69b6, #0a1a2f), linear-gradient(to right, #0b69b6, #0a1a2f);
|
|
|
- background-size: 100% 5px, 5px 100%, 100% 5px, 5px 100%;
|
|
|
- background-position: top left, top right, bottom right, bottom left;
|
|
|
- background-repeat: no-repeat;
|
|
|
- z-index: 9999999;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
+ };
|
|
|
+ const fold = () => {
|
|
|
+ isFold.value = !isFold.value;
|
|
|
+ if (!isFold.value) {
|
|
|
+ getHistoryList();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ // 获取历史会话列表
|
|
|
+ async function getHistoryList() {
|
|
|
+ sessionHistory.value = [];
|
|
|
+ try {
|
|
|
+ // 并行请求两个接口,提升效率
|
|
|
+ const [response1, response2] = await Promise.all([
|
|
|
+ // 第一个请求
|
|
|
+ fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
+ method: 'get',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: 'Bearer app-tSFRUnv0Qkbtik1dwtlhnpkd',
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ // 第二个请求
|
|
|
+ fetch(`http://39.97.59.228:8000/v1/conversations?user=${userid}`, {
|
|
|
+ method: 'get',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ Authorization: 'Bearer app-kprgsFKtySM4Wjxs0ZGzaNFN',
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ ]);
|
|
|
|
|
|
-.close {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- align-items: center;
|
|
|
- width: 100%;
|
|
|
- padding: 1px;
|
|
|
- border-radius: 8px;
|
|
|
-}
|
|
|
+ // 检查响应是否成功
|
|
|
+ if (!response1.ok || !response2.ok) {
|
|
|
+ throw new Error('接口请求失败');
|
|
|
+ }
|
|
|
|
|
|
-.closeBtn {
|
|
|
- background-color: #007bff;
|
|
|
- color: white;
|
|
|
- border: none;
|
|
|
- border-radius: 4px;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.3s ease;
|
|
|
-}
|
|
|
+ // 解析两个响应的JSON数据
|
|
|
+ const [data1, data2] = await Promise.all([response1.json(), response2.json()]);
|
|
|
|
|
|
-.closeBtn:hover {
|
|
|
- background-color: #0056b3;
|
|
|
-}
|
|
|
-.left-side {
|
|
|
- background: #0c2842;
|
|
|
- transition: width 0.5s ease; /* 平滑过渡动画 */
|
|
|
- width: 140px; /* 展开时宽度 */
|
|
|
- position: relative; /* 用于按钮定位 */
|
|
|
-}
|
|
|
-.left-side.collapsed {
|
|
|
- width: 40px; /* 折叠时宽度 */
|
|
|
-}
|
|
|
+ // 合并两个数组(核心修改:用扩展运算符合并)
|
|
|
+ // 确保 data1.data 和 data2.data 都是数组(防止接口返回异常)
|
|
|
+ const list1 = Array.isArray(data1.data) ? data1.data : [];
|
|
|
+ const list2 = Array.isArray(data2.data) ? data2.data : [];
|
|
|
|
|
|
-.addBtn {
|
|
|
- height: 30px;
|
|
|
- position: absolute;
|
|
|
- background-size: 100% 100%;
|
|
|
- background-position: center;
|
|
|
- padding: 2px;
|
|
|
- right: 10px;
|
|
|
- bottom: 40px;
|
|
|
- left: 10px;
|
|
|
- align-items: center;
|
|
|
- border-radius: 3px;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-.custom-list {
|
|
|
- height: 650px;
|
|
|
- overflow-y: auto;
|
|
|
-}
|
|
|
-.text-container {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- width: 100%;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-.btn-container {
|
|
|
- display: flex;
|
|
|
-}
|
|
|
-.jeecg-layout-header-action span[role='img'] {
|
|
|
- padding: 0;
|
|
|
-}
|
|
|
-.text-ellipsis {
|
|
|
- flex: 1;
|
|
|
-}
|
|
|
-.edit-text {
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- white-space: nowrap;
|
|
|
- width: 90px;
|
|
|
- color: #fff;
|
|
|
- font-size: 12px;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-.edit-icon {
|
|
|
- flex-shrink: 0;
|
|
|
- margin-left: auto;
|
|
|
- line-height: 23px;
|
|
|
-}
|
|
|
-.delete-icon {
|
|
|
- flex-shrink: 0;
|
|
|
- margin-left: auto;
|
|
|
- line-height: 23px;
|
|
|
-}
|
|
|
-.edit-icon:hover {
|
|
|
- color: #1890ff !important;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-.delete-icon:hover {
|
|
|
- color: #1890ff !important;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-.edit-input {
|
|
|
- font-size: 10px;
|
|
|
-}
|
|
|
-.btn-text-bg {
|
|
|
- width: 14px;
|
|
|
- height: 14px;
|
|
|
- position: absolute;
|
|
|
- background-size: 100% 100%;
|
|
|
- right: 10px;
|
|
|
- top: 10px;
|
|
|
- left: 10px;
|
|
|
- bottom: 10px;
|
|
|
-}
|
|
|
-.btn-text {
|
|
|
- margin-left: 3px;
|
|
|
- font-size: 12px;
|
|
|
- color: #fff;
|
|
|
- white-space: nowrap;
|
|
|
- margin-left: 30px;
|
|
|
- line-height: 29px;
|
|
|
-}
|
|
|
-.historyBtn {
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- position: absolute;
|
|
|
- background-size: 100% 100%;
|
|
|
- background-position: center;
|
|
|
- padding: 2px;
|
|
|
- right: 10px;
|
|
|
- top: 10px;
|
|
|
-}
|
|
|
-.historyBtn1 {
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- position: absolute;
|
|
|
- background-size: 100% 100%;
|
|
|
- background-position: center;
|
|
|
- left: 3px;
|
|
|
-}
|
|
|
-.divider0 {
|
|
|
- border-bottom: 1px solid #1074c1;
|
|
|
- width: auto;
|
|
|
- margin: 0 10px;
|
|
|
- height: 13%;
|
|
|
- display: block;
|
|
|
- background: transparent;
|
|
|
-}
|
|
|
-.foldBtn {
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- position: absolute;
|
|
|
- background-size: 100% 100%;
|
|
|
- background-position: center;
|
|
|
- padding: 2px;
|
|
|
- right: 10px;
|
|
|
- bottom: 10px;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
+ // 合并数组并赋值给 sessionHistory
|
|
|
+ sessionHistory.value = [...list1, ...list2];
|
|
|
+ sessionHistory.value.forEach((item) => {
|
|
|
+ currentSessionID.value = item.id;
|
|
|
+ });
|
|
|
+ console.log(sessionHistory.value, '历史数据');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取历史数据失败:', error);
|
|
|
+ // 错误处理(比如提示用户)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //格式化消息
|
|
|
+ const formatMessage = (text) => {
|
|
|
+ if (!text) return '';
|
|
|
+ let formatted = text
|
|
|
+ // 处理换行
|
|
|
+ .replace(/\n\n/g, '<br>')
|
|
|
+ .replace(/\n###/g, '<br>')
|
|
|
+ .replace(/###/g, '')
|
|
|
+ .replace(/---/g, '')
|
|
|
+ .replace(/^- /gm, '<br> - ')
|
|
|
+ // 处理粗体
|
|
|
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
|
+ // 处理斜体
|
|
|
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
|
+ // 处理行内代码
|
|
|
+ .replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
|
+ return formatted;
|
|
|
+ };
|
|
|
+ // 初始化按钮定位
|
|
|
+ onMounted(() => {
|
|
|
+ getHistoryList();
|
|
|
+ // 初始化(仅执行一次)
|
|
|
+ initMarked();
|
|
|
+ });
|
|
|
+</script>
|
|
|
|
|
|
-.right-side {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- width: calc(100% - 134px) !important;
|
|
|
- .title {
|
|
|
- text-align: center;
|
|
|
- font-size: 14px;
|
|
|
- padding: 5px;
|
|
|
+<style lang="less" scoped>
|
|
|
+ .btn-header {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ margin-right: 5px;
|
|
|
+ margin-bottom: 5px;
|
|
|
}
|
|
|
- .dialog-area {
|
|
|
- flex: 1;
|
|
|
- gap: 30px;
|
|
|
- overflow-y: auto;
|
|
|
- padding: 5px;
|
|
|
+ .container {
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
- color: #fff;
|
|
|
-
|
|
|
- .ask-message {
|
|
|
- padding: 10px;
|
|
|
- border-radius: 5px;
|
|
|
- background: #0c2842;
|
|
|
- max-width: 300px;
|
|
|
- min-width: 50px;
|
|
|
- white-space: pre-wrap;
|
|
|
- word-wrap: break-word;
|
|
|
- overflow-wrap: break-word;
|
|
|
- overflow: auto;
|
|
|
- line-height: 1.5;
|
|
|
- }
|
|
|
- .answer-message {
|
|
|
- padding: 10px;
|
|
|
- border-radius: 5px;
|
|
|
- background: #0c2842;
|
|
|
- max-width: 90%;
|
|
|
- }
|
|
|
+ flex-direction: row;
|
|
|
}
|
|
|
-
|
|
|
- .input-area {
|
|
|
- margin: 10px 10px 20px 10px;
|
|
|
+ .mini-chat {
|
|
|
+ display: flex;
|
|
|
+ min-width: none;
|
|
|
+ width: 950px;
|
|
|
+ height: 85%;
|
|
|
+ border-radius: 4px;
|
|
|
+ position: fixed;
|
|
|
+ top: 50px;
|
|
|
+ right: 20px;
|
|
|
padding: 10px;
|
|
|
- background-color: #043256;
|
|
|
- border: 1px solid #2cb6ff;
|
|
|
- border-radius: 5px;
|
|
|
+ background-color: #0a1a2f;
|
|
|
+ background-image:
|
|
|
+ linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f), linear-gradient(to top, #0b69b6, #0a1a2f),
|
|
|
+ linear-gradient(to right, #0b69b6, #0a1a2f);
|
|
|
+ background-size:
|
|
|
+ 100% 5px,
|
|
|
+ 5px 100%,
|
|
|
+ 100% 5px,
|
|
|
+ 5px 100%;
|
|
|
+ background-position:
|
|
|
+ top left,
|
|
|
+ top right,
|
|
|
+ bottom right,
|
|
|
+ bottom left;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ z-index: 9999999;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ .doc {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- justify-content: space-between;
|
|
|
- gap: 5px;
|
|
|
- height: 25%;
|
|
|
- }
|
|
|
- /* 文件列表容器 */
|
|
|
- .uploaded-files {
|
|
|
- padding: 8px;
|
|
|
- background-color: #f5f5f5;
|
|
|
+ min-width: 600px;
|
|
|
+ height: 85%;
|
|
|
border-radius: 4px;
|
|
|
- min-height: 40px;
|
|
|
+ position: fixed;
|
|
|
+ top: 50px;
|
|
|
+ right: 51%;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #0a1a2f;
|
|
|
+ background-image:
|
|
|
+ linear-gradient(to bottom, #0b69b6 1%, #0a1a2f 100%), linear-gradient(to left, #0b69b6, #0a1a2f), linear-gradient(to top, #0b69b6, #0a1a2f),
|
|
|
+ linear-gradient(to right, #0b69b6, #0a1a2f);
|
|
|
+ background-size:
|
|
|
+ 100% 5px,
|
|
|
+ 5px 100%,
|
|
|
+ 100% 5px,
|
|
|
+ 5px 100%;
|
|
|
+ background-position:
|
|
|
+ top left,
|
|
|
+ top right,
|
|
|
+ bottom right,
|
|
|
+ bottom left;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ z-index: 9999999;
|
|
|
+ color: #fff;
|
|
|
}
|
|
|
|
|
|
- /* 单个文件项 */
|
|
|
- .file-item {
|
|
|
- display: inline-flex;
|
|
|
+ .close {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
align-items: center;
|
|
|
- padding: 4px 8px;
|
|
|
- margin-right: 8px;
|
|
|
- margin-bottom: 8px;
|
|
|
- background-color: #fff;
|
|
|
- border: 1px solid #e9e9e9;
|
|
|
+ width: 100%;
|
|
|
+ padding: 1px;
|
|
|
+ border-radius: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .closeBtn {
|
|
|
+ background-color: #007bff;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
- /* 文件名 */
|
|
|
- .file-name {
|
|
|
- margin-left: 8px;
|
|
|
- margin-right: 8px;
|
|
|
- max-width: 150px;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
+ .closeBtn:hover {
|
|
|
+ background-color: #0056b3;
|
|
|
+ }
|
|
|
+ .left-side {
|
|
|
+ background: #0c2842;
|
|
|
+ transition: width 0.5s ease; /* 平滑过渡动画 */
|
|
|
+ width: 140px; /* 展开时宽度 */
|
|
|
+ position: relative; /* 用于按钮定位 */
|
|
|
+ }
|
|
|
+ .left-side.collapsed {
|
|
|
+ width: 40px; /* 折叠时宽度 */
|
|
|
}
|
|
|
|
|
|
- .ant-input {
|
|
|
- border: none;
|
|
|
- background-color: rgba(255, 255, 255, 0) !important;
|
|
|
- color: #fff;
|
|
|
+ .addBtn {
|
|
|
+ height: 30px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ padding: 2px;
|
|
|
+ right: 10px;
|
|
|
+ bottom: 40px;
|
|
|
+ left: 10px;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 3px;
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
|
- .ant-input:focus {
|
|
|
- border: none; /* 聚焦时无边框 */
|
|
|
- outline: none; /* 聚焦时无轮廓 */
|
|
|
- box-shadow: none; /* 移除可能存在的阴影效果 */
|
|
|
+ .custom-list {
|
|
|
+ height: 650px;
|
|
|
+ overflow-y: auto;
|
|
|
}
|
|
|
- .ctrl-btn {
|
|
|
+ .text-container {
|
|
|
display: flex;
|
|
|
- flex-direction: row;
|
|
|
justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
- .question-input {
|
|
|
- background-color: #1e293b !important;
|
|
|
- border-color: #334155 !important;
|
|
|
- color: #e2e8f0 !important;
|
|
|
- border-radius: 8px !important;
|
|
|
- padding: 12px 16px !important;
|
|
|
- font-size: 14px !important;
|
|
|
+ .btn-container {
|
|
|
+ display: flex;
|
|
|
}
|
|
|
-
|
|
|
- .question-input::placeholder {
|
|
|
- color: #64748b !important;
|
|
|
+ .jeecg-layout-header-action span[role='img'] {
|
|
|
+ padding: 0;
|
|
|
}
|
|
|
-
|
|
|
- .control-btn {
|
|
|
- height: 25px;
|
|
|
- background-color: #043256;
|
|
|
- border: 1px solid #2cb6ff;
|
|
|
+ .text-ellipsis {
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+ .edit-text {
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ width: 90px;
|
|
|
color: #fff;
|
|
|
- font-size: 10px;
|
|
|
- margin-right: 10px;
|
|
|
+ font-size: 12px;
|
|
|
cursor: pointer;
|
|
|
- transition: background-color 0.2s ease; /* 平滑过渡效果 */
|
|
|
}
|
|
|
- /* 激活状态样式(点击后) */
|
|
|
- .control-btn.active {
|
|
|
- background-color: #2cb6ff; /* 蓝色背景(Ant Design 主色) */
|
|
|
- color: white; /* 白色文字 */
|
|
|
+ .edit-icon {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-left: auto;
|
|
|
+ line-height: 23px;
|
|
|
}
|
|
|
- .control-btn1 {
|
|
|
- height: 20px;
|
|
|
- background-color: #234a6b;
|
|
|
- border: 1px solid #234a6b;
|
|
|
- color: #fff;
|
|
|
- font-size: 10px;
|
|
|
- margin-right: 10px;
|
|
|
+ .delete-icon {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-left: auto;
|
|
|
+ line-height: 23px;
|
|
|
+ }
|
|
|
+ .edit-icon:hover {
|
|
|
+ color: #1890ff !important;
|
|
|
cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
}
|
|
|
- .control-btn2 {
|
|
|
- height: 20px;
|
|
|
- background-color: #2cb6ff;
|
|
|
- border: 1px solid #2cb6ff;
|
|
|
- color: #fff;
|
|
|
- font-size: 10px;
|
|
|
- margin-right: 10px;
|
|
|
+ .delete-icon:hover {
|
|
|
+ color: #1890ff !important;
|
|
|
cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
}
|
|
|
- /* 文件上传区 */
|
|
|
- .file-upload {
|
|
|
+ .edit-input {
|
|
|
+ font-size: 10px;
|
|
|
+ }
|
|
|
+ .btn-text-bg {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
position: absolute;
|
|
|
- right: 20px;
|
|
|
- bottom: 70px;
|
|
|
- width: 180px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 10px;
|
|
|
- border: 1px solid #2cb6ff;
|
|
|
- background-color: #234a6b;
|
|
|
- border-radius: 6px;
|
|
|
- padding: 10px;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ right: 10px;
|
|
|
+ top: 10px;
|
|
|
+ left: 10px;
|
|
|
+ bottom: 10px;
|
|
|
}
|
|
|
-
|
|
|
- .input-container {
|
|
|
- position: relative;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- width: 100%;
|
|
|
+ .btn-text {
|
|
|
+ margin-left: 3px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #fff;
|
|
|
+ white-space: nowrap;
|
|
|
+ margin-left: 30px;
|
|
|
+ line-height: 29px;
|
|
|
}
|
|
|
-
|
|
|
- .file-input {
|
|
|
- flex: 1;
|
|
|
- background-color: #234a6b;
|
|
|
- border-color: #2cb6ff !important;
|
|
|
- color: #e2e8f0 !important;
|
|
|
- border-radius: 6px !important;
|
|
|
- font-size: 10px !important;
|
|
|
- padding-right: 70px !important;
|
|
|
- height: 36px !important;
|
|
|
- width: 100% !important;
|
|
|
+ .historyBtn {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ padding: 2px;
|
|
|
+ right: 10px;
|
|
|
+ top: 10px;
|
|
|
}
|
|
|
-
|
|
|
- .confirm-btn {
|
|
|
+ .historyBtn1 {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
position: absolute;
|
|
|
- right: 5px;
|
|
|
- background-color: #2cb6ff;
|
|
|
- border: none;
|
|
|
- color: #fff;
|
|
|
- border-radius: 4px;
|
|
|
- font-size: 12px;
|
|
|
- padding: 4px 10px;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ left: 3px;
|
|
|
+ }
|
|
|
+ .divider0 {
|
|
|
+ border-bottom: 1px solid #1074c1;
|
|
|
+ width: auto;
|
|
|
+ margin: 0 10px;
|
|
|
+ height: 13%;
|
|
|
+ display: block;
|
|
|
+ background: transparent;
|
|
|
+ }
|
|
|
+ .foldBtn {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ padding: 2px;
|
|
|
+ right: 10px;
|
|
|
+ bottom: 10px;
|
|
|
cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
- height: 28px;
|
|
|
}
|
|
|
|
|
|
- .confirm-btn:hover {
|
|
|
- background-color: #2cb6ff;
|
|
|
- }
|
|
|
+ .right-side {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ width: calc(100% - 134px) !important;
|
|
|
+ .title {
|
|
|
+ text-align: center;
|
|
|
+ font-size: 14px;
|
|
|
+ padding: 5px;
|
|
|
+ }
|
|
|
+ .dialog-area {
|
|
|
+ flex: 1;
|
|
|
+ gap: 30px;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 5px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ color: #fff;
|
|
|
|
|
|
- .custom-upload {
|
|
|
- width: 100%;
|
|
|
- padding: 0 !important;
|
|
|
- }
|
|
|
+ .ask-message {
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+ background: #0c2842;
|
|
|
+ max-width: 300px;
|
|
|
+ min-width: 50px;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-wrap: break-word;
|
|
|
+ overflow-wrap: break-word;
|
|
|
+ overflow: auto;
|
|
|
+ line-height: 1.5;
|
|
|
+ }
|
|
|
+ .answer-message {
|
|
|
+ padding: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+ background: #0c2842;
|
|
|
+ max-width: 90%;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- .upload-btn {
|
|
|
- background-color: #234a6b !important;
|
|
|
- border: 1px solid #2188c3 !important;
|
|
|
- color: #dbeafe !important;
|
|
|
- border-radius: 6px !important;
|
|
|
- font-size: 12px !important;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
- padding: 8px 0 !important;
|
|
|
- width: 190% !important;
|
|
|
- height: 36px !important;
|
|
|
- box-sizing: border-box !important;
|
|
|
- }
|
|
|
- .upload-btn:hover {
|
|
|
- background-color: #1f84bd !important;
|
|
|
- color: #fff !important;
|
|
|
+ .input-area {
|
|
|
+ margin: 10px 10px 20px 10px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #043256;
|
|
|
+ border: 1px solid #2cb6ff;
|
|
|
+ border-radius: 5px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 5px;
|
|
|
+ height: 25%;
|
|
|
+ }
|
|
|
+ /* 文件列表容器 */
|
|
|
+ .uploaded-files {
|
|
|
+ padding: 8px;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ border-radius: 4px;
|
|
|
+ min-height: 40px;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 单个文件项 */
|
|
|
+ .file-item {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 4px 8px;
|
|
|
+ margin-right: 8px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ background-color: #fff;
|
|
|
+ border: 1px solid #e9e9e9;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 文件名 */
|
|
|
+ .file-name {
|
|
|
+ margin-left: 8px;
|
|
|
+ margin-right: 8px;
|
|
|
+ max-width: 150px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-input {
|
|
|
+ border: none;
|
|
|
+ background-color: rgba(255, 255, 255, 0) !important;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ .ant-input:focus {
|
|
|
+ border: none; /* 聚焦时无边框 */
|
|
|
+ outline: none; /* 聚焦时无轮廓 */
|
|
|
+ box-shadow: none; /* 移除可能存在的阴影效果 */
|
|
|
+ }
|
|
|
+ .ctrl-btn {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ justify-content: space-between;
|
|
|
+ }
|
|
|
+ .question-input {
|
|
|
+ background-color: #1e293b !important;
|
|
|
+ border-color: #334155 !important;
|
|
|
+ color: #e2e8f0 !important;
|
|
|
+ border-radius: 8px !important;
|
|
|
+ padding: 12px 16px !important;
|
|
|
+ font-size: 14px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-input::placeholder {
|
|
|
+ color: #64748b !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .control-btn {
|
|
|
+ height: 25px;
|
|
|
+ background-color: #043256;
|
|
|
+ border: 1px solid #2cb6ff;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 10px;
|
|
|
+ margin-right: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: background-color 0.2s ease; /* 平滑过渡效果 */
|
|
|
+ }
|
|
|
+ /* 激活状态样式(点击后) */
|
|
|
+ .control-btn.active {
|
|
|
+ background-color: #2cb6ff; /* 蓝色背景(Ant Design 主色) */
|
|
|
+ color: white; /* 白色文字 */
|
|
|
+ }
|
|
|
+ .control-btn1 {
|
|
|
+ height: 20px;
|
|
|
+ background-color: #234a6b;
|
|
|
+ border: 1px solid #234a6b;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 10px;
|
|
|
+ margin-right: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ }
|
|
|
+ .control-btn2 {
|
|
|
+ height: 20px;
|
|
|
+ background-color: #2cb6ff;
|
|
|
+ border: 1px solid #2cb6ff;
|
|
|
+ color: #fff;
|
|
|
+ font-size: 10px;
|
|
|
+ margin-right: 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ }
|
|
|
+ /* 文件上传区 */
|
|
|
+ .file-upload {
|
|
|
+ position: absolute;
|
|
|
+ right: 20px;
|
|
|
+ bottom: 70px;
|
|
|
+ width: 180px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+ border: 1px solid #2cb6ff;
|
|
|
+ background-color: #234a6b;
|
|
|
+ border-radius: 6px;
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .input-container {
|
|
|
+ position: relative;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-input {
|
|
|
+ flex: 1;
|
|
|
+ background-color: #234a6b;
|
|
|
+ border-color: #2cb6ff !important;
|
|
|
+ color: #e2e8f0 !important;
|
|
|
+ border-radius: 6px !important;
|
|
|
+ font-size: 10px !important;
|
|
|
+ padding-right: 70px !important;
|
|
|
+ height: 36px !important;
|
|
|
+ width: 100% !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .confirm-btn {
|
|
|
+ position: absolute;
|
|
|
+ right: 5px;
|
|
|
+ background-color: #2cb6ff;
|
|
|
+ border: none;
|
|
|
+ color: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 4px 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ height: 28px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .confirm-btn:hover {
|
|
|
+ background-color: #2cb6ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .custom-upload {
|
|
|
+ width: 100%;
|
|
|
+ padding: 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-btn {
|
|
|
+ background-color: #234a6b !important;
|
|
|
+ border: 1px solid #2188c3 !important;
|
|
|
+ color: #dbeafe !important;
|
|
|
+ border-radius: 6px !important;
|
|
|
+ font-size: 12px !important;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s;
|
|
|
+ padding: 8px 0 !important;
|
|
|
+ width: 190% !important;
|
|
|
+ height: 36px !important;
|
|
|
+ box-sizing: border-box !important;
|
|
|
+ }
|
|
|
+ .upload-btn:hover {
|
|
|
+ background-color: #1f84bd !important;
|
|
|
+ color: #fff !important;
|
|
|
+ }
|
|
|
+ .custom-upload .ant-upload-select:hover .ant-btn {
|
|
|
+ border-color: #1f84bd !important;
|
|
|
+ }
|
|
|
+ .message-wrapper.ai-message-wrapper {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ }
|
|
|
+ .answerIcon {
|
|
|
+ flex: 0 0 45px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .suggestion-item {
|
|
|
+ height: 30px;
|
|
|
+ margin-left: 45px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 10px 16px;
|
|
|
+ border: 1px solid #1890ff;
|
|
|
+ color: white;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+ width: 33%;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ .thinking-section {
|
|
|
+ border-left: 3px solid #e5e7eb;
|
|
|
+ padding-left: 12px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .thinking-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 0;
|
|
|
+ cursor: pointer;
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .thinking-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #6b7280;
|
|
|
+ }
|
|
|
}
|
|
|
- .custom-upload .ant-upload-select:hover .ant-btn {
|
|
|
- border-color: #1f84bd !important;
|
|
|
+</style>
|
|
|
+<style scoped>
|
|
|
+ .zxm-popover-inner-content {
|
|
|
+ padding: 1px;
|
|
|
}
|
|
|
- .message-wrapper.ai-message-wrapper {
|
|
|
+ .message-wrapper {
|
|
|
display: flex;
|
|
|
align-items: flex-start;
|
|
|
+ position: relative;
|
|
|
}
|
|
|
- .answerIcon {
|
|
|
- flex: 0 0 45px;
|
|
|
+ .user-message-wrapper {
|
|
|
+ flex-direction: row-reverse;
|
|
|
}
|
|
|
-
|
|
|
- .suggestion-item {
|
|
|
- height: 30px;
|
|
|
- margin-left: 45px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 10px 16px;
|
|
|
- border: 1px solid #1890ff;
|
|
|
- color: white;
|
|
|
- border-radius: 4px;
|
|
|
- cursor: pointer;
|
|
|
- width: 33%;
|
|
|
- font-size: 12px;
|
|
|
+ .ai-message-wrapper {
|
|
|
+ flex-direction: row;
|
|
|
}
|
|
|
- .thinking-section {
|
|
|
- border-left: 3px solid #e5e7eb;
|
|
|
- padding-left: 12px;
|
|
|
- margin-bottom: 16px;
|
|
|
+ /* 鼠标滑过 .message-wrapper 时,显示图标 */
|
|
|
+ .ai-message-wrapper:hover .copy-icon-container {
|
|
|
+ opacity: 1;
|
|
|
+ visibility: visible;
|
|
|
}
|
|
|
-
|
|
|
- .thinking-header {
|
|
|
+ .user-message-wrapper:hover .copy-icon-container {
|
|
|
+ opacity: 1;
|
|
|
+ visibility: visible;
|
|
|
+ }
|
|
|
+ .copy-icon {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #666;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ /* 默认隐藏,hover 时显示 */
|
|
|
+ .copy-icon-container {
|
|
|
+ position: absolute;
|
|
|
display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- padding: 8px 0;
|
|
|
+ flex-direction: row;
|
|
|
+ bottom: -20px;
|
|
|
+ right: 15%;
|
|
|
+ opacity: 0;
|
|
|
+ visibility: hidden;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ z-index: 10;
|
|
|
+ }
|
|
|
+ /* 复制图标样式 */
|
|
|
+ .copy-icon {
|
|
|
+ font-size: 16px;
|
|
|
cursor: pointer;
|
|
|
- user-select: none;
|
|
|
+ margin-right: 20%;
|
|
|
+ float: right;
|
|
|
+ color: #fff;
|
|
|
}
|
|
|
-
|
|
|
- .thinking-title {
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
- color: #6b7280;
|
|
|
+ .message-wrapper:hover .copy-icon {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ .copy-icon:hover {
|
|
|
+ color: #1890ff;
|
|
|
+ }
|
|
|
+ ::v-deep table {
|
|
|
+ border-collapse: collapse;
|
|
|
+ width: 100%;
|
|
|
+ margin: 10px 0;
|
|
|
+ border: 1px solid #333;
|
|
|
+ }
|
|
|
+ ::v-deep th {
|
|
|
+ border: 1px solid #333;
|
|
|
+ background-color: #234a6b;
|
|
|
+ padding: 5px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ ::v-deep td {
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ padding: 8px 12px;
|
|
|
+ text-align: center;
|
|
|
}
|
|
|
-}
|
|
|
-</style>
|
|
|
-<style scoped>
|
|
|
-.zxm-popover-inner-content {
|
|
|
- padding: 1px;
|
|
|
-}
|
|
|
-.message-wrapper {
|
|
|
- display: flex;
|
|
|
- align-items: flex-start;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-.user-message-wrapper {
|
|
|
- flex-direction: row-reverse;
|
|
|
-}
|
|
|
-.ai-message-wrapper {
|
|
|
- flex-direction: row;
|
|
|
-}
|
|
|
-/* 鼠标滑过 .message-wrapper 时,显示图标 */
|
|
|
-.ai-message-wrapper:hover .copy-icon-container {
|
|
|
- opacity: 1;
|
|
|
- visibility: visible;
|
|
|
-}
|
|
|
-.user-message-wrapper:hover .copy-icon-container {
|
|
|
- opacity: 1;
|
|
|
- visibility: visible;
|
|
|
-}
|
|
|
-.copy-icon {
|
|
|
- font-size: 16px;
|
|
|
- color: #666;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-/* 默认隐藏,hover 时显示 */
|
|
|
-.copy-icon-container {
|
|
|
- position: absolute;
|
|
|
- display: flex;
|
|
|
- flex-direction: row;
|
|
|
- bottom: -20px;
|
|
|
- right: 15%;
|
|
|
- opacity: 0;
|
|
|
- visibility: hidden;
|
|
|
- transition: all 0.3s ease;
|
|
|
- z-index: 10;
|
|
|
-}
|
|
|
-/* 复制图标样式 */
|
|
|
-.copy-icon {
|
|
|
- font-size: 16px;
|
|
|
- cursor: pointer;
|
|
|
- margin-right: 20%;
|
|
|
- float: right;
|
|
|
- color: #fff;
|
|
|
-}
|
|
|
-.message-wrapper:hover .copy-icon {
|
|
|
- opacity: 1;
|
|
|
-}
|
|
|
-.copy-icon:hover {
|
|
|
- color: #1890ff;
|
|
|
-}
|
|
|
-::v-deep table {
|
|
|
- border-collapse: collapse;
|
|
|
- width: 100%;
|
|
|
- margin: 10px 0;
|
|
|
- border: 1px solid #333;
|
|
|
-}
|
|
|
-::v-deep th {
|
|
|
- border: 1px solid #333;
|
|
|
- background-color: #234a6b;
|
|
|
- padding: 5px;
|
|
|
- text-align: center;
|
|
|
-}
|
|
|
-::v-deep td {
|
|
|
- border: 1px solid #ddd;
|
|
|
- padding: 8px 12px;
|
|
|
- text-align: center;
|
|
|
-}
|
|
|
</style>
|
|
|
<style scoped>
|
|
|
-/* 已上传文件列表 */
|
|
|
-.file-list {
|
|
|
- margin-top: 20px;
|
|
|
-}
|
|
|
-.pre-container {
|
|
|
- flex: 1;
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- overflow: auto;
|
|
|
-}
|
|
|
-.vue-office-excel {
|
|
|
- background: #fff !important;
|
|
|
-}
|
|
|
+ /* 已上传文件列表 */
|
|
|
+ .file-list {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+ .pre-container {
|
|
|
+ flex: 1;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ }
|
|
|
+ .vue-office-excel {
|
|
|
+ background: #fff !important;
|
|
|
+ }
|
|
|
|
|
|
-.file-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 12px 15px;
|
|
|
- background-color: #ddd;
|
|
|
- border-radius: 6px;
|
|
|
- margin-bottom: 10px;
|
|
|
- transition: background-color 0.2s ease;
|
|
|
-}
|
|
|
+ .file-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12px 15px;
|
|
|
+ background-color: #ddd;
|
|
|
+ border-radius: 6px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ transition: background-color 0.2s ease;
|
|
|
+ }
|
|
|
|
|
|
-.file-item:hover {
|
|
|
- background-color: #f0f2f5;
|
|
|
-}
|
|
|
+ .file-item:hover {
|
|
|
+ background-color: #f0f2f5;
|
|
|
+ }
|
|
|
|
|
|
-.file-info {
|
|
|
- flex: 1;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
+ .file-info {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
|
|
|
-.file-name {
|
|
|
- font-size: 15px;
|
|
|
- color: #1d2129;
|
|
|
- white-space: nowrap;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- margin-bottom: 3px;
|
|
|
-}
|
|
|
+ .file-name {
|
|
|
+ font-size: 15px;
|
|
|
+ color: #1d2129;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ margin-bottom: 3px;
|
|
|
+ }
|
|
|
|
|
|
-.file-actions {
|
|
|
- display: flex;
|
|
|
- gap: 10px;
|
|
|
-}
|
|
|
+ .file-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ }
|
|
|
|
|
|
-.btn {
|
|
|
- padding: 0px 15px;
|
|
|
- margin-left: 10px;
|
|
|
- border-radius: 4px;
|
|
|
- border: none;
|
|
|
- font-size: 14px;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s ease;
|
|
|
-}
|
|
|
+ .btn {
|
|
|
+ padding: 0px 15px;
|
|
|
+ margin-left: 10px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: none;
|
|
|
+ font-size: 14px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ }
|
|
|
|
|
|
-.btn-preview {
|
|
|
- background-color: #165dff;
|
|
|
- color: white;
|
|
|
-}
|
|
|
+ .btn-preview {
|
|
|
+ background-color: #165dff;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
|
|
|
-.btn-preview:hover {
|
|
|
- background-color: #0d47a1;
|
|
|
-}
|
|
|
+ .btn-preview:hover {
|
|
|
+ background-color: #0d47a1;
|
|
|
+ }
|
|
|
|
|
|
-.btn-delete {
|
|
|
- background-color: #f2f3f5;
|
|
|
- color: #4e5969;
|
|
|
-}
|
|
|
+ .btn-delete {
|
|
|
+ background-color: #f2f3f5;
|
|
|
+ color: #4e5969;
|
|
|
+ }
|
|
|
|
|
|
-.btn-delete:hover {
|
|
|
- background-color: #e5e6eb;
|
|
|
- color: #1d2129;
|
|
|
-}
|
|
|
-</style>
|
|
|
+ .btn-delete:hover {
|
|
|
+ background-color: #e5e6eb;
|
|
|
+ color: #1d2129;
|
|
|
+ }
|
|
|
+</style>
|