头条新闻(vue项目实战)--搜索页

今天来实现搜索页面,首先分析一下搜索页面都要完成什么功能。第一肯定需要搜索框,在这里搜索框可以使用受控组件也可以使用非受控组件,看个人喜好。第二搜索框的下方应该有一个历史搜索记录,这个历史搜索记录可以存在localStorage中。然后我们还可以控制历史记录的显示与隐藏,当搜索框有内容时历史记录就不会展示。第三根据输入的关键字调用接口渲染数据。好了,接下来就开始实现吧!

<template>
  <div>
    <div>
      <van-search
        v-model="value"
        v-haha
        @input="input"
        shape="round"
        background="#007bff"
        placeholder="请输入搜索关键词"
        @search="search"
      >
        <template #left>
          <van-icon name="arrow-left" @click="jump" />
        </template>
      </van-search>
    </div>
    <!-- 搜索联想 -->
    <div class="sugg-list">
      <div
        class="sugg-item"
        v-for="(str, index) in suggestList"
        :key="index"
        v-html="lightFn(str, value)"
        @click="click(str)"
      >
        {{ str }}
      </div>
    </div>

    <!-- 搜索历史 -->
    <div class="search-history">
    <!-- 标题 -->
    <van-cell title="搜索历史">
        <!-- 使用 right-icon 插槽来自定义右侧图标 -->
        <template #right-icon>
<van-icon name="delete" class="search-icon" @click="clear"/>
        </template>
    </van-cell>

    <!-- 历史列表 -->
    <div class="history-list" @click="historyFn(str)" v-for="(str, index) in history" :key="index">
        <span class="history-item">{{str}}</span>
    </div>
    </div>
  </div>
</template>

<script>
import Vue from 'vue'
import { Search } from 'vant'
import { suggestAPI } from '@/api/index'
import { lightFn } from '@/utils/str'
Vue.use(Search)
export default {
  methods: {
    jump () {
      this.$router.back()
      console.log(this)
    },
    // 防抖
    input () {
      clearTimeout(this.timer)
      // 清空联想列表
      if (this.value.length === 0) {
        this.suggestList = []
      } else {
        this.timer = setTimeout(async () => {
          if (this.value.length === 0) return // 防止空内容触发下面逻辑
          const res = await suggestAPI({
            q: this.value
          })
          this.suggestList = res.data.data.options
        }, 500)
      }
    },
    lightFn,
    search () {
      // 搜索关键字保存到数组里
      if (this.value.length > 0) {
        this.history.push(this.value)
        setTimeout(() => {
          this.$router.push(`/searchResult/${this.value}`)
        })
      }
    },
    // 点击联想跳结果页
    click (str) {
      // 搜索关键字保存到数组里
      this.history.push(str)
      setTimeout(() => {
        this.$router.push(`/searchResult/${str}`)
      })
    },
    // 点击历史记录跳转结果页
    historyFn (str) {
      setTimeout(() => {
        this.$router.push(`/searchResult/${str}`)
      })
    },
    // 清空历史记录
    clear () {
      this.history = []
    }
  },
  data () {
    return {
      timer: null,
      value: '',
      suggestList: [], // 建议列表
      history: JSON.parse(localStorage.getItem('his')) || []
    }
  },
  watch: {
    history: {
      deep: true,
      handler () {
        // Set数组去重
        const theSet = new Set(this.history)
        // set类型转回数组
        const arr = Array.from(theSet)
        localStorage.setItem('his', JSON.stringify(arr)) // 将数据存储到本地
      }
    }
  }
}
</script>

<style lang="less" scoped>
/deep/.van-icon-arrow-left {
  color: white;
}
.sugg-list {
  .sugg-item {
    padding: 0 15px;
    border-bottom: 1px solid #f8f8f8;
    font-size: 14px;
    line-height: 50px;
    // 实现省略号的三行代码
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
.search-icon {
  font-size: 16px;
  line-height: inherit;
}
}
.history-list {
  padding: 0 10px;
  float: left;
  .history-item {
    display: inline-block;
    font-size: 12px;
    padding: 8px 14px;
    background-color: #efefef;
    margin: 10px 8px 0px 8px;
    border-radius: 10px;

  }
}
</style>

页面效果展示:

自动获取焦点的函数:

// 封装一个自定义指令,用于搜索框自动对焦

Vue.directive('haha', {

  inserted (el) {

    // nodeName获取原生DOM标签的标签名,注意标签名均是大写

    if (el.nodeName === 'TEXTAREA' || el.nodeName === 'INPUT') {

      el.focus()

    } else {

      const theInput = el.querySelector('input')

      const theTextArea = el.querySelector('textarea')

      if (theInput) theInput.focus()

      if (theTextArea) theTextArea.focus()

    }

  },

  update (el) {

    // 指令所在标签,被更新时触发

    if (el.nodeName === 'TEXTAREA' || el.nodeName === 'INPUT') {

      el.focus()

    } else {

      const theInput = el.querySelector('input')

      const theTextArea = el.querySelector('textarea')

      if (theInput) theInput.focus()

      if (theTextArea) theTextArea.focus()

    }

  }

})

搜索建议结果列表

<template>
 <div>
   <div>
    <van-nav-bar
  title="搜索结果"
  left-text="返回"
  left-arrow
  @click-left="$router.go(-1)"
/>
 <!-- <van-cell v-for="item in list" :key="item.art_id" :title="item.title"  /> -->
  </div>
  <van-list
  v-model="loading"
  :finished="finished"
  finished-text="没有更多了"
  :immediate-check="false"
  @load="onLoad"
>
  <div>
    <ArticleItem v-for="item in list" :key="item.art_id" :artObj="item" :isShow="false" @click.native="click(item.art_id)"/>
  </div>
  </van-list>
 </div>
</template>

<script>
import Vue from 'vue'
import { NavBar, Cell, CellGroup, List } from 'vant'
import { searchResultAPI } from '@/api/index'
import ArticleItem from '@/components/ArticleItem.vue'
Vue.use(Cell)
Vue.use(List)
Vue.use(CellGroup)
Vue.use(NavBar)
export default {
  async created () {
    const res = await searchResultAPI({
      q: this.$route.params.id
    })
    console.log(res)
    this.list = res.data.data.results
  },
  data () {
    return {
      list: [], // 结果列表
      loading: false, // 加载状态
      finished: false // 完成状态
    }
  },
  components: {
    ArticleItem
  },
  methods: {
    async  onLoad () {
      if (this.list.length > 0) {
        const res = await searchResultAPI({
          q: this.$route.params.id
        })
        console.log(res)
        if (res.data.data.results === 0) {
          this.finished = true
          return
        }
        this.list = [...this.list, ...res.data.data.results]
        this.loading = false
      }
    },
    click (id) {
      // 点击跳转到详情页
      this.$router.push({
        path: `/detail?art_id=${id}`
      })
    }
  }
}
</script>

<style>

</style>

 

用到的接口请求:

// 获取搜索结果

export const searchResultAPI = ({ q }) => request({

  url: '/v1_0/search',

  params: {

    q: q

  }

})

// 获取搜索联想结果

export const suggestAPI = ({ q }) => request({

  url: '/v1_0/suggestion',

  params: {

    q: q

  }

})

好了,到这里搜索页功能基本完成。 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zwq8023520

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值