小程序开发初体验-豆瓣

1,297 阅读14分钟

微信小程序仿豆瓣APP开发过程

1. 需求

  1. 小程序上方为搜索框。
  2. 搜索框下方为当下热门电影、电视剧和综艺模块。
  3. 列表可左右滑动,点击影片可进入影片详情页面。
  4. 影片详情页面可查看评论、影片标签和影片详情信息。

演示效果:

小程序-豆瓣演示.gif

2. 初始化项目

打开微信开发工具 -> 创建一个新项目:

image-20211122194912234.png

删除 pages 文件夹下面的所有文件:

image-20211122200545449.png

将项目下面的 app.js 文件修改成如下内容:

// app.js
App({
  
})

修改项目目录下的 app.json 文件,将之前的页面路径删除,修改 window 属性,具体如下:

{
  "pages": [
    
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#41BE57",
    "navigationBarTitleText": "豆瓣",
    "navigationBarTextStyle": "white"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

在pages文件夹下创建一个文件夹 home ,右键选择 新建page 命名为 home 。如下所示:

image-20211122201329237.png

3. 编写搜索组件

微信小程序官方提供了一套 weui ,它里面就提供了一个组件 searchbar。不过这不是我们的重点,我们要从头造轮子!

3.1 创建组件

在我们项目目录下创建一个文件夹 components ,这个文件夹专门存放我们自己自定义的组件,方便后期我们管理项目。创建components后右键新建一个文件取名searchbar,对着searchbar文件夹右键选中新建Component取名叫searchbar。如下图所示:

image-20211122202219921.png

3.2 引入搜索组件

为了我们方便调试,我们需要先引用我们刚刚创建的组件到首页中。

首先,打开首页的home.json文件,修改usingComponents属性,具体如下:

{
  "usingComponents": {
    "searchbar": "../../components/searchbar/searchbar"
  }
}

./表示当前所在目录;../ 表示回到上一层目录;/表示根目录

此时我们的首页就可以使用searchbar组件了,我们在home.wxml中使用它吧!!!先把home.wxml中的text组件删除,之后调用searchbar组件,代码如下:

<!--pages/home/home.wxml-->
<searchbar></searchbar>

这个时候你会发现左边会出现 components/searchbar/searchbar.wxml 字样!这表明我们已经成功引用自己写的组件了。

image-20211122203249228.png

3.3 编写搜索组件页面代码

打开searchbar.wxml文件,添加如下代码:

<!--pages/component/search/searchbar.wxml-->
<view class="searchbar">
    <view class="content">
        <!-- 显示搜索图标 -->
        <image class="search-icon" src="../../images/search.png" />
        <!-- 显示输入框 -->
        <input class="search-input" type="text" confirm-type="search" placeholder="{{placeholder}}" bindinput="onSearchInputEvent" bindconfirm="onSearchSubmitEvent"/>
    </view>
</view>

在上面代码中可以看到input组件中设置了文本输入类型为text,提交类型为search(这样设置,在输入法上面会显示搜索按钮),默认提示为用户预设属性。我们还监听了输入框的输入事件和输入框的提交事件,处理这两个事件的方法分别为onSearchInputEvent、onSearchSubmitEvent。

注意:需要自行寻找搜索图标然后在项目根目录下创建images文件夹,将搜索图标放入进去。

处理页面样式,打开searchbar.wxss文件,添加如下代码:

/* pages/component/search/searchbar.wxss */
/* 搜索框的整体样式 */
.searchbar {
    height: 80rpx;
    background-color: #41BE57;
    padding: 20rpx;
    display: flex;
}
/* 内容 */
.content {
    width: 100%;
    height: 80rpx;
    background-color: white;
    /* 设置圆角 */
    border-radius: 10rpx;
    display: flex;
    /* 垂直居中 */
    align-items: center;
    /* 水平方向靠左 */
    justify-content: flex-start;
}
/* 设置搜索图标的样式 */
.search-icon {
    width: 60rpx;
    height: 60rpx;
    margin-left: 10rpx;
}
/* 输入框样式 */
.search-input {
    /* 填满剩余内容 */
    flex: 1;
    margin: 0 20rpx;
}

此时我们可以看到searchbar最终的样子了。

image-20211122204744480.png

3.4 编写搜索组件逻辑代码

打开searchbar.js文件,添加如下代码:

// pages/component/search/searchbar.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        placeholder: {
            type: String,
            value: '搜索'
        }
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {
        /**
         * 处理输入的事件
         * @param {*} event 事件
         */
        onSearchInputEvent: function (event) {
            // 自定义事件所携带的数据
            var detail = event.detail
            // 触发事件的选项
            var option = {}
            this.triggerEvent('input', detail, option);
        },
        
        /**
         * 处理提交的事件
         * @param {*} event 事件
         */
        onSearchSubmitEvent: function (event) {
            var detail = { "value": event.detail.value }
            var option = {}
            this.triggerEvent('confirm', detail, option);
        }
    }
})

  1. properties:组件的对外属性,是属性名到属性设置的映射表。
  2. data:组件的对外属性,是属性名到属性设置的映射表。
  3. methods:组件的方法,包括事件响应函数和任意的自定义方法,关于事件响应函数的使用,参见 组件间通信与事件

我们对searchbar组件添加了一个属性,可以让用户在不输入搜索内容时默认显示我们提前设置好要显示的文本。在methods中我们还为组件添加了两个方法处理输入框的输入事件和提交事件。triggerEvent('', {}, {})第一个参数是自定义事件名称,这个名称是在页面调用组件时bind的名称,第二个对象就可以将想要的属性拿到,第三个触发事件的选项。通过这个方法我们就可以在首页监听输入框的输入事件和提交事件了。

我们可以试着在首页监听一下,打开home.wxmlhome.js文件,添加如下代码:

<!--pages/home/home.wxml-->
<searchbar bindinput="searchInput" bindconfirm="searchSubmit"></searchbar>
// pages/home/home.js
Page({

    /**
     * 页面的初始数据
     */
    data: {

    },

    /**
     * 生命周期函数--监听页面加载
     */
    onLoad: function (options) {

    },

    searchInput: function(event) {
        console.log('用户输入的值:' + event.detail.value);
    },

    searchSubmit: function(event) {
        console.log('用户提交的值:' + event.detail.value);
    }
})

效果如下:

搜索组件演示.gif

4. 编写评分组件

4.1 创建评分组件

评分组件在影片列表和影片详情中有使用,为了减少重复编写相同的代码,我们需要自行创建一个评分组件。创建评分组件过程与上面步骤一致:选中components文件夹 -> 右键新建文件夹 -> 命名ratingbar -> 右键新建component -> 命名ratingbar。

image-20211122212232440.png

4.2 引入评分组件

过程也是和上面一致的,打开home.json,添加ratingbar组件。具体代码如下:

{
  "usingComponents": {
    "searchbar": "../../components/searchbar/searchbar",
    "ratingbar": "../../components/ratingbar/ratingbar"
  }
}

home.wxml文件

<!--pages/home/home.wxml-->
<searchbar bindinput="searchInput" bindconfirm="searchSubmit"></searchbar>
<ratingbar></ratingbar>

image-20211122212700843.png

4.3 编写评分组件页面代码

打开ratingbar.wxml文件,添加如下代码:

<!--components/ratingbar/ratingbar.wxml-->
<view class="ratingbar">
    <!-- 遍历星星列表 -->
    <image class="rating-icon" wx:for="{{ratingList}}" wx:key="index" src="{{item.src}}" />
    <!-- 显示评分 -->
    <text wx:if="{{isDisplayText}}" class="rating-text">{{rating}}</text>
</view>

这里使用for循环来控制要显示多少颗星星,if用来控制是否显示文本。

编写组件样式,打开ratingbar.wxss文件,添加如下代码:

/* components/ratingbar/ratingbar.wxss */
.ratingbar {
    display: flex;
    align-items: center;
    padding: 20rpx;
}
.rating-icon {
    width: 36rpx;
    height: 36rpx;
}
.rating-text {
    color: #FFB712;
    margin-left: 8rpx;
}

4.4 编写评分组件逻辑代码

打开ratingbar.js文件,添加如下代码:

// components/ratingbar/ratingbar.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        value: {
            type: Number,
            value: 4.5,
            /**
             * 监听分数的变化
             * @param {Number} newVal 新的值
             * @param {Number} oldVal 旧的值
             */
            observer: function(newVal, oldVal) {
                this.handleStar();
            }
        },
        max: {
            type: Number,
            value: 5
        },
        isDisplayText: {
            type: Boolean,
            value: true
        }
    },

    /**
     * 组件的初始数据
     */
    data: {
        ratingList: []
    },

    /**
     * 组件的方法列表
     */
    methods: {
        /**
         * 计算星星
         */
        handleStar: function () {
            var rating = (this.data.value * 5 / this.data.max).toFixed(1);
            console.log("评分:" + rating);
            // 装星星的列表
            var list = [];
            // 添加点亮的星星
            var light = parseInt(rating);
            // console.log("light=" + light);
            for (let index = 0; index < light; index++) {
                var star = {
                    state: 'light',
                    src: '../../images/rate_light.png'
                };
                list.push(star);
            }
            // 添加半个星星
            var half = rating - light;
            // console.log("half=" + half);
            // 如果大于等于0.5就添加,灰色星星向下取整;否则灰色星星向上取整
            if (half >= 0.5) {
                var star = {
                    state: 'half',
                    src: '../../images/rate_half.png'
                };
                list.push(star);
                // 添加灰色星星
                var gray = Math.floor(5 - rating);
                // console.log("gray=" + gray);
                for (let index = 0; index < gray; index++) {
                    var star = {
                        state: 'gray',
                        src: '../../images/rate_gray.png'
                    };
                    list.push(star);
                }
            } else {
                // 添加灰色星星
                var gray = Math.ceil(5 - rating);
                // console.log("gray=" + gray);
                for (let index = 0; index < gray; index++) {
                    var star = {
                        state: 'gray',
                        src: '../../images/rate_gray.png'
                    };
                    list.push(star);
                }
            }
            // 更新数据
            this.setData({
                ratingList: list,
                rating: rating
            });
        }
    },

    /**
     * 生命周期
     */
    lifetimes: {
        /**
         * 在组件实例进入页面节点树时执行
         */
        attached: function () {
            // 计算星星
            this.handleStar();
        }
    }
})

可以看到我们添加了rating和max属性,分别是评分和最大评分;observer监听属性值的变化,如果值发生了变化会重新计算星星数量;handleStar()是用来计算星星数量的,计算显示多少可亮着的星星,多少颗半亮的星星,多少可不亮的星星。attached是在组件实例进入页面节点树时计算星星数量。

这个时候可以看到我们的ratingbar组件已经显示在home页面上了,如图所示:

image-20211122221319120.png

如果要ratingbar显示我们设定的值,只需要在home.wxml中添加如下代码:

<ratingbar max="100" value="70"></ratingbar>

image-20211125100647204.png

5. 编写影片模块组件

5.1 创建影片模块组件

老套路和上面一样。

image-20211123164530552.png

5.2 引入影片模块组件

home.json引用组件

{
  "usingComponents": {
    "searchbar": "../../components/searchbar/searchbar",
    "ratingbar": "../../components/ratingbar/ratingbar",
    "poster": "../../components/poster/poster"
  }
}

home.wxml文件

<!--pages/home/home.wxml-->
<searchbar bindinput="searchInput" bindconfirm="searchSubmit"></searchbar>
<ratingbar></ratingbar>
<poster></poster>

image-20211123164810336.png

由于影片模块会用到ratingbar组件,所有我们需要打开poster组件的poster.json文件,添加如下代码:

{
    "component": true,
    "usingComponents": {
        "ratingbar": "../poster/poster"
    }
}

5.3 编写影片模块页面代码

打开poster.wxml文件,添加如下代码:

<!--components/poster/poster.wxml-->
<!-- 跳转至目标页面 -->
<navigator wx:if="{{info}}" class="poster-card" url="{{navigatorTo}}" >
    <view class="poster-container">
        <image class="poster-img" src="{{info.cover.url}}" />
        <text class="title">{{info.title}}</text>
        <ratingbar max="{{info.rating.max != NaN ? info.rating.max : 10}}" value="{{info.rating.value != NaN ? info.rating.value : 0}}"/>
    </view>
</navigator>

这里我们使用navigator来实现用户点击影片模块跳转至影片详情页,url为要跳转的页面路径。if是用来判断属性info是否不为空,如果不为空则显示海报、影片名和评分,否则什么都不显示。

打开poster.wxss样式,添加如下代码:

/* components/poster/poster.wxss */
.poster-card {
    width: 300rpx;
    /* 设置为行内元素 */
    display: inline-block;
    box-sizing: border-box;
    padding: 20rpx;
    margin: 10rpx;
    height: 540rpx;
}

.poster-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 500rpx;
}

.poster-img {
    width: 100%;
    margin-bottom: 10rpx;
    border-radius: 20rpx;
}

.title {
    width: 100%;
    /* 行高 */
    line-height: 2em;
    /* 文本居中 */
    text-align: center;
    /* background-color: pink; */
    /* 强制文本在一行 */
    white-space: nowrap;
    /* 多出的隐藏 */
    overflow:hidden;
    /* 显示省略号 */
    text-overflow: ellipsis;
}

5.4 编写影片模块逻辑代码

// components/poster/poster.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        info: {
            type: Object,
            value: {
                cover: {
                    url: '../../images/logan.jpg'
                },
                title: '金刚狼3',
                rating: {
                    max: 10,
                    value: 5
                },
                id: 1
            }
        },
        navigatorTo: {
            type: String,
            value: ''
        }
    },

    /**
     * 组件的初始数据
     */
    data: {
    },

    /**
     * 组件的方法列表
     */
    methods: {
    }
})

这里我们只需要设置一下属性值info,并默认让它显示一些数据,方便我们调试。

这个时候我们就可以看到预览页面已经出现我们想要的效果了,如图所示:

image-20211125102218466.png

6. 编写影片列表组件

6.1 创建列表组件

老套路,这里我们取名叫horizontalList组件,具体如下:

image-20211125102548586.png

6.2 引入列表组件

home.json文件:

{
  "usingComponents": {
    "searchbar": "../../components/searchbar/searchbar",
    "ratingbar": "../../components/ratingbar/ratingbar",
    "poster": "../../components/poster/poster",
    "horizontalList": "../../components/horizontalList/horizontalList"
  }
}

home.wxml文件:

<!--pages/home/home.wxml-->
<searchbar bindinput="searchInput" bindconfirm="searchSubmit"></searchbar>
<ratingbar max="100" value="70"></ratingbar>
<poster></poster>
<horizontalList></horizontalList>

效果图:

image-20211125102920849.png

6.3 编写影片列表页面代码

在编写之前,我们需要让这个控件引入我们上面写好的poster组件。打开horizontalList.json文件,添加如下代码:

{
    "component": true,
    "usingComponents": {
        "poster": "../poster/poster"
    }
}

打开horizontalList.wxml文件,编写页面代码,具体如下:

<!--components/horizontalList/horizontalList.wxml-->
<view class="container">
    <text class="title">{{title}}</text>
    <scroll-view class="listview" scroll-x="{{true}}">
        <poster wx:for="{{list}}" wx:key="index" info="{{item}}" navigatorTo="../../pages/detail/detail?id={{item.id}}&type={{title}}" />
    </scroll-view>
</view>

这里使用text组件来显示列表的标题;scroll-view组件用于滑动列表,通过scroll-x属性来设置为横向滚动;通过for循环来显示poster组件,info属性则是我们要显示的数据对象,navigatorTo则是我们要跳转到电影详情页面。

打开horizontalList.wxss文件,编写样式代码:

/* components/horizontalList/horizontalList.wxss */
.container {
    display: flex;
    flex-direction: column;
    padding: 10rpx;
    /* background-color: coral; */
    box-sizing: border-box;
    margin-bottom: 10rpx;
}

.title {
    font-size: larger;
    margin-left: 30rpx;
    color: "#586678";
}

.listview {
    height: 540rpx;
    white-space: nowrap;
    width: 100%;
}

6.4 编写影片列表逻辑代码

打开horizontalList.js文件,添加如下代码:

// components/horizontalList/horizontalList.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        title: {
            type: String,
            value: "movie"
        },
        list: {
            type: Array,
            // 这里为了演示效果,默认添加了4个影片。可直接写成value:[]
            value: [
                {
                    cover: {
                        url: '../../images/logan.jpg'
                    },
                    title: '金刚狼3',
                    rating: {
                        max: 10,
                        value: 5
                    },
                    id: 1
                },
                {
                    cover: {
                        url: '../../images/logan.jpg'
                    },
                    title: '金刚狼3',
                    rating: {
                        max: 10,
                        value: 5
                    },
                    id: 1
                },
                {
                    cover: {
                        url: '../../images/logan.jpg'
                    },
                    title: '金刚狼3',
                    rating: {
                        max: 10,
                        value: 5
                    },
                    id: 1
                },
                {
                    cover: {
                        url: '../../images/logan.jpg'
                    },
                    title: '金刚狼3',
                    rating: {
                        max: 10,
                        value: 5
                    },
                    id: 1
                }
            ]
        }
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {

    }
})

从上面的代码可以看出,我们只添加了2个属性,一个用来显示列表标题,一个则是列表的数据。

注意:list属性的value值可直接写成value:[]。这里是为了方便查看效果,故添加了4个数据进去。

在写完逻辑代码之后就可以看到预览页面已经出现我们想要的效果了。

影片列表演示.gif

7. 实现首页效果

7.1 编写首页页面代码

打开home.wxml文件,将我们先前为了方便调试组件效果的代码删除,添加如下代码:

<!--pages/home/home.wxml-->
<view class="container">
    <searchbar bindconfirm="search"></searchbar>
    <!-- list:绑定列表数据; title:设置标题 -->
    <horizontalList list="{{movies}}" class="listview" title="电影"></horizontalList>
    <view class="line"></view>
    <horizontalList list="{{tv}}" class="listview" title="电视剧"></horizontalList>
    <view class="line"></view>
    <horizontalList list="{{varietyShow}}" class="listview" title="综艺"></horizontalList>
</view>

注意:要确保home已经引入我们使用的组件。

编写app.wxss文件,代码如下:

/**app.wxss**/
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
}

编写home.wxss文件,代码如下:

/* pages/home/home.wxss */
.listview {
    margin-top: 30rpx;
}

.line {
    height: 2rpx;
    background-color: #D8D8D8;
}

这个时候可以看到页面只有搜索框和两条横线,这是因为我们在页面代码绑定了js的数据,此时js我们还没有编写,所有才会出现这样的效果!

image-20211125105842307.png

7.2 编写首页逻辑代码

打开home.js文件,在onLoad()方法中添加如下代码:

/**
 * 生命周期函数--监听页面加载
 */
onLoad: function (options) {
    let that = this;
    // 电影
    wx.request({
        // 接口地址
        url: "https://m.douban.com/rexxar/api/v2/subject_collection/movie_showing/items?count=10",
        // 请求成功回调
        success: function (res) {
            console.log(res.data.subject_collection_items);
            // 更新列表数据
            that.setData({
                movies: res.data.subject_collection_items
            });
        },
        // 请求失败回调的方法
        fail: function(res) {
            console.log(res);
        }
    });
    // 电视剧
    wx.request({
        url: "https://m.douban.com/rexxar/api/v2/subject_collection/tv_hot/items?count=10",
        success: function (res) {
            console.log(res.data.subject_collection_items);
            that.setData({
                tv: res.data.subject_collection_items
            });
        }
    });
    // 综艺
    wx.request({
        url: "https://m.douban.com/rexxar/api/v2/subject_collection/tv_variety_show/items?count=10",
        success: function (res) {
            console.log(res.data.subject_collection_items);
            that.setData({
                varietyShow: res.data.subject_collection_items
            });
        }
    });
}

注意:我们需要在详情 -> 本地设置 -> 勾上不校验合法域名、web-view、TLS版本以及HTTPS证书。

这个时候我们的首页就已经大功告成了!

首页展示.gif

8. 实现tag组件

8.1 创建tag组件

image-20211125161010695.png

8.2 引用tag组件

在引用之前我们需要先创建一个详情页detail,如下图所示:

image-20211125161139420.png

创建好之后我们需要打开detail.json文件,引入我们的组件。

{
  "usingComponents": {
    "tag": "/components/tag/tag"
  }
}

打开detail.wxml引用我们的组件。

<!--pages/detail/detail.wxml-->
<text>pages/detail/detail.wxml</text>
<tag></tag>

这个时候我们点击影片就可以跳转至影片详情了。

跳转影片详情演示-1637828881653.gif

注意:为什么我们点击影片可以跳转呢?因为我们在写horizontalList组件时,对poster组件的属性navigatorTo属性设置了要跳转的页面,并携带了影片的id和影片类型title参数。

<poster wx:for="{{list}}" wx:key="index" info="{{item}}" navigatorTo="../../pages/detail/detail?id={{item.id}}&type={{title}}" />

8.3 编写tag页面代码

tag.wxml文件:

<!-- components/tag/tag.wxml -->
<view class="tag-list">
    <text class="tag" wx:for="{{tags}}" wx:key="index">{{item}}</text>
</view>

tag.wxss文件:

/* components/tag/tag.wxss */
.tag-list {
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
    flex-wrap: wrap; 
    padding: 5rpx;
}

.tag {
    padding: 10rpx 20rpx;
    background-color: #F5F5F5;
    border-radius: 50rpx;
    color: #353535;
    text-align: center;
    margin: 5rpx;
}

8.3 编写tag逻辑代码

打开tag.js文件,添加如下代码:

// components/tag/tag.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        tags: {
            type: Array,
            // 这里默认显示以下标签,方便我们调试。可直接写成value:[]
            value: ['动作', '爱情', '悬疑', '动作', '爱情', '悬疑', '动作', '爱情', '悬疑']
        }
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {

    }
})

此时我们再点击影片详情可查看tag的效果,如下:

tag展示.gif

9. 实现短评列表组件

9.1 创建短评列表组件

直接看图:

image-20211125163814346.png

9.2 引入短评列表组件

detail.json文件:

{
  "usingComponents": {
    "tag": "../../components/tag/tag",
    "commentlist": "../../components/commentlist/commentlist"
  }
}

detail.wxml文件:

<!--pages/detail/detail.wxml-->
<text>pages/detail/detail.wxml</text>
<tag></tag>
<commentlist></commentlist>

效果图:

image-20211125164047760.png

9.3 编写短评列表组件页面代码

打开commentlist.wxml文件,添加如下代码:

<!-- components/commentlist/commentlist.wxml -->
<view class="container">
    <image class="avatar" src="{{comment.user.avatar}}" />
    <view class="right">
        <view class="right-name-rating">
            <text>{{comment.user.name}}</text>
            <ratingbar isDisplayText="{{false}}" max="{{comment.rating.max}}" value="{{comment.rating.value}}"></ratingbar>
        </view>
        <view class="right-time">{{comment.create_time}}</view>
        <view class="right-comment">{{comment.comment}}</view>
    </view>
</view>

注意:这里我们使用到了ratingbar组件,所有需要在commentlist.json中引用。

{
    "component": true,
    "usingComponents": {
        "ratingbar":"../ratingbar/ratingbar"
    }
}

commentlist.wxss文件,添加如下代码:

/* components/commentlist/commentlist.wxss */
/* 将整组件内的组件排序方向改为水平排序,并且紧靠左边。 */
.container {
    display: flex;
    align-items: flex-start;
    /* background-color: purple; */
}
/* 设置头像大小,并且改为圆形 */
.avatar {
    width: 100rpx;
    height: 100rpx;
    border-radius: 50rpx;
}
/* 右边的布局 */
.right {
    /* 填满剩余空间 */
    flex: 1;
    display: flex;
    flex-direction: column;
    margin-left: 20rpx;
}

.right-name-rating {
    display: flex;
    align-items: center;
    /* background-color: red; */
}

.right-time {
    color: #cacaca;
    margin: 10rpx 0;
}

从上面的样式代码可以看出,整体布局分为左右两半,左边只显示头像,右边则显示详情信息。右边又分为上中下三个部分。

9.4 编写短评列表逻辑代码

打开commentlist.js文件:

// components/commentlist/commentlist.js
Component({
    /**
     * 组件的属性列表
     */
    properties: {
        comment: {
            type: Object,
            value: {
                user: {
                    avatar: "https://img9.doubanio.com/icon/up164627301-5.jpg",
                    name: "sherlin"
                },
                create_time: "2021-11-11 20:57:55",
                rating: {
                    max: 5,
                    value: 5
                },
                comment: "尹正最后伸出又收回的手 谁懂。"
            }
        }
    },

    /**
     * 组件的初始数据
     */
    data: {

    },

    /**
     * 组件的方法列表
     */
    methods: {

    }
})

效果如下:

image-20211125165414390.png

10. 实现影片详情页

10.1 编写影片详情页面代码

我需要先删除detail.wxml中的内容,并且修改如下代码:

<!--pages/detail/detail.wxml-->
<view class="container">
    <text class="detail-title">
        {{detail.title + ' ' + detail.original_title + '(' + detail.year + ')'}}
    </text>
    <!-- 描述 -->
    <view class="detail-description">
        <view class="detail-left">
            <image src="{{detail.cover_url}}" />
        </view>
        <view class="detail-right">
            <!-- 评分 -->
            <view class="detail-rating">
                <starbar totalscore="{{detail.rating.max}}" score="{{detail.rating.value}}"></starbar>
                <text>{{detail.rating.count}}人评价</text>
            </view>
            <!-- 分钟和类型 -->
            <view class="detail-time-type">{{detail.durations[0] + ' ' + detail.genres}}</view>
            <!-- 上映时间和国家 -->
            <view class="detail-time-type">{{detail.pubdate + ' ' + detail.countries[0]}}</view>
            <!-- 演员 -->
            <view class="detail-time-type">{{actors}}</view>
        </view>
    </view>
    <!-- 标签 -->
    <view class="detail-tag">
        <text style="color:#cacaca; margin-bottom:20rpx">豆瓣成员常用标签</text>
        <tag tags="{{tags}}" />
    </view>
    <!-- 短评 -->
    <view class="detail-comment">
        <text style="color:#cacaca;">短评({{detail.comment_count}})</text>
        <commentlist wx:key="index" wx:for="{{interests}}" data="{{item}}"/>
    </view>
</view>

注意:我们详情页页使用到了ratingbar,所有也是需要引用的。打开detail.json文件,添加ratingbar。

{
  "usingComponents": {
    "tag": "../../components/tag/tag",
    "commentlist": "../../components/commentlist/commentlist",
    "ratingbar": "../../components/ratingbar/ratingbar"
  }
}

detail.wxss文件:

/* pages/detail/detail.wxss */
.container {
    display: flex;
    flex-direction: column;
    padding: 0 20rpx;
}

.detail-title {
    font-size: larger;
    color: black;
}

.detail-description {
    width: 100%;
    margin-top: 20rpx;
    display: flex;
    height: 360rpx;
    /* background-color: plum; */
}

.detail-left > image {
    width: 260rpx;
    height: 100%;

}

.detail-right {
    width: 100%;
    margin-left: 20rpx;
    /* background-color: pink; */
    display: flex;
    flex-direction: column;
    justify-content:space-between;
}

.detail-rating {
    display: flex;
    align-items: center;
}

.detail-rating > text {
    font-size: x-small;
    color: #cacaca;
}

.detail-time-type {
    padding-left: 20rpx;
    font-size: small;
}

.detail-tag {
    /* background-color: brown; */
    margin: 40rpx 0;
    display: flex;
    flex-direction: column;
}

.detail-comment {
    /* background-color: pink; */
    display: flex;
    flex-direction: column;
    margin-bottom: 20rpx;
}

.detail-comment > commentlist {
    margin-top: 20rpx;
}

这里详情页面的布局可以查看下图方便理解。在知道如何布局后就可以用flex将页面切出来。

image-20211125170055176.png

10.2 编写影片详情页逻辑代码

打开detail.js文件,在onLoad()方法中添加如下代码:

/**
 * 生命周期函数--监听页面加载
 */
onLoad: function (options) {
    let that = this;
    // 查详情数据
    wx.request({
        // 用movie也可以查电影、电视剧、综艺节目详情数据
        url: "https://m.douban.com/rexxar/api/v2/movie/" + options.id,
        success: function (res) {
            var data = res.data;
            console.log(data);
            // 获取演员
            var actors = [];
            // 只获取6位演员
            for (let index = 0; index < 6; index++) {
                actors.push(data.actors[index].name);
            }
            // 获取tags
            var tags = [];
            for (let i = 0; i < data.tags.length; i++) {
                console.log(data.tags[i].name);
                tags.push(data.tags[i].name);
            }
            that.setData({
                detail: data,
                actors: actors,
                tags: tags
            });
        }
    });
    // 查短评
    wx.request({
        url: "https://m.douban.com/rexxar/api/v2/movie/" + options.id + "/interests",
        success: function (res) {
            console.log(res.data);
            var data = res.data;
            that.setData({
                interests: data.interests
            });
        },
        fail: function (res) {
            wx.showToast({
                title: '获取评论错误啦',
                icon: 'error',
                duration: 2000
            })
        }
    });
}

由于影片的id我们在页面跳转的时候通过参数传递进来,那么我们可以从options对象中将影片id取出来。拿到id后通过wx.request()方法发起2条网络请求,分别拿到影片详情数据和短评列表数据,通过suceess(res)回调函数将我们拿到的数据绑定在组件上。

此时大工告成,我们只需要在detail.json文件中修改一下标题栏的内容就完成详情页的功能了。

{
  "usingComponents": {
    "tag": "../../components/tag/tag",
    "commentlist": "../../components/commentlist/commentlist",
    "ratingbar": "../../components/ratingbar/ratingbar"
  },
  "navigationBarTitleText": "详情"
}

接下来看看效果:

详情页展示.gif