feat(assets): refine compact inventory drilldowns and exact subtotal scopes

This commit is contained in:
shishengliang
2026-09-23 17:52:33 +08:00
parent d20bc71efe
commit b5a48b6417
15 changed files with 534 additions and 111 deletions
@@ -2,8 +2,8 @@ import { Fragment, type Dispatch, type SetStateAction } from 'react';
import { ArrowUpRight, ChevronRight } from 'lucide-react';
import { InventoryStatusCounts } from './InventoryStatusCounts';
import { InventoryModelTags } from './InventoryModelTags';
import { inventoryBrandGroups, inventorySelection, type InventoryDisplayGroup } from './inventory-presentation';
import type { InventoryFilters, VehicleModalSelection } from '../../model';
import { inventoryBrandGroups, inventoryRegionRows, inventorySelection, inventorySubtotalSelection, inventoryTotal, type InventoryDisplayGroup } from './inventory-presentation';
import { inventoryTypeLabel, type InventoryFilters, type VehicleModalSelection } from '../../model';
import type { RegionalInventoryStats } from '../../types';
export interface InventoryListProps {
@@ -23,13 +23,13 @@ export function InventoryQuantity({ quantity, label, onClick }: { quantity: numb
}
export function selectInventoryRow(row: RegionalInventoryStats, select: InventoryListProps['select']) {
select(inventorySelection(row, `库存统计 · ${row.city} · ${row.model}`));
select(inventorySelection(row, `库存统计 · ${row.city} · ${row.model}`, { vehicleType: row.type }));
}
export function selectInventoryGroup(group: InventoryDisplayGroup, { tab, filters, select }: Pick<InventoryListProps, 'tab' | 'filters' | 'select'>) {
select(inventorySelection(filters, `库存统计 · ${group.name}`, tab === 'region'
select(inventorySelection(tab === 'model' ? { ...filters, type: group.name } : filters, `库存统计 · ${group.name}`, tab === 'region'
? { location: filters.city || group.name }
: { vehicleType: group.name }));
: {}));
}
export function InventoryShare({ quantity, total }: { quantity: number; total: number }) {
@@ -37,16 +37,26 @@ export function InventoryShare({ quantity, total }: { quantity: number; total: n
return <div className="inventory-share"><span className="inventory-share-track" aria-hidden="true"><span style={{ width: `${percent}%` }} /></span><span>{percent.toFixed(1)}%</span></div>;
}
function InventorySubtotal({ rows, label, select }: { rows: RegionalInventoryStats[]; label: string; select: InventoryListProps['select'] }) {
return <div className={`inventory-subtotal${rows.length === 1 ? ' inventory-subtotal-inline' : ''}`} aria-label={`${label}合计 ${inventoryTotal(rows)} 台`}>
<button className="inventory-subtotal-button" disabled={!inventoryTotal(rows)} aria-label={`${label}合计 ${inventoryTotal(rows)} 台,查看车辆明细`} onClick={() => select(inventorySubtotalSelection(rows, label))}>合计 <strong>{inventoryTotal(rows).toLocaleString()}</strong> 台<ArrowUpRight size={12} aria-hidden="true" /></button>
<InventoryStatusCounts rows={rows} />
</div>;
}
function InventoryDetailRows({ rows, city, tab, total, select }: Pick<InventoryListProps, 'tab' | 'total' | 'select'> & { rows: RegionalInventoryStats[]; city?: string }) {
const brands = inventoryBrandGroups(rows, tab === 'region' ? 'type' : 'model');
return brands.map((brand, brandIndex) => brand.variants.map((variants, variantIndex) => variants.map((row, index) =>
return brands.map((brand, brandIndex) => brand.variants.map((variants, variantIndex) =>
(tab === 'model' ? inventoryRegionRows(variants) : variants.map(row => ({ row, regionSpan: 0 }))).map(({ row, regionSpan }, index) =>
<tr className="inventory-detail-row" key={`${brandIndex}-${variantIndex}-${index}`}>
{tab === 'region' && brandIndex === 0 && variantIndex === 0 && index === 0 && <th rowSpan={rows.length} scope="rowgroup" className="inventory-merged-cell inventory-city-cell">{city || '未分配'}</th>}
{variantIndex === 0 && index === 0 && <td rowSpan={brand.rows.length} className="inventory-merged-cell">{brand.brand || '—'}</td>}
{tab === 'region' && brandIndex === 0 && variantIndex === 0 && index === 0 && <th rowSpan={rows.length} scope="rowgroup" className="inventory-merged-cell inventory-city-cell">{city || '未分配'}<InventorySubtotal select={select} rows={rows} label={city || '未分配'} /></th>}
{variantIndex === 0 && index === 0 && <td rowSpan={brand.rows.length} className="inventory-merged-cell">{brand.brand || '—'}<InventorySubtotal select={select} rows={brand.rows} label={brand.brand || '未分配品牌'} /></td>}
{index === 0 && <td rowSpan={variants.length} className="inventory-merged-cell">
{tab === 'region' ? <span className="inventory-tag inventory-tag-type">{row.type || '未分类'}</span> : <InventoryModelTags row={row} hideType />}
{tab === 'region' ? <span className="inventory-tag inventory-tag-type">{inventoryTypeLabel(row.type) || '未分类'}</span> : <InventoryModelTags row={row} hideType />}
<InventorySubtotal select={select} rows={variants} label={tab === 'region' ? inventoryTypeLabel(row.type) : row.model} />
</td>}
<td>{tab === 'region' ? <InventoryModelTags row={row} hideType /> : <><span className="inventory-place-region">{row.region}</span><span>{row.city}</span></>}</td>
{tab === 'model' && regionSpan > 0 && <td rowSpan={regionSpan} className="inventory-merged-cell inventory-region-cell">{row.region || '未分配'}<InventorySubtotal select={select} rows={variants.filter(item => item.region === row.region)} label={row.region || '未分配'} /></td>}
<td>{tab === 'region' ? <InventoryModelTags row={row} hideType /> : row.city || '未分配'}</td>
<td className="inventory-number-column"><InventoryQuantity quantity={row.quantity} label={`${row.city} · ${row.brand} · ${row.model}`} onClick={() => selectInventoryRow(row, select)} /><InventoryStatusCounts rows={[row]} /></td>
<td><InventoryShare quantity={row.quantity} total={total} /></td>
</tr>
@@ -62,14 +72,14 @@ export function InventoryDesktopTable(props: InventoryListProps) {
{tab === 'region' && <th scope="col">区域 / 城市</th>}
<th scope="col">品牌</th>
{tab === 'region' && <th scope="col">车型</th>}
<th scope="col">类别 / 颜色 / 归属方</th>
{tab === 'model' && <th scope="col">区域 / 城市</th>}
<th scope="col">类别</th>
{tab === 'model' && <><th scope="col">区域</th><th scope="col">城市</th></>}
<th scope="col" className="inventory-number-column">库存数量</th>
<th scope="col" className="inventory-share-column">库存占比</th>
</tr></thead>
{groups.map(group => <Fragment key={group.name}><tbody>
<tr className="inventory-group-row">
<th scope="row" colSpan={tab === 'region' ? 4 : 3}>
<tr className="inventory-group-row" data-expanded={expanded.has(group.name)}>
<th scope="row" colSpan={4}>
<div className="inventory-group-heading">
<button className="inventory-expand" aria-expanded={expanded.has(group.name)} onClick={() => toggle(group.name)}>
<ChevronRight size={16} aria-hidden="true" /><strong>{group.name || '未分配'}</strong><span>{group.sections.length} 个{tab === 'region' ? '城市' : '型号'}</span>