¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view class="u-index-list"> |
| | | <!-- #ifdef APP-NVUE --> |
| | | <list |
| | | :scrollTop="scrollTop" |
| | | enable-back-to-top |
| | | :offset-accuracy="1" |
| | | :style="{ |
| | | maxHeight: $u.addUnit(scrollViewHeight) |
| | | }" |
| | | @scroll="scrollHandler" |
| | | ref="uList" |
| | | > |
| | | <cell |
| | | v-if="$slots.header" |
| | | ref="header" |
| | | > |
| | | <slot name="header" /> |
| | | </cell> |
| | | <slot /> |
| | | <cell v-if="$slots.footer"> |
| | | <slot name="footer" /> |
| | | </cell> |
| | | </list> |
| | | <!-- #endif --> |
| | | <!-- #ifndef APP-NVUE --> |
| | | <scroll-view |
| | | :scrollTop="scrollTop" |
| | | :scrollIntoView="scrollIntoView" |
| | | :offset-accuracy="1" |
| | | :style="{ |
| | | maxHeight: $u.addUnit(scrollViewHeight) |
| | | }" |
| | | scroll-y |
| | | @scroll="scrollHandler" |
| | | ref="uList" |
| | | > |
| | | <view v-if="$slots.header"> |
| | | <slot name="header" /> |
| | | </view> |
| | | <slot /> |
| | | <view v-if="$slots.footer"> |
| | | <slot name="footer" /> |
| | | </view> |
| | | </scroll-view> |
| | | <!-- #endif --> |
| | | <view |
| | | class="u-index-list__letter" |
| | | ref="u-index-list__letter" |
| | | :style="{ top: $u.addUnit(letterInfo.top || 100) }" |
| | | @touchstart="touchStart" |
| | | @touchmove.stop.prevent="touchMove" |
| | | @touchend.stop.prevent="touchEnd" |
| | | @touchcancel.stop.prevent="touchEnd" |
| | | > |
| | | <view |
| | | class="u-index-list__letter__item" |
| | | v-for="(item, index) in uIndexList" |
| | | :key="index" |
| | | :style="{ |
| | | backgroundColor: activeIndex === index ? activeColor : 'transparent' |
| | | }" |
| | | > |
| | | <text |
| | | class="u-index-list__letter__item__index" |
| | | :style="{color: activeIndex === index ? '#fff' : inactiveColor}" |
| | | >{{ item }}</text> |
| | | </view> |
| | | </view> |
| | | <u-transition |
| | | mode="fade" |
| | | :show="touching" |
| | | :customStyle="{ |
| | | position: 'fixed', |
| | | right: '50px', |
| | | top: $u.addUnit(indicatorTop), |
| | | zIndex: 2 |
| | | }" |
| | | > |
| | | <view |
| | | class="u-index-list__indicator" |
| | | :class="['u-index-list__indicator--show']" |
| | | :style="{ |
| | | height: $u.addUnit(indicatorHeight), |
| | | width: $u.addUnit(indicatorHeight) |
| | | }" |
| | | > |
| | | <text class="u-index-list__indicator__text">{{ uIndexList[activeIndex] }}</text> |
| | | </view> |
| | | </u-transition> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | const indexList = () => { |
| | | const indexList = []; |
| | | const charCodeOfA = 'A'.charCodeAt(0); |
| | | for (let i = 0; i < 26; i++) { |
| | | indexList.push(String.fromCharCode(charCodeOfA + i)); |
| | | } |
| | | return indexList; |
| | | } |
| | | import props from './props.js'; |
| | | // #ifdef APP-NVUE |
| | | // ç±äºweex为é¿éçKPIä¸ç»©èæ ¸ç产ç©ï¼æä»¥ä¸æ¯æç¾åæ¯åä½ï¼è¿ééè¦éè¿domæ¥è¯¢ç»ä»¶ç宽度 |
| | | const dom = uni.requireNativePlugin('dom') |
| | | // #endif |
| | | /** |
| | | * IndexList ç´¢å¼å表 |
| | | * @description éè¿æå 颿¿æ¶çº³å
容åºå |
| | | * @tutorial https://uviewui.com/components/indexList.html |
| | | * @property {String} inactiveColor å³è¾¹éç¹éæ¿æ´»çé¢è² ( é»è®¤ '#606266' ) |
| | | * @property {String} activeColor å³è¾¹éç¹æ¿æ´»çé¢è² ( é»è®¤ '#5677fc' ) |
| | | * @property {Array} indexList ç´¢å¼å符åè¡¨ï¼æ°ç»å½¢å¼ |
| | | * @property {Boolean} sticky æ¯å¦å¼å¯éç¹èªå¨å¸é¡¶ ( é»è®¤ true ) |
| | | * @property {String | Number} customNavHeight èªå®ä¹å¯¼èªæ çé«åº¦ ( é»è®¤ 0 ) |
| | | * */ |
| | | export default { |
| | | name: 'u-index-list', |
| | | mixins: [uni.$u.mpMixin, uni.$u.mixin, props], |
| | | // #ifdef MP-WEIXIN |
| | | // å°èªå®ä¹èç¹è®¾ç½®æèæçï¼æ´å æ¥è¿Vueç»ä»¶ç表ç°ï¼è½æ´å¥½ç使ç¨flex屿§ |
| | | options: { |
| | | virtualHost: true |
| | | }, |
| | | // #endif |
| | | data() { |
| | | return { |
| | | // å½åæ£å¨è¢«éä¸çåæ¯ç´¢å¼ |
| | | activeIndex: -1, |
| | | touchmoveIndex: 1, |
| | | // ç´¢å¼åæ¯çä¿¡æ¯ |
| | | letterInfo: { |
| | | height: 0, |
| | | itemHeight: 0, |
| | | top: 0 |
| | | }, |
| | | // è®¾ç½®åæ¯æç¤ºå¨çé«åº¦ï¼åé¢ä¸ºäºè®©æç¤ºå¨è·é忝ï¼å¹¶å°å°è§é¨åæå忝çä¸é¨ï¼éè¦ä¾èµæ¤å¼ |
| | | indicatorHeight: 50, |
| | | // 忝æ¾å¤§æç¤ºå¨çtopå¼ï¼ä¸ºäºè®©å
¶æåå½åæ¿æ´»ç忝 |
| | | // indicatorTop: 0 |
| | | // å½åæ¯å¦æ£å¨è¢«è§¦æ¸ç¶æ |
| | | touching: false, |
| | | // æ»å¨æ¡é¡¶é¨topå¼ |
| | | scrollTop: 0, |
| | | // scroll-viewçé«åº¦ |
| | | scrollViewHeight: 0, |
| | | // ç³»ç»ä¿¡æ¯ |
| | | sys: uni.$u.sys(), |
| | | scrolling: false, |
| | | scrollIntoView: '', |
| | | } |
| | | }, |
| | | computed: { |
| | | // 妿æä¼ å
¥å¤é¨çindexListéç¹æ°ç»å使ç¨ï¼å¦å使ç¨å
é¨çæA-Z忝 |
| | | uIndexList() { |
| | | return this.indexList.length ? this.indexList : indexList() |
| | | }, |
| | | // 忝æ¾å¤§æç¤ºå¨çtopå¼ï¼ä¸ºäºè®©å
¶æåå½åæ¿æ´»ç忝 |
| | | indicatorTop() { |
| | | const { |
| | | top, |
| | | itemHeight |
| | | } = this.letterInfo |
| | | return Math.floor(top + itemHeight * this.activeIndex + itemHeight / 2 - this.indicatorHeight / 2) |
| | | } |
| | | }, |
| | | watch: { |
| | | // çå¬åæ¯ç´¢å¼çååï¼éæ°è®¾ç½®å°ºå¯¸ |
| | | uIndexList: { |
| | | immediate: true, |
| | | handler() { |
| | | uni.$u.sleep().then(() => { |
| | | this.setIndexListLetterInfo() |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.children = [] |
| | | this.anchors = [] |
| | | this.init() |
| | | }, |
| | | mounted() { |
| | | this.setIndexListLetterInfo() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 设置å表çé«åº¦ä¸ºæ´ä¸ªå±å¹çé«åº¦ |
| | | //åå»this.customNavHeightï¼å¹¶å°this.scrollViewHeight设置为maxHeight |
| | | //è§£å³å½u-index-listç»ä»¶æ¾å¨tabbar页颿¶,scroll-viewå
容è¾å°æ¶ï¼è¿è½æ»å¨ |
| | | this.scrollViewHeight = this.sys.windowHeight - this.customNavHeight |
| | | }, |
| | | // ç´¢å¼åè¡¨è¢«è§¦æ¸ |
| | | touchStart(e) { |
| | | // è·å触æ¸ç¹ä¿¡æ¯ |
| | | const touchStart = e.changedTouches[0] |
| | | if (!touchStart) return |
| | | this.touching = true |
| | | const { |
| | | pageY |
| | | } = touchStart |
| | | // æ ¹æ®å½å触æ¸ç¹çåæ ï¼è·åå½å触æ¸ç为第å ä¸ªåæ¯ |
| | | const currentIndex = this.getIndexListLetter(pageY) |
| | | this.setValueForTouch(currentIndex) |
| | | }, |
| | | // ç´¢å¼åæ¯åè¡¨è¢«è§¦æ¸æ»å¨ä¸ |
| | | touchMove(e) { |
| | | // è·å触æ¸ç¹ä¿¡æ¯ |
| | | let touchMove = e.changedTouches[0] |
| | | if (!touchMove) return; |
| | | |
| | | // æ»å¨ç»æåè¿
éå¼å§ç¬¬äºæ¬¡æ»å¨æ¶å touching 为 false é æä¸æ¾ç¤º indicator é®é¢ |
| | | if (!this.touching) { |
| | | this.touching = true |
| | | } |
| | | const { |
| | | pageY |
| | | } = touchMove |
| | | const currentIndex = this.getIndexListLetter(pageY) |
| | | this.setValueForTouch(currentIndex) |
| | | }, |
| | | // 触æ¸ç»æ |
| | | touchEnd(e) { |
| | | // å»¶æ¶ä¸å®æ¶é´ååéèæç¤ºå¨ï¼ä¸ºäºè®©ç¨æ·ççæ´ç´è§ï¼åæ¶ä¹æ¯ä¸ºäºæ¶é¤å¿«é忢u-transitionçshow带æ¥çå½±å |
| | | uni.$u.sleep(300).then(() => { |
| | | this.touching = false |
| | | }) |
| | | }, |
| | | // è·åç´¢å¼å表ç尺寸以åå个å符çå°ºå¯¸ä¿¡æ¯ |
| | | getIndexListLetterRect() { |
| | | return new Promise(resolve => { |
| | | // å»¶æ¶ä¸å®æ¶é´ï¼ä»¥è·ådom尺寸 |
| | | // #ifndef APP-NVUE |
| | | this.$uGetRect('.u-index-list__letter').then(size => { |
| | | resolve(size) |
| | | }) |
| | | // #endif |
| | | |
| | | // #ifdef APP-NVUE |
| | | const ref = this.$refs['u-index-list__letter'] |
| | | dom.getComponentRect(ref, res => { |
| | | resolve(res.size) |
| | | }) |
| | | // #endif |
| | | }) |
| | | }, |
| | | // 设置indexListç´¢å¼çå°ºå¯¸ä¿¡æ¯ |
| | | setIndexListLetterInfo() { |
| | | this.getIndexListLetterRect().then(size => { |
| | | const { |
| | | height |
| | | } = size |
| | | const sys = uni.$u.sys() |
| | | const windowHeight = sys.windowHeight |
| | | let customNavHeight = 0 |
| | | // æ¶é¤åç«¯å¯¼èªæ éåçååç导è´çå·®å¼ï¼è®©ç´¢å¼åè¡¨åæ¯å¯¹å±å¹åç´å±
ä¸ |
| | | if (this.customNavHeight == 0) { |
| | | // #ifdef H5 |
| | | customNavHeight = sys.windowTop |
| | | // #endif |
| | | // #ifndef H5 |
| | | // å¨éH5ä¸ï¼ä¸ºåçå¯¼èªæ ï¼å
¶é«åº¦ä¸ç®å¨windowHeightå
ï¼è¿é设置为è´å¼ï¼åé¢ç¸å æ¶åæåå»å
¶é«åº¦çä¸å |
| | | customNavHeight = -(sys.statusBarHeight + 44) |
| | | // #endif |
| | | } else { |
| | | customNavHeight = uni.$u.getPx(this.customNavHeight) |
| | | } |
| | | this.letterInfo = { |
| | | height, |
| | | // 为äºè®©åæ¯å表对å±å¹ç»å¯¹å±
ä¸ï¼è®©å
¶å¯¹å¯¼èªæ è¿è¡ä¿®æ£ï¼ä¹å³å¾ä¸åç§»å¯¼èªæ çä¸åé«åº¦ |
| | | top: (windowHeight - height) / 2 + customNavHeight / 2, |
| | | itemHeight: Math.floor(height / this.uIndexList.length) |
| | | } |
| | | }) |
| | | }, |
| | | // è·åå½å被触æ¸çç´¢å¼åæ¯ |
| | | getIndexListLetter(pageY) { |
| | | const { |
| | | top, |
| | | height, |
| | | itemHeight |
| | | } = this.letterInfo |
| | | // 对H5çpageYè¿è¡ä¿®æ£ï¼è¿æ¯ç±äºuni-appèªä½å¤æ
å¨H5ä¸å°è§¦æ¸ç¹çåæ è·H5çå¯¼èªæ ç»å导è´çé®é¢ |
| | | // #ifdef H5 |
| | | pageY += uni.$u.sys().windowTop |
| | | // #endif |
| | | // 对第ä¸åæåä¸ä¸ªåæ¯åè¾¹çå¤çï¼å ä¸ºç¨æ·å¯è½å¨åæ¯å表ä¸è§¦æ¸å°ä¸¤ç«¯ç尽头åä¾ç¶ç»§ç»æ»å¨ |
| | | if (pageY < top) { |
| | | return 0 |
| | | } else if (pageY >= top + height) { |
| | | // 妿è¶
åºäºï¼åæåä¸ä¸ªåæ¯ |
| | | return this.uIndexList.length - 1 |
| | | } else { |
| | | // å°è§¦æ¸ç¹çYè½´åç§»å¼ï¼åå»ç´¢å¼åæ¯çtopå¼ï¼é¤ä»¥æ¯ä¸ªåæ¯çé«åº¦ï¼å³å¯å¾å°å½å触æ¸ç¹è½å¨åªä¸ªåæ¯ä¸ |
| | | return Math.floor((pageY - top) / itemHeight); |
| | | } |
| | | }, |
| | | // 设置å项ç±è§¦æ¸è导è´ååçå¼ |
| | | setValueForTouch(currentIndex) { |
| | | // 妿åç§»é太å°ï¼ååå¾åºç伿¯åä¸ä¸ªç´¢å¼åæ¯ï¼ä¸ºäºé²æï¼è¿è¡è¿å |
| | | if (currentIndex === this.activeIndex) return |
| | | this.activeIndex = currentIndex |
| | | // #ifndef APP-NVUE || MP-WEIXIN |
| | | // å¨énvueä¸ï¼ç±äºanchoråitemé½å¨u-index-itemä¸ï¼æä»¥éè¦å¯¹index-itemè¿è¡åç§» |
| | | this.scrollIntoView = `u-index-item-${this.uIndexList[currentIndex].charCodeAt(0)}` |
| | | // #endif |
| | | // #ifdef MP-WEIXIN |
| | | // 微信å°ç¨åºä¸ï¼scroll-viewçscroll-into-view屿§æ æ³å¯¹slotä¸çå
容çidçæï¼åªè½éè¿è®¾ç½®scrollTopçå½¢å¼å»ç§»å¨æ»å¨æ¡ |
| | | this.scrollTop = this.children[currentIndex].top |
| | | // #endif |
| | | // #ifdef APP-NVUE |
| | | // å¨nvueä¸ï¼ç±äºcellåheader为å级å
ç´ ï¼æä»¥å®é
æ¯éè¦å¯¹header(anchor)è¿è¡åç§» |
| | | const anchor = `u-index-anchor-${this.uIndexList[currentIndex]}` |
| | | dom.scrollToElement(this.anchors[currentIndex].$refs[anchor], { |
| | | offset: 0, |
| | | animated: false |
| | | }) |
| | | // #endif |
| | | }, |
| | | getHeaderRect() { |
| | | // è·åheader slotçé«åº¦ï¼å 为listç»ä»¶ä¸è·åå
ç´ çå°ºå¯¸æ¯æ²¡ætopå¼ç |
| | | return new Promise(resolve => { |
| | | dom.getComponentRect(this.$refs.header, res => { |
| | | resolve(res.size) |
| | | }) |
| | | }) |
| | | }, |
| | | // scroll-viewçæ»å¨äºä»¶ |
| | | async scrollHandler(e) { |
| | | if (this.touching || this.scrolling) return |
| | | // æ¯è¿ä¸å®æ¶é´åæ ·ä¸æ¬¡ï¼åå°èµæºæè以åå¯è½å¸¦æ¥çå¡é¡¿ |
| | | this.scrolling = true |
| | | uni.$u.sleep(10).then(() => { |
| | | this.scrolling = false |
| | | }) |
| | | let scrollTop = 0 |
| | | const len = this.children.length |
| | | let children = this.children |
| | | const anchors = this.anchors |
| | | // #ifdef APP-NVUE |
| | | // nvueä¸è·åçæ»å¨æ¡åç§»ä¸ºè´æ°ï¼éè¦è½¬ä¸ºæ£æ° |
| | | scrollTop = Math.abs(e.contentOffset.y) |
| | | // è·åheader slotçå°ºå¯¸ä¿¡æ¯ |
| | | const header = await this.getHeaderRect() |
| | | // itemçtopå¼ï¼å¨nvueä¸ï¼æ¨¡æåºçanchorçtopï¼ç±»ä¼¼énvueä¸çindex-itemçtop |
| | | let top = header.height |
| | | // ç±äºlistç»ä»¶æ æ³è·åcellçtopå¼ï¼è¿ééè¿header slotåå个itemä¹é´çheightï¼æ¨¡æåºç±»ä¼¼énvueä¸çä½ç½®ä¿¡æ¯ |
| | | children = this.children.map((item, index) => { |
| | | const child = { |
| | | height: item.height, |
| | | top |
| | | } |
| | | // è¿è¡ç´¯å ï¼ç»ä¸ä¸ä¸ªitemæä¾è®¡ç®ä¾æ® |
| | | top += item.height + anchors[index].height |
| | | return child |
| | | }) |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | // énvueéè¿detailè·åæ»å¨æ¡ä½ç§» |
| | | scrollTop = e.detail.scrollTop |
| | | // #endif |
| | | for (let i = 0; i < len; i++) { |
| | | const item = children[i], |
| | | nextItem = children[i + 1] |
| | | // 妿æ»å¨æ¡é«åº¦å°äºç¬¬ä¸ä¸ªitemçtopå¼ï¼æ¤æ¶æ é设置任æåæ¯ä¸ºé«äº® |
| | | if (scrollTop <= children[0].top || scrollTop >= children[len - 1].top + children[len - |
| | | 1].height) { |
| | | this.activeIndex = -1 |
| | | break |
| | | } else if (!nextItem) { |
| | | // å½ä¸åå¨ä¸ä¸ä¸ªitemæ¶ï¼æå³çåéå°äºæåä¸ä¸ª |
| | | this.activeIndex = len - 1 |
| | | break |
| | | } else if (scrollTop > item.top && scrollTop < nextItem.top) { |
| | | this.activeIndex = i |
| | | break |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | @import "../../libs/css/components.scss"; |
| | | |
| | | .u-index-list { |
| | | |
| | | &__letter { |
| | | position: fixed; |
| | | right: 0; |
| | | text-align: center; |
| | | z-index: 3; |
| | | padding: 0 6px; |
| | | |
| | | &__item { |
| | | width: 16px; |
| | | height: 16px; |
| | | border-radius: 100px; |
| | | margin: 1px 0; |
| | | @include flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | &--active { |
| | | background-color: $u-primary; |
| | | } |
| | | |
| | | &__index { |
| | | font-size: 12px; |
| | | text-align: center; |
| | | line-height: 12px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &__indicator { |
| | | width: 50px; |
| | | height: 50px; |
| | | border-radius: 100px 100px 0 100px; |
| | | text-align: center; |
| | | color: #ffffff; |
| | | background-color: #c9c9c9; |
| | | transform: rotate(-45deg); |
| | | @include flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | |
| | | &__text { |
| | | font-size: 28px; |
| | | line-height: 28px; |
| | | font-weight: bold; |
| | | color: #fff; |
| | | transform: rotate(45deg); |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | </style> |