• Vue3.0项目——打造企业级音乐App(一)Tab栏、轮播图、歌单列表、滚动组件


    系列文章目录



    设置移动端限制

    该项目为移动端的项目,我们要设置缩放比例为 1,并且禁止用户双击缩放。

    在这里插入图片描述

    引入全局样式文件

    在 main.js 中引入样式文件

    import '@/assets/scss/index.scss'
    
    • 1

    Tab 组件实现

    总体效果图:

    在这里插入图片描述

    header.vue 组件

    • 设置头部组件的内容,样式
    • 两个 icon,一个名字
    <template>
      <div class="header">
        <span class="icon"></span>
        <h1 class="text">Joyful Music</h1>
        <router-link class="mine" to="/user">
          <i class="icon-mine"></i>
        </router-link>
      </div>
    </template>
    
    <script>
      export default {
        name: 'm-header'
      }
    </script>
    
    <style lang="scss" scoped>...</style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    tab 组件

    <template>
      <div class="tab">
        <router-link
          class="tab-item"
          v-for="tab in tabs"
          :key="tab.path"
          :to="tab.path"
        >
          <span class="tab-link">
            {{tab.name}}
          </span>
        </router-link>
      </div>
    </template>
    
    <script>
      export default {
        name: 'tab',
        data() {
          return {
            tabs: [
              {
                name: '推荐',
                path: '/recommend'
              },
              {
                name: '歌手',
                path: '/singer'
              },
              {
                name: '排行',
                path: '/top-list'
              },
              {
                name: '搜索',
                path: '/search'
              }
            ]
          }
        }
      }
    </script>
    
    <style lang="scss" scoped>...</style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

    App.vue 组件

    <template>
      <m-header></m-header>
      <tab></tab>
      <router-view></router-view>
    </template>
    
    <script>
    import Header from '@/components/header/header.vue'
    import Tab from '@/components/tab/tab.vue'
    
    export default {
      components: {
        MHeader: Header,
        Tab
      }
    }
    </script>
    
    <style lang="scss"></style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ./router/index.js 文件

    import { createRouter, createWebHashHistory } from 'vue-router'
    import Recommend from '@/views/recommend'
    import Singer from '@/views/singer'
    import TopList from '@/views/top-list'
    import Search from '@/views/search'
    
    const routes = [
      {
        path: '/',
        redirect: '/recommend' // 重定向
      },
      {
        path: '/recommend',
        component: Recommend
      },
      {
        path: '/singer',
        component: Singer
      },
      {
        path: '/top-list',
        component: TopList
      },
      {
        path: '/search',
        component: Search
      }
    ]
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes
    })
    
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    获取轮播图接口数据

    ./server/base.js 文件

    • 封装 axios
    import axios from 'axios'
    
    const ERR_OK = 0
    // 开发环境下这样定义baseURL
    const baseURL = '/'
    
    axios.defaults.baseURL = baseURL
    
    export function get(url, params) {
        return axios.get(url, {
            params
        }).then((res) => {
            const serverData = res.data
            if (serverData.code === ERR_OK) {
                return serverData.result
            }
        }).catch((e) => {
            console.log(e)
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ./server/recommend.js 文件

    • 获取轮播图接口
    • ./base 里面是后端的一些配置
    import { get } from './base'
    
    export function getRecommend() {
        return get('/api/getRecommend')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    recommend.vue 组件

    • 把刚刚的轮播图文件导入进来
    • async await 异步处理,打印获取的结果
    <template>
      <div class="recommend">
        推荐页面
      </div>
    </template>
    
    <script>
      import { getRecommend } from '@/service/recommend'
      export default {
        name: 'recommend',
        async created() {
          const result = await getRecommend()
          console.log(result)
        }
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    推荐页轮播图

    轮播图效果使用的是 BetterScroll 2.0 参考链接

    在这里插入图片描述

    ./base/slider/use-slider.js 文件

    • 导入轮播图核心滚动和滑动栏
    • 定义滑动栏和当前页
    • new BScroll() 可以接收多个参数,第二个参数可以是对象的形式,里面添加多个参数
    import BScroll from '@better-scroll/core'
    import Slide from '@better-scroll/slide'
    
    import { onMounted, onUnmounted, ref } from 'vue'
    
    BScroll.use(Slide)
    
    export default function useSlider(wrapperRef) {
        const slider = ref(null)
        const currentPageIndex = ref(0)
        onMounted(() => {
            const sliderVal = slider.value = new BScroll(wrapperRef.value, {
                click: true,
                scrollX: true, // 横向滚动
                scrollY: false,
                momentum: false, // 避免惯性动画带来的快速滚动时的闪烁的问题和快速滑动时一次滚动多页的问题
                bounce: false, // 避免在循环衔接的时候出现闪烁
                probeType: 2, // 在用户拖动 slide 时,实时获取到 slide 的 PageIndex 的改变,需要设置为 2 or 3
                slide: true
            })
    
            sliderVal.on('slideWillChange', (page) => {
                currentPageIndex.value = page.pageX
            })
        })
    
        // 销毁
        onUnmounted(() => {
            slider.value.destroy()
        })
    
        return {
            slider,
            currentPageIndex
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    ./component/base/slider/slider.vue 组件

    • 渲染图片和滚动条
    <template>
      <div class="slider" ref="rootRef">
        <div class="slider-group">
          <div
            class="slider-page"
            v-for="item in sliders"
            :key="item.id"
          >
            <a :href="item.link">
              <img :src="item.pic"/>
            </a>
          </div>
        </div>
        <div class="dots-wrapper">
          <span
            class="dot"
            v-for="(item, index) in sliders"
            :key="item.id"
            :class="{'active': currentPageIndex === index}">
          </span>
        </div>
      </div>
    </template>
    
    <script>
      import { ref } from 'vue'
      import useSlider from './use-slider'
    
      export default {
        name: 'slider',
        props: {
          sliders: {
            type: Array,
            default() {
              return []
            }
          }
        },
        setup() {
          const rootRef = ref(null)
          const { currentPageIndex } = useSlider(rootRef)
          useSlider(rootRef)
          return {
            rootRef,
            currentPageIndex
          }
        }
      }
    </script>
    
    <style lang="scss" scoped>...</style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    ./views/recommend.vue 组件

    • 获取数据,绑定到推荐页面上
    <template>
      <div class="recommend">
        <div class="slider-warpper">
          <div class="slider-content">
            <slider v-if="sliders.length" :sliders="sliders"></slider>
          </div>
        </div>
      </div>
    </template>
    
    <script>
      import { getRecommend } from '@/service/recommend'
      import Slider from '@/components/base/slider/slider'
      export default {
        name: 'recommend',
        components: {
          Slider
        },
        data() {
          return {
            sliders: []
          }
        },
        async created() {
          const result = await getRecommend()
          // 拿到数据
          this.sliders = result.sliders
        }
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    歌单列表实现 & 滚动组件(可回弹)

    效果图如下:

    在这里插入图片描述

    ./components/scroll/use-scroll.js 文件

    observe-dom 插件特性:

    • 针对改变频繁的 CSS 属性,增加 debounce
    • 如果改变发生在 scroll 动画过程中,则不会触发 refresh
    import BScroll from '@better-scroll/core'
    import ObserveDOM from '@better-scroll/observe-dom'
    import { onMounted, onUnmounted, ref } from 'vue'
    
    BScroll.use(ObserveDOM)
    
    export default function useScroll(warpperRef, options) {
        const scroll = ref(null)
    
        onMounted(() => {
            scroll.value = new BScroll(warpperRef.value, {
                observeDOM: true,
                ...options
            })
        })
    
        onUnmounted(() => {
            scroll.value.destroy()
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ./component/scroll/scroll.vue 组件

    • 滚动组件使用封装的 js 文件
    • 给 setup 传入默认行为
    <template>
      <div ref="rootRef">
        <slot></slot>
      </div>
    </template>
    
    <script>
      import useScroll from './use-scroll'
      import { ref } from 'vue'
    
      export default {
        name: 'scroll',
        // 组件的默认行为
        props: {
          click: {
            type: Boolean,
            default: true
          }
        },
        setup(props) {
          const rootRef = ref(null)
          useScroll(rootRef, props)
    
          return {
            rootRef
          }
        }
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    ./views/recommend.vue 组件

    <template>
      <div class="recommend">
        <scroll class="recommend-content">
          <div>
            <div class="slider-wrapper">
              <div class="slider-content">
                <slider v-if="sliders.length" :sliders="sliders"></slider>
              </div>
            </div>
            <div class="recommend-list">
              <h1 class="list-title">热门歌单推荐</h1>
              <ul>
                <li
                  v-for="item in albums"
                  class="item"
                  :key="item.id"
                >
                  <div class="icon">
                    <img width="60" height="60" :src="item.pic">
                  </div>
                  <div class="text">
                    <h2 class="name">
                      {{ item.username }}
                    </h2>
                    <p class="title">
                      {{item.title}}
                    </p>
                  </div>
                </li>
              </ul>
            </div>
          </div>
        </scroll>
      </div>
    </template>
    
    <script>
      import { getRecommend } from '@/service/recommend'
      import Slider from '@/components/base/slider/slider'
      import Scroll from '@/components/base/scroll/scroll'
    
      export default {
        name: 'recommend',
        components: {
        Slider,
        Scroll
    },
        data() {
          return {
            sliders: [],
            albums: []
          }
        },
        async created () {
          const result = await getRecommend()
          this.sliders = result.sliders
          this.albums = result.albums
        }
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    不积跬步无以至千里 不积小流无以成江海

    点个关注不迷路,持续更新中…

  • 相关阅读:
    Golang struct 结构体 面向对象编程思想-抽象 理解抽象
    Shiro【核心功能、核心组件、项目搭建 、配置文件认证、数据库认证 】(一)-全面详解(学习总结---从入门到深化)
    大型监控网络设备架构
    Fiddler之Replay功能详解
    python3读写dbf文件
    ORB-SLAM2 ---- Frame::ComputeBoW函数(TrackReferenceKeyFrame调用版)
    安装python扩展库
    【社媒营销】进来了解下Whatsapp Business API?出海群发不用愁!
    轻量封装WebGPU渲染系统示例<33>- 单精度浮点纹理(源码)
    python 正则表达式
  • 原文地址:https://blog.csdn.net/qq_45902692/article/details/126328303