音乐页面

第一章:搜索栏
一、界面布局
音乐 tabBar 页整体背景颜色为 #fafafa。所以改变 van-search 组件背景颜色为 #fafafa。
<van-search
shape="round"
background="#fafafa"
placeholder="请输入搜索关键词"
bind:tap="onSearchClick" />但想让搜索框内的背景颜色为白色,怎么办?解决办法①: 使用调试工具找到类名,覆盖样式;解决办法②: 全局变量;解决办法③: 外部样式类。
page {
--search-background-color: #fff; /* 这里采用全局变量方式覆盖默认样式 */
--search-padding: 10px 0;
box-sizing: border-box;
padding: 0 24rpx;
background-color: #fafafa;
}
第二章:轮播图
一、获取数据
1. 封装 API
\services\modules\music.js
export function getMusicBanner(type = 0) {
return ddfRequest.get("/banner", {
type
})
}2. 调用接口
\pages\main-music\main-music.js
onLoad(options) {
this.fetchMusicBanner()
}
async fetchMusicBanner() {
const res = await getMusicBanner()
const arr = res.banners.filter(item => item.typeTitle !== "广告")
this.setData({ banners: arr })
},二、展示数据
1. 基本展示
\pages\main-music\main-music.wxml
<!-- 轮播图 -->
<swiper
class="banner"
circular
indicator-dots
autoplay
wx:if="{{banners.length}}"
>
<block wx:for="{{banners}}" wx:key="targetId">
<swiper-item class="item">
<image
class="banner-image"
src="{{item.imageUrl}}"
mode="widthFix"
/>
</swiper-item>
</block>
</swiper>mode 属性设置为 widthFix 表示宽度不变,高度自动变化,保持原图宽高比不变。
.banner {
border-radius: 12rpx;
overflow: hidden;
}
.banner-image {
width: 100%;
}2. 轮播图高度怎么计算?
image 组件使用 mode 属性值为 widthFix,发现图片高度小于轮播图默认高度。

使用微信小程序提供的 API 来获取组件(图片)大小、位置等信息。
1)封装工具函数 \utils\query-select.js
export default function querySelect(selector) {
return new Promise(resolve => {
const query = wx.createSelectorQuery()
query.select(selector).boundingClientRect()
query.exec((res) => {
resolve(res)
})
})
}2)在 image 组件添加 bindload="onBannerImageLoad"。bindload 属性是当图片载入完毕时触发。
// \pages\main-music\main-music.js
onBannerImageLoad(event) {
querySelectThrottle(".banner-image").then(res => {
this.setData({ bannerHeight: res[0].height })
})
},3)高度存到 data 中了,这样就可以设置 swiper 组件的高度了。给 swiper 组件添加 style="height: px;"。
轮播图图片有多个,这样 image 组件的 onBannerImageLoad 回调就会执行多次。这里要使用节流函数。
第三章:推荐歌曲

一、数据获取
1. 封装 API
接口文档:获取歌单详情
推荐歌曲其实是网易云音乐的热歌歌单。

\services\modules\music.js
export function getPlaylistDetail(id) {
return ddfRequest.get("/playlist/detail", {
id
})
}2. 数据共享
1)安装
npm install hy-event-store别忘记构建 npm。
2)使用
\store\recommendStore.js
import { HYEventStore } from "hy-event-store"
const recommendStore = new HYEventStore({
state: {
// 要共享的数据
recommendSongInfo: {}
},
actions: {
// 更改 state 中的数据,也可以发起异步任务
fetchRecommendSongsAction(ctx) {
const res = await wx.request( /* ...... */ )
ctx.recommendSongInfo = res.playlist
}
}
})
export default recommendStore\pages\main-music\main-music.js
import recommendStore from '../store/recommendStore'
// 监听所需数据的改变,执行回调
recommendStore.onState("recommendSongInfo", this.handleRecommendSongs)
// 派发事件,发起网络请求
recommendStore.dispatch("fetchRecommendSongsAction")
// ====================== 从Store中获取数据 ======================
handleRecommendSongs(value) {
if (!value.tracks) return
this.setData({ recommendSongs: value.tracks.slice(0, 6) })
},二、界面展示
第四章:热门 & 华语歌单

一、数据获取
1. 封装 API
接口文档:获取不同分类下的歌单
热门 & 华语歌单取自网易云音乐的热门全部歌单。

\services\modules\music.js
export function getSongMenuList(cat = "全部", limit = 6, offset = 0) {
return ddfRequest.get("/top/playlist", {
cat,
limit,
offset
})
}2. 获取数据
\pages\main-music\main-music.js
import { getSongMenuList } from '../../services/index'
onLoad(options) {
// ......
this.fetchSongMenuList()
}
async fetchSongMenuList() {
getSongMenuList().then(res => {
this.setData({ hotMenuList: res.playlists })
})
getSongMenuList("华语").then(res => {
this.setData({ recMenuList: res.playlists })
})
},二、界面展示
热门 & 华语歌单观察页面样式发现一模一样,所以封装为组件。
第五章:榜单

一、数据获取
接口文档:获取歌单详情
榜单其实是网易云音乐的不同分类歌单。

1. 封装 API
\services\modules\music.js
依旧是之前的歌单详情信息获取 API。
export function getPlaylistDetail(id) {
return ddfRequest.get("/playlist/detail", {
id
})
}2.存到 store
在单击榜单每个分类的时候,会跳转到歌曲详情页,也展示的是 /playlist/detail 接口信息,只不过是展示多少的问题。音乐页和歌曲详情页都要使用同样的数据,所以数据需要共享。
\store\rankingStore.js
import { HYEventStore } from "hy-event-store"
import { getPlaylistDetail } from "../services/index"
export const rankingsMap = {
newRanking: 3779629,
originRanking: 2884035,
upRanking: 19723756
}
const rankingStore = new HYEventStore({
state: {
newRanking: {},
originRanking: {},
upRanking: {}
},
actions: {
fetchRankingDataAction(ctx) {
for (const key in rankingsMap) {
const id = rankingsMap[key]
getPlaylistDetail(id).then(res => {
ctx[key] = res.playlist
})
}
}
}
})
export default rankingStore3. 页面拿到数据
onLoad(options) {
rankingStore.onState("newRanking", this.handleNewRanking)
rankingStore.onState("originRanking", this.handleOriginRanking)
rankingStore.onState("upRanking", this.handleUpRanking)
rankingStore.dispatch("fetchRankingDataAction")
}
handleNewRanking(value) {
if (!value.name) return
this.setData({ isRankingData: true })
const newRankingInfos = { ...this.data.rankingInfos, newRanking: value }
this.setData({ rankingInfos: newRankingInfos })
},
handleOriginRanking(value) {
if (!value.name) return
this.setData({ isRankingData: true })
const newRankingInfos = { ...this.data.rankingInfos, originRanking: value }
this.setData({ rankingInfos: newRankingInfos })
},
handleUpRanking(value) {
if (!value.name) return
this.setData({ isRankingData: true })
const newRankingInfos = { ...this.data.rankingInfos, upRanking: value }
this.setData({ rankingInfos: newRankingInfos })
},
onUnload() {
rankingStore.offState("newRanking", this.handleNewRanking)
rankingStore.offState("originRanking", this.handleOriginRanking)
rankingStore.offState("upRanking", this.handleUpRanking)
}4. 优化代码
上面 handleNewRanking、handleOriginRanking、handleUpRanking 以及 onState 代码重复,优化代码。
// ============================= 监听所需数据改变 =============================
rankingStore.onState("newRanking", this.getRankingHanlder("newRanking"))
rankingStore.onState("originRanking", this.getRankingHanlder("originRanking"))
rankingStore.onState("upRanking", this.getRankingHanlder("upRanking"))
// 上面还是有重复代码,进一步优化
for (const key in rankingsMap) {
rankingStore.onState(key, this.getRankingHanlder(key))
}
// ============================= 监听数据改变的 state =============================
getRankingHanlder(ranking) {
return value => {
const newRankingInfos = { ...this.data.rankingInfos, [ranking]: value }
this.setData({ rankingInfos: newRankingInfos })
}
},二、界面展示
<!-- 榜单 -->
<view class="ranking" wx:if="{{isRankingData}}">
<area-header title="榜单" hasMore="{{false}}"/>
<view class="ranking-list">
<block wx:for="{{rankingInfos}}" wx:key="id">
<ranking-item itemData="{{item}}" key="{{index}}"/>
</block>
</view>
</view>