重构消息通知弹窗
This commit is contained in:
parent
72a08a7950
commit
0c8d8f187c
@ -71,14 +71,14 @@ const getLayoutedElements = (
|
|||||||
const dagreGraph = new dagre.graphlib.Graph();
|
const dagreGraph = new dagre.graphlib.Graph();
|
||||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||||
|
|
||||||
const nodeWidth = 200;
|
const nodeWidth = 180;
|
||||||
const nodeHeight = 150;
|
const nodeHeight = 120;
|
||||||
|
|
||||||
const isHorizontal = direction === 'LR';
|
const isHorizontal = direction === 'LR';
|
||||||
dagreGraph.setGraph({
|
dagreGraph.setGraph({
|
||||||
rankdir: direction,
|
rankdir: direction,
|
||||||
nodesep: isHorizontal ? 80 : 100, // 同一层节点间距:横向80,纵向100
|
nodesep: isHorizontal ? 90 : 120, // 同一层节点间距:横向80,纵向100
|
||||||
ranksep: isHorizontal ? 150 : 120, // 层级间距:横向150,纵向120
|
ranksep: isHorizontal ? 170 : 160, // 层级间距:横向150,纵向120
|
||||||
marginx: 40,
|
marginx: 40,
|
||||||
marginy: 40,
|
marginy: 40,
|
||||||
align: 'UL', // 对齐方式
|
align: 'UL', // 对齐方式
|
||||||
@ -138,7 +138,7 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
|||||||
const nodeContent = (
|
const nodeContent = (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'px-4 py-3 rounded-lg w-[190px] min-h-[140px] transition-all duration-200 relative overflow-hidden flex flex-col',
|
'px-4 py-3 rounded-lg w-[180px] min-h-[120px] transition-all duration-200 relative overflow-hidden flex flex-col',
|
||||||
isNotStarted && 'border-2 border-dashed bg-gradient-to-br from-slate-50 to-gray-50',
|
isNotStarted && 'border-2 border-dashed bg-gradient-to-br from-slate-50 to-gray-50',
|
||||||
!isNotStarted && 'border-2 border-solid shadow-lg hover:shadow-2xl bg-white',
|
!isNotStarted && 'border-2 border-solid shadow-lg hover:shadow-2xl bg-white',
|
||||||
isRunning && 'ring-2 ring-blue-400 ring-opacity-60 shadow-blue-200',
|
isRunning && 'ring-2 ring-blue-400 ring-opacity-60 shadow-blue-200',
|
||||||
@ -168,7 +168,7 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
|||||||
{/* 节点状态 - 使用徽章样式 */}
|
{/* 节点状态 - 使用徽章样式 */}
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-bold mb-2",
|
"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-bold mb-1",
|
||||||
isRunning && "animate-pulse"
|
isRunning && "animate-pulse"
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
@ -190,21 +190,21 @@ const CustomFlowNode: React.FC<any> = ({ data }) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 弹性空间,让底部提示始终在底部 */}
|
{/* 弹性空间,让底部提示始终在底部 */}
|
||||||
<div className="flex-1 min-h-[4px]" />
|
<div className="flex-1 min-h-[2px]" />
|
||||||
|
|
||||||
{/* 错误提示 - 增强样式 */}
|
{/* 错误提示 - 增强样式 */}
|
||||||
{hasFailed && errorMessage && (
|
{hasFailed && errorMessage && (
|
||||||
<div className="flex items-center gap-1.5 px-2 py-1 rounded bg-red-50 border border-red-200">
|
<div className="flex items-center gap-1 text-xs text-red-600">
|
||||||
<AlertCircle className="h-3.5 w-3.5 text-red-600 flex-shrink-0" />
|
<AlertCircle className="h-3.5 w-3.5 text-red-600 flex-shrink-0" />
|
||||||
<span className="text-xs font-medium text-red-700">查看错误</span>
|
<span className="font-medium">查看错误</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 查看日志提示 - 增强样式 */}
|
{/* 查看日志提示 - 增强样式 */}
|
||||||
{canViewLog && !hasFailed && (
|
{canViewLog && !hasFailed && (
|
||||||
<div className="flex items-center gap-1.5 px-2 py-1 rounded bg-blue-50 border border-blue-200">
|
<div className="flex items-center gap-1 text-xs text-blue-600">
|
||||||
<FileText className="h-3.5 w-3.5 text-blue-600 flex-shrink-0" />
|
<FileText className="h-3.5 w-3.5 text-blue-600 flex-shrink-0" />
|
||||||
<span className="text-xs font-medium text-blue-700">点击查看日志</span>
|
<span className="font-medium">查看日志</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -675,7 +675,7 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
|||||||
id: edge.id || `edge-${source}-${target}-${index}`,
|
id: edge.id || `edge-${source}-${target}-${index}`,
|
||||||
source,
|
source,
|
||||||
target,
|
target,
|
||||||
type: 'smoothstep', // 使用平滑曲线类型
|
type: layoutDirection === 'TB' ? 'step' : 'smoothstep', // TB用直角折线,LR用平滑曲线
|
||||||
animated,
|
animated,
|
||||||
style: {
|
style: {
|
||||||
stroke: strokeColor,
|
stroke: strokeColor,
|
||||||
@ -690,7 +690,7 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}, [flowData, nodeInstanceMap, visibleNodeIds]);
|
}, [flowData, nodeInstanceMap, visibleNodeIds, layoutDirection]);
|
||||||
|
|
||||||
// 应用自动布局
|
// 应用自动布局
|
||||||
const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => {
|
const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => {
|
||||||
@ -786,7 +786,7 @@ export const DeployFlowGraphModal: React.FC<DeployFlowGraphModalProps> = ({
|
|||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
fitView
|
fitView
|
||||||
className="bg-muted/10"
|
className="bg-muted/10"
|
||||||
fitViewOptions={{ padding: 0.15, maxZoom: 1.2, minZoom: 0.3 }}
|
fitViewOptions={{ padding: 0.22, maxZoom: 1.2, minZoom: 0.3 }}
|
||||||
nodesDraggable={false}
|
nodesDraggable={false}
|
||||||
nodesConnectable={false}
|
nodesConnectable={false}
|
||||||
elementsSelectable={true}
|
elementsSelectable={true}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useMemo, useCallback } from 'react';
|
import React, { useState, useMemo, useCallback } from 'react';
|
||||||
import { BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath, useReactFlow } from '@xyflow/react';
|
import { BaseEdge, EdgeLabelRenderer, EdgeProps, getBezierPath, useReactFlow } from '@xyflow/react';
|
||||||
import type { FlowNode, ControlPoint } from '../types';
|
import type { FlowNode, ControlPoint } from '../types';
|
||||||
import { convertToDisplayName } from '@/utils/workflow/variableConversion';
|
import { convertToDisplayName } from '@/utils/workflow/variableConversion';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自定义边组件
|
* 自定义边组件
|
||||||
* 支持hover高亮、样式优化和拖动改变路径
|
* 使用简单贝塞尔曲线 + 单控制点,支持 hover 高亮和拖动调整路径
|
||||||
*/
|
*/
|
||||||
const CustomEdge: React.FC<EdgeProps> = ({
|
const CustomEdge: React.FC<EdgeProps> = ({
|
||||||
id,
|
id,
|
||||||
@ -25,9 +25,6 @@ const CustomEdge: React.FC<EdgeProps> = ({
|
|||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const { getNodes, setEdges, screenToFlowPosition } = useReactFlow();
|
const { getNodes, setEdges, screenToFlowPosition } = useReactFlow();
|
||||||
|
|
||||||
// 从 data 中获取自定义的控制点
|
|
||||||
const customControlPoint = data?.controlPoint as ControlPoint | undefined;
|
|
||||||
|
|
||||||
// 将 label 中的 UUID 转换为节点名
|
// 将 label 中的 UUID 转换为节点名
|
||||||
const displayLabel = useMemo(() => {
|
const displayLabel = useMemo(() => {
|
||||||
if (!label || typeof label !== 'string') {
|
if (!label || typeof label !== 'string') {
|
||||||
@ -38,77 +35,98 @@ const CustomEdge: React.FC<EdgeProps> = ({
|
|||||||
return convertToDisplayName(label, allNodes);
|
return convertToDisplayName(label, allNodes);
|
||||||
}, [label, getNodes]);
|
}, [label, getNodes]);
|
||||||
|
|
||||||
// 计算三个拐点(vertices):优先用数据中的 vertices;否则从旧的 controlPoint 推导;最后用默认1/4、1/2、3/4
|
// 计算单个控制点:优先使用保存的 controlPoint,否则使用智能默认位置
|
||||||
const vertices: ControlPoint[] = useMemo(() => {
|
const controlPoint: ControlPoint = useMemo(() => {
|
||||||
const dataVertices = (data as any)?.vertices as ControlPoint[] | undefined;
|
const savedCP = (data as any)?.controlPoint as ControlPoint | undefined;
|
||||||
if (Array.isArray(dataVertices) && dataVertices.length === 3) return dataVertices;
|
if (savedCP) return savedCP;
|
||||||
const cp = customControlPoint;
|
|
||||||
if (cp) {
|
|
||||||
return [
|
|
||||||
{ x: (sourceX + cp.x) / 2, y: (sourceY + cp.y) / 2 },
|
|
||||||
{ x: cp.x, y: cp.y },
|
|
||||||
{ x: (cp.x + targetX) / 2, y: (cp.y + targetY) / 2 },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
{ x: sourceX + (targetX - sourceX) * 0.25, y: sourceY + (targetY - sourceY) * 0.25 },
|
|
||||||
{ x: (sourceX + targetX) / 2, y: (sourceY + targetY) / 2 },
|
|
||||||
{ x: sourceX + (targetX - sourceX) * 0.75, y: sourceY + (targetY - sourceY) * 0.75 },
|
|
||||||
];
|
|
||||||
}, [data, customControlPoint, sourceX, sourceY, targetX, targetY]);
|
|
||||||
|
|
||||||
// 若数据中缺少 vertices,则在首次渲染时写回,确保后续保存能持久化
|
// 智能默认位置:根据源和目标位置关系计算
|
||||||
|
const dx = targetX - sourceX;
|
||||||
|
const dy = targetY - sourceY;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
|
||||||
|
// 控制点在中点位置,垂直偏移距离的 20%,方向取决于节点位置关系
|
||||||
|
const midX = (sourceX + targetX) / 2;
|
||||||
|
const midY = (sourceY + targetY) / 2;
|
||||||
|
|
||||||
|
// 计算垂直方向的偏移(顺时针 90 度)
|
||||||
|
const offsetRatio = 0.2;
|
||||||
|
const offsetX = -dy / distance * distance * offsetRatio;
|
||||||
|
const offsetY = dx / distance * distance * offsetRatio;
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: midX + offsetX,
|
||||||
|
y: midY + offsetY,
|
||||||
|
};
|
||||||
|
}, [data, sourceX, sourceY, targetX, targetY]);
|
||||||
|
|
||||||
|
// 若数据中缺少 controlPoint,则在首次渲染时写回
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const hasDataVertices = Array.isArray((data as any)?.vertices) && (data as any).vertices.length === 3;
|
if (!(data as any)?.controlPoint) {
|
||||||
if (!hasDataVertices) {
|
setEdges((eds) => eds.map(ed =>
|
||||||
setEdges((eds) => eds.map(ed => ed.id === id ? { ...ed, data: { ...ed.data, vertices } } : ed));
|
ed.id === id ? { ...ed, data: { ...ed.data, controlPoint } } : ed
|
||||||
|
));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 生成路径:使用 Catmull-Rom -> Cubic Bezier 插值,曲线严格经过拐点
|
// 使用 Catmull-Rom 样条曲线,确保曲线严格经过控制点且平滑
|
||||||
const [edgePath, labelX, labelY] = useMemo(() => {
|
const [edgePath, labelX, labelY] = useMemo(() => {
|
||||||
// 拼接点序列:起点、3个拐点、终点
|
// Catmull-Rom 样条曲线会经过所有关键点
|
||||||
const pts = [
|
// 转换为三次贝塞尔曲线:需要计算切线
|
||||||
{ x: sourceX, y: sourceY },
|
|
||||||
vertices[0],
|
// 点序列:起点 -> 控制点 -> 终点
|
||||||
vertices[1],
|
const p0 = { x: sourceX, y: sourceY };
|
||||||
vertices[2],
|
const p1 = controlPoint;
|
||||||
{ x: targetX, y: targetY },
|
const p2 = { x: targetX, y: targetY };
|
||||||
];
|
|
||||||
// Catmull-Rom 转三次贝塞尔:C1 = P0 + (P1 - P_{-1})/6, C2 = P1 - (P2 - P0)/6
|
// 为了使用 Catmull-Rom,需要虚拟的前后点(用于计算切线)
|
||||||
const segs: string[] = [];
|
// 虚拟前点:起点的镜像延伸
|
||||||
for (let i = 0; i < pts.length - 1; i++) {
|
const p_1 = {
|
||||||
const p_1 = i === 0 ? pts[i] : pts[i - 1];
|
x: p0.x - (p1.x - p0.x) * 0.3,
|
||||||
const p0 = pts[i];
|
y: p0.y - (p1.y - p0.y) * 0.3,
|
||||||
const p1 = pts[i + 1];
|
};
|
||||||
const p2 = i + 2 < pts.length ? pts[i + 2] : p1;
|
|
||||||
const c1x = p0.x + (p1.x - p_1.x) / 6;
|
// 虚拟后点:终点的镜像延伸
|
||||||
const c1y = p0.y + (p1.y - p_1.y) / 6;
|
const p3 = {
|
||||||
const c2x = p1.x - (p2.x - p0.x) / 6;
|
x: p2.x + (p2.x - p1.x) * 0.3,
|
||||||
const c2y = p1.y - (p2.y - p0.y) / 6;
|
y: p2.y + (p2.y - p1.y) * 0.3,
|
||||||
segs.push(`C ${c1x},${c1y} ${c2x},${c2y} ${p1.x},${p1.y}`);
|
};
|
||||||
}
|
|
||||||
const path = `M ${pts[0].x},${pts[0].y} ${segs.join(' ')}`;
|
// Catmull-Rom 转贝塞尔:第一段(p0 -> p1)
|
||||||
// 标签显示在第二个拐点
|
// 切线: t0 = (p1 - p_1) / 2, t1 = (p2 - p0) / 2
|
||||||
return [path, vertices[1].x, vertices[1].y] as [string, number, number];
|
const c1x = p0.x + (p1.x - p_1.x) / 6;
|
||||||
}, [vertices, sourceX, sourceY, targetX, targetY]);
|
const c1y = p0.y + (p1.y - p_1.y) / 6;
|
||||||
|
const c2x = p1.x - (p2.x - p0.x) / 6;
|
||||||
|
const c2y = p1.y - (p2.y - p0.y) / 6;
|
||||||
|
|
||||||
|
// Catmull-Rom 转贝塞尔:第二段(p1 -> p2)
|
||||||
|
// 切线: t0 = (p2 - p0) / 2, t1 = (p3 - p1) / 2
|
||||||
|
const c3x = p1.x + (p2.x - p0.x) / 6;
|
||||||
|
const c3y = p1.y + (p2.y - p0.y) / 6;
|
||||||
|
const c4x = p2.x - (p3.x - p1.x) / 6;
|
||||||
|
const c4y = p2.y - (p3.y - p1.y) / 6;
|
||||||
|
|
||||||
|
// 拼接两段三次贝塞尔曲线
|
||||||
|
const path = `M ${p0.x},${p0.y} C ${c1x},${c1y} ${c2x},${c2y} ${p1.x},${p1.y} C ${c3x},${c3y} ${c4x},${c4y} ${p2.x},${p2.y}`;
|
||||||
|
|
||||||
|
// 标签显示在控制点位置
|
||||||
|
return [path, controlPoint.x, controlPoint.y] as [string, number, number];
|
||||||
|
}, [controlPoint, sourceX, sourceY, targetX, targetY]);
|
||||||
|
|
||||||
// 拖动处理(支持第 idx 个拐点)
|
// 拖动控制点处理
|
||||||
const handleVertexMouseDown = useCallback((idx: number, e: React.MouseEvent) => {
|
const handleControlPointMouseDown = useCallback((e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDragging(true);
|
setIsDragging(true);
|
||||||
|
|
||||||
const handleMove = (moveEvent: MouseEvent) => {
|
const handleMove = (moveEvent: MouseEvent) => {
|
||||||
const flowPosition = screenToFlowPosition({ x: moveEvent.clientX, y: moveEvent.clientY });
|
const flowPosition = screenToFlowPosition({ x: moveEvent.clientX, y: moveEvent.clientY });
|
||||||
setEdges((eds) => eds.map(ed => {
|
setEdges((eds) => eds.map(ed =>
|
||||||
if (ed.id !== id) return ed;
|
ed.id === id
|
||||||
const old = (ed as any).data?.vertices as ControlPoint[] | undefined;
|
? { ...ed, data: { ...ed.data, controlPoint: { x: flowPosition.x, y: flowPosition.y } } }
|
||||||
const base = Array.isArray(old) && old.length === 3 ? [...old] : vertices.slice();
|
: ed
|
||||||
base[idx] = { x: flowPosition.x, y: flowPosition.y };
|
));
|
||||||
return { ...ed, data: { ...ed.data, vertices: base } } as any;
|
|
||||||
}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUp = () => {
|
const handleUp = () => {
|
||||||
@ -119,7 +137,8 @@ const CustomEdge: React.FC<EdgeProps> = ({
|
|||||||
|
|
||||||
window.addEventListener('mousemove', handleMove);
|
window.addEventListener('mousemove', handleMove);
|
||||||
window.addEventListener('mouseup', handleUp);
|
window.addEventListener('mouseup', handleUp);
|
||||||
}, [id, setEdges, screenToFlowPosition, vertices]);
|
}, [id, setEdges, screenToFlowPosition]);
|
||||||
|
|
||||||
|
|
||||||
// 根据状态确定样式
|
// 根据状态确定样式
|
||||||
const edgeStyle = {
|
const edgeStyle = {
|
||||||
@ -129,7 +148,6 @@ const CustomEdge: React.FC<EdgeProps> = ({
|
|||||||
transition: 'all 0.2s ease',
|
transition: 'all 0.2s ease',
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ 修复:确保 markerEnd 是对象后再 spread
|
|
||||||
const markerEndStyle = (() => {
|
const markerEndStyle = (() => {
|
||||||
if (!markerEnd || typeof markerEnd !== 'object') return markerEnd;
|
if (!markerEnd || typeof markerEnd !== 'object') return markerEnd;
|
||||||
const markerObj = markerEnd as Record<string, any>;
|
const markerObj = markerEnd as Record<string, any>;
|
||||||
@ -153,36 +171,36 @@ const CustomEdge: React.FC<EdgeProps> = ({
|
|||||||
fill="none"
|
fill="none"
|
||||||
stroke="transparent"
|
stroke="transparent"
|
||||||
strokeWidth={20}
|
strokeWidth={20}
|
||||||
style={{ cursor: isDragging ? 'grabbing' : 'grab' }}
|
style={{ cursor: isDragging ? 'grabbing' : 'pointer' }}
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 控制点(三个拐点,直接在SVG中渲染,可拖动) */}
|
{/* 单个可拖动控制点 */}
|
||||||
{(selected || isHovered) && (
|
{(selected || isHovered) && (
|
||||||
<g>
|
<g>
|
||||||
{vertices.map((pt, idx) => (
|
<circle
|
||||||
<g key={`v-${idx}`}>
|
cx={controlPoint.x}
|
||||||
<circle
|
cy={controlPoint.y}
|
||||||
cx={pt.x}
|
r={8}
|
||||||
cy={pt.y}
|
fill={isDragging ? '#2563eb' : '#3b82f6'}
|
||||||
r={8}
|
stroke="white"
|
||||||
fill={isDragging ? '#2563eb' : '#3b82f6'}
|
strokeWidth={2}
|
||||||
stroke="white"
|
style={{
|
||||||
strokeWidth={2}
|
cursor: isDragging ? 'grabbing' : 'grab',
|
||||||
style={{ cursor: isDragging ? 'grabbing' : 'grab', filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.3))', pointerEvents: 'all' }}
|
filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.3))',
|
||||||
onMouseDown={(e) => handleVertexMouseDown(idx, e)}
|
pointerEvents: 'all'
|
||||||
/>
|
}}
|
||||||
<circle
|
onMouseDown={handleControlPointMouseDown}
|
||||||
cx={pt.x}
|
/>
|
||||||
cy={pt.y}
|
<circle
|
||||||
r={15}
|
cx={controlPoint.x}
|
||||||
fill="transparent"
|
cy={controlPoint.y}
|
||||||
style={{ cursor: isDragging ? 'grabbing' : 'grab', pointerEvents: 'all' }}
|
r={15}
|
||||||
onMouseDown={(e) => handleVertexMouseDown(idx, e)}
|
fill="transparent"
|
||||||
/>
|
style={{ cursor: isDragging ? 'grabbing' : 'grab', pointerEvents: 'all' }}
|
||||||
</g>
|
onMouseDown={handleControlPointMouseDown}
|
||||||
))}
|
/>
|
||||||
</g>
|
</g>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types';
|
import {ConfigurableNodeDefinition, NodeType, NodeCategory, defineNodeOutputs} from './types';
|
||||||
import { DataSourceType } from '@/domain/dataSource';
|
import {DataSourceType} from '@/domain/dataSource';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jenkins构建节点定义(纯配置)
|
* Jenkins构建节点定义(纯配置)
|
||||||
@ -69,10 +69,18 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = {
|
|||||||
required: ["serverId", "jobName"]
|
required: ["serverId", "jobName"]
|
||||||
},
|
},
|
||||||
outputs: defineNodeOutputs(
|
outputs: defineNodeOutputs(
|
||||||
|
{
|
||||||
|
name: "buildStatus",
|
||||||
|
title: "构建状态",
|
||||||
|
type: "string",
|
||||||
|
enum: ["SUCCESS", "FAILURE", "ABORTED", "NOT_FOUND"],
|
||||||
|
description: "节点执行的最终状态",
|
||||||
|
required: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "buildNumber",
|
name: "buildNumber",
|
||||||
title: "构建编号",
|
title: "构建编号",
|
||||||
type: "number",
|
type: "number",
|
||||||
description: "Jenkins构建的唯一编号",
|
description: "Jenkins构建的唯一编号",
|
||||||
example: 123,
|
example: 123,
|
||||||
required: true
|
required: true
|
||||||
@ -80,7 +88,7 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = {
|
|||||||
{
|
{
|
||||||
name: "buildUrl",
|
name: "buildUrl",
|
||||||
title: "构建URL",
|
title: "构建URL",
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "Jenkins构建页面的访问地址",
|
description: "Jenkins构建页面的访问地址",
|
||||||
example: "http://jenkins.example.com/job/app/123/",
|
example: "http://jenkins.example.com/job/app/123/",
|
||||||
required: true
|
required: true
|
||||||
@ -88,7 +96,7 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = {
|
|||||||
{
|
{
|
||||||
name: "artifactUrl",
|
name: "artifactUrl",
|
||||||
title: "构建产物地址",
|
title: "构建产物地址",
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "构建生成的jar/war包下载地址",
|
description: "构建生成的jar/war包下载地址",
|
||||||
example: "http://jenkins.example.com/job/app/123/artifact/target/app-1.0.0.jar",
|
example: "http://jenkins.example.com/job/app/123/artifact/target/app-1.0.0.jar",
|
||||||
required: false
|
required: false
|
||||||
@ -96,7 +104,7 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = {
|
|||||||
{
|
{
|
||||||
name: "gitCommitId",
|
name: "gitCommitId",
|
||||||
title: "Git提交ID",
|
title: "Git提交ID",
|
||||||
type: "string",
|
type: "string",
|
||||||
description: "本次构建使用的Git提交哈希值",
|
description: "本次构建使用的Git提交哈希值",
|
||||||
example: "a3f5e8d2c4b1a5e9f2d3e7b8c9d1a2f3e4b5c6d7",
|
example: "a3f5e8d2c4b1a5e9f2d3e7b8c9d1a2f3e4b5c6d7",
|
||||||
required: true
|
required: true
|
||||||
@ -104,10 +112,42 @@ export const JenkinsBuildNodeDefinition: ConfigurableNodeDefinition = {
|
|||||||
{
|
{
|
||||||
name: "buildDuration",
|
name: "buildDuration",
|
||||||
title: "构建时长",
|
title: "构建时长",
|
||||||
type: "number",
|
type: "number",
|
||||||
description: "构建执行的时长(秒)",
|
description: "构建执行的时长(秒)",
|
||||||
example: 120,
|
example: 120,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "buildDurationMillis",
|
||||||
|
title: "构建时长(毫秒)",
|
||||||
|
type: "number",
|
||||||
|
description: "构建执行的时长(毫秒)",
|
||||||
|
example: 158000,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "buildDurationFormatted",
|
||||||
|
title: "构建时长(格式)",
|
||||||
|
type: "string",
|
||||||
|
description: "构建执行的时长(格式:HH:mm:ss)",
|
||||||
|
example: "00:02:39",
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "buildEndTimeMillis",
|
||||||
|
title: "部署结束时间(ms)",
|
||||||
|
type: "number",
|
||||||
|
description: "部署结束时间(毫秒)",
|
||||||
|
example: 1700000000000,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "buildEndTime",
|
||||||
|
title: "部署结束时间",
|
||||||
|
type: "string",
|
||||||
|
description: "部署结束时间(格式化)",
|
||||||
|
example: "2025-11-14 15:30:00",
|
||||||
|
required: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user