chore(history): import initial web product snapshot

This commit is contained in:
wmq
2026-07-25 00:30:00 +08:00
commit cf0eed46c3
131 changed files with 15693 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
import { useQuery } from '@tanstack/react-query'
import { PageLoading, ErrorState } from '@/components/ui/feedback'
import { PageHeader } from '@/components/common/PageHeader'
import { Badge } from '@/components/ui/badge'
import { Card, CardHeader } from '@/components/ui/card'
import { StatCard } from '@/components/common/StatCard'
import { DemoNote } from '@/components/common/DemoNote'
import { CountryFlag } from '@/components/common/CountryFlag'
import { ChartCard, TrendChart, DonutChart, FunnelChart, RankBars, SimpleBarChart } from '@/components/charts'
import { getAnalytics } from '@/services/api/org'
import { formatNumber } from '@/utils/format'
import { Activity, BadgePercent, TrendingUp, Users, Zap, Timer, AlertTriangle, Target } from 'lucide-react'
export function AnalyticsPage() {
const { data, isLoading, isError, refetch } = useQuery({
queryKey: ['analytics'],
queryFn: () => getAnalytics(),
})
if (isLoading) return <PageLoading />
if (isError || !data) return <ErrorState onRetry={() => refetch()} />
const k = data.kpis
return (
<div className="p-4 md:p-6">
<PageHeader
title="数据分析中心"
description="企业经营数据全景 · 当前展示演示数据"
actions={<DemoNote />}
extra={
<div className="flex flex-wrap items-center gap-2">
{['today', '7d', '30d', 'quarter'].map((p) => (
<button key={p} className="rounded-full bg-muted px-3 py-1 text-xs text-muted-foreground hover:bg-border">{p === '30d' ? '近30天' : p === '7d' ? '近7天' : p === 'today' ? '今日' : '本季度'}</button>
))}
</div>
}
/>
<div className="grid grid-cols-2 gap-3 md:grid-cols-5 mb-4">
<StatCard title="新增客户" value={k.newCustomers.value} delta={k.newCustomers.delta} icon={Users} />
<StatCard title="有效询盘" value={k.validInquiries.value} delta={k.validInquiries.delta} icon={Activity} />
<StatCard title="高意向客户" value={k.highIntent.value} delta={k.highIntent.delta} icon={Target} />
<StatCard title="转化率" value={`${k.conversionRate.value}%`} delta={k.conversionRate.delta} icon={BadgePercent} />
<StatCard title="AI采纳率" value={`${k.aiAdoptionRate.value}%`} delta={k.aiAdoptionRate.delta} icon={Zap} />
</div>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<ChartCard title="客户增长趋势" height={260}><TrendChart data={data.customerGrowth} series={[{ key: 'count', name: '新增客户', color: '#247b71' }]} /></ChartCard>
<ChartCard title="各渠道询盘数量" height={260}><SimpleBarChart data={data.channelInquiries} xKey="name" barKey="count" name="询盘数" /></ChartCard>
<ChartCard title="客户国家/地区分布" height={260}><DonutChart data={data.countryDistribution} /></ChartCard>
<ChartCard title="客户阶段转化漏斗" height={260}><FunnelChart data={data.funnel} /></ChartCard>
<ChartCard title="意向分数分布" height={260}><DonutChart data={data.intentDistribution} nameKey="range" /></ChartCard>
<ChartCard title="产品关注度排行" height={260}><RankBars data={data.productRank.map((p) => ({ name: p.product, value: p.inquiries }))} /></ChartCard>
<ChartCard title="团队回复效率排行" height={240}><RankBars data={data.teamReplyRank.map((r) => ({ name: r.name, value: r.avgResponseMin }))} unit="分钟" max={30} /></ChartCard>
<ChartCard title="团队客户转化排行" height={240}><RankBars data={data.teamConversionRank.map((r) => ({ name: r.name, value: r.rate }))} unit="%" /></ChartCard>
</div>
{/* 老板看板 */}
<h2 className="mt-6 mb-3 text-lg font-semibold">老板看板 <DemoNote className="align-middle" /></h2>
<div className="grid grid-cols-1 gap-4 lg:grid-cols-3">
<Card><CardHeader title="🔥 今日需处理的高价值客户" /><div className="divide-y divide-border">{data.bossBoard.urgentHighValue.map((c) => (<div key={c.id} className="flex items-center justify-between px-4 py-2.5 text-sm"><span>{c.name} <CountryFlag code={c.country} withName={false} /></span><span className="text-xs text-muted-foreground">{c.reason.slice(0, 20)}…</span></div>))}</div></Card>
<Card><CardHeader title="⚠️ 存在逾期跟进的业务员" /><div className="divide-y divide-border">{data.bossBoard.overdueMembers.map((m) => (<div key={m.id} className="flex items-center justify-between px-4 py-2.5 text-sm"><span>{m.name}</span><Badge variant="danger">{m.overdueCount} 项逾期</Badge></div>))}</div></Card>
<Card><CardHeader title="📈 客户增长最快的国家" /><div className="divide-y divide-border">{data.bossBoard.fastestCountries.map((c) => (<div key={c.country} className="flex items-center justify-between px-4 py-2.5 text-sm"><CountryFlag code={c.country} /><Badge variant="success">+{(c.growth * 100).toFixed(0)}%</Badge></div>))}</div></Card>
<Card><CardHeader title="🏅 最优渠道" /><div className="p-4 text-center"><p className="text-2xl font-semibold text-primary-700">{data.bossBoard.bestChannel.name}</p><p className="text-sm text-muted-foreground">转化率 {data.bossBoard.bestChannel.conversion}%</p></div></Card>
<Card><CardHeader title="📊 最热门产品" /><div className="space-y-2 p-4">{data.bossBoard.topProducts.map((p) => (<div key={p.product} className="flex items-center justify-between text-sm"><span>{p.product}</span><span className="text-muted-foreground">{p.inquiries} 次询盘</span></div>))}</div></Card>
<Card><CardHeader title="🚨 正在流失的高意向客户" /><div className="divide-y divide-border">{data.bossBoard.churningHighIntent.map((c) => (<div key={c.id} className="px-4 py-2.5 text-sm"><span>{c.name} <CountryFlag code={c.country} withName={false} /></span><span className="text-xs text-muted-foreground ml-2">{c.daysSilent}天未联系 · 意向{c.intentScore}分</span></div>))}</div></Card>
</div>
</div>
)
}