123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- <template>
- <view>
- <view class="fs-sidebar" @touchmove="handleMove" @touchend="handleEnd" v-if="list.length">
- <view
- class="fs-sidebar-item"
- :class="{ primary: state.activeId === item }"
- v-for="(item, index) in letters"
- :key="index"
- @click="handleClick(item)"
- >
- {{ item }}
- </view>
- </view>
- <view class="fs-contact" :style="{ 'margin-top': showSearch ? '110rpx' : '' }">
- <scroll-view scroll-y :scroll-into-view="state.intoView" @scroll="scroll" class="fs-contact-list">
- <view v-for="item in list" :key="item.name" :id="item.name" class="fs-contact-list-item">
- <view class="fs-panel-title" :class="{ 'fs-sticky': sticky }">{{ item[titleKey] }}</view>
- <fs-cell
- v-for="subitem in item[childrenKey]"
- :key="subitem.name"
- border
- justify="left"
- align="center"
- :link="link + '?id=' + subitem.id"
- @click="handleRoute(subitem)"
- >
- <template #title>
- <fs-avatar :src="subitem[avatarKey]">{{ subitem.alais }}</fs-avatar>
- </template>
- <template #value>
- <view class="fs-contact-hd">{{ subitem[hdKey] }}</view>
- <view class="fs-contact-bd">{{ subitem[bdKey] }}</view>
- </template>
- </fs-cell>
- </view>
- </scroll-view>
- </view>
- <view class="fs-layer" v-if="state.showLayer">{{ state.activeId }}</view>
- </view>
- </template>
- <script>
- /**
- * 索引列表组件
- * @description 索引列表组件
- * @property {Array} list 列表
- * @property {String} childrenKey children Key
- * @property {String} titleKey title Key
- * @property {String} avatarKey avatar Key
- * @property {String} hdKey hd Key
- * @property {String} bdKey bd Key
- * @property {String} link 跳转地址
- * @property {Boolean} sticky 是否固定
- * @property {Boolean} showSearch 是否显示搜索
- */
- export default {
- name: 'fs-index-list'
- }
- </script>
- <script setup>
- import { reactive } from 'vue'
- const letters = []
- for (var i = 0; i < 26; i++) {
- letters.push(String.fromCharCode(65 + i))
- }
- const props = defineProps({
- list: Array,
- childrenKey: {
- type: String,
- default: 'list'
- },
- titleKey: {
- type: String,
- default: 'name'
- },
- avatarKey: {
- type: String,
- default: 'src'
- },
- hdKey: {
- type: String,
- default: 'name'
- },
- bdKey: {
- type: String,
- default: 'phone'
- },
- link: String,
- sticky: {
- type: Boolean,
- default: true
- },
- showSearch: Boolean
- })
- const windowHeight = uni.getSystemInfoSync().windowHeight
- const offsetHeight = 50
- const navHeight = windowHeight - offsetHeight * 2
- const letterPos = []
- const letterHeight = navHeight / letters.length
- letters.forEach((item, index) => {
- letterPos.push(offsetHeight + letterHeight * index)
- })
- const state = reactive({
- intoView: '',
- activeId: '',
- showLayer: false
- })
- const handleClick = id => {
- state.intoView = id
- state.activeId = id
- }
- const handleMove = e => {
- const y = e.touches[0].clientY
- for (let i = 0, len = letterPos.length; i < len; i++) {
- if (y >= letterPos[i] && y <= letterPos[i] + letterHeight) {
- state.intoView = letters[i]
- state.activeId = letters[i]
- state.showLayer = true
- break
- }
- }
- }
- const handleEnd = e => {
- setTimeout(() => {
- state.showLayer = false
- }, 200)
- }
- const scroll = e => {
- uni
- .createSelectorQuery()
- .selectAll('.list-item')
- .boundingClientRect(rects => {
- for (let i = 0; i < rects.length; i++) {
- let rect = rects[i]
- if (rect.top === 0 || rect.bottom > 0) {
- state.activeId = rect.id
- break
- }
- }
- })
- .exec()
- }
- const handleRoute = item => {
- getApp().globalData.addrbookDetail = item
- }
- </script>
- <style lang="scss" scoped>
- .fs-sidebar {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- position: fixed;
- top: 50%;
- right: 0;
- align-items: center;
- z-index: 900;
- transform: translateY(-50%);
- &-item {
- padding: 6rpx 20rpx;
- flex-shrink: 1;
- font-size: 24rpx;
- }
- }
- .fs-sticky {
- position: sticky;
- top: 0;
- z-index: 100;
- }
- .fs-contact {
- position: fixed;
- top: var(--window-top);
- left: var(--gutter);
- right: 60rpx;
- bottom: 0;
- &-list {
- height: 100%;
- }
- &-hd {
- font-size: 16px;
- }
- .fs-panel-title {
- padding: var(--gutter);
- color: var(--title);
- text-align: left;
- background-color: var(--bg-color);
- }
- }
- .fs-layer {
- width: 150rpx;
- height: 150rpx;
- background: rgba(0, 0, 0, 0.5);
- border-radius: 50%;
- line-height: 150rpx;
- color: #ffffff;
- text-align: center;
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- font-weight: bolder;
- font-size: 28px;
- }
- </style>
|