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.

368 lines
8.5 KiB

5 months ago
<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 -->
<indexed-list-item :list="list" :loaded="loaded" :idx="idx" :showSelect="showSelect"
@itemClick="onClick"></indexed-list-item>
<!-- #ifndef APP-NVUE -->
</view>
</scroll-view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
</cell>
</list>
<!-- #endif -->
<view class="uni-indexed-list__menu" @touchstart="touchStart" @touchmove.stop.prevent="touchMove"
@touchend="touchEnd" @mousedown.stop="mousedown" @mousemove.stop.prevent="mousemove"
@mouseleave.stop="mouseleave">
<view v-for="(list, key) in lists" :key="key" class="uni-indexed-list__menu-item"
:class="touchmoveIndex == key ? 'uni-indexed-list__menu--active' : ''">
<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 indexedListItem 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: {
indexedListItem
},
emits: ['click'],
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: '',
touchmovable: true,
loaded: false,
isPC: false
}
},
watch: {
options: {
handler: function() {
this.setList()
},
deep: true
}
},
mounted() {
// #ifdef H5
this.isPC = this.IsPC()
// #endif
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 = this.isPC ? e.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 = this.isPC ? e.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
},
/**
* 兼容 PC @tian
*/
mousedown(e) {
if (!this.isPC) return
this.touchStart(e)
},
mousemove(e) {
if (!this.isPC) return
this.touchMove(e)
},
mouseleave(e) {
if (!this.isPC) return
this.touchEnd(e)
},
// #ifdef H5
IsPC() {
var userAgentInfo = navigator.userAgent;
var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
var flag = true;
for (let v = 0; v < Agents.length - 1; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
// #endif
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;
/* #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;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-indexed-list__menu-text {
font-size: 12px;
text-align: center;
color: #aaa;
}
.uni-indexed-list__menu--active {
// background-color: rgb(200, 200, 200);
}
.uni-indexed-list__menu--active {}
.uni-indexed-list__menu-text--active {
border-radius: 16px;
width: 16px;
height: 16px;
line-height: 16px;
background-color: #007aff;
color: #fff;
}
.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>