JavaScript

超轻量级php框架startmvc

vue实现城市列表选择功能

更新时间:2020-07-16 17:18:01 作者:startmvc
成果展示最后的成果就是下面所展示的内容,因为gif图没有做,只能截图所展示,接下来,

成果展示

最后的成果就是下面所展示的内容,因为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实现城市列表选择 vue 列表选择