AdvancedMode.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. <!-- Chat.vue -->
  2. <template>
  3. <div class="chat-container" style="padding-top: 3rem;">
  4. <!-- <div v-if="!messages.length && !msgLoading" class="message-list">
  5. <pageMask />
  6. </div> -->
  7. <!-- 消息列表 -->
  8. <div class="message-list">
  9. <el-scrollbar ref="scrollbar" @scroll="handleScroll" v-loading="msgLoading">
  10. <!-- 加载更多指示器 -->
  11. <div v-if="isLoadingMore" class="loading-more-indicator">
  12. <div class="loading-spinner"></div>
  13. <span>加载更多消息...</span>
  14. </div>
  15. <div class="messages" ref="messagesContainer" >
  16. <div v-for="(message, index) in messages" :key="index"
  17. :class="['message-item', message.role === 'user' ? 'self' : 'other']">
  18. <el-avatar :size="32" :src="message.role === 'user' ? userAvatar : avatar" />
  19. <div class="message-content">
  20. <div class="content" v-if="message.role ==='system'"
  21. :class="{ 'loading-content': message.content === '' }">
  22. <StepsDisplay
  23. v-if="message.type === 'plan'"
  24. :content="message.rawContent"
  25. :initialStep="0"
  26. @step-change="handleStepChange" @complete="handleComplete"
  27. />
  28. <span v-else v-html="message.content"></span>
  29. <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
  30. <span class="dot"></span>
  31. <span class="dot"></span>
  32. <span class="dot"></span>
  33. </span>
  34. </div>
  35. <document v-else-if="message.type === 'document' && message.role === 'user'" :content="message.content"
  36. :rawContent="message.rawContent" />
  37. <div v-else class="content">
  38. <span v-if="message.type === ''">{{ message.content }}</span>
  39. <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
  40. <span class="dot"></span>
  41. <span class="dot"></span>
  42. <span class="dot"></span>
  43. </span>
  44. </div>
  45. <div class="timestamp ">{{ moment(message.sortKey).format('MM-DD HH:mm') }}
  46. <!-- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span> -->
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. </el-scrollbar>
  52. <ScrollToBottom :target="scrollbar" ref="scrollToBottomRef" />
  53. </div>
  54. <!-- <Tools v-if="selectModal" :disHistory="sendLoading" :upload="type === FunctionList.File_Operation || !!formInfo" @read-click="readClick" @upload-file="(file) => createFileObj(file)" @handle-capture="handleCapture" @his-records="hisRecords"
  55. @add-new-dialogue="addNewDialogue" @handle-current-change="handleCurrentChange"
  56. @handel-intelligent-filling-click="handelIntelligentFillingClick" /> -->
  57. <div>
  58. <!-- 输入区域 -->
  59. <div class="input-area">
  60. <el-icon class="closeShow" :style="{ display: isShowPage ? 'block' : 'none'}" size="16px" color="#909399" @click="closePageInfo">
  61. <CircleClose />
  62. </el-icon>
  63. <!-- <div v-show="isShowPage" style="border-bottom: 1px solid #F0F0F0;">
  64. <div class="card_list">
  65. <div v-for="(v, i) in pageInfoList" :key="i"
  66. :class="`card-content ${pageInfoList.length > 1 ? 'card_width' : ''}`">
  67. <div class="loading-more-indicator" v-if="v.loading">
  68. <div class="loading-spinner"></div>
  69. </div>
  70. <img v-else :src="v?.favIconUrl" style="width: 24px;display: block" />
  71. <div class="title-wrapper">
  72. <span class="els title-scroller">{{ v?.title }}</span>
  73. <span class="els url-scroller">{{ v.loading ? v?.state : v?.url }}</span>
  74. </div>
  75. <el-icon class="closeIcon" size="16px" color="#909399" @click="deletePageInfo(i)">
  76. <CircleClose />
  77. </el-icon>
  78. </div>
  79. <div v-show="!pageInfoList.length && type === FunctionList.Intelligent_Form_filling">智能填表</div>
  80. </div>
  81. <div class="card-btn">
  82. <el-tooltip content="总结" placement="top">
  83. <el-button :disabled="disabledBtn" v-if="type !== FunctionList.Intelligent_Form_filling" round @click="handleSummary">总结</el-button>
  84. </el-tooltip>
  85. <el-tooltip content="抽取表单信息" placement="top">
  86. <el-button :disabled="disabledBtn" v-if="type === FunctionList.Intelligent_Form_filling && pageInfoList.length" round @click="handleSummaryFile">抽取</el-button>
  87. </el-tooltip>
  88. </div>
  89. </div> -->
  90. <el-input ref="textareaRef" v-model="inputMessage" type="textarea" :rows="3" placeholder="输入消息..."
  91. @keyup.enter="() => handleAsk()" />
  92. <div class="chat_area_op">
  93. <el-button v-if="sendLoading" style="background-color:#ffffff;border:2px solid rgb(134 143 153);" circle
  94. @click="handleStopAsk">
  95. <svg-icon icon-class="stop" color="red" />
  96. </el-button>
  97. <el-button
  98. v-else
  99. :style="`background-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'};border-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'}`"
  100. type="primary" circle @click="() => handleAsk()"
  101. :disabled="disSend">
  102. <svg-icon icon-class="send" color="#000000" />
  103. </el-button>
  104. </div>
  105. </div>
  106. </div>
  107. <!-- <historyComponent :msgUuid="msgUuid" ref="historyComponentRef" @currentData="(e) => handleCurrentData(e)" /> -->
  108. </div>
  109. </template>
  110. <script setup>
  111. import { ref, onMounted, nextTick, inject, useTemplateRef, reactive,onBeforeMount } from 'vue'
  112. import { ElScrollbar, ElAvatar, ElInput, ElButton } from 'element-plus'
  113. import moment from 'moment'
  114. import fileLogo from '@/assets/svg/file.svg'
  115. import {
  116. getFileContent,
  117. buildObjPrompt,
  118. controllerList,
  119. formatMessage
  120. } from '@/entrypoints/sidepanel/utils/ai-service.js'
  121. import { storeToRefs } from 'pinia'
  122. import { ElMessage } from 'element-plus'
  123. import { useMsg } from '@/entrypoints/sidepanel/hook/useMsg.ts'
  124. import Tools from '@/entrypoints/sidepanel/component/tools.vue'
  125. import historyComponent from '@/entrypoints/sidepanel/component/historyComponent.vue'
  126. import document from '@/entrypoints/sidepanel/component/document.vue'
  127. import pageMask from '@/entrypoints/sidepanel/component/pageMask.vue'
  128. import ScrollToBottom from '@/entrypoints/sidepanel/component/ScrollToBottom.vue'
  129. import formTable from '@/entrypoints/sidepanel/component/formTable.vue'
  130. import StepsDisplay from './component/StepsDisplay.vue'
  131. import userAvatar from '@/assets/images/user.png'
  132. import avatar from '@/public/icon/icon.png'
  133. import { options, FunctionList } from '@/entrypoints/sidepanel/mock'
  134. import { useAutoResizeTextarea } from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts'
  135. import { getPageInfo, getPageInfoClean, handleInput,downloadFile } from './utils/index.js'
  136. import { useMsgStore } from '@/store/modules/msg.ts'
  137. import { useUserStore } from '@/store/modules/user'
  138. // import { putChat, uploadFile } from "@/api/index.js";
  139. // import {askQues,getFormKey} from '@/api/modal.js'
  140. import { debounce } from 'lodash'
  141. const userStore = useUserStore()
  142. // 在其他状态变量附近添加
  143. const isLoadingMore = ref(false)
  144. import { v4 as uuidv4 } from 'uuid';
  145. import { getPlan,getPlan2 } from '@/api/advance.js'
  146. // 滚动条引用
  147. const scrollbar = ref(null)
  148. const scrollToBottomRef = ref(null)
  149. const xlsxData = ref({})
  150. const isShowPage = ref(false)
  151. const tareRef = useTemplateRef('textareaRef')
  152. const drawerRef = useTemplateRef('historyComponentRef')
  153. const disSend = computed(() => {
  154. if (pageInfoList.value.length && type.value === FunctionList.Intelligent_Form_filling) return true
  155. if (sendLoading.value) return true
  156. if (inputMessage.value.trim()) return false
  157. return true
  158. })
  159. // 获取父组件提供的 Hook 实例
  160. const msgStore = useMsgStore()
  161. const { pageInfoList, messages, msgUuid, AIModel,page,hasNext,msgLoading,selectModal } = storeToRefs(useMsgStore())
  162. const formInfo = ref('')
  163. const {
  164. taklToHtml,
  165. sendLoading,
  166. type,
  167. fetchRes
  168. } = useMsg(scrollbar)
  169. const inputMessage = ref('')
  170. const pageInfo = ref('')
  171. const summaryHtml = ref(false)
  172. function handleStopAsk() {
  173. inputMessage.value = ''
  174. pageInfoList.value = []
  175. isShowPage.value = false
  176. taklToHtml.value = false
  177. type.value = FunctionList.File_Operation
  178. controllerList.value[0]?.abort()
  179. controllerList.value = []
  180. sendLoading.value = false
  181. formInfo.value = null
  182. }
  183. function handleScroll(a) {
  184. const { wrapRef } = scrollbar.value
  185. const { scrollHeight, clientHeight } = wrapRef
  186. scrollToBottomRef.value.showButton = scrollHeight - a.scrollTop - clientHeight >= 350
  187. // 检测是否滚动到顶部
  188. if (a.scrollTop <= 10 && hasNext.value) {
  189. handleScrollToTop()
  190. }
  191. }
  192. const messagesContainer = ref(null)
  193. let innerText = ''
  194. // 处理滚动到顶部的事件
  195. const handleScrollToTop = debounce(async function () {
  196. console.log('滚动到顶部,可以加载更多历史消息')
  197. if (!sendLoading.value && !isLoadingMore.value) {
  198. isLoadingMore.value = true
  199. try {
  200. // 记录当前第一条消息的位置
  201. const firstMessage = messagesContainer.value?.firstElementChild.querySelector('.timestamp')
  202. innerText = firstMessage.innerText
  203. const oldHeight = firstMessage?.offsetTop || 0
  204. await msgStore.changePage()
  205. setTimeout(() => {
  206. if (firstMessage) {
  207. console.log([...messagesContainer.value.querySelectorAll('.timestamp')].find(_ => _.innerText === innerText).offsetTop, innerText, 778);
  208. const newHeight = [...messagesContainer.value.querySelectorAll('.timestamp')].find(_ => _.innerText === innerText).offsetTop
  209. const scrollOffset = newHeight - oldHeight
  210. scrollbar.value?.setScrollTop(scrollOffset)
  211. }
  212. }, 400);
  213. // 恢复滚动位置
  214. } finally {
  215. isLoadingMore.value = false
  216. }
  217. }
  218. }, 500, { leading: false, trailing: true })
  219. const handleStepChange = (step) => {
  220. console.log('当前步骤:', step)
  221. }
  222. const handleComplete = () => {
  223. console.log('所有步骤已完成')
  224. }
  225. /**
  226. * @param {string} msg
  227. * @param {string} raw
  228. * @param {string} type 发送的类型 document 、text
  229. * **/
  230. async function addMessage(msg, raw, type) {
  231. const newMessage = reactive({
  232. id:+new Date(),
  233. type: type || '',
  234. rawContent: raw ?? msg,
  235. senderId: userStore.userInfo.id,
  236. receiverId:-1, //大模型
  237. content: msg,
  238. sortKey: moment().valueOf(),
  239. role: 'user',
  240. conversationId:msgUuid.value,
  241. addToHistory: `${!taklToHtml.value}`,
  242. redisKey: undefined,
  243. fileId:undefined
  244. })
  245. if (type === 'document') {
  246. newMessage.content = msg
  247. newMessage.rawContent = raw
  248. }
  249. if (!msg) return
  250. messages.value.push(newMessage)
  251. nextTick(() => {
  252. if (scrollbar.value && scrollbar.value.wrapRef) {
  253. scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
  254. }
  255. })
  256. return newMessage
  257. }
  258. const handleSummary = async () => {
  259. if(sendLoading.value) return
  260. handleAsk(`请帮我总结当前${summaryHtml.value ? '页面' : '文件'}`)
  261. summaryHtml.value = false
  262. }
  263. const handleSummaryFile = async () => {
  264. if (sendLoading.value) return
  265. try {
  266. sendLoading.value = true
  267. const userMsg = await addMessage(JSON.stringify([...pageInfoList.value, { type: 'text', value: '抽取表单' }]), '抽取表单', 'document')
  268. userMsg.fileId = pageInfoList.value[0].fileId
  269. userMsg.redisKey = pageInfoList.value[0].redisKey
  270. await putChat(userMsg)
  271. createWS(userMsg)
  272. formInfo.value = ''
  273. // const msg = reactive({
  274. // type: '',
  275. // rawContent: '',
  276. // senderId: -1,
  277. // receiverId: userStore.userInfo.id, //大模型
  278. // content: '',
  279. // sortKey: moment().valueOf(),
  280. // role: 'system',
  281. // conversationId: msgUuid.value,
  282. // })
  283. // messages.value.push(msg)
  284. // isShowPage.value = false
  285. pageInfoList.value = []
  286. // const resForm = await askQues({
  287. // conversationId: msgUuid.value,
  288. // modelName: '通义千问-Max',
  289. // question: buildObjPrompt(xlsxData.value,formInfo.value),
  290. // id: '699637194561691650',
  291. // // redisKey:msg.redisKey
  292. // })
  293. // return console.log(resForm);
  294. } catch (error) {
  295. console.log(error);
  296. } finally {
  297. // sendLoading.value = false
  298. // type.value = FunctionList.File_Operation
  299. }
  300. }
  301. async function handelIntelligentFillingClick() {
  302. handleInput()
  303. isShowPage.value = true
  304. // taklToHtml.value = true
  305. // const tempPageInfo = await getPageInfo()
  306. // pageInfo.value = tempPageInfo
  307. // pageInfoList.value = [tempPageInfo]
  308. inputMessage.value = '/智能填表 '
  309. type.value = FunctionList.Intelligent_Form_filling
  310. }
  311. function handleCurrentChange(e) {
  312. msgStore.updateAIModel(e)
  313. // if (AIModel.value.file === true) {
  314. // isShowPage.value = false
  315. // taklToHtml.value = false
  316. // pageInfoList.value = []
  317. // }
  318. }
  319. async function handleCurrentData(e) {
  320. drawerRef.value.drawer = false
  321. isShowPage.value = false
  322. if (!e) {
  323. addNewDialogue()
  324. return
  325. }
  326. messages.value = []
  327. page.value = 1
  328. msgUuid.value = e
  329. chrome.storage.local.set({ msgUuid: msgUuid.value })
  330. await msgStore.initMsg()
  331. nextTick(() => {
  332. if (scrollbar.value && scrollbar.value.wrapRef) {
  333. scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
  334. }
  335. })
  336. }
  337. let SystemMsg = null
  338. watchEffect(() => {
  339. console.log(isShowPage.value);
  340. if (!isShowPage.value) {
  341. summaryHtml.value = false
  342. type.value = FunctionList.File_Operation
  343. SystemMsg = null
  344. inputMessage.value = ''
  345. }
  346. })
  347. const htmlIcon = ref('')
  348. async function readClick() {
  349. if (type.value === FunctionList.Intelligent_Form_filling) {
  350. pageInfoList.value = []
  351. type.value = FunctionList.File_Operation
  352. }
  353. summaryHtml.value = true
  354. isShowPage.value = true
  355. taklToHtml.value = true
  356. if (pageInfoList.value.length >= Number(import.meta.env.VITE_MAX_FILE_NUMBER)) {
  357. ElMessage.warning(`最多添加${import.meta.env.VITE_MAX_FILE_NUMBER}个文件`)
  358. return
  359. }
  360. const tempPageInfo = await getPageInfo()
  361. htmlIcon.value = tempPageInfo.favIconUrl
  362. const blob = new Blob([tempPageInfo.content.mainContent], { type: 'text/plain' })
  363. const file = new File([blob], '页面' + '.txt', { type: 'text/plain' })
  364. // console.log(file);
  365. // 下载文件到本地
  366. // downloadFile(file);
  367. console.log(tempPageInfo);
  368. await createFileObj(file)
  369. // await handleUpload(file,tempPageInfo.title + '.txt')
  370. }
  371. // 添加下载文件的函数
  372. function deletePageInfo(i) {
  373. pageInfoList.value.splice(i, 1)
  374. if (pageInfoList.value.length === 0) {
  375. if (type.value === FunctionList.Intelligent_Form_filling) {
  376. inputMessage.value = ''
  377. SystemMsg.content = '智能填表流程中断'
  378. SystemMsg.rawContent = '智能填表流程中断'
  379. putChat({
  380. ...SystemMsg,
  381. content: '',
  382. })
  383. }
  384. handleStopAsk()
  385. }
  386. }
  387. function addNewDialogue() {
  388. if (messages.value.length === 0) {
  389. ElMessage.warning('已经是新对话')
  390. return
  391. }
  392. summaryHtml.value = false
  393. type.value = FunctionList.File_Operation
  394. isShowPage.value = false
  395. taklToHtml.value = false
  396. messages.value = []
  397. page.value = 1
  398. pageInfoList.value = []
  399. msgUuid.value = uuidv4()
  400. hasNext.value = false
  401. chrome.storage.local.set({ msgUuid: msgUuid.value })
  402. }
  403. const disabledBtn = computed(() => {
  404. return !!(pageInfoList.value.find(_ => _.loading ))
  405. })
  406. function closePageInfo() {
  407. pageInfoList.value = []
  408. if (type.value === FunctionList.Intelligent_Form_filling && SystemMsg ) {
  409. SystemMsg.content = '智能填表流程中断'
  410. SystemMsg.rawContent = '智能填表流程中断'
  411. putChat({
  412. ...SystemMsg,
  413. content: '',
  414. })
  415. }
  416. handleStopAsk()
  417. }
  418. async function handleAsk(value) {
  419. if (sendLoading.value) return
  420. const str = value ?? inputMessage.value.trim()
  421. inputMessage.value = ''
  422. if (type.value === FunctionList.Intelligent_Form_filling) {
  423. const msg = await addMessage(str)
  424. // await putChat(msg)
  425. const [res,obj] = await fetchRes(str)
  426. if (res.status === 'ok') {
  427. console.log(obj);
  428. formInfo.value = res.data
  429. SystemMsg = obj
  430. } else {
  431. type.value = FunctionList.File_Operation
  432. isShowPage.value = false
  433. }
  434. return
  435. }
  436. if (sendLoading.value) return
  437. let msg = null
  438. if (pageInfoList.value.length) {
  439. msg = await addMessage(JSON.stringify([...pageInfoList.value, { type: 'text', value: str }]), str, 'document')
  440. msg.fileId = pageInfoList.value[0].fileId
  441. msg.redisKey = pageInfoList.value[0].redisKey
  442. } else msg = await addMessage(str)
  443. pageInfoList.value = []
  444. isShowPage.value = false
  445. // await putChat(msg)
  446. createWS(msg)
  447. }
  448. /**
  449. *
  450. * @param msg 用户消息对象,调用askQues时需要
  451. */
  452. async function createWS(msg) {
  453. sendLoading.value = true
  454. const obj = reactive({
  455. type: '',
  456. rawContent: '',
  457. senderId: -1,
  458. receiverId: userStore.userInfo.id, //大模型
  459. content: '',
  460. sortKey: moment().valueOf(),
  461. role: 'system',
  462. conversationId: msgUuid.value,
  463. })
  464. try {
  465. messages.value.push(obj)
  466. nextTick(() => {
  467. if (scrollbar.value && scrollbar.value.wrapRef) {
  468. scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
  469. }
  470. })
  471. getPlan2({
  472. conversationId:8,
  473. messageId:28,
  474. messageContent: msg.rawContent,
  475. }).then(res => {
  476. sendLoading.value = false
  477. obj.type = 'plan'
  478. console.log(res,5558);
  479. obj.rawContent = res.data.plan
  480. // handlePlan(res)
  481. }).catch(res => {
  482. obj.rawContent = '接口出错,请重试。'
  483. obj.content = '接口出错,请重试。'
  484. })
  485. } finally {
  486. }
  487. }
  488. function handlePlan(res) {
  489. const obj = reactive({
  490. type: 'plan',
  491. rawContent: res,
  492. senderId: -1,
  493. receiverId: userStore.userInfo.id, //大模型
  494. content: '',
  495. sortKey: moment().valueOf(),
  496. role: 'system',
  497. conversationId: msgUuid.value,
  498. })
  499. messages.value.push(obj)
  500. }
  501. function handleCapture() {
  502. ElMessage({
  503. message: '开发中...',
  504. grouping: true,
  505. showClose: true
  506. })
  507. }
  508. function hisRecords() {
  509. drawerRef.value.drawer = true
  510. }
  511. const createFileObj = async (file) => {
  512. handleUpload(file)
  513. }
  514. const handleUpload = async (file) => {
  515. const obj = reactive({
  516. title: summaryHtml.value ? file.name.split('.')[0] : file.name,
  517. state: '上传中...',
  518. favIconUrl: fileLogo,
  519. loading: true,
  520. url:'',
  521. fileId: '',
  522. redisKey:'',
  523. content: {
  524. mainContent: ''
  525. }
  526. })
  527. if (type.value === FunctionList.Intelligent_Form_filling) {
  528. pageInfoList.value = []
  529. }
  530. if (summaryHtml.value) obj.favIconUrl = htmlIcon.value
  531. isShowPage.value = true
  532. // const obj = reactive({
  533. // title: file.name,
  534. // state: '上传中...',
  535. // favIconUrl: fileLogo,
  536. // loading: true,
  537. // url:'',
  538. // fileId: '',
  539. // redisKey:'',
  540. // content: {
  541. // mainContent: ''
  542. // }
  543. // })
  544. pageInfoList.value = [obj]
  545. try {
  546. let formData = new FormData()
  547. formData.append('avatarFile', file)
  548. const res = await uploadFile(formData)
  549. if (type.value === FunctionList.Intelligent_Form_filling) {
  550. obj.state = '解析中...'
  551. obj.redisKey = res.data.redisKey
  552. obj.id = res.data.file.id
  553. obj.url = res.data.file.url
  554. // const fileExtension = file.name.split('.').pop().toLowerCase()
  555. const result = await getFormKey({
  556. body: formInfo.value,
  557. input_data:res.data.data
  558. })
  559. xlsxData.value = JSON.parse(result.data)
  560. // if (fileExtension === 'xlsx') {
  561. // const readData = await getXlsxValue(file)
  562. // readData[0].forEach((header, i) => {
  563. // // if (!xlsxData.value[header]) xlsxData.value[header] = []
  564. // xlsxData.value[header] = readData[1][i]
  565. // })
  566. // } else {
  567. // const result = await getFormKey({
  568. // body: formInfo.value,
  569. // input_data:res.data.data
  570. // })
  571. // xlsxData.value = JSON.parse(result.data)
  572. // }
  573. }
  574. nextTick(() => {
  575. if (scrollbar.value && scrollbar.value.wrapRef) {
  576. scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
  577. }
  578. })
  579. if (type.value === FunctionList.File_Operation) {
  580. obj.loading = false
  581. obj.url = res.data.url
  582. obj.redisKey = res.data.redisKey
  583. obj.fileId = res.data.file.id
  584. obj.url = res.data.file.url
  585. }
  586. obj.loading = false
  587. console.log(SystemMsg);
  588. } catch (error) {
  589. if (SystemMsg) {
  590. SystemMsg.content = '数据上传出错,请重试'
  591. SystemMsg.rawContent = '数据上传出错,请重试'
  592. }
  593. console.log(error);
  594. ElMessage.error('上传出错')
  595. pageInfoList.value = []
  596. isShowPage.value = false
  597. } finally {
  598. SystemMsg && putChat({
  599. ...SystemMsg,
  600. content: '',
  601. })
  602. SystemMsg = null
  603. }
  604. }
  605. async function getFileValue(file) {
  606. let formData = new FormData()
  607. formData.append('file', file)
  608. const res = await getFileContent(formData)
  609. return res.data
  610. }
  611. let a = null
  612. // 组件挂载时滚动到底部
  613. onBeforeMount(async () => {
  614. await msgStore.initMsg()
  615. await msgStore.initModal()
  616. })
  617. onMounted(async () => {
  618. // msgStore.updateAIModel(options[0].options[0])
  619. useAutoResizeTextarea(tareRef, inputMessage)
  620. chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  621. if (message.type === 'TO_SIDE_PANEL_PAGE_INFO') {
  622. pageInfo.value = message.data
  623. // 转发到 content.js
  624. chrome.tabs.sendMessage(sender.tab.id, {
  625. type: 'TO_CONTENT_SCRIPT',
  626. data: message.data
  627. })
  628. }
  629. if (message.type === 'TO_SIDE_PANEL_PAGE_CHANGE') {
  630. pageInfo.value = message.data
  631. }
  632. })
  633. nextTick(() => {
  634. if (scrollbar.value && scrollbar.value.wrapRef) {
  635. scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
  636. }
  637. })
  638. })
  639. </script>
  640. <style lang="scss" scoped>
  641. @use '@/entrypoints/sidepanel/css/chat.scss';
  642. @use '@/entrypoints/sidepanel/css/markdown.scss';
  643. .loading-more-indicator {
  644. display: flex;
  645. align-items: center;
  646. justify-content: center;
  647. padding: 10px 0;
  648. color: #909399;
  649. font-size: 14px;
  650. .loading-spinner {
  651. width: 20px;
  652. height: 20px;
  653. margin-right: 8px;
  654. border: 2px solid #e6e6e6;
  655. border-top-color: #4d6bfe;
  656. border-radius: 50%;
  657. animation: spin 1s linear infinite;
  658. }
  659. }
  660. @keyframes spin {
  661. to {
  662. transform: rotate(360deg);
  663. }
  664. }
  665. </style>