From 78db3fc9e4ce680733506221ebd575816bd2566c Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Thu, 25 Dec 2025 12:33:12 +0800 Subject: [PATCH] =?UTF-8?q?=E8=81=8A=E5=A4=A9=E5=AE=A4=E6=9B=B4=E6=96=B0ma?= =?UTF-8?q?rkdown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- urbanLifelineWeb/package-lock.json | 159 ++++++++++++++++-- .../chatRoom/chatRoom/ChatRoom.scss | 83 +++++++++ .../components/chatRoom/chatRoom/ChatRoom.vue | 54 +++++- .../src/views/public/AIChat/AIChatView.scss | 107 +++++++++++- .../src/views/public/AIChat/AIChatView.vue | 92 +++++++++- .../pages/chatRoom/chatRoom/chatRoom.uvue | 38 ++++- .../chatRoom/chatRoomList/chatRoomList.uvue | 40 ++++- .../workcase_wechat/pages/index/index.uvue | 38 ++++- .../project.private.config.json | 2 +- 9 files changed, 578 insertions(+), 35 deletions(-) diff --git a/urbanLifelineWeb/package-lock.json b/urbanLifelineWeb/package-lock.json index 132c5333..344e4a22 100644 --- a/urbanLifelineWeb/package-lock.json +++ b/urbanLifelineWeb/package-lock.json @@ -1784,6 +1784,12 @@ "resolved": "packages/shared", "link": true }, + "node_modules/@stomp/stompjs": { + "version": "7.2.1", + "resolved": "https://registry.npmmirror.com/@stomp/stompjs/-/stompjs-7.2.1.tgz", + "integrity": "sha512-DLd/WeicnHS5SsWWSk3x6/pcivqchNaEvg9UEGVqAcfYEBVmS9D6980ckXjTtfpXLjdLDsd96M7IuX4w7nzq5g==", + "license": "Apache-2.0" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", @@ -1816,6 +1822,13 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/sockjs-client": { + "version": "1.5.4", + "resolved": "https://registry.npmmirror.com/@types/sockjs-client/-/sockjs-client-1.5.4.tgz", + "integrity": "sha512-zk+uFZeWyvJ5ZFkLIwoGA/DfJ+pYzcZ8eH4H/EILCm2OBZyHH6Hkdna1/UWL/CFruh5wj6ES7g75SvUB0VsH5w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.20", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", @@ -1834,6 +1847,12 @@ "resolved": "packages/workcase", "link": true }, + "node_modules/@vant/weapp": { + "version": "1.11.7", + "resolved": "https://registry.npmmirror.com/@vant/weapp/-/weapp-1.11.7.tgz", + "integrity": "sha512-Rwn9BBnb4kHSV4XmvBicwtd42J+amEUfnFDcXJsGNPNX4a9c/DoT6YLsm4X1wB2+sQbdiQsbFBLAvGRBxCkD8g==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.4", "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", @@ -2363,9 +2382,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001759", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001759.tgz", - "integrity": "sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==", + "version": "1.0.30001761", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", "dev": true, "funding": [ { @@ -2925,6 +2944,15 @@ "node": ">= 0.6" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "4.22.1", "resolved": "https://registry.npmmirror.com/express/-/express-4.22.1.tgz", @@ -2986,6 +3014,18 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", @@ -3322,6 +3362,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3934,6 +3980,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", @@ -3982,6 +4034,12 @@ "node": ">=0.10.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz", @@ -4824,6 +4882,34 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sockjs-client": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.6.1.tgz", + "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^2.0.2", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4952,6 +5038,10 @@ "node": ">=16.0.0" } }, + "node_modules/taihao-service-miniprogram": { + "resolved": "packages/wechat_demo", + "link": true + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -5089,6 +5179,16 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", @@ -5275,6 +5375,29 @@ "typescript": ">=5.0.0" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", @@ -5291,6 +5414,10 @@ "node": ">= 8" } }, + "node_modules/workcase_wechat": { + "resolved": "packages/workcase_wechat", + "link": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -5423,11 +5550,13 @@ "version": "1.0.0", "dependencies": { "@element-plus/icons-vue": "^2.3.2", + "@stomp/stompjs": "^7.2.1", "cors": "^2.8.5", "element-plus": "^2.12.0", "express": "^4.18.2", "lucide-vue-next": "^0.561.0", "ofetch": "^1.4.1", + "sockjs-client": "^1.6.1", "vue": "^3.5.13", "vue-router": "^4.5.0" }, @@ -6899,22 +7028,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "packages/wechat_demo": { + "name": "taihao-service-miniprogram", + "version": "1.0.0", + "dependencies": { + "@vant/weapp": "^1.11.7" + } + }, "packages/workcase": { "name": "@urbanlifeline/workcase", "version": "1.0.0", "dependencies": { "@element-plus/icons-vue": "^2.3.2", + "@stomp/stompjs": "^7.2.1", "@vueuse/core": "^11.3.0", "axios": "^1.7.9", "element-plus": "^2.8.6", "lucide-vue-next": "^0.561.0", "pinia": "^2.2.8", + "sockjs-client": "^1.6.1", "vue": "^3.5.13", "vue-router": "^4.5.0" }, "devDependencies": { "@module-federation/vite": "^1.9.3", "@types/node": "^22.0.0", + "@types/sockjs-client": "^1.5.4", "@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue-jsx": "^4.1.1", "typescript": "^5.7.2", @@ -6922,18 +7061,6 @@ "vue-tsc": "^2.2.0" } }, - "packages/workcase_wechat": { - "name": "workcase-wechat", - "version": "1.0.0", - "extraneous": true, - "dependencies": { - "vue": "^3.0.0" - }, - "devDependencies": { - "@dcloudio/uni-cli-shared": "latest", - "@dcloudio/vite-plugin-uni": "latest", - "vite": "latest" - } - } + "packages/workcase_wechat": {} } } diff --git a/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.scss b/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.scss index d911a6de..add89690 100644 --- a/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.scss +++ b/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.scss @@ -345,3 +345,86 @@ $brand-color-hover: #004488; } } } + +// ==================== Markdown样式 ==================== +.message-bubble { + // 粗体 + strong { + font-weight: 600; + color: inherit; + } + + // 斜体 + em { + font-style: italic; + } + + // 行内代码 + .inline-code { + background: rgba(0, 0, 0, 0.05); + padding: 2px 6px; + border-radius: 4px; + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-size: 13px; + color: #e53e3e; + } + + // 代码块 + .code-block { + background: rgba(0, 0, 0, 0.05); + padding: 12px; + border-radius: 8px; + overflow-x: auto; + margin: 8px 0; + + code { + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-size: 13px; + line-height: 1.5; + color: #334155; + } + } + + // 链接 + .md-link { + color: $brand-color; + text-decoration: underline; + + &:hover { + color: $brand-color-hover; + } + } + + // 标题 + .md-h1 { + font-size: 20px; + font-weight: 700; + margin: 12px 0 8px; + color: inherit; + } + + .md-h2 { + font-size: 18px; + font-weight: 600; + margin: 10px 0 6px; + color: inherit; + } + + .md-h3 { + font-size: 16px; + font-weight: 600; + margin: 8px 0 4px; + color: inherit; + } + + // 列表 + .md-ul, .md-ol { + margin: 8px 0; + padding-left: 20px; + } + + .md-li { + margin: 4px 0; + line-height: 1.6; + } +} diff --git a/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.vue b/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.vue index f6e1ee31..1cd20ce0 100644 --- a/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.vue +++ b/urbanLifelineWeb/packages/workcase/src/components/chatRoom/chatRoom/ChatRoom.vue @@ -40,7 +40,10 @@
-

{{ message.content }}

+
@@ -235,14 +238,59 @@ const formatTime = (time?: string) => { const date = new Date(time) const now = new Date() const diff = now.getTime() - date.getTime() - + if (diff < 60000) return '刚刚' if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前' if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前' - + return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' }) } +// Markdown渲染函数 +const renderMarkdown = (text: string): string => { + if (!text) return '' + + // 转义HTML标签 + let html = text + .replace(/&/g, '&') + .replace(//g, '>') + + // 处理代码块(```语法) + html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => { + return `
${code.trim()}
` + }) + + // 处理行内代码(`语法) + html = html.replace(/`([^`]+)`/g, '$1') + + // 处理粗体(**语法) + html = html.replace(/\*\*([^\*]+)\*\*/g, '$1') + + // 处理斜体(*语法) + html = html.replace(/\*([^\*]+)\*/g, '$1') + + // 处理链接([text](url)语法) + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1') + + // 处理标题(# ## ###等) + html = html.replace(/^### (.+)$/gm, '

$1

') + html = html.replace(/^## (.+)$/gm, '

$1

') + html = html.replace(/^# (.+)$/gm, '

$1

') + + // 处理无序列表(- 或 * 开头) + html = html.replace(/^[*-] (.+)$/gm, '
  • $1
  • ') + html = html.replace(/(
  • .*<\/li>)/s, '
      $1
    ') + + // 处理有序列表(数字. 开头) + html = html.replace(/^\d+\. (.+)$/gm, '
  • $1
  • ') + + // 处理换行 + html = html.replace(/\n/g, '
    ') + + return html +} + // 暴露方法给父组件 defineExpose({ scrollToBottom diff --git a/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.scss b/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.scss index e636a50e..009b6151 100644 --- a/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.scss +++ b/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.scss @@ -361,7 +361,6 @@ $brand-color-hover: #004488; // ==================== 消息列表 ==================== .messages-list { - max-width: 768px; margin: 0 auto; padding: 24px 16px; @@ -485,7 +484,6 @@ $brand-color-hover: #004488; background: #fff; .quick-bar-inner { - max-width: 768px; margin: 0 auto; display: flex; gap: 8px; @@ -528,7 +526,6 @@ $brand-color-hover: #004488; background: #f8fafc; .input-wrapper { - max-width: 768px; margin: 0 auto; } @@ -616,3 +613,107 @@ $brand-color-hover: #004488; margin: 12px 0 0; } } + +// ==================== Markdown样式 ==================== +.message-bubble { + // 粗体 + strong { + font-weight: 600; + color: inherit; + } + + // 斜体 + em { + font-style: italic; + } + + // 行内代码 + .inline-code { + background: rgba(0, 0, 0, 0.05); + padding: 2px 6px; + border-radius: 4px; + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-size: 13px; + color: #e53e3e; + } + + // 代码块 + .code-block { + background: rgba(0, 0, 0, 0.05); + padding: 12px; + border-radius: 8px; + overflow-x: auto; + margin: 8px 0; + + code { + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + font-size: 13px; + line-height: 1.5; + color: #334155; + } + } + + // 链接 + .md-link { + color: $brand-color; + text-decoration: underline; + + &:hover { + color: $brand-color-hover; + } + } + + // 标题 + .md-h1 { + font-size: 20px; + font-weight: 700; + margin: 12px 0 8px; + color: inherit; + } + + .md-h2 { + font-size: 18px; + font-weight: 600; + margin: 10px 0 6px; + color: inherit; + } + + .md-h3 { + font-size: 16px; + font-weight: 600; + margin: 8px 0 4px; + color: inherit; + } + + // 列表 + .md-ul, .md-ol { + margin: 8px 0; + padding-left: 20px; + } + + .md-li { + margin: 4px 0; + line-height: 1.6; + } +} + +// 用户消息中的markdown样式(白色背景) +.message-bubble.user { + .inline-code { + background: rgba(255, 255, 255, 0.2); + color: #fff; + } + + .code-block { + background: rgba(255, 255, 255, 0.15); + + code { + color: #fff; + } + } + + .md-link { + color: #fff; + text-decoration: underline; + } +} diff --git a/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.vue b/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.vue index c10f1fac..ab8cf24a 100644 --- a/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.vue +++ b/urbanLifelineWeb/packages/workcase/src/views/public/AIChat/AIChatView.vue @@ -59,7 +59,7 @@ >
    -
    {{ conv.title || '新对话' }}
    +
    {{ getPlainTextPreview(conv.title || '新对话') }}
    {{ formatTime(conv.createTime) }}