fix: KPI统计跟随筛选条件变化+客户筛选修正+部门排序
- KPI统计(总里程/平均单车/监控台数)改为基于筛选后数据计算 - 移除不需要的 onlineCount 字段 - 快捷筛选"按客户"和全屏表格"客户"列改为真正的客户筛选 - 删除混乱的 projects 变量映射 - 部门列表按 一部→二部→三部 顺序排序 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -106,7 +106,7 @@ export default function MonitoringView() {
|
|||||||
const [appliedMileageRange, setAppliedMileageRange] = useState({ min: '', max: '' });
|
const [appliedMileageRange, setAppliedMileageRange] = useState({ min: '', max: '' });
|
||||||
|
|
||||||
const [vehicles, setVehicles] = useState<MonitoringVehicle[]>([]);
|
const [vehicles, setVehicles] = useState<MonitoringVehicle[]>([]);
|
||||||
const [stats, setStats] = useState<MonitoringStats>({ totalToday: 0, totalAll: 0, onlineCount: 0, vehicleCount: 0 });
|
const [stats, setStats] = useState<MonitoringStats>({ totalToday: 0, totalAll: 0, vehicleCount: 0 });
|
||||||
const [filterOptions, setFilterOptions] = useState<MonitoringFilters>({ departments: [], customers: [], plates: [], projects: [], entities: [] });
|
const [filterOptions, setFilterOptions] = useState<MonitoringFilters>({ departments: [], customers: [], plates: [], projects: [], entities: [] });
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
@@ -119,7 +119,6 @@ export default function MonitoringView() {
|
|||||||
|
|
||||||
const departments = filterOptions.departments;
|
const departments = filterOptions.departments;
|
||||||
const plateNumbers = filterOptions.plates;
|
const plateNumbers = filterOptions.plates;
|
||||||
const projects = filterOptions.customers;
|
|
||||||
|
|
||||||
// 加载首页数据
|
// 加载首页数据
|
||||||
const loadFirstPage = useCallback(() => {
|
const loadFirstPage = useCallback(() => {
|
||||||
@@ -327,14 +326,14 @@ export default function MonitoringView() {
|
|||||||
<th className="p-4 text-[10px] font-bold text-slate-500 uppercase">在线状态</th>
|
<th className="p-4 text-[10px] font-bold text-slate-500 uppercase">在线状态</th>
|
||||||
<th className="p-4 text-[10px] font-bold text-slate-500 uppercase">
|
<th className="p-4 text-[10px] font-bold text-slate-500 uppercase">
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<span>客户项目</span>
|
<span>客户</span>
|
||||||
<select
|
<select
|
||||||
className="bg-slate-800 border-none rounded-lg px-2 py-1 text-[9px] text-slate-300 outline-none focus:ring-1 focus:ring-blue-500/30"
|
className="bg-slate-800 border-none rounded-lg px-2 py-1 text-[9px] text-slate-300 outline-none focus:ring-1 focus:ring-blue-500/30"
|
||||||
value={filterProject}
|
value={filterCustomer}
|
||||||
onChange={(e) => setFilterProject(e.target.value)}
|
onChange={(e) => setFilterCustomer(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="All">全部客户</option>
|
<option value="All">全部客户</option>
|
||||||
{projects.map(p => <option key={p} value={p}>{p}</option>)}
|
{filterOptions.customers.map(p => <option key={p} value={p}>{p}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export interface MonitoringVehicle {
|
|||||||
export interface MonitoringStats {
|
export interface MonitoringStats {
|
||||||
totalToday: number;
|
totalToday: number;
|
||||||
totalAll: number;
|
totalAll: number;
|
||||||
onlineCount: number;
|
|
||||||
vehicleCount: number;
|
vehicleCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ interface CachedVehicle {
|
|||||||
|
|
||||||
interface MonitoringCache {
|
interface MonitoringCache {
|
||||||
vehicles: CachedVehicle[];
|
vehicles: CachedVehicle[];
|
||||||
stats: { totalToday: number; totalAll: number; onlineCount: number; vehicleCount: number };
|
stats: { totalToday: number; totalAll: number; vehicleCount: number };
|
||||||
filters: { departments: string[]; customers: string[]; plates: string[]; projects: string[]; entities: string[] };
|
filters: { departments: string[]; customers: string[]; plates: string[]; projects: string[]; entities: string[] };
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
}
|
}
|
||||||
@@ -113,10 +113,16 @@ async function refreshMonitoringCache() {
|
|||||||
// 预计算统计信息
|
// 预计算统计信息
|
||||||
const totalToday = vehicles.reduce((sum, v) => sum + v.dailyKm, 0);
|
const totalToday = vehicles.reduce((sum, v) => sum + v.dailyKm, 0);
|
||||||
const totalAll = vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0);
|
const totalAll = vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0);
|
||||||
const onlineCount = vehicles.filter(v => v.isOnline).length;
|
|
||||||
|
|
||||||
// 预提取筛选选项
|
// 预提取筛选选项
|
||||||
const departments = Array.from(new Set(vehicles.map(v => v.department).filter(Boolean))) as string[];
|
const deptOrder = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十'];
|
||||||
|
const departments = (Array.from(new Set(vehicles.map(v => v.department).filter(Boolean))) as string[])
|
||||||
|
.sort((a, b) => {
|
||||||
|
const ai = deptOrder.findIndex(d => a.includes(d));
|
||||||
|
const bi = deptOrder.findIndex(d => b.includes(d));
|
||||||
|
return (ai === -1 ? 99 : ai) - (bi === -1 ? 99 : bi);
|
||||||
|
});
|
||||||
const customers = Array.from(new Set(vehicles.map(v => v.customer).filter(Boolean))) as string[];
|
const customers = Array.from(new Set(vehicles.map(v => v.customer).filter(Boolean))) as string[];
|
||||||
const plates = vehicles.map(v => v.plate);
|
const plates = vehicles.map(v => v.plate);
|
||||||
const projects = Array.from(new Set(vehicles.map(v => v.project).filter(Boolean))) as string[];
|
const projects = Array.from(new Set(vehicles.map(v => v.project).filter(Boolean))) as string[];
|
||||||
@@ -124,7 +130,7 @@ async function refreshMonitoringCache() {
|
|||||||
|
|
||||||
monitoringCache = {
|
monitoringCache = {
|
||||||
vehicles,
|
vehicles,
|
||||||
stats: { totalToday, totalAll, onlineCount, vehicleCount: vehicles.length },
|
stats: { totalToday, totalAll, vehicleCount: vehicles.length },
|
||||||
filters: { departments, customers, plates, projects, entities },
|
filters: { departments, customers, plates, projects, entities },
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
@@ -142,7 +148,7 @@ setInterval(refreshMonitoringCache, 2 * 60 * 1000);
|
|||||||
// GET /monitoring — 从缓存取数据,支持筛选/排序/分页
|
// GET /monitoring — 从缓存取数据,支持筛选/排序/分页
|
||||||
app.get('/monitoring', (c) => {
|
app.get('/monitoring', (c) => {
|
||||||
if (!monitoringCache) {
|
if (!monitoringCache) {
|
||||||
return c.json({ vehicles: [], stats: { totalToday: 0, totalAll: 0, onlineCount: 0, vehicleCount: 0 }, filters: { departments: [], customers: [], plates: [], projects: [], entities: [] }, total: 0, page: 1, totalPages: 1, updatedAt: new Date().toISOString() });
|
return c.json({ vehicles: [], stats: { totalToday: 0, totalAll: 0, vehicleCount: 0 }, filters: { departments: [], customers: [], plates: [], projects: [], entities: [] }, total: 0, page: 1, totalPages: 1, updatedAt: new Date().toISOString() });
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortBy = c.req.query('sortBy') || 'today';
|
const sortBy = c.req.query('sortBy') || 'today';
|
||||||
@@ -179,6 +185,13 @@ app.get('/monitoring', (c) => {
|
|||||||
|
|
||||||
const total = vehicles.length;
|
const total = vehicles.length;
|
||||||
|
|
||||||
|
// 基于筛选后的数据计算统计
|
||||||
|
const filteredStats = {
|
||||||
|
totalToday: vehicles.reduce((sum, v) => sum + v.dailyKm, 0),
|
||||||
|
totalAll: vehicles.reduce((sum, v) => sum + (v.totalKm || 0), 0),
|
||||||
|
vehicleCount: vehicles.length,
|
||||||
|
};
|
||||||
|
|
||||||
// 排序
|
// 排序
|
||||||
vehicles = [...vehicles].sort((a, b) => {
|
vehicles = [...vehicles].sort((a, b) => {
|
||||||
const valA = sortBy === 'today' ? a.dailyKm : (a.totalKm || 0);
|
const valA = sortBy === 'today' ? a.dailyKm : (a.totalKm || 0);
|
||||||
@@ -192,7 +205,7 @@ app.get('/monitoring', (c) => {
|
|||||||
|
|
||||||
return c.json({
|
return c.json({
|
||||||
vehicles: paged,
|
vehicles: paged,
|
||||||
stats: monitoringCache.stats,
|
stats: filteredStats,
|
||||||
filters: monitoringCache.filters,
|
filters: monitoringCache.filters,
|
||||||
total,
|
total,
|
||||||
page,
|
page,
|
||||||
|
|||||||
Reference in New Issue
Block a user