当前位置: 首页 > news >正文

logicflow___文档3

# LogicFlow 流程图编辑器开发文档

## 📖 概述

这是一个基于 LogicFlow 的流程图编辑器完整开发指南,包含详细的代码示例和实现方案。该编辑器支持节点创建、连接线绘制、关系管理等功能。

## 🚀 快速开始

### 1. 安装依赖

```bash
npm install @logicflow/core @logicflow/extension element-plus
```

### 2. 基础引入

```javascript
// 核心库
import LogicFlow from '@logicflow/core'
import '@logicflow/core/dist/index.css'

// 扩展插件
import { Menu, SelectionSelect, Control, MiniMap, Snapshot } from '@logicflow/extension'
import '@logicflow/extension/dist/index.css'

// Vue 3 + Element Plus
import { onMounted, onBeforeUnmount, ref, reactive } from 'vue'
import { ElMessage, ElDialog, ElButton, ElForm, ElFormItem, ElInput } from 'element-plus'
```

## 📋 完整组件实现

### 1. 基础模板结构

```vue
<template>
<div class="logicflow-page">
<!-- 侧边栏 -->
<div class="sidebar">
<!-- 组件面板 -->
<div class="palette-title">组件面板</div>
<div
v-for="nodeType in nodeTypes"
:key="nodeType.type"
class="palette-item"
@mousedown="startDrag(nodeType.type, nodeType.text)"
>
{{ nodeType.text }}
</div>

<!-- 关联关系选择器 -->
<div class="palette-title" style="margin-top: 20px;">关联关系</div>
<div class="edge-type-selector">
<div
v-for="edgeType in edgeTypes"
:key="edgeType.type"
class="edge-type-item"
:class="{ active: selectedEdgeType === edgeType.type }"
@click="selectEdgeType(edgeType.type)"
>
<div :class="['edge-preview', edgeType.type + '-line']"></div>
<span>{{ edgeType.label }}</span>
</div>
</div>

<!-- 操作说明 -->
<div class="palette-title" style="margin-top: 20px;">操作说明</div>
<div class="help-text">
<p>• 点击节点/连接线:编辑文字</p>
<p>• 右键节点/连接线:删除</p>
<p>• Delete/Backspace:删除选中元素</p>
<p>• Ctrl+A:全选</p>
<p>• Ctrl+C:复制</p>
<p>• Ctrl+V:粘贴</p>
</div>
</div>

<!-- 画布区域 -->
<div class="canvas-wrap">
<div ref="lfContainerRef" class="lf-container"></div>
</div>

<!-- 节点配置对话框 -->
<el-dialog v-model="nodeDialogVisible" title="节点配置" width="600px">
<el-form label-width="100px">
<el-form-item label="节点ID">
<el-input v-model="selectedNodeId" disabled />
</el-form-item>
<el-form-item label="显示文本">
<el-input v-model="selectedNodeText" placeholder="请输入节点文本" />
</el-form-item>
<el-form-item label="节点类型">
<el-input v-model="selectedNodeType" placeholder="请输入节点类型" />
</el-form-item>
<el-form-item label="节点描述">
<el-input
v-model="selectedNodeDescription"
type="textarea"
:rows="2"
placeholder="请输入节点描述"
/>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="nodeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="applyNodeChange">保存</el-button>
</div>
</template>
</el-dialog>

<!-- 连接线配置对话框 -->
<el-dialog v-model="edgeDialogVisible" title="连接线配置" width="500px">
<el-form label-width="100px">
<el-form-item label="连接线ID">
<el-input v-model="selectedEdgeId" disabled />
</el-form-item>
<el-form-item label="显示文本">
<el-input v-model="selectedEdgeText" placeholder="请输入连接线文本" />
</el-form-item>
<el-form-item label="线条类型">
<el-select v-model="selectedEdgeLineType" placeholder="选择线条类型">
<el-option label="实线" value="solid" />
<el-option label="虚线" value="dashed" />
<el-option label="点线" value="dotted" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="edgeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="applyEdgeChange">保存</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
```

### 2. 核心脚本实现

```javascript
<script setup>
import { onMounted, onBeforeUnmount, ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import LogicFlow from '@logicflow/core'
import { Menu, SelectionSelect, Control, MiniMap, Snapshot } from '@logicflow/extension'
import { RectNode, RectNodeModel, LineEdge, LineEdgeModel, h } from '@logicflow/core'
import '@logicflow/core/dist/index.css'
import '@logicflow/extension/dist/index.css'

// ==================== 响应式数据 ====================
const lfContainerRef = ref(null)
let lf = null

// 对话框状态
const nodeDialogVisible = ref(false)
const edgeDialogVisible = ref(false)

// 选中的元素数据
const selectedNodeId = ref('')
const selectedNodeText = ref('')
const selectedNodeType = ref('')
const selectedNodeDescription = ref('')

const selectedEdgeId = ref('')
const selectedEdgeText = ref('')
const selectedEdgeType = ref('solid')
const selectedEdgeLineType = ref('solid')

// 组件配置
const nodeTypes = [
{ type: 'custom-rect', text: '矩形' },
{ type: 'circle', text: '圆形' },
{ type: 'diamond', text: '菱形' },
{ type: 'text', text: '文本' }
]

const edgeTypes = [
{ type: 'solid', label: '包含关系' },
{ type: 'dashed', label: '关联关系' },
{ type: 'dotted', label: '使用关系' }
]

// ==================== 自定义节点组件 ====================
// 自定义矩形节点
class CustomRectNode extends RectNode {}

class CustomRectNodeModel extends RectNodeModel {
constructor(data, graphModel) {
super(data, graphModel)
// 设置默认尺寸
this.width = 180
this.height = 40
}

// 自定义节点样式
getNodeStyle() {
const style = super.getNodeStyle()
return {
...style,
fill: '#f0f9ff', // 填充色
stroke: '#3b82f6', // 边框色
strokeWidth: 2, // 边框宽度
borderRadius: 8, // 圆角
}
}

// 自定义文字样式
getTextStyle() {
const style = super.getTextStyle()
return {
...style,
fontSize: 14,
fontWeight: 'bold',
fill: '#1e40af',
}
}

getWidth() {
return this.width
}

getHeight() {
return this.height
}
}

// ==================== 自定义连接线组件 ====================
class CustomEdge extends LineEdge {
// 自定义连线文字位置
getText() {
const { model } = this.props
const text = model.text?.value || ''
 
if (!text.trim()) return null

// 获取连线坐标
let startX, startY, endX, endY
 
if (model.x1 !== undefined && model.x2 !== undefined) {
startX = model.x1
startY = model.y1
endX = model.x2
endY = model.y2
} else if (model.startPoint && model.endPoint) {
startX = model.startPoint.x
startY = model.startPoint.y
endX = model.endPoint.x
endY = model.endPoint.y
} else {
const sourceNode = model.graphModel.getNodeModelById(model.sourceNodeId)
const targetNode = model.graphModel.getNodeModelById(model.targetNodeId)
if (sourceNode && targetNode) {
startX = sourceNode.x
startY = sourceNode.y
endX = targetNode.x
endY = targetNode.y
} else {
return null
}
}
 
// 计算中心点
const centerX = (startX + endX) / 2
const centerY = (startY + endY) / 2
 
// 获取文字样式
const textColor = model.properties?.textColor || '#374151'
const textSize = model.properties?.textSize || 12
 
// 渲染文字
return h('text', {
x: centerX,
y: centerY,
textAnchor: 'middle',
dominantBaseline: 'central',
fontSize: textSize,
fill: textColor,
fontFamily: 'Arial, sans-serif',
fontWeight: '500'
}, text)
}
}

class CustomEdgeModel extends LineEdgeModel {
// 自定义边样式
getEdgeStyle() {
const style = super.getEdgeStyle()
const lineType = this.properties?.lineType || 'solid'
 
// 根据线条类型设置不同样式
const lineStyles = {
solid: {
stroke: '#3b82f6',
strokeWidth: 2,
strokeDasharray: '0',
},
dashed: {
stroke: '#10b981',
strokeWidth: 2,
strokeDasharray: '8,4',
},
dotted: {
stroke: '#f59e0b',
strokeWidth: 2,
strokeDasharray: '2,2',
}
}
 
return {
...style,
...lineStyles[lineType]
}
}
 
// 自定义箭头样式
getArrowStyle() {
const style = super.getArrowStyle()
const lineType = this.properties?.lineType || 'solid'
 
const arrowColors = {
solid: '#3b82f6',
dashed: '#10b981',
dotted: '#f59e0b'
}
 
return {
...style,
fill: arrowColors[lineType],
stroke: arrowColors[lineType],
strokeWidth: 1,
}
}
}

// ==================== 核心方法 ====================
// 拖拽创建节点
function startDrag(type, text) {
if (!lf) return
lf.dnd.startDrag({ type, text })
}

// 选择边类型
function selectEdgeType(edgeType) {
selectedEdgeType.value = edgeType
if (lf) {
lf.setDefaultEdgeType('custom-edge')
}
}

// 打开节点配置对话框
function openNodeDialog(nodeData) {
selectedNodeId.value = nodeData.id
selectedNodeText.value = nodeData.text?.value || ''
selectedNodeType.value = nodeData.properties?.nodeType || ''
selectedNodeDescription.value = nodeData.properties?.description || ''
nodeDialogVisible.value = true
}

// 打开连接线配置对话框
function openEdgeDialog(edgeData) {
selectedEdgeId.value = edgeData.id
selectedEdgeText.value = edgeData.text?.value || ''
selectedEdgeLineType.value = edgeData.properties?.lineType || 'solid'
edgeDialogVisible.value = true
}

// 应用节点更改
function applyNodeChange() {
if (!selectedNodeId.value) return
 
// 更新节点属性
lf.setProperties(selectedNodeId.value, {
text: selectedNodeText.value,
nodeType: selectedNodeType.value,
description: selectedNodeDescription.value
})
 
// 更新节点文本
lf.updateText(selectedNodeId.value, selectedNodeText.value)
 
nodeDialogVisible.value = false
ElMessage.success('已更新节点配置')
}

// 应用连接线更改
function applyEdgeChange() {
if (!selectedEdgeId.value) return
 
// 更新连接线属性
lf.setProperties(selectedEdgeId.value, {
text: selectedEdgeText.value,
lineType: selectedEdgeLineType.value,
textColor: getLineTypeColor(selectedEdgeLineType.value),
textSize: 12
})
 
// 更新连接线文本
lf.updateText(selectedEdgeId.value, selectedEdgeText.value)
 
edgeDialogVisible.value = false
ElMessage.success('已更新连接线配置')
}

// 获取线条类型对应的颜色
function getLineTypeColor(lineType) {
const colors = {
solid: '#3b82f6',
dashed: '#10b981',
dotted: '#f59e0b'
}
return colors[lineType] || '#374151'
}

// 绑定事件监听
function bindEvents() {
// 监听节点点击
lf.on('node:click', ({ data }) => {
openNodeDialog(data)
})

// 监听连接线点击
lf.on('edge:click', ({ data }) => {
openEdgeDialog(data)
})

// 监听连接线双击
lf.on('edge:dbclick', ({ data }) => {
openEdgeDialog(data)
})

// 监听边创建
lf.on('edge:add', ({ data }) => {
const lineType = selectedEdgeType.value
const relationLabel = getRelationLabel(lineType)
 
// 设置新边的属性
lf.setProperties(data.id, {
lineType: lineType,
textColor: getLineTypeColor(lineType),
textSize: 12
})
 
// 设置连线文本
lf.updateText(data.id, relationLabel)
 
console.log('新连接线创建:', data)
})

// 监听键盘删除
lf.on('keydown', ({ data }) => {
if (data.key === 'Delete' || data.key === 'Backspace') {
const selectedElements = lf.getSelectElements()
if (selectedElements.nodes.length > 0 || selectedElements.edges.length > 0) {
lf.deleteSelectElements()
ElMessage.success('已删除选中元素')
}
}
})

// 监听右键菜单
lf.on('node:contextmenu', ({ data }) => {
ElMessage.info(`右键点击节点: ${data.text?.value || data.id}`)
})

lf.on('edge:contextmenu', ({ data }) => {
ElMessage.info(`右键点击连接线: ${data.text?.value || data.id}`)
})
}

// 获取关系标签
function getRelationLabel(lineType) {
const labels = {
solid: '包含',
dashed: '关联',
dotted: '使用'
}
return labels[lineType] || '关系'
}

// ==================== 生命周期钩子 ====================
onMounted(() => {
// 初始化 LogicFlow
lf = new LogicFlow({
container: lfContainerRef.value,
grid: true, // 显示网格
keyboard: { enabled: true }, // 启用键盘快捷键
snapline: true, // 启用对齐线
edgeType: 'custom-edge', // 默认连接线类型
plugins: [Menu, SelectionSelect, Control, MiniMap, Snapshot]
})

// 注册自定义矩形节点
lf.register({
type: 'custom-rect',
view: CustomRectNode,
model: CustomRectNodeModel,
})

// 注册自定义连接线
lf.register({
type: 'custom-edge',
view: CustomEdge,
model: CustomEdgeModel,
})

// 绑定事件
bindEvents()

// 渲染初始示例数据
lf.render({
nodes: [
{ id: 'n1', type: 'custom-rect', x: 200, y: 80, text: '开始' },
{ id: 'n2', type: 'custom-rect', x: 420, y: 80, text: '处理' },
{ id: 'n3', type: 'custom-rect', x: 640, y: 80, text: '结束' }
],
edges: [
{
id: 'e1',
sourceNodeId: 'n1',
targetNodeId: 'n2',
text: '包含',
properties: {
lineType: 'solid',
textColor: '#3b82f6',
textSize: 12
}
},
{
id: 'e2',
sourceNodeId: 'n2',
targetNodeId: 'n3',
text: '关联',
properties: {
lineType: 'dashed',
textColor: '#10b981',
textSize: 12
}
}
]
})

console.log('LogicFlow 初始化完成')
})

onBeforeUnmount(() => {
if (lf) {
lf.destroy()
lf = null
}
})
</script>
```

### 3. 样式定义

```scss
<style scoped>
.logicflow-page {
display: flex;
height: calc(100vh - 120px);
padding: 12px;
box-sizing: border-box;
}

.sidebar {
width: 200px;
border-right: 1px solid #eee;
padding-right: 12px;
background: #fafafa;
}

.palette-title {
font-weight: 600;
margin-bottom: 8px;
color: #333;
}

.palette-item {
background: #f6f7fb;
border: 1px dashed #c0c4cc;
padding: 8px 10px;
margin-bottom: 8px;
cursor: grab;
border-radius: 4px;
user-select: none;
transition: all 0.2s;
}

.palette-item:hover {
border-color: #409EFF;
background: #f0f9ff;
transform: translateY(-1px);
}

.palette-item:active {
cursor: grabbing;
transform: scale(0.95);
}

.help-text {
font-size: 12px;
color: #666;
line-height: 1.5;
}

.help-text p {
margin: 4px 0;
}

.canvas-wrap {
flex: 1;
padding-left: 12px;
}

.lf-container {
width: 100%;
height: 100%;
background: #fff;
border: 1px solid #eee;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* 边类型选择器样式 */
.edge-type-selector {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 12px;
}

.edge-type-item {
display: flex;
align-items: center;
padding: 8px 10px;
border: 1px solid #e5e7eb;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
background: #f9fafb;
}

.edge-type-item:hover {
border-color: #3b82f6;
background: #eff6ff;
}

.edge-type-item.active {
border-color: #3b82f6;
background: #dbeafe;
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
}

.edge-preview {
width: 30px;
height: 2px;
margin-right: 10px;
position: relative;
}

.solid-line {
background: #3b82f6;
}

.dashed-line {
background: linear-gradient(to right, #10b981 0%, #10b981 50%, transparent 50%, transparent 100%);
background-size: 8px 2px;
}

.dotted-line {
background: linear-gradient(to right, #f59e0b 0%, #f59e0b 25%, transparent 25%, transparent 50%, #f59e0b 50%, #f59e0b 75%, transparent 75%, transparent 100%);
background-size: 4px 2px;
}

/* 对话框样式 */
.dialog-footer {
text-align: right;
}

.dialog-footer .el-button {
margin-left: 10px;
}

/* LogicFlow 样式优化 */
:deep(.lf-edge-text) {
pointer-events: none;
}

:deep(.lf-edge-text rect),
:deep(.lf-edge-text-bg) {
fill: transparent !important;
stroke: transparent !important;
display: none !important;
}

:deep(.lf-edge text) {
text-anchor: middle !important;
dominant-baseline: central !important;
user-select: none;
filter: drop-shadow(1px 1px 1px rgba(255, 255, 255, 0.8));
}
</style>
```

## 🔧 高级功能实现

### 1. 数据持久化

```javascript
// 保存数据到本地存储
function saveData() {
const graphData = lf.getGraphData()
localStorage.setItem('logicflow-data', JSON.stringify(graphData))
ElMessage.success('数据已保存')
}

// 从本地存储加载数据
function loadData() {
const savedData = localStorage.getItem('logicflow-data')
if (savedData) {
const graphData = JSON.parse(savedData)
lf.render(graphData)
ElMessage.success('数据已加载')
} else {
ElMessage.warning('没有找到保存的数据')
}
}

// 导出为JSON文件
function exportJSON() {
const graphData = lf.getGraphData()
const dataStr = JSON.stringify(graphData, null, 2)
const dataBlob = new Blob([dataStr], {type: 'application/json'})
const url = URL.createObjectURL(dataBlob)
const link = document.createElement('a')
link.href = url
link.download = 'logicflow-diagram.json'
link.click()
URL.revokeObjectURL(url)
ElMessage.success('JSON文件已导出')
}

// 导入JSON文件
function importJSON(file) {
const reader = new FileReader()
reader.onload = (e) => {
try {
const graphData = JSON.parse(e.target.result)
lf.render(graphData)
ElMessage.success('JSON文件已导入')
} catch (error) {
ElMessage.error('JSON文件格式错误')
}
}
reader.readAsText(file)
}
```

### 2. 图片导出

```javascript
// 导出为PNG图片
function exportPNG() {
lf.getSnapshot().then(dataUrl => {
const link = document.createElement('a')
link.download = 'logicflow-diagram.png'
link.href = dataUrl
link.click()
ElMessage.success('PNG图片已导出')
}).catch(error => {
ElMessage.error('导出失败: ' + error.message)
})
}

// 导出为SVG
function exportSVG() {
const svgData = lf.getSvgData()
const svgBlob = new Blob([svgData], {type: 'image/svg+xml'})
const url = URL.createObjectURL(svgBlob)
const link = document.createElement('a')
link.href = url
link.download = 'logicflow-diagram.svg'
link.click()
URL.revokeObjectURL(url)
ElMessage.success('SVG文件已导出')
}
```

### 3. 批量操作

```javascript
// 全选
function selectAll() {
lf.selectAll()
ElMessage.info('已全选所有元素')
}

// 清空画布
function clearCanvas() {
lf.clearData()
ElMessage.success('画布已清空')
}

// 撤销/重做
function undo() {
if (lf.history.canUndo) {
lf.history.undo()
ElMessage.success('已撤销')
} else {
ElMessage.warning('没有可撤销的操作')
}
}

function redo() {
if (lf.history.canRedo) {
lf.history.redo()
ElMessage.success('已重做')
} else {
ElMessage.warning('没有可重做的操作')
}
}

// 复制粘贴
function copy() {
const selectedElements = lf.getSelectElements()
if (selectedElements.nodes.length > 0 || selectedElements.edges.length > 0) {
lf.cloneSelectElements()
ElMessage.success('已复制选中元素')
} else {
ElMessage.warning('请先选择要复制的元素')
}
}
```

### 4. 自定义工具栏

```vue
<template>
<!-- 工具栏 -->
<div class="toolbar">
<el-button-group>
<el-button @click="saveData" icon="Document">保存</el-button>
<el-button @click="loadData" icon="FolderOpened">加载</el-button>
<el-button @click="clearCanvas" icon="Delete">清空</el-button>
</el-button-group>
 
<el-button-group>
<el-button @click="undo" icon="RefreshLeft">撤销</el-button>
<el-button @click="redo" icon="RefreshRight">重做</el-button>
</el-button-group>
 
<el-button-group>
<el-button @click="selectAll" icon="Select">全选</el-button>
<el-button @click="copy" icon="CopyDocument">复制</el-button>
</el-button-group>
 
<el-button-group>
<el-button @click="exportPNG" icon="Picture">导出PNG</el-button>
<el-button @click="exportJSON" icon="Download">导出JSON</el-button>
</el-button-group>
</div>
</template>

<style>
.toolbar {
padding: 10px;
border-bottom: 1px solid #eee;
background: #f5f5f5;
display: flex;
gap: 10px;
align-items: center;
}
</style>
```

## 📊 数据结构说明

### 1. 节点数据结构

```javascript
// 节点数据示例
const nodeData = {
id: 'node_1', // 节点唯一ID
type: 'custom-rect', // 节点类型
x: 200, // X坐标
y: 100, // Y坐标
text: { // 文本信息
value: '节点文本',
x: 200,
y: 100
},
properties: { // 自定义属性
nodeType: '处理节点',
description: '这是一个处理节点',
color: '#ff0000',
customData: {}
}
}
```

### 2. 连接线数据结构

```javascript
// 连接线数据示例
const edgeData = {
id: 'edge_1', // 连接线唯一ID
type: 'custom-edge', // 连接线类型
sourceNodeId: 'node_1', // 源节点ID
targetNodeId: 'node_2', // 目标节点ID
startPoint: { // 起始点坐标
x: 290,
y: 100
},
endPoint: { // 结束点坐标
x: 410,
y: 100
},
text: { // 文本信息
value: '连接线文本',
x: 350,
y: 100
},
properties: { // 自定义属性
lineType: 'solid', // 线条类型: solid/dashed/dotted
textColor: '#3b82f6', // 文字颜色
textSize: 12, // 文字大小
arrowType: 'default', // 箭头类型
relationType: 'contains' // 关系类型
}
}
```

### 3. 完整图形数据

```javascript
// 图形数据示例
const graphData = {
nodes: [
{
id: 'n1',
type: 'custom-rect',
x: 200,
y: 80,
text: '开始节点',
properties: {
nodeType: 'start',
description: '流程开始'
}
},
{
id: 'n2',
type: 'custom-rect',
x: 400,
y: 80,
text: '处理节点',
properties: {
nodeType: 'process',
description: '数据处理'
}
}
],
edges: [
{
id: 'e1',
sourceNodeId: 'n1',
targetNodeId: 'n2',
text: '包含关系',
properties: {
lineType: 'solid',
relationType: 'contains',
textColor: '#3b82f6'
}
}
]
}
```

## 🎯 最佳实践

### 1. 性能优化

```javascript
// 大量节点时的性能优化
const performanceConfig = {
// 开启虚拟滚动
virtual: true,
// 设置可视区域
viewport: {
width: 800,
height: 600
},
// 禁用不必要的动画
animation: false,
// 优化渲染
optimize: true
}

// 初始化时应用配置
lf = new LogicFlow({
container: lfContainerRef.value,
...performanceConfig
})
```

### 2. 错误处理

```javascript
// 安全的操作包装
function safeOperation(operation, errorMessage = '操作失败') {
try {
return operation()
} catch (error) {
console.error('LogicFlow操作错误:', error)
ElMessage.error(errorMessage + ': ' + error.message)
return null
}
}

// 使用示例
function safeAddNode(nodeData) {
return safeOperation(() => {
return lf.addNode(nodeData)
}, '添加节点失败')
}

function safeUpdateText(elementId, text) {
return safeOperation(() => {
lf.updateText(elementId, text)
}, '更新文本失败')
}
```

### 3. 数据验证

```javascript
// 节点数据验证
function validateNodeData(nodeData) {
if (!nodeData.id) {
throw new Error('节点ID不能为空')
}
if (!nodeData.type) {
throw new Error('节点类型不能为空')
}
if (typeof nodeData.x !== 'number' || typeof nodeData.y !== 'number') {
throw new Error('节点坐标必须为数字')
}
return true
}

// 连接线数据验证
function validateEdgeData(edgeData) {
if (!edgeData.id) {
throw new Error('连接线ID不能为空')
}
if (!edgeData.sourceNodeId || !edgeData.targetNodeId) {
throw new Error('连接线必须指定源节点和目标节点')
}
if (edgeData.sourceNodeId === edgeData.targetNodeId) {
throw new Error('连接线不能连接自身')
}
return true
}
```

## 🐛 常见问题解决

### 1. 文字显示问题

```javascript
// 问题:连接线文字不显示
// 解决:检查文字样式和位置计算

// 正确的文字渲染方式
getText() {
const { model } = this.props
const text = model.text?.value || ''
 
if (!text.trim()) return null
 
// 确保坐标计算正确
const centerX = (model.x1 + model.x2) / 2
const centerY = (model.y1 + model.y2) / 2
 
return h('text', {
x: centerX,
y: centerY,
textAnchor: 'middle',
dominantBaseline: 'central',
fontSize: 12,
fill: '#333'
}, text)
}
```

### 2. 样式不生效

```scss
/* 问题:自定义样式不生效 */
/* 解决:使用 :deep() 选择器 */

:deep(.lf-node-rect) {
fill: #f0f9ff !important;
stroke: #3b82f6 !important;
}

:deep(.lf-edge-path) {
stroke: #10b981 !important;
stroke-width: 2px !important;
}
```

### 3. 事件监听失效

```javascript
// 问题:事件监听器没有响应
// 解决:确保在正确的时机绑定事件

onMounted(() => {
// 先初始化LogicFlow
lf = new LogicFlow(config)
 
// 再注册组件
registerComponents()
 
// 最后绑定事件(重要!)
bindEvents()
 
// 渲染数据
renderData()
})
```

## 📚 API 参考

### 核心API

| 方法 | 说明 | 参数 | 返回值 |
|------|------|------|--------|
| `lf.render(data)` | 渲染图形数据 | `data: GraphData` | `void` |
| `lf.getGraphData()` | 获取图形数据 | - | `GraphData` |
| `lf.addNode(nodeData)` | 添加节点 | `nodeData: NodeData` | `NodeModel` |
| `lf.addEdge(edgeData)` | 添加连接线 | `edgeData: EdgeData` | `EdgeModel` |
| `lf.deleteNode(nodeId)` | 删除节点 | `nodeId: string` | `boolean` |
| `lf.deleteEdge(edgeId)` | 删除连接线 | `edgeId: string` | `boolean` |
| `lf.updateText(elementId, text)` | 更新文本 | `elementId: string, text: string` | `void` |
| `lf.setProperties(elementId, properties)` | 设置属性 | `elementId: string, properties: object` | `void` |

### 事件API

| 事件 | 说明 | 回调参数 |
|------|------|----------|
| `node:click` | 节点点击 | `{ data: NodeData }` |
| `edge:click` | 连接线点击 | `{ data: EdgeData }` |
| `node:add` | 节点添加 | `{ data: NodeData }` |
| `edge:add` | 连接线添加 | `{ data: EdgeData }` |
| `node:delete` | 节点删除 | `{ data: NodeData }` |
| `edge:delete` | 连接线删除 | `{ data: EdgeData }` |

---

*本文档提供了LogicFlow的完整开发指南,包含详细的代码示例和最佳实践。适用于Vue 3 + Element Plus技术栈。*
http://www.hskmm.com/?act=detail&tid=12442

相关文章:

  • 2025年运营商API安全建设最佳实践:某头部省级电信案例解析与方案推荐
  • 软件工程第二次作业-第一次个人编程作业
  • 面向对象入门2与类的识别
  • 202508_天山固网_to
  • jmeter分布式压测
  • 怎么屏蔽 ahref.com 上你不想看到的网站链接(垃圾外链)
  • 浅谈字典树
  • go-mapus为局域网地图协作而生
  • 《手搓动态顺序表:从数组到自动扩容的华丽转身》 - 详解
  • 板子大全
  • 通过人大金仓数据库的逻辑备份与还原功能实现数据迁移
  • 第十二节:订单普通下单、支付回调、退款、退款回调详解
  • 《原子习惯》-读书笔记7
  • 第3周预习作业
  • 《原子习惯》-读书笔记6
  • Java LTS版本进化秀:从8到21的欢乐升级之旅
  • 201912_EASER
  • 搜索百科(3):Elasticsearch — 搜索界的“流量明星”
  • 打印机漏洞、匿名协议与AWS安全:一周技术热点解析
  • 从零开始训练推理模型:GRPO+Unsloth改造Qwen实战指南
  • ALLinSSL,开源免费的SSL证书自动化管理平台
  • 《原子习惯》-读书笔记5
  • 03-袁东申论-概括原因
  • 包和final
  • 实现双向循环链表 - 详解
  • 2025-09-21 网站前几分钟还运行的好好地,几分钟后查看居然显示文件无法加载,访问首页提示无法访问此网站??!==ssl证书过期+域名解析失效
  • 20231321王曦轶《密码系统设计》第二周
  • 爱锋拍照工具 - 隐私政策
  • 周计划+总结
  • [POI 2004] MOS