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

功能展示

这是选择结果

这是选择城市

这是搜索小区

这是搜索小区接口,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的城市选择器和搜索城市对应的小区的更多相关文章

  1. 基于vue的颜色选择器color-picker

    项目中有用到颜色选择器的童鞋们可以看过来了 关于color-picker的jquery的插件是有蛮多,不过vue组件没有吧,反正我没有找到, 虽然element-ui里面有这个,但是你愿意为了一个小功 ...

  2. 基于vue的颜色选择器vue-color-picker

    项目中有用到颜色选择器的童鞋们可以看过来了 关于color-picker的jquery的插件是有蛮多,不过vue组件没有吧,反正我没有找到, 虽然element-ui里面有这个,但是你愿意为了一个小功 ...

  3. vue_shop(基于vue电商管理后台网站)

    vue_shop 目录 vue_shop day01 实现登录功能 项目预开发处理 Login.vue完整代码: 处理步骤: 添加element-ui的表单组件 添加第三方字体: 添加表单验证 导入a ...

  4. VBox 一款基于vue开发的音乐盒 序章

    己基于vue写了一个 Mplayer, github地址:https://github.com/xiangwenhu/MPlaer, 演示地址:http://babydairy2017.cloudap ...

  5. 基于vue.js的简单用户管理

    功能描述:添加.修改.搜索过滤 效果图: <!DOCTYPE html> <html lang="en"> <head> <title&g ...

  6. 基于Vue.js 2.0 + Vuex打造微信项目

    一.项目简介 基于Vue + Vuex + Vue-router + Webpack 2.0打造微信界面,实现了微信聊天.搜索.点赞.通讯录(快速导航).个人中心.模拟对话.朋友圈.设置等功能. 二. ...

  7. 基于vue全家桶制作的移动端音乐WebApp

    Vue.js 2.0实战项目 基于Vue + Vuex + Vue-router + Webpack 2.0 打造移动端音乐WebAPP,实现了轮播图.音乐推荐.歌手列表.音乐搜索.注册等功能. 技术 ...

  8. 基于Vue + Vuex + Vue-router + Webpack 2.0打造微信界面

    一.项目简介 基于Vue + Vuex + Vue-router + Webpack 2.0打造微信界面,实现了微信聊天.搜索.点赞.通讯录(快速导航).个人中心.模拟对话.朋友圈.设置等功能. 二. ...

  9. vue+node+mongoDB 火车票H5(五)---城市选择

    选择城市,支持搜索,储存选择过城市的历史记录,点击索引能跳到相应位置 1. 父子组件传值,trainCity城市选择组件,选择城市后改变父组件的值 2. 把城市数组按照字母A到Z排序 3.点击字母索引 ...

随机推荐

  1. redis读写分离及可用性设计

    Redis缓存架构设计 对于下面两个架构图,有如下想法: 1)redis主从复制模式,为了解决master读写压力,对master进行写操作,对slave进行读操作. 2)而在分片集群中,如果对部分分 ...

  2. Git那点事儿

    Git的分支你们是怎么管理的?/ Git的分支管理策略 主分支 master 开发分支 develop 功能分支 feature 预发布分支 release bug 分支 fixbug 其它分支 ot ...

  3. 实体类转xml

    看项目中需要实体类转xml,大家是拼接的.感觉可以利用反射实现.于是写了下 代码如下 package com.kevin.util; import org.springframework.util.O ...

  4. 《从缺陷中学习CC++》总结

    从缺陷中学习CC++总结 从本质上来说,这就是一个CC++的错题集.全书中包括63个问题引发的错误,即书名中的缺陷.共分为10章,每一张还有一个比较经典的小结,个人感觉这本书只需要仔细看一下每一章后面 ...

  5. 利用python爬取贝壳网租房信息

    最近准备换房子,在网站上寻找各种房源信息,看得眼花缭乱,于是想着能否将基本信息汇总起来便于查找,便用python将基本信息爬下来放到excel,这样一来就容易搜索了. 1. 利用lxml中的xpath ...

  6. 微信小程序发送订阅消息(之前是模板消息)

    之前的模板消息已经废弃,现在改为订阅消息,订阅消息发布前,需要用户确认后才能接收订阅消息. 小程序端 index.wxml <button bindtap="send"> ...

  7. Java方法传参,测试在方法内部改变参数内容是否会影响到原值

    我分了三种类型的参数进行测试 一.基本类型 public static void main(String[] args) { System.out.println("验证基本类型int作为参 ...

  8. Qt setMouseTracking使用

    Qt setMouseTracking使用(转载)   bool mouseTracking 这个属性保存的是窗口部件跟踪鼠标是否生效. 如果鼠标跟踪失效(默认),当鼠标被移动的时候只有在至少一个鼠标 ...

  9. 5G边缘计算:开源架起5G MEC生态发展新通路

    摘要:‍‍本文尝试从‍‍边缘计算的角度来阐述了‍‍为什么‍‍要把边缘计算当做一种新的生产关系来构建,‍‍以及如何用开源来构建这种新的生产关系. 5G推动新一轮工业革命 过去‍‍人类经历了三次工业革命, ...

  10. Flink自定义Sink

    Flink自定义Sink Flink 自定义Sink,把socket数据流数据转换成对象写入到mysql存储. #创建Student类 public class Student { private i ...