From a061881d4ad6f9badab886355acb484be5f45e03 Mon Sep 17 00:00:00 2001 From: RockYang Date: Thu, 23 Mar 2023 15:11:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20markdown=20=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=A7=A3=E6=9E=90=E5=92=8C=E8=87=AA=E5=8A=A8=E9=AB=98?= =?UTF-8?q?=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- server/config_handler.go | 1 - web/package-lock.json | 124 ++++++++++++++++++++++++++++--- web/package.json | 2 + web/src/components/ChatReply.vue | 4 + web/src/main.js | 8 +- web/src/views/Chat.vue | 85 ++++++++++++++++----- web/src/views/Test.vue | 49 ++++++++++++ 8 files changed, 245 insertions(+), 31 deletions(-) create mode 100644 web/src/views/Test.vue diff --git a/README.md b/README.md index b1c2e062..e5ea5406 100644 --- a/README.md +++ b/README.md @@ -11,5 +11,6 @@ * [x] OpenAI API 负载均衡,限制每个 API Key 每分钟之内调用次数不超过 15次,防止被封 * [ ] 角色设定,预设一些角色,比如程序员,产品经理,医生,作家,老师... * [ ] markdown 语法解析 -* [ ] 用户配置界面 +* [ ] 用户配置界面,配置用户的使用习惯 +* [ ] 嵌入 AI 绘画功能,支持根据描述词生成图片 diff --git a/server/config_handler.go b/server/config_handler.go index 338d9423..eb1944b7 100644 --- a/server/config_handler.go +++ b/server/config_handler.go @@ -98,7 +98,6 @@ func (s *Server) ConfigSetHandle(c *gin.Context) { } // 保存配置文件 - logger.Infof("Config: %+v", s.Config) err = types.SaveConfig(s.Config, s.ConfigPath) if err != nil { c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Failed to save config file"}) diff --git a/web/package-lock.json b/web/package-lock.json index 7c45f7dd..e1f5a3c1 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -13,7 +13,9 @@ "core-js": "^3.8.3", "element-plus": "^2.1.11", "good-storage": "^1.1.1", + "highlight.js": "^11.7.0", "json-bigint": "^1.0.0", + "markdown-it": "^13.0.1", "qs": "^6.11.1", "vue": "^3.2.13", "vue-router": "^4.0.15" @@ -3962,6 +3964,15 @@ "node": ">=8" } }, + "node_modules/cli-highlight/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/cli-highlight/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", @@ -6228,12 +6239,11 @@ } }, "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true, + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==", "engines": { - "node": "*" + "node": ">=12.0.0" } }, "node_modules/hosted-git-info": { @@ -6901,6 +6911,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", @@ -7250,12 +7268,48 @@ "node": ">=8" } }, + "node_modules/markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", @@ -10081,6 +10135,11 @@ "node": ">= 0.6" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -14150,6 +14209,12 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true + }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", @@ -15957,10 +16022,9 @@ "dev": true }, "highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "dev": true + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.7.0.tgz", + "integrity": "sha512-1rRqesRFhMO/PRF+G86evnyJkCgaZFOI+Z6kdj15TA18funfoqJXvgPCLSf0SWq3SRfg1j3HlDs8o4s3EGq1oQ==" }, "hosted-git-info": { "version": "2.8.9", @@ -16490,6 +16554,14 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "requires": { + "uc.micro": "^1.0.1" + } + }, "loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz", @@ -16775,12 +16847,41 @@ "semver": "^6.0.0" } }, + "markdown-it": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", + "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", + "requires": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==" + } + } + }, "mdn-data": { "version": "2.0.14", "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz", "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", "dev": true }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz", @@ -18969,6 +19070,11 @@ "mime-types": "~2.1.24" } }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/web/package.json b/web/package.json index 90d3fc83..346cbde3 100644 --- a/web/package.json +++ b/web/package.json @@ -13,7 +13,9 @@ "core-js": "^3.8.3", "element-plus": "^2.1.11", "good-storage": "^1.1.1", + "highlight.js": "^11.7.0", "json-bigint": "^1.0.0", + "markdown-it": "^13.0.1", "qs": "^6.11.1", "vue": "^3.2.13", "vue-router": "^4.0.15" diff --git a/web/src/components/ChatReply.vue b/web/src/components/ChatReply.vue index 76bf9724..27ffbb4e 100644 --- a/web/src/components/ChatReply.vue +++ b/web/src/components/ChatReply.vue @@ -69,6 +69,10 @@ export default defineComponent({ font-size: var(--content-font-size); border-radius: 5px; + p:last-child { + margin-bottom: 0 + } + //.cursor { // height 24px; // border-left 1px solid black; diff --git a/web/src/main.js b/web/src/main.js index 2ca76f72..df20f0c9 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -5,6 +5,7 @@ import "element-plus/dist/index.css" import App from './App.vue' import Chat from './views/Chat.vue' import NotFound from './views/404.vue' +import TestPage from './views/Test.vue' import './utils/prototype' import "./assets/css/bootstrap.min.css" import {Global} from "@/utils/storage"; @@ -14,7 +15,12 @@ Global['Chat'] = Chat const routes = [ { name: 'home', path: '/', component: Chat, meta: { - title: 'ChatGPT-Console' + title: 'WeChat-GPT' + } + }, + { + name: 'test', path: '/test', component: TestPage, meta: { + title: '测试页面' } }, { diff --git a/web/src/views/Chat.vue b/web/src/views/Chat.vue index 3e38d37d..784b6318 100644 --- a/web/src/views/Chat.vue +++ b/web/src/views/Chat.vue @@ -6,7 +6,7 @@ 欢迎来到人工智能时代 -
+
{\n" + + // " console.log('WebSocket 连接已打开');\n" + + // "\n" + + // " // 发送消息\n" + + // " socket.send('Hello WebSocket!');\n" + + // "});\n" + + // "\n" + + // "// 监听 WebSocket 接收到消息事件\n" + + // "socket.addEventListener('message', (event) => {\n" + + // " console.log('接收到消息:' + event.data);\n" + + // "});\n" + + // "\n" + + // "// 监听 WebSocket 连接关闭事件\n" + + // "socket.addEventListener('close', (event) => {\n" + + // " console.log('WebSocket 连接已关闭');\n" + + // "});\n" + + // "\n" + + // "// 监听 WebSocket 出错事件\n" + + // "socket.addEventListener('error', (event) => {\n" + + // " console.log('WebSocket 连接出错');\n" + + // "});\n" + + // "```\n" + + // "\n" + + // "在实际使用时,需要替换上述代码中的 WebSocket 连接地址和端口号。此外,根据后端的实现,可能需要在客户端发送的消息中携带一些特定信息,以便后端能够正确地处理这些消息。", // }); // } + // + // let md = require('markdown-it')(); + // this.chatData[this.chatData.length - 1]["content"] = md.render(this.chatData[this.chatData.length - 1]["content"]); + // + // nextTick(() => { + // const lines = document.querySelectorAll('.chat-line'); + // const blocks = lines[lines.length - 1].querySelectorAll('pre code'); + // blocks.forEach((block) => { + // hl.highlightElement(block) + // }) + // }) window.addEventListener("resize", () => { this.chatBoxHeight = window.innerHeight - this.toolBoxHeight; }); + + }, methods: { @@ -141,7 +186,7 @@ export default defineComponent({ this.connect(); } // 发送心跳 - setTimeout(() => this.checkSession(), 5000); + //setTimeout(() => this.checkSession(), 5000); }).catch((res) => { if (res.code === 400) { this.showLoginDialog = true; @@ -173,9 +218,8 @@ export default defineComponent({ socket.addEventListener('open', () => { ElMessage.success('创建会话成功!'); - if (this.connectingMessageBox != null) { + if (this.connectingMessageBox && typeof this.connectingMessageBox.close === 'function') { this.connectingMessageBox.close(); - this.connectingMessageBox = null; } }); @@ -195,16 +239,19 @@ export default defineComponent({ }); } else if (data.type === 'end') { this.sending = false; + this.lineBuffer = ''; // 清空缓冲 } else { - let content = data.content; - // 替换换行符 - if (content.indexOf("\n\n") >= 0) { - if (this.chatData[this.chatData.length - 1]["content"].length === 0) { - return - } - content = content.replace("\n\n", "
"); - } - this.chatData[this.chatData.length - 1]["content"] += content; + this.lineBuffer += data.content; + let md = require('markdown-it')(); + this.chatData[this.chatData.length - 1]["content"] = md.render(this.lineBuffer); + + nextTick(() => { + const lines = document.querySelectorAll('.chat-line'); + const blocks = lines[lines.length - 1].querySelectorAll('pre code'); + blocks.forEach((block) => { + hl.highlightElement(block) + }) + }) } // 将聊天框的滚动条滑动到最底部 nextTick(() => { diff --git a/web/src/views/Test.vue b/web/src/views/Test.vue new file mode 100644 index 00000000..00abfbe1 --- /dev/null +++ b/web/src/views/Test.vue @@ -0,0 +1,49 @@ + + +