feat: 资产总览新增所属公司筛选,支持按归属主体过滤全页数据
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

- 后端:新增 /api/vehicles/subjects 端点返回公司列表+台数预览;所有聚合端点接受 ?subject= 参数按 tab_truck.org_id 对应的主体公司过滤
- 前端:标题下方新增 Scope Chip 单选下拉,支持搜索+台数预览,选中后全页 KPI/汇总/库存统计按公司联动刷新

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-04-15 16:50:25 +08:00
parent d6c31dd2b6
commit 820fde5547
3 changed files with 260 additions and 58 deletions

View File

@@ -11,12 +11,29 @@ import { fetchJson } from '../../auth/api-client';
const BASE = '/api/vehicles';
export async function fetchSummary(): Promise<SummaryData> {
return fetchJson<SummaryData>(`${BASE}/summary`);
export interface SubjectOption {
name: string;
total: number;
inventory: number;
operating: number;
}
export async function fetchByType(): Promise<TypeSummary[]> {
return fetchJson<TypeSummary[]>(`${BASE}/by-type`);
function withSubject(path: string, subject?: string | null): string {
if (!subject) return path;
const sep = path.includes('?') ? '&' : '?';
return `${path}${sep}subject=${encodeURIComponent(subject)}`;
}
export async function fetchSubjects(): Promise<SubjectOption[]> {
return fetchJson<SubjectOption[]>(`${BASE}/subjects`);
}
export async function fetchSummary(subject?: string | null): Promise<SummaryData> {
return fetchJson<SummaryData>(withSubject(`${BASE}/summary`, subject));
}
export async function fetchByType(subject?: string | null): Promise<TypeSummary[]> {
return fetchJson<TypeSummary[]>(withSubject(`${BASE}/by-type`, subject));
}
export async function fetchVehicleList(params: {
@@ -32,6 +49,7 @@ export async function fetchVehicleList(params: {
isTrailer?: string;
department?: string;
attendance?: string;
subject?: string | null;
}): Promise<VehicleListItem[]> {
const query = new URLSearchParams();
if (params.batch) query.set('batch', params.batch);
@@ -46,6 +64,7 @@ export async function fetchVehicleList(params: {
if (params.isTrailer) query.set('isTrailer', params.isTrailer);
if (params.department) query.set('department', params.department);
if (params.attendance) query.set('attendance', params.attendance);
if (params.subject) query.set('subject', params.subject);
return fetchJson<VehicleListItem[]>(`${BASE}/list?${query.toString()}`);
}
@@ -57,29 +76,40 @@ export interface WeeklyDetailItem {
customer_name: string | null;
}
export async function fetchDeptStats(): Promise<DeptGroup[]> {
return fetchJson<DeptGroup[]>(`${BASE}/dept-stats`);
export async function fetchDeptStats(subject?: string | null): Promise<DeptGroup[]> {
return fetchJson<DeptGroup[]>(withSubject(`${BASE}/dept-stats`, subject));
}
export async function fetchRegionStats(params?: { customer?: string; city?: string; region?: string }): Promise<RegionGroup[]> {
export async function fetchRegionStats(
params?: { customer?: string; city?: string; region?: string },
subject?: string | null,
): 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);
if (subject) query.set('subject', subject);
const qs = query.toString();
return fetchJson<RegionGroup[]>(`${BASE}/region-stats${qs ? `?${qs}` : ''}`);
}
export async function fetchCustomerStats(): Promise<CustomerStats[]> {
return fetchJson<CustomerStats[]>(`${BASE}/customer-stats`);
export async function fetchCustomerStats(subject?: string | null): Promise<CustomerStats[]> {
return fetchJson<CustomerStats[]>(withSubject(`${BASE}/customer-stats`, subject));
}
export async function fetchInventoryStats(): Promise<RegionalInventoryStats[]> {
return fetchJson<RegionalInventoryStats[]>(`${BASE}/inventory-stats`);
export async function fetchInventoryStats(subject?: string | null): Promise<RegionalInventoryStats[]> {
return fetchJson<RegionalInventoryStats[]>(withSubject(`${BASE}/inventory-stats`, subject));
}
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}&source=${source}`);
export async function fetchRegionChart(
groupBy: string,
top = 8,
source = 'realtime',
subject?: string | null,
): Promise<{ name: string; value: number }[]> {
return fetchJson<{ name: string; value: number }[]>(
withSubject(`${BASE}/region-chart?groupBy=${groupBy}&top=${top}&source=${source}`, subject),
);
}
export async function fetchWeeklyDetail(type: string): Promise<WeeklyDetailItem[]> {