123 lines
5.2 KiB
TypeScript
123 lines
5.2 KiB
TypeScript
import {
|
|
Bar,
|
|
BarChart,
|
|
CartesianGrid,
|
|
Legend,
|
|
Line,
|
|
LineChart,
|
|
ResponsiveContainer,
|
|
Tooltip,
|
|
XAxis,
|
|
YAxis,
|
|
} from 'recharts';
|
|
import { SurfaceCard } from '../../../components/ui/surface';
|
|
import type { DailyMileageReport } from '../api';
|
|
import { fmtDate, fmtKm } from './utils';
|
|
|
|
interface ReportChartsProps {
|
|
report: DailyMileageReport;
|
|
trendTargetId: number | null;
|
|
onTrendTargetChange: (targetId: number | null) => void;
|
|
}
|
|
|
|
export default function ReportCharts({
|
|
report,
|
|
trendTargetId,
|
|
onTrendTargetChange,
|
|
}: ReportChartsProps) {
|
|
const comparisonData = report.groups.map(group => ({
|
|
name: group.displayName,
|
|
运营里程: group.operatingMileage,
|
|
库存里程: group.inventoryMileage,
|
|
}));
|
|
const trendGroup = trendTargetId == null
|
|
? null
|
|
: report.groups.find(group => group.targetId === trendTargetId) || null;
|
|
const trendData = trendGroup?.trend || report.trend;
|
|
const trendName = trendGroup?.displayName || '总计';
|
|
const trendSubtitle = '选择总计或车型查看近 7 日每日里程,包含库存车辆里程';
|
|
const trendColor = trendGroup ? '#0f766e' : '#2563eb';
|
|
|
|
return (
|
|
<div className="grid gap-3 xl:grid-cols-[minmax(0,1.35fr)_minmax(360px,0.65fr)]">
|
|
<SurfaceCard
|
|
title="近 7 日里程趋势"
|
|
subtitle={trendSubtitle}
|
|
actions={(
|
|
<div className="flex max-w-full gap-1 overflow-x-auto rounded-lg bg-slate-100 p-1">
|
|
<button
|
|
type="button"
|
|
onClick={() => onTrendTargetChange(null)}
|
|
className={`h-7 shrink-0 rounded-md px-2.5 text-[10px] font-black transition-colors ${trendGroup == null ? 'bg-white text-blue-600 shadow-sm' : 'text-slate-500 hover:text-slate-700'}`}
|
|
>
|
|
总计
|
|
</button>
|
|
{report.groups.map(group => (
|
|
<button
|
|
key={group.targetId}
|
|
type="button"
|
|
onClick={() => onTrendTargetChange(group.targetId)}
|
|
className={`h-7 shrink-0 rounded-md px-2.5 text-[10px] font-black transition-colors ${trendTargetId === group.targetId ? 'bg-white text-emerald-700 shadow-sm' : 'text-slate-500 hover:text-slate-700'}`}
|
|
>
|
|
{group.displayName}
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
>
|
|
<div className="h-[270px] px-2 py-3">
|
|
<ResponsiveContainer
|
|
width="100%"
|
|
height="100%"
|
|
minWidth={0}
|
|
minHeight={0}
|
|
initialDimension={{ width: 640, height: 246 }}
|
|
>
|
|
<LineChart data={trendData} margin={{ top: 14, right: 18, left: -4, bottom: 0 }}>
|
|
<CartesianGrid vertical={false} stroke="#e2e8f0" strokeDasharray="3 3" />
|
|
<XAxis dataKey="date" axisLine={false} tickLine={false} tick={{ fontSize: 10, fill: '#94a3b8' }} tickFormatter={fmtDate} />
|
|
<YAxis axisLine={false} tickLine={false} width={58} tick={{ fontSize: 10, fill: '#94a3b8' }} tickFormatter={fmtKm} />
|
|
<Tooltip
|
|
labelFormatter={label => fmtDate(String(label))}
|
|
formatter={value => [`${fmtKm(Number(value))} km`, trendName]}
|
|
contentStyle={{ borderRadius: 8, border: '1px solid #e2e8f0', fontSize: 11 }}
|
|
/>
|
|
<Line type="monotone" dataKey="totalMileage" stroke={trendColor} strokeWidth={2.5} dot={{ r: 3, fill: trendColor }} activeDot={{ r: 5 }} />
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
</SurfaceCard>
|
|
|
|
<SurfaceCard
|
|
title={(
|
|
<span className="flex flex-wrap items-center gap-2">
|
|
<span>车型里程构成</span>
|
|
<span className="rounded-md bg-blue-50 px-1.5 py-0.5 text-[10px] font-black text-blue-600 ring-1 ring-blue-100">统计日期 {fmtDate(report.reportDate)}</span>
|
|
</span>
|
|
)}
|
|
subtitle="运营与库存里程分开展示"
|
|
>
|
|
<div className="h-[270px] px-2 py-3">
|
|
<ResponsiveContainer
|
|
width="100%"
|
|
height="100%"
|
|
minWidth={0}
|
|
minHeight={0}
|
|
initialDimension={{ width: 480, height: 246 }}
|
|
>
|
|
<BarChart data={comparisonData} layout="vertical" margin={{ top: 6, right: 14, left: 8, bottom: 0 }}>
|
|
<CartesianGrid horizontal={false} stroke="#e2e8f0" strokeDasharray="3 3" />
|
|
<XAxis type="number" axisLine={false} tickLine={false} tick={{ fontSize: 10, fill: '#94a3b8' }} tickFormatter={fmtKm} />
|
|
<YAxis type="category" dataKey="name" width={92} axisLine={false} tickLine={false} tick={{ fontSize: 10, fill: '#64748b', fontWeight: 700 }} />
|
|
<Tooltip formatter={(value, name) => [`${fmtKm(Number(value))} km`, String(name)]} contentStyle={{ borderRadius: 8, border: '1px solid #e2e8f0', fontSize: 11 }} />
|
|
<Legend wrapperStyle={{ fontSize: 10, fontWeight: 700 }} />
|
|
<Bar dataKey="运营里程" stackId="mileage" fill="#10b981" radius={[3, 0, 0, 3]} />
|
|
<Bar dataKey="库存里程" stackId="mileage" fill="#f59e0b" radius={[0, 3, 3, 0]} />
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
</SurfaceCard>
|
|
</div>
|
|
);
|
|
}
|