|
@@ -1,119 +1,132 @@
|
|
|
<template>
|
|
|
- <div 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
|
|
|
+ <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="{
|
|
|
- padding: '8px 10px 0 8px',
|
|
|
- color: '#5e7081',
|
|
|
- fontSize: '10px',
|
|
|
- position: 'relative', // 新增定位
|
|
|
+ backgroundImage: `url(${!isFold ? '/src/assets/images/vent/home/addB.svg' : ''})`,
|
|
|
}"
|
|
|
- @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)" />
|
|
|
+ ></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>
|
|
|
|
|
|
- <!-- 输入框 -->
|
|
|
- <a-input
|
|
|
- size="small"
|
|
|
- v-else
|
|
|
- v-model:value="editText"
|
|
|
- v-focus
|
|
|
- @blur="handleSave(item)"
|
|
|
- @keyup.enter="handleSave(item)"
|
|
|
- class="edit-input"
|
|
|
- />
|
|
|
+ <!-- 系统回答样式 -->
|
|
|
+ <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>
|
|
|
- </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-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 v-else class="system-message">
|
|
|
- <div class="answerIcon"></div>
|
|
|
- <div class="answer-message">
|
|
|
- <div v-html="formatMessage(message.content)"></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 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">
|
|
|
- <!-- 文件上传按钮 -->
|
|
|
- <label class="upload-btn">
|
|
|
- <div class="send-file" />
|
|
|
- <span class="divider"> | </span>
|
|
|
- <div class="send-img" />
|
|
|
- </label>
|
|
|
- <!-- 发送按钮 -->
|
|
|
- <div class="send-btn" @click="handleSend"></div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </transition>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
@@ -123,7 +136,7 @@
|
|
|
import { EditOutlined } from '@ant-design/icons-vue';
|
|
|
// 响应式变量声明
|
|
|
const dialogVisible = ref(false);
|
|
|
- const isFold = ref(true); // 是否折叠
|
|
|
+ const isFold = ref(false); // 是否折叠
|
|
|
const inputText = ref(''); // 输入框内容
|
|
|
const historySessions = ref([]); // 消会话历史
|
|
|
const spinning = ref(false); // 加载状态
|
|
@@ -134,16 +147,26 @@
|
|
|
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; // 唯一标识(可用时间戳生成)
|
|
|
+ id: string; // 唯一标识时间戳
|
|
|
type: 'user' | 'system';
|
|
|
- content: string;
|
|
|
+ content?: string;
|
|
|
+ content1?: string;
|
|
|
+ Origintype?: string;
|
|
|
+ Origintype1?: string;
|
|
|
timestamp: number; // 排序依据
|
|
|
};
|
|
|
const messageList = ref<MessageItem[]>([]);
|
|
@@ -160,19 +183,24 @@
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- const openMenu = () => {
|
|
|
- dialogVisible.value = !dialogVisible.value;
|
|
|
- if (dialogVisible.value) {
|
|
|
- // addNew();
|
|
|
- hasCreated.value = true;
|
|
|
- }
|
|
|
- };
|
|
|
+ // 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;
|
|
@@ -291,13 +319,12 @@
|
|
|
content: inputText.value,
|
|
|
timestamp: Date.now(),
|
|
|
});
|
|
|
-
|
|
|
// 构造请求参数
|
|
|
const params = {
|
|
|
chat_session_id: session_id.value, // 替换为实际的会话 ID
|
|
|
prompt: inputText.value,
|
|
|
ref_file_ids: [],
|
|
|
- thinking_enabled: false,
|
|
|
+ thinking_enabled: isThinking.value,
|
|
|
};
|
|
|
inputText.value = ''; // 清空输入框
|
|
|
try {
|
|
@@ -323,6 +350,8 @@
|
|
|
id: `response_${Date.now()}`,
|
|
|
type: 'response', // 消息类型
|
|
|
content: '',
|
|
|
+ content1: '',
|
|
|
+ Origintype: '',
|
|
|
timestamp: Date.now(), // 时间戳用来排序
|
|
|
};
|
|
|
|
|
@@ -340,17 +369,30 @@
|
|
|
// 将流数据转换为字符串
|
|
|
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);
|
|
|
- if (data.type === 'text') {
|
|
|
+ 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();
|
|
|
}
|
|
|
}
|
|
@@ -362,14 +404,17 @@
|
|
|
}
|
|
|
} catch (error) {
|
|
|
// 请求失败时设置系统消息
|
|
|
- systemMessage.value = '服务器异常';
|
|
|
- messageList.value.push({
|
|
|
- id: `system_${Date.now()}`,
|
|
|
- type: 'system',
|
|
|
- content: systemMessage.value,
|
|
|
- timestamp: Date.now(),
|
|
|
- });
|
|
|
- console.error('请求失败:', 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; // 停止加载
|
|
|
}
|
|
@@ -424,14 +469,33 @@
|
|
|
content: item.content,
|
|
|
timestamp: Date.now(),
|
|
|
});
|
|
|
- } else {
|
|
|
+ } else if (item.role === 'assistant') {
|
|
|
// role== assistant 机器回答
|
|
|
- messageList.value.push({
|
|
|
- id: `system_${Date.now()}`,
|
|
|
- type: 'system',
|
|
|
- content: item.content,
|
|
|
- timestamp: Date.now(),
|
|
|
- });
|
|
|
+ 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);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -452,9 +516,16 @@
|
|
|
.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
|
return formatted;
|
|
|
}
|
|
|
+ const emit = defineEmits(['update:modelValue']);
|
|
|
+
|
|
|
+ const close = () => {
|
|
|
+ emit('update:modelValue', false);
|
|
|
+ };
|
|
|
+
|
|
|
// 初始化按钮定位
|
|
|
onMounted(() => {
|
|
|
sessionsHistoryList();
|
|
|
+ console.log('ssssssssss');
|
|
|
});
|
|
|
</script>
|
|
|
|
|
@@ -693,10 +764,15 @@
|
|
|
padding: 10px;
|
|
|
margin: 10px;
|
|
|
border-radius: 5px;
|
|
|
- color: #fff;
|
|
|
background: #0c2842;
|
|
|
}
|
|
|
-
|
|
|
+ .thinking-text {
|
|
|
+ color: gray;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ .answer-text {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
/** 系统返回信息**/
|
|
|
.system-message {
|
|
|
display: flex;
|
|
@@ -713,14 +789,11 @@
|
|
|
background-image: url('/@/assets/images/vent/home/answerIcon.svg');
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
- .answer-message {
|
|
|
- float: left;
|
|
|
- width: 100%;
|
|
|
- padding: 10px;
|
|
|
- margin: 10px;
|
|
|
- border-radius: 5px;
|
|
|
+ .think-area {
|
|
|
+ color: #7979799f;
|
|
|
+ }
|
|
|
+ .answer-area {
|
|
|
color: #fff;
|
|
|
- background: #0c2842;
|
|
|
}
|
|
|
.dialog-area {
|
|
|
flex: 1; /* 占据剩余空间 */
|
|
@@ -755,12 +828,42 @@
|
|
|
|
|
|
.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: 12px;
|
|
|
+ gap: 8px;
|
|
|
}
|
|
|
|
|
|
.upload-btn {
|
|
|
display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+ .upload-btn {
|
|
|
+ float: right;
|
|
|
+ display: flex;
|
|
|
cursor: pointer;
|
|
|
padding: 6px 12px;
|
|
|
}
|
|
@@ -790,7 +893,7 @@
|
|
|
.send-btn {
|
|
|
width: 20px;
|
|
|
height: 20px;
|
|
|
- margin-left: auto;
|
|
|
+ margin-left: 10px;
|
|
|
margin-right: 10px;
|
|
|
background-color: #1074c1;
|
|
|
background-image: url('/@/assets/images/vent/home/send.svg');
|