インラインデータ形式のチャートコンポーネント設計
背景・目的
課題
従来の実装では、データがスクリプト内の変数(epsDataMap, sgaDataMapなど)に定義され、テンプレートのコンポーネントは変数名を参照していた。
<script setup>
// データはここに定義(テンプレートから物理的に離れている)
const microsoftExpenseRatioData = computed(() => { ... })
</script>
<template>
<!-- 変数名を参照 -->
<ExpenseRatioChart
:ratio-data="microsoftExpenseRatioData"
:current-period-index="microsoftPeriodIndex"
/>
</template>
問題点:
- データと表示が物理的に離れている
- 何を渡したら何が表示されるか分かりにくい
- 別の会社のデータを追加するとき、コピペしづらい
目標
AIにデータを渡して「このフォーマットに変換して」と言えば、そのままコピペで動くコードが生成される形式にしたい。
<!-- AIが生成したコードをそのままペーストできる -->
<ExpenseRatioChart
:config="{
financials: [
{ label: '2015', revenue: 93580, sga: 20324, rd: 12046 },
{ label: '2016', revenue: 91154, sga: 19198, rd: 11988 },
...
],
currentPeriodIndex: microsoftPeriodIndex
}"
/>
設計方針
1. 元データを渡し、比率はコンポーネント内で計算
理由:
- Koyfinなどから取得した元データをそのまま渡せる
- AIに「比率を計算して」という追加の指示が不要
- 計算ロジックがコンポーネントに集約される
// コンポーネント内で比率を計算
const ratioData = computed(() =>
props.config.financials.map(d => ({
label: d.label,
sgaRatio: (d.sga / d.revenue) * 100,
rdRatio: (d.rd / d.revenue) * 100
}))
)
2. currentPeriodIndex は変数参照を許容
アニメーションで動的に変わる値なので、これだけは変数参照が必要。
<ExpenseRatioChart
:config="{
financials: [...], // リテラルデータ
currentPeriodIndex: microsoftPeriodIndex // 変数参照(動的)
}"
/>
3. 将来的にはラッパーコンポーネントで統合
最終形は、全チャートをラップするコンポーネント:
<CompanyFinancialDashboard
:config="{
companyName: 'Microsoft',
financials: [
{
label: '2015',
revenue: 93580,
grossProfit: 60542,
operatingIncome: 28172,
netIncome: 12193,
eps: 1.48,
sga: 20324,
rd: 12046,
operatingCF: 29668,
capex: -5944,
fcf: 23724
},
...
]
}"
/>
ラッパー内部で:
currentPeriodIndexを管理- 各チャートに適切なデータを渡す
- 比率を計算して渡す
実装手順
Step 1: ExpenseRatioChart を変更(完了)
Before:
interface Props {
ratioData: { label: string; sgaRatio: number; rdRatio: number }[]
currentPeriodIndex: number
}
After:
interface FinancialDataItem {
label: string
revenue: number
sga: number
rd: number
}
interface ChartConfig {
financials: FinancialDataItem[]
currentPeriodIndex: number
}
interface Props {
config: ChartConfig
}
// 比率はコンポーネント内で計算
const ratioData = computed(() =>
props.config.financials.map(d => ({
label: d.label,
sgaRatio: (d.sga / d.revenue) * 100,
rdRatio: (d.rd / d.revenue) * 100
}))
)
Step 2: 呼び出し側をインラインデータに変更
<ExpenseRatioChart
:config="{
financials: [
{ label: '2015', revenue: 93580, sga: 20324, rd: 12046 },
{ label: '2016', revenue: 91154, sga: 19198, rd: 11988 },
{ label: '2017', revenue: 96571, sga: 19942, rd: 13037 },
{ label: '2018', revenue: 110360, sga: 22223, rd: 14726 },
{ label: '2019', revenue: 125843, sga: 23098, rd: 16876 },
{ label: '2020', revenue: 143015, sga: 24709, rd: 19269 },
{ label: '2021', revenue: 168088, sga: 25224, rd: 20716 },
{ label: '2022', revenue: 198270, sga: 27725, rd: 24512 },
{ label: '2023', revenue: 211915, sga: 30334, rd: 27195 },
{ label: '2024', revenue: 245122, sga: 32065, rd: 29510 },
{ label: '2025', revenue: 277985, sga: 32877, rd: 32488 },
{ label: 'LTM', revenue: 289054, sga: 33010, rd: 33090 }
],
currentPeriodIndex: microsoftPeriodIndex
}"
/>
Step 3: 他のチャートも同様に変更
- ProfitMarginChart
- EPSChart
- FCFChart
Step 4: ラッパーコンポーネント作成(オプション)
全チャートを1つのconfigでまとめて渡せるようにする。
AIへの指示例
Koyfinなどからデータをコピーして、以下のように依頼:
以下のMicrosoftの財務データを、このフォーマットに変換してください:
{ label: '年度', revenue: 売上, sga: SG&A, rd: R&D }
データ:
[ここにKoyfinのデータをペースト]
懸念点と対策
| 懸念 | 対策 |
|---|---|
| Vueファイルが長くなる | データと表示が近いのでむしろ見やすい |
| 同じデータを別ページで使いたい | コピペ、または必要時にcomposableに切り出す |
| 型安全性 | TypeScriptのprops定義で検出 |
まとめ
- 目的: AIでコード生成 → コピペで動く
- 方針: 元データを渡し、比率はコンポーネント内で計算
- 手順: まずExpenseRatioChartで試し、うまくいったら他に展開