From 37cd5ae7e126a98277135af202ba723e1459e45f Mon Sep 17 00:00:00 2001 From: freedakgmail Date: Tue, 4 Aug 2026 08:19:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BA=97=E9=95=BF=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E5=8F=B0=E6=96=B0=E5=A2=9E=E4=BA=BA=E5=8A=9B=E6=8E=92=E7=8F=AD?= =?UTF-8?q?Tab=E5=8F=8A=E6=A6=82=E8=A7=88=E6=8A=98=E5=8F=A0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端新增 /api/stores/:code/staffing 路由,返回人工成本概览、按星期/餐段排班建议、日营收明细、综合诊断 - 前端 StorePage 新增 staffing Tab,展示 MetricCard 概览、优化建议、排班图表、诊断算法说明 - 修复公司均值人工率被异常门店污染问题(过滤 wage_rate_pct > 100) - 诊断逻辑改为与公司均值动态比较,建议人工率改为动态计算 - 增强任务反馈表单(执行证据、未完成原因、下一步计划) - 点击非概览Tab时自动折叠上方概况区域 --- client/src/pages/StorePage.tsx | 285 ++++++++++++++++++++++++++++++--- server/src/routes/data.ts | 195 ++++++++++++++++++++++ 2 files changed, 459 insertions(+), 21 deletions(-) diff --git a/client/src/pages/StorePage.tsx b/client/src/pages/StorePage.tsx index 7b2f787..59423bd 100644 --- a/client/src/pages/StorePage.tsx +++ b/client/src/pages/StorePage.tsx @@ -22,7 +22,7 @@ const SCORE_DIMENSIONS = [ { key: 'score_bill', label: '客单价', max: 5 }, ] -type DetailTab = 'overview' | 'meal' | 'category' | 'cost' | 'member' | 'anomaly' | 'tasks' +type DetailTab = 'overview' | 'meal' | 'category' | 'cost' | 'member' | 'anomaly' | 'staffing' const TABS: { key: DetailTab; label: string }[] = [ { key: 'overview', label: '概览' }, @@ -31,12 +31,15 @@ const TABS: { key: DetailTab; label: string }[] = [ { key: 'cost', label: '成本分析' }, { key: 'member', label: '会员分析' }, { key: 'anomaly', label: '异常账单' }, + { key: 'staffing', label: '人力排班' }, ] export function StorePage() { const queryClient = useQueryClient() const [executeText, setExecuteText] = useState('') const [activeTaskId, setActiveTaskId] = useState(null) + const [incompleteReason, setIncompleteReason] = useState('') + const [nextStep, setNextStep] = useState('') const userStoreCode = (() => { try { const u = JSON.parse(localStorage.getItem('user') || '{}') @@ -46,6 +49,7 @@ export function StorePage() { const [storeCode, setStoreCode] = useState(userStoreCode || '1111') const [month, setMonth] = useState('2026-04') const [detailTab, setDetailTab] = useState('overview') + const [overviewOpen, setOverviewOpen] = useState(true) const { data: storesData, isLoading: storesLoading } = useQuery({ queryKey: ['stores-list', month], @@ -129,6 +133,12 @@ export function StorePage() { enabled: detailTab === 'anomaly', }) + const { data: staffingData } = useQuery({ + queryKey: ['store', storeCode, 'staffing', month], + queryFn: () => api.get(`/stores/${storeCode}/staffing`, { params: { month } }), + enabled: detailTab === 'staffing', + }) + const card = (cardData as any)?.data const anomalies = card?.anomalies || [] const tasks = (tasksData as any)?.data || [] @@ -152,6 +162,7 @@ export function StorePage() { const member = (memberData as any)?.data const anomalyDetailRows = ((anomalyDetailData as any)?.data) || [] const anomalyDetailTotal = (anomalyDetailData as any)?.meta?.total || anomalyDetailRows.length + const staffing = (staffingData as any)?.data const sc = sd?.scorecard const action = sd?.action @@ -210,7 +221,7 @@ export function StorePage() { const anomalyCount = allAnomalyItems.filter((i: any) => i.is_anomaly).length const executeMutation = useMutation({ - mutationFn: ({ id, text }: { id: number; text: string }) => api.put(`/tasks/${id}/execute`, { process_evidence: text }), + mutationFn: ({ id, data }: { id: number; data: any }) => api.put(`/tasks/${id}/execute`, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['store', storeCode, 'tasks'] }) setExecuteText('') @@ -246,6 +257,19 @@ export function StorePage() { + {/* 概览折叠区 */} +
+ +
+ + {overviewOpen && (<> + {/* 门店概况 */}
@@ -466,24 +490,45 @@ export function StorePage() {

截止日: {t.deadline?.substring(0, 10)}

{activeTaskId === t.task_id ? ( -
-