Vue.js学习记录-15-Vue去哪儿网项目实战:景点详情页开发-功能点概述 + Detail + Banner(通用组件:Gallery、Fade)

版权声明:未经博主本人同意,请勿私自转发分享。 https://blog.csdn.net/Nerver_77/article/details/84108448

3. 景点详情页开发

  • 功能点概述

    • 用户首页点击热销推荐景点,即可跳转景点详情页面。详情页面包括三部分内容:顶部图片展示、景点门票详情、隐藏页面头。(拖动至下方会出现)
      在这里插入图片描述
    • 用户点击顶部图片展示,进入图片轮播区域(全屏),可左右滑动进行图片浏览,点击图片区域外进行返回景点详情页,过渡动画的使用,图片轮播区域配置首页返回按钮。
      在这里插入图片描述
    • 用户向下拖动景点详情页,即可看见隐藏页面头部,同时具备返回按钮,可以返回至首页。
      在这里插入图片描述
    • 景点门票详情,针对返回的多层数据,进行组件的递归调用展示。
  • Detail:城市详情父组件

    • 路由配置

      在该模块中,路由采用了动态参数的形式,进行路由配置,坐标/router/index.js:

        import Detail from '@/pages/detail/Detail'
        {
          // 绑定动态参数
          path: '/detail/:id',
          name: 'detail',
          component: Detail
        }
      

      其中 :id 绑定了动态的URL参数,在组件中可以通过:this.$route.params.id 进行获取

    • 组件管理:

      • 子组件引入及注册:Banner、Header、List
        • 引入

            import DetailBanner from './components/Banner'
            import DetailHeader from './components/Header'
            import DetailList from './components/List'
          
        • 注册

            components: {
                DetailBanner,
                DetailHeader,
                DetailList
            }
          
    • 组件交互:三个交互点

      • 首页热销推荐景点点击路由跳转

        坐标:Home.Recommend,路由跳转时,携带item.id

          <!-- 采用router-link进行页面跳转,tag标识标签转换为li,to携带参数进行页面跳转 -->
          <router-link
              tag="li"
              :to="'/detail/' + item.id"
              class="item border-bottom"
              v-for="item of list"
              :key="item.id">
            <img class="item-img" :src="item.imgUrl" />
            <div class="item-info">
              <p class="item-title">{{item.title}}</p>
              <p class="item-desc">{{item.desc}}</p>
              <button class="item-button">查看详情</button>
            </div>
          </router-link>
        
      • Banner、Header点击返回按钮路由跳转

        坐标:Header,下文详细组件中进行介绍

    • 数据传递:采用axios进行数据传递

      • axios引入

          import axios from 'axios'
        
      • 初始化data

          data() {
            return {
              sightName: '',
              bannerImg: '',
              gallaryImgs: [],
              list: []
            }
          }
        
      • 数据请求

        • 触发数据请求

            //页面渲染时触发方法
            mounted() {
              this.getDetailInfo()
            }
          
        • 数据请求:注意请求拼装URL的方式,前面已经提到了调用动态参数的方法:this.$route.params.id

            methods: {
                getDetailInfo() {
                  axios.get('/api/detail.json?', {
                    params: {
                      id: this.$route.params.id
                    }
                  }).then(this.handleGetDataSucc)
                },
                handleGetDataSucc(res) {
                  res = res.data
                  if (res.ret && res.data) {
                    const data = res.data
                    this.sightName = data.sightName
                    this.bannerImg = data.bannerImg
                    this.gallaryImgs = data.gallaryImgs
                    this.list = data.categoryList
                }
              }
            },			
          
      • 数据传递:三个数据项传递给Banner组件、一个数据项传递给List组件

          <template>
            <div>
              <detail-banner :sightName="sightName" :bannerImg="bannerImg" :gallaryImgs="gallaryImgs"></detail-banner>
              <detail-header></detail-header>
              <div class="content">
                <detail-list :list="list"></detail-list>
              </div>
            </div>
          </template>
        
  • Banner:景点图片画廊(通用组件)

    写在开头,这一部分要实现的具体功能细节有:

    1. 景点主图片、景点名称、画廊图片数目展示

    2. 点击主图片进入图片画廊,图片画廊可以左右滑动,点击非图片区域可退回景点详情页

    细节1实现:

    根据上文可知,父组件Detail以属性绑定的方式向Banner组件传递了三个数据项,分别是:

    1. sightName:景点名称
    2. bannerImg:景点主图片
    3. gallaryImgs:画廊图片集

    数据获取

      props: {
        sightName: String,
        bannerImg: String,
        gallaryImgs: Array
      },
    

    数据映射渲染:该DOM上绑定了点击事件:handleBannerClick,细节2中会提到。

      <div class="banner" @click="handleBannerClick">
          <img class="banner-img" :src="bannerImg" alt=""/>
          <div class="banner-info">
              <div class="banner-title">
                  {{this.sightName}}
              </div>
              <div class="banner-number">
                  <span class="iconfont banner-icon">&#xe67b;</span>
                  {{this.gallaryImgs.length}}
              </div>
          </div>
       </div>
    

    细节2实现:通用组件Gallery、Fade的使用

    在该部分的实现中,将Gallery画廊组件以及Fade简单动画组件做了封装,封装为通用组件,也方便其他组件进行调用。

    Gallery:画廊组件

    关于画廊组件,本质上是Swiper组件,进行了些定制化的配置。比如,铺满全屏,分页器样式,状态检查等。

    关于Swiper组件,详情API及使用方法见:https://3.swiper.com.cn/api/index.html

    <template>:循环数据项、点击事件:handleGallaryClick,控制是否进入景点图片画廊

      <template>
        <div class="container" @click="handleGallaryClick">
          <div class="wrapper">
            <swiper :options="swiperOptions">
              <!-- slides -->
              <swiper-slide v-for="(item, index) in imgs" :key="index">
                <img class="gallary-img" :src="item">
              </swiper-slide>
              <!-- Optional controls -->
              <div class="swiper-pagination" slot="pagination"></div>
            </swiper>
          </div>
        </div>
      </template>
    

    循环数据项:由于Gallery组件为公共组件,调用该组件的即为其父组件(这里是Banner)。Banner组件将gallaryImgs数组传递给Gallery组件,在模板中通过index为索引进行了遍历输出。

      props: {
        imgs: {
          type: Array,
      	// 默认值为空数组
          default() {
            return []
          }
        }
      },
    

    点击事件:handleGallaryClick,当点击区域时触发点击事件,向父组件(Banner)触发close事件,关闭通用画廊组件。

      methods: {
        handleGallaryClick() {
          // 关闭公共画廊事件
          this.$emit('close')
        }
      },
    

    定制化配置:Swiper3 相关配置 https://3.swiper.com.cn/api/

      data() {
        return {
          swiperOptions: {
            pagination: '.swiper-pagination',
            // 分式分页器
            paginationType: 'fraction',
            // 将observe应用于Swiper的父元素。当Swiper的父元素变化时,例如window.resize,Swiper更新。
            observeParents: true,
            // 启动动态检查器(OB/观众/观看者),当改变swiper的样式(例如隐藏/显示)或者修改swiper的子元素时,自动初始化swiper。
            observer: true
          }
        }
      },
    

    官网API说明: https://3.swiper.com.cn/api/
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    Fade:简单CSS动画组件

    该通用组件实质上为过渡效果的动画组件,之前文章中也提到过,CSS简单的动画过渡效果,这里同一封装为组件方便其他组件使用。

    其他组件在使用Fade组件时候,外层嵌套组件标签即可,组件将以插槽的方式包裹在<transition>标签内部。

      <template>
        <transition>
          <slot></slot>
        </transition>
      </template>
      
      <script>
      export default {
        name: 'Fade'
      }
      </script>
      
      <style lang="stylus" scoped>
        .v-enter, .v-leave-to
          opacity: 0
        .v-enter-active, .v-leave-active
          transition: opacity .5s
      </style>
    

    我们回到Banner组件中,引入并使用这两个通用组件。

    • 引入并注册通用组件

        import CommonGallary from 'common/gallary/Gallary'
        import FadeAnimation from 'common/fade/Fade'
      
        components: {
          CommonGallary,
          FadeAnimation
        }
      
    • 使用通用组件

      <template>

        <!-- 添加动画效果:渐隐渐现,内部common-gallary组件以插槽的方式嵌入 -->
        <fade-animation>
          <!-- 公用图片画廊 父子组件交互 + 展示控制-->
          <common-gallary
            :imgs="gallaryImgs"
            @close="handleGallaryClose"
            v-show="showGallary"></common-gallary>
        </fade-animation>
      

      这里可以看到传递给Gallery组件传递给父组件Banner的close的事件:handleGallaryClose,并且该组件上采用v-show做了组件展示的控制,控制变量为showGallary。

      默认情况下:

        data() {
          return {
            showGallary: false
          }
        },
      

      点击景点主图片区域时:

        handleBannerClick() {
      		this.showGallary = true
        }
      

      点击通用组件Gallary区域时:

        handleGallaryClose() {
          this.showGallary = false
        }
      

猜你喜欢

转载自blog.csdn.net/Nerver_77/article/details/84108448