Vue 2.0 高级实战-开发移动端音乐WebApp 课程笔记(第十章 搜索页面开发)
搜索页面search-box组件开发
添加基础组件search-box
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70<template>
<div class="search-box">
<i class="icon-search"></i>
<input ref="query" v-model="query" class="box" :placeholder="placeholder"/>
<i @click="clear" v-show="query" class="icon-dismiss"></i>
</div>
</template>
<script>
import {debounce} from 'common/js/util'
export default {
props: {
placeholder: {
type: String,
default: '搜索歌曲、歌手'
}
},
data() {
return {
query: ''
}
},
methods: {
clear() {
this.query = ''
},
setQuery(query) {
this.query = query
},
blur() {
this.$refs.query.blur()
}
},
created() {
// 监听query
this.$watch('query', debounce((newQuery) => {
this.$emit('query', newQuery)
}, 200))
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/variable"
.search-box
display: flex
align-items: center
box-sizing: border-box
width: 100%
padding: 0 6px
height: 40px
background: $color-highlight-background
border-radius: 6px
.icon-search
font-size: 24px
color: $color-background
.box
flex: 1
margin: 0 5px
line-height: 18px
background: $color-highlight-background
color: $color-text
font-size: $font-size-medium
&::placeholder
color: $color-text-d
.icon-dismiss
font-size: 16px
color: $color-background
</style>添加业务组件search
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68<template>
<div class="search">
<div class="search-box-wrapper">
<search-box ref="searchBox"></search-box>
</div>
</div>
</template>
<script>
import SearchBox from 'base/search-box/search-box'
export default {
components: {
SearchBox
}
}
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/variable"
@import "~common/stylus/mixin"
.search
.search-box-wrapper
margin: 20px
.shortcut-wrapper
position: fixed
top: 178px
bottom: 0
width: 100%
.shortcut
height: 100%
overflow: hidden
.hot-key
margin: 0 20px 20px 20px
.title
margin-bottom: 20px
font-size: $font-size-medium
color: $color-text-l
.item
display: inline-block
padding: 5px 10px
margin: 0 20px 10px 0
border-radius: 6px
background: $color-highlight-background
font-size: $font-size-medium
color: $color-text-d
.search-history
position: relative
margin: 0 20px
.title
display: flex
align-items: center
height: 40px
font-size: $font-size-medium
color: $color-text-l
.text
flex: 1
.clear
extend-click()
.icon-clear
font-size: $font-size-medium
color: $color-text-d
.search-result
position: fixed
width: 100%
top: 178px
bottom: 0
</style>
搜索页面热门搜索数据抓取和应用
1 | export function getHotKey() { |
1 | <template> |
搜索页面suggest组件开发
添加接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23export function search(query, page, zhida, perpage) {
const url = 'https://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp'
const data = Object.assign({}, commonParams, {
w: query,
p: page,
perpage,
n: perpage,
catZhida: zhida ? 1 : 0,
zhidaqu: 1,
t: 0,
flag: 1,
ie: 'utf-8',
sem: 1,
aggr: 0,
remoteplace: 'txt.mqq.all',
uin: 0,
needNewCode: 1,
platform: 'h5'
})
return jsonp(url, data, options)
}实现上拉刷新, 扩展scroll组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14// props
pullup: {
type: Boolean,
default: false
}
// methods
// 当滚动结束时离底部还有50px时, 派发事件scrollToEnd
if (this.pullup) {
this.scroll.on('scrollEnd', () => {
if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
this.$emit('scrollToEnd')
}
})
}suggest中添加方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18searchMore() {
if (!this.hasMore) {
return
}
this.page++
search(this.query, this.page, this.showSinger, perpage).then((res) => {
if (res.code === ERR_OK) {
this.result = this.result.concat(this._genResult(res.data))
this._checkMore(res.data)
}
})
},
_checkMore(data) {
const song = data.song
if (!song.list.length || (song.curnum + song.curpage * perpage) >= song.totalnum) {
this.hasMore = false
}
}添加actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44export const insertSong = function ({commit, state}, song) {
let playlist = state.playlist.slice()
let sequenceList = state.sequenceList.slice()
let currentIndex = state.currentIndex
// 记录当前歌曲
let currentSong = playlist[currentIndex]
// 查找当前列表中是否有待插入的歌曲并返回其索引
let fpIndex = findIndex(playlist, song)
// 因为是插入歌曲,所以索引+1
currentIndex++
// 插入这首歌到当前索引位置
playlist.splice(currentIndex, 0, song)
// 如果已经包含了这首歌
if (fpIndex > -1) {
// 如果当前插入的序号大于列表中的序号
if (currentIndex > fpIndex) {
playlist.splice(fpIndex, 1)
currentIndex--
} else {
playlist.splice(fpIndex + 1, 1)
}
}
// 应该插入的位置
let currentSIndex = findIndex(sequenceList, currentSong) + 1
let fsIndex = findIndex(sequenceList, song)
sequenceList.splice(currentSIndex, 0, song)
if (fsIndex > -1) {
if (currentSIndex > fsIndex) {
sequenceList.splice(fsIndex, 1)
} else {
sequenceList.splice(fsIndex + 1, 1)
}
}
commit(types.SET_PLAYLIST, playlist)
commit(types.SET_SEQUENCE_LIST, sequenceList)
commit(types.SET_CURRENT_INDEX, currentIndex)
commit(types.SET_FULL_SCREEN, true)
commit(types.SET_PLAYING_STATE, true)
}添加通用组件no-result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34<template>
<div class="no-result">
<div class="no-result-icon"></div>
<p class="no-result-text">{{title}}</p>
</div>
</template>
<script type="text/ecmascript-6">
export default {
props: {
title: {
type: String,
default: ''
}
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/variable"
@import "~common/stylus/mixin"
.no-result
text-align: center
.no-result-icon
width: 86px
height: 90px
margin: 0 auto
bg-image('no-result')
background-size: 86px 90px
.no-result-text
margin-top: 30px
font-size: $font-size-medium
color: $color-text-d
</style>在common/js/util.js中添加节流函数
1
2
3
4
5
6
7
8
9
10
11
12export function debounce(func, delay) {
let timer
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
func.apply(this, args)
}, delay)
}
}
搜索页面搜索结果保存功能实现
suggest组件selectItem方法派发事件
1
this.$emit('select', item)
添加commom/js/cache.js 缓存操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function insertArray(arr, val, compare, maxLen) {
const index = arr.findIndex(compare)
if (index === 0) {
return
}
// 存在元素删除后插入, 没有直接插入
if (index > 0) {
arr.splice(index, 1)
}
arr.unshift(val)
if (maxLen && arr.length > maxLen) {
arr.pop()
}
}
export function saveSearch(query) {
let searches = storage.get(SEARCH_KEY, [])
insertArray(searches, query, (item) => {
return item === query
}, SEARCH_MAX_LEN)
storage.set(SEARCH_KEY, searches)
return searches
}添加actions
1
2
3export const saveSearchHistory = function ({commit}, query) {
commit(types.SET_SEARCH_HISTORY, saveSearch(query))
}
搜索页面search-list 组件功能实现
添加组件search-list
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48<template>
<div class="search-list" v-show="searches.length">
<ul>
<li class="search-item" @click="selectItem(item)" v-for="item in searches">
<span class="text">{{item}}</span>
<span class="icon" @click.stop="deleteOne(item)">
<i class="icon-delete"></i>
</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
searches: {
type: Array,
default: []
}
},
methods: {
selectItem(item) {
this.$emit('select', item)
},
deleteOne(item) {
this.$emit('delete', item)
}
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
@import "~common/stylus/variable"
.search-list
.search-item
display: flex
align-items: center
height: 40px
.text
flex: 1
color: $color-text-l
.icon
extend-click()
.icon-delete
font-size: $font-size-small
color: $color-text-d
</style>cache添加方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14function deleteFromArray(arr, compare) {
const index = arr.findIndex(compare)
if (index > -1) {
arr.splice(index, 1)
}
}
export function deleteSearch(query) {
let searches = storage.get(SEARCH_KEY, [])
deleteFromArray(searches, (item) => {
return item === query
})
storage.set(SEARCH_KEY, searches)
return searches
}添加action
1
2
3export const deleteSearchHistory = function ({commit}, query) {
commit(types.SET_SEARCH_HISTORY, deleteSearch(query))
}添加cache
1
2
3
4export function clearSearch() {
storage.remove(SEARCH_KEY)
return []
}添加actions
1
2
3export const clearSearchHistory = function ({commit}) {
commit(types.SET_SEARCH_HISTORY, clearSearch())
}
搜索页面confirm 组件功能实现
1 | <template> |
搜索页面剩余功能实现
给滚动组件传值, 一个复合属性1
2
3
4
5computed: {
shortcut() {
return this.hotKey.concat(this.searchHistory)
},
}