效果图如下
虽然丑了点,但是能用就行哈。
提前准备如下: 1.element组件 这个很简单 自己研究 2.安装vue-scrollto 我的是2.20.0版本 自行下载
那么可以来构思了
第一步:为了实现导航树结构 还需要children下子元素才可以作为点击的导航,父容器点击仅仅只是展开,高亮也必须高亮导航的文件格式,我们进行拆分数据结构。一个是treeData,一个是allPdfList。treeData用来树结构展示,至于allPdfList则是我们自己进行换算,将所有需要的子元素合并上一级用作导航
第二步:数据初始化 利用树结构的setCurrentKey方法 进行选中
第三步:给滚动div进行循环,同时赋值id,我的思路是id绑定索引。循环allPdfList来对应容器
第四步:点击tree事件单独抽出来编写,进行dom高度获取,同时利用vue-scrollto特性进行动画滚动
第五步:监听pdf滚动容器的滚动高度,进行2种情况换算 一种是在当前区域内的 另外一种是在最后一个滑块的逻辑
全部的代码直接附图:
代码片段如下:
<template>
<div class="con">
<div class="Nav flex1">
<el-tree
ref="tree"
style="height: calc(100% - 16px); overflow-y: auto"
:data="treeData"
:props="defaultProps"
highlight-current
default-expand-all
node-key="configname"
@node-click="handleNodeClick"
>
<span slot-scope="{ data }" class="custom-tree-node">
<span
:class="[
'treeicon',
!data.father ? 'el-icon-document' : 'el-icon-folder-opened',
]"
></span>
<span style="margin-left: 5px">{{ data.configname }}</span>
</span>
</el-tree>
</div>
<div class="pdf flex5" ref="scrollDiv" @scroll="scrollEvent()">
<div v-for="(item,index) in allPdfList" :key="index" :id="'IDDIV_'+index" class="min-h">
<h1>{{item.configname}}</h1>
</div>
</div>
</div>
</template>
<script>
export default {
name: "TreePdf",
data() {
return {
defaultProps: {
children: "children",
label: "configname"
},
treeData: [],
allPdfList: []
};
},
mounted() {
this.getList();
},
methods: {
getList() {
this.treeData = [
{ configname: "导航1", id: "dh_01" },
{ configname: "导航2", id: "dh_02" },
{
configname: "导航3",
id: "dh_03",
father: true,
children: [
{ configname: "导航3-1", id: "dh_03-001" },
{ configname: "导航3-2", id: "dh_03-002" }
]
},
{ configname: "导航4", id: "dh_04" }
];
this.allPdfList = []; // 无视父级,同级所需索引用到的导航数组
this.treeData.forEach(e => {
if (!e?.father) {
this.allPdfList.push(e);
} else if (e.children.length > 0) {
const childArr = e.children.map(e => {
return { ...e, configname: e?.configname };
});
this.allPdfList = [...this.allPdfList, ...childArr];
}
});
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.allPdfList[0]?.configname);
});
},
handleNodeClick(row) {
// 点击锚点触发
this.$nextTick(() => {
const rowName = row?.configname;
const names = this.allPdfList.map(e => {
return e?.configname;
});
const id = "IDDIV_" + names.indexOf(rowName);
const dom = document.getElementById(id);
// console.log(indexObj, key, this.allPdfList.length)
if (dom && this.$refs.scrollDiv) {
// console.log(this.showHeader ? 60 : 0)
// 嵌入式 缺少头部引用 少算60
this.$refs.scrollDiv.scrollTo({
top: dom.offsetTop - 5,
behavior: "smooth"
});
}
});
},
// 滚动pdf监听
scrollEvent() {
const nowScroll = this.$refs.scrollDiv.scrollTop + 42;// h1的高度是42
const allIds = this.allPdfList.map((e, index) => {
return "IDDIV_" + index;
});
// console.log(allIds)
allIds.forEach((id, index) => {
// 每一个id容器的高度获取
const dom = document.getElementById(id);
if (index < allIds.length - 1) {
// console.log(index, allIds.length)
const domNext = document.getElementById(allIds[index + 1]);
// 监听至滚动在指定的模块区域中
if (nowScroll > dom.offsetTop && nowScroll < domNext.offsetTop) {
// 在当前dom选中内
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.allPdfList[index]?.configname);
});
}
} else {
// 滚动至最后一个tree选中的条件
if (nowScroll > dom.offsetTop) {
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(this.allPdfList[index]?.configname);
});
}
}
});
}
}
};
</script>
<style lang="scss" scoped>
.con {
background: #ddd;
padding: 8px;
display: flex;
.flex1 {
flex: 1;
}
.flex5 {
flex: 5;
}
.Nav {
height: calc(100vh - 32px);
background: #fff;
padding: 8px;
overflow-y: auto;
}
.pdf {
background: #fff;
margin-left: 8px;
padding: 8px;
overflow: auto;
.min-h {
min-height: 100%;
}
}
}
::v-deep
.el-tree--highlight-current
.el-tree-node.is-current
> .el-tree-node__content {
background-color: #eaf7f0 !important;
border-radius: 4px;
color: #28a670 !important;
}
::v-deep
.el-tree--highlight-current
.el-tree-node
> .el-tree-node__content {
line-height: 38px;
height: 38px;
}
</style>