vue中el-table和jsplumb實(shí)現(xiàn)連線功能
前言
最近在開(kāi)發(fā)中遇到了一個(gè)需求,是將兩組樹(shù)形結(jié)構(gòu)的數(shù)據(jù)通過(guò)連線進(jìn)行關(guān)聯(lián)映射。what?連線功能?還是兩組樹(shù)形結(jié)構(gòu)???讓我頭疼的并非連線而是對(duì)樹(shù)形結(jié)構(gòu)的展示,我需要一個(gè)能夠清晰的展示樹(shù)形結(jié)構(gòu)的關(guān)系和每條數(shù)據(jù)里的詳細(xì)數(shù)據(jù)的組件,就這樣el-table走進(jìn)了我的視野里。
準(zhǔn)備工作
所用插件: "element-plus" + "jsplumb"npm install 就完事
第一步: 定義靜態(tài)數(shù)據(jù)并顯示table組件
新建一個(gè)line.vue文件,定義一個(gè)id為container的div標(biāo)簽,在這個(gè)標(biāo)簽內(nèi)放入兩個(gè)el-table,并根據(jù)定義的靜態(tài)數(shù)據(jù)進(jìn)行基礎(chǔ)的配置。需要注意的是el-table當(dāng) row 中包含 children 字段時(shí),被視為樹(shù)形數(shù)據(jù)。 渲染嵌套數(shù)據(jù)需要 prop 的 row-key。具體見(jiàn)下面代碼:
<template>
<div class="line">
<div id="container" style="display: flex; justify-content: space-between; position: relative">
<el-table
ref="leftTable"
:data="leftTreeData"
style="width: 40%; margin-bottom: 20px; display: inline-block;"
row-key="id"
border
default-expand-all
>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="gender" label="性別"/>
<el-table-column prop="age" label="年齡"/>
</el-table>
<el-table
ref="rightTable"
:data="rightTreeData"
style="width: 40%; margin-bottom: 20px; display: inline-block;"
row-key="id"
border
default-expand-all
>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="gender" label="性別"/>
<el-table-column prop="age" label="年齡"/>
</el-table>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
// 左側(cè)靜態(tài)數(shù)據(jù)
const leftData = ref([
{id: 1, name: '張三', gender: '男', age: 23, parentId: null, hasChild: true},
{id: 2, name: '李四', gender: '男', age: 22, parentId: null, hasChild: false},
{id: 3, name: '坤坤', gender: '女', age: 24, parentId: 1, hasChild: true},
{id: 4, name: '小黑子', gender: '男', age: 25, parentId: 3, hasChild: false},
]);
const leftTreeData = ref([
{
id: 1, name: '張三', gender: '男', age: 23, parentId: null, children: [
{id: 3, name: '坤坤', gender: '女', age: 24, parentId: 1, children: [
{id: 4, name: '小黑子', gender: '男', age: 25, parentId: 3},
]
},
]
},
{id: 2, name: '李四', gender: '男', age: 22, parentId: null},
]);
// 右側(cè)靜態(tài)數(shù)據(jù)
const rightData = ref([
{id: 5, name: '柯潔', gender: '男', age: 23, parentId: null, hasChild: true},
{id: 6, name: '戰(zhàn)鷹', gender: '女', age: 30, parentId: 5, hasChild: false},
{id: 7, name: '唱跳', gender: '男', age: 24, parentId: null, hasChild: false},
{id: 8, name: 'rap', gender: '男', age: 25, parentId: null, hasChild: false},
]);
const rightTreeData = ref([
{id: 5, name: '柯潔', gender: '男', age: 23, parentId: null, children: [
{id: 6, name: '戰(zhàn)鷹', gender: '女', age: 30, parentId: 6},
]
},
{id: 7, name: '唱跳', gender: '男', age: 24, parentId: null},
{id: 8, name: 'rap', gender: '男', age: 25, parentId: null},
]);
const leftTable = ref(null);
const rightTable = ref(null);
</script>
第二步: 初始化jsPlumb并設(shè)置可以連線的元素
引入jsplumb 并創(chuàng)建jsplumb實(shí)例,實(shí)例中大部分的配置主要是對(duì)箭頭的樣式配置,具體參數(shù)信息可以查看官網(wǎng)。
import { jsPlumb } from 'jsplumb'
let instance = null;
function init() {
instance = jsPlumb.getInstance({
Connector: "Straight", //連接線形狀 Bezier: 貝塞爾曲線 Flowchart: 具有90度轉(zhuǎn)折點(diǎn)的流程線 StateMachine: 狀態(tài)機(jī) Straight: 直線
PaintStyle: { strokeWidth: 3, stroke: "#dfbee7" }, //連接線樣式
Endpoint: ["Blank", { radius: 1 }], //端點(diǎn)
anchor: 'Right',
// 繪制箭頭
Overlays: [['Arrow', { width: 12, length: 12, location: 1 }]],
EndpointStyle: { fill: "#000000" }, //端點(diǎn)樣式
Container: "container", //目標(biāo)容器id
ListStyle: {
endpoint: ["Rectangle", { width: 30, height: 30 }],
},
});
}由于業(yè)務(wù)的需求是只能從左側(cè)的數(shù)據(jù)連接到右側(cè)的數(shù)據(jù),并且有子項(xiàng)的那一行不能進(jìn)行連接操作,所以我這邊要將左側(cè)的每行數(shù)據(jù)設(shè)置為起點(diǎn)右側(cè)為終點(diǎn)并過(guò)濾那些有子項(xiàng)的行,而設(shè)置起點(diǎn)和終點(diǎn)需要拿到對(duì)應(yīng)的元素或者標(biāo)識(shí),這時(shí)我們就需要給el-table的row設(shè)置class名
<template>
<div class="line">
<div id="container" style="display: flex; justify-content: space-between; position: relative">
<el-table
ref="leftTable"
:data="leftTreeData"
:row-class-name="({row}) => `leftRow Id-${row.id}`"
style="width: 40%; margin-bottom: 20px; display: inline-block;"
row-key="id"
border
default-expand-all
>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="gender" label="性別"/>
<el-table-column prop="age" label="年齡"/>
</el-table>
<el-table
ref="rightTable"
:data="rightTreeData"
:row-class-name="({row}) => `rightRow Id-${row.id}`"
style="width: 40%; margin-bottom: 20px; display: inline-block;"
row-key="id"
border
default-expand-all
>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="gender" label="性別"/>
<el-table-column prop="age" label="年齡"/>
</el-table>
</div>
</div>
</template>我這里設(shè)置了兩個(gè)類名 leftRow 和 rightRow 來(lái)區(qū)分他是左側(cè)還是右側(cè)的行,Id-${row.id}作為唯一標(biāo)識(shí)讓我們能獲取到某一行元素
// 設(shè)置可以連線的元素
function setContainer() {
const leftElList = document.querySelectorAll('.leftRow'); // 左側(cè)行元素集合
const rightElList = document.querySelectorAll('.rightRow'); // 右側(cè)行元素集合
// 將dom元素設(shè)置為連線的起點(diǎn)或者終點(diǎn) 設(shè)置了起點(diǎn)的元素才能開(kāi)始連線 設(shè)置為終點(diǎn)的元素才能為連線終點(diǎn)
instance.batch(function () {
[leftElList, rightElList].forEach((trList, index) => {
trList.forEach((tr) => {
const id = interceptId(tr.classList[2]);
if (index === 0) {
const item = leftData.value.find(i => i.id == id);
// 判斷是否有子項(xiàng),若沒(méi)有則設(shè)置為起點(diǎn)
!item?.hasChild && instance.makeSource(tr, {
allowLoopback: false,
anchor: ["Right"], // 設(shè)置端點(diǎn)位置
maxConnections: 1
});
} else {
const item = rightData.value.find(i => i.id == id);
// 判斷是否有子項(xiàng),若沒(méi)有則設(shè)置為終點(diǎn)
!item?.hasChild && instance.makeTarget(tr, {
anchor: ["Left"],
maxConnections: 1
});
}
});
});
});
}
// 截取元素類名中的id
const interceptId = className => {
return className.slice(className.indexOf('-') + 1);
}然后我們?cè)?code>onMounted中調(diào)用這些方法就可以實(shí)現(xiàn)連線功能了
const initJsPlumb = () => {
jsPlumb.ready(function () {
// 初始化jsPlumb 創(chuàng)建jsPlumb實(shí)例
init();
// 設(shè)置可以為連線起點(diǎn)和連線終點(diǎn)的元素
setContainer();
});
}
onMounted(() => {
initJsPlumb();
})
看!成功啦!
設(shè)置默認(rèn)連線和刪除連線功能
const relationship = reactive([
{sourceId: 4, targetId: 8}
])
// 設(shè)置默認(rèn)連線
function setConnect(relationship) {
setTimeout(() => {
relationship.forEach(function (data) {
// source是連線起點(diǎn)元素id target是連線終點(diǎn)元素id
instance.connect({
source: document.querySelector(`.Id-${data.sourceId}`),
target: document.querySelector(`.Id-${data.targetId}`)
});
});
})
}
// 綁定事件監(jiān)聽(tīng)
function setEvent() {
// 連線事件
instance.bind("connection", function (connInfo, originalEvent) {
// connInfo是jsPlumb對(duì)象 可以打印出來(lái)康康有哪些東西
console.log(connInfo, originalEvent, 'connInfo')
});
// 點(diǎn)擊連接線刪除該條線
instance.bind('click', function (connection, originalEvent) {
instance.deleteConnection(connection);
})
}
const initJsPlumb = () => {
jsPlumb.ready(function () {
// 初始化jsPlumb 創(chuàng)建jsPlumb實(shí)例
init();
// 設(shè)置可以為連線起點(diǎn)和連線終點(diǎn)的元素
setContainer();
// 設(shè)置默認(rèn)連線
setConnect(relationship);
// 綁定事件監(jiān)聽(tīng)
setEvent();
});
}
onMounted(() => {
initJsPlumb();
})將上述代碼中的兩個(gè)函數(shù)一并放入到initJsPlumb函數(shù)中執(zhí)行即可
禁用el-table的expand功能
由于數(shù)據(jù)的結(jié)構(gòu)是樹(shù)結(jié)構(gòu)所以el-table在對(duì)含有子節(jié)點(diǎn)的行的最左側(cè)添加了展開(kāi)和收起功能的一個(gè)圖標(biāo)按鈕,所以當(dāng)我們點(diǎn)擊它收縮后會(huì)改變?cè)械囊晥D結(jié)構(gòu),而jsplumb是用canvas繪制的線條,它的位置并不能實(shí)時(shí)更改,所以我的解決方案是利用el-table的expand-change事件監(jiān)聽(tīng)配合toggleRowExpansion方法實(shí)現(xiàn)不管用戶怎么操作都是展開(kāi)的狀態(tài)。

<el-table
ref="leftTable"
:data="leftTreeData"
:row-class-name="({row}) => `leftRow Id-${row.id}`"
style="width: 40%; margin-bottom: 20px; display: inline-block;"
row-key="id"
border
default-expand-all
@expand-change="(row, expanded) => !expanded && leftTable?.toggleRowExpansion(row)"
>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="gender" label="性別"/>
<el-table-column prop="age" label="年齡"/>
</el-table>
<el-table
ref="rightTable"
:data="rightTreeData"
:row-class-name="({row}) => `rightRow Id-${row.id}`"
style="width: 40%; margin-bottom: 20px; display: inline-block;"
row-key="id"
border
default-expand-all
@expand-change="(row, expanded) => !expanded && rightTable?.toggleRowExpansion(row)"
>
<el-table-column prop="name" label="姓名"/>
<el-table-column prop="gender" label="性別"/>
<el-table-column prop="age" label="年齡"/>
</el-table>結(jié)言
到此這篇關(guān)于el-table和jsplumb實(shí)現(xiàn)連線功能的文章就介紹到這了,更多相關(guān)el-table jsplumb連線內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解vue axios用post提交的數(shù)據(jù)格式
這篇文章主要介紹了詳解vue axios用post提交的數(shù)據(jù)格式,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Vue 頁(yè)面跳轉(zhuǎn)不用router-link的實(shí)現(xiàn)代碼
這篇文章主要介紹了 Vue 頁(yè)面跳轉(zhuǎn)不用router-link的實(shí)現(xiàn)代碼,文中給大家介紹了vue router-link跳轉(zhuǎn)傳值示例,需要的朋友可以參考下2018-04-04
Vue3?Transition組件給頁(yè)面切換并加上動(dòng)畫(huà)效果
這篇文章主要給大家介紹了關(guān)于Vue3?Transition組件給頁(yè)面切換并加上動(dòng)畫(huà)效果的相關(guān)資料,vue的過(guò)渡動(dòng)畫(huà)主要是transition標(biāo)簽的使用,配合css動(dòng)畫(huà)實(shí)現(xiàn)的,需要的朋友可以參考下2023-06-06
vue+springboot實(shí)現(xiàn)圖形驗(yàn)證碼Kaptcha的示例
圖形驗(yàn)證碼是做網(wǎng)站常用的功能,本文主要介紹了vue+springboot實(shí)現(xiàn)圖形驗(yàn)證碼Kaptcha的示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11
Vue3 封裝 element-plus 圖標(biāo)選擇器實(shí)現(xiàn)步驟
這篇文章主要介紹了Vue3 封裝 element-plus 圖標(biāo)選擇器,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09
Vue實(shí)現(xiàn)用戶沒(méi)有登陸時(shí),訪問(wèn)后自動(dòng)跳轉(zhuǎn)登錄頁(yè)面的實(shí)現(xiàn)思路
這篇文章主要介紹了Vue實(shí)現(xiàn)用戶沒(méi)有登陸時(shí),訪問(wèn)后自動(dòng)跳轉(zhuǎn)登錄頁(yè)面,定義路由的時(shí)候配置屬性,這里使用needLogin標(biāo)記訪問(wèn)頁(yè)面是否需要登錄,本文通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02

