vue实现城市列表选择功能
成果展示
最后的成果就是下面所展示的内容,因为gif图没有做,只能截图所展示,接下来,会带着大家一步一步的完成下面功能,脚手架搭建和node安装在本次案例不会讲解,如果了解,可以在我的博客园找到有详细介绍
准备工作:
引入axios插件,调用better-scroll第三方插件,本地json文件,可以参考目录中的city.json,有条件的也可以自己去扒
功能分析
1.获取json数据展示城市列表 。
2.侧边字母定位滚动到相应的位置。
3.实现搜索城市
接下来我们开始对组件进行划分:本次案例中,总共划分为五个组件,下面就是组件的划分图
创建city组件,通过父组件获取数据,传递给子组件
<template>
<div class="city">
<CityHeader></CityHeader> //头部
<Search :list="cities"></Search> //搜索
<List :hot="hotCity" :letter="letter" :list="cities"></List> //城市列表
<Alphabet @chang="handleLetterChang" :list="cities"></Alphabet> //A-Z
</div>
</template>
<script>
import axios from 'axios'
import CityHeader from './components/Header'
import Search from './components/Search'
import List from './components/List'
import Alphabet from './components/Alphabet'
export default {
data () {
return {
cities:{}, // 城市列表
hotCity:[], //热门城市
letter: '' // A-Z
}
},
components: {
CityHeader,
Search,
List,
Alphabet
},
methods:{
getCityInfo () {
axios.get('/api/city.json').then(this.getCityInfoSucc)
},
getCityInfoSucc(res){
res = res.data
if (res.ret && res.data) {
const data = res.data
this.hotCity = data.hotCities
this.cities = data.cities
}
console.log(this.cities)
},
handleLetterChang(letter) { //接受子组件传过来的
// console.log(letter)
this.letter = letter
}
},
mounted () {
this.getCityInfo ()
}
}
</script>
<style scoped lang="stylus">
</style>
把得到的数据分次传递个对应的子组件,这样有利于网站优化,不用频繁的请数据
<template>
<div class="city">
<CityHeader></CityHeader>
<Search :list="cities"></Search>
<List :hot="hotCity" :letter="letter" :list="cities"></List>
<Alphabet @chang="handleLetterChang" :list="cities"></Alphabet>
</div>
</template>
export default {
data () {
return {
cities:{}, // 城市列表
hotCity:[], //热门城市
letter: '' // A-Z
}
},
components: {
CityHeader,
Search,
List,
Alphabet
},
methods:{
getCityInfo () {
axios.get('/api/city.json').then(this.getCityInfoSucc) //请求本地配置的mock数据
},
getCityInfoSucc(res){
res = res.data
if (res.ret && res.data) {
const data = res.data
this.hotCity = data.hotCities
this.cities = data.cities
}
}
},
mounted () {
this.getCityInfo ()
}
}
创建头部组件,
<template>
<div class="header">
城市选择
<router-link to="/">
<div class="iconfont back-icon"></div>
</router-link>
</div>
</template>
<script>
export default {
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
.header
overflow: hidden
height $headerHeight
line-height: $headerHeight
text-align: center
color: #fff
background: $bgColor
font-size: .4rem
.back-icon
position: absolute
left: 0
top: 0
width: .64rem
font-size: .4rem
text-align: center
color: #fff
</style>
创建搜索组件页面,接受父组件传递的数据,引入better-scroll第三方插件,实现列表滚动
<template>
<div>
<div class="search">
<input v-model="keyword" class="search-input" type="text" placeholder="输入城市名或者拼音" />
</div>
<div class="search-content" ref="search" v-show="keyword">
<ul>
<li class="serach-item border-bottom" v-for="item in listItem" :key="item.id">{{item.name}}</li>
<li v-show="hasNoData" class="serach-item border-bottom">没有搜索到匹配的数据</li>
</ul>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
props: {
list: Object,
},
data() {
return {
keyword:'',
listItem:[],
timer:null
}
},
computed: {
hasNoData() {
return !this.listItem.length //没有搜索的条件是否显示
}
},
watch: {
keyword () {
if (this.timer) {
clearTimeout(this.timer)
}
if(!this.keyword) { //清空
this.listItem = ""
return
}
this.timer = setTimeout(() => {
const result = []
for (let i in this.list) {
this.list[i].forEach((value) => { //匹配搜索的条件
if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
result.push(value)
}
})
}
this.listItem= result
},100)
}
},
mounted () {
this.scroll = new BScroll(this.$refs.search)
}
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl'
@import '~styles/mixins.styl'
.search
height: .72rem
padding: 0 .1rem
background:$bgColor
.search-input
box-sizing: border-box
width:100%
height: .62rem
line-height: .62rem
text-align: center
border-radius: .06rem
padding: 0 .1rem
color: #666
.search-content
z-index: 1
overflow:hidden
position:absolute
top: 1.58rem
left: 0
right: 0
bottom: 0
background: #eee
.serach-item
line-height: .62rem
padding-left:.2rem
color:#666
background: #fff
</style>
创建城市列表组件,引入better-scroll插件,实现列表滚动,通过watch监听letter,实现字母与城市列表滚动
<template>
<div class="list" ref="wrapper">
<div>
<div class="area">
<div class="title border-topbottom">当前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">郑州</div>
</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">热门城市</div>
<div class="button-list">
<div class="button-wrapper" v-for="item in hot" :key="item.id">
<div class="button">{{item.name}}</div>
</div>
</div>
</div>
<div class="area"
v-for="(item,key) in list"
:ref="key"
:key="key">
<div class="title border-topbottom">{{key}}</div>
<ul class="item-list">
<li class="item border-bottom"
v-for="listInner in item"
:key="listInner.id"
>{{listInner.name}}</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
props: {
hot: Array,
list: Object,
letter:String
},
mounted () {
this.scroll = new BScroll(this.$refs.wrapper)
},
watch:{
letter () { //监听列表滚动事件 A-Z
if(this.letter) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
.border-topbottom
&:before
background: #ccc
&:after
background:#ccc
.border-bottom
&:before
background: #ccc
.list
overflow: hidden
position:absolute
top:1.58rem
left:0
right:0
bottom:0
.title
line-height: .54rem;
background: #eee;
padding-left: .2rem;
color: #666;
font-size: .26rem;
.button-list
overflow:hidden
padding: .1rem .6rem .1rem .1rem
.button-wrapper
float:left
width:33.33%
.button
margin: .1rem
padding: .1rem 0
text-align: center
border: .02rem solid #ccc
border-radius: .06rem
.item-list
.item
line-height: .76rem
color:#212121
padding-left: .2rem
font-size: .28rem
text-overflow: ellipsis
white-space: nowrap
</style>
创建字母组件,点击字母,左边列表城市想对应,通过this.$emit事件,子组件在触发的事件传递给父组件,父组件通过子组件传递的事件,在传递给List组件,
<template>
<div class="list">
<li class="item"
:ref="item"
@click="handeClick"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend= "handleTouchEnd"
v-for="item of letter"
:key="item">{{item}}</li>
</div>
</template>
<script>
export default {
props: {
list: Object
},
data () {
return {
touchstart:false,
startY:0,
timer: null
}
},
updated () {
this.startY = this.$refs['A'][0].offsetTop
},
computed: {
letter () {
const letter =[]
for (let i in this.list) { //循环A-Z
letter.push(i)
}
return letter
}
},
methods: {
handeClick(e) {
this.$emit('chang',e.target.innerText) //传给父组件City
},
handleTouchStart () {
// 手指放上
this.touchstart = true
},
handleTouchMove (e) {
// 手指移动
if(this.touchstart) {
if(this.timer) {
clearInterval(this.timer)
}
this.timer = setTimeout(() => {
const touchY = e.touches[0].clientY -79 //到蓝色头部的距离
const index = Math.floor((touchY - this.startY ) / 20)
if(index >=0 && index < this.letter.length) {
this.$emit('chang',this.letter[index])
}
},16)
}
},
handleTouchEnd () {
// 手指离开
this.touchstart = false
}
}
}
</script>
<style scoped lang="stylus">
@import '~styles/varibles.styl';
@import '~styles/mixins.styl';
.list
display: flex
flex-direction:column
justify-content: center
position:absolute
top: 1.58rem
right: 0
bottom: 0
width: .4rem
.item
line-height:.44rem
text-align: center
color: $bgColor
list-style:none
</style>
总结
以上所述是小编给大家介绍的vue实现城市列表选择功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
以上是 vue实现城市列表选择功能 的全部内容, 来源链接: utcz.com/z/352064.html