Commit b856ece6 authored by chenghong_tao's avatar chenghong_tao

优化部分代码

parent 3213252c
...@@ -17,10 +17,11 @@ npm run dev ...@@ -17,10 +17,11 @@ npm run dev
## iframe/script 模式 ## iframe/script 模式
### 注意事项 ### 注意事项
1. 使用embed模式时,需要手动修改`iframePlugin/embed.js`中的域名或IP地址。 1. 使用embed模式时,引入embed.js文件时需要带URL参数
```javascript ```html
// 大概21行 <script src="/embed.min.js?url=当前前端所在的IP或域名地址"></script>
img.src = 'https://tk-test.infi-inside.com:2443/deepseek.png'; // https://tk-test.infi-inside.com:2443修改成自己的ip或域名 ```
// 大概77行 如:在本地运行前端时,引入embed.js文件时,参数则为url=http://127.0.0.1:3000
iframe.src = 'https://tk-test.infi-inside.com:2443/index.html'; // https://tk-test.infi-inside.com:2443修改成自己的ip或域名 ```html
<script src="/embed.min.js?url=http://127.0.0.1:3000"></script>
``` ```
(function() { (function() {
// 获取URL
const urlParams = new URLSearchParams(window.location.search);
const baseUrl = urlParams.get('url') || 'https://tk-test.infi-inside.com:2443';
// 创建悬浮按钮 // 创建悬浮按钮
const button = document.createElement('button'); const button = document.createElement('button');
button.id = 'dify-chatbot-bubble-button'; button.id = 'chatbot-bubble-button';
button.style.position = 'fixed'; button.style.position = 'fixed';
button.style.bottom = '20px'; button.style.bottom = '20px';
button.style.right = '20px'; button.style.right = '20px';
...@@ -18,7 +22,7 @@ ...@@ -18,7 +22,7 @@
// Create an img element // Create an img element
const img = document.createElement('img'); const img = document.createElement('img');
img.src = 'https://tk-test.infi-inside.com:2443/deepseek.png'; // Replace with the path to your image img.src = baseUrl + '/deepseek.png'; // Replace with the path to your image
img.style.width = '60px'; img.style.width = '60px';
// img.style.height = '100%'; // img.style.height = '100%';
img.style.objectFit = 'cover'; img.style.objectFit = 'cover';
...@@ -30,7 +34,7 @@ ...@@ -30,7 +34,7 @@
// 创建iframe容器 // 创建iframe容器
const iframeContainer = document.createElement('div'); const iframeContainer = document.createElement('div');
iframeContainer.id = 'dify-chatbot-bubble-window'; iframeContainer.id = 'chatbot-bubble-window';
iframeContainer.style.position = 'fixed'; iframeContainer.style.position = 'fixed';
iframeContainer.style.bottom = '80px'; iframeContainer.style.bottom = '80px';
iframeContainer.style.right = '20px'; iframeContainer.style.right = '20px';
...@@ -74,7 +78,9 @@ ...@@ -74,7 +78,9 @@
// 创建iframe // 创建iframe
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe');
iframe.src = 'https://tk-test.infi-inside.com:2443/index.html'; // 替换为你的聊天窗口页面地址 // 获取URL
iframe.src = baseUrl; // 替换为你的聊天窗口页面地址
iframe.style.width = '100%'; iframe.style.width = '100%';
iframe.style.height = '100%'; iframe.style.height = '100%';
iframe.style.border = 'none'; iframe.style.border = 'none';
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<template v-for="(item, index) in chatMessageList" :key="'chatMessage - ' + index"> <template v-for="(item, index) in chatMessageList" :key="'chatMessage - ' + index">
<div class="chat-message-item"> <div class="chat-message-item">
<aiBubble <aiBubble
:query="item.content" :query="item.query"
v-if="item.type === 'ai' || item.type === 'ai-history'" v-if="item.type === 'ai' || item.type === 'ai-history'"
:messageType="item.type" :messageType="item.type"
:content="item.content" :content="item.content"
...@@ -59,11 +59,13 @@ const userQuery = (query, isLoading=false) => { ...@@ -59,11 +59,13 @@ const userQuery = (query, isLoading=false) => {
} }
chatMessageList.value.push({ chatMessageList.value.push({
type: 'user', type: 'user',
query: '',
content: query, content: query,
}) })
chatMessageList.value.push({ chatMessageList.value.push({
type: 'ai', type: 'ai',
content: query, query,
content: null,
}) })
if (isLoading) { if (isLoading) {
inputMessageRef.value.changeLoading(true) inputMessageRef.value.changeLoading(true)
...@@ -123,10 +125,12 @@ watch(() => props.historyMsgList, (newValue) => { ...@@ -123,10 +125,12 @@ watch(() => props.historyMsgList, (newValue) => {
newValue.forEach((item) => { newValue.forEach((item) => {
chatMessageList.value.push({ chatMessageList.value.push({
type: 'user', type: 'user',
content: item?.query, query: '',
content: item.query
}) })
chatMessageList.value.push({ chatMessageList.value.push({
type: 'ai-history', type: 'ai-history',
query: item.query,
content: item?.answer, content: item?.answer,
}) })
console.log('zhisx'); console.log('zhisx');
......
...@@ -5,13 +5,28 @@ ...@@ -5,13 +5,28 @@
</template> </template>
<template #content> <template #content>
<div class="custom-bubble-content" v-adjust-width> <div class="custom-bubble-content" v-adjust-width>
<workflowItem <!-- 工作流 -->
<!-- <workflowItem
v-if="workflowContent.length > 0" v-if="workflowContent.length > 0"
:workflowContent="workflowContent" :workflowContent="workflowContent"
ref="workflowItemRef" ref="workflowItemRef"
@workflow-is-error="workflowIsError" @workflow-is-error="workflowIsError"
/> /> -->
<!-- 使用 MarkdownRenderer 组件渲染 messageContent --> <!-- 使用 MarkdownRenderer 组件渲染 messageContent -->
<div class="icon" v-if="workflowStatus && workflowStatus != 'success'">
<div v-if="workflowStatus == 'loading'">
<el-icon style="font-size: 18px" class="is-loaidng">
<Loading />
</el-icon>
<span class="is-loaidng">AI正在思考,请您稍等...</span>
</div>
<div v-if="workflowStatus == 'error'">
<el-icon style="font-size: 18px" class="is-error">
<WarningFilled />
</el-icon>
<span class="is-error">AI处理失败,请您重试或重新完善问题描述</span>
</div>
</div>
<MarkdownRenderer :content="messageContent" /> <MarkdownRenderer :content="messageContent" />
</div> </div>
...@@ -52,7 +67,7 @@ ...@@ -52,7 +67,7 @@
</template> </template>
<script setup> <script setup>
import { computed, ref, watch } from "vue"; import { computed, ref, watch } from "vue";
import { Refresh, Search, Star, DocumentCopy } from "@element-plus/icons-vue"; import { Refresh, Search, Star, DocumentCopy, Loading, SuccessFilled, WarningFilled } from "@element-plus/icons-vue";
import workflowItem from "./workflowItem.vue"; import workflowItem from "./workflowItem.vue";
import MarkdownRenderer from '../markdown/markdownRender.vue'; import MarkdownRenderer from '../markdown/markdownRender.vue';
import { Bubble, useXStream } from "vue-element-plus-x"; import { Bubble, useXStream } from "vue-element-plus-x";
...@@ -93,6 +108,8 @@ watch( ...@@ -93,6 +108,8 @@ watch(
{ immediate: true } { immediate: true }
); );
const isReStartWorkflow = ref(false)
const workflowStatus = ref(null)
// 默认支持 SSE 协议 // 默认支持 SSE 协议
async function startSSE(query) { async function startSSE(query) {
try { try {
...@@ -118,7 +135,7 @@ async function startSSE(query) { ...@@ -118,7 +135,7 @@ async function startSSE(query) {
// 机器人的 content 计算属性 // 机器人的 content 计算属性
const workflowContent = computed(() => { const workflowContent = computed(() => {
if (props.messageType === 'ai-history') { if (props.messageType === 'ai-history' && !isReStartWorkflow.value) {
return []; return [];
} }
// 以下是处理非历史消息记录 // 以下是处理非历史消息记录
...@@ -131,18 +148,25 @@ const workflowContent = computed(() => { ...@@ -131,18 +148,25 @@ const workflowContent = computed(() => {
const parsedChunk = JSON.parse(chunk); const parsedChunk = JSON.parse(chunk);
workflowList.push(parsedChunk); workflowList.push(parsedChunk);
if (parsedChunk.event === "workflow_started") { if (parsedChunk.event === "workflow_started") {
workflowStatus.value = 'loading';
taskStore.setTaskId(parsedChunk.task_id); taskStore.setTaskId(parsedChunk.task_id);
taskStore.setConversationId(parsedChunk.conversation_id); taskStore.setConversationId(parsedChunk.conversation_id);
taskStore.setMessageId(parsedChunk.message_id); taskStore.setMessageId(parsedChunk.message_id);
taskStore.setWorkflowRunId(parsedChunk.workflow_run_id); taskStore.setWorkflowRunId(parsedChunk.workflow_run_id);
} }
if (parsedChunk.event === "error") {
ElMessage.error(parsedChunk.message)
workflowStatus.value = 'error';
emit('workflow-is-error', true)
break;
}
if (parsedChunk.event === "message_end") { if (parsedChunk.event === "message_end") {
workflowStatus.value = 'success';
emits("messageFinished", true); emits("messageFinished", true);
} }
} catch (error) { } catch (error) {
// 这个 结束标识 是后端给的,所以这里这样判断
// 实际项目中,以项目需要为准 // 实际项目中,以项目需要为准
if (chunk === " [DONE]") { if (chunk === "[DONE]") {
// 处理数据结束的情况 // 处理数据结束的情况
// console.log('数据接收完毕') // console.log('数据接收完毕')
} else { } else {
...@@ -155,15 +179,19 @@ const workflowContent = computed(() => { ...@@ -155,15 +179,19 @@ const workflowContent = computed(() => {
return workflowList; return workflowList;
}); });
const workflowItemRef = ref(null); // const workflowItemRef = ref(null);
const reStartSSE = () => { const reStartSSE = () => {
workflowItemRef.value.clearWorkflow(); isReStartWorkflow.value = true;
// if(workflowItemRef.value) {
// workflowItemRef.value.clearWorkflow();
// }
startSSE(props.query); startSSE(props.query);
console.log('reStartSSE', props.query, props.messageType, props.content)
}; };
// AI回答问题 // AI回答问题
const messageContent = computed(() => { const messageContent = computed(() => {
if (props.messageType === 'ai-history') { if (props.messageType === 'ai-history' && !isReStartWorkflow.value) {
return props.content; return props.content;
} }
let messageContent = ""; let messageContent = "";
...@@ -187,11 +215,9 @@ const copyContent = () => { ...@@ -187,11 +215,9 @@ const copyContent = () => {
ElMessage.success("复制成功"); ElMessage.success("复制成功");
}; };
const workflowIsError = (isError) => { // const workflowIsError = (isError) => {
emits("workflow-is-error", isError); // emits("workflow-is-error", isError);
}; // };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.message-content { .message-content {
...@@ -207,4 +233,43 @@ const workflowIsError = (isError) => { ...@@ -207,4 +233,43 @@ const workflowIsError = (isError) => {
transform: rotate(180deg); /* 旋转 180 度 */ transform: rotate(180deg); /* 旋转 180 度 */
transition: transform 0.3s ease; /* 添加过渡效果,让旋转更平滑 */ transition: transform 0.3s ease; /* 添加过渡效果,让旋转更平滑 */
} }
.icon {
display: flex;
align-items: center;
width: 100%;
line-height: 28px;
background-color: var(--el-color-white);
padding: 5px 0px;
i {
width: 28px;
vertical-align: middle;
}
span {
flex: 1;
vertical-align: middle;
}
.is-success {
color: var(--el-color-success);
}
.is-error {
color: var(--el-color-danger);
}
.is-loaidng {
color: var(--el-color-primary);
animation: spin 1s linear infinite;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style> </style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment