-
对话框
-
+
+
+
+
+
+
AI
+
{{ item.content }}
+
+
+
+
+
+
+
+
@@ -98,13 +116,11 @@
import { form } from '@crud/crud'
import { mapGetters } from 'vuex'
import { FetchDictionaryTree } from '@/api/system/dict'
-import editor from '@/components/quillEditor/index'
-// import strJson from './str.json'
const defaultForm = { id: null, title: null, researchType: null }
export default {
name: 'Form',
- components: { editor },
+ components: { },
mixins: [
form(function() {
return Object.assign({}, defaultForm)
@@ -112,16 +128,10 @@ export default {
],
data() {
return {
- formType: 1,
formTitle: '',
formVisible: false,
- selectedFiles: [],
- textarea: '帮我写一篇AI赋能档案管理的报告',
editorRef: 'test',
editorContent: '',
- mockResponse: '',
- loading: false,
- intervalId: null,
options: [],
rules: {
@@ -133,7 +143,13 @@ export default {
imageUrl: '',
fileName: '',
isImage: false,
- fileType: ''
+ fileType: '',
+ message: [],
+ controller: null,
+ arequestData: {
+ model: 'deepseek-r1:14b',
+ messages: []
+ }
}
},
computed: {
@@ -154,87 +170,180 @@ export default {
console.log(err)
})
},
- handleFileChange(event) {
+ async handleFileChange(event) {
const file = event.target.files[0]
+ const allowedExtensions = ['.xlsx', '.xls', '.docx', '.doc', '.pdf', '.ofd', '.pptx', '.txt']
+ const maxSize = 10 * 1024 * 1024
+
if (file) {
- this.fileType = file.type
- console.log('this.fileType ', this.fileType)
+ const fileExtension = '.' + file.name.split('.').pop().toLowerCase()
if (file.type.indexOf('image') !== -1) {
- this.isImage = true
- const reader = new FileReader()
- reader.onload = (e) => {
- this.imageUrl = e.target.result
- this.fileName = file.name
- }
- reader.readAsDataURL(file)
- } else {
- this.isImage = false
- this.fileName = file.name
- this.imageUrl = ''
+ this.$message.error('不允许上传图片文件!')
+ return
}
+ if (!allowedExtensions.includes(fileExtension)) {
+ this.$message.error('仅允许上传以下格式的文件:.xlsx, .xls, .docx, .doc, .pdf, .ofd, .pptx, .txt')
+ return
+ }
+ if (file.size > maxSize) {
+ this.$message.error('文件大小不能超过 10MB!')
+ return
+ }
+
+ this.fileType = file.type
+ this.isImage = false
+ this.fileName = file.name
+ this.imageUrl = ''
+
+ // 这种情况前端就给个提示:您上传的文件不是纯文本格式,暂不支持解析
}
},
- deleteFile() {
- this.imageUrl = ''
- this.fileName = ''
- this.fileType = ''
- this.isImage = false
- },
- previewFiles() {
- const files = this.$refs.file.files
- this.selectedFiles = []
- for (let i = 0; i < files.length; i++) {
- this.selectedFiles.push(files[i])
+ async handleMessage(e) {
+ if (e) { // 处理键盘事件
+ if (e.keyCode === 13) {
+ if (e.ctrlKey) { // Ctrl+Enter 发送消息
+ this.sendMessage()
+ } else { // Enter 键换行
+ this.inputValue += '\n'
+ this.scrollToBottom()
+ return
+ }
+ }
+ } else {
+ this.sendMessage()
}
},
- handleAIEditing() {
- if (this.selectedFiles.length === 0) {
- this.$message({ message: '请上传要编研的文件', type: 'error', offset: 8 })
+ async sendMessage(e) {
+ if (!this.inputValue) {
+ this.$message.error('请输入问题')
return
}
- if (this.textarea === '') {
- this.$message({ message: '请输入提问要求', type: 'error', offset: 8 })
- return
+ // 如果已经有一个正在进行的流式请求,则中止它
+ if (this.controller) {
+ this.controller.abort()
}
- this.loading = true
- this.editorContent = ''
- const lines = this.mockResponse.split('\n')
- let index = 0
+ setTimeout(() => {
+ this.scrollToBottom()
+ }, 50)
- this.intervalId = setInterval(() => {
- if (index < lines.length) {
- this.editorContent += (index > 0 ? '\n' : '') + lines[index]
- index++
- } else {
- clearInterval(this.intervalId)
- this.loading = false
+ this.message.push({
+ role: 'user',
+ content: this.inputValue
+ })
+ this.message.push({
+ role: 'ai',
+ content: '',
+ markDownText: '',
+ showMarkdown: false
+ })
+ try {
+ this.controller = new AbortController()
+ const signal = this.controller.signal
+ this.arequestData.messages.push({ role: 'user', content: this.inputValue })
+ const linkSrc = process.env.NODE_ENV === 'production' ? window.g.AIDeepSeekUrl : process.env.VUE_APP_AIDEEPSEEK_API
+ const response = await fetch(linkSrc + '/api/chat', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(this.arequestData),
+ signal
+ })
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`)
}
- // this.$nextTick(() => {
- // const container = this.$refs.typingContainer.$el.querySelector('.markdown-body')
- // if (container) {
- // container.scrollTop = container.scrollHeight
- // }
- // })
- }, 200) // 每500毫秒添加一行
+ const reader = response.body.getReader()
+ const decoder = new TextDecoder()
+ let result = ''
+
+ let done = false
+ while (!done) {
+ const { done: isDone, value } = await reader.read()
+ done = isDone
+ if (done) break
+ result += decoder.decode(value, { stream: true })
+ const jsonChunks = result.split('\n').filter(line => line.trim())
+ for (const chunk of jsonChunks) {
+ try {
+ const data = JSON.parse(chunk)
+ this.message[this.message.length - 1].content += data.message.content
+
+ setTimeout(() => {
+ this.scrollToBottom()
+ }, 50)
+ } catch (e) {
+ // 处理 JSON 解析错误
+ }
+ }
+
+ // 去除
和 之间的内容
+ const cleanText = this.message[this.message.length - 1].content.replace(/
[^]*?<\/think>/g, '')
+ console.log('cleanText', cleanText)
+ this.message[this.message.length - 1].markDownText = cleanText
+
+ result = ''
+ }
+ } catch (error) {
+ if (error.name === 'AbortError') {
+ console.log('Stream aborted')
+ this.message[this.message.length - 1].content = 'Stream aborted'
+ this.message[this.message.length - 1].markDownText = 'Stream aborted'
+ } else {
+ console.error('Streaming error:', error)
+ this.message[this.message.length - 1].content = 'Stream error' + error
+ this.message[this.message.length - 1].markDownText = 'Stream error' + error
+ }
+ } finally {
+ this.arequestData.messages.push({
+ role: 'assistant',
+ content: this.message[this.message.length - 1].content
+ })
+ this.message[this.message.length - 1].showMarkdown = true
+ setTimeout(() => {
+ this.scrollToBottom()
+ }, 50)
+ this.inputValue = ''
+ }
+ },
+ scrollToBottom() {
+ const talkContent = this.$refs.talkContent
+ if (talkContent) {
+ talkContent.scrollTop = talkContent.scrollHeight
+ }
+ },
+ beforeDestroy() {
+ // 组件销毁时中止流式请求
+ if (this.controller) {
+ this.controller.abort()
+ }
+ },
+ onClickContent(talkContent) {
+ this.editorContent = ''
+ this.editorContent = talkContent
+ },
+ deleteFile() {
+ this.imageUrl = ''
+ this.fileName = ''
+ this.fileType = ''
+ this.isImage = false
},
handleCloseForm() {
- this.selectedFiles = []
- this.textarea = ''
+ this.inputValue = ''
this.editorContent = ''
+ this.message = []
this.formVisible = false
- clearInterval(this.intervalId)
- this.loading = false
},
handleComfiredEditing() {
- // this.$refs.form.validate(valid => {
- // if (valid) {
- // console.log('保存数据', this.form);
- // }
- // });
- this.handleCloseForm()
+ this.$refs.form.validate(valid => {
+ if (valid) {
+ console.log('保存数据', this.form)
+ this.handleCloseForm()
+ }
+ })
},
toggleCollapse() {
this.isCollapsed = !this.isCollapsed
@@ -246,7 +355,7 @@ export default {
diff --git a/src/views/AIAssistant/AIKeywords/module/keywordMark.vue b/src/views/AIAssistant/AIKeywords/module/keywordMark.vue
index be88722..3a32716 100644
--- a/src/views/AIAssistant/AIKeywords/module/keywordMark.vue
+++ b/src/views/AIAssistant/AIKeywords/module/keywordMark.vue
@@ -138,7 +138,7 @@ export default {
context = resultJson.data.map(item => item.content).join('\n\n')
console.log('多张图片,用swiper')
}
- const prompt = '帮我提取5-10个关键词,关键词包含时间名称、地点名称、人名、部门机构名称以及档案学专用名称,按词频降序排列,用json格式带{}形式输出,其中只有一个关键字keywords数组字段,然后所有的关键字都放到这个keywords数组字段中'
+ const prompt = '帮我提取5-10个关键词,关键词包含地点名称、人名、部门机构名称以及档案学专用名称,按词频降序排列,用json格式带{}形式输出,其中只有一个关键字keywords数组字段,然后所有的关键字都放到这个keywords数组字段中'
this.sendMessage(prompt, context)
}).catch(err => {
console.log(err)
@@ -201,9 +201,6 @@ export default {
}
}
})
-
- // ... 原有的代码 ...
-
// 滚动条始终保持在底部
const container = this.$refs.typingContainer
if (container) {
diff --git a/src/views/AIAssistant/AIknowledgeGraph/index.vue b/src/views/AIAssistant/AIknowledgeGraph/index.vue
index 08367bf..e40df67 100644
--- a/src/views/AIAssistant/AIknowledgeGraph/index.vue
+++ b/src/views/AIAssistant/AIknowledgeGraph/index.vue
@@ -36,7 +36,7 @@
-
+