data-visualizer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseData Visualizer Skill
数据可视化技能
I help you build beautiful, interactive data visualizations and dashboards.
我可以帮助你构建美观、交互式的数据可视化内容和仪表盘。
What I Do
我的能力范围
Chart Creation:
- Line charts, bar charts, pie charts
- Area charts, scatter plots, heatmaps
- Complex visualizations (Sankey, treemaps, network graphs)
Dashboard Building:
- KPI cards and metrics
- Real-time data dashboards
- Interactive filters and drill-downs
- Responsive layouts
Data Presentation:
- Data storytelling
- Color schemes and accessibility
- Animation and interactions
- Export capabilities
图表创建:
- 折线图、柱状图、饼图
- 面积图、散点图、热力图
- 复杂可视化(桑基图、树状图、网络图)
仪表盘搭建:
- KPI卡片与指标展示
- 实时数据仪表盘
- 交互式筛选与下钻功能
- 响应式布局
数据呈现:
- 数据叙事
- 配色方案与可访问性
- 动画与交互效果
- 导出功能
Library Selection Guide
库选择指南
Recharts (Recommended for React)
Recharts(推荐用于React)
Best for:
- Quick, simple charts
- React/Next.js projects
- Standard chart types
- Responsive design
Example:
typescript
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'
const data = [
{ month: 'Jan', revenue: 4000, expenses: 2400 },
{ month: 'Feb', revenue: 3000, expenses: 1398 },
{ month: 'Mar', revenue: 2000, expenses: 9800 },
]
function RevenueChart() {
return (
<LineChart width={600} height={300} data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="revenue" stroke="#8884d8" />
<Line type="monotone" dataKey="expenses" stroke="#82ca9d" />
</LineChart>
)
}最佳适用场景:
- 快速创建简单图表
- React/Next.js项目
- 标准图表类型
- 响应式设计
示例:
typescript
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts'
const data = [
{ month: 'Jan', revenue: 4000, expenses: 2400 },
{ month: 'Feb', revenue: 3000, expenses: 1398 },
{ month: 'Mar', revenue: 2000, expenses: 9800 },
]
function RevenueChart() {
return (
<LineChart width={600} height={300} data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="revenue" stroke="#8884d8" />
<Line type="monotone" dataKey="expenses" stroke="#82ca9d" />
</LineChart>
)
}Chart.js (Recommended for Vue/Angular)
Chart.js(推荐用于Vue/Angular)
Best for:
- Framework-agnostic
- Simple API
- Good documentation
- Standard chart types
Example:
typescript
import { Chart } from 'chart.js/auto'
const ctx = document.getElementById('myChart')
const chart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [
{
label: 'Sales',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'rgba(54, 162, 235, 0.5)'
}
]
},
options: {
responsive: true,
plugins: {
legend: { position: 'top' },
title: { display: true, text: 'Monthly Sales' }
}
}
})最佳适用场景:
- 与框架无关
- 简洁API
- 完善的文档
- 标准图表类型
示例:
typescript
import { Chart } from 'chart.js/auto'
const ctx = document.getElementById('myChart')
const chart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
datasets: [
{
label: 'Sales',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: 'rgba(54, 162, 235, 0.5)'
}
]
},
options: {
responsive: true,
plugins: {
legend: { position: 'top' },
title: { display: true, text: 'Monthly Sales' }
}
}
})D3.js (Advanced)
D3.js(进阶)
Best for:
- Custom visualizations
- Complex interactions
- Full control over rendering
- Data-driven documents
When to use:
- Need custom chart type
- Complex data transformations
- Advanced interactions
- Publication-quality graphics
Example:
typescript
import * as d3 from 'd3'
function createBarChart(data: Array<{ name: string; value: number }>) {
const width = 600
const height = 400
const margin = { top: 20, right: 20, bottom: 30, left: 40 }
const svg = d3.select('#chart').append('svg').attr('width', width).attr('height', height)
const x = d3
.scaleBand()
.domain(data.map(d => d.name))
.range([margin.left, width - margin.right])
.padding(0.1)
const y = d3
.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height - margin.bottom, margin.top])
svg
.selectAll('rect')
.data(data)
.join('rect')
.attr('x', d => x(d.name))
.attr('y', d => y(d.value))
.attr('height', d => y(0) - y(d.value))
.attr('width', x.bandwidth())
.attr('fill', 'steelblue')
// Add axes
svg
.append('g')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x))
svg.append('g').attr('transform', `translate(${margin.left},0)`).call(d3.axisLeft(y))
}最佳适用场景:
- 自定义可视化内容
- 复杂交互效果
- 渲染完全可控
- 数据驱动文档
使用时机:
- 需要自定义图表类型
- 复杂数据转换
- 进阶交互效果
- 出版级图形
示例:
typescript
import * as d3 from 'd3'
function createBarChart(data: Array<{ name: string; value: number }>) {
const width = 600
const height = 400
const margin = { top: 20, right: 20, bottom: 30, left: 40 }
const svg = d3.select('#chart').append('svg').attr('width', width).attr('height', height)
const x = d3
.scaleBand()
.domain(data.map(d => d.name))
.range([margin.left, width - margin.right])
.padding(0.1)
const y = d3
.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height - margin.bottom, margin.top])
svg
.selectAll('rect')
.data(data)
.join('rect')
.attr('x', d => x(d.name))
.attr('y', d => y(d.value))
.attr('height', d => y(0) - y(d.value))
.attr('width', x.bandwidth())
.attr('fill', 'steelblue')
// Add axes
svg
.append('g')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x))
svg.append('g').attr('transform', `translate(${margin.left},0)`).call(d3.axisLeft(y))
}Dashboard Patterns
仪表盘模式
Pattern 1: KPI Dashboard
模式1:KPI仪表盘
Use case: Executive dashboard with key metrics
typescript
// components/KPIDashboard.tsx
import { Card } from '@/components/ui/card'
interface KPICardProps {
title: string
value: string | number
change: number
trend: 'up' | 'down'
}
function KPICard({ title, value, change, trend }: KPICardProps) {
const trendColor = trend === 'up' ? 'text-green-600' : 'text-red-600'
const trendIcon = trend === 'up' ? '↑' : '↓'
return (
<Card className="p-6">
<h3 className="text-sm font-medium text-gray-600">{title}</h3>
<div className="mt-2 flex items-baseline">
<p className="text-3xl font-semibold">{value}</p>
<span className={`ml-2 text-sm ${trendColor}`}>
{trendIcon} {Math.abs(change)}%
</span>
</div>
</Card>
)
}
export default function Dashboard() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<KPICard title="Total Revenue" value="$45,231" change={12.5} trend="up" />
<KPICard title="Active Users" value="2,350" change={-5.2} trend="down" />
<KPICard title="Conversion Rate" value="3.24%" change={8.1} trend="up" />
<KPICard title="Avg Order Value" value="$158" change={2.3} trend="up" />
</div>
)
}适用场景: 包含关键指标的高管仪表盘
typescript
// components/KPIDashboard.tsx
import { Card } from '@/components/ui/card'
interface KPICardProps {
title: string
value: string | number
change: number
trend: 'up' | 'down'
}
function KPICard({ title, value, change, trend }: KPICardProps) {
const trendColor = trend === 'up' ? 'text-green-600' : 'text-red-600'
const trendIcon = trend === 'up' ? '↑' : '↓'
return (
<Card className="p-6">
<h3 className="text-sm font-medium text-gray-600">{title}</h3>
<div className="mt-2 flex items-baseline">
<p className="text-3xl font-semibold">{value}</p>
<span className={`ml-2 text-sm ${trendColor}`}>
{trendIcon} {Math.abs(change)}%
</span>
</div>
</Card>
)
}
export default function Dashboard() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<KPICard title="Total Revenue" value="$45,231" change={12.5} trend="up" />
<KPICard title="Active Users" value="2,350" change={-5.2} trend="down" />
<KPICard title="Conversion Rate" value="3.24%" change={8.1} trend="up" />
<KPICard title="Avg Order Value" value="$158" change={2.3} trend="up" />
</div>
)
}Pattern 2: Real-Time Dashboard
模式2:实时数据仪表盘
Use case: Live data monitoring
typescript
// components/RealtimeDashboard.tsx
'use client'
import { useEffect, useState } from 'react'
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
interface DataPoint {
time: string
value: number
}
export default function RealtimeDashboard() {
const [data, setData] = useState<DataPoint[]>([])
useEffect(() => {
// Fetch initial data
fetch('/api/metrics/realtime')
.then(res => res.json())
.then(setData)
// Subscribe to real-time updates
const eventSource = new EventSource('/api/metrics/stream')
eventSource.onmessage = (event) => {
const newDataPoint = JSON.parse(event.data)
setData(prev => {
const updated = [...prev, newDataPoint]
// Keep last 20 data points
return updated.slice(-20)
})
}
return () => eventSource.close()
}, [])
return (
<div className="p-6 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-4">Live Traffic</h2>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<XAxis dataKey="time" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="value"
stroke="#8884d8"
strokeWidth={2}
dot={false}
isAnimationActive={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
)
}API Route for SSE:
typescript
// app/api/metrics/stream/route.ts
export async function GET(req: Request) {
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
const interval = setInterval(async () => {
const value = Math.floor(Math.random() * 100)
const time = new Date().toLocaleTimeString()
const data = `data: ${JSON.stringify({ time, value })}\n\n`
controller.enqueue(encoder.encode(data))
}, 1000)
// Cleanup on close
req.signal.addEventListener('abort', () => {
clearInterval(interval)
controller.close()
})
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
}
})
}适用场景: 实时数据监控
typescript
// components/RealtimeDashboard.tsx
'use client'
import { useEffect, useState } from 'react'
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
interface DataPoint {
time: string
value: number
}
export default function RealtimeDashboard() {
const [data, setData] = useState<DataPoint[]>([])
useEffect(() => {
// Fetch initial data
fetch('/api/metrics/realtime')
.then(res => res.json())
.then(setData)
// Subscribe to real-time updates
const eventSource = new EventSource('/api/metrics/stream')
eventSource.onmessage = (event) => {
const newDataPoint = JSON.parse(event.data)
setData(prev => {
const updated = [...prev, newDataPoint]
// Keep last 20 data points
return updated.slice(-20)
})
}
return () => eventSource.close()
}, [])
return (
<div className="p-6 bg-white rounded-lg shadow">
<h2 className="text-xl font-bold mb-4">Live Traffic</h2>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={data}>
<XAxis dataKey="time" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="value"
stroke="#8884d8"
strokeWidth={2}
dot={false}
isAnimationActive={false}
/>
</LineChart>
</ResponsiveContainer>
</div>
)
}SSE接口实现:
typescript
// app/api/metrics/stream/route.ts
export async function GET(req: Request) {
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
const interval = setInterval(async () => {
const value = Math.floor(Math.random() * 100)
const time = new Date().toLocaleTimeString()
const data = `data: ${JSON.stringify({ time, value })}\n\n`
controller.enqueue(encoder.encode(data))
}, 1000)
// Cleanup on close
req.signal.addEventListener('abort', () => {
clearInterval(interval)
controller.close()
})
}
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
}
})
}Pattern 3: Interactive Dashboard with Filters
模式3:带筛选功能的交互式仪表盘
typescript
// components/SalesDashboard.tsx
'use client'
import { useState } from 'react'
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
type Period = '7d' | '30d' | '90d'
type Region = 'all' | 'us' | 'eu' | 'asia'
export default function SalesDashboard() {
const [period, setPeriod] = useState<Period>('30d')
const [region, setRegion] = useState<Region>('all')
const { data, loading } = useSalesData({ period, region })
return (
<div className="space-y-6">
{/* Filters */}
<div className="flex gap-4">
<select
value={period}
onChange={(e) => setPeriod(e.target.value as Period)}
className="px-4 py-2 border rounded"
>
<option value="7d">Last 7 days</option>
<option value="30d">Last 30 days</option>
<option value="90d">Last 90 days</option>
</select>
<select
value={region}
onChange={(e) => setRegion(e.target.value as Region)}
className="px-4 py-2 border rounded"
>
<option value="all">All Regions</option>
<option value="us">United States</option>
<option value="eu">Europe</option>
<option value="asia">Asia</option>
</select>
</div>
{/* Chart */}
{loading ? (
<div>Loading...</div>
) : (
<ResponsiveContainer width="100%" height={400}>
<BarChart data={data}>
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Bar dataKey="sales" fill="#8884d8" />
</BarChart>
</ResponsiveContainer>
)}
</div>
)
}
// Custom hook for data fetching
function useSalesData({ period, region }: { period: Period, region: Region }) {
const [data, setData] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
setLoading(true)
fetch(`/api/sales?period=${period}®ion=${region}`)
.then(res => res.json())
.then(data => {
setData(data)
setLoading(false)
})
}, [period, region])
return { data, loading }
}typescript
// components/SalesDashboard.tsx
'use client'
import { useState } from 'react'
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'
type Period = '7d' | '30d' | '90d'
type Region = 'all' | 'us' | 'eu' | 'asia'
export default function SalesDashboard() {
const [period, setPeriod] = useState<Period>('30d')
const [region, setRegion] = useState<Region>('all')
const { data, loading } = useSalesData({ period, region })
return (
<div className="space-y-6">
{/* 筛选器 */}
<div className="flex gap-4">
<select
value={period}
onChange={(e) => setPeriod(e.target.value as Period)}
className="px-4 py-2 border rounded"
>
<option value="7d">最近7天</option>
<option value="30d">最近30天</option>
<option value="90d">最近90天</option>
</select>
<select
value={region}
onChange={(e) => setRegion(e.target.value as Region)}
className="px-4 py-2 border rounded"
>
<option value="all">所有地区</option>
<option value="us">美国</option>
<option value="eu">欧洲</option>
<option value="asia">亚洲</option>
</select>
</div>
{/* 图表 */}
{loading ? (
<div>加载中...</div>
) : (
<ResponsiveContainer width="100%" height={400}>
<BarChart data={data}>
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Bar dataKey="sales" fill="#8884d8" />
</BarChart>
</ResponsiveContainer>
)}
</div>
)
}
// 数据获取自定义Hook
function useSalesData({ period, region }: { period: Period, region: Region }) {
const [data, setData] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
setLoading(true)
fetch(`/api/sales?period=${period}®ion=${region}`)
.then(res => res.json())
.then(data => {
setData(data)
setLoading(false)
})
}, [period, region])
return { data, loading }
}Chart Types Guide
图表类型指南
Line Chart
折线图
Best for: Trends over time, continuous data
typescript
<LineChart data={data}>
<Line type="monotone" dataKey="value" stroke="#8884d8" />
</LineChart>Use when:
- Stock prices, temperature, website traffic
- Showing change over time
- Multiple data series comparison
最佳适用场景: 展示时间趋势、连续数据
typescript
<LineChart data={data}>
<Line type="monotone" dataKey="value" stroke="#8884d8" />
</LineChart>使用时机:
- 股票价格、气温、网站流量
- 展示随时间的变化
- 多数据系列对比
Bar Chart
柱状图
Best for: Comparing categories
typescript
<BarChart data={data}>
<Bar dataKey="value" fill="#8884d8" />
</BarChart>Use when:
- Sales by product, users by country
- Discrete categories
- Ranking/comparison
最佳适用场景: 分类对比
typescript
<BarChart data={data}>
<Bar dataKey="value" fill="#8884d8" />
</BarChart>使用时机:
- 各产品销量、各国用户数
- 离散分类数据
- 排名/对比
Pie/Donut Chart
饼图/环形图
Best for: Part-to-whole relationships
typescript
<PieChart>
<Pie data={data} dataKey="value" nameKey="name" fill="#8884d8" />
</PieChart>Use when:
- Market share, budget allocation
- Proportions (max 5-7 slices)
- Simple percentages
⚠️ Avoid when:
- Too many categories (> 7)
- Precise comparison needed (use bar chart)
最佳适用场景: 展示部分与整体的关系
typescript
<PieChart>
<Pie data={data} dataKey="value" nameKey="name" fill="#8884d8" />
</PieChart>使用时机:
- 市场份额、预算分配
- 比例关系(最多5-7个分类)
- 简单百分比展示
⚠️ 避免使用场景:
- 分类过多(超过7个)
- 需要精确对比(建议使用柱状图)
Area Chart
面积图
Best for: Volume over time
typescript
<AreaChart data={data}>
<Area type="monotone" dataKey="value" fill="#8884d8" />
</AreaChart>Use when:
- Cumulative totals
- Filled regions show magnitude
- Stacked categories
最佳适用场景: 展示随时间的体量变化
typescript
<AreaChart data={data}>
<Area type="monotone" dataKey="value" fill="#8884d8" />
</AreaChart>使用时机:
- 累计总量
- 填充区域展示量级
- 堆叠分类
Scatter Plot
散点图
Best for: Correlation between variables
typescript
<ScatterChart>
<Scatter data={data} fill="#8884d8" />
</ScatterChart>Use when:
- Finding correlations
- Outlier detection
- Distribution analysis
最佳适用场景: 展示变量间的相关性
typescript
<ScatterChart>
<Scatter data={data} fill="#8884d8" />
</ScatterChart>使用时机:
- 寻找变量相关性
- 异常值检测
- 分布分析
Heatmap
热力图
Best for: Intensity across two dimensions
typescript
// Using D3
const colorScale = d3.scaleSequential(d3.interpolateBlues).domain([0, d3.max(data)])
svg
.selectAll('rect')
.data(data)
.join('rect')
.attr('fill', d => colorScale(d.value))Use when:
- Time-based patterns (day/hour)
- Geographic intensity
- Matrix data
最佳适用场景: 展示二维维度的强度
typescript
// 使用D3实现
const colorScale = d3.scaleSequential(d3.interpolateBlues).domain([0, d3.max(data)])
svg
.selectAll('rect')
.data(data)
.join('rect')
.attr('fill', d => colorScale(d.value))使用时机:
- 时间模式(日/小时)
- 地理强度分布
- 矩阵数据
Responsive Design
响应式设计
Pattern: Mobile-Friendly Charts
移动端友好图表模式
typescript
'use client'
import { useEffect, useState } from 'react'
import { LineChart, Line, ResponsiveContainer } from 'recharts'
export default function ResponsiveChart({ data }) {
const [isMobile, setIsMobile] = useState(false)
useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768)
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
return (
<ResponsiveContainer width="100%" height={isMobile ? 200 : 400}>
<LineChart data={data}>
<Line
dataKey="value"
stroke="#8884d8"
strokeWidth={isMobile ? 1 : 2}
/>
</LineChart>
</ResponsiveContainer>
)
}typescript
'use client'
import { useEffect, useState } from 'react'
import { LineChart, Line, ResponsiveContainer } from 'recharts'
export default function ResponsiveChart({ data }) {
const [isMobile, setIsMobile] = useState(false)
useEffect(() => {
const checkMobile = () => setIsMobile(window.innerWidth < 768)
checkMobile()
window.addEventListener('resize', checkMobile)
return () => window.removeEventListener('resize', checkMobile)
}, [])
return (
<ResponsiveContainer width="100%" height={isMobile ? 200 : 400}>
<LineChart data={data}>
<Line
dataKey="value"
stroke="#8884d8"
strokeWidth={isMobile ? 1 : 2}
/>
</LineChart>
</ResponsiveContainer>
)
}Color Schemes
配色方案
Accessible Colors
可访问性配色
typescript
// colors.ts
export const chartColors = {
// WCAG AA compliant
primary: '#0066CC', // Blue
success: '#007A3D', // Green
warning: '#C87000', // Orange
danger: '#D32F2F', // Red
// Multi-series (colorblind-safe)
series: [
'#0066CC', // Blue
'#CC6600', // Orange
'#7A00CC', // Purple
'#00CC66', // Green
'#CC0066' // Magenta
]
}Colorblind-Safe Palettes:
typescript
// For up to 5 data series
const colorblindSafe = [
'#000000', // Black
'#E69F00', // Orange
'#56B4E9', // Sky Blue
'#009E73', // Green
'#F0E442' // Yellow
]typescript
// colors.ts
export const chartColors = {
// 符合WCAG AA标准
primary: '#0066CC', // 蓝色
success: '#007A3D', // 绿色
warning: '#C87000', // 橙色
danger: '#D32F2F', // 红色
// 多系列配色(色盲友好)
series: [
'#0066CC', // 蓝色
'#CC6600', // 橙色
'#7A00CC', // 紫色
'#00CC66', // 绿色
'#CC0066' // 品红色
]
}色盲友好调色板:
typescript
// 最多支持5个数据系列
const colorblindSafe = [
'#000000', // 黑色
'#E69F00', // 橙色
'#56B4E9', // 天蓝色
'#009E73', // 绿色
'#F0E442' // 黄色
]Data Formatting
数据格式化
Number Formatting
数字格式化
typescript
// utils/formatters.ts
export function formatCurrency(value: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(value)
}
export function formatPercent(value: number): string {
return new Intl.NumberFormat('en-US', {
style: 'percent',
minimumFractionDigits: 1,
maximumFractionDigits: 1,
}).format(value / 100)
}
export function formatNumber(value: number): string {
if (value >= 1000000) {
return `${(value / 1000000).toFixed(1)}M`
}
if (value >= 1000) {
return `${(value / 1000).toFixed(1)}K`
}
return value.toFixed(0)
}
// Usage in chart
<YAxis tickFormatter={formatCurrency} />typescript
// utils/formatters.ts
export function formatCurrency(value: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(value)
}
export function formatPercent(value: number): string {
return new Intl.NumberFormat('en-US', {
style: 'percent',
minimumFractionDigits: 1,
maximumFractionDigits: 1,
}).format(value / 100)
}
export function formatNumber(value: number): string {
if (value >= 1000000) {
return `${(value / 1000000).toFixed(1)}M`
}
if (value >= 1000) {
return `${(value / 1000).toFixed(1)}K`
}
return value.toFixed(0)
}
// 在图表中使用
<YAxis tickFormatter={formatCurrency} />Export Functionality
导出功能
Export Chart as PNG
将图表导出为PNG
typescript
'use client'
import html2canvas from 'html2canvas'
export function ExportableChart({ children }) {
const chartRef = useRef<HTMLDivElement>(null)
const exportToPNG = async () => {
if (!chartRef.current) return
const canvas = await html2canvas(chartRef.current)
const link = document.createElement('a')
link.download = 'chart.png'
link.href = canvas.toDataURL()
link.click()
}
return (
<div>
<button onClick={exportToPNG} className="mb-4 px-4 py-2 bg-blue-600 text-white rounded">
Export as PNG
</button>
<div ref={chartRef}>
{children}
</div>
</div>
)
}typescript
'use client'
import html2canvas from 'html2canvas'
export function ExportableChart({ children }) {
const chartRef = useRef<HTMLDivElement>(null)
const exportToPNG = async () => {
if (!chartRef.current) return
const canvas = await html2canvas(chartRef.current)
const link = document.createElement('a')
link.download = 'chart.png'
link.href = canvas.toDataURL()
link.click()
}
return (
<div>
<button onClick={exportToPNG} className="mb-4 px-4 py-2 bg-blue-600 text-white rounded">
导出为PNG
</button>
<div ref={chartRef}>
{children}
</div>
</div>
)
}Export Data as CSV
将数据导出为CSV
typescript
export function exportToCSV(data: any[], filename: string) {
const headers = Object.keys(data[0])
const csv = [
headers.join(','),
...data.map(row => headers.map(h => row[h]).join(','))
].join('\n')
const blob = new Blob([csv], { type: 'text/csv' })
const link = document.createElement('a')
link.download = `${filename}.csv`
link.href = URL.createObjectURL(blob)
link.click()
}
// Usage
<button onClick={() => exportToCSV(data, 'sales-data')}>
Export to CSV
</button>typescript
export function exportToCSV(data: any[], filename: string) {
const headers = Object.keys(data[0])
const csv = [
headers.join(','),
...data.map(row => headers.map(h => row[h]).join(','))
].join('\n')
const blob = new Blob([csv], { type: 'text/csv' })
const link = document.createElement('a')
link.download = `${filename}.csv`
link.href = URL.createObjectURL(blob)
link.click()
}
// 使用示例
<button onClick={() => exportToCSV(data, 'sales-data')}>
导出为CSV
</button>Performance Optimization
性能优化
Lazy Loading Charts
懒加载图表
typescript
// Lazy load chart libraries (reduce initial bundle)
import dynamic from 'next/dynamic'
const LineChart = dynamic(
() => import('recharts').then(mod => mod.LineChart),
{ ssr: false }
)
export default function ChartPage() {
return <LineChart data={data} />
}typescript
// 懒加载图表库(减少初始包体积)
import dynamic from 'next/dynamic'
const LineChart = dynamic(
() => import('recharts').then(mod => mod.LineChart),
{ ssr: false }
)
export default function ChartPage() {
return <LineChart data={data} />
}Virtualization for Large Datasets
大数据集虚拟化
typescript
import { useVirtualizer } from '@tanstack/react-virtual'
export function LargeDataTable({ data }: { data: any[] }) {
const parentRef = useRef<HTMLDivElement>(null)
const virtualizer = useVirtualizer({
count: data.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
})
return (
<div ref={parentRef} className="h-96 overflow-auto">
<div style={{ height: `${virtualizer.getTotalSize()}px` }}>
{virtualizer.getVirtualItems().map((virtualRow) => (
<div key={virtualRow.index} className="py-2 border-b">
{data[virtualRow.index].name}: {data[virtualRow.index].value}
</div>
))}
</div>
</div>
)
}typescript
import { useVirtualizer } from '@tanstack/react-virtual'
export function LargeDataTable({ data }: { data: any[] }) {
const parentRef = useRef<HTMLDivElement>(null)
const virtualizer = useVirtualizer({
count: data.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
})
return (
<div ref={parentRef} className="h-96 overflow-auto">
<div style={{ height: `${virtualizer.getTotalSize()}px` }}>
{virtualizer.getVirtualItems().map((virtualRow) => (
<div key={virtualRow.index} className="py-2 border-b">
{data[virtualRow.index].name}: {data[virtualRow.index].value}
</div>
))}
</div>
</div>
)
}Animation Best Practices
动画最佳实践
Smooth Transitions
平滑过渡
typescript
<LineChart data={data}>
<Line
type="monotone"
dataKey="value"
stroke="#8884d8"
animationDuration={500}
animationEasing="ease-in-out"
/>
</LineChart>typescript
<LineChart data={data}>
<Line
type="monotone"
dataKey="value"
stroke="#8884d8"
animationDuration={500}
animationEasing="ease-in-out"
/>
</LineChart>Disable Animation for Real-Time
实时场景禁用动画
typescript
// For real-time dashboards, disable animation
<Line
dataKey="value"
isAnimationActive={false}
/>typescript
// 对于实时仪表盘,禁用动画
<Line
dataKey="value"
isAnimationActive={false}
/>Common Patterns
常见模式
Pattern: Drill-Down Chart
下钻图表模式
typescript
'use client'
import { useState } from 'react'
import { BarChart, Bar, XAxis, YAxis } from 'recharts'
export default function DrillDownChart() {
const [level, setLevel] = useState<'year' | 'month' | 'day'>('year')
const [selectedYear, setSelectedYear] = useState<number | null>(null)
const handleBarClick = (data: any) => {
if (level === 'year') {
setSelectedYear(data.year)
setLevel('month')
} else if (level === 'month') {
setLevel('day')
}
}
const goBack = () => {
if (level === 'day') setLevel('month')
else if (level === 'month') {
setLevel('year')
setSelectedYear(null)
}
}
return (
<div>
{level !== 'year' && (
<button onClick={goBack} className="mb-4">← Back</button>
)}
<BarChart data={getData(level, selectedYear)} width={600} height={300}>
<Bar dataKey="value" fill="#8884d8" onClick={handleBarClick} />
<XAxis dataKey="name" />
<YAxis />
</BarChart>
</div>
)
}typescript
'use client'
import { useState } from 'react'
import { BarChart, Bar, XAxis, YAxis } from 'recharts'
export default function DrillDownChart() {
const [level, setLevel] = useState<'year' | 'month' | 'day'>('year')
const [selectedYear, setSelectedYear] = useState<number | null>(null)
const handleBarClick = (data: any) => {
if (level === 'year') {
setSelectedYear(data.year)
setLevel('month')
} else if (level === 'month') {
setLevel('day')
}
}
const goBack = () => {
if (level === 'day') setLevel('month')
else if (level === 'month') {
setLevel('year')
setSelectedYear(null)
}
}
return (
<div>
{level !== 'year' && (
<button onClick={goBack} className="mb-4">← 返回</button>
)}
<BarChart data={getData(level, selectedYear)} width={600} height={300}>
<Bar dataKey="value" fill="#8884d8" onClick={handleBarClick} />
<XAxis dataKey="name" />
<YAxis />
</BarChart>
</div>
)
}When to Use Me
适用场景
Perfect for:
- Building analytics dashboards
- Creating interactive charts
- Data storytelling
- Real-time monitoring
- Visualizing complex datasets
I'll help you:
- Choose the right chart type
- Implement responsive layouts
- Add interactivity
- Optimize performance
- Ensure accessibility
最佳适用场景:
- 搭建分析仪表盘
- 创建交互式图表
- 数据叙事
- 实时监控
- 复杂数据集可视化
我可以帮你:
- 选择合适的图表类型
- 实现响应式布局
- 添加交互效果
- 优化性能
- 确保可访问性
What I'll Create
我能创建的内容
📊 Charts and Visualizations
📈 KPI Dashboards
🎨 Custom Color Schemes
📱 Responsive Layouts
⚡ Real-Time Updates
💾 Export FunctionalityLet's make your data beautiful and understandable!
📊 图表与可视化内容
📈 KPI仪表盘
🎨 自定义配色方案
📱 响应式布局
⚡ 实时更新
💾 导出功能让我们一起让你的数据变得美观且易于理解!