zoukankan      html  css  js  c++  java
  • vue | 基于vue的城市选择器和搜索城市对应的小区

    城市选择器应该是比较常用的一个组件,用户可以去选择自己的城市,选择城市后返回,又根据自己选择的城市搜索小区。

    功能展示

     这是选择结果

    这是选择城市

    这是搜索小区

    这是搜索小区接口,key为城市名字,id是城市的id

    假如切换城市

    搜索接口也会相应变化,id=0997 就是指定的搜索城市id

    技术栈

     vue2.0+vue-router+webpack+vuex+less+better-scroll+axios

    webpack
    resolve: {
        extensions: ['.js', '.vue', '.json'],
        alias: {
          'vue$': 'vue/dist/vue.esm.js',
          'src': resolve('src'),
          'common' : resolve('src/common'),
          'components': resolve('src/components'),
          'base': resolve('src/base'),
          "api":resolve('src/api')
        }
      },

    用less需要引入less和less-loader,但是不需要在webpack操作,webpack已经操作好了

    技术栈介绍

    所有城市是本地维护的,在city.js中

    1、axios封装

    import {ajaxUrl} from "./config"
    import axios from 'axios'
    
    export function getSearchData(key,id){
        var obj = {
            op:"search",
            key,
            id
        }
        return axios.get(ajaxUrl.searcUrl,{
            params: obj
        }).then((res)=>{
            return Promise.resolve(res.data);
        }).catch((err)=>{
            return Promise.resolve(err);
        })
    }

    2、axios调用,因为目前没有接口,所以只是模拟演示接口,但不影响逻辑

    import {getSearchData} from "api/search"
    _getDiscList(key,id){
          this.searchList=["八方城","西溪北苑北区","西溪北苑西区","西溪北苑东区","万科城","恒大城","西溪科技园","未来科技城","智慧城","春天家园","茶张新苑","双水磨小区","小区1","小区2","小区3","小区4","小区5","小区6","小区7"];
          getSearchData(key,id).then((res)=>{
          },(err)=>{})
        },

    3、vuex状态管理,vuex我就不介绍了,具体可以去看官网

     

    4、主要介绍一下state中变量的含义

    import {initial} from "common/js/config"
    const state = {
        selectCity:initial.city,
        selectCommunity:initial.community,
        hasSelCityID:-1
    }
    export default state

    5、config.js

    export const initial = {
        city:"杭州",
        community:"八方城"
    }

    selectCity是选择的城市

    selectCommunity是选择的小区

    hasSelCityID是选择城市的id,根据此id选择对应小区

    6、better-scroll

    better-scroll 是之前封装好的一个页面滚动组件

    7、vue-router

    8、页面滑动对应title也变化原理

    首先需要记住变量scrollY,这是记录页面滚动到哪个title

    data(){
        return{
          city:[],
          scrollY: -1,
          currentIndex:0,
          diff:-1,
        }
      },

    初始化的时候初始化这三个变量,probetype=3是better-scroll可支持touchmove事件的参数

    created(){
        this.touch = {};
        this.listenScroll = true;
        this.listenHeight = [];
        this.probetype = 3;
      },

    时刻计算高度,并检测页面滚动到哪个位置

    watch:{
        city(){
          setTimeout(()=>{
            this._calculateHeight()
          },20)
        },
        scrollY(newY){
          // 滚动到中间部分
          const listenHeight = this.listenHeight;
          // 滚动到头部以上
          if (newY>=-25) {
            this.currentIndex = 0;
            return;
          }
          for(let i=0;i<listenHeight.length-1;i++){
            let height1 = listenHeight[i];
            let height2 = listenHeight[i+1];
            // 如果没在下限,且在height1和height2之间
            if (-newY>=height1 && -newY<=height2) {
              this.currentIndex = i;
              this.diff = height2 + newY;
              return;
            }
          }
        },
        diff(newVal){
          let fixedTop = (newVal>0 && newVal<TITLE_HEIGHT)?newVal-TITLE_HEIGHT:0;
          if (this.fixedTop === fixedTop) {
            return
          }
          this.fixedTop = fixedTop;
          this.$refs.fixed.style.transform = `translate3d(0,${fixedTop}px,0)`
        }
      }
    _calculateHeight(){
          this.listenHeight = [];
          const list = this.$refs.listGroup;
          let height = 0;
          this.listenHeight.push(height);
          for(let i =0;i<list.length;i++){
            let item = list[i];
            height +=item.clientHeight;
            this.listenHeight.push(height);
          }
        },

    滚动指定位置

        _scrollTo(index){
          if (!index && index!=0) {
            return
          }
          // 点击右边字母跳到指定位置并高亮
          this.scrollY = -this.listenHeight[index]-1;
          this.$refs.cityList.scrollToElement(this.$refs.listGroup[index],0);
        },

    改变标题

    fixedTitle(){
          if (this.scrollY>0) {
            return ""
          }
          return this.city[this.currentIndex]?this.city[this.currentIndex].initial:""
        }

    点击字右边索引跳转指定位置

    onShortcutTouchStart(e){
          let anchorIndex = getData(e.target,'index');
          console.log(anchorIndex);
          let firstTouch = e.touches[0];
          this.touch.y1 = firstTouch.pageY;
          this.touch.anchorIndex = anchorIndex;
          // this.$refs.singerlist.scrollToElement(this.$refs.listGroup[anchorIndex],0);
          this._scrollTo(anchorIndex)
        },

    9、搜索组件

    搜索输入框是一个组件,组件负责监听input的model变化,只要变化就派发事件,引用该组件的组件,只需要监听派发的事件即可

    created(){
        this.$watch('query',(newQuery)=>{
          this.$emit('query',newQuery)
        })
       }

    11、城市搜索,支持首字母(不区分大小写)搜索

    首先给城市加首字母

    _addFirstLetter(citylist){
          for(var i=0;i<citylist.length;i++){
            for(var j=0;j<citylist[i].list.length;j++){
              citylist[i].list[j]['firstLetter'] = citylist[i].initial;
            }
          }
          this._formatCityList(citylist);
        },

    序列化数组

    // 序列化数组
        _formatCityList(arr){
          var letterArr = {};
          for (var i = 0; i < arr.length; i++) {
            if (!(arr[i]['initial'] in letterArr)) {
              letterArr[arr[i]['initial']] = [];
              for(var j=0;j<arr[i].list.length;j++){
                letterArr[arr[i]['initial']].push(arr[i].list[j]);
              }
            }else{
             for(var j=0;j<arr[i].list.length;j++){
                letterArr[arr[i]['initial']].push(arr[i].list[j]);
              }
            }
          }
          this.letterList = letterArr;
        },

    搜索

    正则 var reg = new RegExp(newVal == '' ? 'xxyy' :newVal, 'ig');   ig是不区分大小写

    // 搜索
        _search(newVal){
            var reg = new RegExp(newVal == '' ? 'xxyy' :newVal, 'ig');
            var _arr = [];
            for(var i in this.letterList){
                for(var j = 0; j < this.letterList[i].length; j++){
                    if(
                        reg.test(this.letterList[i][j][
                            'name'
                        ]) ||
                        reg.test(this.letterList[i][j][
                            'firstLetter'
                        ])
                    ){
                        _arr.push(this.letterList[i][j]);
                    }
                }
            }
            this.searchList = _arr;
        },

    因为引入的搜索框组件,所以只需要监听input内容改变后派发的事件即可

     this._search(newVal);
     this.queryCity = newVal;

    data 搜索结果会放在searchList里面,只需要v-for即可,但是需要边缘处理,没有搜索结果,有一个UI上的一个展示

    data(){
        return{
          city:[],
          letterList:[],
          searchList: [], //搜索结果
          queryCity:"",
          placeholder:"输入城市名称"
        }
      },

     10、每次点击搜索城市后触发mutation,修改state

    selectItem(item){
          this.afterSelect(item)
        },
        selectSearchItem(item){
          this.afterSelect(item)
        },
        // 选择之后的操作
        afterSelect(item){
          this.$router.back();
          this.setCity(item.name);
          this.setCityId(item.zip);
        },
        ...mapMutations({
          setCity:"SET_CITY",
          setCityId:"SET_CITYID"
        })

    推荐使用vuex钩子,具体如何使用可去看官网

    import {mapMutations} from "vuex"
    import {mapGetters} from "vuex"
    业务功能模板

     1、select.vue

    <template lang="html">
    <!-- <transition name="slide"> -->
      <div>
        <div @click="city" class="city clearfix">
          <i>所在城市</i>
          <em></em>
          <span>{{selCity}}</span>
          
        </div>
        <div @click="community" class="community clearfix">
          <i>小区名称</i>
          <em></em>
          <span>{{selCommunity}}</span>
        </div>
      </div>
    <!-- </transition> -->
    </template>

    2、city.vue

    <transition name="slide">
      <div class="xin-widget-citys animated">
        <SearchBox class="search" @query="query" :placeholder="placeholder"></SearchBox>
        <div class="currentCity" v-if="queryCity===''">
          <ul>
            <h2>当前定位城市</h2>
            <li>杭州</li>
          </ul>
        </div>
        <Scroll :data="searchList" class="searchlist" v-if="queryCity !== ''" :class="{'bg':searchList.length === 0}">
            <div>
              <ul v-if="searchList.length!==0">
                  <li class="bdb" v-for="item in searchList" @click="selectSearchItem(item)">{{item.name}}</li>
              </ul>
                <img v-else src="../../common/img/404.png" class="nomatch"/>
            </div>
        </Scroll>
        <CityList class="city" v-if="queryCity===''" @selectItem="selectItem"></CityList>
      </div>
    </transition>

    3、search.vue

    <transition name="slide">
        <div class="xin-widget-citys animated">
            <SearchBox class="search" @query="query" :placeholder="placeholder"></SearchBox>
            <Scroll :data="searchList" class="searchlist" v-if="queryCity !== ''" :class="{'bg':searchList.length === 0}">
                <div>
                  <ul v-if="searchList.length!==0">
                      <li v-for="item in searchList" @click="selectSearchItem(item)">{{item}}</li>
                  </ul>
                    <img v-else src="../../common/img/404.png" class="nomatch"/>
                </div>
            </Scroll>
        </div>
      </transition>
    基础组件模板

    1、city-list.vue

    <Scroll class="citylist" :data="city" ref="cityList" :listenScroll="listenScroll" @scroll="scroll" :probetype="probetype">
      <div>
        <div v-for="(item,index) in city" class="allCity" ref="listGroup">
          <h2>{{item.initial}}</h2>
          <ul>
            <li v-for="city in item.list" @click="selectItem(city)">
                {{city.name}}
            </li>
          </ul>
        </div>
      </div>
      <div class="list-shortcut" @touchstart="onShortcutTouchStart">
        <ul>
          <li class="starCity"></li>
          <li v-for="(item,index) in city" class="item"  :data-index="index">
            {{item.initial}}
          </li>
        </ul>
      </div>
      <div class="list-fixed" v-show="fixedTitle" ref="fixed">
          <h1 class="fixed-title">{{fixedTitle}}</h1>
      </div>
      </Scroll>

    2、search-box.vue

    <template>
      <div class="search-box">
        <i class="icon-search"></i>
        <input ref="query"  class="box" :placeholder="placeholder" v-model="query"/>
      </div>
    </template>
    总结

     以上就是城市选择器的大概介绍,源码我已经放在了我的github上了,有需要可去下载,如果有帮助,麻烦给个star,鼓励我继续努力,谢谢!

     代码地址:https://github.com/dirkhe1051931999/writeBlog

  • 相关阅读:
    菜根谭#54
    菜根谭#53
    svn 的权限配置及命令
    linux部署git环境
    百度编辑器造成无用图片解决方案
    php无限极分类的实现
    phpstudy 升级mysql 5.7
    yii2常用查询
    mysql创建外键注意事项
    阿里云短信
  • 原文地址:https://www.cnblogs.com/dirkhe/p/9573352.html
Copyright © 2011-2022 走看看