|
@@ -12,10 +12,6 @@
|
|
|
<div class="loading-spinner"></div>
|
|
|
<span>加载更多消息...</span>
|
|
|
</div>
|
|
|
- // 添加一个 ref 用于获取消息列表容器
|
|
|
- const messagesContainer = ref(null)
|
|
|
-
|
|
|
- // 修改模板中的消息列表 div,添加 ref
|
|
|
<div class="messages" ref="messagesContainer">
|
|
|
<div v-for="(message, index) in messages" :key="index"
|
|
|
:class="['message-item', message.role === 'user' ? 'self' : 'other']">
|
|
@@ -42,7 +38,7 @@
|
|
|
</span>
|
|
|
</div>
|
|
|
<div class="timestamp ">{{ moment(message.sortKey).format('MM-DD HH:mm') }}
|
|
|
- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
|
|
|
+ <!-- <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -51,7 +47,7 @@
|
|
|
<ScrollToBottom :target="scrollbar" ref="scrollToBottomRef" />
|
|
|
</div>
|
|
|
|
|
|
- <Tools @read-click="readClick" @upload-file="handleUpload" @handle-capture="handleCapture" @his-records="hisRecords"
|
|
|
+ <Tools @read-click="readClick" @upload-file="(file) => createFileObj(file)" @handle-capture="handleCapture" @his-records="hisRecords"
|
|
|
@add-new-dialogue="addNewDialogue" @handle-current-change="handleCurrentChange"
|
|
|
@handel-intelligent-filling-click="handelIntelligentFillingClick" />
|
|
|
|
|
@@ -69,7 +65,7 @@
|
|
|
|
|
|
<div class="title-wrapper">
|
|
|
<span class="els title-scroller">{{ v?.title }}</span>
|
|
|
- <span class="els url-scroller">{{ v?.url }}</span>
|
|
|
+ <span class="els url-scroller">{{ v.loading ? v?.state : v?.url }}</span>
|
|
|
</div>
|
|
|
<el-icon class="closeIcon" size="16px" color="#909399" @click="deletePageInfo(i)">
|
|
|
<CircleClose />
|
|
@@ -77,19 +73,22 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <div v-show="type !== FunctionList.Intelligent_Form_filling" class="card-btn">
|
|
|
- <el-tooltip content="总结当前页面" placement="top">
|
|
|
- <el-button round @click="handleSummary">总结</el-button>
|
|
|
+ <div class="card-btn">
|
|
|
+ <el-tooltip content="总结当前页面" placement="top">
|
|
|
+ <el-button v-if="type !== FunctionList.Intelligent_Form_filling" round @click="handleSummary">总结</el-button>
|
|
|
+ </el-tooltip>
|
|
|
+ <el-tooltip content="抽取表单信息" placement="top">
|
|
|
+ <el-button v-if="type === FunctionList.Intelligent_Form_filling && pageInfoList.length" round @click="handleSummary">抽取</el-button>
|
|
|
</el-tooltip>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<el-input ref="textareaRef" v-model="inputMessage" type="textarea" :rows="3" placeholder="输入消息..."
|
|
|
- @keyup.enter="handleAsk" />
|
|
|
+ @keyup.enter="() => handleAsk()" />
|
|
|
<div class="chat_area_op">
|
|
|
<el-button
|
|
|
:style="`background-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'};border-color:${inputMessage.trim() ? '#4d6bfe' : '#d6dee8'}`"
|
|
|
- v-if="inputMessage.trim() || !sendLoading" type="primary" circle @click="handleAsk"
|
|
|
+ v-if="inputMessage.trim() || !sendLoading" type="primary" circle @click="() => handleAsk()"
|
|
|
:disabled="!inputMessage.trim() || sendLoading">
|
|
|
<svg-icon icon-class="send" color="#000000" />
|
|
|
</el-button>
|
|
@@ -139,7 +138,7 @@ import { getPageInfo, getXlsxValue, handleInput } from './utils/index.js'
|
|
|
import { useMsgStore } from '@/store/modules/msg.ts'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
import { putChat, uploadFile } from "@/api/index.js";
|
|
|
-import {askQues} from '@/api/modal.js'
|
|
|
+import {askQues,getFormKey} from '@/api/modal.js'
|
|
|
import {debounce} from 'lodash'
|
|
|
const userStore = useUserStore()
|
|
|
import { getChatDetail } from '@/api/index.js'
|
|
@@ -227,7 +226,6 @@ const handleScrollToTop = debounce(async function () {
|
|
|
* @param {string} type 发送的类型 document 、text
|
|
|
* **/
|
|
|
async function addMessage(msg, raw, type) {
|
|
|
- console.log(msg);
|
|
|
const newMessage = reactive({
|
|
|
id:+new Date(),
|
|
|
type: type || '',
|
|
@@ -238,7 +236,9 @@ async function addMessage(msg, raw, type) {
|
|
|
sortKey: moment().valueOf(),
|
|
|
role: 'user',
|
|
|
conversationId:msgUuid.value,
|
|
|
- addToHistory: `${!taklToHtml.value}`
|
|
|
+ addToHistory: `${!taklToHtml.value}`,
|
|
|
+ redisKey: undefined,
|
|
|
+ fileId:undefined
|
|
|
})
|
|
|
if (type === 'document') {
|
|
|
newMessage.content = msg
|
|
@@ -246,60 +246,25 @@ async function addMessage(msg, raw, type) {
|
|
|
}
|
|
|
if (!msg) return
|
|
|
messages.value.push(newMessage)
|
|
|
- await nextTick(() => {
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
+ console.log(scrollbar.value);
|
|
|
+ scrollbar.value?.scrollTo(Number.MAX_VALUE)
|
|
|
+ nextTick(() => {
|
|
|
+
|
|
|
})
|
|
|
return newMessage
|
|
|
}
|
|
|
|
|
|
const handleSummary = async () => {
|
|
|
- let params = []
|
|
|
- if (1) {
|
|
|
- pageInfoList.value.forEach((pageInfo) => {
|
|
|
- params.push({ role: 'system', content: `fileid://${pageInfo.fileId}` })
|
|
|
- })
|
|
|
- params.push({
|
|
|
- role: 'user', content: `
|
|
|
- 请根据这几个文件的文本内容综合分析总结
|
|
|
- 要求:
|
|
|
- 1.抽取文件的文本内容
|
|
|
- 2.对每个文件中的文本内容进行分别总结
|
|
|
- 3.对步骤二总结的内容进行综合总结`
|
|
|
- })
|
|
|
- } else {
|
|
|
- let tempStr = ''
|
|
|
- pageInfoList.value.forEach((pageInfo, index) => {
|
|
|
- pageInfo.handleText = getSummaryPrompt(pageInfo.content)
|
|
|
- tempStr += `第${index + 1}段:${pageInfo.handleText}\n`
|
|
|
- })
|
|
|
- params = [{
|
|
|
- role: 'user',
|
|
|
- content: `请根据以下这几个内容综合总结出结果:
|
|
|
- ${tempStr}要求:
|
|
|
- 1. 用简洁清晰的语言提取所有的核心要点
|
|
|
- 2. 保持客观中立的语气
|
|
|
- 3. 按重要性排序
|
|
|
- 4. 返回内容做好换行,以及展示样式
|
|
|
- 5. 请以"以下是对该文件内容的总结:"开头,然后用要点的形式列出主要内容。
|
|
|
- 6. 对这几个内容进行综合分析及联想`
|
|
|
- }]
|
|
|
- }
|
|
|
- const msg = await addMessage(JSON.stringify(pageInfoList.value), JSON.stringify(params.map(_ => _.content)), 'document')
|
|
|
- putChat(msg)
|
|
|
- if (requestFlowFn) {
|
|
|
- isShowPage.value = false
|
|
|
- taklToHtml.value = false
|
|
|
- pageInfoList.value = []
|
|
|
- const res = await requestFlowFn(params)
|
|
|
- }
|
|
|
+ if(sendLoading.value) return
|
|
|
+ handleAsk('请帮我总结当前文件')
|
|
|
}
|
|
|
|
|
|
async function handelIntelligentFillingClick() {
|
|
|
- isShowPage.value = true
|
|
|
- taklToHtml.value = true
|
|
|
- const tempPageInfo = await getPageInfo()
|
|
|
- pageInfo.value = tempPageInfo
|
|
|
- pageInfoList.value = [tempPageInfo]
|
|
|
+ // isShowPage.value = true
|
|
|
+ // taklToHtml.value = true
|
|
|
+ // const tempPageInfo = await getPageInfo()
|
|
|
+ // pageInfo.value = tempPageInfo
|
|
|
+ // pageInfoList.value = [tempPageInfo]
|
|
|
inputMessage.value = '/智能填表 '
|
|
|
type.value = FunctionList.Intelligent_Form_filling
|
|
|
}
|
|
@@ -330,7 +295,11 @@ async function handleCurrentData(e) {
|
|
|
msgUuid.value = e
|
|
|
chrome.storage.local.set({ msgUuid: msgUuid.value })
|
|
|
await msgStore.initMsg()
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+})
|
|
|
}
|
|
|
|
|
|
async function readClick() {
|
|
@@ -345,12 +314,10 @@ async function readClick() {
|
|
|
return
|
|
|
}
|
|
|
const tempPageInfo = await getPageInfo()
|
|
|
- if (AIModel.value.file === true) {
|
|
|
- const blob = new Blob([tempPageInfo.content.mainContent], { type: 'text/plain' })
|
|
|
- const file = new File([blob], tempPageInfo.title + '.txt', { type: 'text/plain' })
|
|
|
- tempPageInfo.fileId = await modelFileUpload(file)
|
|
|
- }
|
|
|
- pageInfoList.value.unshift(tempPageInfo)
|
|
|
+ const blob = new Blob([tempPageInfo.content.mainContent], { type: 'text/plain' })
|
|
|
+ const file = new File([blob], tempPageInfo.title + '.txt', { type: 'text/plain' })
|
|
|
+ await createFileObj(file)
|
|
|
+ // await handleUpload(file,tempPageInfo.title + '.txt')
|
|
|
}
|
|
|
|
|
|
function deletePageInfo(i) {
|
|
@@ -374,56 +341,105 @@ function addNewDialogue() {
|
|
|
page.value = 1
|
|
|
pageInfoList.value = []
|
|
|
msgUuid.value = uuidv4()
|
|
|
+ hasNext.value = false
|
|
|
chrome.storage.local.set({ msgUuid: msgUuid.value })
|
|
|
}
|
|
|
|
|
|
-async function handleAsk() {
|
|
|
- const str = inputMessage.value.trim()
|
|
|
+async function handleAsk(value) {
|
|
|
+ const str = value ?? inputMessage.value.trim()
|
|
|
+ if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
+ await addMessage(str)
|
|
|
+ const res = await fetchRes(str)
|
|
|
+ if (res.status === 'ok') {
|
|
|
+ formInfo.value = res.data
|
|
|
+ console.log(res, 55558)
|
|
|
+ } else {
|
|
|
+ type.value = FunctionList.File_Operation
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
inputMessage.value = ''
|
|
|
if (sendLoading.value) return
|
|
|
- const msg = await addMessage(str)
|
|
|
+ let msg = null
|
|
|
+ if (pageInfoList.value.length) {
|
|
|
+ msg = await addMessage(JSON.stringify([...pageInfoList.value, { type: 'text', value: str }]), str, 'document')
|
|
|
+ msg.fileId = pageInfoList.value[0].fileId
|
|
|
+ msg.redisKey = pageInfoList.value[0].redisKey
|
|
|
+ } else msg = await addMessage(str)
|
|
|
await putChat(msg)
|
|
|
- const obj = reactive({
|
|
|
- type: type || '',
|
|
|
- rawContent:'',
|
|
|
- senderId: -1,
|
|
|
- receiverId: userStore.userInfo.id, //大模型
|
|
|
- content: '',
|
|
|
- sortKey: moment().valueOf(),
|
|
|
- role: 'system',
|
|
|
- conversationId: msgUuid.value,
|
|
|
- })
|
|
|
sendLoading.value = true
|
|
|
-
|
|
|
+ // messages.value.push(obj)
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+})
|
|
|
+ const obj = reactive({
|
|
|
+ type: type || '',
|
|
|
+ rawContent: '',
|
|
|
+ senderId: -1,
|
|
|
+ receiverId: userStore.userInfo.id, //大模型
|
|
|
+ content: '',
|
|
|
+ sortKey: moment().valueOf(),
|
|
|
+ role: 'system',
|
|
|
+ conversationId: msgUuid.value,
|
|
|
+ })
|
|
|
messages.value.push(obj)
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
const res = await askQues({
|
|
|
conversationId: msgUuid.value,
|
|
|
modelName: '通义千问-Max',
|
|
|
question: msg.rawContent,
|
|
|
- id:'699637194561691650'
|
|
|
+ id: '699637194561691650',
|
|
|
+ redisKey:msg.redisKey
|
|
|
})
|
|
|
const result = res.data[res.data.length - 1].content
|
|
|
- obj.content = formatMessage(result)
|
|
|
+ isShowPage.value = false
|
|
|
+ pageInfoList.value = []
|
|
|
+ // 保存原始内容
|
|
|
obj.rawContent = result
|
|
|
- putChat({
|
|
|
- ...obj,
|
|
|
- content:''
|
|
|
- })
|
|
|
- sendLoading.value = false
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
- // if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
- // const res = await fetchRes(str)
|
|
|
- // if (res.status === 'ok') {
|
|
|
- // formInfo.value = res.data
|
|
|
- // console.log(res, 55558)
|
|
|
- // } else {
|
|
|
- // type.value = FunctionList.File_Operation
|
|
|
- // isShowPage.value = false
|
|
|
- // taklToHtml.value = false
|
|
|
- // pageInfoList.value = []
|
|
|
- // }
|
|
|
- // } else streamRes(taklToHtml.value)
|
|
|
+
|
|
|
+ // 实现打字机效果
|
|
|
+ const formattedText = formatMessage(result)
|
|
|
+ const typewriterEffect = async (text) => {
|
|
|
+ let currentIndex = 0
|
|
|
+ const totalLength = text.length
|
|
|
+ const typingSpeed = 10 // 打字速度,可以调整
|
|
|
+
|
|
|
+ // 清除之前的内容
|
|
|
+ obj.content = ''
|
|
|
+
|
|
|
+ // 逐字添加内容
|
|
|
+ const typing = setInterval(() => {
|
|
|
+ if (currentIndex < totalLength) {
|
|
|
+ obj.content += text[currentIndex]
|
|
|
+ currentIndex++
|
|
|
+ // 滚动到底部,保持视图跟随
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+})
|
|
|
+ } else {
|
|
|
+ clearInterval(typing)
|
|
|
+ sendLoading.value = false
|
|
|
+ // 打字完成后再次滚动到底部
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+})
|
|
|
+ }
|
|
|
+ }, typingSpeed)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始打字机效果
|
|
|
+ await typewriterEffect(formattedText)
|
|
|
+
|
|
|
+ // 保存到数据库
|
|
|
+ putChat({
|
|
|
+ ...obj,
|
|
|
+ content: '' // 保存完整内容
|
|
|
+ })
|
|
|
}
|
|
|
function handleCapture() {
|
|
|
ElMessage({
|
|
@@ -448,23 +464,50 @@ const handleUpload = async (formData,name) => {
|
|
|
isShowPage.value = true
|
|
|
const obj = reactive({
|
|
|
title: name,
|
|
|
- url: '上传中...',
|
|
|
+ state: '上传中...',
|
|
|
favIconUrl: fileLogo,
|
|
|
loading: true,
|
|
|
+ url:'',
|
|
|
+ fileId: '',
|
|
|
+ redisKey:'',
|
|
|
content: {
|
|
|
mainContent: ''
|
|
|
}
|
|
|
})
|
|
|
- pageInfoList.value.push(obj)
|
|
|
- const res = await uploadFile(formData)
|
|
|
+ pageInfoList.value = [obj]
|
|
|
+ try {
|
|
|
+ const res = await uploadFile(formData)
|
|
|
if (type.value === FunctionList.Intelligent_Form_filling) {
|
|
|
- obj.url = '解析中...'
|
|
|
-
|
|
|
+ obj.state = '解析中...'
|
|
|
+ obj.redisKey = res.data.redisKey
|
|
|
+ obj.id = res.data.file.id
|
|
|
+ obj.url = res.data.file.url
|
|
|
+ console.log({
|
|
|
+ body: formInfo.value,
|
|
|
+ uploadFile:res.data.data
|
|
|
+ },888569);
|
|
|
+
|
|
|
+ const result = await getFormKey({
|
|
|
+ body: formInfo.value,
|
|
|
+ uploadFile:res.data.data
|
|
|
+ })
|
|
|
+ console.log(result);
|
|
|
+
|
|
|
}
|
|
|
if (type.value === FunctionList.File_Operation) {
|
|
|
obj.loading = false
|
|
|
obj.url = res.data.url
|
|
|
-
|
|
|
+ obj.redisKey = res.data.redisKey
|
|
|
+ obj.fileId = res.data.file.id
|
|
|
+ obj.url = res.data.file.url
|
|
|
+ }
|
|
|
+ obj.loading = false
|
|
|
+ } catch (error) {
|
|
|
+ console.log(error);
|
|
|
+
|
|
|
+ ElMessage.error('上传出错')
|
|
|
+ pageInfoList.value = []
|
|
|
+ isShowPage.value = false
|
|
|
}
|
|
|
}
|
|
|
async function getFileValue(file) {
|
|
@@ -492,14 +535,17 @@ onMounted(async () => {
|
|
|
pageInfo.value = message.data
|
|
|
}
|
|
|
})
|
|
|
- nextTick(() => {
|
|
|
- scrollbar.value?.setScrollTop(99999)
|
|
|
- })
|
|
|
+ nextTick(() => {
|
|
|
+ if (scrollbar.value && scrollbar.value.wrapRef) {
|
|
|
+ scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
|
|
|
+ }
|
|
|
+})
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@use '@/entrypoints/sidepanel/css/chat.scss';
|
|
|
+@use '@/entrypoints/sidepanel/css/markdown.scss';
|
|
|
|
|
|
.loading-more-indicator {
|
|
|
display: flex;
|