Vue3?+?Element?Plus?實(shí)現(xiàn)可搜索、可折疊、可拖拽的部門(mén)樹(shù)組件功能
Vue3 + Element Plus 實(shí)現(xiàn)可搜索、可折疊、可拖拽的部門(mén)樹(shù)組件
在后臺(tái)管理系統(tǒng)中,左側(cè)樹(shù)型菜單是常見(jiàn)的 UI 組件。本文將手把手教你使用 Vue3 + Element Plus 來(lái)實(shí)現(xiàn)一個(gè)美觀、可搜索、可折疊、可拖拽的部門(mén)樹(shù)組件。
功能特點(diǎn)
我們的樹(shù)組件具有以下功能:
- 可搜索:輸入部門(mén)名稱(chēng)實(shí)時(shí)過(guò)濾樹(shù)節(jié)點(diǎn)。
- 選中高亮:點(diǎn)擊節(jié)點(diǎn)高亮顯示,選中狀態(tài)可切換。
- 可折疊/展開(kāi):可以收起左側(cè)樹(shù),也可以展開(kāi)。
- 可拖拽調(diào)整寬度:鼠標(biāo)拖拽可以改變樹(shù)的寬度。
- 美觀風(fēng)格:簡(jiǎn)潔清爽,箭頭和葉子圖標(biāo)統(tǒng)一。
組件目錄結(jié)構(gòu)
假設(shè)組件命名為 DeptTree.vue,父組件使用 el-container 布局:
---
## 代碼實(shí)現(xiàn)
下面是完整的 `DeptTree.vue` 代碼:
```vue
<!-- DeptTree.vue -->
<template>
<el-aside :style="{ width: leftWidth + 'px', height: props.height }" class="left-pane">
<!-- 搜索 -->
<el-input
v-if="props.showFilter"
v-model="deptName"
placeholder="請(qǐng)輸入部門(mén)名稱(chēng)"
clearable
size="small"
prefix-icon="Search"
class="filter-tree"
/>
<!-- 樹(shù) -->
<el-tree
ref="deptTreeRef"
:data="deptOptions"
:props="{ label: 'name', children: 'children' }"
node-key="id"
highlight-current
:default-expanded-keys="expandedKeys"
@node-click="onNodeClick"
:filter-node-method="filterNode"
:expand-on-click-node="false"
>
<template #default="{ node }">
<div
class="tree-node"
:class="{ 'is-current': node.isCurrent }"
@click.stop="selectNode(node)"
>
<el-icon v-if="node.children && node.children.length" class="arrow">
<component :is="node.expanded ? ArrowDown : ArrowRight" />
</el-icon>
<el-icon v-else class="leaf"><Tickets /></el-icon>
<span class="label">{{ node.label }}</span>
</div>
</template>
</el-tree>
</el-aside>
<!-- 拖拽欄 -->
<div class="resize-bar" @mousedown="startResize">
<el-icon class="collapse-icon" @click.stop="toggleCollapse">
<component :is="leftWidth === 0 ? ArrowRight : ArrowLeft" />
</el-icon>
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits, watch } from "vue";
import { ArrowLeft, ArrowRight, ArrowDown, Tickets, Search } from "@element-plus/icons-vue";
const props = defineProps({
deptOptions: Array,
leftWidth: { type: Number, default: 280 },
height: { type: String, default: "100%" },
showFilter: { type: Boolean, default: true },
defaultExpand: { type: Boolean, default: false },
});
const emit = defineEmits(["node-click", "update:leftWidth"]);
const deptName = ref("");
const deptTreeRef = ref(null);
const leftWidth = ref(props.leftWidth);
const expandedKeys = ref([]);
// 默認(rèn)展開(kāi)一級(jí)
watch(() => props.deptOptions, val => {
if (val?.length) expandedKeys.value = val.map(i => i.id);
}, { immediate: true });
// 過(guò)濾節(jié)點(diǎn)
const filterNode = (val, data) => !val || data.name.includes(val);
watch(deptName, val => deptTreeRef.value?.filter(val));
// 拖拽
let startX = 0, isResizing = false;
const startResize = e => {
isResizing = true;
startX = e.clientX;
document.addEventListener("mousemove", updateResize);
document.addEventListener("mouseup", stopResize);
};
const updateResize = e => {
if (!isResizing) return;
leftWidth.value += e.clientX - startX;
startX = e.clientX;
};
const stopResize = () => {
isResizing = false;
document.removeEventListener("mousemove", updateResize);
document.removeEventListener("mouseup", stopResize);
};
// 折疊/展開(kāi)
const toggleCollapse = () => {
leftWidth.value = leftWidth.value === 0 ? 280 : 0;
emit("update:leftWidth", leftWidth.value);
};
// 節(jié)點(diǎn)選中邏輯
const clearCurrent = nodes => nodes?.forEach(n => {
n.isCurrent = false;
n.children && clearCurrent(n.children);
});
const selectNode = node => {
clearCurrent(props.deptOptions);
node.isCurrent = true;
emit("node-click", node);
};
const onNodeClick = node => selectNode(node);
</script>
<style scoped lang="scss">
.left-pane { background:#fff; overflow:hidden; border-right:1px solid #eee; padding:12px; }
.filter-tree { margin-bottom:12px; }
.tree-node {
display:flex; align-items:center; padding:4px 8px; border-radius:4px; cursor:pointer; transition: all .2s;
&.is-current { background:#f0f5ff; color:var(--el-color-primary); }
&:hover { background:#f5f7fa; }
.arrow, .leaf { font-size:14px; margin-right:6px; }
.label { flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-size:14px; }
}
.resize-bar { width:8px; cursor:ew-resize; background:#f0f2f5; display:flex; align-items:center; justify-content:center; }
.collapse-icon { font-size:20px; color:#aaa; cursor:pointer; padding:4px; }
</style>功能分析
1. 樹(shù)結(jié)構(gòu)渲染
- 使用
el-tree渲染數(shù)據(jù)。 props指定標(biāo)簽和子節(jié)點(diǎn)。- 使用
node-key保證每個(gè)節(jié)點(diǎn)唯一。 - 通過(guò)
highlight-current高亮選中節(jié)點(diǎn)。
2. 搜索過(guò)濾
- 使用
filter-node-method實(shí)現(xiàn)關(guān)鍵字過(guò)濾。 - 搜索框綁定
deptName,實(shí)時(shí)調(diào)用treeRef.filter(val)。
3. 節(jié)點(diǎn)選中高亮
- 點(diǎn)擊節(jié)點(diǎn)觸發(fā)
selectNode。 - 使用遞歸函數(shù)清空其他節(jié)點(diǎn)的
isCurrent,保證只有當(dāng)前節(jié)點(diǎn)高亮。
4. 可折疊與拖拽
- 左側(cè)寬度
leftWidth可通過(guò)拖拽調(diào)整。 - 點(diǎn)擊折疊圖標(biāo)可以收起或展開(kāi)樹(shù)。
- 鼠標(biāo)拖動(dòng)監(jiān)聽(tīng)
mousemove,動(dòng)態(tài)更新寬度。
5. 樣式優(yōu)化
- 父容器
el-aside背景白色,樹(shù)節(jié)點(diǎn)圓角和 hover 高亮。 - 箭頭和葉子節(jié)點(diǎn)圖標(biāo)統(tǒng)一,界面簡(jiǎn)潔現(xiàn)代。
- 選中節(jié)點(diǎn)顏色與 Element Plus 主色調(diào)一致。
6. 父組件使用示例
<template>
<el-container style="height: 100vh">
<DeptTree
:deptOptions="deptOptions"
:leftWidth="leftWidth"
ref="deptTreeRef"
@node-click="handleNodeClick"
/>
<el-main>
<h3>右側(cè)內(nèi)容區(qū)域</h3>
</el-main>
</el-container>
</template>
<script setup>
import { ref } from "vue";
import DeptTree from "./components/DeptTree.vue";
const deptOptions = ref([
{ id: 1, name: "技術(shù)部", children: [{ id: 11, name: "前端組" }, { id: 12, name: "后端組" }] },
{ id: 2, name: "市場(chǎng)部" },
]);
const leftWidth = ref(280);
const handleNodeClick = node => console.log("點(diǎn)擊節(jié)點(diǎn)", node);
</script>總結(jié)與效果
通過(guò) Vue3 + Element Plus,我們實(shí)現(xiàn)了一個(gè)可搜索、可選中高亮、可折疊、可拖拽的部門(mén)樹(shù)組件。
組件特點(diǎn):
- 邏輯清晰,代碼優(yōu)雅;
- 樣式現(xiàn)代美觀;
- 可復(fù)用性強(qiáng),可快速應(yīng)用到后臺(tái)系統(tǒng)。
效果展示
- 搜索過(guò)濾效果
- 輸入部門(mén)名稱(chēng)即可實(shí)時(shí)過(guò)濾樹(shù)節(jié)點(diǎn),便于快速定位目標(biāo)部門(mén)。
- 選中高亮效果
- 點(diǎn)擊節(jié)點(diǎn)高亮顯示,之前選中的節(jié)點(diǎn)高亮?xí)詣?dòng)取消,保證視覺(jué)清晰。
- 折疊與展開(kāi)效果
- 點(diǎn)擊左側(cè)折疊按鈕即可收起或展開(kāi)樹(shù),界面簡(jiǎn)潔靈活。
- 拖拽調(diào)整寬度效果
- 鼠標(biāo)拖拽左側(cè)樹(shù)邊緣可以調(diào)整寬度,適應(yīng)不同屏幕布局。

到此這篇關(guān)于Vue3 + Element Plus 實(shí)現(xiàn)可搜索、可折疊、可拖拽的部門(mén)樹(shù)組件的文章就介紹到這了,更多相關(guān)vue elementplus部門(mén)樹(shù)組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue3+elementui plus如何實(shí)現(xiàn)多選分頁(yè)列表的新增和修改
- Vue3+Element?Plus動(dòng)態(tài)表單實(shí)現(xiàn)
- vue3.0安裝element?plus依賴(lài)的過(guò)程
- vue3中element-plus表格搜索過(guò)濾數(shù)據(jù)
- 基于Vue3和Element Plus實(shí)現(xiàn)自動(dòng)導(dǎo)入功能
- 使用Vue實(shí)現(xiàn)一個(gè)樹(shù)組件的示例
- Vue使用zTree插件封裝樹(shù)組件操作示例
- vue文件樹(shù)組件使用詳解
- 基于Vue制作組織架構(gòu)樹(shù)組件
相關(guān)文章
vue移動(dòng)端彈起蒙層滑動(dòng)禁止底部滑動(dòng)操作
這篇文章主要介紹了vue移動(dòng)端彈起蒙層滑動(dòng)禁止底部滑動(dòng)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
Vue?數(shù)據(jù)綁定事件綁定樣式綁定語(yǔ)法示例
這篇文章主要為大家介紹了Vue?數(shù)據(jù)綁定事件綁定樣式綁定語(yǔ)法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
基于vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框效果
這篇文章主要為大家詳細(xì)介紹了如何使用Vue3實(shí)現(xiàn)一個(gè)簡(jiǎn)單的輸入框,可以實(shí)現(xiàn)輸入文字,添加表情等功能,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03
vue項(xiàng)目中swiper輪播active圖片實(shí)現(xiàn)居中并放大
這篇文章主要介紹了vue項(xiàng)目中swiper輪播active圖片實(shí)現(xiàn)居中并放大方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
Vue使用Clipboard.JS在h5頁(yè)面中復(fù)制內(nèi)容實(shí)例詳解
在本篇文章里小編給大家整理了關(guān)于Vue使用Clipboard.JS在h5頁(yè)面中復(fù)制內(nèi)容以及相關(guān)知識(shí)點(diǎn)內(nèi)容,需要的朋友們可以學(xué)習(xí)下。2019-09-09
基于VUE實(shí)現(xiàn)判斷設(shè)備是PC還是移動(dòng)端
這篇文章主要介紹了基于VUE實(shí)現(xiàn)判斷設(shè)備是PC還是移動(dòng)端,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07

