feat(k8s-ui): HPA 创建按钮醒目化 + K8sWorkloads 命名空间切换

DeploymentDetail:
  - HPA 未配置时,状态卡片内直接显示「创建 HPA」按钮
  - 无需切换到 HPA Tab 即可一键创建

K8sWorkloads:
  - 新增命名空间下拉选择器(自动加载集群全部 ns)
  - 切换集群时自动重置命名空间为「全部」
  - Deployment/Pod 列表按命名空间过滤
  - 统计卡片始终显示全量数据(不受 ns 过滤影响)
This commit is contained in:
attacker committed 2026-06-19 07:52:46 +00:00
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">
<CardContent className="p-3">
<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>
</Card>
</div>
+30 -3
View File
@@ -22,6 +22,8 @@ export function K8sWorkloads() {
const { toast } = useToast();
const [clusters, setClusters] = useState<any[]>([]);
const [selectedCluster, setSelectedCluster] = useState('');
const [namespaces, setNamespaces] = useState<string[]>([]);
const [selectedNamespace, setSelectedNamespace] = useState('all');
const [deployments, setDeployments] = useState<any[]>([]);
const [pods, setPods] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
@@ -43,6 +45,18 @@ export function K8sWorkloads() {
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 () => {
if (!selectedCluster) return;
setLoading(true);
@@ -60,10 +74,12 @@ export function K8sWorkloads() {
useEffect(() => { loadData(); }, [loadData]);
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) =>
(p.name || '').toLowerCase().includes(search.toLowerCase()),
(p.name || '').toLowerCase().includes(search.toLowerCase()) &&
(selectedNamespace === 'all' || p.namespace === selectedNamespace),
);
const getPodStatusBadge = (status: string) => {
@@ -112,7 +128,7 @@ export function K8sWorkloads() {
<p className="text-sm text-slate-500 mt-1">Deployment 与 Pod 运行状态</p>
</div>
<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">
<SelectValue placeholder="选择集群" />
</SelectTrigger>
@@ -122,6 +138,17 @@ export function K8sWorkloads() {
))}
</SelectContent>
</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}>
{loading ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
</Button>