From 3112f9fc34d4095f049c8c02d4ab771b78b7c2b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:08:45 +0000 Subject: [PATCH] Fix scenario mode UI issues: add icon, fix blank space, enable node dragging, enlarge editor, fix API call Co-authored-by: whyour <22700758+whyour@users.noreply.github.com> --- src/layouts/defaultProps.tsx | 2 +- src/locales/en-US.json | 9 ---- src/locales/zh-CN.json | 9 ---- src/pages/scenario/flowgram/Editor.tsx | 51 +++++++++++++++--- src/pages/scenario/flowgram/editor.css | 52 ++++++++++++++++-- src/pages/scenario/flowgramWorkflowModal.tsx | 4 +- src/pages/scenario/index.tsx | 55 +++++++++++++------- 7 files changed, 134 insertions(+), 48 deletions(-) diff --git a/src/layouts/defaultProps.tsx b/src/layouts/defaultProps.tsx index 75a1cd95..9456f599 100644 --- a/src/layouts/defaultProps.tsx +++ b/src/layouts/defaultProps.tsx @@ -69,7 +69,7 @@ export default { { path: '/scenario', name: intl.get('场景模式'), - icon: , + icon: , component: '@/pages/scenario/index', }, { diff --git a/src/locales/en-US.json b/src/locales/en-US.json index f3706eeb..1e975d7b 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -558,9 +558,6 @@ "任一满足": "Any satisfied", "字段名": "Field Name", "操作符": "Operator", - "包含": "Contains", - "不包含": "Not Contains", - "值": "Value", "添加条件": "Add Condition", "动作配置": "Action Configuration", "动作类型": "Action Type", @@ -570,28 +567,22 @@ "发送通知": "Send Notification", "变量名": "Variable Name", "变量值": "Variable Value", - "命令": "Command", "消息": "Message", "添加动作": "Add Action", "高级设置": "Advanced Settings", "延迟执行": "Delay Execution", - "秒": "seconds", "失败熔断阈值": "Failure Threshold", "连续失败多少次后自动禁用": "Auto-disable after N consecutive failures", "最大重试次数": "Max Retry Count", "重试延迟": "Retry Delay", "退避倍数": "Backoff Multiplier", "每次重试延迟的乘数": "Multiplier for retry delay", - "启用": "Enable", "时间": "Time", "条件匹配": "Condition Matched", "执行时间": "Execution Time", "重试次数": "Retry Count", "错误信息": "Error Message", - "是": "Yes", - "否": "No", "共": "Total", - "项": "items", "日志名称": "Log Name", "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate", "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "Custom log folder name to distinguish logs from different tasks. Leave blank to auto-generate. Supports absolute paths like /dev/null", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 1753b66d..1b0d3a15 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -558,9 +558,6 @@ "任一满足": "任一满足", "字段名": "字段名", "操作符": "操作符", - "包含": "包含", - "不包含": "不包含", - "值": "值", "添加条件": "添加条件", "动作配置": "动作配置", "动作类型": "动作类型", @@ -570,28 +567,22 @@ "发送通知": "发送通知", "变量名": "变量名", "变量值": "变量值", - "命令": "命令", "消息": "消息", "添加动作": "添加动作", "高级设置": "高级设置", "延迟执行": "延迟执行", - "秒": "秒", "失败熔断阈值": "失败熔断阈值", "连续失败多少次后自动禁用": "连续失败多少次后自动禁用", "最大重试次数": "最大重试次数", "重试延迟": "重试延迟", "退避倍数": "退避倍数", "每次重试延迟的乘数": "每次重试延迟的乘数", - "启用": "启用", "时间": "时间", "条件匹配": "条件匹配", "执行时间": "执行时间", "重试次数": "重试次数", "错误信息": "错误信息", - "是": "是", - "否": "否", "共": "共", - "项": "项", "日志名称": "日志名称", "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成", "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null": "自定义日志文件夹名称,用于区分不同任务的日志,留空则自动生成。支持绝对路径如 /dev/null", diff --git a/src/pages/scenario/flowgram/Editor.tsx b/src/pages/scenario/flowgram/Editor.tsx index db98abc7..6128af57 100644 --- a/src/pages/scenario/flowgram/Editor.tsx +++ b/src/pages/scenario/flowgram/Editor.tsx @@ -25,6 +25,7 @@ export const FlowgramEditor: React.FC = ({ value, onChange const [graph, setGraph] = useState(value || { nodes: [], edges: [] }); const [selectedNode, setSelectedNode] = useState(null); const [drawerVisible, setDrawerVisible] = useState(false); + const [draggedNode, setDraggedNode] = useState(null); const [form] = Form.useForm(); // Initialize editor @@ -120,6 +121,42 @@ export const FlowgramEditor: React.FC = ({ value, onChange notifyChange(newGraph); }, [graph, notifyChange]); + // Handle node drag start + const handleDragStart = useCallback((e: React.DragEvent, node: FlowgramNode) => { + setDraggedNode(node); + e.dataTransfer.effectAllowed = 'move'; + }, []); + + // Handle node drag over + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }, []); + + // Handle node drop + const handleDrop = useCallback((e: React.DragEvent, targetNode: FlowgramNode) => { + e.preventDefault(); + if (!draggedNode || draggedNode.id === targetNode.id) return; + + // Reorder nodes + const draggedIndex = graph.nodes.findIndex(n => n.id === draggedNode.id); + const targetIndex = graph.nodes.findIndex(n => n.id === targetNode.id); + + if (draggedIndex === -1 || targetIndex === -1) return; + + const newNodes = [...graph.nodes]; + const [removed] = newNodes.splice(draggedIndex, 1); + newNodes.splice(targetIndex, 0, removed); + + const newGraph = { + ...graph, + nodes: newNodes, + }; + + notifyChange(newGraph); + setDraggedNode(null); + }, [draggedNode, graph, notifyChange]); + // Validate workflow const handleValidate = useCallback(() => { const validation = validateWorkflow(graph); @@ -367,18 +404,18 @@ export const FlowgramEditor: React.FC = ({ value, onChange

工作流节点 ({graph.nodes.length})

- + {graph.nodes.map((node) => (
handleNodeClick(node)} + draggable + onDragStart={(e) => handleDragStart(e, node)} + onDragOver={handleDragOver} + onDrop={(e) => handleDrop(e, node)} style={{ - padding: '10px', - border: '1px solid #d9d9d9', - borderRadius: '4px', - cursor: 'pointer', - background: '#fff', + background: draggedNode?.id === node.id ? '#f0f0f0' : '#fff', }} >
{node.data.label}
diff --git a/src/pages/scenario/flowgram/editor.css b/src/pages/scenario/flowgram/editor.css index 2f83d416..4cc351d9 100644 --- a/src/pages/scenario/flowgram/editor.css +++ b/src/pages/scenario/flowgram/editor.css @@ -1,10 +1,56 @@ .flowgram-editor-container { - display: flex; - flex-direction: column; - height: 600px; border: 1px solid #d9d9d9; border-radius: 4px; + padding: 16px; + min-height: 600px; + display: flex; + flex-direction: column; +} + +.flowgram-editor-toolbar { + margin-bottom: 16px; + display: flex; + gap: 8px; + flex-wrap: wrap; + flex-shrink: 0; +} + +.flowgram-editor-canvas { + flex: 1; + min-height: 500px; + border: 1px dashed #d9d9d9; + border-radius: 4px; + padding: 16px; background: #fafafa; + overflow-y: auto; + max-height: calc(80vh - 300px); +} + +.flowgram-node-card { + padding: 12px; + background: white; + border: 1px solid #d9d9d9; + border-radius: 4px; + margin-bottom: 8px; + cursor: move; + user-select: none; + position: relative; +} + +.flowgram-node-card:hover { + border-color: #40a9ff; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.flowgram-node-card-dragging { + opacity: 0.5; + cursor: grabbing; +} + +.flowgram-node-list { + display: flex; + flex-direction: column; + gap: 8px; } .flowgram-toolbar { diff --git a/src/pages/scenario/flowgramWorkflowModal.tsx b/src/pages/scenario/flowgramWorkflowModal.tsx index e7cbc4f8..38a79c05 100644 --- a/src/pages/scenario/flowgramWorkflowModal.tsx +++ b/src/pages/scenario/flowgramWorkflowModal.tsx @@ -80,7 +80,9 @@ const FlowgramWorkflowModal: React.FC = ({ onOk={handleOk} onCancel={handleCancel} confirmLoading={loading} - width={1000} + width={1400} + style={{ top: 20 }} + bodyStyle={{ height: '80vh', overflow: 'auto' }} maskClosable={false} >
diff --git a/src/pages/scenario/index.tsx b/src/pages/scenario/index.tsx index edc10090..93b41b83 100644 --- a/src/pages/scenario/index.tsx +++ b/src/pages/scenario/index.tsx @@ -108,6 +108,31 @@ const Scenario = () => { } }; + const handleModalOk = async (values: any) => { + try { + if (selectedScenario) { + const { code } = await request.put('/api/scenarios', values); + if (code === 200) { + message.success(intl.get('更新成功')); + setIsModalVisible(false); + setSelectedScenario(null); + fetchScenarios(); + } + } else { + const { code } = await request.post('/api/scenarios', values); + if (code === 200) { + message.success(intl.get('创建成功')); + setIsModalVisible(false); + setSelectedScenario(null); + fetchScenarios(); + } + } + } catch (error) { + console.error('Failed to save scenario:', error); + message.error(intl.get('操作失败')); + } + }; + const handleViewLogs = (record: any) => { setSelectedScenario(record); setIsLogModalVisible(true); @@ -263,19 +288,17 @@ const Scenario = () => { return ( } - onClick={handleCreate} - > - {intl.get('新建场景')} - , - ], - }} + title={intl.get('场景模式')} + extra={[ + , + ]} > { setIsModalVisible(false); setSelectedScenario(null); }} - onOk={() => { - setIsModalVisible(false); - setSelectedScenario(null); - fetchScenarios(); - }} + onOk={handleModalOk} />