|
|
|
|
<template>
|
|
|
|
|
<view class="uni-indexed-list" ref="list" id="list">
|
|
|
|
|
<!-- #ifdef APP-NVUE -->
|
|
|
|
|
<list class="uni-indexed-list__scroll" scrollable="true" show-scrollbar="false">
|
|
|
|
|
<cell v-for="(list, idx) in lists" :key="idx" :ref="'uni-indexed-list-' + idx">
|
|
|
|
|
<!-- #endif -->
|
|
|
|
|
<!-- #ifndef APP-NVUE -->
|
|
|
|
|
<scroll-view :scroll-into-view="scrollViewId" class="uni-indexed-list__scroll" scroll-y>
|
|
|
|
|
<view v-for="(list, idx) in lists" :key="idx" :id="'uni-indexed-list-' + idx">
|
|
|
|
|
<!-- #endif -->
|
|
|
|
|
<uni-indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect" @itemClick="onClick"></uni-indexed-list-item>
|
|
|
|
|
<!-- #ifndef APP-NVUE -->
|
|
|
|
|
</view>
|
|
|
|
|
</scroll-view>
|
|
|
|
|
<!-- #endif -->
|
|
|
|
|
<!-- #ifdef APP-NVUE -->
|
|
|
|
|
</cell>
|
|
|
|
|
</list>
|
|
|
|
|
<!-- #endif -->
|
|
|
|
|
<view :class="touchmove ? 'uni-indexed-list__menu--active' : ''" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
|
|
|
|
|
@touchend="touchEnd" class="uni-indexed-list__menu">
|
|
|
|
|
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item">
|
|
|
|
|
<text class="uni-indexed-list__menu-text" :class="touchmoveIndex == key ? 'uni-indexed-list__menu-text--active' : ''">{{ list.key }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
|
|
|
|
|
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex].key }}</text>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
<script>
|
|
|
|
|
import uniIcons from '../uni-icons/uni-icons.vue'
|
|
|
|
|
import uniIndexedListItem from './uni-indexed-list-item.vue'
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
const dom = weex.requireModule('dom');
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
function throttle(func, delay) {
|
|
|
|
|
var prev = Date.now();
|
|
|
|
|
return function() {
|
|
|
|
|
var context = this;
|
|
|
|
|
var args = arguments;
|
|
|
|
|
var now = Date.now();
|
|
|
|
|
if (now - prev >= delay) {
|
|
|
|
|
func.apply(context, args);
|
|
|
|
|
prev = Date.now();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function touchMove(e) {
|
|
|
|
|
let pageY = e.touches[0].pageY
|
|
|
|
|
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
|
|
|
|
|
if (this.touchmoveIndex === index) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
let item = this.lists[index]
|
|
|
|
|
if (item) {
|
|
|
|
|
// #ifndef APP-NVUE
|
|
|
|
|
this.scrollViewId = 'uni-indexed-list-' + index
|
|
|
|
|
this.touchmoveIndex = index
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
|
|
|
|
|
animated: false
|
|
|
|
|
})
|
|
|
|
|
this.touchmoveIndex = index
|
|
|
|
|
// #endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const throttleTouchMove = throttle(touchMove, 40)
|
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* IndexedList 索引列表
|
|
|
|
|
* @description 用于展示索引列表
|
|
|
|
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=375
|
|
|
|
|
* @property {Boolean} showSelect = [true|false] 展示模式
|
|
|
|
|
* @value true 展示模式
|
|
|
|
|
* @value false 选择模式
|
|
|
|
|
* @property {Object} options 索引列表需要的数据对象
|
|
|
|
|
* @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
|
|
|
|
|
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
|
|
|
|
|
*/
|
|
|
|
|
export default {
|
|
|
|
|
name: 'UniIndexedList',
|
|
|
|
|
components: {
|
|
|
|
|
uniIcons,
|
|
|
|
|
uniIndexedListItem
|
|
|
|
|
},
|
|
|
|
|
props: {
|
|
|
|
|
options: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default () {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
showSelect: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
lists: [],
|
|
|
|
|
winHeight: 0,
|
|
|
|
|
itemHeight: 0,
|
|
|
|
|
winOffsetY: 0,
|
|
|
|
|
touchmove: false,
|
|
|
|
|
touchmoveIndex: -1,
|
|
|
|
|
scrollViewId: '',
|
|
|
|
|
touchmoveTimeout: '',
|
|
|
|
|
loaded: false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
options: {
|
|
|
|
|
handler: function() {
|
|
|
|
|
this.setList()
|
|
|
|
|
},
|
|
|
|
|
deep: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.setList()
|
|
|
|
|
}, 50)
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.loaded = true
|
|
|
|
|
}, 300);
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
setList() {
|
|
|
|
|
let index = 0;
|
|
|
|
|
this.lists = []
|
|
|
|
|
this.options.forEach((value, index) => {
|
|
|
|
|
if (value.data.length === 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
let indexBefore = index
|
|
|
|
|
let items = value.data.map(item => {
|
|
|
|
|
let obj = {}
|
|
|
|
|
obj['key'] = value.letter
|
|
|
|
|
obj['name'] = item
|
|
|
|
|
obj['itemIndex'] = index
|
|
|
|
|
index++
|
|
|
|
|
obj.checked = item.checked ? item.checked : false
|
|
|
|
|
return obj
|
|
|
|
|
})
|
|
|
|
|
this.lists.push({
|
|
|
|
|
title: value.letter,
|
|
|
|
|
key: value.letter,
|
|
|
|
|
items: items,
|
|
|
|
|
itemIndex: indexBefore
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
// #ifndef APP-NVUE
|
|
|
|
|
uni.createSelectorQuery()
|
|
|
|
|
.in(this)
|
|
|
|
|
.select('#list')
|
|
|
|
|
.boundingClientRect()
|
|
|
|
|
.exec(ret => {
|
|
|
|
|
this.winOffsetY = ret[0].top
|
|
|
|
|
this.winHeight = ret[0].height
|
|
|
|
|
this.itemHeight = this.winHeight / this.lists.length
|
|
|
|
|
})
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
dom.getComponentRect(this.$refs['list'], (res) => {
|
|
|
|
|
this.winOffsetY = res.size.top
|
|
|
|
|
this.winHeight = res.size.height
|
|
|
|
|
this.itemHeight = this.winHeight / this.lists.length
|
|
|
|
|
})
|
|
|
|
|
// #endif
|
|
|
|
|
},
|
|
|
|
|
touchStart(e) {
|
|
|
|
|
this.touchmove = true
|
|
|
|
|
let pageY = e.touches[0].pageY
|
|
|
|
|
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
|
|
|
|
|
let item = this.lists[index]
|
|
|
|
|
if (item) {
|
|
|
|
|
this.scrollViewId = 'uni-indexed-list-' + index
|
|
|
|
|
this.touchmoveIndex = index
|
|
|
|
|
// #ifdef APP-NVUE
|
|
|
|
|
dom.scrollToElement(this.$refs['uni-indexed-list-' + index][0], {
|
|
|
|
|
animated: false
|
|
|
|
|
})
|
|
|
|
|
// #endif
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
touchMove(e) {
|
|
|
|
|
// #ifndef APP-PLUS
|
|
|
|
|
let pageY = e.touches[0].pageY
|
|
|
|
|
let index = Math.floor((pageY - this.winOffsetY) / this.itemHeight)
|
|
|
|
|
if (this.touchmoveIndex === index) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
let item = this.lists[index]
|
|
|
|
|
if (item) {
|
|
|
|
|
this.scrollViewId = 'uni-indexed-list-' + index
|
|
|
|
|
this.touchmoveIndex = index
|
|
|
|
|
}
|
|
|
|
|
// #endif
|
|
|
|
|
// #ifdef APP-PLUS
|
|
|
|
|
throttleTouchMove.call(this, e)
|
|
|
|
|
// #endif
|
|
|
|
|
},
|
|
|
|
|
touchEnd() {
|
|
|
|
|
this.touchmove = false
|
|
|
|
|
this.touchmoveIndex = -1
|
|
|
|
|
},
|
|
|
|
|
onClick(e) {
|
|
|
|
|
let {
|
|
|
|
|
idx,
|
|
|
|
|
index
|
|
|
|
|
} = e
|
|
|
|
|
let obj = {}
|
|
|
|
|
for (let key in this.lists[idx].items[index]) {
|
|
|
|
|
obj[key] = this.lists[idx].items[index][key]
|
|
|
|
|
}
|
|
|
|
|
let select = []
|
|
|
|
|
if (this.showSelect) {
|
|
|
|
|
this.lists[idx].items[index].checked = !this.lists[idx].items[index].checked
|
|
|
|
|
this.lists.forEach((value, idx) => {
|
|
|
|
|
value.items.forEach((item, index) => {
|
|
|
|
|
if (item.checked) {
|
|
|
|
|
let obj = {}
|
|
|
|
|
for (let key in this.lists[idx].items[index]) {
|
|
|
|
|
obj[key] = this.lists[idx].items[index][key]
|
|
|
|
|
}
|
|
|
|
|
select.push(obj)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
this.$emit('click', {
|
|
|
|
|
item: obj,
|
|
|
|
|
select: select
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.uni-indexed-list {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
/* #ifndef APP-NVUE */
|
|
|
|
|
display: flex;
|
|
|
|
|
/* #endif */
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__scroll {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__menu {
|
|
|
|
|
width: 24px;
|
|
|
|
|
background-color: lightgrey;
|
|
|
|
|
/* #ifndef APP-NVUE */
|
|
|
|
|
display: flex;
|
|
|
|
|
/* #endif */
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__menu-item {
|
|
|
|
|
/* #ifndef APP-NVUE */
|
|
|
|
|
display: flex;
|
|
|
|
|
/* #endif */
|
|
|
|
|
flex: 1;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__menu-text {
|
|
|
|
|
line-height: 20px;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: #aaa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__menu--active {
|
|
|
|
|
background-color: rgb(200, 200, 200);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__menu-text--active {
|
|
|
|
|
color: #007aff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__alert-wrapper {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
/* #ifndef APP-NVUE */
|
|
|
|
|
display: flex;
|
|
|
|
|
/* #endif */
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.uni-indexed-list__alert {
|
|
|
|
|
width: 80px;
|
|
|
|
|
height: 80px;
|
|
|
|
|
border-radius: 80px;
|
|
|
|
|
text-align: center;
|
|
|
|
|
line-height: 80px;
|
|
|
|
|
font-size: 35px;
|
|
|
|
|
color: #fff;
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
|
}
|
|
|
|
|
</style>
|