|
@@ -0,0 +1,905 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <transition name="fade">
|
|
|
+ <div v-if="visible" class="dialog-overlay">
|
|
|
+ <!-- 左侧折叠区域 -->
|
|
|
+ <div class="left-side" :class="{ collapsed: isFold }" id="leftSide">
|
|
|
+ <div
|
|
|
+ class="addBtn"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/add.svg' : ''})`,
|
|
|
+ backgroundColor: isFold ? '' : '#2cb6ff',
|
|
|
+ width: isFold ? '20px' : 'auto',
|
|
|
+ }"
|
|
|
+ @click="addNew"
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ class="btn-text-bg"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/addB.svg' : ''})`,
|
|
|
+ }"
|
|
|
+ ></span>
|
|
|
+ <span v-if="!isFold" class="btn-text">添加新对话</span>
|
|
|
+ </div>
|
|
|
+ <div class="divider0"></div>
|
|
|
+ <div
|
|
|
+ v-if="isFold"
|
|
|
+ class="historyBtn"
|
|
|
+ :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/history.svg' : '/src/assets/images/vent/home/history.svg'})` }"
|
|
|
+ @click="addNew"
|
|
|
+ ></div>
|
|
|
+ <div v-else class="historyBtn1">
|
|
|
+ <span
|
|
|
+ class="btn-text-bg"
|
|
|
+ :style="{
|
|
|
+ backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/history.svg' : ''})`,
|
|
|
+ }"
|
|
|
+ ></span>
|
|
|
+ <span v-if="!isFold" class="btn-text">历史对话</span>
|
|
|
+ <a-list style="width: 110px" :split="false" :data-source="historySessions" :scroll="200" class="custom-list">
|
|
|
+ <template #renderItem="{ item }">
|
|
|
+ <a-list-item
|
|
|
+ :style="{
|
|
|
+ padding: '8px 10px 0 8px',
|
|
|
+ color: '#5e7081',
|
|
|
+ fontSize: '10px',
|
|
|
+ position: 'relative', // 新增定位
|
|
|
+ }"
|
|
|
+ @click="sessionsHistory(item.id)"
|
|
|
+ >
|
|
|
+ <!-- 新增flex布局容器 -->
|
|
|
+ <div style="display: flex; justify-content: space-between; width: 100%">
|
|
|
+ <div v-if="editingId !== item.id" class="text-container">
|
|
|
+ <span class="edit-text">{{ item.title || '新会话' }}</span>
|
|
|
+ <edit-outlined class="edit-icon" @click="startEditing(item)" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 输入框 -->
|
|
|
+ <a-input
|
|
|
+ size="small"
|
|
|
+ v-else
|
|
|
+ v-model:value="editText"
|
|
|
+ v-focus
|
|
|
+ @blur="handleSave(item)"
|
|
|
+ @keyup.enter="handleSave(item)"
|
|
|
+ class="edit-input"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </a-list-item>
|
|
|
+ </template>
|
|
|
+ </a-list>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class="foldBtn"
|
|
|
+ :style="{ backgroundImage: `url(${isFold ? '/src/assets/images/vent/home/Fold.svg' : '/src/assets/images/vent/home/unfold.svg'})` }"
|
|
|
+ @click="fold"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ <!-- 右侧对话框 -->
|
|
|
+ <div class="right-side">
|
|
|
+ <div class="input-content">
|
|
|
+ <!-- 对话区域 -->
|
|
|
+ <div class="dialog-area">
|
|
|
+ <div v-for="message in sortedMessages" :key="message.id" :class="['message-item', message.type]">
|
|
|
+ <!-- 用户提问样式 -->
|
|
|
+ <div v-if="message.type === 'user'" class="ask-message">
|
|
|
+ <span>{{ message.content }}</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 系统回答样式 -->
|
|
|
+ <div v-else class="system-message">
|
|
|
+ <div class="answerIcon"></div>
|
|
|
+ <div class="answer-message">
|
|
|
+ <div v-if="message.Origintype === 'thinking'">
|
|
|
+ <span :id="'thinking-' + message.id" class="thinking-text" v-html="formatMessage(message.content)"></span>
|
|
|
+ </div>
|
|
|
+ <div v-if="message.Origintype1 === 'text'">
|
|
|
+ <span :id="'text-' + message.id" class="answer-text" v-html="formatMessage(message.content1)"></span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 文本输入区域 -->
|
|
|
+ <div v-if="spinning" class="thinking-area">
|
|
|
+ <span style="color: #fff">思考中···</span>
|
|
|
+ <a-spin :spinning="spinning"></a-spin>
|
|
|
+ </div>
|
|
|
+ <div class="input-area">
|
|
|
+ <textarea v-model="inputText" placeholder="请输入你的问题"> </textarea>
|
|
|
+ <!-- 底部操作栏 -->
|
|
|
+ <div class="action-bar">
|
|
|
+ <!-- 左侧深度思考按钮 -->
|
|
|
+ <div class="think-btn" :class="{ active: isThinking }" @click="toggleThinking"> <span>深度思考</span> </div>
|
|
|
+
|
|
|
+ <!-- 右侧操作按钮 -->
|
|
|
+ <div class="right-actions">
|
|
|
+ <label class="upload-btn">
|
|
|
+ <div class="send-file"></div>
|
|
|
+ <span class="divider"> | </span>
|
|
|
+ <div class="send-img"></div>
|
|
|
+ <div class="send-btn" @click="handleSend"></div>
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { ref, onMounted, unref, nextTick, computed } from 'vue';
|
|
|
+import { useUserStore } from '/@/store/modules/user';
|
|
|
+import { EditOutlined } from '@ant-design/icons-vue';
|
|
|
+// 响应式变量声明
|
|
|
+const dialogVisible = ref(false);
|
|
|
+const isFold = ref(false); // 是否折叠
|
|
|
+const inputText = ref(''); // 输入框内容
|
|
|
+const historySessions = ref([]); // 消会话历史
|
|
|
+const spinning = ref(false); // 加载状态
|
|
|
+const systemMessage = ref(''); // 系统返回信息
|
|
|
+const session_id = ref(''); // 会话id
|
|
|
+const hasCreated = ref(false); // 标志位,防止重复调用create接口
|
|
|
+const hasAdd = ref(false); // 标志位,防止重复调用create接口
|
|
|
+const userStore = useUserStore(); //获取用户信息
|
|
|
+const editingId = ref<number | null>(null);
|
|
|
+const editText = ref('');
|
|
|
+const isThinking = ref(false);
|
|
|
+interface ListItem {
|
|
|
+ id: number;
|
|
|
+ title?: string;
|
|
|
+}
|
|
|
+defineProps({
|
|
|
+ visible: {
|
|
|
+ type: Boolean,
|
|
|
+ required: true,
|
|
|
+ },
|
|
|
+});
|
|
|
+let userId = unref(userStore.getUserInfo).id;
|
|
|
+// const userId = ref(0);
|
|
|
+type MessageItem = {
|
|
|
+ id: string; // 唯一标识时间戳
|
|
|
+ type: 'user' | 'system';
|
|
|
+ content?: string;
|
|
|
+ content1?: string;
|
|
|
+ Origintype?: string;
|
|
|
+ Origintype1?: string;
|
|
|
+ timestamp: number; // 排序依据
|
|
|
+};
|
|
|
+const messageList = ref<MessageItem[]>([]);
|
|
|
+const sortedMessages = computed(() => {
|
|
|
+ return messageList.value.sort((a, b) => a.timestamp - b.timestamp);
|
|
|
+});
|
|
|
+const vFocus = {
|
|
|
+ mounted: (el: HTMLElement) => el.querySelector('input')?.focus(),
|
|
|
+};
|
|
|
+const scrollToBottom = () => {
|
|
|
+ const dialogArea = document.querySelector('.dialog-area');
|
|
|
+ if (dialogArea) {
|
|
|
+ dialogArea.scrollTop = dialogArea.scrollHeight;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// const openMenu = () => {
|
|
|
+// dialogVisible.value = props.modelValue;
|
|
|
+// // console.log(props.dialogVisible, 'ssssssssss');
|
|
|
+// if (dialogVisible.value) {
|
|
|
+// // addNew();
|
|
|
+// hasCreated.value = true;
|
|
|
+// }
|
|
|
+// };
|
|
|
+const fold = () => {
|
|
|
+ isFold.value = !isFold.value;
|
|
|
+ if (!isFold.value) {
|
|
|
+ sessionsHistoryList();
|
|
|
+ }
|
|
|
+};
|
|
|
+//启用深度思考
|
|
|
+const toggleThinking = () => {
|
|
|
+ isThinking.value = !isThinking.value;
|
|
|
+};
|
|
|
+//创建新对话
|
|
|
+async function addNew() {
|
|
|
+ hasAdd.value = !hasAdd.value;
|
|
|
+ const params = {
|
|
|
+ user_id: userId,
|
|
|
+ };
|
|
|
+ let response = await fetch('http://182.92.126.35:6005/sessions/create', {
|
|
|
+ method: 'post',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(params),
|
|
|
+ });
|
|
|
+ const data = await response.json();
|
|
|
+ session_id.value = data.id;
|
|
|
+ messageList.value = [];
|
|
|
+}
|
|
|
+//编辑标题
|
|
|
+const startEditing = (item: ListItem) => {
|
|
|
+ editingId.value = item.id;
|
|
|
+ editText.value = item.title || '';
|
|
|
+};
|
|
|
+
|
|
|
+// 保存修改
|
|
|
+const handleSave = async (item: ListItem) => {
|
|
|
+ const params = {
|
|
|
+ chat_session_id: item.id,
|
|
|
+ new_title: editText.value,
|
|
|
+ };
|
|
|
+ try {
|
|
|
+ let response = await fetch('http://182.92.126.35:6005/sessions/change_title', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(params),
|
|
|
+ });
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+ item.title = editText.value;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('保存失败:', error);
|
|
|
+ }
|
|
|
+ editingId.value = null;
|
|
|
+};
|
|
|
+//获取消息列表
|
|
|
+async function handleSend() {
|
|
|
+ if (session_id.value === '') {
|
|
|
+ await addNew();
|
|
|
+ createSessionTitle({ session_id: session_id.value, title: inputText.value });
|
|
|
+ sendMessage1();
|
|
|
+ } else {
|
|
|
+ createSessionTitle({ session_id: session_id.value, title: inputText.value });
|
|
|
+ sendMessage1();
|
|
|
+ }
|
|
|
+}
|
|
|
+//发送消息
|
|
|
+async function sendMessage() {
|
|
|
+ spinning.value = true;
|
|
|
+ // 添加用户消息
|
|
|
+ messageList.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
+ content: inputText.value,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ const params = {
|
|
|
+ chat_session_id: session_id.value,
|
|
|
+ prompt: inputText.value,
|
|
|
+ ref_file_ids: [],
|
|
|
+ thinking_enabled: false,
|
|
|
+ };
|
|
|
+ inputText.value = ''; // 清空输入框
|
|
|
+ //将用户输入的内容发送到后端
|
|
|
+ try {
|
|
|
+ // 将用户输入的内容发送到后端
|
|
|
+ let response = await fetch('http://182.92.126.35:6005/chat', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(params),
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+
|
|
|
+ const data = await response.json();
|
|
|
+ const assistantReply = data.reply.content; // 获取助手回复
|
|
|
+ // formatMessage(assistantReply);
|
|
|
+ systemMessage.value = assistantReply;
|
|
|
+
|
|
|
+ // 添加系统回答
|
|
|
+ messageList.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: systemMessage.value,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ // 请求失败时设置系统消息为"服务器异常"
|
|
|
+ systemMessage.value = '服务器异常';
|
|
|
+ console.error('请求失败:', error);
|
|
|
+ } finally {
|
|
|
+ spinning.value = false; // 无论请求成功与否,都停止加载指示器
|
|
|
+ }
|
|
|
+}
|
|
|
+//发送消息 流式响应
|
|
|
+const sendMessage1 = async () => {
|
|
|
+ spinning.value = true; // 开始加载
|
|
|
+ messageList.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
+ content: inputText.value,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ // 构造请求参数
|
|
|
+ const params = {
|
|
|
+ chat_session_id: session_id.value, // 替换为实际的会话 ID
|
|
|
+ prompt: inputText.value,
|
|
|
+ ref_file_ids: [],
|
|
|
+ thinking_enabled: isThinking.value,
|
|
|
+ };
|
|
|
+ inputText.value = ''; // 清空输入框
|
|
|
+ try {
|
|
|
+ // 发送 POST 请求
|
|
|
+ const response = await fetch('http://182.92.126.35:6005/chat_stream', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(params),
|
|
|
+ });
|
|
|
+
|
|
|
+ // 检查响应是否成功
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error('Network response was not ok');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取可读流
|
|
|
+ const reader = response.body.getReader();
|
|
|
+
|
|
|
+ // 创建一条新的消息对象
|
|
|
+ const newMessage = {
|
|
|
+ id: `response_${Date.now()}`,
|
|
|
+ type: 'response', // 消息类型
|
|
|
+ content: '',
|
|
|
+ content1: '',
|
|
|
+ Origintype: '',
|
|
|
+ timestamp: Date.now(), // 时间戳用来排序
|
|
|
+ };
|
|
|
+
|
|
|
+ // 将新消息添加到消息列表
|
|
|
+ messageList.value.push(newMessage);
|
|
|
+
|
|
|
+ // 读取流式数据
|
|
|
+ while (true) {
|
|
|
+ const { done, value } = await reader.read();
|
|
|
+ if (done) {
|
|
|
+ console.log('Stream complete');
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 将流数据转换为字符串
|
|
|
+ const chunk = new TextDecoder().decode(value);
|
|
|
+ console.log('Received chunk:', chunk);
|
|
|
+
|
|
|
+ // 使用正则表达式匹配完整的 JSON 对象
|
|
|
+ const jsonRegex = /{.*?}/g;
|
|
|
+ const matches = chunk.match(jsonRegex);
|
|
|
+ if (matches) {
|
|
|
+ matches.forEach((match) => {
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(match);
|
|
|
+ console.log(data.type, '数据类型11111111111111111');
|
|
|
+ if (data.type === 'thinking') {
|
|
|
+ // 找到当前消息对象并更新 content
|
|
|
+ const targetMessage = messageList.value.find((msg) => msg.id === newMessage.id);
|
|
|
+ if (targetMessage) {
|
|
|
+ targetMessage.content += data.content; // 追加内容
|
|
|
+ targetMessage.Origintype = data.type;
|
|
|
+ scrollToBottom();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (data.type === 'text') {
|
|
|
+ // 找到当前消息对象并更新 content
|
|
|
+ const targetMessage = messageList.value.find((msg) => msg.id === newMessage.id);
|
|
|
+ if (targetMessage) {
|
|
|
+ targetMessage.content1 += data.content; // 追加内容
|
|
|
+ targetMessage.Origintype1 = data.type;
|
|
|
+ scrollToBottom();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to parse JSON:', error);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ // 请求失败时设置系统消息
|
|
|
+ if (!response || !response.ok) {
|
|
|
+ systemMessage.value = '服务器异常';
|
|
|
+ messageList.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: systemMessage.value,
|
|
|
+ Origintype: 'text',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ console.error('请求失败:', error);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ spinning.value = false; // 停止加载
|
|
|
+ }
|
|
|
+};
|
|
|
+//创建标题
|
|
|
+async function createSessionTitle({ session_id, title }) {
|
|
|
+ const params = {
|
|
|
+ chat_session_id: session_id,
|
|
|
+ prompt: title,
|
|
|
+ };
|
|
|
+ let response = await fetch('http://182.92.126.35:6005/sessions/title', {
|
|
|
+ method: 'post',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(params),
|
|
|
+ });
|
|
|
+ const data = await response.json();
|
|
|
+}
|
|
|
+//获取会话历史
|
|
|
+async function sessionsHistoryList() {
|
|
|
+ const params = {
|
|
|
+ user_id: userId,
|
|
|
+ };
|
|
|
+ let response = await fetch(`http://182.92.126.35:6005/sessions`, {
|
|
|
+ method: 'post',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify(params),
|
|
|
+ });
|
|
|
+ const data = await response.json();
|
|
|
+ historySessions.value = data.chat_sessions;
|
|
|
+}
|
|
|
+//获取具体会话记录
|
|
|
+async function sessionsHistory(id: string) {
|
|
|
+ let response = await fetch(`http://182.92.126.35:6005/sessions/history_chat/?chat_session_id=${id}`, {
|
|
|
+ method: 'get',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ });
|
|
|
+ const data = await response.json();
|
|
|
+ if (data.chat_messages.length > 0) {
|
|
|
+ messageList.value = [];
|
|
|
+ data.chat_messages.forEach((item: any) => {
|
|
|
+ // role== user 用户提问
|
|
|
+ if (item.role === 'user') {
|
|
|
+ messageList.value.push({
|
|
|
+ id: `user_${Date.now()}`,
|
|
|
+ type: 'user',
|
|
|
+ content: item.content,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ } else if (item.role === 'assistant') {
|
|
|
+ // role== assistant 机器回答
|
|
|
+ if (item.thinking_enabled) {
|
|
|
+ messageList.value.push({
|
|
|
+ id: `system_${Date.now()}_thinking`,
|
|
|
+ type: 'system',
|
|
|
+ content: item.thinking_content,
|
|
|
+ Origintype: 'thinking',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ messageList.value.push({
|
|
|
+ id: `system_${Date.now()}_text`,
|
|
|
+ type: 'system',
|
|
|
+ content1: item.content,
|
|
|
+ Origintype1: 'text',
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ messageList.value.push({
|
|
|
+ id: `system_${Date.now()}`,
|
|
|
+ type: 'system',
|
|
|
+ content: item.content,
|
|
|
+ timestamp: Date.now(),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(item.content);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+//格式化消息
|
|
|
+function formatMessage(text: string) {
|
|
|
+ let formatted = text
|
|
|
+ // 处理换行
|
|
|
+ .replace(/\n\n/g, '<br>')
|
|
|
+ .replace(/\n###/g, '<br> ')
|
|
|
+ .replace(/###/g, '')
|
|
|
+ .replace(/---/g, '')
|
|
|
+ // 处理粗体
|
|
|
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
|
|
+ // 处理斜体
|
|
|
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
|
|
|
+ // 处理行内代码
|
|
|
+ .replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
|
+ return formatted;
|
|
|
+}
|
|
|
+const emit = defineEmits(['update:modelValue']);
|
|
|
+
|
|
|
+const close = () => {
|
|
|
+ emit('update:modelValue', false);
|
|
|
+};
|
|
|
+
|
|
|
+// 初始化按钮定位
|
|
|
+onMounted(() => {
|
|
|
+ sessionsHistoryList();
|
|
|
+ console.log('ssssssssss');
|
|
|
+});
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+@keyframes menuShow {
|
|
|
+ 0% {
|
|
|
+ width: 0;
|
|
|
+ height: 0;
|
|
|
+ }
|
|
|
+ 100% {
|
|
|
+ width: 480px;
|
|
|
+ height: 100vh;
|
|
|
+ }
|
|
|
+}
|
|
|
+.custom-list {
|
|
|
+ height: 360px;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+/* 穿透组件作用域 */
|
|
|
+::v-deep .custom-list {
|
|
|
+ scrollbar-width: thin;
|
|
|
+ scrollbar-color: #1890ff #f0f0f0;
|
|
|
+ &::-webkit-scrollbar {
|
|
|
+ width: 4px;
|
|
|
+ height: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-thumb {
|
|
|
+ background: #1890ff;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ &::-webkit-scrollbar-track {
|
|
|
+ background: #f0f0f0;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+}
|
|
|
+::v-deep .zxm-list-items {
|
|
|
+ color: #1890ff;
|
|
|
+}
|
|
|
+::v-deep .zxm-list-item:hover {
|
|
|
+ text-decoration: underline;
|
|
|
+ color: #1890ff !important;
|
|
|
+}
|
|
|
+.text-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.text-ellipsis {
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+.edit-text {
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+.edit-icon {
|
|
|
+ flex-shrink: 0;
|
|
|
+ cursor: pointer;
|
|
|
+ margin-left: auto;
|
|
|
+}
|
|
|
+.edit-input {
|
|
|
+ font-size: 10px;
|
|
|
+}
|
|
|
+.trigger-button {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 10px;
|
|
|
+ right: 10px;
|
|
|
+ z-index: 1000000;
|
|
|
+ .icon {
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ position: relative;
|
|
|
+ background-image: url('/@/assets/images/vent/home/wakeBtn.png');
|
|
|
+ background-position: center;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
+.dialog-overlay {
|
|
|
+ width: 32%;
|
|
|
+ height: 55%;
|
|
|
+ z-index: 999;
|
|
|
+ display: flex;
|
|
|
+ position: fixed;
|
|
|
+ right: 90px;
|
|
|
+ bottom: 20px;
|
|
|
+ box-shadow: 0 0 3px 3px #1074c1;
|
|
|
+ background-color: #09172c;
|
|
|
+}
|
|
|
+
|
|
|
+/* 遮罩层淡入淡出 */
|
|
|
+.fade-enter-active,
|
|
|
+.fade-leave-active {
|
|
|
+ transition: opacity 0.3s;
|
|
|
+}
|
|
|
+.fade-enter-from,
|
|
|
+.fade-leave-to {
|
|
|
+ opacity: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 弹窗缩放动画 */
|
|
|
+.scale-enter-active,
|
|
|
+.scale-leave-active {
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+.scale-enter-from {
|
|
|
+ transform: scale(0.5) translate(-50%, -50%);
|
|
|
+ opacity: 0;
|
|
|
+}
|
|
|
+.scale-leave-to {
|
|
|
+ transform: scale(1.2) translate(-50%, -50%);
|
|
|
+ opacity: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.left-side {
|
|
|
+ background: #0c2842;
|
|
|
+ transition: width 0.5s ease; /* 平滑过渡动画 */
|
|
|
+ width: 120px; /* 展开时宽度 */
|
|
|
+ position: relative; /* 用于按钮定位 */
|
|
|
+}
|
|
|
+.left-side.collapsed {
|
|
|
+ width: 40px; /* 折叠时宽度 */
|
|
|
+}
|
|
|
+
|
|
|
+.addBtn {
|
|
|
+ height: 30px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ padding: 2px;
|
|
|
+ right: 10px;
|
|
|
+ top: 10px;
|
|
|
+ left: 10px;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 3px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.btn-text-bg {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ right: 10px;
|
|
|
+ top: 9px;
|
|
|
+ left: 10px;
|
|
|
+ bottom: 10px;
|
|
|
+}
|
|
|
+.btn-text {
|
|
|
+ margin-left: 3px;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #fff;
|
|
|
+ white-space: nowrap;
|
|
|
+ margin-left: 30px;
|
|
|
+ line-height: 26px;
|
|
|
+}
|
|
|
+.historyBtn {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ padding: 2px;
|
|
|
+ right: 10px;
|
|
|
+ top: 100px;
|
|
|
+}
|
|
|
+.historyBtn1 {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ position: absolute;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-position: center;
|
|
|
+ left: 3px;
|
|
|
+ top: 80px;
|
|
|
+}
|
|
|
+.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;
|
|
|
+}
|
|
|
+
|
|
|
+.right-side {
|
|
|
+ flex: 1; /* 占据剩余空间 */
|
|
|
+ background: #09172c;
|
|
|
+}
|
|
|
+
|
|
|
+.input-content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: flex-end; /* 内容底部对齐 */
|
|
|
+ height: 100%;
|
|
|
+ padding: 20px; /* 统一内边距 */
|
|
|
+}
|
|
|
+.ask-message {
|
|
|
+ align-self: flex-end;
|
|
|
+ float: right;
|
|
|
+ max-width: 70%;
|
|
|
+ padding: 10px;
|
|
|
+ margin: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+ color: #fff;
|
|
|
+ background: #0c2842;
|
|
|
+ align-self: flex-end; /* 右侧对齐:ml-citation{ref="2" data="citationList"} */
|
|
|
+}
|
|
|
+.answer {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+}
|
|
|
+.answerIcon {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-top: 10px;
|
|
|
+ width: 35px;
|
|
|
+ height: 35px;
|
|
|
+ background-image: url('/@/assets/images/vent/home/answerIcon.svg');
|
|
|
+ background-size: 100% 100%;
|
|
|
+}
|
|
|
+.answer-message {
|
|
|
+ float: left;
|
|
|
+ padding: 10px;
|
|
|
+ margin: 10px;
|
|
|
+ border-radius: 5px;
|
|
|
+ background: #0c2842;
|
|
|
+}
|
|
|
+.thinking-text {
|
|
|
+ color: gray;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+.answer-text {
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+/** 系统返回信息**/
|
|
|
+.system-message {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-self: flex-start;
|
|
|
+ width: 100%;
|
|
|
+ padding: 12px;
|
|
|
+ display: flex;
|
|
|
+}
|
|
|
+.answerIcon {
|
|
|
+ margin-top: 10px;
|
|
|
+ width: 35px;
|
|
|
+ height: 35px;
|
|
|
+ background-image: url('/@/assets/images/vent/home/answerIcon.svg');
|
|
|
+ background-size: 100% 100%;
|
|
|
+}
|
|
|
+.think-area {
|
|
|
+ color: #7979799f;
|
|
|
+}
|
|
|
+.answer-area {
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+.dialog-area {
|
|
|
+ flex: 1; /* 占据剩余空间 */
|
|
|
+ gap: 50px; /* 消息块间隔统一控制 */
|
|
|
+ overflow-y: auto; /* 垂直滚动条 */
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+.loading-wrapper,
|
|
|
+.content-wrapper {
|
|
|
+ min-height: 40px;
|
|
|
+}
|
|
|
+.message-item.user {
|
|
|
+ margin-bottom: 50px;
|
|
|
+}
|
|
|
+.input-area {
|
|
|
+ background-color: #043256 !important;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+textarea {
|
|
|
+ background-color: #043256 !important;
|
|
|
+ width: 100%;
|
|
|
+ height: 40px;
|
|
|
+ border: none;
|
|
|
+ resize: none;
|
|
|
+ outline: none;
|
|
|
+ overflow: hidden;
|
|
|
+ padding: 10px; /* 统一内边距 */
|
|
|
+ color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+.action-bar {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.think-btn {
|
|
|
+ border: 1px solid #ccc;
|
|
|
+ width: 120px;
|
|
|
+ height: 20px;
|
|
|
+ line-height: 20px;
|
|
|
+ text-align: center;
|
|
|
+ border-radius: 50px;
|
|
|
+ cursor: pointer;
|
|
|
+ background: white;
|
|
|
+ transition: background 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+.think-btn.active {
|
|
|
+ background: #1890ff;
|
|
|
+ color: white;
|
|
|
+ border-color: #1890ff;
|
|
|
+}
|
|
|
+.right-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.upload-btn {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+.upload-btn {
|
|
|
+ float: right;
|
|
|
+ display: flex;
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 6px 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.divider {
|
|
|
+ color: #ccc;
|
|
|
+ font-weight: 300;
|
|
|
+ margin: 0 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.send-file {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ background-image: url('/@/assets/images/vent/home/sendFile.svg');
|
|
|
+ background-size: 100% 100%;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.send-img {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ background-image: url('/@/assets/images/vent/home/sendImg.svg');
|
|
|
+ background-size: 100% 100%;
|
|
|
+ border-radius: 4px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+.send-btn {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ margin-left: 10px;
|
|
|
+ margin-right: 10px;
|
|
|
+ background-color: #1074c1;
|
|
|
+ background-image: url('/@/assets/images/vent/home/send.svg');
|
|
|
+ background-position: center;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ border-radius: 2px;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+</style>
|