【筆記】實戰mpvue2.0多端小程序框架——搜索模塊(下)


搜索開發 | 「小慕讀書」官網


一、搜索模塊組件集成

清除src\pages\search\search.vue頁面之前的測試代碼,修改爲:

<template>
  <div>
    <SearchBar
      :focus="searchFocus"
      @onChange="onChange"
    />
    <TagGroup
      header-text="熱門搜索"
      btn-text="換一批"
      :value="hotSearch"
      @onBtnClick="changeHotSearch"
      @onTagClick="showBookDetail"
      v-if="hotSearch.length > 0 && !showList"
    />
    <TagGroup
      header-text="歷史搜索"
      btn-text="清空"
      :value="historySearch"
      @onBtnClick="clearHistorySearch"
      @onTagClick="searchKeyWord"
      v-if="historySearch.length > 0 && !showList"
    />
    <SearchList
      :data="searchList"
      v-if="showList"
    />
  </div>
</template>

<script>

import SearchBar from '../../components/home/SearchBar'
import SearchList from '../../components/search/SearchList'
export default {
  components: {SearchList, SearchBar},
  computed: {
    showList() {
      const keys = Object.keys(this.searchList)
      return keys.length > 0
    }
  },
  data() {
    return {
      hotSearch: [],
      historySearch: [],
      searchList: {},
      searchFocus: true
    }
  },
  methods: {
    changeHotSearch() {
      console.log('change hot search')
    },
    showBookDetail() {
      console.log('showBookDetail')
    },
    clearHistorySearch() {
      console.log('clearHistorySearch')
    },
    searchKeyWord() {
      console.log('searchKeyWord')
    },
    onChange(keyword) {
      console.log(keyword)
    }
  }
}
</script>

<style lang="scss" scoped>
</style>

這裏的顯示與否取決於內容是否爲空:

  • 數組:Array.length > 0
  • 對象:Object.keys.length > 0

二、搜索模塊API對接

src\api\index.js中新增搜索接口調用方法

export function search(params) {
  return get(`${API_URL}/book/search`, params)
}

修改src\components\search\SearchList.vue:

<template>
  <div class="search-list-wrapper">
    <SearchItem
      icon="apps-o"
      :title="category"
      sub-title="Category"
      @onClick="showList(category.title, 'category')"
      v-if="category"
    />
    <SearchItem
      icon="user-o"
      :title="author"
      sub-title="Author"
      @onClick="showList(author.title, 'author')"
      v-if="author"
    />
    <SearchItem
      icon="newspaper-o"
      :title="publisher"
      sub-title="Publisher"
      @onClick="showList(publisher.title, 'publisher')"
      v-if="publisher"
    />
    <SearchTable :data="data.book" @onClick="onBookClick"/>
  </div>
</template>

<script>
import SearchItem from './SearchItem'
import SearchTable from './SearchTable'
export default {
  components: {SearchTable, SearchItem},
  props: {
    data: Object
  },
  computed: {
    category() {
      if (this.data && this.data.category && this.data.category.length > 0) {
        return this.data.category[0].categoryText
      } else {
        return null
      }
    },
    author() {
      if (this.data && this.data.author && this.data.author.length > 0) {
        return this.data.author[0].author
      } else {
        return null
      }
    },
    publisher() {
      if (this.data && this.data.publisher && this.data.publisher.length > 0) {
        return this.data.publisher[0].publisher
      } else {
        return null
      }
    }
  },
  methods: {
    showList(text, key) {
      console.log(text, key)
    },
    onBookClick(book) {
      console.log(book)
    }
  }
}
</script>

<style lang="scss" scoped>
</style>

修改src\components\search\SearchItem.vue的部分樣式:

.search-item-info {
  width: 80%;
  ...
  .search-item-title {
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    ...
  }
  ...

修改src\pages\search\search.vue:

  • 修改onChange方法並新增onSearch方法
    onChange(keyword) {
      if (!keyword || keyword.trim().length === 0) {
        this.searchList = {}
        return
      }
      this.onSearch(keyword)
    },
    onSearch(keyword) {
      search({
        keyword, openId: this.openId
      }).then(res => {
        this.searchList = res.data.data
      })
    }
  • 新增data:openId: ''並通過本地緩存獲取:
  mounted() {
    this.openId = getStorageSync('openId')
  },

預覽:
在這裏插入圖片描述

三、熱門搜索API對接

在src\pages\search\search.vue爲SearchBar新增onClear,用來在點擊清除按鈕時進行數據清除:

<SearchBar
  :focus="searchFocus"
  @onChange="onChange"
  @onClear="onClear"
/>
    onClear() {
      this.searchList = {}
    }

src\api\index.js中新增熱門搜索接口調用方法

export function hotSearch(params) {
  return get(`${API_URL}/book/hot-search`, params)
}

修改src\pages\index\index.vue中跳轉搜索頁面的事件,使在跳轉搜索頁面時攜帶參數hotSearch:

onSearchBarClick() {
  this.$router.push({
    path: '/pages/search/main',
    query: {
      hotSearch: this.hotSearch
    }
  })
},

src\pages\search\search.vue頁面加載時調用hotSearch接口方法,並拿到首頁傳來的hotSearch賦給搜索頁面新增的datahotSearchKeyword

  data() {
    return {
      hotSearch: [],
      historySearch: [],
      hotSearchKeyword: '',
      searchList: {},
      searchFocus: true,
      openId: ''
    }
  },
  mounted() {
    this.openId = getStorageSync('openId')
    hotSearch().then(res => {
      this.hotSearch = res.data.data
    })
    this.hotSearchKeyword = this.$route.query.hotSearch
  },

在計算屬性中創建hotSearchArray方法來獲取熱門搜索標題,並在中調用

hotSearchArray() {
  const _hotSearch = []
  this.hotSearch.forEach(obj => _hotSearch.push(obj.title))
  return _hotSearch
}

修改src\components\base\TagGroup.vue的樣式

<style lang="scss" scoped>
  .tag-group-wrapper {
  	...
    width: 100%;
    padding-bottom: 10px;
    ...
    .tag-goup {
      width: 100%;
      box-sizing: border-box;
      ...
      .tag-group-inner {
        max-width: 100%;
        box-sizing: border-box;
        ...
      }
    }
  }
</style>

修改src\components\base\Tag.vue的樣式:

<style lang="scss" scoped>
  .tag-wrapper {
    width: 100%;
    box-sizing: border-box;
    ...
    .tag {
      width: 100%;
      ...
    }
  }
</style>

這樣熱門搜索就可以正常顯示了
在這裏插入圖片描述
修改showBookDetail方法

showBookDetail(text, index) {
  console.log('showBookDetail', index, text)
},

四、歷史搜索和熱搜更新功能開發

在src\pages\search\search.vue中新增onConfirm方法:

    onConfirm(keyword) {
      // 1.判斷是否有搜索關鍵詞
      if (!keyword || keyword.trim().length === 0) {
        // 如果沒有,則獲取熱門搜索詞,通過熱門搜索詞發起請求
        keyword = this.hotSearchKeyword
        this.$refs.searchBar.setValue(keyword)
      } else {
        // 如果有,使用搜索關鍵詞發起請求
      }
      this.onSearch(keyword)
      // 2.將搜索結果寫入歷史搜索
      if (!this.historySearch.includes(keyword)) {
        this.historySearch.push(keyword)
        // 將歷史搜索寫入緩存
        setStorageSync(KEY_HISTORY_SEARCH, this.historySearch)
      }
      // 3.將搜索框失去焦點
      this.searchFocus = false
    }

清空歷史記錄:

    clearHistorySearch() {
      this.historySearch = []
      setStorageSync(KEY_HISTORY_SEARCH, [])
    },

在搜索頁面加載時從緩存中拿到歷史搜索記錄:

  mounted() {
    this.openId = getStorageSync('openId')
    hotSearch().then(res => {
      // 從接口中拿到熱門搜索關鍵詞內容,並賦值本頁面
      this.hotSearch = res.data.data
    })
    // 將首頁傳來的參數(熱門搜索關鍵詞)賦給本頁面的熱門搜索關鍵詞,完成參數傳遞
    this.hotSearchKeyword = this.$route.query.hotSearch
    // 若緩存中有歷史搜索記錄,頁面渲染完畢就拿到,否則置空
    this.historySearch = getStorageSync(KEY_HISTORY_SEARCH) || []
  },

爲了方便,可以定義一個historySearch常量,然後在需要的地方引用:

const KEY_HISTORY_SEARCH = 'historySearch'

換一批其實也就是重新從hotSearch接口拿一遍數據:

hotSearch().then(res => {
  // 從接口中拿到熱門搜索關鍵詞內容,並賦值本頁面
  this.hotSearch = res.data.data
})

點擊歷史搜索的tag觸發searchKeyWord事件:

searchKeyWord(text) {
  this.$refs.searchBar.setValue(text)
  this.onSearch(text)
},

預覽:
在這裏插入圖片描述

五、觸底自動刷新功能開發

頁面滑動時搜索框失去焦點(調用的是一個頁面的生命週期函數):

  onPageScroll() {
    if (this.searchFocus) {
      this.searchFocus = false
    }
  },

新增datapage: 1,在onSearch給search接口傳參時傳入:

onSearch(keyword) {
  search({
    keyword, openId: this.openId, page: this.page
  }).then(res => {
    this.searchList = res.data.data
  })
},

在src\api\wechat.js中新增

export function showToast(title) {
  mpvue.showToast({
    title,
    duration: 2000
  })
}

到達頁面底部觸發頁面周期函數onReachBottom:

  onReachBottom() {
    if (this.showList) {
      // 再加載20條數據,即一頁數據
      this.page++
      // 拿到搜索關鍵詞
      const searchWord = this.$refs.searchBar.getValue()
      // 這時就不能直接調用onSearch方法,否則整個頁面都會加載新的一頁數據來替換掉原有頁數據
      search({
        keyword: searchWord,
        openId: this.openId,
        page: this.page
      }).then(res => {
        // 通過解構的方式拿到數據
        const { book } = res.data.data
        if (book && book.length > 0) {
          // 這裏使用拓展運算符可以把book數組裏面的元素散着push進去而不是整個數組push進去
          this.searchList.book.push(...book)
        } else {
          showToast('沒有更多數據了')
        }
      })
    }
  },

記得要在關鍵詞改變時初始化page爲1

    onChange(keyword) {
      ...
      this.page = 1
      this.searchFocus = true	// 爲了防止焦點丟失,建議加上
      ...
    },

在mounted中也是如此:

  mounted() {
    ...
    this.page = 1
    ...
  },
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章