Vue Flow 上手篇

在Vue3專案裡使用Vue Flow套件之範例

因專案需求而研究Vue Flow套件,將基本的介紹作筆記。

首先開新Vue3專案,在終端機輸入安裝

npm add @vue-flow/core		

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';

html,
body,
#app {
  margin: 0;
  height: 100%;
}

main.js

import { createApp } from 'vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import './assets/index.css';
import './assets/dragDropEx.css';
import App from './App.vue';

const app = createApp(App);
app.use(ElementPlus);
app.mount('#app');

引用VueFlow與資料屬性之範例1:

•nodes:節點

•edges:邊,也叫連接線

<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'

const nodes = ref([
  {
    id: '1',
    type: 'input',
    position: { x: 100, y: 100 },
    data: { label: '煮珍珠(工序 A)' },
  },

  {
    id: '2',
    position: { x: 100, y: 200 },
    data: { label: '泡茶(工序 B)' },
  },

  {
    id: '3',
    position: { x: 100, y: 300 },
    data: { label: '加料混合(工序 C)' },
  },

  {
    id: '4',
     type: 'output',
    position: { x: 100, y: 400 },
    data: { label: '封口 & 包裝(工序 D)' },
  },
])

const edges = ref([
    {
        id: 'e1->2',
        source: '1',
        target: '2',
        type: 'straight'
    },
    {
        id: 'e2->3',
        source: '2',
        target: '3',
        type: 'straight'
    },
    {
        id: 'e3->4',
        source: '3',
        target: '4',
    }
])
</script>

<template>
  <VueFlow :nodes="nodes" :edges="edges">
  </VueFlow>
</template>

<style>

</style>

折線範例:edges 裡 type: 'step'

<script setup>
import { ref } from 'vue'
import { VueFlow } from '@vue-flow/core'

const nodes = ref([
    {
        id: '1',
        type: 'input',
        position: { x: 100, y: 100 },
        data: { label: '煮珍珠(工序 A)' },
    },

    {
        id: '2',
        position: { x: 200, y: 200 },
        data: { label: '泡茶(工序 B)' },
    },

    {
        id: '3',
        position: { x: 300, y: 300 },
        data: { label: '加料混合(工序 C)' },
    },

    {
        id: '4',
        type: 'output',
        position: { x: 400, y: 400 },
        data: { label: '封口 & 包裝(工序 D)' },
    },
])

const edges = ref([
    {
        id: 'e1->2',
        source: '1',
        target: '2',
        type: 'step'
    },
    {
        id: 'e2->3',
        source: '2',
        target: '3',
        type: 'step'
    },
    {
        id: 'e3->4',
        source: '3',
        target: '4',
        type: 'step'
    }
])
</script>

<template>
    <VueFlow :nodes="nodes" :edges="edges">
    </VueFlow>
</template>

<style></style>

串連連接線範例:

const edges = ref([

])

const { onConnect, addEdges } = useVueFlow()
onConnect(({ source, target, sourceHandle, targetHandle }) => {
    console.log('source', source)
    console.log('target', target)
    console.log('sourceHandle', sourceHandle)
    console.log('targetHandle', targetHandle)
    addEdges({ source, target, sourceHandle, targetHandle })

    const timeStr = `${new Date().getMinutes()}-${new Date().getSeconds()}`
    edges.value.push({
        id: timeStr,
        source: source,
        target: target,
    })
})

客製化連接線(刪除圖示)範例:

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,
  },
  deleteFunction:Function
})

// 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="deleteFunction(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>

Ex5.vue

<script setup>
import { ref } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import CustomEdge from "../components/CustomEdge.vue";

const nodes = ref([
    {
        id: '1',
        type: 'input',
        position: { x: 100, y: 100 },
        data: { label: '煮珍珠(工序 A)' },
    },

    {
        id: '2',
        position: { x: 200, y: 200 },
        data: { label: '泡茶(工序 B)' },
    },

    {
        id: '3',
        position: { x: 300, y: 300 },
        data: { label: '加料混合(工序 C)' },
    },

    {
        id: '4',
        type: 'output',
        position: { x: 400, y: 400 },
        data: { label: '封口 & 包裝(工序 D)' },
    },
])

const edges = ref([
    {
        id: 'e1->2',
        source: '1',
        target: '2',
        type: "custom"
    },
    {
        id: 'e2->3',
        source: '2',
        target: '3',
        type: "custom"
    },
    {
        id: 'e3->4',
        source: '3',
        target: '4',
        type: "custom"
    }
])

const { onConnect, addEdges, removeEdges } = useVueFlow()
onConnect(({ source, target, sourceHandle, targetHandle }) => {
    console.log('source', source)
    console.log('target', target)
    console.log('sourceHandle', sourceHandle)
    console.log('targetHandle', targetHandle)
    // addEdges({ source, target, sourceHandle, targetHandle })

    const timeStr = `${new Date().getMinutes()}-${new Date().getSeconds()}`

    addEdges([
        {
            id: timeStr, // 可選,若未指定會自動生成
            source: source,
            target: target,
            sourceHandle: sourceHandle,
            targetHandle: targetHandle,
            type: 'custom', // 指定自訂類型
        },
    ])

    edges.value.push({
        id: timeStr,
        source: source,
        target: target,
        type: "custom"
    })
})

/**
 * 刪除邊
 * @param id 
 */
function deleteEdge(id) {
    console.log('deleteEdge id', id)
    removeEdges(id)
    edges.value = edges.value.filter(x => x.id != id)
}
</script>

<template>
    <VueFlow :nodes="nodes" :edges="edges">
        <template #edge-custom="edgeProps">
            <CustomEdge v-bind="edgeProps" :deleteFunction="deleteEdge" />
        </template>
    </VueFlow>
</template>

<style></style>

連接線動畫範例:edges內animated: true

const edges = ref([
    {
        id: 'e1->2',
        source: '1',
        target: '2',
        animated: true
    },
    {
        id: 'e2->3',
        source: '2',
        target: '3',
        animated: true
    },
    {
        id: 'e3->4',
        source: '3',
        target: '4',
        animated: true
    }
])

以上是摘錄部份sourcecode的範例之筆記。