uni-app项目之电影预告
目录
一、创建项目
开发工具:HBuilderX
Vue版本:Vue3
1.新建项目
2.构建基础页面
1.在pages目录下创建search、me页面
3.构建tabBar
tabBar主要在page.json里配置
"tabBar": {
"color": "#bfbfbf",
"selectedColor": "#515151",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabBarIco/index.png",
"selectedIconPath": "static/tabBarIco/index_sel.png"
},
{
"pagePath": "pages/search/search",
"text": "搜索",
"iconPath": "static/tabBarIco/search.png",
"selectedIconPath": "static/tabBarIco/search_sel.png"
},
{
"pagePath": "pages/me/me",
"text": "我的",
"iconPath": "static/tabBarIco/me.png",
"selectedIconPath": "static/tabBarIco/me_sel.png"
}
]
}
二、构建首页-轮播图
在App.vue里配置首页公共样式
<style>
/*每个页面公共css */
.page{
width: 100%;
height: 100%;
background-color: #f7f7f7;
}
</style>
1.使用轮播组件
<template>
<view class="page">
<swiper :indicator-dots="true" :autoplay="true" :circular="true" class="carousel">
<swiper-item>
<image src="/static/carousel/寒战.png" class="carousel"></image>
</swiper-item>
<swiper-item>
<image src="../../static/carousel/你的婚礼.png" class="carousel"></image>
</swiper-item>
<swiper-item >
<image src="../../static/carousel/无限战争.png" class="carousel"></image>
</swiper-item>
</swiper>
</view>
</template>
效果图:
2.禁用原生导航栏达到页面全屏化
在pages.json里通过配置 navigationStyle为custom,或titleNView为false,来禁用原生导航栏。
{
"path": "pages/index/index",
"style": {
// "navigationBarTitleText": ""
"app-plus": {
"titleNView": false
}
}
}
去掉原生导航栏前:
去掉原生导航栏后:
3.引用组件实现全局变量
- 创建common目录,新增common.js文件
const serverUrl_dev = "http://192.168.31.218:8091";
const serverUrl_prod = "https://www.baidu.com";
export default {
serverUrl_dev
}
- 引用组件
<script>
import common from "../../common/common.js"
export default {
data() {
return {
carouselList: []
}
},
onLoad() {
var me = this;
//获取common.js中的服务器地址
var serverUrl = common.serverUrl_dev;
// var serverUrl = me.serverUrl;
uni.request({
url: serverUrl + '/index/carousel/list',
method: 'GET',
success: (res) => {
var data = res.data;
console.log(data);
if(data.status==200){
var carouselList = data.content;
me.carouselList = carouselList;
}
}
})
},
methods: {
}
}
</script>
三、构建首页-热门电影
3.1 使用scoll-view组件
- template
<scroll-view scroll-x="true" class="page-block hot">
<view class="single-poster" v-for="hotMovie in hotMovieList">
<view class="poster-wrapper">
<image :src="hotMovie.poster" class="poster"></image>
<view class="movie-name">{{hotMovie.name}}</view>
<view class="movie-score-wrapper">
<image src="../../static/icons/star-yellow.png" class="star-ico"></image>
<image src="../../static/icons/star-yellow.png" class="star-ico"></image>
<image src="../../static/icons/star-yellow.png" class="star-ico"></image>
<image src="../../static/icons/star-yellow.png" class="star-ico"></image>
<image src="../../static/icons/star-white.png" class="star-ico"></image>
<view class="movie-score">{{hotMovie.score}}</view>
</view>
</view>
</view>
</scroll-view>
<!-- 热门电影 end -->
- js
//获取热门电影start
uni.request({
url:serverUrl + '/index/movie/selByType/hot',
method:"GET",
success: (res) => {
var data = res.data;
if(data.status == 200) {
var hotMovieList = data.content;
me.hotMovieList = hotMovieList;
}
}
})
//获取热门电影end
- css
/* 热门电影 start */
.movie-hot{
margin-top: 12rpx;
padding: 20rpx;
}
.hot-title-wrapper{
display: flex; /*flex布局*/
flex-direction: row;
}
.hot-ico{
width: 40rpx;
height: 40rpx;
margin-top: 5rpx;
}
.hot-title{
font-size: 20px;
margin-left: 15rpx;
font-weight: bold;
}
.hot{
width: 100%;
white-space: nowrap; /*文字不换行*/
}
.single-poster{
display: inline-block; /*内联块元素,从左到右依次排列,可以设定宽高*/
margin-left: 20rpx;
}
.poster-wrapper{
display: flex;
flex-direction: column;
}
.poster{
width: 200rpx;
height: 270rpx;
}
.movie-name{
width: 200rpx;
margin-top: 10rpx;
font-size: 14px;
font-weight: bold;
/* 名字超出则省略start */
white-space: nowrap;
overflow: hidden; /*内容被修剪,多余的内容被隐藏*/
text-overflow: ellipsis; /*显示省略符号 ... 来代表被修剪的文本*/
/* 名字超出则省略end */
}
.movie-score-wrapper{
display: flex;
flex-direction: row;
width: 200rpx;
}
.star-ico{
width: 30rpx;
height: 30rpx;
margin-top: 10rpx;
}
.movie-score{
font-size: 14px;
font-weight: thin;
margin-top: 6rpx;
margin-left: 2rpx;
color: grey; /*字体颜色*/
}
/* 热门电影 end */
3.2 开发自定义组件
-
在commpents目录下创建一个组件helloComp
-
引入组件
import helloComp from "../../components/helloComp.vue" //引用组件
- 注册组件
components:{ //注册组件
helloComp
}
- 使用组件
<!-- 使用组件 -->
<helloComp></helloComp>
3.3 父组件向自定义组件传递值
- 定义组件内部使用的属性
<template name="helloComp"> <!-- 定义组件的名称为helloComp -->
<view>
{{msg}}
<view>
<input type="text" :value="myVal" class="txt" />
</view>
</view>
</template>
<script>
export default {
// 定义组件的名称为helloComp
name:"helloComp",
data() {
return {
"msg": "你好,这是自定义组件~~~"
};
},
// 定义组件内部使用的属性
props: {
// 自定义一个变量,用于接受父组件(首页或其他页面)传入的参数值
myVal: {
type: String //定义这个参数的类型
}
}
}
</script>
- 父组件使用参数会传值
<!-- 使用组件 -->
<helloComp myVal="hello,电影预告"></helloComp>
3.4 完成评分自定义组件
1.在components目录新增评分组件score.vue
<template name="score">
<view class="movie-score-wrapper">
<view v-if="showNumber == 1" class="starScore">
<image v-for="yellow in yellowStars"
src="../static/icons/star-yellow.png" class="star-ico"></image>
<image v-for="white in whiteStars"
src="../static/icons/star-white.png" class="star-ico"></image>
<view class="movie-score">{{innerScore}}</view>
</view>
</view>
</template>
<script>
export default {
name:"score",
data() {
return {
yellowStars: 0,
whiteStars: 5
};
},
props: {
// innerScore: {
// type: Number
// },
// showNum: {
// type: Number
// }
innerScore: 0, //接收父组件传递的值,默认为0
showNumber: 0 //是否显示评分,1为显示,0为不显示
},
created() {
var tempScore = 0;
// debugger;
if (this.innerScore !== null && this.innerScore !== undefined && this.innerScore > 0){
tempScore = this.innerScore;
}
var yellowStars = parseInt(tempScore/2);
var whiteStars = 5 - yellowStars;
this.yellowStars = yellowStars;
this.whiteStars = whiteStars;
}
}
</script>
<style>
.movie-score-wrapper{
display: flex;
flex-direction: row;
width: 200rpx;
}
.star-ico{
width: 30rpx;
height: 30rpx;
margin-top: 10rpx;
}
.movie-score{
font-size: 14px;
font-weight: thin;
margin-top: 6rpx;
margin-left: 2rpx;
color: grey; /*字体颜色*/
}
.starScore{
display: flex;
flex-direction: row;
}
</style>
- 导入score组件
import score from "../../components/score.vue"
- 注册组件
components: { //注册组件
helloComp,
score
}
- 使用组件
<score :innerScore="hotMovie.score" showNumber="1"></score>
构建首页-热门预告
构建首页-猜你喜欢
1. 编写flex布局
template:
<!-- 猜你喜欢start -->
<view class="page-block movie-hot">
<view class="hot-title-wrapper">
<image src="../../static/icons/guess-like.png" class="hot-ico"></image>
<view class="hot-title">猜你喜欢</view>
</view>
</view>
<view class="page-block guess-like">
<view class="guess-like-wrapper">
<image src="../../static/poster/信条.png" class="guess-like-poster"></image>
<view class="guess-like-info">
<view class="guess-movie-name">我的祖国我的祖国我的祖国我的祖国</view>
<score :innerScore="9.1" showNumber="0"></score>
<view class="guess-movie-type">2018 / 美国 / 科幻 / 超级英雄</view>
<view class="guess-movie-time">上映:2022-11-17(中国大陆)</view>
<view class="guess-movie-author"> 张国荣/张丰毅/巩俐/葛优/英达 </view>
</view >
<view class="guess-like-praise">
</view>
</view>
</view>
<!-- 猜你喜欢end -->
css:
/* 猜你喜欢start */
.guess-like{
display: flex;
flex-direction: column;
}
.guess-like-wrapper{
display: flex;
flex-direction: row;
padding: 30rpx 20rpx;
justify-content: space-between;
}
.guess-like-poster{
width: 180rpx;
height: 240rpx;
border-radius: 3%;
}
.guess-like-info{
width: 340rpx;
display: flex;
flex-direction: column;
}
.guess-movie-name{
/* font-size: 14px; */
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.guess-movie-type{
font-size: 14px;
color: #808080;
}
.guess-movie-time{
font-size: 14px;
color: #808080;
}
.guess-movie-author{
font-size: 14px;
color: #808080;
}
/* 猜你喜欢end */
2. 实现点赞动画效果
template:
<view class="guess-like-praise" @click="praiseMe">
<image src="../../static/icons/praise.png" class="praise-ico"></image>
<view class="praise-me">点赞</view>
<view class="praise-me praise-me-opacity" :animation="animationData">+1</view>
</view>
css:
.guess-like-praise {
width: 140rpx;
display: flex;
flex-direction: column;
justify-content: center;
border-left: 2px dashed lightgrey;
}
.praise-ico {
width: 50rpx;
height: 50rpx;
align-self: center;
}
.praise-me {
font-size: 14px;
align-self: center;
color: orange;
}
.praise-me-opacity{
font-weight: bold;
opacity: 0;
}
js:在methods里面添加
//实现点赞动画效果start
praiseMe(){
//构建动画数据,并且通过step来表示这组动画的完成
this.animation.translateY(-60).opacity(1).step({
duration: 400
});
//导出动画数据到view组件,实现组件的动画效果
this.animationData = this.animation.export();
//还原动画
setTimeout(function(){
this.animation.translateY(0).opacity(0).step({
"timingFunction": "step-start"
});
this.animationData = this.animation.export();
}.bind(this),500)
},
//实现点赞动画效果end
3.还原动画
//还原动画
setTimeout(function(){
this.animation.translateY(0).opacity(0).step({
"timingFunction": "step-start"
});
this.animationData = this.animation.export();
}.bind(this),500)
4.动态渲染列表
template
<!-- 猜你喜欢start -->
<view class="page-block movie-hot">
<view class="hot-title-wrapper">
<image src="../../static/icons/guess-like.png" class="hot-ico"></image>
<view class="hot-title">猜你喜欢</view>
</view>
</view>
<view class="page-block guess-like" >
<view class="guess-like-wrapper" v-for="(guessLike,gIndex) in guessLikeList" :key="guessLike.id">
<image :src="guessLike.cover" class="guess-like-poster"></image>
<view class="guess-like-info">
<view class="guess-movie-name">{{guessLike.name}}</view>
<score :innerScore="guessLike.score" showNumber="0"></score>
<view class="guess-movie-type">{{guessLike.baseInfo}}</view>
<view class="guess-movie-time">{{guessLike.releaseDate}}</view>
</view >
<view class="guess-like-praise" :data-gIndex="gIndex" @click="praiseMe">
<image src="../../static/icons/praise.png" class="praise-ico"></image>
<view class="praise-me">点赞</view>
<view class="praise-me praise-me-opacity" :animation="animationDataArr[gIndex]">+1</view>
</view>
</view>
</view>
<!-- 猜你喜欢end -->
js:方法写在methods里面
// 猜你喜欢start
guessLike(){
var me = this;
//显示loading
uni.showLoading({
mask: true
});
//显示导航栏的loading
uni.showNavigationBarLoading();
var serverUrl = common.serverUrl_dev;
uni.request({
url:serverUrl + '/index/movie/guessLike',
method:"GET",
success: (res) => {
var data = res.data;
if(data.status == 200) {
var guessLikeList = data.content;
me.guessLikeList = guessLikeList;
}
},
complete() {
uni.hideLoading(); //隐藏loading
uni.hideNavigationBarLoading(); //隐藏导航条的loading
uni.stopPullDownRefresh(); //停止刷新
}
})
},
// 猜你喜欢end
5. 实现动画数组(修复多个电影同时点赞问题)
js
//实现点赞动画效果start
praiseMe(e){
// console.log(e);
var gIndex = e.currentTarget.dataset.gindex;
//构建动画数据,并且通过step来表示这组动画的完成
this.animation.translateY(-60).opacity(1).step({
duration: 400,
timingFunction: "ease"
});
//导出动画数据到view组件,实现组件的动画效果
// this.animationData = this.animation.export();
this.animationDataArr[gIndex] = this.animation.export();
//还原动画
setTimeout(function(){
this.animation.translateY(0).opacity(0).step({
timingFunction: "step-start"
});
// this.animationData = this.animation.export();
this.animationDataArr[gIndex] = this.animation.export();
}.bind(this),500)
},
//实现点赞动画效果end
template
<view class="guess-like-praise" :data-gIndex="gIndex" @click="praiseMe">
<image src="../../static/icons/praise.png" class="praise-ico"></image>
<view class="praise-me">点赞</view>
<view class="praise-me praise-me-opacity" :animation="animationDataArr[gIndex]">+1</view>
</view>
下拉刷新
- 在pages.json里开启下拉刷新功能
{
"path": "pages/index/index",
"style": {
"enablePullDownRefresh": true, //开启下拉刷新功能
// "navigationBarTitleText": ""
"app-plus": {
// "titleNView": false .//禁用原生导航栏
"titleNView": {
"type": "transparent"
}
}
}
}
- 监听下拉刷新动作
onPullDownRefresh() {
// console.log('refresh');
//下拉刷新猜你喜欢数据start
this.guessLike();
//下拉刷新猜你喜欢数据end
},
构建搜索页面
静态搜索页面及搜索框固定在顶部
1.template
<template>
<view class="page">
<view clas="search-block page-block">
<view class="search-ico-wrapper">
<image src="../../static/icons/search.png" class="search-ico"></image>
<input class="search-text" type="text" maxlength="10" placeholder="请输入电影名" focus/>
</view>
</view>
<view class="movie-list page-block">
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
<view class="movie-wrapper">
<image src="http://192.168.31.218:8091/images/movie/1/cover/1.png" class="moive-poster"></image>
</view>
</view>
</view>
</template>
2.css
/* 搜索框start */
.search-block {
display: flex;
flex-direction: row;
padding: 0rpx 20rpx 20rpx 20rpx;
}
.search-ico-wrapper {
background-color: #eaeaea;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
padding: 0rpx 10rpx;
width: 680rpx;
margin-left: 20rpx;
/* 搜索框悬浮 */
position: fixed;
top: 100;
z-index: 2000;
}
.search-ico {
width: 40rpx;
height: 40rpx;
/* display: flex;
flex-direction: column;
justify-content: center; */
}
.search-text {
font-size: 14px;
background-color: #eaeaea;
height: 60rpx;
width: 680rpx;
margin-left: 15rpx;
/* flex: 1; */
}
/* 搜索框end */
/* 电影列表start */
.page-block {
background-color: #ffffff;
}
.movie-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
padding: 80rpx 10rpx 10rpx 10rpx;
}
.movie-wrapper {
/* margin: 10rpx; */
padding: 10rpx 20rpx;
}
.moive-poster {
width: 200rpx;
height: 270rpx;
}
/* 电影列表end */
动态获取电影列表
script
let me = this;
let pageNum = 1;
me.pageNum = pageNum;
uni.showLoading({
mask:true,
title: 'onShow加载中...',
});
uni.showNavigationBarLoading();
let serverUrl = common.serverUrl_dev;
// 搜索页获取电影列表start
uni.request({
url: serverUrl + '/search/queryPageList',
data: {
name: me.name,
pageNum: me.pageNum,
pageSize: me.pageSize
},
success: (res) => {
console.log('onshow加载...')
let data = res.data;
if (data.code == 200) {
me.movieList = data.data.rows;
me.total = data.data.total;
}
},
complete() {
uni.hideLoading();
uni.hideNavigationBarLoading();
}
})
// 搜索页获取电影列表start
template
<template>
<view class="page">
<view clas="search-block page-block">
<view class="search-ico-wrapper">
<image src="../../static/icons/search.png" class="search-ico"></image>
<input
class="search-text"
confirm-type="search"
maxlength="10"
placeholder="请输入电影名"
@confirm="searchMovie"/>
</view>
</view>
<view class="movie-list page-block">
<view class="movie-wrapper" v-for="movie in movieList">
<image :src="movie.cover" class="moive-poster"></image>
</view>
</view>
</view>
</template>
分页查询
script
onReachBottom() {
let me = this;
let pageNum = me.pageNum + 1;
let name = me.name;
let totalPages = (me.total%me.pageSize) > 0 ? (me.total/me.pageSize)+1 : (me.total/me.pageSize);
console.log('totlal:'+ me.total + ',pageSize:' + me.pageSize + ',totalPages:' + totalPages + ',pageNum:' + pageNum)
if (pageNum > totalPages) {
return;
}
me.queryPageList(name, pageNum, me.pageSize);
}
queryPageList(name, pageNum, pageSize) {
var me = this;
// 显示loading条
uni.showLoading({
mask: true,
title: '加载中...'
})
uni.showNavigationBarLoading();
var url = common.serverUrl_dev;
// 搜索页获取电影列表start
uni.request({
url: url + "/search/queryPageList",
data: {
name: name,
pageNum: pageNum,
pageSize: pageSize
},
method: 'GET',
success: (res) => {
var data = res.data;
if (200 == res.data.code) {
let tempList = data.data.rows;
me.movieList = me.movieList.concat(tempList);
me.total = data.data.total; //获取总记录数
me.pageNum = pageNum; //当前页数
}
},
complete() {
uni.hideLoading();
uni.hideNavigationBarLoading();
}
})
// 搜索页获取电影列表end
}
页面路由api与传参
template:
<view class="movie-wrapper" v-for="movie in movieList">
<image
:src="movie.cover"
:data-movieId="movie.id"
@click="movieDetail"
class="moive-poster">
</image>
</view>
script:
// 电影详情页start
movieDetail(e) {
console.log(e);
let movieId = e.currentTarget.dataset.movieid;
uni.navigateTo({
url:"/pages/movieDetail/movieDetail?id=" + movieId
})
},
// 电影详情页end