mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 17:18:40 +08:00
refactor: ♻️ 索引栏组件兼容钉钉小程序
This commit is contained in:
parent
f84e9affb1
commit
0d18b39ff6
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
使用一个固定高度的元素包裹`wd-index-bar`组件,组件的宽高会和包裹元素相同。
|
使用一个固定高度的元素包裹`wd-index-bar`组件,组件的宽高会和包裹元素相同。
|
||||||
|
|
||||||
将`wd-index-anchor`作为子组件使用,会根据anchor组件的`index`属性生成锚点以及侧边栏。
|
将`wd-index-anchor`作为子组件使用,会根据 anchor 组件的`index`属性生成锚点以及侧边栏。
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<template>
|
<template>
|
||||||
<view class="wrap" :style="{ height: wrapHeight + 'px' }">
|
<view class="wraper">
|
||||||
<wd-index-bar>
|
<wd-index-bar>
|
||||||
<view v-for="item in data" :key="item.index" class="city-wrap">
|
<view v-for="item in data" :key="item.index" class="city-wrap">
|
||||||
<wd-index-anchor :index="item.index" />
|
<wd-index-anchor :index="item.index" />
|
||||||
@ -22,50 +22,96 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
const wrapHeight = ref(400)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const info = uni.getWindowInfo()
|
|
||||||
wrapHeight.value = info.windowHeight
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = ref([
|
const data = ref([
|
||||||
{
|
{
|
||||||
index: 'A',
|
index: 'A',
|
||||||
data: ["阿坝", "阿拉善", "阿里", "安康", "安庆", "鞍山", "安顺", "安阳", "澳门",]
|
data: ['阿坝', '阿拉善', '阿里', '安康', '安庆', '鞍山', '安顺', '安阳', '澳门']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'B',
|
index: 'B',
|
||||||
data: ["北京", "白银", "保定", "宝鸡", "保山", "包头", "巴中", "北海", "蚌埠", "本溪", "毕节", "滨州", "百色", "亳州"]
|
data: ['北京', '白银', '保定', '宝鸡', '保山', '包头', '巴中', '北海', '蚌埠', '本溪', '毕节', '滨州', '百色', '亳州']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'C',
|
index: 'C',
|
||||||
data: ["重庆", "成都", "长沙", "长春", "沧州", "常德", "昌都", "长治", "常州", "巢湖", "潮州", "承德", "郴州", "赤峰", "池州", "崇左", "楚雄", "滁州", "朝阳"]
|
data: [
|
||||||
|
'重庆',
|
||||||
|
'成都',
|
||||||
|
'长沙',
|
||||||
|
'长春',
|
||||||
|
'沧州',
|
||||||
|
'常德',
|
||||||
|
'昌都',
|
||||||
|
'长治',
|
||||||
|
'常州',
|
||||||
|
'巢湖',
|
||||||
|
'潮州',
|
||||||
|
'承德',
|
||||||
|
'郴州',
|
||||||
|
'赤峰',
|
||||||
|
'池州',
|
||||||
|
'崇左',
|
||||||
|
'楚雄',
|
||||||
|
'滁州',
|
||||||
|
'朝阳'
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'D',
|
index: 'D',
|
||||||
data: ["大连", "东莞", "大理", "丹东", "大庆", "大同", "大兴安岭", "德宏", "德阳", "德州", "定西", "迪庆", "东营"]
|
data: ['大连', '东莞', '大理', '丹东', '大庆', '大同', '大兴安岭', '德宏', '德阳', '德州', '定西', '迪庆', '东营']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'E',
|
index: 'E',
|
||||||
data: ["鄂尔多斯", "恩施", "鄂州"]
|
data: ['鄂尔多斯', '恩施', '鄂州']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'F',
|
index: 'F',
|
||||||
data: ["福州", "防城港", "佛山", "抚顺", "抚州", "阜新", "阜阳"]
|
data: ['福州', '防城港', '佛山', '抚顺', '抚州', '阜新', '阜阳']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'G',
|
index: 'G',
|
||||||
data: ["广州", "桂林", "贵阳", "甘南", "赣州", "甘孜", "广安", "广元", "贵港", "果洛"]
|
data: ['广州', '桂林', '贵阳', '甘南', '赣州', '甘孜', '广安', '广元', '贵港', '果洛']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 'H',
|
index: 'H',
|
||||||
data: ["杭州", "哈尔滨", "合肥", "海口", "呼和浩特", "海北", "海东", "海南", "海西", "邯郸", "汉中", "鹤壁", "河池", "鹤岗", "黑河", "衡水", "衡阳", "河源", "贺州", "红河", "淮安", "淮北", "怀化", "淮南", "黄冈", "黄南", "黄山", "黄石", "惠州", "葫芦岛", "呼伦贝尔", "湖州", "菏泽"]
|
data: [
|
||||||
|
'杭州',
|
||||||
|
'哈尔滨',
|
||||||
|
'合肥',
|
||||||
|
'海口',
|
||||||
|
'呼和浩特',
|
||||||
|
'海北',
|
||||||
|
'海东',
|
||||||
|
'海南',
|
||||||
|
'海西',
|
||||||
|
'邯郸',
|
||||||
|
'汉中',
|
||||||
|
'鹤壁',
|
||||||
|
'河池',
|
||||||
|
'鹤岗',
|
||||||
|
'黑河',
|
||||||
|
'衡水',
|
||||||
|
'衡阳',
|
||||||
|
'河源',
|
||||||
|
'贺州',
|
||||||
|
'红河',
|
||||||
|
'淮安',
|
||||||
|
'淮北',
|
||||||
|
'怀化',
|
||||||
|
'淮南',
|
||||||
|
'黄冈',
|
||||||
|
'黄南',
|
||||||
|
'黄山',
|
||||||
|
'黄石',
|
||||||
|
'惠州',
|
||||||
|
'葫芦岛',
|
||||||
|
'呼伦贝尔',
|
||||||
|
'湖州',
|
||||||
|
'菏泽'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
</script>
|
</script>
|
||||||
@ -78,10 +124,13 @@ const data = ref([
|
|||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.city-wrap .city:last-child {
|
.wraper {
|
||||||
margin-bottom: 10px;
|
height: calc(100vh - var(--window-top));
|
||||||
|
height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom));
|
||||||
|
height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.wot-theme-dark {
|
.wot-theme-dark {
|
||||||
.city {
|
.city {
|
||||||
color: white;
|
color: white;
|
||||||
@ -91,6 +140,12 @@ const data = ref([
|
|||||||
</style>
|
</style>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||||
|
| ------ | ------------ | ------- | ------ | ------ | -------- |
|
||||||
|
| sticky | 索引是否吸顶 | boolean | - | false | - |
|
||||||
|
|
||||||
## IndexAnchor Attributes
|
## IndexAnchor Attributes
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<wd-config-provider :theme="theme" :theme-vars="isRed ? themeVars : {}">
|
<wd-config-provider :theme="theme" :theme-vars="isRed ? themeVars : {}">
|
||||||
<wd-toast />
|
<wd-toast />
|
||||||
<view :class="['page-wraper', safeAreaInsetBottom ? 'is-safe' : '']">
|
<view class="page-wraper">
|
||||||
<wd-cell title="切换暗黑" title-width="240px" center v-if="showDarkMode">
|
<wd-cell title="切换暗黑" title-width="240px" center v-if="showDarkMode">
|
||||||
<wd-switch v-model="isDark" />
|
<wd-switch v-model="isDark" />
|
||||||
</wd-cell>
|
</wd-cell>
|
||||||
@ -9,9 +9,9 @@
|
|||||||
<wd-switch v-model="isRed" />
|
<wd-switch v-model="isRed" />
|
||||||
</wd-cell>
|
</wd-cell>
|
||||||
<slot />
|
<slot />
|
||||||
|
<wd-gap height="0" v-if="safeAreaInsetBottom" safe-area-bottom></wd-gap>
|
||||||
</view>
|
</view>
|
||||||
<wd-notify />
|
<wd-notify />
|
||||||
<wd-gap height="0" safe-area-bottom></wd-gap>
|
|
||||||
</wd-config-provider>
|
</wd-config-provider>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
@ -67,10 +67,5 @@ onMounted(() => {
|
|||||||
.page-wraper {
|
.page-wraper {
|
||||||
min-height: calc(100vh - var(--window-top));
|
min-height: calc(100vh - var(--window-top));
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.is-safe {
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-bottom: constant(safe-area-inset-bottom);
|
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<page-wraper>
|
<page-wraper>
|
||||||
<view class="wrap" :style="{ height: wrapHeight + 'px' }">
|
<view class="wraper">
|
||||||
<wd-index-bar>
|
<wd-index-bar sticky>
|
||||||
<view v-for="item in data" :key="item.index" class="city-wrap">
|
<view v-for="item in data" :key="item.index" class="city-wrap">
|
||||||
<wd-index-anchor :index="item.index" />
|
<wd-index-anchor :index="item.index" />
|
||||||
<view v-for="city in item.data" class="city" :key="city">{{ city }}</view>
|
<view v-for="city in item.data" class="city" :key="city" @click="handleClick(item.index, city)">{{ city }}</view>
|
||||||
</view>
|
</view>
|
||||||
</wd-index-bar>
|
</wd-index-bar>
|
||||||
</view>
|
</view>
|
||||||
@ -12,15 +12,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { useToast } from '@/uni_modules/wot-design-uni'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { onMounted } from 'vue'
|
const { show: showToast } = useToast()
|
||||||
|
|
||||||
const wrapHeight = ref(400)
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const info = uni.getWindowInfo()
|
|
||||||
wrapHeight.value = info.windowHeight
|
|
||||||
})
|
|
||||||
|
|
||||||
const data = ref([
|
const data = ref([
|
||||||
{
|
{
|
||||||
@ -110,6 +104,10 @@ const data = ref([
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
function handleClick(index: string, city: string) {
|
||||||
|
showToast(`当前点击项:${index},城市:${city}`)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -120,8 +118,10 @@ const data = ref([
|
|||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.city-wrap .city:last-child {
|
.wraper {
|
||||||
margin-bottom: 10px;
|
height: calc(100vh - var(--window-top));
|
||||||
|
height: calc(100vh - var(--window-top) - constant(safe-area-inset-bottom));
|
||||||
|
height: calc(100vh - var(--window-top) - env(safe-area-inset-bottom));
|
||||||
}
|
}
|
||||||
|
|
||||||
.wot-theme-dark {
|
.wot-theme-dark {
|
||||||
|
|||||||
@ -123,7 +123,7 @@ import { useCell } from '../composables/useCell'
|
|||||||
import { FORM_KEY, type FormItemRule } from '../wd-form/types'
|
import { FORM_KEY, type FormItemRule } from '../wd-form/types'
|
||||||
import { useParent } from '../composables/useParent'
|
import { useParent } from '../composables/useParent'
|
||||||
import { useTranslate } from '../composables/useTranslate'
|
import { useTranslate } from '../composables/useTranslate'
|
||||||
import { calendarProps, CalendarExpose } from './types'
|
import { calendarProps, type CalendarExpose } from './types'
|
||||||
import type { CalendarType } from '../wd-calendar-view/types'
|
import type { CalendarType } from '../wd-calendar-view/types'
|
||||||
const { translate } = useTranslate('calendar')
|
const { translate } = useTranslate('calendar')
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #ifdef MP-DINGTALK
|
||||||
|
@include b(index-anchor-ding) {
|
||||||
|
|
||||||
|
@include when(sticky){
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
@include b(index-anchor) {
|
@include b(index-anchor) {
|
||||||
background-color: $-color-gray-3;
|
background-color: $-color-gray-3;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $-color-title;
|
color: $-color-title;
|
||||||
|
|
||||||
|
@include when(sticky){
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include b(index-anchor) {
|
||||||
|
background-color: $-color-gray-3;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $-color-title;
|
||||||
|
|
||||||
|
@include when(sticky){
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="wd-index-anchor" :class="customClass" :style="customStyle">
|
<!-- #ifdef MP-DINGTALK -->
|
||||||
<slot>
|
<view :class="`wd-index-anchor-ding ${indexBar && indexBar.props.sticky && indexBar.anchorState.activeIndex === index ? 'is-sticky' : ''}`">
|
||||||
{{ index }}
|
<!-- #endif -->
|
||||||
</slot>
|
<view
|
||||||
|
:class="`wd-index-anchor ${indexBar && indexBar.props.sticky && indexBar.anchorState.activeIndex === index ? 'is-sticky' : ''} ${customClass}`"
|
||||||
|
:style="customStyle"
|
||||||
|
:id="indexAnchorId"
|
||||||
|
>
|
||||||
|
<slot>
|
||||||
|
{{ index }}
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
<!-- #ifdef MP-DINGTALK -->
|
||||||
</view>
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { indexAnchorProps } from './type'
|
import { indexAnchorProps } from './type'
|
||||||
import { onMounted, getCurrentInstance, inject } from 'vue'
|
import { onMounted, getCurrentInstance, ref } from 'vue'
|
||||||
import { indexBarInjectionKey } from '../wd-index-bar/type'
|
import { indexBarInjectionKey } from '../wd-index-bar/type'
|
||||||
import { getRect } from '../common/util'
|
import { getRect, isDef, uuid } from '../common/util'
|
||||||
|
import { useParent } from '../composables/useParent'
|
||||||
|
|
||||||
const props = defineProps(indexAnchorProps)
|
const props = defineProps(indexAnchorProps)
|
||||||
|
|
||||||
const indexBar = inject(indexBarInjectionKey)!
|
const { parent: indexBar } = useParent(indexBarInjectionKey)
|
||||||
|
|
||||||
|
const indexAnchorId = ref<string>(`indexBar${uuid()}`)
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()!
|
const { proxy } = getCurrentInstance()!
|
||||||
|
|
||||||
function getInfo() {
|
function getInfo() {
|
||||||
getRect('.wd-index-anchor', false, proxy).then((res) => {
|
getRect(`#${indexAnchorId.value}`, false, proxy).then((res) => {
|
||||||
const anchor = indexBar.anchorList.value.find((v) => v.index === props.index)!
|
if (isDef(indexBar)) {
|
||||||
anchor.top = res.top!
|
const anchor = indexBar.anchorState.anchorList.find((v) => v.index === props.index)!
|
||||||
|
anchor.top = res.top!
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
indexBar.anchorList.value.push({ top: 0, index: props.index })
|
if (isDef(indexBar)) {
|
||||||
|
indexBar.anchorState.anchorList.push({ top: 0, index: props.index })
|
||||||
|
}
|
||||||
getInfo()
|
getInfo()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import type { InjectionKey } from 'vue'
|
import type { InjectionKey } from 'vue'
|
||||||
import type { ExtractPropTypes } from 'vue'
|
import type { ExtractPropTypes } from 'vue'
|
||||||
|
import { makeBooleanProp } from '../common/props'
|
||||||
|
|
||||||
export type AnchorIndex = number | string
|
export type AnchorIndex = number | string
|
||||||
|
|
||||||
@ -9,10 +10,21 @@ export interface AnchorItem {
|
|||||||
index: AnchorIndex
|
index: AnchorIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
export const indexBarProps = {}
|
export const indexBarProps = {
|
||||||
|
/**
|
||||||
|
* @description 索引是否吸顶
|
||||||
|
*/
|
||||||
|
sticky: makeBooleanProp(false)
|
||||||
|
}
|
||||||
|
|
||||||
export type IndexBarProps = ExtractPropTypes<typeof indexBarProps>
|
export type IndexBarProps = ExtractPropTypes<typeof indexBarProps>
|
||||||
|
|
||||||
export const indexBarInjectionKey = 'wdIndexBar' as unknown as InjectionKey<{
|
export type InderBarProvide = {
|
||||||
anchorList: Ref<AnchorItem[]>
|
props: { sticky?: boolean }
|
||||||
}>
|
anchorState: {
|
||||||
|
anchorList: AnchorItem[] // 锚点列表
|
||||||
|
activeIndex: AnchorIndex | null // 当前激活的索引
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const indexBarInjectionKey: InjectionKey<InderBarProvide> = Symbol('wd-index-bar')
|
||||||
|
|||||||
@ -1,34 +1,55 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="wd-index-bar">
|
<view class="wd-index-bar" :id="indexBarId">
|
||||||
<scroll-view :scrollTop="scrollTop" :scroll-y="true" class="wd-index-bar__content" @scroll="hanleScroll">
|
<!-- #ifdef MP-DINGTALK -->
|
||||||
<slot></slot>
|
<view class="wd-index-bar" :id="indexBarId">
|
||||||
</scroll-view>
|
<!-- #endif -->
|
||||||
<view
|
<scroll-view :scrollTop="scrollTop" :scroll-y="true" class="wd-index-bar__content" @scroll="hanleScroll">
|
||||||
class="wd-index-bar__sidebar"
|
<slot></slot>
|
||||||
@touchmove.stop.prevent="handleTouchMove"
|
</scroll-view>
|
||||||
@touchend.stop.prevent="handleTouchEnd"
|
<view
|
||||||
@touchcancel.stop.prevent="handleTouchEnd"
|
class="wd-index-bar__sidebar"
|
||||||
>
|
@touchmove.stop.prevent="handleTouchMove"
|
||||||
<view class="wd-index-bar__index" :class="{ 'is-active': item.index === currentAnchorIndex }" v-for="item in anchorList" :key="item.index">
|
@touchend.stop.prevent="handleTouchEnd"
|
||||||
{{ item.index }}
|
@touchcancel.stop.prevent="handleTouchEnd"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="wd-index-bar__index"
|
||||||
|
:class="{ 'is-active': item.index === state.activeIndex }"
|
||||||
|
v-for="item in state.anchorList"
|
||||||
|
:key="item.index"
|
||||||
|
>
|
||||||
|
{{ item.index }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- #ifdef MP-DINGTALK -->
|
||||||
</view>
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AnchorItem, AnchorIndex } from './type'
|
import type { AnchorItem, AnchorIndex } from './type'
|
||||||
import { indexBarInjectionKey } from './type'
|
import { indexBarInjectionKey, indexBarProps } from './type'
|
||||||
import { provide, ref, getCurrentInstance, onMounted } from 'vue'
|
import { ref, getCurrentInstance, onMounted, reactive } from 'vue'
|
||||||
import { getRect } from '../common/util'
|
import { getRect, isDef, uuid } from '../common/util'
|
||||||
|
import { useChildren } from '../composables/useChildren'
|
||||||
|
|
||||||
|
const props = defineProps(indexBarProps)
|
||||||
|
|
||||||
|
const indexBarId = ref<string>(`indexBar${uuid()}`)
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance()!
|
const { proxy } = getCurrentInstance()!
|
||||||
|
|
||||||
const anchorList = ref<AnchorItem[]>([])
|
const state = reactive({
|
||||||
provide(indexBarInjectionKey, { anchorList })
|
activeIndex: null as AnchorIndex | null,
|
||||||
|
anchorList: [] as AnchorItem[]
|
||||||
|
})
|
||||||
|
|
||||||
|
const { linkChildren } = useChildren(indexBarInjectionKey)
|
||||||
|
|
||||||
|
linkChildren({ props, anchorState: state })
|
||||||
|
|
||||||
const scrollTop = ref(0)
|
const scrollTop = ref(0)
|
||||||
const currentAnchorIndex = ref<AnchorIndex | null>()
|
|
||||||
|
|
||||||
// 组件距离页面顶部的高度
|
// 组件距离页面顶部的高度
|
||||||
let offsetTop = 0
|
let offsetTop = 0
|
||||||
@ -41,10 +62,10 @@ let sidebarInfo = {
|
|||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
currentAnchorIndex.value = anchorList.value[0]?.index
|
state.activeIndex = state.anchorList[0]?.index
|
||||||
|
|
||||||
Promise.all([
|
Promise.all([
|
||||||
getRect('.wd-index-bar', false, proxy),
|
getRect(`#${indexBarId.value}`, false, proxy),
|
||||||
getRect('.wd-index-bar__sidebar', false, proxy),
|
getRect('.wd-index-bar__sidebar', false, proxy),
|
||||||
getRect('.wd-index-bar__index', false, proxy)
|
getRect('.wd-index-bar__index', false, proxy)
|
||||||
]).then(([bar, sidebar, index]) => {
|
]).then(([bar, sidebar, index]) => {
|
||||||
@ -61,26 +82,25 @@ onMounted(() => {
|
|||||||
|
|
||||||
function hanleScroll(scrollEvent: any) {
|
function hanleScroll(scrollEvent: any) {
|
||||||
const { detail } = scrollEvent
|
const { detail } = scrollEvent
|
||||||
scrollTop.value = detail.scrollTop
|
const anchor = state.anchorList.find((item, index) => {
|
||||||
const anchor = anchorList.value.find((item, index) => {
|
if (!isDef(state.anchorList[index + 1])) return true
|
||||||
if (anchorList.value[index + 1] == null) return true
|
if (item.top - offsetTop <= detail.scrollTop && state.anchorList[index + 1].top - offsetTop > detail.scrollTop) return true
|
||||||
if (item.top - offsetTop <= scrollTop.value && anchorList.value[index + 1].top - offsetTop > scrollTop.value) return true
|
|
||||||
return false
|
return false
|
||||||
})!
|
})!
|
||||||
currentAnchorIndex.value = anchor.index
|
state.activeIndex = anchor.index
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAnchorByPageY(pageY: number) {
|
function getAnchorByPageY(pageY: number) {
|
||||||
const y = pageY - sidebarInfo.offsetTop
|
const y = pageY - sidebarInfo.offsetTop
|
||||||
let idx = Math.floor(y / sidebarInfo.indexHeight)
|
let idx = Math.floor(y / sidebarInfo.indexHeight)
|
||||||
if (idx < 0) idx = 0
|
if (idx < 0) idx = 0
|
||||||
else if (idx > anchorList.value.length - 1) idx = anchorList.value.length - 1
|
else if (idx > state.anchorList.length - 1) idx = state.anchorList.length - 1
|
||||||
return anchorList.value[idx]
|
return state.anchorList[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchMove(e: TouchEvent) {
|
function handleTouchMove(e: TouchEvent) {
|
||||||
const clientY = e.touches[0].pageY
|
const clientY = e.touches[0].pageY
|
||||||
currentAnchorIndex.value = getAnchorByPageY(clientY).index
|
state.activeIndex = getAnchorByPageY(clientY).index
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTouchEnd(e: TouchEvent) {
|
function handleTouchEnd(e: TouchEvent) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user