feat: AI预测分析接入真实后端数据 + 修复CloudBilling文件损坏
- AIPredictiveAnalytics: 替换3组硬编码mock为实时数据驱动预测 - 指标预测: 基于dashboard metrics时序数据(cpu/memory/disk)线性回归计算斜率与R² - 容量规划: 基于日均增长率推算阈值触及时间(天),动态生成AI扩容建议 - 异常预测: 基于告警规则阈值+当前趋势+事件频率计算异常触发概率 - 概览卡片数据驱动化(置信度基于R²拟合度,高风险/容量预警计数实时计算) - CloudBilling.tsx: 修复read_file导致的行号前缀损坏(501行全部含N|前缀)
This commit is contained in:
1 parent
0ebe22d142
commit
2da4f52209
17 files changed
+2299
-322
No files matched your search
@@ -0,0 +1,96 @@
|
||||
import { useState, useCallback, createContext, useContext } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import {
|
||||
Dialog, DialogContent, DialogHeader, DialogTitle,
|
||||
DialogDescription, DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface ConfirmOptions {
|
||||
title?: string;
|
||||
description?: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
variant?: 'default' | 'destructive';
|
||||
}
|
||||
|
||||
interface ConfirmContextValue {
|
||||
confirm: (options: ConfirmOptions | string) => Promise<boolean>;
|
||||
}
|
||||
|
||||
const ConfirmContext = createContext<ConfirmContextValue | null>(null);
|
||||
|
||||
export function useConfirm() {
|
||||
const ctx = useContext(ConfirmContext);
|
||||
if (!ctx) throw new Error('useConfirm must be used within ConfirmProvider');
|
||||
return ctx.confirm;
|
||||
}
|
||||
|
||||
export function ConfirmProvider({ children }: { children: ReactNode }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [options, setOptions] = useState<Required<ConfirmOptions>>({
|
||||
title: '确认操作',
|
||||
description: '',
|
||||
confirmText: '确认',
|
||||
cancelText: '取消',
|
||||
variant: 'default',
|
||||
});
|
||||
const [resolver, setResolver] = useState<((v: boolean) => void) | null>(null);
|
||||
|
||||
const confirm = useCallback((opts: ConfirmOptions | string) => {
|
||||
const normalized = typeof opts === 'string'
|
||||
? { title: '确认操作', description: opts }
|
||||
: opts;
|
||||
setOptions({
|
||||
title: normalized.title || '确认操作',
|
||||
description: normalized.description || '',
|
||||
confirmText: normalized.confirmText || '确认',
|
||||
cancelText: normalized.cancelText || '取消',
|
||||
variant: (normalized.variant || 'default') as 'default' | 'destructive',
|
||||
});
|
||||
setOpen(true);
|
||||
return new Promise<boolean>((resolve) => setResolver(() => resolve));
|
||||
}, []);
|
||||
|
||||
const handleConfirm = () => {
|
||||
setOpen(false);
|
||||
resolver?.(true);
|
||||
setResolver(null);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setOpen(false);
|
||||
resolver?.(false);
|
||||
setResolver(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfirmContext.Provider value={{ confirm }}>
|
||||
{children}
|
||||
<Dialog open={open} onOpenChange={(v) => { if (!v) handleCancel(); }}>
|
||||
<DialogContent className="max-w-sm bg-slate-900 border-slate-800" onCloseAutoFocus={(e) => e.preventDefault()}>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-slate-100">{options.title}</DialogTitle>
|
||||
{options.description && (
|
||||
<DialogDescription className="text-slate-400">{options.description}</DialogDescription>
|
||||
)}
|
||||
</DialogHeader>
|
||||
<DialogFooter className="gap-2 sm:gap-2">
|
||||
<Button variant="outline" className="border-slate-700 text-slate-300" onClick={handleCancel}>
|
||||
{options.cancelText}
|
||||
</Button>
|
||||
<Button
|
||||
variant={options.variant === 'destructive' ? 'destructive' : 'default'}
|
||||
className={options.variant === 'destructive'
|
||||
? 'bg-red-600 hover:bg-red-700 text-white'
|
||||
: 'bg-blue-600 hover:bg-blue-700 text-white'}
|
||||
onClick={handleConfirm}
|
||||
>
|
||||
{options.confirmText}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</ConfirmContext.Provider>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user