6

说明

1.上一章--页面图标ico的设置
2.苍渡大神的项目源码--项目地址
3.UI框架--Mint ui
4.数据接口地址--接口地址
5.下一章--组件的使用(svg及watch的简单使用)

开始

1.先看看咱们目前的city样式
图片描述

2.再来看看咱们需要实现的样式
图片描述图片描述

3.样式
city.vue样式修改如下

<template>
  <div>
    <mt-header :title="$store.state.nowcity.name" class='fs1-2' fixed>
        <mt-button slot="left"><mt-button icon="back"></mt-button></mt-button>
        <mt-button slot="right" class='fs0-8'>切换城市</mt-button>
    </mt-header>

    <div class="mgtop50 padlr10 bgfff padbot10">
      <input class="cityinput" placeholder="输入商务楼,学校,地址"></input>
      <div class="submit bgcol ih40">提交</div>
    </div>

    <div class="main">

      <div class="his after">
        <div class='maintop fs0-8 padlr10'>搜索历史</div>
        <div class="mainbody bgfff ">
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="clearall ih30 pad10 col9f">
              清空所有
          </div>
        </div>
      </div>

      <div class='search bgfff'> 
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
      </div>

    </div>

  </div>
</template>

<script>

export default {
  data () {
    return {
      
    }
  },
  component:{
  //注册组件

  },
  mounted:function(){
  //生命周期


  },
  computed:{
  //计算属性
      
  },
  methods:{
  //函数
    

  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.cityinput{
  width:100%;
  height:40px;
  margin:10px 0px;
  outline:0px;
  padding:0px 5px;
  box-sizing:border-box;
}
.submit{
  text-align:center;
  color:white;
  border-radius:3px;
}
.fs0-8{
  font-size:0.8rem !important;
}

.main{
  border-top:2px solid #E4E4E4;
}
.maintop{ 
  border-bottom:2px solid #E4E4E4;
}
.clearall{
  text-align:center;
}
</style>

页面显示为
图片描述

我们先把所有的样式都写出来,然后再来控制显示哪个div

4.点击搜索
4.1当我们在搜索框输入地址后,点击提交,应该弹出所有搜索的地址。所以我们应该设置一个变量inputval来存放输入框的值,一个变量list来存放搜索到的数据。

 data () {
    return {
      inputval:"",
      list:""
    }
  }

4.2点击事件。先看数据接口api
图片描述
先写发送请求函数searchcity(这里我把参数拼接到url上就没错,在url后加个{}来传参数就报 参数错误 ,哪位老铁知道玄机么?)

methods:{
  //函数
    searchcity:function(){
      this.$http.get('http://cangdu.org:8001/v1/pois?city_id='+this.$store.state.nowcity.id+'&keyword='+this.inputval+'&type=search').then(response => {
        console.log(response);
        this.list=response.body;
      }, response => {
        console.log(response);
        
      });
    }

  }

this.$store.state.nowcity.id是从vuex里获取当前城市的id,在第八章存入;this.inputval是咱们输入框的值。
然后把点击函数searchcity绑定到元素上

 <div @click="searchcity" class="submit bgcol ih40">提交</div>

运行试试,结果如下
图片描述
解决,可以看到数据已经请求回来了。

4.3显示
现在我们来控制class='his'(搜索历史的div)和class='search'(搜索结果的div)的显示与隐藏。当我们点击提交时,请求数据,将请求的数据加到list上去。那我们就判断,list为空时,说明没点提交,就显示搜索历史的div,list不为空时,显示搜索结果的div。
那我们怎么控制div的显示隐藏呢?vue封装了一个方法v-if。在使用元素上加上v-if=""即可,只要""中间返回的值是true,元素就会显示,反之则隐藏。maindiv修改如下

<div class="main">

      <div v-if="list==''" class="his after">
        <div class='maintop fs0-8 padlr10'>搜索历史</div>
        <div class="mainbody bgfff ">
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="clearall ih30 pad10 col9f">
              清空所有
          </div>
        </div>
      </div>

      <div v-if="list!=''" class='search bgfff'> 
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          
      </div>

    </div>

div显示隐藏写好了,下面用显示数据。数据循环显示依旧用v-for,咱们在第五章已经讲过了。maindiv修改如下

<div class="main">

      <div v-if="list==''" class="his after">
        <div class='maintop fs0-8 padlr10'>搜索历史</div>
        <div class="mainbody bgfff ">
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="pad10 after">
              <div class="ih30">南开区公园</div>
              <div class="ih30 fs0-8 col9f">天津市南开区金马路112号</div>
          </div>
          <div class="clearall ih30 pad10 col9f">
              清空所有
          </div>
        </div>
      </div>

      <div v-if="list!=''" class='search bgfff'> 
          <div v-for="item in list" class="pad10 after">
              <div class="ih30">{{item.name}}</div>
              <div class="ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
      </div>

    </div>

看看结果
图片描述图片描述

完美!数据显示成功。

5.存储搜索历史

首先我们要设置一个变量his来存放搜索历史,这样我们显示的时候直接v-for循环就可以了。

data () {
    return {
      inputval:"",
      list:"",
      his:""
    }
  },

现在要点击搜索结果列表,会有两个反应。一个是页面跳转到商家列表页,咱们先不做,另一个是把点击的地点存到搜索历史里。那我们把搜索历史存到哪里?想了想后觉得存到localstorage里(哪位老铁另有妙计?),既然要存,咱们自然不能犯前面的错误(只存名字),所以我们要把经度,纬度,经纬度合计,名字,地址都存进去。
先写点击事件goaddress在methods里

goaddress:function(e){
      var arr=[];
      if(localStorage.getItem("his")){
          arr=JSON.parse(localStorage.getItem("his"));
          arr.push(e);
      }else{
          arr.push(e);
      }
      localStorage.setItem("his",JSON.stringify(arr));
      this.his=JSON.parse(localStorage.getItem("his"));
      this.list='';
    },

其中his使我们要存到localStorage里的键值名称,先判断有没有,有说明有历史记录,我们就把当前新加的地址放到his里,没有我们就新建一个his,然后再存到localStorage里。添加结束后我们要把his赋值给city.vue的his里,这样我们就可以循环his在页面里显示了(其实应该不用显示,直接页面就跳转了,但我们为了效果先不做跳转,先做历史记录的存储与读取,this.list=''是为了让搜索结果的div隐藏)。

函数写完后我们绑定到要点击的元素上

<div v-if="list!=''" class='search bgfff'> 
          <div v-for="item in list" @click="goaddress({name:item.name,latitude:item.latitude,longitude:item.longitude,address:item.address,geohash:item.geohash})" class="pad10 after">
              <div class="ih30">{{item.name}}</div>
              <div class="ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
      </div>

咱们直接把要存储的对象当做参数传给点击事件goaddress,参数为{name:名字,latitude:经度,longitude:维度,address:地址,,geohash:经纬度合计}

现在我们先点击试试
图片描述图片描述

成功!我们可以在右侧的控制台(F12)的Application下的localStorage里看到我们已经存进去了一条数据

6.显示搜索历史
现在我们的变量his已经有数据了,我们只需要把它显示出来就可以

 <div class="main">

      <div v-if="list==''" class="his after">
        <div class='maintop fs0-8 padlr10'>搜索历史</div>
        <div v-if="his!=''" class="mainbody bgfff ">
          <div v-for="item in his" class="pad10 after">
              <div class="ih30">{{item.name}}</div>
              <div class="ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
          <div class="clearall ih30 pad10 col9f">
              清空所有
          </div>
        </div>
      </div>

      <div v-if="list!=''" class='search bgfff'> 
          <div v-for="item in list" @click="goaddress({name:item.name,latitude:item.latitude,longitude:item.longitude,address:item.address,geohash:item.geohash})" class="pad10 after">
              <div class="ih30">{{item.name}}</div>
              <div class="ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
      </div>

    </div>

这时候发现 清空所有 按钮 并没有功能,所以我们再写一个清空搜索历史的函数removeall(并不会把搜索历史hislocalStorage绑定起来,每次都要手动管理his,哪位老铁可有妙计?)

removeall:function(){
      localStorage.clear();
      this.his="";
    }

然后绑定到清空所有的div上

<div @click='removeall' class="clearall ih30 pad10 col9f">
              清空所有
</div>

ok,运行试试

图片描述图片描述图片描述图片描述图片描述图片描述

看着没问题了,但是可能有老铁已经注意到了--历史记录应该页面一进来就显示出来,所以我们应该在vue的生命周期mounted函数里写操作

mounted:function(){
  //生命周期
    if(localStorage.getItem("his")){
     this.his=localStorage.getItem("his");
    }
  },

判断本地缓存是否有his,有就加到city.vue里的his里。city.vue完整代码如下

<template>
  <div>
    <mt-header :title="$store.state.nowcity.name" class='fs1-2' fixed>
        <mt-button slot="left"><mt-button icon="back"></mt-button></mt-button>
        <mt-button slot="right" class='fs0-8'>切换城市</mt-button>
    </mt-header>

    <div class="mgtop50 padlr10 bgfff padbot10">
      <input v-model="inputval" class="cityinput" placeholder="输入商务楼,学校,地址"></input>
      <div @click="searchcity" class="submit bgcol ih40">提交</div>
    </div>

    <div class="main">

      <div v-if="list==''" class="his after">
        <div class='maintop fs0-8 padlr10'>搜索历史</div>
        <div v-if="his!=''" class="mainbody bgfff ">
          <div v-for="item in his" class="pad10 after">
              <div class="ih30">{{item.name}}</div>
              <div class="ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
          <div @click='removeall' class="clearall ih30 pad10 col9f">
              清空所有
          </div>
        </div>
      </div>

      <div v-if="list!=''" class='search bgfff'> 
          <div v-for="item in list" @click="goaddress({name:item.name,latitude:item.latitude,longitude:item.longitude,address:item.address,geohash:item.geohash})" class="pad10 after">
              <div class="ih30">{{item.name}}</div>
              <div class="ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
      </div>

    </div>

  </div>
</template>

<script>

export default {
  data () {
    return {
      inputval:"",
      list:"",
      his:""
    }
  },
  component:{
  //注册组件

  },
  mounted:function(){
  //生命周期
    if(localStorage.getItem("his")){
     this.his=JSON.parse(localStorage.getItem("his"));
    }
  },
  computed:{
  //计算属性
      
  },
  methods:{
  //函数
    searchcity:function(){
      this.$http.get('http://cangdu.org:8001/v1/pois?city_id='+this.$store.state.nowcity.id+'&keyword='+this.inputval+'&type=search').then(response => {
        console.log(response);
        this.list=response.body;
      }, response => {
        console.log(response);
        
      });
    },
    goaddress:function(e){
      var arr=[];
      if(localStorage.getItem("his")){
          arr=JSON.parse(localStorage.getItem("his"));
          arr.push(e);
      }else{
          arr.push(e);
      }
      localStorage.setItem("his",JSON.stringify(arr));
      this.his=JSON.parse(localStorage.getItem("his"));
      this.list='';
    },
    removeall:function(){
      localStorage.clear();
      this.his="";
    }

  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.cityinput{
  width:100%;
  height:40px;
  margin:10px 0px;
  outline:0px;
  padding:0px 5px;
  box-sizing:border-box;
}
.submit{
  text-align:center;
  color:white;
  border-radius:3px;
}
.fs0-8{
  font-size:0.8rem !important;
}

.main{
  border-top:2px solid #E4E4E4;
}
.maintop{ 
  border-bottom:2px solid #E4E4E4;
}
.clearall{
  text-align:center;
}
</style>

运行试试--
图片描述图片描述图片描述

ok!到这里city.vue的搜索历史的存储与
读取基本写完了

修改

页面目前还有几个小bug

1.输入框数据为空时不能点击提交,把输入框放到form表单里,增加一个required属性

<div class="mgtop50 padlr10 bgfff padbot10">
      <form v-on:submit.prevent>
        <input v-model="inputval" class="cityinput" required placeholder="输入商务楼,学校,地址"></input>
        <input type='submit' name='submit' value='提交' @click="searchcity" class="submit bgcol ih40"></input>
      </form>
    </div>

再在函数searchcity里判断搜索地址数据是否为空,不为空再发送请求。

searchcity:function(){
      if(this.inputval){
        this.$http.get('http://cangdu.org:8001/v1/pois?city_id='+this.$store.state.nowcity.id+'&keyword='+this.inputval+'&type=search').then(response => {
          console.log(response);
          this.list=response.body;
        }, response => {
          console.log(response);
          
        });
      }     
    },

2.页面内容明明就没有填满,却出现滚动条,这是因为咱们的第一个div即form的父元素(固定定位的头部不算)有一个magtop50导致的(遇见过很多次,原因是啥不知道),咱们去掉这个class再在form的父元素外层加一个div加上padtop50

<div class='padtop50'>
      <div class="padlr10 bgfff padbot10">
        <form v-on:submit.prevent>
          <input v-model="inputval" class="cityinput" required placeholder="输入商务楼,学校,地址"></input>
          <input type='submit' name='submit' value='提交' @click="searchcity" class="submit bgcol ih40"></input>
        </form>
      </div>
</div>

3.搜索的结果返回数据为空怎么办?地址输入 隐隐 点击提交
图片描述
可以看到,返回数据的body为空。那咱们就给他弹出个消息框提示数据为空。
消息提示框可以用 Mint UI 的 Toast组件,例子写的很清楚,咱们用最简单的就行
图片描述
在city.vue的<script>里第一行写入

import { Toast } from 'mint-ui';

先引入,引入后就可以使用。在提交的点击事件searchcity里判断返回数据是否为空

searchcity:function(){
      if(this.inputval){
        this.$http.get('http://cangdu.org:8001/v1/pois?city_id='+this.$store.state.nowcity.id+'&keyword='+this.inputval+'&type=search').then(response => {
          console.log(response);
          this.list=response.body;
          if(response.body==""){
            Toast('抱歉,空空如也');
          }
        }, response => {
          console.log(response);
          
        });
      }     
    },

运行试试
图片描述

解决!弹出返回数据为空时消息提示框!

4.判断是否重复
当点击搜索结果与搜索历史有相同时,就不添加到历史记录。

goaddress:function(e){
      var arr=[];
      if(localStorage.getItem("his")){
          arr=JSON.parse(localStorage.getItem("his"));
          for(var i=0;i<arr.length;i++){
            if(arr[i].geohash==e.geohash){
               var isok=true;
            }
          }
          if(!isok){
            arr.unshift(e);
          }
      }else{
          arr.unshift(e);
      }
      localStorage.setItem("his",JSON.stringify(arr));
      this.his=JSON.parse(localStorage.getItem("his"));
      this.list='';
},

这里要注意

  1. 通过经纬度的集合geohash来判断是否相等
  2. 因为最近点击的搜索地址要在搜索历史的最上面,所以要用unshift添加而不是push
  3. 最重要一点,可能有老铁会问,在判断是否有相同的地址后,为什么要通过isok这个中间变量来改变是否添加新地址,而不是把 arr.unshift(e)直接写在if判断里。。。嘿嘿,如果你用unshift在if判断直接添加新元素,会出现死循环,因为当你把元素加到数组第一位后,数组所有的元素都会往后退一位,这样你下次循环进来,取到的元素仍是上一次循环的元素...(我也没想明白,而我的学长强哥Topqiang一眼就看出来了,各位老铁如果有其他的好方法可以分享一下)

city.vue修改后的完整代码如下

<template>
  <div>
    <mt-header :title="$store.state.nowcity.name" class='fs1-2' fixed>
        <mt-button slot="left"><mt-button icon="back"></mt-button></mt-button>
        <mt-button slot="right" class='fs0-8'>切换城市</mt-button>
    </mt-header>

    <div class='padtop50'>
      <div class="padlr10 bgfff padbot10">
        <form v-on:submit.prevent>
          <input v-model="inputval" class="cityinput" required placeholder="输入商务楼,学校,地址"></input>
          <input type='submit' name='submit' value='提交' @click="searchcity" class="submit bgcol ih40"></input>
        </form>
      </div>
    </div>

    <div class="main">

      <div v-if="list==''" class="his after">
        <div class='maintop fs0-8 padlr10'>搜索历史</div>
        <div v-if="his!=''" class="mainbody bgfff ">
          <div v-for="item in his" class="pad10 after">
              <div class="ih30 nowarp">{{item.name}}</div>
              <div class="nowarp ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
          <div @click='removeall' class="clearall ih30 pad10 col9f">
              清空所有
          </div>
        </div>
      </div>

      <div v-if="list!=''" class='search bgfff'> 
          <div v-for="item in list" @click="goaddress({name:item.name,latitude:item.latitude,longitude:item.longitude,address:item.address,geohash:item.geohash})" class="pad10 after">
              <div class="ih30 nowarp">{{item.name}}</div>
              <div class="nowarp ih30 fs0-8 col9f">{{item.address}}</div>
          </div>
      </div>

    </div>

  </div>
</template>

<script>
import { Toast } from 'mint-ui';

export default {
  data () {
    return {
      inputval:"",
      list:"",
      his:""
    }
  },
  component:{
  //注册组件

  },
  mounted:function(){
  //生命周期
    if(localStorage.getItem("his")){
     this.his=JSON.parse(localStorage.getItem("his"));
    }
  },
  computed:{
  //计算属性
      
  },
  methods:{
  //函数
    searchcity:function(){
      if(this.inputval){
        this.$http.get('http://cangdu.org:8001/v1/pois?city_id='+this.$store.state.nowcity.id+'&keyword='+this.inputval+'&type=search').then(response => {
          console.log(response);
          this.list=response.body;
          if(response.body==""){
            Toast('抱歉,空空如也');
          }
        }, response => {
          console.log(response);
          
        });
      }     
    },
    goaddress:function(e){
      var arr=[];
      if(localStorage.getItem("his")){
          arr=JSON.parse(localStorage.getItem("his"));
          for(var i=0;i<arr.length;i++){
            if(arr[i].geohash==e.geohash){
               var isok=true;
            }
          }
          if(!isok){
            arr.unshift(e);
          }
      }else{
          arr.unshift(e);
      }
      localStorage.setItem("his",JSON.stringify(arr));
      this.his=JSON.parse(localStorage.getItem("his"));
      this.list='';
    },
    removeall:function(){
      localStorage.clear();
      this.his="";
    }

  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.cityinput{
  width:100%;
  height:40px;
  margin:10px 0px;
  outline:0px;
  padding:0px 5px;
  box-sizing:border-box;
}
.submit{
  text-align:center;
  color:white;
  border-radius:3px;
  width:100%;
  border:0px;
  outline:0px;
}
.fs0-8{
  font-size:0.8rem !important;
}

.main{
  border-top:2px solid #E4E4E4;
}
.maintop{ 
  border-bottom:2px solid #E4E4E4;
}
.clearall{
  text-align:center;
}
</style>

运行试试
图片描述图片描述图片描述图片描述
解决!

下面,咱们写点击搜索结果的另一个反应--页面跳转


你的前端
132 声望93 粉丝

你的前端