You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
8.9 KiB
Vue

1 year ago
<template>
<view class="tm-quickIndex " :style="{
height: activeHeight_watch + 'px'
}">
<view :style="{
height: activeHeight_watch + 'px'
}">
<tm-loadding v-if="loadding" label="处理中..."></tm-loadding>
<scroll-view scroll-y :class="[black_tmeme?'grey-darken-4':'white']" :style="{
height: activeHeight_watch + 'px',
}" @scroll="scrollIn" :scroll-into-view="guid+'_'+(isScroll?'':active_value)">
<view v-for="(item,index) in dataList" :key="index" :id="guid+'_'+index" class="tm-quickIndex-item">
<view :class="[black_tmeme?'grey-darken-5':'grey-lighten-4 text']" class=" text-size-s text-weight-b px-32 py-12">{{item.title}}</view>
<view>
<view v-for="(item2,index2) in item.children" :key="index2">
<slot name="cell" :data="{prevent:index,children:index2,total:item.children.length,item:item2,title:item2[rangKey],color:color_tmeme,black:black_tmeme}">
<view :class="[index2!==item.children.length-1?'border-grey-lighten-4-b-1 ':'',black_tmeme?'bk':'']" class="mx-32 py-24 flex-start" @click="changeIndex(index,index2,item2)">
<view v-if="item2['icon']" style="width: 48rpx;height: 48rpx;" class="mr-24 rounded flex-center overflow">
<tm-icons :size="48" :name="item2['icon']"></tm-icons>
</view>
<view class="text-size-n">
{{item2[rangKey]}}
</view>
</view>
</slot>
</view>
</view>
</view>
</scroll-view>
</view>
<view class="tm-quickIndex-index flex-center flex-col pr-16" :style="{
height: activeHeight_watch + 'px'
}">
<view v-if="showtips"
:class="[`text-${color_tmeme}`,black_tmeme?'bk':'']"
class="tm-quickIndex-index-Tips absolute rounded shadow-10 flex-center white text-size-g text-weight-b">
{{ returnIndexStr(scrollIndx) }}
</view>
<!-- <view v-if="scrollInBarIndex"
:class="[`text-${color}`]"
class="tm-quickIndex-index-Tips absolute rounded shadow-10 flex-center white text-size-g text-weight-b">
{{returnIndexStr(scrollIndx)}} :class="[scrollIndx==index?`text-${color} text-weight-b`:'']"
</view> -->
<view v-if="activeHeight_watch>0" @touchend.stop.prevent="indexMove($event,'end')" @touchmove.stop.prevent="indexMove($event,'scroll')"
class="tm-quickIndex-index-Bk round-24 shadow-3 " :class="[black_tmeme?'grey-darken-5 bk':'white']">
<view @click.stop="acitveItemClick($event,index)"
class="tm-quickIndex-index-item text-size-xxs flex-center px-2"
v-for="(item,index) in dataList" :key="index">
{{
returnIndexStr(index)
}}
</view>
</view>
</view>
</view>
</template>
<script>
/**
* 快速索引
* @property {Array} list = [] 默认[],列表数据格式[{title:"汽车品牌",children:[{title:"宝马"},{title:"奔驰"}]}]
* @property {String} rang-key = [] 默认'title',列表对象key
* @property {String | Number} height = [] 默认0,高度默认为0时自动使用屏幕的高度
* @property {Number} value = [] 默认0,当前滚动的索引位置,推荐使用v-model或者value.sync
* @property {String} color = [] 默认primary,主题色
* @property {Function} change 点击列表项时产生的事件返回参数{prent:父Index,children:子index,data:项数据}
* @example <tm-quickIndex :list='[{title:"汽车品牌",children:[{title:"宝马"},{title:"奔驰"}]}]'></tm-quickIndex>
* 如果 不提供index索引字符将截取title第一个字符作为索引如果title第一个没有将使用自建的数字索引
*/
import tmIcons from "@/tm-vuetify/components/tm-icons/tm-icons.vue"
import tmLoadding from "@/tm-vuetify/components/tm-loadding/tm-loadding.vue"
export default {
components:{tmIcons,tmLoadding},
name: 'tm-quickIndex',
model: {
prop: 'value',
event: 'input'
},
props: {
// 高度默认为0时自动使用屏幕的高度。
height: {
type: String | Number,
default: 0
},
// 当前滚动的位置。
value: {
type: Number,
default: 0
},
// 当前滚动的位置。
color: {
type: String,
default: "primary"
},
list: {
type: Array,
default: () => {
return [];
}
},
rangKey: {
type: String,
default: "title"
},
black: {
type: String|Boolean,
default: null
},
// 跟随主题色的改变而改变。
fllowTheme:{
type:Boolean|String,
default:true
}
},
watch: {
value: function() {
this.active = this.value;
this.isScroll=false;
this.scrollIndx = this.value;
},
list:{
deep:true,
handler(){
this.dataList = this.list;
}
}
},
computed: {
black_tmeme: function() {
if (this.black !== null) return this.black;
return this.$tm.vx.state().tmVuetify.black;
},
color_tmeme:function(){
if(this.$tm.vx.state().tmVuetify.color!==null&&this.$tm.vx.state().tmVuetify.color && this.fllowTheme){
return this.$tm.vx.state().tmVuetify.color;
}
return this.color;
},
active: {
get: function() {
return this.active_value;
},
set: async function(val) {
this.active_value = val;
this.$emit('input', val);
this.$emit('update:value', val);
let t = this;
this.showtips = true;
let idx = 5655555
clearTimeout(idx)
idx = setTimeout(function(){
t.showtips = false;
},500)
}
},
activeHeight_watch: {
get: function() {
return this.activeHeight;
},
set: function(val) {
this.activeHeight = val;
}
}
},
data() {
return {
minTop:0,
activeHeight: 0,
guid: "",
active_value: 0,
listBound: [],
nowIndex: 0,
showtips: false,
isScroll: true,
quinkBar: null,
scrollIndx: 0,
scrollInBarIndex: false,
dataList:[],
loadding:true
};
},
async mounted() {
this.guid = uni.$tm.guid();
let t = this;
this.activeHeight_watch = uni.upx2px(this.height);
this.loadding=true;
await uni.$tm.sleep(50)
this.dataList = [...this.list];
this.$nextTick(async function() {
if (!this.activeHeight_watch) {
let sysinfo = uni.getSystemInfoSync();
this.activeHeight_watch = sysinfo.windowHeight;
}
let df = await this.$Querey(".tm-quickIndex",this).catch(e=>{});
this.minTop = df[0].top;
let indexbar = await t.$Querey(".tm-quickIndex-index-Bk", t).catch(e => {})
t.quinkBar = indexbar[0]
await uni.$tm.sleep(100)
t.active = t.value;
uni.createSelectorQuery().in(t).selectAll('.tm-quickIndex-item')
.boundingClientRect(res => {
res.forEach(item => {
t.listBound.push(item.top)
})
t.loadding=false;
}).exec()
});
},
methods: {
returnIndexStr(index){
let item = this.list[index];
if(!item || typeof item === 'undefined') return;
if(item['index']&& typeof item['index'] !=='undefined'){
return item['index'];
}else{
if(item[this.rangKey][0]&& typeof item[this.rangKey][0] !=='undefined'){
return item[this.rangKey][0];
}
}
return index+1
},
scrollIn(e) {
let t = this;
let y = e.detail.scrollTop;
this.isScroll = true;
function chatIndex(min) {
let index = 0;
for (let i = 0; i < t.listBound.length; i++) {
if (t.listBound[i] >= min + t.minTop+1) {
index = i;
break;
}
}
return index;
}
this.nowIndex = chatIndex(y) - 1;
},
changeIndex(prentindex, childrenindex, item) {
this.$emit('change', {
prent: prentindex,
children: childrenindex,
data: item
})
},
async acitveItemClick(e, indx) {
this.isScroll = false;
if (this.list.length <= 0) return;
this.active = indx;
},
async indexMove(e, type) {
let t = this;
if (this.list.length <= 0) return;
if (e.changedTouches.length > 1) return;
let y = e.changedTouches[0].clientY;
let itemHeight = uni.upx2px(40);
let ClickTop = e.target.offsetTop;
let index = 0;
if (y <= this.quinkBar.top) {
index = 0;
} else if (y >= this.quinkBar.bottom) {
index = this.list.length - 1;
} else {
let xy = y - this.quinkBar.top
index = Math.floor(xy / itemHeight);
}
if(index>=this.list.length-1) index = this.list.length-1
if(index<=0) index = 0;
this.isScroll = false;
if(this.scrollIndx!==index){
this.scrollIndx = index
}
if(this.active!==index){
this.active = index;
}
if (type == 'end') {
t.scrollInBarIndex = false;
} else {
t.scrollInBarIndex = true;
}
}
},
};
</script>
<style lang="scss" scoped>
.tm-quickIndex {
position: relative;
.tm-quickIndex-index {
position: absolute;
right: 0upx;
top: 0;
.tm-quickIndex-index-item {
width: 40rpx;
height: 40rpx;
// background: rgba(255,255,255,0.1);
}
.tm-quickIndex-index-Tips {
right: 160rpx;
width: 100rpx;
height: 100rpx;
}
}
}
</style>