feat: 全部页面添加 MonthPicker 月份切换
新增 MonthPicker 的页面(6个): - CostPage: 成本与库存管理 - SKUPage: 商品SKU管理 - PlatformPage: 平台与优惠管理 - MemberPage: 会员与复购管理 - SiteSelectionPage: 门店选址分析 - DataQualityPage: 数据质量看板 容器+子组件添加 MonthPicker(2个页面, 11个子组件): - StoreExpensePage: 门店营业费用分析(10个子组件全部传month) - CostAnalysisPage/StoreTab: 门店成本tab 跳过(后端不支持month参数): - RiskPage: 风险管理(全量视图, 不按月过滤) - TimePage: 时间段分析(全量视图, 不按月过滤)
This commit is contained in:
@@ -17,12 +17,12 @@ const SAFETY_COLORS: Record<string, string> = {
|
||||
'安全边际充足': 'text-green-600',
|
||||
}
|
||||
|
||||
export function BreakEvenTab() {
|
||||
export function BreakEvenTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('safety_margin_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('asc')
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/break-even', page, sort, filter, order], queryFn: () => api.get(`/store-expense/break-even?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/break-even', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/break-even?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载盈亏平衡分析..." />
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import { formatCurrency, formatPercent } from '@/lib/utils'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export function ContributionTab() {
|
||||
export function ContributionTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('actual_store_contribution_rate_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('asc')
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/contribution', page, sort, filter, order], queryFn: () => api.get(`/store-expense/store-contribution?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/contribution', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/store-contribution?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载门店贡献利润..." />
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import { formatCurrency, formatPercent } from '@/lib/utils'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export function DeliveryTab() {
|
||||
export function DeliveryTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('commission_to_delivery_sales_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('desc')
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/delivery', page, sort, filter, order], queryFn: () => api.get(`/store-expense/delivery-commission?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/delivery', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/delivery-commission?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载外卖佣金分析..." />
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import { formatCurrency, formatPercent, formatNumber } from '@/lib/utils'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export function EfficiencyTab() {
|
||||
export function EfficiencyTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('received_per_sqm')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('desc')
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/efficiency', page, sort, filter, order], queryFn: () => api.get(`/store-expense/efficiency?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/efficiency', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/efficiency?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载人效坪效数据..." />
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContaine
|
||||
|
||||
const PIE_COLORS = ['#3b82f6', '#ef4444', '#22c55e', '#f97316', '#eab308', '#8b5cf6', '#06b6d4', '#ec4899', '#84cc16', '#a3a3a3', '#f43f5e', '#6366f1', '#14b8a6', '#facc15', '#7c3aed', '#10b981', '#fb923c']
|
||||
|
||||
export function ExpenseOverviewTab() {
|
||||
const { data: overview, isLoading: l1 } = useQuery({ queryKey: ['se/overview'], queryFn: () => api.get('/store-expense/overview') })
|
||||
const { data: structure, isLoading: l2 } = useQuery({ queryKey: ['se/structure'], queryFn: () => api.get('/store-expense/expense-structure') })
|
||||
export function ExpenseOverviewTab({ month }: { month: string }) {
|
||||
const { data: overview, isLoading: l1 } = useQuery({ queryKey: ['se/overview', month], queryFn: () => api.get('/store-expense/overview', { params: { month } }) })
|
||||
const { data: structure, isLoading: l2 } = useQuery({ queryKey: ['se/structure', month], queryFn: () => api.get('/store-expense/expense-structure', { params: { month } }) })
|
||||
|
||||
if (l1 || l2) return <LoadingSpinner text="加载费用总览..." />
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import { formatCurrency, formatPercent } from '@/lib/utils'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export function FixedVariableTab() {
|
||||
export function FixedVariableTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('fixed_rate_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('desc')
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/fixed-variable', page, sort, filter, order], queryFn: () => api.get(`/store-expense/fixed-variable?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/fixed-variable', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/fixed-variable?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载固定/变动费用拆分..." />
|
||||
|
||||
|
||||
@@ -185,13 +185,13 @@ function StoreDetailModal({ store, onClose }: { store: StoreDetail | null, onClo
|
||||
)
|
||||
}
|
||||
|
||||
export function LossDiagnosisTab() {
|
||||
export function LossDiagnosisTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('actual_store_contribution')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('asc')
|
||||
const [selectedStore, setSelectedStore] = useState<StoreDetail | null>(null)
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/loss-diagnosis', page, sort, filter, order], queryFn: () => api.get(`/store-expense/loss-diagnosis?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/loss-diagnosis', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/loss-diagnosis?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载亏损门店诊断..." />
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ const RISK_COLORS: Record<string, string> = {
|
||||
'正常': '',
|
||||
}
|
||||
|
||||
export function RentRiskTab() {
|
||||
export function RentRiskTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('rent_rate_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [riskOnly, setRiskOnly] = useState(false)
|
||||
const [order, setOrder] = useState('desc')
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/rent', page, sort, filter, riskOnly, order], queryFn: () => api.get(`/store-expense/rent-risk?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&risk_only=${riskOnly}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/rent', month, page, sort, filter, riskOnly, order], queryFn: () => api.get(`/store-expense/rent-risk?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&risk_only=${riskOnly}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载房租及租约风险..." />
|
||||
|
||||
|
||||
@@ -107,13 +107,13 @@ function EvalDetailModal({ store, onClose }: { store: EvalStoreDetail | null, on
|
||||
)
|
||||
}
|
||||
|
||||
export function StoreEvaluationTab() {
|
||||
export function StoreEvaluationTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('actual_store_contribution_rate_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('asc')
|
||||
const [selectedStore, setSelectedStore] = useState<EvalStoreDetail | null>(null)
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/evaluation', page, sort, filter, order], queryFn: () => api.get(`/store-expense/store-evaluation?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/evaluation', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/store-evaluation?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载门店评估..." />
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ import { formatCurrency, formatPercent, formatNumber } from '@/lib/utils'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
export function StoreRankingTab() {
|
||||
export function StoreRankingTab({ month }: { month: string }) {
|
||||
const [page, setPage] = useState(1)
|
||||
const [sort, setSort] = useState('operating_expense_rate_pct')
|
||||
const [filter, setFilter] = useState('')
|
||||
const [order, setOrder] = useState('desc')
|
||||
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/ranking', page, sort, filter, order], queryFn: () => api.get(`/store-expense/store-ranking?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}`) })
|
||||
const { data, isLoading } = useQuery({ queryKey: ['se/ranking', month, page, sort, filter, order], queryFn: () => api.get(`/store-expense/store-ranking?page=${page}&page_size=${PAGE_SIZE}&sort=${sort}&filter=${filter}&order=${order}&month=${month}`) })
|
||||
|
||||
if (isLoading) return <LoadingSpinner text="加载门店费用率排名..." />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user