fix: 客户饼图省份用车辆表province、区域柱状图用realtime表;加出勤说明
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 后端region-chart新增source参数:vehicle用车辆表,realtime用实时表 - 客户运营地区占比"按省份"改用source=vehicle - 区域资产分布概览"按省份"继续用source=realtime - 部门Tab加说明文字:*说明:当天里程>0即为出勤。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -508,7 +508,7 @@ export default function App() {
|
|||||||
const [customerProvinceData, setCustomerProvinceData] = useState<{ name: string; value: number }[]>([]);
|
const [customerProvinceData, setCustomerProvinceData] = useState<{ name: string; value: number }[]>([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (customerChartView === 'province') {
|
if (customerChartView === 'province') {
|
||||||
fetchRegionChart('province', 5).then(setCustomerProvinceData).catch(() => setCustomerProvinceData([]));
|
fetchRegionChart('province', 5, 'vehicle').then(setCustomerProvinceData).catch(() => setCustomerProvinceData([]));
|
||||||
}
|
}
|
||||||
}, [customerChartView]);
|
}, [customerChartView]);
|
||||||
|
|
||||||
@@ -1456,6 +1456,8 @@ export default function App() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<span className="text-[10px] text-red-500 font-medium">*说明:当天里程>0即为出勤。</span>
|
||||||
|
|
||||||
<div className="flex-1 max-w-[240px]">
|
<div className="flex-1 max-w-[240px]">
|
||||||
{deptViewMode === 'manager' && (
|
{deptViewMode === 'manager' && (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ export async function fetchInventoryStats(): Promise<RegionalInventoryStats[]> {
|
|||||||
return fetchJson<RegionalInventoryStats[]>(`${BASE}/inventory-stats`);
|
return fetchJson<RegionalInventoryStats[]>(`${BASE}/inventory-stats`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchRegionChart(groupBy: string, top = 8): Promise<{ name: string; value: number }[]> {
|
export async function fetchRegionChart(groupBy: string, top = 8, source = 'realtime'): Promise<{ name: string; value: number }[]> {
|
||||||
return fetchJson<{ name: string; value: number }[]>(`${BASE}/region-chart?groupBy=${groupBy}&top=${top}`);
|
return fetchJson<{ name: string; value: number }[]>(`${BASE}/region-chart?groupBy=${groupBy}&top=${top}&source=${source}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchWeeklyDetail(type: string): Promise<WeeklyDetailItem[]> {
|
export async function fetchWeeklyDetail(type: string): Promise<WeeklyDetailItem[]> {
|
||||||
|
|||||||
@@ -1035,22 +1035,31 @@ app.get('/region-chart', async (c) => {
|
|||||||
const vehicles = await getVehicles();
|
const vehicles = await getVehicles();
|
||||||
const operating = vehicles.filter((v) => v.status === 'Operating');
|
const operating = vehicles.filter((v) => v.status === 'Operating');
|
||||||
const groupBy = c.req.query('groupBy') || 'region'; // 'region' | 'province'
|
const groupBy = c.req.query('groupBy') || 'region'; // 'region' | 'province'
|
||||||
|
const source = c.req.query('source') || 'realtime'; // 'realtime' | 'vehicle'
|
||||||
const top = Number(c.req.query('top')) || 8;
|
const top = Number(c.req.query('top')) || 8;
|
||||||
|
|
||||||
let counts: Map<string, number>;
|
let counts: Map<string, number>;
|
||||||
if (groupBy === 'province') {
|
if (groupBy === 'province') {
|
||||||
// Get realtime province data
|
|
||||||
const [rows] = await pool.query<any[]>(`SELECT plate_number, province FROM tab_truck_remote_sync_realtime_info WHERE is_deleted = 0 AND plate_number IS NOT NULL`);
|
|
||||||
const plateProvince = new Map<string, string>();
|
|
||||||
for (const row of rows as any[]) {
|
|
||||||
const plate = (row.plate_number || '').trim();
|
|
||||||
const prov = (row.province || '').replace(/省|市$/, '').trim();
|
|
||||||
if (plate && prov) plateProvince.set(plate, prov);
|
|
||||||
}
|
|
||||||
counts = new Map<string, number>();
|
counts = new Map<string, number>();
|
||||||
for (const v of operating) {
|
if (source === 'vehicle') {
|
||||||
const prov = plateProvince.get(v.plateNumber) || '未知';
|
// Use vehicle table's own province field
|
||||||
counts.set(prov, (counts.get(prov) || 0) + 1);
|
for (const v of operating) {
|
||||||
|
const prov = (v.province || '').replace(/省|市$/, '').trim() || '未知';
|
||||||
|
counts.set(prov, (counts.get(prov) || 0) + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use realtime table province
|
||||||
|
const [rows] = await pool.query<any[]>(`SELECT plate_number, province FROM tab_truck_remote_sync_realtime_info WHERE is_deleted = 0 AND plate_number IS NOT NULL`);
|
||||||
|
const plateProvince = new Map<string, string>();
|
||||||
|
for (const row of rows as any[]) {
|
||||||
|
const plate = (row.plate_number || '').trim();
|
||||||
|
const prov = (row.province || '').replace(/省|市$/, '').trim();
|
||||||
|
if (plate && prov) plateProvince.set(plate, prov);
|
||||||
|
}
|
||||||
|
for (const v of operating) {
|
||||||
|
const prov = plateProvince.get(v.plateNumber) || '未知';
|
||||||
|
counts.set(prov, (counts.get(prov) || 0) + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
counts = new Map<string, number>();
|
counts = new Map<string, number>();
|
||||||
|
|||||||
Reference in New Issue
Block a user