- 实验目的
- 掌握小程序基础组件使用;
- 掌握小程序基本开发,能够实现仿豆瓣电影项目的开发;
- 实验准备与环境
- PC
- Windows操作系统
- 微信开发者工具
- 实验要求
- 学习如何应用小程序基础组件设计开发仿豆瓣电影项目;
- 实验步骤
功能要求:设计一款模拟“豆瓣电影”的小程序,要求能够实现底部Tab切换效果、海报轮播效果、列表方式布局、电影详情页面布局等功能。
完整代码实现(包含代码和运行截图)
Index.wxll
<wxs src="../../utils/tool.wxs" module="tool"></wxs>
<view class="box">
<!-- 静态轮播图 -->
<view class="banner">
<swiper indicator-dots indicator-color="rgba(255,255,255,0.5)" indicator-active-color="#fff" circular autoplay interval="4000">
<swiper-item>
<image src="/images/images/1.jpg"></image>
</swiper-item>
<swiper-item>
<image src="/images/images/2.jpg"></image>
</swiper-item>
<swiper-item>
<image src="/images/images/3.jpg"></image>
</swiper-item>
<swiper-item>
<image src="/images/images/4.jpg"></image>
</swiper-item>
</swiper>
</view>
<!-- 今日热映 -->
<view>
<h3 class="hotscreening1">今日热映</h3>
</view>
<!-- 电影列表 -->
<view class="bigbox">
<view class="section" wx:for="{{movies}}" wx:key="id">
<view class="top" bind:tap="goMovieDetail" data-id="{{item.id}}">
<image class="movieImg" src="{{item.img}}"></image>
</view>
<view class="bottom">
<text class="moveTitle">{{item.nm}}</text>
<view class="star-rating">
<!-- 显示电影评分的星星 -->
<block wx:for="{{tool.convertToStarsArray(item.sc)}}" wx:key="index">
<image src="/images/images/stars.png" class="star" />
</block>
<!-- 显示电影评分 -->
<text class="movie-score">{{item.sc}}</text>
</view>
</view>
</view>
</view>
</view>
Index.js
Page({
data: {
movies:[]
},
onReady() {
let that = this;
wx.request({
url: 'https://m.maoyan.com/ajax/movieOnInfoList',
success (res) {
that.setData({movies:res.data.movieList})
}
})
},
goMovieDetail(e) {
let id = e.currentTarget.dataset.id;
wx.navigateTo({
url: '/pages/moviedetail/moviedetail?id=' + id,
})
}
})
Index.wxss
/* 主页整体样式 */
.box{
padding: 10px;
}
/* 轮播图样式 */
.banner{
width: 100%;
height: 140rpx;
border-radius: 20rpx;
}
.banner swiper{
height: 120rpx;
width: 100%;
}
.banner image{
width: 100%;
height:50px;
border-top-left-radius: 10rpx;
border-top-right-radius: 10rpx;
}
/* 标题样式 */
.hotscreening1{
margin-top: 5rpx;
font-weight: bold;
font-size: 50rpx;
}
/*
电影方框样式 */
image{
width: 100%;
height:50px;
/* border-radius: 10rpx; */
border-top-left-radius: 10rpx;
border-top-right-radius: 10rpx;
}
.title{
display: block;
margin: 18rpx 0;
font-size: 15pt;
font-weight: 500
}
.bigbox{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.section{
flex: 0 0 31.3%;
border-radius: 10rpx;
box-sizing: border-box;
margin-right:3rpx;
margin-bottom:18rpx;
padding-top:10rpx;
text-align: center;
padding-top: 0;
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;
background-color: white;
}
.section:nth-child(3n+3){
margin-right:0;
}
.bottom{
padding-bottom: 10rpx;
padding-left: 6rpx;
padding-right: 6rpx;
box-sizing: border-box;
}
.movieImg{
height: 300rpx;
}
.moveTitle{
font-size: 10pt;
overflow: hidden;
font-weight: 700;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1; /*3表示只显示3行*/
-webkit-box-orient: vertical;
}
/* 五星样式开始 */
.star-rating {
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 从行首开始排列 */
vertical-align: middle;
}
.star {
margin-right: 5rpx;
width: 12px; /* 调整为需要的尺寸 */
height: 12px; /* 调整为需要的尺寸 */
display: inline-block; /* 确保图标按行内块级元素显示 */
}
.movie-score {
margin-left: 6rpx; /* 评分文字与星星图标的间隔 */
font-size:10pt;
color: #666; /* 可以根据需要调整颜色 */
}
/* 五星样式结束 */
Moviesdeail.wxml
<wxs src="../../utils/tool.wxs" module="tool"></wxs>
<view class="main">
<!-- 头部 -->
<view class="bannerTop">
<view class="left">
<image src="{{movieDetail.img}}" mode="widthFix" class="mimg" />
</view>
<view class="right">
<view class="top">
<text class="mName">{{movieDetail.nm}}</text>
<text class="engName">{{movieDetail.enm}}</text>
</view>
<view class="middle">
<text class="mSubject">{{movieDetail.typeDesc}}/{{movieDetail.cat}}</text>
<text class="locatimes">{{movieDetail.src}}/片长{{movieDetail.dur}}分钟</text>
<text class="stars">{{movieDetail.star}}</text>
<!-- 显示电影评分的星星 -->
<view class="star-rating ">
<block wx:for="{{tool.convertToStarsArray(movieDetail.sc)}}" wx:key="index">
<image src="/pages/images/Start/{{item}}.png" class="star" />
</block>
<!-- 显示电影评分 -->
<text class="movie-score">{{movieDetail.sc}} 分</text>
</view>
</view>
<view class="bottom">
<text class="releaseDate">{{movieDetail.pubDesc}}</text>
</view>
</view>
</view>
<!-- 简介 -->
<!-- 提供文字展开功能 -->
<view class="bannerCenter">
<h3>简介</h3>
<view class="contentInner showArea {{!onFold ? 'text-clamp' + 2 : ''}}" bindtap="{{foldable ? 'handleFold' : ''}}" id="show">
{{movieDetail.dra || "示例文本"}}
</view>
<view class="contentInner hideArea" id="hide">{{movieDetail.dra || "示例文本"}}</view>
<view class="foldInner flex-end" wx:if="{{showFold}}">
<text class="fold" catchtap="handleFold">{{onFold ? "收起" : "展开"}}</text>
</view>
</view>
<!-- 演示剧照 -->
<view class="bannerSection1">
<h3>剧照</h3>
<view class="scroll">
<view class="page-section">
<view class="page-section-spacing">
<scroll-view class="scroll-view_H" scroll-x="true" style="width: 100%">
<view class="scroll-view-item_H " wx:for="{{movieDetail.photos}}" wx:key="index">
<image src="{{item}}" mode="heightFix" class="stagePhoto" bindtap="photoView" data-image="{{item}}"></image>
</view>
</scroll-view>
</view>
</view>
</view>
<view>
</view>
</view>
<!-- 预告片 -->
<view class="video-section">
<h3>预告片</h3>
</view>
<video id="myVideo" src="{{videoSrc}}" show-center-play-btn='{{true}}' show-play-btn="{{true}}" controls style="width: 100%;"></video>
</view>
Moviesdeail.js
// pages/detail/detail.js
Page({
/**
* 页面的初始数据
*/
data: {
id: "",
movieDetail: {},
previewImage: [],
onFold: false,
showFold: false,
onReady: false,
foldable: true,
videoSrc: "",
mtitle: ""
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.setData({
id: options.id
});
setTimeout(() => {
this.getMovieDetail();
}, 500)
},
photoView(e) {
console.log(e)
const photos = e.currentTarget.dataset.image;
wx.previewImage({
current: 'photos', // 当前显示图片的http链接
success(res) {
console.log("ok")
}
})
},
//获取电影信息
getMovieDetail() {
let that = this;
wx.request({
url: 'https://m.maoyan.com/ajax/detailmovie?movieId=' + this.data.id,
header: {
'content-type': 'application/json' // 默认值
},
success(res) {
console.log(res.data.detailMovie);
that.setData({
movieDetail: res.data.detailMovie,
previewImage: res.data.detailMovie.photos,
videoSrc: res.data.detailMovie.vd,
})
wx.setNavigationBarTitle({
title: res.data.detailMovie.nm
});
}
})
},
//点击图片预览
photoView(e) {
let that = this;
let image = e.currentTarget.dataset.image
wx.previewImage({
current: image, // 当前显示图片的http链接
urls: that.data.previewImage // 需要预览的图片http链接列表
})
},
// 展开折叠按钮
handleFold() {
this.setData({
onFold: !this.data.onFold
})
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
this.data.onReady = false;
},
// 获取高度,用于显示展开折叠 两个文字
getHeight(){
const query = wx.createSelectorQuery().in(this);
query.selectAll(".showArea,.hideArea").boundingClientRect(res => {
this.setData({
showFold: res[0].height < res[1].height
})
console.log(res)
}).exec()
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
setTimeout(()=>{
this.getHeight();
},800)
}
})
Moviedeail.wwxss
page{
background-color: #14a720e0;
color: aliceblue;
padding-bottom: 20rpx;
}
.main {
padding: 30rpx;
}
.bannerTop {
display: flex;
box-sizing: border-box;
position: relative;
}
.bannerSection1{
margin-top: 15rpx;
margin-bottom: 15rpx;
}
.video-section{
margin-bottom: 20rpx;
}
.bannerTop>view {
flex: 0 0 60%;
}
.mimg {
width: 100%;
border-radius: 10rpx;
}
.bannerTop>.left {
flex: 1;
}
.bannerTop>.right {
margin-left: 20rpx;
display: flex;
flex-direction: column;
align-content: space-around;
justify-content: space-evenly;
}
.bannerTop>.right text {
display: block;
}
.mName {
font-size: 13pt;
font-weight: 700;
color: aliceblue;
}
.right>.top {
/* justify-content: space-evenly; */
color: rgb(255, 255, 255);
font-size: 9pt;
font-weight: 500;
line-height: 1.5;
}
.engName {
font-size: 11pt;
font-weight: 600;
}
h3 {
line-height: 2.2;
font-weight: 700;
font-size: 14pt;
}
.mSubject,.locatimes{
font-size: 10pt;
}
.releaseDate{
font-size: 10pt;
}
.stars{
font-size: 11pt;
}
/* 文字省略展开 */
.contentInner {
width: 690rpx;
color: #ffffff;
font-size: 30rpx;
line-height: 1.35;
text-align: justify;
box-sizing: border-box;
}
.hideArea {
box-sizing: border-box;
display: -webkit-box;
overflow: hidden;
position: fixed;
top: 100vh;
left: -100vw;
}
.foldInner {
width: 690rpx;
padding-top: 10rpx;
transition: 1s all;
}
.foldInner .fold {
color: #ffffff;
font-size: 32rpx;
cursor: pointer;
}
.text-clamp2 {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.flex-end {
display: -webkit-box;
display: -webkit-flex;
display: flex;
justify-content: flex-end;
-webkit-justify-content: flex-end;
}
/* 滚动条 */
.scroll-view_H{
white-space: nowrap;
}
.scroll-view-item_H{
display: inline-block;
padding: 10rpx;
box-sizing: border-box;
/* width: 100%; */
}
.stagePhoto{
height: 300rpx;
}
/* 五星样式开始 */
/* 五星样式开始 */
.star-rating {
display: flex; /* 使用Flex布局 */
align-items: center; /* 垂直居中 */
justify-content: flex-start; /* 从行首开始排列 */
vertical-align: middle;
/* margin-bottom: 15rpx;
margin-top: 15rpx; */
}
.star {
margin-right: 5rpx;
width: 15px; /* 调整为需要的尺寸 */
height: 15px; /* 调整为需要的尺寸 */
display: inline-block; /* 确保图标按行内块级元素显示 */
}
.movie-score {
margin-left: 5rpx; /* 评分文字与星星图标的间隔 */
font-size: 10pt;
color: rgb(255, 255, 255); /* 可以根据需要调整颜色 */
}
/* 五星样式结束 */
Search.wxml
<view class="containers">
<view class="searchBox">
<view class="search-container">
<input class="search-input" type="text" placeholder="请输入您要搜索的电影" model:value="{{searchKey}}" bindconfirm="searchMovie" />
<button class="search-btn" bindtap="searchMovie">搜索</button>
</view>
<view class="searchHistory" wx:if="{{searchList.length==0}}">
<view class="title">暂时没有搜索记录</view>
</view>
</view>
<view class="cons" wx:if="{{searchList.length>0}}">
<view >
<view class="tops">
<view class="title">搜索历史</view>
<view bind:tap="clearHistory" class="clear">清空历史</view>
</view>
<view class="bottoms">
<block wx:for="{{searchList}}" wx:key="index">
<view class="c_item" data-index="{{index}}" bindlongpress="deleteSearchOne" bind:tap="getSearchOne">{{item}}</view>
</block>
</view>
</view>
</view>
</view>
Search.js
Page({
data: {
searchKey: "",
searchList: [],
movieList:[]
},
onLoad(options) {
},
onReady() {
if (wx.getStorageSync('search_history')) {
console.log(JSON.parse(wx.getStorageSync('search_history')));
this.setData({
searchList: JSON.parse(wx.getStorageSync('search_history'))
})
} else {
wx.setStorageSync('search_history', JSON.stringify(this.data.searchList))
}
},
// 搜索按钮
searchMovie() {
let that = this;
this.data.searchList.forEach((item, index) => {
if (item == this.data.searchKey) {
this.data.searchList.splice(index, 1);
}
})
this.data.searchList.unshift(this.data.searchKey);
this.setData({
searchList: this.data.searchList.slice(0, 15)
})
wx.setStorageSync('search_history', JSON.stringify(this.data.searchList))
that.goSearchDetail();
},
goSearchDetail(){
let that = this;
wx.navigateTo({
url: '/pages/SearchDetail/SearchDetail?searchName='+that.data.searchKey,
success: function(res) {
console.log("成功打开详情页")
}
})
},
// 点击了搜索历史
getSearchOne(e) {
let {
index
} = e.currentTarget.dataset, {
searchList
} = this.data;
let va = searchList[index]
this.setData({
searchKey: va
})
this.searchMovie()
},
//删除搜索历史
deleteSearchOne(e) {
let that = this;
let {
index
} = e.currentTarget.dataset, {
searchList
} = this.data;
let va = searchList[index];
wx.showModal({
title: '提示',
content: '删除该记录?',
success(res) {
if (res.confirm) {
that.data.searchList.forEach((item, index) => {
if (item == va) {
that.data.searchList.splice(index, 1);
}
})
that.setData({
searchList: that.data.searchList.slice(0, 15)
})
wx.setStorageSync('search_history', JSON.stringify(that.data.searchList))
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
},
clearHistory() {
console.log(111)
this.setData({
searchList: []
})
wx.removeStorageSync('search_history')
},
})
Search.wxss
.containers{
padding: 20rpx;
}
.search-container {
display: flex;
}
.search-input {
flex: 2;
border: 1px solid #ddd;
padding: 5px 10px;
border-radius: 3px;
margin-right: 10px;
}
.search-btn {
flex: 0.5;
padding: 5px 10px;
background-color: #198aad;
color: white;
border: none;
border-radius: 3px;
white-space: nowrap; /* 防止文本换行,如果按钮文本较长的话 */
}
.searchHistory{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 80vh;
font-size: 15pt;
font-weight: 500;
}
image{
width:30vw;
}
/* 搜索历史 */
.cons{
padding: 15rpx;
box-sizing: border-box;
margin-top: 15rpx;
}
.tops{
display: flex;
justify-content: space-between;
margin-bottom: 15rpx;
}
.bottoms{
display: flex;
flex-direction: row;
margin-top: 20rpx;
}
.title{
font-size: 13pt;
font-weight: 600;
color: #4e5050;
}
.clear{
color: #999;
font-size: 13pt;
}
.c_item{
font-size: 12pt;
height: 58rpx;
line-height: 58rpx;
padding: 0 26rpx;
background: rgba(34, 37, 158, 0.171);
border-radius: 31rpx;
justify-content: center;
margin-bottom: 24rpx;
margin-right: 24rpx;
white-space:pre;
color: #999;
}
.list{
margin-top: 40rpx;
flex-wrap: wrap;
}
.color{
color: #83B7FD;
}
Home.wxml
<!-- index.wxml -->
<view class="container">
<view class="user-info">
<image class="avatar" src="{{avatarUrl}}"></image>
<text class="nickname">{{nickname}}</text>
</view>
<view>
<text>收藏的电影</text>
<view>
<block wx:for="{{movies}}" wx:key="index">
<text>{{movies.img}}</text>
</block>
</view>
</view>
<view class="section">
<text class="section-title">观看记录</text>
<view class="movie-list">
<block wx:for="{{watchedMovies}}" wx:key="index">
<text>{{item}}</text>
</block>
</view>
</view>
</view>
Home.js
// 在Page({})中编写页面逻辑
Page({
data: {
avatarUrl: 'https://p2.music.126.net/9FhSEQtMhP-JP3_U84YfWQ==/109951165798773745.jpg?param=130y130',
nickname: '李泉城',
movies:[]
},
onReady() {
let that = this;
wx.request({
url: 'https://m.maoyan.com/ajax/movieOnInfoList',
success (res) {
that.setData({movies:res.data.movieList})
}
})
}
});
Home.wxss
.container {
padding: 20rpx;
margin-left: 10rpx;
display: flex;
flex-direction: column;
align-items: flex-start;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 20rpx;
margin-left: 50rpx;
}
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.nickname {
font-size: 24rpx;
}
.section {
margin-bottom: 20rpx;
}
.section-title {
font-size: 20rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.movie-list {
display: flex;
flex-wrap: wrap;
}
.movie-list text {
padding: 5rpx 10rpx;
margin-right: 10rpx;
margin-bottom: 10rpx;
background-color: #f0f0f0;
border-radius: 5rpx;
}