完成页面统计图渲染

This commit is contained in:
2025-02-28 20:16:43 +08:00
parent ce59d63412
commit bfffbc9868
3 changed files with 107 additions and 18 deletions

View File

@@ -10,11 +10,11 @@
}, },
"dependencies": { "dependencies": {
"chart.js": "^4.4.8", "chart.js": "^4.4.8",
"chartjs-adapter-moment": "^1.0.1",
"echarts": "^5.6.0", "echarts": "^5.6.0",
"eventsource-client": "^1.1.3", "eventsource-client": "^1.1.3",
"licia": "^1.46.0", "licia": "^1.46.0",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"markdown-it-container": "^4.0.0",
"mermaid": "^11.4.1", "mermaid": "^11.4.1",
"mermaid-it-markdown": "^1.0.8", "mermaid-it-markdown": "^1.0.8",
"vue": "^3.5.13" "vue": "^3.5.13"

29
client/pnpm-lock.yaml generated
View File

@@ -11,6 +11,9 @@ importers:
chart.js: chart.js:
specifier: ^4.4.8 specifier: ^4.4.8
version: 4.4.8 version: 4.4.8
chartjs-adapter-moment:
specifier: ^1.0.1
version: 1.0.1(chart.js@4.4.8)(moment@2.30.1)
echarts: echarts:
specifier: ^5.6.0 specifier: ^5.6.0
version: 5.6.0 version: 5.6.0
@@ -23,9 +26,6 @@ importers:
markdown-it: markdown-it:
specifier: ^14.1.0 specifier: ^14.1.0
version: 14.1.0 version: 14.1.0
markdown-it-container:
specifier: ^4.0.0
version: 4.0.0
mermaid: mermaid:
specifier: ^11.4.1 specifier: ^11.4.1
version: 11.4.1 version: 11.4.1
@@ -758,6 +758,12 @@ packages:
resolution: {integrity: sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==, tarball: https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz} resolution: {integrity: sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==, tarball: https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz}
engines: {pnpm: '>=8'} engines: {pnpm: '>=8'}
chartjs-adapter-moment@1.0.1:
resolution: {integrity: sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==}
peerDependencies:
chart.js: '>=3.0.0'
moment: ^2.10.2
chevrotain-allstar@0.3.1: chevrotain-allstar@0.3.1:
resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
peerDependencies: peerDependencies:
@@ -1047,7 +1053,7 @@ packages:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
eventsource-client@1.1.3: eventsource-client@1.1.3:
resolution: {integrity: sha512-6GJGePDMxin/6VH1Dex7RqnZRguweO1BVKhXpBYwKcQbVLOZPLfeDr9vYSb9ra88kjs0NpT8FaEDz5rExedDkg==, tarball: https://registry.npmjs.org/eventsource-client/-/eventsource-client-1.1.3.tgz} resolution: {integrity: sha512-6GJGePDMxin/6VH1Dex7RqnZRguweO1BVKhXpBYwKcQbVLOZPLfeDr9vYSb9ra88kjs0NpT8FaEDz5rExedDkg==}
engines: {node: '>=18.0.0'} engines: {node: '>=18.0.0'}
eventsource-parser@3.0.0: eventsource-parser@3.0.0:
@@ -1240,9 +1246,6 @@ packages:
magic-string@0.30.17: magic-string@0.30.17:
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
markdown-it-container@4.0.0:
resolution: {integrity: sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==}
markdown-it@13.0.2: markdown-it@13.0.2:
resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==}
hasBin: true hasBin: true
@@ -1291,6 +1294,9 @@ packages:
mlly@1.7.4: mlly@1.7.4:
resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
moment@2.30.1:
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
mrmime@2.0.1: mrmime@2.0.1:
resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -2363,6 +2369,11 @@ snapshots:
dependencies: dependencies:
'@kurkle/color': 0.3.4 '@kurkle/color': 0.3.4
chartjs-adapter-moment@1.0.1(chart.js@4.4.8)(moment@2.30.1):
dependencies:
chart.js: 4.4.8
moment: 2.30.1
chevrotain-allstar@0.3.1(chevrotain@11.0.3): chevrotain-allstar@0.3.1(chevrotain@11.0.3):
dependencies: dependencies:
chevrotain: 11.0.3 chevrotain: 11.0.3
@@ -2865,8 +2876,6 @@ snapshots:
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/sourcemap-codec': 1.5.0
markdown-it-container@4.0.0: {}
markdown-it@13.0.2: markdown-it@13.0.2:
dependencies: dependencies:
argparse: 2.0.1 argparse: 2.0.1
@@ -2952,6 +2961,8 @@ snapshots:
pkg-types: 1.3.1 pkg-types: 1.3.1
ufo: 1.5.4 ufo: 1.5.4
moment@2.30.1: {}
mrmime@2.0.1: {} mrmime@2.0.1: {}
ms@2.1.3: {} ms@2.1.3: {}

View File

@@ -1,9 +1,27 @@
<script setup> <script setup>
import {onMounted, ref} from 'vue' import {onMounted, ref, toRaw} from 'vue'
import MarkdownIt from 'markdown-it' import MarkdownIt from 'markdown-it'
import MarkDownMermaidPlugin from 'mermaid-it-markdown'
import {createEventSource} from 'eventsource-client' import {createEventSource} from 'eventsource-client'
import * as echarts from 'echarts'
import {CategoryScale, Chart, LinearScale, LineController, LineElement, PointElement, Title,} from 'chart.js'
import 'chartjs-adapter-moment'
import mermaid from 'mermaid'
import {each, isEqual, isJson, nextTick} from 'licia'
Chart.register(
LineController,
LineElement,
PointElement,
LinearScale,
Title,
CategoryScale
)
const chartsMap = ref({
echarts: {},
chartjs: {},
mermaid: {},
})
// 初始化 markdown-it 实例 // 初始化 markdown-it 实例
const md = new MarkdownIt({ const md = new MarkdownIt({
html: true, // 启用 HTML 标签 html: true, // 启用 HTML 标签
@@ -11,7 +29,33 @@ const md = new MarkdownIt({
linkify: true, // 自动转换 URL 为链接 linkify: true, // 自动转换 URL 为链接
typographer: true, // 启用一些语言中性的替换 + 引号美化 typographer: true, // 启用一些语言中性的替换 + 引号美化
}) })
md.use(MarkDownMermaidPlugin) md.use(function (md) {
md.renderer.rules.fence = function (tokens, idx, options, env, slf) {
// console.log('tokens', tokens, tokens[idx].info)
const content = tokens[idx].content
// 此处判断是否为 echarts 代码块
if (tokens[idx].info === 'echarts') {
if (isJson(content)) {
chartsMap.value.echarts[idx] = JSON.parse(content) //此处表示将内容存起来,存到当前页面的变量去
}
return `<div id="echarts-${idx}" style="width: 600px;height:400px;"></div>`
} else if (tokens[idx].info === 'chartjs') {
// 此处判断是否为 chartjs 代码块
if (isJson(content)) {
chartsMap.value.chartjs[idx] = JSON.parse(content)
}
return `<canvas id="chartjs-${idx}"></canvas>`
} else if (tokens[idx].info === 'mermaid') {
// 此处判断是否为 mermaidChart 代码块
const code = content.trim()
chartsMap.value.mermaid[idx] = code
return `<div id="mermaid-${idx}">${code}</div>`
} else {
// 其他代码块
return `<pre><code class='languge-${tokens[idx].info}'>${content}</code></pre>`
}
}
})
const question = ref('') const question = ref('')
const answer = ref('') const answer = ref('')
@@ -33,12 +77,25 @@ const showToast = ref(false)
const toastMessage = ref('') const toastMessage = ref('')
const toastMessageType = ref('success') const toastMessageType = ref('success')
const onEventSourceClose = (eventSource) => {
console.log(answer.value)
console.log(renderedAnswer.value)
console.log(chartsMap.value)
eventSource.close()
renderCharts()
}
const handleSubmit = async () => { const handleSubmit = async () => {
if (!question.value.trim() || loading.value) return if (!question.value.trim() || loading.value) return
loading.value = true loading.value = true
answer.value = '' answer.value = ''
renderedAnswer.value = '' renderedAnswer.value = ''
chartsMap.value = {
echarts: {},
chartjs: {},
mermaid: {},
}
try { try {
const url = 'http://localhost:7891/chat/stream' const url = 'http://localhost:7891/chat/stream'
@@ -59,18 +116,18 @@ const handleSubmit = async () => {
url: url, url: url,
method: 'POST', method: 'POST',
body: formData, body: formData,
onDisconnect: () => {
console.log(answer.value)
console.log(renderedAnswer.value)
eventSource.close()
},
}) })
for await (const { data } of eventSource) { for await (const { data } of eventSource) {
if (isEqual(data, '#CLOSE#')) {
onEventSourceClose(eventSource)
break
}
answer.value += data.replaceAll(/#\/#/g, '') answer.value += data.replaceAll(/#\/#/g, '')
renderedAnswer.value = md.render(answer.value) renderedAnswer.value = md.render(answer.value)
} }
} catch (error) { } catch (error) {
console.error('请求出错:', error) console.error('请求出错:', error)
console.log(answer.value)
answer.value = '抱歉,请求出现错误' answer.value = '抱歉,请求出现错误'
renderedAnswer.value = answer.value renderedAnswer.value = answer.value
showToastMessage('请求失败,请检查服务器连接', 'error') showToastMessage('请求失败,请检查服务器连接', 'error')
@@ -141,8 +198,25 @@ const handleCloseSettings = () => {
showToastMessage('设置已保存', 'success') showToastMessage('设置已保存', 'success')
} }
const renderCharts = () => {
const map = toRaw(chartsMap.value)
each(map.echarts, (value, key) => {
echarts.init(document.getElementById(`echarts-${key}`)).setOption(value)
})
each(map.chartjs, (value, key) => {
new Chart(document.getElementById(`chartjs-${key}`), value)
})
each(map.mermaid, (value, key) => {
mermaid.initialize({ startOnLoad: true })
mermaid.init(undefined, document.getElementById(`mermaid-${key}`))
})
}
// 在组件挂载时加载设置 // 在组件挂载时加载设置
onMounted(() => { onMounted(() => {
nextTick(() => {
renderCharts()
})
loadSettingsFromStorage() loadSettingsFromStorage()
}) })
</script> </script>
@@ -350,6 +424,10 @@ onMounted(() => {
</template> </template>
<style scoped> <style scoped>
.mermaid {
font-family: 'Arial', sans-serif;
}
.chat-container { .chat-container {
margin: 0; margin: 0;
height: 100vh; height: 100vh;