feat: convert all filters to searchable dropdowns with datalist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
229
src/App.tsx
229
src/App.tsx
@@ -270,11 +270,19 @@ export default function App() {
|
|||||||
inventoryByRegion[s.region][s.city].push(s);
|
inventoryByRegion[s.region][s.city].push(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
const inventoryByModel: Record<string, Record<string, RegionalInventoryStats[]>> = {};
|
const INVENTORY_TYPE_ORDER = ['4.5T普货', '4.5T冷链', '18T', '49T', '挂车', '其他'];
|
||||||
|
const inventoryByModelRaw: Record<string, Record<string, RegionalInventoryStats[]>> = {};
|
||||||
for (const s of filteredInventoryStats) {
|
for (const s of filteredInventoryStats) {
|
||||||
if (!inventoryByModel[s.type]) inventoryByModel[s.type] = {};
|
if (!inventoryByModelRaw[s.type]) inventoryByModelRaw[s.type] = {};
|
||||||
if (!inventoryByModel[s.type][s.model]) inventoryByModel[s.type][s.model] = [];
|
if (!inventoryByModelRaw[s.type][s.model]) inventoryByModelRaw[s.type][s.model] = [];
|
||||||
inventoryByModel[s.type][s.model].push(s);
|
inventoryByModelRaw[s.type][s.model].push(s);
|
||||||
|
}
|
||||||
|
const inventoryByModel: Record<string, Record<string, RegionalInventoryStats[]>> = {};
|
||||||
|
for (const t of INVENTORY_TYPE_ORDER) {
|
||||||
|
if (inventoryByModelRaw[t]) inventoryByModel[t] = inventoryByModelRaw[t];
|
||||||
|
}
|
||||||
|
for (const t of Object.keys(inventoryByModelRaw)) {
|
||||||
|
if (!inventoryByModel[t]) inventoryByModel[t] = inventoryByModelRaw[t];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derived data for dept section
|
// Derived data for dept section
|
||||||
@@ -297,13 +305,20 @@ export default function App() {
|
|||||||
const uniqueDepts = Array.from(new Set(customerData.map((s) => s.department).filter(Boolean)));
|
const uniqueDepts = Array.from(new Set(customerData.map((s) => s.department).filter(Boolean)));
|
||||||
const uniqueRegions = Array.from(new Set(customerData.map((s) => s.region)));
|
const uniqueRegions = Array.from(new Set(customerData.map((s) => s.region)));
|
||||||
const uniqueCities = Array.from(new Set(customerData.map((s) => s.city).filter(Boolean)));
|
const uniqueCities = Array.from(new Set(customerData.map((s) => s.city).filter(Boolean)));
|
||||||
|
const uniqueCustomerNames = Array.from(new Set(customerData.map((s) => s.customer).filter(Boolean)));
|
||||||
|
const uniqueCustomerManagers = Array.from(new Set(customerData.map((s) => s.manager).filter(Boolean)));
|
||||||
|
const uniqueInventoryModels = Array.from(new Set(inventoryData.map((s) => s.model).filter(Boolean)));
|
||||||
|
const uniqueModalPlates = Array.from(new Set(modalVehicles.map(v => v.plateNumber || v.vin).filter(Boolean)));
|
||||||
|
const uniqueModalModels = Array.from(new Set(modalVehicles.map(v => v.model).filter(Boolean)));
|
||||||
|
const uniqueModalBrands = Array.from(new Set(modalVehicles.map(v => v.brandLabel).filter((x): x is string => Boolean(x))));
|
||||||
|
const uniqueModalLocations = Array.from(new Set(modalVehicles.map(v => v.location).filter(Boolean)));
|
||||||
|
|
||||||
// Derived data for region section
|
// Derived data for region section
|
||||||
const filteredRegionData = regionData.filter((r) => !regionFilters.region || r.region === regionFilters.region);
|
const filteredRegionData = regionData.filter((r) => !regionFilters.region || r.region === regionFilters.region);
|
||||||
|
|
||||||
// Filtered modal vehicles based on modal filters
|
// Filtered modal vehicles based on modal filters
|
||||||
const filteredModalVehicles = modalVehicles.filter((v) => {
|
const filteredModalVehicles = modalVehicles.filter((v) => {
|
||||||
const mp = !modalFilters.plateNumber || v.plateNumber.toLowerCase().includes(modalFilters.plateNumber.toLowerCase());
|
const mp = !modalFilters.plateNumber || v.plateNumber.toLowerCase().includes(modalFilters.plateNumber.toLowerCase()) || v.vin.toLowerCase().includes(modalFilters.plateNumber.toLowerCase());
|
||||||
const mm = !modalFilters.model || v.model.toLowerCase().includes(modalFilters.model.toLowerCase());
|
const mm = !modalFilters.model || v.model.toLowerCase().includes(modalFilters.model.toLowerCase());
|
||||||
const mb = !modalFilters.brand || (v.brandLabel || '').toLowerCase().includes(modalFilters.brand.toLowerCase());
|
const mb = !modalFilters.brand || (v.brandLabel || '').toLowerCase().includes(modalFilters.brand.toLowerCase());
|
||||||
const ml = !modalFilters.location || v.location.toLowerCase().includes(modalFilters.location.toLowerCase());
|
const ml = !modalFilters.location || v.location.toLowerCase().includes(modalFilters.location.toLowerCase());
|
||||||
@@ -1003,59 +1018,75 @@ export default function App() {
|
|||||||
<div className="space-y-3 text-left">
|
<div className="space-y-3 text-left">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[10px] text-slate-400 block mb-1">区域</label>
|
<label className="text-[10px] text-slate-400 block mb-1">区域</label>
|
||||||
<select
|
<input
|
||||||
|
list="dl-inv-region"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
value={inventoryFilters.region}
|
value={inventoryFilters.region}
|
||||||
onChange={(e) => setInventoryFilters({...inventoryFilters, region: e.target.value})}
|
onChange={(e) => setInventoryFilters({...inventoryFilters, region: e.target.value})}
|
||||||
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer"
|
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
>
|
/>
|
||||||
<option value="">全部区域</option>
|
<datalist id="dl-inv-region">
|
||||||
{uniqueInventoryRegions.map(r => <option key={r} value={r}>{r}</option>)}
|
{uniqueInventoryRegions.map(r => <option key={r} value={r} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[10px] text-slate-400 block mb-1">城市</label>
|
<label className="text-[10px] text-slate-400 block mb-1">城市</label>
|
||||||
<select
|
<input
|
||||||
|
list="dl-inv-city"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
value={inventoryFilters.city}
|
value={inventoryFilters.city}
|
||||||
onChange={(e) => setInventoryFilters({...inventoryFilters, city: e.target.value})}
|
onChange={(e) => setInventoryFilters({...inventoryFilters, city: e.target.value})}
|
||||||
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer"
|
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
>
|
/>
|
||||||
<option value="">全部城市</option>
|
<datalist id="dl-inv-city">
|
||||||
{uniqueInventoryCities.map(c => <option key={c} value={c}>{c}</option>)}
|
{uniqueInventoryCities.map(c => <option key={c} value={c} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[10px] text-slate-400 block mb-1">品牌</label>
|
<label className="text-[10px] text-slate-400 block mb-1">品牌</label>
|
||||||
<select
|
<input
|
||||||
|
list="dl-inv-brand"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
value={inventoryFilters.brand}
|
value={inventoryFilters.brand}
|
||||||
onChange={(e) => setInventoryFilters({...inventoryFilters, brand: e.target.value})}
|
onChange={(e) => setInventoryFilters({...inventoryFilters, brand: e.target.value})}
|
||||||
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer"
|
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
>
|
/>
|
||||||
<option value="">全部品牌</option>
|
<datalist id="dl-inv-brand">
|
||||||
{uniqueInventoryBrands.map(b => <option key={b} value={b}>{b}</option>)}
|
{uniqueInventoryBrands.map(b => <option key={b} value={b} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[10px] text-slate-400 block mb-1">批次</label>
|
<label className="text-[10px] text-slate-400 block mb-1">批次</label>
|
||||||
<select
|
<input
|
||||||
|
list="dl-inv-batch"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
value={inventoryFilters.batch}
|
value={inventoryFilters.batch}
|
||||||
onChange={(e) => setInventoryFilters({...inventoryFilters, batch: e.target.value})}
|
onChange={(e) => setInventoryFilters({...inventoryFilters, batch: e.target.value})}
|
||||||
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer"
|
className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
>
|
/>
|
||||||
<option value="">全部批次</option>
|
<datalist id="dl-inv-batch">
|
||||||
{uniqueInventoryBatches.map(b => <option key={b} value={b}>{b}</option>)}
|
{uniqueInventoryBatches.map(b => <option key={b} value={b} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-[10px] text-slate-400 block mb-1">车型名称</label>
|
<label className="text-[10px] text-slate-400 block mb-1">车型名称</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-slate-400" />
|
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-slate-400" />
|
||||||
<input
|
<input
|
||||||
|
list="dl-inv-model"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="搜索车型..."
|
placeholder="搜索车型..."
|
||||||
value={inventoryFilters.model}
|
value={inventoryFilters.model}
|
||||||
onChange={(e) => setInventoryFilters({...inventoryFilters, model: e.target.value})}
|
onChange={(e) => setInventoryFilters({...inventoryFilters, model: e.target.value})}
|
||||||
className="w-full text-xs bg-white border border-slate-200 rounded-lg pl-7 pr-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
className="w-full text-xs bg-white border border-slate-200 rounded-lg pl-7 pr-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-inv-model">
|
||||||
|
{uniqueInventoryModels.map(m => <option key={m} value={m} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1410,17 +1441,20 @@ export default function App() {
|
|||||||
{deptViewMode === 'manager' && (
|
{deptViewMode === 'manager' && (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Filter className="absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400" size={14} />
|
<Filter className="absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400" size={14} />
|
||||||
<select
|
<input
|
||||||
value={selectedManager}
|
list="dl-dept-manager"
|
||||||
onChange={(e) => setSelectedManager(e.target.value)}
|
type="text"
|
||||||
className="w-full pl-9 pr-8 py-1.5 bg-white border border-gray-200 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm appearance-none cursor-pointer font-bold text-gray-700"
|
placeholder="搜索或选择..."
|
||||||
>
|
value={selectedManager === 'All' ? '' : selectedManager}
|
||||||
|
onChange={(e) => setSelectedManager(e.target.value || 'All')}
|
||||||
|
className="w-full pl-9 pr-8 py-1.5 bg-white border border-gray-200 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm font-bold text-gray-700"
|
||||||
|
/>
|
||||||
|
<datalist id="dl-dept-manager">
|
||||||
<option value="All">所有业务员</option>
|
<option value="All">所有业务员</option>
|
||||||
{allManagersList.map(m => (
|
{allManagersList.map(m => (
|
||||||
<option key={m} value={m}>{m}</option>
|
<option key={m} value={m} />
|
||||||
))}
|
))}
|
||||||
</select>
|
</datalist>
|
||||||
<ChevronDown className="absolute right-2.5 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none" size={14} />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -1921,38 +1955,48 @@ export default function App() {
|
|||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">客户名称</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">客户名称</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={14} />
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={14} />
|
||||||
<input
|
<input
|
||||||
|
list="dl-region-customer"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="搜索客户名称..."
|
placeholder="搜索客户名称..."
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-9 pr-3 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all"
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-9 pr-3 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all"
|
||||||
value={regionFilters.customer}
|
value={regionFilters.customer}
|
||||||
onChange={(e) => setRegionFilters(prev => ({ ...prev, customer: e.target.value }))}
|
onChange={(e) => setRegionFilters(prev => ({ ...prev, customer: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-region-customer">
|
||||||
|
{uniqueCustomerNames.map(c => <option key={c} value={c} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">区域</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">区域</label>
|
||||||
<select
|
<input
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all cursor-pointer"
|
list="dl-region-region"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all"
|
||||||
value={regionFilters.region}
|
value={regionFilters.region}
|
||||||
onChange={(e) => setRegionFilters(prev => ({ ...prev, region: e.target.value }))}
|
onChange={(e) => setRegionFilters(prev => ({ ...prev, region: e.target.value }))}
|
||||||
>
|
/>
|
||||||
<option value="">所有区域</option>
|
<datalist id="dl-region-region">
|
||||||
{uniqueRegions.map(r => <option key={r} value={r}>{r}</option>)}
|
{uniqueRegions.map(r => <option key={r} value={r} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">城市</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">城市</label>
|
||||||
<select
|
<input
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all cursor-pointer"
|
list="dl-region-city"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all"
|
||||||
value={regionFilters.city}
|
value={regionFilters.city}
|
||||||
onChange={(e) => setRegionFilters(prev => ({ ...prev, city: e.target.value }))}
|
onChange={(e) => setRegionFilters(prev => ({ ...prev, city: e.target.value }))}
|
||||||
>
|
/>
|
||||||
<option value="">所有城市</option>
|
<datalist id="dl-region-city">
|
||||||
{uniqueCities.map(c => <option key={c} value={c}>{c}</option>)}
|
{uniqueCities.map(c => <option key={c} value={c} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2221,13 +2265,17 @@ export default function App() {
|
|||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">客户名称</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">客户名称</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" size={12} />
|
<Search className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" size={12} />
|
||||||
<input
|
<input
|
||||||
type="text"
|
list="dl-cust-customer"
|
||||||
placeholder="搜索客户..."
|
type="text"
|
||||||
|
placeholder="搜索客户..."
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-8 pr-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-8 pr-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
||||||
value={customerFilters.customer}
|
value={customerFilters.customer}
|
||||||
onChange={(e) => setCustomerFilters(prev => ({ ...prev, customer: e.target.value }))}
|
onChange={(e) => setCustomerFilters(prev => ({ ...prev, customer: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-cust-customer">
|
||||||
|
{uniqueCustomerNames.map(c => <option key={c} value={c} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -2235,49 +2283,62 @@ export default function App() {
|
|||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">业务负责人</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">业务负责人</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" size={12} />
|
<Search className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" size={12} />
|
||||||
<input
|
<input
|
||||||
type="text"
|
list="dl-cust-manager"
|
||||||
placeholder="搜索负责人..."
|
type="text"
|
||||||
|
placeholder="搜索负责人..."
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-8 pr-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-8 pr-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
||||||
value={customerFilters.manager}
|
value={customerFilters.manager}
|
||||||
onChange={(e) => setCustomerFilters(prev => ({ ...prev, manager: e.target.value }))}
|
onChange={(e) => setCustomerFilters(prev => ({ ...prev, manager: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-cust-manager">
|
||||||
|
{uniqueCustomerManagers.map(m => <option key={m} value={m} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">品牌</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">品牌</label>
|
||||||
<select
|
<input
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all cursor-pointer"
|
list="dl-cust-brand"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
||||||
value={customerFilters.brand}
|
value={customerFilters.brand}
|
||||||
onChange={(e) => setCustomerFilters(prev => ({ ...prev, brand: e.target.value }))}
|
onChange={(e) => setCustomerFilters(prev => ({ ...prev, brand: e.target.value }))}
|
||||||
>
|
/>
|
||||||
<option value="">所有品牌</option>
|
<datalist id="dl-cust-brand">
|
||||||
{uniqueBrands.map(b => <option key={b} value={b}>{b}</option>)}
|
{uniqueBrands.map(b => <option key={b} value={b} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">部门</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">部门</label>
|
||||||
<select
|
<input
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all cursor-pointer"
|
list="dl-cust-dept"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
||||||
value={customerFilters.department}
|
value={customerFilters.department}
|
||||||
onChange={(e) => setCustomerFilters(prev => ({ ...prev, department: e.target.value }))}
|
onChange={(e) => setCustomerFilters(prev => ({ ...prev, department: e.target.value }))}
|
||||||
>
|
/>
|
||||||
<option value="">所有部门</option>
|
<datalist id="dl-cust-dept">
|
||||||
{uniqueDepts.map(d => <option key={d} value={d}>{d}</option>)}
|
{uniqueDepts.map(d => <option key={d} value={d} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">区域</label>
|
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">区域</label>
|
||||||
<select
|
<input
|
||||||
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all cursor-pointer"
|
list="dl-cust-region"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索或选择..."
|
||||||
|
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
|
||||||
value={customerFilters.region}
|
value={customerFilters.region}
|
||||||
onChange={(e) => setCustomerFilters(prev => ({ ...prev, region: e.target.value }))}
|
onChange={(e) => setCustomerFilters(prev => ({ ...prev, region: e.target.value }))}
|
||||||
>
|
/>
|
||||||
<option value="">所有区域</option>
|
<datalist id="dl-cust-region">
|
||||||
{uniqueRegions.map(r => <option key={r} value={r}>{r}</option>)}
|
{uniqueRegions.map(r => <option key={r} value={r} />)}
|
||||||
</select>
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2567,12 +2628,16 @@ export default function App() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
|
list="dl-modal-plate"
|
||||||
type="text"
|
type="text"
|
||||||
value={modalFilters.plateNumber}
|
value={modalFilters.plateNumber}
|
||||||
onChange={(e) => setModalFilters({...modalFilters, plateNumber: e.target.value})}
|
onChange={(e) => setModalFilters({...modalFilters, plateNumber: e.target.value})}
|
||||||
placeholder="搜索车牌..."
|
placeholder="搜索车牌..."
|
||||||
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-modal-plate">
|
||||||
|
{uniqueModalPlates.map(p => <option key={p} value={p} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
@@ -2580,12 +2645,16 @@ export default function App() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
|
list="dl-modal-model"
|
||||||
type="text"
|
type="text"
|
||||||
value={modalFilters.model}
|
value={modalFilters.model}
|
||||||
onChange={(e) => setModalFilters({...modalFilters, model: e.target.value})}
|
onChange={(e) => setModalFilters({...modalFilters, model: e.target.value})}
|
||||||
placeholder="搜索车型..."
|
placeholder="搜索车型..."
|
||||||
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-modal-model">
|
||||||
|
{uniqueModalModels.map(m => <option key={m} value={m} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
@@ -2593,12 +2662,16 @@ export default function App() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
|
list="dl-modal-brand"
|
||||||
type="text"
|
type="text"
|
||||||
value={modalFilters.brand}
|
value={modalFilters.brand}
|
||||||
onChange={(e) => setModalFilters({...modalFilters, brand: e.target.value})}
|
onChange={(e) => setModalFilters({...modalFilters, brand: e.target.value})}
|
||||||
placeholder="搜索品牌..."
|
placeholder="搜索品牌..."
|
||||||
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-modal-brand">
|
||||||
|
{uniqueModalBrands.map(b => <option key={b} value={b} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
@@ -2606,12 +2679,16 @@ export default function App() {
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
<Search size={12} className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" />
|
||||||
<input
|
<input
|
||||||
|
list="dl-modal-location"
|
||||||
type="text"
|
type="text"
|
||||||
value={modalFilters.location}
|
value={modalFilters.location}
|
||||||
onChange={(e) => setModalFilters({...modalFilters, location: e.target.value})}
|
onChange={(e) => setModalFilters({...modalFilters, location: e.target.value})}
|
||||||
placeholder="搜索所在地..."
|
placeholder="搜索所在地..."
|
||||||
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
className="w-full text-[11px] pl-7 pr-2 py-1.5 bg-white border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm"
|
||||||
/>
|
/>
|
||||||
|
<datalist id="dl-modal-location">
|
||||||
|
{uniqueModalLocations.map(l => <option key={l} value={l} />)}
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2701,7 +2778,7 @@ export default function App() {
|
|||||||
<td className="p-2 border-r border-gray-100 text-gray-600">{v.type}</td>
|
<td className="p-2 border-r border-gray-100 text-gray-600">{v.type}</td>
|
||||||
<td className="p-2 border-r border-gray-100 text-gray-500 text-[10px] leading-tight">{v.subjectOrg || '—'}</td>
|
<td className="p-2 border-r border-gray-100 text-gray-500 text-[10px] leading-tight">{v.subjectOrg || '—'}</td>
|
||||||
<td className="p-2 border-r border-gray-100 font-bold text-gray-800">{v.customerName || '—'}</td>
|
<td className="p-2 border-r border-gray-100 font-bold text-gray-800">{v.customerName || '—'}</td>
|
||||||
<td className="p-2 border-r border-gray-100 font-mono font-bold text-blue-700">{v.plateNumber}</td>
|
<td className={`p-2 border-r border-gray-100 font-mono font-bold ${v.plateNumber ? 'text-blue-700' : 'text-orange-500'}`}>{v.plateNumber || v.vin || '—'}</td>
|
||||||
<td className="p-2 border-r border-gray-100 text-center">
|
<td className="p-2 border-r border-gray-100 text-center">
|
||||||
<span className={`px-1.5 py-0.5 rounded-full text-[9px] font-bold ${
|
<span className={`px-1.5 py-0.5 rounded-full text-[9px] font-bold ${
|
||||||
v.status === 'Operating' ? 'bg-green-100 text-green-700' :
|
v.status === 'Operating' ? 'bg-green-100 text-green-700' :
|
||||||
@@ -2718,7 +2795,7 @@ export default function App() {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<td className="p-2 border-r border-gray-100 font-mono font-bold text-blue-700 text-center">{v.plateNumber}</td>
|
<td className={`p-2 border-r border-gray-100 font-mono font-bold text-center ${v.plateNumber ? 'text-blue-700' : 'text-orange-500'}`}>{v.plateNumber || v.vin || '—'}</td>
|
||||||
{showPlateNumbers.source !== 'asset' && (
|
{showPlateNumbers.source !== 'asset' && (
|
||||||
<td className="p-2 border-r border-gray-100 font-bold text-gray-800 text-center">{v.customerName || '—'}</td>
|
<td className="p-2 border-r border-gray-100 font-bold text-gray-800 text-center">{v.customerName || '—'}</td>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user