You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
3.3 KiB
126 lines
3.3 KiB
<template>
|
|
<div class="form-anchor">
|
|
<div
|
|
v-for="i in data"
|
|
:key="i.ref"
|
|
:class="{'active':i.ref === cur}"
|
|
@click="() => click(i.ref)"
|
|
>
|
|
{{ i.label }}
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {debounce} from "@/util"
|
|
import {getScroll, getOffsetTop, scrollTo} from "@/util/browser"
|
|
|
|
export default {
|
|
name: "FormAnchor",
|
|
|
|
props: {
|
|
reference: Function,
|
|
data: {type: Array, default: () => []},
|
|
offsetTop: {type: Number, default: 15},
|
|
bounds: {type: Number, default: 0},
|
|
getContainer: {
|
|
type: Function,
|
|
default: () => document.querySelector('#app .app-main>.el-scrollbar>.el-scrollbar__wrap')
|
|
},
|
|
},
|
|
|
|
data: () => ({cur: null}),
|
|
|
|
methods: {
|
|
getCur(offsetTop, bounds) {
|
|
const sections = []
|
|
const container = this.getContainer()
|
|
this.data.forEach(({ref}) => {
|
|
const target = this.getDomFromRef(ref)
|
|
const top = getOffsetTop(target, container)
|
|
if (top <= offsetTop + bounds) {
|
|
sections.push({ref, top})
|
|
}
|
|
})
|
|
|
|
if (sections.length) {
|
|
const max = sections.reduce((prev, curr) => (curr.top > prev.top ? curr : prev))
|
|
return max.ref
|
|
}
|
|
return ''
|
|
},
|
|
|
|
getDomFromRef(ref) {
|
|
const refs = this.reference()
|
|
return refs[ref]['$el'] || refs[ref][0]['$el']
|
|
},
|
|
|
|
click(ref) {
|
|
if (!ref || this.animating || !this.reference) return
|
|
this.cur = ref
|
|
const {offsetTop, getContainer} = this
|
|
const container = getContainer()
|
|
const scrollTop = getScroll(container, true)
|
|
const targetElement = this.getDomFromRef(ref)
|
|
if (!targetElement) return
|
|
const eleOffsetTop = getOffsetTop(targetElement, container)
|
|
const y = scrollTop + eleOffsetTop - offsetTop
|
|
this.animating = true
|
|
scrollTo(y, {callback: () => this.animating = false, getContainer})
|
|
},
|
|
|
|
scroll() {
|
|
if (this.animating || !this.reference) return
|
|
this.cur = this.getCur(this.offsetTop, this.bounds)
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this.scroll()
|
|
|
|
const container = this.getContainer()
|
|
this.handleScroll = debounce(this.scroll)
|
|
|
|
container.addEventListener('scroll', this.handleScroll)
|
|
this.$once('hook:beforeDestroy', () => {
|
|
container.removeEventListener('scroll', this.handleScroll)
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "~@/asset/style/variables.scss";
|
|
|
|
.form-anchor {
|
|
position: fixed;
|
|
right: 0;
|
|
top: 200px;
|
|
min-width: 72px;
|
|
overflow: hidden;
|
|
border-radius: 4px 0 0 4px;
|
|
background: #fff;
|
|
box-shadow: 0 2px 12px 0 rgba(166, 167, 173, 0.5);
|
|
z-index: 100;
|
|
|
|
> div {
|
|
width: inherit;
|
|
height: 28px;
|
|
line-height: 28px;
|
|
text-align: center;
|
|
color: #7C7E85;
|
|
padding: 0 10px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
|
|
&:hover,
|
|
&.active {
|
|
color: $--color-primary
|
|
}
|
|
|
|
&:not(:last-child) {
|
|
box-shadow: 0 1px 0 0 rgba(166, 167, 173, 0.1);
|
|
}
|
|
}
|
|
}
|
|
</style>
|
|
|