logicFlow ,画布节点自定义

/**
* 判断指定节点是否有连接线关联
* @param nodeId 节点ID
* @returns {boolean} true表示节点有连接线,false表示节点没有连接线
*/
function isNodeConnected(nodeId) {
if (!lf) return false
 
const graphData = lf.getGraphData()
const edges = graphData.edges || []
 
// 检查是否有任何边的起点或终点是该节点
return edges.some(edge =>
edge.sourceNodeId === nodeId || edge.targetNodeId === nodeId
)
}

/**
* 判断画布中的所有节点是否都有连接线关联
* @returns {Object} 返回检查结果对象
* {
* allConnected: boolean, // 是否所有节点都有连接
* totalNodes: number, // 总节点数
* connectedNodes: number, // 已连接的节点数
* unconnectedNodes: Array, // 未连接的节点列表
* message: string // 描述信息
* }
*/
function areAllNodesConnected() {
if (!lf) {
return {
allConnected: false,
totalNodes: 0,
connectedNodes: 0,
unconnectedNodes: [],
message: 'LogicFlow实例未初始化'
}
}
 
const graphData = lf.getGraphData()
const nodes = graphData.nodes || []
const edges = graphData.edges || []
 
// 如果没有节点,返回true(空画布视为满足条件)
if (nodes.length === 0) {
return {
allConnected: true,
totalNodes: 0,
connectedNodes: 0,
unconnectedNodes: [],
message: '画布中没有节点'
}
}
 
// 统计每个节点的连接情况
const unconnectedNodes = []
let connectedCount = 0
 
nodes.forEach(node => {
const hasConnection = edges.some(edge =>
edge.sourceNodeId === node.id || edge.targetNodeId === node.id
)
 
if (hasConnection) {
connectedCount++
} else {
unconnectedNodes.push({
id: node.id,
text: node.text?.value || node.id,
type: node.type
})
}
})
 
const allConnected = unconnectedNodes.length === 0
 
return {
allConnected,
totalNodes: nodes.length,
connectedNodes: connectedCount,
unconnectedNodes,
message: allConnected
? `所有节点(${nodes.length}个)都有连接线关联`
: `有${unconnectedNodes.length}个节点没有连接线关联`
}
}




// 检查所有节点的连接状态
function checkConnections() {
if (!lf) return

const result = areAllNodesConnected()

// 先清除所有高亮
const graphData = lf.getGraphData()
graphData.nodes.forEach(node => {
lf.getNodeModelById(node.id).setProperties('isUnconnected', false)
})

if (result.allConnected) {
ElMessage.success(result.message)
} else {
ElMessage.warning(result.message)
 
// 高亮未连接的节点
result.unconnectedNodes.forEach(nodeInfo => {
const nodeModel = lf.getNodeModelById(nodeInfo.id)
if (nodeModel) {
nodeModel.setProperties('isUnconnected', true)
}
})

// 将视图聚焦到第一个未连接的节点
if (result.unconnectedNodes.length > 0) {
const firstUnconnectedNodeId = result.unconnectedNodes[0].id
lf.focusOnElement(firstUnconnectedNodeId, 1.2)
}
}
}





// 使用 updateEditConfig 来控制画布是否可编辑
lf.updateEditConfig({
isSilentMode: !isEditable.value,
stopZoomGraph: !isEditable.value,
stopScrollGraph: !isEditable.value,
stopMoveGraph: !isEditable.value,
adjustNodePosition: isEditable.value,
nodeTextEdit: isEditable.value,
edgeTextEdit: isEditable.value
})



getShape() {

    const { model } = this.props;

    const { x, y, width, height } = model;

    const style = model.getNodeStyle();

 

    // 1. 基础矩形

    const rect = h('rect', {

      ...style,

      x: x - width / 2,

      y: y - height / 2,

      width,

      height,

      rx: style.borderRadius,

      ry: style.borderRadius,

    });

 

    // 2. 获取文本元素

    const text = this.getText();

 

    // 3. 获取并创建icon元素

    const { icon } = model.properties;

    let iconEl = null;

    if (icon) {

      iconEl = h('image', {

        x: x - width / 2 + 5, // 5px 左边距

        y: y - height / 2 + 5, // 5px 上边距

        width: 20,

        height: 20,

        href: icon,

      });

    }

 

    // 4. 组合所有元素

    return h('g', {}, [rect, text, iconEl]);

  }




function startDrag(type: string, item: PaletteItem) {

  isDraggingFromPalette = true; // 设置拖动状态标志

  hideNodeTooltip();

  if (!lf) return;

 

  const { label: text, key, icon } = item;

  console.log('开始拖拽,参数:', { type, text, key, icon });

  currentDragKey = key || null;

 

  const dragData = {

    type,

    text,

    properties: { 

      paletteKey: key,

      icon: icon // 传递icon属性

    } as any

  };

 

  console.log('拖拽数据:', dragData);

  

  try {

    lf.dnd.startDrag(dragData);

    console.log('拖拽启动成功');

  } catch (error) {

    console.error('拖拽启动失败:', error);

  }

}

 

————————————————————————————————————————————————————————————————————————
 
 
 

// 监听选中的组件类型变化,改变对应画布节点的背景色

watch(selectedComponentType, (newType) => {

  if (!lf) return;

 

  const graphData = lf.getGraphData();

 

  graphData.nodes.forEach(node => {

    const nodeModel = lf.getNodeModelById(node.id);

    if (nodeModel) {

      const { componentType } = nodeModel.properties;

      let newBackgroundColor = '#f0f9ff'; // 默认颜色

 

      // 如果选中了“全部”,或者当前节点的类型与选中类型匹配,则应用高亮颜色

      if (newType === 'all' || componentType === newType) {

        const typeConfig = componentTypes.value.find(t => t.key === componentType);

        if (typeConfig) {

          newBackgroundColor = typeConfig.color;

        }

      }

      

      // 设置 backgroundColor 属性

      nodeModel.setProperties({

        backgroundColor: newBackgroundColor

      });

      // 显式更新样式以触发重绘

      nodeModel.updateStyles();

    }

  });

});






getNodeStyle() {

    const style = super.getNodeStyle()

    const { isUnconnected, isHighlighted } = this.properties

 

    let stroke = '#3b82f6' // 默认颜色

    let strokeWidth = 2

 

    if (isHighlighted) {

      stroke = '#f59e0b' // 高亮颜色

      strokeWidth = 3

    }

 

    if (isUnconnected) {

      stroke = '#ef4444' // 未连接状态的颜色优先级更高

      strokeWidth = 3

    }

 

    // Directly modify the style object from the super class

    style.fill = this.properties.backgroundColor || '#f0f9ff';

    style.stroke = stroke;

    style.strokeWidth = strokeWidth;

    style.borderRadius = 8;

 

    return style;

  }

 

 

 

 

 

// 添加更多拖拽相关事件监听
lf.on('node:dnd-add', ({ data: nodeData }) => {
if (nodeData) {
// 1. 与组件面板联动
let key = nodeData.properties?.paletteKey || currentDragKey;
if (key) {
lf.setProperties(nodeData.id, { paletteKey: key });
setPaletteItemUsed(key, true);
} else {
console.log('未找到 paletteKey,无法隐藏组件');
}

// 2. 新增:检查并应用高亮状态
const { componentType } = nodeData.properties;
const currentSelectedType = selectedComponentType.value;

if (componentType && (currentSelectedType === 'all' || currentSelectedType === componentType)) {
const typeConfig = componentTypes.value.find(t => t.key === componentType);
if (typeConfig) {
const newBackgroundColor = typeConfig.color;
const nodeModel = lf.getNodeModelById(nodeData.id);
if (nodeModel) {
// 使用 setTimeout 确保节点已完全渲染
setTimeout(() => {
nodeModel.setProperties({
backgroundColor: newBackgroundColor
});
nodeModel.updateStyles();
}, 0);
}
}
}

// 3. 清理工作
currentDragKey = null;
}

// 拖放完成后,强制隐藏tooltip并重置拖动状态标志
hideNodeTooltip();
isDraggingFromPalette = false;
});




  // 重写getData方法,确保自定义属性被序列化

  getData() {

    const data = super.getData();

    data.properties = {

      ...data.properties,

      backgroundColor: this.properties.backgroundColor

    };

    return data;

  }






 

 

 

// 监听 History 撤销/重做及历史状态变化

  lf.on('history:undo', () => {

    console.log('[Control] 撤销(上一步)完成')

  })

  lf.on('history:redo', () => {

    console.log('[Control] 重做(下一步)完成')

  })

  lf.on('history:change', ({ data }) => {

    const { undoAble, redoAble, canUndo, canRedo } = data || {}

    const canUndoNow = typeof undoAble === 'boolean' ? undoAble : !!canUndo

    const canRedoNow = typeof redoAble === 'boolean' ? redoAble : !!canRedo

    console.log('[Control] 历史状态变化 -> canUndo:', canUndoNow, 'canRedo:', canRedoNow, data)

  })

 

  // 可选:监听 Control 按钮点击(不同版本可能没有该事件)

  lf.on?.('control:click', (payload) => {

    console.log('[Control] 按钮点击:', payload)

  })

 

  // 兜底:劫持 undo/redo 方法,确保任何来源都会打印日志

  if (typeof lf.undo === 'function') {

    const origUndo = lf.undo.bind(lf)

    lf.undo = (...args) => {

      const ret = origUndo(...args)

      try { console.log('[Control] 撤销(上一步)完成(hook)') } catch {}

      return ret

    }

  }

  if (typeof lf.redo === 'function') {

    const origRedo = lf.redo.bind(lf)

    lf.redo = (...args) => {

      const ret = origRedo(...args)

      try { console.log('[Control] 重做(下一步)完成(hook)') } catch {}

      return ret

    }

  }



function isValidURL(url) {
    const urlPattern = /^(https?:\/\/)?(www\.)?([a-zA-Z0-9-]+(\.[a-zA-Z]{2,})+)(:[0-9]{1,5})?(\/[^\s]*)?$/;
    return urlPattern.test(url);
}

 

posted @ 2025-11-16 22:04  Action_swt  阅读(168)  评论(0)    收藏  举报