簡單電影推薦小程序


博客班級 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;
}

收獲總結

  這次作業總的來說並不是很困難,不過在做的過程中遇到了挺多困難的,出現這些困難的主要原因還是自己的編程習慣並不是很好,在思考時也沒有先考慮完善再開始編寫代碼,而是想到哪寫到哪,經過這次作業讓我懂得了這樣的做法是非常不對的,還是要把思路理清楚。

  總得來說這次作業讓我學到了要善於查找資料,提高自己的資料查詢能力和要善於思考,程序中有些實現難點和最初完成程序時免不了的許多不足就需要自己根據代碼認真思考來解決。

  過程很艱辛,結果很滿意。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM