useMsg.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. // 消息数组
  2. import { ref, reactive, nextTick, inject } from 'vue'
  3. import { storeToRefs } from 'pinia'
  4. import avator from '@/public/icon/32.png'
  5. import moment from 'moment'
  6. import {
  7. getFormKey,
  8. buildObjPrompt,
  9. getFileSummaryPrompt,
  10. formatMessage,
  11. sendMessage
  12. } from '../utils/ai-service.js'
  13. import { ElMessage } from 'element-plus'
  14. import { getPageInfo } from '../utils/index.js'
  15. import { mockData, mockData2 } from '../mock'
  16. import { useMsgStore } from '@/store/modules/msg'
  17. import { FunctionList } from '../mock'
  18. export function useMsg(scrollbar?: any) {
  19. const { messages, msgUuid } = storeToRefs(useMsgStore())
  20. const indexTemp = ref(0)
  21. const taklToHtml = ref<any>(false)
  22. const sendLoading = ref(false)
  23. const pageInfo = ref<any>({})
  24. const type = ref(FunctionList.File_Operation)
  25. const formMap = ref([])
  26. // 获取父组件提供的 Hook 实例
  27. const { useStore } = inject('indexedDBHook') as any
  28. const getFileSummary = async (file: any) => {
  29. const obj = reactive({
  30. id: moment(),
  31. username: '用户1',
  32. rawContent: '',
  33. content: '解析文件中',
  34. timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
  35. isSelf: false,
  36. avatar: avator,
  37. addToHistory: !taklToHtml.value
  38. })
  39. try {
  40. sendLoading.value = true
  41. messages.value.push(obj)
  42. nextTick(() => scrollbar.value?.setScrollTop(99999))
  43. const res = await getFileValue(file)
  44. console.log(res.data, file)
  45. await streamRes()
  46. } catch (error) {
  47. obj.content = '解析出错'
  48. } finally {
  49. sendLoading.value = false
  50. }
  51. }
  52. const getFormKeyAndValue = async (file: any, form?: any) => {
  53. // const obj = reactive({
  54. // id: moment(),
  55. // username: '用户1',
  56. // rawContent: '',
  57. // content: '解析文件中',
  58. // timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
  59. // isSelf: false,
  60. // avatar: avator,
  61. // addToHistory: !taklToHtml.value
  62. // })
  63. try {
  64. sendLoading.value = true
  65. // messages.value.push(obj)
  66. const response = await getFormKey({
  67. body: form,
  68. input_data: file
  69. })
  70. return response
  71. } catch (error) {
  72. // obj.content = '解析出错'
  73. } finally {
  74. sendLoading.value = false
  75. }
  76. }
  77. // 发送消息
  78. // const handleSend = async (msg: any) => {
  79. // if ( msg?.startsWith('/')) {
  80. // if (!taklToHtml.value)
  81. // return messages.value.push({
  82. // id: messages.value.length + 1,
  83. // username: '用户1',
  84. // content: '请打开与页面对话!',
  85. // rawContent: '请打开与页面对话!',
  86. // timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
  87. // isSelf: false,
  88. // avatar: avator,
  89. // addToHistory: false
  90. // })
  91. // indexTemp.value = 0
  92. // fetchRes(msg)
  93. // }
  94. // }
  95. async function awaitFindForm(obj: any) {
  96. return await new Promise((res, rej) => {
  97. chrome.runtime.sendMessage(
  98. {
  99. type: 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM'
  100. },
  101. ({ status, data }) => {
  102. if (status === 'error') {
  103. obj.content = '当前页面未找到表单'
  104. res({ status })
  105. }
  106. if (status === 'ok') {
  107. obj.content = '请上传数据'
  108. res({ status, data })
  109. }
  110. if (status === 'select') {
  111. obj.content = '检测到左侧页面中有多个表单,请选择要填写的表单。'
  112. function handle(message, sender, sendResponse) {
  113. if (message.type === 'TO_SIDE_PANEL_FORM_INFO') {
  114. console.log('收到一次性消息:', message.data)
  115. res({ status: 'ok', data: message.data })
  116. obj.content = '请上传数据'
  117. console.log(565656)
  118. // 销毁监听器(确保只触发一次)
  119. chrome.runtime.onMessage.removeListener(handle)
  120. }
  121. }
  122. chrome.runtime.onMessage.addListener(handle)
  123. }
  124. }
  125. )
  126. })
  127. }
  128. const fetchRes = async (msg: any) => {
  129. indexTemp.value = 0
  130. sendLoading.value = true
  131. const obj: any = reactive({
  132. id: messages.value.length + 1,
  133. username: '用户1',
  134. content: '',
  135. timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
  136. isSelf: false,
  137. avatar: avator,
  138. addToHistory: !taklToHtml.value
  139. })
  140. messages.value.push(obj)
  141. msg = msg.split('/智能填表')[1]
  142. nextTick(() => scrollbar.value?.setScrollTop(99999))
  143. if (!msg) {
  144. sendLoading.value = false
  145. const res = await awaitFindForm(obj)
  146. return res
  147. }
  148. const res = await fetchDataAndProcess(msg, obj)
  149. sendLoading.value = false
  150. if (res.status === 'ok') {
  151. await new Promise((res: any) =>
  152. setTimeout(() => {
  153. res()
  154. }, 2000)
  155. )
  156. const res = await awaitFindForm(obj)
  157. return res
  158. }
  159. }
  160. let str = ''
  161. async function fetchDataAndProcess(input: any, obj: any) {
  162. str = input
  163. console.log(str)
  164. const pageInfo = await getPageInfo()
  165. await new Promise((res: any) =>
  166. setTimeout(() => {
  167. res()
  168. }, 2000)
  169. )
  170. // const res = await hepl({
  171. // input_data: input,
  172. // body: pageInfo.content.mainContent
  173. // })
  174. const res: any = await new Promise((resolve, reject) => {
  175. setTimeout(() => {
  176. resolve({
  177. data:
  178. pageInfo.title === '智能招采'
  179. ? mockData[indexTemp.value]
  180. : mockData2[indexTemp.value]
  181. })
  182. }, 1000)
  183. })
  184. if (!res.data.tag || res.data.tag === 'undefined') {
  185. ElMessage({
  186. message: '未找到标签,请重试',
  187. type: 'error',
  188. duration: 4 * 1000,
  189. grouping: true
  190. })
  191. obj.content = '未找到标签,请重试'
  192. return { status: 'error' }
  193. }
  194. await new Promise((resolve) => setTimeout(resolve, 2000))
  195. obj.content = `点击${res.data.tag}元素`
  196. const res2 = await new Promise((resolve, rej) => {
  197. chrome.runtime.sendMessage(
  198. {
  199. type: 'FROM_SIDE_PANEL_TO_ACTION',
  200. data: res.data
  201. },
  202. async ({ data, status }) => {
  203. if (chrome.runtime.lastError) {
  204. console.error('消息发送错误:', chrome.runtime.lastError)
  205. } else {
  206. if (status === 'error') {
  207. obj.content = data
  208. resolve({ data, status })
  209. }
  210. if (res.data.next === '是') {
  211. const arr = str.split(',')
  212. arr.shift()
  213. str = arr.join(',')
  214. indexTemp.value++
  215. const res = await fetchDataAndProcess(str, obj)
  216. resolve(res)
  217. } else resolve({ status: 'ok' })
  218. // else {
  219. // await new Promise((resolve, reject) => {
  220. // setTimeout(() => {
  221. // resolve(1)
  222. // }, 2000)
  223. // })
  224. // obj.content = `请选择表单`
  225. // ElMessage({
  226. // message: '请选择表单',
  227. // type: 'success',
  228. // duration: 4 * 1000,
  229. // grouping: true
  230. // })
  231. // chrome.runtime.sendMessage({
  232. // type: 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM'
  233. // })
  234. // }
  235. }
  236. }
  237. )
  238. })
  239. return res2
  240. }
  241. /**
  242. *
  243. * @param addHtml 是否添加页面信息
  244. */
  245. const streamRes = async (addHtml: any = false) => {
  246. pageInfo.value = await getPageInfo()
  247. sendLoading.value = true
  248. const obj = reactive<any>({
  249. id: messages.value.length + 1,
  250. username: '用户1',
  251. content: '',
  252. rawContent: '', // 存储原始内容
  253. timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
  254. isSelf: false,
  255. avatar: avator,
  256. addToHistory: !taklToHtml.value
  257. })
  258. let history = []
  259. if (taklToHtml.value) {
  260. if (addHtml) {
  261. history.push({
  262. role: 'user',
  263. content: `页面主要内容${pageInfo.value.content.mainContent}`
  264. })
  265. }
  266. history.push({
  267. role: 'user',
  268. content: messages.value[messages.value.length - 1].rawContent
  269. })
  270. } else {
  271. history = messages.value
  272. .filter((item: any) => item.addToHistory)
  273. .slice(-20)
  274. .map((item: any) => ({
  275. role: item.isSelf ? 'user' : 'system',
  276. content: item.rawContent
  277. }))
  278. }
  279. messages.value.push(obj)
  280. nextTick(() => {
  281. scrollbar.value?.setScrollTop(99999)
  282. })
  283. const iterator = await sendMessage(history)
  284. for await (const chunk of iterator) {
  285. if (chunk) {
  286. const decodedChunk = chunk.choices[0].delta.content
  287. if (decodedChunk) {
  288. // 保存原始内容
  289. obj.rawContent += decodedChunk
  290. // 实时格式化显示内容
  291. obj.content = formatMessage(obj.rawContent)
  292. // if (type.value === '2') obj.content = obj.content.replace(/item/g, '表单项').replace(/excelColumn/g, '对应数据源')
  293. }
  294. }
  295. scrollbar.value?.setScrollTop(99999)
  296. }
  297. console.log(obj.rawContent)
  298. //添加到存储历史
  299. useStore(msgUuid.value).add({ ...obj })
  300. // 处理最终内容
  301. sendLoading.value = false
  302. nextTick(() => {
  303. scrollbar.value?.setScrollTop(99999)
  304. })
  305. if (type.value === '2') {
  306. const arr = JSON.parse(obj.rawContent.split('json')[1].split('```')[0])
  307. const newArr = arr
  308. .map((obj) => {
  309. return `| ${[
  310. `“findBy”: \` "label",`,
  311. `"findByValue" \` “${obj.findByValue}”,`,
  312. `"数据来源字段" \` “${obj.excelColumn}”,`,
  313. `"表单项" \` “${obj.item}"`
  314. ].join(' ')} |`
  315. })
  316. .join('\n')
  317. obj.content = formatMessage('```json' + JSON.stringify(newArr) + '```')
  318. console.log(formatMessage('```json' + JSON.stringify(newArr) + '```'))
  319. }
  320. return obj.rawContent
  321. }
  322. async function requestFlowFn(data: any[]) {
  323. sendLoading.value = true
  324. const obj = reactive<any>({
  325. id: messages.value.length + 1,
  326. username: '用户1',
  327. content: '',
  328. rawContent: '', // 存储原始内容
  329. timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
  330. isSelf: false,
  331. avatar: avator,
  332. addToHistory: !taklToHtml.value
  333. })
  334. messages.value.push(obj)
  335. scrollbar.value?.setScrollTop(99999)
  336. const iterator = await sendMessage(data)
  337. if (iterator.error) {
  338. // 实时格式化显示内容
  339. obj.content = iterator.error
  340. } else {
  341. for await (const chunk of iterator) {
  342. if (chunk) {
  343. const decodedChunk = chunk.choices[0].delta.content
  344. if (decodedChunk) {
  345. // 保存原始内容
  346. obj.rawContent += decodedChunk
  347. // 实时格式化显示内容
  348. obj.content = formatMessage(obj.rawContent)
  349. }
  350. }
  351. scrollbar.value?.setScrollTop(99999)
  352. }
  353. }
  354. //添加到存储历史
  355. useStore(msgUuid.value).add({ ...obj })
  356. // 处理最终内容
  357. sendLoading.value = false
  358. nextTick(() => {
  359. scrollbar.value?.setScrollTop(99999)
  360. })
  361. }
  362. return {
  363. indexTemp,
  364. taklToHtml,
  365. sendLoading,
  366. formMap,
  367. type,
  368. fetchRes,
  369. streamRes,
  370. getFormKeyAndValue,
  371. getFileSummary,
  372. requestFlowFn
  373. }
  374. }