|
@@ -1,5 +1,5 @@
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
-import { ref, nextTick, onMounted, computed, onUnmounted, reactive, isReactive } from 'vue'
|
|
|
|
|
|
+import { ref, nextTick, onMounted, computed, onUnmounted, reactive, isReactive, watch } from 'vue'
|
|
import { Local } from '/@/utils/storage'
|
|
import { Local } from '/@/utils/storage'
|
|
import {
|
|
import {
|
|
User,
|
|
User,
|
|
@@ -15,7 +15,6 @@ import {
|
|
Download,
|
|
Download,
|
|
MoreFilled,
|
|
MoreFilled,
|
|
Setting,
|
|
Setting,
|
|
- Document,
|
|
|
|
Loading,
|
|
Loading,
|
|
Promotion,
|
|
Promotion,
|
|
VideoPause,
|
|
VideoPause,
|
|
@@ -27,7 +26,7 @@ import ToolsLoadingPlugin from '/@/components/markdown/plugins/tools-loading'
|
|
import TablePlugin from '/@/components/markdown/plugins/table'
|
|
import TablePlugin from '/@/components/markdown/plugins/table'
|
|
import Markdown from '/@/components/markdown/Markdown.vue'
|
|
import Markdown from '/@/components/markdown/Markdown.vue'
|
|
import assist from '/@/api/assist'
|
|
import assist from '/@/api/assist'
|
|
-import { ChatResponse, LmConfigInfo, LmSession, Message, Prompt, PromptListParams } from '/@/api/assist/type'
|
|
|
|
|
|
+import { ChatResponse, LmConfigInfo, LmSession, Message, Prompt } from '/@/api/assist/type'
|
|
import { useLoading } from '/@/utils/loading-util'
|
|
import { useLoading } from '/@/utils/loading-util'
|
|
import { Setting as EleSetting } from '@element-plus/icons-vue'
|
|
import { Setting as EleSetting } from '@element-plus/icons-vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useRouter } from 'vue-router'
|
|
@@ -159,15 +158,31 @@ const { loading: loadingEmbeddingModels, doLoading: loadEmbeddingModel } = useLo
|
|
|
|
|
|
onMounted(loadEmbeddingModel)
|
|
onMounted(loadEmbeddingModel)
|
|
|
|
|
|
-
|
|
|
|
// 提示词列表相关状态
|
|
// 提示词列表相关状态
|
|
|
|
+const customPrompt = ref('')
|
|
const promptList = ref<Prompt[]>([])
|
|
const promptList = ref<Prompt[]>([])
|
|
-const selectPromptId = ref<number|undefined>(undefined)
|
|
|
|
|
|
+
|
|
|
|
+const displayPromptList = computed(() => {
|
|
|
|
+ //有提示词展示自定义提示词
|
|
|
|
+ let r = [...promptList.value]
|
|
|
|
+ if (customPrompt.value.trim() !== '') {
|
|
|
|
+ r.splice(1, 0, {
|
|
|
|
+ id: -2,
|
|
|
|
+ title: `自定义提示词 (${customPrompt.value.length}) 字`,
|
|
|
|
+ prompt: customPrompt.value,
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ return r
|
|
|
|
+})
|
|
|
|
+const selectPromptId = ref<number | undefined>(undefined)
|
|
|
|
+watch(selectPromptId,(newVal)=>{
|
|
|
|
+ inputMessage.value = displayPromptList.value.find(i=>i.id === newVal)?.placeholder ?? ''
|
|
|
|
+})
|
|
const promptLabel = computed(() => {
|
|
const promptLabel = computed(() => {
|
|
if (!loadingPromptList.value && selectPromptId.value === undefined) {
|
|
if (!loadingPromptList.value && selectPromptId.value === undefined) {
|
|
return '未配置提示词'
|
|
return '未配置提示词'
|
|
}
|
|
}
|
|
- const select = promptList.value.filter((i) => i.id === selectPromptId.value)
|
|
|
|
|
|
+ const select = displayPromptList.value.filter((i) => i.id === selectPromptId.value)
|
|
|
|
|
|
if (select.length === 0) {
|
|
if (select.length === 0) {
|
|
return '加载中'
|
|
return '加载中'
|
|
@@ -176,7 +191,7 @@ const promptLabel = computed(() => {
|
|
return select[0].title
|
|
return select[0].title
|
|
})
|
|
})
|
|
const { loading: loadingPromptList, doLoading: loadPromptList } = useLoading(async () => {
|
|
const { loading: loadingPromptList, doLoading: loadPromptList } = useLoading(async () => {
|
|
- const data: { list: Prompt[]; total: number } = await assist.chat.prompt.list({pageSize:10,pageNum:1,keyWord:''}).catch(() => {
|
|
|
|
|
|
+ const data: { list: Prompt[]; total: number } = await assist.chat.prompt.list({ pageSize: 10, pageNum: 1, keyWord: '' }).catch(() => {
|
|
return {
|
|
return {
|
|
list: [],
|
|
list: [],
|
|
total: 0,
|
|
total: 0,
|
|
@@ -198,6 +213,9 @@ onMounted(loadPromptList)
|
|
|
|
|
|
const selectedModel = ref<number | undefined>(undefined)
|
|
const selectedModel = ref<number | undefined>(undefined)
|
|
|
|
|
|
|
|
+// 提示词管理对话框可见性
|
|
|
|
+const promptDialogVisible = ref(false)
|
|
|
|
+
|
|
const chatInstance = ref<(() => void) | undefined>(undefined)
|
|
const chatInstance = ref<(() => void) | undefined>(undefined)
|
|
onUnmounted(() => chatInstance.value?.())
|
|
onUnmounted(() => chatInstance.value?.())
|
|
// 是否正在对话
|
|
// 是否正在对话
|
|
@@ -324,8 +342,8 @@ const replaceMessage = async (index: number) => {
|
|
|
|
|
|
const chatInternal = (rtn: Message, context: Message[] = messages.value) => {
|
|
const chatInternal = (rtn: Message, context: Message[] = messages.value) => {
|
|
let r = [...context]
|
|
let r = [...context]
|
|
- if (selectPromptId.value != undefined) {
|
|
|
|
- const prompt = promptList.value.find((i) => i.id === selectPromptId.value)?.prompt ?? ''
|
|
|
|
|
|
+ if (selectPromptId.value != undefined && selectPromptId.value != -1) {
|
|
|
|
+ const prompt = displayPromptList.value.find((i) => i.id === selectPromptId.value)?.prompt ?? ''
|
|
//insert element on top
|
|
//insert element on top
|
|
r.unshift({
|
|
r.unshift({
|
|
id: messages.value.length,
|
|
id: messages.value.length,
|
|
@@ -743,30 +761,26 @@ const pageSizeOptions = [3, 10, 20, 30, 50, 100]
|
|
const maxPages = 100
|
|
const maxPages = 100
|
|
|
|
|
|
const exportToMarkDown = (message: Message) => {
|
|
const exportToMarkDown = (message: Message) => {
|
|
- try {
|
|
|
|
- // 创建CSV内容
|
|
|
|
- const csvContent = message.render_content
|
|
|
|
|
|
+ // 创建CSV内容
|
|
|
|
+ const csvContent = message.render_content
|
|
|
|
|
|
- // 创建Blob对象
|
|
|
|
- const blob = new Blob([csvContent], { type: 'text/markdown;charset=utf-8;' })
|
|
|
|
|
|
+ // 创建Blob对象
|
|
|
|
+ const blob = new Blob([csvContent], { type: 'text/markdown;charset=utf-8;' })
|
|
|
|
|
|
- // 创建下载链接
|
|
|
|
- const link = document.createElement('a')
|
|
|
|
- const url = URL.createObjectURL(blob)
|
|
|
|
- link.setAttribute('href', url)
|
|
|
|
- link.setAttribute('download', `export_${new Date().getTime()}.md`)
|
|
|
|
- link.style.visibility = 'hidden'
|
|
|
|
|
|
+ // 创建下载链接
|
|
|
|
+ const link = document.createElement('a')
|
|
|
|
+ const url = URL.createObjectURL(blob)
|
|
|
|
+ link.setAttribute('href', url)
|
|
|
|
+ link.setAttribute('download', `export_${new Date().getTime()}.md`)
|
|
|
|
+ link.style.visibility = 'hidden'
|
|
|
|
|
|
- // 触发下载
|
|
|
|
- document.body.appendChild(link)
|
|
|
|
- link.click()
|
|
|
|
- document.body.removeChild(link)
|
|
|
|
|
|
+ // 触发下载
|
|
|
|
+ document.body.appendChild(link)
|
|
|
|
+ link.click()
|
|
|
|
+ document.body.removeChild(link)
|
|
|
|
|
|
- // 清理URL对象
|
|
|
|
- URL.revokeObjectURL(url)
|
|
|
|
- } catch (error) {
|
|
|
|
- console.error('导出CSV失败:', error)
|
|
|
|
- }
|
|
|
|
|
|
+ // 清理URL对象
|
|
|
|
+ URL.revokeObjectURL(url)
|
|
}
|
|
}
|
|
|
|
|
|
// 导出对话历史
|
|
// 导出对话历史
|
|
@@ -837,6 +851,9 @@ const isBlank = (str: string) => {
|
|
<el-dropdown-item @click="redirectToModelManager">
|
|
<el-dropdown-item @click="redirectToModelManager">
|
|
<span class="settings-label">模型管理</span>
|
|
<span class="settings-label">模型管理</span>
|
|
</el-dropdown-item>
|
|
</el-dropdown-item>
|
|
|
|
+ <el-dropdown-item @click="promptDialogVisible = true">
|
|
|
|
+ <span class="settings-label">提示词管理</span>
|
|
|
|
+ </el-dropdown-item>
|
|
<el-dropdown-item @click="multiDeleteConversationModel.visible = true">
|
|
<el-dropdown-item @click="multiDeleteConversationModel.visible = true">
|
|
<span class="settings-label">对话管理</span>
|
|
<span class="settings-label">对话管理</span>
|
|
</el-dropdown-item>
|
|
</el-dropdown-item>
|
|
@@ -1119,8 +1136,8 @@ const isBlank = (str: string) => {
|
|
<el-dropdown trigger="click" placement="top-start" :disabled="loadingModels || modelLabel == '未配置模型'">
|
|
<el-dropdown trigger="click" placement="top-start" :disabled="loadingModels || modelLabel == '未配置模型'">
|
|
<button class="control-btn model-btn">
|
|
<button class="control-btn model-btn">
|
|
<el-icon>
|
|
<el-icon>
|
|
- <Setting v-if="!loadingModels"/>
|
|
|
|
- <Loading v-else class="spin"/>
|
|
|
|
|
|
+ <Setting v-if="!loadingModels" />
|
|
|
|
+ <Loading v-else class="spin" />
|
|
</el-icon>
|
|
</el-icon>
|
|
<span>{{ modelLabel }}</span>
|
|
<span>{{ modelLabel }}</span>
|
|
</button>
|
|
</button>
|
|
@@ -1142,8 +1159,8 @@ const isBlank = (str: string) => {
|
|
<el-dropdown trigger="click" placement="top-start" :disabled="loadingModels || embeddingModelLabel == '未配置词嵌入'">
|
|
<el-dropdown trigger="click" placement="top-start" :disabled="loadingModels || embeddingModelLabel == '未配置词嵌入'">
|
|
<button class="control-btn embedding-model-btn">
|
|
<button class="control-btn embedding-model-btn">
|
|
<el-icon>
|
|
<el-icon>
|
|
- <CopyDocument v-if="!loadingEmbeddingModels"/>
|
|
|
|
- <Loading v-else class="spin"/>
|
|
|
|
|
|
+ <CopyDocument v-if="!loadingEmbeddingModels" />
|
|
|
|
+ <Loading v-else class="spin" />
|
|
</el-icon>
|
|
</el-icon>
|
|
<span>{{ embeddingModelLabel }}</span>
|
|
<span>{{ embeddingModelLabel }}</span>
|
|
</button>
|
|
</button>
|
|
@@ -1165,15 +1182,15 @@ const isBlank = (str: string) => {
|
|
<el-dropdown trigger="click" placement="top-start" :disabled="loadingModels || promptLabel == '未配置提示词'">
|
|
<el-dropdown trigger="click" placement="top-start" :disabled="loadingModels || promptLabel == '未配置提示词'">
|
|
<button class="control-btn embedding-model-btn">
|
|
<button class="control-btn embedding-model-btn">
|
|
<el-icon>
|
|
<el-icon>
|
|
- <CopyDocument v-if="!loadingPromptList"/>
|
|
|
|
- <Loading v-else class="spin"/>
|
|
|
|
|
|
+ <CopyDocument v-if="!loadingPromptList" />
|
|
|
|
+ <Loading v-else class="spin" />
|
|
</el-icon>
|
|
</el-icon>
|
|
<span>{{ promptLabel }}</span>
|
|
<span>{{ promptLabel }}</span>
|
|
</button>
|
|
</button>
|
|
<template #dropdown>
|
|
<template #dropdown>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-menu>
|
|
<el-dropdown-item
|
|
<el-dropdown-item
|
|
- v-for="item in promptList"
|
|
|
|
|
|
+ v-for="item in displayPromptList"
|
|
:key="item.id"
|
|
:key="item.id"
|
|
@click="selectPromptId = item.id"
|
|
@click="selectPromptId = item.id"
|
|
:class="{ 'is-selected': selectPromptId === item.id }"
|
|
:class="{ 'is-selected': selectPromptId === item.id }"
|
|
@@ -1241,6 +1258,27 @@ const isBlank = (str: string) => {
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</el-main>
|
|
</el-main>
|
|
|
|
+
|
|
|
|
+ <!-- 提示词管理对话框 -->
|
|
|
|
+ <el-dialog v-model="promptDialogVisible" title="提示词管理" width="60%" append-to-body>
|
|
|
|
+ <div class="prompt-dialog-content">
|
|
|
|
+ <div class="prompt-input-section">
|
|
|
|
+ <h4>自定义提示词</h4>
|
|
|
|
+ <el-input
|
|
|
|
+ v-model="customPrompt"
|
|
|
|
+ type="textarea"
|
|
|
|
+ :autosize="{ minRows: 12, maxRows: 24 }"
|
|
|
|
+ placeholder="在此编写你的提示词...(将用于本次会话的系统提示)"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ <template #footer>
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
+ <el-button @click="promptDialogVisible = false">取 消</el-button>
|
|
|
|
+ <el-button type="primary" @click="promptDialogVisible = false">确 认</el-button>
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
</el-container>
|
|
</el-container>
|
|
</template>
|
|
</template>
|
|
|
|
|