Files
SBrainCO/client/src/pages/MonthlyReviewPage.tsx
T
2026-07-26 22:48:08 +08:00

125 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useQuery } from '@tanstack/react-query'
import api from '@/lib/api'
import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer } from 'recharts'
import { DataTable } from '@/components/DataTable'
import { Badge } from '@/components/Badge'
import { MetricCard } from '@/components/MetricCard'
import { Pagination } from '@/components/Pagination'
import { useState, useMemo } from 'react'
const REVIEW_COLORS = { '达标': '#22c55e', '改善中': '#eab308', '未改善': '#ef4444' }
const PAGE_SIZE = 10
export function MonthlyReviewPage() {
const [month, setMonth] = useState('2026-05')
const [page, setPage] = useState(1)
const { data } = useQuery({
queryKey: ['monthly-review', month],
queryFn: () => api.get('/tasks/monthly-review', { params: { month } }),
})
const rd = (data as any)?.data
const summary = rd?.summary || { total: 0, passed: 0, improving: 0, failed: 0 }
const details = rd?.details || []
const pagedDetails = useMemo(() => details.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), [details, page])
const pieData = [
{ name: '达标', value: summary.passed },
{ name: '改善中', value: summary.improving },
{ name: '未改善', value: summary.failed },
].filter((d) => d.value > 0)
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-xl font-bold"></h1>
<input
type="month"
value={month}
onChange={(e) => setMonth(e.target.value)}
className="rounded-md border px-3 py-1.5 text-sm"
/>
</div>
{/* 汇总卡片 */}
<div className="grid grid-cols-2 gap-3 md:grid-cols-4">
<MetricCard title="总任务数" value={summary.total} />
<MetricCard title="达标" value={summary.passed} />
<MetricCard title="改善中" value={summary.improving} />
<MetricCard title="未改善" value={summary.failed} />
</div>
<div className="grid gap-4 lg:grid-cols-2">
{/* 饼图 */}
<div className="rounded-lg border bg-card p-4">
<h2 className="mb-3 text-sm font-bold"></h2>
{pieData.length > 0 ? (
<ResponsiveContainer width="100%" height={250}>
<PieChart>
<Pie data={pieData} cx="50%" cy="50%" outerRadius={80} dataKey="value"
label={({ name, value }: any) => `${name}: ${value}`}>
{pieData.map((d) => <Cell key={d.name} fill={REVIEW_COLORS[d.name as keyof typeof REVIEW_COLORS]} />)}
</Pie>
<Tooltip />
</PieChart>
</ResponsiveContainer>
) : <div className="py-20 text-center text-muted-foreground"></div>}
</div>
{/* 经验标准化 */}
<div className="rounded-lg border bg-card p-4">
<h2 className="mb-3 text-sm font-bold"></h2>
<p className="text-sm text-muted-foreground mb-3">广</p>
<PracticeSection />
</div>
</div>
{/* 明细表 */}
<div className="rounded-lg border bg-card p-4">
<h2 className="mb-3 text-sm font-bold"></h2>
<Pagination page={page} pageSize={PAGE_SIZE} total={details.length} onPageChange={setPage} />
<div className="mt-3">
<DataTable
columns={[
{ key: 'store_name', label: '门店' },
{ key: 'problem_indicator', label: '问题指标' },
{ key: 'baseline_value', label: '基线', align: 'right' },
{ key: 'target_value', label: '目标', align: 'right' },
{ key: 'actual_value', label: '实际', align: 'right' },
{ key: 'review_result', label: '结果', render: (r) => <Badge type="review" text={r.review_result} /> },
]}
data={pagedDetails}
/>
</div>
</div>
</div>
)
}
function PracticeSection() {
const { data } = useQuery({
queryKey: ['practices'],
queryFn: () => api.get('/tasks/practices'),
})
const practices = (data as any)?.data || []
return (
<div className="space-y-2">
{practices.length === 0 ? (
<p className="text-sm text-muted-foreground"></p>
) : practices.map((p: any) => (
<div key={p.id} className="rounded-md border p-3">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">{p.practice_module}</span>
<Badge text={p.status} />
</div>
<p className="mt-1 text-xs text-muted-foreground">{p.benchmark_store_name}</p>
<p className="mt-1 text-xs">{p.key_actions}</p>
</div>
))}
</div>
)
}