DialogWrap.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305
  1. <template>
  2. <div class="dialog-flex-wrap">
  3. <div class="dialog-flex" ref="dialogScroll">
  4. <div class="section-open" v-if="open">
  5. <header class="dialog-header">
  6. <a-avatar shape="circle" :size="28">
  7. <img alt="avatar" src="/src/assets/slider/logo.jpg" />
  8. </a-avatar>
  9. </header>
  10. <footer class="dialog-footer">
  11. <p>{{ open }}</p>
  12. </footer>
  13. </div>
  14. <div class="section-suggestions">
  15. <div :class="['section-suggestion', readonly ? 'disabled' : '']" v-for="(suggestion, key) in suggestions"
  16. :key="key" @click="selectSuggestion(suggestion)">
  17. {{ suggestion }}<icon-arrow-right />
  18. </div>
  19. </div>
  20. <div class="dialog-list" v-if="dialogList.length > 0" :key="randomKey">
  21. <div :class="['dialog-item', isSetting ? '' : 'dialog-absolute']" v-for="(item, key) in dialogList"
  22. :key="item.key">
  23. <div class="dialog-header" v-if="item?.question">
  24. <div class="user-name" style="background-color: rgb(255, 110, 110)">
  25. {{ userName.slice(0, 1) }}
  26. </div>
  27. </div>
  28. <div class="dialog-footer" v-if="item?.question">
  29. <p style="margin-top: 0">{{ item.question }}</p>
  30. </div>
  31. <!-- 如果是代码则不显示头像 -->
  32. <div class="dialog-header" v-if="item?.capacity && !(item.code || item.sql)">
  33. <img alt="avatar" src="/src/assets/slider/logo.jpg" width="28" />
  34. </div>
  35. <!-- 没有处理答案则渲染代码和sql -->
  36. <div class="dialog-code-footer" v-if="item.code || item.sql">
  37. <div v-if="item.code">
  38. <p class="dialog-code-footer-title" v-if="item.code">Python:</p>
  39. <span v-html="item.code"></span>
  40. </div>
  41. <div v-if="item.sql">
  42. <p class="dialog-code-footer-title" v-if="item.sql">SQL:</p>
  43. <span v-html="item.sql"></span>
  44. </div>
  45. </div>
  46. <div class="dialog-footer" v-else-if="item?.capacity"
  47. :style="{ display: dialogConfig.type === 'paper' ? 'inline-block' : 'block' }">
  48. <div v-if="type === 'knowledge'" @click="openCapacity(key)"
  49. :class="[readonly || item.capacity.status === 'loading' ? 'disabled' : '']">
  50. <a-spin :size="18" v-if="item.capacity.status === 'loading'" />
  51. <icon-check-circle-fill :size="18" v-else />
  52. <span>{{ item.capacity.status === "loading" ? "小数正在检索" : "查看检索信息" }}</span>
  53. <!-- <b>{{ item.capacity.text }}</b> -->
  54. <icon-caret-right size="18" v-if="item.capacity.status === 'loaded' && !menuList[key]?.open" />
  55. <icon-caret-down size="18" v-if="item.capacity.status === 'loaded' && menuList[key]?.open" />
  56. </div>
  57. <div v-else v-show="item.capacity.status === 'loading'"
  58. :class="[readonly || item.capacity.status === 'loading' ? 'disabled' : '']">
  59. <a-spin :size="18" />
  60. <span>{{ type === 'excel' ? '正在为您合并...' : '正在为您搜索答案...' }}</span>
  61. </div>
  62. <section v-if="type === 'knowledge' && item.capacity.status === 'loaded'" v-show="menuList[key].open">
  63. <div v-if="item.capacity.data.list">
  64. <p v-for="(item, key) in item.capacity.data.list" :key="key">
  65. <a-typography-paragraph :ellipsis="{ rows: 1, css: true }" :style="{ flex: 1, margin: '0' }">
  66. {{ item }}
  67. </a-typography-paragraph>
  68. </p>
  69. </div>
  70. <div v-if="item.capacity.data.documents">
  71. <div v-for="(document, documentKey) in item.capacity.data.documents" :key="documentKey">
  72. <div @click="openDocument(key, documentKey)" :class="[readonly ? 'disabled' : '']">
  73. <span>{{ document.type }}</span>
  74. <a-typography-paragraph :ellipsis="{ rows: 1, css: true }" :style="{ flex: 1, margin: '0' }">
  75. <span>{{ document.title }}</span>
  76. </a-typography-paragraph>
  77. <span>
  78. <em v-for="(item, documentIndex) in document.list" :key="documentIndex">{{
  79. item.title
  80. }}</em>
  81. </span>
  82. <icon-caret-down size="12" v-if="documentKey === menuList[key].documentOpenIndex" />
  83. <icon-caret-right size="12" v-else />
  84. </div>
  85. <div v-show="documentKey === menuList[key].documentOpenIndex">
  86. <p v-for="(item, index) in document.list" :key="index">
  87. <span>{{ item.title }}</span>
  88. <a-typography-paragraph :ellipsis="{ rows: 3, css: true }" :style="{ flex: 1, margin: '0' }">
  89. <span style="color: rgba(153, 153, 153, 1); font-size: 12px">{{
  90. item.content
  91. }}</span>
  92. </a-typography-paragraph>
  93. </p>
  94. </div>
  95. </div>
  96. </div>
  97. </section>
  98. <p v-if="type === 'knowledge' && item.answer" style="margin-top: 10px; font-size: 18px; font-weight: bold">
  99. 答案生成结果
  100. </p>
  101. <p v-if="item.answer" :style="{ 'margin-top': type === 'knowledge' ? '12px' : '0' }">
  102. <span v-if="type === 'paper'" class="paper-wrap">
  103. <img src="@/assets/question/word.svg" />
  104. <span>
  105. <b>黄冈试卷考试题</b>
  106. <em>DOCX,13.19KB</em>
  107. </span>
  108. </span>
  109. <span v-else v-html="getHTML(item.answer)"></span>
  110. </p>
  111. </div>
  112. <div class="dialog-footer" v-else>
  113. <span v-html="getHTML(item.answer)"></span>
  114. </div>
  115. <!-- 处理来源 -->
  116. <div v-if="(type === 'knowledge' || type === 'knowledgeQuiz') && item.reference && item.reference.length > 0"
  117. class="knowledge-wrap">
  118. <header>
  119. <p>来源</p>
  120. <!-- TODO详情暂时注释 -->
  121. <!-- <p @click="goKnowledge">查看全部关联 ></p> -->
  122. </header>
  123. <section>
  124. <div v-for="(knowledge, knowledgeIndex) in item.reference" :key="knowledgeIndex">
  125. <a-popover :title="knowledge.doc_name" position="left">
  126. <div @click="openDocuementPage(knowledge)">
  127. <span>{{ knowledgeIndex + 1 }}</span>
  128. <span>{{ knowledge.doc_name }}</span>
  129. </div>
  130. <template #content>
  131. <div class="knowledge-detail">
  132. <div class="knowledge-content">
  133. {{ knowledge.content_ltks }}
  134. </div>
  135. <div class="knowledge-footer">
  136. <p>检索片段</p>
  137. <p>
  138. <span v-for="(content, contentIndex) in knowledge.content_ltks" :key="contentIndex"
  139. :class="{ active: knowledge.page === contentIndex }" @click="() => {
  140. knowledge.page = contentIndex;
  141. }
  142. ">{{ contentIndex + 1 }}</span>
  143. </p>
  144. <!-- TODO详情暂时注释 -->
  145. <!-- <p @click="goKnowledge(knowledge.id)">查看详情></p> -->
  146. </div>
  147. </div>
  148. </template>
  149. </a-popover>
  150. </div>
  151. </section>
  152. </div>
  153. <!-- 如果有图片 -->
  154. <div v-if="item?.image_url">
  155. <a-image width="500" :src="item?.image_url" />
  156. </div>
  157. <div v-if="item.file" class="paper-wrap" @click="downloadFile(item.file.file_url)">
  158. <img src="@/assets/question/excel.svg" />
  159. <!-- <img src="@/assets/question/word.svg" v-else /> -->
  160. <span>
  161. <b>{{ item.file.file_name }}</b>
  162. </span>
  163. </div>
  164. <div class="dialog-other" v-if="item?.other">
  165. <p v-if="isSetting">
  166. <img src="@/assets/icons/check.png" width="16" />
  167. <span>{{ item.other.token }}</span>
  168. </p>
  169. <p v-else></p>
  170. <div>
  171. <a-tooltip content="重新生成">
  172. <p @click="reQuestion(item.question)" :class="[readonly ? 'disabled' : '']">
  173. <img src="@/assets/icons/refresh.png" width="15" />
  174. </p>
  175. </a-tooltip>
  176. <a-tooltip content="复制">
  177. <p @click="copyAnswer(item.answer)" :class="[readonly ? 'disabled' : '']">
  178. <img src="@/assets/icons/copy.png" width="15" />
  179. </p>
  180. </a-tooltip>
  181. <a-tooltip content="收藏">
  182. <p @click="saveAnswer(key)" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  183. <img src="@/assets/icons/star-full.png" width="15" v-if="menuList[key].save" />
  184. <img src="@/assets/icons/star.png" width="15" v-else />
  185. </p>
  186. </a-tooltip>
  187. <a-tooltip content="点赞">
  188. <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  189. <img src="@/assets/icons/thumb-up.png" width="15" />
  190. </p>
  191. </a-tooltip>
  192. <a-tooltip content="反馈">
  193. <p @click="feedbackAnswer" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  194. <img src="@/assets/icons/thumb-down.png" width="15" />
  195. </p>
  196. </a-tooltip>
  197. <a-tooltip content="分享">
  198. <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  199. <img src="@/assets/icons/share.png" width="15" />
  200. </p>
  201. </a-tooltip>
  202. </div>
  203. </div>
  204. </div>
  205. </div>
  206. </div>
  207. <!-- <div v-show="readonly" @click="stopWriting" class="dialog-writing-button">
  208. <icon-record-stop :size="20" style="margin-right: 4px" />停止生成
  209. </div> -->
  210. <dialog-feedback :showFeedback="showFeedback" @handleCancel="handleCancel" @handleOk="handleOk" v-if="!isSetting" />
  211. </div>
  212. </template>
  213. <script>
  214. import DialogFeedback from "./DialogFeedback.vue";
  215. import { defineComponent, ref, onBeforeUnmount, onMounted, reactive, nextTick, inject } from "vue";
  216. import formatAnwserString from './refference-effect/format-anwser-string';
  217. import applyRefferencePopover from './refference-effect/apply-refference-popover';
  218. export default defineComponent({
  219. components: {
  220. DialogFeedback
  221. },
  222. props: ["open", "suggestions", "isSetting", "readonly", "type", "list", "agentId", "dialogId", 'reference'],
  223. setup(props, { emit }) {
  224. const knowledgeList = ref([[{}]]);
  225. const DOCUMENT_RESULT = {};
  226. const getHTML = (string) => {
  227. return formatAnwserString(string);
  228. };
  229. const replaceCode = (text, index) => {
  230. const icon = ` <span class="circle" data-list-index="${index}"></span>`
  231. return text.replace(/##\d\$\$/g, icon);
  232. }
  233. const { helper, ajax } = inject("$global");
  234. const userName = ref(helper.read("userName") || "");
  235. const list = props.list.map((item, index) => {
  236. return {
  237. answer: replaceCode(item.answer, index),
  238. question: item.question,
  239. reference: (item.reference && item.reference.chunks && item.reference.doc_aggs) ? item.reference.doc_aggs.map(reference => {
  240. return {
  241. id: reference.doc_id,
  242. doc_name: reference.doc_name,
  243. page: 0,
  244. content_ltks: [item.reference.chunks.find(chunk => chunk.doc_id == reference.doc_id).content_ltks]
  245. }
  246. }) : []
  247. }
  248. });
  249. const dialogList = reactive(list || []);
  250. const randomKey = ref(0);
  251. const showFeedback = ref(false);
  252. const menuList = reactive([]);
  253. let dialogIndex = dialogList.length;
  254. const dialogScroll = ref("");
  255. const dialogConfig = ref({});
  256. const scrollToEnd = async () => {
  257. await nextTick();
  258. if (dialogScroll.value) {
  259. dialogScroll.value.scrollTop = dialogScroll.value.scrollHeight;
  260. }
  261. };
  262. let answerInterval = null;
  263. const stopWriting = () => {
  264. window.clearInterval(answerInterval);
  265. emit("set-readonly", false);
  266. if (dialogList[dialogIndex]) {
  267. dialogList[dialogIndex].other = {
  268. token: "4.0s | Tokens"
  269. };
  270. }
  271. scrollToEnd();
  272. dialogIndex += 1;
  273. };
  274. const addData = (data) => {
  275. dialogList.splice(dialogIndex, 1, data);
  276. randomKey.value = Math.random();
  277. scrollToEnd();
  278. };
  279. const saveAnswer = (index) => {
  280. if (!props.readonly) {
  281. menuList[index].save = !menuList[index].save;
  282. }
  283. };
  284. const openCapacity = (index) => {
  285. if (!props.readonly) {
  286. menuList[index].open = !menuList[index].open;
  287. }
  288. };
  289. const openDocument = (index, documentIndex) => {
  290. if (menuList[index].documentOpenIndex === documentIndex) {
  291. menuList[index].documentOpenIndex = -1;
  292. } else {
  293. menuList[index].documentOpenIndex = documentIndex;
  294. }
  295. };
  296. const feedbackAnswer = () => {
  297. if (!props.readonly) {
  298. showFeedback.value = true;
  299. }
  300. };
  301. const handleOk = () => {
  302. showFeedback.value = false;
  303. };
  304. const handleCancel = () => {
  305. showFeedback.value = false;
  306. };
  307. const selectSuggestion = (text) => {
  308. if (!props.readonly) {
  309. }
  310. };
  311. const copyAnswer = (text) => {
  312. if (!props.readonly) {
  313. helper.copyText(text, "复制成功");
  314. }
  315. };
  316. const reQuestion = (text) => {
  317. if (!props.readonly) {
  318. setTimeout(() => {
  319. helper.$bus.emit("send-message", text);
  320. }, 200);
  321. }
  322. };
  323. const goKnowledge = () => {
  324. // emit("go-document");
  325. };
  326. // 点击文档,打开新页面
  327. const openDocuementPage = (knowledge) => {
  328. const doc_name = knowledge.doc_name;
  329. const doc_id = knowledge.id;
  330. const pdf_url = `http://192.168.20.119:11080/v1/document/get/${doc_id}`;
  331. window.open(pdf_url);
  332. }
  333. const addLoading = (data) => {
  334. const mockData = DOCUMENT_RESULT;
  335. data.capacity = {
  336. type: mockData.type,
  337. text: mockData.text,
  338. status: "loading",
  339. data: null
  340. };
  341. menuList[dialogIndex] = {
  342. save: false,
  343. open: false,
  344. documentOpenIndex: -1
  345. };
  346. addData(data);
  347. return data;
  348. };
  349. const loadingData = ref(null);
  350. const isLoaded = ref(false);
  351. const answerData = ref("");
  352. const answerReference = ref([]);
  353. const imageUrl = ref(undefined);
  354. const answerFile = ref(undefined);
  355. const answerCode = ref(undefined);
  356. const answerSql = ref(undefined);
  357. const addQuestion = (question, file = null) => {
  358. let result = { question, capacity: null, answer: "", other: null, file: null, };
  359. addData(result);
  360. return result;
  361. };
  362. const addAnswer = (data) => {
  363. data.answer = answerData.value;
  364. data.reference = answerReference.value;
  365. data.image_url = imageUrl.value;
  366. data.code = answerCode.value;
  367. data.file = answerFile.value;
  368. data.sql = answerSql.value;
  369. addData(data);
  370. };
  371. const answerLoaded = () => {
  372. isLoaded.value = true;
  373. emit("set-readonly", true);
  374. loadingData.value.capacity = DOCUMENT_RESULT;
  375. addAnswer(loadingData.value);
  376. };
  377. const downloadFile = (url) => {
  378. window.open(url, "_blank");
  379. };
  380. onBeforeUnmount(() => {
  381. helper.$bus.off("send-message");
  382. helper.$bus.off("clear-message");
  383. helper.$bus.off("stop-writing");
  384. helper.webSocket.close();
  385. });
  386. onMounted(() => {
  387. scrollToEnd();
  388. /**
  389. * 应用引用效果
  390. * 答案中 引用图标(感叹号)点击时,浮层显示引用内容
  391. */
  392. applyRefferencePopover(props.list);
  393. // socket 消息队列
  394. let msg_list = [];
  395. function createSocket(){
  396. const token = helper.read("token");
  397. // 如果是智能数据,则拿缓存里的数据开启对话
  398. if (helper.read("sendData") && helper.read("sendData").agent_id == 'basic_excel_talk') {
  399. const data = helper.read("sendData");
  400. helper.webSocket = ajax.message.create(token, data.agent_id, data.chat_id, props.type);
  401. // 创建对话之后清空缓存
  402. helper.write('sendData', {});
  403. } else {
  404. helper.webSocket = ajax.message.create(token, props.agentId, props.dialogId, props.type);
  405. }
  406. // 监听socket开启事件
  407. // 当socket开启后,遍历消息,并发送
  408. helper.webSocket.onopen = function(event){
  409. msg_list.forEach(item => {
  410. helper.webSocket.send(item);
  411. });
  412. // 所有消息发送完成后,清空消息队列
  413. msg_list = [];
  414. }
  415. }
  416. // 发送socket数据
  417. function sendSocketMsg(dt){
  418. // 判断socket是否开启
  419. // 如果开启,则直接发送,如果没有开启,则将数据推入消息队列
  420. if(helper.webSocket.readyState === 0){
  421. msg_list.push(dt);
  422. }
  423. if(helper.webSocket.readyState === 1){
  424. helper.webSocket.send(dt);
  425. }
  426. if(helper.webSocket.readyState === 2){
  427. // 正在关闭
  428. }
  429. if(helper.webSocket.readyState === 3){
  430. msg_list.push(dt);
  431. // 已关闭: 重新开启socket
  432. createSocket();
  433. }
  434. }
  435. // 创建socket链接
  436. createSocket();
  437. helper.webSocket.onmessage = (event) => {
  438. const { type, reference, step_message, files } = JSON.parse(event.data);
  439. let { message } = JSON.parse(event.data);
  440. if (!loadingData.value) return;
  441. if (type === "stream") {
  442. // 没解析完就一直loading
  443. if (step_message == '文件上传完成,开始解析') {
  444. emit("changeParse", true);
  445. }
  446. if (step_message == '文件解析完成') {
  447. emit("changeParse", false);
  448. }
  449. if (files && files.length > 0) {
  450. loadingData.value.file = files[0];
  451. addData(loadingData.value);
  452. } else {
  453. answerData.value += step_message ? getHTML(step_message) + "</br>" : message
  454. if (answerData.value && !isLoaded.value) {
  455. answerLoaded();
  456. }
  457. addAnswer(loadingData.value);
  458. }
  459. }
  460. if (type === "message") {
  461. message = replaceCode(message);
  462. answerData.value = getHTML(message);
  463. answerReference.value = [];
  464. // 如有文档引用处理接收到的新消息和引用
  465. if (reference && reference.doc_aggs && reference.doc_aggs.length > 0) {
  466. answerReference.value = reference.doc_aggs.map(item => {
  467. return {
  468. id: item.doc_id,
  469. doc_name: item.doc_name,
  470. page: 0,
  471. content_ltks: [reference.chunks.find(chunk => chunk.doc_id == item.doc_id).content_ltks]
  472. }
  473. });
  474. }
  475. // 如果是智能数据则优先处理代码
  476. if (props.type == 'smartData') {
  477. answerData.value = '正在处理中...'
  478. // 以下是智能数据对话的变量
  479. const { image_url, excel_url, excel_name, code, sql } = JSON.parse(event.data);
  480. const port = window.location.port ? `:${window.location.port}` : '';
  481. // 如果有图片或excel,则更新图片或excel地址
  482. if (image_url) {
  483. imageUrl.value = `http://${window.location.hostname}${port}${image_url}`
  484. }
  485. if (excel_url) {
  486. const file = {
  487. file_url: `http://${window.location.hostname}${port}${excel_url}`,
  488. file_name: `${excel_name}.xlsx` || '点击下载',
  489. }
  490. answerFile.value = file
  491. }
  492. if (code) answerCode.value = getHTML(code);
  493. if (sql) answerSql.value = getHTML(sql);
  494. }
  495. if (answerData.value && !isLoaded.value) {
  496. answerLoaded();
  497. }
  498. addAnswer(loadingData.value);
  499. }
  500. if (type === "close") {
  501. if (!answerData.value) {
  502. answerLoaded();
  503. answerData.value = getHTML(message);
  504. addAnswer(loadingData.value);
  505. }
  506. stopWriting();
  507. answerData.value = "";
  508. isLoaded.value = false;
  509. }
  510. };
  511. helper.$bus.on("send-message", (value) => {
  512. dialogConfig.value = helper.dialogConfig;
  513. const question = props.type === "report" ? value.inputs.report_name : value;
  514. const data = addQuestion(question);
  515. loadingData.value = addLoading(data);
  516. let sendData =
  517. props.type === "report" || props.type === "excel"
  518. ? value
  519. : {
  520. message: value
  521. };
  522. if (props.type === 'doc') {
  523. sendData.doc_ids = [helper.read('docId')];
  524. }
  525. const dt = JSON.stringify(sendData);
  526. // 发送socket数据
  527. sendSocketMsg(dt)
  528. });
  529. helper.$bus.on("clear-message", () => {
  530. dialogList.splice(0);
  531. });
  532. helper.$bus.on("stop-writing", () => {
  533. stopWriting();
  534. });
  535. });
  536. return {
  537. goKnowledge,
  538. randomKey,
  539. openDocument,
  540. showFeedback,
  541. copyAnswer,
  542. dialogList,
  543. getHTML,
  544. selectSuggestion,
  545. dialogScroll,
  546. reQuestion,
  547. menuList,
  548. saveAnswer,
  549. openCapacity,
  550. feedbackAnswer,
  551. handleOk,
  552. handleCancel,
  553. stopWriting,
  554. knowledgeList,
  555. dialogConfig,
  556. userName,
  557. downloadFile,
  558. openDocuementPage
  559. };
  560. }
  561. });
  562. </script>
  563. <style>
  564. .dialog-header {
  565. display: flex;
  566. align-items: center;
  567. .dialog-span {
  568. color: rgba(51, 51, 51, 1);
  569. font-size: 14px;
  570. font-weight: bold;
  571. }
  572. }
  573. </style>
  574. <style scoped>
  575. .user-name {
  576. width: 28px;
  577. height: 28px;
  578. border-radius: 14px;
  579. display: flex;
  580. align-items: center;
  581. justify-content: center;
  582. color: #fff;
  583. }
  584. .paper-download {
  585. padding-left: 50px;
  586. >span {
  587. width: 26px;
  588. height: 26px;
  589. border-radius: 3px;
  590. cursor: pointer;
  591. display: flex;
  592. align-items: center;
  593. justify-content: center;
  594. &:hover {
  595. background: rgba(150, 171, 185, 0.2);
  596. }
  597. }
  598. }
  599. .paper-wrap {
  600. display: flex;
  601. align-items: center;
  602. margin-bottom: 10px;
  603. cursor: pointer;
  604. background-color: #F5F7FE;
  605. border-radius: 16px;
  606. padding: 6px;
  607. >img {
  608. width: 50px;
  609. margin: 8px 6px;
  610. }
  611. >span {
  612. flex: 1;
  613. white-space: nowrap;
  614. overflow: hidden;
  615. text-overflow: ellipsis;
  616. >b {
  617. white-space: nowrap;
  618. overflow: hidden;
  619. text-overflow: ellipsis;
  620. display: block;
  621. width: 100%;
  622. }
  623. >em {
  624. font-style: normal;
  625. }
  626. }
  627. }
  628. .knowledge-detail {
  629. width: 320px;
  630. padding-top: 20px;
  631. .knowledge-content {
  632. height: 300px;
  633. overflow-y: auto;
  634. line-height: 30px;
  635. }
  636. .knowledge-footer {
  637. border-top: 1px solid rgba(229, 229, 229, 1);
  638. margin-top: 20px;
  639. padding-top: 20px;
  640. display: flex;
  641. justify-content: space-between;
  642. align-items: center;
  643. p {
  644. &:nth-child(1) {
  645. opacity: 0.8;
  646. }
  647. &:nth-child(2) {
  648. flex: 1;
  649. display: flex;
  650. justify-content: left;
  651. margin: 0 30px;
  652. >span {
  653. margin: 0 5px;
  654. cursor: pointer;
  655. &:hover,
  656. &.active {
  657. color: #00aea3;
  658. }
  659. }
  660. }
  661. &:nth-child(3) {
  662. font-weight: bold;
  663. cursor: pointer;
  664. &:hover {
  665. color: #00aea3;
  666. }
  667. }
  668. }
  669. }
  670. }
  671. .knowledge-wrap {
  672. padding: 10px 0 20px 50px;
  673. >header {
  674. display: flex;
  675. justify-content: space-between;
  676. >p {
  677. &:nth-child(1) {
  678. color: #7f7f7f;
  679. font-weight: bold;
  680. }
  681. &:nth-child(2) {
  682. cursor: pointer;
  683. &:hover {
  684. color: #00aea3;
  685. }
  686. }
  687. }
  688. }
  689. >section {
  690. >div {
  691. >div {
  692. height: 40px;
  693. display: inline-flex;
  694. align-items: center;
  695. >span {
  696. &:nth-child(1) {
  697. display: inline-block;
  698. background-color: rgba(0, 174, 163, 0.2);
  699. color: #00aea3;
  700. padding: 1px 5px;
  701. border-radius: 2px;
  702. margin-right: 5px;
  703. font-size: 12px;
  704. }
  705. &:nth-child(2) {
  706. cursor: pointer;
  707. &:hover {
  708. color: #00aea3;
  709. }
  710. }
  711. }
  712. }
  713. }
  714. }
  715. }
  716. .dialog-absolute {
  717. .dialog-header {
  718. position: absolute;
  719. &:nth-child(1) {
  720. transform: translateY(10px);
  721. }
  722. &:nth-child(3) {
  723. transform: translateY(16px);
  724. }
  725. }
  726. .dialog-footer {
  727. margin-left: 50px;
  728. &:nth-child(2) {
  729. margin-left: 50px;
  730. padding-left: 0;
  731. }
  732. }
  733. .dialog-code-footer {
  734. border-radius: 12px;
  735. font-size: 14px;
  736. background-color: #32333c;
  737. color: #BCBDC3;
  738. margin: 16px 0;
  739. .dialog-code-footer-title {
  740. font-size: 16px;
  741. font-weight: 500;
  742. margin: 12px 0;
  743. width: 100%;
  744. padding: 4px 8px;
  745. background-color: #3D3F3F;
  746. color: #BCBDC3;
  747. }
  748. &:nth-child(2) {
  749. background-color: rgba(0, 0, 0, 0);
  750. margin: 0;
  751. }
  752. >p {
  753. font-size: 14px;
  754. }
  755. >div {
  756. font-size: 14px;
  757. padding: 4px 8px;
  758. border-radius: 4px;
  759. align-items: center;
  760. cursor: pointer;
  761. &.disabled {
  762. cursor: not-allowed;
  763. }
  764. >b {
  765. margin-right: 8px;
  766. }
  767. }
  768. >section {
  769. padding: 0 16px;
  770. background-color: #ffffff;
  771. border-radius: 12px;
  772. margin-top: 16px;
  773. >p {
  774. display: inline-block;
  775. color: rgba(16, 16, 16, 1);
  776. font-size: 12px;
  777. margin-top: 16px;
  778. width: 100%;
  779. }
  780. >div {
  781. margin-top: 4px;
  782. overflow: hidden;
  783. margin-bottom: 16px;
  784. display: inline-block;
  785. width: 100%;
  786. >div {
  787. margin-top: 8px;
  788. >div {
  789. &:nth-child(1) {
  790. border-radius: 4px;
  791. color: rgba(16, 16, 16, 1);
  792. font-size: 12px;
  793. height: 24px;
  794. border: 1px solid rgba(229, 229, 229, 1);
  795. display: flex;
  796. justify-content: center;
  797. align-items: center;
  798. padding-right: 8px;
  799. cursor: pointer;
  800. &.disabled {
  801. cursor: not-allowed;
  802. }
  803. &:hover {
  804. background: rgba(229, 229, 229, 0.2);
  805. }
  806. >span {
  807. &:nth-child(1) {
  808. background-color: rgba(0, 174, 163, 0.2);
  809. color: #00aea3;
  810. font-size: 12px;
  811. border: 1px solid rgba(64, 95, 234, 0.13);
  812. padding: 0 4px;
  813. margin: 0 4px 0 0;
  814. }
  815. &:nth-child(3) {
  816. color: #00aea3;
  817. font-size: 12px;
  818. em {
  819. padding: 0 4px;
  820. font-style: normal;
  821. border-left: 1px solid rgba(64, 95, 234, 1);
  822. font-weight: bold;
  823. &:nth-child(1) {
  824. border: none;
  825. }
  826. }
  827. }
  828. }
  829. }
  830. &:nth-child(2) {
  831. >p {
  832. margin: 8px 0;
  833. display: flex;
  834. >span {
  835. transform: translateY(2px);
  836. color: #00aea3;
  837. font-weight: bold;
  838. font-size: 12px;
  839. margin-right: 8px;
  840. }
  841. }
  842. }
  843. }
  844. }
  845. >p {
  846. float: left;
  847. width: 48%;
  848. border-radius: 4px;
  849. background-color: rgba(245, 247, 254, 1);
  850. color: rgba(16, 16, 16, 1);
  851. font-size: 12px;
  852. margin-top: 8px;
  853. height: 36px;
  854. line-height: 36px;
  855. padding: 0 8px;
  856. display: flex;
  857. align-items: center;
  858. &:nth-child(odd) {
  859. margin-right: 4%;
  860. }
  861. }
  862. }
  863. }
  864. }
  865. }
  866. .dialog-flex-wrap {
  867. position: relative;
  868. flex: 1;
  869. margin-top: 16px;
  870. height: 100%;
  871. }
  872. .dialog-flex {
  873. position: absolute;
  874. left: 0;
  875. top: 0;
  876. right: 0;
  877. bottom: 0;
  878. overflow-y: auto;
  879. overflow-x: hidden;
  880. }
  881. .section-open {
  882. margin-top: 12px;
  883. }
  884. .dialog-file {
  885. width: 421px;
  886. height: 48px;
  887. border-radius: 12px;
  888. background-color: rgba(255, 255, 255, 1);
  889. color: rgba(16, 16, 16, 1);
  890. font-size: 14px;
  891. border: 1px solid rgba(229, 229, 229, 1);
  892. display: flex;
  893. align-items: center;
  894. margin-bottom: 16px;
  895. padding: 0 8px;
  896. }
  897. .dialog-footer {
  898. border-radius: 12px;
  899. background-color: rgba(245, 247, 254, 1);
  900. padding: 12px 16px;
  901. margin: 16px 0;
  902. &:nth-child(2) {
  903. background-color: rgba(0, 0, 0, 0);
  904. margin: 0;
  905. }
  906. >p {
  907. color: rgba(16, 16, 16, 1);
  908. font-size: 14px;
  909. }
  910. >div {
  911. border: 1px solid rgba(64, 95, 234, 0.42);
  912. background-color: rgba(255, 255, 255, 1);
  913. color: rgba(16, 16, 16, 1);
  914. font-size: 14px;
  915. display: inline-flex;
  916. padding: 4px 8px;
  917. border-radius: 4px;
  918. align-items: center;
  919. cursor: pointer;
  920. height: 30px;
  921. &.disabled {
  922. cursor: not-allowed;
  923. }
  924. &:hover {
  925. background: rgb(245, 247, 254, 0.4);
  926. }
  927. >span {
  928. margin: 0 8px;
  929. }
  930. >b {
  931. margin-right: 8px;
  932. }
  933. }
  934. >section {
  935. padding: 0 16px;
  936. background-color: #ffffff;
  937. border-radius: 12px;
  938. margin-top: 16px;
  939. >p {
  940. display: inline-block;
  941. color: rgba(16, 16, 16, 1);
  942. font-size: 12px;
  943. margin-top: 16px;
  944. width: 100%;
  945. }
  946. >div {
  947. margin-top: 4px;
  948. overflow: hidden;
  949. margin-bottom: 16px;
  950. display: inline-block;
  951. width: 100%;
  952. >div {
  953. margin-top: 8px;
  954. >div {
  955. &:nth-child(1) {
  956. border-radius: 4px;
  957. color: rgba(16, 16, 16, 1);
  958. font-size: 12px;
  959. height: 24px;
  960. border: 1px solid rgba(229, 229, 229, 1);
  961. display: flex;
  962. justify-content: center;
  963. align-items: center;
  964. padding-right: 8px;
  965. cursor: pointer;
  966. &.disabled {
  967. cursor: not-allowed;
  968. }
  969. &:hover {
  970. background: rgba(229, 229, 229, 0.2);
  971. }
  972. >span {
  973. &:nth-child(1) {
  974. background-color: rgba(0, 174, 163, 0.2);
  975. color: #00aea3;
  976. font-size: 12px;
  977. border: 1px solid rgba(64, 95, 234, 0.13);
  978. padding: 0 4px;
  979. margin: 0 4px 0 0;
  980. }
  981. &:nth-child(3) {
  982. color: #00aea3;
  983. font-size: 12px;
  984. em {
  985. padding: 0 4px;
  986. font-style: normal;
  987. border-left: 1px solid rgba(64, 95, 234, 1);
  988. font-weight: bold;
  989. &:nth-child(1) {
  990. border: none;
  991. }
  992. }
  993. }
  994. }
  995. }
  996. &:nth-child(2) {
  997. >p {
  998. margin: 8px 0;
  999. display: flex;
  1000. >span {
  1001. transform: translateY(2px);
  1002. color: #00aea3;
  1003. font-weight: bold;
  1004. font-size: 12px;
  1005. margin-right: 8px;
  1006. }
  1007. }
  1008. }
  1009. }
  1010. }
  1011. >p {
  1012. float: left;
  1013. width: 48%;
  1014. border-radius: 4px;
  1015. background-color: rgba(245, 247, 254, 1);
  1016. color: rgba(16, 16, 16, 1);
  1017. font-size: 12px;
  1018. margin-top: 8px;
  1019. height: 36px;
  1020. line-height: 36px;
  1021. padding: 0 8px;
  1022. display: flex;
  1023. align-items: center;
  1024. &:nth-child(odd) {
  1025. margin-right: 4%;
  1026. }
  1027. }
  1028. }
  1029. }
  1030. }
  1031. .section-suggestions {
  1032. margin: 12px 0 20px;
  1033. .section-suggestion {
  1034. width: 380px;
  1035. height: 40px;
  1036. line-height: 40px;
  1037. border: 1px solid #e5e5e5;
  1038. border-radius: 12px;
  1039. padding: 0 8px;
  1040. display: flex;
  1041. align-items: center;
  1042. justify-content: space-between;
  1043. cursor: pointer;
  1044. margin-top: 8px;
  1045. &.disabled {
  1046. cursor: not-allowed;
  1047. }
  1048. &:hover {
  1049. background: rgb(245, 247, 254);
  1050. }
  1051. }
  1052. }
  1053. .dialog-other {
  1054. margin-bottom: 16px;
  1055. display: flex;
  1056. align-items: center;
  1057. justify-content: space-between;
  1058. >p {
  1059. display: flex;
  1060. align-items: center;
  1061. span {
  1062. margin-left: 8px;
  1063. color: rgba(16, 16, 16, 1);
  1064. font-size: 12px;
  1065. }
  1066. }
  1067. >div {
  1068. display: flex;
  1069. p {
  1070. display: flex;
  1071. align-items: center;
  1072. justify-content: center;
  1073. width: 30px;
  1074. height: 30px;
  1075. border-radius: 15px;
  1076. border: 1px solid rgba(229, 229, 229, 1);
  1077. margin-left: 8px;
  1078. cursor: pointer;
  1079. &.disabled {
  1080. cursor: not-allowed;
  1081. }
  1082. &:hover {
  1083. background-color: rgba(245, 247, 254, 1);
  1084. }
  1085. }
  1086. }
  1087. }
  1088. .feedback-title {
  1089. color: rgba(102, 102, 102, 1);
  1090. font-size: 12px;
  1091. margin: 0 0 8px 4px;
  1092. }
  1093. .feedback-content {
  1094. margin-bottom: 16px;
  1095. }
  1096. .dialog-writing-button {
  1097. position: absolute;
  1098. bottom: -30px;
  1099. left: 50%;
  1100. transform: translateX(-50%);
  1101. text-align: center;
  1102. width: 106px;
  1103. height: 34px;
  1104. line-height: 34px;
  1105. border-radius: 4px;
  1106. background-color: rgba(255, 255, 255, 1);
  1107. color: rgba(16, 16, 16, 1);
  1108. font-size: 14px;
  1109. border: 1px solid rgba(229, 229, 229, 1);
  1110. display: flex;
  1111. align-items: center;
  1112. justify-content: center;
  1113. cursor: pointer;
  1114. z-index: 10;
  1115. &:hover {
  1116. background: rgb(245, 247, 254);
  1117. }
  1118. }
  1119. </style>
  1120. <style>
  1121. .circle {
  1122. width: 13px;
  1123. height: 13px;
  1124. margin-left: 4px;
  1125. border-radius: 50%;
  1126. cursor: pointer;
  1127. display: inline-block;
  1128. border: 1px solid #888;
  1129. }
  1130. .circle::before {
  1131. content: "!";
  1132. font-size: 12px;
  1133. color: #000;
  1134. line-height: 12px;
  1135. text-align: center;
  1136. display: block;
  1137. }
  1138. </style>