Answer.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. <template>
  2. <div class="dialog_answer">
  3. <div class="answer-item">
  4. <div class="answer-item-icon">
  5. <a-dropdown @select="handleSelect" trigger="hover">
  6. <img :src="target_icon" alt="" class="img-icon" />
  7. <template #content>
  8. <a-doption value="1">切换头像</a-doption>
  9. </template>
  10. </a-dropdown>
  11. <!-- <a-tooltip content="编辑">
  12. <icon-edit class="edit-icon" @click="handleEditIcon" />
  13. </a-tooltip> -->
  14. </div>
  15. <div class="answer-item-content" v-if="chat_type === 'znwd'">
  16. <!-- {{ dialog.question }} -->
  17. <DialogContent
  18. id="refference_parent"
  19. :content="answer_html"
  20. :type="dialog.client_custom_item_type"
  21. :is_loading="is_content_loading"
  22. :is_think="is_think"
  23. :deep_loading="deep_loading"
  24. :deep_answer="deep_answer"
  25. >
  26. <!-- <HomeChart v-if="has_echarts"></HomeChart> -->
  27. <DialogActions v-if="answer_status == 2" class="answer-actions-btns" :type="type" @copy="handleAnswerCopy" />
  28. </DialogContent>
  29. <!-- 引用数据 -->
  30. <Reference v-if="answer_status == 2 && !has_echarts" ref="refference" :full_refference="full_refference">
  31. </Reference>
  32. <!-- {{ (has_chunks, answer_status) }} -->
  33. <!-- 对话已经结束,并且有引用数据 -->
  34. </div>
  35. <div class="answer-item-content" v-if="chat_type === 'bgsc'">
  36. <DialogReportContent
  37. v-if="!is_report_outline"
  38. :is_loading="is_content_loading"
  39. :type="type"
  40. :result_list="report_content"
  41. :answer_status="answer_status"
  42. >
  43. <DialogActions
  44. v-if="answer_status == 2"
  45. class="answer-actions-btns"
  46. :type="type"
  47. @copy="handleReportCopy"
  48. @edit="handleReportEdit"
  49. />
  50. </DialogReportContent>
  51. <DialogReportOutline v-if="is_report_outline" :content="report_outline" :is_loading="is_content_loading">
  52. <DialogOutlineActions
  53. v-if="answer_status == 2"
  54. @update="toUpdateOutLine"
  55. @confirm="toConfirmOutLine"
  56. ></DialogOutlineActions>
  57. </DialogReportOutline>
  58. <!-- -->
  59. <AttachFileList v-if="has_attach" :attach_file_list="attach_file_list"></AttachFileList>
  60. <!-- 报告文件 -->
  61. <div class="file_list">
  62. <ReportFile
  63. v-if="has_Report"
  64. :file="report_file"
  65. :is_download_loading="is_download_loading"
  66. @toDownload="toDownload"
  67. >
  68. </ReportFile>
  69. <ReportExcel
  70. v-if="has_excel"
  71. :file="report_excel"
  72. :is_download_loading="is_download_loading_excel"
  73. @toDownload="toDownloadExcel"
  74. >
  75. </ReportExcel>
  76. </div>
  77. </div>
  78. <div class="answer-item-content" v-if="chat_type === 'znws'">
  79. <!-- 智能问数的组件渲染和报告生成的一致,先使用同一个,后续有变动再单独拆分 -->
  80. <DialogReportContent
  81. :is_loading="is_content_loading"
  82. :type="type"
  83. :answer_status="answer_status"
  84. :result_list="report_content"
  85. >
  86. <DialogActions
  87. v-if="answer_status == 2"
  88. class="answer-actions-btns"
  89. :type="type"
  90. :like_status="answer_evaluation"
  91. @copy="handleReportCopy"
  92. @like="handleLike"
  93. @hate="handleHate"
  94. />
  95. </DialogReportContent>
  96. </div>
  97. <div class="answer-item-content" v-if="chat_type === 'bgcl'">
  98. <DialogBgclContent :content="bgcl_content" v-if="analyzeOrMerge">
  99. <RuleAction
  100. :event_id="props.dialog.event_id"
  101. :disabled="props.dialog.client_custom_config_rule_disabled"
  102. @merge="toMerge"
  103. >
  104. </RuleAction>
  105. </DialogBgclContent>
  106. <DialogBgclMerge
  107. :loading="merge_loading"
  108. :content="merge_content"
  109. :merge_error_content="merge_error_content"
  110. :merge_table_data="merge_table_data"
  111. :dialog="dialog"
  112. v-if="!analyzeOrMerge"
  113. >
  114. <ReportExcel
  115. v-if="show_merge_excel"
  116. :file="merge_excel"
  117. @toDownload="downloadMergeExcel"
  118. :is_download_loading="is_download_loading"
  119. ></ReportExcel>
  120. </DialogBgclMerge>
  121. </div>
  122. </div>
  123. </div>
  124. <RobotIcons
  125. v-if="showModal"
  126. :showModal="showModal"
  127. @close="handleClose"
  128. @update="handleUpdate"
  129. :target_agent_icon="target_agent_icon"
  130. ></RobotIcons>
  131. </template>
  132. <script setup>
  133. import { ref, computed, onMounted } from 'vue';
  134. import { useRoute, useRouter } from 'vue-router';
  135. import { Message } from '@arco-design/web-vue';
  136. import DialogContent from './DialogContent.vue';
  137. import DialogReportOutline from './DialogReportOutline.vue';
  138. import DialogReportContent from './DialogReportContent.vue';
  139. import DialogBgclContent from './DialogBgclContent.vue';
  140. import DialogBgclMerge from './DialogBgclMerge.vue';
  141. import RuleAction from './RuleAction.vue';
  142. import DialogActions from './DialogActions.vue';
  143. import HomeChart from '../chat_echart/HomeChart.vue';
  144. import ReportFile from '../chat_files/ReportFile.vue';
  145. import ReportExcel from '../chat_files/ReportExcel.vue';
  146. import AttachFileList from '../chat_files/AttachFileList.vue';
  147. import RobotIcons from './RobotIcons.vue';
  148. import DialogOutlineActions from './DialogOutlineActions.vue';
  149. import copyText from '../../3rd-libs/clipboard';
  150. import formatAnwserString from '../conversation-dialog/format-anwser-string';
  151. // import RefferenceFileBlocks from '../answer-refference/RefferenceFileBlocks.vue';
  152. import Reference from './Reference.vue';
  153. import { useThemeStore } from '../../base/store/use-theme';
  154. import { THEME_LIGHT, THEME_DARK, THEME_GREEN, THEME_PARTY } from '../../base/variables';
  155. import { getIcon } from '../common/use-icon-map';
  156. import { downloadReport, downloadExcel, toEvaluation } from '../conversation-dialog/api-dialog';
  157. import { getMergeExcel, getCheckedErrorList } from '../table-dialog/api-dialog';
  158. import { mergeTableData } from '../../modules/table-dialog/api-dialog';
  159. import { formatMdToHtmlForView } from '../conversation-dialog/format-html-md-switch-for-report';
  160. const route = useRoute();
  161. const router = useRouter();
  162. const props = defineProps({
  163. dialog: {
  164. type: Object,
  165. default: ''
  166. },
  167. type: {
  168. type: String,
  169. default: 'znwd'
  170. },
  171. target_agent_icon: {
  172. type: String,
  173. default: 'icon_1'
  174. }
  175. });
  176. const emit = defineEmits(['update', 'merge', 'update_outline', 'confirm_outline']);
  177. const themeStore = useThemeStore();
  178. const refference = ref(null);
  179. const showModal = ref(false);
  180. const is_download_loading = ref(false);
  181. const is_download_loading_excel = ref(false);
  182. const target_icon = computed(() => {
  183. if (themeStore.value == THEME_GREEN) {
  184. return getIcon(props.target_agent_icon, 'a');
  185. } else if (themeStore.value == THEME_PARTY) {
  186. return getIcon(props.target_agent_icon, 'b');
  187. } else if (themeStore.value == THEME_LIGHT) {
  188. return getIcon(props.target_agent_icon, 'c');
  189. } else {
  190. return getIcon(props.target_agent_icon, 'a');
  191. }
  192. });
  193. console.log(themeStore.value, THEME_PARTY, props.target_agent_icon, 909);
  194. const report_content = computed(() => {
  195. // console.log(props.dialog)
  196. const result_list = props.dialog.result_list;
  197. // console.log(result_list)
  198. // if (result_list) {
  199. // result_list.forEach(item => {
  200. // if (typeof item === 'string') {
  201. // console.log(111111111111111)
  202. // item = formatAnwserString(item)
  203. // }
  204. // })
  205. // }
  206. return result_list;
  207. });
  208. const chat_type = computed(() => {
  209. return route.query.id || 'znwd';
  210. });
  211. const answer_html = computed(() => {
  212. const answer = props.dialog?.answer;
  213. let answer_string = formatAnwserString(answer);
  214. // let answer_string = answer ? marked.parse(answer) : '暂无回答';
  215. return answer_string;
  216. });
  217. const answer_status = computed(() => {
  218. return props.dialog.client_custom_chat_status;
  219. });
  220. /**
  221. * 内容是否正在加载中
  222. */
  223. const is_content_loading = computed(() => {
  224. // console.log(props.dialog);
  225. return answer_status.value === 0;
  226. });
  227. /**
  228. * 是否展示报告大纲
  229. * */
  230. const is_report_outline = computed(() => {
  231. return props.dialog.client_custom_report_outline;
  232. });
  233. const report_outline = computed(() => {
  234. if (props.dialog.client_custom_report_outline) {
  235. // console.log(props.dialog?.result_list[0]);
  236. if (props.dialog?.result_list && props.dialog?.result_list[0]) {
  237. return formatMdToHtmlForView(props.dialog.result_list[0]);
  238. } else {
  239. return '';
  240. }
  241. } else {
  242. return '';
  243. }
  244. });
  245. /**
  246. * 展示图标
  247. * */
  248. const has_echarts = computed(() => {
  249. return answer_status.value == 2 && props.dialog.has_echarts;
  250. });
  251. /**
  252. * 展示文档
  253. * */
  254. const has_Report = computed(() => {
  255. const request_id = props.dialog.client_request_id;
  256. if (request_id / 1 === 5) {
  257. return false;
  258. }
  259. return answer_status.value == 2 && props.dialog.report_files && props.dialog.report_files.file_name;
  260. });
  261. const has_excel = computed(() => {
  262. const request_id = props.dialog.client_request_id;
  263. if (request_id / 1 === 5) {
  264. return false;
  265. }
  266. return answer_status.value == 2 && props.dialog.report_excels && props.dialog.report_excels.file_name;
  267. });
  268. /**
  269. * 报告内容
  270. * */
  271. const report_file = computed(() => {
  272. return props.dialog.report_files;
  273. });
  274. /**
  275. * 报表内容
  276. * */
  277. const report_excel = computed(() => {
  278. return props.dialog.report_excels;
  279. });
  280. /**
  281. * 月报附件
  282. * */
  283. const has_attach = computed(() => {
  284. return props.dialog.client_custom_attach_list && props.dialog.client_custom_attach_list.length;
  285. });
  286. /**
  287. * 月报附件列表
  288. * */
  289. const attach_file_list = computed(() => {
  290. return props.dialog.client_custom_attach_list;
  291. });
  292. /**
  293. * 评估状态
  294. * */
  295. const answer_evaluation = computed(() => {
  296. return props.dialog.answer_evaluation || 0;
  297. });
  298. /**
  299. * 完整引用数据
  300. */
  301. const full_refference = computed(() => {
  302. if (!props.dialog) {
  303. return [];
  304. }
  305. const answer = props.dialog;
  306. if (!answer.client_custom_linked_refference_full_content) {
  307. return [];
  308. }
  309. const refference = JSON.parse(answer.client_custom_linked_refference_full_content);
  310. if (Object.keys(refference).length === 0) {
  311. return [];
  312. }
  313. return refference;
  314. });
  315. /**
  316. * 是否显示引用效果
  317. */
  318. const has_refference = computed(() => {
  319. if (!full_refference.value) {
  320. return false;
  321. }
  322. return true;
  323. });
  324. /**
  325. * 是否显示思考过程
  326. * */
  327. const is_think = computed(() => {
  328. return props.dialog.client_custom_item_think;
  329. // return true
  330. });
  331. /**
  332. * 思考状态
  333. * */
  334. const deep_loading = computed(() => {
  335. return props.dialog.client_custom_chat_deep_loading;
  336. // return true
  337. });
  338. /**
  339. * 思考内容
  340. * */
  341. const deep_answer = computed(() => {
  342. const answer = props.dialog.client_custom_item_deep_answer;
  343. let answer_string = formatAnwserString(answer);
  344. return answer_string;
  345. // const answer_string = formatAnwserString('截止2025年4月20日,决策平台全省全口径供电可靠率99.845%,四川公司停电总时户数2206317时户,同比增加55.80%,停电时户数较多的单位为甘孜623161时户、凉山292774时户、阿坝199088时户。 截止2025年4月20日,决策平台全省全口径供电可靠率99.845%,四川公司停电总时户数2206317时户,同比增加55.80%,停电时户数较多的单位为甘孜623161时户、凉山292774时户、阿坝199088时户。')
  346. // return answer_string
  347. });
  348. /**
  349. * 判断当前是解析还是合并
  350. * */
  351. const analyzeOrMerge = computed(() => {
  352. if (props.dialog.is_analyze && !props.dialog.is_merge) return true;
  353. if (!props.dialog.is_analyze && props.dialog.is_merge) return false;
  354. });
  355. /**
  356. * 表格处理后,表格解析成功内容
  357. * */
  358. const bgcl_content = computed(() => {
  359. return props.dialog.is_analyze && props.dialog.analyzeContent;
  360. });
  361. /**
  362. * 表格合并状态
  363. * */
  364. const merge_loading = computed(() => {
  365. return props.dialog.status == 1 ? true : false;
  366. });
  367. /**
  368. * 表格处理后,表格合并内容
  369. * */
  370. const merge_content = computed(() => {
  371. return (props.dialog.is_merge && props.dialog.mergeContent) || '';
  372. });
  373. const merge_error_content = computed(() => {
  374. return (props.dialog.is_merge && props.dialog.error_content) || '';
  375. });
  376. /**
  377. * 表格处理后,表格合并中的内容
  378. * */
  379. const merge_table_data = computed(() => {
  380. return props.dialog.client_custom_bgcl_table_data;
  381. });
  382. /**
  383. * 表格处理,点击历史记录查看详情时,表格数据需要重新调用接口查询
  384. *
  385. */
  386. /**
  387. * 是否展示合并文件
  388. * */
  389. const show_merge_excel = computed(() => {
  390. return props.dialog.client_show_merge_file || false;
  391. });
  392. /**
  393. * 合并完成后,文件
  394. * */
  395. const merge_excel = computed(() => {
  396. const file = {};
  397. file.file_name = props.dialog.file_name;
  398. file.file_size = props.dialog.file_size;
  399. return file;
  400. });
  401. const handleAnswerCopy = () => {
  402. const answer = props.dialog?.answer_origin;
  403. copyText(answer);
  404. Message.success('复制成功');
  405. };
  406. const handleReportCopy = () => {
  407. const result_list = props.dialog.result_list;
  408. const copy_text = result_list.filter((item) => typeof item == 'string').join('');
  409. copyText(copy_text);
  410. Message.success('复制成功');
  411. };
  412. const handleLike = async (status) => {
  413. await toEvaluationFun(props.dialog.client_custom_chat_message_id, status);
  414. Message.success('感谢您的认可!\n您的反馈将帮助我们优化回答质量。');
  415. };
  416. const handleHate = async (status) => {
  417. await toEvaluationFun(props.dialog.client_custom_chat_message_id, status);
  418. Message.success('感谢反馈!我们会认真分析并改进这个问题。');
  419. };
  420. const handleEditIcon = () => {
  421. showModal.value = true;
  422. };
  423. const handleClose = () => {
  424. showModal.value = false;
  425. };
  426. const handleUpdate = (icon) => {
  427. emit('update', icon);
  428. showModal.value = false;
  429. };
  430. const handleReportEdit = () => {
  431. // console.log(props.dialog, 888);
  432. if (props.dialog?.report_files?.record_id) {
  433. router.push({
  434. path: `/report/${props.dialog.report_files.record_id}`
  435. });
  436. } else {
  437. if (props.dialog?.record_id) {
  438. router.push({
  439. path: `/report/${props.dialog.record_id}`
  440. });
  441. }
  442. }
  443. };
  444. const handleSelect = (v) => {
  445. if (v / 1 === 1) {
  446. handleEditIcon();
  447. }
  448. };
  449. const toMerge = (data) => {
  450. emit('merge', data);
  451. };
  452. const showRefferenceDetails = (refIndex) => {
  453. if (refference.value && refIndex) {
  454. const item = full_refference.value?.find((item) => item.id == refIndex);
  455. if (item) {
  456. refference.value.toDetail(item);
  457. }
  458. }
  459. };
  460. const toEvaluationFun = async (id, status) => {
  461. try {
  462. const res = await toEvaluation(id, status);
  463. } catch (e) {
  464. console.log(e);
  465. } finally {
  466. }
  467. };
  468. const downloadMergeExcel = async () => {
  469. const data = {
  470. filename: props.dialog.file_name,
  471. event_id: props.dialog.event_id
  472. };
  473. is_download_loading.value = true;
  474. try {
  475. const res = await getMergeExcel(data);
  476. // if(res.code/1)
  477. const url = URL.createObjectURL(res.data);
  478. const link = document.createElement('a');
  479. link.href = url;
  480. link.download = data.filename;
  481. link.click();
  482. } catch (e) {
  483. console.log(e);
  484. } finally {
  485. is_download_loading.value = false;
  486. }
  487. };
  488. const toDownload = () => {
  489. const record_id = props.dialog.report_files.record_id;
  490. if (record_id) {
  491. is_download_loading.value = true;
  492. downloadReport({ record_id })
  493. .then((res) => {
  494. const url = URL.createObjectURL(res.data);
  495. const link = document.createElement('a');
  496. link.href = url;
  497. link.download = props.dialog.report_files.file_name;
  498. link.click();
  499. })
  500. .catch((e) => {
  501. console.log(e);
  502. })
  503. .finally(() => {
  504. is_download_loading.value = false;
  505. });
  506. }
  507. };
  508. const toDownloadExcel = () => {
  509. const record_id = props.dialog.report_excels.record_id;
  510. if (record_id) {
  511. is_download_loading_excel.value = true;
  512. downloadExcel({ record_id })
  513. .then((res) => {
  514. const url = URL.createObjectURL(res.data);
  515. console.log(url);
  516. const link = document.createElement('a');
  517. link.href = url;
  518. link.download = props.dialog.report_excels.file_name;
  519. link.click();
  520. })
  521. .catch((e) => {
  522. console.log(e);
  523. })
  524. .finally(() => {
  525. is_download_loading_excel.value = false;
  526. });
  527. }
  528. };
  529. const toUpdateOutLine = () => {
  530. let md_content = '';
  531. console.log(props.dialog);
  532. console.log(props.dialog.result_list);
  533. if (props.dialog?.result_list && props.dialog?.result_list[0]) {
  534. md_content = props.dialog?.result_list[0];
  535. }
  536. const data = {
  537. md_content,
  538. start_date: props.dialog.client_custom_report_outline_start_date || '',
  539. end_date: props.dialog.client_custom_report_outline_end_date || '',
  540. area: props.dialog.client_custom_report_outline_area || '',
  541. theme: props.dialog.client_custom_report_outline_theme || ''
  542. };
  543. emit('update_outline', data);
  544. };
  545. const toConfirmOutLine = () => {
  546. emit('confirm_outline');
  547. };
  548. onMounted(() => {
  549. const refference_parent = document.getElementById('refference_parent');
  550. // console.log(refference_parent, 999)
  551. if (refference_parent) {
  552. refference_parent.addEventListener('click', (e) => {
  553. // console.log(e.target.classList, 999)
  554. if (e.target.classList.contains('client-formated-markdown-doc-refference-flag')) {
  555. const refIndex = e.target.getAttribute('data-ref-index');
  556. // console.log(refIndex, 1111)
  557. showRefferenceDetails(refIndex);
  558. }
  559. });
  560. }
  561. });
  562. </script>
  563. <style scoped lang="css">
  564. .dialog_answer {
  565. width: calc(100%);
  566. height: auto;
  567. }
  568. .file_list {
  569. display: flex;
  570. align-items: center;
  571. /* justify-content: center; */
  572. gap: 15px;
  573. }
  574. .answer-item {
  575. display: flex;
  576. /* justify-content: flex-end; */
  577. align-items: flex-start;
  578. }
  579. .answer-item-content {
  580. max-width: calc(100% - 62px - 62px + 55px);
  581. }
  582. .answer-item-icon {
  583. position: relative;
  584. }
  585. .answer-item-icon .edit-icon {
  586. position: absolute;
  587. right: 0;
  588. bottom: 10px;
  589. visibility: hidden;
  590. cursor: pointer;
  591. }
  592. .answer-item-icon:hover .edit-icon {
  593. visibility: visible;
  594. }
  595. .answer-item-icon img {
  596. margin-right: 22px;
  597. /* border-radius: 50%; */
  598. width: 40px;
  599. /* height: 30px; */
  600. flex-shrink: 0;
  601. cursor: pointer;
  602. }
  603. </style>