@@ -13,9 +13,11 @@ interface AdvancedFilters {
region : string ;
vehicleType : string ;
customer : string ;
department : string ;
manager : string ;
}
const EMPTY_FILTERS : AdvancedFilters = { plateSearch : '' , region : '' , vehicleType : '' , customer : '' } ;
const EMPTY_FILTERS : AdvancedFilters = { plateSearch : '' , region : '' , vehicleType : '' , customer : '' , department : '' , manager : '' } ;
function shortTargetName ( name : string ) : string {
const match = name . match ( /(\d+)[辆台](.+)/ ) ;
@@ -32,24 +34,16 @@ function hasActiveFilters(f: AdvancedFilters): boolean {
return f . plateSearch !== '' || f . region !== '' || f . vehicleType !== '' || f . customer !== '' ;
}
/** Dropdown select with search */
function FilterSelect ( { label , options , value , onChange , placeholder } : {
label : string ;
options : string [ ] ;
value : string ;
onChange : ( v : string ) = > void ;
placeholder : string ;
label : string ; options : string [ ] ; value : string ; onChange : ( v : string ) = > void ; placeholder : string ;
} ) {
const [ open , setOpen ] = useState ( false ) ;
const [ search , setSearch ] = useState ( '' ) ;
const ref = useRef < HTMLDivElement > ( null ) ;
const filtered = options . filter ( o = > o . toLowerCase ( ) . includes ( search . toLowerCase ( ) ) ) ;
useEffect ( ( ) = > {
const handler = ( e : MouseEvent ) = > {
if ( ref . current && ! ref . current . contains ( e . target as Node ) ) setOpen ( false ) ;
} ;
const handler = ( e : MouseEvent ) = > { if ( ref . current && ! ref . current . contains ( e . target as Node ) ) setOpen ( false ) ; } ;
document . addEventListener ( 'mousedown' , handler ) ;
return ( ) = > document . removeEventListener ( 'mousedown' , handler ) ;
} , [ ] ) ;
@@ -65,37 +59,22 @@ function FilterSelect({ label, options, value, onChange, placeholder }: {
< ChevronDown size = { 14 } className = { ` text-slate-400 transition-transform ${ open ? 'rotate-180' : '' } ` } / >
< / button >
{ open && (
< div className = "bg-white border border-slate-200 rounded-lg shadow-lg max-h-48 overflow-hidden z-10" >
< div className = "bg-white border border-slate-200 rounded-lg shadow-lg max-h-48 overflow-hidden z-10 relative " >
{ options . length > 5 && (
< div className = "p-1.5 border-b border-slate-100" >
< div className = "relative" >
< Search size = { 12 } className = "absolute left-2 top-1/2 -translate-y-1/2 text-slate-400" / >
< input
type = "text"
value = { search }
onChange = { e = > setSearch ( e . target . value ) }
placeholder = "搜索..."
className = "w-full pl-7 pr-2 py-1.5 text-xs bg-slate-50 rounded border-none outline-none"
autoFocus
/ >
< input type = "text" value = { search } onChange = { e = > setSearch ( e . target . value ) } placeholder = "搜索..." autoFocus
className = "w-full pl-7 pr-2 py-1.5 text-xs bg-slate-50 rounded border-none outline-none" / >
< / div >
< / div >
) }
< div className = "overflow-y-auto max-h-36" >
< button
onClick = { ( ) = > { onChange ( '' ) ; setOpen ( false ) ; setSearch ( '' ) ; } }
className = { ` w-full text-left px-3 py-2 text-xs hover:bg-slate-50 cursor-pointer ${ ! value ? 'text-blue-600 font-bold' : 'text-slate-400' } ` }
>
全 部
< / button >
< button onClick = { ( ) = > { onChange ( '' ) ; setOpen ( false ) ; setSearch ( '' ) ; } }
className = { ` w-full text-left px-3 py-2 text-xs hover:bg-slate-50 cursor-pointer ${ ! value ? 'text-blue-600 font-bold' : 'text-slate-400' } ` } > 全 部 < / button >
{ filtered . map ( opt = > (
< button
key = { opt }
onClick = { ( ) = > { onChange ( opt ) ; setOpen ( false ) ; setSearch ( '' ) ; } }
className = { ` w-full text-left px-3 py-2 text-xs hover:bg-slate-50 cursor-pointer ${ value === opt ? 'text-blue-600 font-bold bg-blue-50' : 'text-slate-700' } ` }
>
{ opt }
< / button >
< button key = { opt } onClick = { ( ) = > { onChange ( opt ) ; setOpen ( false ) ; setSearch ( '' ) ; } }
className = { ` w-full text-left px-3 py-2 text-xs hover:bg-slate-50 cursor-pointer ${ value === opt ? 'text-blue-600 font-bold bg-blue-50' : 'text-slate-700' } ` } > { opt } < / button >
) ) }
< / div >
< / div >
@@ -116,169 +95,153 @@ export default function SchedulingModule() {
const loadData = useCallback ( async ( ) = > {
setLoading ( true ) ;
try {
const result = await fetchSuggestions ( selectedTargetId ) ;
setData ( result ) ;
} finally {
setLoading ( false ) ;
}
try { setData ( await fetchSuggestions ( selectedTargetId ) ) ; } finally { setLoading ( false ) ; }
} , [ selectedTargetId ] ) ;
useEffect ( ( ) = > { loadData ( ) ; } , [ loadData ] ) ;
const handleNotifySuccess = useCallback ( ( ) = > { loadData ( ) ; } , [ loadData ] ) ;
// Compute filter options from data
const filterOptions = useMemo ( ( ) = > {
if ( ! data ) return { regions : [ ] , vehicleTypes : [ ] , customers : [ ] } ;
const regions = new Set < string > ( ) ;
const vehicleTypes = new Set < string > ( ) ;
const customers = new Set < string > ( ) ;
if ( ! data ) return { regions : [ ] , vehicleTypes : [ ] , customers : [ ] , departments : [ ] , managers : [ ] } ;
const r = new Set < string > ( ) , t = new Set < string > ( ) , c = new Set < string > ( ) , d = new Set < string > ( ) , m = new Set < string > ( ) ;
for ( const s of data . suggestions ) {
const v = s . currentVehicle ;
if ( v . region ) regions . add ( v . region ) ;
if ( v . vehicleType ) vehicleTypes . add ( v . vehicleType ) ;
if ( v . customer ) customers . add ( v . customer ) ;
if ( v . region ) r . add ( v . region ) ;
if ( v . vehicleType ) t . add ( v . vehicleType ) ;
if ( v . customer ) c . add ( v . customer ) ;
if ( v . department ) d . add ( v . department ) ;
if ( v . manager ) m . add ( v . manager ) ;
}
return {
regions : Array.from ( regions ) . sort ( ) ,
vehicleTypes : Array.from ( vehicleTypes ) . sort ( ) ,
customers : Array.from ( customers ) . sort ( ) ,
} ;
return { regions : [ . . . r ] . sort ( ) , vehicleTypes : [ . . . t ] . sort ( ) , customers : [ . . . c ] . sort ( ) , departments : [ . . . d ] . sort ( ) , managers : [ . . . m ] . sort ( ) } ;
} , [ data ] ) ;
const filteredSuggestions = useMemo ( ( ) = > {
if ( ! data ) return [ ] ;
let list = data . suggestions ;
// Type filter from cards
if ( typeFilter === 'qualified' ) list = list . filter ( s = > s . type === 'replace_qualified' ) ;
if ( typeFilter === 'hopeless' ) list = list . filter ( s = > s . type === 'rescue_hopeless' ) ;
// Advanced filters
if ( filters . plateSearch ) {
const q = filters . plateSearch . toLowerCase ( ) ;
list = list . filter ( s = > s . currentVehicle . plateNumber . toLowerCase ( ) . includes ( q ) ) ;
}
if ( filters . plateSearch ) { const q = filters . plateSearch . toLowerCase ( ) ; list = list . filter ( s = > s . currentVehicle . plateNumber . toLowerCase ( ) . includes ( q ) ) ; }
if ( filters . region ) list = list . filter ( s = > s . currentVehicle . region === filters . region ) ;
if ( filters . vehicleType ) list = list . filter ( s = > s . currentVehicle . vehicleType === filters . vehicleType ) ;
if ( filters . customer ) list = list . filter ( s = > s . currentVehicle . customer === filters . customer ) ;
if ( filters . department ) list = list . filter ( s = > s . currentVehicle . department === filters . department ) ;
if ( filters . manager ) list = list . filter ( s = > s . currentVehicle . manager === filters . manager ) ;
return list ;
} , [ data , typeFilter , filters ] ) ;
const summary = data ? . summary ;
const activeFilterCount = [ filters . plateSearch , filters . region , filters . vehicleType , filters . customer ] . filter ( Boolean ) . length ;
const activeFilterCount = [ filters . plateSearch , filters . region , filters . vehicleType , filters . customer , filters . department , filters . manager ]. filter ( Boolean ) . length ;
return (
< div className = "min-h-screen bg-[#F8F9FB ] text-gray -800 font-sans p-3 md:p-6" style = { { overflowX : 'clip' } } >
< div className = "min-h-screen bg-[#F0F4F8 ] text-slate -800 font-sans p-3 md:p-6" style = { { overflowX : 'clip' } } >
< div className = "max-w-6xl mx-auto flex flex-col gap-3 pb-16 md:pb-0" >
{ /* ========== Top: Summary Cards ========== */ }
< div className = "grid grid-cols-3 gap-3 " >
{ /* ===== Summary Cards ===== */ }
< div className = "grid grid-cols-3 gap-2.5 " >
{ /* 里程高·换下 — warm orange */ }
< button
onClick = { ( ) = > setTypeFilter ( typeFilter === 'qualified' ? 'all' : 'qualified' ) }
className = { ` p-4 rounded-2xl text-left transition-all cursor-pointer ${
className = { ` p-3.5 rounded-2xl text-left transition-all cursor-pointer ${
typeFilter === 'qualified'
? 'bg-amber-100 border-2 border-amber-400 shadow-sm '
: 'bg-amber-50 border border-amber-10 0'
? 'bg-orange-500 text-white shadow-lg shadow-orange-500/25 '
: 'bg-gradient-to-br from-orange-50 to- amber-50 border border-orange-200/6 0'
} ` }
>
< div className = " text-[10px] font-bold text-amber-700 mb-1" > 里 程 高 · 需 换 下 < / div >
< div className = "text-2xl font-black text-amber-800" >
{ loading && ! data ? '-' : summary ? . qualifiedCount ? ? 0 }
< span className = " text-xs font-normal text-amber-600 ml-1" > 台 < / span >
< div className = { ` text-[10px] font-bold mb-1 ${ typeFilter === 'qualified' ? 'text-orange-100' : 'text-orange-600' } ` } >
里 程 高 · 需 换 下
< / div >
< div className = { ` text-2xl font-black ${ typeFilter === 'qualified' ? 'text-white' : 'text-orange-700' } ` } >
{ loading && ! data ? '-' : summary ? . qualifiedCount ? ? 0 }
< span className = { ` text-[10px] font-normal ml-1 ${ typeFilter === 'qualified' ? 'text-orange-200' : 'text-orange-400' } ` } > 台 < / span >
< / div >
< div className = { ` text-[9px] mt-0.5 ${ typeFilter === 'qualified' ? 'text-orange-200' : 'text-orange-400' } ` } >
已 达 标 , 换 上 里 程 少 的 车
< / div >
< div className = "text-[9px] text-amber-600 mt-1" > 已 达 标 , 换 上 里 程 少 的 车 < / div >
< / button >
{ /* 里程低·换走 — cool blue */ }
< button
onClick = { ( ) = > setTypeFilter ( typeFilter === 'hopeless' ? 'all' : 'hopeless' ) }
className = { ` p-4 rounded-2xl text-left transition-all cursor-pointer ${
className = { ` p-3.5 rounded-2xl text-left transition-all cursor-pointer ${
typeFilter === 'hopeless'
? 'bg-blue-1 00 border-2 border-blue-400 shadow-sm '
: 'bg-blue -50 border border-blue-10 0'
? 'bg-blue-6 00 text-white shadow-lg shadow-blue-600/25 '
: 'bg-gradient-to-br from-blue-50 to-indigo -50 border border-blue-200/6 0'
} ` }
>
< div className = " text-[10px] font-bold text-blue-7 00 mb-1" > 里 程 低 · 需 换 走 < / div >
< div className = "text-2xl font-black text-blue-800" >
{ loading && ! data ? '-' : summary ? . hopelessCount ? ? 0 }
< span className = " text-xs font-normal text-blue-6 00 ml-1" > 台 < / span >
< div className = { ` text-[10px] font-bold mb-1 ${ typeFilter === 'hopeless' ? ' text-blue-1 00' : 'text-blue-600' } ` } >
里 程 低 · 需 换 走
< / div >
< div className = { ` text-2xl font-black ${ typeFilter === 'hopeless' ? 'text-white' : ' text-blue-7 00' } ` } >
{ loading && ! data ? '-' : summary ? . hopelessCount ? ? 0 }
< span className = { ` text-[10px] font-normal ml-1 ${ typeFilter === 'hopeless' ? 'text-blue-200' : 'text-blue-400' } ` } > 台 < / span >
< / div >
< div className = { ` text-[9px] mt-0.5 ${ typeFilter === 'hopeless' ? 'text-blue-200' : 'text-blue-400' } ` } >
无 法 达 标 , 调 给 高 里 程 客 户
< / div >
< div className = "text-[9px] text-blue-600 mt-1" > 无 法 达 标 , 调 给 高 里 程 客 户 < / div >
< / button >
{ /* 替换建议 — neutral dark */ }
< button
onClick = { ( ) = > setTypeFilter ( 'all' ) }
className = { ` p-4 rounded-2xl text-left transition-all cursor-pointer ${
className = { ` p-3.5 rounded-2xl text-left transition-all cursor-pointer ${
typeFilter === 'all'
? 'bg-emerald-100 border-2 border-emerald-400 shadow-sm '
: 'bg-emerald-5 0 border border-emerald-10 0'
? 'bg-slate-800 text-white shadow-lg shadow-slate-800/25 '
: 'bg-gradient-to-br from-slate-50 to-slate-10 0 border border-slate-200/6 0'
} ` }
>
< div className = " text-[10px] font-bold text-emerald-700 mb-1" > 替 换 建 议 < / div >
< div className = "text-2xl font-black text-emerald-800" >
{ loading && ! data ? '-' : summary ? . suggestionCount ? ? 0 }
< span className = "text-xs font-normal text-emerald-600 ml-1" > 条 < / span >
< div className = { ` text-[10px] font-bold mb-1 ${ typeFilter === 'all' ? 'text-slate-300' : 'text-slate-500' } ` } >
替 换 建 议
< / div >
< div className = "text-[9px] text-emerald-600 mt-1" >
< div className = { ` text-2xl font-black ${ typeFilter === 'all' ? 'text-white' : 'text-slate-800' } ` } >
{ loading && ! data ? '-' : summary ? . suggestionCount ? ? 0 }
< span className = { ` text-[10px] font-normal ml-1 ${ typeFilter === 'all' ? 'text-slate-400' : 'text-slate-400' } ` } > 条 < / span >
< / div >
< div className = { ` text-[9px] mt-0.5 ${ typeFilter === 'all' ? 'text-slate-400' : 'text-slate-400' } ` } >
预 计 + { summary ? . estimatedGain ? ? 0 } 台 可 新 增 达 标
< / div >
< / button >
< / div >
{ /* ========== Bottom: List Card ========== */ }
< div className = "bg-white rounded-2xl border border-slate-10 0 shadow-sm overflow-hidden" >
{ /* ===== List Card ===== */ }
< div className = "bg-white rounded-2xl border border-slate-200/6 0 shadow-sm overflow-hidden" >
{ /* Header */ }
< div className = "px-4 py-3 border-b border-slate-100" >
< div className = "flex items-center justify-between mb-3" >
< h3 className = "text-sm font-bold text-slate-900" > 智 能 调 度 干 预 清 单 < / h3 >
< div className = "flex items-center gap-1" >
< button
onClick = { loadData }
disabled = { loading }
className = "p-1.5 text-slate-400 hover:text-slate-600 transition-colors rounded-lg hover:bg-slate-50 cursor-pointer"
>
< button onClick = { loadData } disabled = { loading }
className = "p-1.5 text-slate-400 hover:text-slate-600 transition-colors rounded-lg hover:bg-slate-50 cursor-pointer" >
< RotateCcw size = { 15 } className = { loading ? 'animate-spin' : '' } / >
< / button >
< button
onClick = { ( ) = > { setShowFilter ( ! showFilter ) ; setTempFilters ( filters ) ; } }
className = { ` relative p-1.5 transition-colors rounded-lg cursor-pointer ${
showFilter || activeFilterCount > 0
? 'text-blue-600 bg-blue-50'
: 'text-slate-400 hover:text-slate-600 hover:bg-slate-50'
showFilter || activeFilterCount > 0 ? 'text-blue-600 bg-blue-50' : 'text-slate-400 hover:text-slate-600 hover:bg-slate-50'
} ` }
>
< Filter size = { 15 } / >
{ activeFilterCount > 0 && (
< span className = "absolute -top-1 -right-1 w-4 h-4 bg-blue-600 text-white text-[8px] font-bold rounded-full flex items-center justify-center" >
{ activeFilterCount }
< / span >
< span className = "absolute -top-1 -right-1 w-4 h-4 bg-blue-600 text-white text-[8px] font-bold rounded-full flex items-center justify-center" > { activeFilterCount } < / span >
) }
< / button >
< / div >
< / div >
{ /* Batch selector pills */ }
< div className = "flex gap-2 overflow-x-auto no-scrollbar" >
< button
onClick = { ( ) = > { setSelectedTargetId ( undefined ) ; setTypeFilter ( 'all' ) ; } }
className = { ` px-4 py-1.5 rounded-full text-[11px] font-bold whitespace-nowrap transition-all cursor-pointer ${
selectedTargetId === undefined
? 'bg-blue-600 text-white shadow-md shadow-blue-600/20'
: 'bg-slate-50 text-slate-500 hover:bg-slate-100'
selectedTargetId === undefined ? 'bg-slate-800 text-white shadow-sm' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
} ` }
>
全 部 批 次
< / button >
{ data ? . targets . map ( t = > (
< button
key = { t . id }
< button key = { t . id }
onClick = { ( ) = > { setSelectedTargetId ( t . id ) ; setTypeFilter ( 'all' ) ; } }
className = { ` px-4 py-1.5 rounded-full text-[11px] font-bold whitespace-nowrap transition-all cursor-pointer ${
selectedTargetId === t . id
? 'bg-blue-600 text-white shadow-md shadow-blue-600/20'
: 'bg-slate-50 text-slate-500 hover:bg-slate-100'
selectedTargetId === t . id ? 'bg-slate-800 text-white shadow-sm' : 'bg-slate-100 text-slate-500 hover:bg-slate-200'
} ` }
>
{ shortTargetName ( t . name ) }
@@ -287,83 +250,37 @@ export default function SchedulingModule() {
< / div >
< / div >
{ /* Advanced Filter Panel */ }
{ /* Filter Panel */ }
< AnimatePresence >
{ showFilter && (
< motion.div
initial = { { height : 0 , opacity : 0 } }
animate = { { height : 'auto' , opacity : 1 } }
exit = { { height : 0 , opacity : 0 } }
className = "overflow-hidden border-b border-slate-100"
>
< div className = "px-4 py-4 bg-slate-50/50 space-y-3" >
< motion.div initial = { { height : 0 , opacity : 0 } } animate = { { height : 'auto' , opacity : 1 } } exit = { { height : 0 , opacity : 0 } } className = "overflow-hidden border-b border-slate-100" >
< div className = "px-4 py-4 bg-slate-50/60 space-y-3" >
< div className = "flex items-center justify-between" >
< span className = "text-xs font-bold text-slate-700" > 高 级 筛 选 < / span >
{ hasActiveFilters ( tempFilters ) && (
< button
onClick = { ( ) = > setTempFilters ( EMPTY_FILTERS ) }
className = "text-[10px] text-slate-400 hover:text-slate-600 cursor-pointer"
>
重 置
< / button >
< button onClick = { ( ) = > setTempFilters ( EMPTY_FILTERS ) } className = "text-[10px] text-slate-400 hover:text-slate-600 cursor-pointer" > 重 置 < / button >
) }
< / div >
{ /* Plate search */ }
< div className = "space-y-1" >
< label className = "text-[10px] text-slate-400 uppercase font-bold" > 车 牌 号 < / label >
< div className = "relative" >
< Search size = { 12 } className = "absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" / >
< input
type = "text"
value = { tempFilters . plateSearch }
onChange = { e = > setTempFilters ( prev = > ( { . . . prev , plateSearch : e.target.value } ) ) }
placeholder = "搜索车牌号..."
className = "w-full pl-8 pr-3 py-2 bg-white rounded-lg text-xs border border-slate-200 outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-400 transition-all"
/ >
< input type = "text" value = { tempFilters . plateSearch } onChange = { e = > setTempFilters ( prev = > ( { . . . prev , plateSearch : e.target.value } ) ) }
placeholder = "搜索车牌号..." className = "w-full pl-8 pr-3 py-2 bg-white rounded-lg text-xs border border-slate-200 outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-400 transition-all" / >
< / div >
< / div >
{ /* Selects in 2-column grid */ }
< div className = "grid grid-cols-2 gap-3" >
< FilterSelect
label = "运营区域"
options = { filterOptions . regions }
value = { tempFilters . region }
onChange = { v = > setTempFilters ( prev = > ( { . . . prev , region : v } ) ) }
placeholder = "全部区域"
/ >
< FilterSelect
label = "车辆类型"
options = { filterOptions . vehicleTypes }
value = { tempFilters . vehicleType }
onChange = { v = > setTempFilters ( prev = > ( { . . . prev , vehicleType : v } ) ) }
placeholder = "全部类型"
/ >
< FilterSelect label = "运营区域" options = { filterOptions . regions } value = { tempFilters . region } onChange = { v = > setTempFilters ( prev = > ( { . . . prev , region : v } ) ) } placeholder = "全部区域" / >
< FilterSelect label = "车辆类型" options = { filterOptions . vehicleTypes } value = { tempFilters . vehicleType } onChange = { v = > setTempFilters ( prev = > ( { . . . prev , vehicleType : v } ) ) } placeholder = "全部类型" / >
< / div >
< FilterSelect
label = "客户"
options = { filterOptions . customers }
value = { tempFilters . customer }
onChange = { v = > setTempFilters ( prev = > ( { . . . prev , customer : v } ) ) }
placeholder = "全部客户"
/ >
{ /* Apply / Cancel */ }
< div className = "grid grid-cols-2 gap-3" >
< FilterSelect label = "业务部门" options = { filterOptions . departments } value = { tempFilters . department } onChange = { v = > setTempFilters ( prev = > ( { . . . prev , department : v } ) ) } placeholder = "全部部门" / >
< FilterSelect label = "业务负责人" options = { filterOptions . managers } value = { tempFilters . manager } onChange = { v = > setTempFilters ( prev = > ( { . . . prev , manager : v } ) ) } placeholder = "全部负责人" / >
< / div >
< FilterSelect label = "客户" options = { filterOptions . customers } value = { tempFilters . customer } onChange = { v = > setTempFilters ( prev = > ( { . . . prev , customer : v } ) ) } placeholder = "全部客户" / >
< div className = "flex gap-2 pt-1" >
< button
onClick = { ( ) = > setShowFilter ( false ) }
className = "flex-1 py-2 text-xs font-bold text-slate-500 bg-white border border-slate-200 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors"
>
取 消
< / button >
< button
onClick = { ( ) = > { setFilters ( tempFilters ) ; setShowFilter ( false ) ; } }
className = "flex-1 py-2 text-xs font-bold text-white bg-blue-600 rounded-lg cursor-pointer hover:bg-blue-700 transition-colors shadow-sm shadow-blue-200"
>
确 认 筛 选
< / button >
< button onClick = { ( ) = > setShowFilter ( false ) } className = "flex-1 py-2 text-xs font-bold text-slate-500 bg-white border border-slate-200 rounded-lg cursor-pointer hover:bg-slate-50 transition-colors" > 取 消 < / button >
< button onClick = { ( ) = > { setFilters ( tempFilters ) ; setShowFilter ( false ) ; } } className = "flex-1 py-2 text-xs font-bold text-white bg-slate-800 rounded-lg cursor-pointer hover:bg-slate-900 transition-colors shadow-sm" > 确 认 筛 选 < / button >
< / div >
< / div >
< / motion.div >
@@ -374,66 +291,31 @@ export default function SchedulingModule() {
{ activeFilterCount > 0 && ! showFilter && (
< div className = "px-4 py-2 border-b border-slate-100 flex items-center gap-2 flex-wrap" >
< span className = "text-[10px] text-slate-400" > 筛 选 : < / span >
{ filters . plateSearch && (
< span className = "text-[10px] bg-blue-5 0 text-blu e-7 00 px-2 py-0.5 rounded-full flex items-center gap-1" >
车 牌 "{filters.plateSearch}"
< X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , plateSearch : '' } ) ) } / >
< / span >
) }
{ f ilters. region && (
< span className = "text-[10px] bg-blue-50 text-blue-700 px-2 py-0.5 rounded-full flex items-center gap-1" >
{ filters . region }
< X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , region : '' } ) ) } / >
< / span >
) }
{ filters . vehicleType && (
< span className = "text-[10px] bg-blue-50 text-blue-700 px-2 py-0.5 rounded-full flex items-center gap-1" >
{ filters . vehicleType }
< X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , vehicleType : '' } ) ) } / >
< / span >
) }
{ filters . customer && (
< span className = "text-[10px] bg-blue-50 text-blue-700 px-2 py-0.5 rounded-full flex items-center gap-1" >
{ filters . customer }
< X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , customer : '' } ) ) } / >
< / span >
) }
< button
onClick = { ( ) = > setFilters ( EMPTY_FILTERS ) }
className = "text-[10px] text-slate-400 hover:text-slate-600 cursor-pointer"
>
清 除 全 部
< / button >
{ filters . plateSearch && < span className = "text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full flex items-center gap-1" > 车 牌 "{filters.plateSearch}" < X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , plateSearch : '' } ) ) } / > < / span > }
{ filters . region && < span className = "text-[10px] bg-slate-10 0 text-slat e-6 00 px-2 py-0.5 rounded-full flex items-center gap-1" > { filters . region } < X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , region : '' } ) ) } / > < / span > }
{ filters . vehicleType && < span className = "text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full flex items-center gap-1" > { filters . vehicleType } < X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , vehicleType : '' } ) ) } / > < / span > }
{ filters . department && < span className = "text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full flex items-center gap-1" > { filters . department } < X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , department : '' } ) ) } / > < / span > }
{ filters . manager && < span className = "text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full flex items-center gap-1" > { filters . manager } < X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , manager : '' } ) ) } / > < / span > }
{ filters . customer && < span className = "text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full flex items-center gap-1" > { filters . customer } < X size = { 10 } className = "cursor-pointer" onClick = { ( ) = > setFilters ( prev = > ( { . . . prev , customer : '' } ) ) } / > < / span > }
< button onClick = { ( ) = > setF ilters( EMPTY_FILTERS ) } className = "text-[10px] text-slate-400 hover:text-slate-600 cursor-pointer" > 清 除 < / button >
< / div >
) }
{ /* Result count when filtered */ }
{ ( activeFilterCount > 0 || typeFilter !== 'all' ) && (
< div className = "px-4 py-1.5 border-b border-slate-50 text-[10px] text-slate-400" >
共 { filteredSuggestions . length } 条 结 果
< / div >
< div className = "px-4 py-1.5 border-b border-slate-50 text-[10px] text-slate-400" > 共 { filteredSuggestions . length } 条 结 果 < / div >
) }
{ /* List body */ }
{ loading && ! data ? (
< div className = "flex items-center justify-center py-20" >
< div className = "w-6 h-6 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" / >
< / div >
) : (
< SuggestionList
suggestions = { filteredSuggestions }
onSelect = { setSelectedSuggestion }
/ >
< SuggestionList suggestions = { filteredSuggestions } onSelect = { setSelectedSuggestion } / >
) }
< / div >
{ /* Detail Modal */ }
{ selectedSuggestion && (
< SuggestionDetail
suggestion = { selectedSuggestion }
onClose = { ( ) = > setSelectedSuggestion ( null ) }
onNotifySuccess = { handleNotifySuccess }
/ >
< SuggestionDetail suggestion = { selectedSuggestion } onClose = { ( ) = > setSelectedSuggestion ( null ) } onNotifySuccess = { handleNotifySuccess } / >
) }
< / div >
< / div >