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.
364 lines
8.8 KiB
364 lines
8.8 KiB
4 years 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 -->
|
||
|
<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.value }}</text>
|
||
|
</view>
|
||
|
</view>
|
||
|
<view v-if="touchmove" class="uni-indexed-list__alert-wrapper">
|
||
|
<text class="uni-indexed-list__alert">{{ lists[touchmoveIndex] }}</text>
|
||
|
</view>
|
||
|
</view>
|
||
|
</template>
|
||
|
<script>
|
||
|
import uniIcons from '../uni-icons/uni-icons.vue'
|
||
|
import uniIndexedListItem from './uni-data-indexed-list-item.vue'
|
||
|
import clientdb from './clientdb.js'
|
||
|
// #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 索引列表需要的数据对象
|
||
|
* @property {String|DBCollectionString} collection 表名
|
||
|
* @property {String|ClientDBActionString} action 云端执行数据库查询的前或后,触发某个action函数操作,进行预处理或后处理
|
||
|
* @property {String|DBFieldString} field 查询字段,多个字段用 `,` 分割
|
||
|
* @property {String} orderby 排序字段及正序倒叙设置
|
||
|
* @property {String|JQLString} where 查询条件
|
||
|
* @event {Function} click 点击列表事件 ,返回当前选择项的事件对象
|
||
|
* @example <uni-indexed-list options="" showSelect="false" @click=""></uni-indexed-list>
|
||
|
*/
|
||
|
export default {
|
||
|
name: 'UniDataIndexedList',
|
||
|
mixins: [clientdb],
|
||
|
components: {
|
||
|
uniIcons,
|
||
|
uniIndexedListItem
|
||
|
},
|
||
|
props: {
|
||
|
options: {
|
||
|
type: Array,
|
||
|
default () {
|
||
|
return []
|
||
|
}
|
||
|
},
|
||
|
localdata: {
|
||
|
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() {
|
||
|
if (this.localdata.length || this.options.length) {
|
||
|
setTimeout(() => {
|
||
|
this.setList()
|
||
|
}, 50)
|
||
|
setTimeout(() => {
|
||
|
this.loaded = true
|
||
|
}, 300);
|
||
|
} else if (this.collection) {
|
||
|
if (!this.manual) {
|
||
|
this._execLoadData((data) => {
|
||
|
this.lists = this.groupData(data);
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
methods: {
|
||
|
groupData(data) {
|
||
|
let groups = {};
|
||
|
for (let i = 0; i < data.length; i++) {
|
||
|
let item = data[i];
|
||
|
let group = item.group;
|
||
|
if (!groups[group]) {
|
||
|
groups[group] = {
|
||
|
"title": group,
|
||
|
"value": group,
|
||
|
"itemIndex": i,
|
||
|
"items": []
|
||
|
}
|
||
|
}
|
||
|
groups[group].items.push(item);
|
||
|
}
|
||
|
let result = []
|
||
|
for (let g in groups) {
|
||
|
let group = groups[g];
|
||
|
let items = group.items;
|
||
|
for (let j = 0; j < items.length; j++) {
|
||
|
items[j].itemIndex = j;
|
||
|
}
|
||
|
result.push(group);
|
||
|
}
|
||
|
return result;
|
||
|
},
|
||
|
setList(data) {
|
||
|
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['value'] = value.letter
|
||
|
obj['text'] = item
|
||
|
obj['itemIndex'] = index
|
||
|
index++
|
||
|
obj.checked = item.checked ? item.checked : false
|
||
|
return obj
|
||
|
})
|
||
|
this.lists.push({
|
||
|
title: value.letter,
|
||
|
value: 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 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: #c8c8c8;
|
||
|
}
|
||
|
|
||
|
.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>
|