diff --git a/vite-frontend/src/pages/forward.tsx b/vite-frontend/src/pages/forward.tsx
index d2de647..8966ea8 100644
--- a/vite-frontend/src/pages/forward.tsx
+++ b/vite-frontend/src/pages/forward.tsx
@@ -63,6 +63,7 @@ import {
resumeForwardService,
diagnoseForward,
updateForwardOrder,
+ getConfigByName,
} from "@/api";
import {
type ForwardAddressItem,
@@ -145,6 +146,8 @@ interface ForwardTunnelGroup {
const UNKNOWN_FORWARD_USER_NAME = "未知用户";
const UNCATEGORIZED_FORWARD_TUNNEL_NAME = "未分类";
+const FORWARD_COMPACT_MODE_CONFIG_KEY = "forward_compact_mode";
+const FORWARD_COMPACT_MODE_EVENT = "forwardCompactModeChanged";
const FORWARD_GROUPED_TABLE_MIN_WIDTH_CLASS = "min-w-[1320px]";
const FORWARD_GROUPED_TABLE_COLUMN_CLASS = {
select: "w-14",
@@ -205,6 +208,7 @@ export default function ForwardPage() {
"",
);
const [isSearchVisible, setIsSearchVisible] = useState(false);
+ const [compactMode, setCompactMode] = useState(false);
// 显示模式状态 - 从localStorage读取,默认为平铺显示
const [viewMode, setViewMode] = useState<"grouped" | "direct">(() => {
@@ -308,6 +312,43 @@ export default function ForwardPage() {
};
}, []);
+ useEffect(() => {
+ const loadForwardCompactMode = async () => {
+ try {
+ const response = await getConfigByName(FORWARD_COMPACT_MODE_CONFIG_KEY);
+ const enabled =
+ response.code === 0 &&
+ typeof response.data?.value === "string" &&
+ response.data.value === "true";
+
+ setCompactMode(enabled);
+ } catch {
+ setCompactMode(false);
+ }
+ };
+
+ const handleCompactModeChanged = (event: Event) => {
+ const customEvent = event as CustomEvent<{ enabled?: boolean }>;
+
+ if (typeof customEvent.detail?.enabled === "boolean") {
+ setCompactMode(customEvent.detail.enabled);
+ }
+ };
+
+ loadForwardCompactMode();
+ window.addEventListener(
+ FORWARD_COMPACT_MODE_EVENT,
+ handleCompactModeChanged,
+ );
+
+ return () => {
+ window.removeEventListener(
+ FORWARD_COMPACT_MODE_EVENT,
+ handleCompactModeChanged,
+ );
+ };
+ }, []);
+
const parseShareIdFromTunnelName = (tunnelName: string): number | null => {
const normalized = (tunnelName || "").trim();
@@ -1372,9 +1413,14 @@ export default function ForwardPage() {
);
const overTunnelGroupKey = buildForwardTunnelGroupKey(overForward?.tunnelName);
- // 仅允许在同一用户+隧道分组内拖拽,避免混排
- if (activeUserId !== overUserId || activeTunnelGroupKey !== overTunnelGroupKey) {
- return;
+ // 非精简模式仅允许在同一用户+隧道分组内拖拽,避免混排
+ if (!compactMode) {
+ if (
+ activeUserId !== overUserId ||
+ activeTunnelGroupKey !== overTunnelGroupKey
+ ) {
+ return;
+ }
}
const oldIndex = forwardOrder.indexOf(activeId);
@@ -1773,12 +1819,19 @@ export default function ForwardPage() {
return groups;
}, [orderedForwards, isAdmin, tokenUserId]);
- const sortedForwards = useMemo(
- () =>
- groupedForwards.flatMap((group) =>
- group.tunnels.flatMap((tunnel) => tunnel.items),
- ),
- [groupedForwards],
+ const sortedForwards = useMemo(() => {
+ if (compactMode) {
+ return orderedForwards;
+ }
+
+ return groupedForwards.flatMap((group) =>
+ group.tunnels.flatMap((tunnel) => tunnel.items),
+ );
+ }, [compactMode, orderedForwards, groupedForwards]);
+
+ const sortableForwardIds = useMemo(
+ () => sortedForwards.map((f) => f.id).filter((id) => id > 0),
+ [sortedForwards],
);
// 可拖拽的转发卡片组件
@@ -2066,6 +2119,221 @@ export default function ForwardPage() {
);
};
+ const SortableCompactTableRow = ({
+ forward,
+ selectMode,
+ selectedIds,
+ toggleSelect,
+ getStrategyDisplay,
+ formatInAddress,
+ formatRemoteAddress,
+ handleServiceToggle,
+ handleEdit,
+ handleDelete,
+ handleDiagnose,
+ showAddressModal,
+ hasMultipleAddresses,
+ formatFlow,
+ }: any) => {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({ id: forward.id });
+
+ const style = {
+ transform: transform
+ ? CSS.Transform.toString({
+ ...transform,
+ x: Math.round(transform.x),
+ y: Math.round(transform.y),
+ })
+ : undefined,
+ transition: isDragging ? undefined : transition || undefined,
+ opacity: isDragging ? 0.5 : 1,
+ backgroundColor: isDragging ? "var(--nextui-default-100)" : undefined,
+ };
+
+ const strategyDisplay = getStrategyDisplay(forward.strategy);
+
+ return (
+
+ 还没有创建任何转发配置,点击上方按钮开始创建 +
++ 还没有创建任何转发配置,点击上方按钮开始创建 +
++ 转发页面精简模式 +
++ 开启后,转发页面列表使用 2.1.6-alpha8 样式。 +
++ 仅管理员可修改该全局配置。 +
+ )} +