­

vue实现城市列表选择

  • 2019 年 10 月 6 日
  • 筆記

成果展示

最后的成果就是下面所展示的内容,因为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">&#xe624;</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>

 以上都是所有本次的内容,如果喜欢可以关注一下