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
以上是 vue | 基于vue的城市选择器和搜索城市对应的小区 的全部内容, 来源链接: utcz.com/z/375979.html






