增加隐藏详细信息

This commit is contained in:
2025-10-26 12:49:41 +08:00
parent 3ee320b2b4
commit f04594e56f
2 changed files with 91 additions and 11 deletions

View File

@@ -5,6 +5,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Badge } from '@/components/ui/badge';
import { CardDescription } from '@/components/ui/card';
import {
Dialog,
DialogContent,
@@ -43,7 +45,9 @@ import {
Save,
X,
Download,
Upload
Upload,
GripVertical,
Globe,
} from 'lucide-react';
import {
DndContext,
@@ -53,6 +57,9 @@ import {
useSensor,
useSensors,
DragEndEvent,
DragStartEvent,
DragOverlay,
defaultDropAnimationSideEffects,
} from '@dnd-kit/core';
import {
arrayMove,
@@ -106,20 +113,36 @@ const RelayStationManager: React.FC<RelayStationManagerProps> = ({ onBack }) =>
const [quotaData, setQuotaData] = useState<Record<string, PackycodeUserQuota>>({});
const [loadingQuota, setLoadingQuota] = useState<Record<string, boolean>>({});
// 拖拽状态
const [activeStation, setActiveStation] = useState<RelayStation | null>(null);
const { t } = useTranslation();
// 拖拽传感器配置
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(PointerSensor, {
activationConstraint: {
distance: 8, // 需要拖动8px才激活避免误触
},
}),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
// 拖拽开始处理
const handleDragStart = (event: DragStartEvent) => {
const { active } = event;
const station = stations.find(s => s.id === active.id);
setActiveStation(station || null);
};
// 拖拽结束处理
const handleDragEnd = async (event: DragEndEvent) => {
const { active, over } = event;
setActiveStation(null); // 清除拖拽状态
if (over && active.id !== over.id) {
const oldIndex = stations.findIndex(station => station.id === active.id);
const newIndex = stations.findIndex(station => station.id === over.id);
@@ -140,6 +163,17 @@ const RelayStationManager: React.FC<RelayStationManagerProps> = ({ onBack }) =>
}
};
// 自定义拖拽动画
const dropAnimationConfig = {
sideEffects: defaultDropAnimationSideEffects({
styles: {
active: {
opacity: '0.4',
},
},
}),
};
// Token 脱敏函数
const maskToken = (token: string): string => {
if (!token || token.length <= 8) {
@@ -855,6 +889,7 @@ const RelayStationManager: React.FC<RelayStationManagerProps> = ({ onBack }) =>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
>
<SortableContext
@@ -892,6 +927,38 @@ const RelayStationManager: React.FC<RelayStationManagerProps> = ({ onBack }) =>
)}
</div>
</SortableContext>
{/* 拖拽预览层 */}
<DragOverlay dropAnimation={dropAnimationConfig}>
{activeStation ? (
<Card className="shadow-2xl ring-2 ring-blue-500 rotate-3 cursor-grabbing">
<CardHeader className="pb-2 pt-3 px-3">
<div className="flex justify-between items-center">
<div className="flex items-center flex-1 min-w-0 mr-2">
<div className="mr-2 flex-shrink-0">
<GripVertical className="h-4 w-4 text-blue-500" />
</div>
<div className="flex-1 min-w-0">
<CardTitle className="text-sm font-medium">{activeStation.name}</CardTitle>
<CardDescription className="text-xs mt-0.5">
{getAdapterDisplayName(activeStation.adapter)}
</CardDescription>
</div>
</div>
<Badge variant={activeStation.enabled ? "default" : "secondary"} className="text-xs">
{activeStation.enabled ? '已启用' : '已禁用'}
</Badge>
</div>
</CardHeader>
<CardContent className="pt-1 pb-3 px-3">
<div className="flex items-center text-xs text-muted-foreground">
<Globe className="mr-1.5 h-3 w-3 flex-shrink-0" />
<span className="truncate">{activeStation.api_url}</span>
</div>
</CardContent>
</Card>
) : null}
</DragOverlay>
</DndContext>
{/* 编辑对话框 */}

View File

@@ -50,6 +50,7 @@ export const SortableStationItem: React.FC<SortableStationItemProps> = ({
transform,
transition,
isDragging,
isOver,
} = useSortable({ id: station.id });
// 展开/收起状态,从 localStorage 读取
@@ -66,24 +67,34 @@ export const SortableStationItem: React.FC<SortableStationItemProps> = ({
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
opacity: isDragging ? 0.4 : 1,
};
// 是否有详情内容需要显示
const hasDetails = station.description || station.adapter === 'packycode';
return (
<Card ref={setNodeRef} style={style} className="relative">
<Card
ref={setNodeRef}
style={style}
className={`relative transition-all duration-200 ${
isDragging
? 'shadow-2xl ring-2 ring-blue-500 scale-105 z-50'
: isOver
? 'ring-2 ring-blue-400 ring-offset-2 bg-blue-50 dark:bg-blue-950/50 scale-102'
: 'hover:shadow-md'
}`}
>
<CardHeader className="pb-2 pt-3 px-3">
<div className="flex justify-between items-center">
<div className="flex items-center flex-1 min-w-0 mr-2">
<button
className="cursor-grab active:cursor-grabbing mr-2 touch-none"
{...attributes}
{...listeners}
>
<div
className="flex items-center flex-1 min-w-0 mr-2 cursor-grab active:cursor-grabbing"
{...attributes}
{...listeners}
>
<div className="mr-2 flex-shrink-0">
<GripVertical className="h-4 w-4 text-muted-foreground hover:text-foreground transition-colors" />
</button>
</div>
<div className="flex-1 min-w-0">
<CardTitle className="text-sm font-medium">{station.name}</CardTitle>
<CardDescription className="text-xs mt-0.5">
@@ -97,6 +108,7 @@ export const SortableStationItem: React.FC<SortableStationItemProps> = ({
variant="ghost"
size="icon"
className="h-8 w-8"
disabled={isDragging}
onClick={(e) => {
e.stopPropagation();
setSelectedStation(station);
@@ -109,6 +121,7 @@ export const SortableStationItem: React.FC<SortableStationItemProps> = ({
variant="ghost"
size="icon"
className="h-8 w-8 text-red-500 hover:text-red-700"
disabled={isDragging}
onClick={(e) => {
e.stopPropagation();
openDeleteDialog(station);