问题:由于el-timeline无法实现点击跳转,以及根据内容滚动来改变样式表示进度等问题,所以根据需求要求为了实现这两点要求封装了组件和使用。
实现timeline时间线效果演示
封装组件anchor.vue
<template>
<div class="detail_main flex" :loading="loading">
<div class="detail_main_left" :class="'detail_main_left'+id" @scroll="onScroll">
<div class="detail_main_left_item" :class="'detail_main_left_item'+id" v-for="(item, index) in list" :key="index">
<div class="item-title">
<span class="item-title-main">{{item.stepTitle}}</span>
</div>
<div class="item-contents">
<el-tag v-show="item.content && item.content.length>0" type="danger" effect="plain" size="small" class="tag" v-for="i in item.content" :key= i.id>{{ i.name }}</el-tag>
<el-empty class="no-pic" v-show="!item.content||item.content.length==0" :image-size="1" :description="content"></el-empty>
</div>
</div>
</div>
<div class="detail_main_right">
<ul>
<li class="detail_main_right_item" :class="{active:activeStep===index}" v-for="(item, index) in list" :key="index" @click="jump(index)">
{{ item.stepTitle }}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: "anchor",
props: {
list: {
type: Array,
default: () => [],
},
id: {
type: String,
default: () => '',
},
type: {
type: String,
default: () => '',
},
},
data() {
return {
loading: false,
activeStep:0,
dangerDisFlag:false,
content:''
};
},
watch:{
type:{
handler(val){
if(val=='suggest'){
this.content='暂无建议'
}else{
this.content='暂无分析'
}
},
immediate:true,
deep:true,
}
},
methods: {
change(val){
this.$emit('changeCheckbox',val)
},
// 滚动触发按钮高亮
onScroll (e) {
// console.log(1111,e)
let scrollItems = document.querySelectorAll('.detail_main_left_item'+this.id)
for (let i = scrollItems.length - 1; i >= 0; i--) {
// 判断滚动条滚动距离是否大于当前滚动项可滚动距离
let judge = e.target.scrollTop >= scrollItems[i].offsetTop - scrollItems[0].offsetTop
if (judge) {
this.activeStep = i
break
}
}
},
// 点击切换锚点
jump (index) {
console.log('0',index)
let target = document.querySelector('.detail_main_left'+this.id)
let scrollItems = document.querySelectorAll('.detail_main_left_item'+this.id)
// 判断滚动条是否滚动到底部
if (target.scrollHeight <= target.scrollTop + target.clientHeight) {
this.activeStep = index
}
let total = scrollItems[index].offsetTop - scrollItems[0].offsetTop // 锚点元素距离其offsetParent(这里是body)顶部的距离(待滚动的距离)
let distance = document.querySelector('.detail_main_left'+this.id).scrollTop // 滚动条距离滚动区域顶部的距离
// let distance = document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset // 滚动条距离滚动区域顶部的距离(滚动区域为窗口)
// 滚动动画实现, 使用setTimeout的递归实现平滑滚动,将距离细分为50小段,10ms滚动一次
// 计算每一小段的距离
let step = total / 50
if (total > distance) {
smoothDown(document.querySelector('.detail_main_left'+this.id))
} else {
let newTotal = distance - total
step = newTotal / 50
smoothUp(document.querySelector('.detail_main_left'+this.id))
}
// 参数element为滚动区域
function smoothDown (element) {
if (distance < total) {
distance += step
element.scrollTop = distance
setTimeout(smoothDown.bind(this, element), 10)
} else {
element.scrollTop = total
}
}
// 参数element为滚动区域
function smoothUp (element) {
if (distance > total) {
distance -= step
element.scrollTop = distance
setTimeout(smoothUp.bind(this, element), 10)
} else {
element.scrollTop = total
}
}
}
},
};
</script>
<style lang="less" scoped>
.detail_main {
font-size: 12px;
.detail_main_left {
width: 83%;
overflow-y: auto;
padding-right: 15px;
box-sizing: border-box;
// height: auto;
// height: calc(100vh - 115px);
height: 100%;
.detail_main_left_item, .detail_main_left_itemTitle{
// height: 200px;
// line-height: 1.5;
font-size: 12px;
padding: 10px;
border-bottom: 1px solid #eee;
.item-title{
font-size: 12px;
color: #303133;
margin-bottom: 10px;
text-align: left;
.item-title-main{
font-weight: bold;
}
.item-title-tips{
color: #C0C4CC;
margin: 0 10px;
}
}
.special{
.flex{
line-height: 28px;
// height: 28px;
.title{
text-align: left;
font-size:12px;
font-weight: 400;
min-width:60px;
}
}
}
.item-contents{
text-align: left;
}
}
.detail_main_left_itemTitle{
font-weight: bold;
font-size: 30px;
color: #303133;
}
.edit_btn{
line-height: 86px;
color: blue;
cursor: pointer;
}
>div:last-child{
// min-height: 280px;
min-height: 100%;
border-bottom: none;
}
}
.detail_main_right {
width: 15%;
height: 100%;
padding-top:0px ;
padding-left: 5px;
overflow: auto;
ul {
padding: 0 0 0 5px;
box-sizing: border-box;
border-left: 1px solid #eee;
li {
color: #303133;
margin-bottom: 16px;
cursor: pointer;
}
.active{
color: #1989FE;
position: relative;
}
.active:before{
z-index: 9999;
position: absolute;
content: "";
width: 7px;
height: 7px;
border-radius: 50%;
top: 20%;
left: -10px;
border:1px solid #1989FE;
background: #fff;
}
}
}
}
::-webkit-scrollbar {
width: 0 !important;
}
::-webkit-scrollbar {
width: 0 !important;
height: 0;
}
.flex{
display: flex;
}
ul,li{
list-style: none;
}
</style>
使用 main.vue
<template>
<div style="background:#fff;padding-left:20px;">
<table-title title="消息列表"></table-title>
<Anchor :list="detailsList" style="width:50%;height:300px;border:1px solid #e5e5e5;" :id="detailsListId" type="details"/>
</div>
</template>
<script>
import Anchor from './anchor'
import {guid} from "@/utils/util";
export default {
components:{
Anchor
},
data() {
return {
detailsList:[],
detailsListId: null,
};
},
beforeDestroy() {},
mounted() {
this.detailsList=[
{stepId:guid(),stepTitle:'疾病分析',content:[
{id:1,name:'疾病分析1'},
{id:2,name:'疾病分析2'},
{id:3,name:'疾病分析3'},
{id:4,name:'疾病分析4'},
{id:5,name:'疾病分析5'},
]},
{stepId:guid(),stepTitle:'诊断分析',content:[]},
{stepId:guid(),stepTitle:'临床分析',content:[]},
{stepId:guid(),stepTitle:'诊断依据',content:[]},
{stepId:guid(),stepTitle:'鉴别诊断',content:[]},
{stepId:guid(),stepTitle:'治疗方案',content:[]},
]
this.detailsListId=guid()
},
};
</script>
<style scoped lang="less">
</style>