简单电影推荐小程序

时间:2024-03-12 10:41:46
博客班级 https://edu.cnblogs.com/campus/zjcsxy/SE2020
作业要求 https://edu.cnblogs.com/campus/zjcsxy/SE2020/homework/11334
作业目标 完成一个小程序,上传代码,完成一篇博文
作业源代码  https://github.com/Iamm401/software
学号  31801095
院系  计算机科学与技术

 

 

 

 

 

 

 

 

项目简介

  本项目是一个简易的电影推荐小程序,小程序中的所有电影信息都是来自豆瓣。用户可以进入小程序查看到近期推荐的电影和查看豆瓣高分电影,选择自己想了解的电影进入之后可以查看到电影的一些基本信息,还提供了豆瓣的链接,用户可以点击复制链接,然后前往豆瓣查看。

项目页面展示

  • 热门电影推荐页面:

     

 

 

  •  高分电影页面

    

 

 

  •  电影详情页面

    

 

 

 全局配置

  • app.json
{
  "pages":[
    "pages/index/index",
    "pages/movie_detail/index"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle":"black"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}
  • app.js
//app.js
import {
  request,
  requestNoBaseUrl
} from "/request/index";
import {
  login,
  showToast
} from "/utils/asyncWx.js";
import regeneratorRuntime from "/lib/runtime/runtime.js";
App({
  onLaunch: function () {
  },
  globalData: {
    // POST,DELETE,PUT请求的header
    requestPostHeader: {
      "Content-Type": "application/x-www-form-urlencoded"
    },

  },
  //预览图片
  previewImages(allImgerc, nowImgSrc) {
    if (!nowImgSrc) nowImgSrc = allImgerc[0];
    wx.previewImage({
      current: nowImgSrc,
      urls: allImgerc,
      success: (result) => {},
      fail: (err) => {
        console.log(err);
      },
      complete: () => {},
    });
  },
});

各页面代码

  1. 电影列表页面(首页)
    • wxml
<view class="fade container">
    <block wx:if="{{isLoading}}">
        <view class="load_wrap">
            <my_loading>页面加载中</my_loading>
        </view>
    </block>
    <block wx:else>
        <van-tabs nav-class="nav-class" bind:change="handleTabChange" swipeable sticky line-height="4rpx" color="" tab-active-class="tab-active-class">
            <van-tab title="热门">
                <view class="fade recommend_movie_list">
                    <block wx:for="{{recommendMovieList}}" wx:for-item="movies" wx:key="index" wx:for-index="x">
                        <view class="fade recommend_movie_item" wx:for="{{movies}}" wx:key="id" wx:for-index="y" bindtap="handleToAimPage" data-url="../../pages/movie_detail/index?id={{item.id}}">
                            <view class="fade top">
                                <image src="{{item.cover}}" mode="aspectFill"/>
                            </view>                    
                            <view class="fade bottom">
                                <view class="fade title_rate">
                                    <text class="fade title">{{item.title}}</text><text class="fade rate">{{item.rate}}</text>
                                </view>           
                            </view>
                        </view>
                    </block>
                </view>
                <ShowEnd wx:if="{{isShowEnd_1}}">到底了</ShowEnd>
            </van-tab>
            <van-tab title="高分">
                <view class="fade recommend_movie_list">
                    <block wx:for="{{rateMovieList}}" wx:for-item="movies" wx:key="index" wx:for-index="x">
                        <view class="fade recommend_movie_item" wx:for="{{movies}}" wx:key="id" wx:for-index="y" bindtap="handleToAimPage" data-url="../../pages/movie_detail/index?id={{item.id}}">
                            <view class="fade top">
                                <image src="{{item.cover}}" mode="aspectFill"/>
                            </view>                    
                            <view class="fade bottom">
                                <view class="fade title_rate">
                                    <text class="fade title">{{item.title}}</text><text class="fade rate">{{item.rate}}</text>
                                </view>           
                            </view>
                        </view>
                    </block>
                </view>
                <ShowEnd wx:if="{{isShowEnd_2}}">到底了</ShowEnd>
            </van-tab>
        </van-tabs>
    </block>
</view>

 

    • js
var app = getApp();
import regeneratorRuntime from "../../lib/runtime/runtime.js";
import {
  showLoading,
  showWarnToast,
} from "../../utils/asyncWx.js";
import { request, requestNoBaseUrl } from "../../request/index";
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tabIndex: 0,
    isLoading: true,
    recommendMovieList: [],
    rateMovieList: [],
    isShowEnd_1: false,
    isShowEnd_2: false,
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    Promise.all([this.getRecommendMovieList()])
      .then((res) => {
        this.setData({
          isLoading: false,
        });
      })
      .catch((err) => {
        this.setData({
          isLoading: false,
        });
        showWarnToast({ title: "加载失败!", duration: 1111 });
      });
  },

  pageParams: {
    refreshTime: 0,
    pageNum: 0,
    pageSize: 18,
  },

  totalNum: 0,
  totalPages: 0,

  ratePageParams: {
    refreshTime: 0,
    pageNum: 0,
    pageSize: 18,
  },

  rateTotalNum: 0,
  rateTotalPages: 0,

  async getRecommendMovieList() {
    try {
      const pageParams = { ...this.pageParams };
      ++pageParams.pageNum;
      if (pageParams.pageNum === 1)
        pageParams.refreshTime = new Date().getTime();
      const res = await request({
        url: "/movie/movieList",
        data: { ...pageParams },
      });
      if (res.code && res.code !== 200) throw new Errror(res.message);
      const { total, pages, pageNum, data } = res;
      this.pageParams.pageNum = pageNum;
      this.totalNum = total;
      this.totalPages = pages;
      data.forEach((res) => {
        res.rate = res.rate.toFixed(1);
      });
      this.setData({
        ["recommendMovieList[" + (pageNum - 1) + "]"]: data,
      });
    } catch (error) {
      throw new Errror(error);
    }
  },

  async getRateMovieList() {
    try {
      const pageParams = { ...this.ratePageParams };
      ++pageParams.pageNum;
      if (pageParams.pageNum === 1)
        pageParams.refreshTime = new Date().getTime();
      const res = await request({
        url: "/movie/rate/movieList",
        data: { ...pageParams },
      });
      if (res.code && res.code !== 200) throw new Errror(res.message);
      const { total, pages, pageNum, data } = res;
      this.ratePageParams.pageNum = pageNum;
      this.rateTotalNum = total;
      this.rateTotalPages = pages;
      data.forEach((res) => {
        res.rate = res.rate.toFixed(1);
      });
      this.setData({
        ["rateMovieList[" + (pageNum - 1) + "]"]: data,
      });
    } catch (error) {
      throw new Errror(error);
    }
  },

  handleToAimPage(e) {
    const { url } = e.currentTarget.dataset;
    showLoading({ title: "页面跳转中..." });
    wx.navigateTo({
      url,
      success: (result) => {},
      fail: () => {},
      complete: () => {
        wx.hideLoading();
      },
    });
  },

  handleTabChange(e) {
    this.setData({
      tabIndex: e.detail.index,
    });
    switch (e.detail.index) {
      case 0:
        // if (this.totalNum === 0) {
        this.totalNum = 0;
        this.totalPages = 0;
        this.pageParams = {
          refreshTime: 0,
          pageNum: 0,
          pageSize: 18,
        };
        this.setData({
          recommendMovieList:[]
        })
        showLoading({
          title: "加载中",
        });
        Promise.all([this.getRecommendMovieList()])
          .then((res) => {
            wx.hideLoading();
          })
          .catch((err) => {
            wx.hideLoading();
          });
        // }
        break;
      case 1:
        // if(this.rateTotalNum === 0){
        this.ratePageParams = {
          refreshTime: 0,
          pageNum: 0,
          pageSize: 18,
        };
        this.rateTotalNum = 0;
        this.rateTotalPages = 0;
        this.setData({
          rateMovieList:[]
        })
        showLoading({
          title: "加载中",
        });
        Promise.all([this.getRateMovieList()])
          .then((res) => {
            wx.hideLoading();
          })
          .catch((err) => {
            wx.hideLoading();
          });
        // }
        break;
    }
  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {
    switch (this.data.tabIndex) {
      case 0:
        // 1 判断还有没有下一页数据
        if (this.pageParams.pageNum >= this.totalPages) {
          // 没有下一页数据
          this.setData({
            isShowEnd_1: true,
          });
        } else {
          // 没有下一页数据
          this.setData({
            isShowEnd_1: false,
          });
          showLoading({
            title: "加载中",
          });
          Promise.all([this.getRecommendMovieList()])
            .then((res) => {
              wx.hideLoading();
            })
            .catch((err) => {
              wx.hideLoading();
            });
        }
        break;
      case 1:
        // 1 判断还有没有下一页数据
        if (this.ratePageParams.pageNum >= this.rateTotalPages) {
          // 没有下一页数据
          this.setData({
            isShowEnd_2: true,
          });
        } else {
          // 没有下一页数据
          this.setData({
            isShowEnd_2: false,
          });
          showLoading({
            title: "加载中",
          });
          Promise.all([this.getRateMovieList()])
            .then((res) => {
              wx.hideLoading();
            })
            .catch((err) => {
              wx.hideLoading();
            });
        }
        break;
    }
  },
});
    • json
{
  "usingComponents": {
    "my_loading":"../../components/my_loading/my_loading",
    "ShowEnd": "../../components/ShowEnd/ShowEnd",
    "van-tab": "../../components/tab/index",
  "van-tabs": "../../components/tabs/index"
  },
  "backgroundTextStyle": "dark",
  "navigationBarTitleText":"影视推荐",
  "navigationBarBackgroundColor":"#2CADE4",
  "navigationBarTextStyle":"white"
}

 

    • wxss
.container .tab-active-class {
  color: #ee0a24;
  font-size: 66rpx;
}
.container .nav-class {
  background-color: #f8f9f8;
}
.container .load_wrap {
  width: 100vw;
  height: 100vh;
}
.container .recommend_movie_list {
  padding: 22rpx;
  display: grid;
  grid-template-rows: 444rpx;
  grid-auto-rows: 444rpx;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 22rpx;
}
.container .recommend_movie_list .recommend_movie_item:active {
  opacity: 0.888;
}
.container .recommend_movie_list .recommend_movie_item {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.container .recommend_movie_list .recommend_movie_item .top {
  height: 350rpx;
  width: 100%;
}
.container .recommend_movie_list .recommend_movie_item .top image {
  width: 100%;
  height: 100%;
}
.container .recommend_movie_list .recommend_movie_item .bottom {
  display: flex;
  justify-content: center;
  padding-top: 10rpx;
}
.container .recommend_movie_list .recommend_movie_item .bottom .title_rate {
  font-size: 26rpx;
  letter-spacing: 1rpx;
  word-break: break-all;
  text-overflow: ellipsis;
  display: -webkit-box;
  /** 对象作为伸缩盒子模型显示 **/
  -webkit-box-orient: vertical;
  /** 设置或检索伸缩盒对象的子元素的排列方式 **/
  -webkit-line-clamp: 2;
  /** 显示的行数 **/
  overflow: hidden;
  /** 隐藏超出的内容 **/
}
.container .recommend_movie_list .recommend_movie_item .bottom .title_rate .title {
  color: #3377aa;
}
.container .recommend_movie_list .recommend_movie_item .bottom .title_rate .rate {
  padding-left: 10rpx;
  color: #e09015;
}

 

  1. 电影详情页面
    • wxml
<block wx:if="{{isLoading}}">
    <view class="load_wrap">
        <my_loading>页面加载中</my_loading>
    </view>
</block>
<block wx:else>
    <view class="fade container">
        <view class="fade top">
            <view class="fade title">
                <text selectable="{{true}}" class="fade title_text">{{movieDetail.title}}</text>
                <text>{{movieDetail.year}}</text>
            </view>
            <view class="fade content">
                <view class="fade left">
                    <view class="fade cover">
                        <image src="{{movieDetail.cover}}" data-url="{{movieDetail.cover}}" mode="widthFix" bindtap="handlePreViewImg"/>
                    </view>
                </view>
                <view class="fade right">
                    <view class="fade label">豆瓣评分</view>
                    <view class="fade grade">
                        <view class="fade grade_val">{{movieDetail.rate}}</view>
                        <view class="fade grade_star_show">
                            <van-rate size="40rpx" value="{{ movieDetail.rate/2 }}" readonly="{{true}}" allow-half="{{true}}" />
                        </view>
                    </view>
                    <view class="fade star_list">
                        <view class="fade star_item">
                            <view class="fade star_item_left">5星</view>
                            <view class="fade star_item_center" style="width:{{movieDetail.star_5*2}}rpx"></view>
                            <view class="fade star_item_right">{{movieDetail.star_5}}%</view>
                        </view>
                        <view class="fade star_item">
                            <view class="fade star_item_left">4星</view>
                            <view class="fade star_item_center" style="width:{{movieDetail.star_4*2}}rpx"></view>
                            <view class="fade star_item_right">{{movieDetail.star_4}}%</view>
                        </view>
                        <view class="fade star_item">
                            <view class="fade star_item_left">3星</view>
                            <view class="fade star_item_center" style="width:{{movieDetail.star_3*2}}rpx"></view>
                            <view class="fade star_item_right">{{movieDetail.star_3}}%</view>
                        </view>
                        <view class="fade star_item">
                            <view class="fade star_item_left">2星</view>
                            <view class="fade star_item_center" style="width:{{movieDetail.star_2*2}}rpx"></view>
                            <view class="fade star_item_right">{{movieDetail.star_2}}%</view>
                        </view>
                        <view class="fade star_item">
                            <view class="fade star_item_left">1星</view>
                            <view class="fade star_item_center" style="width:{{movieDetail.star_1*2}}rpx"></view>
                            <view class="fade star_item_right">{{movieDetail.star_1}}%</view>
                        </view>
                    </view>
                </view>
            </view>
        </view>
        <view class="fade bottom">
            <view class="fade movie_director bottom_item">
                <text class="fade info_label">导演:</text>
                <text selectable="{{true}}" class="fade info_text">{{movieDetail.movie_director}}</text>
            </view>
            <view class="fade scriptwriter bottom_item">
                <text class="fade info_label">编剧:</text>
                <text selectable="{{true}}" class="fade info_text">{{movieDetail.scriptwriter}}</text>
            </view>
            <view class="fade protagonist bottom_item">
                <text class="fade info_label">主演:</text>
                <text selectable="{{true}}" class="fade info_text">{{movieDetail.protagonist}}</text>
            </view>
            <view class="fade movie_type bottom_item">
                <text class="fade info_label">类型:</text>
                <text selectable="{{true}}" class="fade info_text">{{movieDetail.movie_type}}</text>
            </view>
            <view class="fade release_date bottom_item">
                <text class="fade info_label">上映日期:</text>
                <text selectable="{{true}}" class="fade info_text">{{movieDetail.release_date}}</text>
            </view>
            <view class="fade duration bottom_item">
                <text class="fade info_label">片长:</text>
                <text selectable="{{true}}" class="fade info_text">{{movieDetail.duration}}</text>
            </view>
            <view class="fade douban_url bottom_item">
                <text class="fade info_label">豆瓣链接:</text>
                <text selectable="{{true}}" class="fade info_text" data-value="{{movieDetail.url}}" bindtap="handleCopyText">{{movieDetail.url}}</text>
            </view>
        </view>
        <view class="fade intro">
            <view class="fade intro_label">{{movieDetail.title}}的剧情简介 · · · · · ·</view>
            <view class="fade intro_text">
                <text selectable="{{true}}" space="nbsp">{{movieDetail.intro}}</text>
            </view>
        </view>
        <view class="fade statement">
            <view class="fade source">本页面数据来自豆瓣</view>
            <view class="fade tip">如有侵权,请准备好证明材料并联系客服,会立即删除相关内容</view>
        </view>
    </view>
</block>
    • js
var app = getApp();
import regeneratorRuntime from "../../lib/runtime/runtime.js";
import {
  showToast,
  showModal,
  showModalPlus,
  showLoading,
  showWarnToast
} from "../../utils/asyncWx.js";
import { request, requestNoBaseUrl } from "../../request/index";
// pages/movie_detail/index.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    isLoading:true,
    movieDetail:{}
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    const {id} = options;
    Promise.all([this.getMovieDetail(id)]).then(res=>{
      this.setData({
        isLoading:false
      })
    }).catch(err=>{
      this.setData({
        isLoading:false
      })
      showWarnToast({ title: "加载失败!", duration: 1111 });
    })
  },

  async getMovieDetail(id){
    try {
      const res = await request({url:\'/movie/movieDetail\',data:{id}})
      if(res.code && res.code !== 200) throw new Error(res.message)
      res.rate = res.rate.toFixed(1)
      this.setData({
        movieDetail:res
      })
    } catch (error) {
      throw new Error(error)
    }
  },

  handleCopyText(e){
    wx.setClipboardData({
      data: e.currentTarget.dataset.value,
    });
  },

  handleTouchStart(e){
    this.touchStart = e.timeStamp;
  },

  handleTouchEnd(e){
    this.touchEnd = e.timeStamp;
  },

  handlePreViewImg(e){
    const {url} = e.currentTarget.dataset;
    app.previewImages([url],url);
  }
 
})

 

    • json
{
  "usingComponents": {
    "my_loading":"../../components/my_loading/my_loading",
    "ShowEnd": "../../components/ShowEnd/ShowEnd",
    "van-rate": "../../components/rate/index"
  },
  "backgroundTextStyle": "dark",
  "navigationBarTitleText":"影视详情",
  "navigationBarBackgroundColor":"#2CADE4",
  "navigationBarTextStyle":"white"
}
    • wxss
.load_wrap {
  width: 100vw;
  height: 100vh;
}
.container {
  width: 100%;
  padding: 16.66rpx;
  color: #000;
}
.container .top .title .title_text {
  font-size: 50rpx;
  font-weight: bold;
  color: #494949;
}
.container .top .title text {
  font-size: 50rpx;
  font-weight: bold;
  color: #888888;
}
.container .top .content {
  padding-top: 33rpx;
  display: flex;
}
.container .top .content .left {
  width: 40%;
  display: flex;
  justify-content: center;
}
.container .top .content .left .cover {
  width: 100%;
}
.container .top .content .left .cover image {
  width: 99%;
}
.container .top .content .right {
  display: flex;
  flex-direction: column;
  padding-left: 20rpx;
  margin-left: 20rpx;
}
.container .top .content .right .label {
  color: #898989;
}
.container .top .content .right .grade {
  display: flex;
  align-items: center;
}
.container .top .content .right .grade .grade_val {
  font-size: 56.66rpx;
  color: #494949;
}
.container .top .content .right .grade .grade_star_show {
  padding-left: 10rpx;
}
.container .top .content .right .star_list {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.container .top .content .right .star_list .star_item {
  display: flex;
  align-items: center;
  margin-top: 10rpx;
}
.container .top .content .right .star_list .star_item .star_item_left {
  color: #888;
}
.container .top .content .right .star_list .star_item .star_item_center {
  margin-left: 10rpx;
  background-color: #ffd596;
  transition: all 3s inherit;
  height: 33rpx;
  width: 0;
}
.container .top .content .right .star_list .star_item .star_item_right {
  padding-left: 10rpx;
}
.container .bottom {
  margin-top: 44.44rpx;
}
.container .bottom .bottom_item {
  margin-top: 16.66rpx;
}
.container .bottom .bottom_item .info_label {
  display: inline;
  color: #007722;
  font-size: 33rpx;
}
.container .bottom .bottom_item .info_text {
  padding-left: 10rpx;
  font-size: 33rpx;
  display: inline;
}
.container .bottom .douban_url .info_text {
  color: #3377aa;
}
.container .intro {
  padding: 44.44rpx 0;
}
.container .intro .intro_label {
  color: #007722;
  font-size: 36.66rpx;
}
.container .intro .intro_text {
  padding-top: 22rpx;
  text-indent: 2rem;
}
.container .intro .intro_text text {
  font-size: 30rpx;
}
.container .statement {
  border-top: 1rpx solid #ccc;
  padding-top: 10rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  color: #aaa;
}
.container .statement .source {
  text-align: center;
  font-size: 24rpx;
}
.container .statement .tip {
  text-align: center;
  font-size: 22rpx;
}

收获总结

  这次作业总的来说并不是很困难,不过在做的过程中遇到了挺多困难的,出现这些困难的主要原因还是自己的编程习惯并不是很好,在思考时也没有先考虑完善再开始编写代码,而是想到哪写到哪,经过这次作业让我懂得了这样的做法是非常不对的,还是要把思路理清楚。

  总得来说这次作业让我学到了要善于查找资料,提高自己的资料查询能力和要善于思考,程序中有些实现难点和最初完成程序时免不了的许多不足就需要自己根据代码认真思考来解决。

  过程很艰辛,结果很满意。