mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-10 19:36:36 +08:00
refactor: 统一术语,将'转发'改为'规则' (#273)
- 更新所有页面中的'转发'术语为'规则' - 统一UI文案,提升用户体验一致性 - 关联 #269 #271 Entire-Checkpoint: 5646e42aa33b
This commit is contained in:
@@ -119,8 +119,8 @@ const CONFIG_ITEMS: ConfigItem[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "forward_compact_mode",
|
key: "forward_compact_mode",
|
||||||
label: "转发页面精简模式",
|
label: "规则页面精简模式",
|
||||||
description: "开启后,转发页面列表使用 2.1.6-alpha8 样式(全局配置)",
|
description: "开启后,规则页面列表使用 2.1.6-alpha8 样式(全局配置)",
|
||||||
type: "switch",
|
type: "switch",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -153,7 +153,7 @@ const BACKUP_TYPE_OPTIONS = [
|
|||||||
{ value: "users", label: "用户" },
|
{ value: "users", label: "用户" },
|
||||||
{ value: "nodes", label: "节点" },
|
{ value: "nodes", label: "节点" },
|
||||||
{ value: "tunnels", label: "隧道" },
|
{ value: "tunnels", label: "隧道" },
|
||||||
{ value: "forwards", label: "转发" },
|
{ value: "forwards", label: "规则" },
|
||||||
{ value: "userTunnels", label: "用户隧道权限" },
|
{ value: "userTunnels", label: "用户隧道权限" },
|
||||||
{ value: "speedLimits", label: "限速规则" },
|
{ value: "speedLimits", label: "限速规则" },
|
||||||
{ value: "tunnelGroups", label: "隧道分组" },
|
{ value: "tunnelGroups", label: "隧道分组" },
|
||||||
|
|||||||
@@ -616,7 +616,7 @@ export default function DashboardPage() {
|
|||||||
</svg>
|
</svg>
|
||||||
}
|
}
|
||||||
iconClassName="bg-purple-100 dark:bg-purple-500/20"
|
iconClassName="bg-purple-100 dark:bg-purple-500/20"
|
||||||
title="转发配额"
|
title="规则配额"
|
||||||
value={formatNumber(userInfo.num || 0)}
|
value={formatNumber(userInfo.num || 0)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -650,7 +650,7 @@ export default function DashboardPage() {
|
|||||||
</svg>
|
</svg>
|
||||||
}
|
}
|
||||||
iconClassName="bg-orange-100 dark:bg-orange-500/20"
|
iconClassName="bg-orange-100 dark:bg-orange-500/20"
|
||||||
title="已用转发"
|
title="已用规则"
|
||||||
value={forwardList.length}
|
value={forwardList.length}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -753,7 +753,7 @@ export default function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-default-600 mb-1">
|
<p className="text-sm text-default-600 mb-1">
|
||||||
转发配额
|
规则配额
|
||||||
</p>
|
</p>
|
||||||
<p className="font-semibold text-foreground">
|
<p className="font-semibold text-foreground">
|
||||||
{formatNumber(tunnel.num)}
|
{formatNumber(tunnel.num)}
|
||||||
@@ -761,7 +761,7 @@ export default function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-default-600 mb-1">
|
<p className="text-sm text-default-600 mb-1">
|
||||||
已用转发
|
已用规则
|
||||||
</p>
|
</p>
|
||||||
<p className="font-semibold text-foreground">
|
<p className="font-semibold text-foreground">
|
||||||
{getTunnelUsedForwards(tunnel.tunnelId)}
|
{getTunnelUsedForwards(tunnel.tunnelId)}
|
||||||
@@ -784,7 +784,7 @@ export default function DashboardPage() {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 转发配置 */}
|
{/* 规则配置 */}
|
||||||
<Card className="border border-gray-200 dark:border-default-200 shadow-md">
|
<Card className="border border-gray-200 dark:border-default-200 shadow-md">
|
||||||
<CardHeader className="pb-3">
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -801,7 +801,7 @@ export default function DashboardPage() {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<h2 className="text-lg lg:text-xl font-semibold text-foreground">
|
<h2 className="text-lg lg:text-xl font-semibold text-foreground">
|
||||||
转发配置
|
规则配置
|
||||||
</h2>
|
</h2>
|
||||||
<span className="px-2 py-1 bg-default-100 dark:bg-default-50 text-default-600 rounded-full text-xs">
|
<span className="px-2 py-1 bg-default-100 dark:bg-default-50 text-default-600 rounded-full text-xs">
|
||||||
{forwardList.length}
|
{forwardList.length}
|
||||||
@@ -825,7 +825,7 @@ export default function DashboardPage() {
|
|||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<p className="text-default-500">暂无转发配置</p>
|
<p className="text-default-500">暂无规则配置</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -839,7 +839,7 @@ export default function DashboardPage() {
|
|||||||
{group.tunnelName}
|
{group.tunnelName}
|
||||||
</h3>
|
</h3>
|
||||||
<span className="px-2 py-1 bg-primary-100 dark:bg-primary-500/20 text-primary-700 dark:text-primary-300 rounded-md text-sm">
|
<span className="px-2 py-1 bg-primary-100 dark:bg-primary-500/20 text-primary-700 dark:text-primary-300 rounded-md text-sm">
|
||||||
{group.forwards.length} 个转发
|
{group.forwards.length} 个规则
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -675,7 +675,9 @@ export default function ForwardPage() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTunnel = allTunnels.find((tunnel) => tunnel.id === form.tunnelId);
|
const currentTunnel = allTunnels.find(
|
||||||
|
(tunnel) => tunnel.id === form.tunnelId,
|
||||||
|
);
|
||||||
|
|
||||||
return (currentTunnel?.inNodeId?.length || 0) > 1;
|
return (currentTunnel?.inNodeId?.length || 0) > 1;
|
||||||
}, [allTunnels, form.tunnelId]);
|
}, [allTunnels, form.tunnelId]);
|
||||||
@@ -1174,7 +1176,7 @@ export default function ForwardPage() {
|
|||||||
saveOrder(FORWARD_ORDER_KEY, order);
|
saveOrder(FORWARD_ORDER_KEY, order);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
toast.error(forwardsRes.msg || "获取转发列表失败");
|
toast.error(forwardsRes.msg || "获取规则列表失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tunnelsRes.code === 0) {
|
if (tunnelsRes.code === 0) {
|
||||||
@@ -1256,9 +1258,9 @@ export default function ForwardPage() {
|
|||||||
const newErrors: { [key: string]: string } = {};
|
const newErrors: { [key: string]: string } = {};
|
||||||
|
|
||||||
if (!form.name.trim()) {
|
if (!form.name.trim()) {
|
||||||
newErrors.name = "请输入转发名称";
|
newErrors.name = "请输入规则名称";
|
||||||
} else if (form.name.length < 2 || form.name.length > 50) {
|
} else if (form.name.length < 2 || form.name.length > 50) {
|
||||||
newErrors.name = "转发名称长度应在2-50个字符之间";
|
newErrors.name = "规则名称长度应在2-50个字符之间";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!form.tunnelId) {
|
if (!form.tunnelId) {
|
||||||
@@ -1308,7 +1310,7 @@ export default function ForwardPage() {
|
|||||||
return Object.keys(newErrors).length === 0;
|
return Object.keys(newErrors).length === 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 新增转发
|
// 新增规则
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
setIsEdit(false);
|
setIsEdit(false);
|
||||||
setInIpTouched(false);
|
setInIpTouched(false);
|
||||||
@@ -1326,7 +1328,7 @@ export default function ForwardPage() {
|
|||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 编辑转发
|
// 编辑规则
|
||||||
const handleEdit = (forward: Forward) => {
|
const handleEdit = (forward: Forward) => {
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
setInIpTouched(false);
|
setInIpTouched(false);
|
||||||
@@ -1352,7 +1354,7 @@ export default function ForwardPage() {
|
|||||||
setDeleteModalOpen(true);
|
setDeleteModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 确认删除转发
|
// 确认删除规则
|
||||||
const confirmDelete = async () => {
|
const confirmDelete = async () => {
|
||||||
if (!forwardToDelete) return;
|
if (!forwardToDelete) return;
|
||||||
|
|
||||||
@@ -1367,7 +1369,7 @@ export default function ForwardPage() {
|
|||||||
} else {
|
} else {
|
||||||
// 删除失败,询问是否强制删除
|
// 删除失败,询问是否强制删除
|
||||||
const confirmed = window.confirm(
|
const confirmed = window.confirm(
|
||||||
`常规删除失败:${res.msg || "删除失败"}\n\n是否需要强制删除?\n\n⚠️ 注意:强制删除不会去验证节点端是否已经删除对应的转发服务。`,
|
`常规删除失败:${res.msg || "删除失败"}\n\n是否需要强制删除?\n\n⚠️ 注意:强制删除不会去验证节点端是否已经删除对应的规则服务。`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
@@ -1489,7 +1491,7 @@ export default function ForwardPage() {
|
|||||||
// 处理服务开关
|
// 处理服务开关
|
||||||
const handleServiceToggle = async (forward: Forward) => {
|
const handleServiceToggle = async (forward: Forward) => {
|
||||||
if (forward.status !== 1 && forward.status !== 0) {
|
if (forward.status !== 1 && forward.status !== 0) {
|
||||||
toast.error("转发状态异常,无法操作");
|
toast.error("规则状态异常,无法操作");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1514,7 +1516,7 @@ export default function ForwardPage() {
|
|||||||
|
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
toast.success(targetState ? "服务已启动" : "服务已暂停");
|
toast.success(targetState ? "服务已启动" : "服务已暂停");
|
||||||
// 更新转发状态
|
// 更新规则状态
|
||||||
setForwards((prev) =>
|
setForwards((prev) =>
|
||||||
prev.map((f) =>
|
prev.map((f) =>
|
||||||
f.id === forward.id ? { ...f, status: targetState ? 1 : 0 } : f,
|
f.id === forward.id ? { ...f, status: targetState ? 1 : 0 } : f,
|
||||||
@@ -1540,7 +1542,7 @@ export default function ForwardPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 诊断转发
|
// 诊断规则
|
||||||
const handleDiagnose = async (forward: Forward) => {
|
const handleDiagnose = async (forward: Forward) => {
|
||||||
diagnosisAbortRef.current?.abort();
|
diagnosisAbortRef.current?.abort();
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
@@ -1795,7 +1797,7 @@ export default function ForwardPage() {
|
|||||||
await copyToClipboard(allAddresses, "所有地址");
|
await copyToClipboard(allAddresses, "所有地址");
|
||||||
};
|
};
|
||||||
|
|
||||||
// 导出转发数据
|
// 导出规则数据
|
||||||
const handleExport = () => {
|
const handleExport = () => {
|
||||||
setSelectedTunnelForExport(null);
|
setSelectedTunnelForExport(null);
|
||||||
setExportData("");
|
setExportData("");
|
||||||
@@ -1813,13 +1815,13 @@ export default function ForwardPage() {
|
|||||||
setExportLoading(true);
|
setExportLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取要导出的转发列表
|
// 获取要导出的规则列表
|
||||||
const forwardsToExport = sortedForwards.filter(
|
const forwardsToExport = sortedForwards.filter(
|
||||||
(forward) => forward.tunnelId === selectedTunnelForExport,
|
(forward) => forward.tunnelId === selectedTunnelForExport,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (forwardsToExport.length === 0) {
|
if (forwardsToExport.length === 0) {
|
||||||
toast.error("所选隧道没有转发数据");
|
toast.error("所选隧道没有规则数据");
|
||||||
setExportLoading(false);
|
setExportLoading(false);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -1842,10 +1844,10 @@ export default function ForwardPage() {
|
|||||||
|
|
||||||
// 复制导出数据
|
// 复制导出数据
|
||||||
const copyExportData = async () => {
|
const copyExportData = async () => {
|
||||||
await copyToClipboard(exportData, "转发数据");
|
await copyToClipboard(exportData, "规则数据");
|
||||||
};
|
};
|
||||||
|
|
||||||
// 导入转发数据
|
// 导入规则数据
|
||||||
const handleImport = () => {
|
const handleImport = () => {
|
||||||
setImportData("");
|
setImportData("");
|
||||||
setImportResults([]);
|
setImportResults([]);
|
||||||
@@ -1967,7 +1969,7 @@ export default function ForwardPage() {
|
|||||||
{
|
{
|
||||||
line,
|
line,
|
||||||
success: false,
|
success: false,
|
||||||
message: "格式错误:需要至少包含目标地址和转发名称",
|
message: "格式错误:需要至少包含目标地址和规则名称",
|
||||||
},
|
},
|
||||||
...prev,
|
...prev,
|
||||||
]);
|
]);
|
||||||
@@ -1981,7 +1983,7 @@ export default function ForwardPage() {
|
|||||||
{
|
{
|
||||||
line,
|
line,
|
||||||
success: false,
|
success: false,
|
||||||
message: "目标地址和转发名称不能为空",
|
message: "目标地址和规则名称不能为空",
|
||||||
},
|
},
|
||||||
...prev,
|
...prev,
|
||||||
]);
|
]);
|
||||||
@@ -2395,7 +2397,7 @@ export default function ForwardPage() {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 根据排序顺序获取转发列表
|
// 根据排序顺序获取规则列表
|
||||||
const orderedForwards = useMemo((): Forward[] => {
|
const orderedForwards = useMemo((): Forward[] => {
|
||||||
// 确保 forwards 数组存在且有效
|
// 确保 forwards 数组存在且有效
|
||||||
if (!forwards || forwards.length === 0) {
|
if (!forwards || forwards.length === 0) {
|
||||||
@@ -2430,7 +2432,7 @@ export default function ForwardPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保过滤后的转发列表有效
|
// 确保过滤后的规则列表有效
|
||||||
if (!filteredForwards || filteredForwards.length === 0) {
|
if (!filteredForwards || filteredForwards.length === 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -2464,7 +2466,7 @@ export default function ForwardPage() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加不在排序列表中的转发(新添加的)
|
// 添加不在排序列表中的规则(新添加的)
|
||||||
filteredForwards.forEach((forward) => {
|
filteredForwards.forEach((forward) => {
|
||||||
if (!forwardOrder.includes(forward.id)) {
|
if (!forwardOrder.includes(forward.id)) {
|
||||||
localSortedForwards.push(forward);
|
localSortedForwards.push(forward);
|
||||||
@@ -2768,7 +2770,7 @@ export default function ForwardPage() {
|
|||||||
<span className={titleClassName}>{tunnel.tunnelName}</span>
|
<span className={titleClassName}>{tunnel.tunnelName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className={countClassName}>{tunnel.items.length} 条转发</span>
|
<span className={countClassName}>{tunnel.items.length} 条规则</span>
|
||||||
<div
|
<div
|
||||||
className="cursor-grab active:cursor-grabbing p-1 text-default-400 hover:text-default-600 transition-colors"
|
className="cursor-grab active:cursor-grabbing p-1 text-default-400 hover:text-default-600 transition-colors"
|
||||||
title="拖拽分组排序"
|
title="拖拽分组排序"
|
||||||
@@ -2791,7 +2793,7 @@ export default function ForwardPage() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 可拖拽的转发卡片组件
|
// 可拖拽的规则卡片组件
|
||||||
const SortableForwardCard = ({ forward }: { forward: Forward }) => {
|
const SortableForwardCard = ({ forward }: { forward: Forward }) => {
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
@@ -3292,7 +3294,7 @@ export default function ForwardPage() {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 渲染转发卡片
|
// 渲染规则卡片
|
||||||
const renderForwardCard = (forward: Forward, listeners?: any) => {
|
const renderForwardCard = (forward: Forward, listeners?: any) => {
|
||||||
const statusDisplay = getStatusDisplay(forward.status);
|
const statusDisplay = getStatusDisplay(forward.status);
|
||||||
const strategyDisplay = getStrategyDisplay(forward.strategy);
|
const strategyDisplay = getStrategyDisplay(forward.strategy);
|
||||||
@@ -3585,7 +3587,7 @@ export default function ForwardPage() {
|
|||||||
<div className="flex-1 max-w-sm flex items-center gap-2">
|
<div className="flex-1 max-w-sm flex items-center gap-2">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
isVisible={isSearchVisible}
|
isVisible={isSearchVisible}
|
||||||
placeholder="搜索转发名称、地址或用户名"
|
placeholder="搜索规则名称、地址或用户名"
|
||||||
value={searchKeyword}
|
value={searchKeyword}
|
||||||
onChange={setSearchKeyword}
|
onChange={setSearchKeyword}
|
||||||
onClose={() => setIsSearchVisible(false)}
|
onClose={() => setIsSearchVisible(false)}
|
||||||
@@ -3811,7 +3813,7 @@ export default function ForwardPage() {
|
|||||||
strategy={verticalListSortingStrategy}
|
strategy={verticalListSortingStrategy}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
aria-label="全部转发列表"
|
aria-label="全部规则列表"
|
||||||
classNames={{
|
classNames={{
|
||||||
th: "bg-default-100/50 text-default-600 font-semibold text-sm border-b border-divider py-3 uppercase tracking-wider",
|
th: "bg-default-100/50 text-default-600 font-semibold text-sm border-b border-divider py-3 uppercase tracking-wider",
|
||||||
td: "py-3 border-b border-divider/50 group-data-[last=true]:border-b-0",
|
td: "py-3 border-b border-divider/50 group-data-[last=true]:border-b-0",
|
||||||
@@ -3833,7 +3835,10 @@ export default function ForwardPage() {
|
|||||||
<TableColumn>状态</TableColumn>
|
<TableColumn>状态</TableColumn>
|
||||||
<TableColumn className="text-right">操作</TableColumn>
|
<TableColumn className="text-right">操作</TableColumn>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody emptyContent="暂无转发配置" items={sortedForwards}>
|
<TableBody
|
||||||
|
emptyContent="暂无规则配置"
|
||||||
|
items={sortedForwards}
|
||||||
|
>
|
||||||
{(forward) => (
|
{(forward) => (
|
||||||
<SortableCompactTableRow
|
<SortableCompactTableRow
|
||||||
formatFlow={formatFlow}
|
formatFlow={formatFlow}
|
||||||
@@ -3861,42 +3866,52 @@ export default function ForwardPage() {
|
|||||||
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
||||||
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
||||||
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
||||||
暂无转发配置
|
暂无规则配置
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
||||||
还没有创建任何转发配置,点击上方按钮开始创建
|
还没有创建任何规则配置,点击上方按钮开始创建
|
||||||
</p>
|
</p>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
) : sortedForwards.length > 0 ? (
|
) : sortedForwards.length > 0 ? (
|
||||||
<DndContext
|
<>
|
||||||
collisionDetection={closestCenter}
|
<div className="flex items-center justify-between px-1 mb-3">
|
||||||
sensors={sensors}
|
<span className="text-sm font-semibold text-foreground">
|
||||||
onDragEnd={handleDragEnd}
|
全部规则
|
||||||
onDragStart={() => {}}
|
</span>
|
||||||
>
|
<span className="text-xs text-default-600">
|
||||||
<SortableContext
|
{sortedForwards.length} 条规则
|
||||||
items={sortableForwardIds}
|
</span>
|
||||||
strategy={rectSortingStrategy}
|
</div>
|
||||||
|
<DndContext
|
||||||
|
collisionDetection={closestCenter}
|
||||||
|
sensors={sensors}
|
||||||
|
onDragEnd={handleDragEnd}
|
||||||
|
onDragStart={() => {}}
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4">
|
<SortableContext
|
||||||
{sortedForwards.map((forward) =>
|
items={sortableForwardIds}
|
||||||
forward && forward.id ? (
|
strategy={rectSortingStrategy}
|
||||||
<SortableForwardCard key={forward.id} forward={forward} />
|
>
|
||||||
) : null,
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-4">
|
||||||
)}
|
{sortedForwards.map((forward) =>
|
||||||
</div>
|
forward && forward.id ? (
|
||||||
</SortableContext>
|
<SortableForwardCard key={forward.id} forward={forward} />
|
||||||
</DndContext>
|
) : null,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SortableContext>
|
||||||
|
</DndContext>
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
||||||
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
||||||
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
||||||
暂无转发配置
|
暂无规则配置
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
||||||
还没有创建任何转发配置,点击上方按钮开始创建
|
还没有创建任何规则配置,点击上方按钮开始创建
|
||||||
</p>
|
</p>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -3929,7 +3944,7 @@ export default function ForwardPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-default-600">
|
<span className="text-xs text-default-600">
|
||||||
{groupForwardCount} 条转发
|
{groupForwardCount} 条规则
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -3984,7 +3999,7 @@ export default function ForwardPage() {
|
|||||||
onDragEnd={handleDragEnd}
|
onDragEnd={handleDragEnd}
|
||||||
>
|
>
|
||||||
<Table
|
<Table
|
||||||
aria-label={`${group.userName}-${tunnel.tunnelName}转发列表`}
|
aria-label={`${group.userName}-${tunnel.tunnelName}规则列表`}
|
||||||
className={`table-fixed ${FORWARD_GROUPED_TABLE_MIN_WIDTH_CLASS}`}
|
className={`table-fixed ${FORWARD_GROUPED_TABLE_MIN_WIDTH_CLASS}`}
|
||||||
classNames={{
|
classNames={{
|
||||||
th: "bg-default-100/50 text-default-600 font-semibold text-sm border-b border-divider py-3 uppercase tracking-wider",
|
th: "bg-default-100/50 text-default-600 font-semibold text-sm border-b border-divider py-3 uppercase tracking-wider",
|
||||||
@@ -4058,7 +4073,7 @@ export default function ForwardPage() {
|
|||||||
</TableColumn>
|
</TableColumn>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody
|
<TableBody
|
||||||
emptyContent="暂无转发配置"
|
emptyContent="暂无规则配置"
|
||||||
items={tunnel.items}
|
items={tunnel.items}
|
||||||
>
|
>
|
||||||
{(forward) => (
|
{(forward) => (
|
||||||
@@ -4110,10 +4125,10 @@ export default function ForwardPage() {
|
|||||||
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
||||||
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
||||||
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
||||||
暂无转发配置
|
暂无规则配置
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
||||||
还没有创建任何转发配置,点击上方按钮开始创建
|
还没有创建任何规则配置,点击上方按钮开始创建
|
||||||
</p>
|
</p>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -4145,7 +4160,7 @@ export default function ForwardPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-default-600">
|
<span className="text-xs text-default-600">
|
||||||
{groupForwardCount} 条转发
|
{groupForwardCount} 条规则
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -4230,10 +4245,10 @@ export default function ForwardPage() {
|
|||||||
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
<Card className="shadow-sm border border-gray-200 dark:border-gray-700 bg-default-50/50">
|
||||||
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
<CardBody className="text-center py-20 flex flex-col items-center justify-center min-h-[240px]">
|
||||||
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
<h3 className="text-xl font-medium text-foreground tracking-tight mb-2">
|
||||||
暂无转发配置
|
暂无规则配置
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
<p className="text-default-500 text-sm max-w-xs mx-auto leading-relaxed">
|
||||||
还没有创建任何转发配置,点击上方按钮开始创建
|
还没有创建任何规则配置,点击上方按钮开始创建
|
||||||
</p>
|
</p>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -4253,10 +4268,10 @@ export default function ForwardPage() {
|
|||||||
<>
|
<>
|
||||||
<ModalHeader className="flex flex-col gap-1">
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
<h2 className="text-xl font-bold">
|
<h2 className="text-xl font-bold">
|
||||||
{isEdit ? "编辑转发" : "新增转发"}
|
{isEdit ? "编辑规则" : "新增规则"}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-small text-default-500">
|
<p className="text-small text-default-500">
|
||||||
{isEdit ? "修改现有转发配置的信息" : "创建新的转发配置"}
|
{isEdit ? "修改现有规则配置的信息" : "创建新的规则配置"}
|
||||||
</p>
|
</p>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
@@ -4264,8 +4279,8 @@ export default function ForwardPage() {
|
|||||||
<Input
|
<Input
|
||||||
errorMessage={errors.name}
|
errorMessage={errors.name}
|
||||||
isInvalid={!!errors.name}
|
isInvalid={!!errors.name}
|
||||||
label="转发名称"
|
label="规则名称"
|
||||||
placeholder="请输入转发名称"
|
placeholder="请输入规则名称"
|
||||||
value={form.name}
|
value={form.name}
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -4369,10 +4384,10 @@ export default function ForwardPage() {
|
|||||||
isCurrentTunnelMultiEntrance
|
isCurrentTunnelMultiEntrance
|
||||||
? "多入口隧道使用节点默认IP"
|
? "多入口隧道使用节点默认IP"
|
||||||
: form.tunnelId
|
: form.tunnelId
|
||||||
? currentTunnelIpOptions.length > 0
|
? currentTunnelIpOptions.length > 0
|
||||||
? "选择入口监听IP"
|
? "选择入口监听IP"
|
||||||
: "当前隧道入口节点暂无可选IP"
|
: "当前隧道入口节点暂无可选IP"
|
||||||
: "请先选择隧道"
|
: "请先选择隧道"
|
||||||
}
|
}
|
||||||
selectedKeys={[form.inIp || "__default__"]}
|
selectedKeys={[form.inIp || "__default__"]}
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
@@ -4441,7 +4456,7 @@ export default function ForwardPage() {
|
|||||||
isLoading={submitLoading}
|
isLoading={submitLoading}
|
||||||
onPress={handleSubmit}
|
onPress={handleSubmit}
|
||||||
>
|
>
|
||||||
{isEdit ? "保存修改" : "创建转发"}
|
{isEdit ? "保存修改" : "创建规则"}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</>
|
</>
|
||||||
@@ -4466,14 +4481,14 @@ export default function ForwardPage() {
|
|||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p className="text-default-600">
|
<p className="text-default-600">
|
||||||
确定要删除转发{" "}
|
确定要删除规则{" "}
|
||||||
<span className="font-semibold text-foreground">
|
<span className="font-semibold text-foreground">
|
||||||
"{forwardToDelete?.name}"
|
"{forwardToDelete?.name}"
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
吗?
|
吗?
|
||||||
</p>
|
</p>
|
||||||
<p className="text-small text-default-500 mt-2">
|
<p className="text-small text-default-500 mt-2">
|
||||||
此操作无法撤销,删除后该转发将永久消失。
|
此操作无法撤销,删除后该规则将永久消失。
|
||||||
</p>
|
</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
@@ -4548,9 +4563,9 @@ export default function ForwardPage() {
|
|||||||
>
|
>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader className="flex flex-col gap-1">
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
<h2 className="text-xl font-bold">导出转发数据</h2>
|
<h2 className="text-xl font-bold">导出规则数据</h2>
|
||||||
<p className="text-small text-default-500">
|
<p className="text-small text-default-500">
|
||||||
格式:目标地址|转发名称|入口端口
|
格式:目标地址|规则名称|入口端口
|
||||||
</p>
|
</p>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody className="pb-6">
|
<ModalBody className="pb-6">
|
||||||
@@ -4700,11 +4715,11 @@ export default function ForwardPage() {
|
|||||||
>
|
>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalHeader className="flex flex-col gap-1">
|
<ModalHeader className="flex flex-col gap-1">
|
||||||
<h2 className="text-xl font-bold">导入转发数据</h2>
|
<h2 className="text-xl font-bold">导入规则数据</h2>
|
||||||
{importFormat === "flvx" ? (
|
{importFormat === "flvx" ? (
|
||||||
<>
|
<>
|
||||||
<p className="text-small text-default-500">
|
<p className="text-small text-default-500">
|
||||||
格式:目标地址|转发名称|入口端口,每行一个,入口端口留空将自动分配可用端口
|
格式:目标地址|规则名称|入口端口,每行一个,入口端口留空将自动分配可用端口
|
||||||
</p>
|
</p>
|
||||||
<p className="text-small text-default-400">
|
<p className="text-small text-default-400">
|
||||||
目标地址支持单个地址(如:example.com:8080)或多个地址用逗号分隔(如:3.3.3.3:3,4.4.4.4:4)
|
目标地址支持单个地址(如:example.com:8080)或多个地址用逗号分隔(如:3.3.3.3:3,4.4.4.4:4)
|
||||||
@@ -4788,8 +4803,8 @@ export default function ForwardPage() {
|
|||||||
minRows={8}
|
minRows={8}
|
||||||
placeholder={
|
placeholder={
|
||||||
importFormat === "flvx"
|
importFormat === "flvx"
|
||||||
? "请输入要导入的转发数据,格式:目标地址|转发名称|入口端口"
|
? "请输入要导入的规则数据,格式:目标地址|规则名称|入口端口"
|
||||||
: '请输入ny格式数据,每行一个JSON对象,如:{"dest":["1.2.3.4:80"],"listen_port":8080,"name":"转发1"};listen_port可省略自动分配'
|
: '请输入ny格式数据,每行一个JSON对象,如:{"dest":["1.2.3.4:80"],"listen_port":8080,"name":"规则1"};listen_port可省略自动分配'
|
||||||
}
|
}
|
||||||
value={importData}
|
value={importData}
|
||||||
variant="flat"
|
variant="flat"
|
||||||
@@ -4929,7 +4944,7 @@ export default function ForwardPage() {
|
|||||||
{(onClose) => (
|
{(onClose) => (
|
||||||
<>
|
<>
|
||||||
<ModalHeader className="flex flex-col gap-1 bg-content1 border-b border-divider">
|
<ModalHeader className="flex flex-col gap-1 bg-content1 border-b border-divider">
|
||||||
<h2 className="text-xl font-bold">转发诊断结果</h2>
|
<h2 className="text-xl font-bold">规则诊断结果</h2>
|
||||||
{currentDiagnosisForward && (
|
{currentDiagnosisForward && (
|
||||||
<div className="flex items-center gap-2 min-w-0">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
<span className="text-small text-default-500 truncate flex-1 min-w-0">
|
<span className="text-small text-default-500 truncate flex-1 min-w-0">
|
||||||
@@ -4941,7 +4956,7 @@ export default function ForwardPage() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="flat"
|
variant="flat"
|
||||||
>
|
>
|
||||||
转发服务
|
规则服务
|
||||||
</Chip>
|
</Chip>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -5500,7 +5515,7 @@ export default function ForwardPage() {
|
|||||||
<ModalHeader>确认删除</ModalHeader>
|
<ModalHeader>确认删除</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p>
|
<p>
|
||||||
确定要删除选中的 {selectedIds.size} 项转发吗?此操作不可撤销。
|
确定要删除选中的 {selectedIds.size} 项规则吗?此操作不可撤销。
|
||||||
</p>
|
</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
@@ -5531,7 +5546,7 @@ export default function ForwardPage() {
|
|||||||
<ModalHeader>隧道</ModalHeader>
|
<ModalHeader>隧道</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p className="mb-4">
|
<p className="mb-4">
|
||||||
将选中的 {selectedIds.size} 项转发迁移到新隧道:
|
将选中的 {selectedIds.size} 项规则迁移到新隧道:
|
||||||
</p>
|
</p>
|
||||||
<Select
|
<Select
|
||||||
label="目标隧道"
|
label="目标隧道"
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export const SettingsPage = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (response.code === 0) {
|
if (response.code === 0) {
|
||||||
toast.success(`转发页面精简模式已${enabled ? "开启" : "关闭"}`);
|
toast.success(`规则页面精简模式已${enabled ? "开启" : "关闭"}`);
|
||||||
window.dispatchEvent(
|
window.dispatchEvent(
|
||||||
new CustomEvent("forwardCompactModeChanged", {
|
new CustomEvent("forwardCompactModeChanged", {
|
||||||
detail: { enabled },
|
detail: { enabled },
|
||||||
@@ -232,10 +232,10 @@ export const SettingsPage = () => {
|
|||||||
<div className="flex items-center justify-between gap-4">
|
<div className="flex items-center justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
<p className="text-sm font-medium text-gray-900 dark:text-white">
|
||||||
转发页面精简模式
|
规则页面精简模式
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
开启后,转发页面列表使用 2.1.6-alpha8 样式。
|
开启后,规则页面列表使用 2.1.6-alpha8 样式。{" "}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
+254
-232
@@ -1843,10 +1843,10 @@ export default function TunnelPage() {
|
|||||||
isMultiNodeGroup
|
isMultiNodeGroup
|
||||||
? "多节点跳使用节点默认IP"
|
? "多节点跳使用节点默认IP"
|
||||||
: groupSelectedNodeIds.length === 0
|
: groupSelectedNodeIds.length === 0
|
||||||
? "请先选择节点"
|
? "请先选择节点"
|
||||||
: groupIpOptions.length > 0
|
: groupIpOptions.length > 0
|
||||||
? "选择连接IP"
|
? "选择连接IP"
|
||||||
: "所选节点无共同可选IP"
|
: "所选节点无共同可选IP"
|
||||||
}
|
}
|
||||||
selectedKeys={[
|
selectedKeys={[
|
||||||
selectedGroupConnectIp || "__default__",
|
selectedGroupConnectIp || "__default__",
|
||||||
@@ -1906,243 +1906,259 @@ export default function TunnelPage() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-2">
|
||||||
{/* 节点选择 - 移动端100%,桌面端50% */}
|
{/* 节点选择 - 移动端100%,桌面端50% */}
|
||||||
<div className="col-span-1 md:col-span-2">
|
<div className="col-span-1 md:col-span-2">
|
||||||
<Select
|
<Select
|
||||||
classNames={{
|
classNames={{
|
||||||
label: "text-xs",
|
label: "text-xs",
|
||||||
value: "text-sm",
|
value: "text-sm",
|
||||||
}}
|
}}
|
||||||
disabledKeys={[
|
disabledKeys={[
|
||||||
...nodes
|
...nodes
|
||||||
.filter((node) => node.status !== 1)
|
.filter((node) => node.status !== 1)
|
||||||
.map((node) => node.id.toString()),
|
.map((node) => node.id.toString()),
|
||||||
...form.inNodeId.map((ct) =>
|
...form.inNodeId.map((ct) =>
|
||||||
ct.nodeId.toString(),
|
ct.nodeId.toString(),
|
||||||
),
|
),
|
||||||
...getSelectedChainNodeIds().map((id) =>
|
...getSelectedChainNodeIds().map((id) =>
|
||||||
id.toString(),
|
id.toString(),
|
||||||
),
|
),
|
||||||
]}
|
]}
|
||||||
dropdownPlacement="top"
|
dropdownPlacement="top"
|
||||||
errorMessage={errors.outNodeId}
|
errorMessage={errors.outNodeId}
|
||||||
isInvalid={!!errors.outNodeId}
|
isInvalid={!!errors.outNodeId}
|
||||||
label="节点"
|
label="节点"
|
||||||
placeholder="请选择出口节点(可多选)"
|
placeholder="请选择出口节点(可多选)"
|
||||||
selectedKeys={
|
selectedKeys={
|
||||||
form.outNodeId
|
form.outNodeId
|
||||||
? form.outNodeId
|
? form.outNodeId
|
||||||
.filter((ct) => ct.nodeId !== -1)
|
.filter((ct) => ct.nodeId !== -1)
|
||||||
.map((ct) => ct.nodeId.toString())
|
.map((ct) => ct.nodeId.toString())
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
selectionMode="multiple"
|
selectionMode="multiple"
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
onSelectionChange={(keys) => {
|
onSelectionChange={(keys) => {
|
||||||
const selectedIds = toSelectedNodeIds(keys);
|
const selectedIds = toSelectedNodeIds(keys);
|
||||||
|
|
||||||
setForm((prev) => {
|
setForm((prev) => {
|
||||||
const currentOutNodes = prev.outNodeId || [];
|
const currentOutNodes =
|
||||||
const protocol =
|
prev.outNodeId || [];
|
||||||
currentOutNodes[0]?.protocol || "tls";
|
const protocol =
|
||||||
const strategy =
|
currentOutNodes[0]?.protocol || "tls";
|
||||||
currentOutNodes[0]?.strategy || "round";
|
const strategy =
|
||||||
const realNodes = currentOutNodes.filter(
|
currentOutNodes[0]?.strategy || "round";
|
||||||
(ct) => ct.nodeId !== -1,
|
const realNodes = currentOutNodes.filter(
|
||||||
);
|
(ct) => ct.nodeId !== -1,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
outNodeId: mergeOrderedNodes(
|
outNodeId: mergeOrderedNodes(
|
||||||
realNodes,
|
realNodes,
|
||||||
selectedIds,
|
selectedIds,
|
||||||
(nodeId) => ({
|
(nodeId) => ({
|
||||||
nodeId,
|
nodeId,
|
||||||
chainType: 3,
|
chainType: 3,
|
||||||
protocol,
|
protocol,
|
||||||
strategy,
|
strategy,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{nodes.map((node) => (
|
{nodes.map((node) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
key={node.id}
|
key={node.id}
|
||||||
textValue={`${node.name}`}
|
textValue={`${node.name}`}
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span>{node.name}</span>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Chip
|
|
||||||
color={
|
|
||||||
node.status === 1
|
|
||||||
? "success"
|
|
||||||
: "default"
|
|
||||||
}
|
|
||||||
size="sm"
|
|
||||||
variant="flat"
|
|
||||||
>
|
>
|
||||||
{node.status === 1 ? "在线" : "离线"}
|
<div className="flex items-center justify-between">
|
||||||
</Chip>
|
<span>{node.name}</span>
|
||||||
{form.inNodeId.some(
|
<div className="flex items-center gap-2">
|
||||||
(ct) => ct.nodeId === node.id,
|
<Chip
|
||||||
) && (
|
color={
|
||||||
<Chip
|
node.status === 1
|
||||||
color="warning"
|
? "success"
|
||||||
size="sm"
|
: "default"
|
||||||
variant="flat"
|
}
|
||||||
>
|
size="sm"
|
||||||
已选为入口
|
variant="flat"
|
||||||
</Chip>
|
>
|
||||||
)}
|
{node.status === 1
|
||||||
{getSelectedChainNodeIds().includes(
|
? "在线"
|
||||||
node.id,
|
: "离线"}
|
||||||
) && (
|
</Chip>
|
||||||
<Chip
|
{form.inNodeId.some(
|
||||||
color="primary"
|
(ct) => ct.nodeId === node.id,
|
||||||
size="sm"
|
) && (
|
||||||
variant="flat"
|
<Chip
|
||||||
>
|
color="warning"
|
||||||
已选为转发链
|
size="sm"
|
||||||
</Chip>
|
variant="flat"
|
||||||
)}
|
>
|
||||||
</div>
|
已选为入口
|
||||||
</div>
|
</Chip>
|
||||||
</SelectItem>
|
)}
|
||||||
))}
|
{getSelectedChainNodeIds().includes(
|
||||||
</Select>
|
node.id,
|
||||||
</div>
|
) && (
|
||||||
|
<Chip
|
||||||
|
color="primary"
|
||||||
|
size="sm"
|
||||||
|
variant="flat"
|
||||||
|
>
|
||||||
|
已选为转发链
|
||||||
|
</Chip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 协议选择 - 25% */}
|
{/* 协议选择 - 25% */}
|
||||||
<Select
|
<Select
|
||||||
classNames={{
|
classNames={{
|
||||||
label: "text-xs",
|
label: "text-xs",
|
||||||
value: "text-sm",
|
value: "text-sm",
|
||||||
}}
|
}}
|
||||||
errorMessage={errors.protocol}
|
errorMessage={errors.protocol}
|
||||||
isInvalid={!!errors.protocol}
|
isInvalid={!!errors.protocol}
|
||||||
label="协议"
|
label="协议"
|
||||||
placeholder="选择协议"
|
placeholder="选择协议"
|
||||||
selectedKeys={[
|
selectedKeys={[
|
||||||
(() => {
|
(() => {
|
||||||
if (
|
if (
|
||||||
!form.outNodeId ||
|
!form.outNodeId ||
|
||||||
form.outNodeId.length === 0
|
form.outNodeId.length === 0
|
||||||
)
|
)
|
||||||
return "tls";
|
return "tls";
|
||||||
|
|
||||||
return form.outNodeId[0].protocol || "tls";
|
return form.outNodeId[0].protocol || "tls";
|
||||||
})(),
|
})(),
|
||||||
]}
|
]}
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
onSelectionChange={(keys) => {
|
onSelectionChange={(keys) => {
|
||||||
const selectedKey = Array.from(keys)[0] as string;
|
const selectedKey = Array.from(
|
||||||
|
keys,
|
||||||
|
)[0] as string;
|
||||||
|
|
||||||
if (selectedKey) {
|
if (selectedKey) {
|
||||||
setForm((prev) => {
|
setForm((prev) => {
|
||||||
const currentOutNodes = prev.outNodeId || [];
|
const currentOutNodes =
|
||||||
const currentStrategy =
|
prev.outNodeId || [];
|
||||||
currentOutNodes.length > 0
|
const currentStrategy =
|
||||||
? currentOutNodes[0].strategy || "round"
|
currentOutNodes.length > 0
|
||||||
: "round";
|
? currentOutNodes[0].strategy ||
|
||||||
|
"round"
|
||||||
|
: "round";
|
||||||
|
|
||||||
if (currentOutNodes.length === 0) {
|
if (currentOutNodes.length === 0) {
|
||||||
// 如果还没有出口节点,创建一个占位节点保存设置
|
// 如果还没有出口节点,创建一个占位节点保存设置
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
outNodeId: [
|
outNodeId: [
|
||||||
{
|
{
|
||||||
nodeId: -1,
|
nodeId: -1,
|
||||||
chainType: 3,
|
chainType: 3,
|
||||||
protocol: selectedKey,
|
protocol: selectedKey,
|
||||||
strategy: currentStrategy,
|
strategy: currentStrategy,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新所有出口节点的协议
|
// 更新所有出口节点的协议
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
outNodeId: currentOutNodes.map((ct) => ({
|
outNodeId: currentOutNodes.map(
|
||||||
...ct,
|
(ct) => ({
|
||||||
protocol: selectedKey,
|
...ct,
|
||||||
})),
|
protocol: selectedKey,
|
||||||
};
|
}),
|
||||||
});
|
),
|
||||||
}
|
};
|
||||||
}}
|
});
|
||||||
>
|
}
|
||||||
<SelectItem key="tls">TLS</SelectItem>
|
}}
|
||||||
<SelectItem key="wss">WSS</SelectItem>
|
>
|
||||||
<SelectItem key="tcp">TCP</SelectItem>
|
<SelectItem key="tls">TLS</SelectItem>
|
||||||
<SelectItem key="mtls">MTLS</SelectItem>
|
<SelectItem key="wss">WSS</SelectItem>
|
||||||
<SelectItem key="mwss">MWSS</SelectItem>
|
<SelectItem key="tcp">TCP</SelectItem>
|
||||||
<SelectItem key="mtcp">MTCP</SelectItem>
|
<SelectItem key="mtls">MTLS</SelectItem>
|
||||||
</Select>
|
<SelectItem key="mwss">MWSS</SelectItem>
|
||||||
|
<SelectItem key="mtcp">MTCP</SelectItem>
|
||||||
|
</Select>
|
||||||
|
|
||||||
{/* 负载策略 - 25% */}
|
{/* 负载策略 - 25% */}
|
||||||
<Select
|
<Select
|
||||||
classNames={{
|
classNames={{
|
||||||
label: "text-xs",
|
label: "text-xs",
|
||||||
value: "text-sm",
|
value: "text-sm",
|
||||||
}}
|
}}
|
||||||
label="负载策略"
|
label="负载策略"
|
||||||
placeholder="选择策略"
|
placeholder="选择策略"
|
||||||
selectedKeys={[
|
selectedKeys={[
|
||||||
(() => {
|
(() => {
|
||||||
if (
|
if (
|
||||||
!form.outNodeId ||
|
!form.outNodeId ||
|
||||||
form.outNodeId.length === 0
|
form.outNodeId.length === 0
|
||||||
)
|
)
|
||||||
return "round";
|
return "round";
|
||||||
|
|
||||||
return form.outNodeId[0].strategy || "round";
|
return (
|
||||||
})(),
|
form.outNodeId[0].strategy || "round"
|
||||||
]}
|
);
|
||||||
variant="bordered"
|
})(),
|
||||||
onSelectionChange={(keys) => {
|
]}
|
||||||
const selectedKey = Array.from(keys)[0] as string;
|
variant="bordered"
|
||||||
|
onSelectionChange={(keys) => {
|
||||||
|
const selectedKey = Array.from(
|
||||||
|
keys,
|
||||||
|
)[0] as string;
|
||||||
|
|
||||||
if (selectedKey) {
|
if (selectedKey) {
|
||||||
setForm((prev) => {
|
setForm((prev) => {
|
||||||
const currentOutNodes = prev.outNodeId || [];
|
const currentOutNodes =
|
||||||
const currentProtocol =
|
prev.outNodeId || [];
|
||||||
currentOutNodes.length > 0
|
const currentProtocol =
|
||||||
? currentOutNodes[0].protocol || "tls"
|
currentOutNodes.length > 0
|
||||||
: "tls";
|
? currentOutNodes[0].protocol || "tls"
|
||||||
|
: "tls";
|
||||||
|
|
||||||
if (currentOutNodes.length === 0) {
|
if (currentOutNodes.length === 0) {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
outNodeId: [
|
outNodeId: [
|
||||||
{
|
{
|
||||||
nodeId: -1,
|
nodeId: -1,
|
||||||
chainType: 3,
|
chainType: 3,
|
||||||
protocol: currentProtocol,
|
protocol: currentProtocol,
|
||||||
strategy: selectedKey,
|
strategy: selectedKey,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
outNodeId: currentOutNodes.map((ct) => ({
|
outNodeId: currentOutNodes.map(
|
||||||
...ct,
|
(ct) => ({
|
||||||
strategy: selectedKey,
|
...ct,
|
||||||
})),
|
strategy: selectedKey,
|
||||||
};
|
}),
|
||||||
});
|
),
|
||||||
}
|
};
|
||||||
}}
|
});
|
||||||
>
|
}
|
||||||
<SelectItem key="fifo">主备</SelectItem>
|
}}
|
||||||
<SelectItem key="round">轮询</SelectItem>
|
>
|
||||||
<SelectItem key="rand">随机</SelectItem>
|
<SelectItem key="fifo">主备</SelectItem>
|
||||||
</Select>
|
<SelectItem key="round">轮询</SelectItem>
|
||||||
|
<SelectItem key="rand">随机</SelectItem>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 连接IP - 出口节点 */}
|
{/* 连接IP - 出口节点 */}
|
||||||
@@ -2179,9 +2195,13 @@ export default function TunnelPage() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="bordered"
|
variant="bordered"
|
||||||
onSelectionChange={(keys) => {
|
onSelectionChange={(keys) => {
|
||||||
const selectedKey = Array.from(keys)[0] as string;
|
const selectedKey = Array.from(
|
||||||
|
keys,
|
||||||
|
)[0] as string;
|
||||||
const value =
|
const value =
|
||||||
selectedKey === "__default__" ? "" : selectedKey;
|
selectedKey === "__default__"
|
||||||
|
? ""
|
||||||
|
: selectedKey;
|
||||||
|
|
||||||
setForm((prev) => {
|
setForm((prev) => {
|
||||||
const currentOutNodes = prev.outNodeId || [];
|
const currentOutNodes = prev.outNodeId || [];
|
||||||
@@ -2211,7 +2231,9 @@ export default function TunnelPage() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectItem key="__default__">默认连接IP</SelectItem>
|
<SelectItem key="__default__">
|
||||||
|
默认连接IP
|
||||||
|
</SelectItem>
|
||||||
{commonOutIpOptions.map((ip) => (
|
{commonOutIpOptions.map((ip) => (
|
||||||
<SelectItem key={ip}>{ip}</SelectItem>
|
<SelectItem key={ip}>{ip}</SelectItem>
|
||||||
))}
|
))}
|
||||||
@@ -2887,7 +2909,7 @@ export default function TunnelPage() {
|
|||||||
<ModalBody>
|
<ModalBody>
|
||||||
<p>
|
<p>
|
||||||
确定要删除选中的 {selectedIds.size}{" "}
|
确定要删除选中的 {selectedIds.size}{" "}
|
||||||
项隧道吗?此操作不可撤销,相关转发也将被删除。
|
项隧道吗?此操作不可撤销,相关规则也将被删除。
|
||||||
</p>
|
</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|||||||
@@ -845,7 +845,7 @@ export default function UserPage() {
|
|||||||
{/* 其他信息 */}
|
{/* 其他信息 */}
|
||||||
<div className="space-y-1.5 pt-2 border-t border-divider">
|
<div className="space-y-1.5 pt-2 border-t border-divider">
|
||||||
<div className="flex justify-between text-sm">
|
<div className="flex justify-between text-sm">
|
||||||
<span className="text-default-600">转发数量</span>
|
<span className="text-default-600">规则数量</span>
|
||||||
<span className="font-medium text-xs">
|
<span className="font-medium text-xs">
|
||||||
{user.num}
|
{user.num}
|
||||||
</span>
|
</span>
|
||||||
@@ -1001,7 +1001,7 @@ export default function UserPage() {
|
|||||||
/>
|
/>
|
||||||
<Input
|
<Input
|
||||||
isRequired
|
isRequired
|
||||||
label="转发数量"
|
label="规则数量"
|
||||||
max="99999"
|
max="99999"
|
||||||
min="1"
|
min="1"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -1138,7 +1138,7 @@ export default function UserPage() {
|
|||||||
<h3 className="text-lg font-semibold mb-4">分配新权限</h3>
|
<h3 className="text-lg font-semibold mb-4">分配新权限</h3>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="text-sm text-default-500 bg-default-100 dark:bg-default-50 p-3 rounded-lg border border-default-200 dark:border-default-100/30">
|
<div className="text-sm text-default-500 bg-default-100 dark:bg-default-50 p-3 rounded-lg border border-default-200 dark:border-default-100/30">
|
||||||
流量限制、转发数量、到期时间、流量重置时间将自动继承用户设置
|
流量限制、规则数量、到期时间、流量重置时间将自动继承用户设置
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-2 max-h-72 overflow-y-auto pr-1">
|
<div className="grid gap-2 max-h-72 overflow-y-auto pr-1">
|
||||||
@@ -1296,7 +1296,7 @@ export default function UserPage() {
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableColumn>隧道名称</TableColumn>
|
<TableColumn>隧道名称</TableColumn>
|
||||||
<TableColumn>流量统计</TableColumn>
|
<TableColumn>流量统计</TableColumn>
|
||||||
<TableColumn>转发数量</TableColumn>
|
<TableColumn>规则数量</TableColumn>
|
||||||
<TableColumn>状态</TableColumn>
|
<TableColumn>状态</TableColumn>
|
||||||
<TableColumn>限速规则</TableColumn>
|
<TableColumn>限速规则</TableColumn>
|
||||||
<TableColumn>重置时间</TableColumn>
|
<TableColumn>重置时间</TableColumn>
|
||||||
@@ -1453,7 +1453,7 @@ export default function UserPage() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
label="转发数量"
|
label="规则数量"
|
||||||
max="99999"
|
max="99999"
|
||||||
min="1"
|
min="1"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -1668,7 +1668,7 @@ export default function UserPage() {
|
|||||||
的权限吗?
|
的权限吗?
|
||||||
</p>
|
</p>
|
||||||
<p className="text-small text-default-500 mt-1">
|
<p className="text-small text-default-500 mt-1">
|
||||||
删除后该用户将无法使用此隧道创建转发,此操作不可撤销。
|
删除后该用户将无法使用此隧道创建规则,此操作不可撤销。{" "}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user