|
|
@@ -18,7 +18,7 @@
|
|
|
<div :class="['dialog-item', isSetting ? '' : 'dialog-absolute', 'dialog-item-' + key]"
|
|
|
v-for="(item, key) in dialogList" :key="item.client_gen_id" :data-item-index="key"
|
|
|
:data-item-key="item.client_gen_id">
|
|
|
- <div class="dialog-header" v-if="item?.question">
|
|
|
+ <div class="dialog-header header-question" v-if="item?.question">
|
|
|
<!-- <div class="user-name" style="background-color: rgb(255, 110, 110)">
|
|
|
{{ userName.slice(0, 1) }}
|
|
|
</div> -->
|
|
|
@@ -31,21 +31,16 @@
|
|
|
:agentId="agentId" />
|
|
|
</div>
|
|
|
<!-- 如果是代码则不显示头像 -->
|
|
|
- <div class="dialog-header" v-if="item?.capacity">
|
|
|
+ <div class="dialog-header header-answer" :class="{ 'has-code': item.code || item.sql }"
|
|
|
+ v-if="item?.capacity">
|
|
|
<img alt="avatar" src="/src/assets/slider/logo.jpg" width="28" />
|
|
|
</div>
|
|
|
|
|
|
- <!-- 没有处理答案则渲染代码和sql -->
|
|
|
+ <!-- 处理python代码和SQL代码 -->
|
|
|
<div class="dialog-code-footer" v-if="item.code || item.sql">
|
|
|
- <div v-if="item.code">
|
|
|
- <p class="dialog-code-footer-title" v-if="item.code">Python:</p>
|
|
|
- <span v-html="item.code"></span>
|
|
|
- </div>
|
|
|
+ <CodeDisplay v-if="item.code" type="python" :code="item.code" :index="key" />
|
|
|
|
|
|
- <div v-if="item.sql">
|
|
|
- <p class="dialog-code-footer-title" v-if="item.sql">SQL:</p>
|
|
|
- <span v-html="item.sql"></span>
|
|
|
- </div>
|
|
|
+ <CodeDisplay v-if="item.sql" type="sql" :code="item.sql" :index="key" />
|
|
|
</div>
|
|
|
|
|
|
<div class="dialog-footer" v-else-if="item?.capacity"
|
|
|
@@ -146,33 +141,7 @@
|
|
|
|
|
|
<section>
|
|
|
<DocRefItem v-for="(knowledge, knowledgeIndex) in item.reference" :key="knowledgeIndex"
|
|
|
- :knowledge="knowledge" :knowledgeIndex="knowledgeIndex" @go-document="goKnowledge" />
|
|
|
-
|
|
|
- <!-- <div v-for="(knowledge, knowledgeIndex) in item.reference" :key="knowledgeIndex">
|
|
|
- <a-popover :title="knowledge.doc_name" position="left">
|
|
|
- <div @click="openDocuementPage(knowledge)">
|
|
|
- <span>{{ knowledgeIndex + 1 }}</span>
|
|
|
- <span>{{ knowledge.doc_name }}</span>
|
|
|
- </div>
|
|
|
- <template #content>
|
|
|
- <div class="knowledge-detail">
|
|
|
- <div class="knowledge-content">
|
|
|
- {{ knowledge.content_ltks }}
|
|
|
- </div>
|
|
|
- <div class="knowledge-footer">
|
|
|
- <p>检索片段</p>
|
|
|
- <p>
|
|
|
- <span v-for="(content, contentIndex) in knowledge.content_ltks" :key="contentIndex"
|
|
|
- :class="{ active: knowledge.page === contentIndex }" @click="() => {
|
|
|
- knowledge.page = contentIndex;
|
|
|
- }
|
|
|
- ">{{ contentIndex + 1 }}</span>
|
|
|
- </p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- </a-popover>
|
|
|
- </div> -->
|
|
|
+ :knowledge="knowledge" :knowledgeIndex="knowledgeIndex" />
|
|
|
</section>
|
|
|
</div>
|
|
|
|
|
|
@@ -261,9 +230,12 @@ import formatDialogItem from './dialog-wrap/format-dialog-item';
|
|
|
|
|
|
import DocRefItem from './dialog-wrap/DocRefItem.vue';
|
|
|
|
|
|
+import CodeDisplay from './dialog-wrap/CodeDisplay.vue';
|
|
|
+
|
|
|
import { useImageUploadStatus } from '../store/index';
|
|
|
|
|
|
-import { applyHighLight } from './answer-effect/marked-lib';
|
|
|
+import { formatCodePython, formatCodeSql } from './dialog-wrap/format-code-string';
|
|
|
+
|
|
|
|
|
|
export default defineComponent({
|
|
|
components: {
|
|
|
@@ -275,7 +247,8 @@ export default defineComponent({
|
|
|
Question,
|
|
|
Answer,
|
|
|
|
|
|
- DocRefItem
|
|
|
+ DocRefItem,
|
|
|
+ CodeDisplay
|
|
|
},
|
|
|
props: [
|
|
|
"open",
|
|
|
@@ -324,50 +297,7 @@ export default defineComponent({
|
|
|
const tempHistory = initTempHistory();
|
|
|
|
|
|
const list = props.list.map((item, index) => {
|
|
|
- // const client_id = generateClientUniqueId(JSON.stringify(item));
|
|
|
-
|
|
|
return formatDialogItem(item, index);
|
|
|
-
|
|
|
- // return {
|
|
|
- // /**
|
|
|
- // * 客户端生成的ID
|
|
|
- // * 此数据作为列表唯一的key
|
|
|
- // */
|
|
|
- // client_gen_id: client_id,
|
|
|
-
|
|
|
- // answer: item.answer && replaceCode(item.answer, index),
|
|
|
- // question: item.question,
|
|
|
- // // 如果有引用
|
|
|
- // reference: (item.reference && item.reference.chunks && item.reference.doc_aggs) ? item.reference.doc_aggs.map(reference => {
|
|
|
- // return {
|
|
|
- // id: reference.doc_id,
|
|
|
- // doc_name: reference.doc_name,
|
|
|
- // page: 0,
|
|
|
- // content_ltks: [item.reference.chunks.find(chunk => chunk.doc_id == reference.doc_id).content_ltks]
|
|
|
- // }
|
|
|
- // }) : [],
|
|
|
-
|
|
|
- // // 完整的引用数据: pdf文档引用效果需要使用此数据
|
|
|
- // fullRefference: item.reference,
|
|
|
- // // 合法可用的图片地址
|
|
|
- // fullImageUrls: (() => {
|
|
|
- // if(item.images){
|
|
|
- // return item.images;
|
|
|
- // }else{
|
|
|
- // return [];
|
|
|
- // }
|
|
|
- // })(),
|
|
|
-
|
|
|
- // // 如果有文件处理完拿第一个,后续有变动再改
|
|
|
- // file: (item.files && item.files.length > 0) ? item.files.map(item => {
|
|
|
- // const port = window.location.port ? `:${window.location.port}` : '';
|
|
|
- // return {
|
|
|
- // file_type: 'word',
|
|
|
- // file_name: item.file_name,
|
|
|
- // file_url: `http://${window.location.hostname}${port}${item.file_url}`,
|
|
|
- // }
|
|
|
- // })[0] : null,
|
|
|
- // }
|
|
|
});
|
|
|
|
|
|
const dialogList = reactive(list || []);
|
|
|
@@ -552,18 +482,6 @@ export default defineComponent({
|
|
|
onMounted(() => {
|
|
|
scrollToEnd();
|
|
|
|
|
|
- // 应用代码高亮效果
|
|
|
- setTimeout(() => {
|
|
|
- applyHighLight();
|
|
|
- });
|
|
|
-
|
|
|
- /**
|
|
|
- * 应用引用效果
|
|
|
- * 答案中 引用图标(感叹号)点击时,浮层显示引用内容
|
|
|
- */
|
|
|
- // applyRefferencePopover(props.list);
|
|
|
- // applyImageDisplay(props.list);
|
|
|
-
|
|
|
/**
|
|
|
* TODO: 只允许保存一条消息,避免出现多条消息同时发送的情况
|
|
|
* 当网络状况不佳,用户发送第一条数据时,socket还未连接成功,紧接着又发送第二条数据,第三条数据....
|
|
|
@@ -625,7 +543,16 @@ export default defineComponent({
|
|
|
// 创建socket链接
|
|
|
createSocket();
|
|
|
|
|
|
- let temp_step_msg = '';
|
|
|
+ // let temp_step_msg = '';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 当 websocket 返回的消息 type=stream 时
|
|
|
+ * 消息内容是一个字一个字返回的,此时,前端需要将返回的消息进行拼接,以显示完整的消息内容
|
|
|
+ * 当 type=stream 消息开始时,temp_stream_msg = temp_stream_msg + new_message,
|
|
|
+ * 并在界面上显示 temp_stream_msg 对应的值
|
|
|
+ * 当 type=stream 类型的消息结束时,应清空 temp_stream_msg 数据内容
|
|
|
+ * 以便下一次 type=stream 的消息来临时继续重新拼接
|
|
|
+ */
|
|
|
let temp_stream_msg = '';
|
|
|
|
|
|
/**
|
|
|
@@ -695,13 +622,13 @@ export default defineComponent({
|
|
|
// }
|
|
|
|
|
|
if (step_message) {
|
|
|
- temp_step_msg = temp_step_msg + step_message
|
|
|
- temp_stream_msg = '';
|
|
|
+ temp_stream_msg = temp_stream_msg + step_message
|
|
|
+ // temp_stream_msg = '';
|
|
|
|
|
|
- answerData.value = getHTML(temp_step_msg);
|
|
|
+ answerData.value = getHTML(temp_stream_msg);
|
|
|
} else {
|
|
|
temp_stream_msg = temp_stream_msg + message;
|
|
|
- temp_step_msg = '';
|
|
|
+ // temp_step_msg = '';
|
|
|
|
|
|
answerData.value = getHTML(temp_stream_msg);
|
|
|
}
|
|
|
@@ -714,6 +641,10 @@ export default defineComponent({
|
|
|
|
|
|
addAnswer(loadingData.value);
|
|
|
}
|
|
|
+ } else {
|
|
|
+ // 当 type 不是 stream 时,及时清理此值
|
|
|
+ // 避免下一次拼接时,与前一次拼接冲突
|
|
|
+ temp_stream_msg = ''
|
|
|
}
|
|
|
|
|
|
if (msg_type === "message") {
|
|
|
@@ -761,25 +692,13 @@ export default defineComponent({
|
|
|
}
|
|
|
|
|
|
if (code) {
|
|
|
- const python_code = `
|
|
|
- <pre>
|
|
|
- <code class="hljs language-python">
|
|
|
-${code}
|
|
|
- </code>
|
|
|
- </pre>
|
|
|
- `
|
|
|
+ const python_code = formatCodePython(code);
|
|
|
|
|
|
answerCode.value = python_code;
|
|
|
}
|
|
|
|
|
|
if (sql) {
|
|
|
- const sql_code = `
|
|
|
- <pre>
|
|
|
- <code class="hljs language-sql">
|
|
|
-${sql}
|
|
|
- </code>
|
|
|
- </pre>
|
|
|
- `
|
|
|
+ const sql_code = formatCodeSql(sql);
|
|
|
|
|
|
answerSql.value = sql_code;
|
|
|
}
|
|
|
@@ -806,10 +725,10 @@ ${sql}
|
|
|
|
|
|
addAnswer(loadingData.value);
|
|
|
|
|
|
- // 应用代码高亮效果
|
|
|
- setTimeout(() => {
|
|
|
- applyHighLight();
|
|
|
- });
|
|
|
+ // // 应用代码高亮效果
|
|
|
+ // setTimeout(() => {
|
|
|
+ // applyHighLight();
|
|
|
+ // });
|
|
|
}
|
|
|
|
|
|
if (msg_type === "close") {
|
|
|
@@ -893,6 +812,13 @@ ${sql}
|
|
|
* 通过websocket进行发送
|
|
|
*/
|
|
|
helper.$bus.on("send-message", (value) => {
|
|
|
+ /**
|
|
|
+ * 当消息发送时,及时清理stream的拼接值
|
|
|
+ * 避免本次发送,返回的stream数据与前一次返回的stream数据冲突
|
|
|
+ */
|
|
|
+ temp_stream_msg = '';
|
|
|
+
|
|
|
+
|
|
|
dialogConfig.value = helper.dialogConfig;
|
|
|
|
|
|
const chat_type = props.type; // 对话的类型
|
|
|
@@ -963,6 +889,23 @@ ${sql}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
+ /**
|
|
|
+ * 检查对话数据记录中是否存在代码(python或者SQL)
|
|
|
+ * 在 智能数据 显示代码时,不用显示用户头像
|
|
|
+ **/
|
|
|
+ const chatItemHasCode = (item) => {
|
|
|
+ if (item) {
|
|
|
+ if (item.code || item.sql) {
|
|
|
+ console.log('有代码');
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('无代码');
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
return {
|
|
|
dialogId: props.dialogId,
|
|
|
|
|
|
@@ -992,7 +935,8 @@ ${sql}
|
|
|
// openDocuementPage,
|
|
|
|
|
|
show_info_btns,
|
|
|
- chat_type
|
|
|
+
|
|
|
+ chatItemHasCode
|
|
|
};
|
|
|
}
|
|
|
});
|
|
|
@@ -1008,6 +952,11 @@ ${sql}
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/* 如果有代码,则不显示头像 */
|
|
|
+.dialog-header.header-answer.has-code {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
</style>
|
|
|
<style scoped>
|
|
|
/* .user-name {
|
|
|
@@ -1203,21 +1152,26 @@ ${sql}
|
|
|
|
|
|
.dialog-code-footer {
|
|
|
border-radius: 12px;
|
|
|
- font-size: 14px;
|
|
|
- background-color: #32333c;
|
|
|
- color: #BCBDC3;
|
|
|
- margin: 16px 0;
|
|
|
- margin-left: 50px;
|
|
|
+ background-color: rgba(245, 247, 254, 1);
|
|
|
+ padding: 12px 16px;
|
|
|
+ /* margin: 16px 0; */
|
|
|
+ margin: 16px 0 16px 50px;
|
|
|
+ }
|
|
|
|
|
|
- .dialog-code-footer-title {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- /* margin: 12px 0; */
|
|
|
- width: 100%;
|
|
|
- /* padding: 4px 8px; */
|
|
|
- /* background-color: #3D3F3F; */
|
|
|
- color: #BCBDC3;
|
|
|
- }
|
|
|
+ /* .dialog-code-footer {
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ background-color: #32333c;
|
|
|
+ color: #BCBDC3;
|
|
|
+ margin: 16px 0;
|
|
|
+ margin-left: 50px;
|
|
|
+
|
|
|
+ .dialog-code-footer-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ width: 100%;
|
|
|
+ color: #BCBDC3;
|
|
|
+ }
|
|
|
|
|
|
&:nth-child(2) {
|
|
|
background-color: rgba(0, 0, 0, 0);
|
|
|
@@ -1348,13 +1302,13 @@ ${sql}
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
|
|
|
- &:nth-child(odd) {
|
|
|
- margin-right: 4%;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ &:nth-child(odd) {
|
|
|
+ margin-right: 4%;
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
}
|
|
|
+ } */
|
|
|
}
|
|
|
|
|
|
.dialog-flex-wrap {
|