1.33 日志通用查询
This commit is contained in:
parent
5056b133ca
commit
9225414a87
@ -6,6 +6,7 @@ interface UsePendingApprovalOptions {
|
||||
teams: DeployTeam[];
|
||||
currentTeamId?: number | null; // 当前选中的团队ID
|
||||
currentEnvId?: number | null; // 当前选中的环境ID
|
||||
canApprove?: boolean; // 当前用户是否有审批权限
|
||||
pollingEnabled?: boolean;
|
||||
pollingInterval?: number; // 轮询间隔,默认30秒
|
||||
}
|
||||
@ -20,6 +21,7 @@ export function usePendingApproval({
|
||||
teams,
|
||||
currentTeamId,
|
||||
currentEnvId,
|
||||
canApprove = false,
|
||||
pollingEnabled = true,
|
||||
pollingInterval = 30000 // 默认30秒
|
||||
}: UsePendingApprovalOptions) {
|
||||
@ -41,7 +43,9 @@ export function usePendingApproval({
|
||||
|
||||
// 加载待审批数量 - 使用 useCallback 避免重复创建
|
||||
const loadPendingApprovalCount = useCallback(async () => {
|
||||
if (!pollingEnabled || workflowDefinitionKeys.length === 0) {
|
||||
// 只有当用户有审批权限时才调用接口
|
||||
if (!canApprove || !pollingEnabled || workflowDefinitionKeys.length === 0) {
|
||||
setPendingApprovalCount(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -59,11 +63,18 @@ export function usePendingApproval({
|
||||
// 静默失败,不影响主页面
|
||||
console.error('Failed to load pending approval count:', error);
|
||||
}
|
||||
}, [workflowDefinitionKeys, currentTeamId, currentEnvId, pollingEnabled]);
|
||||
}, [canApprove, workflowDefinitionKeys, currentTeamId, currentEnvId, pollingEnabled]);
|
||||
|
||||
// 轮询待审批数量
|
||||
useEffect(() => {
|
||||
if (!pollingEnabled || workflowDefinitionKeys.length === 0) {
|
||||
// 只有当用户有审批权限时才启动轮询
|
||||
if (!canApprove || !pollingEnabled || workflowDefinitionKeys.length === 0) {
|
||||
// 清除计数并停止轮询
|
||||
setPendingApprovalCount(0);
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,7 +94,7 @@ export function usePendingApproval({
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
};
|
||||
}, [loadPendingApprovalCount, pollingEnabled, workflowDefinitionKeys.length, pollingInterval]);
|
||||
}, [canApprove, loadPendingApprovalCount, pollingEnabled, workflowDefinitionKeys.length, pollingInterval]);
|
||||
|
||||
return {
|
||||
approvalModalOpen,
|
||||
|
||||
@ -55,10 +55,22 @@ const Dashboard: React.FC = () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 计算当前用户是否有审批权限(需要在 usePendingApproval 之前计算)
|
||||
const currentCanApprove = React.useMemo(() => {
|
||||
if (!deploymentData.currentTeam || !deploymentData.currentEnvId) {
|
||||
return false;
|
||||
}
|
||||
const currentEnv = deploymentData.currentTeam.environments.find(
|
||||
env => env.environmentId === deploymentData.currentEnvId
|
||||
);
|
||||
return currentEnv?.canApprove === true;
|
||||
}, [deploymentData.currentTeam, deploymentData.currentEnvId]);
|
||||
|
||||
const approvalData = usePendingApproval({
|
||||
teams: deploymentData.teams,
|
||||
currentTeamId: deploymentData.currentTeamId,
|
||||
currentEnvId: deploymentData.currentEnvId,
|
||||
canApprove: currentCanApprove,
|
||||
pollingEnabled: !deploymentData.loading
|
||||
});
|
||||
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { getServerList } from '@/pages/Resource/Server/List/service';
|
||||
import type { ServerResponse } from '@/pages/Resource/Server/List/types';
|
||||
|
||||
interface DockerRuntimeConfigProps {
|
||||
dockerServerId: number | null;
|
||||
dockerContainerName: string;
|
||||
onDockerServerChange: (serverId: number | null) => void;
|
||||
onDockerContainerNameChange: (containerName: string) => void;
|
||||
}
|
||||
|
||||
export const DockerRuntimeConfig: React.FC<DockerRuntimeConfigProps> = ({
|
||||
dockerServerId,
|
||||
dockerContainerName,
|
||||
onDockerServerChange,
|
||||
onDockerContainerNameChange,
|
||||
}) => {
|
||||
const [servers, setServers] = useState<ServerResponse[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadServers();
|
||||
}, []);
|
||||
|
||||
const loadServers = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await getServerList();
|
||||
setServers(result || []);
|
||||
} catch (error) {
|
||||
console.error('加载服务器列表失败:', error);
|
||||
setServers([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pl-4 border-l-2 border-muted">
|
||||
<div className="space-y-2">
|
||||
<Label>Docker服务器</Label>
|
||||
<Select
|
||||
value={dockerServerId?.toString() || ''}
|
||||
onValueChange={(value) => onDockerServerChange(value ? Number(value) : null)}
|
||||
disabled={loading}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={loading ? '加载中...' : '选择Docker服务器'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{servers.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
{loading ? '加载中...' : '暂无服务器'}
|
||||
</div>
|
||||
) : (
|
||||
servers.map((server) => (
|
||||
<SelectItem key={server.id} value={server.id!.toString()}>
|
||||
{server.serverName} ({server.hostIp})
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>容器名称</Label>
|
||||
<Input
|
||||
placeholder="请输入Docker容器名称"
|
||||
value={dockerContainerName}
|
||||
onChange={(e) => onDockerContainerNameChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,197 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { getExternalSystemList } from '@/pages/Resource/External/List/service';
|
||||
import { getK8sNamespaceList, getK8sDeploymentByNamespace } from '@/pages/Resource/K8s/List/service';
|
||||
|
||||
interface K8sRuntimeConfigProps {
|
||||
k8sSystemId: number | null;
|
||||
k8sNamespaceId: number | null;
|
||||
k8sDeploymentId: number | null;
|
||||
onK8sSystemChange: (systemId: number | null) => void;
|
||||
onK8sNamespaceChange: (namespaceId: number | null) => void;
|
||||
onK8sDeploymentIdChange: (deploymentId: number | null) => void;
|
||||
}
|
||||
|
||||
export const K8sRuntimeConfig: React.FC<K8sRuntimeConfigProps> = ({
|
||||
k8sSystemId,
|
||||
k8sNamespaceId,
|
||||
k8sDeploymentId,
|
||||
onK8sSystemChange,
|
||||
onK8sNamespaceChange,
|
||||
onK8sDeploymentIdChange,
|
||||
}) => {
|
||||
const [k8sSystems, setK8sSystems] = useState<any[]>([]);
|
||||
const [namespaces, setNamespaces] = useState<any[]>([]);
|
||||
const [deployments, setDeployments] = useState<any[]>([]);
|
||||
const [loadingNamespaces, setLoadingNamespaces] = useState(false);
|
||||
const [loadingDeployments, setLoadingDeployments] = useState(false);
|
||||
|
||||
// 加载K8S系统列表
|
||||
useEffect(() => {
|
||||
const loadK8sSystems = async () => {
|
||||
try {
|
||||
const systems = await getExternalSystemList({ type: 'K8S' });
|
||||
setK8sSystems(systems || []);
|
||||
} catch (error) {
|
||||
console.error('加载K8S系统失败:', error);
|
||||
setK8sSystems([]);
|
||||
}
|
||||
};
|
||||
loadK8sSystems();
|
||||
}, []);
|
||||
|
||||
// 加载Namespace列表
|
||||
useEffect(() => {
|
||||
const loadNamespaces = async () => {
|
||||
if (!k8sSystemId) {
|
||||
setNamespaces([]);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingNamespaces(true);
|
||||
try {
|
||||
const data = await getK8sNamespaceList(k8sSystemId);
|
||||
setNamespaces(data || []);
|
||||
} catch (error) {
|
||||
console.error('加载Namespace失败:', error);
|
||||
setNamespaces([]);
|
||||
} finally {
|
||||
setLoadingNamespaces(false);
|
||||
}
|
||||
};
|
||||
loadNamespaces();
|
||||
}, [k8sSystemId]);
|
||||
|
||||
// 加载Deployment列表
|
||||
useEffect(() => {
|
||||
const loadDeployments = async () => {
|
||||
if (!k8sSystemId || !k8sNamespaceId) {
|
||||
setDeployments([]);
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingDeployments(true);
|
||||
try {
|
||||
const data = await getK8sDeploymentByNamespace(k8sSystemId, k8sNamespaceId);
|
||||
setDeployments(data || []);
|
||||
} catch (error) {
|
||||
console.error('加载Deployment失败:', error);
|
||||
setDeployments([]);
|
||||
} finally {
|
||||
setLoadingDeployments(false);
|
||||
}
|
||||
};
|
||||
loadDeployments();
|
||||
}, [k8sSystemId, k8sNamespaceId]);
|
||||
|
||||
return (
|
||||
<div className="space-y-4 p-4 border rounded-lg bg-muted/30">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="h-1 w-1 rounded-full bg-blue-500" />
|
||||
<span className="text-sm font-medium text-muted-foreground">Kubernetes 配置</span>
|
||||
</div>
|
||||
|
||||
{/* K8S系统选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label>K8S系统</Label>
|
||||
<Select
|
||||
value={k8sSystemId?.toString() || ''}
|
||||
onValueChange={(value) => {
|
||||
onK8sSystemChange(Number(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择K8S系统" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{k8sSystems.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
暂无K8S系统
|
||||
</div>
|
||||
) : (
|
||||
k8sSystems.map((system) => (
|
||||
<SelectItem key={system.id} value={system.id.toString()}>
|
||||
{system.name}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Namespace选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label>Namespace</Label>
|
||||
<Select
|
||||
value={k8sNamespaceId?.toString() || ''}
|
||||
onValueChange={(value) => {
|
||||
onK8sNamespaceChange(Number(value));
|
||||
}}
|
||||
disabled={!k8sSystemId || loadingNamespaces}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={loadingNamespaces ? '加载中...' : '选择Namespace'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{loadingNamespaces ? (
|
||||
<div className="p-4 text-center">
|
||||
<Loader2 className="h-4 w-4 animate-spin mx-auto" />
|
||||
</div>
|
||||
) : namespaces.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
{k8sSystemId ? '暂无Namespace' : '请先选择K8S系统'}
|
||||
</div>
|
||||
) : (
|
||||
namespaces.map((ns) => (
|
||||
<SelectItem key={ns.id} value={ns.id.toString()}>
|
||||
{ns.namespaceName}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Deployment选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label>Deployment</Label>
|
||||
<Select
|
||||
value={k8sDeploymentId?.toString() || ''}
|
||||
onValueChange={(value) => {
|
||||
onK8sDeploymentIdChange(Number(value));
|
||||
}}
|
||||
disabled={!k8sNamespaceId || loadingDeployments}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={loadingDeployments ? '加载中...' : '选择Deployment'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{loadingDeployments ? (
|
||||
<div className="p-4 text-center">
|
||||
<Loader2 className="h-4 w-4 animate-spin mx-auto" />
|
||||
</div>
|
||||
) : deployments.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
{k8sNamespaceId ? '暂无Deployment' : '请先选择Namespace'}
|
||||
</div>
|
||||
) : (
|
||||
deployments.map((deployment) => (
|
||||
<SelectItem key={deployment.id} value={deployment.id.toString()}>
|
||||
{deployment.deploymentName}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,114 @@
|
||||
import React from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { K8sRuntimeConfig } from './K8sRuntimeConfig';
|
||||
import { DockerRuntimeConfig } from './DockerRuntimeConfig';
|
||||
import { ServerRuntimeConfig } from './ServerRuntimeConfig';
|
||||
import type { RuntimeType } from '../types';
|
||||
import { RUNTIME_TYPE_OPTIONS } from '../types';
|
||||
|
||||
interface RuntimeConfigSectionProps {
|
||||
runtimeType: RuntimeType | null;
|
||||
onRuntimeTypeChange: (type: RuntimeType | null) => void;
|
||||
// K8S配置
|
||||
k8sSystemId: number | null;
|
||||
k8sNamespaceId: number | null;
|
||||
k8sDeploymentId: number | null;
|
||||
onK8sSystemChange: (systemId: number | null) => void;
|
||||
onK8sNamespaceChange: (namespaceId: number | null) => void;
|
||||
onK8sDeploymentIdChange: (deploymentId: number | null) => void;
|
||||
// Docker配置(预留)
|
||||
dockerServerId?: number | null;
|
||||
dockerContainerName?: string;
|
||||
onDockerServerChange?: (serverId: number | null) => void;
|
||||
onDockerContainerNameChange?: (containerName: string) => void;
|
||||
// Server配置(预留)
|
||||
serverId?: number | null;
|
||||
logQueryCommand?: string;
|
||||
onServerChange?: (serverId: number | null) => void;
|
||||
onLogQueryCommandChange?: (command: string) => void;
|
||||
}
|
||||
|
||||
export const RuntimeConfigSection: React.FC<RuntimeConfigSectionProps> = ({
|
||||
runtimeType,
|
||||
onRuntimeTypeChange,
|
||||
k8sSystemId,
|
||||
k8sNamespaceId,
|
||||
k8sDeploymentId,
|
||||
onK8sSystemChange,
|
||||
onK8sNamespaceChange,
|
||||
onK8sDeploymentIdChange,
|
||||
dockerServerId,
|
||||
dockerContainerName,
|
||||
onDockerServerChange,
|
||||
onDockerContainerNameChange,
|
||||
serverId,
|
||||
logQueryCommand,
|
||||
onServerChange,
|
||||
onLogQueryCommandChange,
|
||||
}) => {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* 运行时类型选择 */}
|
||||
<div className="space-y-2">
|
||||
<Label>运行时类型</Label>
|
||||
<Select
|
||||
value={runtimeType || ''}
|
||||
onValueChange={(value) => {
|
||||
onRuntimeTypeChange(value as RuntimeType);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="选择运行时类型" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{RUNTIME_TYPE_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
选择应用的运行时环境类型,用于日志查询和监控
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 根据运行时类型显示对应的配置 */}
|
||||
{runtimeType === 'K8S' && (
|
||||
<K8sRuntimeConfig
|
||||
k8sSystemId={k8sSystemId}
|
||||
k8sNamespaceId={k8sNamespaceId}
|
||||
k8sDeploymentId={k8sDeploymentId}
|
||||
onK8sSystemChange={onK8sSystemChange}
|
||||
onK8sNamespaceChange={onK8sNamespaceChange}
|
||||
onK8sDeploymentIdChange={onK8sDeploymentIdChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
{runtimeType === 'DOCKER' && (
|
||||
<DockerRuntimeConfig
|
||||
dockerServerId={dockerServerId || null}
|
||||
dockerContainerName={dockerContainerName || ''}
|
||||
onDockerServerChange={onDockerServerChange || (() => {})}
|
||||
onDockerContainerNameChange={onDockerContainerNameChange || (() => {})}
|
||||
/>
|
||||
)}
|
||||
|
||||
{runtimeType === 'SERVER' && (
|
||||
<ServerRuntimeConfig
|
||||
serverId={serverId || null}
|
||||
logQueryCommand={logQueryCommand || ''}
|
||||
onServerChange={onServerChange || (() => {})}
|
||||
onLogQueryCommandChange={onLogQueryCommandChange || (() => {})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,88 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { getServerList } from '@/pages/Resource/Server/List/service';
|
||||
import type { ServerResponse } from '@/pages/Resource/Server/List/types';
|
||||
|
||||
interface ServerRuntimeConfigProps {
|
||||
serverId: number | null;
|
||||
logQueryCommand: string;
|
||||
onServerChange: (serverId: number | null) => void;
|
||||
onLogQueryCommandChange: (command: string) => void;
|
||||
}
|
||||
|
||||
export const ServerRuntimeConfig: React.FC<ServerRuntimeConfigProps> = ({
|
||||
serverId,
|
||||
logQueryCommand,
|
||||
onServerChange,
|
||||
onLogQueryCommandChange,
|
||||
}) => {
|
||||
const [servers, setServers] = useState<ServerResponse[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadServers();
|
||||
}, []);
|
||||
|
||||
const loadServers = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await getServerList();
|
||||
setServers(result || []);
|
||||
} catch (error) {
|
||||
console.error('加载服务器列表失败:', error);
|
||||
setServers([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4 pl-4 border-l-2 border-muted">
|
||||
<div className="space-y-2">
|
||||
<Label>服务器</Label>
|
||||
<Select
|
||||
value={serverId?.toString() || ''}
|
||||
onValueChange={(value) => onServerChange(value ? Number(value) : null)}
|
||||
disabled={loading}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={loading ? '加载中...' : '选择服务器'} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{servers.length === 0 ? (
|
||||
<div className="p-4 text-center text-sm text-muted-foreground">
|
||||
{loading ? '加载中...' : '暂无服务器'}
|
||||
</div>
|
||||
) : (
|
||||
servers.map((server) => (
|
||||
<SelectItem key={server.id} value={server.id!.toString()}>
|
||||
{server.serverName} ({server.hostIp})
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>日志查询命令</Label>
|
||||
<Input
|
||||
placeholder="请输入日志查询命令,如:tail -f /var/log/app.log"
|
||||
value={logQueryCommand}
|
||||
onChange={(e) => onLogQueryCommandChange(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
用于查询应用日志的Shell命令
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -26,8 +26,10 @@ import type {
|
||||
TeamApplication,
|
||||
Application,
|
||||
BuildType,
|
||||
RuntimeType,
|
||||
} from '../types';
|
||||
import { BUILD_TYPE_OPTIONS } from '../types';
|
||||
import { RuntimeConfigSection } from './RuntimeConfigSection';
|
||||
import type { RepositoryBranchResponse } from '@/pages/Resource/Git/List/types';
|
||||
import type { WorkflowDefinition } from '@/pages/Workflow/Definition/List/types';
|
||||
import { getExternalSystemList } from '@/pages/Resource/External/List/service';
|
||||
@ -57,9 +59,18 @@ interface TeamApplicationDialogProps {
|
||||
workflowDefinitionId: number | null;
|
||||
sourceGitSystemId: number | null;
|
||||
sourceGitProjectId: number | null;
|
||||
targetGitSystemId: number | null; // 🆕 目标Git系统ID
|
||||
targetGitProjectId: number | null; // 🆕 目标Git项目ID
|
||||
targetBranch: string; // 🆕 目标分支
|
||||
targetGitSystemId: number | null;
|
||||
targetGitProjectId: number | null;
|
||||
targetBranch: string;
|
||||
// 运行时配置
|
||||
runtimeType: RuntimeType | null;
|
||||
k8sSystemId: number | null;
|
||||
k8sNamespaceId: number | null;
|
||||
k8sDeploymentId: number | null;
|
||||
dockerServerId: number | null;
|
||||
dockerContainerName: string;
|
||||
serverId: number | null;
|
||||
logQueryCommand: string;
|
||||
}) => Promise<void>;
|
||||
onLoadBranches: (appId: number, app: Application) => Promise<RepositoryBranchResponse[]>;
|
||||
onLoadJenkinsJobs: (systemId: number) => Promise<any[]>;
|
||||
@ -97,6 +108,15 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
targetGitSystemId: null as number | null, // 目标Git系统ID
|
||||
targetGitProjectId: null as number | null, // 目标Git项目ID
|
||||
targetBranch: '', // 目标分支名称
|
||||
// 运行时配置
|
||||
runtimeType: null as RuntimeType | null, // 运行时类型
|
||||
k8sSystemId: null as number | null, // K8S系统ID
|
||||
k8sNamespaceId: null as number | null, // K8S命名空间ID
|
||||
k8sDeploymentId: null as number | null, // K8S Deployment ID
|
||||
dockerServerId: null as number | null, // Docker服务器ID
|
||||
dockerContainerName: '', // Docker容器名称
|
||||
serverId: null as number | null, // 服务器ID
|
||||
logQueryCommand: '', // 日志查询命令
|
||||
});
|
||||
|
||||
// 加载状态
|
||||
@ -137,6 +157,15 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
targetGitSystemId: application.targetGitSystemId || null,
|
||||
targetGitProjectId: application.targetGitProjectId || null,
|
||||
targetBranch: application.targetBranch || '',
|
||||
// 运行时配置
|
||||
runtimeType: application.runtimeType || null,
|
||||
k8sSystemId: application.k8sSystemId || null,
|
||||
k8sNamespaceId: application.k8sNamespaceId || null,
|
||||
k8sDeploymentId: application.k8sDeploymentId || null,
|
||||
dockerServerId: application.dockerServerId || null,
|
||||
dockerContainerName: application.dockerContainerName || '',
|
||||
serverId: application.serverId || null,
|
||||
logQueryCommand: application.logQueryCommand || '',
|
||||
});
|
||||
|
||||
// 加载源仓库项目
|
||||
@ -184,6 +213,15 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
targetGitSystemId: null,
|
||||
targetGitProjectId: null,
|
||||
targetBranch: '',
|
||||
// 运行时配置
|
||||
runtimeType: null,
|
||||
k8sSystemId: null,
|
||||
k8sNamespaceId: null,
|
||||
k8sDeploymentId: null,
|
||||
dockerServerId: null,
|
||||
dockerContainerName: '',
|
||||
serverId: null,
|
||||
logQueryCommand: '',
|
||||
});
|
||||
setBranches([]);
|
||||
setJenkinsJobs([]);
|
||||
@ -279,6 +317,15 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
targetGitSystemId: null,
|
||||
targetGitProjectId: null,
|
||||
targetBranch: '',
|
||||
// 保留运行时配置
|
||||
runtimeType: formData.runtimeType,
|
||||
k8sSystemId: formData.k8sSystemId,
|
||||
k8sNamespaceId: formData.k8sNamespaceId,
|
||||
k8sDeploymentId: formData.k8sDeploymentId,
|
||||
dockerServerId: formData.dockerServerId,
|
||||
dockerContainerName: formData.dockerContainerName,
|
||||
serverId: formData.serverId,
|
||||
logQueryCommand: formData.logQueryCommand,
|
||||
});
|
||||
// 清空分支列表(分支现在基于代码源,不基于应用)
|
||||
setBranches([]);
|
||||
@ -435,6 +482,34 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
// 运行时配置验证
|
||||
if (formData.runtimeType === 'K8S') {
|
||||
if (!formData.k8sSystemId) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: '请选择K8S系统',
|
||||
description: '已选择K8S运行时类型,必须配置K8S系统',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!formData.k8sNamespaceId) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: '请选择Namespace',
|
||||
description: '已选择K8S运行时类型,必须配置Namespace',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!formData.k8sDeploymentId) {
|
||||
toast({
|
||||
variant: 'destructive',
|
||||
title: '请选择Deployment',
|
||||
description: '已选择K8S运行时类型,必须配置Deployment',
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setSaving(true);
|
||||
try {
|
||||
await onSave({
|
||||
@ -450,6 +525,15 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
targetGitSystemId: formData.targetGitSystemId,
|
||||
targetGitProjectId: formData.targetGitProjectId,
|
||||
targetBranch: formData.targetBranch,
|
||||
// 运行时配置
|
||||
runtimeType: formData.runtimeType,
|
||||
k8sSystemId: formData.k8sSystemId,
|
||||
k8sNamespaceId: formData.k8sNamespaceId,
|
||||
k8sDeploymentId: formData.k8sDeploymentId,
|
||||
dockerServerId: formData.dockerServerId,
|
||||
dockerContainerName: formData.dockerContainerName,
|
||||
serverId: formData.serverId,
|
||||
logQueryCommand: formData.logQueryCommand,
|
||||
});
|
||||
|
||||
toast({
|
||||
@ -783,6 +867,48 @@ const TeamApplicationDialog: React.FC<TeamApplicationDialogProps> = ({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* 运行时配置 */}
|
||||
<RuntimeConfigSection
|
||||
runtimeType={formData.runtimeType}
|
||||
onRuntimeTypeChange={(type) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
runtimeType: type,
|
||||
// 切换类型时清空所有运行时配置
|
||||
k8sSystemId: null,
|
||||
k8sNamespaceId: null,
|
||||
k8sDeploymentId: null,
|
||||
dockerServerId: null,
|
||||
dockerContainerName: '',
|
||||
serverId: null,
|
||||
logQueryCommand: '',
|
||||
});
|
||||
}}
|
||||
k8sSystemId={formData.k8sSystemId}
|
||||
k8sNamespaceId={formData.k8sNamespaceId}
|
||||
k8sDeploymentId={formData.k8sDeploymentId}
|
||||
onK8sSystemChange={(systemId) => setFormData(prev => ({
|
||||
...prev,
|
||||
k8sSystemId: systemId,
|
||||
k8sNamespaceId: null,
|
||||
k8sDeploymentId: null
|
||||
}))}
|
||||
onK8sNamespaceChange={(namespaceId) => setFormData(prev => ({
|
||||
...prev,
|
||||
k8sNamespaceId: namespaceId,
|
||||
k8sDeploymentId: null
|
||||
}))}
|
||||
onK8sDeploymentIdChange={(deploymentId) => setFormData(prev => ({ ...prev, k8sDeploymentId: deploymentId }))}
|
||||
dockerServerId={formData.dockerServerId}
|
||||
dockerContainerName={formData.dockerContainerName}
|
||||
onDockerServerChange={(serverId) => setFormData(prev => ({ ...prev, dockerServerId: serverId }))}
|
||||
onDockerContainerNameChange={(containerName) => setFormData(prev => ({ ...prev, dockerContainerName: containerName }))}
|
||||
serverId={formData.serverId}
|
||||
logQueryCommand={formData.logQueryCommand}
|
||||
onServerChange={(serverId) => setFormData(prev => ({ ...prev, serverId: serverId }))}
|
||||
onLogQueryCommandChange={(command) => setFormData(prev => ({ ...prev, logQueryCommand: command }))}
|
||||
/>
|
||||
</div>
|
||||
</DialogBody>
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { PaginatedTable, type ColumnDef, type PaginatedTableRef } from '@/components/ui/paginated-table';
|
||||
import { Plus, Edit, Trash2, GitBranch } from 'lucide-react';
|
||||
import { Plus, Edit, Trash2, GitBranch, Hammer, Box, Container, Server } from 'lucide-react';
|
||||
import type { Environment } from '@/pages/Deploy/Environment/List/types';
|
||||
import type { TeamApplication, Application } from '../types';
|
||||
import type { WorkflowDefinition } from '@/pages/Workflow/Definition/List/types';
|
||||
@ -129,9 +129,18 @@ export const TeamApplicationManageDialog: React.FC<
|
||||
workflowDefinitionId: number | null;
|
||||
sourceGitSystemId: number | null;
|
||||
sourceGitProjectId: number | null;
|
||||
targetGitSystemId: number | null; // 🆕 目标Git系统ID
|
||||
targetGitProjectId: number | null; // 🆕 目标Git项目ID
|
||||
targetBranch: string; // 🆕 目标分支
|
||||
targetGitSystemId: number | null;
|
||||
targetGitProjectId: number | null;
|
||||
targetBranch: string;
|
||||
// 运行时配置
|
||||
runtimeType: 'K8S' | 'DOCKER' | 'SERVER' | null;
|
||||
k8sSystemId: number | null;
|
||||
k8sNamespaceId: number | null;
|
||||
k8sDeploymentId: number | null;
|
||||
dockerServerId: number | null;
|
||||
dockerContainerName: string;
|
||||
serverId: number | null;
|
||||
logQueryCommand: string;
|
||||
}) => {
|
||||
if (!editingEnvironment) return;
|
||||
|
||||
@ -147,10 +156,19 @@ export const TeamApplicationManageDialog: React.FC<
|
||||
workflowDefinitionId: data.workflowDefinitionId || undefined,
|
||||
sourceGitSystemId: data.sourceGitSystemId || undefined,
|
||||
sourceGitProjectId: data.sourceGitProjectId || undefined,
|
||||
// 🆕 目标Git相关字段
|
||||
// 目标Git相关字段
|
||||
targetGitSystemId: data.targetGitSystemId || undefined,
|
||||
targetGitProjectId: data.targetGitProjectId || undefined,
|
||||
targetBranch: data.targetBranch || undefined,
|
||||
// 运行时配置字段
|
||||
runtimeType: data.runtimeType || undefined,
|
||||
k8sSystemId: data.k8sSystemId || undefined,
|
||||
k8sNamespaceId: data.k8sNamespaceId || undefined,
|
||||
k8sDeploymentId: data.k8sDeploymentId || undefined,
|
||||
dockerServerId: data.dockerServerId || undefined,
|
||||
dockerContainerName: data.dockerContainerName || undefined,
|
||||
serverId: data.serverId || undefined,
|
||||
logQueryCommand: data.logQueryCommand || undefined,
|
||||
};
|
||||
|
||||
if (appDialogMode === 'edit' && data.id) {
|
||||
@ -295,26 +313,50 @@ export const TeamApplicationManageDialog: React.FC<
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'buildType',
|
||||
title: '构建类型',
|
||||
width: '120px',
|
||||
render: (_, app) =>
|
||||
app.buildType === 'JENKINS' ? 'Jenkins构建' : app.buildType === 'NATIVE' ? '脚本部署' : '-',
|
||||
key: 'buildConfig',
|
||||
title: '构建配置',
|
||||
width: '250px',
|
||||
render: (_, app) => {
|
||||
if (!app.buildType) {
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
}
|
||||
|
||||
// Jenkins 构建
|
||||
if (app.buildType === 'JENKINS') {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Hammer className="h-3.5 w-3.5 text-blue-500" />
|
||||
<span className="inline-flex items-center rounded-md bg-blue-50 px-2 py-0.5 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10">
|
||||
Jenkins构建
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground pl-5">
|
||||
{app.deploySystemName || '-'}
|
||||
</div>
|
||||
{app.deployJob && (
|
||||
<div className="text-xs text-muted-foreground pl-5 truncate" title={app.deployJob}>
|
||||
{app.deployJob}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 脚本部署
|
||||
if (app.buildType === 'NATIVE') {
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Hammer className="h-3.5 w-3.5 text-green-500" />
|
||||
<span className="inline-flex items-center rounded-md bg-green-50 px-2 py-0.5 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-700/10">
|
||||
脚本部署
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
},
|
||||
{
|
||||
key: 'deploySystemName',
|
||||
title: 'Jenkins系统',
|
||||
width: '150px',
|
||||
render: (_, app) =>
|
||||
app.buildType === 'JENKINS'
|
||||
? (app.deploySystemName || (app.deploySystemId ? `系统 ${app.deploySystemId}` : '-'))
|
||||
: '-',
|
||||
},
|
||||
{
|
||||
key: 'deployJob',
|
||||
title: 'Jenkins Job',
|
||||
width: '150px',
|
||||
render: (_, app) => (app.buildType === 'JENKINS' ? (app.deployJob || '-') : '-'),
|
||||
},
|
||||
{
|
||||
key: 'workflowDefinitionName',
|
||||
@ -323,6 +365,85 @@ export const TeamApplicationManageDialog: React.FC<
|
||||
width: '180px',
|
||||
render: (value) => value || '-',
|
||||
},
|
||||
{
|
||||
key: 'runtimeConfig',
|
||||
title: '运行时配置',
|
||||
width: '250px',
|
||||
render: (_, app) => {
|
||||
if (!app.runtimeType) {
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
}
|
||||
|
||||
// K8S 运行时配置
|
||||
if (app.runtimeType === 'K8S') {
|
||||
const k8sPath = [
|
||||
app.k8sSystemName,
|
||||
app.k8sNamespaceName,
|
||||
app.k8sDeploymentName
|
||||
].filter(Boolean).join(' / ') || '-';
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Box className="h-3.5 w-3.5 text-purple-500" />
|
||||
<span className="inline-flex items-center rounded-md bg-purple-50 px-2 py-0.5 text-xs font-medium text-purple-700 ring-1 ring-inset ring-purple-700/10">
|
||||
Kubernetes
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground pl-5 truncate" title={k8sPath}>
|
||||
{k8sPath}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Docker 运行时配置
|
||||
if (app.runtimeType === 'DOCKER') {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Container className="h-3.5 w-3.5 text-orange-500" />
|
||||
<span className="inline-flex items-center rounded-md bg-orange-50 px-2 py-0.5 text-xs font-medium text-orange-700 ring-1 ring-inset ring-orange-700/10">
|
||||
Docker
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground pl-5">
|
||||
{app.dockerServerName || '-'}
|
||||
</div>
|
||||
{app.dockerContainerName && (
|
||||
<div className="text-xs text-muted-foreground pl-5 truncate" title={app.dockerContainerName}>
|
||||
{app.dockerContainerName}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Server 运行时配置
|
||||
if (app.runtimeType === 'SERVER') {
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Server className="h-3.5 w-3.5 text-gray-500" />
|
||||
<span className="inline-flex items-center rounded-md bg-gray-50 px-2 py-0.5 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-700/10">
|
||||
服务器
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground pl-5">
|
||||
{app.serverName || '-'}
|
||||
</div>
|
||||
{app.logQueryCommand && (
|
||||
<div className="text-xs text-muted-foreground pl-5 truncate" title={app.logQueryCommand}>
|
||||
{app.logQueryCommand}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <span className="text-muted-foreground">-</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
title: '操作',
|
||||
|
||||
@ -172,6 +172,20 @@ export const BUILD_TYPE_OPTIONS = [
|
||||
{ value: 'NATIVE', label: '脚本部署' },
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* 运行时类型枚举
|
||||
*/
|
||||
export type RuntimeType = 'K8S' | 'DOCKER' | 'SERVER';
|
||||
|
||||
/**
|
||||
* 运行时类型选项
|
||||
*/
|
||||
export const RUNTIME_TYPE_OPTIONS = [
|
||||
{ value: 'K8S', label: 'Kubernetes' },
|
||||
{ value: 'DOCKER', label: 'Docker' },
|
||||
{ value: 'SERVER', label: '服务器' },
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* 团队应用关联响应
|
||||
*/
|
||||
@ -189,6 +203,16 @@ export interface TeamApplication extends BaseResponse {
|
||||
targetGitSystemId?: number; // 目标Git系统ID(仅SYNC_MODE)
|
||||
targetGitProjectId?: number; // 目标Git项目ID
|
||||
targetBranch?: string; // 目标分支名称
|
||||
// 运行时配置字段
|
||||
runtimeType?: RuntimeType; // 运行时类型
|
||||
k8sSystemId?: number; // K8S系统ID
|
||||
k8sNamespaceId?: number; // K8S命名空间ID
|
||||
k8sDeploymentId?: number; // K8S Deployment ID
|
||||
dockerServerId?: number; // Docker服务器ID
|
||||
dockerContainerName?: string; // Docker容器名称
|
||||
serverId?: number; // 服务器ID
|
||||
logQueryCommand?: string; // 日志查询命令(Server类型使用)
|
||||
// 关联数据
|
||||
teamName?: string;
|
||||
applicationName?: string;
|
||||
applicationCode?: string;
|
||||
@ -199,6 +223,12 @@ export interface TeamApplication extends BaseResponse {
|
||||
sourceGitProjectName?: string; // 源Git项目名称
|
||||
targetGitSystemName?: string; // 目标Git系统名称
|
||||
targetGitProjectName?: string; // 目标Git项目名称
|
||||
// 运行时配置关联数据
|
||||
k8sSystemName?: string; // K8S系统名称
|
||||
k8sNamespaceName?: string; // K8S命名空间名称
|
||||
k8sDeploymentName?: string; // K8S Deployment名称
|
||||
dockerServerName?: string; // Docker服务器名称
|
||||
serverName?: string; // 服务器名称
|
||||
}
|
||||
|
||||
/**
|
||||
@ -218,5 +248,14 @@ export interface TeamApplicationRequest {
|
||||
targetGitSystemId?: number; // 目标Git系统ID(仅SYNC_MODE)
|
||||
targetGitProjectId?: number; // 目标Git项目ID
|
||||
targetBranch?: string; // 目标分支名称
|
||||
// 运行时配置字段
|
||||
runtimeType?: RuntimeType; // 运行时类型
|
||||
k8sSystemId?: number; // K8S系统ID
|
||||
k8sNamespaceId?: number; // K8S命名空间ID
|
||||
k8sDeploymentId?: number; // K8S Deployment ID
|
||||
dockerServerId?: number; // Docker服务器ID
|
||||
dockerContainerName?: string; // Docker容器名称
|
||||
serverId?: number; // 服务器ID
|
||||
logQueryCommand?: string; // 日志查询命令(Server类型使用)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user