小黑子的——vue从入门到入土过程:第四章

VUE2.0——VUE3.0系列第四章

1. swiper 组件

1.1 导入swiper模块化

在终端中,输入cnpm i --save swiper,导入swiper模块化
Films.vue:

<template>
    <div>
        <film-swiper>
            <div class="swiper-slide">1111</div>
            <div class="swiper-slide">2222</div>
            <div class="swiper-slide">3333</div>
        </film-swiper>

        <div>
            二级的声明式导航
        </div>

        <router-view></router-view>
    </div>
</template>
<script>
import filmSwiper from '@/mycomponents/film/FilmSwiper'
export default {
    components:{
        filmSwiper
    }

}
</script>

FilmSwiper.vue:

<template>
    <div class="swiper van">
        <div class="swiper-wrapper">
            <slot></slot>
        </div>
        <div class="swiper-pagination"></div>
    </div>
</template>
<script>
import Swiper from 'swiper/bundle'
import 'swiper/swiper-bundle.css'
export default {
    mounted() {
        new Swiper('.van', {
            pagination: {
                el: '.swiper-pagination'
            },
            loop: this.loop,
            autoplay: {
                deplay: 2500,
                disableOnInteraction: false
            }
        })

    }
}
</script>

在这里插入图片描述

1.2 axios 引入遍历数据

Films.vue:

<template>
    <div>
        <film-swiper :key="datalist.length">
            <film-swiper-item v-for="data in datalist" :key="data.id" class="filmswiperitem">
                <img :src="data.imgUrl" />
                </film-swiper-item>
        </film-swiper>

        <div>
            二级的声明式导航
        </div>

        <router-view></router-view>
    </div>
</template>
<script>
import filmSwiper from '@/mycomponents/film/FilmSwiper'
import filmSwiperItem from '@/mycomponents/film/FilmSwiperItem'
import axios from 'axios'
export default {
    data(){
        return{
            datalist:[]
        }

    },
    mounted(){
        axios.get('/banner.json').then(res=>{
            console.log(res.data)
            this.datalist = res.data.banner
        })
    },
    components:{
        filmSwiper,
        filmSwiperItem
    }

}
</script>
<style lang="scss" scoped>
.filmswiperitem{
    img{
        width:100%;
        height: 200px;
    }
}
</style>

在这里插入图片描述

2. 选项卡封装

需求:
点击下面的首页,就显示首页组件,点击分类,显示分类组件
点击的时候,当前处于高亮状态,并且文字颜色修改为 red!

思路
TabBar 一个大组件 里面是一个 可以被替换!=> 被TabBarItem(组件) 替换
TabBarItem 这是一个item组件 上面为图标 下面为文本 => 这些也是设置为 为了复用
在item组件 之中 需要使用一个props来接受外面的 path => 用于跳转!

TabBar.vue组件的实现
在component文件下 创建一个tabbar文件夹 => tabbar.vue
#tab-bar 里面包裹一个插槽,用于存放 tabbar-item 组件!
css样式也是书写在这边
使用:在App.vue组件之中引入使用即可,或者在其他之中使用!
<tab-bar> 需要先引入组件 注册组件 使用组件!

TabBar.vue:

<template>

    <footer>
        <ul>
      <router-link to="/films" custom v-slot="{ navigate, isActive }">
        <!-- isActive记录是否被点中 -->
        <li @click="navigate"  :class="isActive?'vanactive':''">电影-{{ isActive }}</li>
      </router-link>


      <router-link to="/cinemas" active-class="vanacitve" custom v-slot="{ navigate, isActive }">
        <li @click="navigate" :class="isActive?'vanactive':''">影院-{{ isActive }}</li>
      </router-link>

      <router-link to="/center" active-class="vanacitve" custom v-slot="{ navigate, isActive }">
        <li @click="navigate" :class="isActive?'vanactive':''">我的-{{ isActive }}</li>
      </router-link>
    </ul>
    </footer>

</template>
<style lang="scss" scoped>
footer{
    position: fixed;
    bottom: 0;
    left: 0;
    width:100%;
    height: 3.0625rem;
    background-color: white;
    ul{
        display: flex;
        li{
            flex: 1;
            line-height: 3.0625rem;
            text-align: center;
        }
    }
}
</style>

在这里插入图片描述
在这里插入图片描述

3. 电影导航组件

FilmHeader.vue:

<template>
    <ul>
        <router-link to="/films/nowplaying" custom v-slot="{ navigate, isActive }">
        <!-- isActive记录是否被点中 -->
        <li @click="navigate">
            <span :class="isActive ? 'vanactive' : ''">正在热映</span>
        </li>
    </router-link>
    
    <router-link to="/films/comingsoon" custom v-slot="{ navigate, isActive }">
        <!-- isActive记录是否被点中 -->
        <li @click="navigate">
            <span :class="isActive ? 'vanactive' : ''">即将上映</span>
        </li>
    </router-link>
    </ul>
</template>
<style lang="scss" scoped>
ul{
    display: flex;
    height: 3.0625rem;
    line-height: 3.0625rem;
    li{
        flex: 1;
        text-align: center;
    }
}
.vanactive{
    color: red;
    border-bottom: 2px solid red;
}
</style>

Film.vue:

<template>
    <div>
        <film-swiper :key="datalist.length">
            <film-swiper-item v-for="data in datalist" :key="data.id" class="filmswiperitem">
                <img :src="data.imgUrl" />
                </film-swiper-item>
        </film-swiper>
        
        <film-header></film-header>

        <router-view></router-view>
    </div>
</template>
<script>
import filmSwiper from '@/mycomponents/film/FilmSwiper'
import filmSwiperItem from '@/mycomponents/film/FilmSwiperItem'
import filmHeader from '@/mycomponents/film/FilmHeader'
import axios from 'axios'
export default {
    data(){
        return{
            datalist:[]
        }

    },
    mounted(){
        axios.get('/banner.json').then(res=>{
            console.log(res.data)
            this.datalist = res.data.banner
        })
    },
    components:{
        filmSwiper,
        filmSwiperItem,
        filmHeader
    }

}
</script>
<style lang="scss" scoped>
.filmswiperitem{
    img{
        width:100%;
        height: 200px;
    }
}
</style>

在这里插入图片描述

4. 正在热映取数据

前端调试工具-FeHelper:Postman
在这里插入图片描述
获取数据:

<script>
import axios from 'axios'
export default {
    mounted() {
        axios({
            url: 'https://m.maizuo.com/gateway?cityId=440100&pageNum=1&pageSize=10&type=1&k=5877842',
            headers: {
                'X-Client-Info': '{ "a": "3000", "ch": "1002", "v": "5.2.1", "e": "167643376563166084022273", "bc": "440100" }',

                'X-Host': 'mall.film-ticket.film.list'
            }
        }).then(res => {
            console.log(res.data)
        })
    }
</script>

在这里插入图片描述

5. 正在热映渲染

App.vue:

<template>
  <div>
    <tabbar></tabbar>
    <!-- 路由容器 -->
    <section>
      <router-view></router-view>
    </section>
  </div>

</template>
<script>
import tabbar from'@/mycomponents/Tabbar.vue'
export default {
  data() {
    return {

    }
  },
  components:{
    tabbar
  }
}
</script>

<style lang="scss">
*{
  margin: 0;
  padding: 0;
}
ul{
  list-style: none;
}
body{
  font-size: 16px;
}
section{
  padding-bottom: 3.125rem;
}
</style>

nowplaying.vue:

<template>
    <div>
        <ul>
            <li v-for="data in datalist" :key="data.filmId" @click="handleChangePage(data.filmId)">
                <img :src="data.poster" />
                <div>
                    <div class="title">{{ data.name }}</div>
                    <div class="content">
                        <div :class="data.grade?'':'hidden'">观众评分:<span style="color:red;">{{ data.grade }}</span></div>
                        <div class="actors">主演:{{ data.actors |actorsFilter }}</div>
                        <div>
                            {{ data.nation }}|{{ data.runtime }}分钟
                        </div>
                    </div>
                </div>
            </li>
        </ul>
    </div>
</template>
<script>
import axios from 'axios'
import Vue from 'vue'
Vue.filter('actorsFilter',(data)=>{
    if(data===undefined) return '暂无主演'// 防止有undefined的数据导致无法过滤,造成报错
     // 把数据的名字给映射出来
    return data.map(item=>item.name).join('')
})
export default {
    data() {
        return {
            datalist: []
        }
    },
    mounted() {
        axios({
            url: 'https://m.maizuo.com/gateway?cityId=440100&pageNum=1&pageSize=10&type=1&k=5877842',
            headers: {
                'X-Client-Info': '{ "a": "3000", "ch": "1002", "v": "5.2.1", "e": "167643376563166084022273", "bc": "440100" }',

                'X-Host': 'mall.film-ticket.film.list'
            }
        }).then(res => {
            console.log(res.data.data.films)
            this.datalist = res.data.data.films
        })

    },
    methods: {
        handleChangePage(id) {
            // console.log(id)
            // 编程式导航
            //detail/1111
            // 1-通过路径跳转
            // this.$router.push(`/detail/${id}`)

            // 2-通过命名路由跳转
            this.$router.push({
                name: "vanDetail",
                params: {
                    id
                }
            })
        }
    }
}
</script>
<style lang="scss" scoped>
ul{
    li{
        overflow: hidden;
        padding: .9375rem;
        img{
            width: 4.125rem;
            height: 5.625rem;
            float: left;
        }
        .title{
            font-size: 16px;
        }
        .content{
            font-size: 13px;
            color: gray;
            .actors{
                overflow: hidden;
                text-overflow: ellipsis;
                white-space: nowrap;
                width: 12.5rem;
                letter-spacing: 2px;
            }
        }
    }
}

</style>

在这里插入图片描述

6. 获取点击电影的详情数据

<template>
    <div>
        detail
    </div>
</template>

<script>
import axios from 'axios'
export default {
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        axios({
            url: `https://m.maizuo.com/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"167643376563166084022273","bc":"440100"}',

                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
        })
    }
}
</script>

在这里插入图片描述

7. axios 初次封装

7.1 对于数据请求的封装- 函数return axios

http.js:

// 1- 对于数据请求的封装
import axios from 'axios'

function httpForList() {
    return axios({
        url: 'https://m.maizuo.com/gateway?cityId=440100&pageNum=1&pageSize=10&type=1&k=5877842',
            headers: {
                'X-Client-Info': '{ "a": "3000", "ch": "1002", "v": "5.2.1", "e": "167643376563166084022273", "bc": "440100" }',

                'X-Host': 'mall.film-ticket.film.list'
            }
    })
}

function httpForDetail(params) {
    return axios({
        url: `https://m.maizuo.com/gateway?filmId=${params}&k=6928203`,
        headers: {
            'X-Client-Info': '{"a":"3000","ch":"1002","v":"5.2.1","e":"167643376563166084022273","bc":"440100"}',

            'X-Host': 'mall.film-ticket.film.info'
        }
    })
}

export default {
    httpForDetail,
    httpForList
}

7.2 对于数据请求的封装-axios创建的实例http

http.js:

优点:
//可以节省相同的代码,只需把不同的写在各个vue中
//在发请求之前拦截–showLoading
//在成功后拦截–hideLoading

import axios from 'axios'
const http = axios.create({
    baseURL: 'https://m.maizuo.com',
    timeout: 1000,
    headers: { 
        'X-Client-Info': '{ "a": "3000", "ch": "1002", "v": "5.2.1", "e": "167643376563166084022273", "bc": "440100" }'
     }
})

export default http

Detail.vue:

import http from '@/util/http'
export default {
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
        })
    }
}

8. 详情渲染 + Moment.js

Moment.js是一个轻量级的JavaScript时间库,它方便了日常开发中对时间的操作,提高了开发效率。日常开发中,通常会对时间进行下面这几个操作:比如获取时间,设置时间,格式化时间,比较时间等等。

终端引入:cnpm i --save moment

设定moment区域为中国

// require 方式
require('moment/locale/zh-cn')
moment.locale('zh-cn'); 

// import 方式
import 'moment/locale/zh-cn'
moment.locale('zh-cn');   

1)获取当前时间

moment()

2)获取时间戳(以毫秒为单位)

moment().format('x') // 返回值为字符串类型

8.1 引入详情简介

Detail.vue:

<template>
    <div v-if="filmInfo">
        <!-- 当为假,即数据没来时,就不会创建 -->
        <!-- <img :src="filmInfo.poster" /> -->
        <div :style="{
            backgroundImage: 'url(' + filmInfo.poster + ')'
        }" class="poster"></div>
        <div class="content">
            <div>{{ filmInfo.name }}</div>
            <div>
                <div class="detail-text">{{ filmInfo.category }}</div>
                <div class="detail-text">{{ filmInfo.premiereAt | dateFilter }}上映</div>
                <div class="detail-text">{{ filmInfo.nation }}|{{ filmInfo.runtime }}分钟</div>
            </div>
        </div>
    </div>
</template>

<script>
import http from '@/util/http'
import moment from 'moment'
import Vue from 'vue'
moment.locale('zh-cn') // 设置成中午的

Vue.filter('dateFilter', (date) => {
    return moment(date * 1000).format('YYYY-MM-DD')
    // 时间戳为10,格式化一定要 乘1000
})
export default {
    data() {
        return {
            filmInfo: null
        }
    },
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
            this.filmInfo = res.data.data.film
        })
    }
}
</script>
<style lang="scss" scoped>
.poster {
    width: 100%;
    height: 13.125rem;
    background-position: center;
    background-size: cover;
}

.content {
    padding: .9375rem;

    .detail-text {
        color: #797d82;
        font-size: 13px;
        margin-top: .625rem;
    }
}
</style>

在这里插入图片描述

8.2 详情简介下拉

<template>
    <div v-if="filmInfo">
        <!-- 当为假,即数据没来时,就不会创建 -->
        <!-- <img :src="filmInfo.poster" /> -->
        <div :style="{
            backgroundImage: 'url(' + filmInfo.poster + ')'
        }" class="poster"></div>
        <div class="content">
            <div>{{ filmInfo.name }}</div>
            <div>
                <div class="detail-text">{{ filmInfo.category }}</div>
                <div class="detail-text">{{ filmInfo.premiereAt | dateFilter }}上映</div>
                <div class="detail-text">{{ filmInfo.nation }}|{{ filmInfo.runtime }}分钟</div>
                <div class="detail-text" style="line-height: 15px;" :class="isHidden?'hidden':''">{{ filmInfo.synopsis }}</div>

                <div style="text-align:center;"><i class="iconfont" @click="isHidden=!isHidden">v</i></div>
            </div>
        </div>
    </div>
</template>

<script>
import http from '@/util/http'
import moment from 'moment'
import Vue from 'vue'
moment.locale('zh-cn') // 设置成中文的

Vue.filter('dateFilter', (date) => {
    return moment(date * 1000).format('YYYY-MM-DD')
    // 时间戳为10,格式化一定要 乘1000
})
export default {
    data() {
        return {
            filmInfo: null,
            isHidden: true
        }
    },
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
            this.filmInfo = res.data.data.film
        })
    }
}
</script>
<style lang="scss" scoped>
.poster {
    width: 100%;
    height: 13.125rem;
    background-position: center;
    background-size: cover;
}

.content {
    padding: .9375rem;

    .detail-text {
        color: #797d82;
        font-size: 13px;
        margin-top: .625rem;
    }
}
.hidden{
    overflow: hidden;
    height: 30px;
}
</style>

在这里插入图片描述

9. 详情轮播

Detail.vue:

<template>
    <div v-if="filmInfo">
        <!-- 当为假,即数据没来时,就不会创建 -->
        <!-- <img :src="filmInfo.poster" /> -->
        <div :style="{
            backgroundImage: 'url(' + filmInfo.poster + ')'
        }" class="poster"></div>

        <div class="content">
            <div>{{ filmInfo.name }}</div>
            <div>
                <div class="detail-text">{{ filmInfo.category }}</div>
                <div class="detail-text">{{ filmInfo.premiereAt | dateFilter }}上映</div>
                <div class="detail-text">{{ filmInfo.nation }}|{{ filmInfo.runtime }}分钟</div>
                <div class="detail-text" style="line-height: 15px;" :class="isHidden ? 'hidden' : ''">{{
                    filmInfo.synopsis
                }}</div>

                <div style="text-align:center;">
                    <i 
                    class="iconfont" 
                    @click="isHidden = !isHidden">v</i>
                </div>
            </div>
            <!-- 演职人员 -->
            <div>
                <div>演职人员</div>
                <detail-swiper :perview="3.5" name="actors">
                    <detail-swiper-item v-for="(data, index) in filmInfo.actors" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data.avatarAddress + ')'
                        }" class="avatar"></div>
                        <div style="text-align: center;font-size: 12px;">{{ data.name }}</div>
                        <div style="text-align: center;font-size: 13px;">{{ data.role }}</div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>
            <!-- 剧照 -->
            <div>
                <div>剧照</div>
                <detail-swiper :perview="2" name="photos">
                    <detail-swiper-item v-for="(data, index) in filmInfo.photos" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data + ')'
                        }" class="avatar"></div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>
            
        </div>

    </div>
</template>

<script>
import http from '@/util/http'
import moment from 'moment'
import Vue from 'vue'
import detailSwiper from '@/mycomponents/detail/DetailSwiper.vue'
import detailSwiperItem from '@/mycomponents/detail/DetailSwiperItem.vue'
moment.locale('zh-cn') // 设置成中文的
Vue.filter('dateFilter', (date) => {
    return moment(date * 1000).format('YYYY-MM-DD')
    // 时间戳为10,格式化一定要 乘1000
})
export default {
    data() {
        return {
            filmInfo: null,
            isHidden: true
        }
    },
    components: {
        detailSwiper,
        detailSwiperItem
    },
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
            this.filmInfo = res.data.data.film
        })
    }
}
</script>
<style lang="scss" scoped>
.poster {
    width: 100%;
    height: 13.125rem;
    background-position: center;
    background-size: cover;
}

.content {
    padding: .9375rem;

    .detail-text {
        color: #797d82;
        font-size: 13px;
        margin-top: .625rem;
    }
}

.hidden {
    overflow: hidden;
    height: 30px;
}

.avatar {
    width: 100%;
    height: 5.3125rem;
    background-position: center;
    background-size: cover;

}
</style>

DetailSwiper.vue:

<template>
    <div class="swiper" :class="name">
        <div class="swiper-wrapper">
            <slot></slot>
        </div>

    </div>
</template>
<script>
import Swiper from 'swiper/bundle'
import 'swiper/swiper-bundle.css'

export default {
    props: {
        perview: {
            type: Number,
            default: 1
        },
        name: {
            type: String,
            default: 'van'
        }

    },
    mounted() {
        new Swiper('.' + this.name, {
            slidesPerView: this.perview,
            spaceBetween: 30,
            freeMode: true

        })

    }
}
</script>

在这里插入图片描述

10. 详情Header

10.1 详情H-头部组件

头部组件吸顶返回
Detail.vue:

<template>
    <div v-if="filmInfo">
        <!-- 当为假,即数据没来时,就不会创建 -->
        <!-- <img :src="filmInfo.poster" /> -->
        <detail-header>
            {{ filmInfo.name }}
        </detail-header>
        <div :style="{
            backgroundImage: 'url(' + filmInfo.poster + ')'
        }" class="poster"></div>

        <div class="content">
            <div>{{ filmInfo.name }}</div>
            <div>
                <div class="detail-text">{{ filmInfo.category }}</div>
                <div class="detail-text">{{ filmInfo.premiereAt | dateFilter }}上映</div>
                <div class="detail-text">{{ filmInfo.nation }}|{{ filmInfo.runtime }}分钟</div>
                <div class="detail-text" style="line-height: 15px;" :class="isHidden ? 'hidden' : ''">{{
                    filmInfo.synopsis
                }}</div>

                <div style="text-align:center;">
                    <i 
                    class="iconfont" 
                    @click="isHidden = !isHidden">v</i>
                </div>
            </div>
            <!-- 演职人员 -->
            <div>
                <div>演职人员</div>
                <detail-swiper :perview="3.5" name="actors">
                    <detail-swiper-item v-for="(data, index) in filmInfo.actors" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data.avatarAddress + ')'
                        }" class="avatar"></div>
                        <div style="text-align: center;font-size: 12px;">{{ data.name }}</div>
                        <div style="text-align: center;font-size: 13px;">{{ data.role }}</div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>
            <!-- 剧照 -->
            <div>
                <div>剧照</div>
                <detail-swiper :perview="2" name="photos">
                    <detail-swiper-item v-for="(data, index) in filmInfo.photos" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data + ')'
                        }" class="avatar"></div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>
            
        </div>

    </div>
</template>

<script>
import http from '@/util/http'
import moment from 'moment'
import Vue from 'vue'
import detailHeader from '@/mycomponents/detail/DetailHeader.vue'
import detailSwiper from '@/mycomponents/detail/DetailSwiper.vue'
import detailSwiperItem from '@/mycomponents/detail/DetailSwiperItem.vue'
moment.locale('zh-cn') // 设置成中文的
Vue.filter('dateFilter', (date) => {
    return moment(date * 1000).format('YYYY-MM-DD')
    // 时间戳为10,格式化一定要 乘1000
})
export default {
    data() {
        return {
            filmInfo: null,
            isHidden: true
        }
    },
    components: {
        detailSwiper,
        detailSwiperItem,
        detailHeader
    },
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
            this.filmInfo = res.data.data.film
        })
    }
}
</script>
<style lang="scss" scoped>
.poster {
    width: 100%;
    height: 13.125rem;
    background-position: center;
    background-size: cover;
}

.content {
    padding: .9375rem;

    .detail-text {
        color: #797d82;
        font-size: 13px;
        margin-top: .625rem;
    }
}

.hidden {
    overflow: hidden;
    height: 30px;
}

.avatar {
    width: 100%;
    height: 5.3125rem;
    background-position: center;
    background-size: cover;

}
</style>

DetailHeader.vue:

<template>
    <div class="header">
        <i class="iconfont" @click="handleBack">
            &lt;
        </i>
        <slot></slot>
    </div>
</template>
<script>
export default{
    methods:{
        handleBack() {
            this.$router.back() //返回上一个页面
        }
    }
}
</script>

<style lang="scss" scoped>
.header {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 2.75rem;
    line-height: 2.75rem;
    background-color: white;
    text-align: center;

    i {
        font-size: 30px;
        position: fixed;
        left: .625rem;
        top: 0px;
        height: 2.75rem;
        line-height: 2.75rem;
    }
}
</style>

在这里插入图片描述

10.2 详情H-自定义指令

通过window.onscroll,if判断,大于一定距离显示详情,
最后记得小于一定距离的时候要清空

得销毁声明周期:unbind

Detail.vue:

<template>
    <div v-if="filmInfo">
        <!-- 当为假,即数据没来时,就不会创建 -->
        <!-- <img :src="filmInfo.poster" /> -->
        <detail-header v-scroll = "50">
            {{ filmInfo.name }}
        </detail-header>
        <div :style="{
            backgroundImage: 'url(' + filmInfo.poster + ')'
        }" class="poster"></div>

        <div class="content">
            <div>{{ filmInfo.name }}</div>
            <div>
                <div class="detail-text">{{ filmInfo.category }}</div>
                <div class="detail-text">{{ filmInfo.premiereAt | dateFilter }}上映</div>
                <div class="detail-text">{{ filmInfo.nation }}|{{ filmInfo.runtime }}分钟</div>
                <div class="detail-text" style="line-height: 15px;" :class="isHidden ? 'hidden' : ''">{{
                    filmInfo.synopsis
                }}</div>

                <div style="text-align:center;">
                    <i class="iconfont" @click="isHidden = !isHidden">v</i>
                </div>
            </div>
            <!-- 演职人员 -->
            <div>
                <div>演职人员</div>
                <detail-swiper :perview="3.5" name="actors">
                    <detail-swiper-item v-for="(data, index) in filmInfo.actors" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data.avatarAddress + ')'
                        }" class="avatar"></div>
                        <div style="text-align: center;font-size: 12px;">{{ data.name }}</div>
                        <div style="text-align: center;font-size: 13px;">{{ data.role }}</div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>
            <!-- 剧照 -->
            <div>
                <div>剧照</div>
                <detail-swiper :perview="2" name="photos">
                    <detail-swiper-item v-for="(data, index) in filmInfo.photos" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data + ')'
                        }" class="avatar"></div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>

        </div>

    </div>
</template>

<script>
import http from '@/util/http'
import moment from 'moment'
import Vue from 'vue'
import detailHeader from '@/mycomponents/detail/DetailHeader.vue'
import detailSwiper from '@/mycomponents/detail/DetailSwiper.vue'
import detailSwiperItem from '@/mycomponents/detail/DetailSwiperItem.vue'
moment.locale('zh-cn') // 设置成中文的
Vue.filter('dateFilter', (date) => {
    return moment(date * 1000).format('YYYY-MM-DD')
    // 时间戳为10,格式化一定要 乘1000
})

Vue.directive("scroll", {
    inserted(el,binding) {
        // console.log(binding.value)
        el.style.display = 'none'
        window.onscroll = () => {
            if ((document.documentElement.scrollTop || document.body.scrollTop) > binding.value) {
                el.style.display = 'block'
            } else {
                el.style.display = 'none'
            }
        }
    },
    //销毁执行的
    unbind() {
        window.onscroll = null                
    }
})
export default {
    data() {
        return {
            filmInfo: null,
            isHidden: true
        }
    },
    components: {
        detailSwiper,
        detailSwiperItem,
        detailHeader
    },
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
            this.filmInfo = res.data.data.film
        })
    }
}
</script>
<style lang="scss" scoped>
.poster {
    width: 100%;
    height: 13.125rem;
    background-position: center;
    background-size: cover;
}

.content {
    padding: .9375rem;

    .detail-text {
        color: #797d82;
        font-size: 13px;
        margin-top: .625rem;
    }
}

.hidden {
    overflow: hidden;
    height: 30px;
}

.avatar {
    width: 100%;
    height: 5.3125rem;
    background-position: center;
    background-size: cover;

}
</style>

11. 影院组件渲染

Cinemas.vue

<template>
    <div>
        <ul>
            <li v-for="data in cinemaList" :key="data.cinemaId">
                <div class="left">
                    <div class="cinema_name">{{ data.name }}</div>
                    <div class="cinema_text">{{ data.address }}</div>
                </div>
                <div class="right">
                    <div style="color: red;">¥{{ data.lowPrice / 100 }}起</div>
                </div>
            </li>

        </ul>
    </div>
</template>
<script>
import http from '@/util/http'
export default {
    data() {
        return {
            cinemaList: []
        }
    },
    mounted() {
        http({
            url: '/gateway?cityId=440100&ticketFlag=1&k=9458654',
            headers: {
                'X-Host': 'mall.film-ticket.cinema.list'
            }
        }).then(res => {
            // console.log(res.data)
            this.cinemaList = res.data.data.cinemas
        })
    }
}
</script>
<style lang="scss" scoped>
li {
    padding: .9375rem;
    display: flex;
    justify-content: space-between;

    .left {
        width: 13.25rem;
    }
    .right{
        font-size: 15px;
    }

    .cinema_name {
        font-size: 15px;
    }

    .cinema_text {
        color: #797d82;
        font-size: 12px;
        margin-top: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}
</style>

在这里插入图片描述

12. 影院组件优化

1 .js单线程,js线程会阻塞UI线程,所以当脚本执行时间过长就会阻塞渲染,导致页面卡顿

2 .Vue数据变化会有追踪机制,只会更新自己组件的数据,而React会全部重新渲染组件

3 .函数式组件和普通的对象类型的组件不同,函数式组件render生成的式普通的Vnode,不会有递归子组件的过程.怎么感觉是函数才会自动递归组件

4 .在patch过程中,如果遇到一个节点组件,就会递归执行子组件的初始化过程.更react是相反的

5 .函数式组件没有状态,没有响应式数据,生命周期钩子这些东西,可以把他当成普通组件模板中的一部分DOM剥离出来,通过函数方式渲染,是一种DOM层面的渲染

.box {
    // height: 38.625rem;// 但是rem一直比的是宽度的百分比,所以高度在不同窗口下是会有问题的,所以要动态结算高度
    overflow: hidden;
    position:relative;
    // 修正滚动条的的位置

}
//动态结算高度,视口高度-底部选项卡的高度=中心页面滚动的高度
this.height = document.documentElement.clientHeight-选项卡高度

this.height = document.documentElement.clientHeight- document.querySelector('footer').offsetHeight + 'px'

12.1 BetterScroll

https://better-scroll.github.io/docs/zh-CN/
主要完成的功能需要包含Better-Scroll实现页面中拖动滚动、拉动属性等功能

终端引入:cnpm i --save better-scroll

12.2 $nextTick

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

什么意思?

我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新

为什么要有nexttick

{{num}}
for(let i=0; i<100000; i++){
    num = i
}

如果没有 nextTick 更新机制,那么 num 每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick机制,只需要更新一次,所以nextTick本质是一种优化策略

13. elementUI 组件库

1.饿了么UED团队推出的vue前端框架:
(1) PC框架:(element UI)
https://element.eleme.cn/#/zh-CN

Element-Ui简介
element 是基于 vue 实现的一套不依赖业务的 UI 组件库,提供了丰富的PC端组件,减少用户对常用组件的封装,降低了开发的难易程度。

vue与Element-Ui的关系

  1. Element-Ui是基于vue封装的组件库,简化了常用组件的封装,提高了重用性原则;
  2. vue是一个渐进式框架,Element-Ui是组件库;

PCApp.vue:

<template>
    <el-container style="height: 500px; border: 1px solid #eee">
        <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
            <el-menu :default-openeds="['1','2']">
                <el-submenu :index="data.id + ''" v-for="data in sideList" :key="data.id">
                    <template slot="title"><i class="el-icon-message"></i>{{ data.title }}</template>
                    <el-menu-item :index="item.id + ''" v-for="item in data.children
                    " :key="item.id">
                        {{ item.title }}
                    </el-menu-item>

                </el-submenu>

            </el-menu>
        </el-aside>

        <el-container>
            <el-header style="text-align: right; font-size: 12px">
                <el-dropdown>
                    <i class="el-icon-setting" style="margin-right: 15px"></i>
                    <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item>修改</el-dropdown-item>
                        <el-dropdown-item>退出</el-dropdown-item>
                    </el-dropdown-menu>
                </el-dropdown>
                <span>{{ myname }}</span>
            </el-header>

            <el-main>
                <el-table :data="tableData">
                    <el-table-column prop="date" label="日期" width="140">
                    </el-table-column>
                    <el-table-column prop="name" label="姓名" width="120">
                    </el-table-column>
                    <el-table-column prop="address" label="地址">
                    </el-table-column>
                </el-table>
            </el-main>
        </el-container>
    </el-container>
</template>

<script>
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import Vue from 'vue';
Vue.use(ElementUI);

export default {
    methods: {
        handlePrimary() {
            console.log("primary")
        }
    },
    data() {
        
        return {
            myname:"vanwot",
            sideList: [
                {
                    id: 1,
                    title: "用户管理",
                    children: [
                        {
                            id: 2,
                            title: "用户列表"
                        },
                        {
                            id: 3,
                            title: "用户权限"
                        },
                    ]
                },
                {
                    id: 4,
                    title: "权限管理",
                    children: [
                        {
                            id: 5,
                            title: "角色列表"
                        },
                        {
                            id: 6,
                            title: "角色"
                        },
                    ]
                }

            ],
            tableData: [
                {
                    title:"van",
                    address:"magua",
                    date:"2021-05-01"
                },
                {
                    title:"wot",
                    address:"beijin",
                    date:"2021-05-02"
                },
                {
                    title:"xiaoming",
                    address:"hangzhou",
                    date:"2021-05-03"
                },
            ]
        }
    }

};
</script>

<style>
.el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
}

.el-aside {
    color: #333;
}
</style>

在这里插入图片描述

14. vant 引入

https://youzan.github.io/vant-weapp/#/home
安装:

npm i vant -S(简写)
npm install vant --save(完整写法)

14.1 全局引入

main.js:
import Vant from 'vant'
import 'vant/lib/vant-css/index.css'
Vue.use(Vant)
<template>
    <div>
        <van-nav-bar title="影院" ref="navbar">
            <template #left>
                上海<van-icon name="arrow-down" />
            </template>
            <template #right>
                <van-icon name="search" size="20" color="black"/>
            </template>
        </van-nav-bar>


        <div class="box" :style="{
            height: height
        }">
            <ul>
                <li v-for="data in cinemaList" :key="data.cinemaId">
                    <div class="left">
                        <div class="cinema_name">{{ data.name }}</div>
                        <div class="cinema_text">{{ data.address }}</div>
                    </div>
                    <div class="right">
                        <div style="color: red;">¥{{ data.lowPrice / 100 }}起</div>
                    </div>
                </li>

            </ul>
        </div>
</div>
</template>

<script>
import http from '@/util/http'
import BetterScroll from 'better-scroll'
export default {
    data() {
        return {
            cinemaList: [],
            height: '0px'
        }
    },

    mounted() {
        // console.log(this.$refs.navbar.$el.offsetHeight)
        // 动态结算高度,视口高度-底部选项卡的高度-顶部导航栏的高度=中心页面滚动的高度
        this.height = 
        document.documentElement.clientHeight 
        - this.$refs.navbar.$el.offsetHeight
        -document.querySelector("footer").offsetHeight + 'px'
        http({
            url: '/gateway?cityId=440100&ticketFlag=1&k=9458654',
            headers: {
                'X-Host': 'mall.film-ticket.cinema.list'
            }
        }).then(res => {
            // console.log(res.data)
            this.cinemaList = res.data.data.cinemas

            this.$nextTick(() => {// 保证dom上树,保证BetterScroll回调,实现初始化滚动
                new BetterScroll('.box', {
                    scrollbar: {
                        fade: true
                    }
                })
            })
        })
    }
}
</script>
<style lang="scss" scoped>
li {
    padding: .9375rem;
    display: flex;
    justify-content: space-between;

    .left {
        width: 13.25rem;
    }

    .right {
        font-size: 15px;
    }

    .cinema_name {
        font-size: 15px;
    }

    .cinema_text {
        color: #797d82;
        font-size: 12px;
        margin-top: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.box {
    // height: 38.625rem;// 但是rem一直比的是宽度的百分比,所以高度在不同窗口下是会有问题的
    overflow: hidden;
    position: relative;
    // 修正滚动条的的位置

}
</style>

在这里插入图片描述

14.2 按需引入

首先,安装babel-plugin-import

npm i babel-plugin-import -D(简写)
npm install babel-plugin-import --save-dev(完整写法)

然后,在.babelrc文件中

 "plugins": [
     "transform-vue-jsx", 
     "transform-runtime",
     <!--这个是添加的-->
     ["import",{"libraryName":"vant","style":true}]
 ]

最后,在main.js中

import {Button} from 'vant'
Vue.use(Button)
// 引入的多的话
import { Button, Row, Col } from 'vant'
Vue.use(Button).use(Row).use(Col)

14.3 在页面中单独使用组件

import { Button, Row, Col } from 'vant'
export default{
    components: {
        [Button.name]:Button // 有的组件有专门的引入方式,比如Dialog,可以看看
    }
}

15. vant 应用 点击图片预览

通过查询文档,复制粘贴,增加点击事件,如预览效果 ,
通过传入index值,获取每次页面打开时显示的图片页

Detail.vue:

<template>
    <div v-if="filmInfo">
        <!-- 当为假,即数据没来时,就不会创建 -->
        <!-- <img :src="filmInfo.poster" /> -->
        <detail-header v-scroll="50">
            {{ filmInfo.name }}
        </detail-header>
        <div :style="{
            backgroundImage: 'url(' + filmInfo.poster + ')'
        }" class="poster"></div>

        <div class="content">
            <div>{{ filmInfo.name }}</div>
            <div>
                <div class="detail-text">{{ filmInfo.category }}</div>
                <div class="detail-text">{{ filmInfo.premiereAt | dateFilter }}上映</div>
                <div class="detail-text">{{ filmInfo.nation }}|{{ filmInfo.runtime }}分钟</div>
                <div class="detail-text" style="line-height: 15px;" :class="isHidden ? 'hidden' : ''">{{
                    filmInfo.synopsis
                }}</div>

                <div style="text-align:center;">
                    <i class="iconfont" @click="isHidden = !isHidden">v</i>
                </div>
            </div>
            <!-- 演职人员 -->
            <div>
                <div>演职人员</div>
                <detail-swiper :perview="3.5" name="actors">
                    <detail-swiper-item v-for="(data, index) in filmInfo.actors" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data.avatarAddress + ')'
                        }" class="avatar"></div>
                        <div style="text-align: center;font-size: 12px;">{{ data.name }}</div>
                        <div style="text-align: center;font-size: 13px;">{{ data.role }}</div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>
            <!-- 剧照 -->
            <div>
                <div>剧照</div>
                <detail-swiper :perview="2" name="photos">
                    <detail-swiper-item v-for="(data, index) in filmInfo.photos" :key="index">
                        <div :style="{
                            backgroundImage: 'url(' + data + ')'
                        }" class="avatar" @click="handlePreview(index)"></div>
                    </detail-swiper-item>
                </detail-swiper>
            </div>

        </div>

</div>
</template>

<script>
import http from '@/util/http'
import moment from 'moment'
import Vue from 'vue'
import detailHeader from '@/mycomponents/detail/DetailHeader.vue'
import detailSwiper from '@/mycomponents/detail/DetailSwiper.vue'
import detailSwiperItem from '@/mycomponents/detail/DetailSwiperItem.vue'
import { ImagePreview } from 'vant';

moment.locale('zh-cn') // 设置成中文的
Vue.filter('dateFilter', (date) => {
    return moment(date * 1000).format('YYYY-MM-DD')
    // 时间戳为10,格式化一定要 乘1000
})

Vue.directive("scroll", {
    inserted(el, binding) {
        // console.log(binding.value)
        el.style.display = 'none'
        window.onscroll = () => {
            if ((document.documentElement.scrollTop || document.body.scrollTop) > binding.value) {
                el.style.display = 'block'
            } else {
                el.style.display = 'none'
            }
        }
    },
    //销毁执行的
    unbind() {
        window.onscroll = null
    }
})
export default {
    data() {
        return {
            filmInfo: null,
            isHidden: true
        }
    },
    components: {
        detailSwiper,
        detailSwiperItem,
        detailHeader
    },
    methods: {
        handlePreview(index) {
            ImagePreview({
                images: this.filmInfo.photos,
                startPosition: index,
                closeable:true,
                closeIconPosition: 'top-left'
            })
        }
    },
    created() {
        // 拿到的是当前匹配的路由 ,没有r的拿到的是当前匹配到detail的路由
        //this.$router拿到的是整个路由对象
        //params是$route的属性,用来储存数据的键值对(对象形式,{key:value}),储存很多属性(键值对,属性,属性值)在里面
        console.log('created', this.$route.params.id)
        // axios 利用id发请求到详情接口,获取详细数据,布局页面
        http({
            url: `/gateway?filmId=${this.$route.params.id}&k=6928203`,
            headers: {
                'X-Host': 'mall.film-ticket.film.info'
            }
        }).then(res => {
            console.log(res.data.data.film)
            this.filmInfo = res.data.data.film
        })
    }
}
</script>
<style lang="scss" scoped>
.poster {
    width: 100%;
    height: 13.125rem;
    background-position: center;
    background-size: cover;
}

.content {
    padding: .9375rem;

    .detail-text {
        color: #797d82;
        font-size: 13px;
        margin-top: .625rem;
    }
}

.hidden {
    overflow: hidden;
    height: 30px;
}

.avatar {
    width: 100%;
    height: 5.3125rem;
    background-position: center;
    background-size: cover;

}
</style>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值