DialogWrap.vue 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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. <div class="dialog-header" v-if="item?.capacity">
  32. <img alt="avatar" src="/src/assets/slider/logo.jpg" width="28" />
  33. </div>
  34. <div class="dialog-footer" v-if="item.capacity"
  35. :style="{ display: dialogConfig.type === 'paper' ? 'inline-block' : 'block' }">
  36. <div v-if="type === 'knowledge'" @click="openCapacity(key)"
  37. :class="[readonly || item.capacity.status === 'loading' ? 'disabled' : '']">
  38. <a-spin :size="18" v-if="item.capacity.status === 'loading'" />
  39. <icon-check-circle-fill :size="18" v-else />
  40. <span>{{ item.capacity.status === "loading" ? "小数正在检索" : "查看检索信息" }}</span>
  41. <!-- <b>{{ item.capacity.text }}</b> -->
  42. <icon-caret-right size="18" v-if="item.capacity.status === 'loaded' && !menuList[key]?.open" />
  43. <icon-caret-down size="18" v-if="item.capacity.status === 'loaded' && menuList[key]?.open" />
  44. </div>
  45. <div v-else v-show="item.capacity.status === 'loading'"
  46. :class="[readonly || item.capacity.status === 'loading' ? 'disabled' : '']">
  47. <a-spin :size="18" />
  48. <span>{{ type === 'excel' ? '正在为您合并...' : '正在为您搜索答案...' }}</span>
  49. </div>
  50. <section v-if="type === 'knowledge' && item.capacity.status === 'loaded'" v-show="menuList[key].open">
  51. <div v-if="item.capacity.data.list">
  52. <p v-for="(item, key) in item.capacity.data.list" :key="key">
  53. <a-typography-paragraph :ellipsis="{ rows: 1, css: true }" :style="{ flex: 1, margin: '0' }">
  54. {{ item }}
  55. </a-typography-paragraph>
  56. </p>
  57. </div>
  58. <div v-if="item.capacity.data.documents">
  59. <div v-for="(document, documentKey) in item.capacity.data.documents" :key="documentKey">
  60. <div @click="openDocument(key, documentKey)" :class="[readonly ? 'disabled' : '']">
  61. <span>{{ document.type }}</span>
  62. <a-typography-paragraph :ellipsis="{ rows: 1, css: true }" :style="{ flex: 1, margin: '0' }">
  63. <span>{{ document.title }}</span>
  64. </a-typography-paragraph>
  65. <span>
  66. <em v-for="(item, documentIndex) in document.list" :key="documentIndex">{{
  67. item.title
  68. }}</em>
  69. </span>
  70. <icon-caret-down size="12" v-if="documentKey === menuList[key].documentOpenIndex" />
  71. <icon-caret-right size="12" v-else />
  72. </div>
  73. <div v-show="documentKey === menuList[key].documentOpenIndex">
  74. <p v-for="(item, index) in document.list" :key="index">
  75. <span>{{ item.title }}</span>
  76. <a-typography-paragraph :ellipsis="{ rows: 3, css: true }" :style="{ flex: 1, margin: '0' }">
  77. <span style="color: rgba(153, 153, 153, 1); font-size: 12px">{{
  78. item.content
  79. }}</span>
  80. </a-typography-paragraph>
  81. </p>
  82. </div>
  83. </div>
  84. </div>
  85. </section>
  86. <p v-if="type === 'knowledge' && item.answer" style="margin-top: 10px; font-size: 18px; font-weight: bold">
  87. 答案生成结果
  88. </p>
  89. <p v-if="item.answer" :style="{ 'margin-top': type === 'knowledge' ? '12px' : '0' }">
  90. <span v-if="type === 'paper'" class="paper-wrap">
  91. <img src="@/assets/question/word.svg" />
  92. <span>
  93. <b>黄冈试卷考试题</b>
  94. <em>DOCX,13.19KB</em>
  95. </span>
  96. </span>
  97. <span v-else v-html="item.answer"></span>
  98. </p>
  99. </div>
  100. <div class="dialog-footer" v-else>
  101. <p v-if="item.answer" v-html="item.answer"></p>
  102. </div>
  103. <div v-if="(type === 'knowledge' || type === 'knowledgeQuiz') && knowledgeList.length > 0"
  104. class="knowledge-wrap">
  105. <header>
  106. <p>来源</p>
  107. <!-- TODO详情暂时注释 -->
  108. <!-- <p @click="goKnowledge">查看全部关联 ></p> -->
  109. </header>
  110. <section>
  111. <div v-for="(knowledge, knowledgeIndex) in knowledgeList" :key="knowledgeIndex">
  112. <a-popover :title="knowledge.name" position="left">
  113. <div>
  114. <span>{{ knowledgeIndex + 1 }}</span>
  115. <span>{{ knowledge.name }}</span>
  116. </div>
  117. <template #content>
  118. <div class="knowledge-detail">
  119. <div class="knowledge-content">
  120. {{ knowledge.contents[knowledge.page] }}
  121. </div>
  122. <div class="knowledge-footer">
  123. <p>检索片段</p>
  124. <p>
  125. <span v-for="(content, contentIndex) in knowledge.contents" :key="contentIndex"
  126. :class="{ active: knowledge.page === contentIndex }" @click="() => {
  127. knowledge.page = contentIndex;
  128. }
  129. ">{{ contentIndex + 1 }}</span>
  130. </p>
  131. <!-- TODO详情暂时注释 -->
  132. <!-- <p @click="goKnowledge(knowledge.id)">查看详情></p> -->
  133. </div>
  134. </div>
  135. </template>
  136. </a-popover>
  137. </div>
  138. </section>
  139. </div>
  140. <div v-if="item.file" class="paper-wrap" @click="downloadFile(item.file.file_url)">
  141. <img src="@/assets/question/excel.svg" v-if="type === 'excel'" />
  142. <img src="@/assets/question/word.svg" v-else />
  143. <span>
  144. <b>{{ item.file.file_name }}</b>
  145. </span>
  146. </div>
  147. <div class="dialog-other" v-if="item?.other">
  148. <p v-if="isSetting">
  149. <img src="@/assets/icons/check.png" width="16" />
  150. <span>{{ item.other.token }}</span>
  151. </p>
  152. <p v-else></p>
  153. <div>
  154. <a-tooltip content="重新生成">
  155. <p @click="reQuestion(item.question)" :class="[readonly ? 'disabled' : '']">
  156. <img src="@/assets/icons/refresh.png" width="15" />
  157. </p>
  158. </a-tooltip>
  159. <a-tooltip content="复制">
  160. <p @click="copyAnswer(item.answer)" :class="[readonly ? 'disabled' : '']">
  161. <img src="@/assets/icons/copy.png" width="15" />
  162. </p>
  163. </a-tooltip>
  164. <a-tooltip content="收藏">
  165. <p @click="saveAnswer(key)" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  166. <img src="@/assets/icons/star-full.png" width="15" v-if="menuList[key].save" />
  167. <img src="@/assets/icons/star.png" width="15" v-else />
  168. </p>
  169. </a-tooltip>
  170. <a-tooltip content="点赞">
  171. <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  172. <img src="@/assets/icons/thumb-up.png" width="15" />
  173. </p>
  174. </a-tooltip>
  175. <a-tooltip content="反馈">
  176. <p @click="feedbackAnswer" :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  177. <img src="@/assets/icons/thumb-down.png" width="15" />
  178. </p>
  179. </a-tooltip>
  180. <a-tooltip content="分享">
  181. <p :class="[readonly ? 'disabled' : '']" v-if="!isSetting">
  182. <img src="@/assets/icons/share.png" width="15" />
  183. </p>
  184. </a-tooltip>
  185. </div>
  186. </div>
  187. </div>
  188. </div>
  189. </div>
  190. <!-- <div v-show="readonly" @click="stopWriting" class="dialog-writing-button">
  191. <icon-record-stop :size="20" style="margin-right: 4px" />停止生成
  192. </div> -->
  193. <dialog-feedback :showFeedback="showFeedback" @handleCancel="handleCancel" @handleOk="handleOk" v-if="!isSetting" />
  194. </div>
  195. </template>
  196. <script>
  197. import DialogFeedback from "./DialogFeedback.vue";
  198. import { defineComponent, ref, onBeforeUnmount, onMounted, reactive, nextTick, inject } from "vue";
  199. import { marked } from "marked";
  200. export default defineComponent({
  201. components: {
  202. DialogFeedback
  203. },
  204. props: ["open", "suggestions", "isSetting", "readonly", "type", "list", "agentId", "dialogId", 'reference'],
  205. setup(props, { emit }) {
  206. console.log('type', props.type)
  207. const knowledgeList = ref([]);
  208. const DOCUMENT_RESULT = {
  209. type: "document",
  210. text: "谷歌知识库",
  211. status: "loaded",
  212. data: {
  213. title: "AI Agent的论文",
  214. documents: [
  215. {
  216. type: "PDF",
  217. title: "科比的传奇一生,你知道多少?",
  218. list: [
  219. {
  220. title: "P1",
  221. content:
  222. "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
  223. },
  224. {
  225. title: "P2",
  226. content:
  227. "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
  228. }
  229. ]
  230. },
  231. {
  232. type: "PDF",
  233. title: "科比的传奇一生,你知道多少?",
  234. list: [
  235. {
  236. title: "P1",
  237. content:
  238. "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
  239. },
  240. {
  241. title: "P2",
  242. content:
  243. "科比·布莱恩特(Kobe Bryant),1978年8月23日出生,2020年1月26日不幸逝世,是美国著名的职业篮球运动员,司职得分后卫/小前锋。他的NBA生涯全部在洛杉矶湖人队度过,共获得5次NBA总冠军、1次NBA常规赛MVP、2次NBA总决赛MVP"
  244. }
  245. ]
  246. }
  247. ]
  248. }
  249. };
  250. const { helper, ajax } = inject("$global");
  251. const userName = ref(helper.read("userName") || "");
  252. const dialogList = reactive(props.list || []);
  253. const randomKey = ref(0);
  254. const showFeedback = ref(false);
  255. const menuList = reactive([]);
  256. let dialogIndex = dialogList.length;
  257. const dialogScroll = ref("");
  258. const dialogConfig = ref({});
  259. const scrollToEnd = async () => {
  260. await nextTick();
  261. if (dialogScroll.value) {
  262. dialogScroll.value.scrollTop = dialogScroll.value.scrollHeight;
  263. }
  264. };
  265. let answerInterval = null;
  266. const stopWriting = () => {
  267. window.clearInterval(answerInterval);
  268. emit("set-readonly", false);
  269. if (dialogList[dialogIndex]) {
  270. dialogList[dialogIndex].other = {
  271. token: "4.0s | Tokens"
  272. };
  273. }
  274. scrollToEnd();
  275. dialogIndex += 1;
  276. };
  277. const addData = (data) => {
  278. dialogList.splice(dialogIndex, 1, data);
  279. randomKey.value = Math.random();
  280. scrollToEnd();
  281. };
  282. const mockLink = (text, file = null) => {
  283. let num2 = 0;
  284. let p1 = text;
  285. const p2 = `尊敬的领导,
  286. 因个人原因,我计划于2023年11月6日至11月10日请假。在此期间,我将妥善安排工作,确保任务顺利完成。恳请批准。
  287. 谢谢!`;
  288. let result = { question: "", capacity: null, answer: "", other: null, file: null };
  289. // 第一步,输入问题
  290. const step1 = () => {
  291. result.question = p1;
  292. if (file) {
  293. result.file = file;
  294. addData(result);
  295. }
  296. step2();
  297. };
  298. // 第二步,调用能力
  299. const step2 = () => {
  300. const mockData = DOCUMENT_RESULT;
  301. result.capacity = {
  302. type: mockData.type,
  303. text: mockData.text,
  304. status: "loading",
  305. data: null
  306. };
  307. menuList[dialogIndex] = {
  308. save: false,
  309. open: false,
  310. documentOpenIndex: -1
  311. };
  312. addData(result);
  313. setTimeout(() => {
  314. result.capacity = mockData;
  315. addData(result);
  316. step3();
  317. }, 1000);
  318. };
  319. // 第三步,输出答案
  320. const step3 = () => {
  321. emit("set-readonly", true);
  322. answerInterval = setInterval(() => {
  323. if (num2 < p2.length + 1) {
  324. result.answer = p2.slice(0, num2);
  325. num2 += 1;
  326. addData(result);
  327. } else {
  328. stopWriting();
  329. }
  330. }, 50);
  331. };
  332. step1();
  333. };
  334. const saveAnswer = (index) => {
  335. if (!props.readonly) {
  336. menuList[index].save = !menuList[index].save;
  337. }
  338. };
  339. const openCapacity = (index) => {
  340. if (!props.readonly) {
  341. menuList[index].open = !menuList[index].open;
  342. }
  343. };
  344. const openDocument = (index, documentIndex) => {
  345. if (menuList[index].documentOpenIndex === documentIndex) {
  346. menuList[index].documentOpenIndex = -1;
  347. } else {
  348. menuList[index].documentOpenIndex = documentIndex;
  349. }
  350. };
  351. const feedbackAnswer = () => {
  352. if (!props.readonly) {
  353. showFeedback.value = true;
  354. }
  355. };
  356. const handleOk = () => {
  357. showFeedback.value = false;
  358. };
  359. const handleCancel = () => {
  360. showFeedback.value = false;
  361. };
  362. const selectSuggestion = (text) => {
  363. if (!props.readonly) {
  364. mockLink(text);
  365. }
  366. };
  367. const copyAnswer = (text) => {
  368. if (!props.readonly) {
  369. helper.copyText(text, "复制成功");
  370. }
  371. };
  372. const reQuestion = (text) => {
  373. if (!props.readonly) {
  374. setTimeout(() => {
  375. helper.$bus.emit("send-message", text);
  376. }, 200);
  377. }
  378. };
  379. const goKnowledge = () => {
  380. emit("go-document");
  381. };
  382. const addLoading = (data) => {
  383. const mockData = DOCUMENT_RESULT;
  384. data.capacity = {
  385. type: mockData.type,
  386. text: mockData.text,
  387. status: "loading",
  388. data: null
  389. };
  390. menuList[dialogIndex] = {
  391. save: false,
  392. open: false,
  393. documentOpenIndex: -1
  394. };
  395. addData(data);
  396. return data;
  397. };
  398. const loadingData = ref(null);
  399. const isLoaded = ref(false);
  400. const answerData = ref("");
  401. const addQuestion = (question, file = null) => {
  402. let result = { question, capacity: null, answer: "", other: null, file: null };
  403. addData(result);
  404. return result;
  405. };
  406. const addAnswer = (data) => {
  407. data.answer = answerData.value;
  408. addData(data);
  409. };
  410. const answerLoaded = () => {
  411. isLoaded.value = true;
  412. emit("set-readonly", true);
  413. loadingData.value.capacity = DOCUMENT_RESULT;
  414. addAnswer(loadingData.value);
  415. };
  416. const downloadFile = (url) => {
  417. window.open(url, "_blank");
  418. };
  419. onBeforeUnmount(() => {
  420. helper.$bus.off("send-message");
  421. helper.$bus.off("clear-message");
  422. helper.$bus.off("stop-writing");
  423. helper.webSocket.close();
  424. });
  425. const getHTML = (string) => {
  426. return marked.parse(string);
  427. };
  428. onMounted(() => {
  429. scrollToEnd();
  430. helper.webSocket = ajax.message.create(helper.read("token"), props.agentId, props.dialogId, props.type);
  431. helper.webSocket.onmessage = (event) => {
  432. const { type, message, reference, step_message, files } = JSON.parse(event.data);
  433. if (!loadingData.value) return;
  434. if (type === "stream") {
  435. if (files && files.length > 0) {
  436. loadingData.value.file = files[0];
  437. addData(loadingData.value);
  438. } else {
  439. answerData.value += step_message ? getHTML(step_message) + "</br>" : message
  440. if (answerData.value && !isLoaded.value) {
  441. answerLoaded();
  442. }
  443. addAnswer(loadingData.value);
  444. }
  445. }
  446. if (type === "message") {
  447. answerData.value = getHTML(message);
  448. if (answerData.value && !isLoaded.value) {
  449. answerLoaded();
  450. }
  451. addAnswer(loadingData.value);
  452. if (reference.chunks && reference.chunks.length > 0) {
  453. knowledgeList.value = reference.chunks.map(item => {
  454. return {
  455. id: item.doc_id,
  456. name: item.docnm_kwd,
  457. page: 0,
  458. contents: [item.content_ltks]
  459. }
  460. })
  461. }
  462. }
  463. if (type === "close") {
  464. if (!answerData.value) {
  465. answerLoaded();
  466. answerData.value = getHTML(message);
  467. addAnswer(loadingData.value);
  468. }
  469. stopWriting();
  470. answerData.value = "";
  471. isLoaded.value = false;
  472. }
  473. };
  474. helper.$bus.on("send-message", (value) => {
  475. dialogConfig.value = helper.dialogConfig;
  476. const question = props.type === "report" ? value.inputs.report_name : value;
  477. const data = addQuestion(question);
  478. loadingData.value = addLoading(data);
  479. let sendData =
  480. props.type === "report" || props.type === "excel"
  481. ? value
  482. : {
  483. message: value
  484. };
  485. if (props.type === 'doc') {
  486. sendData.doc_ids = [helper.read('docId')];
  487. }
  488. helper.webSocket.send(JSON.stringify(sendData));
  489. });
  490. helper.$bus.on("clear-message", () => {
  491. dialogList.splice(0);
  492. });
  493. helper.$bus.on("stop-writing", () => {
  494. stopWriting();
  495. });
  496. });
  497. return {
  498. goKnowledge,
  499. randomKey,
  500. openDocument,
  501. showFeedback,
  502. copyAnswer,
  503. dialogList,
  504. selectSuggestion,
  505. dialogScroll,
  506. reQuestion,
  507. menuList,
  508. saveAnswer,
  509. openCapacity,
  510. feedbackAnswer,
  511. handleOk,
  512. handleCancel,
  513. stopWriting,
  514. knowledgeList,
  515. dialogConfig,
  516. userName,
  517. downloadFile
  518. };
  519. }
  520. });
  521. </script>
  522. <style>
  523. .dialog-header {
  524. display: flex;
  525. align-items: center;
  526. .dialog-span {
  527. color: rgba(51, 51, 51, 1);
  528. font-size: 14px;
  529. font-weight: bold;
  530. }
  531. }
  532. </style>
  533. <style scoped>
  534. .user-name {
  535. width: 28px;
  536. height: 28px;
  537. border-radius: 14px;
  538. display: flex;
  539. align-items: center;
  540. justify-content: center;
  541. color: #fff;
  542. }
  543. .paper-download {
  544. padding-left: 50px;
  545. >span {
  546. width: 26px;
  547. height: 26px;
  548. border-radius: 3px;
  549. cursor: pointer;
  550. display: flex;
  551. align-items: center;
  552. justify-content: center;
  553. &:hover {
  554. background: rgba(150, 171, 185, 0.2);
  555. }
  556. }
  557. }
  558. .paper-wrap {
  559. display: flex;
  560. max-width: 200px;
  561. align-items: center;
  562. margin-left: 40px;
  563. margin-bottom: 10px;
  564. cursor: pointer;
  565. >img {
  566. width: 50px;
  567. margin-right: 10px;
  568. }
  569. >span {
  570. flex: 1;
  571. white-space: nowrap;
  572. overflow: hidden;
  573. text-overflow: ellipsis;
  574. >b {
  575. white-space: nowrap;
  576. overflow: hidden;
  577. text-overflow: ellipsis;
  578. display: block;
  579. width: 100%;
  580. }
  581. >em {
  582. font-style: normal;
  583. }
  584. }
  585. }
  586. .knowledge-detail {
  587. width: 320px;
  588. padding-top: 20px;
  589. .knowledge-content {
  590. height: 300px;
  591. overflow-y: auto;
  592. line-height: 30px;
  593. }
  594. .knowledge-footer {
  595. border-top: 1px solid rgba(229, 229, 229, 1);
  596. margin-top: 20px;
  597. padding-top: 20px;
  598. display: flex;
  599. justify-content: space-between;
  600. align-items: center;
  601. p {
  602. &:nth-child(1) {
  603. opacity: 0.8;
  604. }
  605. &:nth-child(2) {
  606. flex: 1;
  607. display: flex;
  608. justify-content: left;
  609. margin: 0 30px;
  610. >span {
  611. margin: 0 5px;
  612. cursor: pointer;
  613. &:hover,
  614. &.active {
  615. color: #00aea3;
  616. }
  617. }
  618. }
  619. &:nth-child(3) {
  620. font-weight: bold;
  621. cursor: pointer;
  622. &:hover {
  623. color: #00aea3;
  624. }
  625. }
  626. }
  627. }
  628. }
  629. .knowledge-wrap {
  630. padding: 10px 0 20px 50px;
  631. >header {
  632. display: flex;
  633. justify-content: space-between;
  634. >p {
  635. &:nth-child(1) {
  636. color: #7f7f7f;
  637. font-weight: bold;
  638. }
  639. &:nth-child(2) {
  640. cursor: pointer;
  641. &:hover {
  642. color: #00aea3;
  643. }
  644. }
  645. }
  646. }
  647. >section {
  648. >div {
  649. >div {
  650. height: 40px;
  651. display: inline-flex;
  652. align-items: center;
  653. >span {
  654. &:nth-child(1) {
  655. display: inline-block;
  656. background-color: rgba(0, 174, 163, 0.2);
  657. color: #00aea3;
  658. padding: 1px 5px;
  659. border-radius: 2px;
  660. margin-right: 5px;
  661. font-size: 12px;
  662. }
  663. &:nth-child(2) {
  664. cursor: pointer;
  665. &:hover {
  666. color: #00aea3;
  667. }
  668. }
  669. }
  670. }
  671. }
  672. }
  673. }
  674. .dialog-absolute {
  675. .dialog-header {
  676. position: absolute;
  677. &:nth-child(1) {
  678. transform: translateY(10px);
  679. }
  680. &:nth-child(3) {
  681. transform: translateY(16px);
  682. }
  683. }
  684. .dialog-footer {
  685. margin-left: 50px;
  686. &:nth-child(2) {
  687. margin-left: 50px;
  688. padding-left: 0;
  689. }
  690. }
  691. }
  692. .dialog-flex-wrap {
  693. position: relative;
  694. flex: 1;
  695. margin-top: 16px;
  696. height: 100%;
  697. }
  698. .dialog-flex {
  699. position: absolute;
  700. left: 0;
  701. top: 0;
  702. right: 0;
  703. bottom: 0;
  704. overflow-y: auto;
  705. overflow-x: hidden;
  706. }
  707. .section-open {
  708. margin-top: 12px;
  709. }
  710. .dialog-file {
  711. width: 421px;
  712. height: 48px;
  713. border-radius: 12px;
  714. background-color: rgba(255, 255, 255, 1);
  715. color: rgba(16, 16, 16, 1);
  716. font-size: 14px;
  717. border: 1px solid rgba(229, 229, 229, 1);
  718. display: flex;
  719. align-items: center;
  720. margin-bottom: 16px;
  721. padding: 0 8px;
  722. }
  723. .dialog-footer {
  724. border-radius: 12px;
  725. background-color: rgba(245, 247, 254, 1);
  726. padding: 12px 16px;
  727. margin: 16px 0;
  728. &:nth-child(2) {
  729. background-color: rgba(0, 0, 0, 0);
  730. margin: 0;
  731. }
  732. >p {
  733. color: rgba(16, 16, 16, 1);
  734. font-size: 14px;
  735. }
  736. >div {
  737. border: 1px solid rgba(64, 95, 234, 0.42);
  738. background-color: rgba(255, 255, 255, 1);
  739. color: rgba(16, 16, 16, 1);
  740. font-size: 14px;
  741. display: inline-flex;
  742. padding: 4px 8px;
  743. border-radius: 4px;
  744. align-items: center;
  745. cursor: pointer;
  746. height: 30px;
  747. &.disabled {
  748. cursor: not-allowed;
  749. }
  750. &:hover {
  751. background: rgb(245, 247, 254, 0.4);
  752. }
  753. >span {
  754. margin: 0 8px;
  755. }
  756. >b {
  757. margin-right: 8px;
  758. }
  759. }
  760. >section {
  761. padding: 0 16px;
  762. background-color: #ffffff;
  763. border-radius: 12px;
  764. margin-top: 16px;
  765. >p {
  766. display: inline-block;
  767. color: rgba(16, 16, 16, 1);
  768. font-size: 12px;
  769. margin-top: 16px;
  770. width: 100%;
  771. }
  772. >div {
  773. margin-top: 4px;
  774. overflow: hidden;
  775. margin-bottom: 16px;
  776. display: inline-block;
  777. width: 100%;
  778. >div {
  779. margin-top: 8px;
  780. >div {
  781. &:nth-child(1) {
  782. border-radius: 4px;
  783. color: rgba(16, 16, 16, 1);
  784. font-size: 12px;
  785. height: 24px;
  786. border: 1px solid rgba(229, 229, 229, 1);
  787. display: flex;
  788. justify-content: center;
  789. align-items: center;
  790. padding-right: 8px;
  791. cursor: pointer;
  792. &.disabled {
  793. cursor: not-allowed;
  794. }
  795. &:hover {
  796. background: rgba(229, 229, 229, 0.2);
  797. }
  798. >span {
  799. &:nth-child(1) {
  800. background-color: rgba(0, 174, 163, 0.2);
  801. color: #00aea3;
  802. font-size: 12px;
  803. border: 1px solid rgba(64, 95, 234, 0.13);
  804. padding: 0 4px;
  805. margin: 0 4px 0 0;
  806. }
  807. &:nth-child(3) {
  808. color: #00aea3;
  809. font-size: 12px;
  810. em {
  811. padding: 0 4px;
  812. font-style: normal;
  813. border-left: 1px solid rgba(64, 95, 234, 1);
  814. font-weight: bold;
  815. &:nth-child(1) {
  816. border: none;
  817. }
  818. }
  819. }
  820. }
  821. }
  822. &:nth-child(2) {
  823. >p {
  824. margin: 8px 0;
  825. display: flex;
  826. >span {
  827. transform: translateY(2px);
  828. color: #00aea3;
  829. font-weight: bold;
  830. font-size: 12px;
  831. margin-right: 8px;
  832. }
  833. }
  834. }
  835. }
  836. }
  837. >p {
  838. float: left;
  839. width: 48%;
  840. border-radius: 4px;
  841. background-color: rgba(245, 247, 254, 1);
  842. color: rgba(16, 16, 16, 1);
  843. font-size: 12px;
  844. margin-top: 8px;
  845. height: 36px;
  846. line-height: 36px;
  847. padding: 0 8px;
  848. display: flex;
  849. align-items: center;
  850. &:nth-child(odd) {
  851. margin-right: 4%;
  852. }
  853. }
  854. }
  855. }
  856. }
  857. .section-suggestions {
  858. margin: 12px 0 20px;
  859. .section-suggestion {
  860. width: 380px;
  861. height: 40px;
  862. line-height: 40px;
  863. border: 1px solid #e5e5e5;
  864. border-radius: 12px;
  865. padding: 0 8px;
  866. display: flex;
  867. align-items: center;
  868. justify-content: space-between;
  869. cursor: pointer;
  870. margin-top: 8px;
  871. &.disabled {
  872. cursor: not-allowed;
  873. }
  874. &:hover {
  875. background: rgb(245, 247, 254);
  876. }
  877. }
  878. }
  879. .dialog-other {
  880. margin-bottom: 16px;
  881. display: flex;
  882. align-items: center;
  883. justify-content: space-between;
  884. >p {
  885. display: flex;
  886. align-items: center;
  887. span {
  888. margin-left: 8px;
  889. color: rgba(16, 16, 16, 1);
  890. font-size: 12px;
  891. }
  892. }
  893. >div {
  894. display: flex;
  895. p {
  896. display: flex;
  897. align-items: center;
  898. justify-content: center;
  899. width: 30px;
  900. height: 30px;
  901. border-radius: 15px;
  902. border: 1px solid rgba(229, 229, 229, 1);
  903. margin-left: 8px;
  904. cursor: pointer;
  905. &.disabled {
  906. cursor: not-allowed;
  907. }
  908. &:hover {
  909. background-color: rgba(245, 247, 254, 1);
  910. }
  911. }
  912. }
  913. }
  914. .feedback-title {
  915. color: rgba(102, 102, 102, 1);
  916. font-size: 12px;
  917. margin: 0 0 8px 4px;
  918. }
  919. .feedback-content {
  920. margin-bottom: 16px;
  921. }
  922. .dialog-writing-button {
  923. position: absolute;
  924. bottom: -30px;
  925. left: 50%;
  926. transform: translateX(-50%);
  927. text-align: center;
  928. width: 106px;
  929. height: 34px;
  930. line-height: 34px;
  931. border-radius: 4px;
  932. background-color: rgba(255, 255, 255, 1);
  933. color: rgba(16, 16, 16, 1);
  934. font-size: 14px;
  935. border: 1px solid rgba(229, 229, 229, 1);
  936. display: flex;
  937. align-items: center;
  938. justify-content: center;
  939. cursor: pointer;
  940. z-index: 10;
  941. &:hover {
  942. background: rgb(245, 247, 254);
  943. }
  944. }
  945. </style>