diff --git a/README.md b/README.md
index 877507cc..99391c43 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
```bash
cd chat; cd api
+go install github.com/cosmtrek/air@latest
go mod tidy
# export env var, change base on your env
export PG_HOST=192.168.0.135
diff --git a/web/src/assets/recommend.json b/web/src/assets/recommend.json
new file mode 100644
index 00000000..71f62a6b
--- /dev/null
+++ b/web/src/assets/recommend.json
@@ -0,0 +1,8 @@
+[
+ {
+ "key": "awesome-chatgpt-prompts-zh",
+ "desc": "ChatGPT 中文调教指南",
+ "downloadUrl": "https://raw.githubusercontent.com/Nothing1024/chatgpt-prompt-collection/main/awesome-chatgpt-prompts-zh.json",
+ "url": "https://github.com/PlexPt/awesome-chatgpt-prompts-zh"
+ }
+]
\ No newline at end of file
diff --git a/web/src/components/common/PromptStore/index.vue b/web/src/components/common/PromptStore/index.vue
new file mode 100644
index 00000000..f2a8eee1
--- /dev/null
+++ b/web/src/components/common/PromptStore/index.vue
@@ -0,0 +1,429 @@
+
+
+
+
+
+
+
+
+
+
+
+ 添加
+
+
+ 导入
+
+
+ 导出
+
+
+
+
+ 清空
+
+
+ 确认是否清空数据?
+
+
+
+
+
+
+ 注意:请检查下载 JSON 文件来源,恶意的JSON文件可能会破坏您的计算机!
+
+
+
+
+
+
+ 下载
+
+
+
+
+
+
+ {{ info.desc }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ info.desc }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 模板标题
+
+ 模板内容
+
+ { modalMode === 'add' ? addPromptTemplate() : modifyPromptTemplate() }"
+ >
+ 确定
+
+
+
+
+ { importPromptTemplate() }"
+ >
+ 导入
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web/src/components/common/index.ts b/web/src/components/common/index.ts
index 16b6d108..d8f03ec6 100644
--- a/web/src/components/common/index.ts
+++ b/web/src/components/common/index.ts
@@ -3,5 +3,6 @@ import NaiveProvider from './NaiveProvider/index.vue'
import SvgIcon from './SvgIcon/index.vue'
import UserAvatar from './UserAvatar/index.vue'
import Setting from './Setting/index.vue'
+import PromptStore from './PromptStore/index.vue'
-export { HoverButton, NaiveProvider, SvgIcon, UserAvatar, Setting }
+export { HoverButton, NaiveProvider, SvgIcon, UserAvatar, Setting, PromptStore }
diff --git a/web/src/locales/en-US.json b/web/src/locales/en-US.json
index 5bdfb591..4212bf0d 100644
--- a/web/src/locales/en-US.json
+++ b/web/src/locales/en-US.json
@@ -75,7 +75,7 @@
"maxTokens": "Max Total Tokens : {maxTokens}",
"model": "Model (OpenAI GPT4 needs separate application, Claude key can use all Claude models)",
"new": "New Chat",
- "placeholder": "What would you like to say... (Shift + Enter = newline)",
+ "placeholder": "What would you like to say... (Shift + Enter = newline, '/' to trigger prompts)",
"placeholderMobile": "What would you like to say...",
"presencePenalty": "Presence Penalty",
"sessionConfig": "Conversation Settings:",
diff --git a/web/src/locales/zh-CN.json b/web/src/locales/zh-CN.json
index 1b2485c2..086e6011 100644
--- a/web/src/locales/zh-CN.json
+++ b/web/src/locales/zh-CN.json
@@ -34,7 +34,7 @@
},
"chat": {
"new": "新对话",
- "placeholder": "来说点什么吧...(Shift + Enter = 换行)",
+ "placeholder": "来说点什么吧...(Shift + Enter = 换行, '/' 触发提示词)",
"placeholderMobile": "来说点什么...",
"copy": "复制",
"copied": "复制成功",
diff --git a/web/src/locales/zh-TW.json b/web/src/locales/zh-TW.json
index 7a081bb4..08029fbd 100644
--- a/web/src/locales/zh-TW.json
+++ b/web/src/locales/zh-TW.json
@@ -75,7 +75,7 @@
"maxTokens": "最大問答總token數量: {maxTokens}",
"model": "模型(OPENAI GPT4 需要個別申請,CLAUDE的免費密鑰可以使用所有CLAUDE模型)",
"new": "新建聊天",
- "placeholder": "說些什麼...(Shift + Enter = 換行)",
+ "placeholder": "說些什麼...(Shift + Enter = 換行, '/' 触发提示词)",
"placeholderMobile": "說些什麼...",
"presencePenalty": "存在懲罰",
"sessionConfig": "會話配置",
diff --git a/web/src/store/modules/index.ts b/web/src/store/modules/index.ts
index b33b51b5..c6ff33c6 100644
--- a/web/src/store/modules/index.ts
+++ b/web/src/store/modules/index.ts
@@ -2,3 +2,4 @@ export * from './app'
export * from './chat'
export * from './user'
export * from './auth'
+export * from './prompt'
diff --git a/web/src/store/modules/prompt/helper.ts b/web/src/store/modules/prompt/helper.ts
new file mode 100644
index 00000000..973438d3
--- /dev/null
+++ b/web/src/store/modules/prompt/helper.ts
@@ -0,0 +1,18 @@
+import { ss } from '@/utils/storage'
+
+const LOCAL_NAME = 'promptStore'
+
+export type PromptList = []
+
+export interface PromptStore {
+ promptList: PromptList
+}
+
+export function getLocalPromptList(): PromptStore {
+ const promptStore: PromptStore | undefined = ss.get(LOCAL_NAME)
+ return promptStore ?? { promptList: [] }
+}
+
+export function setLocalPromptList(promptStore: PromptStore): void {
+ ss.set(LOCAL_NAME, promptStore)
+}
\ No newline at end of file
diff --git a/web/src/store/modules/prompt/index.ts b/web/src/store/modules/prompt/index.ts
new file mode 100644
index 00000000..869400f3
--- /dev/null
+++ b/web/src/store/modules/prompt/index.ts
@@ -0,0 +1,17 @@
+import { defineStore } from 'pinia'
+import type { PromptStore } from './helper'
+import { getLocalPromptList, setLocalPromptList } from './helper'
+
+export const usePromptStore = defineStore('prompt-store', {
+ state: (): PromptStore => getLocalPromptList(),
+
+ actions: {
+ updatePromptList(promptList: []) {
+ this.$patch({ promptList })
+ setLocalPromptList({ promptList })
+ },
+ getPromptList() {
+ return this.$state
+ },
+ },
+})
\ No newline at end of file
diff --git a/web/src/views/chat/index.vue b/web/src/views/chat/index.vue
index 1d030a6b..d72eadaf 100644
--- a/web/src/views/chat/index.vue
+++ b/web/src/views/chat/index.vue
@@ -2,7 +2,9 @@
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { v4 as uuidv4 } from 'uuid'
import { useRoute } from 'vue-router'
-import { NButton, NInput, NModal, useDialog, useMessage } from 'naive-ui'
+import { NModal } from 'naive-ui'
+import { storeToRefs } from 'pinia'
+import { NAutoComplete, NButton, NInput, useDialog, useMessage } from 'naive-ui'
import html2canvas from 'html2canvas'
import { Message } from './components'
import { useScroll } from './hooks/useScroll'
@@ -13,7 +15,7 @@ import SessionConfig from './components/Session/SessionConfig.vue'
import { createChatSnapshot, fetchChatStream, updateChatData } from '@/api'
import { HoverButton, SvgIcon } from '@/components/common'
import { useBasicLayout } from '@/hooks/useBasicLayout'
-import { useChatStore } from '@/store'
+import { useChatStore,usePromptStore } from '@/store'
import { t } from '@/locales'
import { genTempDownloadLink } from '@/utils/download'
let controller = new AbortController()
@@ -38,8 +40,40 @@ const chatSession = computed(() => chatStore.getChatSessionByUuid(uuid))
const prompt = ref('')
const loading = ref(false)
+
const showModal = ref(false)
+// 添加PromptStore
+const promptStore = usePromptStore()
+// 使用storeToRefs,保证store修改后,联想部分能够重新渲染
+const { promptList: promptTemplate } = storeToRefs(promptStore)
+
+
+// 可优化部分
+// 搜索选项计算,这里使用value作为索引项,所以当出现重复value时渲染异常(多项同时出现选中效果)
+// 理想状态下其实应该是key作为索引项,但官方的renderOption会出现问题,所以就需要value反renderLabel实现
+const searchOptions = computed(() => {
+ if (prompt.value.startsWith('/')) {
+ return promptTemplate.value.filter((item: { key: string }) => item.key.toLowerCase().includes(prompt.value.substring(1).toLowerCase())).map((obj: { value: any }) => {
+ return {
+ label: obj.value,
+ value: obj.value,
+ }
+ })
+ }
+ else {
+ return []
+ }
+})
+// value反渲染key
+const renderOption = (option: { label: string }) => {
+ for (const i of promptTemplate.value) {
+ if (i.value === option.label)
+ return [i.key]
+ }
+ return []
+}
+
function handleSubmit() {
onConversationStream()
}
@@ -510,10 +544,15 @@ function getDataFromResponseText(responseText: string): string {
-
+
+
+
+
+
import type { CSSProperties } from 'vue'
-import { computed, watch } from 'vue'
+import { computed, watch, ref } from 'vue'
import { NButton, NLayoutSider } from 'naive-ui'
import List from './List.vue'
@@ -10,11 +10,13 @@ import { useBasicLayout } from '@/hooks/useBasicLayout'
import { t } from '@/locales'
import { SvgIcon } from '@/components/common'
import { getChatSessionDefault } from '@/api'
+import { PromptStore } from '@/components/common'
const appStore = useAppStore()
const chatStore = useChatStore()
const { isMobile } = useBasicLayout()
+const show = ref(false)
const collapsed = computed(() => appStore.siderCollapsed)
@@ -88,6 +90,11 @@ watch(
+
+
+ Prompt Store
+
+
@@ -95,4 +102,5 @@ watch(
+