feat: 配送物化视图优化查询性能 + 饼图统一处理 + SKU长尾治理明细 + 地图初始聚焦北京
This commit is contained in:
@@ -14,7 +14,7 @@ const SCORE_DIMENSIONS = [
|
||||
{ key: 'score_revenue', label: '营收规模', max: 20 },
|
||||
{ key: 'score_cost', label: '成本控制', max: 20 },
|
||||
{ key: 'score_margin', label: '毛利率', max: 15 },
|
||||
{ key: 'score_risk', label: '风险等级', max: 10 },
|
||||
{ key: 'score_risk', label: '风险控制', max: 10 },
|
||||
{ key: 'score_repeat', label: '复购率', max: 10 },
|
||||
{ key: 'score_member', label: '会员占比', max: 10 },
|
||||
{ key: 'score_task', label: '任务完成', max: 10 },
|
||||
@@ -46,7 +46,7 @@ export function StorePage() {
|
||||
})
|
||||
const stores = ((storesData as any)?.data || []).map((s: any) => ({ code: s.store_code, name: s.store_name, risk: s.risk_level, issue: s.primary_issue }))
|
||||
|
||||
const { data: priorityData } = useQuery({
|
||||
const { data: priorityData, isLoading: priorityLoading } = useQuery({
|
||||
queryKey: ['stores-priority-detail', month],
|
||||
queryFn: () => api.get('/stores/priority', { params: { month } }),
|
||||
})
|
||||
@@ -71,23 +71,23 @@ export function StorePage() {
|
||||
queryFn: () => api.get('/tasks/followup', { params: { month } }),
|
||||
})
|
||||
|
||||
const { data: skuData } = useQuery({
|
||||
const { data: skuData, isLoading: skuLoading } = useQuery({
|
||||
queryKey: ['sku-attach', storeCode],
|
||||
queryFn: () => api.get('/sku/attach', { params: { month } }),
|
||||
})
|
||||
|
||||
const { data: abcData } = useQuery({
|
||||
const { data: abcData, isLoading: abcLoading } = useQuery({
|
||||
queryKey: ['sku-abc'],
|
||||
queryFn: () => api.get('/sku/abc', { params: { month } }),
|
||||
})
|
||||
|
||||
// StoreDetailPage queries
|
||||
const { data: storeDetailData } = useQuery({
|
||||
const { data: storeDetailData, isLoading: storeDetailLoading } = useQuery({
|
||||
queryKey: ['store-detail', storeCode, month],
|
||||
queryFn: () => api.get(`/stores/${storeCode}`, { params: { month } }),
|
||||
})
|
||||
|
||||
const { data: healthData } = useQuery({
|
||||
const { data: healthData, isLoading: healthLoading } = useQuery({
|
||||
queryKey: ['store-health', storeCode, month],
|
||||
queryFn: () => api.get('/situational-awareness/health-score', { params: { month } }),
|
||||
})
|
||||
@@ -217,7 +217,7 @@ export function StorePage() {
|
||||
return 'bg-green-100 text-green-700 border-green-300'
|
||||
}
|
||||
|
||||
const pageLoading = storesLoading || cardLoading || tasksLoading || dailyLoading || followupLoading
|
||||
const pageLoading = storesLoading || priorityLoading || cardLoading || tasksLoading || dailyLoading || followupLoading || skuLoading || abcLoading || storeDetailLoading || healthLoading
|
||||
|
||||
if (pageLoading) {
|
||||
return <LoadingSpinner text="加载店长工作台..." />
|
||||
@@ -267,7 +267,7 @@ export function StorePage() {
|
||||
{/* 经营概览 */}
|
||||
<div className="rounded-lg border bg-card p-4">
|
||||
<div className="mb-3 flex items-center justify-between">
|
||||
<h2 className="text-sm font-bold">最新经营概览({month})</h2>
|
||||
<h2 className="text-sm font-bold">日均经营概览({month})</h2>
|
||||
{anomalyCount > 0 && (
|
||||
<span className="rounded-md bg-red-100 px-2 py-0.5 text-xs font-medium text-red-700">{anomalyCount} 项异常</span>
|
||||
)}
|
||||
@@ -510,10 +510,10 @@ export function StorePage() {
|
||||
{/* ========== 门店详情区 ========== */}
|
||||
{sc && Number(sc.received) > 0 && (
|
||||
<>
|
||||
{/* 核心指标 */}
|
||||
{/* 核心指标 - 月度汇总 */}
|
||||
<div className="grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-6">
|
||||
<MetricCard title="实收" value={sc.received} format="currency" />
|
||||
<MetricCard title="账单数" value={sc.bill_count} format="number" />
|
||||
<MetricCard title="月度实收" value={sc.received} format="currency" />
|
||||
<MetricCard title="月度账单数" value={sc.bill_count} format="number" />
|
||||
<MetricCard title="客单价" value={sc.avg_bill_value} format="currency" />
|
||||
<MetricCard title="优惠率" value={sc.discount_rate_pct} format="percent" />
|
||||
<MetricCard title="理论毛利率" value={sc.theoretical_margin_pct} format="percent" />
|
||||
@@ -611,8 +611,8 @@ export function StorePage() {
|
||||
<PolarGrid />
|
||||
<PolarAngleAxis dataKey="dimension" tick={{ fontSize: 10 }} />
|
||||
<PolarRadiusAxis angle={90} domain={[0, 100]} tick={{ fontSize: 8 }} />
|
||||
<Radar dataKey="score" stroke={Number(health.health_score) >= 75 ? '#22c55e' : Number(health.health_score) >= 55 ? '#eab308' : '#ef4444'} fill={Number(health.health_score) >= 75 ? '#22c55e' : Number(health.health_score) >= 55 ? '#eab308' : '#ef4444'} fillOpacity={0.3} />
|
||||
<Tooltip />
|
||||
<Radar dataKey="score" name="得分" stroke={Number(health.health_score) >= 75 ? '#22c55e' : Number(health.health_score) >= 55 ? '#eab308' : '#ef4444'} fill={Number(health.health_score) >= 75 ? '#22c55e' : Number(health.health_score) >= 55 ? '#eab308' : '#ef4444'} fillOpacity={0.3} />
|
||||
<Tooltip formatter={(v: any) => [v, '得分']} />
|
||||
</RadarChart>
|
||||
</ResponsiveContainer>
|
||||
<div className="space-y-2">
|
||||
@@ -718,10 +718,11 @@ export function StorePage() {
|
||||
<p className="mb-2 text-xs font-medium text-muted-foreground">门店品类占比</p>
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<PieChart>
|
||||
<Pie data={pieData} cx="50%" cy="50%" outerRadius={70} dataKey="value" label={({ name, value }: any) => `${name} ${value}%`}>
|
||||
<Pie data={pieData} cx="50%" cy="50%" outerRadius={60} dataKey="value" nameKey="name" labelLine={false} label={({ name, value }: any) => <tspan fontSize={11}>{`${name} ${value}%`}</tspan>}>
|
||||
{pieData.map((d, i) => <Cell key={i} fill={d.fill} />)}
|
||||
</Pie>
|
||||
<Tooltip />
|
||||
<Tooltip formatter={(v: any) => [`${v}%`, '占比']} />
|
||||
<Legend wrapperStyle={{ fontSize: 10 }} />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
@@ -732,7 +733,7 @@ export function StorePage() {
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="name" tick={{ fontSize: 10 }} />
|
||||
<YAxis unit="%" tick={{ fontSize: 10 }} />
|
||||
<Tooltip />
|
||||
<Tooltip formatter={(v: any) => [`${v}%`, '占比']} />
|
||||
<Legend wrapperStyle={{ fontSize: 10 }} />
|
||||
<Bar dataKey="门店占比" fill="#3b82f6" />
|
||||
<Bar dataKey="公司占比" fill="#e2e8f0" />
|
||||
|
||||
Reference in New Issue
Block a user