diff --git a/README.md b/README.md index 61622b89..cdcc1bce 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## TODOLIST -* [ ] 使用 level DB 保存用户聊天记录 +* [x] 使用 level DB 保存用户聊天记录 * [x] 用户聊天鉴权,设置口令模式 * [ ] 定期清理不在线的会话 sessionID 和聊天上下文记录 * [x] 给 Token 设置调用次数 diff --git a/server/chat_handler.go b/server/chat_handler.go index 42362e77..617a4885 100644 --- a/server/chat_handler.go +++ b/server/chat_handler.go @@ -341,3 +341,25 @@ func (s *Server) GetChatHistoryHandle(c *gin.Context) { c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Data: messages}) } + +// ClearHistoryHandle 清空聊天记录 +func (s *Server) ClearHistoryHandle(c *gin.Context) { + sessionId := c.GetHeader(types.TokenName) + var data struct { + Role string `json:"role"` + } + err := json.NewDecoder(c.Request.Body).Decode(&data) + if err != nil { + c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Invalid args"}) + return + } + + session := s.ChatSession[sessionId] + err = ClearChatHistory(session.Username, data.Role) + if err != nil { + c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Failed to remove data from DB"}) + return + } + + c.JSON(http.StatusOK, types.BizVo{Code: types.Success}) +} diff --git a/server/config_handler.go b/server/config_handler.go index 1b96c984..40512288 100644 --- a/server/config_handler.go +++ b/server/config_handler.go @@ -291,6 +291,7 @@ func (s *Server) GetChatRoleListHandle(c *gin.Context) { }) } } + c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Message: types.OkMsg, Data: res}) } diff --git a/server/server.go b/server/server.go index 5576bfa1..125e6ab0 100644 --- a/server/server.go +++ b/server/server.go @@ -83,6 +83,7 @@ func (s *Server) Run(webRoot embed.FS, path string, debug bool) { engine.POST("/api/login", s.LoginHandle) engine.Any("/api/chat", s.ChatHandle) engine.POST("api/chat/history", s.GetChatHistoryHandle) + engine.POST("api/chat/history/clear", s.ClearHistoryHandle) engine.POST("/api/config/set", s.ConfigSetHandle) engine.GET("/api/config/chat-roles/get", s.GetChatRoleListHandle) @@ -157,7 +158,7 @@ func corsMiddleware() gin.HandlerFunc { c.Header("Access-Control-Allow-Origin", origin) c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") //允许跨域设置可以返回其他子段,可以自定义字段 - c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, ChatGPT-Username") + c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, ChatGPT-TOKEN") // 允许浏览器(客户端)可以解析的头部 (重要) c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers") //设置缓存时间 diff --git a/web/src/views/Chat.vue b/web/src/views/Chat.vue index 1b740380..249b380f 100644 --- a/web/src/views/Chat.vue +++ b/web/src/views/Chat.vue @@ -3,7 +3,7 @@
- 欢迎来到人工智能时代 + @@ -14,6 +14,12 @@ :value="item.key" /> + + + + + +
@@ -89,7 +95,7 @@ import ChatPrompt from "@/components/ChatPrompt.vue"; import ChatReply from "@/components/ChatReply.vue"; import {randString} from "@/utils/libs"; import {ElMessage, ElMessageBox} from 'element-plus' -import {Tools, Lock} from '@element-plus/icons-vue' +import {Tools, Lock, Delete} from '@element-plus/icons-vue' import ConfigDialog from '@/components/ConfigDialog.vue' import {httpPost, httpGet} from "@/utils/http"; import {getSessionId, setSessionId} from "@/utils/storage"; @@ -98,7 +104,7 @@ import 'highlight.js/styles/a11y-dark.css' export default defineComponent({ name: "XChat", - components: {ChatPrompt, ChatReply, Tools, Lock, ConfigDialog}, + components: {ChatPrompt, ChatReply, Tools, Lock, Delete, ConfigDialog}, data() { return { title: 'ChatGPT 控制台', @@ -118,7 +124,7 @@ export default defineComponent({ socket: null, toolBoxHeight: 61 + 42, // 工具框的高度 inputBoxWidth: window.innerWidth - 20, - sending: true, + sending: false, loading: false } }, @@ -189,9 +195,6 @@ export default defineComponent({ }); this.connect(); - - this.fetchChatHistory(); - }, methods: { @@ -203,13 +206,16 @@ export default defineComponent({ socket.addEventListener('open', () => { // 获取聊天角色 httpGet("/api/config/chat-roles/get").then((res) => { - ElMessage.success('创建会话成功!'); + // ElMessage.success('创建会话成功!'); this.chatRoles = res.data; this.loading = false }).catch(() => { ElMessage.error("获取聊天角色失败"); }) + // 加载聊天记录 + this.fetchChatHistory(); + if (this.connectingMessageBox && typeof this.connectingMessageBox.close === 'function') { this.connectingMessageBox.close(); this.connectingMessageBox = null; @@ -303,16 +309,33 @@ export default defineComponent({ break; } } - - this.fetchChatHistory(); }, // 从后端获取聊天历史记录 fetchChatHistory: function () { httpPost("/api/chat/history", {role: this.role}).then((res) => { - this.chatData = res.data - }).catch((e) => { - console.error(e.message) + const data = res.data + const md = require('markdown-it')(); + for (let i = 0; i < data.length; i++) { + if (data[i].type === "prompt") { + this.chatData.push(data[i]); + continue; + } + + data[i].content = md.render(data[i].content); + this.chatData.push(data[i]); + } + + nextTick(() => { + hl.configure({ignoreUnescapedHTML: true}) + const lines = document.querySelectorAll('.chat-line'); + const blocks = lines[lines.length - 1].querySelectorAll('pre code'); + blocks.forEach((block) => { + hl.highlightElement(block) + }) + }) + }).catch(() => { + // console.error(e.message) }) }, @@ -390,6 +413,31 @@ export default defineComponent({ if (e.keyCode === 13) { this.submitToken(); } + }, + + // 清空聊天记录 + clearChatHistory: function () { + ElMessageBox.confirm( + '确认要清空当前角色聊天历史记录吗?
此操作不可以撤销!', + '操作提示:', + { + confirmButtonText: '确认', + cancelButtonText: '取消', + type: 'warning', + dangerouslyUseHTMLString: true, + showClose: true, + closeOnClickModal: false, + center: true, + } + ).then(() => { + httpPost("/api/chat/history/clear", {role: this.role}).then(() => { + ElMessage.success("当前角色会话已清空"); + this.chatData = []; + }).catch(() => { + ElMessage.error("删除失败") + }) + }).catch(() => { + }) } }, @@ -418,7 +466,7 @@ export default defineComponent({ align-items center; .el-select { - max-width 150px; + max-width 120px; } .chat-role { @@ -428,6 +476,10 @@ export default defineComponent({ .el-image { margin-right 5px; } + + .clear-history { + margin-left 5px; + } } .chat-box {