前言

若文章有误,欢迎读者留言反馈

💻Installation

1
git clone https://github.com/coding327/mymovies.git

详情页

绘制详情页

pages目录下新建一个文件夹为detail,接着在里面鼠标右键选择新建Page,输入detail回车即可,这样页面就创建好了

每项电影都是我们的film-item组件,点击跳转到详情页,只需要更改film-item组件,把最外层的view换成navigator就可以了,并添加上跳转地址url="/pages/detail/detail"

1
2
3
<!-- 最外层view标签替换为下面这个 -->
<navigator url="/pages/detail/detail">
</navigator>

为了方便观察详情页,我们去app.jsonpages中的detail放最前面,这样应用就会直接显示详情页

1
2
3
4
5
6
"pages": [
"pages/detail/detail",
"pages/home/home",
"pages/list/list",
"pages/about/about"
],

进入detail.wxml,把默认内容清除掉,首先放容器view,根据设计图,划分板块,书写布局和样式代码
代码比较多这里我就不做展示了,大家可以拉取代码仓库进入/pages/detail文件夹下查看

电影详情页数据的加载

首先看下电影详情的接口地址https://m.douban.com/rexxar/api/v2/movie/电影ID,我们可以发现这里传参的方式并不是采用传统?的方式,而是写在路径里作为路径的一部分,我们叫它Rest风格

回到api.js文件中,封装请求api

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
// 电影详情页数据
const loadFilmByFilmId = function (filmId) {
return new Promise((resolve, reject) => {
wx.request({
url: URLS.detailUrl + filmId,
success: resolve,
fail: reject
})
}).then(res => {
if (res.statusCode == 200) {
return res.data
} else {
return Promise.reject({
message: res.errMsg
})
}
})
}

// 注意要暴露出去
module.exports = {
showError,
loadHotFilms,
loadLatestFilms,
loadFreeFilms,
loadFilmByFilmId
}

进入app.json文件中,把首页放到最上面

1
2
3
4
5
6
"pages": [
"pages/home/home",
"pages/detail/detail",
"pages/list/list",
"pages/about/about"
],

我们要传递电影的id给到详情页,进入film-item.wxml文件中,url后面拼接id,注意film里面是有个id的,如果你之前在控制台仔细查看过数据,是会发现有个id

1
2
<navigator url="/pages/detail/detail?filmId={{ film.id }}">
</navigator>

接收filmId,同时把它保存到我们的数据仓库中,进入detail.js文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
},

/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.data.filmId = options.filmId
},

导入api,在onReady中发请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const api = require('../../api/api')

/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
api.loadFilmByFilmId(this.data.filmId).then(data => {
// console.log(data); // 成功返回电影信息
// 将它们保存到数据仓库中,页面上需要直接使用
this.setData({
film: data
})
}).catch(api.showError)
},

展示电影数据,进入detail.wxml文件中,控制台的AppData打开,找到数据仓库中的film,到其中找数据,看哪个字段合适放上去
【注意: 由于接口问题】演员的图片那里目前少数据,所以没更换,然后评论数据是有个单独的数据接口

电影评论数据的加载

分析这个获取评论的接口地址,我们可以发现它前面是detailUrl的地址【rest风格】但是后面又是查询字符串格式,所以我们可以直接使用detailUrl,没有将它单独写到统一的URLS中,然后后面拼接参数即可
进入api.js文件中,封装获取评论数据的接口

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
// 电影详情页评论数据
const loadCommentsByFilmId = function (filmId,params={}) {
return new Promise((resolve, reject) => {
wx.request({
url: URLS.detailUrl+filmId+'/interests',
data:params,
success: resolve,
fail: reject
})
}).then(res => {
if (res.statusCode == 200) {
return res.data
} else {
return Promise.reject({
message: res.errMsg
})
}
})
}

// 注意要暴露出去
module.exports = {
showError,
loadHotFilms,
loadLatestFilms,
loadFreeFilms,
loadFilmByFilmId,
loadCommentsByFilmId
}

回到detail.js文件中,调用接口获取数据
首先需要在数据仓库定义两个字段

1
2
3
4
5
6
7
8
9
10
/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
film: {}, // 存储电影信息
start: 0, // 评论的起始索引
count: 10, // 评论的条数
comments: [], // 存储电影的评论
},

接着依旧是在onReady中发请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
api.loadFilmByFilmId(this.data.filmId).then(data => {
// console.log(data); // 成功返回电影信息
// 将它们保存到数据仓库中,页面上需要直接使用
this.setData({
film: data
})
}).catch(api.showError)
// 单独加载电影的评论
api.loadCommentsByFilmId(this.data.filmId, {
start: this.data.start,
count: this.data.count,
order_by: 'time' // 时间的降序,最新评论在最上面
}).then(data => {
console.log(data); // 打印下data,找到评论数据
this.setData({
comments: data.interests
})
}).catch(api.showError)
},

接着我们可以在AppData中看下我们的数据仓库中有没有评论数据
展示数据,进入detail.wxml文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 电影评论 start -->
<view class="film-comment">
<view class="comment-title">
<text>评论:</text>
</view>
<view class="comment-item" wx:for="{{ comments }}" wx:for-item="comment" wx:key="index">
<view class="left">
<image class="user-img" src="{{ comment.user.avatar }}"></image>
</view>
<view class="right">
<view class="name">{{ comment.user.name }}</view>
<view class="time">{{ comment.create_time }}</view>
<view class="content">{{ comment.comment }}</view>
</view>
</view>
</view>
<!-- 电影评论 end -->

上拉加载更多评论

依旧是使用onReachBottom这个函数
在数据仓库的把总的数据条数存储起来

1
2
3
4
5
6
7
8
9
10
11
/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
film: {}, // 存储电影信息
start: 0, // 评论的起始索引
count: 10, // 评论的条数
total: 0, // 总的评论条数
comments: [], // 存储电影的评论
},

同时起始索引也还要加,total存储起来,评论数组,要与新的评论数组合并

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
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
api.loadFilmByFilmId(this.data.filmId).then(data => {
// console.log(data); // 成功返回电影信息
// 将它们保存到数据仓库中,页面上需要直接使用
this.setData({
film: data
})
}).catch(api.showError)
// 单独加载电影的评论
api.loadCommentsByFilmId(this.data.filmId, {
start: this.data.start,
count: this.data.count,
order_by: 'time' // 时间的降序,最新评论在最上面
}).then(data => {
console.log(data); // 打印下data,找到评论数据
this.setData({
// comments: data.interests, // 评论数据
comments: this.data.comments.concat(data.interests), // 评论数组的累加
start: this.data.start + this.data.count, // 起始索引每次累加
total: data.total // 记录总条数
})
}).catch(api.showError)
},

判断后台有没有多的数据,让你去请求,没有就显示没有更多,在onReachBottom这个函数中判断
加载评论单独抽离成一个方法loadComments,因为考虑到上拉加载每次只需要加载评论即可【封装代码这里不做展示了,具体可以拉取代码查看detail.js文件】

1
2
3
4
5
6
7
8
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
if (this.data.start < this.data.total) {
this.loadComments()
}
},

为了考虑用户体验,发请求获取数据需要时间,显示loading效果,同时没有评论了,显示没有更多
这里我们把list.wxml中的loadingnomore代码给复制过来粘贴到detail.wxml中,同时到数据仓库中定义控制显示、隐藏的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 页面的初始数据
*/
data: {
filmId: '', // 电影id
film: {}, // 存储电影信息
start: 0, // 评论的起始索引
count: 10, // 评论的条数
total: 0, // 总的评论条数
comments: [], // 存储电影的评论
showLoading: false, // loading的开关
showNomore: false // 没有更多的开关
},

loadingnomore显示隐藏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
if (this.data.start < this.data.total) { // 有数据
// 为了考虑用户体验,发请求获取数据需要时间,显示loading效果
this.setData({
showLoading: true
})
// 注意拿到数据之后才能再让loading隐藏,但是发请求是个异步,我们需要借助promise.then,即把之前加载评论数据封装的方法里的promise对象return出来即可
this.loadComments().then(() => {
this.setData({
showLoading: false // 拿到返回的评论数据
})
})
} else { // 没有更多数据
this.setData({
nomore: true
})
}
},

这里我们可以发现当初把loadingnomore抽离成模板的好处了,很多地方都会用到
最后还有一个收藏功能未实现,这里等我们把个人页【关于页】做了再来实现