@@ -53,15 +53,23 @@ const SelectNetItem: React.FC<SelectNetItemProps> = ({
5353 < Collapsible
5454 open = { isOpen }
5555 onOpenChange = { onToggleOpen }
56- className = "border rounded-lg p-2 "
56+ className = "border border-slate-200 dark:border-slate-700 rounded-xl bg-white/50 dark:bg-slate-800/50 backdrop-blur-sm shadow-sm hover:shadow-md transition-all duration-300 "
5757 >
58- < CollapsibleTrigger className = "flex items-center justify-between w-full p-2" >
59- < span className = "text-lg font-medium" > { netName } </ span >
58+ < CollapsibleTrigger className = "flex items-center justify-between w-full p-4 hover:bg-slate-50/50 dark:hover:bg-slate-800/50 rounded-xl transition-all duration-200" >
59+ < div className = "flex items-center gap-3" >
60+ < div className = "w-3 h-3 rounded-full bg-gradient-to-r from-blue-500 to-purple-500" />
61+ < span className = "text-lg font-semibold text-slate-800 dark:text-slate-200" > { netName } </ span >
62+ { net . selected && (
63+ < span className = "text-xs px-2 py-1 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full" >
64+ { net . selected }
65+ </ span >
66+ ) }
67+ </ div >
6068 < div className = "flex items-center gap-2" >
6169 < Button
6270 variant = "ghost"
6371 size = "icon"
64- className = "h-8 w-8 "
72+ className = "h-9 w-9 hover:bg-slate-100 dark:hover:bg-slate-700 rounded-lg transition-all duration-200 "
6573 disabled = { net . list ?. some ( item => testingStates [ item ] ) }
6674 onClick = { ( e ) => {
6775 e . preventDefault ( ) ;
@@ -70,38 +78,64 @@ const SelectNetItem: React.FC<SelectNetItemProps> = ({
7078 }
7179 } }
7280 >
73- < Timer className = { clsx ( "h-4 w-4" , {
74- "animate-spin" : net . list ?. some ( item => testingStates [ item ] )
81+ < Timer className = { clsx ( "h-4 w-4 text-slate-600 dark:text-slate-400 " , {
82+ "animate-spin text-blue-500 " : net . list ?. some ( item => testingStates [ item ] )
7583 } ) } />
7684 </ Button >
77- < ChevronDown
78- className = { clsx ( "h-4 w-4 transition-transform duration-200" , {
79- "transform rotate-180" : isOpen
80- } ) }
81- />
85+ < div className = { clsx ( "h-8 w-8 flex items-center justify-center rounded-lg bg-slate-100 dark:bg-slate-800 transition-all duration-200" , {
86+ "bg-gradient-to-r from-blue-500 to-purple-500 text-white" : isOpen
87+ } ) } >
88+ < ChevronDown
89+ className = { clsx ( "h-4 w-4 text-slate-600 dark:text-slate-400 transition-transform duration-200" , {
90+ "transform rotate-180 text-white" : isOpen
91+ } ) }
92+ />
93+ </ div >
8294 </ div >
8395 </ CollapsibleTrigger >
84- < CollapsibleContent className = "mt-2 flex flex-wrap gap-2" >
85- { net . list ?. map ( ( item ) => {
86- const latency = latencyResults [ item ] ?. response ;
87- const { text : latencyText , colorClass } = formatLatency ( latency ) ;
96+ < CollapsibleContent className = "px-4 pb-4" >
97+ < div className = "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3" >
98+ { net . list ?. map ( ( item ) => {
99+ const latency = latencyResults [ item ] ?. response ;
100+ const { text : latencyText , colorClass } = formatLatency ( latency ) ;
101+ const isSelected = item === net . selected ;
102+ const isTesting = testingStates [ item ] ;
88103
89- return (
90- < Button
91- key = { item }
92- variant = { item === net . selected ? 'default' : 'outline' }
93- onClick = { ( ) => onSelect ( item ) }
94- disabled = { testingStates [ item ] }
95- >
96- { item }
97- { item in latencyResults && (
98- < span className = { cn ( "ml-2 text-xs" , colorClass ) } >
99- { latencyText }
100- </ span >
101- ) }
102- </ Button >
103- ) ;
104- } ) }
104+ return (
105+ < Button
106+ key = { item }
107+ variant = { isSelected ? 'default' : 'outline' }
108+ onClick = { ( ) => onSelect ( item ) }
109+ disabled = { isTesting }
110+ className = { cn (
111+ "h-auto py-3 px-4 text-left transition-all duration-200" ,
112+ isSelected && "bg-gradient-to-r from-blue-500 to-purple-500 border-0 shadow-md shadow-blue-500/25" ,
113+ ! isSelected && "hover:bg-slate-50 dark:hover:bg-slate-800 hover:border-slate-300 dark:hover:border-slate-600" ,
114+ isTesting && "opacity-50 cursor-not-allowed"
115+ ) }
116+ >
117+ < div className = "flex flex-col items-start gap-1" >
118+ < span className = "font-medium text-sm" > { item } </ span >
119+ { item in latencyResults && (
120+ < div className = "flex items-center gap-1" >
121+ < span className = { cn ( "text-xs font-medium" , colorClass ) } >
122+ { latencyText }
123+ </ span >
124+ < span className = "text-xs text-slate-500 dark:text-slate-400" >
125+ { latencyResults [ item ] ?. region && `• ${ latencyResults [ item ] ?. region } ` }
126+ </ span >
127+ </ div >
128+ ) }
129+ </ div >
130+ { isTesting && (
131+ < div className = "absolute top-2 right-2" >
132+ < div className = "w-2 h-2 bg-blue-500 rounded-full animate-pulse" />
133+ </ div >
134+ ) }
135+ </ Button >
136+ ) ;
137+ } ) }
138+ </ div >
105139 </ CollapsibleContent >
106140 </ Collapsible >
107141 ) ;
@@ -200,19 +234,33 @@ export const SelectNetPanel: React.FC = () => {
200234
201235 return (
202236 < div className = "space-y-4" >
203- { selectNets . map ( ( [ netName , net ] ) => (
204- < SelectNetItem
205- key = { netName }
206- netName = { netName }
207- net = { net }
208- isOpen = { openStates [ netName ] }
209- onToggleOpen = { ( ) => toggleOpen ( netName ) }
210- onSelect = { ( selected ) => handleSelect ( netName , selected ) }
211- testingStates = { testingStates }
212- latencyResults = { latencyResults }
213- onBatchSpeedTest = { handleBatchSpeedTest }
214- />
215- ) ) }
237+ { selectNets . length === 0 ? (
238+ < div className = "text-center py-12" >
239+ < div className = "w-16 h-16 mx-auto mb-4 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full flex items-center justify-center" >
240+ < Activity className = "h-8 w-8 text-white" />
241+ </ div >
242+ < h3 className = "text-lg font-medium text-slate-800 dark:text-slate-200 mb-2" >
243+ 暂无可用节点
244+ </ h3 >
245+ < p className = "text-sm text-slate-600 dark:text-slate-400" >
246+ 请检查配置文件或添加新的代理节点
247+ </ p >
248+ </ div >
249+ ) : (
250+ selectNets . map ( ( [ netName , net ] ) => (
251+ < SelectNetItem
252+ key = { netName }
253+ netName = { netName }
254+ net = { net }
255+ isOpen = { openStates [ netName ] }
256+ onToggleOpen = { ( ) => toggleOpen ( netName ) }
257+ onSelect = { ( selected ) => handleSelect ( netName , selected ) }
258+ testingStates = { testingStates }
259+ latencyResults = { latencyResults }
260+ onBatchSpeedTest = { handleBatchSpeedTest }
261+ />
262+ ) )
263+ ) }
216264 </ div >
217265 ) ;
218266} ;
0 commit comments