原本使用element plus的el-tree,可是他的UI不匹配,狠难改成自己想要的,所以只能自己去写一个,做法:使用递归组件
效果
组件代码itemDir.vue
// itemDir.vue
<template>
<div>
<ul v-for="node in lists" :key="node.id" class="menu-item">
<li :class="{ active: node.id === activeId }" @click="selectNode(node)">
<span class="item-item-text" :style="{ paddingLeft: depth * 20 + 'px' }">{{ node.name }}</span>
<el-icon v-if="node.childs && node.childs.length" class="arrow">
<ArrowDown v-if="isExpanded(node)" />
<ArrowRight v-else />
</el-icon>
</li>
<el-collapse-transition>
<template v-if="isExpanded(node) && node.childs && node.childs.length">
<ItemDir v-model="activeId" :lists="node.childs" :depth="depth + 1" />
</template>
</el-collapse-transition>
</ul>
</div>
</template>
<script setup>
import { ArrowRight, ArrowDown } from '@element-plus/icons-vue'
import { ref, watch } from 'vue'
const props = defineProps({
modelValue: {
type: Number,
default: undefined
},
lists: {
type: Array,
default: () => []
},
depth: {
type: Number,
default: 0
}
})
const emits = defineEmits(['update:modelValue'])
const activeId = ref(props.modelValue)
const expandedNodes = ref([])
watch(
() => props.modelValue,
newValue => {
activeId.value = newValue
}
)
watch(activeId, newValue => {
emits('update:modelValue', newValue)
})
const isExpanded = node => expandedNodes.value.some(n => n.id === node.id)
const selectNode = node => {
activeId.value = node.id
console.log(node)
const index = expandedNodes.value.findIndex(n => n.id === node.id)
if (index === -1) {
expandedNodes.value.push(node)
} else {
expandedNodes.value.splice(index, 1)
}
}
</script>
<style lang="scss" scoped>
.menu-item {
> li {
display: flex;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
height: 44px;
padding-right: 12px;
padding-left: 24px;
color: #666666;
font-weight: 400;
font-size: 12px;
font-family: PingFangSC, 'PingFang SC';
font-style: normal;
line-height: 12px;
text-align: left;
cursor: pointer;
}
.active {
color: #333333;
font-weight: bold;
background: #f5f5f5;
}
}
</style>
调用index.vue
// index.vue
<template>
<div class="menu-list">
<itemDir v-model="activeId" :lists="menus" />
</div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import itemDir from './itemDir.vue'
const activeId = ref(undefined)
const menus = ref([
{
"id": 187,
"pid": null,
"name": "地图资源库",
"childs": [
{
"id": 201,
"pid": 187,
"name": "电子地图底座",
"childs": [
{
"id": 225,
"pid": 201,
"name": "全球S-57电子海图数据",
"childs": null,
},
{
"id": 226,
"pid": 201,
"name": "基于互联网应用的中国电子海图服务",
"childs": null,
},
{
"id": 227,
"pid": 201,
"name": "S-57全球连续无缝背景电子海图",
"childs": null,
}
],
},
{
"id": 202,
"pid": 187,
"name": "海陆图融合",
"childs": [
{
"id": 222,
"pid": 202,
"name": "海图",
"childs": null,
},
{
"id": 223,
"pid": 202,
"name": "海图及陆图",
"childs": null,
},
{
"id": 224,
"pid": 202,
"name": "海图及卫星图",
"childs": null,
}
],
},
{
"id": 203,
"pid": 187,
"name": "区域边界设置",
"childs": [
{
"id": 215,
"pid": 203,
"name": "管辖边界标注",
"childs": null,
},
{
"id": 216,
"pid": 203,
"name": "航道边界标注",
"childs": null,
},
{
"id": 217,
"pid": 203,
"name": "港口边界标注",
"childs": null,
},
{
"id": 218,
"pid": 203,
"name": "锚地边界标注",
"childs": null,
},
{
"id": 219,
"pid": 203,
"name": "领海边界标注",
"childs": null,
},
{
"id": 220,
"pid": 203,
"name": "经济专属区边界标注",
"childs": null,
},
{
"id": 221,
"pid": 203,
"name": "管控区域边界标注",
"childs": null,
}
],
},
{
"id": 204,
"pid": 187,
"name": "长江航道瓦片图",
"childs": null,
},
{
"id": 205,
"pid": 187,
"name": "地图叠加图层",
"childs": [
{
"id": 206,
"pid": 205,
"name": "辖区航标数据库",
"childs": null,
},
{
"id": 207,
"pid": 205,
"name": "辖区海区数据库",
"childs": null,
},
{
"id": 208,
"pid": 205,
"name": "辖区时区数据库",
"childs": null,
},
{
"id": 209,
"pid": 205,
"name": "辖区国家领海基线数据库",
"childs": null,
},
{
"id": 210,
"pid": 205,
"name": "辖区重要海峡数据库",
"childs": null,
},
{
"id": 211,
"pid": 205,
"name": "辖区海上贸易区数据库",
"childs": null,
},
{
"id": 212,
"pid": 205,
"name": "辖区金融敏感区数据库",
"childs": null,
},
{
"id": 213,
"pid": 205,
"name": "辖区主要海盗区数据库",
"childs": null,
},
{
"id": 214,
"pid": 205,
"name": "辖区废弃污染物ECA排放控制区数据库",
"childs": null,
}
],
}
],
},
])
</script>