Insight Charts 修正方案
問題分析
- Path問題: insight頁面直接使用unovis組件,但沒有適當的錯誤處理
- 缺少狀態管理: 沒有使用
useChartStateWithComponent進行圖表狀態管理 - 組件結構問題: 直接在template中組合圖表組件
解決方案
1. 創建專用圖表組件
為每個insight頁面創建專用的圖表組件,放在對應的目錄下:
src/components/insights/
├── health/
│ ├── HealthTrendChart.vue
│ ├── HealthComparisonChart.vue
│ └── RiskDistributionChart.vue
├── customer/
│ ├── RFMDistributionChart.vue
│ ├── LTVTrendChart.vue
│ └── ActionPriorityChart.vue
├── operational/
│ ├── HourlyEfficiencyChart.vue
│ ├── EfficiencyTrendChart.vue
│ └── BottleneckChart.vue
└── risk/
├── RiskLevelChart.vue
├── RiskTrendChart.vue
└── RiskComparisonChart.vue2. 圖表組件範例結構
每個圖表組件應該遵循現有模式,例如:
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VisLine } from '@unovis/vue'
import { Line, BulletShape } from '@unovis/ts'
import XYContainer from '@/components/charts/base/XYContainer.vue'
import ChartCard from '@/components/charts/ChartCard.vue'
type DataRecord = {
week: string
customerQuality: number
operationalEfficiency: number
supportEffectiveness: number
}
defineProps<{
data: DataRecord[]
}>()
const chartConfig = ref({
x: (d: DataRecord) => new Date(d.week + 'T00:00:00'),
y: [
(d: DataRecord) => d.customerQuality,
(d: DataRecord) => d.operationalEfficiency,
(d: DataRecord) => d.supportEffectiveness,
],
})
const auxiliary = ref({
axis: [
{
type: 'x',
position: 'bottom',
tickFormat: (d: any) => new Date(d).toLocaleDateString(),
},
{ type: 'y', position: 'left' },
],
legend: {
items: [
{ name: '客戶價值', shape: BulletShape.Circle },
{ name: '營運效率', shape: BulletShape.Square },
{ name: '客服效能', shape: BulletShape.Triangle },
],
},
tooltip: {
triggers: {
[Line.selectors.line]: (d: any) => `
<div class="p-2">
<div class="text-sm font-semibold">${new Date(d.week + 'T00:00:00').toLocaleDateString()}</div>
<div class="text-xs mt-1">
客戶價值: ${d.customerQuality.toFixed(1)}%<br/>
營運效率: ${d.operationalEfficiency.toFixed(1)}%<br/>
客服效能: ${d.supportEffectiveness.toFixed(1)}%
</div>
</div>
`,
},
},
})
</script>
<template>
<ChartCard title="健康度週度趨勢">
<template #default="{ width, height }">
<XYContainer
:data="data"
:auxiliary="auxiliary"
:containerSize="{ width, height }"
>
<VisLine v-bind="chartConfig" />
</XYContainer>
</template>
</ChartCard>
</template>3. 在Insight頁面中使用
修正後的insight頁面應該像這樣使用:
vue
<script setup lang="ts">
import DashboardHeader from '@/components/dashboard/DashboardHeader.vue'
import HealthTrendChart from '@/components/insights/health/HealthTrendChart.vue'
import HealthComparisonChart from '@/components/insights/health/HealthComparisonChart.vue'
import RiskDistributionChart from '@/components/insights/health/RiskDistributionChart.vue'
import { useBusinessHealthOverview } from '@/composables/queries/useBusinessHealthQueries'
import { useChartStateWithComponent } from '@/composables/useChartState'
import { useDashboardRefresh } from '@/composables/useDashboardRefresh'
import { computed } from 'vue'
// 查詢數據
const businessHealthQuery = useBusinessHealthOverview()
const dashboardRefresh = useDashboardRefresh('executive-health')
// 處理數據
const healthTrendData = computed(() => {
const metrics = businessHealthQuery.data.value?.metrics || []
return metrics
.slice(0, 12)
.reverse()
.map((metric) => ({
week: metric.week,
customerQuality: (metric.customer_quality_ratio || 0) * 100,
operationalEfficiency: (metric.completion_rate || 0) * 100,
supportEffectiveness: Math.max(
0,
100 - (metric.avg_response_time || 0) * 10,
),
}))
})
const healthComparisonData = computed(() => {
const latest = businessHealthQuery.data.value?.metrics?.[0]
if (!latest) return []
return [
{
dimension: '客戶價值',
current: (latest.customer_quality_ratio || 0) * 100,
target: 80,
},
{
dimension: '營運效率',
current: (latest.completion_rate || 0) * 100,
target: 85,
},
{
dimension: '客服效能',
current: Math.max(0, 100 - (latest.avg_response_time || 0) * 10),
target: 90,
},
]
})
// 使用 useChartStateWithComponent 管理圖表狀態
const healthTrendChart = useChartStateWithComponent(
businessHealthQuery,
HealthTrendChart,
{
emptyMessage: '暫無健康度趨勢數據',
errorMessage: '載入健康度趨勢失敗',
chartProps: {
data: healthTrendData,
},
},
)
const healthComparisonChart = useChartStateWithComponent(
businessHealthQuery,
HealthComparisonChart,
{
emptyMessage: '暫無健康度對比數據',
errorMessage: '載入健康度對比失敗',
chartProps: {
data: healthComparisonData,
},
},
)
</script>
<template>
<div class="space-y-6">
<!-- Dashboard Header -->
<DashboardHeader
title="經營健康度總覽"
description="業務全貌洞察、戰略決策支撐和風險預警系統"
:last-updated="dashboardRefresh.lastUpdated.value"
:is-refreshing="
dashboardRefresh.isRefreshing.value ||
businessHealthQuery.isLoading.value
"
:on-refresh="businessHealthQuery.refetch"
/>
<!-- 基礎圖表區塊 -->
<div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
<!-- 使用 ChartStateWithComponent 的結果 -->
<component :is="healthTrendChart.renderChart" />
<component :is="healthComparisonChart.renderChart" />
<component :is="riskDistributionChart.renderChart" />
</div>
<!-- 其他現有內容... -->
</div>
</template>實施步驟
- 創建圖表組件目錄結構
- 將每個insight頁面的圖表邏輯提取到獨立組件
- 更新insight頁面使用新的圖表組件
- 使用useChartStateWithComponent管理圖表狀態
- 測試所有圖表功能
這樣修正後,insight頁面將具有:
- 適當的錯誤處理
- 載入狀態管理
- 數據驗證
- 可重用的圖表組件
- 與現有dashboard相同的代碼品質