告警规则表单优化:数字输入框使用本地状态管理,解决清空后回退到默认值问题
This commit is contained in:
parent
a5dda91f20
commit
c1e0763472
@ -40,7 +40,7 @@ export const Terminal: React.FC<TerminalProps> = ({
|
||||
const [showSearch, setShowSearch] = useState(false);
|
||||
const [currentTheme, setCurrentTheme] = useState('dark');
|
||||
const [auditShown, setAuditShown] = useState(false);
|
||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('disconnected'); // 初始状态,会被实例状态覆盖
|
||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>('connecting'); // 初始状态为连接中,符合正常生命周期
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const [connectedTime, setConnectedTime] = useState<Date | null>(null);
|
||||
|
||||
@ -103,10 +103,11 @@ export const Terminal: React.FC<TerminalProps> = ({
|
||||
}
|
||||
});
|
||||
|
||||
// 延迟连接(仅在未连接时)
|
||||
// 延迟连接(仅在未连接或需要连接时)
|
||||
const timer = setTimeout(() => {
|
||||
const currentState = instance.getState();
|
||||
if (currentState.status === 'disconnected' || currentState.status === 'error') {
|
||||
// 在 connecting、disconnected 或 error 状态下需要建立连接
|
||||
if (currentState.status === 'connecting' || currentState.status === 'disconnected' || currentState.status === 'error') {
|
||||
instance.connect();
|
||||
}
|
||||
}, 300);
|
||||
|
||||
@ -22,7 +22,7 @@ export type ErrorCallback = (error: string) => void;
|
||||
* 抽象连接策略基类
|
||||
*/
|
||||
export abstract class BaseConnectionStrategy {
|
||||
protected status: ConnectionStatus = 'disconnected';
|
||||
protected status: ConnectionStatus = 'connecting'; // 初始状态为connecting,符合打开终端时的正常生命周期
|
||||
protected statusListeners: Set<StatusChangeCallback> = new Set();
|
||||
protected messageListeners: Set<MessageCallback> = new Set();
|
||||
protected errorListeners: Set<ErrorCallback> = new Set();
|
||||
|
||||
@ -3,7 +3,22 @@ import * as React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
({ className, type, ...props }, ref) => {
|
||||
({ className, type, onChange, ...props }, ref) => {
|
||||
// 为 number 类型的输入框提供智能处理,避免空值被转换为 0
|
||||
const handleChange = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (type === 'number' && onChange) {
|
||||
const value = e.target.value;
|
||||
// 当输入为空时,将 value 设置为空字符串,防止外部 Number() 转换为 0
|
||||
// 外部应该判断空字符串并转为 undefined/null
|
||||
onChange(e);
|
||||
} else {
|
||||
onChange?.(e);
|
||||
}
|
||||
},
|
||||
[type, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
@ -12,6 +27,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
onChange={handleChange}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -51,12 +51,8 @@ const DeleteDialog: React.FC<DeleteDialogProps> = ({
|
||||
onSuccess();
|
||||
onOpenChange(false);
|
||||
} catch (error) {
|
||||
// 错误提示统一由全局 request 拦截器处理,这里仅记录日志
|
||||
console.error('删除失败:', error);
|
||||
toast({
|
||||
title: '删除失败',
|
||||
description: error instanceof Error ? error.message : '未知错误',
|
||||
variant: 'destructive'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Loader2, Search, Check, ChevronDown } from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -31,10 +31,13 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { AlertRuleResponse, ServerResponse, AlertRuleRequest } from '../types';
|
||||
import { AlertType, AlertTypeLabels } from '../types';
|
||||
import { alertRuleFormSchema, type AlertRuleFormValues } from '../schema';
|
||||
import { createAlertRule, updateAlertRule, getServers } from '../service';
|
||||
import { createAlertRule, updateAlertRule, getServerList } from '../service';
|
||||
|
||||
interface AlertRuleFormDialogProps {
|
||||
open: boolean;
|
||||
@ -52,6 +55,8 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [servers, setServers] = useState<ServerResponse[]>([]);
|
||||
const [serverSearchValue, setServerSearchValue] = useState('');
|
||||
const [serverPopoverOpen, setServerPopoverOpen] = useState(false);
|
||||
|
||||
const form = useForm<AlertRuleFormValues>({
|
||||
resolver: zodResolver(alertRuleFormSchema),
|
||||
@ -76,8 +81,8 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
|
||||
const loadServers = async () => {
|
||||
try {
|
||||
const result = await getServers({ pageNum: 0, size: 1000 });
|
||||
setServers(result?.content || []);
|
||||
const result = await getServerList();
|
||||
setServers(result || []);
|
||||
} catch (error) {
|
||||
console.error('加载服务器列表失败:', error);
|
||||
}
|
||||
@ -168,26 +173,101 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
render={({ field }) => (
|
||||
<FormItem className="col-span-2">
|
||||
<FormLabel>规则范围</FormLabel>
|
||||
<Select
|
||||
value={field.value === null ? 'global' : field.value.toString()}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value === 'global' ? null : Number(value));
|
||||
<Popover open={serverPopoverOpen} onOpenChange={setServerPopoverOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<FormControl>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className={cn(
|
||||
'w-full justify-between',
|
||||
field.value === null && 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
{field.value === null ? (
|
||||
'全局规则(适用于所有服务器)'
|
||||
) : (() => {
|
||||
const server = servers.find(s => s.id === field.value);
|
||||
return server ? `专属规则 - ${server.serverName} (${server.hostIp})` : '选择规则范围';
|
||||
})()}
|
||||
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</FormControl>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<div className="flex items-center border-b px-3">
|
||||
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<input
|
||||
placeholder="搜索服务器..."
|
||||
className="flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground"
|
||||
value={serverSearchValue}
|
||||
onChange={(e) => setServerSearchValue(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<ScrollArea className="h-[300px]">
|
||||
<div className="p-1">
|
||||
{/* 全局规则选项 */}
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground',
|
||||
field.value === null && 'bg-accent text-accent-foreground'
|
||||
)}
|
||||
onClick={() => {
|
||||
field.onChange(null);
|
||||
setServerSearchValue('');
|
||||
setServerPopoverOpen(false);
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择规则范围" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="global">全局规则(适用于所有服务器)</SelectItem>
|
||||
{servers.map((server) => (
|
||||
<SelectItem key={server.id} value={server.id.toString()}>
|
||||
专属规则 - {server.serverName} ({server.hostIp})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="flex-1 truncate">
|
||||
全局规则(适用于所有服务器)
|
||||
</div>
|
||||
{field.value === null && (
|
||||
<Check className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 服务器列表 */}
|
||||
{(() => {
|
||||
const filteredServers = servers.filter(server =>
|
||||
server.serverName.toLowerCase().includes(serverSearchValue.toLowerCase()) ||
|
||||
server.hostIp.toLowerCase().includes(serverSearchValue.toLowerCase())
|
||||
);
|
||||
|
||||
if (filteredServers.length === 0 && serverSearchValue) {
|
||||
return (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
未找到服务器
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return filteredServers.map((server) => (
|
||||
<div
|
||||
key={server.id}
|
||||
className={cn(
|
||||
'relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground',
|
||||
server.id === field.value && 'bg-accent text-accent-foreground'
|
||||
)}
|
||||
onClick={() => {
|
||||
field.onChange(server.id);
|
||||
setServerSearchValue('');
|
||||
setServerPopoverOpen(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex-1 truncate">
|
||||
<span className="font-medium">专属规则 - {server.serverName}</span>
|
||||
<span className="text-muted-foreground"> ({server.hostIp})</span>
|
||||
</div>
|
||||
{server.id === field.value && (
|
||||
<Check className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
));
|
||||
})()}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FormDescription>
|
||||
全局规则适用于所有服务器,专属规则只对指定服务器生效(会覆盖同类型的全局规则)
|
||||
</FormDescription>
|
||||
@ -248,7 +328,17 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="durationMinutes"
|
||||
render={({ field }) => (
|
||||
render={({ field }) => {
|
||||
const [localValue, setLocalValue] = React.useState<string>(
|
||||
field.value?.toString() ?? ''
|
||||
);
|
||||
|
||||
// 同步外部值变化到本地状态
|
||||
React.useEffect(() => {
|
||||
setLocalValue(field.value?.toString() ?? '');
|
||||
}, [field.value]);
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
持续时长(分钟) <span className="text-destructive">*</span>
|
||||
@ -257,8 +347,23 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(Number(e.target.value))}
|
||||
value={localValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setLocalValue(value);
|
||||
// 只有在有效值时才更新表单
|
||||
if (value !== '') {
|
||||
field.onChange(Number(value));
|
||||
}
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
// 失焦时,如果为空则设置为undefined触发验证
|
||||
if (localValue === '') {
|
||||
field.onChange(undefined);
|
||||
}
|
||||
field.onBlur();
|
||||
}}
|
||||
name={field.name}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
@ -266,14 +371,27 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 警告阈值 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="warningThreshold"
|
||||
render={({ field }) => (
|
||||
render={({ field }) => {
|
||||
const currentAlertType = form.watch('alertType');
|
||||
const isServerStatus = currentAlertType === AlertType.SERVER_STATUS;
|
||||
const [localValue, setLocalValue] = React.useState<string>(
|
||||
field.value?.toString() ?? ''
|
||||
);
|
||||
|
||||
// 同步外部值变化到本地状态
|
||||
React.useEffect(() => {
|
||||
setLocalValue(field.value?.toString() ?? '');
|
||||
}, [field.value]);
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
警告阈值 <span className="text-destructive">*</span>
|
||||
@ -284,26 +402,54 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.1"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(Number(e.target.value))}
|
||||
step={isServerStatus ? "1" : "0.1"}
|
||||
value={localValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setLocalValue(value);
|
||||
// 只有在有效值时才更新表单
|
||||
if (value !== '') {
|
||||
field.onChange(Number(value));
|
||||
}
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
// 失焦时,如果为空则设置为undefined触发验证
|
||||
if (localValue === '') {
|
||||
field.onChange(undefined);
|
||||
}
|
||||
field.onBlur();
|
||||
}}
|
||||
name={field.name}
|
||||
className="flex-1"
|
||||
/>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{AlertTypeLabels[form.watch('alertType')]?.unit || '%'}
|
||||
{AlertTypeLabels[currentAlertType]?.unit || '%'}
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 严重阈值 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="criticalThreshold"
|
||||
render={({ field }) => (
|
||||
render={({ field }) => {
|
||||
const currentAlertType = form.watch('alertType');
|
||||
const isServerStatus = currentAlertType === AlertType.SERVER_STATUS;
|
||||
const [localValue, setLocalValue] = React.useState<string>(
|
||||
field.value?.toString() ?? ''
|
||||
);
|
||||
|
||||
// 同步外部值变化到本地状态
|
||||
React.useEffect(() => {
|
||||
setLocalValue(field.value?.toString() ?? '');
|
||||
}, [field.value]);
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
严重阈值 <span className="text-destructive">*</span>
|
||||
@ -314,19 +460,35 @@ export const AlertRuleFormDialog: React.FC<AlertRuleFormDialogProps> = ({
|
||||
type="number"
|
||||
min="0"
|
||||
max="100"
|
||||
step="0.1"
|
||||
{...field}
|
||||
onChange={(e) => field.onChange(Number(e.target.value))}
|
||||
step={isServerStatus ? "1" : "0.1"}
|
||||
value={localValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setLocalValue(value);
|
||||
// 只有在有效值时才更新表单
|
||||
if (value !== '') {
|
||||
field.onChange(Number(value));
|
||||
}
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
// 失焦时,如果为空则设置为undefined触发验证
|
||||
if (localValue === '') {
|
||||
field.onChange(undefined);
|
||||
}
|
||||
field.onBlur();
|
||||
}}
|
||||
name={field.name}
|
||||
className="flex-1"
|
||||
/>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{AlertTypeLabels[form.watch('alertType')]?.unit || '%'}
|
||||
{AlertTypeLabels[currentAlertType]?.unit || '%'}
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 是否启用 */}
|
||||
|
||||
@ -59,6 +59,7 @@ const ServerList: React.FC = () => {
|
||||
const [categories, setCategories] = useState<ServerCategoryResponse[]>([]);
|
||||
const [servers, setServers] = useState<ServerResponse[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [pageLoading, setPageLoading] = useState(false); // 分页加载状态
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize] = useState(10);
|
||||
const [totalElements, setTotalElements] = useState(0);
|
||||
@ -142,11 +143,16 @@ const ServerList: React.FC = () => {
|
||||
};
|
||||
|
||||
// 加载服务器列表
|
||||
const loadServers = async (silent: boolean = false) => {
|
||||
const loadServers = async (silent: boolean = false, isPagination: boolean = false) => {
|
||||
// silent 为 true 时不显示全局 loading 状态(用于操作后的静默刷新)
|
||||
// isPagination 为 true 时只显示分页 loading,不显示全局 loading
|
||||
if (!silent) {
|
||||
if (isPagination) {
|
||||
setPageLoading(true);
|
||||
} else {
|
||||
setLoading(true);
|
||||
}
|
||||
}
|
||||
try {
|
||||
const params = {
|
||||
categoryId: selectedCategoryId,
|
||||
@ -184,6 +190,7 @@ const ServerList: React.FC = () => {
|
||||
} finally {
|
||||
if (!silent) {
|
||||
setLoading(false);
|
||||
setPageLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -335,10 +342,21 @@ const ServerList: React.FC = () => {
|
||||
);
|
||||
});
|
||||
|
||||
// 监听筛选条件、分页和视图模式变化
|
||||
// 监听筛选条件和视图模式变化(使用全局loading)
|
||||
useEffect(() => {
|
||||
loadServers();
|
||||
}, [pageIndex, pageSize, selectedCategoryId, selectedStatus, selectedOsType, viewMode]);
|
||||
loadServers(false, false); // 筛选加载,使用全局loading
|
||||
// 筛选条件变化时重置分页到第一页
|
||||
setPageIndex(0);
|
||||
}, [pageSize, selectedCategoryId, selectedStatus, selectedOsType, viewMode]);
|
||||
|
||||
// 监听分页变化(单独处理,使用pageLoading)
|
||||
// 注意:这个useEffect只处理用户主动翻页的情况
|
||||
useEffect(() => {
|
||||
if (viewMode === 'table' && pageIndex > 0) {
|
||||
// pageIndex > 0 说明是用户翻页操作,使用pageLoading
|
||||
loadServers(false, true);
|
||||
}
|
||||
}, [pageIndex]);
|
||||
|
||||
// 使用statsData作为统计数据
|
||||
const stats = statsData;
|
||||
@ -607,6 +625,15 @@ const ServerList: React.FC = () => {
|
||||
|
||||
{/* 表格视图 */}
|
||||
{!loading && filteredServers.length > 0 && viewMode === 'table' && (
|
||||
<div className="relative">
|
||||
{pageLoading && (
|
||||
<div className="absolute inset-0 bg-background/80 backdrop-blur-sm z-10 flex items-center justify-center">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ServerTable
|
||||
servers={filteredServers}
|
||||
loading={loading}
|
||||
@ -619,6 +646,7 @@ const ServerList: React.FC = () => {
|
||||
isCollecting={(serverId) => collectingServerId === serverId}
|
||||
getOsIcon={getOsIcon}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 分页(仅列表模式) */}
|
||||
|
||||
@ -101,6 +101,24 @@ export const alertRuleFormSchema = z.object({
|
||||
path: ['criticalThreshold'],
|
||||
});
|
||||
}
|
||||
|
||||
// 服务器状态告警的阈值必须是整数
|
||||
if (data.alertType === AlertType.SERVER_STATUS) {
|
||||
if (!Number.isInteger(data.warningThreshold)) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: '服务器状态告警的警告阈值必须是整数',
|
||||
path: ['warningThreshold'],
|
||||
});
|
||||
}
|
||||
if (!Number.isInteger(data.criticalThreshold)) {
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
message: '服务器状态告警的严重阈值必须是整数',
|
||||
path: ['criticalThreshold'],
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export type AlertRuleFormValues = z.infer<typeof alertRuleFormSchema>;
|
||||
|
||||
@ -292,6 +292,8 @@ export enum AlertType {
|
||||
DISK = 'DISK',
|
||||
/** 网络流量 */
|
||||
NETWORK = 'NETWORK',
|
||||
/** 服务器状态 */
|
||||
SERVER_STATUS = 'SERVER_STATUS',
|
||||
}
|
||||
|
||||
/**
|
||||
@ -366,5 +368,6 @@ export const AlertTypeLabels: Record<AlertType, { label: string; unit: string; d
|
||||
[AlertType.MEMORY]: { label: '内存使用率', unit: '%', description: '内存使用率告警' },
|
||||
[AlertType.DISK]: { label: '磁盘使用率', unit: '%', description: '磁盘使用率告警' },
|
||||
[AlertType.NETWORK]: { label: '网络流量', unit: 'MB/s', description: '网络流量告警' },
|
||||
[AlertType.SERVER_STATUS]: { label: '服务器状态', unit: '次', description: '服务器连接状态告警' },
|
||||
};
|
||||
|
||||
|
||||
@ -124,7 +124,6 @@ export const HttpRequestNodeDefinition: ConfigurableNodeDefinition = {
|
||||
title: "超时时间(毫秒)",
|
||||
description: "请求超时时间,0表示不限制",
|
||||
minimum: 1000,
|
||||
maximum: 300000,
|
||||
default: 30000
|
||||
},
|
||||
responseBodyType: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user