|
|
@@ -0,0 +1,969 @@
|
|
|
+package com.mes.ui.component;
|
|
|
+
|
|
|
+import com.mes.core.*;
|
|
|
+import com.mes.step.IWorkflowStep;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import javax.swing.*;
|
|
|
+import javax.swing.border.EmptyBorder;
|
|
|
+import javax.swing.border.TitledBorder;
|
|
|
+import java.awt.*;
|
|
|
+import java.util.List;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 流程监控面板 - 显示工作流步骤状态,支持条件检测和跳过操作
|
|
|
+ * 采用垂直列表方案,每个步骤显示:序号、名称、状态、条件、跳过按钮
|
|
|
+ */
|
|
|
+public class WorkflowMonitorPanel extends JPanel {
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(WorkflowMonitorPanel.class);
|
|
|
+
|
|
|
+ // 颜色定义
|
|
|
+ private static final Color COLOR_COMPLETED = new Color(46, 204, 113); // 绿色 - 已完成
|
|
|
+ private static final Color COLOR_CURRENT = new Color(52, 152, 219); // 蓝色 - 当前步骤
|
|
|
+ private static final Color COLOR_PENDING = new Color(149, 165, 166); // 灰色 - 待执行
|
|
|
+ private static final Color COLOR_ERROR = new Color(231, 76, 60); // 红色 - 错误
|
|
|
+ private static final Color COLOR_CONDITION_OK = new Color(46, 204, 113); // 条件满足
|
|
|
+ private static final Color COLOR_CONDITION_FAIL = new Color(231, 76, 60); // 条件不满足
|
|
|
+
|
|
|
+ // 工位选择(多工位模式)
|
|
|
+ private List<WorkflowEngine> engines;
|
|
|
+ private List<StationContext> contexts;
|
|
|
+ private int selectedStationIndex = 0;
|
|
|
+
|
|
|
+ // UI组件
|
|
|
+ private JComboBox<String> stationSelector;
|
|
|
+ private JPanel stepsContainer;
|
|
|
+ private List<StepPanel> stepPanels;
|
|
|
+ private JLabel statusLabel;
|
|
|
+ private Timer refreshTimer;
|
|
|
+
|
|
|
+ // ========== 调试控制相关 ==========
|
|
|
+ private DebugConfigManager debugConfigManager;
|
|
|
+ private JPanel debugControlPanel;
|
|
|
+ private JCheckBox debugEnabledCheckbox;
|
|
|
+ private JPanel conditionsPanel;
|
|
|
+ private JPanel rulesPanel;
|
|
|
+ private List<ConditionInterceptor.ConditionMeta> conditionMetas;
|
|
|
+
|
|
|
+ public WorkflowMonitorPanel() {
|
|
|
+ this.engines = new ArrayList<>();
|
|
|
+ this.contexts = new ArrayList<>();
|
|
|
+ this.stepPanels = new ArrayList<>();
|
|
|
+ this.debugConfigManager = new DebugConfigManager();
|
|
|
+ this.conditionMetas = ConditionInterceptor.scanConditions(StationContext.class);
|
|
|
+ initUI();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置工位列表
|
|
|
+ */
|
|
|
+ public void setWorkstations(List<WorkflowEngine> engines, List<StationContext> contexts) {
|
|
|
+ this.engines = engines != null ? engines : new ArrayList<>();
|
|
|
+ this.contexts = contexts != null ? contexts : new ArrayList<>();
|
|
|
+
|
|
|
+ // 为每个工位初始化拦截器
|
|
|
+ for (StationContext ctx : this.contexts) {
|
|
|
+ if (ctx.getConditionInterceptor() == null) {
|
|
|
+ ConditionInterceptor interceptor = new ConditionInterceptor();
|
|
|
+ ctx.setConditionInterceptor(interceptor);
|
|
|
+ // 应用已保存的配置
|
|
|
+ debugConfigManager.applyToInterceptor(ctx.getStationCode(), interceptor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新工位选择器
|
|
|
+ stationSelector.removeAllItems();
|
|
|
+ for (int i = 0; i < this.contexts.size(); i++) {
|
|
|
+ StationContext ctx = this.contexts.get(i);
|
|
|
+ stationSelector.addItem(ctx.getStationCode() + " - " + ctx.getStationName());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.engines.isEmpty()) {
|
|
|
+ stationSelector.setSelectedIndex(0);
|
|
|
+ selectedStationIndex = 0;
|
|
|
+ refreshSteps();
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化UI
|
|
|
+ */
|
|
|
+ private void initUI() {
|
|
|
+ setLayout(new BorderLayout(10, 10));
|
|
|
+ setBorder(new EmptyBorder(10, 10, 10, 10));
|
|
|
+
|
|
|
+ // 顶部:工位选择器和状态
|
|
|
+ JPanel topPanel = createTopPanel();
|
|
|
+ add(topPanel, BorderLayout.NORTH);
|
|
|
+
|
|
|
+ // 中间:使用分割面板,左边步骤列表,右边调试控制
|
|
|
+ JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
|
|
+ splitPane.setResizeWeight(0.6);
|
|
|
+
|
|
|
+ // 左侧:步骤列表(滚动)
|
|
|
+ stepsContainer = new JPanel();
|
|
|
+ stepsContainer.setLayout(new BoxLayout(stepsContainer, BoxLayout.Y_AXIS));
|
|
|
+
|
|
|
+ JScrollPane stepsScrollPane = new JScrollPane(stepsContainer);
|
|
|
+ stepsScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
|
|
|
+ stepsScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
|
|
+ stepsScrollPane.getVerticalScrollBar().setUnitIncrement(16);
|
|
|
+ stepsScrollPane.getVerticalScrollBar().setBlockIncrement(80);
|
|
|
+ stepsScrollPane.setBorder(BorderFactory.createTitledBorder(
|
|
|
+ BorderFactory.createLineBorder(Color.GRAY),
|
|
|
+ "流程步骤",
|
|
|
+ TitledBorder.LEFT,
|
|
|
+ TitledBorder.TOP,
|
|
|
+ new Font("微软雅黑", Font.BOLD, 14)));
|
|
|
+ splitPane.setLeftComponent(stepsScrollPane);
|
|
|
+
|
|
|
+ // 右侧:调试控制面板
|
|
|
+ debugControlPanel = createDebugControlPanel();
|
|
|
+ JScrollPane debugScrollPane = new JScrollPane(debugControlPanel);
|
|
|
+ debugScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
|
|
|
+ debugScrollPane.getVerticalScrollBar().setUnitIncrement(16);
|
|
|
+ debugScrollPane.getVerticalScrollBar().setBlockIncrement(80);
|
|
|
+ debugScrollPane.setBorder(BorderFactory.createTitledBorder(
|
|
|
+ BorderFactory.createLineBorder(new Color(255, 152, 0)),
|
|
|
+ "调试控制",
|
|
|
+ TitledBorder.LEFT,
|
|
|
+ TitledBorder.TOP,
|
|
|
+ new Font("微软雅黑", Font.BOLD, 14),
|
|
|
+ new Color(255, 152, 0)));
|
|
|
+ splitPane.setRightComponent(debugScrollPane);
|
|
|
+
|
|
|
+ add(splitPane, BorderLayout.CENTER);
|
|
|
+
|
|
|
+ // 底部:刷新按钮和状态
|
|
|
+ JPanel bottomPanel = createBottomPanel();
|
|
|
+ add(bottomPanel, BorderLayout.SOUTH);
|
|
|
+
|
|
|
+ // 自动刷新定时器(每秒刷新一次)
|
|
|
+ refreshTimer = new Timer(1000, e -> refresh());
|
|
|
+ refreshTimer.start();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建顶部面板
|
|
|
+ */
|
|
|
+ private JPanel createTopPanel() {
|
|
|
+ JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 5));
|
|
|
+
|
|
|
+ JLabel label = new JLabel("工位:");
|
|
|
+ label.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ panel.add(label);
|
|
|
+
|
|
|
+ stationSelector = new JComboBox<>();
|
|
|
+ stationSelector.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ stationSelector.setPreferredSize(new Dimension(200, 30));
|
|
|
+ stationSelector.addActionListener(e -> {
|
|
|
+ int index = stationSelector.getSelectedIndex();
|
|
|
+ if (index >= 0 && index != selectedStationIndex) {
|
|
|
+ selectedStationIndex = index;
|
|
|
+ refreshSteps();
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ panel.add(stationSelector);
|
|
|
+
|
|
|
+ // 手动刷新按钮
|
|
|
+ JButton refreshBtn = new JButton("刷新");
|
|
|
+ refreshBtn.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ refreshBtn.addActionListener(e -> refresh());
|
|
|
+ panel.add(refreshBtn);
|
|
|
+
|
|
|
+ return panel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建底部面板
|
|
|
+ */
|
|
|
+ private JPanel createBottomPanel() {
|
|
|
+ JPanel panel = new JPanel(new BorderLayout(10, 5));
|
|
|
+ panel.setBorder(new EmptyBorder(5, 0, 0, 0));
|
|
|
+
|
|
|
+ // 状态标签
|
|
|
+ statusLabel = new JLabel("就绪");
|
|
|
+ statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
|
|
+ panel.add(statusLabel, BorderLayout.WEST);
|
|
|
+
|
|
|
+ // 说明
|
|
|
+ JLabel helpLabel = new JLabel("提示:点击\"跳过\"将模拟该步骤执行成功");
|
|
|
+ helpLabel.setFont(new Font("微软雅黑", Font.ITALIC, 11));
|
|
|
+ helpLabel.setForeground(Color.GRAY);
|
|
|
+ panel.add(helpLabel, BorderLayout.EAST);
|
|
|
+
|
|
|
+ return panel;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 调试控制面板相关方法 ==========
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建调试控制面板
|
|
|
+ */
|
|
|
+ private JPanel createDebugControlPanel() {
|
|
|
+ JPanel panel = new JPanel();
|
|
|
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
|
|
|
+ panel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
|
+
|
|
|
+ // 启用调试复选框
|
|
|
+ JPanel enablePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
|
+ debugEnabledCheckbox = new JCheckBox("启用调试拦截");
|
|
|
+ debugEnabledCheckbox.setFont(new Font("微软雅黑", Font.BOLD, 12));
|
|
|
+ debugEnabledCheckbox.setForeground(new Color(255, 87, 34));
|
|
|
+ debugEnabledCheckbox.addActionListener(e -> onDebugEnabledChanged());
|
|
|
+ enablePanel.add(debugEnabledCheckbox);
|
|
|
+ enablePanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 35));
|
|
|
+ panel.add(enablePanel);
|
|
|
+
|
|
|
+ // 条件变量区域
|
|
|
+ conditionsPanel = new JPanel();
|
|
|
+ conditionsPanel.setLayout(new BoxLayout(conditionsPanel, BoxLayout.Y_AXIS));
|
|
|
+ conditionsPanel.setBorder(BorderFactory.createTitledBorder(
|
|
|
+ BorderFactory.createLineBorder(Color.LIGHT_GRAY),
|
|
|
+ "条件变量",
|
|
|
+ TitledBorder.LEFT, TitledBorder.TOP,
|
|
|
+ new Font("微软雅黑", Font.PLAIN, 12)));
|
|
|
+ panel.add(conditionsPanel);
|
|
|
+
|
|
|
+ panel.add(Box.createRigidArea(new Dimension(0, 10)));
|
|
|
+
|
|
|
+ // 持续控制规则区域
|
|
|
+ rulesPanel = new JPanel();
|
|
|
+ rulesPanel.setLayout(new BoxLayout(rulesPanel, BoxLayout.Y_AXIS));
|
|
|
+ rulesPanel.setBorder(BorderFactory.createTitledBorder(
|
|
|
+ BorderFactory.createLineBorder(new Color(76, 175, 80)),
|
|
|
+ "持续控制规则",
|
|
|
+ TitledBorder.LEFT, TitledBorder.TOP,
|
|
|
+ new Font("微软雅黑", Font.PLAIN, 12),
|
|
|
+ new Color(76, 175, 80)));
|
|
|
+ panel.add(rulesPanel);
|
|
|
+
|
|
|
+ return panel;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新调试面板
|
|
|
+ */
|
|
|
+ private void refreshDebugPanel() {
|
|
|
+ if (selectedStationIndex < 0 || selectedStationIndex >= contexts.size()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ StationContext context = contexts.get(selectedStationIndex);
|
|
|
+ String stationCode = context.getStationCode();
|
|
|
+
|
|
|
+ // 更新启用状态
|
|
|
+ DebugConfigManager.StationDebugConfig config = debugConfigManager.getStationConfig(stationCode);
|
|
|
+ debugEnabledCheckbox.setSelected(config.isEnabled());
|
|
|
+
|
|
|
+ // 切换工位时需要重建条件面板
|
|
|
+ rebuildConditionsPanel(context);
|
|
|
+
|
|
|
+ // 刷新规则显示
|
|
|
+ refreshRulesPanel(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 存储值标签的映射,用于刷新时只更新值而不重建
|
|
|
+ private java.util.Map<String, JLabel> valueLabels = new java.util.HashMap<>();
|
|
|
+ private boolean conditionsPanelInitialized = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新条件变量面板
|
|
|
+ */
|
|
|
+ private void refreshConditionsPanel(StationContext context) {
|
|
|
+ // 如果面板未初始化,则完整构建
|
|
|
+ if (!conditionsPanelInitialized || conditionsPanel.getComponentCount() == 0) {
|
|
|
+ buildConditionsPanel(context);
|
|
|
+ conditionsPanelInitialized = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只更新值标签,不重建面板
|
|
|
+ java.util.Set<String> stepIds = getCurrentStepIds();
|
|
|
+ for (ConditionInterceptor.ConditionMeta meta : conditionMetas) {
|
|
|
+ if (!isConditionRelevant(meta, stepIds)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ JLabel valueLabel = valueLabels.get(meta.getFieldName());
|
|
|
+ if (valueLabel != null) {
|
|
|
+ Object value = ConditionInterceptor.getFieldValue(context, meta.getFieldName());
|
|
|
+ valueLabel.setText(String.valueOf(value));
|
|
|
+ if (meta.isBoolean()) {
|
|
|
+ valueLabel.setForeground(Boolean.TRUE.equals(value) ?
|
|
|
+ new Color(76, 175, 80) : new Color(244, 67, 54));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前工位流程中实际配置的步骤ID集合
|
|
|
+ */
|
|
|
+ private java.util.Set<String> getCurrentStepIds() {
|
|
|
+ java.util.Set<String> stepIds = new java.util.HashSet<>();
|
|
|
+ if (selectedStationIndex >= 0 && selectedStationIndex < engines.size()) {
|
|
|
+ for (IWorkflowStep step : engines.get(selectedStationIndex).getSteps()) {
|
|
|
+ stepIds.add(step.getStepId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return stepIds;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断条件变量是否与当前流程相关
|
|
|
+ * affectedSteps为空 → 不关联任何步骤,隐藏
|
|
|
+ * affectedSteps与当前步骤ID有交集 → 相关,显示
|
|
|
+ */
|
|
|
+ private boolean isConditionRelevant(ConditionInterceptor.ConditionMeta meta, java.util.Set<String> stepIds) {
|
|
|
+ String[] affected = meta.getAffectedSteps();
|
|
|
+ if (affected == null || affected.length == 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for (String s : affected) {
|
|
|
+ if (stepIds.contains(s)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建条件变量面板(只在初始化时调用)
|
|
|
+ * 只显示与当前工位流程相关的条件变量
|
|
|
+ */
|
|
|
+ private void buildConditionsPanel(StationContext context) {
|
|
|
+ conditionsPanel.removeAll();
|
|
|
+ valueLabels.clear();
|
|
|
+
|
|
|
+ java.util.Set<String> stepIds = getCurrentStepIds();
|
|
|
+
|
|
|
+ String currentGroup = "";
|
|
|
+ for (ConditionInterceptor.ConditionMeta meta : conditionMetas) {
|
|
|
+ // 过滤掉与当前流程无关的条件变量
|
|
|
+ if (!isConditionRelevant(meta, stepIds)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 分组标题
|
|
|
+ if (!meta.getGroup().equals(currentGroup)) {
|
|
|
+ currentGroup = meta.getGroup();
|
|
|
+ JLabel groupLabel = new JLabel("【" + currentGroup + "】");
|
|
|
+ groupLabel.setFont(new Font("微软雅黑", Font.BOLD, 11));
|
|
|
+ groupLabel.setForeground(new Color(33, 150, 243));
|
|
|
+ groupLabel.setBorder(new EmptyBorder(5, 0, 2, 0));
|
|
|
+ conditionsPanel.add(groupLabel);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 条件行
|
|
|
+ JPanel rowPanel = createConditionRow(context, meta);
|
|
|
+ conditionsPanel.add(rowPanel);
|
|
|
+ }
|
|
|
+
|
|
|
+ conditionsPanel.revalidate();
|
|
|
+ conditionsPanel.repaint();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 强制重建条件面板(切换工位时调用)
|
|
|
+ */
|
|
|
+ private void rebuildConditionsPanel(StationContext context) {
|
|
|
+ conditionsPanelInitialized = false;
|
|
|
+ refreshConditionsPanel(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查字段是否有持续规则
|
|
|
+ */
|
|
|
+ private boolean hasRule(StationContext context, String fieldName) {
|
|
|
+ ConditionInterceptor interceptor = context.getConditionInterceptor();
|
|
|
+ return interceptor != null && interceptor.hasRule(fieldName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建锁定/解锁按钮
|
|
|
+ */
|
|
|
+ private JButton createLockButton(StationContext context, ConditionInterceptor.ConditionMeta meta) {
|
|
|
+ boolean locked = hasRule(context, meta.getFieldName());
|
|
|
+ JButton lockBtn = new JButton(locked ? "解锁" : "锁定");
|
|
|
+ lockBtn.setFont(new Font("微软雅黑", Font.BOLD, 11));
|
|
|
+ lockBtn.setPreferredSize(new Dimension(50, 22));
|
|
|
+ lockBtn.setMargin(new java.awt.Insets(1, 2, 1, 2));
|
|
|
+ if (locked) {
|
|
|
+ lockBtn.setForeground(new Color(244, 67, 54));
|
|
|
+ lockBtn.setToolTipText("点击解除持续控制");
|
|
|
+ } else {
|
|
|
+ lockBtn.setToolTipText("添加持续控制规则");
|
|
|
+ }
|
|
|
+ lockBtn.addActionListener(e -> {
|
|
|
+ if (hasRule(context, meta.getFieldName())) {
|
|
|
+ removePersistentRule(context, meta.getFieldName());
|
|
|
+ } else {
|
|
|
+ showAddRuleDialog(context, meta);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return lockBtn;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建单个条件变量行
|
|
|
+ */
|
|
|
+ private JPanel createConditionRow(StationContext context, ConditionInterceptor.ConditionMeta meta) {
|
|
|
+ JPanel row = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));
|
|
|
+ row.setMaximumSize(new Dimension(Integer.MAX_VALUE, 30));
|
|
|
+
|
|
|
+ // 条件名称
|
|
|
+ JLabel nameLabel = new JLabel(meta.getName() + ":");
|
|
|
+ nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 11));
|
|
|
+ nameLabel.setPreferredSize(new Dimension(90, 20));
|
|
|
+ row.add(nameLabel);
|
|
|
+
|
|
|
+ // 当前值(如果有持续规则,显示规则值)
|
|
|
+ Object value = ConditionInterceptor.getFieldValue(context, meta.getFieldName());
|
|
|
+ ConditionInterceptor interceptor = context.getConditionInterceptor();
|
|
|
+ boolean locked = interceptor != null && interceptor.hasRule(meta.getFieldName());
|
|
|
+ if (locked) {
|
|
|
+ value = interceptor.getRuleValue(meta.getFieldName());
|
|
|
+ }
|
|
|
+
|
|
|
+ JLabel valueLabel = new JLabel(String.valueOf(value));
|
|
|
+ valueLabel.setFont(new Font("Consolas", Font.BOLD, 11));
|
|
|
+ valueLabel.setPreferredSize(new Dimension(50, 20));
|
|
|
+ if (meta.isBoolean()) {
|
|
|
+ valueLabel.setForeground(Boolean.TRUE.equals(value) ?
|
|
|
+ new Color(76, 175, 80) : new Color(244, 67, 54));
|
|
|
+ }
|
|
|
+ row.add(valueLabel);
|
|
|
+
|
|
|
+ // 存储值标签引用,用于后续刷新
|
|
|
+ valueLabels.put(meta.getFieldName(), valueLabel);
|
|
|
+
|
|
|
+ // 控制按钮
|
|
|
+ if (meta.isControllable()) {
|
|
|
+ if (meta.isBoolean()) {
|
|
|
+ JButton trueBtn = new JButton("真");
|
|
|
+ trueBtn.setFont(new Font("微软雅黑", Font.BOLD, 11));
|
|
|
+ trueBtn.setPreferredSize(new Dimension(45, 22));
|
|
|
+ trueBtn.setMargin(new java.awt.Insets(1, 2, 1, 2));
|
|
|
+ trueBtn.setToolTipText("设置为 true");
|
|
|
+ trueBtn.addActionListener(e -> setConditionValue(context, meta.getFieldName(), true));
|
|
|
+ row.add(trueBtn);
|
|
|
+
|
|
|
+ JButton falseBtn = new JButton("假");
|
|
|
+ falseBtn.setFont(new Font("微软雅黑", Font.BOLD, 11));
|
|
|
+ falseBtn.setPreferredSize(new Dimension(45, 22));
|
|
|
+ falseBtn.setMargin(new java.awt.Insets(1, 2, 1, 2));
|
|
|
+ falseBtn.setToolTipText("设置为 false");
|
|
|
+ falseBtn.addActionListener(e -> setConditionValue(context, meta.getFieldName(), false));
|
|
|
+ row.add(falseBtn);
|
|
|
+
|
|
|
+ row.add(createLockButton(context, meta));
|
|
|
+ } else if (meta.isNumeric()) {
|
|
|
+ JTextField inputField = new JTextField(4);
|
|
|
+ inputField.setFont(new Font("Consolas", Font.PLAIN, 11));
|
|
|
+ // 如果有持续规则,输入框显示规则值
|
|
|
+ if (locked) {
|
|
|
+ inputField.setText(String.valueOf(interceptor.getRuleValue(meta.getFieldName())));
|
|
|
+ } else {
|
|
|
+ inputField.setText(String.valueOf(ConditionInterceptor.getFieldValue(context, meta.getFieldName())));
|
|
|
+ }
|
|
|
+ row.add(inputField);
|
|
|
+
|
|
|
+ JButton setBtn = new JButton("设置");
|
|
|
+ setBtn.setFont(new Font("微软雅黑", Font.BOLD, 11));
|
|
|
+ setBtn.setPreferredSize(new Dimension(50, 22));
|
|
|
+ setBtn.setMargin(new java.awt.Insets(1, 2, 1, 2));
|
|
|
+ setBtn.addActionListener(e -> {
|
|
|
+ try {
|
|
|
+ int intValue = Integer.parseInt(inputField.getText().trim());
|
|
|
+ setConditionValue(context, meta.getFieldName(), intValue);
|
|
|
+ } catch (NumberFormatException ex) {
|
|
|
+ JOptionPane.showMessageDialog(this, "请输入有效数字", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ row.add(setBtn);
|
|
|
+
|
|
|
+ row.add(createLockButton(context, meta));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return row;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置条件值
|
|
|
+ */
|
|
|
+ private void setConditionValue(StationContext context, String fieldName, Object value) {
|
|
|
+ ConditionInterceptor.setFieldValue(context, fieldName, value);
|
|
|
+ log.info("[Debug] 设置 {} = {}", fieldName, value);
|
|
|
+ context.updateUI();
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 显示添加规则对话框
|
|
|
+ */
|
|
|
+ private void showAddRuleDialog(StationContext context, ConditionInterceptor.ConditionMeta meta) {
|
|
|
+ Object[] options;
|
|
|
+ String message;
|
|
|
+
|
|
|
+ if (meta.isBoolean()) {
|
|
|
+ options = new Object[]{"持续 true", "持续 false", "取消"};
|
|
|
+ message = String.format("为 \"%s\" 添加持续控制规则:", meta.getName());
|
|
|
+ } else {
|
|
|
+ String input = JOptionPane.showInputDialog(this,
|
|
|
+ String.format("为 \"%s\" 设置持续值:", meta.getName()),
|
|
|
+ "添加持续控制规则",
|
|
|
+ JOptionPane.QUESTION_MESSAGE);
|
|
|
+ if (input != null && !input.trim().isEmpty()) {
|
|
|
+ try {
|
|
|
+ int value = Integer.parseInt(input.trim());
|
|
|
+ addPersistentRule(context, meta.getFieldName(), value);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ JOptionPane.showMessageDialog(this, "请输入有效数字", "错误", JOptionPane.ERROR_MESSAGE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int result = JOptionPane.showOptionDialog(this, message, "添加持续控制规则",
|
|
|
+ JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
|
|
|
+
|
|
|
+ if (result == 0) {
|
|
|
+ addPersistentRule(context, meta.getFieldName(), true);
|
|
|
+ } else if (result == 1) {
|
|
|
+ addPersistentRule(context, meta.getFieldName(), false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加持续控制规则
|
|
|
+ */
|
|
|
+ private void addPersistentRule(StationContext context, String fieldName, Object value) {
|
|
|
+ String stationCode = context.getStationCode();
|
|
|
+
|
|
|
+ // 保存到配置
|
|
|
+ debugConfigManager.addRule(stationCode, fieldName, value);
|
|
|
+
|
|
|
+ // 应用到拦截器
|
|
|
+ ConditionInterceptor interceptor = context.getConditionInterceptor();
|
|
|
+ if (interceptor != null) {
|
|
|
+ interceptor.addRule(fieldName, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 立即设置当前值
|
|
|
+ ConditionInterceptor.setFieldValue(context, fieldName, value);
|
|
|
+
|
|
|
+ log.info("[Debug] 添加持续规则: {} = {}", fieldName, value);
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 移除持续控制规则
|
|
|
+ */
|
|
|
+ private void removePersistentRule(StationContext context, String fieldName) {
|
|
|
+ String stationCode = context.getStationCode();
|
|
|
+
|
|
|
+ // 从配置移除
|
|
|
+ debugConfigManager.removeRule(stationCode, fieldName);
|
|
|
+
|
|
|
+ // 从拦截器移除
|
|
|
+ ConditionInterceptor interceptor = context.getConditionInterceptor();
|
|
|
+ if (interceptor != null) {
|
|
|
+ interceptor.removeRule(fieldName);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("[Debug] 移除持续规则: {}", fieldName);
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新规则面板
|
|
|
+ */
|
|
|
+ private void refreshRulesPanel(StationContext context) {
|
|
|
+ rulesPanel.removeAll();
|
|
|
+
|
|
|
+ String stationCode = context.getStationCode();
|
|
|
+ DebugConfigManager.StationDebugConfig config = debugConfigManager.getStationConfig(stationCode);
|
|
|
+ Map<String, Object> rules = config.getPersistentRules();
|
|
|
+
|
|
|
+ if (rules.isEmpty()) {
|
|
|
+ JLabel emptyLabel = new JLabel(" (无持续控制规则)");
|
|
|
+ emptyLabel.setFont(new Font("微软雅黑", Font.ITALIC, 11));
|
|
|
+ emptyLabel.setForeground(Color.GRAY);
|
|
|
+ rulesPanel.add(emptyLabel);
|
|
|
+ } else {
|
|
|
+ for (Map.Entry<String, Object> entry : rules.entrySet()) {
|
|
|
+ JPanel ruleRow = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));
|
|
|
+ ruleRow.setMaximumSize(new Dimension(Integer.MAX_VALUE, 28));
|
|
|
+
|
|
|
+ // 规则图标
|
|
|
+ JLabel iconLabel = new JLabel("🔒");
|
|
|
+ ruleRow.add(iconLabel);
|
|
|
+
|
|
|
+ // 规则内容
|
|
|
+ JLabel ruleLabel = new JLabel(entry.getKey() + " = " + entry.getValue());
|
|
|
+ ruleLabel.setFont(new Font("Consolas", Font.PLAIN, 11));
|
|
|
+ ruleLabel.setForeground(new Color(76, 175, 80));
|
|
|
+ ruleRow.add(ruleLabel);
|
|
|
+
|
|
|
+ // 删除按钮
|
|
|
+ JButton deleteBtn = new JButton("×");
|
|
|
+ deleteBtn.setFont(new Font("微软雅黑", Font.BOLD, 12));
|
|
|
+ deleteBtn.setPreferredSize(new Dimension(30, 20));
|
|
|
+ deleteBtn.setForeground(Color.RED);
|
|
|
+ deleteBtn.setToolTipText("删除此规则");
|
|
|
+ String fieldName = entry.getKey();
|
|
|
+ deleteBtn.addActionListener(e -> removePersistentRule(context, fieldName));
|
|
|
+ ruleRow.add(deleteBtn);
|
|
|
+
|
|
|
+ rulesPanel.add(ruleRow);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清空所有规则按钮
|
|
|
+ if (!rules.isEmpty()) {
|
|
|
+ JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
|
+ JButton clearAllBtn = new JButton("清空所有规则");
|
|
|
+ clearAllBtn.setFont(new Font("微软雅黑", Font.PLAIN, 11));
|
|
|
+ clearAllBtn.addActionListener(e -> {
|
|
|
+ int result = JOptionPane.showConfirmDialog(this,
|
|
|
+ "确定要清空所有持续控制规则吗?", "确认", JOptionPane.YES_NO_OPTION);
|
|
|
+ if (result == JOptionPane.YES_OPTION) {
|
|
|
+ ConditionInterceptor interceptor = context.getConditionInterceptor();
|
|
|
+ if (interceptor != null) {
|
|
|
+ interceptor.clearRules();
|
|
|
+ }
|
|
|
+ DebugConfigManager.StationDebugConfig cfg = debugConfigManager.getStationConfig(stationCode);
|
|
|
+ cfg.getPersistentRules().clear();
|
|
|
+ debugConfigManager.save();
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ btnPanel.add(clearAllBtn);
|
|
|
+ rulesPanel.add(btnPanel);
|
|
|
+ }
|
|
|
+
|
|
|
+ rulesPanel.revalidate();
|
|
|
+ rulesPanel.repaint();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调试启用状态变化
|
|
|
+ */
|
|
|
+ private void onDebugEnabledChanged() {
|
|
|
+ if (selectedStationIndex < 0 || selectedStationIndex >= contexts.size()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ StationContext context = contexts.get(selectedStationIndex);
|
|
|
+ String stationCode = context.getStationCode();
|
|
|
+ boolean enabled = debugEnabledCheckbox.isSelected();
|
|
|
+
|
|
|
+ debugConfigManager.setEnabled(stationCode, enabled);
|
|
|
+
|
|
|
+ ConditionInterceptor interceptor = context.getConditionInterceptor();
|
|
|
+ if (interceptor != null) {
|
|
|
+ if (enabled) {
|
|
|
+ debugConfigManager.applyToInterceptor(stationCode, interceptor);
|
|
|
+ } else {
|
|
|
+ interceptor.clearRules();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("[Debug] 工位 {} 调试模式: {}", stationCode, enabled ? "启用" : "禁用");
|
|
|
+ refreshDebugPanel();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新步骤列表(重建UI)
|
|
|
+ */
|
|
|
+ private void refreshSteps() {
|
|
|
+ stepsContainer.removeAll();
|
|
|
+ stepPanels.clear();
|
|
|
+
|
|
|
+ if (selectedStationIndex < 0 || selectedStationIndex >= engines.size()) {
|
|
|
+ stepsContainer.revalidate();
|
|
|
+ stepsContainer.repaint();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ WorkflowEngine engine = engines.get(selectedStationIndex);
|
|
|
+ StationContext context = contexts.get(selectedStationIndex);
|
|
|
+ List<IWorkflowStep> steps = engine.getSteps();
|
|
|
+ int currentIndex = engine.getCurrentStepIndex();
|
|
|
+
|
|
|
+ for (int i = 0; i < steps.size(); i++) {
|
|
|
+ IWorkflowStep step = steps.get(i);
|
|
|
+ StepPanel stepPanel = new StepPanel(i, step, context, i == currentIndex, i < currentIndex);
|
|
|
+ stepPanels.add(stepPanel);
|
|
|
+ stepsContainer.add(stepPanel);
|
|
|
+ stepsContainer.add(Box.createRigidArea(new Dimension(0, 5)));
|
|
|
+ }
|
|
|
+
|
|
|
+ stepsContainer.revalidate();
|
|
|
+ stepsContainer.repaint();
|
|
|
+ updateStatus();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新(只更新状态,不重建UI)
|
|
|
+ */
|
|
|
+ public void refresh() {
|
|
|
+ if (selectedStationIndex < 0 || selectedStationIndex >= engines.size()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ WorkflowEngine engine = engines.get(selectedStationIndex);
|
|
|
+ StationContext context = contexts.get(selectedStationIndex);
|
|
|
+ int currentIndex = engine.getCurrentStepIndex();
|
|
|
+
|
|
|
+ // 检查是否需要重建(步骤数量变化)
|
|
|
+ if (stepPanels.size() != engine.getSteps().size()) {
|
|
|
+ refreshSteps();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新每个步骤面板
|
|
|
+ for (int i = 0; i < stepPanels.size(); i++) {
|
|
|
+ StepPanel panel = stepPanels.get(i);
|
|
|
+ panel.update(context, i == currentIndex, i < currentIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateStatus();
|
|
|
+
|
|
|
+ // 刷新调试面板的条件值显示
|
|
|
+ refreshConditionsPanel(context);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新状态栏
|
|
|
+ */
|
|
|
+ private void updateStatus() {
|
|
|
+ if (selectedStationIndex < 0 || selectedStationIndex >= contexts.size()) {
|
|
|
+ statusLabel.setText("无工位");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ StationContext context = contexts.get(selectedStationIndex);
|
|
|
+ WorkflowEngine engine = engines.get(selectedStationIndex);
|
|
|
+
|
|
|
+ String state = engine.isRunning() ? (engine.isPaused() ? "已暂停" : "运行中") : "已停止";
|
|
|
+ int current = engine.getCurrentStepIndex() + 1;
|
|
|
+ int total = engine.getSteps().size();
|
|
|
+
|
|
|
+ statusLabel.setText(String.format("状态: %s | 进度: %d/%d | 工件码: %s",
|
|
|
+ state, current, total,
|
|
|
+ context.getProductSn() != null ? context.getProductSn() : "(无)"));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 跳过指定步骤
|
|
|
+ */
|
|
|
+ private void skipStep(int stepIndex) {
|
|
|
+ if (selectedStationIndex < 0 || selectedStationIndex >= engines.size()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ WorkflowEngine engine = engines.get(selectedStationIndex);
|
|
|
+ int currentIndex = engine.getCurrentStepIndex();
|
|
|
+
|
|
|
+ if (stepIndex != currentIndex) {
|
|
|
+ JOptionPane.showMessageDialog(this,
|
|
|
+ "只能跳过当前步骤",
|
|
|
+ "提示",
|
|
|
+ JOptionPane.WARNING_MESSAGE);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ IWorkflowStep step = engine.getSteps().get(stepIndex);
|
|
|
+ int result = JOptionPane.showConfirmDialog(this,
|
|
|
+ String.format("确定要跳过步骤 \"%s\" 吗?\n\n跳过将模拟该步骤执行成功。", step.getStepName()),
|
|
|
+ "确认跳过",
|
|
|
+ JOptionPane.YES_NO_OPTION,
|
|
|
+ JOptionPane.QUESTION_MESSAGE);
|
|
|
+
|
|
|
+ if (result == JOptionPane.YES_OPTION) {
|
|
|
+ boolean success = engine.skipCurrentStep();
|
|
|
+ if (success) {
|
|
|
+ log.info("跳过步骤成功: {}", step.getStepId());
|
|
|
+ refresh();
|
|
|
+ } else {
|
|
|
+ JOptionPane.showMessageDialog(this,
|
|
|
+ "跳过步骤失败",
|
|
|
+ "错误",
|
|
|
+ JOptionPane.ERROR_MESSAGE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 停止刷新定时器
|
|
|
+ */
|
|
|
+ public void stopRefresh() {
|
|
|
+ if (refreshTimer != null) {
|
|
|
+ refreshTimer.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动刷新定时器
|
|
|
+ */
|
|
|
+ public void startRefresh() {
|
|
|
+ if (refreshTimer != null) {
|
|
|
+ refreshTimer.start();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ========== 内部类:步骤面板 ==========
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单个步骤的显示面板
|
|
|
+ */
|
|
|
+ private class StepPanel extends JPanel {
|
|
|
+ private final int stepIndex;
|
|
|
+ private final IWorkflowStep step;
|
|
|
+
|
|
|
+ private JLabel indexLabel;
|
|
|
+ private JLabel nameLabel;
|
|
|
+ private JLabel statusLabel;
|
|
|
+ private JPanel conditionsPanel;
|
|
|
+ private JButton skipButton;
|
|
|
+
|
|
|
+ public StepPanel(int index, IWorkflowStep step, StationContext context,
|
|
|
+ boolean isCurrent, boolean isCompleted) {
|
|
|
+ this.stepIndex = index;
|
|
|
+ this.step = step;
|
|
|
+ initUI();
|
|
|
+ update(context, isCurrent, isCompleted);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initUI() {
|
|
|
+ setLayout(new BorderLayout(10, 5));
|
|
|
+ setBorder(BorderFactory.createCompoundBorder(
|
|
|
+ BorderFactory.createLineBorder(Color.LIGHT_GRAY),
|
|
|
+ new EmptyBorder(8, 10, 8, 10)));
|
|
|
+ setMaximumSize(new Dimension(Integer.MAX_VALUE, 150));
|
|
|
+
|
|
|
+ // 左侧:序号和名称
|
|
|
+ JPanel leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
|
|
|
+ leftPanel.setOpaque(false);
|
|
|
+
|
|
|
+ indexLabel = new JLabel();
|
|
|
+ indexLabel.setFont(new Font("微软雅黑", Font.BOLD, 16));
|
|
|
+ indexLabel.setPreferredSize(new Dimension(30, 30));
|
|
|
+ indexLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ leftPanel.add(indexLabel);
|
|
|
+
|
|
|
+ nameLabel = new JLabel(step.getStepName());
|
|
|
+ nameLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
|
|
|
+ leftPanel.add(nameLabel);
|
|
|
+
|
|
|
+ statusLabel = new JLabel();
|
|
|
+ statusLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
|
|
+ leftPanel.add(statusLabel);
|
|
|
+
|
|
|
+ add(leftPanel, BorderLayout.NORTH);
|
|
|
+
|
|
|
+ // 中间:条件列表
|
|
|
+ conditionsPanel = new JPanel();
|
|
|
+ conditionsPanel.setLayout(new BoxLayout(conditionsPanel, BoxLayout.Y_AXIS));
|
|
|
+ conditionsPanel.setOpaque(false);
|
|
|
+ conditionsPanel.setBorder(new EmptyBorder(5, 40, 5, 10));
|
|
|
+ add(conditionsPanel, BorderLayout.CENTER);
|
|
|
+
|
|
|
+ // 右侧:跳过按钮
|
|
|
+ JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 5, 0));
|
|
|
+ rightPanel.setOpaque(false);
|
|
|
+
|
|
|
+ skipButton = new JButton("跳过");
|
|
|
+ skipButton.setFont(new Font("微软雅黑", Font.PLAIN, 12));
|
|
|
+ skipButton.setPreferredSize(new Dimension(70, 28));
|
|
|
+ skipButton.addActionListener(e -> skipStep(stepIndex));
|
|
|
+ rightPanel.add(skipButton);
|
|
|
+
|
|
|
+ add(rightPanel, BorderLayout.EAST);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新显示状态
|
|
|
+ */
|
|
|
+ public void update(StationContext context, boolean isCurrent, boolean isCompleted) {
|
|
|
+ // 更新背景色
|
|
|
+ if (isCompleted) {
|
|
|
+ setBackground(new Color(232, 245, 233)); // 浅绿
|
|
|
+ indexLabel.setForeground(COLOR_COMPLETED);
|
|
|
+ statusLabel.setText("[已完成]");
|
|
|
+ statusLabel.setForeground(COLOR_COMPLETED);
|
|
|
+ } else if (isCurrent) {
|
|
|
+ setBackground(new Color(227, 242, 253)); // 浅蓝
|
|
|
+ indexLabel.setForeground(COLOR_CURRENT);
|
|
|
+ statusLabel.setText("[当前]");
|
|
|
+ statusLabel.setForeground(COLOR_CURRENT);
|
|
|
+ } else {
|
|
|
+ setBackground(Color.WHITE);
|
|
|
+ indexLabel.setForeground(COLOR_PENDING);
|
|
|
+ statusLabel.setText("[待执行]");
|
|
|
+ statusLabel.setForeground(COLOR_PENDING);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新序号
|
|
|
+ indexLabel.setText(String.valueOf(stepIndex + 1));
|
|
|
+
|
|
|
+ // 更新条件
|
|
|
+ updateConditions(context, isCurrent);
|
|
|
+
|
|
|
+ // 只有当前步骤可以跳过
|
|
|
+ skipButton.setEnabled(isCurrent);
|
|
|
+ skipButton.setVisible(isCurrent);
|
|
|
+
|
|
|
+ revalidate();
|
|
|
+ repaint();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新条件显示
|
|
|
+ */
|
|
|
+ private void updateConditions(StationContext context, boolean isCurrent) {
|
|
|
+ conditionsPanel.removeAll();
|
|
|
+
|
|
|
+ // 只有当前步骤显示详细条件
|
|
|
+ if (isCurrent) {
|
|
|
+ List<IWorkflowStep.StepCondition> conditions = step.getConditions(context);
|
|
|
+
|
|
|
+ if (conditions.isEmpty()) {
|
|
|
+ JLabel noCondLabel = new JLabel(" 无特殊条件");
|
|
|
+ noCondLabel.setFont(new Font("微软雅黑", Font.ITALIC, 11));
|
|
|
+ noCondLabel.setForeground(Color.GRAY);
|
|
|
+ conditionsPanel.add(noCondLabel);
|
|
|
+ } else {
|
|
|
+ for (IWorkflowStep.StepCondition cond : conditions) {
|
|
|
+ JLabel condLabel = new JLabel();
|
|
|
+ String icon = cond.isSatisfied() ? "✓" : "✗";
|
|
|
+ condLabel.setText(" " + icon + " " + cond.getDescription());
|
|
|
+ condLabel.setFont(new Font("微软雅黑", Font.PLAIN, 11));
|
|
|
+ condLabel.setForeground(cond.isSatisfied() ? COLOR_CONDITION_OK : COLOR_CONDITION_FAIL);
|
|
|
+ conditionsPanel.add(condLabel);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 非当前步骤:简单显示步骤信息
|
|
|
+ String info = String.format(" ID: %s | 异步: %s | 需交互: %s",
|
|
|
+ step.getStepId(),
|
|
|
+ step.isAsync() ? "是" : "否",
|
|
|
+ step.requiresUserInteraction() ? "是" : "否");
|
|
|
+ JLabel infoLabel = new JLabel(info);
|
|
|
+ infoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 10));
|
|
|
+ infoLabel.setForeground(Color.GRAY);
|
|
|
+ conditionsPanel.add(infoLabel);
|
|
|
+ }
|
|
|
+
|
|
|
+ conditionsPanel.revalidate();
|
|
|
+ conditionsPanel.repaint();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|