vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)


Posted in Javascript onApril 11, 2020

完整效果演示

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

首先完成这个伪搜索框

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

src/components/search/index.vue (通用搜索框组件)

<template>
 <div class="mine-search-box-wrapper">
  <i class="iconfont icon-search"></i>
  <div class="mine-search-box" v-if="fake">{{placeholder}}</div>
  <input
   class="mine-search-box"
   type="text"
   title="搜索框"
   :placeholder="placeholder"
   ref="input"
   v-model="query"
   v-if="!fake"
  >
  <i
   class="iconfont icon-close"
   v-show="query"
   @click="reset"
  ></i>
 </div>
</template>

<script>
import {debounde} from 'assets/js/util';


export default {
  name:'Search',
  props:{//接收的参数
    placeholder:{
      type:String,
      default:'请输入搜索内容'
    },
    fake:{
      type:Boolean,
      default:false
    }
  },
  data(){
    return{
      query:'',
    }
  },
  watch:{
    query:debounde(function(){
      this.$emit('query',this.query);
    })
  },
  methods:{
    focus(){
      this.$refs.input && this.$refs.input.focus();
    },
    clear(){
      this.query='';
    },
    reset(){//重置
      this.clear();
      this.focus();
    }
  }
}
</script>

<style lang="scss" scoped>
  $search-box-height: 30px;
  $icon-color: #ccc;
  $icon-font-size-sm: 18px;

 .mine-search-box-wrapper {
  display: flex;
  align-items: center;
  width: 85%;
  height: $search-box-height;
  padding: 0 7px;
  background-color: #fff;
  border-radius: $search-box-height / 2;
  margin-left:15px;
 }

 .iconfont {
  color: $icon-color;
  font-size: $icon-font-size-sm;
  font-weight: bold;
 }

 .mine-search-box {
  flex: 1;
  background: none;
  border: none;
  margin: 0 6px;
  color: #666;
  line-height: 1.5;
 }
</style>

src/assets/js/util.js  节流函数(防止请求数据时频率过快消耗性能)

//函数节流
export const debounde=(func,delay=200)=>{
  let timer=null;

  return function(...args){
    timer && clearTimeout(timer);

    timer=setTimeout(()=>{
      func.apply(this,args);
    },delay);
  }
}

在分类页的头部组件中引入搜索框组件

src/pages/category/header.vue

<template>
  <div class="header">
    <i class="iconfont icon-scan header-left"></i>
    <div class="header-center">
      <search placeholder="开学季有礼,好货五折起" @query='getQuery' fake @click.native="goToSearch" />
    </div>
    <i class="iconfont icon-msg header-right"></i>
  </div>
</template>

<script>
import Search from 'components/search';

export default {
  name:'CategoryHeader',
  components:{
    Search
  },
  methods:{
    getQuery(query){
      console.log(query);
    },
    goToSearch(){
      this.$router.push('/search');
    }
  }
}
</script>

<style lang="scss" scoped>
  .header{
    background-color:rgba(222, 24, 27, 0.9);
    transition:background-color 0.5s;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding:5px 20px;

    .iconfont{
      font-size:24px;
      color:#fff;
    }

    .header-center{
      flex:1;
    }
  } 
</style>

点击搜索框之后会跳转到真正的搜索页

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

热门搜索组件

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

src/pages/search/hot.vue

<template>
 <div class="hot">
  <h4 class="hot-title">热门搜索</h4>
  <div class="loading-container" v-if="!hots.length">
   <me-loading/>
  </div>
  <ul class="hot-list" v-else>
   <li class="hot-item" v-for="(item,index) in hots" :key="index" @click="$_selectItem(item.hotWord)">
     {{item.hotWord}}
   </li>
  </ul>
 </div>
</template>

<script>
import Search from 'components/search';
import MeLoading from 'components/loading';
import {getHot} from 'api/search';
import {searchMixin} from 'api/mixins';

export default {
  name:'SearchHot',
  components:{
    MeLoading
  },
  data(){
    return{
      hots:[]
    }
  },
  mixins:[searchMixin],
  created(){
    this.getHot().then(()=>{
      this.$emit('loaded');
    })
  },
  methods:{
    getHot(){
      return getHot().then(data=>{
        return new Promise(resolve=>{
          if(data){
            this.hots=data;
            resolve();
          }
        })
      })
    }
  }
}
</script>

<style lang="scss" scoped>
$border-color: #e5e5e5;
$font-size-base: 12px;
$font-size-l: $font-size-base + 2;

 .hot {
  padding-left: 10px;
  background-color: #fff;
  border-bottom: 1px solid $border-color;
  margin-bottom: 10px;

  &-title {
   height: 34px;
   line-height: 34px;
   font-size: $font-size-l;
   font-weight: bold;
  }

  &-list {
   display: flex;
   flex-wrap: wrap;
  }

  &-item {
   padding: 8px;
   background-color: #f0f2f5;
   border-radius: 4px;
   margin: 0 10px 10px 0;
   color: #686868;
  }
 }

 .loading-container {
  padding: 10px 0;
 }
</style>

axios获取热门搜索数据

src/api/search.js

import axios from 'axios';

//获取热门搜索数据 ajax
export const getHot=()=>{
  return axios.get('http://www.imooc.com/api/search/hot').then(res=>{
    
    res=res.data.hotKeyWord;
    if(res && res.owner){
      return res.owner;
    }
    throw new Error('没有成功获取到数据');

  }).catch(err=>{
    console.log(err);
  });
}

点击搜索的关键词,跳转到淘宝搜索程序

src/api/mixins.js

import storage from 'assets/js/storage';
import {SEARCH_HISTORY_KEYWORD_KEY} from 'pages/search/config';

export const searchMixin={
  methods:{
    $_selectItem(keyword){
      let keywords=storage.get(SEARCH_HISTORY_KEYWORD_KEY,[]);//找到所有搜索历史
 
       if(keywords.length!=0){
         keywords=keywords.filter(val=>val!=keyword);//这次的关键词如果在搜索历史里已存在,先剔除掉
       }
 
       keywords.unshift(keyword);//把这次的关键词放在搜索历史的最开头
      
      storage.set(SEARCH_HISTORY_KEYWORD_KEY,keywords);//更新搜索历史
 
      //跳转到淘宝搜索页
      location.href = `https://s.m.taobao.com/h5?event_submit_do_new_search_auction=1&_input_charset=utf-8&topSearch=1&atype=b&searchfrom=1&action=home%3Aredirect_app_action&from=1&sst=1&n=20&buying=buyitnow&q=${keyword}`;
    }
  }
}

本地存储文件 assets/js/storage.js

const storage = window.localStorage;

export default {
 set(key, val) {
  if (val === undefined) {
   return;
  }
  storage.setItem(key, serialize(val));
 },
 get(key, def) {
  const val = deserialize(storage.getItem(key));
  return val === undefined ? def : val;
 },
 remove(key) {
  storage.removeItem(key);
 },
 clear() {
  storage.clear();
 }
};

function serialize(val) {
 return JSON.stringify(val);
}

function deserialize(val) {
 if (typeof val !== 'string') {
  return undefined;
 }
 try {
  return JSON.parse(val);
 } catch (e) {
  return val || undefined;
 }
}

 搜索页配置文件 src/pages/search/config.js

const prefix = 'mall-search';
const suffix = 'key';
export const SEARCH_HISTORY_KEYWORD_KEY = `${prefix}-history-keyword-${suffix}`;

历史搜索组件

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

src/pages/search/history.vue

<template>
 <div class="history" v-if="historys.length">
  <h4 class="history-title">历史搜索</h4>
  <transition-group class="g-list" tag="ul" name="list">
   <li class="g-list-item" v-for="item in historys" :key="item" @click="$_selectItem(item)">
     <span class="g-list-text">{{item}}</span>
     <!-- .stop 禁止事件冒泡 -->
     <i class="iconfont icon-delete" @click.stop="removeItem(item)"></i>
   </li>
  </transition-group>
  <a href="javascript:;" rel="external nofollow" class="history-btn" @click="showConfirm">
    <i class="iconfont icon-clear" ></i>
    清空历史搜索
  </a>
 </div>
</template>

<script>
import storage from 'assets/js/storage';
import {SEARCH_HISTORY_KEYWORD_KEY} from 'pages/search/config';
import {searchMixin} from 'api/mixins';


export default {
  name:'SearchHistory',
  data(){
    return{
      historys:[]
    }
  },
  mixins:[searchMixin],
  created(){
    this.getKeyword();
  },
  methods:{
    update(){
     this.getKeyword();
    },
    getKeyword(){
      this.historys=storage.get(SEARCH_HISTORY_KEYWORD_KEY,[]);
      this.$emit('loaded');
    },
    removeItem(item){
     this.historys=this.historys.filter(val=>val!==item);//点击后删除该项
     storage.set(SEARCH_HISTORY_KEYWORD_KEY,this.historys);//更新缓存
     this.$emit('remove-item');
    },
    showConfirm(){
     this.$emit('show-confirm');
    },
    clear(){
     storage.remove(SEARCH_HISTORY_KEYWORD_KEY);
    }
  }
}
</script>

<style lang="scss" scoped>

  $border-color: #e5e5e5;
  $font-size-base: 12px;
  $font-size-l: $font-size-base + 2;
  $border-color: #e5e5e5;

  @mixin flex-center($direction: row) {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: $direction;
  }

 .history {
  padding-bottom: 30px;
  background-color: #fff;

  &-title {
   height: 34px;
   line-height: 34px;
   padding: 0 10px;
   font-size: $font-size-l;
   font-weight: bold;
  }

  &-btn {
   @include flex-center();
   width: 80%;
   height: 40px;
   background: none;
   border: 1px solid #ccc;
   border-radius: 4px;
   margin: 0 auto;
   color: #686868;

   .iconfont {
    margin-right: 5px;
   }
  }
 }

 .g-list {
  border-top: 1px solid $border-color;
  border-bottom: 1px solid $border-color;
  margin-bottom: 20px;
 }

 .list {
  &-enter-active,
  &-leave-active {
   transition: height 0.1s;
  }

  &-enter,
  &-leave-to {
   height: 0;
  }
 }

</style>

列表样式统一抽离出去

src/assets/scss/_list.scss

// list
@mixin flex-between() {
  display: flex;
  justify-content: space-between;
  align-items: center;
 }

//ellipsis
@mixin ellipsis() {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
 }

 $border-color: #e5e5e5;
 
.g-list {
 padding-left: 10px;
}
.g-list-item {
 overflow: hidden;
 @include flex-between();
 height: 44px;
 padding-right: 10px;
 border-bottom: 1px solid $border-color;
 color: #686868;

 &:last-child {
  border-bottom: none;
 }
}
.g-list-text {
 flex: 1;
 line-height: 1.5;
 @include ellipsis();
}

src/assets/scss/index.scss

@import 'icons';
@import 'list';

*{
  margin:0;
  padding:0;
}
html,body{
  // 必须设置,否则内容滚动效果无法实现
  width:100%;
  height:100%;
}
ul,li{
  list-style:none;
}
a{
  text-decoration: none;
  color:#333;
}

确认框组件

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

src/components/comfirm/index.vue

<template>
  <transition name="mine-confirm">
    <div class="mine-confirm-wrapper" v-show="visible">
      <div class="mine-confirm">
        <div class="mine-confirm-title">{{title}}</div>
        <div class="mine-confirm-msg">{{msg}}</div>
        <div class="mine-confirm-btns">
          <button class="mine-confirm-btn mine-confirm-cancel" @click="cancel">{{cancelBtnText}}</button>
          <button class="mine-confirm-btn mine-confirm-ok" @click="confirm">{{confirmBtnText}}</button>
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name:'MineConfirm',
  props:{
    title:{
      type:String,
      default:''
    },
    msg:{
      type:String,
      default:'确定执行此操作吗?'
    },
    cancelBtnText:{
      type:String,
      default:'取消'
    },
    confirmBtnText:{
      type:String,
      default:'确定'
    }
  },
  data(){
    return{
      visible:false
    }
  },
  methods:{
    show(){
      this.visible=true;
    },
    hide(){
      this.visible=false;
    },
    cancel(){
      this.hide();
      this.$emit('cancel');
    },
    confirm(){
      this.hide();
      this.$emit('confirm');
    }
  }
}
</script>

<style lang="scss" scoped>

$search-z-index: 1200;
$search-popup-z-index: $search-z-index + 10;
$modal-bgc: rgba(0, 0, 0, 0.4);

@mixin flex-center($direction: row) {
 display: flex;
 justify-content: center;
 align-items: center;
 flex-direction: $direction;
}
@mixin ellipsis() {
 overflow: hidden;
 text-overflow: ellipsis;
 white-space: nowrap;
}

 .mine-confirm-wrapper {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: $search-popup-z-index;
  @include flex-center();
  background-color: $modal-bgc;
 }

 .mine-confirm {
  overflow: hidden;
  width: 88%;
  background-color: #fff;
  border-radius: 10px;
  font-size: 16px;

  &-title {
   padding: 20px 15px 0;
   font-size: 18px;
   text-align: center;
   @include ellipsis();

   & + .mine-confirm-msg {
    padding-top: 20px;
    padding-bottom: 20px;
   }
  }

  &-msg {
   padding: 40px 15px;
   text-align: center;
   line-height: 1.5;
  }

  &-btns {
   display: flex;
  }

  &-btn {
   flex: 1;
   height: 44px;
   line-height: 44px;
   background: none;
   border: none;
  }

  &-cancel {
   border-top: 1px solid #e3e5e9;
  }

  &-ok {
   background-color: #f23030;
   color: #fff;
  }
 }

 .mine-confirm {
  &-enter-active,
  &-leave-active {
   transition: opacity 0.3s;
  }

  &-enter,
  &-leave-to {
   opacity: 0;
  }

  &-enter-active {
   .mine-confirm {
    animation: bounce-in 0.3s;
   }
  }
 }

 // https://cn.vuejs.org/v2/guide/transitions.html#CSS-动画
 @keyframes bounce-in {
  0% {
   transform: scale(0);
  }
  50% {
   transform: scale(1.1);
  }
  100% {
   transform: scale(1);
  }
 }
</style>

 搜索结果页

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

src/pages/search/result.vue

<template>
  <div class="result">
  <div class="loading-container" v-show="loading">
   <me-loading/>
  </div>
  <ul class="g-list" v-show="!loading && results.length">
   <li
    class="g-list-item"
    v-for="(item, index) in results"
    :key="index"
    @click="$_selectItem(item[0])"
   >
    <span class="g-list-text">{{item[0]}}</span>
   </li>
  </ul>
  <div class="no-result" v-show="!loading && !results.length">没有结果</div>
 </div>
</template>

<script>
 import MeLoading from 'components/loading';
 import {getSearchResult} from 'api/search';
 import {searchMixin} from 'api/mixins';

export default {
  name:'SearchResult',
  components:{
    MeLoading
  },
  data(){
    return{
      results:[],
      loading:false
    }
  },
  props:{
    query:{
      type:String,
      default:''
    }
  },
  mixins:[searchMixin],
  watch:{
    query(query){
      
      this.getResults(query);
    }
  },
  methods:{
    getResults(keyword){
      if(!keyword){
        return;
      }

      this.loading=true;
      getSearchResult(keyword).then(data=>{
        console.log(data);
        if(data){
          
          this.results=data;
          this.loading=false;
        }
      })
    }
  }
}
</script>

修改src/api/search.js

import axios from 'axios';
import jsonp from 'assets/js/jsonp';

//获取热门搜索数据 ajax
export const getHot=()=>{
  return axios.get('http://www.imooc.com/api/search/hot').then(res=>{
    
    res=res.data.hotKeyWord;
    if(res && res.owner){
      return res.owner;
    }
    throw new Error('没有成功获取到数据');

  }).catch(err=>{
    console.log(err);
  });
}

//获取搜索框的搜索结果
export const getSearchResult=keyword=>{
  const url='https://suggest.taobao.com/sug';
  
  const params={
    q:keyword,
    code:'utf-8',
    area:'c2c',
    nick:'',
    sid:null
  };
  //https://suggest.taobao.com/sug?q=apple&code=utf-8&area=c2c&nick=&sid=null&callback=jsonp5
  return jsonp(url, params, {
    param: 'callback'
   }).then(res => {
     console.log(res);
    if (res.result) {
      // console.log(res);
      return res.result;
    }

    throw new Error('没有成功获取到数据!');
  }).catch(err => {
    if (err) {
      console.log(err);
    }
  });
};

最后,当删除历史搜索之后,也需要更新滚动条

修改src/pages/search/index.vue

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

修改src/pages/search/history.vue

(因为页面加载时有100ms延迟的动画,因此这里更新滚动条也需要相同的延迟)

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

注意滚动条组件的更新操作,需要使用 $nextTick( ) 实现异步

vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)

总结

到此这篇关于vue搜索页开发(热门搜索,历史搜索,淘宝接口演示)的文章就介绍到这了,更多相关vue搜索页开发内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
根据鼠标的位置动态的控制层的位置
Nov 24 Javascript
javascript KeyDown、KeyPress和KeyUp事件的区别与联系
Dec 03 Javascript
使用Jquery打造最佳用户体验的登录页面的实现代码
Jul 08 Javascript
js之onload事件的一点使用心得
Aug 14 Javascript
禁用JavaScript控制台调试的方法
Mar 07 Javascript
For循环中分号隔开的3部分的执行顺序探讨
May 27 Javascript
JavaScript中用sort()方法对数组元素进行排序的操作
Jun 09 Javascript
jQuery Ajax全解析
Feb 13 Javascript
如何使用electron-builder及electron-updater给项目配置自动更新
Dec 24 Javascript
jQuery实现验证用户登录
Dec 10 jQuery
vue 查看dist文件里的结构(多种方式)
Jan 17 Javascript
Element-ui 自带的两种远程搜索(模糊查询)用法讲解
Jan 29 Javascript
JavaScript实现轮播图特效
Apr 10 #Javascript
JavaScript实现图片伪异步上传过程解析
Apr 10 #Javascript
Vue实现Layui的集成方法步骤
Apr 10 #Javascript
如何基于layui的laytpl实现数据绑定的示例代码
Apr 10 #Javascript
vue-drag-chart 拖动/缩放图表组件的实例代码
Apr 10 #Javascript
vue+vant使用图片预览功能ImagePreview的问题解决
Apr 10 #Javascript
JS实现省市县三级下拉联动
Apr 10 #Javascript
You might like
PHP微信开发用Cache 解决数据缓存
2016/07/11 PHP
javascript jQuery $.post $.ajax用法
2008/07/09 Javascript
一段批量给页面上的控件赋值js
2010/06/19 Javascript
jquery控制listbox中项的移动并排序的实现代码
2010/09/28 Javascript
用js判断页面刷新或关闭的方法(onbeforeunload与onunload事件)
2012/06/22 Javascript
js中的前绑定和后绑定详解
2013/08/01 Javascript
JS实现一键回顶功能示例代码
2013/10/28 Javascript
IE、FF浏览器下修改标签透明度
2014/01/28 Javascript
jQuery中removeData()方法用法实例
2014/12/27 Javascript
JavaScript Math.floor方法(对数值向下取整)
2015/01/09 Javascript
JavaScript实现的链表数据结构实例
2015/04/02 Javascript
AngularJS基础 ng-src 指令简单示例
2016/08/03 Javascript
[原创]JS基于FileSaver.js插件实现文件保存功能示例
2016/12/08 Javascript
vue实现ajax滚动下拉加载,同时具有loading效果(推荐)
2017/01/11 Javascript
Vue.js实现的计算器功能完整示例
2018/07/11 Javascript
layui树形菜单动态遍历的例子
2019/09/23 Javascript
Weex开发之WEEX-EROS开发踩坑(小结)
2019/10/16 Javascript
vue 动态表单开发方法案例详解
2019/12/02 Javascript
element中table高度自适应的实现
2020/10/21 Javascript
[24:42]VP vs TNC Supermajor小组赛B组 BO3 第三场 6.2
2018/06/03 DOTA
Python 学习笔记
2008/12/27 Python
Python时间模块datetime、time、calendar的使用方法
2016/01/13 Python
Python自定义函数计算给定日期是该年第几天的方法示例
2019/05/30 Python
对python 树状嵌套结构的实现思路详解
2019/08/09 Python
Python3将jpg转为pdf文件的方法示例
2019/12/13 Python
python 线性回归分析模型检验标准--拟合优度详解
2020/02/24 Python
django admin 添加自定义链接方式
2020/03/11 Python
Pycharm Available Package无法显示/安装包的问题Error Loading Package List解决
2020/09/18 Python
HTML5中的Article和Section元素认识及使用
2013/03/22 HTML / CSS
商务英语应届生自我鉴定
2013/12/08 职场文书
本科毕业生求职自荐信
2014/04/09 职场文书
店铺转让协议书(2014版)
2014/09/23 职场文书
入党自荐书范文
2015/03/05 职场文书
工伤认定行政答辩状
2015/05/22 职场文书
2016年全国爱眼日宣传教育活动总结
2016/04/05 职场文书
从QQtabBar看css命名规范BEM的详细介绍
2021/08/07 HTML / CSS