364 lines
8.8 KiB
Vue
364 lines
8.8 KiB
Vue
<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> |