fix: 区域统计客户筛选生效,后端region-stats支持过滤参数
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端/region-stats新增customer/city/region查询参数 - 前端regionFilters变化时重新请求后端数据 - 移除前端冗余过滤逻辑,由后端统一处理 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
18
src/App.tsx
18
src/App.tsx
@@ -236,6 +236,18 @@ export default function App() {
|
||||
return () => clearInterval(interval);
|
||||
}, [loadData]);
|
||||
|
||||
// Re-fetch region data when filters change
|
||||
useEffect(() => {
|
||||
const hasFilter = regionFilters.customer || regionFilters.city || regionFilters.region;
|
||||
if (hasFilter) {
|
||||
fetchRegionStats({ customer: regionFilters.customer || undefined, city: regionFilters.city || undefined, region: regionFilters.region || undefined })
|
||||
.then(setRegionData).catch(() => {});
|
||||
} else {
|
||||
// No filters: use data from the main loadData cycle
|
||||
fetchRegionStats().then(setRegionData).catch(() => {});
|
||||
}
|
||||
}, [regionFilters]);
|
||||
|
||||
// Fetch region chart data when view changes
|
||||
useEffect(() => {
|
||||
fetchRegionChart(regionChartView, regionChartView === 'city' ? 5 : 8).then(setRegionChartData).catch(() => setRegionChartData([]));
|
||||
@@ -477,7 +489,7 @@ export default function App() {
|
||||
const uniqueModalLocations = useMemo(() => Array.from(new Set(modalVehicles.map(v => v.location).filter(Boolean))), [modalVehicles]);
|
||||
|
||||
// Derived data for region section
|
||||
const filteredRegionData = useMemo(() => regionData.filter((r) => !regionFilters.region || r.region === regionFilters.region), [regionData, regionFilters.region]);
|
||||
// regionData is already filtered by backend based on regionFilters
|
||||
|
||||
// Filtered modal vehicles based on modal filters
|
||||
const filteredModalVehicles = useMemo(() => modalVehicles.filter((v) => {
|
||||
@@ -2063,7 +2075,7 @@ export default function App() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="text-xs">
|
||||
{regionData.filter(r => !regionFilters.region || r.region === regionFilters.region).map((r) => {
|
||||
{regionData.map((r) => {
|
||||
const isExpanded = expandedRegions.has(r.region);
|
||||
return (
|
||||
<React.Fragment key={r.region}>
|
||||
@@ -2124,7 +2136,7 @@ export default function App() {
|
||||
|
||||
{/* Mobile View (Region) */}
|
||||
<div className="lg:hidden p-2 space-y-3">
|
||||
{regionData.filter(r => !regionFilters.region || r.region === regionFilters.region).map((r) => {
|
||||
{regionData.map((r) => {
|
||||
const isExpanded = expandedRegions.has(r.region);
|
||||
return (
|
||||
<div key={r.region} className="bg-slate-50/50 rounded-xl border border-slate-100 overflow-hidden">
|
||||
|
||||
@@ -66,8 +66,13 @@ export async function fetchDeptStats(): Promise<DeptGroup[]> {
|
||||
return fetchJson<DeptGroup[]>(`${BASE}/dept-stats`);
|
||||
}
|
||||
|
||||
export async function fetchRegionStats(): Promise<RegionGroup[]> {
|
||||
return fetchJson<RegionGroup[]>(`${BASE}/region-stats`);
|
||||
export async function fetchRegionStats(params?: { customer?: string; city?: string; region?: string }): Promise<RegionGroup[]> {
|
||||
const query = new URLSearchParams();
|
||||
if (params?.customer) query.set('customer', params.customer);
|
||||
if (params?.city) query.set('city', params.city);
|
||||
if (params?.region) query.set('region', params.region);
|
||||
const qs = query.toString();
|
||||
return fetchJson<RegionGroup[]>(`${BASE}/region-stats${qs ? `?${qs}` : ''}`);
|
||||
}
|
||||
|
||||
export async function fetchCustomerStats(): Promise<CustomerStats[]> {
|
||||
|
||||
@@ -724,7 +724,11 @@ app.get('/dept-stats', async (c) => {
|
||||
// GET /api/vehicles/region-stats — macro-region with city drill-down
|
||||
app.get('/region-stats', async (c) => {
|
||||
const vehicles = await getVehicles();
|
||||
const operating = vehicles.filter((v) => v.status === 'Operating' || v.status === 'Pending');
|
||||
const { customer, city: filterCity, region: filterRegion } = c.req.query();
|
||||
let operating = vehicles.filter((v) => v.status === 'Operating' || v.status === 'Pending');
|
||||
if (customer) operating = operating.filter((v) => v.customerName === customer);
|
||||
if (filterCity) operating = operating.filter((v) => resolveCity(v.city, v.province) === filterCity);
|
||||
if (filterRegion) operating = operating.filter((v) => mapMacroRegion(v.province, v.city) === filterRegion);
|
||||
|
||||
const regionCityMap = new Map<string, Map<string, Vehicle[]>>();
|
||||
for (const v of operating) {
|
||||
|
||||
Reference in New Issue
Block a user