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 相关文章推荐
Bootstrap每天必学之按钮
Nov 26 Javascript
实例讲解JS中setTimeout()的用法
Jan 28 Javascript
jquery ajax结合thinkphp的getjson实现跨域的方法
Jun 06 Javascript
AngularJS模块详解及示例代码
Aug 17 Javascript
Bootstrap响应式侧边栏改进版
Sep 17 Javascript
JavaScript 最佳实践:帮你提升代码质量
Dec 03 Javascript
移动端点击态处理的三种实现方式
Jan 12 Javascript
关于webpack代码拆分的解析
Jul 20 Javascript
微信小程序App生命周期详解
Jan 31 Javascript
微信小程序开发常见问题及解决方案
Jul 11 Javascript
JS数组Reduce方法功能与用法实例详解
Apr 29 Javascript
vue使用Sass时报错问题的解决方法
Oct 14 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调用三种数据库的方法(2)
2006/10/09 PHP
如何在WIN2K下安装PHP4.04
2006/10/09 PHP
PHP学习之字符串比较和查找
2011/04/17 PHP
PHP验证码函数代码(简单实用)
2013/09/29 PHP
PHP后端银联支付及退款实例代码
2017/06/23 PHP
PHP排序算法之希尔排序(Shell Sort)实例分析
2018/04/20 PHP
PHP pthreads v3下同步处理synchronized用法示例
2020/02/21 PHP
JavaScript入门教程(7) History历史对象
2009/01/31 Javascript
解析ScrollPic在ie8下只滚动一遍,然后变为空白 ie6,ie7,chrome,firefox正常
2013/06/26 Javascript
jQuery的选择器中的通配符使用介绍
2014/03/20 Javascript
jQuery实现tab选项卡效果的方法
2015/07/08 Javascript
javascript实现的闭包简单实例
2015/07/17 Javascript
javascript通过获取html标签属性class实现多选项卡的方法
2015/07/27 Javascript
如何屏蔽防止别的网站嵌入框架代码
2015/08/24 Javascript
Bootstrap按钮下拉菜单组件详解
2016/05/10 Javascript
JS编写函数实现对身份证号码最后一位的验证功能
2016/12/29 Javascript
Vue中v-for的数据分组实例
2018/03/07 Javascript
Vue项目引发的「过滤器」使用教程
2019/03/12 Javascript
Python 使用os.remove删除文件夹时报错的解决方法
2017/01/13 Python
Python操作Excel插入删除行的方法
2018/12/10 Python
Python Django切换MySQL数据库实例详解
2019/07/16 Python
手把手教你Python yLab的绘制折线图的画法
2019/10/23 Python
python 爬取马蜂窝景点翻页文字评论的实现
2020/01/20 Python
详解Python3中的 input() 函数
2020/03/18 Python
numpy库reshape用法详解
2020/04/19 Python
Django serializer优化类视图的实现示例
2020/07/16 Python
python 模拟登陆github的示例
2020/12/04 Python
美国名牌太阳镜折扣网站:Eyedictive
2017/05/15 全球购物
Omio意大利:全欧洲低价大巴、火车和航班搜索和比价
2017/12/02 全球购物
香蕉共和国加拿大官网:Banana Republic加拿大
2018/08/06 全球购物
VC++笔试题
2014/10/13 面试题
行政部主管岗位职责
2013/12/28 职场文书
法学专业自我鉴定
2014/02/05 职场文书
新党章的学习心得体会
2014/11/07 职场文书
北京英语导游词
2015/02/12 职场文书
【海涛dota解说】海涛小满开黑4v5被破两路翻盘潮汐第一视角解说
2022/04/01 DOTA