From e1220856d419dfa447e5e0909700c14a6c78d297 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 22 五月 2026 14:44:39 +0800
Subject: [PATCH] fix(financial): 修复财务数据处理中的数值转换和异常处理问题
---
src/components/AIChatSidebar/index.vue | 149 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 147 insertions(+), 2 deletions(-)
diff --git a/src/components/AIChatSidebar/index.vue b/src/components/AIChatSidebar/index.vue
index a6d46da..7b22d7b 100644
--- a/src/components/AIChatSidebar/index.vue
+++ b/src/components/AIChatSidebar/index.vue
@@ -227,6 +227,12 @@
:id="`ai-chart-${index}-${key}`"
></div>
</div>
+ <div
+ v-else-if="message.chartMarkdownParseFailed"
+ class="chart-empty-state"
+ >
+ 鍥捐〃瑙f瀽澶辫触锛岃绋嶅悗閲嶈瘯銆�
+ </div>
<!-- 琛ㄦ牸鍐呭 -->
<div v-if="message.type === 'todo_list' && message.tableData" class="table-wrapper">
@@ -2112,6 +2118,7 @@
purchaseData: null,
purchaseIntentData: null,
financeData: null,
+ chartMarkdownParseFailed: false,
localUploadFiles: isUser ? mapHistoryFilePathsToSnapshots(msg.filePaths, uuid.value, idx) : []
}
@@ -2361,6 +2368,7 @@
messageObj.purchaseData = null
messageObj.purchaseIntentData = null
messageObj.financeData = null
+ messageObj.chartMarkdownParseFailed = false
if (isPurchaseIntentNotRecognized) {
messageObj.purchaseIntentData = normalizePurchaseIntentNotRecognizedData(parsedData)
@@ -3615,7 +3623,8 @@
salesData: null,
purchaseData: null,
purchaseIntentData: null,
- financeData: null
+ financeData: null,
+ chartMarkdownParseFailed: false
})
outputState.value[botMsgIndex] = {
@@ -3671,6 +3680,7 @@
if (extracted) {
applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex, !outputState.value[botMsgIndex].hasRenderedChart)
}
+ currentMsg.htmlContent = convertStreamOutput(currentMsg.content || '', botMsgIndex)
// 鏈�缁堣В鏋愮‘淇濆浘琛ㄦ覆鏌�
if (currentMsg.chartOptions && !outputState.value[botMsgIndex].hasRenderedChart) {
@@ -3744,7 +3754,8 @@
salesData: null,
purchaseData: null,
purchaseIntentData: null,
- financeData: null
+ financeData: null,
+ chartMarkdownParseFailed: false
}
messages.value.push(botMsg)
@@ -3794,6 +3805,7 @@
if (extracted) {
applyStructuredMessageData(currentMsg, extracted.data, botMsgIndex)
}
+ currentMsg.htmlContent = convertStreamOutput(currentMsg.content || '', botMsgIndex)
}).catch(err => {
if (err.name === 'CanceledError' || err.name === 'AbortError') {
console.log('Request aborted by user')
@@ -3835,6 +3847,126 @@
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\n/g, '<br>')
+}
+
+const localChartMarkdownImagePattern = /!\[[^\]]*]\((https?:\/\/local\/generate_chart\?[^)\s]+)\)/gi
+
+const parseLocalChartOptionText = (optionText = '') => {
+ const text = String(optionText || '').trim()
+ if (!text) return null
+
+ const parseCandidates = [text]
+ try {
+ const decoded = decodeURIComponent(text)
+ if (decoded && decoded !== text) {
+ parseCandidates.push(decoded)
+ }
+ } catch (err) {
+ // Keep original text candidate.
+ }
+
+ for (const candidate of parseCandidates) {
+ try {
+ const parsed = JSON.parse(candidate)
+ if (isPlainObject(parsed)) {
+ return parsed
+ }
+ } catch (err) {
+ continue
+ }
+ }
+
+ return null
+}
+
+const parseLocalChartOptionFromUrl = (urlText = '') => {
+ try {
+ const url = new URL(String(urlText || '').trim())
+ if (String(url.hostname || '').toLowerCase() !== 'local' || !String(url.pathname || '').includes('/generate_chart')) {
+ return null
+ }
+ const optionText = url.searchParams.get('options')
+ return parseLocalChartOptionText(optionText)
+ } catch (err) {
+ return null
+ }
+}
+
+const extractLocalChartMarkdown = (text = '') => {
+ const sourceText = String(text || '')
+ if (!sourceText) {
+ return {
+ cleanedText: '',
+ hasLocalChartMarkdown: false,
+ chartOptions: null,
+ parseFailed: false
+ }
+ }
+
+ let hasLocalChartMarkdown = false
+ let chartIndex = 0
+ const chartOptions = {}
+
+ const cleanedText = sourceText.replace(localChartMarkdownImagePattern, (fullMatch, chartUrl) => {
+ hasLocalChartMarkdown = true
+ const option = parseLocalChartOptionFromUrl(chartUrl)
+ if (option) {
+ chartOptions[`markdownChart_${chartIndex++}`] = option
+ }
+ return ''
+ })
+
+ const normalizedText = cleanedText
+ .replace(/\n[ \t]*\n[ \t]*\n+/g, '\n\n')
+ .trim()
+ const hasParsedCharts = Object.keys(chartOptions).length > 0
+
+ return {
+ cleanedText: normalizedText,
+ hasLocalChartMarkdown,
+ chartOptions: hasParsedCharts ? chartOptions : null,
+ parseFailed: hasLocalChartMarkdown && !hasParsedCharts
+ }
+}
+
+const applyLocalChartMarkdownFallback = (displayText, msgIndex) => {
+ const messageObj = messages.value[msgIndex]
+ if (!messageObj || messageObj.isUser) return displayText
+
+ const {
+ cleanedText,
+ hasLocalChartMarkdown,
+ chartOptions,
+ parseFailed
+ } = extractLocalChartMarkdown(displayText)
+
+ if (!hasLocalChartMarkdown) {
+ return displayText
+ }
+
+ if (chartOptions) {
+ messageObj.chartOptions = chartOptions
+ messageObj.chartRenderReady = true
+ messageObj.chartMarkdownParseFailed = false
+
+ const streamState = outputState.value[msgIndex]
+ if (!streamState || !streamState.hasRenderedChart) {
+ renderCharts(msgIndex, chartOptions)
+ if (streamState) {
+ streamState.hasRenderedChart = true
+ }
+ }
+
+ return cleanedText || '宸蹭负鎮ㄧ敓鎴愬垎鏋愬浘琛ㄣ��'
+ }
+
+ if (!messageObj.chartOptions || !messageObj.chartRenderReady) {
+ messageObj.chartOptions = null
+ messageObj.chartRenderReady = false
+ messageObj.chartMarkdownParseFailed = parseFailed
+ }
+
+ return cleanedText || '鍥捐〃瑙f瀽澶辫触锛岃绋嶅悗閲嶈瘯銆�'
}
const convertStreamOutput = (output, msgIndex) => {
@@ -3902,6 +4034,7 @@
}
}
+ display = applyLocalChartMarkdownFallback(display, msgIndex)
let html = convertTextToHtml(display)
// 杩樺師浠g爜鍧�
@@ -4884,6 +5017,18 @@
margin-bottom: 12px;
}
+.chart-empty-state {
+ margin-top: 12px;
+ width: 100%;
+ border-radius: 10px;
+ border: 1px dashed rgba(148, 163, 184, 0.6);
+ background: #f8fafc;
+ color: #64748b;
+ font-size: 13px;
+ line-height: 1.6;
+ padding: 12px;
+}
+
.table-wrapper {
margin-top: 12px;
background: #fff;
--
Gitblit v1.9.3