diff --git a/package.json b/package.json index c9f8890..d15c345 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,16 @@ "@ant-design/icons-vue": "^7.0.1", "ant-design-vue": "~4.2.6", "ant-design-x-vue": "^1.3.2", + "highlight.js": "^11.11.1", + "markdown-it": "^14.1.0", + "markdown-it-highlightjs": "^4.2.0", "pinia": "^3.0.3", "vue": "^3.5.22", "vue-router": "^4.6.3" }, "devDependencies": { "@tsconfig/node22": "^22.0.2", + "@types/markdown-it": "^14.1.2", "@types/node": "^22.18.11", "@vitejs/plugin-vue": "^6.0.1", "@vue/eslint-config-prettier": "^10.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a1410a1..b6bff3a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,15 @@ importers: ant-design-x-vue: specifier: ^1.3.2 version: 1.3.2(ant-design-vue@4.2.6(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3)) + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 + markdown-it: + specifier: ^14.1.0 + version: 14.1.0 + markdown-it-highlightjs: + specifier: ^4.2.0 + version: 4.2.0 pinia: specifier: ^3.0.3 version: 3.0.3(typescript@5.9.3)(vue@3.5.22(typescript@5.9.3)) @@ -30,6 +39,9 @@ importers: '@tsconfig/node22': specifier: ^22.0.2 version: 22.0.2 + '@types/markdown-it': + specifier: ^14.1.2 + version: 14.1.2 '@types/node': specifier: ^22.18.11 version: 22.18.12 @@ -617,6 +629,15 @@ 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/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@22.18.12': resolution: {integrity: sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==} @@ -1155,6 +1176,10 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + hookable@5.5.3: resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} @@ -1259,6 +1284,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1282,6 +1310,16 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + markdown-it-highlightjs@4.2.0: + resolution: {integrity: sha512-NC7pXE8KkOl6xWJVRNt8p6wgJVznXKsE0HgYGdk6DD2tn1l4L9f0ALf3VIoGVkotNU1uGQatSxfBF1zZPUMmuQ==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} @@ -1434,6 +1472,10 @@ packages: engines: {node: '>=14'} hasBin: true + 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'} @@ -1567,6 +1609,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -2191,6 +2236,15 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/linkify-it@5.0.0': {} + + '@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@22.18.12': dependencies: undici-types: 6.21.0 @@ -2847,6 +2901,8 @@ snapshots: has-flag@4.0.0: {} + highlight.js@11.11.1: {} + hookable@5.5.3: {} ignore@5.3.2: {} @@ -2917,6 +2973,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -2939,6 +2999,21 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-it-highlightjs@4.2.0: + dependencies: + highlight.js: 11.11.1 + + markdown-it@14.1.0: + 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: {} + memorystream@0.3.1: {} merge2@1.4.1: {} @@ -3067,6 +3142,8 @@ snapshots: prettier@3.6.2: {} + punycode.js@2.3.1: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} @@ -3196,6 +3273,8 @@ snapshots: typescript@5.9.3: {} + uc.micro@2.1.0: {} + undici-types@6.21.0: {} unplugin-utils@0.3.1: diff --git a/src/views/AiChatPage.vue b/src/views/AiChatPage.vue index e47ba0c..63a4277 100644 --- a/src/views/AiChatPage.vue +++ b/src/views/AiChatPage.vue @@ -19,18 +19,14 @@ import { SmileOutlined, } from '@ant-design/icons-vue' import { Badge, Button, Flex, Space, theme } from 'ant-design-vue' -import { - Attachments, - Bubble, - Conversations, - Prompts, - Sender, - Welcome, -} from 'ant-design-x-vue' +import { Attachments, Bubble, Conversations, Prompts, Sender, Welcome } from 'ant-design-x-vue' +import markdownit from 'markdown-it' import { computed, h, ref, watch } from 'vue' const { token } = theme.useToken() +const md = markdownit({ html: true, breaks: true }) + const styles = computed(() => { return { layout: { @@ -185,6 +181,9 @@ const roles: BubbleListProps['roles'] = { borderRadius: '16px', }, }, + messageRender: (content) => { + return h('div', { innerHTML: md.render(content) }) + }, }, local: { placement: 'end', @@ -299,7 +298,8 @@ async function sendMessage(userMessage: string) { const data = block.substring(5).trim() console.log('[Parse] 接收到数据:', data) - if (data) { // 若响应数据不为空 + if (data) { + // 若响应数据不为空 // 解析 JSON const response = JSON.parse(data) console.log('[Parse] 解析后的内容:', JSON.stringify(response)) @@ -381,27 +381,35 @@ const handleFileChange: AttachmentsProps['onChange'] = (info) => // ==================== Nodes ==================== const placeholderNode = computed(() => h(Space, { direction: 'vertical', size: 16, style: styles.value.placeholder }, () => [ - h(Welcome as Component, { - variant: 'borderless', - icon: 'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp', - description: '基于Ant Design,AGI产品界面解决方案,创造更美好的智能愿景~', - extra: h(Space, {}, () => [ - h(Button, { icon: h(ShareAltOutlined) }), - h(Button, { icon: h(EllipsisOutlined) }), - ]), - }, () => "Hello, I'm Ant Design X"), - h(Prompts as Component, { - items: placeholderPromptsItems, - styles: { - list: { - width: '100%', - }, - item: { - flex: 1, - }, + h( + Welcome as Component, + { + variant: 'borderless', + icon: 'https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp', + description: '基于Ant Design,AGI产品界面解决方案,创造更美好的智能愿景~', + extra: h(Space, {}, () => [ + h(Button, { icon: h(ShareAltOutlined) }), + h(Button, { icon: h(EllipsisOutlined) }), + ]), }, - onItemClick: onPromptsItemClick, - }, () => '想要了是吧?'), + () => "Hello, I'm Ant Design X", + ), + h( + Prompts as Component, + { + items: placeholderPromptsItems, + styles: { + list: { + width: '100%', + }, + item: { + flex: 1, + }, + }, + onItemClick: onPromptsItemClick, + }, + () => '想要了是吧?', + ), ]), ) @@ -512,9 +520,7 @@ const items = computed(() => { Click or drag files to this area to upload -
- Drop file here -
+
Drop file here