vue-flow 是一個基于 Vue.js 的強大且靈活的可視化流程圖庫,它允許開發者輕松創建交互式的流程圖、工作流圖、節點圖等。
主要特點
- 易于使用 :提供了簡潔的 API 和組件,開發者可以快速上手并創建復雜的流程圖。
- 高度可定制 :支持自定義節點、邊、連接點等元素的樣式和行為,能夠滿足不同項目的設計需求。
- 交互功能豐富 :內置了節點拖拽、連接、縮放、平移等交互功能,增強用戶體驗。
- 響應式設計 :能夠自適應不同的屏幕尺寸和容器大小,保證在各種設備上都有良好的顯示效果。
- 性能優化 :采用了高效的渲染機制,即使在處理大量節點和邊時也能保持流暢的性能。
組件
- VueFlow :主組件,用于包裹整個流程圖區域,負責管理流程圖的狀態和交互。
- Node :表示流程圖中的節點,可以自定義節點的內容和樣式。
- Edge :表示節點之間的連接邊,支持不同的連接類型和樣式。
- Handle :連接點組件,用于定義節點上的輸入和輸出點,用戶可以通過拖拽連接點來創建邊。
- @vue-flow/background 用于在流程圖的背景添加網格或圖案,增強流程圖的視覺效果,方便用戶更直觀地定位節點和邊。
- @vue-flow/controls 用于控制流程圖視圖的工具按鈕,包含縮放、平移等常用操作。
- @vue-flow/minimap 用于顯示流程圖的縮略圖,方便用戶在處理大型復雜流程圖時快速導航到指定區域。
安裝
# 創建vue項目
yarn create vue@latest
cd <your-project-name># 安裝依賴
yarn
yarn add @vue-flow/core
yarn add @vue-flow/minimap
yarn add @vue-flow/controls
yarn add @vue-flow/background# 運行
yarn dev
使用示例
index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><link rel="icon" href="/favicon.ico" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Vite App</title></head><body><div id="app"></div><script type="module" src="/src/main.js"></script></body>
</html>
main.js
import { createApp } from "vue";
import "./assets/index.css";
import App from "./App.vue";createApp(App).mount("#app");
index.css
/* import the required styles */
@import '@vue-flow/core/dist/style.css';/* import the default theme (optional) */
@import '@vue-flow/core/dist/theme-default.css';@import '@vue-flow/controls/dist/style.css';
@import '@vue-flow/minimap/dist/style.css';body {color: #111;padding: 5px;
}#app {text-transform: uppercase;font-weight: 600;font-family: 'JetBrains Mono', monospace;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;
}html,
body,
#app {margin: 0;height: 100%;
}
App.vue
<script setup>
import { h, ref } from 'vue'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'
import { MiniMap } from '@vue-flow/minimap'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import CustomNode from './CustomNode.vue'
import CustomEdge from './CustomEdge.vue'const { onConnect, addEdges } = useVueFlow()const nodes = ref([{ id: '1', type: 'input', label: 'Node 1', position: { x: 250, y: 5 } },{ id: '2', type: 'output', label: 'Node 2', position: { x: 100, y: 100 } },{ id: '3', type: 'custom', label: 'Node 3', position: { x: 400, y: 100 } },
])const edges = ref([{ id: 'e1-2', source: '1', target: '2', type: 'custom' },{ id: 'e1-3', source: '1', target: '3', animated: true },
])onConnect((params) => {addEdges([params])
})
</script><template><div style="height: 100vh"><VueFlowv-model:nodes="nodes"v-model:edges="edges"fit-view-on-initclass="vue-flow-basic-example":default-zoom="1.5":min-zoom="0.2":max-zoom="4"><Background pattern-color="#aaa" :gap="8" /><MiniMap /><Controls /><template #node-custom="nodeProps"><CustomNode v-bind="nodeProps" /></template><template #edge-custom="edgeProps"><CustomEdge v-bind="edgeProps" /></template></VueFlow></div>
</template>
CustomNode.vue
<script setup>
import { Handle, Position } from '@vue-flow/core'
import { ref } from 'vue'const counter = ref(0)
</script><template><div class="custom-node"><Handle type="target" :position="Position.Top" /><button class="increment nodrag" @click="counter++">Increment</button><div v-if="counter > 0" class="counter"><div class="count" v-for="count of counter" :key="`count-${count}`">{{ count }}</div></div></div>
</template><style>
.custom-node {min-width: 100px;gap: 4px;padding: 8px;background: white;border: 1px solid black;border-radius: 4px;
}.increment {border-radius: 4px;background: #42b983;font-size: 10px;color: #fff;cursor: pointer;border: none;
}.increment:hover {box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}.counter {margin-top: 8px;display: grid;grid-template-columns: repeat(4, minmax(0, 1fr));gap: 4px;
}.count {font-size: 6px;color: #ff0072;border: 1px solid rgba(0, 0, 0, 0.3);border-radius: 8px;
}
</style>
CustomEdge.vue
<script setup>
import { computed } from 'vue'
import { BaseEdge, EdgeLabelRenderer, getBezierPath, useVueFlow } from '@vue-flow/core'const props = defineProps({id: {type: String,required: true,},sourceX: {type: Number,required: true,},sourceY: {type: Number,required: true,},targetX: {type: Number,required: true,},targetY: {type: Number,required: true,},sourcePosition: {type: String,required: true,},targetPosition: {type: String,required: true,},data: {type: Object,required: false,},markerEnd: {type: String,required: false,},style: {type: Object,required: false,},
})const { removeEdges } = useVueFlow()const path = computed(() => getBezierPath(props))
</script><script>
export default {inheritAttrs: false,
}
</script><template><BaseEdge :path="path[0]" /><EdgeLabelRenderer><div:style="{pointerEvents: 'all',position: 'absolute',transform: `translate(-50%, -50%) translate(${path[1]}px,${path[2]}px)`,}"class="nodrag nopan"><button class="edgebutton" @click="removeEdges(id)">×</button></div></EdgeLabelRenderer>
</template><style>
.edgebutton {border-radius: 999px;cursor: pointer;
}.edgebutton:hover {box-shadow: 0 0 0 2px pink, 0 0 0 4px #f05f75;
}
</style>
效果圖
相關鏈接
https://vueflow.dev/