This commit is contained in:
街角小林 2025-02-19 10:26:02 +08:00
parent b834b6fdd7
commit f8149ce383
7 changed files with 347 additions and 77 deletions

11
web/package-lock.json generated
View File

@ -15699,7 +15699,8 @@
"node_modules/viewerjs": { "node_modules/viewerjs": {
"version": "1.11.6", "version": "1.11.6",
"resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.6.tgz", "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.6.tgz",
"integrity": "sha512-TlhdSp2oEOLFXvEp4psKaeTjR5zBjTRcM/sHUN8PkV1UWuY8HKC8n7GaVdW5Xqnwdr/F1OmzLik1QwDjI4w/nw==" "integrity": "sha512-TlhdSp2oEOLFXvEp4psKaeTjR5zBjTRcM/sHUN8PkV1UWuY8HKC8n7GaVdW5Xqnwdr/F1OmzLik1QwDjI4w/nw==",
"peer": true
}, },
"node_modules/vm-browserify": { "node_modules/vm-browserify": {
"version": "1.1.2", "version": "1.1.2",
@ -19257,7 +19258,6 @@
"integrity": "sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q==", "integrity": "sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/core": "^7.11.0",
"@babel/helper-compilation-targets": "^7.9.6", "@babel/helper-compilation-targets": "^7.9.6",
"@babel/helper-module-imports": "^7.8.3", "@babel/helper-module-imports": "^7.8.3",
"@babel/plugin-proposal-class-properties": "^7.8.3", "@babel/plugin-proposal-class-properties": "^7.8.3",
@ -19270,7 +19270,6 @@
"@vue/babel-plugin-jsx": "^1.0.3", "@vue/babel-plugin-jsx": "^1.0.3",
"@vue/babel-preset-jsx": "^1.2.4", "@vue/babel-preset-jsx": "^1.2.4",
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"core-js": "^3.6.5",
"core-js-compat": "^3.6.5", "core-js-compat": "^3.6.5",
"semver": "^6.1.0" "semver": "^6.1.0"
} }
@ -29566,8 +29565,7 @@
"resolved": "https://registry.npmjs.org/v-viewer/-/v-viewer-1.7.4.tgz", "resolved": "https://registry.npmjs.org/v-viewer/-/v-viewer-1.7.4.tgz",
"integrity": "sha512-K3PQ8utnVXXBCa5IRXRAhk/m83fNIsK77gTSXqAmPJe8eDTaSY1nifAOWPUmQDjzuCxYfa14UjGftHR9MFV70Q==", "integrity": "sha512-K3PQ8utnVXXBCa5IRXRAhk/m83fNIsK77gTSXqAmPJe8eDTaSY1nifAOWPUmQDjzuCxYfa14UjGftHR9MFV70Q==",
"requires": { "requires": {
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21"
"viewerjs": "^1.11.6"
} }
}, },
"v8-compile-cache": { "v8-compile-cache": {
@ -29632,7 +29630,8 @@
"viewerjs": { "viewerjs": {
"version": "1.11.6", "version": "1.11.6",
"resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.6.tgz", "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.6.tgz",
"integrity": "sha512-TlhdSp2oEOLFXvEp4psKaeTjR5zBjTRcM/sHUN8PkV1UWuY8HKC8n7GaVdW5Xqnwdr/F1OmzLik1QwDjI4w/nw==" "integrity": "sha512-TlhdSp2oEOLFXvEp4psKaeTjR5zBjTRcM/sHUN8PkV1UWuY8HKC8n7GaVdW5Xqnwdr/F1OmzLik1QwDjI4w/nw==",
"peer": true
}, },
"vm-browserify": { "vm-browserify": {
"version": "1.1.2", "version": "1.1.2",

View File

@ -422,5 +422,58 @@ export default {
nodeTagStyle: { nodeTagStyle: {
placeholder: 'Please enter the tag content', placeholder: 'Please enter the tag content',
delete: 'Delete this tag' delete: 'Delete this tag'
},
ai: {
chatTitle: 'AI dialogue',
clearRecords: 'Clear records',
connectFailedTitle: 'Client connection failure prompt',
connectFailedTip: 'Client connection failed, please check:',
connectFailedCheckTip1:
'1. Have you installed the mind mapping client? If not, please click here to install:',
connectFailedCheckTip2: '2. If the client is installed, please confirm if the client is opened.',
connectFailedCheckTip3:
'If it has already been installed and started, you can try closing and restarting it.',
connectFailedCheckTip4: 'After completing the above steps, you can click on:',
baiduNetdisk: 'Baidu Netdisk',
createMindMapTitle: 'One click generation of mind maps',
createTip:
'Please enter a theme, and AI will generate a mind map based on your theme, such as: Hangzhou weekend travel plan.',
importantTip: 'Important note: One click generation will overwrite existing data. It is recommended to export the current data first.',
wantModifyAiConfigTip: 'Do you want to modify the AI configuration? Please click on:',
modifyAIConfiguration: 'Modify AI configuration',
chatInputPlaceholder: 'Enter to send, Shift+Enter to wrap.',
send: 'Send',
stopGenerating: 'Stop generating',
generationFailed: 'Generation failed',
aiGenerationSuccess: 'AI generation completed',
stoppedGenerating: 'Stopped generating',
AIConfiguration: 'AI configuration',
VolcanoArkLargeModelConfiguration: 'Volcano Ark Large Model Configuration:',
configTip: 'At present, only the Volcano Ark model is supported, and you need to obtain the key yourself. For detailed operation steps, please refer to:',
course: 'Course',
inferenceAccessPoint: 'Inference access point',
mindMappingClientConfiguration: 'Mind mapping client configuration:',
port: 'Port',
cancel: 'Cancel',
confirm: 'Confirm',
close: 'Close',
configSaveSuccessTip: 'Configuration saved successfully',
apiValidateTip: 'Please enter the interface',
keyValidateTip: 'Please enter the API Key',
modelValidateTip: 'Please enter the inference access point',
portValidateTip: 'Please enter the port',
methodValidateTip: 'Please select the request method',
noInputTip: 'Please enter the content',
connectSuccessful: 'Connection successful',
connectFailed: 'connection failed',
connectionDetection: 'Connection detection',
configurationMissing: 'Configuration missing',
aiCreateMsgPrefix: 'Help me write one【',
aiCreateMsgPostfix:
'】. It needs to be returned in Markdown format and can only use two syntax: Markdown title and unordered list. It can support multiple layers of nesting. Just return the content.',
aiCreatePartMsgPrefix: 'I have a theme for【',
aiCreatePartMsgCenter: '】Can you help me continue writing one of the contents of the mind map【',
aiCreatePartMsgPostfix:
'】The subordinate content of the node needs to be returned in Markdown format and can only use two syntax: Markdown title and unordered list. It can support multi-level nesting. Just return the content.'
} }
} }

View File

@ -416,6 +416,56 @@ export default {
delete: '删除此标签' delete: '删除此标签'
}, },
ai: { ai: {
chatTitle: 'AI对话' chatTitle: 'AI对话',
clearRecords: '清空记录',
connectFailedTitle: '客户端连接失败提示',
connectFailedTip: '客户端连接失败,请检查:',
connectFailedCheckTip1:
'1.是否安装了思绪思维导图客户端,如果没有请点此安装:',
connectFailedCheckTip2: '2.如果安装了客户端,请确认是否打开了客户端。',
connectFailedCheckTip3:
'3.如果已经安装并启动了,那么可以尝试关闭然后重新启动。',
connectFailedCheckTip4: '完成以上步骤后可点击:',
baiduNetdisk: '百度网盘',
createMindMapTitle: '一键生成思维导图',
createTip:
'请输入一个主题AI会根据你的主题生成思维导图杭州周末出游计划。',
importantTip: '重要提示:一键生成会覆盖现有数据,建议先导出当前数据。',
wantModifyAiConfigTip: '想要修改AI配置请点击',
modifyAIConfiguration: '修改AI配置',
chatInputPlaceholder: 'Enter 发送Shift + Enter 换行。',
send: '发送',
stopGenerating: '停止生成',
generationFailed: '生成失败',
aiGenerationSuccess: 'AI生成完成',
stoppedGenerating: '已停止生成',
AIConfiguration: 'AI配置',
VolcanoArkLargeModelConfiguration: '火山方舟大模型配置:',
configTip: '目前仅支持火山方舟大模型需要自行去获取key详细操作步骤见',
course: '教程',
inferenceAccessPoint: '推理接入点',
mindMappingClientConfiguration: '思绪思维导图客户端配置:',
port: '端口',
cancel: '取消',
confirm: '确认',
close: '关闭',
configSaveSuccessTip: '配置保存成功',
apiValidateTip: '请输入接口',
keyValidateTip: '请输入API Key',
modelValidateTip: '请输入推理接入点',
portValidateTip: '请输入端口',
methodValidateTip: '请选择请求方式',
noInputTip: '请输入内容',
connectSuccessful: '连接成功',
connectFailed: '连接失败',
connectionDetection: '连接检测',
configurationMissing: '配置缺失',
aiCreateMsgPrefix: '帮我写一个【',
aiCreateMsgPostfix:
'】需要以Markdown格式返回并且只能使用Markdown的标题和无序列表两种语法可以支持多层嵌套。只需返回内容即可。',
aiCreatePartMsgPrefix: '我有一个主题为【',
aiCreatePartMsgCenter: '】的思维导图,帮我续写其中一个内容为【',
aiCreatePartMsgPostfix:
'】的节点的下级内容需要以Markdown格式返回并且只能使用Markdown的标题和无序列表两种语法可以支持多层嵌套。只需返回内容即可。'
} }
} }

View File

@ -413,5 +413,58 @@ export default {
nodeTagStyle: { nodeTagStyle: {
placeholder: '請輸入標籤內容', placeholder: '請輸入標籤內容',
delete: '刪除此標籤' delete: '刪除此標籤'
},
ai: {
chatTitle: 'AI對話',
clearRecords: '清空記錄',
connectFailedTitle: '客戶端連接失敗提示',
connectFailedTip: '客戶端連接失敗,請檢查:',
connectFailedCheckTip1:
'1.是否安裝了思緒思維導圖客戶端,如果沒有請點此安裝:',
connectFailedCheckTip2: '2.如果安裝了客戶端,請確認是否打開了客戶端。',
connectFailedCheckTip3:
'3.如果已經安裝並啓動了,那麽可以嘗試關閉然後重新啓動。',
connectFailedCheckTip4: '完成以上步驟後可點擊:',
baiduNetdisk: '百度網盤',
createMindMapTitle: '一鍵生成思維導圖',
createTip:
'請輸入一個主題AI會根據你的主題生成思維導圖杭州周末出遊計劃。',
importantTip: '重要提示:一鍵生成會覆蓋現有數據,建議先導出當前數據。',
wantModifyAiConfigTip: '想要修改AI配置請點擊',
modifyAIConfiguration: '修改AI配置',
chatInputPlaceholder: 'Enter 發送Shift Enter 換行。',
send: '發送',
stopGenerating: '停止生成',
generationFailed: '生成失敗',
aiGenerationSuccess: 'AI生成完成',
stoppedGenerating: '已停止生成',
AIConfiguration: 'AI配置',
VolcanoArkLargeModelConfiguration: '火山方舟大模型配置:',
configTip: '目前僅支持火山方舟大模型需要自行去獲取key詳細操作步驟見',
course: '教程',
inferenceAccessPoint: '推理接入點',
mindMappingClientConfiguration: '思緒思維導圖客戶端配置:',
port: '端口',
cancel: '取消',
confirm: '確認',
close: '關閉',
configSaveSuccessTip: '配置保存成功',
apiValidateTip: '請輸入接口',
keyValidateTip: '請輸入API Key',
modelValidateTip: '請輸入推理接入點',
portValidateTip: '請輸入端口',
methodValidateTip: '請選擇請求方式',
noInputTip: '請輸入內容',
connectSuccessful: '連接成功',
connectFailed: '連接失敗',
connectionDetection: '連接檢測',
configurationMissing: '配置缺失',
aiCreateMsgPrefix: '幫我寫一個【',
aiCreateMsgPostfix:
'】需要以Markdown格式返回並且只能使用Markdown的標題和無序列表兩種語法可以支持多層嵌套。只需返回內容即可。',
aiCreatePartMsgPrefix: '我有一個主題爲【',
aiCreatePartMsgCenter: '】的思維導圖,幫我續寫其中一個內容爲【',
aiCreatePartMsgPostfix:
'】的節點的下級內容需要以Markdown格式返回並且只能使用Markdown的標題和無序列表兩種語法可以支持多層嵌套。只需返回內容即可。'
} }
} }

View File

@ -2,7 +2,14 @@
<Sidebar ref="sidebar" :title="$t('ai.chatTitle')"> <Sidebar ref="sidebar" :title="$t('ai.chatTitle')">
<div class="aiChatBox" :class="{ isDark: isDark }"> <div class="aiChatBox" :class="{ isDark: isDark }">
<div class="chatHeader"> <div class="chatHeader">
<el-button size="mini" @click="clear">清空记录</el-button> <el-button size="mini" @click="clear">
<span class="el-icon-delete"></span>
{{ $t('ai.clearRecords') }}
</el-button>
<el-button size="mini" @click="modifyAiConfig">
<span class="el-icon-edit"></span>
{{ $t('ai.modifyAIConfiguration') }}
</el-button>
</div> </div>
<div class="chatResBox customScrollbar" ref="chatResBoxRef"> <div class="chatResBox customScrollbar" ref="chatResBoxRef">
<div <div
@ -29,13 +36,22 @@
<textarea <textarea
v-model="text" v-model="text"
class="customScrollbar" class="customScrollbar"
placeholder="Enter 发送Shift + Enter 换行。" :placeholder="$t('ai.chatInputPlaceholder')"
@keydown.enter.prevent @keydown="onKeydown"
@keyup.enter.prevent="send"
></textarea> ></textarea>
<el-button class="btn" size="mini" @click="send" :loading="isCreating" <el-button class="btn" size="mini" @click="send" :loading="isCreating">
>发送</el-button {{ $t('ai.send') }}
<span class="el-icon-position"></span>
</el-button>
<el-button
class="stop"
size="mini"
type="warning"
@click="stop"
v-show="isCreating"
> >
{{ $t('ai.stopGenerating') }}
</el-button>
</div> </div>
</div> </div>
</Sidebar> </Sidebar>
@ -43,7 +59,7 @@
<script> <script>
import Sidebar from './Sidebar' import Sidebar from './Sidebar'
import { mapState, mapMutations } from 'vuex' import { mapState } from 'vuex'
import { createUid } from 'simple-mind-map/src/utils' import { createUid } from 'simple-mind-map/src/utils'
import MarkdownIt from 'markdown-it' import MarkdownIt from 'markdown-it'
@ -78,6 +94,16 @@ export default {
created() {}, created() {},
beforeDestroy() {}, beforeDestroy() {},
methods: { methods: {
onKeydown(e) {
if (e.keyCode === 13) {
if (!e.shiftKey) {
e.preventDefault()
this.send()
} else {
}
}
},
send() { send() {
if (this.isCreating) return if (this.isCreating) return
const text = this.text.trim() const text = this.text.trim()
@ -85,6 +111,13 @@ export default {
return return
} }
this.text = '' this.text = ''
const historyUserMsgList = this.chatList
.filter(item => {
return item.type === 'user'
})
.map(item => {
return item.content
})
this.chatList.push({ this.chatList.push({
id: createUid(), id: createUid(),
type: 'user', type: 'user',
@ -96,9 +129,10 @@ export default {
content: '' content: ''
}) })
this.isCreating = true this.isCreating = true
const textList = [...historyUserMsgList, text]
this.$bus.$emit( this.$bus.$emit(
'ai_chat', 'ai_chat',
text, textList,
res => { res => {
if (!md) { if (!md) {
md = new MarkdownIt() md = new MarkdownIt()
@ -111,12 +145,22 @@ export default {
}, },
() => { () => {
this.isCreating = false this.isCreating = false
this.$message.error(this.$t('ai.generationFailed'))
} }
) )
}, },
stop() {
this.$bus.$emit('ai_chat_stop')
this.isCreating = false
},
clear() { clear() {
this.chatList = [] this.chatList = []
},
modifyAiConfig() {
this.$bus.$emit('showAiConfigDialog')
} }
} }
} }
@ -284,6 +328,13 @@ export default {
right: 12px; right: 12px;
bottom: 12px; bottom: 12px;
} }
.stop {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: -30px;
}
} }
} }
</style> </style>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-dialog <el-dialog
class="aiConfigDialog" class="aiConfigDialog"
title="AI配置" :title="$t('ai.AIConfiguration')"
:visible.sync="aiConfigDialogVisible" :visible.sync="aiConfigDialogVisible"
width="550px" width="550px"
append-to-body append-to-body
@ -13,17 +13,15 @@
ref="ruleFormRef" ref="ruleFormRef"
label-width="100px" label-width="100px"
> >
<p class="title">火山方舟大模型配置</p> <p class="title">{{ $t('ai.VolcanoArkLargeModelConfiguration') }}</p>
<p class="desc"> <p class="desc">
目前仅支持火山方舟大模型需要自行去获取key详细操作步骤见<a {{ $t('ai.configTip') }}<a href="">{{ $t('ai.course') }}</a
href=""
>教程</a
> >
</p> </p>
<el-form-item label="API Key" prop="key"> <el-form-item label="API Key" prop="key">
<el-input v-model="ruleForm.key"></el-input> <el-input v-model="ruleForm.key"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="推理接入点" prop="model"> <el-form-item :label="$t('ai.inferenceAccessPoint')" prop="model">
<el-input v-model="ruleForm.model"></el-input> <el-input v-model="ruleForm.model"></el-input>
</el-form-item> </el-form-item>
<!-- <el-form-item label="接口" prop="api"> <!-- <el-form-item label="接口" prop="api">
@ -35,15 +33,17 @@
<el-option key="GET" label="GET" value="GET"></el-option> <el-option key="GET" label="GET" value="GET"></el-option>
</el-select> </el-select>
</el-form-item> --> </el-form-item> -->
<p class="title">思绪思维导图客户端配置</p> <p class="title">{{ $t('ai.mindMappingClientConfiguration') }}</p>
<el-form-item label="端口" prop="port"> <el-form-item :label="$t('ai.port')" prop="port">
<el-input v-model="ruleForm.port"></el-input> <el-input v-model="ruleForm.port"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="cancel">取消</el-button> <el-button @click="cancel">{{ $t('ai.cancel') }}</el-button>
<el-button type="primary" @click="confirm">确认</el-button> <el-button type="primary" @click="confirm">{{
$t('ai.confirm')
}}</el-button>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
@ -73,13 +73,41 @@ export default {
method: '' method: ''
}, },
rules: { rules: {
api: [{ required: true, message: '请输入接口', trigger: 'blur' }], api: [
key: [{ required: true, message: '请输入API Key', trigger: 'blur' }], {
model: [ required: true,
{ required: true, message: '请输入推理接入点', trigger: 'blur' } message: this.$t('ai.apiValidateTip'),
trigger: 'blur'
}
], ],
port: [{ required: true, message: '请输入端口', trigger: 'blur' }], key: [
method: [{ required: true, message: '请选择', trigger: 'blur' }] {
required: true,
message: this.$t('ai.keyValidateTip'),
trigger: 'blur'
}
],
model: [
{
required: true,
message: this.$t('ai.modelValidateTip'),
trigger: 'blur'
}
],
port: [
{
required: true,
message: this.$t('ai.portValidateTip'),
trigger: 'blur'
}
],
method: [
{
required: true,
message: this.$t('ai.methodValidateTip'),
trigger: 'blur'
}
]
} }
} }
}, },
@ -124,7 +152,7 @@ export default {
this.setLocalConfig({ this.setLocalConfig({
...this.ruleForm ...this.ruleForm
}) })
this.$message.success('配置保存成功') this.$message.success(this.$t('ai.configSaveSuccessTip'))
} }
}) })
} }
@ -139,6 +167,10 @@ export default {
} }
.aiConfigBox { .aiConfigBox {
a {
color: #409eff;
}
.title { .title {
margin-bottom: 12px; margin-bottom: 12px;
font-weight: bold; font-weight: bold;

View File

@ -3,37 +3,39 @@
<!-- 客户端连接失败提示弹窗 --> <!-- 客户端连接失败提示弹窗 -->
<el-dialog <el-dialog
class="clientTipDialog" class="clientTipDialog"
title="客户端连接失败提示" :title="$t('ai.connectFailedTitle')"
:visible.sync="clientTipDialogVisible" :visible.sync="clientTipDialogVisible"
width="400px" width="400px"
append-to-body append-to-body
> >
<div class="tipBox"> <div class="tipBox">
<p>客户端连接失败请检查</p> <p>{{ $t('ai.connectFailedTip') }}</p>
<p> <p>
1.是否安装了思绪思维导图客户端如果没有请点此安装<a {{ $t('ai.connectFailedCheckTip1')
}}<a
href="https://pan.baidu.com/s/1huasEbKsGNH2Af68dvWiOg?pwd=3bp3" href="https://pan.baidu.com/s/1huasEbKsGNH2Af68dvWiOg?pwd=3bp3"
>百度网盘</a >{{ $t('ai.baiduNetdisk') }}</a
><a href="https://github.com/wanglin2/mind-map/releases">Github</a> ><a href="https://github.com/wanglin2/mind-map/releases">Github</a>
</p> </p>
<p>2.如果安装了客户端请确认是否打开了客户端</p> <p>{{ $t('ai.connectFailedCheckTip2') }}</p>
<P>3.如果已经安装并启动了那么可以尝试关闭然后重新启动</P> <P>{{ $t('ai.connectFailedCheckTip3') }}</P>
<p> <p>
完成以上步骤后可点击<el-button size="small" @click="testConnect" {{ $t('ai.connectFailedCheckTip4')
>连接检测</el-button }}<el-button size="small" @click="testConnect">{{
> $t('ai.connectionDetection')
}}</el-button>
</p> </p>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="primary" @click="clientTipDialogVisible = false" <el-button type="primary" @click="clientTipDialogVisible = false">{{
>关闭</el-button $t('ai.close')
> }}</el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- ai内容输入弹窗 --> <!-- ai内容输入弹窗 -->
<el-dialog <el-dialog
class="createDialog" class="createDialog"
title="一键生成思维导图" :title="$t('ai.createMindMapTitle')"
:visible.sync="createDialogVisible" :visible.sync="createDialogVisible"
width="450px" width="450px"
append-to-body append-to-body
@ -42,24 +44,27 @@
<el-input <el-input
type="textarea" type="textarea"
:rows="5" :rows="5"
placeholder="请输入一个主题AI会根据你的主题生成思维导图杭州周末出游计划。" :placeholder="$t('ai.createTip')"
v-model="aiInput" v-model="aiInput"
> >
</el-input> </el-input>
<div class="tip warning"> <div class="tip warning">
重要提示一键生成会覆盖现有数据建议先导出当前数据 {{ $t('ai.importantTip') }}
</div> </div>
<div class="tip"> <div class="tip">
想要修改AI配置请点击<el-button {{ $t('ai.wantModifyAiConfigTip')
size="small" }}<el-button size="small" @click="showAiConfigDialog">{{
@click="aiConfigDialogVisible = true" $t('ai.modifyAIConfiguration')
>修改配置</el-button }}</el-button>
>
</div> </div>
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="closeAiCreateDialog">取消</el-button> <el-button @click="closeAiCreateDialog">{{
<el-button type="primary" @click="doAiCreate">确认</el-button> $t('ai.cancel')
}}</el-button>
<el-button type="primary" @click="doAiCreate">{{
$t('ai.confirm')
}}</el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- ai生成中添加一个透明层防止期间用户进行操作 --> <!-- ai生成中添加一个透明层防止期间用户进行操作 -->
@ -68,9 +73,9 @@
ref="aiCreatingMaskRef" ref="aiCreatingMaskRef"
v-show="aiCreatingMaskVisible" v-show="aiCreatingMaskVisible"
> >
<el-button type="warning" class="btn" @click="stopCreate" <el-button type="warning" class="btn" @click="stopCreate">{{
>停止生成</el-button $t('ai.stopGenerating')
> }}</el-button>
</div> </div>
<AiConfigDialog v-model="aiConfigDialogVisible"></AiConfigDialog> <AiConfigDialog v-model="aiConfigDialogVisible"></AiConfigDialog>
</div> </div>
@ -124,6 +129,8 @@ export default {
this.$bus.$on('ai_create_all', this.aiCrateAll) this.$bus.$on('ai_create_all', this.aiCrateAll)
this.$bus.$on('ai_create_part', this.aiCreatePart) this.$bus.$on('ai_create_part', this.aiCreatePart)
this.$bus.$on('ai_chat', this.aiChat) this.$bus.$on('ai_chat', this.aiChat)
this.$bus.$on('ai_chat_stop', this.aiChatStop)
this.$bus.$on('showAiConfigDialog', this.showAiConfigDialog)
}, },
mounted() { mounted() {
document.body.appendChild(this.$refs.aiCreatingMaskRef) document.body.appendChild(this.$refs.aiCreatingMaskRef)
@ -132,20 +139,27 @@ export default {
this.$bus.$off('ai_create_all', this.aiCrateAll) this.$bus.$off('ai_create_all', this.aiCrateAll)
this.$bus.$off('ai_create_part', this.aiCreatePart) this.$bus.$off('ai_create_part', this.aiCreatePart)
this.$bus.$off('ai_chat', this.aiChat) this.$bus.$off('ai_chat', this.aiChat)
this.$bus.$off('ai_chat_stop', this.aiChatStop)
this.$bus.$off('showAiConfigDialog', this.showAiConfigDialog)
}, },
methods: { methods: {
// AI
showAiConfigDialog() {
this.aiConfigDialogVisible = true
},
// //
async testConnect() { async testConnect() {
try { try {
await fetch(`http://localhost:${this.aiConfig.port}/ai/test`, { await fetch(`http://localhost:${this.aiConfig.port}/ai/test`, {
method: 'GET' method: 'GET'
}) })
this.$message.success('连接成功') this.$message.success(this.$t('ai.connectSuccessful'))
this.clientTipDialogVisible = false this.clientTipDialogVisible = false
this.createDialogVisible = true this.createDialogVisible = true
} catch (error) { } catch (error) {
console.log(error) console.log(error)
this.$message.error('连接失败') this.$message.error(this.$t('ai.connectFailed'))
} }
}, },
@ -160,8 +174,8 @@ export default {
this.aiConfig.port this.aiConfig.port
) )
) { ) {
this.aiConfigDialogVisible = true this.showAiConfigDialog()
throw new Error('配置缺失') throw new Error(this.$t('ai.configurationMissing'))
} }
// //
let isConnect = false let isConnect = false
@ -175,7 +189,7 @@ export default {
this.clientTipDialogVisible = true this.clientTipDialogVisible = true
} }
if (!isConnect) { if (!isConnect) {
throw new Error('连接失败') throw new Error(this.$t('ai.connectFailed'))
} }
}, },
@ -199,7 +213,7 @@ export default {
doAiCreate() { doAiCreate() {
const aiInputText = this.aiInput.trim() const aiInputText = this.aiInput.trim()
if (!aiInputText) { if (!aiInputText) {
this.$message.warning('请输入内容') this.$message.warning(this.$t('ai.noInputTip'))
return return
} }
this.closeAiCreateDialog() this.closeAiCreateDialog()
@ -217,7 +231,9 @@ export default {
messages: [ messages: [
{ {
role: 'user', role: 'user',
content: `帮我写一个【${aiInputText}需要以Markdown格式返回并且只能使用Markdown的标题和无序列表两种语法可以支持多层嵌套。只需返回内容即可。` content: `${this.$t(
'ai.aiCreateMsgPrefix'
)}${aiInputText}${this.$t('ai.aiCreateMsgPostfix')}`
} }
] ]
}, },
@ -230,12 +246,12 @@ export default {
content => { content => {
this.aiCreatingContent = content this.aiCreatingContent = content
this.resetOnAiCreatingStop() this.resetOnAiCreatingStop()
this.$message.success('AI生成完成') this.$message.success(this.$t('ai.aiGenerationSuccess'))
}, },
() => { () => {
this.resetOnAiCreatingStop() this.resetOnAiCreatingStop()
this.resetOnRenderEnd() this.resetOnRenderEnd()
this.$message.error('生成失败') this.$message.error(this.$t('ai.generationFailed'))
} }
) )
}, },
@ -261,7 +277,7 @@ export default {
this.aiInstance.stop() this.aiInstance.stop()
this.isAiCreating = false this.isAiCreating = false
this.aiCreatingMaskVisible = false this.aiCreatingMaskVisible = false
this.$message.success('已停止生成') this.$message.success(this.$t('ai.stoppedGenerating'))
}, },
// //
@ -374,11 +390,13 @@ export default {
messages: [ messages: [
{ {
role: 'user', role: 'user',
content: `我有一个主题为【${getStrWithBrFromHtml( content: `${this.$t(
'ai.aiCreatePartMsgPrefix'
)}${getStrWithBrFromHtml(
currentMindMapData.data.text currentMindMapData.data.text
)}的思维导图帮我续写其中一个内容为${getStrWithBrFromHtml( )}${this.$t('ai.aiCreatePartMsgCenter')}${getStrWithBrFromHtml(
node.getData('text') node.getData('text')
)}的节点的下级内容需要以Markdown格式返回并且只能使用Markdown的标题和无序列表两种语法可以支持多层嵌套只需返回内容即可` )}${this.$t('ai.aiCreatePartMsgPostfix')}`
} }
] ]
}, },
@ -391,12 +409,12 @@ export default {
content => { content => {
this.aiCreatingContent = content this.aiCreatingContent = content
this.resetOnAiCreatingStop() this.resetOnAiCreatingStop()
this.$message.success('AI生成完成') this.$message.success(this.$t('ai.aiGenerationSuccess'))
}, },
() => { () => {
this.resetOnAiCreatingStop() this.resetOnAiCreatingStop()
this.resetOnRenderEnd() this.resetOnRenderEnd()
this.$message.error('生成失败') this.$message.error(this.$t('ai.generationFailed'))
} }
) )
} catch (error) { } catch (error) {
@ -472,7 +490,12 @@ export default {
}, },
// AI // AI
async aiChat(text, progress = () => {}, end = () => {}, err = () => {}) { async aiChat(
messageList = [],
progress = () => {},
end = () => {},
err = () => {}
) {
try { try {
await this.aiTest() await this.aiTest()
// //
@ -483,12 +506,12 @@ export default {
this.aiInstance.init('huoshan', this.aiConfig) this.aiInstance.init('huoshan', this.aiConfig)
this.aiInstance.request( this.aiInstance.request(
{ {
messages: [ messages: messageList.map(msg => {
{ return {
role: 'user', role: 'user',
content: text content: msg
} }
] })
}, },
content => { content => {
progress(content) progress(content)
@ -503,6 +526,15 @@ export default {
} catch (error) { } catch (error) {
console.log(error) console.log(error)
} }
},
// AI
aiChatStop() {
if (this.aiInstance) {
this.aiInstance.stop()
this.isAiCreating = false
this.aiInstance = null
}
} }
} }
} }