mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-29 07:56:37 +08:00
优化显示,新增流量重置
This commit is contained in:
Binary file not shown.
@@ -172,6 +172,8 @@ get_config_params() {
|
||||
echo "🔧 请输入配置参数:"
|
||||
|
||||
echo "📡 节点端服务器和面板通信的地址,需要能正常访问(IPv6不需要加[])"
|
||||
echo "不要套CDN,不支持HTTPS,直接输入面板服务器的ip"
|
||||
echo "用于节点和面板通讯的地址,节点和面板通讯内容有加密!!!!!"
|
||||
while true; do
|
||||
read -p "当前面板服务器地址: " SERVER_HOST
|
||||
if [ -n "$SERVER_HOST" ]; then
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.admin.common.dto;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
@Data
|
||||
public class ResetFlowDto {
|
||||
|
||||
@NotNull(message = "重置账号id不能为空")
|
||||
private Integer id;
|
||||
|
||||
@NotNull(message = "重置类型不能为空")
|
||||
private Integer type;
|
||||
}
|
||||
@@ -3,11 +3,7 @@ package com.admin.controller;
|
||||
|
||||
import com.admin.common.aop.LogAnnotation;
|
||||
import com.admin.common.annotation.RequireRole;
|
||||
import com.admin.common.dto.ChangePasswordDto;
|
||||
import com.admin.common.dto.LoginDto;
|
||||
import com.admin.common.dto.PageDto;
|
||||
import com.admin.common.dto.UserDto;
|
||||
import com.admin.common.dto.UserUpdateDto;
|
||||
import com.admin.common.dto.*;
|
||||
import com.admin.common.lang.R;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -78,4 +74,14 @@ public class UserController extends BaseController {
|
||||
public R updatePassword(@Validated @RequestBody ChangePasswordDto changePasswordDto) {
|
||||
return userService.updatePassword(changePasswordDto);
|
||||
}
|
||||
|
||||
@LogAnnotation
|
||||
@RequireRole
|
||||
@PostMapping("/reset")
|
||||
public R reset(@Validated @RequestBody ResetFlowDto resetFlowDto) {
|
||||
return userService.reset(resetFlowDto);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package com.admin.service;
|
||||
|
||||
import com.admin.common.dto.ChangePasswordDto;
|
||||
import com.admin.common.dto.LoginDto;
|
||||
import com.admin.common.dto.PageDto;
|
||||
import com.admin.common.dto.UserDto;
|
||||
import com.admin.common.dto.UserUpdateDto;
|
||||
import com.admin.common.dto.*;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.entity.User;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
@@ -32,4 +28,6 @@ public interface UserService extends IService<User> {
|
||||
R getUserPackageInfo();
|
||||
|
||||
R updatePassword(ChangePasswordDto changePasswordDto);
|
||||
|
||||
R reset(ResetFlowDto resetFlowDto);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,7 @@ package com.admin.service.impl;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.admin.common.dto.ChangePasswordDto;
|
||||
import com.admin.common.dto.LoginDto;
|
||||
import com.admin.common.dto.PageDto;
|
||||
import com.admin.common.dto.UserDto;
|
||||
import com.admin.common.dto.UserUpdateDto;
|
||||
import com.admin.common.dto.UserPackageDto;
|
||||
import com.admin.common.dto.GostDto;
|
||||
import com.admin.common.dto.*;
|
||||
import com.admin.common.lang.R;
|
||||
import com.admin.common.task.DelayQueueManager;
|
||||
import com.admin.common.task.DelayTask;
|
||||
@@ -351,6 +345,24 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R reset(ResetFlowDto resetFlowDto) {
|
||||
if (resetFlowDto.getType() == 1){ // 清零账号流量
|
||||
User user = this.getById(resetFlowDto.getId());
|
||||
if (user == null) return R.err(ERROR_USER_NOT_FOUND);
|
||||
user.setInFlow(0L);
|
||||
user.setOutFlow(0L);
|
||||
this.updateById(user);
|
||||
}else { // 清零隧道流量
|
||||
UserTunnel tunnel = userTunnelService.getById(resetFlowDto.getId());
|
||||
if (tunnel == null) return R.err("隧道不存在");
|
||||
tunnel.setInFlow(0L);
|
||||
tunnel.setOutFlow(0L);
|
||||
userTunnelService.updateById(tunnel);
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
// ========== 私有辅助方法 ==========
|
||||
|
||||
/**
|
||||
@@ -691,7 +703,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
UserPackageDto.UserInfoDto userInfo = buildUserInfoDto(user);
|
||||
|
||||
// 2. 获取隧道权限详情
|
||||
List<UserPackageDto.UserTunnelDetailDto> tunnelPermissions = getTunnelPermissions(user.getId(), roleId);
|
||||
List<UserPackageDto.UserTunnelDetailDto> tunnelPermissions = getTunnelPermissions(user.getId());
|
||||
|
||||
// 3. 获取转发详情
|
||||
List<UserPackageDto.UserForwardDetailDto> forwards = userMapper.getUserForwardDetails(user.getId().intValue());
|
||||
@@ -731,15 +743,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
* 获取隧道权限详情
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param roleId 角色ID
|
||||
* @return 隧道权限详情列表
|
||||
*/
|
||||
private List<UserPackageDto.UserTunnelDetailDto> getTunnelPermissions(Long userId, Integer roleId) {
|
||||
if (roleId != null && roleId == ADMIN_ROLE_ID) {
|
||||
return userMapper.getAllTunnelsForAdmin();
|
||||
} else {
|
||||
return userMapper.getUserTunnelDetails(userId.intValue());
|
||||
}
|
||||
private List<UserPackageDto.UserTunnelDetailDto> getTunnelPermissions(Long userId) {
|
||||
return userMapper.getUserTunnelDetails(userId.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,6 +72,9 @@ export const deleteSpeedLimit = (id: number) => Network.post("/speed-limit/delet
|
||||
// 修改密码接口
|
||||
export const updatePassword = (data: any) => Network.post("/user/updatePassword", data);
|
||||
|
||||
// 重置流量接口
|
||||
export const resetUserFlow = (data: { id: number; type: number }) => Network.post("/user/reset", data);
|
||||
|
||||
// 网站配置相关接口
|
||||
export const getConfigs = () => Network.post("/config/list");
|
||||
export const getConfigByName = (name: string) => Network.post("/config/get", { name });
|
||||
|
||||
@@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig;
|
||||
|
||||
// 缓存相关常量
|
||||
const CACHE_PREFIX = 'vite_config_';
|
||||
const VERSION = "1.2.0";
|
||||
const VERSION = "1.2.1";
|
||||
|
||||
// 缓存策略:
|
||||
// - 长期有效,不设置过期时间
|
||||
|
||||
@@ -774,11 +774,11 @@ export default function DashboardPage() {
|
||||
<div className="pt-2 border-t border-gray-200 dark:border-default-200">
|
||||
<div className="grid grid-cols-3 gap-1 text-xs">
|
||||
<div className="text-center">
|
||||
<div className="text-default-500 mb-1">入站</div>
|
||||
<div className="text-default-500 mb-1">上传</div>
|
||||
<div className="font-medium text-green-600 dark:text-green-400 truncate">{formatFlow(forward.inFlow || 0)}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-default-500 mb-1">出站</div>
|
||||
<div className="text-default-500 mb-1">下载</div>
|
||||
<div className="font-medium text-orange-600 dark:text-orange-400 truncate">{formatFlow(forward.outFlow || 0)}</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
|
||||
@@ -760,14 +760,15 @@ export default function ForwardPage() {
|
||||
<Chip color={strategyDisplay.color as any} variant="flat" size="sm" className="text-xs">
|
||||
{strategyDisplay.text}
|
||||
</Chip>
|
||||
<Chip variant="flat" size="sm" className="text-xs">
|
||||
{formatFlow((forward.inFlow || 0) + (forward.outFlow || 0))}
|
||||
</Chip>
|
||||
{forward.userName && (
|
||||
<Chip variant="flat" size="sm" className="text-xs">
|
||||
{forward.userName}
|
||||
<div className="flex items-center gap-1">
|
||||
<Chip variant="flat" size="sm" className="text-xs" color="primary">
|
||||
↑{formatFlow(forward.inFlow || 0)}
|
||||
</Chip>
|
||||
|
||||
</div>
|
||||
<Chip variant="flat" size="sm" className="text-xs" color="success">
|
||||
↓{formatFlow(forward.outFlow || 0)}
|
||||
</Chip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -284,6 +284,34 @@ export default function NodePage() {
|
||||
return parseFloat((bytesPerSecond / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
// 格式化开机时间
|
||||
const formatUptime = (seconds: number): string => {
|
||||
if (seconds === 0) return '-';
|
||||
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
if (days > 0) {
|
||||
return `${days}天${hours}小时`;
|
||||
} else if (hours > 0) {
|
||||
return `${hours}小时${minutes}分钟`;
|
||||
} else {
|
||||
return `${minutes}分钟`;
|
||||
}
|
||||
};
|
||||
|
||||
// 格式化流量
|
||||
const formatTraffic = (bytes: number): string => {
|
||||
if (bytes === 0) return '0 B';
|
||||
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
};
|
||||
|
||||
// 获取进度条颜色
|
||||
const getProgressColor = (value: number, offline = false): "default" | "primary" | "secondary" | "success" | "warning" | "danger" => {
|
||||
if (offline) return "default";
|
||||
@@ -633,6 +661,15 @@ export default function NodePage() {
|
||||
<span className="text-default-600">版本</span>
|
||||
<span className="text-xs">{node.version || '未知'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-default-600">开机时间</span>
|
||||
<span className="text-xs">
|
||||
{node.connectionStatus === 'online' && node.systemInfo
|
||||
? formatUptime(node.systemInfo.uptime)
|
||||
: '-'
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 系统监控 */}
|
||||
@@ -700,6 +737,28 @@ export default function NodePage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 流量统计 */}
|
||||
<div className="grid grid-cols-2 gap-2 text-xs">
|
||||
<div className="text-center p-2 bg-primary-50 dark:bg-primary-100/20 rounded border border-primary-200 dark:border-primary-300/20">
|
||||
<div className="text-primary-600 dark:text-primary-400 mb-0.5">↑ 上行流量</div>
|
||||
<div className="font-mono text-primary-700 dark:text-primary-300">
|
||||
{node.connectionStatus === 'online' && node.systemInfo
|
||||
? formatTraffic(node.systemInfo.uploadTraffic)
|
||||
: '-'
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center p-2 bg-success-50 dark:bg-success-100/20 rounded border border-success-200 dark:border-success-300/20">
|
||||
<div className="text-success-600 dark:text-success-400 mb-0.5">↓ 下行流量</div>
|
||||
<div className="font-mono text-success-700 dark:text-success-300">
|
||||
{node.connectionStatus === 'online' && node.systemInfo
|
||||
? formatTraffic(node.systemInfo.downloadTraffic)
|
||||
: '-'
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
|
||||
@@ -518,23 +518,17 @@ export default function TunnelPage() {
|
||||
{/* 配置信息 */}
|
||||
<div className="flex justify-between items-center pt-2 border-t border-divider">
|
||||
<div className="text-left">
|
||||
<div className="text-xs text-default-500">流量计算</div>
|
||||
<div className="text-xs font-medium text-foreground">
|
||||
{getFlowDisplay(tunnel.flow)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<div className="text-xs text-default-500">倍率</div>
|
||||
<div className="text-xs font-medium text-foreground">
|
||||
{tunnel.trafficRatio}x
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 时间信息 */}
|
||||
<div className="text-xs text-default-500 pt-1 border-t border-divider">
|
||||
创建时间: {formatTime(tunnel.createdTime)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-1.5 mt-3">
|
||||
|
||||
@@ -46,7 +46,8 @@ import {
|
||||
getUserTunnelList,
|
||||
removeUserTunnel,
|
||||
updateUserTunnel,
|
||||
getSpeedLimitList
|
||||
getSpeedLimitList,
|
||||
resetUserFlow
|
||||
} from '@/api';
|
||||
import { SearchIcon, PlusIcon, EditIcon, DeleteIcon, UserIcon, SettingsIcon } from '@/components/icons';
|
||||
import { parseDate } from "@internationalized/date";
|
||||
@@ -157,6 +158,16 @@ export default function UserPage() {
|
||||
const { isOpen: isDeleteTunnelModalOpen, onOpen: onDeleteTunnelModalOpen, onClose: onDeleteTunnelModalClose } = useDisclosure();
|
||||
const [tunnelToDelete, setTunnelToDelete] = useState<UserTunnel | null>(null);
|
||||
|
||||
// 重置流量确认相关状态
|
||||
const { isOpen: isResetFlowModalOpen, onOpen: onResetFlowModalOpen, onClose: onResetFlowModalClose } = useDisclosure();
|
||||
const [userToReset, setUserToReset] = useState<User | null>(null);
|
||||
const [resetFlowLoading, setResetFlowLoading] = useState(false);
|
||||
|
||||
// 重置隧道流量确认相关状态
|
||||
const { isOpen: isResetTunnelFlowModalOpen, onOpen: onResetTunnelFlowModalOpen, onClose: onResetTunnelFlowModalClose } = useDisclosure();
|
||||
const [tunnelToReset, setTunnelToReset] = useState<UserTunnel | null>(null);
|
||||
const [resetTunnelFlowLoading, setResetTunnelFlowLoading] = useState(false);
|
||||
|
||||
// 其他数据
|
||||
const [tunnels, setTunnels] = useState<Tunnel[]>([]);
|
||||
const [speedLimits, setSpeedLimits] = useState<SpeedLimit[]>([]);
|
||||
@@ -444,6 +455,70 @@ export default function UserPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 重置流量相关函数
|
||||
const handleResetFlow = (user: User) => {
|
||||
setUserToReset(user);
|
||||
onResetFlowModalOpen();
|
||||
};
|
||||
|
||||
const handleConfirmResetFlow = async () => {
|
||||
if (!userToReset) return;
|
||||
|
||||
setResetFlowLoading(true);
|
||||
try {
|
||||
const response = await resetUserFlow({
|
||||
id: userToReset.id,
|
||||
type: 1 // 1表示重置用户流量
|
||||
});
|
||||
|
||||
if (response.code === 0) {
|
||||
toast.success('流量重置成功');
|
||||
onResetFlowModalClose();
|
||||
setUserToReset(null);
|
||||
loadUsers(); // 重新加载用户列表
|
||||
} else {
|
||||
toast.error(response.msg || '重置失败');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('重置失败');
|
||||
} finally {
|
||||
setResetFlowLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 隧道流量重置相关函数
|
||||
const handleResetTunnelFlow = (userTunnel: UserTunnel) => {
|
||||
setTunnelToReset(userTunnel);
|
||||
onResetTunnelFlowModalOpen();
|
||||
};
|
||||
|
||||
const handleConfirmResetTunnelFlow = async () => {
|
||||
if (!tunnelToReset) return;
|
||||
|
||||
setResetTunnelFlowLoading(true);
|
||||
try {
|
||||
const response = await resetUserFlow({
|
||||
id: tunnelToReset.id,
|
||||
type: 2 // 2表示重置隧道流量
|
||||
});
|
||||
|
||||
if (response.code === 0) {
|
||||
toast.success('隧道流量重置成功');
|
||||
onResetTunnelFlowModalClose();
|
||||
setTunnelToReset(null);
|
||||
if (currentUser) {
|
||||
loadUserTunnels(currentUser.id); // 重新加载隧道权限列表
|
||||
}
|
||||
} else {
|
||||
toast.error(response.msg || '重置失败');
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error('重置失败');
|
||||
} finally {
|
||||
setResetTunnelFlowLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 过滤数据
|
||||
const availableTunnels = tunnels.filter(
|
||||
tunnel => !userTunnels.some(ut => ut.tunnelId === tunnel.id)
|
||||
@@ -595,15 +670,16 @@ export default function UserPage() {
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="text-default-600">过期时间</span>
|
||||
<div className="text-right">
|
||||
<div className="text-xs">{formatDate(user.expTime)}</div>
|
||||
{expStatus && (
|
||||
{expStatus && expStatus.color === 'success' ? (
|
||||
<div className="text-xs">{formatDate(user.expTime)}</div>
|
||||
) : (
|
||||
<Chip
|
||||
color={expStatus.color}
|
||||
color={expStatus?.color || 'default'}
|
||||
variant="flat"
|
||||
size="sm"
|
||||
className="text-xs mt-0.5"
|
||||
className="text-xs"
|
||||
>
|
||||
{expStatus.text}
|
||||
{expStatus?.text || '未知状态'}
|
||||
</Chip>
|
||||
)}
|
||||
</div>
|
||||
@@ -623,6 +699,20 @@ export default function UserPage() {
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="flat"
|
||||
color="warning"
|
||||
onPress={() => handleResetFlow(user)}
|
||||
className="flex-1 min-h-8"
|
||||
startContent={
|
||||
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clipRule="evenodd" />
|
||||
</svg>
|
||||
}
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="flat"
|
||||
@@ -959,6 +1049,18 @@ export default function UserPage() {
|
||||
>
|
||||
<EditIcon className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="flat"
|
||||
color="warning"
|
||||
isIconOnly
|
||||
onClick={() => handleResetTunnelFlow(userTunnel)}
|
||||
title="重置流量"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="flat"
|
||||
@@ -1179,6 +1281,140 @@ export default function UserPage() {
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
{/* 重置流量确认对话框 */}
|
||||
<Modal
|
||||
isOpen={isResetFlowModalOpen}
|
||||
onClose={onResetFlowModalClose}
|
||||
size="md"
|
||||
scrollBehavior="outside"
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
确认重置流量
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-warning-100 rounded-full flex items-center justify-center">
|
||||
<svg className="w-6 h-6 text-warning" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-foreground">
|
||||
确定要重置用户 <span className="font-semibold text-warning">"{userToReset?.user}"</span> 的流量吗?
|
||||
</p>
|
||||
<p className="text-small text-default-500 mt-1">
|
||||
该操作只会重置账号流量不会重置隧道权限流量,重置后该用户的上下行流量将归零,此操作不可撤销。
|
||||
</p>
|
||||
<div className="mt-2 p-2 bg-warning-50 dark:bg-warning-100/10 rounded text-xs">
|
||||
<div className="text-warning-700 dark:text-warning-300">
|
||||
当前流量使用情况:
|
||||
</div>
|
||||
<div className="mt-1 space-y-1">
|
||||
<div className="flex justify-between">
|
||||
<span>上行流量:</span>
|
||||
<span className="font-mono">{userToReset ? formatFlow(userToReset.inFlow || 0) : '-'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>下行流量:</span>
|
||||
<span className="font-mono">{userToReset ? formatFlow(userToReset.outFlow || 0) : '-'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between font-medium">
|
||||
<span>总计:</span>
|
||||
<span className="font-mono text-warning-700 dark:text-warning-300">
|
||||
{userToReset ? formatFlow(calculateUserTotalUsedFlow(userToReset)) : '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="light"
|
||||
onPress={onResetFlowModalClose}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
color="warning"
|
||||
onPress={handleConfirmResetFlow}
|
||||
isLoading={resetFlowLoading}
|
||||
>
|
||||
确认重置
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
{/* 重置隧道流量确认对话框 */}
|
||||
<Modal
|
||||
isOpen={isResetTunnelFlowModalOpen}
|
||||
onClose={onResetTunnelFlowModalClose}
|
||||
size="md"
|
||||
scrollBehavior="outside"
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader className="flex flex-col gap-1">
|
||||
确认重置隧道流量
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-warning-100 rounded-full flex items-center justify-center">
|
||||
<svg className="w-6 h-6 text-warning" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-foreground">
|
||||
确定要重置用户 <span className="font-semibold">{currentUser?.user}</span> 对隧道 <span className="font-semibold text-warning">"{tunnelToReset?.tunnelName}"</span> 的流量吗?
|
||||
</p>
|
||||
<p className="text-small text-default-500 mt-1">
|
||||
该操作只会重置隧道权限流量不会重置账号流量,重置后该隧道权限的上下行流量将归零,此操作不可撤销。
|
||||
</p>
|
||||
<div className="mt-2 p-2 bg-warning-50 dark:bg-warning-100/10 rounded text-xs">
|
||||
<div className="text-warning-700 dark:text-warning-300">
|
||||
当前流量使用情况:
|
||||
</div>
|
||||
<div className="mt-1 space-y-1">
|
||||
<div className="flex justify-between">
|
||||
<span>上行流量:</span>
|
||||
<span className="font-mono">{tunnelToReset ? formatFlow(tunnelToReset.inFlow || 0) : '-'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>下行流量:</span>
|
||||
<span className="font-mono">{tunnelToReset ? formatFlow(tunnelToReset.outFlow || 0) : '-'}</span>
|
||||
</div>
|
||||
<div className="flex justify-between font-medium">
|
||||
<span>总计:</span>
|
||||
<span className="font-mono text-warning-700 dark:text-warning-300">
|
||||
{tunnelToReset ? formatFlow(calculateTunnelUsedFlow(tunnelToReset)) : '-'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="light"
|
||||
onPress={onResetTunnelFlowModalClose}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
color="warning"
|
||||
onPress={handleConfirmResetTunnelFlow}
|
||||
isLoading={resetTunnelFlowLoading}
|
||||
>
|
||||
确认重置
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
</AdminLayout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user