Home.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. <template>
  2. <div id="app">
  3. <Slider @select-nav="selectNav" :index="navId" v-show="!closeOthers"></Slider>
  4. <div class="main">
  5. <div :class="{ 'history-wrap': true, 'history-wrap-open': !closeOthers && navId > -1 }">
  6. <History :list="historyList" @set-new="() => {
  7. showDialog = false;
  8. selectId = selectId;
  9. }
  10. " :selectId="selectId" @handle-click="selectHistory" />
  11. </div>
  12. <div class="wrap">
  13. <Index :number="swiperNumber" v-if="navId === -1" @select-nav="selectNav" />
  14. <div :class="{ 'border-wrap': true, 'border-no-wrap': navId === -2 }" v-else>
  15. <div>
  16. <header v-if="!showDialog">
  17. <div>
  18. <img src="@/assets/other/robot.gif" width="52" />
  19. <p>
  20. {{ SYSTEM_NAME }}<span>{{ slider[navId].title }}</span>
  21. </p>
  22. <span>{{ slider[navId].dialog }}</span>
  23. </div>
  24. </header>
  25. <section :class="{ 'section-padding': showDialog }">
  26. <!-- 报告生成 -->
  27. <DocumentCombine v-if="navId == 0 && !showDialog" />
  28. <!-- 报表合并 -->
  29. <Document v-if="navId === 1 && !showDialog" />
  30. <!-- 知识问答 -->
  31. <DocumentQuestion v-if="navId === 2 && !showDialog" />
  32. <!-- 文档智能 -->
  33. <DocumentAnswer v-if="navId === 3 && !showDialog" :agentId="selectId" />
  34. <!-- 智能问答 -->
  35. <Answer v-if="navId === 4 && !showDialog" />
  36. <!-- 文库详情 -->
  37. <DocumentDetail v-if="navId === -2" @go-back="goBack" />
  38. <dialog-wrap v-if="showDialog && initDialog" v-show="!hideDialog" :type="dialogType[navId]"
  39. :isSetting="false" :readonly="readonly" @setReadonly="setReadonly"
  40. @goDocument="goDocument" :agentId="selectId" :dialogId="dialogId" :list="dialogList" />
  41. </section>
  42. <footer v-if="navId !== -2 && navId !== 0 && navId !== 3">
  43. <message-input :readonly="readonly" eventName="open-dialog" />
  44. </footer>
  45. <footer v-if="navId === 0 && !showDialog" class="upload-footer">
  46. <section v-for="(item, key) in variables" :key="key">
  47. <div>
  48. <p>
  49. <span>{{ item.variable_name }}</span>
  50. </p>
  51. </div>
  52. <div>
  53. <a-input :style="{ flex: '1' }" :placeholder="`请输入${item.variable_name}`"
  54. allow-clear @change="(event) => {
  55. customChange(event, item.node_id, item.variable_name);
  56. }
  57. " v-if="item.value_type === 1" />
  58. <a-upload v-else :style="{ width: '300px' }" :multiple="false" :limit="1"
  59. :custom-request="(option) => {
  60. customRequest(option, item.node_id, item.variable_name);
  61. }
  62. " :show-upload-button="{ showOnExceedLimit: true }"
  63. accept=".pdf,.doc,.docx,.txt,.md,.ppt.pptx" />
  64. </div>
  65. </section>
  66. <a-textarea @keydown.enter="beginCombine" :max-length="maxLength" v-model="textValue"
  67. :show-word-limit="textValue.length > maxLength / 2" :auto-size="{ maxRows: 6 }"
  68. placeholder="输入您想了解的内容,Shift+Enter换行" :style="{
  69. flex: 1,
  70. borderRadius: '12px',
  71. marginTop: '20px',
  72. padding: '0px 30px 0 16px',
  73. background: 'transparent',
  74. border: '1px solid #e3e3e3',
  75. minHeight: '120px',
  76. paddingBottom: `${textValue.length > maxLength / 2 ? '20px' : ''}`
  77. }">
  78. </a-textarea>
  79. <a-button :style="{ width: '500px' }" type="primary" @click="beginCombine">开始</a-button>
  80. </footer>
  81. <footer v-if="navId === 3 && !showDialog" class="upload-footer">
  82. <section v-for="(item, key) in variables" :key="key">
  83. <div>
  84. <p>
  85. <span>{{ item.variable_name }}</span>
  86. </p>
  87. </div>
  88. <div>
  89. <a-input :style="{ flex: '1' }" :placeholder="`请输入${item.variable_name}`"
  90. allow-clear @change="(event) => {
  91. customChange(event, item.node_id, item.variable_name);
  92. }
  93. " v-if="item.value_type === 1" />
  94. <a-upload v-else :style="{ width: '300px' }" :multiple="false" :limit="1"
  95. :custom-request="(option) => {
  96. customRequest(option, item.node_id, item.variable_name);
  97. }
  98. " :show-upload-button="{ showOnExceedLimit: true }"
  99. accept=".pdf,.doc,.docx,.txt,.md,.ppt.pptx" />
  100. </div>
  101. </section>
  102. <!-- <a-textarea @keydown.enter="beginCombine" :max-length="maxLength" v-model="textValue"
  103. :show-word-limit="textValue.length > maxLength / 2" :auto-size="{ maxRows: 6 }"
  104. placeholder="输入您想了解的内容,Shift+Enter换行" :style="{
  105. flex: 1,
  106. borderRadius: '12px',
  107. marginTop: '20px',
  108. padding: '0px 30px 0 16px',
  109. background: 'transparent',
  110. border: '1px solid #e3e3e3',
  111. minHeight: '120px',
  112. paddingBottom: `${textValue.length > maxLength / 2 ? '20px' : ''}`
  113. }">
  114. </a-textarea> -->
  115. <a-button :style="{ width: '500px' }" type="primary" @click="beginCombine">开始</a-button>
  116. </footer>
  117. <footer v-if="navId === 3 && showDialog" class="upload-footer">
  118. <a-textarea @keydown.enter="beginCombine" :max-length="maxLength" v-model="textValue"
  119. :show-word-limit="textValue.length > maxLength / 2" :auto-size="{ maxRows: 6 }"
  120. placeholder="输入您想了解的内容,Shift+Enter换行" :style="{
  121. flex: 1,
  122. borderRadius: '12px',
  123. marginTop: '20px',
  124. padding: '0px 30px 0 16px',
  125. background: 'transparent',
  126. border: '1px solid #e3e3e3',
  127. minHeight: '120px',
  128. paddingBottom: `${textValue.length > maxLength / 2 ? '20px' : ''}`
  129. }">
  130. </a-textarea>
  131. <a-button :style="{ width: '500px' }" type="primary" @click="continueCombine">开始</a-button>
  132. </footer>
  133. <a-tooltip :content="closeOthers ? '收起' : '展开'">
  134. <div @click="openAll" v-if="navId !== -2">
  135. <img src="@/assets/other/open.svg" width="20" v-if="closeOthers" />
  136. <img src="@/assets/other/close.svg" width="20" v-else />
  137. </div>
  138. </a-tooltip>
  139. </div>
  140. </div>
  141. </div>
  142. <!-- <right v-if="navId === 0 || navId === 2" /> -->
  143. </div>
  144. </div>
  145. </template>
  146. <script>
  147. import Right from "../components/Right.vue";
  148. import DialogWrap from "../components/DialogWrap.vue";
  149. import Slider from "../components/Slider.vue";
  150. import History from "../components/History.vue";
  151. import MessageInput from "../components/MessageInput.vue";
  152. import DocumentDetail from "./DocumentDetail.vue";
  153. import DocumentCombine from "./DocumentCombine.vue";
  154. import Index from "./Index.vue";
  155. import Answer from "./Answer.vue";
  156. import Document from "./Document.vue";
  157. import PicArticle from "./PicArticle.vue";
  158. import DocumentAnswer from "./DocumentAnswer.vue";
  159. import DocumentQuestion from "./DocumentQuestion.vue";
  160. import Analysis from "./Analysis.vue";
  161. import { defineComponent, ref, onMounted, inject, onBeforeUnmount } from "vue";
  162. export default defineComponent({
  163. components: {
  164. Right,
  165. Slider,
  166. History,
  167. Index,
  168. Answer,
  169. MessageInput,
  170. DialogWrap,
  171. Document,
  172. PicArticle,
  173. DocumentAnswer,
  174. DocumentQuestion,
  175. Analysis,
  176. DocumentDetail,
  177. DocumentCombine
  178. },
  179. setup() {
  180. // nav对应的问答类型,4文库问答,对应的是knowledge类型
  181. // NOTE: 先把4文库问答的类型改成report类型,后续再改回来
  182. const dialogType = ["report", "excel", "knowledgeQuiz", "report", "normal", "normal", "normal", "normal"];
  183. const { config, helper, ajax } = inject("$global");
  184. const SYSTEM_NAME = config.defaultTitle
  185. const closeOthers = ref(false);
  186. const swiperNumber = ref(5);
  187. const navId = ref(-1);
  188. const readonly = ref(false);
  189. const showDialog = ref(false);
  190. const dialogList = ref([]);
  191. const selectId = ref("");
  192. const initDialog = ref(false);
  193. const hideDialog = ref(false);
  194. const agentIds = ref([]);
  195. const dialogId = ref("");
  196. const historyList = ref([]);
  197. const variables = ref([]);
  198. const textValue = ref("");
  199. const maxLength = ref(6000);
  200. let reportData = {};
  201. let agents = {}
  202. const openAll = () => {
  203. closeOthers.value = !closeOthers.value;
  204. };
  205. const selectNav = async (node) => {
  206. navId.value = node.id;
  207. textValue.value = [];
  208. if (node.index === -1) {
  209. swiperNumber.value = 5;
  210. } else {
  211. swiperNumber.value = 4;
  212. }
  213. helper.$bus.emit("stop-writing");
  214. showDialog.value = false;
  215. selectId.value = (agents.find(agent => agent.name === node.title) || {}).id || null;
  216. const data = await ajax.message.getDialogs(selectId.value);
  217. if (navId.value === 0 || navId.value === 3) {
  218. const result = await ajax.agent.getVariables(selectId.value);
  219. variables.value = result;
  220. reportData = {};
  221. }
  222. historyList.value = data.map((value) => {
  223. return {
  224. content: value.name,
  225. id: value.id,
  226. time: new Date(value.updated_time).toLocaleString()
  227. };
  228. });
  229. };
  230. const setReadonly = (status) => {
  231. readonly.value = status;
  232. };
  233. const goDocument = () => {
  234. navId.value = -2;
  235. hideDialog.value = true;
  236. };
  237. const goBack = () => {
  238. navId.value = 0;
  239. hideDialog.value = false;
  240. };
  241. const selectHistory = async (id) => {
  242. showDialog.value = true;
  243. selectId.value = id;
  244. helper.$bus.emit("set-loading", true);
  245. helper.$bus.emit("stop-writing");
  246. initDialog.value = false;
  247. // TODO 历史记录
  248. // const data = await ajax.message.getHistoryLogs(selectId.value, id);
  249. const data = await ajax.message.getHistoryLogs('42e4fcdc9bea11efac300242ac160006', id);
  250. if (data.length > 0) {
  251. console.log('data', data)
  252. dialogList.value = data
  253. }
  254. initDialog.value = true;
  255. helper.$bus.emit("set-loading", false);
  256. };
  257. const beginCombine = async () => {
  258. let report_name = "";
  259. let inputData = [];
  260. for (let key in reportData) {
  261. if (typeof reportData[key].value === "string") {
  262. report_name += `${reportData[key].key}:${reportData[key].value}\n`;
  263. inputData.push({
  264. id: key,
  265. name: reportData[key].key,
  266. file_path: "",
  267. value: reportData[key].value
  268. });
  269. } else {
  270. report_name += `${reportData[key].key}:${reportData[key].value.name}\n`;
  271. inputData.push({
  272. id: key,
  273. name: reportData[key].key,
  274. file_path: reportData[key].value.url,
  275. value: ""
  276. });
  277. }
  278. }
  279. const data = await ajax.message.createDialog(selectId.value);
  280. dialogId.value = data.chat_id;
  281. dialogList.value = [];
  282. showDialog.value = true;
  283. initDialog.value = true;
  284. const sendData = {
  285. chatHistory: [],
  286. message: textValue.value,
  287. name: "报告生成",
  288. description: "报告生成",
  289. inputs: {
  290. report_name,
  291. id: "Report-PRcYL",
  292. data: inputData
  293. }
  294. };
  295. setTimeout(() => {
  296. helper.$bus.emit("send-message", sendData);
  297. }, 500);
  298. };
  299. const continueCombine = async () => {
  300. const sendData = {
  301. chatHistory: [],
  302. name: "报告生成",
  303. description: "报告生成",
  304. inputs: {
  305. report_name: textValue.value,
  306. id: "Report-PRcYL",
  307. query: textValue.value,
  308. }
  309. };
  310. setTimeout(() => {
  311. helper.$bus.emit("send-message", sendData);
  312. textValue.value = "";
  313. }, 500);
  314. };
  315. const customChange = (event, key, value) => {
  316. reportData[key] = {
  317. key: value,
  318. value: event
  319. };
  320. };
  321. const customRequest = async (option, key, value) => {
  322. const { onSuccess, fileItem } = option;
  323. const { file, uid, name } = fileItem;
  324. const fileList = [];
  325. fileList.push({
  326. id: uid,
  327. file
  328. });
  329. onSuccess();
  330. const formData = new FormData();
  331. fileList.forEach((item) => {
  332. formData.append("file", item.file);
  333. });
  334. const data = await ajax.file.uploadFiles(selectId.value, formData);
  335. reportData[key] = {
  336. key: value,
  337. value: {
  338. name,
  339. url: data.file_path
  340. }
  341. };
  342. };
  343. onMounted(async () => {
  344. agents = await ajax.agent.getAgents();
  345. agentIds.value = agents.map((data) => data.id);
  346. helper.$bus.on("open-dialog", async (value) => {
  347. const data = await ajax.message.createDialog(selectId.value);
  348. dialogId.value = data.chat_id;
  349. dialogList.value = [];
  350. showDialog.value = true;
  351. initDialog.value = true;
  352. setTimeout(() => {
  353. helper.$bus.emit("send-message", value);
  354. }, 200);
  355. });
  356. });
  357. onBeforeUnmount(() => {
  358. helper.webSocket?.close();
  359. helper.$bus.off("open-dialog");
  360. });
  361. return {
  362. SYSTEM_NAME,
  363. customChange,
  364. customRequest,
  365. historyList,
  366. beginCombine,
  367. continueCombine,
  368. selectId,
  369. dialogId,
  370. navId,
  371. variables,
  372. selectNav,
  373. swiperNumber,
  374. slider: config.slider,
  375. readonly,
  376. setReadonly,
  377. showDialog,
  378. selectHistory,
  379. closeOthers,
  380. openAll,
  381. dialogType,
  382. dialogList,
  383. initDialog,
  384. goDocument,
  385. goBack,
  386. hideDialog,
  387. agentIds,
  388. textValue,
  389. maxLength
  390. };
  391. }
  392. });
  393. </script>
  394. <style lang="scss" scoped>
  395. .main {
  396. .upload-footer {
  397. margin-bottom: 50px;
  398. padding: 20px;
  399. width: 540px;
  400. border: 1px solid #eee;
  401. >section {
  402. >div {
  403. display: flex;
  404. align-items: center;
  405. justify-content: space-between;
  406. width: 500px;
  407. >p {
  408. width: 150px;
  409. margin-right: 10px;
  410. }
  411. }
  412. p {
  413. span {
  414. position: relative;
  415. &:after {
  416. content: "*";
  417. display: block;
  418. color: red;
  419. position: absolute;
  420. right: -10px;
  421. top: -5px;
  422. }
  423. }
  424. }
  425. >p,
  426. >div {
  427. margin: 10px 0;
  428. }
  429. }
  430. }
  431. flex: 1;
  432. position: relative;
  433. display: flex;
  434. .history-wrap {
  435. width: 0px;
  436. opacity: 0;
  437. overflow: hidden;
  438. transition-duration: 0.3s;
  439. transition-property: width;
  440. &-open {
  441. opacity: 1;
  442. width: 260px;
  443. }
  444. }
  445. .wrap {
  446. flex: 1;
  447. }
  448. .border-wrap {
  449. position: relative;
  450. padding: 30px 16px 30px 40px;
  451. flex: 1;
  452. height: 100%;
  453. &.border-no-wrap {
  454. padding: 0;
  455. >div {
  456. padding: 0;
  457. >section.section-padding {
  458. padding: 0;
  459. }
  460. }
  461. }
  462. >div {
  463. width: 100%;
  464. height: 100%;
  465. background: #fff;
  466. box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.03);
  467. border-radius: 14px;
  468. display: flex;
  469. flex-direction: column;
  470. padding: 0 32px;
  471. position: relative;
  472. >div {
  473. position: absolute;
  474. bottom: -10px;
  475. left: -10px;
  476. z-index: 10;
  477. width: 38px;
  478. height: 38px;
  479. border-radius: 20px;
  480. background: #fff;
  481. display: flex;
  482. align-items: center;
  483. justify-content: center;
  484. cursor: pointer;
  485. }
  486. >header {
  487. >div {
  488. display: inline-block;
  489. margin-top: 20px;
  490. >img {
  491. transform: translateX(-12px);
  492. }
  493. >p {
  494. font-size: 36px;
  495. font-weight: bold;
  496. color: #000;
  497. >span {
  498. font-weight: bold;
  499. background: linear-gradient(180deg, #41d98c, #31c4c9);
  500. -webkit-background-clip: text;
  501. -webkit-text-fill-color: transparent;
  502. }
  503. }
  504. >span {
  505. display: block;
  506. color: #000;
  507. font-size: 15px;
  508. margin: 5px 0 32px;
  509. }
  510. }
  511. }
  512. >section {
  513. flex: 1;
  514. overflow: hidden;
  515. &.section-padding {
  516. padding-bottom: 50px;
  517. }
  518. }
  519. >footer {}
  520. }
  521. }
  522. }
  523. </style>