Files
track/frontend/src/App.tsx
duxingchen dcc92b223e feat: 生产看板多维度重构(全量在制品、时间维度联动、口径提示)
本次只含后端看板接口与 PC 端页面。移动端的体验优化已在之前的 12 个提交中
入库,不在本提交范围内。

后端 · 服务层 dashboard_service.py
- 在制品列表不再按 20 条静默截断:此前 `.limit(limit*2)` 预取再排序切片,
  前端又拿数组长度当总数,表现为「卡片 23、列表却写共 20 个」的数据被吞。
  现默认返回全量,limit 降级为防御性上限
- 产品流转「已完结」接入右上角时间筛选(since/until),废弃硬编码「本月」。
  此前选「今天」也会看到整月产出,业务语义分裂;待流转/流转中仍为实时快照。
  Product 表无 completed_at,以名下工序的完成时间锚定「完结时刻」
- 返工任务可见性:未完结卡点永远展示,已了结的仅限本月,且与卡片数字共用
  同一份条件,避免「按钮显示 2、点开共 0 条」
- 驳回明细的「返工给」改为真实溯源:直接查派生出的返工任务当前 assignee_id,
  不再复刻 reject_task 的推算逻辑(返工任务事后被转交时会显示旧名字)

后端 · 接口层 dashboard.py
- /wip-tasks 的 limit 默认 20→500、上限 100→1000
- /my-stats 支持 since/until,新增接收/转交/上传备注口径(与 PC 人员操作统计
  逐字段交叉验证一致)

PC 端
- AdminDashboard: 在制品计数绑定真实总数并加截断提示、列表改为容器内滚动;
  驳回明细只展示「已驳回」行(返工行不再独立成行,其信息已由「返工给」承载);
  「待流转」「待接收」图例新增口径 Tooltip,消除「产品数 > 任务数」的误解;
  移除已失真的时间筛选提示条
- App.tsx: dayjs 中文 locale 上提到应用根。此前散落在 AdminPeoplePage /
  AnalyticsDashboard 两个页面里,而路由是 lazy 分割的 —— 直接打开概览页时
  那两个模块未加载,日期面板就露出 Sep/Su/Mo
- AdminPeoplePage / AnalyticsDashboard: 移除重复的页面级 locale 设置
- dashboardApi.ts: fetchWipTasks 默认值同步为 500
2026-09-16 14:29:56 +08:00

92 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 { Suspense, lazy } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { App as AntApp, ConfigProvider } from "antd";
import zhCN from "antd/locale/zh_CN";
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
// ── dayjs 中文 locale ────────────────────────────────────────────────
// 必须设在应用根模块,不能下放到各页面。
//
// antd 的 ConfigProvider locale 只负责「请选择日期 / 今天 / 此刻」这类**文案**
// 日期面板上的**月份与星期名**是 rc-picker 交给 dayjs 取的 ——
// @rc-component/picker/es/generate/dayjs.js 里:
// getShortWeekDays: locale => dayjs().locale(parseLocale(locale)).localeData().weekdaysMin()
// parseLocale 把 antd 传来的 "zh_CN"(下划线)在 localeMap 未命中后按 '_' 切分,
// 得到 "zh",最终查的是 dayjs 的 locale 表 —— 不注册就永远是默认的 en。
//
// 此前这两行被分别写在 AdminPeoplePage / AnalyticsDashboard 两个页面里,而路由是
// lazy() 代码分割:直接打开 /admin/dashboard 或 /admin/matrix 时那两个模块根本没
// 被加载dayjs 仍是 en面板就露出 Sep / Su / Mo —— 表现为「有的页面中文、有的
// 页面英文」。放到根模块可保证任何路由下都先生效。
dayjs.locale("zh-cn");
import { ToastProvider } from "./components/ui/Toast";
import { AuthProvider } from "./contexts/AuthContext";
import LoadingSpinner from "./components/ui/LoadingSpinner";
// Layout 组件静态导入(始终需要,体积小)
import AppLayout from "./components/layout/AppLayout";
import AdminLayout from "./components/layout/AdminLayout";
// 🚀 路由级代码分割 — 按需懒加载页面组件
const ScanPage = lazy(() => import("./pages/ScanPage"));
const MyTasksPage = lazy(() => import("./pages/MyTasksPage"));
const NotificationsPage = lazy(() => import("./pages/NotificationsPage"));
const ProfilePage = lazy(() => import("./pages/ProfilePage"));
const AdminLoginPage = lazy(() => import("./pages/admin/AdminLoginPage"));
const AdminDashboard = lazy(() => import("./pages/admin/AdminDashboard"));
const AdminProductsPage = lazy(() => import("./pages/admin/AdminProductsPage"));
const AdminTasksPage = lazy(() => import("./pages/admin/AdminTasksPage"));
const AdminPeoplePage = lazy(() => import("./pages/admin/AdminPeoplePage"));
const AdminPrintConfigPage = lazy(() => import("./pages/admin/AdminPrintConfigPage"));
const AnalyticsDashboard = lazy(() => import("./pages/admin/AnalyticsDashboard"));
const MatrixBoard = lazy(() => import("./pages/MatrixBoard"));
const ScreenDashboard = lazy(() => import("./pages/admin/ScreenDashboard"));
export default function App() {
return (
<ConfigProvider locale={zhCN}>
<AntApp>
<ToastProvider>
<AuthProvider>
<BrowserRouter>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
{/* 移动端 */}
<Route path="/" element={<Navigate to="/admin/dashboard" replace />} />
<Route element={<AppLayout />}>
<Route path="/scan" element={<ScanPage />} />
<Route path="/tasks" element={<MyTasksPage />} />
<Route path="/notifications" element={<NotificationsPage />} />
<Route path="/profile" element={<ProfilePage />} />
</Route>
{/* PC 管理端 — 登录页(独立,无侧边栏) */}
<Route path="/admin/login" element={<AdminLoginPage />} />
{/* PC 管理端 — 管理层数据大屏(独立全屏,无侧边栏,组件内自带鉴权守卫) */}
<Route path="/admin/screen" element={<ScreenDashboard />} />
{/* PC 管理端 — 需要登录 */}
<Route path="/admin" element={<Navigate to="/admin/dashboard" replace />} />
<Route element={<AdminLayout />}>
<Route path="/admin/dashboard" element={<AdminDashboard />} />
<Route path="/admin/products" element={<AdminProductsPage />} />
<Route path="/admin/tasks" element={<AdminTasksPage />} />
<Route path="/admin/people" element={<AdminPeoplePage />} />
<Route path="/admin/print-config" element={<AdminPrintConfigPage />} />
<Route path="/admin/analytics" element={<AnalyticsDashboard />} />
<Route path="/admin/matrix" element={<MatrixBoard />} />
</Route>
</Routes>
</Suspense>
</BrowserRouter>
</AuthProvider>
</ToastProvider>
</AntApp>
</ConfigProvider>
);
}