-
-
-
-
-
+
+
\ No newline at end of file
+
diff --git a/src/main.js b/src/main.js
index 8b1e32b..d039d3e 100644
--- a/src/main.js
+++ b/src/main.js
@@ -19,7 +19,7 @@ function platCheck() {
const userAgent = navigator.userAgent.toLowerCase();
const mobileKeywords = ['iphone', 'ipod', 'android', 'silk', 'blackberry', 'bb10', 'phone', 'mobile', 'kindle', 'opera mini', 'mobile safari', 'windows phone'];
const isMobileDevice = mobileKeywords.some(keyword => userAgent.includes(keyword));
- if (!isMobileDevice) {
+ if (!isMobileDevice && import.meta.env.PROD) {
let _href = window.location.href;
let _origin = window.location.origin;
window.location.href = _href.replace(_origin, import.meta.env.VITE_PC_HOST);
diff --git a/src/router/index.js b/src/router/index.js
index 915292a..fe878d0 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -16,6 +16,7 @@ const router = createRouter({
meta: {
title: "首页", // 页面标题
group: 'home', // 导航归属
+ footer: false,
},
component: () => import("@/views/home/index.vue"),
},
@@ -33,7 +34,7 @@ const router = createRouter({
component: () => import("@/views/chat/index.vue"),
meta: {
title: 'AI助理',
- footer: false,
+
}
}]
},
diff --git a/src/stores/chat.js b/src/stores/chat.js
index 6d48873..a1b63bf 100644
--- a/src/stores/chat.js
+++ b/src/stores/chat.js
@@ -100,12 +100,21 @@ export const useChat = defineStore("chat-store", {
requestBaseUrl: 'chat',
}).then((res) => {
const { messages } = res;
+ const chat = this.chat.find(item => item.id === uuid)
+ const localMessages = chat?.messages ?? []
const temp = messages.map((item) => {
var key = Object.keys(item)[0];
+
+
+ const localItem = (localMessages.find(item => Object.keys(item)[0] == key) ?? {})[key] ?? {}
+
+
const { role } = item[key]
- const obj = Object.assign({}, { ...item[key] }, { loading: false, inversion: role == 'user' })
+ const obj = Object.assign({}, localItem, { ...item[key] }, {
+ loading: false, inversion: role == 'user'
+ })
return {
- [key]: obj
+ [key]: obj,
};
});
this.setChat(uuid, { ...res, messages: temp })
diff --git a/src/utils/docxUtils.js b/src/utils/docxUtils.js
new file mode 100644
index 0000000..7e5376d
--- /dev/null
+++ b/src/utils/docxUtils.js
@@ -0,0 +1,10 @@
+import mammoth from 'mammoth';
+import { readFileAsArrayBuffer } from './fileUtils'
+
+export default async function extractDocxText(file) {
+ const arrayBuffer = await readFileAsArrayBuffer(file)
+ const options = {}
+ const result = await mammoth.extractRawText({ arrayBuffer }, options)
+ return result.value
+}
+
diff --git a/src/utils/fileUtils.js b/src/utils/fileUtils.js
new file mode 100644
index 0000000..9795b36
--- /dev/null
+++ b/src/utils/fileUtils.js
@@ -0,0 +1,28 @@
+export const readFileAsArrayBuffer = (file) => {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader()
+ reader.onload = () => {
+ resolve(reader.result)
+ }
+ reader.onerror = () => {
+ reject(new Error('Error reading the file.'))
+ }
+ reader.readAsArrayBuffer(file)
+ })
+}
+
+export function getFileInfo(file) {
+ const fileName = file.name;
+ const fileExtension = getFileExtension(fileName);
+ const fileSize = file.size;
+
+ return {
+ name: fileName,
+ extension: fileExtension,
+ size: fileSize
+ };
+}
+export function getFileExtension(fileName) {
+ const parts = fileName.split('.');
+ return parts[parts.length - 1].toLowerCase();
+}
\ No newline at end of file
diff --git a/src/utils/pdfUtils.js b/src/utils/pdfUtils.js
new file mode 100644
index 0000000..25a5f6d
--- /dev/null
+++ b/src/utils/pdfUtils.js
@@ -0,0 +1,28 @@
+import * as pdfjsLib from 'pdfjs-dist'
+import * as pdfWorkerMin from 'pdfjs-dist/build/pdf.worker.min?url'
+
+import { readFileAsArrayBuffer } from './fileUtils'
+
+pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerMin.default
+
+
+export default async function extractTextFromPDF(file) {
+
+ const arrayBuffer = await readFileAsArrayBuffer(file)
+
+ const loadingTask = pdfjsLib.getDocument(arrayBuffer);
+ const pdfDocument = await loadingTask.promise;
+ const numPages = pdfDocument.numPages;
+
+ let text = '';
+
+ for (let pageNum = 1; pageNum <= numPages; pageNum++) {
+ const page = await pdfDocument.getPage(pageNum);
+ const pageText = await page.getTextContent();
+
+ pageText.items.forEach(item => {
+ text += item.str + ' ';
+ });
+ }
+ return text;
+}
diff --git a/src/views/assistant/index.vue b/src/views/assistant/index.vue
index 30b557c..abb5f27 100644
--- a/src/views/assistant/index.vue
+++ b/src/views/assistant/index.vue
@@ -1,16 +1,19 @@
-
-
- CHATGPT接口AI助手
-
-
- 在AI助理板块,用户可以上传文件给AI翻阅并帮助您解决问题,您可以自己输入关键字或者使用自带的prompt模板
-
-
-
-

+
+
+
CHATGPT接口AI助手
+
+ 在AI助理板块,用户可以上传文件给AI翻阅并帮助您解决问题,您可以自己输入关键字或者使用自带的prompt模板
+
+
+
+

+
diff --git a/src/views/business/index.vue b/src/views/business/index.vue
index b1504c5..c8e36a0 100644
--- a/src/views/business/index.vue
+++ b/src/views/business/index.vue
@@ -1,21 +1,26 @@
-
-
-
-
-
-
-
-
AI商情预测
-
- 在AI助理板块,用户可以上传文件给AI翻阅并帮助您解决问题,您可以自己输入关键字或者使用自带的prompt模板在AI助理板块,用户可以上传文件给AI翻阅并帮助您解决问题,您可以自己输入关键字或者使用自带的prompt模
-
-
-
-

-
+
+
+
+
+
+
+
+
+
AI商情预测
+
+ 在AI助理板块,用户可以上传文件给AI翻阅并帮助您解决问题,您可以自己输入关键字或者使用自带的prompt模板在AI助理板块,用户可以上传文件给AI翻阅并帮助您解决问题,您可以自己输入关键字或者使用自带的prompt模
-
+
+
+

+
+
+
+
diff --git a/src/views/chat/components/file.vue b/src/views/chat/components/file.vue
new file mode 100644
index 0000000..527836b
--- /dev/null
+++ b/src/views/chat/components/file.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
.{{ file.extension }}
+
{{ kbToMbWithDecimal(file.size) }} M
+
+
+
+
+
+
+
+
diff --git a/src/views/chat/components/message-group.vue b/src/views/chat/components/message-group.vue
index 70eda1e..af99f32 100644
--- a/src/views/chat/components/message-group.vue
+++ b/src/views/chat/components/message-group.vue
@@ -1,6 +1,8 @@
@@ -32,8 +34,17 @@
diff --git a/src/views/chat/index.vue b/src/views/chat/index.vue
index bb8e028..9bf76fe 100644
--- a/src/views/chat/index.vue
+++ b/src/views/chat/index.vue
@@ -53,20 +53,22 @@
清除
-
-
-
-
- 上传
+
+
+
+
+
+ 上传
+
@@ -94,6 +96,20 @@ import http from '@/io/request'
import { v4 as uuidv4 } from 'uuid'
import { useRoute } from 'vue-router'
import { useChat } from '@/stores'
+import UploadComp from './components/upload-comp.vue'
+import extractTextFromPDF from '@/utils/pdfUtils.js'
+import extractDocxText from '@/utils/docxUtils.js'
+import { getFileInfo } from '@/utils/fileUtils.js'
+import { showToast } from 'vant'
+import { useAuthModal } from '@/stores/authModal'
+import { useUserInfo } from '@/stores/userInfo'
+const userInfo = useUserInfo()
+
+const isLogin = computed(() => {
+ return !!userInfo.userData?.id ?? false
+})
+
+const authModal = useAuthModal()
const { proxy } = getCurrentInstance()
@@ -113,8 +129,67 @@ const loading = ref(false)
const inputValue = ref('')
+const fileLoading = ref(false)
+
const currentChart = computed(() => chatStore.getCurrentChat)
+async function handleFileChange(file) {
+ if (!isLogin.value) {
+ authModal.setAuthModalType('login')
+ authModal.showAuthModal()
+ return
+ }
+
+ if (!userInfo.userData?.equity?.pdf) return showToast('上传文件权限未开通')
+
+ if (fileLoading.value) return
+ fileLoading.value = true
+ try {
+ const valid = validateFile(file)
+ if (!valid) return
+ const info = getFileInfo(file)
+ let result = ''
+ if (info.extension === 'docx' || info.extension === 'doc') {
+ result = await extractDocxText(file)
+ } else if (info.extension === 'pdf') {
+ result = await extractTextFromPDF(file)
+ }
+ if (!result || result.trim() == '') return showToast('文件内容为空')
+ onConversation('next', info, result)
+ } catch (error) {
+ showToast(error.message)
+ } finally {
+ fileLoading.value = false
+ }
+}
+
+function validateFile(file) {
+ const allowedExtensions = ['doc', 'docx', 'pdf']
+ const maxFileSize = 20 * 1024 * 1024 // 20MB
+
+ const fileExtension = getFileExtension(file.name)
+ const fileSize = file.size
+
+ if (fileSize === 0) {
+ throw new Error('文件内容为空')
+ }
+
+ if (!allowedExtensions.includes(fileExtension)) {
+ throw new Error('文件格式无效')
+ }
+
+ if (fileSize > maxFileSize) {
+ throw new Error('文件大小超出限制')
+ }
+
+ return true
+}
+
+function getFileExtension(fileName) {
+ const parts = fileName.split('.')
+ return parts[parts.length - 1].toLowerCase()
+}
+
const conversationList = computed(() => {
return chatStore.getCurrentChat.filter((item) => {
const key = Object.keys(item)[0]
@@ -172,8 +247,8 @@ function handleSubmit() {
onConversation()
}
-async function onConversation(action = 'next') {
- let message = inputValue.value
+async function onConversation(action = 'next', file, fileText) {
+ let message = inputValue.value || fileText
if (loading.value) return
if ((!message || message.trim() === '') && action == 'next') return
@@ -181,8 +256,10 @@ async function onConversation(action = 'next') {
action: action,
conversation_id: chatStore.active,
message: {
- text: message,
+ text: message?.substring(0, 4000),
id: uuidv4(),
+ type: (file?.extension === 'doc' ? 'docx' : file?.extension) || 'text',
+ fileInfo: file,
},
}
if (action == 'next') {
@@ -198,6 +275,8 @@ async function onConversation(action = 'next') {
inversion: true,
parent_id: params.parent_message_id || '',
id: params.message.id,
+ type: params.message.type,
+ fileInfo: file,
})
scrollToBottom()
chatStore.addChatByUuid(chatStore.active, {
@@ -212,7 +291,7 @@ async function onConversation(action = 'next') {
conversationUserList.value[conversationUserList.value.length - 1]
if (lastUserContext) {
const obj = lastUserContext[Object.keys(lastUserContext)[0]]
- params.message.text = obj.text
+ params.message.text = obj.text?.substring(0, 4000)
params.message.id = obj.id
params.parent_message_id = obj.id
}
diff --git a/src/views/communication/components/contacts.vue b/src/views/communication/components/contacts.vue
new file mode 100644
index 0000000..e30b359
--- /dev/null
+++ b/src/views/communication/components/contacts.vue
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
感谢您对海兔AI的关注
+
请留下您的信息
+
我们会尽快联系您!
+
+
+
+
+
+
需求类型
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 确认
+
+
+
+
+
+
+
+
+
diff --git a/src/views/communication/home.vue b/src/views/communication/home.vue
index 337f10f..520d7ec 100644
--- a/src/views/communication/home.vue
+++ b/src/views/communication/home.vue
@@ -27,7 +27,10 @@
一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字
- 立即体验
@@ -63,7 +66,10 @@
一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字一些相关的描述文字