feat: 客户多选筛选、统计报表里程与监控看板数据一致
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 资产管理按客户筛选改为多选(支持同时选多个客户) - 新增 MultiSearchSelect 组件(搜索+标签+复选框) - 统计报表 todayTotal 改用监控缓存数据,与里程看板一致 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,7 @@ import type { SummaryData, TypeSummary, VehicleListItem, DeptGroup, RegionGroup,
|
||||
import { fetchSummary, fetchByType, fetchVehicleList, fetchWeeklyDetail, fetchDeptStats, fetchRegionStats, fetchCustomerStats, fetchInventoryStats, fetchRegionChart } from './api';
|
||||
import type { WeeklyDetailItem } from './api';
|
||||
import { SearchSelect } from '../../components/SearchSelect';
|
||||
import { MultiSearchSelect } from '../../components/MultiSearchSelect';
|
||||
|
||||
|
||||
// --- Constants ---
|
||||
@@ -105,9 +106,9 @@ export default function AssetsModule() {
|
||||
|
||||
// Customer section state
|
||||
const [expandedCustomers, setExpandedCustomers] = useState<Set<string>>(new Set());
|
||||
const [customerFilters, setCustomerFilters] = useState({ customer: '', brand: '', department: '', manager: '', region: '' });
|
||||
const [customerFilters, setCustomerFilters] = useState({ customer: [] as string[], brand: '', department: '', manager: '', region: '' });
|
||||
const [isCustomerFilterOpen, setIsCustomerFilterOpen] = useState(false);
|
||||
const [draftCustomerFilters, setDraftCustomerFilters] = useState({ customer: '', brand: '', department: '', manager: '', region: '' });
|
||||
const [draftCustomerFilters, setDraftCustomerFilters] = useState({ customer: [] as string[], brand: '', department: '', manager: '', region: '' });
|
||||
|
||||
// Inventory statistics section state
|
||||
const [inventoryData, setInventoryData] = useState<RegionalInventoryStats[]>([]);
|
||||
@@ -371,7 +372,7 @@ export default function AssetsModule() {
|
||||
|
||||
// Derived data for customer section
|
||||
const filteredCustomerStats = useMemo(() => customerData.filter((s) => {
|
||||
const mc = !customerFilters.customer || s.customer === customerFilters.customer;
|
||||
const mc = customerFilters.customer.length === 0 || customerFilters.customer.includes(s.customer);
|
||||
const mb = !customerFilters.brand || s.brand === customerFilters.brand;
|
||||
const md = !customerFilters.department || s.department === customerFilters.department;
|
||||
const mm = !customerFilters.manager || s.manager === customerFilters.manager;
|
||||
@@ -2148,14 +2149,14 @@ export default function AssetsModule() {
|
||||
<button
|
||||
onClick={() => { if (!isCustomerFilterOpen) setDraftCustomerFilters({...customerFilters}); setIsCustomerFilterOpen(!isCustomerFilterOpen); }}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
|
||||
isCustomerFilterOpen || Object.values(customerFilters).some(v => v !== '')
|
||||
isCustomerFilterOpen || Object.values(customerFilters).some(v => Array.isArray(v) ? v.length > 0 : v !== '')
|
||||
? 'bg-emerald-400 text-emerald-900 shadow-lg shadow-emerald-900/20'
|
||||
: 'bg-emerald-700/50 text-emerald-100 hover:bg-emerald-700'
|
||||
}`}
|
||||
>
|
||||
<Filter size={14} />
|
||||
<span>筛选</span>
|
||||
{Object.values(customerFilters).some(v => v !== '') && (
|
||||
{Object.values(customerFilters).some(v => Array.isArray(v) ? v.length > 0 : v !== '') && (
|
||||
<span className="w-2 h-2 bg-red-500 rounded-full animate-pulse"></span>
|
||||
)}
|
||||
</button>
|
||||
@@ -2176,14 +2177,14 @@ export default function AssetsModule() {
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-sm font-bold text-gray-900">数据筛选</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<button onClick={() => setDraftCustomerFilters({ customer: '', brand: '', department: '', manager: '', region: '' })} className="text-[10px] text-emerald-600 hover:text-emerald-700 font-medium">重置所有</button>
|
||||
<button onClick={() => setDraftCustomerFilters({ customer: [], brand: '', department: '', manager: '', region: '' })} className="text-[10px] text-emerald-600 hover:text-emerald-700 font-medium">重置所有</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider">客户名称</label>
|
||||
<SearchSelect value={draftCustomerFilters.customer} onChange={(v) => setDraftCustomerFilters(prev => ({ ...prev, customer: v }))} options={uniqueCustomerNames} placeholder="所有客户" className="text-xs py-2 px-2" />
|
||||
<MultiSearchSelect value={draftCustomerFilters.customer} onChange={(v) => setDraftCustomerFilters(prev => ({ ...prev, customer: v }))} options={uniqueCustomerNames} placeholder="所有客户" className="text-xs py-2 px-2" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
@@ -2233,12 +2234,12 @@ export default function AssetsModule() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{Object.values(customerFilters).some(v => v !== '') && (
|
||||
{Object.values(customerFilters).some(v => Array.isArray(v) ? v.length > 0 : v !== '') && (
|
||||
<div className="px-3 py-2 border-b border-gray-100 flex flex-wrap gap-2 items-center">
|
||||
{customerFilters.customer && (
|
||||
{customerFilters.customer.length > 0 && (
|
||||
<span className="px-2 py-0.5 bg-emerald-50 text-emerald-700 rounded text-[10px] flex items-center gap-1">
|
||||
客户: {customerFilters.customer}
|
||||
<button onClick={() => setCustomerFilters(prev => ({...prev, customer: ''}))} className="hover:text-red-500 ml-0.5">×</button>
|
||||
客户: {customerFilters.customer.join(', ')}
|
||||
<button onClick={() => setCustomerFilters(prev => ({...prev, customer: []}))} className="hover:text-red-500 ml-0.5">×</button>
|
||||
</span>
|
||||
)}
|
||||
{customerFilters.manager && (
|
||||
@@ -2265,7 +2266,7 @@ export default function AssetsModule() {
|
||||
<button onClick={() => setCustomerFilters(prev => ({...prev, region: ''}))} className="hover:text-red-500 ml-0.5">×</button>
|
||||
</span>
|
||||
)}
|
||||
<button onClick={() => setCustomerFilters({ customer: '', brand: '', department: '', manager: '', region: '' })} className="text-[11px] text-red-500 font-bold ml-auto hover:text-red-600">清除</button>
|
||||
<button onClick={() => setCustomerFilters({ customer: [], brand: '', department: '', manager: '', region: '' })} className="text-[11px] text-red-500 font-bold ml-auto hover:text-red-600">清除</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user