
一.由于做移动端,所以先根据设计稿设置好设置rem
二、做轮播图
2.使用swiper引入轮播图
1)现在flims文件下自定义film-swiper组件,来显示大轮播,注意加上key值,不然会加载过早。
2)自定义一个film-swiper-item组件包裹着轮播数据
创建film-swiper和film-swiper-item组件
film-swiper-ltem组件
注意要引入swiper插件
导入组件flims页面
3.使用axios获取轮播图信息
这个json文件是本地保存的,为了就是方便设置轮播图
实现效果
三、做底部选项卡
1.先创建Tabbar组件,使用声明式导航,点击转去点击页面,active-class是动态给节点加入样式,当点击时候就给class加上样式
电影
影院
我的
在app页面上引入Tabbar组件
效果图
四、电影导航组件
1.创建swiper-header组件
正在热映
即将上映
整体效果
五、正在热映的数据获取
由于网站没有跨域限制,所以可以拿取数据
不过需要设置请求头
所以使用axios进行封装
const http = axios.create({
baseURL: 'https://m.maizuo.com', //每次访问就是maizuo.com
timeout: 10000, //10秒超时
headers: {
'X-Client-Info':
'{"a":"3000","ch":"1002","v":"5.2.0","e":"16481780177384775358676993","bc":"440100"}'
}
})
可以获取到数据
接下来就是在正在热映的页面使用获取回来的数据
先把得到的电影flims数据给datalist
http({
url: `/gateway?cityId=${this.$store.state.cityId}&pageNum=1&pageSize=10&type=1&k=7388950`,
headers: {
'X-Host': 'mall.film-ticket.film.list'
}
}).then((res) => {
this.datalist = res.data.data.films
})
{{ data.name }}
观众评分:{{ data.grade }}
不过注意这个这里的演员数据是以对象形式存在,不能直接使用
所以必须给数据加上过滤器
主演:{{ data.actors | actorsFilter }}
使用map把item.name映射给item
Vue.filter('actorsFilter', (data) => {
return data.map((item) => item.name).join(' ')
})
得到的效果
六、详情页的制作
点击数据之后跳转到详情页面,并且把filmid传过去,确定是那个页面被打开
this.$router.push({
name: 'bee',
params: {
id
}
})
在detail页面上, $route当前匹配的路由; axios利用id发请求到详情接口,获取详细数据,布局页面
http({
url: `/gateway?filmId=${this.$route.params.id}&k=106604`,
headers: {
'X-Host': ' mall.film-ticket.film.info'
}
}).then((res) => {
console.log(res.data.data.film)
this.filmInfo = res.data.data.film
})
使用的数据进行页面的布局
上映的时间,不过的到的是秒数 需要二次转换加一个过滤器
{{ filmInfo.category }}
{{ filmInfo.premiereAt | dataFilter }}
{{ filmInfo.nation }} | {{ filmInfo.runtime }}分钟
{{ filmInfo.synopsis }}
//是否显示和隐藏的功能
之后得到页面是这样
演职人员:
使用轮播制作 数据的话直接套用上面获取的数据 和之前的swiper 一样的方法
不过注意下面的剧照,也需要使用轮播图,这时候就是两个页面同时使用一个轮播图组件,所以为了避免冲突要使用动态名,只需要把传过去的名改变就可以避免轮播冲突
swiper虽然用的同一组件,但是给的名称name不一样,不会导致轮播冲突
new Swiper('.' + this.name, {
slidesPerView: this.perview, /* 一页显示多少个 支持小数 */
spaceBetween: 30, /* 间隔多少 */
freeMode: true
演职人员
{{ data.name }}
{{ data.role }}
显示的效果:
做一个滚动50距离就显示头部的信息
创建swiper-header组件
点击小箭头就返回原来的页面
methods: {
handleBack () {
this.$router.back()
}
在detail中插入swiper-header组件
{{ filmInfo.name }}
自定也组件scroll
/* 创建往下滚动时候,出现顶部的返回栏 el得到原生dom节点(谁用谁绑定) */
Vue.directive('scroll', {
inserted (el, binding) {
/* 把原生dom的节点一开始隐藏 */
el.style.display = 'none'
window.onscroll = () => {
/* 兼容性问题 */
if (
/* 当滚动距离大于50时,就开始显示 binding.value是传过来的值 50*/
(document.documentElement.scrollTop || document.body.scrollTop) >
binding.value
) {
el.style.display = 'block'
} else {
el.style.display = 'none'
}
}
},
/* 指令也是有声明周期的 当退出时把指令销毁 */
unbind () {
window.onscroll = null
}
})
页面效果
预览图片功能
通过npm安装vant
使用vant实现图片预览功能,引入
ImagePreview
给剧照加一个点击事件
@click="handlePreview(index)
handlePreview (index) {
ImagePreview({
images: this.filmInfo.photos,
/* 预览的图片 */
startPosition: index,
/* 传入的初始位置 点击谁 显示谁 */
closeable: true,
/* 关闭按钮 */
closeIconPosition: 'top-left'
/* 关闭按钮的位置 */
})
}
效果图
影院的组件
先使用axios获取数据
http({
url: `/gateway?cityId=${this.$store.state.cityId}&ticketFlag=1&k=9755784`,
headers: {
"X-Host": "mall.film-ticket.cinema.list",
},
}).then((res) => {
this.cinemaList = res.data.data.cinemas;
}}
{{ data.name }}
{{ data.address }}
¥{{ data.lowPrice / 100 }}起
显示的效果:
使用vant插件,渲染出影院页面的头部
所以使用vant创建左边的地方和右边搜索框
广州
数据懒加载
使用vant里面的list实现数据懒加载功能
list组件说明:
List 组件通过 loading 和 finished 两个变量控制加载状态,当组件滚动到底部时,会触发 load 事件并将 loading 设置成 true。此时可以发起异步 *** 作并更新数据,数据更新完毕后,将 loading 设置成 false 即可。若数据已全部加载完毕,则直接将 finished 设置成 true 即可。
在nowplaying中加入
注意要让loading和finish一开始状态是false。loading为false的话,可以加载;finish为false的话,表示并没有结束,实现懒加载功能;
data () {
return {
loading: false,
finished: false,
current: 1, //返回的页数
total: 0
}
加入list之后会直接显示一次到底了
解决的方法,从手册可知道
所以加上一个immediate-check 注意给的是boolean值,所以在前面加上:
:immediate-check="false" //第一次默认不检查
问题解决了
到底之后再次使用axios请求数据
http({
url: `/gateway?cityId=${this.$store.state.cityId}&pageNum=${this.current}&pageSize=10&type=1&k=7388950`, //currtent 是获取的页数 city是传过来的城市id 后面有说
headers: {
'X-Host': 'mall.film-ticket.film.list' //获取列表数据
}
}).then((res) => {
this.datalist = [...this.datalist, ...res.data.data.films] //取回来的数据进行合并处理 老数组+新获取的数组
/* 记得让loding设置成false */
this.loading = false
})
这时候就会发现 到底之后,loading一直为false ,会不断获取向后端获取数据。但是已经没有数据了,会产生一个死循环
注意到获取回来的数据有一个total值,存放至有多少影片
所以解决方法是,在onload开头加一个if条件,当datalist.length等于total并且不等于0时,就把finish状态设置true,禁用懒加载功能。
onLoad () {
if (this.datalist.length === this.total && this.total != 0) {
this.finished = true
return
}
loading加载
loading功能,就是每次切换页面时候出现小圆圈等待数据加载
还是使用vant的Toast实现:
在create的时候添加Toast,
forbidClick 属性可以禁用背景点击
duration展示时长(ms),值为 0 时,toast 不会消失
Toast.loading({
message: '加载中...',
forbidClick: true,
duration: 0 //
})
当axios请求完数据时候把Toast清除掉
Toast.clear()
axios拦截器
可以在请求之前拦截和在请求之后拦截
所以接合axios拦截器的特点,我们可以把Toast写在axios里面,每次有组件调用axios请求数据,都会实现Toast功能
// 在请求之前拦截 --showloding
http.interceptors.request.use(
function (config) {
Toast.loading({
message: '加载中...',
forbidClick: true,
duration: 0
})
return config
},
function (error) {
return Promise.reject(error)
}
)
// 在请求之后拦截
http.interceptors.response.use(
function (response) {
// 隐藏 清空taost
Toast.clear()
return response
},
function (error) {
// 隐藏 清空taost
Toast.clear()
return Promise.reject(error)
}
)
城市组件
之前在cinema组件用过navbar,有个城市的小按钮
找文档查看一下点击事件
引入city组件和search组件
handleLeft () {
this.$router.push('/city')
},
handleRight () {
this.$router.push('/cinemas/search')
}
同样使用axios获取数据
http({
url: '/gateway?k=9757741',
headers: {
'X-Host': 'mall.film-ticket.city.list'
}
}).then(res => {
this.cityList = this.renderCity(res.data.data.cities) /* 使用函数redercity获取26个字母开头的数组 */
rederCity函数,先把26个字母遍历出来,并插入letterList里面
renderCity (list) {
const letterList = []
const cityList = []
/* 遍历出26个英文字母 */
for (let i = 65; i < 91; i++) {
letterList.push(String.fromCharCode(i))
}
由于的数据里面包含地方的拼音
将leeterlist中的26个字母遍历给letter,再将list里面得到的数据城市拼音的元素全部拿出来,截取第一个元素(拼音的开头字母),将其变成大写 使用es5的过滤filter获取每次过滤的到的结果(把a的打印出来;把b的打印出来....);这就得到26个数组
letterList.forEach(Letter => {
const newlist = list.filter(item => item.pinyin.substring(0, 1).toUpperCase() === Letter)
得到的newlist是26个字母的数组
需要注意的是没有o和v开头的城市
所以需要判断一下
// 只有长度大于0才可以遍历
newlist.length > 0 && cityList.push({
type: Letter,
list: newlist
得到22个城市数组
在页面上渲染,使用到了vant上的
创建的结果如图所示
接下来就是点击城市,需要把点击的数据拿出来,所以在
handleClick (item) {
console.log(item.name, item.cityId)
}
点击之后,获取到了点击的城市和城市id
vuex --状态管理模式
项目中城市信息,来分类各城市影院和影片,所以引入vuex来共享管理city信息
在index文件中写入初始状态
new Vuex.Store({
/* 公共状态 */
state: {
cityId: '440100',
cityName: '广州',
cinemaList: [],
isTabbarShow: true
},
这时候store已经实例化了,可以直接使用this.store就可以访问store了。所以在任何页面中使用this.store.state.cityName城市名的公共状态
所以当点击列表的城市名称时,可以做到切换城市
handleClick (item) {
// vuex -状态管理模式 让公共状态的cityName改变成点击的城市item.name
this.$store.state.cityName = item.name;
this.$router.back() ;//返回上一次页面
}
成功的切换城市名称
但是注意虽然这样很简单,但是有隐患,当每个组件都可以修改state里面的公共状态时,一旦发生错误,就无法知道在哪里使用导致错误,所以得需要有监督
所以就在每次修改时候,都经过一个mutation来进行监控管理,防止状态被恶意修改
vue ComponentMutationDevtoolsState
使用Devtools工具可以查看监控状态,可视化工具,
所以当修改状态时候,提交到mucation中,把item.name传过去,在mucation中修改,这是mucation的同步修改
this.$store.commit('changeCityName', item.name) this.$store.commit('changeCityId', item.cityId)
this.$router.back()
mutations: {
changeCityName (state, cityName) //state 把公共状态传进来修改 {
state.cityName = cityName
},
changeCityId (state, cityId) {
state.cityId = cityId
},
}
效果是一样的
不过当我们做搜索组件的时候,还需要请求后端拿取城市的影院信息,这样每次都要多次请求数据,能不能把数据缓存下来,减少对服务器的请求呢?有,就是使用vuex的异步缓存
注意mucation不支持异步,只支持同步
所以使用vuex的actions,可以支持异步和同步任务
vuex的异步:后端数据的缓存快照,减少重复数据请求,减少服务器的要,提高用户体验
vuex官网的工作流图:
所以我们在cinema页面就不直接请求数据,而是改用dispatch分发到actions中,不过注意当没有数据的时候,才会请求,有有数据时候,代表有缓存,不需要请求数据所以要加一个if条件
if (this.cinemaList.length === 0) { this.$store.dispatch('getCinemaData', this.$store.state.cityId)
}
actions中进行请求
actions: {
getCinemaData (store, cityId) {
http({
url: `/gateway?cityId=${cityId}&ticketFlag=1&k=9755784`,
headers: {
'X-Host': 'mall.film-ticket.cinema.list'
}
}).then((res) => {
store.commit('changeCinemaData', res.data.data.cinemas)
})
}
},
接着commit提交到mucation中对数据的缓存
mutations: {
changeCinemaData (state, data) {
state.cinemaList = data
},
}
不过写完之后会有一个bug,就是切换城市时候,影院信息没有随之更新
分析一波原因发现,当cinemaList有缓存数据,就不请求后端更新数据了
所以需要在每次点击city组件时候,把cinemalist缓存直接清除,这样就会重新请求一次后端数据进行更新
methods: {
handleLeft () {
this.$router.push('/city')
// 切换城市 直接把之前缓存的清空 cinemalist
this.$store.commit('clearCinmaData')
},
mucation *** 作
mucation{
clearCinmaData (state) {
state.cinemaList = []
},
}
这样就可以成功切换城市信息了
search组件
点击搜索按,进入
handleRight () {
this.$router.push('/cinemas/search')
}
使用vant组件库
搜索项的内容,注意设置v-if=value,当没有输入值时候,数据不创建出来
{{ data.name }}
{{ data.address }}
¥{{ data.lowPrice / 100 }}起
computedList方法,就是把包含输入的值的内容全部筛选出来
所以把影院列表中的name那一项的名字,凡是包含value的取出来,但是有大小写的区别,所以直接把输入的value值和获取得得到的值,全部都改成大写 方便匹配。下面地名也是如此 *** 作
computedList () {
return this.$store.state.cinemaList.filter(item => item.name.toUpperCase().includes(this.value.toUpperCase()) || item.address.toUpperCase().includes(this.value.toUpperCase()))
}
效果:
然后把点击取消按钮,就返回cinema页面
onCancel () {
this.$router.back()
}
使用mixin混入,给某些组件混入一些功能,例如底部选显卡,在切换城市和搜索时不需要出现,就要隐藏
const obj = {
created () {
this.$store.commit('hide')
},
destroyed () {
this.$store.commit('show')
}
}
export default obj
mixins: [obj],
没有底部按钮
不过要注意,当切换城市成功之后,再次刷新页面,会变成初始值,所以这就需要vuex的持久化-todo
使用 npm i--save vuex-persistedstate 下载vuex的持久化插件
引入插件
import createPersistedState from 'vuex-persistedstate'
在vuex.store下输入
// 每次数据引入都会在localstoage 缓存数据 达到一个持久化的效果
plugins: [createPersistedState({
reducer: (state) => {
// 规定存储那个值
return {
cityId: state.cityId,
cityName: state.cityName
}
}
})],
原理:在locationStorage 里面保存着这些信息
这样的话就支持vuex中cityid和cityName数据持久化,刷新也不会丢失数据。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)