From f294ac649c865f514259fcf7a6af61cb585469e2 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Mon, 4 May 2026 20:09:11 +0800 Subject: [PATCH] feat(web): chat views (Home/Detail/CreateModal) + Bubble/Uploader/ModelSelector + markdown-it --- web/package.json | 2 + web/pnpm-lock.yaml | 71 +++++++ web/src/views/chat/ChatDetailView.vue | 196 ++++++++++++++++++ web/src/views/chat/ChatHomeView.vue | 7 + .../views/chat/ConversationCreateModal.vue | 142 +++++++++++++ .../chat/components/AttachmentUploader.vue | 90 ++++++++ .../views/chat/components/MessageBubble.vue | 80 +++++++ .../views/chat/components/ModelSelector.vue | 53 +++++ 8 files changed, 641 insertions(+) create mode 100644 web/src/views/chat/ChatDetailView.vue create mode 100644 web/src/views/chat/ChatHomeView.vue create mode 100644 web/src/views/chat/ConversationCreateModal.vue create mode 100644 web/src/views/chat/components/AttachmentUploader.vue create mode 100644 web/src/views/chat/components/MessageBubble.vue create mode 100644 web/src/views/chat/components/ModelSelector.vue diff --git a/web/package.json b/web/package.json index a33be40..58ea92b 100644 --- a/web/package.json +++ b/web/package.json @@ -19,6 +19,7 @@ "license": "ISC", "packageManager": "pnpm@10.32.1", "dependencies": { + "markdown-it": "^14.1.1", "naive-ui": "^2.44.1", "pinia": "^3.0.4", "vue": "^3.5.33", @@ -27,6 +28,7 @@ "devDependencies": { "@eslint/js": "^10.0.1", "@tailwindcss/vite": "^4.2.4", + "@types/markdown-it": "^14.1.2", "@types/node": "^25.6.0", "@vitejs/plugin-vue": "^6.0.6", "@vue/test-utils": "^2.4.9", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 7dfc905..54b179d 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + markdown-it: + specifier: ^14.1.1 + version: 14.1.1 naive-ui: specifier: ^2.44.1 version: 2.44.1(vue@3.5.33(typescript@6.0.3)) @@ -27,6 +30,9 @@ importers: '@tailwindcss/vite': specifier: ^4.2.4 version: 4.2.4(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.8.3)) + '@types/markdown-it': + specifier: ^14.1.2 + version: 14.1.2 '@types/node': specifier: ^25.6.0 version: 25.6.0 @@ -435,12 +441,21 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} '@types/lodash@4.17.24': resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/node@25.6.0': resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} @@ -659,6 +674,9 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -812,6 +830,10 @@ packages: resolution: {integrity: sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==} engines: {node: '>=10.13.0'} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -1117,6 +1139,9 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + local-pkg@1.1.2: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} @@ -1141,6 +1166,13 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + markdown-it@14.1.1: + resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} + hasBin: true + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -1277,6 +1309,10 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1406,6 +1442,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.3: resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} @@ -1910,12 +1949,21 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/linkify-it@5.0.0': {} + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.24 '@types/lodash@4.17.24': {} + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdurl@2.0.0': {} + '@types/node@25.6.0': dependencies: undici-types: 7.19.2 @@ -2217,6 +2265,8 @@ snapshots: ansi-styles@6.2.3: {} + argparse@2.0.1: {} + assertion-error@2.0.1: {} ast-kit@2.2.0: @@ -2348,6 +2398,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 + entities@4.5.0: {} + entities@7.0.1: {} es-module-lexer@2.1.0: {} @@ -2620,6 +2672,10 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + local-pkg@1.1.2: dependencies: mlly: 1.8.2 @@ -2644,6 +2700,17 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-it@14.1.1: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + mdurl@2.0.0: {} + minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 @@ -2783,6 +2850,8 @@ snapshots: proto-list@1.2.4: {} + punycode.js@2.3.1: {} + punycode@2.3.1: {} quansync@0.2.11: {} @@ -2901,6 +2970,8 @@ snapshots: typescript@6.0.3: {} + uc.micro@2.1.0: {} + ufo@1.6.3: {} undici-types@7.19.2: {} diff --git a/web/src/views/chat/ChatDetailView.vue b/web/src/views/chat/ChatDetailView.vue new file mode 100644 index 0000000..41472aa --- /dev/null +++ b/web/src/views/chat/ChatDetailView.vue @@ -0,0 +1,196 @@ + + + + + + + + 新建对话 + + + + {{ c.title || '(未命名)' }} + + + {{ formatTime(c.updated_at) }} + + + + 暂无对话 + + + + + + + 选择或新建会话 + + + + + + + + + + + 发送 + + + 中断 + + + + + + + + + + + + diff --git a/web/src/views/chat/ChatHomeView.vue b/web/src/views/chat/ChatHomeView.vue new file mode 100644 index 0000000..695f14c --- /dev/null +++ b/web/src/views/chat/ChatHomeView.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/web/src/views/chat/ConversationCreateModal.vue b/web/src/views/chat/ConversationCreateModal.vue new file mode 100644 index 0000000..30bbb54 --- /dev/null +++ b/web/src/views/chat/ConversationCreateModal.vue @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + 取消 + + + 创建 + + + + + + + diff --git a/web/src/views/chat/components/AttachmentUploader.vue b/web/src/views/chat/components/AttachmentUploader.vue new file mode 100644 index 0000000..66e114a --- /dev/null +++ b/web/src/views/chat/components/AttachmentUploader.vue @@ -0,0 +1,90 @@ + + + + + 添加附件 + + + + {{ a.filename }} ({{ Math.round(a.size_bytes / 1024) }} KB) + + × + + + + + + diff --git a/web/src/views/chat/components/MessageBubble.vue b/web/src/views/chat/components/MessageBubble.vue new file mode 100644 index 0000000..7e4da0d --- /dev/null +++ b/web/src/views/chat/components/MessageBubble.vue @@ -0,0 +1,80 @@ + + + + + + + {{ message.thinking }} + + + + + … + + + + + ⚠️ {{ message.error_message }} + + 重试 + + + + 已取消 + + 重试 + + + + in {{ message.prompt_tokens }} · out {{ message.completion_tokens ?? 0 }} + · think {{ message.thinking_tokens }} + + + + + + diff --git a/web/src/views/chat/components/ModelSelector.vue b/web/src/views/chat/components/ModelSelector.vue new file mode 100644 index 0000000..1751b38 --- /dev/null +++ b/web/src/views/chat/components/ModelSelector.vue @@ -0,0 +1,53 @@ + + + + + {{ capLabel }}({{ selected.context_window.toLocaleString() }} ctx) + + + + +
{{ message.thinking }}