第七章 Electron Vue3实现音乐播放器

news2024/11/28 9:31:15

一、介绍 🍑 🍑 🍑

一个音乐播放器应该具备播放、暂停、上一首、下一首、播放模式(单曲循环、列表循环、顺序播放……)。除了这些比如还可以扩展进度条的展示、拖拽、音量大小的调节,如果资源允许的话可以进行一些歌词的展示等等。由于博主爬虫的资源没有合适的歌词,所以歌词这块功能暂时没有进行开发,感兴趣的小伙伴可以自行去完善。

本章节的话我们只讲解如何开发一个音乐播放器并且通过nodejs去检索本地下载的歌曲,并展示在列表中,文章中的检索路径是写死的,你可以去更改为自动配置,在页面上弹出文件夹选择框,然后程序根据选择的文件夹去检索,这样更加灵活。本文就不讲解爬虫,先制作好音乐播放器,下一章节我们再详细爬取音乐资源。同时进行下载,收藏等等

二、安装依赖 🍈 🍈 🍈

这里主要是自动按需引入的一些配置和sass支持。感兴趣可以去看Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)[保姆级]_Etc.End的博客-CSDN博客

yarn add @iconify-json/carbon @iconify-json/ep @iconify-json/noto unplugin-auto-import unplugin-vue-components unplugin-icons sass sass-loader @types/node -D

修改vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import * as path from "path";
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

const pathSrc = path.resolve(__dirname, 'src')
export default defineConfig({
  base: './',
  plugins: [
    vue(),
    AutoImport({
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          enabledCollections: ['ep', 'carbon', 'noto']
        })
      ],
      dts: path.resolve(pathSrc + '/autoImport', 'auto-imports.d.ts')
    }),
    Components({
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          enabledCollections: ['ep', 'carbon', 'noto']
        })
      ],
      dts: path.resolve(pathSrc + '/autoImport', 'components.d.ts')
    }),
    Icons({
      autoInstall: true,
      compiler: 'vue3'
    })
  ],
  resolve: {
    alias: {
      '@': pathSrc, // 文件系统路径别名
    }
  },
  server: {
    port: 5173
  }
})

三、音乐播放状态管理器 🍌 🍌 🍌

安装jsmediatags插件,获取下载的音乐信息。比如歌手名称、歌曲名、专辑名称等等。这里可能会因为资源的差异,导致信息也会一定的差异,如果你复制代码没有获取到相对应的歌曲信息的话可以在控制台打印出来观察一下jsmediatags解析出来的相关信息。

yarn add jsmediatags

创建src/pinia/modules/music.modules.ts文件

import { defineStore } from 'pinia';
import {durationConversionTime} from "@/utils/DateTime";
// @ts-ignore
import {ElMessage} from "element-plus";

/**
 * @Description: 歌曲信息类型
 * @CreationDate 2023-05-08 11:50:36
 */
export interface MusicType {
    name?: string
    album?: string
    url?: string
    author?: string
    pic?: string
    percentage?: number
}

/**
 * @Description: 搜索歌曲结果
 * @CreationDate 2023-05-08 17:02:57
 */
export interface QueryMusicResultType {
    music_page_count: number // 搜索出来的歌曲总页数
    music_current_page: number // 当前搜索匹配的页数
    music_loading: boolean // 搜索歌曲时的状态
    music_noMore: boolean // 没有更多了
    music_name: string // 当前搜索歌曲的名称
    music_current_list: MusicType[] // 搜索出来的歌曲列表,叠加
}

/**
 * @Description: 当前播放音乐信息类型
 * @CreationDate 2023-05-08 11:51:00
 */
export interface CurrentPlayMusicType extends MusicType{
    current_play_audio_ready?: boolean //
}

/**
 * @Description: 歌单类型
 * @CreationDate 2023-05-11 11:00:37
 */
export interface PlaylistsListType {
    id?: number
    name: string // 歌单名称
    describe: string // 描述
}

/**
 * @Description: 我喜欢的音乐
 * @CreationDate 2023-05-13 20:06:33
 */
export interface LikeMusicType extends MusicType {
    id: number // 主键
}

/**
 * @Description: 播放器设置
 * @Author: Etc.End(710962805@qq.com)
 * @Copyright: TigerSong
 * @CreationDate 2023-05-13 00:04:43
 */
interface PlayerSettingType {
    playing: string //
    volume: number // 音量
    total_duration?: number // 当前播放歌曲时长
    total_duration_char?: string // 当前播放歌曲时长字符
    current_duration?: number // 当前播放歌曲时段
    current_duration_char?: string // 当前播放歌曲时段
    current_list_name: 'likeMusicList' | 'queryMusicResult' | 'playlistsList' | 'collectList' | 'downloadMusicList' // 当前播放列表 喜欢、查询、歌单、收藏
    play_mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay' // 播放模式 单曲循环 列表循环 顺序播放 随机播放
    previous_is_click: boolean // 可以点击上一首
    next_is_click: boolean // 可以点下一首
    play_is_click: boolean // 可以点播放
    play_index: number // 当前播放歌曲在列表中的索引
    play_name: string // 当前播放歌曲的名称
    play_state: boolean // 当前播放状态
}

/**
 * @Description: musicModule类型
 * @CreationDate 2023-05-08 11:51:15
 */
interface IAppState {
    player?: HTMLAudioElement // 播放器
    queryMusicResult: QueryMusicResultType // 搜索歌曲的结果
    currentPlayMusic: CurrentPlayMusicType // 当前播放音乐的相关信息
    playlistsList: PlaylistsListType[] // 歌单列表
    collectList: MusicType[] // 收藏列表
    likeMusicList: LikeMusicType[] // 喜欢的音乐
    playerSetting: PlayerSettingType // 播放器设置
    downloadMusicList: MusicType[]
}

/**
 * @Description: 音乐播放器状态管理器
 * @Author: Etc.End(710962805@qq.com)
 * @Copyright: TigerSong
 * @CreationDate 2023-05-08 11:51:33
 */
export const musicModule = defineStore({
    id: 'music',
    state(): IAppState {
        return {
            currentPlayMusic: {
                current_play_audio_ready: true,
                name: '',
                url: '',
                author: '',
                pic: '',
            },
            queryMusicResult: {
                music_name: '',
                music_page_count: 0,
                music_current_page: 1,
                music_loading: false,
                music_noMore: false,
                music_current_list: []
            },
            playlistsList: [],
            collectList: [],
            likeMusicList: [],
            playerSetting: {
                playing: '',
                volume: 100,
                current_list_name: 'downloadMusicList',
                play_mode: 'tableCirculation',
                previous_is_click: true,
                next_is_click: true,
                play_is_click: true,
                play_index: -1,
                play_name: '',
                play_state: false
            },
            downloadMusicList: []
        };
    },
    actions: {
        /**
         * @Description: 初始化音乐播放器
         * @CreationDate 2023-05-08 11:50:15
         */
        initMusic(container: HTMLAudioElement) {
            this.player = container
            this.setPreviousAndNextClick()
            this.playerSetting.play_index = -1
        },
        /**
         * @Description: 播放
         * @CreationDate 2023-05-08 11:50:03
         */
        play(index?: number):void {
            if (index || index === 0) {
                this.playerSetting.play_index = index;
                (this.player && this.currentPlayMusic.current_play_audio_ready) && this.player.play();
                this.currentPlayMusic.current_play_audio_ready = false
                this.playerSetting.play_state = true
            } else {
                (this.player) && this.player.play();
                this.playerSetting.play_state = true
            }
            this.setPreviousAndNextClick()
        },
        /**
         * @Description: 更新当前歌曲信息
         * @CreationDate 2023-05-08 11:49:06
         */
        setCurrentMusicInfo(info: MusicType):void {
            this.currentPlayMusic = Object.assign(this.currentPlayMusic, info)
            if (this.player && this.currentPlayMusic.url) {
                this.player.src = this.currentPlayMusic.url
                this.playerSetting.play_name = this.currentPlayMusic.name!
            }
        },
        /**
         * @Description: 获取已经下载的所有文件名
         * @CreationDate 2023-05-16 17:24:08
         */
        getDownloadMusicList() {
            const that = this
            const musicList :MusicType[] = [];
            const fs = require('fs')
            const pathList = fs.readdirSync(`${process.cwd()}/download/music`);

            !(async () => {
                for (let i = 0; i < pathList.length; i++) {
                    const data: any = await that.getDownloadMusicInfo(`${process.cwd()}/download/music/${pathList[i]}`)
                    if (data.tags) {
                        musicList.push({
                            name: data.tags.title || pathList[i],
                            album: data.tags.album,
                            author: data.tags.artist,
                            pic: '',
                            url: `file:${process.cwd()}/download/music/${pathList[i]}`
                        });
                    }
                }
                this.downloadMusicList = musicList
            })();
        },
        /**
         * @Description: 根据jsmediatags插件读取下载的歌曲的信息
         * @CreationDate 2023-05-22 09:33:44
         */
        getDownloadMusicInfo(path: string) {
            return new Promise((resolve, reject) => {
                const jsmediatags = require('jsmediatags')
                new jsmediatags.Reader(path).setTagsToRead(["title", "track", "artist", "album", "year"])
                    .read({
                        onSuccess: (tag: any) => {
                            resolve(tag);
                        },
                        onError: (error: any) => {
                            reject(error);
                        }
                    });
            })
        },
        // -----------------------------------------------------------------播放器相关设置-----------------------------------------------------------------
        ready ():void{
            this.currentPlayMusic.current_play_audio_ready = true
            this.playerSetting.total_duration = ~~this.player!.duration;
            this.playerSetting.total_duration && (this.playerSetting.total_duration_char = durationConversionTime(this.playerSetting.total_duration))
        },
        /**
         * @Description: 报错回调
         * @CreationDate 2023-05-13 17:39:59
         */
        error(code: any): void {
            console.log(code)
        },
        /**
         * @Description: 获取音乐当前播放的时段
         * @CreationDate 2023-05-13 20:46:53
         */
        updateTime(): void {
            if (this.player) {
                this.playerSetting.current_duration = ~~this.player.currentTime
                this.playerSetting.current_duration && (this.playerSetting.current_duration_char = durationConversionTime(this.playerSetting.current_duration))
            }
        },
        /**
         * @Description: 设置音乐当前播放的时段
         * @CreationDate 2023-05-13 20:47:17
         */
        settingDuration(val: number): void {
            if (this.player) {
                this.player.currentTime = val
            }
        },
        /**
         * @Description: 设置音量
         * @CreationDate 2023-05-12 23:44:52
         */
        settingVolume(volume: number):void {
            this.player && (this.player.volume = volume)
        },
        /**
         * @Description: 监听音乐播放结束
         * @CreationDate 2023-05-12 22:24:36
         */
        endPlayback ():void {
            this.playerSetting.current_duration = 0
            // 单曲循环
            if (this.playerSetting.play_mode === 'singleCirculation') {
                this.play(this.playerSetting.play_index)
            } else if (this.playerSetting.play_mode === 'sequentialPlay') { // 顺序播放
                let listLength:number = 0
                if (this.playerSetting.current_list_name === 'queryMusicResult') {
                    listLength = this.queryMusicResult.music_current_list.length
                } else {
                    listLength = this[this.playerSetting.current_list_name].length
                }
                if ((this.playerSetting.play_index + 1) === listLength) {
                    this.playerSetting.current_duration_char = '0:00'
                    this.suspend()
                } else {
                    this.play(this.playerSetting.play_index + 1)
                }
            }
            else { // 播放器默认就是列表循环 列表循环
                this.next()
            }
        },
        /**
         * @Description: 切换播放模式
         * @CreationDate 2023-05-15 09:43:34
         */
        changePlayMode (mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay'):void {
            this.playerSetting.play_mode = mode
        },
        /**
         * @Description: 上一首
         * @CreationDate 2023-05-08 11:49:31
         */
        previous():void {
            if (this.playerSetting.play_index > 1) {
                this.playerSetting.play_index -= 1
                this.setCurrentMusicInfo(this.queryMusicResult.music_current_list[this.playerSetting.play_index])
                this.play()
            }
        },
        /**
         * @Description: 下一首
         * @CreationDate 2023-05-08 11:49:24
         */
        next():void {
            if (this.playerSetting.current_list_name === 'queryMusicResult') {
                if (this.playerSetting.play_index < this.queryMusicResult.music_current_list.length) {
                    this.playerSetting.play_index += 1
                } else {
                    this.playerSetting.play_index = 0
                }
                this.setCurrentMusicInfo(this.queryMusicResult.music_current_list[this.playerSetting.play_index])
            } else {
                if (this.playerSetting.play_index < this[this.playerSetting.current_list_name].length) {
                    this.playerSetting.play_index += 1
                } else {
                    this.playerSetting.play_index = 0
                }
                this.setCurrentMusicInfo(this[this.playerSetting.current_list_name][this.playerSetting.play_index])
            }
            this.play()
        },
        /**
         * @Description: 单曲循环
         * @CreationDate 2023-05-15 10:47:50
         */
        singleCirculation() :void {
            if (this.player) {
                this.player.currentTime = 0
                this.play()
            }
        },
        /**
         * @Description: 暂停
         * @CreationDate 2023-05-08 11:49:42
         */
        suspend():void {
            this.player && this.player.pause();
            this.playerSetting.play_state = false
        },
        /**
         * @Description: 设置上一首下一首按钮可以点击
         * @CreationDate 2023-05-09 10:15:10
         */
        setPreviousAndNextClick():void {
            this.playerSetting.next_is_click = this.playerSetting.play_index !== -1;
            this.playerSetting.previous_is_click = this.playerSetting.play_index !== -1;
        },
    },
    getters: {
    },
});

修改src/pinia/index.ts

import { userModule } from './modules/user.modules';
import { musicModule } from './modules/music.modules';

export interface IAppStore {
    userModule: ReturnType<typeof userModule>;
    musicModule: ReturnType<typeof musicModule>;
}

const appStore: IAppStore = {} as IAppStore;

export const registerStore = () => {
    appStore.userModule = userModule();
    appStore.musicModule = musicModule();
};

export default appStore;

四、编写播放器 🍍 🍍 🍍

创建src/components/music/player.vue文件

<template>
  <!--    <div class="ts-music&#45;&#45;player" v-if="currentMusicInfo.url">-->
    <div style="padding: 0 20px">
        <div class="ts-music--player">
            <div class="ts-music--player__title">
                <el-image :class="{ 'is-move': playerSetting.play_state}" :src="currentPlayMusic.pic"/>
                <span :title="currentPlayMusic.name">{{ currentPlayMusic.name }}</span>
            </div>
            <div class="ts-music--player__state">
                <div>
                    <el-button size="small" v-if="playerSetting.play_mode === 'tableCirculation'" @click="changePlayMode('sequentialPlay')" circle title="列表循环">
                        <template #icon>
                            <i-noto-counterclockwise-arrows-button />
                        </template>
                    </el-button>
                    <el-button size="small" v-else-if="playerSetting.play_mode === 'sequentialPlay'" @click="changePlayMode('randomPlay')" circle title="顺序播放">
                        <template #icon>
                            <i-noto-clockwise-vertical-arrows />
                        </template>
                    </el-button>
                    <el-button size="small" v-else-if="playerSetting.play_mode === 'singleCirculation'" @click="changePlayMode('tableCirculation')" circle title="单曲循环">
                        <template #icon>
                            <i-noto-repeat-single-button />
                        </template>
                    </el-button>
                    <el-button size="small" v-else circle title="随机播放" @click="changePlayMode('singleCirculation')">
                        <template #icon>
                            <i-noto-shuffle-tracks-button />
                        </template>
                    </el-button>
                    <el-button size="small" circle @click="previous" :disabled="!playerSetting.previous_is_click" title="上一首">
                        <template #icon>
                            <i-noto-fast-reverse-button />
                        </template>
                    </el-button>
                    <el-button size="small" circle v-if="!playerSetting.play_state" :disabled="playerSetting.play_index === -1" @click="play" title="播放">
                        <template #icon>
                            <i-noto-play-button />
                        </template>
                    </el-button>
                    <el-button size="small" circle v-else @click="suspend" title="暂停">
                        <template #icon>
                            <i-noto-pause-button />
                        </template>
                    </el-button>
                    <el-button size="small" circle @click="next" :disabled="!playerSetting.previous_is_click" title="下一首">
                        <template #icon>
                            <i-noto-fast-forward-button />
                        </template>
                    </el-button>
                    <el-button size="small" circle color="#ddd">词</el-button>
                </div>
                <div class="music-monitor">
                    <span>{{ playerSetting.current_duration_char }}</span>
                    <el-slider :show-tooltip="false" @change="changeDuration" style="width: 280px!important;margin: 0 16px;" v-model="playerSetting.current_duration" :min="0" :max="playerSetting.total_duration" size="small" />
                    <span>{{ playerSetting.total_duration_char }}</span>
                </div>
            </div>
            <div class="ts-music--player__use">
                <div style="display: flex;align-items: center;justify-content: center;">
                    <i-noto-speaker-high-volume />
                    <el-slider style="width: 100px!important;margin: 0 0 0 6px;" color="#03a9a9" v-model="playerSetting.volume" @input="changeVolume" size="small" />
                </div>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import {defineComponent} from "vue";
import appStore from "@/pinia";
import {storeToRefs} from "pinia";
export default defineComponent({
    name: 'Player',
    // setup(props: Readonly<{ info: MusicType }>) {
    setup() {

        const { currentPlayMusic, playerSetting } = storeToRefs(appStore.musicModule)

        const play = () => {
            appStore.musicModule.play()
        }

        const suspend = () => {
            appStore.musicModule.suspend()
        }

        const previous = () => {
            appStore.musicModule.previous()
        }

        const next = () => {
            appStore.musicModule.next()
        }

        const changeVolume = (val: number) => {
            appStore.musicModule.settingVolume(val / 100)
        }

        const changeDuration = (val: number) => {
            appStore.musicModule.settingDuration(val)
        }

        const changePlayMode = (mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay') => {
            appStore.musicModule.changePlayMode(mode)
        }

        return {
            currentPlayMusic,
            play,
            suspend,
            previous,
            next,
            playerSetting,
            changeVolume,
            changeDuration,
            changePlayMode,
        }
    }
})
</script>

<style scoped lang="scss">
::v-deep(.el-slider__bar) {
  background: #03a9a9!important;
}

.ts-music--player {
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    padding: 0 20px;
    background: #101010;
    border: 1px solid #4e4e4f;
    font-size: 14px;
    color: white;
    height: 70px;
    border-radius: 8px;

    .ts-music--player__title {
        width: 300px;
        display: flex;
        height: 100%;
        align-items: center;

        .el-image {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            margin-right: 5px;
        }

        span {
            width: 220px;
            overflow: hidden;
            text-overflow:ellipsis;
            white-space: nowrap;
            margin-left: 6px;
        }
        
        .is-move {
            animation: rotation 10s linear infinite;
            -webkit-transform: rotate(360deg);
            -webkit-animation: rotation 10s linear infinite ;
            -o-animation: rotation 10s linear infinite;
        }

        @-webkit-keyframes rotation {
            from {
                -webkit-transform: rotate(0deg);
            }
            to {
                -webkit-transform: rotate(360deg);
            }
        }
    }

    .ts-music--player__state {
        height: 100%;
        width: calc(100% - 600px);
        display: flex;
        flex-direction: column;
        justify-content: space-evenly;
        align-items: center;

        .music-monitor {
            display: flex;
            font-size: 11px;
            align-items: center;
        }
    }

    .ts-music--player__use {
        height: 100%;
        width: 300px;
        display: flex;
        flex-direction: column;
        justify-content: space-evenly;
        align-items: center;
    }
}
</style>

修改src/App.vue

我们都知道,在我们切换路由的时候,上一个页面的内容会被销毁,但是我们音乐播放器,希望做到一个切换路由也不会被影响,只要程序在正常运行。它也可以做到一个持久性播放,背景音乐的效果。所有放在App.vue中的目的就是解决这个问题。

<template>
    <Header />
    <router-view />
    <audio
        v-show="false"
        ref="audioRef"
        preload
        @canplay="ready"
        @ended="endPlayback"
        @error="error"
        @timeupdate="updateTime"
    />
</template>

<script lang="ts">
import {defineComponent, nextTick, onMounted, ref} from "vue";
import Header from '@/components/header/index.vue'
import appStore from "@/pinia";
export default defineComponent({
    components: {
        Header
    },
    setup() {

        const isShow = ref<boolean>(false)
        const audioRef = ref()
        const ready = appStore.musicModule.ready
        const endPlayback = appStore.musicModule.endPlayback
        const error = appStore.musicModule.error
        const updateTime = appStore.musicModule.updateTime

        onMounted(() => {
            nextTick(async () => {
                const isActivate = await appStore.userModule.sendActivate('')
                if (isActivate) {
                    isShow.value = true
                    appStore.musicModule.initMusic(audioRef.value)
                    appStore.musicModule.getDownloadMusicList()
                }
            })
        })

        return {
            isShow,
            audioRef,
            ready,
            endPlayback,
            error,
            updateTime,
        }
    }
})
</script>


<style>
html, body, #app {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
}
</style>

五、下载列表 🍇 🍇 🍇

修改src/views/home/index.vue并且引用播放器

<template>
    <div class="ts-music" v-if="downloadMusicList.length > 0">
        <div class="ts-music--search">
            <el-input v-model="musicName" style="width: 400px" placeholder="关键词" class="input-with-select">
                <template #append>
                    <el-button @click="searchMusic">
                        <i-carbon-search style="color: #03a9a9;" />
                    </el-button>
                </template>
            </el-input>
        </div>
        <div style="display: flex;align-items: center;padding: 0;font-size: 12px;color: #a55151;justify-content: center;">注: 双击歌曲进行音乐播放</div>
        <div class="ts-music--content">
            <ul
                    class="ts-music--content__ul"
            >
                <li class="ts-music--content__li" style="font-weight: bold;">
                    <div style="width: 40px;display: flex;justify-content: space-around;align-items: center;height: 60px;font-size: 18px;">
                        序号
                    </div>
                    <div style="text-align: center;width: calc(100% - 380px)">标题</div>
                    <div style="width: 160px;text-align: center;">歌手</div>
                    <div style="text-align: center;width: 240px">专辑</div>
                </li>
                <li v-for="(item, index) in list" :key="index" class="ts-music--content__li" @dblclick="dblclick(item, index)">
                    <div style="width: 40px;display: flex;justify-content: space-around;align-items: center;height: 60px;font-size: 18px;">
                        <i-noto-speaker-high-volume v-if="playerSetting.play_state && playerSetting.play_name === item.name" />
                        <i-noto-speaker-low-volume v-else-if="playerSetting.play_name === item.name && !playerSetting.play_state" />
                        <span v-else>{{ `${index > 8 ? '' : 0 }${index + 1}` }}</span>
                    </div>
                    <div style="text-align: center;width: calc(100% - 380px)" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.name}}</div>
                    <div style="width: 160px;text-align: center;" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.author}}</div>
                    <div style="text-align: center;width: 240px" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.album ? item.album : '-'}}</div>
                </li>
            </ul>
        </div>
        <Player />
    </div>
    <template v-else>
        <div style="width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;">
            <el-empty :image-size="200" description="你还没有下载任何音乐,赶紧去搜索下载吧!" />
        </div>
    </template>
</template>

<script lang="ts">
import  {defineComponent, onMounted, reactive, toRefs} from "vue";
import appStore from "@/pinia";
import {storeToRefs} from "pinia";
import {MusicType} from "@/pinia/modules/music.modules";

export default defineComponent({
    setup() {
        const { downloadMusicList, playerSetting } = storeToRefs(appStore.musicModule)

        const state = reactive({
            list: [] as MusicType[],
            musicName: '' ,
        })

        /**
         * @Description: 搜索功能
         * @CreationDate 2023-05-05 10:15:13
         */
        const searchMusic = async () => {
            const list = JSON.parse(JSON.stringify(downloadMusicList.value))
            state.list = list.filter(item => item?.name?.includes(state.musicName))
        }

        /**
         * @Description: 监听回车事件
         * @CreationDate 2023-05-22 10:38:06
         */
        const keydownEvent = () => {
            document.onkeydown = (e: any) => {
                if (e.defaultPrevented) {
                    return;
                }
                if (e.keyCode === 13) {
                    searchMusic()
                }
            }
        }

        /**
         * @Description: 双击播放音乐
         * @CreationDate 2023-05-22 10:38:14
         */
        const dblclick = (item: MusicType, index: number) => {
            appStore.musicModule.setCurrentMusicInfo({
                author: item.author,
                pic: item.pic,
                name: item.name,
                url: item.url
            })
            appStore.musicModule.play(index)
            playerSetting.value.current_list_name = 'downloadMusicList'
        }

        onMounted(() => {
            appStore.musicModule.getDownloadMusicList()
            searchMusic()
            keydownEvent()
        })

        return {
            ...toRefs(state),
            searchMusic,
            dblclick,
            downloadMusicList,
            playerSetting,
        }
    }
})
</script>

<style scoped lang="scss">
::v-deep(.el-table__fixed-body-wrapper) {
  z-index: auto !important;
}

.ts-music {
    height: calc(100% - 60px);
    width: 100%;
    background: #171717;
    font-size: 14px;
    display: flex;
    color: white;
    flex-direction: column;
    overflow: hidden;
    -webkit-touch-callout:none; /*系统默认菜单被禁用*/
    -webkit-user-select:none; /*webkit浏览器*/
    -khtml-user-select:none; /*早期浏览器*/
    -moz-user-select:none;/*火狐*/
    -ms-user-select:none; /*IE10*/
    user-select:none;

    .ts-music--search {
        display:flex;
        justify-content: center;
        padding: 10px 0;
    }

    .ts-music--content {
        height: calc(100% - 130px);
        overflow-y: hidden;
        padding: 0 20px 0 20px;

        .ts-music--content__ul {
            height: 94%;
            list-style: none;
            padding: 0;
            overflow-y: auto;
            border: 1px solid #4e4e4f;
            border-radius: 8px;

            .ts-music--content__li {
                display: flex;
                flex-direction: row;
                align-items: center;
                justify-content: space-between;
                height: 80px;
                padding: 0 20px;
                border-bottom: 1px solid #4e4e4f;

                &:last-child {
                    border-bottom: none;
                }
            }
        }
    }
}
</style>

下载的歌曲存放目录

我是Etc.End。如果文章对你有所帮助,能否帮我点个免费的赞和收藏😍。

 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/626757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

跨境电商开发的基本流程

在全球化的浪潮下&#xff0c;跨境电商已成为一个热门的商业模式。然而&#xff0c;很多人在跨境电商开发的过程中遇到了许多问题。本文将介绍跨境电商开发的流程&#xff0c;希望能够帮助大家更好地了解这个行业。 首先&#xff0c;跨境电商开发的第一步是寻找合适的产品。这…

DOC文件丢失怎么恢复?用这7种方法找回

在日常生活和工作中&#xff0c;我们经常会因为各种原因丢失重要的DOC文件&#xff0c;这给我们带来了不小的困扰。但是&#xff0c;不必担心&#xff0c;在本文中&#xff0c;我们将带大家了解一下DOC文件丢失怎么恢复。 关于DOC文件 DOC文件是指一种微软公司开发的文字处理软…

【短视频抖音多账号管理系统源码开发路径】

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 短视频矩阵系统开发涉及到多个领域的技术&#xff0c;包括视频编解码技术、大数据处理技术、音视频传输技术、电子商务及支付技术等。因此&#xff0c;短视频矩阵系统开发人员需要具备扎实的计算机基础知识、出色的编…

多边形三角化Earcut分析,以mapbox EarCut源码举例

多边形三角化&#xff0c;简单来说&#xff0c;就是给定一圈有序的多边形点生成三角面片&#xff0c;EarCut的原理描述网络上有很多&#xff08;如https://blog.csdn.net/qq_24459491/article/details/102976671&#xff09;&#xff0c;就不细说了。但是有的时候看是一回事&am…

Ae 效果详解:Mocha AE

Ae菜单&#xff1a;效果/Boris FX Mocha/Mocha AE Effects/Boris FX Mocha/Mocha AE Ae菜单&#xff1a;动画/Track in Boris FX Mocha Animation/Track in Boris FX Mocha Mocha Pro 是一款强大的平面跟踪软件。既可作为独立软件运行&#xff0c;也提供了 Ae 的插件版本&#…

1.4C++运算符重载作函数

C运算符重载函数作为类成员函数 作为类成员函数时&#xff0c;运算符重载函数使用类的成员变量作为操作数。 写个 demo&#xff1a; 运算符重载函数作为类成员函数时&#xff0c;需要注意&#xff1a;运算符重载函数必须是类的成员函数&#xff0c;不能是普通函数或者全局函…

MIT6.024学习笔记(二)——图论(1)

学习不是为了竞争和战胜他人&#xff0c;而是为了更好地了解自己和世界。 - 达赖喇嘛 文章目录 图的相关概念涂色问题基础涂色方法&#xff08;贪婪算法&#xff09;证明 二分图匹配问题应用&#xff1a;稳定婚烟问题算法性质及其证明 图的相关概念 图的定义&#xff1a;一组&…

We need you | 隐语开源共建计划第一期任务等你认领

作为隐私计算开源界的一颗新星&#xff0c;隐语一直致力于推动隐私计算行业的发展&#xff0c;同时也在不断发展和完善中。通过开放、透明和共享的方式&#xff0c;协同行业优秀人才协同合作&#xff0c;推动隐私计算领域蓬勃发展&#xff0c;这是隐语开源的意义。因此&#xf…

FPGA多路视频叠加融合 HLS算法实现 提供2套工程源码和技术支持

目录 1、前言2、视频叠加模块的功能和性能3、HLS视频叠加融合设计4、vivado工程1--单路同源视频的缩放叠加详细设计方案vivado工程详解SDK工程详解 5、vivado工程2--两路非同源视频的缩放叠加详细设计方案vivado工程详解SDK工程详解 6、上板调试验证7、福利&#xff1a;工程代码…

MySQL底层数据结构

1、引入 一个sql语句在mysql中究竟是如何运行的&#xff1f;又应该通过怎样的方式去查找我们要找的数据&#xff1f;这里就涉及到几种存储数据的算法&#xff1b; 可以做索引的数据结构有数组、链表、二叉搜索树和B树&#xff08;B-树、B树&#xff09;。 2、各种数据结构 …

flutter多版本切换

方式一&#xff1a;符号连接(软链接) 注&#xff1a;无需修改环境变量&#xff0c;也不用重启AndroidStudio mac具体flutter版本切换操作步骤&#xff1a; 1、项目clean然后切换自己需要的分支 2、删除文件夹flutter目录文件&#xff08;这里的flutter是原来的符号连接&…

若依集成分库分表(一)

1.本次需求为同库分表 1.1 引入pom依赖 <!-- sharding-jdbc分库分表 --> <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-core</artifactId><version>4.1.1</version> </dependenc…

Linux内核中内存管理相关配置项的详细解析2

接前一篇文章&#xff1a;Linux内核中内存管理相关配置项的详细解析1 3. Default compressor&#xff08;默认压缩器&#xff09; 此项展开后如下图所示&#xff1a; 此项的内核源码详细解释为&#xff1a; Selects the default compression algorithm for the compressed cac…

谷歌发布一个免费的生成式人工智能课程

在过去几周&#xff0c;我们看到的都是AI将如何改变生活&#xff0c;无论是ChatGPT的文本生成&#xff0c;还是SD&#xff0c;Midjourney 的图像生成&#xff0c;这些AI的特点就是都是生成式的AI。而几天前&#xff0c;谷歌推出了一个生成式人工智能学习课程&#xff0c;课程涵…

美团太细了:Springcloud 微服务优雅停机,如何实现?

说在前面 关于Spring Boot、Spring Cloud应用的优雅停机&#xff0c;平时经常会被问到&#xff0c;这也是实际应用过程中&#xff0c;必须要掌握的点。 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如美团、拼多多、极兔、有赞、希音的…

深度学习应用篇-计算机视觉-语义分割综述[5]:FCN、SegNet、Deeplab等分割算法、常用二维三维半立体数据集汇总、前景展望等

【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍&#xff1a;【深度学习入门到进阶】必看系列&#xff0c;含激活函数、优化策略、损失函数、模型调优、归一化…

【新版】系统架构设计师 - 软件可靠性分析与设计

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 软件可靠性分析与设计考点摘要可靠性相关基本概念软件可靠性分析可靠性指标串联系统&#xff08;可靠性&#xff09;并联系统&#xff08;可靠性&#xff09;混合系统&#xff08;可靠性&#xff…

(转载)从0开始学matlab—总结

1.编程实例 下面的例子将向大家介绍如何用 MATLAB 解决问题。 例1 温度转换程序 问题&#xff1a; 设计一个 MATLAB 程序&#xff0c;读取一个华氏温度的输入&#xff0c;输出开尔文温度。 答案&#xff1a; 华氏温度和开尔文温度的转换关系式可在物理学课本中找到。其关系式…

压力测试分为稳定性测试和破坏性测试

压力测试分为稳定性测试和破坏性测试 压力测试是一种对软件系统进行负载测试的常见手段&#xff0c;可以评估其在不同负载条件下的稳定性、性能和可靠性等指标。常见的压力测试分为两种类型&#xff1a;稳定性测试和破坏性测试。 1. 稳定性测试 稳定性测试也称为基准测试&#…

shell脚本基础3——正则表达式

文章目录 一、基本了解二、基本正则表达式2.1 字符匹配2.2 位置锚定2.3 匹配次数2.4 分组 三、扩展正则表达式3.1 字符匹配3.2 位置锚定3.3 匹配次数3.4 分组3.5 或者 一、基本了解 正则表达式分2种&#xff1a;基本正则表达式和扩展正则表达式。grep 、sed命令使用正则较多&am…