feat(s2a): Improve group display layout with grid-based cards
- Replace badge-style group display with grid-based card layout for better readability - Add group name and ID display in selected groups section with truncation handling - Reorganize profile card group summary to use dedicated grid section below parameters - Remove inline group information from parameter summary badge - Improve visual hierarchy and spacing in group display components - Enhance dark mode styling for group cards with better contrast and borders
This commit is contained in:
@@ -420,9 +420,8 @@ export default function S2AConfig() {
|
||||
<Input label="默认优先级" type="number" min={0} max={100} value={formPriority} onChange={(e) => setFormPriority(Number(e.target.value))} hint="数值越大优先级越高" />
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
{/* 标题 + 获取按钮 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300">分组</label>
|
||||
{/* 获取按钮 */}
|
||||
<div className="flex items-center justify-end">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleFetchGroups}
|
||||
@@ -437,15 +436,21 @@ export default function S2AConfig() {
|
||||
<p className="text-xs text-slate-400">请先填写 S2A API 地址和 Admin Key,再获取分组列表</p>
|
||||
) : null}
|
||||
|
||||
{/* 已选分组 badges */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{formGroupIds.map(id => (
|
||||
<span key={id} className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-sm bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400">
|
||||
{getGroupLabel(id)}
|
||||
<button onClick={() => handleRemoveGroupId(id)} className="hover:text-red-500 transition-colors"><X className="h-3 w-3" /></button>
|
||||
</span>
|
||||
))}
|
||||
{formGroupIds.length === 0 && <span className="text-sm text-slate-400">未选择分组</span>}
|
||||
{/* 已选分组 */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-2">
|
||||
{formGroupIds.map(id => {
|
||||
const group = availableGroups.find(g => g.id === id)
|
||||
return (
|
||||
<div key={id} className="flex items-center justify-between px-3 py-2 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/40">
|
||||
<div className="flex flex-col min-w-0">
|
||||
<span className="text-sm font-medium text-blue-700 dark:text-blue-300 truncate">{group ? group.name : `#${id}`}</span>
|
||||
{group && <span className="text-[10px] text-blue-400 dark:text-blue-500">#{id}</span>}
|
||||
</div>
|
||||
<button onClick={() => handleRemoveGroupId(id)} className="ml-2 text-blue-400 hover:text-red-500 transition-colors flex-shrink-0"><X className="h-3.5 w-3.5" /></button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{formGroupIds.length === 0 && <span className="text-sm text-slate-400 col-span-full">未选择分组</span>}
|
||||
</div>
|
||||
|
||||
{/* 可选分组列表 */}
|
||||
@@ -621,18 +626,13 @@ function ProfileCard({ profile, isActive, isActivating, groupNameCache, onActiva
|
||||
</div>
|
||||
|
||||
{/* 参数摘要 */}
|
||||
<div className="flex flex-wrap gap-1.5 mb-4">
|
||||
<div className="flex flex-wrap gap-1.5 mb-2">
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-300">
|
||||
并发 {profile.concurrency}
|
||||
</span>
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs bg-slate-100 text-slate-600 dark:bg-slate-700 dark:text-slate-300">
|
||||
优先级 {profile.priority}
|
||||
</span>
|
||||
{parsedGroups.length > 0 && (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs bg-blue-50 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400">
|
||||
分组 {parsedGroups.map(id => groupNameCache[id] ? `${groupNameCache[id]} #${id}` : `#${id}`).join(', ')}
|
||||
</span>
|
||||
)}
|
||||
{profile.proxy_enabled && (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs bg-orange-50 text-orange-600 dark:bg-orange-900/30 dark:text-orange-400">
|
||||
<Globe className="h-3 w-3 mr-0.5" /> 代理
|
||||
@@ -640,6 +640,18 @@ function ProfileCard({ profile, isActive, isActivating, groupNameCache, onActiva
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 分组列表 */}
|
||||
{parsedGroups.length > 0 && (
|
||||
<div className="grid grid-cols-2 gap-2 mb-4">
|
||||
{parsedGroups.map(id => (
|
||||
<div key={id} className="flex flex-col px-2.5 py-1.5 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800/40">
|
||||
<span className="text-xs font-medium text-blue-700 dark:text-blue-300 truncate">{groupNameCache[id] || `#${id}`}</span>
|
||||
{groupNameCache[id] && <span className="text-[10px] text-blue-400 dark:text-blue-500">#{id}</span>}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<div className="flex items-center gap-2">
|
||||
{!isActive ? (
|
||||
|
||||
Reference in New Issue
Block a user