mrr.sj.front/components/hao-indexList/hao-indexList/hao-indexList.vue

826 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="hao-bigbox" :style="{'top':statusBarHeight+'px','height':`calc(100% - ${statusBarHeight}px)`}">
<!-- 搜索 -->
<view class="hao-search" v-if="isSearch">
<view class="hao-searchCent">
<image src="/static/images/search_1.png" class="hao-searchImg"></image>
<input type="text" v-model="inputKey" @input="searchInput" placeholder="请输入你的服务地址"
class="hao-searchInput" />
</view>
</view>
<!-- 搜索内容 -->
<view v-if="isSearch && searchList.length > 0" class="hao-searchScroll">
<scroll-view scroll-y style="width:100%;height:100%;">
<view>
<view class="hao-secondEach" v-for="(item,index) in searchList" :key="item[idValue]"
@tap="selectIndex(item)">
<view class="hao-avatar" v-if="isAvatar">
<image :src="item[avatarValue]" class="hao-avatarImg"></image>
</view>
<view class="hao-text">
<view class="hao-name">{{item[nameValue]}}</view>
<view class="hao-fu" v-if="isFu">{{item[fuValue]}}</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 右侧索引 -->
<view class="hao-letters" v-if="isLetters&&searchList.length <= 0"
:style="{'top':isSearch?'120rpx':'0rpx','height':isSearch?'calc(100% - 120rpx)':'100%'}">
<image src="/static/haoIndexListImg/top.png" class="hao-letterTop" v-if="isTop" @tap="goTop"></image>
<view v-for="(item,index) in letters" :key="item"
:style="{'height':indexEachHH+'rpx','background-color':(currentCeiling==index&&isIndexAc)?indexAcColor:''}"
class="hao-lettersEach" :class="(currentCeiling==index&&isIndexAc)?'hao-lettersEachAc':''"
@touchmove="handleTouchMove" @touchend="handleTouchEnd(item,index)"
@tap="handleLetterClick(item,index)">
{{item}}
</view>
</view>
<!-- 提示当前索引 -->
<view class="hao-toastIndex" v-if="isIndexToast && toastIndex">{{toastIndex}}</view>
<!-- 吸顶 -->
<view class="ceiling"
v-if="currentCeiling !== null&&isCeil&&listInfo.length>0&&searchList.length <= 0&&isOkCurrent"
:style="{'top':isSearch?'120rpx':'0rpx'}">{{listInfo[currentCeiling].id.split('-')[1]}}</view>
<!-- 列表 -->
<scroll-view scroll-y class="hao-scroll" @scroll="listenScroll" :scroll-top="scrollTop"
:scroll-into-view="scrollEachId" v-show="searchList.length <= 0"
:style="{'top':isSearch?'120rpx':'0rpx','height':isSearch?'calc(100% - 120rpx)':'100%'}">
<view>
<view class="hao-modu" v-if="isCurrentCity || isHotCity">
<!-- <view class="hao-current" v-if="isCurrentCity">
<view class="hao-moduTitle">当前定位城市</view>
<view class="hao-currentBox">
<view class="hao-currentLs">
<image src="/static/haoIndexListImg/posi.png" class="hao-currentImg"></image>
<view class="hao-currentCity" @tap="selectIndex(currentCity)">{{currentCity[nameValue] || '暂无'}}</view>
</view>
</view>
</view> -->
<view class="hao-hotCity" v-if="isHotCity && hotCity.length>0">
<view class="hao-moduTitle">热门城市</view>
<view class="hao-hotCityBox">
<view v-for="(item,index) in hotCity" :key="item[idValue]" @tap="selectIndex(item)"
class="hao-hotEachCity">{{item[nameValue]}}</view>
</view>
</view>
</view>
<view v-for="(item,index) in cityList" :key="index" class="hao-each">
<view class="hao-eachTitle" :id="'hao-'+index">{{index}}</view>
<view class="hao-second">
<view v-for="(ite,ind) in item" :key="ite[idValue]" class="hao-secondEach"
@tap="selectIndex(ite)">
<view class="hao-avatar" v-if="isAvatar">
<image :src="ite[avatarValue]" class="hao-avatarImg"></image>
</view>
<view class="hao-text">
<view class="hao-name">{{ite[nameValue]}}</view>
<view class="hao-fu" v-if="isFu">{{ite[fuValue]}}</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import ChinaCitys from '../../../static/data/newChinaCitys.json'
export default {
name: 'hao-indexList',
data() {
return {
inputKey: '', //搜索关键字
searchTimeout: null, //搜索防抖
searchList: [], //搜索内容
scrollEachId: 'all', //跳转到对应列表子项的id
toastIndex: '', //当前索引提示内容
indexEachHH: 36, //索引项的高度单位rpx
afterConverHH: 0, //索引项的高度转换成PX值
indexFirstTT: 0, //索引第一个子元素顶部位置单位px
indexLastBB: 0, //索引最后一个子元素底部位置单位px
isOk: true, //索引滑动节流
isIndexGo: false, //在滑动索引时是否让列表跳转
currentCeiling: null, //当前吸顶项和当前索引
listInfo: [], //列表分类项位置信息
scrollInfo: {}, //scroll的位置信息
scrollTop: 0.01, //跳转至scroll某个位置
isOkCurrent: false, //当列表不长时,吸顶就不显示
statusBarHeight: 0,
}
},
created() {
// 获取状态栏高度并将单位转为rpx
// #ifdef APP-PLUS
const systemInfo = uni.getSystemInfoSync()
const screenWidth = systemInfo.screenWidth
this.statusBarHeight = (screenWidth * (systemInfo.statusBarHeight * 2 + 88)) / 750;
// #endif
// #ifdef H5
this.statusBarHeight = 44;
// #endif
},
props: {
//右侧索引
letters: {
type: Array,
default: () => []
},
//是否显示右侧索引
isLetters: {
type: Boolean,
default: true
},
//是否显示索引提示
isIndexToast: {
type: Boolean,
default: true
},
//id对应的值名
idValue: {
type: String,
default: 'id'
},
//名字对应的值名
nameValue: {
type: String,
default: 'name'
},
//当前城市
currentCity: {
type: Object,
default: () => {}
},
//是否显示当前城市
isCurrentCity: {
type: Boolean,
default: true
},
//热门城市
hotCity: {
type: Array,
default: () => []
},
//是否显示热门城市
isHotCity: {
type: Boolean,
default: true
},
//是否显示搜索
isSearch: {
type: Boolean,
default: true
},
//城市列表
cityList: {
type: Object,
default: () => {}
},
//是否显示头像
isAvatar: {
type: Boolean,
default: false
},
//头像对应的值名
avatarValue: {
type: String,
default: 'avatar'
},
//是否显示副标题
isFu: {
type: Boolean,
default: false
},
//副标题对应的值名
fuValue: {
type: String,
default: 'fu'
},
//是否吸顶
isCeil: {
type: Boolean,
default: true
},
//是否显示当前索引背景色
isIndexAc: {
type: Boolean,
default: true
},
//当前索引背景色
indexAcColor: {
type: String,
default: '#5594f9'
},
//是否显示回顶部图标
isTop: {
type: Boolean,
default: false
}
},
mounted() {
this.afterConverHH = uni.upx2px(this.indexEachHH); //将索引项的高度值转成px
this.getLocation();
},
methods: {
//回顶部
goTop() {
this.scrollTop = 0;
//防止第二次点击不跳
setTimeout(() => {
this.scrollTop = 0.01;
}, 200)
},
//搜索输入
searchInput() {
if (this.searchTimeout) {
clearTimeout(this.searchTimeout)
}
if (!this.inputKey) {
this.searchList = [];
return
}
//查询匹配的数据
this.searchTimeout = setTimeout(() => {
// let data = this.cityList;
let result = [];
// let list = Object.values(data).flat();
// list.map(r => {
// if (r[this.nameValue].indexOf(this.inputKey) !== -1) {
// result.push(r)
// }
// })
ChinaCitys.districts[0].districts.forEach(item => {
if (item.name == '台湾省') {
if (item.name.startsWith(this.inputKey)) {
result.push({
'name': '台湾',
'adcode': item.adcode,
"center": item.center,
"citycode": item.citycode,
})
}
} else if (item.name == '香港特别行政区') {
if (item.name.startsWith(this.inputKey)) {
result.push({
'name': '香港',
'adcode': item.adcode,
"center": item.center,
"citycode": item.citycode,
})
}
} else if (item.name == '澳门特别行政区') {
if (item.name.startsWith(this.inputKey)) {
result.push({
'name': '澳门',
'adcode': item.adcode,
"center": item.center,
"citycode": item.citycode,
})
}
} else if (item.name == '重庆市') {
if (item.name.startsWith(this.inputKey)) {
result.push({
'name': '重庆',
'adcode': item.adcode,
"center": item.center,
"citycode": item.citycode,
})
item.districts.forEach(item2 => {
console.log(item2)
if (item2.name == '重庆郊县') {
item2.districts.forEach(item3 => {
result.push({
'name': `${item3.name},${item2.name},${item.name}`,
'adcode': item3.adcode,
"center": item3.center,
"citycode": item3.citycode,
})
})
}
})
}
} else if (item.name == '天津市') {
if (item.name.startsWith(this.inputKey)) {
result.push({
'name': '天津',
'adcode': item.adcode,
"center": item.center,
"citycode": item.citycode,
})
}
} else {
item.districts.forEach(item2 => {
if (item2.name.startsWith(this.inputKey)) {
result.push({
'name': `${item2.name},${item.name}`,
'adcode': item2.adcode,
"center": item2.center,
"citycode": item2.citycode,
})
}
item2.districts.forEach(item3 => {
if (item3.name.startsWith(this.inputKey)) {
result.push({
'name': `${item3.name},${item2.name},${item.name}`,
'adcode': item3.adcode,
"center": item3.center,
"citycode": item3.citycode,
})
}
})
})
}
})
console.log(result)
this.searchList = result;
}, 800)
},
//获取节点信息
getLocation() {
const that = this;
//获取每项索引的位置信息
uni.createSelectorQuery()
.in(this)
.selectAll('.hao-lettersEach')
.boundingClientRect(rect => {
that.indexFirstTT = Math.round(rect[0].top);
that.indexLastBB = Math.round(rect[rect.length - 1].bottom);
})
.exec();
//获取scroll位置信息
uni.createSelectorQuery()
.in(this)
.select('.hao-scroll')
.boundingClientRect(rect => {
that.scrollInfo = rect;
})
.exec();
//获取列表每个分类项位置信息
uni.createSelectorQuery()
.in(this)
.selectAll('.hao-eachTitle')
.boundingClientRect(rect => {
that.listInfo = rect;
})
.exec();
},
//监听滚动
listenScroll(e) {
let top = this.scrollInfo.top || e.target.offsetTop; //scroll顶部的位置
let scrollTop = e.detail.scrollTop; //scroll滚动了多少
let allTop = top + scrollTop;
if (this.currentCeiling !== null) {
//大于最后一个
if (allTop >= this.listInfo[this.listInfo.length - 1].top) {
this.currentCeiling = this.listInfo.length - 1;
return
} else if (allTop > this.listInfo[this.currentCeiling].top && allTop > this.listInfo[this
.currentCeiling + 1].top) {
//当前位置远大于上一个的位置,通过遍历查询在哪
//查询当前所在的位置
let aa = this.listInfo.slice(this.currentCeiling + 1, this.listInfo.length); //截取后半段遍历
let bb = true;
aa.some((r, v) => {
if (allTop < r.top && bb) {
this.currentCeiling += v;
bb = false;
}
})
return
}
//当前位置小于之前
if (allTop < this.listInfo[this.currentCeiling].top) {
//小于第一个就隐藏
if (allTop < this.listInfo[0].top) {
this.currentCeiling = null;
} else {
let aa = this.listInfo.slice(0, this.currentCeiling).reverse(); //截取前半段遍历并倒序
let bb = true;
aa.some((r, v) => {
if (allTop >= r.top && bb) {
this.currentCeiling = this.currentCeiling - v - 1;
bb = false;
}
})
}
}
} else {
//在第一个与第二个之间
if (this.listInfo.length > 0 && allTop >= this.listInfo[0].top && allTop < this.listInfo[1].top) {
this.currentCeiling = 0;
//当列表够长时才让吸顶显示
if (!this.isOkCurrent) {
this.isOkCurrent = true;
}
} else if (this.listInfo.length > 0 && allTop > this.listInfo[0].top && allTop > this.listInfo[1]
.top) {
let bb = true;
this.listInfo.some((r, v) => {
if (allTop < r.top && bb) {
this.currentCeiling = v;
bb = false;
}
})
//当列表够长时才让吸顶显示
if (!this.isOkCurrent) {
this.isOkCurrent = true;
}
} else {
this.currentCeiling = null;
}
}
},
//选取
selectIndex(e) {
if (!e || e.target) return;
console.log('选取', e)
this.$emit('haoTap', e);
},
//点击索引跳对应项
handleLetterClick(e, index) {
this.scrollEachId = 'hao-' + e;
this.currentCeiling = index;
},
//手指在索引滑动中
handleTouchMove(e) {
if (this.isOk) {
//节流
this.isOk = false;
setTimeout(() => {
//当前滑动到的位置
if (e.touches[0]) {
let nowY = Math.round(e.touches[0].pageY); //四舍五入
//在索引区域内
if (nowY >= this.indexFirstTT && nowY <= this.indexLastBB) {
let aa = nowY - this.indexFirstTT;
let bb = aa / this.afterConverHH;
let cc = parseInt(bb);
if (bb > cc) {
this.toastIndex = this.letters[cc]; //提示索引
//当end方法比move方法先执行时
if (this.isIndexGo) {
this.isIndexGo = false;
this.currentCeiling = cc;
this.scrollEachId = 'hao-' + this.letters[cc]; //跳到索引指定位置
setTimeout(() => {
this.toastIndex = ''; //关闭提示
}, 700)
}
} else {
this.toastIndex = this.letters[cc - 1]; //提示索引
//当end方法比move方法先执行时
if (this.isIndexGo) {
this.isIndexGo = false;
this.currentCeiling = cc - 1;
this.scrollEachId = 'hao-' + this.letters[cc - 1]; //跳到索引指定位置
setTimeout(() => {
this.toastIndex = ''; //关闭提示
}, 700)
}
}
} else {
//超出范围就为空
this.toastIndex = '';
}
}
this.isOk = true;
}, 100)
}
},
//手指在索引滑动结束
handleTouchEnd(e, index) {
//有索引值才跳转
if (this.toastIndex) {
//isOk为true代表滑动真正结束否则让isIndexGo来决定最后跳转
//由于滑动方法处做了节流当快速滑动时end会比move先执行
if (this.isOk) {
this.scrollEachId = 'hao-' + this.toastIndex; //跳到索引指定位置
this.currentCeiling = index;
setTimeout(() => {
this.toastIndex = '';
}, 700)
} else {
this.isIndexGo = true; //让滑动处来执行跳转
}
}
},
}
}
</script>
<style lang="scss" scoped>
.hao-bigbox {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background-color: #fafafa;
/* 搜索 */
.hao-search {
width: 100%;
height: 120rpx;
padding-top: 32rpx;
display: flex;
// align-items: center;
justify-content: center;
.hao-searchCent {
width: 690rpx;
height: 68rpx;
background: #FFFFFF;
border: 2rpx solid #E8101E;
background-color: #fafafa;
display: flex;
align-items: center;
justify-content: center;
border-radius: 68rpx;
padding: 0 30rpx;
box-sizing: border-box;
.hao-searchImg {
width: 29rpx;
height: 32rpx;
margin-right: 16rpx;
}
.hao-searchInput {
flex: 1;
height: 40rpx;
font-size: 28rpx;
::v-deep .uni-input-placeholder {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 26rpx;
color: #CCCCCC;
line-height: 34rpx;
text-align: left;
font-style: normal;
}
}
}
}
/*搜索结果*/
.hao-searchScroll {
position: absolute;
top: 120rpx;
left: 0;
width: 100%;
height: calc(100% - 120rpx);
background: #fff;
padding: 0 30rpx;
box-sizing: border-box;
}
/*右侧索引*/
.hao-letters {
position: absolute;
top: 0;
right: 0;
width: 60rpx;
z-index: 6;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.hao-letterTop {
width: 36rpx;
height: 36rpx;
}
.hao-lettersEach {
width: 36rpx;
height: 36rpx;
display: flex;
align-items: center;
justify-content: center;
color: #333;
font-size: 28rpx;
}
.hao-lettersEachAc {
color: #fff;
border-radius: 50%;
}
}
/*当前索引提示内容*/
.hao-toastIndex {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 160rpx;
height: 160rpx;
line-height: 160rpx;
text-align: center;
font-size: 56rpx;
font-weight: 700;
border-radius: 20rpx;
z-index: 6;
background-color: #ddd;
color: #fff;
}
/* 当前与热门 */
.hao-modu {
width: 100%;
padding: 20rpx 30rpx;
box-sizing: border-box;
box-shadow: 0rpx 0rpx 12rpx 0rpx rgba(0, 0, 0, 0.05);
background: #fff;
margin-bottom: 19rpx;
.hao-current {
width: 100%;
padding-bottom: 20rpx;
}
.hao-hotCity {
width: 100%;
}
.hao-moduTitle {
width: 100%;
margin-bottom: 20rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #666666;
line-height: 40rpx;
text-align: left;
font-style: normal;
}
.hao-currentBox {
width: 100%;
display: flex;
align-items: center;
.hao-currentLs {
border-radius: 10rpx;
background-color: #fff;
border: 2rpx solid #eee;
padding: 0 40rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
.hao-currentImg {
width: 28rpx;
height: 28rpx;
margin-right: 10rpx;
}
.hao-currentCity {
color: #333;
font-size: 28rpx;
}
}
}
.hao-hotCityBox {
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
column-gap: 22rpx;
row-gap: 20rpx;
.hao-hotEachCity {
width: 156rpx;
height: 58rpx;
background: #EFEFEF;
border-radius: 6rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
}
/* 数据列表 */
.hao-scroll {
position: absolute;
width: 100%;
left: 0;
background-color: #fafafa;
.hao-each {
width: 100%;
background-color: #fff;
.hao-eachTitle {
display: inline-table;
width: 19rpx;
height: 40rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 28rpx;
color: #666666;
line-height: 40rpx;
text-align: left;
font-style: normal;
padding: 20rpx 0rpx;
margin: 0rpx 30rpx;
box-sizing: border-box;
font-weight: 600;
width: 690rpx;
border-bottom: 1px solid #f4f4f4;
}
.hao-second {
width: 100%;
padding: 0 30rpx;
box-sizing: border-box;
}
}
}
/*吸顶*/
.ceiling {
position: absolute;
left: 0;
width: 100%;
z-index: 1;
font-size: 32rpx;
color: #333;
background-color: #f4f4f4;
padding: 10rpx 30rpx;
box-sizing: border-box;
font-weight: 600;
}
/*搜索结果与列表的每一项*/
.hao-secondEach {
width: 100%;
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 2rpx solid #f4f4f4;
.hao-avatar {
width: 120rpx;
height: 120rpx;
margin-right: 20rpx;
.hao-avatarImg {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
.hao-text {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
.hao-name {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #333333;
line-height: 42rpx;
}
.hao-fu {
font-size: 24rpx;
color: #999;
}
}
}
.hao-secondEach:last-child {
border-bottom: none;
}
}
</style>