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 相关文章推荐
jQuery EasyUI API 中文文档 - Panel面板
Sep 30 Javascript
JavaScript高级程序设计(第3版)学习笔记9 js函数(下)
Oct 11 Javascript
使用jQuery中的when实现多个AJAX请求对应单个回调的例子分享
Apr 23 Javascript
Js实现手机发送验证码时按钮延迟操作
Jun 20 Javascript
简单对比分析JavaScript中的apply,call与this的使用
Dec 04 Javascript
js+canvas简单绘制圆圈的方法
Jan 28 Javascript
JS组件系列之Bootstrap table表格组件神器【终结篇】
May 10 Javascript
遍历js中对象的属性和值的实例
Nov 21 Javascript
js定时器实例分享
Dec 20 Javascript
AngularJS 验证码60秒倒计时功能的实现
Jun 05 Javascript
想用好React的你必须要知道的一些事情
Jul 24 Javascript
详解javascript void(0)
Jul 13 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运行环境(Apache配置、Mysql)搭建安装图文教程
2016/04/12 PHP
php短信接口代码
2016/05/13 PHP
将PHP的session数据存储到数据库中的代码实例
2016/06/24 PHP
基于PHP实现用户登录注册功能的详细教程
2020/08/04 PHP
用javascript实现画板的代码
2007/09/05 Javascript
在html页面中包含共享页面的方法
2008/10/24 Javascript
基于jquery的无缝循环新闻列表插件
2011/03/07 Javascript
用js编写的简单的计算器代码程序
2015/08/04 Javascript
JS jQuery使用正则表达式去空字符的简单实现代码
2017/05/20 jQuery
深入理解JS中Number(),parseInt(),parseFloat()三者比较
2018/08/24 Javascript
Vue.js的动态组件模板的实现
2018/11/26 Javascript
node.js express框架实现文件上传与下载功能实例详解
2019/10/15 Javascript
JavaScript中this的学习笔记及用法整理
2020/02/17 Javascript
js模拟实现烟花特效
2020/03/10 Javascript
详细分析vue表单数据的绑定
2020/07/20 Javascript
针对Vue路由history模式下Nginx后台配置操作
2020/10/22 Javascript
vue-router路由懒加载及实现的3种方式
2021/02/28 Vue.js
[13:18]《一刀刀一天》之DOTA全时刻21:详解TI新赛制 A队再露獠牙
2014/06/24 DOTA
[01:03:50]DOTA2-DPC中国联赛 正赛 CDEC vs DLG BO3 第二场 2月7日
2021/03/11 DOTA
[48:31]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第一场 2月2日
2021/03/11 DOTA
python基于queue和threading实现多线程下载实例
2014/10/08 Python
Python遍历目录的4种方法实例介绍
2015/04/13 Python
python中nan与inf转为特定数字方法示例
2017/05/11 Python
Python requests库用法实例详解
2018/08/14 Python
python利用Tesseract识别验证码的方法示例
2019/01/21 Python
Python发送手机动态验证码代码实例
2020/02/28 Python
Brora官网:英国领先的羊绒服装品牌
2019/08/28 全球购物
Lentiamo比利时:便宜的隐形眼镜
2020/02/14 全球购物
国际贸易专业个人求职信格式
2014/02/02 职场文书
大学生军训感想
2014/02/16 职场文书
学习十八大坚定理想信念心得体会
2014/03/11 职场文书
财产保全担保书范文
2014/04/01 职场文书
停车位租赁协议书
2014/09/24 职场文书
班子查摆四风个人对照检查材料思想汇报
2014/10/04 职场文书
原生JS中应该禁止出现的写法
2021/05/05 Javascript
nginx刷新页面出现404解决方案(亲测有效)
2022/03/18 Servers