我是通过组件化的形式实现的父子组件传值,当中使用了vuex进行了数据共享

设计稿

这里写图片描述
最后合并成的城市组件

<template>
    <div id="app">
      <city-header  :citylist="ShowCityList"></city-header>
      <section>
          <city-list
           :hot="HotCity"
           :citylist="ShowCityList"
           :letter="letter"
          ></city-list>
        <city-alphabet :citylist="ShowCityList" @change="alphabetChange"></city-alphabet>
      </section>
    </div>
</template>

<script>
    import CityHeader from "./components/CityHeader";
    import CityList from "./components/CityList";
    import CityAlphabet from "./components/CityAlphabet";
    export default {
        name: "City",
      components: {CityAlphabet, CityList, CityHeader},
      data(){
          return{
            CurrentCity:[],
            HotCity:[],
            ShowCityList:[],
            letter:''
          }
      },
      created(){
        this.https.get("/api/city.json").then((res)=>{

          res = res.data
          if (res.ret && res.data) {
            const data = res.data
            this.HotCity = data.hotCities
            this.ShowCityList = data.cities
          }
        })
      },
      methods:{
        //接收字母的值并传递给list组件
        alphabetChange(letter){
          console.log(letter)
          this.letter=letter
        }
      }
    }

</script>

<style scoped>
section{
  flex: 1;
}
</style>

头部搜索组件

<template>
    <div class="header">
       <div class="htop">
       <a class="iconfont back-icon" @click="goBack">&#58890;</a>
       <h1>城市选择</h1>
       </div>
       <div class="hbottom">
        <input type="text" v-model="keyword" placeholder="输入城市名或拼音"/>
       </div>
      <div
        class="search-content"
        ref="search"
        v-show="keyword"
      >
        <ul>
          <li
            class="search-item border-bottom"
            @click="CityClick(item.name)"
            v-for="item of list"
            :key="item.id"
          >
            {{item.name}}
          </li>
          <li class="search-item border-bottom" v-show="hasNoData">
            没有找到匹配数据
          </li>
        </ul>
      </div>
    </div>
</template>

<script>
  import BScroll from 'better-scroll'
  import { mapMutations } from 'vuex'
    export default {
        name: "CityHeader",
        props:{
          citylist : ""
        },
        data(){
            return{
            keyword:"" ,//获取文本框的值
            list:[],//声明个空数组用来存放搜索到的值
            timer: null
            }
        },
        created(){

        },
        methods:{
          goBack(){
            this.$router.push('/')
          },
          CityClick(city){
          this.changeCity(city)
          this.$router.push('/')
        },
        ...mapMutations(["changeCity"])
        },
      computed: {
        hasNoData () {
          return !this.list.length
        }
      },
      watch:{
    keyword () {
      if (this.timer) {
        clearTimeout(this.timer)
      }
      if (!this.keyword) {
        this.list = []
        return
      }
      this.timer = setTimeout(() => {
        const result = []
        for (let i in this.citylist) {
          this.citylist[i].forEach((value) => {
            if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
              result.push(value)
            }
          })
        }
        this.list = result
      }, 100)
    }
      },
      mounted () {
        this.scroll = new BScroll(this.$refs.search,{click:true})
      }
    }
</script>

<style  lang="less" scoped>
.header{
  width: 100%;
  height: 2.135135rem;
  background: #00bcd4;
  .htop{ width: 100%;
    height: 1.162162rem;
    line-height: 1.162162rem;
    color:#fff;
    font-size: 16px;
    text-align: center;
    display: flex;
    a{ display: block;
        width: 5%;
        height: 1.162162rem;
        line-height: 1.162162rem;
        text-align: center;
        color:#fff;
    }
    h1{
        width: 100%;
        height: 1.162162rem;
        line-height: 1.162162rem;
        text-align: center;
    }
  }
  .hbottom{
    width: 100%;
    height: 0.972972rem;
    line-height: 0.972972rem;
    text-align: center;
    input{ width: 98%;
        height: 0.7rem;
        line-height: 0.7rem;
        margin: 0.1rem;
        text-align: center;
        border-radius: 5.4px;
    }

  }
  .search-content{
    z-index: 1111111;
    overflow: hidden;
    position: absolute;
    top: 2.1rem;
    left: 0;
    right: 0;
    bottom: 0;
    background: #eee;
    .search-item{ line-height:0.62rem;
      padding-left: 0.2rem;
      background: #fff;
      color: #666 }

  }

}
</style>

头部下的列表组件

<template>
  <div class="list" ref="wrapper">
    <div class="main-list">
      <div class="main-list-list">
        <div class="main-list-top">
          <div class="main-list-top-title">
            当前城市
          </div>
          <div class="main-list-top-content">
            <button
              @click="CityClick(item.name)"
            >{{this.city}}</button>
          </div>
        </div>
        <div class="main-list-center">
          <div class="main-list-center-title">
            热门城市
          </div>
          <div class="main-list-center-content">
            <button
              v-for="item of hot"
              :key="item.id"
              @click="CityClick(item.name)"
            >{{item.name}}</button>
          </div>
        </div>
        <div class="main-content">
          <div class="main-list-bottom" v-for="(item,key) of citylist" :key="key" :ref="key">
            <div class="main-list-bottom-title">
              {{key}}
            </div>
            <div class="main-list-bottom-content" v-for="val of item"  :key="item.id" @click="CityClick(val.name)">
              {{val.name}}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import BScroll from 'better-scroll'
  import {mapState,mapMutations} from 'vuex'
    export default {
        name: "CityList",
        props:{
          hot:Array,
          citylist:"",
          letter:String
        },
        data(){
          return{

          }
        },
        computed:{
          ...mapState(["city"])
        },
        methods:{
          CityClick(city){
            console.log(1)
            this.changeCity(city)
            this.$router.push('/')
          },
          ...mapMutations(["changeCity"])
        },
        watch:{
          letter(){
            if(this.letter){
              const element=this.$refs[this.letter][0]
              // console.log(element)
              this.scroll.scrollToElement(element)
              // console.log(this)
            }
          }
        },
        mounted () {
         this.scroll = new BScroll(this.$refs.wrapper,{click:true})
      }
    }
</script>

<style  lang="less" scoped>
  .list{
    flex: 1;
    width: 100%;
    height: 15.837837rem;
    overflow: hidden;
    .main-list{ width: 100%;
      display: flex;
      flex-direction: column;
      .main-list-top{ width: 100%;
        height: 1.972972rem;
        .main-list-top-title{ width: 97%;
          height: 0.729729rem;
          line-height: 0.729729rem;
          font-size: 14px;
          background: #EEEEEE;
          color: #666;
          padding-left: 0.3rem;
        }
        .main-list-top-content{
          width: 97%;
          height: 1.243243rem;
          line-height: 1.243243rem;
          background: #fff;
          padding-left: 0.3rem;
          button{ width: 2.783783rem;
            height: 0.702702rem;
            border: 1px solid #CCCCCC;
            border-radius: 3.4px;
            background: none;
            margin-left: 0.2rem;
          }
        }
      }
      .main-list-center{
        width:100%;
        height: 2.216216rem;
        .main-list-center-title{ width: 97%;
          height: 0.729729rem;
          line-height: 0.729729rem;
          font-size: 14px;
          background: #EEEEEE;
          color: #666;
          padding-left: 0.3rem;
        }
        .main-list-center-content{
          width: 97%;
          height: 2.216216rem;
          line-height: 1.1rem;
          background: #fff;
          padding-left: 0.3rem;
          button{ width: 2.783783rem;
            height: 0.702702rem;
            border: 1px solid #CCCCCC;
            border-radius: 3.4px;
            background: none;
            margin-left: 0.2rem;
          }
        }
      }
      .main-content{
        flex: 1;
        margin-top: 28px;
        .main-list-bottom{ .main-list-bottom-title{ width: 97%;
            height: 0.729729rem;
            line-height: 0.729729rem;
            font-size: 14px;
            background: #EEEEEE;
            color: #666;
            padding-left: 0.3rem;
          }
          .main-list-bottom-content{
            width: 97%;
            height: 1.027027rem;
            line-height: 1.027027rem;
            font-size: 14px;
            background: #fff;
            color: #666;
            padding-left: 0.3rem;
            border-top: 1px solid #ccc;
          }
        }
      }

    }
  }

</style>

首字母渲染组件

<template>
    <ul class="list">
      <li
        v-for="item of citylistArr"
        :key="item"
        :ref="item"
        @click="handeClick"
        @touchstart="handleTouchStart" 
        @touchmove="handleTouchMove" 
        @touchend= "handleTouchEnd"
      >{{item}}</li>
    </ul>
</template>

<script>
    export default {
        name: "CityAlphabet",
        props:{
          citylist : ""
        },
        data(){
          return{
            bool:false,//表示位
            starty:0,
            timer:null
          }
        },
        computed:{
          //定义一个计算属性用于盛放传递过来的值
          citylistArr(){
            const newArr=[] //定义空数组并循环遍历
            for (let i in this.citylist) {
               newArr.push(i)
            }
            return newArr
          }
        },
       updated(){
          this.startY=this.$refs["A"][0].offsetTop
       },
      methods:{
          handeClick(e){
            this.$emit('change',e.target.innerText) //非父子组件传值
          },
          handleTouchStart(){
            // 手指放上去
            this.bool=true
          },
          handleTouchMove(e){
            //在bool的判断为true的时候执行以下判断
            if(this.bool) {

              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.citylistArr.length) {
                  this.$emit('chang',this.citylistArr[index])
                }
              },16)
            }
          },
          handleTouchEnd(){
            // 手指离开
            this.bool=false
          }
      }
    }
</script>

<style scoped lang="less">
.list{
  position: fixed;
  top: 5rem;
  width: 0.54054rem;
  height: 15.891891rem;
  text-align: center;
  right: 0;
  bottom: 0;
  li{ width: 0.54054rem;
    height: 0.54054rem;
    line-height:0.54054rem;
    text-align: center;
    color: #25A4BB;
    font-size: 16px;
  }
}
</style>

效果图

这里写图片描述

Logo

前往低代码交流专区

更多推荐