feat(k8s-ui): HPA 创建按钮醒目化 + K8sWorkloads 命名空间切换
DeploymentDetail: - HPA 未配置时,状态卡片内直接显示「创建 HPA」按钮 - 无需切换到 HPA Tab 即可一键创建 K8sWorkloads: - 新增命名空间下拉选择器(自动加载集群全部 ns) - 切换集群时自动重置命名空间为「全部」 - Deployment/Pod 列表按命名空间过滤 - 统计卡片始终显示全量数据(不受 ns 过滤影响)
This commit is contained in:
1 parent
77045622fc
commit
f7b667b878
2 files changed
+43
-4
No files matched your search
@@ -220,7 +220,19 @@ export function DeploymentDetail({ clusterId, namespace, deploymentName, onBack
|
|||||||
<Card className="bg-slate-900 border-slate-800">
|
<Card className="bg-slate-900 border-slate-800">
|
||||||
<CardContent className="p-3">
|
<CardContent className="p-3">
|
||||||
<p className="text-xs text-slate-500">HPA</p>
|
<p className="text-xs text-slate-500">HPA</p>
|
||||||
<p className="text-lg font-bold text-purple-400">{hpas.length > 0 ? '已启用' : '未配置'}</p>
|
{hpas.length > 0 ? (
|
||||||
|
<p className="text-lg font-bold text-purple-400">已启用</p>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<p className="text-lg font-bold text-slate-500">未配置</p>
|
||||||
|
<CreateHPADialog
|
||||||
|
clusterId={clusterId}
|
||||||
|
namespace={namespace}
|
||||||
|
deploymentName={deploymentName}
|
||||||
|
onCreated={loadData}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ export function K8sWorkloads() {
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [clusters, setClusters] = useState<any[]>([]);
|
const [clusters, setClusters] = useState<any[]>([]);
|
||||||
const [selectedCluster, setSelectedCluster] = useState('');
|
const [selectedCluster, setSelectedCluster] = useState('');
|
||||||
|
const [namespaces, setNamespaces] = useState<string[]>([]);
|
||||||
|
const [selectedNamespace, setSelectedNamespace] = useState('all');
|
||||||
const [deployments, setDeployments] = useState<any[]>([]);
|
const [deployments, setDeployments] = useState<any[]>([]);
|
||||||
const [pods, setPods] = useState<any[]>([]);
|
const [pods, setPods] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@@ -43,6 +45,18 @@ export function K8sWorkloads() {
|
|||||||
|
|
||||||
useEffect(() => { loadClusters(); }, [loadClusters]);
|
useEffect(() => { loadClusters(); }, [loadClusters]);
|
||||||
|
|
||||||
|
// Load namespaces when cluster changes
|
||||||
|
const loadNamespaces = useCallback(async () => {
|
||||||
|
if (!selectedCluster) return;
|
||||||
|
try {
|
||||||
|
const res: any = await k8sApi.getNamespaces(selectedCluster);
|
||||||
|
const nsList: string[] = Array.isArray(res) ? res : (res?.list || res?.data || []);
|
||||||
|
setNamespaces(nsList);
|
||||||
|
} catch { setNamespaces([]); }
|
||||||
|
}, [selectedCluster]);
|
||||||
|
|
||||||
|
useEffect(() => { loadNamespaces(); }, [loadNamespaces]);
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
if (!selectedCluster) return;
|
if (!selectedCluster) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -60,10 +74,12 @@ export function K8sWorkloads() {
|
|||||||
useEffect(() => { loadData(); }, [loadData]);
|
useEffect(() => { loadData(); }, [loadData]);
|
||||||
|
|
||||||
const filteredDeps = deployments.filter((d) =>
|
const filteredDeps = deployments.filter((d) =>
|
||||||
(d.name || '').toLowerCase().includes(search.toLowerCase()),
|
(d.name || '').toLowerCase().includes(search.toLowerCase()) &&
|
||||||
|
(selectedNamespace === 'all' || d.namespace === selectedNamespace),
|
||||||
);
|
);
|
||||||
const filteredPods = pods.filter((p) =>
|
const filteredPods = pods.filter((p) =>
|
||||||
(p.name || '').toLowerCase().includes(search.toLowerCase()),
|
(p.name || '').toLowerCase().includes(search.toLowerCase()) &&
|
||||||
|
(selectedNamespace === 'all' || p.namespace === selectedNamespace),
|
||||||
);
|
);
|
||||||
|
|
||||||
const getPodStatusBadge = (status: string) => {
|
const getPodStatusBadge = (status: string) => {
|
||||||
@@ -112,7 +128,7 @@ export function K8sWorkloads() {
|
|||||||
<p className="text-sm text-slate-500 mt-1">Deployment 与 Pod 运行状态</p>
|
<p className="text-sm text-slate-500 mt-1">Deployment 与 Pod 运行状态</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Select value={selectedCluster} onValueChange={setSelectedCluster}>
|
<Select value={selectedCluster} onValueChange={(v) => { setSelectedCluster(v); setSelectedNamespace('all'); }}>
|
||||||
<SelectTrigger className="w-48 h-9 bg-slate-800/50 border-slate-700 text-slate-200">
|
<SelectTrigger className="w-48 h-9 bg-slate-800/50 border-slate-700 text-slate-200">
|
||||||
<SelectValue placeholder="选择集群" />
|
<SelectValue placeholder="选择集群" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@@ -122,6 +138,17 @@ export function K8sWorkloads() {
|
|||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
<Select value={selectedNamespace} onValueChange={setSelectedNamespace}>
|
||||||
|
<SelectTrigger className="w-40 h-9 bg-slate-800/50 border-slate-700 text-slate-200">
|
||||||
|
<SelectValue placeholder="命名空间" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent className="bg-slate-800 border-slate-700 max-h-[300px]">
|
||||||
|
<SelectItem value="all">全部命名空间</SelectItem>
|
||||||
|
{namespaces.map((ns) => (
|
||||||
|
<SelectItem key={ns} value={ns}>{ns}</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
<Button variant="outline" size="icon" className="h-9 w-9 border-slate-700 text-slate-400" onClick={loadData} disabled={loading}>
|
<Button variant="outline" size="icon" className="h-9 w-9 border-slate-700 text-slate-400" onClick={loadData} disabled={loading}>
|
||||||
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
|
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in new issue
Block a user