Skip to content
This repository was archived by the owner on May 11, 2021. It is now read-only.

Commit 621dd0d

Browse files
committed
更新 - 无限滚动
1 parent 2588b2c commit 621dd0d

File tree

7 files changed

+318
-8
lines changed

7 files changed

+318
-8
lines changed

build/webpack.dev.conf.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,22 @@ const devWebpackConfig = merge(baseWebpackConfig, {
9797
throw Error(`Proxy failed, ${err}`)
9898
})
9999
})
100+
101+
// search
102+
app.get('/api/search', (req, res) => {
103+
const url = 'https://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp'
104+
105+
axios.get(url, {
106+
headers: {
107+
referer: 'https://m.y.qq.com/'
108+
},
109+
params: req.query
110+
}).then(response => {
111+
res.json(response.data)
112+
}, err => {
113+
throw Error(`Proxy failed, ${err}`)
114+
})
115+
})
100116
},
101117

102118
clientLogLevel: 'warning',

src/api/config.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ export const options = {
1010
param: 'jsonpCallback'
1111
}
1212

13-
// 部分 ajax 接口接受 jsonp 请求
14-
1513
export const ERR_OK = 0
1614

15+
// 部分 ajax 接口接受 jsonp 请求
16+
1717
export const RECOMMEND_URL = 'https://c.y.qq.com/musichall/fcgi-bin/fcg_yqqhomepagerecommend.fcg'
1818

1919
export const PLAYLIST_URL = '/api/getPlayList'
@@ -36,3 +36,8 @@ export const RANKING_LIST_URL = 'https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.
3636
export const FULL_RANKING_LIST_URL = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_toplist_cp.fcg'
3737

3838
export const HOTKEY_URL = 'https://c.y.qq.com/splcloud/fcgi-bin/gethotkey.fcg'
39+
40+
// 移动端 search url
41+
// export const SEARCH_URL = 'https://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp'
42+
43+
export const SEARCH_URL = '/api/search'

src/api/the-search.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import jsonp from 'common/js/jsonp'
2-
import { HOTKEY_URL, commonParams, options } from './config'
2+
import { HOTKEY_URL, SEARCH_URL, commonParams, options } from './config'
3+
import axios from 'axios'
34

45
export function getHotKey () {
56
const data = {
@@ -13,3 +14,31 @@ export function getHotKey () {
1314

1415
return jsonp(HOTKEY_URL, data, options)
1516
}
17+
18+
export function searchKey (key, page, zhida) {
19+
const data = {
20+
...commonParams,
21+
w: key,
22+
p: page, // 第几页
23+
catZhida: zhida ? 1 : 0, // 是否解锁显示歌手
24+
uin: 0,
25+
format: 'json',
26+
platform: 'yqq',
27+
needNewCode: 1,
28+
zhidaqu: 1,
29+
t: 0,
30+
flag: 1,
31+
ie: 'utf-8',
32+
sem: 1,
33+
aggr: 0,
34+
perpage: 20,
35+
n: 20,
36+
remoteplace: 'txt.mqq.all',
37+
_: +new Date()
38+
}
39+
40+
// return jsonp(SEARCH_URL, data, options)
41+
return axios.get(SEARCH_URL, {
42+
params: data
43+
}).then(res => res.data)
44+
}

src/base/base-scroll.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,25 @@ export default {
1212
type: Number,
1313
default: 1
1414
},
15+
1516
click: {
1617
type: Boolean,
1718
default: true
1819
},
20+
1921
data: { // 用于跟踪父组件数据对象的变化,即 ajax 是否返回了 slot 插槽中所需的数据,即确定刷新滚动组件的时机
2022
type: Array,
2123
default: null
2224
},
25+
2326
listenScroll: {
2427
type: Boolean,
2528
default: false
29+
},
30+
31+
pullUp: { // 是否开启上拉刷新
32+
type: Boolean,
33+
default: false
2634
}
2735
},
2836
methods: {
@@ -34,11 +42,19 @@ export default {
3442
})
3543
3644
if (this.listenScroll) {
37-
let that = this
45+
let that = this // 若不设置,那么当外部调用时,this 将不是指向本组件
3846
this.scroll.on('scroll', pos => { // 监听原生滚动事件,派发一个滚动事件
3947
that.$emit('scroll', pos)
4048
})
4149
}
50+
51+
if (this.pullUp) {
52+
this.scroll.on('scrollEnd', () => {
53+
if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
54+
this.$emit('scrollToEnd') // 派发滚动到达底部事件
55+
}
56+
})
57+
}
4258
},
4359
4460
scrollTo () {

src/base/base-search-box.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export default {
1818
methods: {
1919
cleanInputBox () {
2020
this.query = ''
21+
},
22+
23+
setQuery (query) {
24+
this.query = query
2125
}
2226
},
2327
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<template>
2+
<BaseScroll
3+
class="search-result-wrapper"
4+
:data="searchResult" :pullUp="pullUp"
5+
@scrollToEnd="scrollToEnd"
6+
>
7+
<ul class="result-list">
8+
<li class="result-item" v-for="(item, index) of searchResult" :key="index">
9+
<div class="item-icon">
10+
<i :class="getIconClass(item)"></i>
11+
</div>
12+
<div class="item-name">
13+
<p class="text" v-html="getDisplayName(item)"></p>
14+
</div>
15+
</li>
16+
</ul>
17+
</BaseScroll>
18+
</template>
19+
20+
<script>
21+
import BaseScroll from 'base/base-scroll'
22+
import { searchKey } from 'api/the-search'
23+
import { ERR_OK } from 'api/config'
24+
import { createSong } from 'common/js/normalize-song'
25+
26+
const TYPE_SINGER = 'singer'
27+
let timer = 0
28+
29+
export default {
30+
props: {
31+
query: {
32+
type: String,
33+
default: ''
34+
},
35+
36+
showArtist: {
37+
type: Boolean,
38+
default: true
39+
}
40+
},
41+
42+
data () {
43+
return {
44+
page: 1,
45+
searchResult: [],
46+
pullUp: true
47+
}
48+
},
49+
50+
methods: {
51+
scrollToEnd () {
52+
this.page++
53+
this.search()
54+
},
55+
56+
getDisplayName (item) {
57+
if (item.type === TYPE_SINGER) {
58+
return item.singername
59+
} else {
60+
return `${item.name} - ${item.artist}`
61+
}
62+
},
63+
64+
getIconClass (item) {
65+
if (item.type === TYPE_SINGER) {
66+
return 'icon-mine'
67+
} else {
68+
return 'icon-music'
69+
}
70+
},
71+
72+
search () {
73+
searchKey(this.query, this.page, this.showArtist).then(res => {
74+
if (res.code === ERR_OK) {
75+
this.searchResult = [...this.searchResult, ...this._normalizeResult(res.data)]
76+
} else {
77+
throw new Error('Check ERR_OK failed')
78+
}
79+
})
80+
},
81+
82+
_normalizeResult (data) {
83+
let ret = []
84+
if (data.zhida && data.zhida.singermid) {
85+
ret.push({
86+
...data.zhida,
87+
type: TYPE_SINGER
88+
})
89+
}
90+
91+
if (data.song) {
92+
ret = [...ret, ...this._normalizeSongs(data.song.list)]
93+
}
94+
95+
return ret
96+
},
97+
98+
_normalizeSongs (list) {
99+
let ret = []
100+
list.forEach(item => {
101+
if (item.songmid && item.albumid) {
102+
ret.push(createSong(item))
103+
}
104+
})
105+
106+
return ret
107+
}
108+
},
109+
110+
watch: {
111+
query (newQuery) {
112+
if (!newQuery) { // 清空输入框时不请求,且初始化组件数据对象
113+
this.page = 0
114+
this.searchResult = []
115+
return
116+
}
117+
118+
if (timer) {
119+
clearTimeout(timer)
120+
timer = 0
121+
}
122+
timer = setTimeout(() => {
123+
this.search()
124+
}, 100)
125+
}
126+
},
127+
128+
components: {
129+
BaseScroll
130+
}
131+
}
132+
</script>
133+
134+
<style lang="scss" scoped>
135+
@import '~scss/variables';
136+
@import '~scss/mixin';
137+
138+
.search-result-wrapper {
139+
position: fixed;
140+
top: 178px;
141+
bottom: 0;
142+
width: 100%;
143+
overflow: hidden;
144+
font-size: 0;
145+
.result-list {
146+
padding: 0 30px;
147+
.result-item {
148+
display: flex;
149+
align-items: center;
150+
padding-bottom: 20px;
151+
.item-icon {
152+
flex: 0 30px;
153+
width: 30px;
154+
.icon-music, .icon-mine {
155+
font-size: $font-size-medium;
156+
color: $color-text-l;
157+
}
158+
}
159+
.item-name {
160+
flex: 1;
161+
overflow: hidden;
162+
.text {
163+
@include ellipsis();
164+
font-size: $font-size-medium;
165+
color: $color-text-l;
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
</style>

0 commit comments

Comments
 (0)