- 后端:新增 /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:
@@ -313,11 +313,21 @@ async function getVehicles(): Promise<Vehicle[]> {
|
||||
async function getVehiclesForUser(c: Context): Promise<Vehicle[]> {
|
||||
const all = await getVehicles();
|
||||
const user = ((c as any).get?.('user') || (c as any).var?.user) as AuthUser | undefined;
|
||||
if (user) {
|
||||
const filtered = filterByPermission(all, user);
|
||||
return maskCustomerNames(filtered);
|
||||
}
|
||||
return maskCustomerNames(all);
|
||||
let list = user ? filterByPermission(all, user) : all;
|
||||
list = applySubjectFilter(c, list);
|
||||
return maskCustomerNames(list);
|
||||
}
|
||||
|
||||
// 归属公司筛选(所属公司 = tab_truck.org_id → org_name, 即 Vehicle.subjectOrg)
|
||||
function getSubjectParam(c: Context): string | null {
|
||||
const raw = (c.req.query('subject') || '').trim();
|
||||
return raw ? raw : null;
|
||||
}
|
||||
|
||||
function applySubjectFilter(c: Context, vehicles: Vehicle[]): Vehicle[] {
|
||||
const subject = getSubjectParam(c);
|
||||
if (!subject) return vehicles;
|
||||
return vehicles.filter((v) => (v.subjectOrg || '') === subject);
|
||||
}
|
||||
|
||||
function getRegionCounts(vehicles: Vehicle[], regions: readonly string[]): Record<string, number> {
|
||||
@@ -611,7 +621,7 @@ app.get('/by-batch', async (c) => {
|
||||
|
||||
// GET /api/vehicles/inventory-analysis — 库存分析,不设数据权限,对所有人开放
|
||||
app.get('/inventory-analysis', async (c) => {
|
||||
const vehicles = await getVehicles();
|
||||
const vehicles = applySubjectFilter(c, await getVehicles());
|
||||
|
||||
const typeFilters = [
|
||||
{ name: '4.5T普货', filter: (v: Vehicle) => v.type === '4.5T' && !v.model.includes('冷链') },
|
||||
@@ -978,7 +988,7 @@ app.get('/list', async (c) => {
|
||||
|
||||
// GET /api/vehicles/inventory-stats — 库存统计,不设数据权限,对所有人开放
|
||||
app.get('/inventory-stats', async (c) => {
|
||||
const vehicles = await getVehicles();
|
||||
const vehicles = applySubjectFilter(c, await getVehicles());
|
||||
const inventory = vehicles.filter((v) => v.status === 'Inventory' || v.status === 'Abnormal');
|
||||
|
||||
const TYPE_NAME_MAP: Record<string, string> = {
|
||||
@@ -1037,6 +1047,30 @@ app.get('/weekly-detail', async (c) => {
|
||||
return c.json(masked);
|
||||
});
|
||||
|
||||
// GET /api/vehicles/subjects — 归属公司列表(含台数预览),用于顶部筛选下拉
|
||||
app.get('/subjects', async (c) => {
|
||||
const all = await getVehicles();
|
||||
const user = ((c as any).get?.('user') || (c as any).var?.user) as AuthUser | undefined;
|
||||
const visible = user ? filterByPermission(all, user) : all;
|
||||
|
||||
const map = new Map<string, { total: number; inventory: number; operating: number }>();
|
||||
for (const v of visible) {
|
||||
const name = (v.subjectOrg || '').trim();
|
||||
if (!name) continue;
|
||||
if (!map.has(name)) map.set(name, { total: 0, inventory: 0, operating: 0 });
|
||||
const s = map.get(name)!;
|
||||
s.total += 1;
|
||||
if (v.status === 'Inventory' || v.status === 'Abnormal') s.inventory += 1;
|
||||
if (v.status === 'Operating') s.operating += 1;
|
||||
}
|
||||
|
||||
const result = Array.from(map.entries())
|
||||
.map(([name, stats]) => ({ name, ...stats }))
|
||||
.sort((a, b) => b.total - a.total);
|
||||
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
// GET /api/vehicles/refresh — force cache refresh
|
||||
app.get('/refresh', async (c) => {
|
||||
lastFetchTime = 0;
|
||||
|
||||
Reference in New Issue
Block a user