feat: add forward flow reset action

This commit is contained in:
sagitchu
2026-07-10 16:57:05 +08:00
parent d153c2832c
commit 5ceced57e8
2 changed files with 182 additions and 4 deletions
+2
View File
@@ -217,6 +217,8 @@ export const pauseForwardService = (forwardId: number) =>
Network.post("/forward/pause", { id: forwardId });
export const resumeForwardService = (forwardId: number) =>
Network.post("/forward/resume", { id: forwardId });
export const resetForwardFlow = (forwardId: number) =>
Network.post("/forward/reset-flow", { id: forwardId });
// 转发诊断操作
export const diagnoseForward = (forwardId: number) =>
+180 -4
View File
@@ -69,6 +69,7 @@ import {
getNodeList,
pauseForwardService,
resumeForwardService,
resetForwardFlow,
diagnoseForward,
updateForwardOrder,
getConfigByName,
@@ -230,7 +231,7 @@ const FORWARD_GROUPED_TABLE_COLUMN_CLASS = {
strategy: "w-[100px]",
totalFlow: "w-[120px]",
status: "w-[100px]",
actions: "w-[144px] text-right",
actions: "w-[176px] text-right",
} as const;
const normalizeForwardUserName = (userName?: string): string => {
@@ -764,6 +765,7 @@ const SortableTableRow = ({
handleEdit,
handleDelete,
handleDiagnose,
handleResetFlow,
showAddressModal,
formatFlow,
}: any) => {
@@ -919,6 +921,29 @@ const SortableTableRow = ({
/>
</svg>
</Button>
<Button
isIconOnly
className="bg-secondary/10 text-secondary hover:bg-secondary/20"
isDisabled={(forward.inFlow || 0) + (forward.outFlow || 0) <= 0}
size="sm"
title="流量清零"
onPress={() => handleResetFlow(forward)}
>
<svg
aria-hidden="true"
className="h-4 w-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
d="M4 4v6h6M20 20v-6h-6M20 9a8 8 0 00-13.657-3.657L4 8m16 8-2.343 2.657A8 8 0 014 15"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
/>
</svg>
</Button>
<Button
isIconOnly
className="bg-danger/10 text-danger hover:bg-danger/20"
@@ -958,6 +983,7 @@ const SortableCompactTableRow = ({
handleEdit,
handleDelete,
handleDiagnose,
handleResetFlow,
showAddressModal,
hasMultipleAddresses,
formatFlow,
@@ -1144,6 +1170,29 @@ const SortableCompactTableRow = ({
/>
</svg>
</Button>
<Button
isIconOnly
className="bg-secondary/10 text-secondary hover:bg-secondary/20"
isDisabled={(forward.inFlow || 0) + (forward.outFlow || 0) <= 0}
size="sm"
title="流量清零"
onPress={() => handleResetFlow(forward)}
>
<svg
aria-hidden="true"
className="h-4 w-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
d="M4 4v6h6M20 20v-6h-6M20 9a8 8 0 00-13.657-3.657L4 8m16 8-2.343 2.657A8 8 0 014 15"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
/>
</svg>
</Button>
<Button
isIconOnly
className="bg-danger/10 text-danger hover:bg-danger/20"
@@ -1289,13 +1338,18 @@ export default function ForwardPage() {
const [modalOpen, setModalOpen] = useState(false);
// isFilterModalOpen removed
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
const [resetFlowModalOpen, setResetFlowModalOpen] = useState(false);
const [addressModalOpen, setAddressModalOpen] = useState(false);
const [diagnosisModalOpen, setDiagnosisModalOpen] = useState(false);
const [isEdit, setIsEdit] = useState(false);
const [submitLoading, setSubmitLoading] = useState(false);
const [deleteLoading, setDeleteLoading] = useState(false);
const [resetFlowLoading, setResetFlowLoading] = useState(false);
const [diagnosisLoading, setDiagnosisLoading] = useState(false);
const [forwardToDelete, setForwardToDelete] = useState<Forward | null>(null);
const [forwardToResetFlow, setForwardToResetFlow] = useState<Forward | null>(
null,
);
const [currentDiagnosisForward, setCurrentDiagnosisForward] =
useState<Forward | null>(null);
const [diagnosisResult, setDiagnosisResult] =
@@ -2171,7 +2225,8 @@ export default function ForwardPage() {
maxConn: forward.maxConn ?? 0,
proxyProtocol: forward.proxyProtocol ?? 0,
proxyProtocolReceive: forward.proxyProtocolReceive ?? 0,
proxyProtocolSend: forward.proxyProtocolSend ?? forward.proxyProtocol ?? 0,
proxyProtocolSend:
forward.proxyProtocolSend ?? forward.proxyProtocol ?? 0,
});
setErrors({});
setModalOpen(true);
@@ -2183,6 +2238,46 @@ export default function ForwardPage() {
setDeleteModalOpen(true);
};
const handleResetFlow = (forward: Forward) => {
if ((forward.inFlow || 0) + (forward.outFlow || 0) <= 0) return;
setForwardToResetFlow(forward);
setResetFlowModalOpen(true);
};
const handleResetFlowModalOpenChange = (isOpen: boolean) => {
if (resetFlowLoading) return;
setResetFlowModalOpen(isOpen);
if (!isOpen) {
setForwardToResetFlow(null);
}
};
const confirmResetFlow = async () => {
if (!forwardToResetFlow) return;
setResetFlowLoading(true);
try {
const res = await resetForwardFlow(forwardToResetFlow.id);
if (res.code !== 0) {
toast.error(res.msg || "流量清零失败");
return;
}
toast.success("规则流量已清零");
setResetFlowModalOpen(false);
setForwardToResetFlow(null);
await refreshForwardList(false);
} catch {
toast.error("流量清零失败");
} finally {
setResetFlowLoading(false);
}
};
// 确认删除规则
const confirmDelete = async () => {
if (!forwardToDelete) return;
@@ -3957,7 +4052,7 @@ export default function ForwardPage() {
</div>
</div>
<div className="flex gap-1.5 mt-3">
<div className="grid grid-cols-2 gap-1.5 mt-3">
<Button
className="flex-1 min-h-8"
color="primary"
@@ -4000,6 +4095,32 @@ export default function ForwardPage() {
>
诊断
</Button>
<Button
className="flex-1 min-h-8"
color="secondary"
isDisabled={(forward.inFlow || 0) + (forward.outFlow || 0) <= 0}
size="sm"
startContent={
<svg
aria-hidden="true"
className="w-3 h-3"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
d="M4 4v6h6M20 20v-6h-6M20 9a8 8 0 00-13.657-3.657L4 8m16 8-2.343 2.657A8 8 0 014 15"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
/>
</svg>
}
variant="flat"
onPress={() => handleResetFlow(forward)}
>
清零
</Button>
<Button
className="flex-1 min-h-8"
color="danger"
@@ -4302,7 +4423,7 @@ export default function ForwardPage() {
<TableColumn className="w-[80px]">策略</TableColumn>
<TableColumn className="w-[100px]">用量</TableColumn>
<TableColumn className="w-[80px]">状态</TableColumn>
<TableColumn align="left" className="w-[120px] pl-4">
<TableColumn align="left" className="w-[160px] pl-4">
操作
</TableColumn>
</TableHeader>
@@ -4321,6 +4442,7 @@ export default function ForwardPage() {
handleDelete={handleDelete}
handleDiagnose={handleDiagnose}
handleEdit={handleEdit}
handleResetFlow={handleResetFlow}
handleServiceToggle={handleServiceToggle}
hasMultipleAddresses={hasMultipleAddresses}
selectMode={selectMode}
@@ -4616,6 +4738,7 @@ export default function ForwardPage() {
handleDelete={handleDelete}
handleDiagnose={handleDiagnose}
handleEdit={handleEdit}
handleResetFlow={handleResetFlow}
handleServiceToggle={
handleServiceToggle
}
@@ -5172,6 +5295,59 @@ export default function ForwardPage() {
</ModalContent>
</Modal>
{/* 规则流量清零确认模态框 */}
<Modal
backdrop="blur"
classNames={{
base: "!w-[calc(100%-32px)] !mx-auto sm:!w-full rounded-2xl overflow-hidden",
}}
isOpen={resetFlowModalOpen}
placement="center"
scrollBehavior="inside"
size="lg"
onOpenChange={handleResetFlowModalOpenChange}
>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">
<h2 className="text-lg font-bold text-secondary">
确认流量清零
</h2>
</ModalHeader>
<ModalBody>
<p className="text-default-600">
确定要清零规则{" "}
<span className="font-semibold text-foreground">
&quot;{forwardToResetFlow?.name}&quot;
</span>{" "}
当前显示的上传和下载流量吗?
</p>
<p className="text-small text-default-500 mt-2">
此操作不可撤销,但不会影响用户总流量、用户隧道配额和历史统计。
</p>
</ModalBody>
<ModalFooter>
<Button
isDisabled={resetFlowLoading}
variant="light"
onPress={onClose}
>
取消
</Button>
<Button
color="secondary"
isLoading={resetFlowLoading}
onPress={confirmResetFlow}
>
确认清零
</Button>
</ModalFooter>
</>
)}
</ModalContent>
</Modal>
{/* 地址列表弹窗 */}
<Modal
classNames={{