下面是完成的效果,点击右侧字母的时候,城市列表跳到对应的城市,页面主要分成三个部分,效果图和代码已经框出来相对应了
city.vue文件(父组件)
父组件主要做的工作就是当点击字母的时候触发letterClick这个事件
将点击的字母赋值给sendClitLetter,然后传递给城市列表组件(子组件 )
子组件的主要工作有(cityList.vue)
这里主要强调一下如果this.scroll.scrollToElement()没有滚动的话,可以在mounted中实例化对象的时候需要加一个延时器settimeout,我就是这样解决的。
在这里我还遇到一个问题,我看网上实例化一个BScroll对象的时候,有两种写法
第一种
this.scroll = new BScroll(this.$refs.wrapper, {
click: true,
scrollY: true
});
第二种
this.scroll = new BScroll(this.$refs.wrapper);
我用的是第二种,大无语事件来了,城市什么的都点不了,也就是说 gotoHome()这个方法不生效,我真的想不出为什么(当时死也没想用第一种试试)就一直在各各地方添加点击事件,想看看因为什么,但是无果,然后我就先忙其他事情了,第二天,我改成第一种写法,就可以实现点击了
下面是父组件和子组件全部的代码
父组件(city.vue)
<template>
<div class="containerOne">
<!-- 顶部内容 -->
<div class="Headerwrap">
<headBar title="城市选择"></headBar>
<div class="searchWrap">
<van-search shape="round" background="white" placeholder="请输入城市名或者拼音" />
</div>
</div>
<!-- 字母排序 -->
<div class="zimuWrap">
<ul class="list">
<li
class="item"
v-for="item in cityZimuList"
:key="item"
:ref="item"
@click="letterClick"
@touchstart="touchStart"
@touchmove="touchMove"
@touchend="touchEnd"
>{{item}}</li>
</ul>
</div>
<!-- 城市选择列表 -->
<city-list @sendList="sendList" :sendCityLetter="sendCityLetter"></city-list>
</div>
</template>
<script>
import cityList from "../components/List.vue";
import headBar from "@/components/headBar.vue";
export default {
components: {
headBar,
cityList
},
data() {
return {
cityZimu: {}, //城市字母列表--cities
sendCityLetter: "", //发送给子组件的数据,单个的城市字母
touchStatus: false, //是否点击字母
startY: 0
};
},
// 数据更新完毕,且页面完成渲染之后触发的事件
updata() {
this.startY = this.$refs["A"][0].offsetTop;
},
methods: {
//发送字母
sendList(e) {
this.cityZimu = e;
},
//点击字母跳转城市名称首字母
letterClick(e) {
this.sendCityLetter = e.target.innerText;
},
//点击字母表时的事件
touchStart() {
this.touchStatus = true;
},
touchMove(e) {
if (this.touchStatus) {
const touchY = e.touches[0].clientY - 180; //指针位置到窗口的距离
//字母下标等于 指针位置 减去 字母距离,最后除以每个字母的高度
const index = Math.floor((touchY - this.startY) / 20); //字母下标
if (index >= 0 && this.cityZimuList.length) {
// this.$emit("changeLetters",this.cityZimuList[index])
this.sendCityLetter = this.cityZimuList[index];
}
}
},
touchEnd() {
this.touchStatus = false;
}
},
computed: {
cityZimuList() {
const letters = [];
for (let i in this.cityZimu) {
letters.push(i);
}
return letters;
}
},
created() {
this.sendList();
}
};
</script>
<style scoped lang="scss">
.containerOne {
.Headerwrap {
width: 100%;
position: fixed;
position: absolute;
top: 0;
left: 0;
z-index:9999;
.cityHeader {
background: #00a5ff;
// background-color: lightskyblue;
display: flex;
height: 10.667vw;
.icon {
font-size: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.title {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
margin-left: -4vw;
}
}
}
.zimuWrap {
height: 100%;
.list {
// padding-top: 100px;
position: fixed;
top: 26%;
right: 1.333vw;
text-align: center;
flex-direction: column;
justify-content: center;
align-items: center;
.item {
padding: 0.32vw 0;
}
}
}
}
</style>
子组件(cityList.vue)
<template>
<div class="container" ref="wrapper">
<div class="content">
<!-- 当前城市 -->
<div class="adress">
<div class="title">当前城市</div>
<div class="button_list">
<div class="button_wrap">
<div class="button">{{city}}</div>
</div>
</div>
</div>
<!-- 热门城市 -->
<div class="adress">
<div class="title">热门城市</div>
<div class="button_list">
<div class="button_wrap">
<div
class="button"
v-for="item in hotCity"
:key="item.name"
@click="gotoHome(item.name)"
>{{item.name}}</div>
</div>
</div>
</div>
<!-- 城市选择列表 -->
<div class="adress" v-for="(item,key) in cityList" :key="key" :ref="key">
<div class="title">{{key}}</div>
<div class="list">
<div
class="item"
v-for="item2 in item"
:key="item2.letter"
@click="gotoHome(item2.city)"
>{{item2.city}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BScroll from "better-scroll";
import { getGoodsCityApi, getHotCityApi } from "@/utils/user.js";
export default {
data() {
return {
hotCity: [],
cityList: {},
city: "深圳市"
};
},
props: {
sendCityLetter: {
type: String,
default: "A"
}
},
methods: {
gotoHome(context, city) {
this.$router.push({ path: "/" });
}
},
created() {
getHotCityApi().then(res => {
if (res.data.code == 200) {
this.hotCity = res.data.data;
}
});
getGoodsCityApi({}).then(res => {
if (res.data.code == 200) {
this.cityList = res.data.data;
this.$emit("sendList", this.cityList);
}
});
},
mounted() {
//setTimeout()解决this.scroll.scrollToElement()无法滚动的问题
setTimeout(() => {
this.scroll = new BScroll(this.$refs.wrapper);
}, 1000);
},
computed: {},
watch: {
sendCityLetter() {
if (this.sendCityLetter) {
let element = this.$refs[this.sendCityLetter][0];
this.scroll.scrollToElement(element);
}
}
}
};
</script>
<style scoped lang="scss">
.container {
// padding-top: 25.333vw;
margin-right: 6.667vw;
position: absolute;
top: 94px;
left: 0;
bottom: 0;
right: 0;
// overflow: hidden;
.content {
.adress {
.title {
background-color: #f6f5fa;
// border-bottom: 1px solid #87cefa;
padding: 2.667vw 0 1.333vw 1.333vw;
}
.button_list {
padding: 1.333vw 2.667vw;
.button_wrap {
display: flex;
width: 100%;
flex-wrap: wrap;
.button {
margin: 1.333vw 0.8vw;
display: flex;
width: 30.3%;
border: 1px solid #dbdbdb;
border-radius: 16px;
padding: 0.8vw 0;
justify-content: center;
flex-wrap: wrap;
}
}
}
.list {
padding-left: 15px;
.item {
padding: 1.09vw 0;
border-bottom: 1px solid #ededed;
}
}
}
}
}
</style>