|
@@ -16,7 +16,13 @@
|
|
|
<span class="dot"></span>
|
|
|
</span>
|
|
|
</div>
|
|
|
- <div v-else class="content">{{ message.content }}</div>
|
|
|
+ <div v-else class="content">{{ message.content }}
|
|
|
+ <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
|
|
|
+ <span class="dot"></span>
|
|
|
+ <span class="dot"></span>
|
|
|
+ <span class="dot"></span>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
<div class="timestamp ">{{ message.timestamp }}
|
|
|
<span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
|
|
|
</div>
|
|
@@ -48,7 +54,7 @@
|
|
|
</div>
|
|
|
<div class="card-btn">
|
|
|
<el-tooltip content="总结当前页面" placement="top">
|
|
|
- <el-button round @click="handleCardButtonClick" :disabled="!taklToHtml">总结</el-button>
|
|
|
+ <el-button round @click="handleSummary" :disabled="!taklToHtml">总结</el-button>
|
|
|
</el-tooltip>
|
|
|
|
|
|
<el-tooltip content="选择后,在输入框描述填表流程" placement="top">
|
|
@@ -80,15 +86,15 @@
|
|
|
import {ref, onMounted, nextTick, inject, useTemplateRef} from 'vue'
|
|
|
import {ElScrollbar, ElAvatar, ElInput, ElButton} from 'element-plus'
|
|
|
import moment from "moment";
|
|
|
-import { buildExcelUnderstandingPrompt ,getFileSummaryPrompt} from '@/utils/ai-service.js'
|
|
|
+import { buildExcelUnderstandingPrompt ,getFileSummaryPrompt,getSummaryPrompt,getFileContent,buildObjPrompt } from '@/utils/ai-service.js'
|
|
|
import * as XLSX from "xlsx";
|
|
|
import {ElMessage} from 'element-plus';
|
|
|
import {useMsg} from '@/entrypoints/sidepanel/hook/useMsg.ts';
|
|
|
import Tools from "@/entrypoints/sidepanel/component/tools.vue";
|
|
|
import historyComponent from '@/entrypoints/sidepanel/component/historyComponent.vue';
|
|
|
-import {useSummary} from '@/entrypoints/sidepanel/hook/useSummary.ts'
|
|
|
import {mockData, startMsg, mockData2} from "@/entrypoints/sidepanel/mock"
|
|
|
-import {useAutoResizeTextarea} from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts';
|
|
|
+import { useAutoResizeTextarea } from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts';
|
|
|
+import {getPageInfo} from './utils/index.js'
|
|
|
|
|
|
// 滚动条引用
|
|
|
const scrollbar = ref(null);
|
|
@@ -101,21 +107,41 @@ const {registerStore, useStore} = inject('indexedDBHook');
|
|
|
const {
|
|
|
msgUuid,
|
|
|
messages,
|
|
|
- inputMessage,
|
|
|
- indexTemp,
|
|
|
taklToHtml,
|
|
|
- pageInfo,
|
|
|
sendLoading,
|
|
|
- type,
|
|
|
- addMessage,
|
|
|
- sendRequese,
|
|
|
- getPageInfo,
|
|
|
streamRes,
|
|
|
handleInput,
|
|
|
- getFileValue
|
|
|
-} = useMsg(scrollbar, xlsxData, fetchDataAndProcess);
|
|
|
-const {handleCardButtonClick} = useSummary(addMessage, sendRequese);
|
|
|
+ getFormKeyAndValue
|
|
|
+} = useMsg(scrollbar, xlsxData);
|
|
|
+const inputMessage = ref('')
|
|
|
+const pageInfo = ref('')
|
|
|
+const addMessage = (msg, raw) => {
|
|
|
+ if (!msg) return
|
|
|
+ const newMessage = reactive({
|
|
|
+ id: messages.value.length + 1,
|
|
|
+ username: '我',
|
|
|
+ rawContent:raw ?? msg,
|
|
|
+ content: msg,
|
|
|
+ timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
|
|
|
+ isSelf: true,
|
|
|
+ avatar: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
|
|
|
+ addToHistory: !taklToHtml.value
|
|
|
+ })
|
|
|
+ messages.value.push(newMessage)
|
|
|
+ useStore(msgUuid.value).add(newMessage)
|
|
|
+ // 滚动到底部
|
|
|
+ nextTick(() => {
|
|
|
+ scrollbar.value?.setScrollTop(99999);
|
|
|
+ })
|
|
|
+ return newMessage
|
|
|
|
|
|
+ }
|
|
|
+const handleSummary = async () => {
|
|
|
+ const res = await getPageInfo()
|
|
|
+ addMessage('总结页面',getSummaryPrompt(res.content))
|
|
|
+ streamRes()
|
|
|
+}
|
|
|
+const type = ref('')
|
|
|
function handelIntelligentFillingClick() {
|
|
|
inputMessage.value = '/智能填表 '
|
|
|
type.value = '2'
|
|
@@ -156,8 +182,9 @@ function addNewDialogue() {
|
|
|
|
|
|
async function handleAsk() {
|
|
|
if (sendLoading.value) return;
|
|
|
- addMessage(inputMessage.value.trim(), true);
|
|
|
+ addMessage(inputMessage.value.trim());
|
|
|
inputMessage.value = '';
|
|
|
+ streamRes(taklToHtml)
|
|
|
}
|
|
|
|
|
|
function handleCapture() {
|
|
@@ -177,9 +204,6 @@ const titleScroll = computed(() => {
|
|
|
return pageInfo.value?.title?.length > 20 // 当标题超过20个字符时触发滚动
|
|
|
})
|
|
|
|
|
|
-// let formMap = []
|
|
|
-let formInfo = []
|
|
|
-const flag = ref(false) //true调用算法接口
|
|
|
|
|
|
const handleUpload =async (file) => {
|
|
|
if (type.value === '2') {
|
|
@@ -189,13 +213,12 @@ const handleUpload =async (file) => {
|
|
|
if (chrome.runtime.lastError) {
|
|
|
console.error("消息发送错误:", chrome.runtime.lastError);
|
|
|
} else {
|
|
|
- addMessage(`已上传文件:${file.name}`, false)
|
|
|
const fileExtension = file.name.split('.').pop().toLowerCase();
|
|
|
if (response.status === 'error') return ElMessage({message:response.message,type: 'error', duration: 4 * 1000, grouping: true})
|
|
|
formInfo = response.data
|
|
|
if (fileExtension === 'xlsx') {
|
|
|
const reader = new FileReader();
|
|
|
- reader.readAsArrayBuffer(file);
|
|
|
+ reader.readAsArrayBuffer(file)
|
|
|
reader.onload = async (e) => {
|
|
|
const data = new Uint8Array(e.target.result);
|
|
|
const workbook = XLSX.read(data, {
|
|
@@ -222,105 +245,41 @@ const handleUpload =async (file) => {
|
|
|
// if (!xlsxData.value[header]) xlsxData.value[header] = []
|
|
|
xlsxData.value[header] = readData[1][i]
|
|
|
})
|
|
|
- if (type.value === '2') {
|
|
|
- await streamRes(buildExcelUnderstandingPrompt(readData, file?.name, response.data), false)
|
|
|
- }
|
|
|
+ addMessage(`已上传文件:${file.name}`,buildExcelUnderstandingPrompt(readData, file?.name, response.data))
|
|
|
+ await streamRes()
|
|
|
};
|
|
|
} else {
|
|
|
- await getFileValue(file, response.data)
|
|
|
-
|
|
|
- // const keys = Object.keys(res)
|
|
|
- // const values = Object.values(res)
|
|
|
- // readData[0].forEach((header, i) => {
|
|
|
- // if (!xlsxData.value[header]) xlsxData.value[header] = []
|
|
|
- // xlsxData.value[header].push(readData[1][i])
|
|
|
- // })
|
|
|
- // console.log(res);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
+ const msg = addMessage(`文件上传中`,)
|
|
|
+ sendLoading.value = true
|
|
|
+ let formData = new FormData();
|
|
|
+ formData.append("file", file);
|
|
|
+ const res = await getFileContent(formData)
|
|
|
+ sendLoading.value = false
|
|
|
+ msg.content = `已上传文件:${file.name}`
|
|
|
+ msg.rawContent = getFileSummaryPrompt(res.data, file.name)
|
|
|
+ const res2 = await getFormKeyAndValue(res.data,response.data)
|
|
|
+ xlsxData.value = res2.data
|
|
|
+ console.log(xlsxData.value);
|
|
|
+ console.log(type.value);
|
|
|
+ }
|
|
|
+ }
|
|
|
return true
|
|
|
});
|
|
|
-
|
|
|
}
|
|
|
if (type.value === '') {
|
|
|
- addMessage(`已上传文件:${file.name}`, false)
|
|
|
- await getFileValue(file, )
|
|
|
-
|
|
|
+ const msg = addMessage(`文件上传中`,)
|
|
|
+ sendLoading.value = true
|
|
|
+ let formData = new FormData();
|
|
|
+ formData.append("file", file);
|
|
|
+ const res = await getFileContent(formData)
|
|
|
+ sendLoading.value = false
|
|
|
+ msg.content = `已上传文件:${file.name}`
|
|
|
+ msg.rawContent = getFileSummaryPrompt(res.data, file.name)
|
|
|
+ streamRes()
|
|
|
}
|
|
|
}
|
|
|
let str = ''
|
|
|
-async function fetchDataAndProcess(input, obj) {
|
|
|
- if (input.startsWith('/智能填表')) {
|
|
|
- input = input.split('/智能填表')[1]
|
|
|
- }
|
|
|
- str = input
|
|
|
- console.log(str);
|
|
|
- const pageInfo = await getPageInfo();
|
|
|
- await new Promise(res => setTimeout(() => {
|
|
|
- res()
|
|
|
- }, 2000))
|
|
|
- // const res = await hepl({
|
|
|
- // input_data: input,
|
|
|
- // body: pageInfo.content.mainContent
|
|
|
- // })
|
|
|
- console.log(pageInfo.title);
|
|
|
|
|
|
- const res = await new Promise((resolve, reject) => {
|
|
|
- setTimeout(() => {
|
|
|
- resolve({ data:pageInfo.title === '智能招采' ? mockData[indexTemp.value] :mockData2[indexTemp.value] })
|
|
|
- }, 1000)
|
|
|
- })
|
|
|
- if (!res.data.tag || res.data.tag === 'undefined') {
|
|
|
- ElMessage({ message: '未找到标签,请重试', type: 'error', duration: 4 * 1000, grouping: true })
|
|
|
- obj.content = '未找到标签,请重试'
|
|
|
- type.value = ''
|
|
|
- return
|
|
|
- }
|
|
|
- await handleClick(res.data, obj);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-async function handleClick(res, msgObj) {
|
|
|
- await new Promise(resolve => setTimeout(resolve, 2000))
|
|
|
- msgObj.content = `点击${res.tag}元素`
|
|
|
- chrome.runtime.sendMessage({
|
|
|
- type: 'FROM_SIDE_PANEL_TO_ACTION',
|
|
|
- data: res
|
|
|
- }, async ({ data, status }) => {
|
|
|
- if (chrome.runtime.lastError) {
|
|
|
- console.error("消息发送错误:", chrome.runtime.lastError);
|
|
|
- rej(chrome.runtime.lastError)
|
|
|
- } else {
|
|
|
- if (status === 'error') {
|
|
|
- msgObj.content = data
|
|
|
- type.value = ''
|
|
|
- return
|
|
|
- }
|
|
|
- if (res.next === '是') {
|
|
|
- const arr = str.split(',')
|
|
|
- arr.shift()
|
|
|
- str = arr.join(',')
|
|
|
- indexTemp.value++
|
|
|
- fetchDataAndProcess(str, msgObj)
|
|
|
- } else {
|
|
|
- if (type.value === '2') {
|
|
|
- await new Promise((resolve, reject) => {
|
|
|
- setTimeout(() => {
|
|
|
- resolve()
|
|
|
- }, 2000)
|
|
|
- })
|
|
|
- msgObj.content = `请上传数据`
|
|
|
- ElMessage({ message: '请上传数据', type: 'success', duration: 4 * 1000, grouping: true })
|
|
|
- } else {
|
|
|
- msgObj.content = `执行完毕`
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return true
|
|
|
- });
|
|
|
-
|
|
|
-}
|
|
|
|
|
|
const isHoveringTitle = ref(false)
|
|
|
|
|
@@ -330,15 +289,12 @@ onMounted(() => {
|
|
|
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
|
if (message.type === 'TO_SIDE_PANEL_PAGE_INFO') {
|
|
|
pageInfo.value = message.data
|
|
|
- console.log(pageInfo.value);
|
|
|
-
|
|
|
// 转发到 content.js
|
|
|
chrome.tabs.sendMessage(sender.tab.id, {
|
|
|
type: 'TO_CONTENT_SCRIPT',
|
|
|
data: message.data
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
if (message.type === 'TO_SIDE_PANEL_PAGE_CHANGE') {
|
|
|
pageInfo.value = message.data
|
|
|
}
|
|
@@ -374,286 +330,5 @@ onMounted(() => {
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.chat-container {
|
|
|
- height: 100vh;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- border: 1px solid #dcdfe6;
|
|
|
- border-radius: 4px;
|
|
|
- background-color: #F0F0F0;
|
|
|
- font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
|
|
-}
|
|
|
-
|
|
|
-.message-list {
|
|
|
- flex: 1;
|
|
|
- padding: 16px;
|
|
|
- overflow: auto;
|
|
|
-}
|
|
|
-
|
|
|
-.messages {
|
|
|
- min-height: 100%;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
-}
|
|
|
-
|
|
|
-.message-item {
|
|
|
- display: flex;
|
|
|
- margin-bottom: 20px;
|
|
|
- gap: 12px;
|
|
|
- color: #333;
|
|
|
- align-items: flex-start;
|
|
|
- animation: fadeIn 0.3s ease-in-out;
|
|
|
-}
|
|
|
-
|
|
|
-@keyframes fadeIn {
|
|
|
- from {
|
|
|
- opacity: 0;
|
|
|
- transform: translateY(10px);
|
|
|
- }
|
|
|
-
|
|
|
- to {
|
|
|
- opacity: 1;
|
|
|
- transform: translateY(0);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.message-item.self {
|
|
|
- flex-direction: row-reverse;
|
|
|
-}
|
|
|
-
|
|
|
-.message-content {
|
|
|
- max-width: 80%;
|
|
|
-}
|
|
|
-
|
|
|
-.username {
|
|
|
- font-size: 14px;
|
|
|
- color: #606266;
|
|
|
- margin-bottom: 4px;
|
|
|
-}
|
|
|
-
|
|
|
-.content {
|
|
|
- padding: 12px;
|
|
|
- min-height: 40px;
|
|
|
- background-color: #F0F4F8;
|
|
|
- border-radius: 12px;
|
|
|
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
|
- word-break: break-all;
|
|
|
- line-height: 1.5;
|
|
|
- font-size: 14px;
|
|
|
-}
|
|
|
-
|
|
|
-.content :deep(pre) {
|
|
|
- margin: 10px 0;
|
|
|
- border-radius: 8px;
|
|
|
-}
|
|
|
-
|
|
|
-.content :deep(code) {
|
|
|
- font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
|
|
|
-}
|
|
|
-
|
|
|
-.content :deep(p) {
|
|
|
- margin: 8px 0;
|
|
|
-}
|
|
|
-
|
|
|
-.content :deep(ul),
|
|
|
-.content :deep(ol) {
|
|
|
- padding-left: 20px;
|
|
|
- margin: 8px 0;
|
|
|
-}
|
|
|
-
|
|
|
-.content :deep(blockquote) {
|
|
|
- border-left: 4px solid #ddd;
|
|
|
- padding-left: 10px;
|
|
|
- color: #666;
|
|
|
- margin: 8px 0;
|
|
|
-}
|
|
|
-
|
|
|
-.self .content {
|
|
|
- background: #409eff;
|
|
|
- color: white;
|
|
|
- border-bottom-right-radius: 4px;
|
|
|
-}
|
|
|
-
|
|
|
-.other .content {
|
|
|
- border-bottom-left-radius: 4px;
|
|
|
-}
|
|
|
-
|
|
|
-.timestamp {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- margin-top: 6px;
|
|
|
-}
|
|
|
-
|
|
|
-.timestamp span {
|
|
|
- transition: color 0.2s;
|
|
|
-}
|
|
|
-
|
|
|
-.timestamp span:hover {
|
|
|
- color: #409eff;
|
|
|
-}
|
|
|
-
|
|
|
-.info-card {
|
|
|
- margin: 10px;
|
|
|
-}
|
|
|
-
|
|
|
-/* .card-content:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
|
|
-} */
|
|
|
-
|
|
|
-.card-icon {
|
|
|
- color: #409eff;
|
|
|
-}
|
|
|
-
|
|
|
-.title-wrapper {
|
|
|
- flex: 1;
|
|
|
- overflow: hidden;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.title-scroller {
|
|
|
- display: inline-block;
|
|
|
- white-space: nowrap;
|
|
|
- color: #333;
|
|
|
- font-weight: 500;
|
|
|
-}
|
|
|
-
|
|
|
-.title-scroller.scroll {
|
|
|
- animation: scrollTitle 10s linear infinite;
|
|
|
-}
|
|
|
-
|
|
|
-@keyframes scrollTitle {
|
|
|
- 0% {
|
|
|
- transform: translateX(0);
|
|
|
- }
|
|
|
-
|
|
|
- 100% {
|
|
|
- transform: translateX(-100%);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.upload :deep(.el-icon) {
|
|
|
- transition: color 0.3s;
|
|
|
-}
|
|
|
-
|
|
|
-.can-hover :deep(.el-icon:hover) {
|
|
|
- color: #409eff !important;
|
|
|
-}
|
|
|
-
|
|
|
-.el-check-tag {
|
|
|
- margin-right: 8px;
|
|
|
- transition: all 0.3s;
|
|
|
-}
|
|
|
-
|
|
|
-.el-check-tag:hover {
|
|
|
- transform: scale(1.05);
|
|
|
-}
|
|
|
-
|
|
|
-/* 加载中的消息样式 */
|
|
|
-.loading-content {
|
|
|
- min-height: 40px;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: flex-start;
|
|
|
-}
|
|
|
-
|
|
|
-.loading-indicator {
|
|
|
- display: inline-block;
|
|
|
- align-items: center;
|
|
|
- gap: 4px;
|
|
|
- margin-left: 2px;
|
|
|
-
|
|
|
- .dot {
|
|
|
- width: 4px;
|
|
|
- height: 4px;
|
|
|
- margin: 0 2px;
|
|
|
- background-color: gray;
|
|
|
- border-radius: 50%;
|
|
|
- display: inline-block;
|
|
|
- animation: pulse 1.5s infinite ease-in-out;
|
|
|
- }
|
|
|
-
|
|
|
- .dot:nth-child(2) {
|
|
|
- animation-delay: 0.3s;
|
|
|
- }
|
|
|
-
|
|
|
- .dot:nth-child(3) {
|
|
|
- animation-delay: 0.6s;
|
|
|
- }
|
|
|
-
|
|
|
- @keyframes pulse {
|
|
|
-
|
|
|
- 0%,
|
|
|
- 100% {
|
|
|
- transform: scale(0.8);
|
|
|
- opacity: 0.6;
|
|
|
- }
|
|
|
-
|
|
|
- 50% {
|
|
|
- transform: scale(1.2);
|
|
|
- opacity: 1;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-.input-area {
|
|
|
- padding: 8px 10px;
|
|
|
- color: black;
|
|
|
- background-color: #fff;
|
|
|
- border: 1px solid rgba(102, 102, 102, 0.3);
|
|
|
- border-radius: 16px;
|
|
|
- margin: 0 12px 12px;
|
|
|
-
|
|
|
- .card-content {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 12px;
|
|
|
- padding: 14px 12px;
|
|
|
- margin: 0 0 6px;
|
|
|
- background: #fff;
|
|
|
- border-radius: 10px;
|
|
|
- border: 1px solid rgba(0, 0, 0, 0.08);
|
|
|
- font-size: 14px;
|
|
|
- /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);*/
|
|
|
- transition: transform 0.4s;
|
|
|
- }
|
|
|
-
|
|
|
- .card-btn {
|
|
|
- padding: 0 0 4px;
|
|
|
-
|
|
|
- .op {
|
|
|
- margin-right: 3px;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .chat_area_op {
|
|
|
- margin-top: 3px;
|
|
|
- display: flex;
|
|
|
- justify-content: end;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.input-area:hover {
|
|
|
- border-color: rgba(102, 102, 102, 0.4);
|
|
|
-}
|
|
|
-
|
|
|
-.input-area :deep(.el-textarea__inner) {
|
|
|
- border-radius: 8px;
|
|
|
- transition: border-color 0.3s;
|
|
|
- resize: none;
|
|
|
- box-shadow: none;
|
|
|
-}
|
|
|
-
|
|
|
-.input-area :deep(.el-textarea__inner) {
|
|
|
- padding: 0 4px;
|
|
|
- margin-top: 3px;
|
|
|
-}
|
|
|
-
|
|
|
-.input-area :deep(.el-textarea__inner:focus) {
|
|
|
- border-color: #409eff;
|
|
|
- box-shadow: none;
|
|
|
-}
|
|
|
+@import '@/entrypoints/sidepanel/css/chat.scss';
|
|
|
</style>
|