Full Code of powerdong/Music-player for AI

master c505e40c41a3 cached
193 files
616.8 KB
250.2k tokens
141 symbols
1 requests
Download .txt
Showing preview only (708K chars total). Download the full file or copy to clipboard to get everything.
Repository: powerdong/Music-player
Branch: master
Commit: c505e40c41a3
Files: 193
Total size: 616.8 KB

Directory structure:
gitextract_0ydqwdr6/

├── .vscode/
│   └── settings.json
├── README.md
└── musicPlayer/
    ├── .babelrc
    ├── .editorconfig
    ├── .eslintignore
    ├── .eslintrc.js
    ├── .gitignore
    ├── .postcssrc.js
    ├── README.md
    ├── build/
    │   ├── build.js
    │   ├── check-versions.js
    │   ├── utils.js
    │   ├── vue-loader.conf.js
    │   ├── webpack.base.conf.js
    │   ├── webpack.dev.conf.js
    │   └── webpack.prod.conf.js
    ├── config/
    │   ├── dev.env.js
    │   ├── index.js
    │   └── prod.env.js
    ├── index.html
    ├── package.json
    ├── src/
    │   ├── App.vue
    │   ├── api/
    │   │   ├── config.js
    │   │   └── index.js
    │   ├── assets/
    │   │   ├── Bus.js
    │   │   ├── Mixins.js
    │   │   ├── styles/
    │   │   │   ├── border.css
    │   │   │   ├── global.less
    │   │   │   └── reset.css
    │   │   └── utils/
    │   │       ├── cookie.js
    │   │       ├── filters.js
    │   │       ├── getAstro.js
    │   │       ├── getPhone.js
    │   │       ├── getRandomArrayElements.js
    │   │       ├── modalScroll.js
    │   │       ├── scrollStopVideo.js
    │   │       └── setKeyWords.js
    │   ├── base/
    │   │   ├── albumPage/
    │   │   │   ├── index.vue
    │   │   │   └── index2.vue
    │   │   ├── alert.vue
    │   │   ├── audioAllTitle.vue
    │   │   ├── button.vue
    │   │   ├── circleLoading.vue
    │   │   ├── comments.vue
    │   │   ├── djDetailPage/
    │   │   │   ├── components/
    │   │   │   │   └── changeNav.vue
    │   │   │   ├── index.vue
    │   │   │   └── index2.vue
    │   │   ├── djSublistCard.vue
    │   │   ├── generalNav.vue
    │   │   ├── icon.vue
    │   │   ├── idxCard.vue
    │   │   ├── imgCard.vue
    │   │   ├── interchangeable.vue
    │   │   ├── loading.vue
    │   │   ├── loginPageIsShow.vue
    │   │   ├── pageErrorInfo.vue
    │   │   ├── pageLoading.vue
    │   │   ├── searchInput.vue
    │   │   ├── shouldLogin.vue
    │   │   ├── slider.vue
    │   │   ├── sliderNav.vue
    │   │   ├── song.vue
    │   │   ├── songListPage/
    │   │   │   └── index.vue
    │   │   └── titleFooter.vue
    │   ├── components/
    │   │   ├── detailPage/
    │   │   │   └── index.vue
    │   │   └── top-tip/
    │   │       └── index.vue
    │   ├── getInfos/
    │   │   ├── getData.js
    │   │   └── icons.js
    │   ├── main.js
    │   ├── pages/
    │   │   ├── audioIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── audioList.vue
    │   │   │   │   ├── bar.vue
    │   │   │   │   ├── functionButton.vue
    │   │   │   │   ├── lyricPage.vue
    │   │   │   │   ├── playIcons.vue
    │   │   │   │   ├── playing.vue
    │   │   │   │   └── small.vue
    │   │   │   └── index.vue
    │   │   ├── commentsIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── albumListInfo.vue
    │   │   │   │   └── centerMenu.vue
    │   │   │   └── index.vue
    │   │   ├── dateRecommend/
    │   │   │   └── index.vue
    │   │   ├── dj/
    │   │   │   ├── childrenPage/
    │   │   │   │   ├── class.vue
    │   │   │   │   ├── classRecommend.vue
    │   │   │   │   ├── djPayGift.vue
    │   │   │   │   ├── ranking-anchor.vue
    │   │   │   │   ├── ranking-program.vue
    │   │   │   │   ├── ranking-radio.vue
    │   │   │   │   ├── ranking.vue
    │   │   │   │   └── topConDetail.vue
    │   │   │   ├── components/
    │   │   │   │   ├── boutiqueRecom.vue
    │   │   │   │   ├── icons.vue
    │   │   │   │   ├── radioRecom.vue
    │   │   │   │   └── swiper.vue
    │   │   │   ├── index.vue
    │   │   │   ├── public.vue
    │   │   │   ├── publicClass.vue
    │   │   │   ├── publicImgWrap.vue
    │   │   │   └── titleAndThree.vue
    │   │   ├── djSublist/
    │   │   │   └── index.vue
    │   │   ├── findIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── chinese.vue
    │   │   │   │   ├── europe.vue
    │   │   │   │   ├── icons.vue
    │   │   │   │   ├── japan.vue
    │   │   │   │   ├── korea.vue
    │   │   │   │   ├── moreNewDish.vue
    │   │   │   │   ├── moreNewSongs.vue
    │   │   │   │   ├── newDish.vue
    │   │   │   │   ├── personalizedSongList.vue
    │   │   │   │   └── swiper.vue
    │   │   │   └── index.vue
    │   │   ├── friendIndex/
    │   │   │   ├── index.vue
    │   │   │   └── public.vue
    │   │   ├── homeIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── addNewPlayList.vue
    │   │   │   │   ├── homeList.vue
    │   │   │   │   ├── icons.vue
    │   │   │   │   └── songList.vue
    │   │   │   └── index.vue
    │   │   ├── idx/
    │   │   │   └── index.vue
    │   │   ├── loginIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── accountLogin.vue
    │   │   │   │   ├── phoneAccount.vue
    │   │   │   │   ├── phonePwd.vue
    │   │   │   │   ├── phoneVerify.vue
    │   │   │   │   ├── verifyCode.vue
    │   │   │   │   └── verifyInfo.vue
    │   │   │   └── index.vue
    │   │   ├── myFavorite/
    │   │   │   ├── components/
    │   │   │   │   ├── albums.vue
    │   │   │   │   ├── artists.vue
    │   │   │   │   ├── column.vue
    │   │   │   │   ├── mlog.vue
    │   │   │   │   └── videos.vue
    │   │   │   └── index.vue
    │   │   ├── nav/
    │   │   │   ├── components/
    │   │   │   │   ├── login-bottom.vue
    │   │   │   │   ├── login-icons-bottom.vue
    │   │   │   │   ├── login-icons-top.vue
    │   │   │   │   ├── login-icons.vue
    │   │   │   │   ├── login-top.vue
    │   │   │   │   └── login.vue
    │   │   │   └── index.vue
    │   │   ├── recentlyPlayed/
    │   │   │   └── index.vue
    │   │   ├── recommend/
    │   │   │   ├── fine/
    │   │   │   │   └── index.vue
    │   │   │   ├── general/
    │   │   │   │   └── index.vue
    │   │   │   ├── index.vue
    │   │   │   ├── navIndex/
    │   │   │   │   └── navList.vue
    │   │   │   └── recommended/
    │   │   │       └── index.vue
    │   │   ├── searchIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── history.vue
    │   │   │   │   └── hotSearch.vue
    │   │   │   └── index.vue
    │   │   ├── searchResults/
    │   │   │   ├── albumIndex/
    │   │   │   │   └── album.vue
    │   │   │   ├── artistIndex/
    │   │   │   │   └── artist.vue
    │   │   │   ├── composite/
    │   │   │   │   ├── components/
    │   │   │   │   │   ├── album.vue
    │   │   │   │   │   ├── artist.vue
    │   │   │   │   │   ├── djRadio.vue
    │   │   │   │   │   ├── playList.vue
    │   │   │   │   │   ├── simQuery.vue
    │   │   │   │   │   ├── song.vue
    │   │   │   │   │   ├── user.vue
    │   │   │   │   │   └── video.vue
    │   │   │   │   └── composite.vue
    │   │   │   ├── djRadioIndex/
    │   │   │   │   └── djRadio.vue
    │   │   │   ├── index.vue
    │   │   │   ├── navIndex/
    │   │   │   │   └── index.vue
    │   │   │   ├── playListIndex/
    │   │   │   │   └── playList.vue
    │   │   │   ├── singerIndex/
    │   │   │   │   ├── select.vue
    │   │   │   │   └── singer.vue
    │   │   │   ├── songIndex/
    │   │   │   │   └── song.vue
    │   │   │   ├── userIndex/
    │   │   │   │   └── user.vue
    │   │   │   └── videoIndex/
    │   │   │       └── video.vue
    │   │   ├── userInfoIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── userDynamic.vue
    │   │   │   │   └── userHome.vue
    │   │   │   └── index.vue
    │   │   └── videoIndex/
    │   │       ├── components/
    │   │       │   ├── acg.vue
    │   │       │   ├── animation.vue
    │   │       │   ├── dance.vue
    │   │       │   ├── game.vue
    │   │       │   ├── listenBGM.vue
    │   │       │   ├── musicFestival.vue
    │   │       │   ├── rock.vue
    │   │       │   ├── scene.vue
    │   │       │   └── singing.vue
    │   │       ├── index.vue
    │   │       ├── public.vue
    │   │       └── videoComments/
    │   │           ├── components/
    │   │           │   ├── video.vue
    │   │           │   ├── videoCreator.vue
    │   │           │   └── videoInfo.vue
    │   │           └── index.vue
    │   ├── router/
    │   │   └── index.js
    │   └── store/
    │       ├── actions.js
    │       ├── getters.js
    │       ├── index.js
    │       ├── mutation-types.js
    │       ├── mutations.js
    │       └── state.js
    └── static/
        └── .gitkeep

================================================
FILE CONTENTS
================================================

================================================
FILE: .vscode/settings.json
================================================
{
  "cSpell.words": [
    "RCVD",
    "autofocus",
    "blogaaa",
    "flexbox",
    "vmax",
    "zhidao"
  ]
}

================================================
FILE: README.md
================================================
<h1 align="center">Welcome to Music-player 👋</h1>

<p align="center">
<img src="https://img.shields.io/badge/webpack-^3.6.0-informational" alt="webpack">
<img src="https://img.shields.io/badge/vue-2.5.2-success" alt="vue">
<img src="https://img.shields.io/badge/node-10.15.3-fa983a" alt="node">
<img src="https://img.shields.io/badge/dependencies-up to date-8c7ae6" alt="dependencies">
<img src="https://img.shields.io/badge/dev dependencies-up to date-44bd32" alt="dev dependencies">
<p>

## :speech_balloon:前言

随着不断的学习 Vue,需要做一个小项目通过在实现项目过程中了解到更多的知识。随着如今人们对于音乐的需求,移动端的使用量愈发增加,项目通过 Vue 编写实现,全面借用**网易云音乐移动端**的 UI 设计、功能实现,努力做到以假乱真的效果。前几天便着手开始弄了,到今天为止也算是勉强能用了。

> 本项目自行构思得出,由个人独立编写程序研究。

**注:此项目纯属个人瞎搞,正常使用请选择[网易云音乐官方](https://music.163.com)客户端。**

## :muscle: 项目目标

全面实现移动端网易云音乐的功能

> **项目还在编写完善中**

## 🚀 如何运行

> node 版本 `[10.15.3]`

### ✨ 开发过程

```
# 克隆
git clone https://github.com/powerdong/Music-player.git
```

```
# 打开项目目录
cd Music-player

cd musicPlayer
```

```
# 安装依赖
npm install
```

```
# 开启本地服务运行项目
npm run dev
```

**欢迎 star,欢迎 issue**

### :eyes: 项目进度

**上一次更新(2020-03-15):** ~~登录功能bug修复~~

**最近一次更新(2020-05-04):** 歌单详情bug修复

> 感谢大家的关注,最近在实习期间未能及时更新,最近发现网易的api返回有变化,回来改了一个问题

### :memo: 版本更新

- **版本信息:** 2.4.8-> 2.8.10
- **时间:** 2019 年 12 月 7 日
- **更新内容:**
  - 增加发现页面下拉刷新+轻提示
  - 增加电台排行页面主播榜
  - 增加电台排行页面节目榜中 24 小时榜
  - 增加电台排行页面电台榜中付费精品榜
  - 修复登录页面不能返回 Bug
  - 修复其他已知问题
  - 优化结构

使用中有任何问题或建议,欢迎 Issue!

本项目在不断完善中,请大家拭目以待~

## 技术栈

### :point_right: 主要依赖

- Vue 全家桶(使用 Vue-cli 作为构建工具)
- WebPack4.0
- ES6
- Less
- ESLint
- Vant UI
- [网易云音乐 API](https://binaryify.github.io/NeteaseCloudMusicApi/#/)

### :clap: 项目演示

[demo 地址](http://140.143.128.100:3000)(请用 chrome/firefox 手机模式预览) -- 暂时停止

## :mega: 目标功能

- [x] 手机登录、注册
- [x] 修改密码
- [x] 我的页面歌单信息
- [x] 添加,删除歌单
- [x] 最近播放
- [x] 心动模式
- [x] 我的电台
- [x] 我的收藏
- [x] 发现页面推荐歌单
- [x] 发现页面新碟
- [x] 发现页面新歌
- [x] 发现页面每日推荐
- [x] 发现页面歌单
- [x] 视频页面
- [ ] 朋友页面
- [x] 歌单广场
- [x] 新歌推荐
- [x] 更多新碟
- [x] 发现页面排行榜
- [x] 发现页面电台
- [x] 退出账号
- [x] 发现页面私人 FM
- [x] 搜索功能
- [x] 搜索结果展示
- [x] 热搜榜
- [x] 历史记录
- [x] 搜索推荐
- [x] 歌手分类
- [x] 播放功能(**小播放器进度条**)
- [x] 播放列表
- [x] 添加删除播放列表
- [x] 签到
- [x] 歌曲喜欢与否
- [x] 专辑收藏与否
- [x] 歌单评论
- [x] 专辑评论
- [x] 点赞、发送、删除评论
- [x] 电台节目评论
- [x] 视频评论
- [x] 用户相关
- [ ] 页面滚动加载
- [ ] 左右滑动切换
- [ ] 页面切换动画
- [ ] 登陆情况判断
- [ ] 全面优化&修复

## :computer: 部分截图

侧边账户中心

![uDnX80.png](https://user-gold-cdn.xitu.io/2019/10/4/16d95706581a456f?w=472&h=837&f=png&s=45997)

发现页面&每日推荐

![uDuYM8.gif](https://user-gold-cdn.xitu.io/2019/10/4/16d9572548c6aaf3?w=491&h=838&f=gif&s=3519632)

歌单&歌单详情

![uDuGxf.gif](https://user-gold-cdn.xitu.io/2019/10/4/16d9572540c179b1?w=491&h=838&f=gif&s=2610859)

排行榜&排行榜信息

![uDu3Gt.gif](https://user-gold-cdn.xitu.io/2019/10/4/16d95725335944d7?w=491&h=838&f=gif&s=934318)

我的页面&最近播放

![uDu1PI.gif](https://user-gold-cdn.xitu.io/2019/10/4/16d95725267c98e0?w=491&h=838&f=gif&s=290121)

我的歌单&播放歌曲

![uDutsS.gif](https://user-gold-cdn.xitu.io/2019/10/4/16d9572557bd8143?w=491&h=838&f=gif&s=7018012)

搜索展示

![uDu8RP.gif](https://user-gold-cdn.xitu.io/2019/10/4/16d9572538e2c261?w=491&h=838&f=gif&s=1094604)

## :page_with_curl: 项目布局

```js
.src
+-- api
|   +-- config.js // 存取相关的api地址
|   +-- index.js // 请求相关的api方法
+-- assets
|   +-- styles
    |   +-- border.css // 移动端的1px边框
    |   +-- global.less // 全局应用样式
    |   +-- reset.css // 重置样式
    |   +-- resetEleUI.less // 修改elementUI组件样式
|   +-- utils // 全局要使用的方法
    |   +-- getPhone // 获取手机号码
    |   +-- modalScroll // 处理移动端滚动条
|   +-- Bus.js // Bus 总线
|   +-- Mixins.js // 混入(mixin)
+-- base // 存取页面公共的小组件
    +-- albumPage // 歌单展示页面组件
    +-- songListPage // 展示歌曲列表
    +-- alert // 提示消息
    +-- audioAllTitle // 播放全部标题行
    +-- button // 登陆页面按钮
    +-- djSublistCard // 类似于我的电台页面的长卡片组件
    +-- generalNav // 通用页面顶部的标题行
    +-- icon // 图标展示
    +-- idxCard // 官方排行榜
    +-- imgCard // 歌单的图片卡
    +-- interchangeable // 用来展示搜索展示页面除单曲以外的项目
    +-- loading // 转圈loading
    +-- pageErrorInfo // 出错提醒
    +-- pageErrorLoading // 页面加载loading
    +-- searchInput // 搜索框
    +-- slider // 播放列表滑块
    +-- sliderNav // 滑动标题
    +-- song // 歌曲项
    +-- titleFooter // 搜索展示页综合页面各项通用头和尾
+-- getInfos // 获取一些静态信息
    +-- getData // 获取静态信息方法
    +-- icon // 存取图标信息
+-- pages // 项目路由页面
+-- router // 路由配置
    +-- index
+-- store // vuex 配置使用
    +-- action // 根级别的 action
    +-- getter // 根级别的 getter
    +-- index // 组装模块并导出 store 的地方
    +-- mutation-types // 根级别的 mutation-types
    +-- mutation // 根级别的 mutation
    +-- state // 根级别的 state
```

## 打包日志

![M0Apjg.png](https://s2.ax1x.com/2019/11/16/M0Apjg.png)

![M0ASgS.png](https://s2.ax1x.com/2019/11/16/M0ASgS.png)

> 本项目会长期更新,欢迎大家指出问题,共同学习

## 作者

👤 **Lambda**


================================================
FILE: musicPlayer/.babelrc
================================================
{
  "presets": [
    ["env", {
      "modules": false,
      "targets": {
        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
      }
    }],
    "stage-2"
  ],
  "plugins": [
    ["import", {
      "libraryName": "vant",
      "libraryDirectory": "es",
      "style": true
    }],
    "transform-vue-jsx", "transform-runtime", [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}


================================================
FILE: musicPlayer/.editorconfig
================================================
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true


================================================
FILE: musicPlayer/.eslintignore
================================================
/build/
/config/
/dist/
/*.js


================================================
FILE: musicPlayer/.eslintrc.js
================================================
// https://eslint.org/docs/user-guide/configuring

module.exports = {
  root: true,
  parserOptions: {
    parser: 'babel-eslint'
  },
  env: {
    browser: true,
  },
  extends: [
    // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
    // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
    'plugin:vue/essential', 
    // https://github.com/standard/standard/blob/master/docs/RULES-en.md
    'standard'
  ],
  // required to lint *.vue files
  plugins: [
    'vue'
  ],
  // add your custom rules here
  rules: {
    // allow async-await
    'generator-star-spacing': 'off',
    // allow debugger during development
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    // "quotes": [1, "double"]
  }
}


================================================
FILE: musicPlayer/.gitignore
================================================
.DS_Store
node_modules/
/dist/
save/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln


================================================
FILE: musicPlayer/.postcssrc.js
================================================
// https://github.com/michael-ciniawsky/postcss-load-config

module.exports = {
  "plugins": {
    "postcss-import": {},
    "postcss-url": {},
    // to edit target browsers: use "browserslist" field in package.json
    "autoprefixer": {}
  }
}


================================================
FILE: musicPlayer/README.md
================================================
# music-player

> 高仿网易云音乐

## Build Setup

``` bash
# install dependencies
npm install

# serve with hot reload at localhost:8080
npm run dev

# build for production with minification
npm run build

# build for production and view the bundle analyzer report
npm run build --report
```

For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).


================================================
FILE: musicPlayer/build/build.js
================================================
'use strict'
require('./check-versions')()

process.env.NODE_ENV = 'production'

const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')

const spinner = ora('building for production...')
spinner.start()

rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
  if (err) throw err
  webpack(webpackConfig, (err, stats) => {
    spinner.stop()
    if (err) throw err
    process.stdout.write(stats.toString({
      colors: true,
      modules: false,
      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
      chunks: false,
      chunkModules: false
    }) + '\n\n')

    if (stats.hasErrors()) {
      console.log(chalk.red('  Build failed with errors.\n'))
      process.exit(1)
    }

    console.log(chalk.cyan('  Build complete.\n'))
    console.log(chalk.yellow(
      '  Tip: built files are meant to be served over an HTTP server.\n' +
      '  Opening index.html over file:// won\'t work.\n'
    ))
  })
})


================================================
FILE: musicPlayer/build/check-versions.js
================================================
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')

function exec (cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {
    name: 'node',
    currentVersion: semver.clean(process.version),
    versionRequirement: packageConfig.engines.node
  }
]

if (shell.which('npm')) {
  versionRequirements.push({
    name: 'npm',
    currentVersion: exec('npm --version'),
    versionRequirement: packageConfig.engines.npm
  })
}

module.exports = function () {
  const warnings = []

  for (let i = 0; i < versionRequirements.length; i++) {
    const mod = versionRequirements[i]

    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
      warnings.push(mod.name + ': ' +
        chalk.red(mod.currentVersion) + ' should be ' +
        chalk.green(mod.versionRequirement)
      )
    }
  }

  if (warnings.length) {
    console.log('')
    console.log(chalk.yellow('To use this template, you must update following to modules:'))
    console.log()

    for (let i = 0; i < warnings.length; i++) {
      const warning = warnings[i]
      console.log('  ' + warning)
    }

    console.log()
    process.exit(1)
  }
}


================================================
FILE: musicPlayer/build/utils.js
================================================
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')

exports.assetsPath = function (_path) {
  const assetsSubDirectory = process.env.NODE_ENV === 'production'
    ? config.build.assetsSubDirectory
    : config.dev.assetsSubDirectory

  return path.posix.join(assetsSubDirectory, _path)
}

exports.cssLoaders = function (options) {
  options = options || {}

  const cssLoader = {
    loader: 'css-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  const postcssLoader = {
    loader: 'postcss-loader',
    options: {
      sourceMap: options.sourceMap
    }
  }

  // generate loader string to be used with extract text plugin
  function generateLoaders (loader, loaderOptions) {
    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]

    if (loader) {
      loaders.push({
        loader: loader + '-loader',
        options: Object.assign({}, loaderOptions, {
          sourceMap: options.sourceMap
        })
      })
    }

    // Extract CSS when that option is specified
    // (which is the case during production build)
    if (options.extract) {
      return ExtractTextPlugin.extract({
        use: loaders,
        fallback: 'vue-style-loader'
      })
    } else {
      return ['vue-style-loader'].concat(loaders)
    }
  }

  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
  return {
    css: generateLoaders(),
    postcss: generateLoaders(),
    less: generateLoaders('less'),
    sass: generateLoaders('sass', { indentedSyntax: true }),
    scss: generateLoaders('sass'),
    stylus: generateLoaders('stylus'),
    styl: generateLoaders('stylus')
  }
}

// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
  const output = []
  const loaders = exports.cssLoaders(options)

  for (const extension in loaders) {
    const loader = loaders[extension]
    output.push({
      test: new RegExp('\\.' + extension + '$'),
      use: loader
    })
  }

  return output
}

exports.createNotifierCallback = () => {
  const notifier = require('node-notifier')

  return (severity, errors) => {
    if (severity !== 'error') return

    const error = errors[0]
    const filename = error.file && error.file.split('!').pop()

    notifier.notify({
      title: packageConfig.name,
      message: severity + ': ' + error.name,
      subtitle: filename || '',
      icon: path.join(__dirname, 'logo.png')
    })
  }
}


================================================
FILE: musicPlayer/build/vue-loader.conf.js
================================================
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
  ? config.build.productionSourceMap
  : config.dev.cssSourceMap

module.exports = {
  loaders: utils.cssLoaders({
    sourceMap: sourceMapEnabled,
    extract: isProduction
  }),
  cssSourceMap: sourceMapEnabled,
  cacheBusting: config.dev.cacheBusting,
  transformToRequire: {
    video: ['src', 'poster'],
    source: 'src',
    img: 'src',
    image: 'xlink:href'
  }
}


================================================
FILE: musicPlayer/build/webpack.base.conf.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-07-30 16:42:30
 * @Update: 2019-11-18 19:13:26
 * @Update log: 更新日志
 */
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')

function resolve (dir) {
  return path.join(__dirname, '..', dir)
}

const createLintingRule = () => ({
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  enforce: 'pre',
  include: [resolve('src'), resolve('test')],
  options: {
    formatter: require('eslint-friendly-formatter'),
    emitWarning: !config.dev.showEslintErrorsInOverlay
  }
})

module.exports = {
  context: path.resolve(__dirname, '../'),
  entry: {
    app: './src/main.js'
  },
  output: {
    path: config.build.assetsRoot,
    filename: '[name].js',
    publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath : config.dev.assetsPublicPath
  },
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'styles': resolve('src/assets/styles'),
      'mixins': resolve('src/assets'),
      'getInfos': resolve('src/getInfos'),
      'base': resolve('src/base'),
      'api': resolve('src/api'),
      'utils': resolve('src/assets/utils')
    }
  },
  module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  },
  node: {
    // prevent webpack from injecting useless setImmediate polyfill because Vue
    // source contains it (although only uses it if it's native).
    setImmediate: false,
    // prevent webpack from injecting mocks to Node native modules
    // that does not make sense for the client
    dgram: 'empty',
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty'
  }
}


================================================
FILE: musicPlayer/build/webpack.dev.conf.js
================================================
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')

const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)

const devWebpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
  },
  // cheap-module-eval-source-map is faster for development
  devtool: config.dev.devtool,

  // these devServer options should be customized in /config/index.js
  devServer: {
    clientLogLevel: 'warning',
    historyApiFallback: {
      rewrites: [
        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
      ],
    },
    hot: true,
    contentBase: false, // since we use CopyWebpackPlugin.
    compress: true,
    host: HOST || config.dev.host,
    port: PORT || config.dev.port,
    open: config.dev.autoOpenBrowser,
    overlay: config.dev.errorOverlay
      ? { warnings: false, errors: true }
      : false,
    publicPath: config.dev.assetsPublicPath,
    proxy: config.dev.proxyTable,
    quiet: true, // necessary for FriendlyErrorsPlugin
    watchOptions: {
      poll: config.dev.poll,
    }
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': require('../config/dev.env')
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
    new webpack.NoEmitOnErrorsPlugin(),
    // https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'index.html',
      inject: true
    }),
    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.dev.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

module.exports = new Promise((resolve, reject) => {
  portfinder.basePort = process.env.PORT || config.dev.port
  portfinder.getPort((err, port) => {
    if (err) {
      reject(err)
    } else {
      // publish the new Port, necessary for e2e tests
      process.env.PORT = port
      // add port to devServer config
      devWebpackConfig.devServer.port = port

      // Add FriendlyErrorsPlugin
      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
        },
        onErrors: config.dev.notifyOnErrors
        ? utils.createNotifierCallback()
        : undefined
      }))

      resolve(devWebpackConfig)
    }
  })
})


================================================
FILE: musicPlayer/build/webpack.prod.conf.js
================================================
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

const env = require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, {
  module: {
    rules: utils.styleLoaders({
      sourceMap: config.build.productionSourceMap,
      extract: true,
      usePostCSS: true
    })
  },
  devtool: config.build.productionSourceMap ? config.build.devtool : false,
  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
  },
  plugins: [
    // http://vuejs.github.io/vue-loader/en/workflow/production.html
    new webpack.DefinePlugin({
      'process.env': env
    }),
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false
        }
      },
      sourceMap: config.build.productionSourceMap,
      parallel: true
    }),
    // extract css into its own file
    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      allChunks: true,
    }),
    // Compress extracted CSS. We are using this plugin so that possible
    // duplicated CSS from different components can be deduped.
    new OptimizeCSSPlugin({
      cssProcessorOptions: config.build.productionSourceMap
        ? { safe: true, map: { inline: false } }
        : { safe: true }
    }),
    // generate dist index.html with correct asset hash for caching.
    // you can customize output by editing /index.html
    // see https://github.com/ampedandwired/html-webpack-plugin
    new HtmlWebpackPlugin({
      filename: config.build.index,
      template: 'index.html',
      inject: true,
      minify: {
        removeComments: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true
        // more options:
        // https://github.com/kangax/html-minifier#options-quick-reference
      },
      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
      chunksSortMode: 'dependency'
    }),
    // keep module.id stable when vendor modules does not change
    new webpack.HashedModuleIdsPlugin(),
    // enable scope hoisting
    new webpack.optimize.ModuleConcatenationPlugin(),
    // split vendor js into its own file
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks (module) {
        // any required modules inside node_modules are extracted to vendor
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    }),
    // extract webpack runtime and module manifest to its own file in order to
    // prevent vendor hash from being updated whenever app bundle is updated
    new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
    // This instance extracts shared chunks from code splitted chunks and bundles them
    // in a separate chunk, similar to the vendor chunk
    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
    new webpack.optimize.CommonsChunkPlugin({
      name: 'app',
      async: 'vendor-async',
      children: true,
      minChunks: 3
    }),

    // copy custom static assets
    new CopyWebpackPlugin([
      {
        from: path.resolve(__dirname, '../static'),
        to: config.build.assetsSubDirectory,
        ignore: ['.*']
      }
    ])
  ]
})

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = webpackConfig


================================================
FILE: musicPlayer/config/dev.env.js
================================================
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"'
})


================================================
FILE: musicPlayer/config/index.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-07-16 19:49:41
 * @Update: 2019-08-18 19:16:52
 * @Update log: 更新日志
 */
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.

const path = require('path')

module.exports = {
  dev: {
    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {// 输入/api 让其去访问http://localhost:3000/api
      '/api': {
        target: 'http://140.143.128.100:3000', // 设置调用的接口域名和端口号 ( 设置代理目标)
        changeOrigin: true,
        pathRewrite: {

          '^/api': '' // 这是一个通配符,设置完了之后每个接口都要在前面加上/api(特别注意这一点)
        }
      }
    },
    // Various Dev Server settings
    host: 'localhost', // can be overwritten by process.env.HOST
    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
    autoOpenBrowser: false,
    errorOverlay: true,
    notifyOnErrors: true,
    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-

    // Use Eslint Loader?
    // If true, your code will be linted during bundling and
    // linting errors and warnings will be shown in the console.
    useEslint: true,
    // If true, eslint errors and warnings will also be shown in the error overlay
    // in the browser.
    showEslintErrorsInOverlay: false,

    /**
     * Source Maps
     */

    // https://webpack.js.org/configuration/devtool/#development
    devtool: 'cheap-module-eval-source-map',

    // If you have problems debugging vue-files in devtools,
    // set this to false - it *may* help
    // https://vue-loader.vuejs.org/en/options.html#cachebusting
    cacheBusting: true,

    cssSourceMap: true
  },

  build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',

    /**
     * Source Maps
     */

    productionSourceMap: true,
    // https://webpack.js.org/configuration/devtool/#production
    devtool: '#source-map',

    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],

    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  }
}


================================================
FILE: musicPlayer/config/prod.env.js
================================================
'use strict'
module.exports = {
  NODE_ENV: '"production"'
}


================================================
FILE: musicPlayer/index.html
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-07-30 16:42:30
 * @Update: 2019-11-04 13:34:22
 * @Update log: 更新日志
 -->
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport"
    content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta name="referrer" content="never">
  <meta name="referrer" content="no-referrer">
  <link href="http://p3.music.126.net/tBTNafgjNnTL1KlZMt7lVA==/18885211718935735.jpg" rel="shortcut icon">
  <style>
    html,
    body {
      overflow-x: hidden;
      overflow-y: auto;
    }

    body {
      position: relative;
      font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;
    }

    #load {
      width: 100vw;
      height: 100vh;
      display: none;
      overflow: hidden;
      background-color: #dd001b;
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      font-size: 10px;
      z-index: 999;
    }

    .index-title {
      position: absolute;
      top: 2.3em;
      width: 100%;
      color: #fff;
      font-size: 4.6em;
      line-height: 1.5;
      text-align: center;
      opacity: 0;
      animation: titleIn 1s both;
    }

    .index-icon {
      position: absolute;
      bottom: 3em;
      font-size: 2em;
      line-height: 1.5;
      width: 100%;
      color: #fff;
      text-align: center;
      float: right;
      opacity: 0;
      animation: iconIn 3s 2s both;
    }

    @keyframes titleIn {
      100% {
        opacity: 1;
      }
    }

    @keyframes iconIn {
      100% {
        opacity: 1;
      }
    }

  </style>
  <title>music-player</title>
</head>

<body>
  <div id="load">
    <p class="index-title">音樂的力量</p>
    <p class="index-icon"><svg t="1567819718363" class="icon" style="transform: translateY(40%)" viewBox="0 0 1024 1024"
        version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1950" width="50" height="50">
        <path
          d="M268.2 89.2h498.9c92 0 166.3 74.3 166.3 166.3v498.9c0 92-74.3 166.3-166.3 166.3H268.2c-92 0-166.3-74.3-166.3-166.3V255.5c0-92 74.3-166.3 166.3-166.3z m0 0"
          fill="#D92916" p-id="1951"></path>
        <path
          d="M640 196.4c19.7 4.8 38.2 13.8 54.2 26.2 6.1 4.3 10.8 10.1 13.8 16.9 4.3 10.6 3.2 22.6-3.1 32.1-10.2 15.9-31.3 20.7-47.3 10.7-3.8-2.4-6.6-5.9-10.4-7.9-10.1-7.1-22.1-11.1-34.5-11.4-8.5 0.9-16.4 5-22.1 11.4-5.7 5.8-8.2 14.1-6.6 22.1l12.8 48c30.7 1.2 60.6 9.9 87 25.5 25.6 15.7 48.2 35.9 66.6 59.7 15.7 20.6 27.4 44.1 34.5 69 7.5 25.9 10.5 52.9 8.6 79.7-1.6 22.6-6.3 44.9-13.8 66.3-19.9 52.4-56.2 97-103.6 127-34.7 21.6-73.7 35.3-114.3 40-27.8 4.1-56.1 4.1-83.9 0-57.6-10-110.6-38.1-151.2-80.1-71-72-100.6-175.1-78.4-273.8 20.6-93.6 85.9-171 174.7-207.1 12.6-5 26.7-4.2 38.7 2.1 10.3 7.4 15.8 19.9 14.2 32.5-1.5 12.8-10 23.7-22.1 28.3-76.2 29-130.3 97.4-141.2 178.1-5 37.1-0.7 74.8 12.4 109.8 18.8 51.1 55.6 93.6 103.6 119.4 29.1 15.1 61.4 23 94.2 22.8 27-0.2 53.7-4.9 79.1-14.2 43.7-15.6 80.2-46.8 102.2-87.7 5.5-9.8 9.9-20.3 13.1-31.1 9.1-31.3 9.1-64.6 0-96-8-26.1-23.2-49.4-43.8-67.3-9-7.9-18.7-15-29-21.1-9.1-5-18.9-8.7-29-11 6.9 27.6 14.5 55.2 21.7 83.2l3.5 20c1.9 57.7-38.3 108.3-94.9 119.4-28.6 5.7-58.3 0.1-82.9-15.5-22.9-15.2-40.1-37.5-49-63.5-5.1-14.6-7.8-29.8-8.3-45.2-1.8-33.8 7.4-67.2 26.2-95.3 23.3-32.6 57.3-56 96-66.3l-8.3-32.1c-7-22.9-5.1-47.5 5.2-69 6.1-12 14.5-22.7 24.9-31.4 10.5-10.2 23.1-17.9 36.9-22.8 17.5-4.7 36-4.9 53.6-0.4z m0 0"
          fill="#FFFFFF" p-id="1952"></path>
        <path
          d="M497.4 448.4c-10.2 10.8-17.3 24.1-20.4 38.7-2.6 13.2-2.6 26.8 0 40 2.6 14.9 10.8 28.2 22.8 37.3 10.1 6.9 22.6 9.3 34.5 6.6 21.8-4.2 37.8-22.7 39-44.9-0.7-5.4-1.7-10.7-3.1-15.9l-23.8-90.1c-18.5 5-35.4 14.7-49 28.3z m0 0"
          fill="#D92916" p-id="1953"></path>
      </svg>网易云音乐</p>
  </div>
  <div id="app""></div>
    <!-- built files will be auto injected -->
    <script>
      const load = document.getElementById('load')
      const body = document.getElementsByTagName('body')[0]
      let data = sessionStorage.getItem('load');
        console.log(!data);
      if (!data) {
        setLoad(body, load)
      }

      function setLoad(body, load) {
        console.log('执行了')
        body.style.overflow = 'hidden';
        load.style.display = 'block';
        sessionStorage.setItem('load', 1);
        setTimeout(() => {
          load.style.display = 'none'
          body.style.overflow = '';
        }, 5000);
      }
    </script>
  </body>
</html>


================================================
FILE: musicPlayer/package.json
================================================
{
  "name": "music-player",
  "version": "1.0.0",
  "description": "高仿网易云音乐",
  "author": "powerdong <m19893170798@163.com>",
  "private": true,
  "scripts": {
    "dev": "webpack-dev-server --inline --progress --host 0.0.0.0 --config build/webpack.dev.conf.js",
    "start": "npm run dev",
    "lint": "eslint --ext .js,.vue src",
    "build": "node build/build.js"
  },
  "dependencies": {
    "axios": "^0.19.0",
    "core-js": "3",
    "fastclick": "^1.0.6",
    "less": "^3.9.0",
    "less-loader": "^5.0.0",
    "qs": "^6.8.0",
    "vant": "^2.2.10",
    "vue": "^2.5.2",
    "vue-lazyload": "^1.3.2",
    "vue-router": "^3.0.1",
    "vuex": "^3.1.1"
  },
  "devDependencies": {
    "autoprefixer": "^7.1.2",
    "babel-core": "^6.22.1",
    "babel-eslint": "^8.2.1",
    "babel-helper-vue-jsx-merge-props": "^2.0.3",
    "babel-loader": "^7.1.1",
    "babel-plugin-component": "^1.1.1",
    "babel-plugin-import": "^1.12.2",
    "babel-plugin-syntax-jsx": "^6.18.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-plugin-transform-vue-jsx": "^3.5.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-stage-2": "^6.22.0",
    "chalk": "^2.0.1",
    "copy-webpack-plugin": "^4.0.1",
    "css-loader": "^0.28.0",
    "eslint": "^4.15.0",
    "eslint-config-standard": "^10.2.1",
    "eslint-friendly-formatter": "^3.0.0",
    "eslint-loader": "^1.7.1",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.2.0",
    "eslint-plugin-promise": "^3.4.0",
    "eslint-plugin-standard": "^3.0.1",
    "eslint-plugin-vue": "^4.0.0",
    "extract-text-webpack-plugin": "^3.0.0",
    "file-loader": "^1.1.4",
    "friendly-errors-webpack-plugin": "^1.6.1",
    "html-webpack-plugin": "^2.30.1",
    "node-notifier": "^5.1.2",
    "optimize-css-assets-webpack-plugin": "^3.2.0",
    "ora": "^1.2.0",
    "portfinder": "^1.0.13",
    "postcss-import": "^11.0.0",
    "postcss-loader": "^2.0.8",
    "postcss-url": "^7.2.1",
    "rimraf": "^2.6.0",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "url-loader": "^0.5.8",
    "vue-loader": "^13.3.0",
    "vue-style-loader": "^3.0.1",
    "vue-template-compiler": "^2.5.2",
    "webpack": "^3.6.0",
    "webpack-bundle-analyzer": "^2.9.0",
    "webpack-dev-server": "^2.9.1",
    "webpack-merge": "^4.1.0"
  },
  "engines": {
    "node": ">= 6.0.0",
    "npm": ">= 3.0.0"
  }
}


================================================
FILE: musicPlayer/src/App.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-08-30 19:47:55
 * @Update: 2020-03-13 22:38:04
 * @Update log: 更新日志
 -->
<template>
  <div id="app">
    <transition name="router-fade" mode="out-in">
      <keep-alive exclude="phoneVerify, phonePwd">
        <router-view></router-view>
        <!-- <router-view v-if="$route.meta.keepAlive"></router-view> -->
      </keep-alive>
      <!-- </transition>
      <transition name="router-fade" mode="out-in">
      <router-view v-if="!$route.meta.keepAlive"></router-view>-->
    </transition>
    <!-- 通过查看播放列表的length来看看播放页面是否可以显示 -->
    <play-song v-show="AUDIO_LIST.length"></play-song>
  </div>
</template>

<script>
import playSong from '@/pages/audioIndex'
import { mapGetters } from 'vuex'
export default {
  name: 'App',
  components: {
    playSong
  },
  computed: {
    ...mapGetters([
      'FULL_SCREEN',
      'AUDIO_LIST'
    ])
  },
  data () {
    return {
      show: false
    }
  }
}
</script>

<style lang="less">
#app {
  height: 100vh;
  width: 100vw;
}
</style>


================================================
FILE: musicPlayer/src/api/config.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-08-19 13:42:17
 * @Update: 2019-12-03 13:37:22
 * @Update log: 更新日志
 */
const api = process.env.NODE_ENV === 'development' ? '/api' : ''

// ===================发现页面
export const bannerSwiper = api + '/banner?type=1' // 请求发现页面轮播图
export const recSongList = api + '/top/playlist' // 推荐歌单,歌单广场
export const highquality = api + '/top/playlist/highquality' // 精品歌单
export const catlist = api + '/playlist/catlist' // 获取歌单分类 !!!
export const hot = api + '/playlist/hot' // 获取热门歌单分类 !!!
export const topList = api + '/toplist/detail' // 获取所有榜单内容摘要
export const idxList = api + '/top/list' // 获取排行榜
export const albumDetail = api + '/playlist/detail' // 获取歌单详情
export const recSongs = api + '/recommend/songs' // 每日推荐歌曲
export const dateRecSongList = api + '/recommend/resource' // 每日推荐歌单,发现页展示的那六个
export const newDish = api + '/top/album' // 发现页新碟
export const getDishInfo = api + '/album' // 获取专辑内容
export const newSongs = api + '/top/song' // 发现页新歌
export const personalFm = api + '/personal_fm' // 发现页私人FM
// ======================播放歌曲
export const songUrl = api + '/song/url' // 获取歌曲url
export const checkSong = api + '/check/music' // 查看歌曲是否可用
export const songLyric = api + '/lyric' // 获取歌词
export const heartMode = api + '/playmode/intelligence/list' // 心动模式播放
export const likeMusicList = api + '/likelist' // 喜欢歌曲列表
export const likeMusic = api + '/like' // 喜欢歌曲
// ===================登陆
export const phoneLogin = api + '/login/cellphone' // 手机号登陆
export const phoneRegistered = api + '/cellphone/existence/check' // 手机号是否被注册
export const sendVerify = api + '/captcha/sent' // 发送验证码
export const verify = api + '/captcha/verify' // 验证验证码
export const loginStatus = api + '/login/status' // 登录状态
export const logout = api + '/logout' // 退出登录
export const signIn = api + '/daily_signin' // 签到
export const register = api + '/register/cellphone' // 注册修改密码
// ===================我的页面相关
export const userRecord = api + '/user/record' // 用户播放记录
export const userInfo = api + '/user/subcount' // 用户信息
export const userDetail = api + '/user/detail' // 用户详情
export const userEvent = api + '/user/event' // 用户动态
export const playlist = api + '/user/playlist' // 用户歌单
export const userDj = api + '/user/dj' // 用户电台 申请做主播那一行
export const favoriteAlbums = api + '/album/sublist' // 获取收藏的专辑
export const favoriteArtists = api + '/artist/sublist' // 获取收藏的歌手
export const favoriteVideos = api + '/mv/sublist' // 获取收藏的视频
export const djSublist = api + '/dj/sublist' // 获取订阅的电台
// =================视频页面下相关
export const getVideoTag = api + '/video/group/list' // 获取视频标签导航
export const getVideoGroup = api + '/video/group' // 获取对应标签的视频详情
export const getVideoUrl = api + '/video/url' // 获取视频播放地址
export const getVideoDetail = api + '/video/detail' // 获取视频详情
export const getVideoRelated = api + '/related/allvideo' // 获取相关视频
export const getVideoComments = api + '/comment/video' // 获取视频评论
// ==================搜索页面相关
export const search = api + '/search' // 搜索关键词
export const defaultSearch = api + '/search/default' // 默认搜索关键词
export const suggestSearch = api + '/search/suggest' // 搜索建议
export const hotSearchList = api + '/search/hot/detail' // 热搜列表
export const singerClass = api + '/artist/list' // 歌手分类
// =================朋友页面
export const friend = api + '/event' // 获取朋友页面的动态
// =================歌单事件
export const addOrDeletePlaylist = api + '/playlist/subscribe' // 收藏/取消收藏歌单
export const addPlaylist = api + '/playlist/create' // 添加歌单
export const deletePlaylist = api + '/playlist/delete' // 删除歌单
// ================发现页面的电台页面
export const djBanner = api + '/dj/banner' // 电台页面的轮播图
export const radioRecommendations = api + '/dj/today/perfered' // 电台推荐数据
export const boutiqueRecommendations = api + '/dj/paygift' // 电台精品推荐
export const djClassification = api + '/dj/catelist' // 电台分类
export const djClassificationInfo = api + '/dj/recommend/type' // 电台分类推荐
export const djProgram = api + '/dj/program' // 电台节目
export const djDetail = api + '/dj/detail' // 电台详情
export const djPayGift = api + '/dj/paygift' // 电台付费精选
export const djSub = api + '/dj/sub' // 订阅/取消订阅电台
export const djToplist = api + '/dj/program/toplist' // 获取最热节目
export const djHotToplist = api + '/dj/toplist' // 获取电台榜
export const djProgramTopHours = api + '/dj/program/toplist/hours' // 获取节目榜24小时榜
export const djToplistHours = api + '/dj/toplist/hours' // 获取主播榜24小时榜
export const djToplistNewComers = api + '/dj/toplist/newcomer' // 获取主播榜新人榜
export const djToplistPopulars = api + '/dj/toplist/popular' // 获取主播榜最热主播
export const djToplistPays = api + '/dj/toplist/pay' // 获取电台磅付费精品榜
// ================评论页面相关
export const commentPlaylist = api + '/comment/playlist' // 获取歌单的评论
export const commentAlbum = api + '/comment/album' // 获取专辑的评论
export const commentDj = api + '/comment/dj' // 获取电台节目评论
export const commentLike = api + '/comment/like' // 给评论点赞
export const pushOrDeleteCom = api + '/comment' // 发送删除评论
export const resourceLike = api + '/resource/like' // 资源点赞


================================================
FILE: musicPlayer/src/api/index.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-08-19 13:47:19
 * @Update: 2019-12-08 12:02:19
 * @Update log: 更新日志
 */
import axios from 'axios'
import {
  bannerSwiper,
  recSongList,
  highquality,
  recSongs,
  topList,
  dateRecSongList,
  newDish,
  phoneLogin,
  albumDetail,
  sendVerify,
  verify,
  phoneRegistered,
  loginStatus,
  userRecord,
  userInfo,
  playlist,
  userDj,
  hotSearchList,
  search,
  defaultSearch,
  suggestSearch,
  songUrl,
  checkSong,
  songLyric,
  idxList,
  addOrDeletePlaylist,
  addPlaylist,
  deletePlaylist,
  heartMode,
  favoriteAlbums,
  favoriteArtists,
  favoriteVideos,
  djSublist,
  newSongs,
  getDishInfo,
  personalFm,
  singerClass,
  logout,
  radioRecommendations,
  boutiqueRecommendations,
  djClassification,
  djClassificationInfo,
  djProgram,
  djDetail,
  djPayGift,
  djSub,
  djBanner,
  djToplist,
  djHotToplist,
  likeMusicList,
  likeMusic,
  userDetail,
  signIn,
  friend,
  getVideoTag,
  getVideoGroup,
  commentPlaylist,
  commentLike,
  commentAlbum,
  userEvent,
  pushOrDeleteCom,
  getVideoUrl,
  register,
  commentDj,
  getVideoDetail,
  getVideoRelated,
  getVideoComments,
  resourceLike,
  djProgramTopHours,
  djToplistHours,
  djToplistNewComers,
  djToplistPopulars,
  djToplistPays
} from './config'
// 请求超时时间
axios.defaults.timeout = 30000

// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'

/**
 * 以后可以利用 ES6 的解构赋值进行重构
 * function personInfo({ name, age, address, gender }) {
      console.log(name, age, address, gender)
    }
    personInfo({ gender: 'man', address: 'changsha', name: 'william', age: 18 })

    function saveInfo({
      name = 'william',
      age = 18,
      address = 'changsha',
      gender = 'man'
    } = {}) {
      console.log(name, age, address, gender)
    }
    saveInfo()
 */

export default {
  /**
   * 请求发现页面首页轮播图
   */
  bannerSwiperFn () {
    return axios.get(bannerSwiper)
  },
  /**
   * 调用此接口 , 可获得每日推荐歌曲 ( 需要登录 )
   */
  recSongsFn () {
    return axios.get(recSongs)
  },
  /**
   * 调用此接口 , 传入歌单 id, 可 以获取对应歌单内的所有的音乐
   * @param {*} id 歌单 id
   * @param {*} s 歌单最近的 s 个收藏者,默认5个
   */
  albumDetailFn (id, s = 5) {
    return axios.get(albumDetail, {
      params: {
        id,
        s
      }
    })
  },
  /**
   * 调用此接口,可获取所有榜单内容摘要
   */
  topListFn () {
    return axios.get(topList)
  },
  /**
   * 请求 可获取推荐歌单
   * ?limit=10&order=hot
   * @param {*} limit 取出数量,默认是30
   * @param {*} order 分别对应最新和最热,可选值为 'new' 和 'hot'
   * @param {*} cat tag, 比如 " 华语 "、" 古风 " 、" 欧美 "、" 流行 ", 默认为 "全部",
   *  :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认 为 0
   */
  recSongListFn (limit = 30, order = 'hot', cat) {
    return axios.get(recSongList, {
      params: {
        limit,
        order,
        cat
      }
    })
  },
  /**
   * 获取精品歌单
   * @param {*} limit 取出歌单数量 , 默认为 30
   * @param {*} before 分页参数,取上一页最后一个歌单的 updateTime 获取下一页数据
   * @param {*} cat cat: tag
   */
  highqualityFn (limit = 30, before, cat) {
    return axios.get(highquality, {
      params: {
        limit,
        before,
        cat
      }
    })
  },
  /**
   * 调用此接口 , 传入数字 idx, 可获取不同排行榜
   * @param {*} idx 排行榜 idx
   */
  idxListFn (idx) {
    return axios.get(idxList, {
      params: {
        idx
      }
    })
  },
  /**
   * 可获得每日推荐歌单 ( 需要登录 )
   */
  dateRecSongListFn () {
    return axios.get(dateRecSongList)
  },
  /**
   * 调用此接口 , 可获取新碟上架列表
   * @param {*} limit 取出数量 , 默认为 50
   * @param {*} offset 偏移数量 , 用于分页
   *  如 :( 页数 -1)*50, 其中 50 为 limit 的值 , 默认 为 0
   */
  newDishFn (limit = 10, offset) {
    return axios.get(newDish, {
      params: {
        limit,
        offset
      }
    })
  },
  /**
   * 调用此接口 , 传入专辑 id, 可获得专辑内容
   * @param {*} id 专辑id
   */
  getDishInfoFn (id) {
    return axios.get(getDishInfo, {
      params: {
        id
      }
    })
  },
  /**
   * 检测手机号码是否已注册
   * @param {*} phone 手机号
   */
  phoneRegisteredFn (phone) {
    return axios.get(phoneRegistered, {
      params: {
        phone
      }
    })
  },
  /**
   * 调用此接口 ,传入手机号码, 可发送验证码
   * @param {number} phone 手机号
   */
  sendVerifyFn (phone) {
    return axios.get(sendVerify, {
      params: {
        phone
      }
    })
  },
  /**
   * 验证验证码
   * 调用此接口 ,传入手机号码和验证码, 可校验验证码是否正确
   * @param {*} phone 手机号
   * @param {*} captcha 验证码
   */
  verifyFn (phone, captcha) {
    return axios.get(verify, {
      params: {
        phone,
        captcha
      }
    })
  },
  /**
   * 用户通过手机登录
   * @param {number} phone 手机号
   * @param {String} password 密码
   */
  phoneLoginFn (phone, password) {
    return axios.get(phoneLogin, {
      params: {
        phone: phone || '',
        password: password || ''
      }
    })
  },
  /**
   * 获取当前登录状态
   */
  loginStatusFn () {
    return axios.get(loginStatus)
  },
  /**
   * 获取用户播放记录
   * 登陆后调用此接口 , 传入用户 id, 可获取用户播放记录
   * @param {*} uid 用户id
   * @param {*} type type=1 时只返回 weekData, type=0 时返回 allData
   */
  userRecordFn (uid, type = 1) {
    return axios.get(userRecord, {
      params: {
        uid,
        type
      }
    })
  },
  /**
   * 获取用户信息 , 歌单,收藏,mv, dj 数量
   * 登陆后调用此接口 , 可以获取用户信息
   * artistCount: 2 我的收藏中的歌手
   * code: 200
   * createDjRadioCount: 0
   * createdPlaylistCount: 2 创建的歌单数
   * djRadioCount: 1 我的电台
   * mvCount: 0
   * newProgramCount: 0
   * programCount: 0
   * subPlaylistCount: 3 收藏的歌单数
   * @param {*} timestamp 时间戳,使得每次请求的URL不同,除掉了默认的2分钟缓存
   */
  userInfoFn (timestamp) {
    return axios.get(userInfo, {
      params: {
        timestamp
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入用户 id, 可以获取用户详情
   * @param {*} uid 用户id
   */
  userDetailFn (uid) {
    return axios.get(userDetail, {
      params: {
        uid
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入用户 id, 可以获取用户动态
   * @param {*} uid 用户 id
   * @param {*} limit  返回数量 , 默认为 30
   * @param {*} lasttime 返回数据的 lasttime ,默认-1,传入上一次返回结果的 lasttime,将会返回下一页的数据
   */
  userEventFn (uid, limit = 20, lasttime) {
    return axios.get(userEvent, {
      params: {
        uid,
        limit,
        lasttime
      }
    })
  },
  /**
   * 调用此接口 , 传入签到类型
   * @param {*} type 签到类型 , 默认 0, 其中 0 为安卓端签到 ,1 为 web/PC 签到
   * 其中安卓端签到可获得 3 点经验 , web/PC 端签到可获得 2 点经验
   */
  signInFn (type) {
    return axios.get(signIn, {
      params: {
        type
      }
    })
  },
  /**
   * 调用此接口 ,传入手机号码和验证码,密码,昵称, 可注册网易云音乐账号(同时可修改密码)
   * @param {*} captcha 验证码
   * @param {*} phone 手机号码
   * @param {*} password 密码
   * @param {*} nickname 昵称
   */
  registerFn (captcha, phone, password, nickname) {
    return axios.get(register, {
      params: {
        captcha,
        phone,
        password,
        nickname
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入用户 id, 可以获取用户歌单
   * @param {*} uid 用户id
   * @param {*} timestamp 时间戳,使得每次请求的URL不同,除掉了默认的2分钟缓存
   */
  playlistFn (uid, timestamp) {
    return axios.get(playlist, {
      params: {
        uid,
        timestamp
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入用户 id, 可以获取用户电台
   * @param {*} uid 用户 id
   */
  userDjFn (uid) {
    return axios.get(userDj, {
      params: {
        uid
      }
    })
  },
  /**
   * 调用此接口,可获取热门搜索列表
   */
  hotSearchListFn () {
    return axios.get(hotSearchList)
  },
  /**
   * 调用此接口 , 传入搜索关键词可以搜索
   * 该音乐 / 专辑 / 歌手 / 歌单 / 用户 , 关键词可以多个 , 以空格隔开
   * @param {*} keywords 关键词
   * @param {*} limit 返回数量 , 默认为 30
   * @param {*} offset 偏移数量,用于分页 默认为 0
   * @param {*} type 搜索类型 默认为 1 即单曲 这里设置默认返回综合
   * 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单
   * 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频, 1018:综合
   */
  searchFn (keywords, limit = 30, offset = 0, type = 1018) {
    return axios.get(search, {
      params: {
        keywords,
        limit,
        offset,
        type
      }
    })
  },
  /**
   * 调用此接口 , 可获取默认搜索关键词
   */
  defaultSearchFn () {
    return axios.get(defaultSearch)
  },
  /**
   * 调用此接口
   * 传入搜索关键词可获得搜索建议 ,
   * 搜索结果同时包含单曲 , 歌手 , 歌单 ,mv 信息
   * @param {*} keywords 关键词
   * @param {*} type 默认返回移动端数据
   */
  suggestSearchFn (keywords, type = 'mobile') {
    return axios.get(suggestSearch, {
      params: {
        keywords,
        type
      }
    })
  },
  /**
   *  使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url
   * 调用此接口 , 传入的音乐 id( 可多个 , 用逗号隔开 )
   * 可以获取对应的音乐的 url( 不需要登录 )
   * @param {*} id 音乐 id
   * @param {*} br 码率,默认设置了 999000 即最大码率,如果要 320k 则可设置为 320000,其他类推
   */
  songUrlFn (id, br) {
    return axios.get(songUrl, {
      params: {
        id,
        br
      }
    })
  },
  /**
   * 传入歌曲 id, 可获取音乐是否可用
   * @param {*} id 歌曲 id
   * @param {*} br 码率,默认设置了 999000 即最大码率,如果要 320k 则可设置为 320000,其他类推
   */
  checkSongFn (id, br) {
    return axios.get(checkSong, {
      params: {
        id,
        br
      }
    })
  },
  /**
   * 传入音乐 id 可获得对应音乐的歌词 ( 不需要登录 )
   * @param {*} id 歌曲id
   */
  songLyricFn (id) {
    return axios.get(songLyric, {
      params: {
        id
      }
    })
  },
  /**
   * 传入类型和歌单 id 可收藏歌单或者取消收藏歌单
   * @param {*} t 1:收藏 2:取消收藏
   * @param {*} id  歌单id
   */
  addOrDeletePlaylistFn (t, id) {
    return axios.get(addOrDeletePlaylist, {
      params: {
        t,
        id
      }
    })
  },
  /**
   * 调用此接口 , 传入歌单名字可新建歌单
   * @param {*} name 歌单名
   * @param {*} privacy 是否为隐私歌单 传'10'设置为隐私歌单
   */
  addPlaylistFn (name, privacy) {
    return axios.get(addPlaylist, {
      params: {
        name,
        privacy
      }
    })
  },
  /**
   * 调用此接口 , 传入歌单名字可新建歌单
   * @param {*} id 歌单id
   */
  deletePlaylistFn (id) {
    return axios.get(deletePlaylist, {
      params: {
        id
      }
    })
  },
  /**
   * 登录后调用此接口 , 可获取心动模式/智能播放列表
   * @param {*} id  歌曲 id
   * @param {*} pid 歌单id
   * @param {*} sid 要开始播放的歌曲id
   */
  heartModeFn (id, pid, sid) {
    return axios.get(heartMode, {
      params: {
        id,
        pid,
        sid
      }
    })
  },
  /**
   * 调用此接口 , 传入音乐 id, 可喜欢该音乐
   * @param {*} id 音乐id
   * @param {*} like 布尔值 , 默认为 true 即喜欢 , 若传 false, 则取消喜欢
   */
  likeMusicFn (id, like) {
    return axios.get(likeMusic, {
      params: {
        id,
        like
      }
    })
  },
  /**
   * 调用此接口 , 传入用户 id, 可获取已喜欢音乐id列表(id数组)
   * @param {*} uid 用户id
   */
  likeMusicListFn (uid) {
    const timestamp = +new Date()
    return axios.get(likeMusicList, {
      params: {
        uid,
        timestamp
      }
    })
  },
  /**
   * 调用此接口 , 可获得已收藏专辑列表
   * @param {*} limit 取出数量 , 默认为 25
   * @param {*} offset 偏移数量 , 用于分页 , 如 :( 页数 -1)*25, 其中 25 为 limit 的值 , 默认 为 0
   */
  favoriteAlbumsFn (limit, offset) {
    return axios.get(favoriteAlbums, {
      params: {
        limit,
        offset
      }
    })
  },
  /**
   * 调用此接口,可获取收藏的歌手列表
   */
  favoriteArtistsFn () {
    return axios.get(favoriteArtists)
  },
  /**
   * 调用此接口,可收藏视频
   */
  favoriteVideosFn () {
    return axios.get(favoriteVideos)
  },
  /**
   * 获取视频详情
   * @param {*} id 视频id
   */
  getVideoDetailFn (id) {
    return axios.get(getVideoDetail, {
      params: {
        id
      }
    })
  },
  /**
   * 获取相关视频
   * @param {*} id 视频id
   */
  getVideoRelatedFn (id) {
    return axios.get(getVideoRelated, {
      params: {
        id
      }
    })
  },
  /**
   * 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该 视频 的所有评论 ( 不需要登录 )
   * @param {*} id 视频的 id
   * @param {*} limit 取出评论数量 , 默认为 20
   * @param {*} offset 偏移数量 , 用于分页 , 如 :( 评论页数 -1)*20, 其中 20 为 limit 的值
   * @param {*} before 分页参数,取上一页最后一项的 time 获取下一页数据(获取超过5000条评论的时候需要用到)
   */
  getVideoCommentsFn (id, limit = 20, offset, before) {
    const timestamp = +new Date()
    return axios.get(getVideoComments, {
      params: {
        id,
        limit,
        offset,
        before,
        timestamp
      }
    })
  },
  /**
   * 登陆后调用此接口 , 可获取订阅的电台列表
   */
  djSublistFn (timestamp) {
    return axios.get(djSublist, {
      params: {
        timestamp
      }
    })
  },
  /**
   * 调用此接口 , 可获取新歌速递
   * @param {*} type 地区类型 id,对应以下:
   * 全部:0 华语:7 欧美:96 日本:8 韩国:16
   */
  newSongsFn (type = 0) {
    return axios.get(newSongs, {
      params: {
        type
      }
    })
  },
  /**
   * 获取私人 FM
   * 需要登录
   */
  personalFmFn () {
    return axios.get(personalFm)
  },
  /**
   * 调用此接口,可获取歌手分类列表
   * @param {*} cat 歌手类型, 必选
   * 入驻歌手 5001
   * 华语男歌手 1001 华语女歌手 1002 华语组合/乐队 1003
   * 欧美男歌手 2001 欧美女歌手 2002 欧美组合/乐队 2003
   * 日本男歌手 6001 日本女歌手 6002 日本组合/乐队 6003
   * 韩国男歌手 7001 韩国女歌手 7002 韩国组合/乐队 7003
   * 其他男歌手 4001 其他女歌手 4002 其他组合/乐队 4003
   * @param {*} limit  返回数量 , 默认为 30
   * @param {*} offset 偏移数量,用于分页,默认为 0
   * @param {*} initial 按首字母索引查找参数
   * /artist/list?cat=1001&initial=b
   * 返回内容将以 name 字段开头为 b 或者拼音开头为 b 为顺序排列
   */
  singerClassFn (cat = 1001, limit = 30, offset = 0, initial) {
    return axios.get(singerClass, {
      params: {
        cat,
        limit,
        offset,
        initial
      }
    })
  },
  /**
   * 退出账号
   */
  logoutFn () {
    return axios.get(logout)
  },
  /**
   * 调用此接口,可获取电台banner
   */
  djBannerFn () {
    return axios.get(djBanner)
  },
  /**
   * 获取电台页面的电台推荐数据
   */
  radioRecomFn () {
    return axios.get(radioRecommendations)
  },
  /**
   * 可以获取付费精选的电台列表
   * @param {*} limit 返回数量 , 默认为 30
   * @param {*} offset  偏移数量, 默认为 0
   */
  boutiqueRecomFn (limit = 3, offset = 0) {
    return axios.get(boutiqueRecommendations, {
      params: {
        limit,
        offset
      }
    })
  },
  /**
   * 登陆后调用此接口 , 可获得电台类型
   */
  djClassificationFn () {
    return axios.get(djClassification)
  },
  /**
   * 登陆后调用此接口 , 可获得推荐电台
   * @param {*} type 电台类型
   * 可通过/dj/catelist获取 , 对应关系为 id 对应 此接口的 type, name 对应类型意义
   */
  djClassificationInfoFn (type) {
    return axios.get(djClassificationInfo, {
      params: {
        type
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入rid, 可查看对应电台的电台节目以及对应的 id
   * 需要 注意的是这个接口返回的 mp3Url 已经无效 , 都为 null
   * 但是通过调用 /song/url 这 个接口 , 传入节目 id 仍然能获取到节目音频 ,
   * 如 /song/url?id=478446370 获取代 码时间的一个节目的音频
   * @param {*} rid 电台 的 id
   * @param {*} limit 返回数量 , 默认为 30
   * @param {*} offset 偏移数量,用于分页
   * @param {*} asc 排序方式,默认为 false (新 => 老 ) 设置 true 可改为 老 => 新
   */
  djProgramFn (rid, limit = 30, offset = 0, asc) {
    return axios.get(djProgram, {
      params: {
        rid,
        limit,
        offset,
        asc
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入rid, 可获得对应电台的详情介绍
   * @param {*} rid 电台 的 id
   */
  djDetailFn (rid) {
    return axios.get(djDetail, {
      params: {
        rid
      }
    })
  },
  /**
   * 可以获取付费精选的电台列表 , 传入 limit 和 offset 可以进行分页
   * @param {*} limit 返回数量 , 默认为 30
   * @param {*} offset 偏移数量,用于分页
   */
  djPayGiftFn (limit = 30, offset = 0) {
    return axios.get(djPayGift, {
      params: {
        limit,
        offset
      }
    })
  },
  /**
   * 登陆后调用此接口 , 传入rid, 可订阅 dj
   * @param {*} rid 电台 的 id
   * @param {*} t t=1 对应关注 t=0 对应取消关注
   */
  djSubFn (rid, t) {
    return axios.get(djSub, {
      params: {
        rid,
        t
      }
    })
  },
  /**
   * 登陆后调用此接口 , 可获得电台节目榜
   * @param {*} limit  返回数量 , 默认为 100
   * @param {*} offset 偏移数量,用于分页
   */
  djToplistFn (limit = 100, offset = 0) {
    return axios.get(djToplist, {
      params: {
        limit,
        offset
      }
    })
  },
  /**
   * 登陆后调用此接口 , 可获得新晋电台榜/热门电台榜
   * @param {*} limit 返回数量 , 默认为 100
   * @param {*} offset 偏移数量,用于分页
   * @param {*} type 榜单类型, new 为新晋电台榜, hot为热门电台榜
   */
  djHotToplistFn (limit = 100, offset = 0, type = 'hot') {
    return axios.get(djHotToplist, {
      params: {
        limit,
        offset,
        type
      }
    })
  },
  /**
   * 调用此接口,可获取24小时节目榜
   * @param {*} limit 返回数量 , 默认为 100 (不支持 offset)
   */
  djProgramTopHoursFn (limit) {
    return axios.get(djProgramTopHours, {
      params: {
        limit
      }
    })
  },
  /**
   * 调用此接口,可获取24小时主播榜
   * @param {*} limit 返回数量 , 默认为 100 (不支持 offset)
   */
  djToplistHoursFn (limit) {
    return axios.get(djToplistHours, {
      params: {
        limit
      }
    })
  },
  /**
   * 说明 : 调用此接口,可获取主播新人榜
   * @param {*} limit 返回数量 , 默认为 100 (不支持 offset)
   */
  djToplistNewComersFn (limit) {
    return axios.get(djToplistNewComers, {
      params: {
        limit
      }
    })
  },
  /**
   * 调用此接口,可获取最热主播榜
   * @param {*} limit 返回数量 , 默认为 100 (不支持 offset)
   */
  djToplistPopularsFn (limit) {
    return axios.get(djToplistPopulars, {
      params: {
        limit
      }
    })
  },
  /**
   * 调用此接口,可获取付费精品电台
   * @param {*} limit 返回数量 , 默认为 100 (不支持 offset)
   */
  djToplistPaysFn (limit) {
    return axios.get(djToplistPays, {
      params: {
        limit
      }
    })
  },
  /**
   * 调用此接口 , 可获取各种动态
   * 对应网页版网易云,朋友界面里的各种动态消息
   * 如分享的视频,音乐,照片等!
   * @param {*} pagesize  每页数据,默认20
   * @param {*} lasttime  返回数据的 lasttime ,默认-1
   * 传入上一次返回结果的 lasttime,将会返回下一页的数据
   */
  friendFn (pagesize = 20, lasttime = -1) {
    return axios.get(friend, {
      params: {
        pagesize,
        lasttime
      }
    })
  },
  /**
   * 调用此接口 , 可获取视频标签列表
   */
  getVideoTagFn () {
    return axios.get(getVideoTag)
  },
  /**
   * 调用此接口 , 传入id,可获取到相关的视频
   * @param {*} id videoGroup 的 id
   */
  getVideoGroupFn (id) {
    return axios.get(getVideoGroup, {
      params: {
        id
      }
    })
  },
  /**
   * 调用此接口 , 传入视频 id,可获取视频播放地址
   * @param {*} id 视频 id
   */
  getVideoUrlFn (id) {
    return axios.get(getVideoUrl, {
      params: {
        id
      }
    })
  },
  /**
   * 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该歌单的所有评论 ( 不需要 登录 )
   * @param {*} id 歌单id
   * @param {*} limit 取出评论数量 , 默认为 20
   * @param {*} offset 偏移数量 , 用于分页
   * @param {*} before 分页参数,取上一页最后一项的 time
   * 获取下一页数据(获取超过5000条评论的时候需要用到)
   */
  commentPlaylistFn (id, limit = 20, offset, before) {
    const timestamp = +new Date()
    return axios.get(commentPlaylist, {
      params: {
        id,
        limit,
        offset,
        before,
        timestamp
      }
    })
  },
  /**
   * 调用此接口 , 传入音乐 id 和 limit 参数 , 可获得该专辑的所有评论 ( 不需要 登录 )
   * @param {*} id 专辑id
   * @param {*} limit 取出评论数量 , 默认为 20
   * @param {*} offset 偏移数量 , 用于分页
   * @param {*} before 分页参数,取上一页最后一项的 time
   * 获取下一页数据(获取超过5000条评论的时候需要用到)
   */
  commentAlbumFn (id, limit = 20, offset, before) {
    const timestamp = +new Date()
    return axios.get(commentAlbum, {
      params: {
        id,
        limit,
        offset,
        before,
        timestamp
      }
    })
  },
  commentDjFn (id, limit = 20, offset, before) {
    const timestamp = +new Date()
    return axios.get(commentDj, {
      params: {
        id,
        limit,
        offset,
        before,
        timestamp
      }
    })
  },
  /**
   * 调用此接口 , 传入 type,
   * 资源 id, 和评论 id cid
   * 是否点赞参数 t 即可给对 应评论点赞 ( 需要登录 )
   * @param {*} id 资源 id, 如歌曲 id,mv id
   * @param {*} cid 评论 id
   * @param {*} t 是否点赞 ,1 为点赞 ,0 为取消点赞
   * @param {*} type  数字 , 资源类型 , 对应歌曲 , mv, 专辑 , 歌单 , 电台, 视频对应以下类型
   * 0: 歌曲 1: mv 2: 歌单 3: 专辑 4: 电台 5: 视频  6: 动态
   */
  commentLikeFn (id, cid, t, type) {
    return axios.get(commentLike, {
      params: {
        id,
        cid,
        t,
        type
      }
    })
  },
  /**
   * 调用此接口,可发送评论
   * @param {*} t 1: 发送评论
   * @param {*} type 数字,资源类型,对应歌曲,mv,专辑,歌单,电台,视频对应以下类型
   * 0: 歌曲 1: mv 2: 歌单 3: 专辑 4: 电台 5: 视频  6: 动态
   * @param {*} id 对应资源 id
   * @param {*} content 要发送的内容
   * @param {*} commentId 回复的评论id (回复评论时必填)
   * @param {*} threadId 如给动态发送评论,则不需要传 id,需要传动态的 threadId
   */
  pushComFn (type, id, content, commentId, threadId) {
    const t = 1
    const timestamp = +new Date()
    return axios.post(pushOrDeleteCom, {
      t,
      type,
      id,
      content,
      commentId,
      threadId,
      timestamp
    })
  },
  /**
   * 调用此接口,可删除评论
   * @param {*} t 0: 删除评论
   * @param {*} type 数字,资源类型,对应歌曲,mv,专辑,歌单,电台,视频对应以下类型
   * 0: 歌曲 1: mv 2: 歌单 3: 专辑 4: 电台 5: 视频  6: 动态
   * @param {*} id 对应资源 id
   * @param {*} threadId 如给动态删除评论,则不需要传 id,需要传动态的 `threadId`
   * @param {*} commentId 回复的评论id (回复评论时必填),删除评论时传入评论的id
   */
  delComFn (type, id, commentId, threadId) {
    const t = 0
    return axios.post(pushOrDeleteCom, {
      t,
      type,
      id,
      threadId,
      commentId
    })
  },
  /**
   * 调用此接口 , 可对 MV,电台,视频点赞
   * @param {*} type 资源类型,对应以下类型 1: mv 4: 电台 5: 视频 6: 动态
   * @param {*} id 资源 id
   */
  resourceLikeInFn (type, id) {
    const t = 1
    return axios.get(resourceLike, {
      params: {
        t,
        type,
        id
      }
    })
  },
  /**
   * 调用此接口 , 可对 MV,电台,视频取消点赞
   * @param {*} type 资源类型,对应以下类型 1: mv 4: 电台 5: 视频 6: 动态
   * @param {*} id 资源 id
   */
  resourceLikeOutFn (type, id) {
    const t = 0
    return axios.get(resourceLike, {
      params: {
        t,
        type,
        id
      }
    })
  }
}


================================================
FILE: musicPlayer/src/assets/Bus.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-08-29 10:48:10
 * @Update: 2019-08-29 10:50:16
 * @Update log: 更新日志
 */
import Vue from 'vue'
export default new Vue()


================================================
FILE: musicPlayer/src/assets/Mixins.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-09-21 15:14:40
 * @Update: 2019-11-24 12:59:18
 * @Update log: 更新日志
 */

import {
  mapGetters
} from 'vuex'
import api from 'api'
import isInSport from 'utils/scrollStopVideo'

/**
 * 这里包含对于不同模式下的icon展示
 * 对于更改mode
 * 对播放列表的展示隐藏
 */
export const audio = {
  props: {
    mode: {
      type: Number
    }
  },
  computed: {
    modeClass: function () {
      switch (this.mode) {
        case 0: // 列表循环
          return 'audioxunhuan'
        case 1: // 单曲循环
          return 'audiosingle-loop'
        case 2: // 随机播放
          return 'audiosuiji'
      }
    }
  },
  methods: {
    changeMode () {
      this.$emit('changeMode')
    },
    showAudioList () {
      this.$emit('showAudioList')
    }
  }
}

let timer = null

export const videoPage = {
  data () {
    return {
      data: [],
      load: true,
      isLogin: +localStorage.getItem('loginState') || 0
    }
  },
  created () {
    this._getVideoDetail(this.$route.params.id)
  },
  methods: {
    _getVideoDetail (id) {
      api.getVideoGroupFn(id)
        .then(res => {
          const {
            data
          } = res
          if (data.code === 200) {
            this.data = data.datas
            this.load = false
          }
        })
        .catch(err => {
          if (err) {
            this.load = false
          }
        })
    },
    hideVideo () {
      const self = this
      if (timer) {
        clearTimeout(timer)
        timer = null
      }
      timer = setTimeout(() => {
        this.stopVideo(self)
      }, 300)
    },
    stopVideo (self) {
      // 父容器
      const wra = self.$el
      // video集合
      const videos = [...wra.querySelectorAll('.video-item')]
      // 获取到当前正在播放的video
      const ele = videos[this.index]
      // 查看当前播放的video是否已经出去!!!
      if (!isInSport(ele, wra)) {
        // 出去的话调用方法,停止视频播放
        self.$refs.public.stopVideoTag()
      }
    },
    getIndex (index) {
      this.index = index
    }
  }
}

/**
 * 当页面显示了播放组件,页面整体需要设置paddingBottom
 * 大小为迷你播放器的高度
 */

export const paddingBottom = {
  computed: {
    ...mapGetters({
      miniAudio: 'FULL_SCREEN'
    })
  },
  mounted () {
    console.log(this.miniAudio)
    if (!this.miniAudio) {
      this.setPagePb()
    }
  },
  watch: {
    /**
     * 监听当前是否播放
     */
    miniAudio: function (val) {
      if (!val) {
        this.setPagePb()
      }
    }
  },
  methods: {
    setPagePb () {
      this.$refs.wrapper.style.paddingBottom = '1.3rem'
    }
  }
}


================================================
FILE: musicPlayer/src/assets/styles/border.css
================================================
@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
    position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
    content: "\0020";
    overflow: hidden;
    position: absolute;
}
/* border
 * 因,边框是由伪元素区域遮盖在父级
 * 故,子级若有交互,需要对子级设置
 * 定位 及 z轴
 */
.border::before {
    box-sizing: border-box;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    border: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
    left: 0;
    width: 100%;
    height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
    top: 0;
    width: 1px;
    height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
    border-top: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
    border-right: 1px solid #eaeaea;
    transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
    border-bottom: 1px solid #eaeaea;
    transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
    border-left: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
    top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
    right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
    bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
    left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
    /* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
    .border::before {
        width: 200%;
        height: 200%;
        transform: scale(.5);
    }
    .border-top::before,
    .border-bottom::before,
    .border-topbottom::before,
    .border-topbottom::after,
    .border-topleft::before,
    .border-rightbottom::after,
    .border-topright::before,
    .border-bottomleft::before {
        transform: scaleY(.5);
    }
    .border-right::before,
    .border-left::before,
    .border-rightleft::before,
    .border-rightleft::after,
    .border-topleft::after,
    .border-rightbottom::before,
    .border-topright::after,
    .border-bottomleft::after {
        transform: scaleX(.5);
    }
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
    .border::before {
        width: 300%;
        height: 300%;
        transform: scale(.33333);
    }
    .border-top::before,
    .border-bottom::before,
    .border-topbottom::before,
    .border-topbottom::after,
    .border-topleft::before,
    .border-rightbottom::after,
    .border-topright::before,
    .border-bottomleft::before {
        transform: scaleY(.33333);
    }
    .border-right::before,
    .border-left::before,
    .border-rightleft::before,
    .border-rightleft::after,
    .border-topleft::after,
    .border-rightbottom::before,
    .border-topright::after,
    .border-bottomleft::after {
        transform: scaleX(.33333);
    }
}


================================================
FILE: musicPlayer/src/assets/styles/global.less
================================================
// 主题色
@bgcolor: #dd001b;
// 图片圆角
@imgBorderRadius: 0.11rem;
// 图标宽度
@iconWidth: 1rem;
// 图标下方文本大小
@iconText: 0.24rem;

// 定义盒子模型 两边对齐布局
.flex-between {
  display: flex;
  justify-content: space-between;
}

.titleFixed {
  position: sticky;
  width: 100vw;
  top: 0;
  background-color: #fff;
  z-index: 99;
}

// 定义盒子模型  分散布局
.flex-around {
  display: flex;
  justify-content: space-around;
}

.flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

// 两行文本溢出打点 ...
.twoLinesEllipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  overflow: hidden;
  /*! autoprefixer: off */
  -webkit-box-orient: vertical;
}

// 单行文字打点
.ellipsis {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

// 小标签
.smallTag {
  box-sizing: border-box;
  padding: 0.1rem 0.1rem 0.1rem 0.13rem;
  display: flex;
  align-items: center;
  // justify-content: center;
  font-size: 0.2rem;
  border-radius: 1rem;
  letter-spacing: 0.05rem;
  text-align: center;
  border: 1px solid #ccc;
}

.tag-group {
  box-sizing: border-box;
  padding: 0.1rem 0.2rem 0.1rem 0.2rem;
  font-size: 0.2rem;
  border-radius: 0.2rem;
  background-color: #eee;
  margin-right: 0.1rem;
}

// 遮罩层
.mask {
  position: fixed;
  z-index: 10;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.7);
}

// 灰色的数字样式
.num {
  font-size: 0.24rem;
  color: #999;
}

// 导航栏的访问样式
.ac {
  color: #000;
  font-size: 0.28rem;
}

// a标签覆盖整个区域
.cover {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  width: 90%;
  height: 100%;
}

// 页面灰色分割线样式
.split {
  width: 100%;
  height: 0.13rem;
  background-color: #eee;
  margin: 0.3rem 0;
}

// 页面有的左右padding
.pd13 {
  box-sizing: border-box;
  padding: 0 0.13rem;
}

// 页面有的左右padding
.pd23 {
  box-sizing: border-box;
  padding: 0 0.23rem;
}

.pb1 {
  box-sizing: border-box;
  padding-bottom: 1rem;
}

// 点击有波纹效果
.ripple {
  position: relative;
  overflow: hidden;
  user-select: none;

  &:after {
    position: absolute;
    content: '';
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    //设置径向渐变
    background-image: radial-gradient(circle, #666 10%, transparent 10.01%);
    background-repeat: no-repeat;
    background-position: 50%;
    transform: scale(10, 10);
    opacity: 0;
    transition: transform .5s, opacity .6s;
  }

  &:active:after {
    transform: scale(0, 0);
    opacity: .3;
    //设置初始状态
    transition: 0s;
  }
}


================================================
FILE: musicPlayer/src/assets/styles/reset.css
================================================
@charset "utf-8";

html {
  background-color: #fff;
  color: #000
}

body,
ul,
ol,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
figure,
form,
fieldset,
legend,
input,
textarea,
button,
p,
blockquote,
th,
td,
pre,
xmp {
  margin: 0;
  padding: 0
}

body,
input,
textarea,
button,
select,
pre,
xmp,
tt,
code,
kbd,
samp {
  line-height: 1.5;
  font-family: tahoma, arial, "Hiragino Sans GB", simsun, sans-serif
}

h1,
h2,
h3,
h4,
h5,
h6,
small,
big,
input,
textarea,
button,
select {
  font-size: 100%
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: tahoma, arial, "Hiragino Sans GB", "微软雅黑", simsun, sans-serif
}

h1,
h2,
h3,
h4,
h5,
h6,
b,
strong {
  font-weight: normal
}

address,
cite,
dfn,
em,
i,
optgroup,
var {
  font-style: normal
}

table {
  border-collapse: collapse;
  border-spacing: 0;
  text-align: left
}

caption,
th {
  text-align: inherit
}

ul,
ol,
menu {
  list-style: none
}

fieldset,
img {
  border: 0
}

img,
object,
input,
textarea,
button,
select {
  vertical-align: middle
}

article,
aside,
footer,
header,
section,
nav,
figure,
figcaption,
hgroup,
details,
menu {
  display: block
}

audio,
canvas,
video {
  display: inline-block;
  *display: inline;
  *zoom: 1
}

blockquote:before,
blockquote:after,
q:before,
q:after {
  content: "\0020"
}

textarea {
  overflow: auto;
  resize: vertical
}

input,
textarea,
button,
select,
a {
  outline: 0 none;
  border: none;
}

button::-moz-focus-inner,
input::-moz-focus-inner {
  padding: 0;
  border: 0
}

mark {
  background-color: transparent
}

a,
ins,
s,
u,
del {
  text-decoration: none
}

sup,
sub {
  vertical-align: baseline
}

html {
  height: 100%;
  font-size: 14vw;
  -webkit-tap-highlight-color: transparent;
}

body {
  font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;
  color: #333;
  font-size: .28em;
  line-height: 1;
  -webkit-text-size-adjust: none;
}

hr {
  height: .02rem;
  margin: .1rem 0;
  border: medium none;
  border-top: .02rem solid #cacaca;
}

a {
  color: #25a4bb;
  text-decoration: none;
}

#audio_pb1 {
  box-sizing: border-box ;
  padding-bottom: 1rem;
}

================================================
FILE: musicPlayer/src/assets/utils/cookie.js
================================================
/*
 * @Author: Lambda
 * @Begin: 2019-10-24 11:00:35
 * @Update: 2019-10-24 11:03:02
 * @Update log: 更新日志
 */
export const getCookie = _name => {
  let name = _name + '='
  let Cookies = document.cookie.split(';')
  for (let i = 0; i < Cookies.length; i++) {
    let Cookie = Cookies[i]
    while (Cookie.charAt(0) === '') {
      Cookie = Cookie.substring(1)
    }
    if (Cookie.indexOf(name) !== -1) {
      return Cookie.substring(name.length, Cookie.length)
    }
  }
  return ''
}

export const setCookie = (name, value, expires, path, domain, secure) => {
  let cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value)
  if (expires instanceof Date) {
    cookieText += '; expires=' + expires.toGMTString()
  }
  if (path) {
    cookieText += '; path=' + path
  }
  if (domain) {
    cookieText += '; domain=' + domain
  }
  if (secure) {
    cookieText += '; secure'
  }
  document.cookie = cookieText
}


================================================
FILE: musicPlayer/src/assets/utils/filters.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-10-14 14:08:38
 * @Update: 2019-12-10 13:40:19
 * @Update log: 更新日志
 */
export const filterSetPlayCount = value => {
  if (!value) return ''
  if (value > 10000) {
    value = parseInt(value / 10000) + '万'
  } else if (value > 100000000) {
    value = (value / 100000000).toFixed(1) + '亿'
  }
  return value
}

/**
 * 将毫秒转换为 分钟:秒数 的形式
 */
export const filterSetTime = value => {
  if (!value) return ''
  let min = parseInt(value / (1000 * 60))
  if (min < 10) {
    min = '0' + min
  }
  let sec = parseInt(value % (1000 * 60) / 1000)
  if (sec < 10) {
    sec = '0' + sec
  }
  value = `${min}:${sec}`
  return value
}

/**
 * 将毫秒转换为 年.月.日
 */
export const filterSetYear = (value, splitY = '.', splitM = '.', splitD = '') => {
  const oDate = new Date(value)
  const oYear = oDate.getFullYear()
  const oMonth = oDate.getMonth() + 1
  const oDay = oDate.getDate()
  value = `${oYear}${splitY}${oMonth}${splitM}${oDay}${splitD}`
  return value
}

/**
 * 将毫秒设置为 月-日 格式
 */
export const filterSetMonth = (value, splitM = '-', splitD = '') => {
  const oDate = new Date(value)
  let oMonth = oDate.getMonth() + 1
  let oDay = oDate.getDate()
  if (oMonth < 10) {
    oMonth = '0' + oMonth
  }
  if (oDay < 10) {
    oDay = '0' + oDay
  }
  value = `${oMonth}${splitM}${oDay}${splitD}`
  return value
}

export const filterAge = time => {
  if (!time) {
    // 这里是因为默认值为0的话会返回1970
    // 当传入默认值退出
    return
  }
  let year = new Date(time).getFullYear()
  const nowYear = new Date().getFullYear()
  let age = ''
  // 存下从1950年到现在时间每5年一个间隔的数值
  let ages = []
  for (let i = 1950; i < nowYear; i += 5) {
    ages.push(i)
  }
  // 这里不能使用 forEach 因为forEach无法跳出循环!!!
  for (let i = 0; i < ages.length; i++) {
    const ele = ages[i]
    if (year <= ele + 5) {
      // 找到第一个满足条件的年份,拆分成数组,截取最后两位,组合返回
      age = ele.toString().split('').splice(-2).join('')
      break
    }
  }
  return age
}

export const filterJson = json => {
  return JSON.parse(json.toString())
}


================================================
FILE: musicPlayer/src/assets/utils/getAstro.js
================================================
/*
 * @Author: Lambda
 * @Begin: 2019-10-28 12:05:42
 * @Update: 2019-10-28 12:06:27
 * @Update log: 更新日志
 */
export const getAstro = (m, d) => {
  return '魔羯水瓶双鱼牡羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯'.substr(m * 2 - (d < '102223444433'.charAt(m - 1) - -19) * 2, 2)
}


================================================
FILE: musicPlayer/src/assets/utils/getPhone.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-08-26 13:35:48
 * @Update: 2019-08-26 13:47:13
 * @Update log: 更新日志
 */
export function getPhone () {
  // 创建一个正则表达式
  let reg = new RegExp(/\d*$/)
  // window.location.hash 返回从 “#” 开始的 url
  // 返回一个数组(未匹配到则返回 null)
  let phone = reg.exec(window.location.hash)[0]
  return phone
}


================================================
FILE: musicPlayer/src/assets/utils/getRandomArrayElements.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-10-11 13:17:17
 * @Update: 2019-10-11 13:18:07
 * @Update log: 更新日志
 */
/**
 * 随机取出数组中的几项
 */
export const getRandomArrayElements = (arr, count) => {
  // eslint-disable-next-line one-var
  let shuffled = arr.slice(0),
    // 克隆一个数组,为了不影响外边的数据
    i = arr.length,
    min = i - count,
    temp,
    index
  // 存下来数组信息  数组的长度
  // 通过改变数组项的位置 输出后边的几位
  while (--i > min) {
    // 随机生成出一个索引
    index = Math.floor((i + 1) * Math.random())
    // 将随机索引项暂存
    // 将数组后边的项与随机项调换
    temp = shuffled[index]
    shuffled[index] = shuffled[i]
    shuffled[i] = temp
  }
  return shuffled.slice(min)
}


================================================
FILE: musicPlayer/src/assets/utils/modalScroll.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-07-27 17:08:42
 * @Update: 2019-08-10 16:42:32
 * @Update log: 更新日志
 */
// 解决遮罩层滚动穿透问题,分别在遮罩层弹出后和关闭前调用
let _scrollTop
class ModalHelper {
  // popup 显示时调用
  static afterOpen () {
    _scrollTop = document.scrollingElement.scrollTop
    document.body.style.position = 'fixed'
    document.body.style.top = -_scrollTop + 'px'
    document.body.style.left = 0
    document.body.style.bottom = 0
    document.body.style.right = 0
  }

  // popup 关闭时调用
  static beforeClose () {
    document.body.style.position = ''
    document.body.style.top = ''
    document.body.style.left = ''
    document.body.style.right = ''
    document.body.style.bottom = ''
    document.scrollingElement.scrollTop = _scrollTop
  }
}

export default ModalHelper


================================================
FILE: musicPlayer/src/assets/utils/scrollStopVideo.js
================================================
/*
 * @Author: Lambda
 * @Begin: 2019-11-19 12:37:10
 * @Update: 2019-11-19 13:48:22
 * @Update log: 更新日志
 */
// 判断一个元素是否在视窗内
function isInSport (ele, wra) {
  // 判断该图片是否能够加载,判断图片是否在可视区域内
  const wraHeight = wra.clientHeight
  const rect = ele.getBoundingClientRect()
  // 如果超出视窗返回false
  if (rect.bottom <= (wraHeight + 100) && rect.bottom >= (wraHeight / 2)) {
    return true
  }
  if (rect.top <= (wraHeight + 100) && rect.top >= 0) {
    return true
  }
  return false
}

export default isInSport


================================================
FILE: musicPlayer/src/assets/utils/setKeyWords.js
================================================
/*
 * @Author: Lambda
 * @Begin: 2019-11-01 13:07:59
 * @Update: 2019-11-04 13:40:35
 * @Update log: 更新日志
 */
/**
 * 传入关键字和内容,将内容中含有关键字的部分添加对应的高亮样式
 * @param {*} keyword
 * @param {*} val
 */
export const filterSetKeyWords = (keyword, val, property) => {
  let results = []
  const _val = val
  _val.map((item, index) => {
    if (keyword && keyword.length > 0) {
      // 匹配关键字正则
      let replaceReg = new RegExp(keyword, 'g')
      // 高亮替换v-html值
      let replaceString =
        `<i style="color: #3399EA">${keyword}</i>`
      _val[index][property] = item[property].replace(
        replaceReg,
        replaceString
      )
    }
  })
  results = _val
  return results
}


================================================
FILE: musicPlayer/src/base/albumPage/index.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-06 11:47:11
 * @Update: 2019-11-02 14:11:21
 * @Update log: 这个是歌单展示的通用组件
 -->
<template>
  <!-- 通过传递参数给子组件,标题,加载样式,图片链接,歌单名称,作者头像,作者昵称,歌单介绍,评论数,分享数,歌单歌曲数,收藏数,是否收藏 -->
  <song-list-page
    :title="title"
    :load="load"
    :imgUrl="albumInfo.coverImgUrl ? albumInfo.coverImgUrl : albumInfo.album ? albumInfo.album.picUrl : ''"
    :albumTitle="albumInfo.name ? albumInfo.name : albumInfo.album ? albumInfo.album.name : ''"
    :albumId="albumId"
    :idxId="idxId"
    :idxComId="idxComId"
    :dishId="dishId"
    :creatorImgUrl="albumInfo.creator ? albumInfo.creator.avatarUrl : albumInfo.album ? albumInfo.album.artist.picUrl:''"
    :author="albumInfo.creator ? albumInfo.creator.nickname : albumInfo.album ? albumInfo.album.artist.name : ''"
    :description="albumInfo.description ? albumInfo.description : albumInfo.album ? albumInfo.album.description : ''"
    :commentCount="albumInfo.commentCount ? albumInfo.commentCount : albumInfo.album ? albumInfo.album.info.commentCount : 0"
    :shareCount="albumInfo.shareCount ? albumInfo.shareCount : albumInfo.album ? albumInfo.album.info.shareCount : 0"
    :trackCount="albumInfo.trackCount ? albumInfo.trackCount : albumInfo.album ? albumInfo.album.size : 0"
    :subscribedCount="albumInfo.subscribedCount"
    :subscribed="albumInfo.subscribed"
    :isSubIn="albumInfo.subscribed"
    :playCount="albumInfo.playCount"
    @startPlayAll="startPlay"
  >
    <!-- 这是一个通用的用来展示歌曲列表的组件,通过for循环组件进行渲染  这里使用 index+1 展示了页面的索引值 -->
    <song-list
      v-for="(item, index) in albumInfo.tracks || albumInfo.songs"
      :key="index"
      :songName="item.name"
      :artists="item.ar"
      :albumName="item.al.name"
      :num="index + 1"
      @beginSong="setAudioList(item, index)"
      :nowSong="item.id === audioSong.id"
    ></song-list>
  </song-list-page>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import songList from 'base/song'
import songListPage from 'base/songListPage'
import api from 'api'

export default {
  name: '',
  data () {
    return {
      // 存储信息的数组
      albumInfo: [],
      // 用来定义是否显示load加载组件
      load: true,
      albumId: 0,
      dishId: 0,
      idxId: 0,
      idxComId: 0,
      title: ''
    }
  },
  components: {
    songListPage,
    songList
  },
  /**
   * 生命钩子函数在实例创建完成后被立即调用
   */
  created () {
    // if (this.albumInfo) {
    //   this.$router.go(-1)
    // }
  },
  activated () {
    this.load = true
    this.albumInfo = []
    let albumId = this.$route.params.albumId
    let idxId = this.$route.params.idxId
    let dishId = this.$route.params.dishId
    if (albumId) {
      this.title = '歌单'
      this.albumId = +albumId
      this._getInfo(albumId)
      return
    }
    if (idxId || idxId === 0) {
      this.title = ''
      this.idxId = +idxId
      this._getIdxInfo(idxId)
      return
    }
    if (dishId) {
      this.title = '专辑'
      this.dishId = +dishId
      this._getDishInfo(dishId)
      return
    }
    // !Number(0) === true
    /**
     * 判断当在歌单页面刷新时无法获取到歌单内容
     * 获取到的id值为undefined
     */
    if (!idxId || !albumId || !dishId) {
      this.$router.go(-1)
    }
  },
  computed: {
    ...mapGetters({ audioSong: 'AUDIO_ING_SONG' })
  },
  methods: {
    /**
     * 根据传入的id获取歌单信息
     *
     * 这里需要增加 catch 方法!!!
     */
    _getInfo (id) {
      // 这里使用的是定义的接口信息,详情查看 api 文件夹
      api.albumDetailFn(id)
        // 请求成功后返回数据
        .then(res => {
          // 接受数据
          const data = res.data
          // 查看返回数据的 code 状态,如果是 200 的话进行使用
          if (data.code === 200) {
            // 将请求回来的数据使用,将load 样式关闭
            this.albumInfo = data.playlist
            this.load = false
          }
        })
        .catch(error => {
          console.log(error)
        })
    },
    /**
     * 获取排行榜信息
     */
    _getIdxInfo (id) {
      api.idxListFn(id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.albumInfo = data.playlist
            this.idxComId = data.playlist.id
            this.load = false
          }
        })
    },
    /**
     * 获取专辑内容
     */
    _getDishInfo (id) {
      api.getDishInfoFn(id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.albumInfo = data
            this.load = false
          }
        })
    },
    setAudioList (item, index) {
      this.selectPlay({
        list: this.albumInfo.tracks,
        index
      })
    },
    startPlay () {
      this.startPlayAll({
        list: this.albumInfo.tracks
      })
    },
    ...mapActions(['selectPlay', 'startPlayAll'])
  },
  destroyed () {
    // 存储信息的数组
    this.albumInfo = []
    // 用来定义是否显示load加载组件
    this.load = true
  }
}
</script>

<style lang='less' scoped>
</style>


================================================
FILE: musicPlayer/src/base/albumPage/index2.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-10-13 12:03:28
 * @Update: 2019-11-30 13:55:02
 * @Update log: 更新日志
 -->
<template>
  <detail-page :name="name" :coverImgUrl="coverImgUrl" :title="title">
    <div slot="data">
      <div class="info-top">
        <div class="img-info">
          <img v-lazy="imgUrl + '?param=200y200'" :key="imgUrl" alt />
          <span class="play-count">
            <i class="date-song bofang"></i>
            {{playCount | setPlay}}
          </span>
        </div>
        <div class="info-con">
          <p class="album-title">{{iAlbumTitle}}</p>
          <div class="creator">
            <div class="img-info">
              <img v-lazy="creatorImgUrl + '?param=200y200'" :key="imgUrl" alt />
            </div>
            <span>
              {{author}}
              <i class="date-song iconfontjiantou5"></i>
            </span>
          </div>
          <div class="desc-wrapper">
            <span class="desc">{{description}}</span>
            <i class="date-song iconfontjiantou5"></i>
          </div>
        </div>
      </div>
      <div class="icons">
        <div class="comments" @click="goComments">
          <i class="date-song pinglun"></i>
          <span>{{commentCount | setCom}}</span>
        </div>
        <div class="comments">
          <i class="date-song fenxiang"></i>
          <span>{{shareCount | setShare}}</span>
        </div>
        <div class="comments">
          <i class="date-song xiazai"></i>
          <span>下载</span>
        </div>
        <div class="comments">
          <i class="date-song duoxuankuang"></i>
          <span>多选</span>
        </div>
      </div>
    </div>
    <div slot="nav-list" class="title pd23">
      <span>
        <span @click="beginAudio">
          <i class="date-song cbofang"></i>
          播放全部
        </span>
        <!-- 当歌单组件时,需要显示当前歌单总共有多少首歌曲的信息 -->
        <span class="count" v-if="isAlbum">(共{{trackCount}}首)</span>
      </span>
      <div class="collection" :class="{ 'bg': !isSubInItem }" ref="collection" v-if="isAlbum">
        <span v-show="!isSubInItem" @click="addPlaylist">+ 收藏({{subscribedCountItem | setCol}})</span>
        <span v-show="isSubInItem" @click="deletePlaylist">
          <i class="date-song wenjianjia"></i>
          {{subscribedCountItem | setCol}}
        </span>
      </div>
    </div>
    <div slot="bottom">
      <song-list
        v-for="(item, index) in albumInfo.tracks || albumInfo.songs"
        :key="index"
        :songName="item.name"
        :artists="item.ar"
        :albumName="item.al.name"
        :num="index + 1"
        @beginSong="setAudioList(item, index)"
        :nowSong="item.id === audioSong.id"
      ></song-list>
    </div>
    <slider
      slot="slider"
      ref="slider"
      :title="title"
      :author="name"
      :imgUrl="coverImgUrl"
      :id="itemId"
      :djDetailList="djDetail"
    ></slider>
  </detail-page>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import { filterSetPlayCount } from 'utils/filters'
import detailPage from '@/components/detailPage'
import songList from 'base/song'
import pageLoading from 'base/pageLoading'
import slider from 'base/slider'

import api from 'api'
export default {
  name: '',
  data () {
    return {
      // 存储信息的数组
      albumInfo: [],
      // 用来定义是否显示load加载组件
      load: true,
      albumId: 0,
      dishId: 0,
      idxId: 0,
      idxComId: 0,
      iTitle: '歌单',
      name: '',
      isSubInItem: false,
      loading: true,
      title: '歌单',
      subed: false,
      ridId: 0,
      itemId: 0,
      imgUrl: '',
      albumTitle: '',
      creatorImgUrl: '',
      author: '',
      description: '',
      commentCount: 0,
      shareCount: 0,
      trackCount: 0,
      subscribedCount: 0,
      subscribe: '',
      isSubIn: false,
      playCount: 0
    }
  },
  filters: {
    setCom: (val) => {
      if (!val) {
        return '评论'
      }
      return filterSetPlayCount(val)
    },
    setShare: (val) => {
      if (!val) {
        return '分享'
      }
      return filterSetPlayCount(val)
    },
    setCol: (val) => {
      if (!val) {
        return ''
      }
      return filterSetPlayCount(val)
    },
    setPlay: (val) => {
      if (!val) {
        return ''
      }
      return filterSetPlayCount(val)
    }
  },
  computed: {
    /**
     * 返回日
     */
    day: function () {
      const day = new Date().getDate() < 10
        ? '0' + new Date().getDate()
        : new Date().getDate()
      return day
    },
    /**
     * 返回月份
     */
    month: function () {
      const month = new Date().getMonth() + 1 < 10
        ? '0' + (new Date().getMonth() + 1)
        : new Date().getMonth() + 1
      return month
    },
    ...mapGetters({ audioSong: 'AUDIO_ING_SONG' })
  },
  activated () {
    this.loading = true
    this.albumInfo = []
    let albumId = this.$route.params.albumId
    let idxId = this.$route.params.idxId
    let dishId = this.$route.params.dishId
    if (albumId) {
      this.title = '歌单'
      this.albumId = +albumId
      this._getInfo(albumId)
      return
    }
    if (idxId || idxId === 0) {
      this.title = ''
      this.idxId = +idxId
      this._getIdxInfo(idxId)
      return
    }
    if (dishId) {
      this.title = '专辑'
      this.dishId = +dishId
      this._getDishInfo(dishId)
      return
    }
    /**
     * 判断当在歌单页面刷新时无法获取到歌单内容
     * 获取到的id值为undefined
     */
    if (!idxId || !albumId || !dishId) {
      this.$router.go(-1)
    }
  },
  methods: {
    /**
     * 收藏歌单
     */
    addPlaylist () {
      const id = this.albumId || this.dishId || this.idxId
      api.addOrDeletePlaylistFn(1, id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.isSubInItem = true
            ++this.subscribedCountItem
          }
        })
    },
    /**
     * 取消收藏歌单
     */
    deletePlaylist () {
      const id = this.albumId || this.dishId || this.idxId
      api.addOrDeletePlaylistFn(2, id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.isSubInItem = false
            --this.subscribedCountItem
          }
        })
    },
    /**
     * 去评论页面
     * 通过传不同的params的属性来判断资源是歌单还是专辑
     */
    goComments () {
      const playlistId = this.albumId ? this.albumId : this.idxComId
      const albumId = this.dishId
      const imgUrl = this.imgUrl
      const title = this.albumTitle
      const author = this.author
      this.$router.push({ name: 'comments', params: { playlistId, albumId, imgUrl, title, author } })
    },
    update_iTitle (con) {
      this.iTitle = con
    },
    _getDjDetailInfo (id) {
      api.djDetailFn(id)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.name = data.djRadio.name
            this.coverImgUrl = data.djRadio.picUrl + '?param=300y300'
            this.subscription = data.djRadio.subCount
            this.avatarUrl = data.djRadio.dj.avatarUrl + '?param=100y100'
            this.desc = data.djRadio.desc
            this.detailName = data.djRadio.dj.nickname
            this.category = data.djRadio.category
            this.subed = data.djRadio.subed
          }
        })
    },
    addDj () {
      api.djSubFn(this.ridId, 1)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.subed = true
            console.log(data)
          }
        })
    },
    showSlider (id) {
      console.log(this.$refs)
      this.title = '电台节目:' + this.name
      this.djDetail = true
      this.itemId = id
      this.$refs.slider.showSlider()
    },
    deleteDj () {
      api.djSubFn(this.ridId, 0)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            console.log(data)
            this.subed = false
          }
        })
    },
    changeToFirst () {
      this.active = 'first'
    },
    changeToSecond () {
      this.active = 'second'
    },
    setAudioList (item, index) {
      this.selectPlay({
        list: this.djProgramData,
        index
      })
    },
    ...mapActions(['selectPlay'])
  },
  components: {
    detailPage,
    songList,
    pageLoading,
    slider
  }
}
</script>

<style lang='less' scoped>
@import url("//at.alicdn.com/t/font_1394963_t6jt71rtm9.css");
@import url("~styles/global.less");
@textColor: #ccc;

.title {
  font-size: 0.3rem;
  height: 1rem;
  line-height: 1rem;
  .flex-between();
  background-color: #fff;
  .count {
    color: #999;
    font-size: small;
  }
  .collection {
    .pd23();
    font-size: smaller;
    margin-top: 3px;
    height: 0.7rem;
    line-height: 0.7rem;
    border-radius: 0.4rem;
    color: #999;
    &.bg {
      background-color: @bgcolor;
      color: #fff;
    }
  }
}
.info-top {
  height: 3rem;
  .flex-between();
  overflow: hidden;
  .img-info {
    @size: 2.6rem;
    width: @size;
    height: 0;
    position: relative;
    padding-bottom: @size;
    border-radius: @imgBorderRadius;
    overflow: hidden;
    img {
      width: @size;
      height: @size;
    }
    .play-count {
      position: absolute;
      top: 0.1rem;
      right: 0.1rem;
      .bofang {
        font-size: 0.24rem;
      }
    }
  }
  .info-con {
    width: 3.6rem;
    height: 2.6rem;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    .album-title {
      font-size: 0.36rem;
      line-height: 1.5;
      .twoLinesEllipsis();
    }
    .creator {
      height: 1rem;
      color: @textColor;
      display: flex;
      align-items: center;
      .img-info {
        @size: 0.6rem;
        width: @size;
        height: 0;
        padding-bottom: @size;
        margin-right: 8px;
        border-radius: 50%;
        overflow: hidden;
        img {
          width: @size;
          height: @size;
        }
      }
    }
    .desc-wrapper {
      display: flex;
      align-items: center;
      color: @textColor;
      .desc {
        width: 3rem;
        .twoLinesEllipsis();
      }
    }
  }
}
.icons {
  margin-top: 8px;
  .flex-around();
  width: 100%;
  .comments {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    .date-song {
      font-size: 0.4rem;
      margin-bottom: 5px;
    }
  }
}
.list-info {
  width: 100%;
  box-sizing: border-box;
  padding: 0.1rem 0.23rem;
  background-color: #fff;
  transform: translate3d(0, -0.5rem, 0);
}
</style>


================================================
FILE: musicPlayer/src/base/alert.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-17 17:10:07
 * @Update: 2019-09-23 17:38:12
 * @Update log: 底部错误信息提示组件
 -->
<template>
  <div>
    <transition>
      <!-- 调用时传入要显示的内容 -->
      <div class="alert" v-show="isAlert">{{ alert }}</div>
    </transition>
  </div>
</template>

<script>
export default {
  name: 'alert',
  props: {
    alert: {
      type: String
    },
    isAlert: {
      type: Boolean
    }
  }
}
</script>

<style lang="less" scoped>
.alert {
  position: fixed;
  bottom: 3rem;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.13rem;
  background-color: rgba(170, 170, 170, 0.6);
  font-size: smaller;
  border-radius: 8px;
}
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s;
}
.v-enter,
.v-leave-to {
  opacity: 0;
}
</style>


================================================
FILE: musicPlayer/src/base/audioAllTitle.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-28 12:56:39
 * @Update: 2019-09-28 12:56:39
 * @Update log: 播放全部的那一行
 *
 * 使用时传入当前列表有几首歌曲,用来显示
 -->
<template>
  <div class="title">
    <span>
      <!-- 点击事件,点击 播放全部 按钮 -->
      <span @click="beginAudio">
        <i class="audio_all audio_allbofang"></i>
        播放全部
      </span>
      <!-- 当歌单组件时,需要显示当前歌单总共有多少首歌曲的信息 -->
      <span class="count">(共{{trackCount}}首)</span>
    </span>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    trackCount: {
      type: Number
    }
  },
  methods: {
    // 触发播放全部事件
    beginAudio () {
      this.$emit('beginAudioAll')
    }
  }
}
</script>

<style lang='less' scoped>
@import url("//at.alicdn.com/t/font_1439756_kjcwl3bv5jo.css");
@import url("~styles/global.less");

.title {
  font-size: 0.3rem;
  height: 0.8rem;
  line-height: 0.8rem;
  .flex-between();
  background-color: #fff;
  .count {
    color: #999;
    font-size: small;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/button.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-14 16:21:48
 * @Update: 2019-08-18 13:43:11
 * @Update log: 通用 button 登录页按钮
 *
 * 使用时传入 button 内容
 -->
<template>
  <div class="btn">
    <button id="btn"></button>
    <label class="label" for="btn" ref="btnText">{{ title }}</label>
  </div>
</template>

<script>
export default {
  name: 'loginBtn',
  // 定义默认的 button 内容为下一步
  props: {
    title: {
      type: String,
      default: '下一步'
    }
  }
}
</script>

<style lang="less" scoped>
@import url("~styles/global.less");
.btn {
  .flex-center();
  text-align: center;
  #btn {
    position: absolute;
    clip: rect(0, 0, 0, 0);
  }
  .label {
    box-sizing: border-box;
    margin-top: 30px;
    padding: 8px;
    border-radius: 20px;
    line-height: 20px;
    width: 80%;
    color: #fff;
    background: @bgcolor;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/circleLoading.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-11-04 13:58:11
 * @Update: 2019-11-07 19:55:14
 * @Update log: 更新日志
 -->
<template>
  <div
    class="circle-load"
    :style="{width, height}"
    v-show="load"
    :class="{absolute: type === 'absolute' }"
  >
    <svg viewBox="25 25 50 50">
      <circle cx="50" cy="50" r="20" />
    </svg>
  </div>
</template>

<script>
export default {
  name: '',
  data () {
    return {
      load: false
    }
  },
  props: {
    type: {
      type: String
    },
    width: {
      type: String
    },
    height: {
      type: String
    }
  },
  methods: {
    block () {
      this.load = true
    },
    none () {
      this.load = false
    }
  }
}
</script>

<style lang='less' scoped>
.circle-load {
  top: 0;
  display: flex;
  justify-content: center;
  &.absolute {
    position: absolute;
    z-index: 5;
  }
}
svg {
  width: 3.75em;
  transform-origin: center;
  animation: rotate 2s linear infinite;
}

circle {
  fill: none;
  stroke: #7b7c7d;
  stroke-width: 2;
  stroke-dasharray: 1, 200;
  stroke-dashoffset: 0;
  stroke-linecap: round;
  animation: dash 1.5s ease-in-out infinite;
}

@keyframes rotate {
  100% {
    transform: rotate(360deg);
  }
}

@keyframes dash {
  0% {
    stroke-dasharray: 1, 200;
    stroke-dashoffset: 0;
  }
  50% {
    stroke-dasharray: 90, 200;
    stroke-dashoffset: -35px;
  }
  100% {
    stroke-dashoffset: -125px;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/comments.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-10-27 09:14:42
 * @Update: 2019-11-12 19:11:06
 * @Update log: 更新日志
 -->
<template>
  <div class="wrapper">
    <h1 class="title">
      {{title}}
      <span class="num">{{total}}</span>
    </h1>
    <div
      class="item border-bottom"
      @click="showMenu(item)"
      v-for="(item, index) in comments"
      :key="index"
    >
      <div class="left-img">
        <img :src="item.user.avatarUrl + '?param=50y50'" alt />
      </div>
      <div class="right-info">
        <div class="top-info">
          <div class="data">
            <div class="nickname">{{item.user.nickname}}</div>
            <div class="time">{{ item.time | setTime}}</div>
          </div>
          <div
            class="like"
            :class="{liked: item.liked}"
            @click.stop="likeThisComment(item.commentId, item.liked)"
          >
            <span v-show="item.likedCount > 0">{{item.likedCount}}</span>
            <i class="comment" :class="{ commentzan: !item.liked, commentzan1:item.liked}"></i>
          </div>
        </div>
        <!-- 这里使用 v-html 用来解析 当出现 \n 换行的时候插入 <br />标签 -->
        <div class="content" v-html="setCon(item.content)"></div>
      </div>
    </div>
  </div>
</template>

<script>
import { filterSetMonth } from 'utils/filters'
import Bus from '@/assets/Bus'
export default {
  name: '',
  props: {
    total: {
      type: Number
    },
    comments: {
      type: Array
    },
    title: {
      type: String
    }
  },
  filters: {
    setTime: function (val) {
      return filterSetMonth(val, '月', '日')
    }
  },
  methods: {
    likeThisComment (cid, isLike) {
      let like = 1
      // 默认为点赞
      if (isLike) {
        // 如果当前歌曲已经赞了,则取消点赞
        like = 0
      }
      this.$emit('likeComment', cid, like)
    },
    showMenu (item) {
      console.log(item)
      const localUserId = +localStorage.getItem('accountUid')
      const userId = item.user.userId
      if (localUserId === userId) {
        // 当前评论是自己的,可以删除,不能举报
        Bus.$emit('user', true)
      }
      Bus.$emit('comId', item.commentId)
      this.$emit('showMenu')
    },
    /**
     * 将返回来的数据进行格式化处理
     * 将 换行符\n 替换成 <br /> 实现换行
     * 将 回车符\r\n 替换成 <br /> 实现换行
     */
    setCon (val) {
      val = val.replace(/(\n)|(\r\n)/g, '<br />')
      return val
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
@import url("//at.alicdn.com/t/font_1478463_b9awmkqysf8.css");

.wrapper {
  margin-top: 0.3rem;
}
.title {
  font-weight: 700;
}
.item {
  display: flex;
  box-sizing: border-box;
  margin: 0.3rem 0;
  padding-bottom: 0.3rem;
  height: auto;
  .left-img {
    width: 0.6rem;
    height: 0;
    padding-bottom: 0.6rem;
    margin-right: 0.2rem;
    img {
      width: 0.6rem;
      height: 0.6rem;
      border-radius: 50%;
    }
  }
  .right-info {
    width: 100%;
    .top-info {
      color: #999;
      line-height: 1.3;
      font-size: 0.24rem;
      .flex-between();
      margin-bottom: 0.13rem;
      .data {
        .time {
          font-size: 0.21rem;
        }
      }
      .liked {
        color: @bgcolor;
      }
    }
    .content {
      line-height: 1.5;
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/djDetailPage/components/changeNav.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-10-14 13:34:16
 * @Update: 2019-10-28 09:17:00
 * @Update log: 更新日志
 -->
<template>
  <div class="title">
    <div class="content" @click="changeToFirst">
      <span class="under-line" :class="{active: active === 'first'}">{{firstNav}}</span>
    </div>
    <div class="content" @click="changeToSecond">
      <span class="under-line" :class="{active: active === 'second'}">{{secondNav}}</span>
      <span class="num">{{count}}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    active: {
      type: String,
      default: 'second'
    },
    count: {
      type: Number
    },
    firstNav: {
      type: String
    },
    secondNav: {
      type: String
    }
  },
  methods: {
    changeToFirst () {
      this.$emit('changeToFirst')
    },
    changeToSecond () {
      this.$emit('changeToSecond')
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
.under-line {
  position: relative;
  padding-bottom: 0.25rem;
}

.under-line::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 100%;
  height: 2px;
  background-color: #fc2f70;
  transform-origin: center;
  transform: translate(-50%, 0) scaleX(0);
  transition: transform 0.3s ease-in-out;
}

.under-line.active::before {
  transform: translate(-50%, 0) scaleX(1);
}
.title {
  height: 0.8rem;
  line-height: 0.8rem;
  background-color: #fff;
  border-top-left-radius: 0.4rem;
  border-top-right-radius: 0.4rem;
  display: flex;
  transform: translateY(-0.7rem);
  .content {
    flex: 1;
    text-align: center;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/djDetailPage/index.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-10-13 12:03:28
 * @Update: 2019-11-29 13:28:44
 * @Update log: 更新日志
 -->
<template>
  <detail-page :title="title" :name="name" :coverImgUrl="coverImgUrl">
    <div slot="data">
      <div>
        <div class="name">{{name}}</div>
        <div class="num">{{subscription | setCount}}人已订阅</div>
      </div>
      <div class="subscription" v-show="!subed" @click.prevent="addDj">
        <svg
          t="1571198266501"
          class="icon"
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="1753"
          width="14"
          height="14"
        >
          <path
            d="M737.792 910.6944a57.2416 57.2416 0 0 1-26.7264-6.656l-197.5296-103.8336-197.5296 103.8336a57.2416 57.2416 0 0 1-83.0464-60.3648l37.6832-220.16L110.848 467.968a57.2928 57.2928 0 0 1 31.744-97.6896L363.52 338.2272 462.1824 138.24a56.832 56.832 0 0 1 51.2-31.8976 56.9344 56.9344 0 0 1 51.2 31.8976l98.7648 200.1408 220.8256 32.0512A57.2928 57.2928 0 0 1 916.48 467.968l-159.7952 155.7504 37.7344 220.16a57.3952 57.3952 0 0 1-56.32 67.0208zM159.8464 430.08l155.2896 151.3984a57.2416 57.2416 0 0 1 16.4352 50.688l-36.6592 213.5552 192-100.9152a57.088 57.088 0 0 1 53.2992 0L732.16 845.7216l-36.6592-213.76a57.344 57.344 0 0 1 16.4352-50.688L867.2768 430.08l-214.6304-31.1808a57.2928 57.2928 0 0 1-43.1104-31.3344l-96-194.56-96 194.56a57.2416 57.2416 0 0 1-43.1104 31.3344z m715.6736 1.024zM509.7984 165.2736z"
            fill="#ffffff"
            p-id="1754"
          />
        </svg>订阅
      </div>
      <div class="subscription1" v-show="subed" @click.prevent="deleteDj">
        <svg
          t="1571203188806"
          class="icon"
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="2523"
          width="14"
          height="14"
        >
          <path
            d="M926.037333 224.256c-22.016-22.016-57.685333-22.016-79.701333 0L384.853333 685.738667 179.370667 480.256c-22.016-22.016-57.685333-22.016-79.701334 0-22.016 22.016-22.016 57.685333 0 79.701333l239.786667 239.786667c12.458667 12.458667 29.184 17.749333 45.397333 16.213333 16.213333 1.536 32.938667-3.754667 45.397334-16.213333l495.786666-495.786667c22.016-22.016 22.016-57.685333 0-79.701333z"
            fill="#ffffff"
            p-id="2524"
          />
        </svg>已订阅
      </div>
    </div>
    <change-nav
      slot="nav-list"
      :active="active"
      :count="count"
      firstNav="详情"
      secondNav="节目"
      @changeToSecond="changeToSecond"
      @changeToFirst="changeToFirst"
    ></change-nav>
    <div slot="bottom">
      <div class="song-list" v-show="active==='second'">
        <page-loading v-show="loading"></page-loading>
        <div v-show="!loading">
          <h1 class="sum-num pd23">共{{count}}期</h1>
          <song-list
            class="pd23"
            v-for="(item, index) in djProgramData"
            :key="index"
            :songName="item.name"
            :num="djProgramData.length - index"
            :createTime="item.createTime"
            :listenerCount="item.listenerCount"
            :duration="item.duration"
            :twoLine="true"
            :itemId="item.id"
            @showSlider="showSlider"
            type="djList"
            @beginSong="setAudioList(item, index)"
            :nowSong="item.id === audioSong.id"
          ></song-list>
        </div>
      </div>
      <div class="detail pd23" v-show="active==='first'">
        <h1 class="anchor">主播</h1>
        <div class="content">
          <div class="img-info">
            <img :src="avatarUrl" alt />
          </div>
          <div class="artist">
            <p class="name">{{detailName}}</p>
            <p class="dec">网易音乐人</p>
          </div>
        </div>
        <h1 class="dj-content">电台内容简介</h1>
        <div class="class">
          <span>分类</span>
          <span class="tag">{{category}}</span>
        </div>
        <p class="text">{{desc}}</p>
      </div>
    </div>
    <slider
      slot="slider"
      ref="slider"
      :title="title"
      :author="name"
      :imgUrl="coverImgUrl"
      :id="itemId"
      :djDetailList="djDetail"
    ></slider>
  </detail-page>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import { filterSetPlayCount } from 'utils/filters'
import detailPage from '@/components/detailPage'
import changeNav from './components/changeNav'
import songList from 'base/song'
import pageLoading from 'base/pageLoading'
import slider from 'base/slider'

import api from 'api'
export default {
  name: '',
  data () {
    return {
      djProgramData: [],
      djDetail: false,
      coverImgUrl: '',
      count: 0,
      name: '',
      subscription: 0,
      active: 'second',
      avatarUrl: '',
      detailName: '',
      category: '',
      desc: '',
      loading: true,
      title: '电台',
      subed: false,
      ridId: 0,
      itemId: 0
    }
  },
  computed: {
    ...mapGetters({ audioSong: 'AUDIO_ING_SONG' })
  },
  activated () {
    this.loading = true
    this.coverImgUrl = ''
    this.count = 0
    this.name = ''
    this.active = 'second'
    const params = this.$route.params
    if (!params.ridId) {
      this.$router.go(-1)
      return
    }
    this.ridId = params.ridId
    const ridId = this.ridId
    this._getDjProgramInfo(ridId)
    this._getDjDetailInfo(ridId)
  },
  filters: {
    setCount: val => (
      filterSetPlayCount(val)
    )
  },
  methods: {
    _getDjProgramInfo (id) {
      let limit = 30
      let offset = 0
      let asc = false
      api.djProgramFn(id, limit, offset, asc)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.count = data.count
            this.djProgramData = data.programs
            this.loading = false
          }
        })
    },
    _getDjDetailInfo (id) {
      api.djDetailFn(id)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.name = data.djRadio.name
            this.coverImgUrl = data.djRadio.picUrl + '?param=300y300'
            this.subscription = data.djRadio.subCount
            this.avatarUrl = data.djRadio.dj.avatarUrl + '?param=100y100'
            this.desc = data.djRadio.desc
            this.detailName = data.djRadio.dj.nickname
            this.category = data.djRadio.category
            this.subed = data.djRadio.subed
          }
        })
    },
    addDj () {
      api.djSubFn(this.ridId, 1)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.subed = true
            console.log(data)
          }
        })
    },
    showSlider (id) {
      console.log(this.$refs)
      this.title = '电台节目:' + this.name
      this.djDetail = true
      this.itemId = id
      this.$refs.slider.showSlider()
    },
    deleteDj () {
      api.djSubFn(this.ridId, 0)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            console.log(data)
            this.subed = false
          }
        })
    },
    changeToFirst () {
      this.active = 'first'
    },
    changeToSecond () {
      this.active = 'second'
    },
    setAudioList (item, index) {
      this.selectPlay({
        list: this.djProgramData,
        index
      })
    },
    ...mapActions(['selectPlay'])
  },
  components: {
    detailPage,
    changeNav,
    songList,
    pageLoading,
    slider
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");

.name {
  color: #fff;
  max-width: 4rem;
  .twoLinesEllipsis();
  font-size: 0.3rem;
  line-height: 1.5;
}
.num {
  color: #eee;
  line-height: 1.5;
}
.subscription {
  box-sizing: border-box;
  position: absolute;
  right: 0.5rem;
  bottom: 0;
  display: flex;
  padding: 0 0.23rem;
  height: 0.5rem;
  align-items: center;
  justify-content: center;
  color: #fff;
  background-color: @bgcolor;
  border-radius: 0.4rem;
  .icon {
    margin-right: 0.2rem;
  }
}
.subscription1 {
  box-sizing: border-box;
  position: absolute;
  right: 0.5rem;
  bottom: 0;
  display: flex;
  padding: 0 0.23rem;
  height: 0.5rem;
  align-items: center;
  justify-content: center;
  color: #fff;
  border-radius: 0.4rem;
  border: 1px solid #fff;
  .icon {
    margin-right: 0.2rem;
  }
}
.song-list {
  .sum-num {
    height: 0.5rem;
    line-height: 0.5rem;
    font-weight: 700;
  }
}
.detail {
  .anchor,
  .dj-content {
    font-weight: 700;
    line-height: 1.5;
    margin: 0.3rem 0;
  }
  .content {
    display: flex;
    align-items: center;
    .img-info {
      width: 1rem;
      height: 0;
      padding-bottom: 1rem;
      margin-right: 0.2rem;
      img {
        border-radius: 50%;
        width: 1rem;
        height: 1rem;
      }
    }
    .artist {
      display: flex;
      flex-direction: column;
      line-height: 1.5;
      .dec {
        font-size: 0.23rem;
        color: #999;
      }
    }
  }
  .class {
    font-size: 0.23rem;
    line-height: 1.5;
    .tag {
      color: @bgcolor;
      box-sizing: border-box;
      padding: 2px;
      border-radius: 4px;
      border: 1px solid @bgcolor;
    }
  }
  .text {
    font-size: 0.23rem;
    color: #999;
    margin-top: 0.13rem;
    line-height: 1.5;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/djDetailPage/index2.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-10-13 12:03:28
 * @Update: 2019-11-14 14:03:58
 * @Update log: 更新日志
 -->
<template>
  <div class="wrapper" @scroll="scrollList">
    <dj-detail-nav @returnPage="returnPage" class="fixed pd23" style="color:#fff;">
      <span class="text">{{iTitle}}</span>
    </dj-detail-nav>
    <div
      class="container-top"
      :class="{coverFixed, position}"
      :style="{backgroundImage: 'url(' + coverImgUrl + ')'}"
    >
      <div class="cover" :style="{backgroundColor: `rgba(0, 0, 0, ${cover})`}"></div>
      <div class="data" v-show="!listFixed" :style="{opacity}">
        <div>
          <div class="name">{{name}}</div>
          <div class="num">{{subscription}}人已订阅</div>
        </div>
        <div class="subscription" v-show="!subed" @click.prevent="addDj">
          <svg
            t="1571198266501"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="1753"
            width="14"
            height="14"
          >
            <path
              d="M737.792 910.6944a57.2416 57.2416 0 0 1-26.7264-6.656l-197.5296-103.8336-197.5296 103.8336a57.2416 57.2416 0 0 1-83.0464-60.3648l37.6832-220.16L110.848 467.968a57.2928 57.2928 0 0 1 31.744-97.6896L363.52 338.2272 462.1824 138.24a56.832 56.832 0 0 1 51.2-31.8976 56.9344 56.9344 0 0 1 51.2 31.8976l98.7648 200.1408 220.8256 32.0512A57.2928 57.2928 0 0 1 916.48 467.968l-159.7952 155.7504 37.7344 220.16a57.3952 57.3952 0 0 1-56.32 67.0208zM159.8464 430.08l155.2896 151.3984a57.2416 57.2416 0 0 1 16.4352 50.688l-36.6592 213.5552 192-100.9152a57.088 57.088 0 0 1 53.2992 0L732.16 845.7216l-36.6592-213.76a57.344 57.344 0 0 1 16.4352-50.688L867.2768 430.08l-214.6304-31.1808a57.2928 57.2928 0 0 1-43.1104-31.3344l-96-194.56-96 194.56a57.2416 57.2416 0 0 1-43.1104 31.3344z m715.6736 1.024zM509.7984 165.2736z"
              fill="#ffffff"
              p-id="1754"
            />
          </svg>订阅
        </div>
        <div class="subscription1" v-show="subed" @click.prevent="deleteDj">
          <svg
            t="1571203188806"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="2523"
            width="14"
            height="14"
          >
            <path
              d="M926.037333 224.256c-22.016-22.016-57.685333-22.016-79.701333 0L384.853333 685.738667 179.370667 480.256c-22.016-22.016-57.685333-22.016-79.701334 0-22.016 22.016-22.016 57.685333 0 79.701333l239.786667 239.786667c12.458667 12.458667 29.184 17.749333 45.397333 16.213333 16.213333 1.536 32.938667-3.754667 45.397334-16.213333l495.786666-495.786667c22.016-22.016 22.016-57.685333 0-79.701333z"
              fill="#ffffff"
              p-id="2524"
            />
          </svg>已订阅
        </div>
      </div>
    </div>
    <change-nav
      :class="{listFixed}"
      :active="active"
      :count="count"
      firstNav="详情"
      secondNav="节目"
      @changeToSecond="changeToSecond"
      @changeToFirst="changeToFirst"
    ></change-nav>
    <div class="container-bottom" :style="{ marginTop: top}">
      <div class="song-list" v-show="active==='second'">
        <page-loading v-show="loading"></page-loading>
        <div v-show="!loading">
          <h1 class="sum-num pd23">共{{count}}期</h1>
          <song-list
            class="pd23"
            v-for="(item, index) in djProgramData"
            :key="index"
            :songName="item.name"
            :num="djProgramData.length - index"
            :createTime="item.createTime"
            :listenerCount="item.listenerCount"
            :duration="item.duration"
            :twoLine="true"
            :itemId="item.id"
            @showSlider="showSlider"
            type="djList"
            @beginSong="setAudioList(item, index)"
            :nowSong="item.id === audioSong.id"
          ></song-list>
        </div>
      </div>
      <div class="detail pd23" v-show="active==='first'">
        <h1 class="anchor">主播</h1>
        <div class="content">
          <div class="img-info">
            <img :src="avatarUrl" alt />
          </div>
          <div class="artist">
            <p class="name">{{detailName}}</p>
            <p class="dec">网易音乐人</p>
          </div>
        </div>
        <h1 class="dj-content">电台内容简介</h1>
        <div class="class">
          <span>分类</span>
          <span class="tag">{{category}}</span>
        </div>
        <p class="text">{{desc}}</p>
      </div>
    </div>
    <slider
      ref="slider"
      :title="title"
      :author="name"
      :imgUrl="coverImgUrl"
      :id="itemId"
      :djDetailList="djDetail"
    ></slider>
  </div>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import djDetailNav from 'base/generalNav'
import changeNav from './components/changeNav'
import songList from 'base/song'
import pageLoading from 'base/pageLoading'
import slider from 'base/slider'

import api from 'api'
export default {
  name: '',
  data () {
    return {
      djProgramData: [],
      djDetail: false,
      coverImgUrl: '',
      count: 0,
      name: '',
      subscription: 0,
      active: 'second',
      avatarUrl: '',
      detailName: '',
      category: '',
      desc: '',
      loading: true,
      cover: '0.2',
      iTitle: '电台',
      title: '电台',
      listFixed: false,
      coverFixed: false,
      position: true,
      top: '0rem',
      subed: false,
      ridId: 0,
      itemId: 0,
      opacity: 1
    }
  },
  computed: {
    ...mapGetters({ audioSong: 'AUDIO_ING_SONG' })
  },
  activated () {
    this.loading = true
    this.coverImgUrl = ''
    this.count = 0
    this.name = ''
    this.active = 'second'
    const params = this.$route.params
    if (!params.ridId) {
      this.$router.go(-1)
      return
    }
    this.ridId = params.ridId
    const ridId = this.ridId
    this._getDjProgramInfo(ridId)
    this._getDjDetailInfo(ridId)
  },
  methods: {
    _getDjProgramInfo (id) {
      let limit = 30
      let offset = 0
      let asc = false
      api.djProgramFn(id, limit, offset, asc)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.count = data.count
            this.djProgramData = data.programs
            this.loading = false
          }
        })
    },
    _getDjDetailInfo (id) {
      api.djDetailFn(id)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.name = data.djRadio.name
            this.coverImgUrl = data.djRadio.picUrl + '?param=300y300'
            this.subscription = data.djRadio.subCount
            this.avatarUrl = data.djRadio.dj.avatarUrl + '?param=100y100'
            this.desc = data.djRadio.desc
            this.detailName = data.djRadio.dj.nickname
            this.category = data.djRadio.category
            this.subed = data.djRadio.subed
          }
        })
    },
    addDj () {
      api.djSubFn(this.ridId, 1)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            this.subed = true
            console.log(data)
          }
        })
    },
    showSlider (id) {
      this.title = '电台节目:' + this.name
      this.djDetail = true
      this.itemId = id
      this.$refs.slider.showSlider()
    },
    deleteDj () {
      api.djSubFn(this.ridId, 0)
        .then(res => {
          const { data } = res
          if (data.code === 200) {
            console.log(data)
            this.subed = false
          }
        })
    },
    changeToFirst () {
      this.active = 'first'
    },
    changeToSecond () {
      this.active = 'second'
    },
    setAudioList (item, index) {
      this.selectPlay({
        list: this.djProgramData,
        index
      })
    },
    returnPage () {
      this.$router.go(-1)
    },
    /**
     * 定义页面滚动事件,
     * 这里需要添加在滚动过程中样式的变化
     */
    scrollList (e) {
      // 获取到 top 值
      let top = this.$el.scrollTop
      this.cover = top / 1000 + 0.3
      this.opacity = 1 - top / 500
      if (this.cover > 0.6) {
        this.cover = 0.6
        this.opacity = 0.4
      } else {
        this.cover = top / 1000 + 0.3
        this.opacity = 1 - top / 500
      }
      if (top >= 282) {
        this.iTitle = this.name
        this.listFixed = true
        this.coverFixed = true
        this.position = false
        this.top = '6.6rem'
      } else {
        this.iTitle = this.title
        this.listFixed = false
        this.coverFixed = false
        this.position = true
        this.top = '0'
      }
    },
    ...mapActions(['selectPlay'])
  },
  components: {
    djDetailNav,
    changeNav,
    songList,
    pageLoading,
    slider
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
.topFixed {
  position: fixed;
  width: 100%;
  height: 0.8rem;
  z-index: 9;
}
.fixed {
  .topFixed();
  top: 0;
}
.listFixed {
  .topFixed();
  top: 1.6rem;
}
.coverFixed {
  position: fixed;
  width: 100%;
  z-index: 3;
  top: 0;
  transform: translateY(-5.4rem);
}
.wrapper {
  width: 100vw;
  height: 100vh;
  position: relative;
  overflow-y: scroll;
  overflow-x: hidden;
  .text {
    font-size: 0.4rem;
    vertical-align: 5px;
  }
  .container-top {
    width: 100%;
    height: 0;
    padding-bottom: 7rem;
    background-size: 100%;
    background-repeat: no-repeat;
    &.position {
      position: relative;
    }
    .cover {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
    }
    .data {
      position: absolute;
      bottom: 1rem;
      left: 0.3rem;
      .flex-between();
      width: 100%;
      box-sizing: border-box;
      padding-right: 0.8rem;
      .name {
        color: #fff;
        max-width: 4rem;
        .twoLinesEllipsis();
        font-size: 0.3rem;
        line-height: 1.5;
      }
      .num {
        color: #eee;
        line-height: 1.5;
      }
      .subscription {
        box-sizing: border-box;
        position: absolute;
        right: 0.5rem;
        bottom: 0;
        display: flex;
        padding: 0 0.23rem;
        height: 0.5rem;
        align-items: center;
        justify-content: center;
        color: #fff;
        background-color: @bgcolor;
        border-radius: 0.4rem;
        .icon {
          margin-right: 0.2rem;
        }
      }
      .subscription1 {
        box-sizing: border-box;
        position: absolute;
        right: 0.5rem;
        bottom: 0;
        display: flex;
        padding: 0 0.23rem;
        height: 0.5rem;
        align-items: center;
        justify-content: center;
        color: #fff;
        border-radius: 0.4rem;
        border: 1px solid #fff;
        .icon {
          margin-right: 0.2rem;
        }
      }
    }
  }
  .container-bottom {
    transform: translate3d(0, -0.8rem, 0);
    .song-list {
      .sum-num {
        height: 0.5rem;
        line-height: 0.5rem;
        font-weight: 700;
      }
    }
    .detail {
      .anchor,
      .dj-content {
        font-weight: 700;
        line-height: 1.5;
        margin: 0.3rem 0;
      }
      .content {
        display: flex;
        align-items: center;
        .img-info {
          width: 1rem;
          height: 0;
          padding-bottom: 1rem;
          margin-right: 0.2rem;
          img {
            border-radius: 50%;
            width: 1rem;
            height: 1rem;
          }
        }
        .artist {
          display: flex;
          flex-direction: column;
          line-height: 1.5;
          .dec {
            font-size: 0.23rem;
            color: #999;
          }
        }
      }
      .class {
        font-size: 0.23rem;
        line-height: 1.5;
        .tag {
          color: @bgcolor;
          box-sizing: border-box;
          padding: 2px;
          border-radius: 4px;
          border: 1px solid @bgcolor;
        }
      }
      .text {
        font-size: 0.23rem;
        color: #999;
        margin-top: 0.13rem;
        line-height: 1.5;
      }
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/djSublistCard.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-10-01 15:14:42
 * @Update: 2019-10-04 13:38:23
 * @Update log: 长卡片组件
 -->
<template>
  <div class="dj_sublist_card">
    <h1 class="title" v-if="title">
      {{title}}
      <span class="num">({{count}})</span>
    </h1>
    <!-- 要遍历的数组 -->
    <div class="card" v-for="(item, index) in djSublist" :key="index">
      <div class="img-info" :class="{circle,bigImg}">
        <!-- 列表项图片信息 -->
        <img :src="item.picUrl ? item.picUrl : item.coverUrl" alt />
        <span class="count" v-if="item.playTime">
          <i class="dj_sublist dj_sublist_bofang1"></i>
          {{item.playTime | numRule}}
        </span>
      </div>
      <div class="info" v-if="type === 'dj'">
        <!-- 列表项名字 -->
        <p class="name">{{ item.name }}</p>
        <!-- 作者名字 -->
        <p class="artist">by {{item.dj.nickname}}</p>
        <!-- 其他信息 -->
        <p class="text">{{item.lastProgramName}}</p>
      </div>
      <div class="info" v-if="type === 'albums'">
        <!-- 列表项名字 -->
        <p class="name">{{ item.name }}</p>
        <!-- 作者名字 -->
        <div>
          <span class="artist" v-for="(item, index) in item.artists" :key="index">{{item.name}}</span>
          <!-- 其他信息 -->
          <span class="text">{{item.size}}首</span>
        </div>
      </div>
      <div class="info" v-if="type === 'artists'">
        <!-- 列表项名字 -->
        <p class="name">{{ item.name }}</p>
        <!-- 作者名字 -->
        <div>
          <span class="artist">专辑:{{item.albumSize}}</span>
          <!-- 其他信息 -->
          <span class="text">MV:{{item.mvSize}}</span>
        </div>
      </div>
      <div class="info" v-if="type === 'video'">
        <!-- 列表项名字 -->
        <p class="name">{{ item.title }}</p>
        <!-- 作者名字 -->
        <div>
          <span class="artist">{{item.durationms | setTime}} by</span>
          <!-- 其他信息 -->
          <span class="text" v-for="(item, index) in item.creator" :key="index">{{item.userName}}</span>
        </div>
      </div>
      <!-- 右边的三个点按钮 -->
      <div class="icon">
        <i class="dj_sublist dj_sublist_diandian"></i>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    djSublist: {
      type: Array
    },
    count: {
      type: Number
    },
    title: {
      type: String
    },
    type: {
      type: String
    },
    circle: {
      type: Boolean
    },
    bigImg: {
      type: Boolean
    }
  },
  filters: {
    numRule: function (value) {
      if (!value) return ''
      if (value > 10000) {
        value = parseInt(value / 10000) + '万'
      } else if (value > 100000000) {
        value = (value / 100000000).toFixed(1) + '亿'
      }
      return value
    },
    setTime: function (value) {
      if (!value) return ''
      let min = parseInt(value / (1000 * 60))
      if (min < 10) {
        min = '0' + min
      }
      let sec = parseInt(value % (1000 * 60) / 1000)
      if (sec < 10) {
        sec = '0' + sec
      }
      value = `${min}:${sec}`
      return value
    }
  }
}
</script>

<style lang='less' scoped>
@import url("//at.alicdn.com/t/font_1443044_ba7y88tq60o.css");
@import url("~styles/global.less");
.dj_sublist_bofang1 {
  font-size: 0.13rem;
  color: #fff;
}

.dj_sublist_card {
  background-color: #fff;
  margin-top: 0.2rem;
  .title {
    font-weight: 700;
  }
  .card {
    margin-top: 0.2rem;
    height: 1.2rem;
    display: flex;
    align-items: center;
    .img-info {
      width: 1rem;
      height: 0;
      padding-bottom: 1rem;
      overflow: hidden;
      position: relative;
      &.circle {
        border-radius: 50%;
      }
      img {
        width: 1rem;
        height: 1rem;
      }
      &.bigImg {
        width: 2.8rem;
        padding-bottom: 1.4rem;
        img {
          width: 2.8rem;
          height: 1.4rem;
        }
      }
      .count {
        position: absolute;
        color: #fff;
        right: 5px;
        top: 5px;
        font-size: 0.13rem;
      }
    }
    .info {
      flex: 1;
      display: flex;
      flex-direction: column;
      margin-left: 0.2rem;
      .name {
        .twoLinesEllipsis();
      }
      .name,
      .artist,
      .text {
        line-height: 1.5;
      }
      .artist,
      .text {
        color: #999;
        font-size: 0.2rem;
      }
    }
    .icon {
      width: 0.4rem;
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/generalNav.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-14 15:44:26
 * @Update: 2019-11-19 22:01:42
 * @Update log: 登录页通用顶部导航
 -->
<template>
  <nav class="phone-nav" ref="nav" :class="{height: height === 'all'}">
    <i class="phone iconzuojiantou" @click="returnPage"></i>
    <!-- slot 插槽将不同页面的标题信息显示 -->
    <slot></slot>
  </nav>
</template>

<script>
export default {
  name: 'loginNav',
  props: {
    nav: {
      type: String,
      default: ''
    },
    height: {
      type: String,
      default: 'all'
    }
  },
  methods: {
    visible () {
      this.$refs.nav.style.visibility = 'visible'
    },
    hidden () {
      this.$refs.nav.style.visibility = 'hidden'
    },
    returnPage () {
      // 这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步
      // 类似 window.history.go(n)。
      this.$emit('returnPage')
    }
  }
}
</script>

<style lang="less" scoped>
@import url("//at.alicdn.com/t/font_1351323_oxqdjg3rufq.css");

.phone-nav {
  display: flex;
  align-items: center;
  &.height {
    height: 1rem;
    line-height: 1rem;
  }
  .phone {
    font-size: 0.7rem;
    margin-right: 5px;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/icon.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-30 20:21:12
 * @Update: 2019-10-11 13:10:30
 * @Update log: 公共图标组件
 -->
<template>
  <div class="icon-list" :class="{width}" @click="linkTo">
    <div class="icon" :class="{bgcolor}">
      <i :class="icons.icon"></i>
      <slot></slot>
    </div>
    <span class="icon-text">{{icons.text}}</span>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    icons: {
      type: [Object, Array]
    },
    width: {
      type: Boolean
    },
    bgcolor: {
      type: Boolean
    }
  },
  methods: {
    linkTo () {
      this.$emit('goPage')
    }
  }
}
</script>

<style lang='less' scoped>
// 这部分样式是对于 我的页面设置的
.width {
  min-width: 1.6rem;
  &:last-of-type > .icon {
    background: #ccc;
  }
}
.icon-list {
  // 每项 icon 样式
  height: 100%;
  display: flex;
  justify-content: space-around;
  flex-direction: column;
  align-items: center;
  color: #000;
  .bgcolor {
    background: linear-gradient(to right, #ff5a4c, #ff1d11);
  }
  .icon {
    width: 0.8rem;
    height: 0.8rem;
    line-height: 0.8rem;
    border-radius: 50%;
    text-align: center;
    position: relative;
    color: #fff;
    .home {
      font-size: 0.4rem;
    }
    .find {
      font-size: 0.5rem;
    }
    .dj {
      font-size: 0.4rem;
    }
    .login {
      font-size: 0.4rem;
      background-color: transparent;
      color: #ff1d11;
    }
  }
  .icon-text {
    font-size: 0.24rem;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/idxCard.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-09 13:31:02
 * @Update: 2019-10-08 12:45:15
 * @Update log: 排行榜中官方榜展示组件
 -->
<template>
  <div>
    <div class="list" @click="searchIdx(idx)">
      <div class="img-info">
        <img :src="imgUrl + '?param=200y200'" alt />
        <span class="time">{{ updateTime }}</span>
      </div>
      <div class="list-con">
        <div class="info" v-for="(item, index) in tracks" :key="index">
          {{index + 1 + '.'}}
          <span>{{item.first}}</span> -
          <span>{{item.second}}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    imgUrl: {
      type: String
    },
    tracks: {
      type: Array
    },
    updateTime: {
      type: String
    },
    idx: {
      type: String
    }
  },
  methods: {
    searchIdx (idx) {
      switch (idx) {
        case '云音乐新歌榜':
          idx = 0
          break
        case '云音乐热歌榜':
          idx = 1
          break
        case '网易原创歌曲榜':
          idx = 2
          break
        case '云音乐飙升榜':
          idx = 3
          break
        case '云音乐说唱榜':
          idx = 23
          break
        case '云音乐ACG音乐榜':
          idx = 22
          break
        case 'KTV唛榜':
          idx = 7
          break
        case 'iTunes榜':
          idx = 8
          break
        case '日本Oricon周榜':
          idx = 10
          break
        case 'Hit FM Top榜':
          idx = 9
          break
        case '台湾Hito排行榜':
          idx = 20
          break
        case 'Beatport全球电子舞曲榜':
          idx = 21
          break
        case '法国 NRJ Vos Hits 周榜':
          idx = 20
          break
        case 'UK排行榜':
          idx = 5
          break
        case '美国Billboard周榜':
          idx = 6
          break
      }
      this.$emit('showIdxPage', idx)
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");

.list {
  height: 2rem;
  display: flex;
  align-items: center;
  .img-info {
    position: relative;
    width: 1.7rem;
    height: 0;
    padding-bottom: 1.7rem;
    border-radius: 0.13rem;
    overflow: hidden;
    img {
      width: 1.7rem;
      height: 1.7rem;
    }
    .time {
      position: absolute;
      bottom: 3px;
      left: 3px;
      font-size: 12px;
      color: #fff;
    }
  }
  .list-con {
    margin-left: 0.2rem;
    .info {
      height: 0.5rem;
      line-height: 0.5rem;
      width: 4.5rem;
      .ellipsis();
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/imgCard.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-08 14:37:08
 * @Update: 2019-11-12 21:42:40
 * @Update log: 通用的方形展示组件
 -->
<template>
  <div class="img-card" @click="searchIdx(idx)" :style="{width, marginTop:top}">
    <span class="tag" v-if="playCount">
      <i class="card cardbofang"></i>
      {{playCount | setPlayCount}}
    </span>
    <span class="swiper-tag" v-if="swiper">
      <i class="card cardbofang1"></i>
    </span>
    <span class="fine-tag" v-if="fine">
      <i class="card cardhuangguan"></i>
    </span>
    <span class="time-tag" v-if="updateTime">{{ updateTime }}</span>
    <div class="img-con" :style="{width,paddingBottom:width}">
      <div class="shadow"></div>
      <!-- 增加key属性,是可以动态切换图片,解决了在none到block时不能正确显示 -->
      <img v-lazy="imgUrl + '?param=200y200'" :key="imgUrl" class="image" />
      <span class="dj-name" v-show="type === 'dj'">{{name}}</span>
      <!-- 跳转到专辑详情页 -->
      <!-- <router-link class="cover"  @click="toAlbum(albumId)" :to="'/albumPage/'+albumId"></router-link> -->
    </div>
    <div class="dec" :class="{lines:lines === 'one',twoLines:lines === 'two'}">{{ dec }}</div>
    <div class="artists" v-if="artists">
      <span v-for="(item, index) in artists" :key="index" class="artist">{{ item.name }}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    playCount: {
      type: Number
    },
    updateTime: {
      type: String
    },
    imgUrl: {
      type: String
    },
    dec: {
      type: String
    },
    type: {
      type: String
    },
    name: {
      type: String
    },
    width: {
      type: String,
      default: '2.1rem'
    },
    lines: {
      type: String,
      default: 'two'
    },
    artists: {
      type: Array
    },
    top: {
      type: String,
      default: '0.3rem'
    },
    // 如果是轮播图,则显示大播放按钮
    swiper: {
      type: Boolean,
      default: false
    },
    fine: {
      type: Boolean,
      default: false
    },
    albumId: {
      type: Number
    },
    dishId: {
      type: Number
    },
    ridId: {
      type: Number
    },
    idx: {
      type: String
    }
  },
  filters: {
    setPlayCount: function (val) {
      if (!val) {
        return ''
      }
      if (val > 100000000) {
        val = ((val / 100000000).toFixed(1)) + '亿'
      } else if (val > 10000) {
        val = Math.floor(val / 10000) + '万'
      }
      return val
    }
  },
  methods: {
    /**
     * 给图片卡片注册点击事件
     *
     * 当没有idx时,查看是否有albumId,如果有跳转歌单页面
     * 如果有idx时说明是排行榜页面,跳转到排行榜页面
     */
    searchIdx (idx) {
      if (!idx) {
        if (this.albumId) {
          // [vue-router] Route with name 'albumPage' does not exist
          // 需要给路由设置name
          // 并且这个路由在配置的时候不能加 /:id
          this.$router.push({ name: 'albumPage', params: { albumId: this.albumId } })
          // this.$router.push(`/albumPage/${this.albumId}`)
          return
        }
        if (this.dishId) {
          this.$router.push({ name: 'albumPage', params: { dishId: this.dishId } })
          return
        }
        if (this.ridId) {
          console.log('go')

          this.$router.push({ name: 'djDetailPage', params: { ridId: this.ridId } })
          return
        }
        return
      }
      switch (idx) {
        case '云音乐新歌榜':
          idx = 0
          break
        case '云音乐热歌榜':
          idx = 1
          break
        case '网易原创歌曲榜':
          idx = 2
          break
        case '云音乐飙升榜':
          idx = 3
          break
        case '云音乐说唱榜':
          idx = 23
          break
        case '云音乐ACG音乐榜':
          idx = 22
          break
        case 'KTV唛榜':
          idx = 7
          break
        case 'iTunes榜':
          idx = 8
          break
        case '日本Oricon周榜':
          idx = 10
          break
        case 'Hit FM Top榜':
          idx = 9
          break
        case '台湾Hito排行榜':
          idx = 20
          break
        case 'Beatport全球电子舞曲榜':
          idx = 21
          break
        case '法国 NRJ Vos Hits 周榜':
          idx = 20
          break
        case '云音乐国电榜':
          idx = 4
          break
        case 'UK排行榜周榜':
          idx = 5
          break
        case '美国Billboard周榜':
          idx = 6
          break
        case '云音乐古典音乐榜':
          idx = 24
          break
        case '云音乐电音榜':
          idx = 25
          break
        case '抖音排行榜':
          idx = 26
          break
        case '新声榜':
          idx = 27
          break
        case '云音乐韩语榜':
          idx = 28
          break
        case '英国Q杂志中文版周榜':
          idx = 29
          break
        case '电竞音乐榜':
          idx = 30
          break
        case '云音乐欧美热歌榜':
          idx = 31
          break
        case '云音乐欧美新歌榜':
          idx = 32
          break
        case '说唱TOP榜':
          idx = 33
          break
      }
      this.$emit('showIdxPage', idx)
    }
  }
}
</script>

<style lang='less' scoped>
@import url("//at.alicdn.com/t/font_1396631_tp8pq8axas.css");
@import url("~styles/global.less");
.img-card {
  position: relative;
  background-color: #fff;
  .time-tag {
    position: absolute;
    bottom: 0.54rem;
    left: 3px;
    font-size: 12px;
    color: #fff;
  }
  .tag {
    position: absolute;
    z-index: 1;
    top: 0.11rem;
    right: 0.11rem;
    font-size: 0.2rem;
    color: #fff;
    .cardbofang {
      font-size: 0.18rem;
    }
  }
  .swiper-tag {
    position: absolute;
    bottom: 1rem;
    right: 0.11rem;
    .cardbofang1 {
      font-size: 1rem;
      color: #fff;
      opacity: 0.6;
    }
  }
  .fine-tag {
    position: absolute;
    transform: rotate(-45deg);
    top: 0;
    left: 0;
    .cardhuangguan {
      color: #f39c12;
      font-size: 0.5rem;
    }
  }
  .img-con {
    position: relative;
    height: 0;
    background-color: #aaa;
    border-radius: 0.2rem;
    overflow: hidden;
    .shadow {
      position: absolute;
      height: 2.1rem;
      width: 100%;
      box-shadow: 0 15px 21px -9px #777 inset;
    }
    .dj-name {
      position: absolute;
      bottom: 8px;
      left: 8px;
      color: #fff;
      font-size: 0.2rem;
      width: 100%;
      .ellipsis();
    }
    img {
      width: 100%;
    }
  }
  .dec {
    margin-top: 0.2rem;
    font-size: 0.23rem;
    line-height: 0.3rem;
    letter-spacing: 1px;
    &.lines {
      .ellipsis();
      height: 0.3rem;
    }
    &.twoLines {
      height: 0.6rem;
      .twoLinesEllipsis();
    }
  }
  .artists {
    .ellipsis();
    .artist {
      font-size: 0.2rem;
      color: #aaa;
      &::after {
        content: "/";
      }
      &:last-child::after {
        content: "";
      }
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/interchangeable.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-02 13:36:42
 * @Update: 2019-11-16 13:54:29
 * @Update log: 这是一个公共的组件,用来展示搜索展示页面除单曲以外的项目
 * 通过props接收对应的结果,渲染。
 * 组件调用是通过 for 循环组件,来循环组件渲染的
 -->
<template>
  <div class="list-item">
    <!-- 左边的图片展示区域,用不同的类名展示不同的效果 -->
    <!-- 在歌单 视频 用户 等三种的图片展示样式不同 -->
    <div
      class="img-info"
      :class="{ bigImg: videoList,
                smallImg: songList,
                circle,
                album,
                dj
                }"
    >
      <!-- 视频的播放量 -->
      <span class="count" v-if="playTime">
        <i class="result bofang1"></i>
        {{playTime | numRule}}
      </span>
      <!-- 图片链接 -->
      <img :src="ImgUrl + `?param=${videoList ? 200 : 100}y${videoList ? 120 : 100}`" alt />
    </div>
    <!-- 右侧的相关文字信息 -->
    <div class="info-content">
      <!-- 标题文字 -->
      <div
        class="play-name"
        :style="{maxWidth}"
        :class="{oneLine: line === 'one', twoLines: line === 'two'}"
      >
        <span v-if="isMv" class="mv">MV</span>
        <span v-html="name"></span>
        <span class="alia" v-show="alia">({{alia}})</span>
        <!-- 在用户区域用类名显示不同的用户性别图标 -->
        <i
          class="result"
          :class="{
                  nan: gender === 1,
                  nv: gender === 2
                  }"
          v-if="gender"
        ></i>
      </div>
      <!-- 右侧信息说明文字 由于各项的展示方式不同,所以分开编写 -->
      <div class="play-tag">
        <!-- 作者 -->
        <p class="user">{{nickname}}</p>
        <!-- 电台展示 -->
        <p class="dj" v-if="dj">
          <span class="dj-art">{{nicknames.nickname}}</span>
        </p>
        <!-- 专辑展示 -->
        <p class="album" v-if="artists">
          <span>
            <span class="album-art" v-for="(item, index) in artists" :key="index">{{item.name}}</span>
          </span>
          <span class="time">{{durationms | setYear}}</span>
        </p>
        <!-- 视频展示 -->
        <p class="video-show" v-if="videoList">
          <span class="time">
            {{durationms | setTime}}
            <span>
              by
              <span
                class="video-art"
                v-for="(item, index) in nicknames"
                :key="index"
              >{{item.userName}}</span>
            </span>
          </span>
        </p>
        <!-- 歌单列表展示 -->
        <p class="song-list" v-if="songList">
          <span class="song-num">{{trackCount}}首</span>
          <span class="song-art">by {{nickname}},</span>
          <span class="play-count">播放{{playCount | numRule}}次</span>
        </p>
      </div>
    </div>
    <!-- 展示歌手是否已入驻 -->
    <span class="artist-is-in" v-if="isIn">
      <i class="result yonghufangkeshu"></i> 已入驻
    </span>
  </div>
</template>

<script>
import { filterSetPlayCount, filterSetTime, filterSetYear } from 'utils/filters'
export default {
  name: '',
  props: {
    videoList: {
      type: Boolean,
      default: false
    },
    isMv: {
      type: Boolean,
      default: false
    },
    gender: {
      type: Number,
      default: 0
    },
    artists: {
      type: Array
    },
    line: {
      type: String
    },
    maxWidth: {
      type: String
    },
    dj: {
      type: Boolean,
      default: false
    },
    album: {
      type: Boolean,
      default: false
    },
    isIn: {
      type: Number
    },
    circle: {
      type: Boolean,
      default: false
    },
    playTime: {
      type: Number
    },
    durationms: {
      type: Number
    },
    nicknames: {
      type: [Array, Object]
    },
    songList: {
      type: Boolean,
      default: false
    },
    name: {
      type: String
    },
    alia: {
      type: String
    },
    trackCount: {
      type: Number
    },
    nickname: {
      type: String
    },
    playCount: {
      type: Number
    },
    title: {
      type: String
    },
    ImgUrl: {
      type: String
    },
    more: {
      type: Boolean
    },
    moreText: {
      type: String
    }
  },
  filters: {
    /**
     * 将播放次数进行格式转换
     */
    numRule: function (value) {
      return filterSetPlayCount(value)
    },
    /**
     * 将毫秒数转换为正常的时间
     *  212245 ==> 03:32
    */
    setTime: function (value) {
      return filterSetTime(value)
    },
    /**
     * 将毫秒转换为 年月日
     */
    setYear: function (value) {
      return filterSetYear(value)
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
.list-item {
  margin-top: 0.26rem;
  width: 100%;
  height: 1.6rem;
  position: relative;
  display: flex;
  align-items: center;
  overflow: hidden;
  .img-info {
    position: relative;
    box-sizing: border-box;
    height: 0;
    background-color: #ccc;
    border-radius: 0.1rem;
    img {
      overflow: hidden;
      border-radius: 0.1rem;
    }
    &.bigImg {
      width: 2.8rem;
      padding-bottom: 1.4rem;
      img {
        width: 2.8rem;
        height: 1.4rem;
      }
    }
    &.smallImg {
      width: 1.6rem;
      padding-bottom: 1.6rem;
      img {
        width: 100%;
      }
    }
    &.circle {
      width: 1.3rem;
      height: 1.3rem;
      border-radius: 50%;
      overflow: hidden;
      img {
        width: 100%;
      }
    }
    &.album {
      width: 1.6rem;
      padding-bottom: 1.6rem;
      img {
        width: 100%;
      }
    }
    &.dj {
      width: 1.6rem;
      padding-bottom: 1.6rem;
      img {
        width: 100%;
      }
    }
    .count {
      position: absolute;
      color: #fff;
      right: 5px;
      top: 5px;
      font-size: 0.13rem;
      .bofang1 {
        font-size: 0.13rem;
      }
    }
  }
  .info-content {
    margin-left: 0.23rem;
    .play-name {
      line-height: 0.4rem;
      .mv {
        box-sizing: border-box;
        padding: 0.01rem 0.05rem;
        border: 1px solid @bgcolor;
        font-size: 0.2rem;
        font-weight: 700;
        color: @bgcolor;
      }
      .alia {
        color: #7c7b7d;
      }
      &.oneLine {
        max-width: 3.8rem;
        .ellipsis();
      }
      &.twoLines {
        .twoLinesEllipsis();
      }
      .nan {
        color: #00cec9;
      }
      .nv {
        color: #fd79a8;
      }
    }
    .play-tag {
      max-width: 5rem;
      height: 0.5rem;
      line-height: 0.5rem;
      font-size: 0.2rem;
      color: #666;
      .user {
        .ellipsis();
      }
      .album-art {
        &::after {
          content: "/";
        }
        &:last-child::after {
          content: "";
        }
      }
      .time {
        margin-left: 0.13rem;
      }
      .song-num {
        margin-right: 0.13rem;
      }
      .song-art {
        margin-right: 0.13rem;
      }
    }
  }
  .artist-is-in {
    margin-left: auto;
    .yonghufangkeshu {
      color: @bgcolor;
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/loading.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-17 17:10:07
 * @Update: 2019-08-17 18:46:40
 * @Update log: 页面加载样式
 -->
<template>
  <div>
    <transition>
      <div class="loading" v-show="isLoading">
        <p></p>
      </div>
    </transition>
  </div>
</template>

<script>
export default {
  name: 'alert',
  props: {
    isLoading: {
      type: Boolean
    }
  }
}
</script>

<style lang="less" scoped>
@keyframes rotate {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}
.loading {
  position: fixed;
  bottom: 5rem;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.13rem;
  font-size: smaller;
  border-radius: 8px;
  p {
    width: 50px;
    height: 50px;
    border: 3px solid #ccc;
    border-top-color: #666;
    border-radius: 50%;
    margin: 0 auto;
    animation: rotate 1s linear infinite;
  }
}
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s;
}
.v-enter,
.v-leave-to {
  opacity: 0;
}
</style>


================================================
FILE: musicPlayer/src/base/loginPageIsShow.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-10-11 07:56:48
 * @Update: 2019-10-11 08:10:27
 * @Update log: 更新日志
 -->
<template>
  <div>
    <transition name="mask-show">
      <div class="mask" v-show="loginPage" @click="HIDE_LOGIN" @touchmove.prevent></div>
    </transition>
    <transition name="login-show" mode="out-in">
      <login v-show="loginPage" @touchmove.prevent></login>
    </transition>
  </div>
</template>

<script>
import login from '../pages/nav/components/login'
import { mapGetters, mapMutations } from 'vuex'

export default {
  name: '',
  components: {
    login
  },
  computed: {
    ...mapGetters({ loginPage: 'LOGIN_PAGE' })
  },
  methods: {
    ...mapMutations(['HIDE_LOGIN'])
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");

// 遮罩层动画
.mask-show-enter,
.mask-show-leave-to {
  opacity: 0;
}

.mask-show-enter-active,
.mask-show-leave-active {
  transition: opacity linear 0.3s;
}
// 左侧侧边栏显示隐藏动画
.login-show-enter,
.login-show-leave-to {
  transform: translateX(-6rem);
}

.login-show-enter-active,
.login-show-leave-active {
  transition: transform linear 0.3s;
}
</style>


================================================
FILE: musicPlayer/src/base/pageErrorInfo.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-05 09:21:33
 * @Update: 2019-10-03 20:01:31
 * @Update log: 搜索展示页如果没有搜索结果展示的信息页面
 -->
<template>
  <div v-if="info" class="info">未找到与"{{keywords}}"相关的内容</div>
</template>

<script>
export default {
  name: '',
  props: {
    info: {
      type: Boolean,
      default: false
    },
    keywords: {
      type: String
    }
  }
}
</script>

<style lang='less' scoped>
.info {
  text-align: center;
  line-height: 1rem;
}
</style>


================================================
FILE: musicPlayer/src/base/pageLoading.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-31 10:19:23
 * @Update: 2019-10-24 08:50:52
 * @Update log: 展示页面加载动画
 -->
<template>
  <div class="container">
    <div class="page-loading">
      <span class="span"></span>
      <span class="span"></span>
      <span class="span"></span>
      <span class="span"></span>
    </div>努力加载中...
  </div>
</template>

<script>
export default {
  name: 'pageLoading'
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
.container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 1rem;
  line-height: 1rem;
  z-index: 999;
}
.page-loading {
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
  justify-content: space-between;
  width: 0.5rem;
  margin-right: 0.2rem;
  .span {
    width: 0.05rem;
    height: 0.2rem;
    background-color: @bgcolor;
  }

  .span:nth-of-type(1) {
    animation: grow 1s -0.45s ease-in-out infinite;
  }

  .span:nth-of-type(2) {
    animation: grow 1s -0.3s ease-in-out infinite;
  }

  .span:nth-of-type(3) {
    animation: grow 1s -0.15s ease-in-out infinite;
  }

  .span:nth-of-type(4) {
    animation: grow 1s ease-in-out infinite;
  }

  @keyframes grow {
    0%,
    100% {
      transform: scaleY(1);
    }

    50% {
      transform: scaleY(2);
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/base/searchInput.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-08-27 12:42:24
 * @Update: 2019-11-14 13:38:05
 * @Update log: 搜索框
 -->
<template>
  <div class="wrapper pd23">
    <!-- 左边的返回箭头 -->
    <i class="iconfont zuojiantou" @click="returnPage"></i>
    <!-- input 框,设置使得 input 页面加载input自动聚焦 -->
    <!-- 聚焦后显示搜索建议 -->
    <input
      class="search"
      type="text"
      :placeholder="placeholder"
      ref="inp"
      autofocus="autofocus"
      v-model.trim="keywords"
      @focus="displayList"
    />
    <!-- 通过观测输入框中是否有内容用来控制右侧的叉按钮是否显示 -->
    <!-- 为叉按钮定义点击事件,点击清空输入框 -->
    <i v-show="keywords" @click="clearInp" class="iconfont guanbi" :style="{right: Right}"></i>
    <i class="iconfont geshou" v-if="page" @click="goSingerPage"></i>
    <!-- 搜索建议列表信息 -->
    <div class="floatInfo" v-show="showList">
      <ul>
        <li @click="searchKey(keywords)" class="blue border-bottom">
          搜索
          <span class="text">"{{ keywords }}"</span>
        </li>
        <li
          @click="searchKey(item.keyword)"
          class="border-bottom"
          v-for="(item, index) in searchList"
          :key="index"
        >
          <i class="iconfont sousuo"></i>
          {{ item.keyword }}
        </li>
      </ul>
    </div>
    <!-- 蒙层,当搜索建议显示,蒙层显示,控制列表不能滚动 -->
    <div class="mask" v-show="showList" @click="hideList"></div>
  </div>
</template>

<script>
import api from 'api'
import Bus from '../assets/Bus'
export default {
  name: 'searchInp',
  props: {
    page: {
      type: String
    },
    Right: {
      default: '0.23rem'
    },
    keyword: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      searchList: [],
      keywords: '',
      showList: false,
      // 将 history 存入 vuex
      history: [],
      // 防抖定时器
      time: null,
      placeholder: ''
    }
  },
  created () {
    // 获取焦点
    this.changFocus()
    // 先将默认搜索建议显示
    this._getPlaceholder()
    // 历史记录项点击搜索
    this.historySearch()
    // 页面首次加载,由于 keyword 没有被watch监听,所以使用函数方法进行赋值
    this.setKeyword()
  },
  mounted () {
    // 获取历史搜索记录
    this.getHistory()
  },
  watch: {
    /**
     * 是否显示搜索建议
     */
    keywords: function (val, oldVal) {
      // 这是对于输入框内容定义的事件,当是跳转过来的
      // 说明内容相等,不显示搜索建议列表
      if (this.keywords === this.keyword) {
        this.hideList()
        return
      }
      // 在内容变化时,并且当内容长度大于0 说明有内容时
      if (this.keywords.length > 0) {
        // 显示建议列表
        this.displayList()
      } else {
        // 隐藏建议列表
        this.hideList()
      }
    },
    // 对于prop传过来的值,在第一次使用方法进行修改,随后监听keyword变化,对搜索内容进行修改
    keyword: function (val, oldVal) {
      if (val) {
        this.keywords = val
      }
    }
  },
  methods: {
    /**
     * 第一次访问需要调用方法更改数据
     * 随后是监听 keyword 改变后赋值
     */
    setKeyword () {
      if (this.keyword) {
        this.keywords = this.keyword
      }
    },
    /**
     * 点击歌手分类图标,跳转到歌手分类页面
     */
    goSingerPage () {
      this.$router.push('/singer')
    },
    /**
     * 历史记录项目点击搜索
     */
    historySearch () {
      Bus.$on('search', (keywords) => {
        this.searchKey(keywords)
      })
    },
    /**
     * 点击清除按钮清除搜索框的内容
     */
    clearInp () {
      this.keywords = ''
    },
    /**
     * 自动获取焦点
     * 当是搜索展示页时不自动获取焦点
     */
    changFocus () {
      if (!this.keyword) {
        this.$nextTick(x => {
          this.$refs.inp.focus()
        })
      }
    },
    /**
     * 返回上一页
     */
    returnPage () {
      this.$router.go(-1)
    },
    /**
     * 设置输入框的默认显示
     */
    _getPlaceholder () {
      api.defaultSearchFn()
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.placeholder = data.data.showKeyword
          }
        })
    },
    /**
     * 隐藏搜索建议列表
     */
    hideList () {
      this.showList = false
    },
    /**
     * 显示搜索列表建议
     */
    displayList () {
      if (!this.keywords) {
        return
      }
      this.showList = true
      // 搜索建议列表内容获取
      this.setSearchList(this.keywords)
    },
    /**
     * 请求搜索建议数据
     */
    _getSuggestList (keyword) {
      api.suggestSearchFn(keyword)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.searchList = data.result.allMatch
          }
        })
    },
    /**
     * 根据搜索内容展示搜索建议列表
     * 使用防抖
     */
    setSearchList (keywords) {
      if (this.time) {
        clearTimeout(this.time)
        this.time = null
      }
      this.time = setTimeout(() => {
        this._getSuggestList(keywords)
      }, 50)
    },
    /**
     * 获取历史搜索记录
     */
    getHistory (key) {
      let keys = localStorage.getItem('keys') ? localStorage.getItem('keys').split(',') : []
      if (key) {
        // 将关键字插入到数组最前面
        keys.unshift(key)
        // 存入本地之前进行去重
        keys = this.unique(keys)
        // 存入本地
        localStorage.setItem('keys', keys)
      }
      this.history = keys
      // 通过Bus 进行兄弟组件之间传值
      // 通过 Bus.$emit('方法名',要传的值)
      Bus.$emit('history', this.history)
    },
    /**
     * 向导航标签传递key值
     */
    pushKey (key) {
      this.$nextTick(() => {
        // DOM 现在更新了
        Bus.$emit('push', key)
      })
    },
    /**
     * 搜索
     * 搜索功能跳转到搜索展示页面
     */
    searchKey (key) {
      this.getHistory(key)
      this.hideList()
      this.clearInp()
      // 这里解决了Bus传值第一次无法获取到的问题
      // 后需解决!!!!
      setTimeout(() => {
        this.pushKey(key)
      }, 0)
      this.$router.push({
        path: `/composite/${key}`
      })
    },
    /**
     * 数组去重
     */
    unique (arr) {
      if (!Array.isArray(arr)) {
        console.log('type error!')
        return
      }
      return Array.prototype.filter.call(arr, function (item, index) {
        return arr.indexOf(item) === index
      })
    },
    beforeDestroy () {
      // 销毁监听事件
      this.$Bus.$off('push', 'history')
    }
  }
}
</script>

<style lang="less" scoped>
@import url("~styles/global.less");
@import url("//at.alicdn.com/t/font_1379594_vh7eh105cbo.css");
.wrapper {
  .flex-between();
  height: 0.7rem;
  line-height: 0.7rem;
  .guanbi {
    position: absolute;
    right: 0.23rem;
  }
  .geshou {
    margin-left: 0.3rem;
  }
  .iconfont {
    font-size: 0.5rem;
  }
  .search {
    flex: 1;
    margin-left: 0.3rem;
    border-bottom: 1px solid #aaa;
  }
  .floatInfo {
    width: 5.7rem;
    position: absolute;
    top: 0.8rem;
    box-shadow: 0 4px 16px #aaa;
    background-color: #fff;
    z-index: 2;
    .pd23();
    li {
      height: 0.8rem;
      line-height: 0.8rem;
      color: #888;
      .text {
        margin-left: 8px;
      }
      .iconfont {
        font-size: 0.4rem;
        vertical-align: -0.04rem;
      }
    }
    .blue {
      color: #38f;
    }
  }
  .mask {
    position: fixed;
    top: 0.7rem;
    left: 0;
    bottom: 0;
    right: 0;
    z-index: 1;
    opacity: 0;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/shouldLogin.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-10-11 07:51:34
 * @Update: 2019-10-11 08:12:58
 * @Update log: 更新日志
 -->
<template>
  <div class="center">
    <p class="m3">当前未登录,请您跳转登陆页面进行登陆</p>
    <router-link to="/login">点我去登陆</router-link>
  </div>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  name: '',
  methods: {
    ...mapMutations(['SHOW_LOGIN'])
  }
}
</script>

<style lang='less' scoped>
.center {
  text-align: center;
  .m3 {
    margin: 0.3rem 0;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/slider.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-27 12:49:09
 * @Update: 2019-11-29 13:18:49
 * @Update log: 更新日志
 -->
<template>
  <div>
    <van-action-sheet v-model="drawer" class="van-ellipsis" :title="title">
      <div>
        <p v-for="(item, index) in data" :key="index" class="item pd23 border-bottom">
          <a class="cover" @click="itemHandle(item.text, id)"></a>
          <i class="slider" :class="item.icon"></i>
          {{item.text}}
        </p>
      </div>
    </van-action-sheet>
  </div>
</template>

<script>
import api from 'api'
import { Dialog, Toast } from 'vant'
export default {
  data () {
    return {
      drawer: false,
      direction: 'btt',
      show: false,
      homePlaylist: [{
        icon: 'sliderxiazai',
        text: '下载'
      }, {
        icon: 'sliderfenxiang',
        text: '分享'
      }, {
        icon: 'sliderbianji',
        text: '编辑歌单信息'
      }, {
        icon: 'slidericonfont-shanchu',
        text: '删除'
      }],
      homeFavoritelist: [{
        icon: 'sliderxiazai',
        text: '下载'
      }, {
        icon: 'sliderfenxiang',
        text: '分享'
      }, {
        icon: 'slidericonfont-shanchu',
        text: '删除'
      }],
      djDetail: [{
        icon: 'sliderxiazai',
        text: '下载'
      }, {
        icon: 'sliderpinglun',
        text: '评论'
      }, {
        icon: 'sliderfenxiang',
        text: '分享'
      }, {
        icon: 'sliderjubao',
        text: '举报'
      }]
    }
  },
  props: {
    title: {
      type: String
    },
    author: {
      type: String
    },
    imgUrl: {
      type: String
    },
    id: {
      type: Number
    },
    homePlaylistSlider: {
      type: Boolean,
      default: false
    },
    homeFavoritelistSlider: {
      type: Boolean,
      default: false
    },
    djDetailList: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    data: function () {
      return this.homePlaylistSlider ? this.homePlaylist : this.homeFavoritelistSlider ? this.homeFavoritelist : this.djDetailList ? this.djDetail : []
    },
    size: function () {
      return this.data.length * 10 + '%'
    }
  },
  methods: {
    /**
     * 删除此项
     */
    deleteItem (id) {
      this.drawer = false
      this.$dialog.confirm({
        message: '确认删除此歌单吗?'
      }).then(_ => {
        if (this.homeFavoritelistSlider) {
          // 取消收藏此歌单
          this._cancelPlayList(id)
        } else {
          // 删除创建的歌单
          this._deleteCreatedList(id)
        }
      })
        .catch(_ => {
          this.drawer = false
        })
    },
    /**
     * 取消收藏
     */
    _cancelPlayList (id) {
      api.addOrDeletePlaylistFn(2, id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            Toast({
              position: 'bottom',
              message: '已删除'
            })
          }
        })
        .catch(err => {
          console.log(err)
        })
    },
    /**
     * 删除歌单
     */
    _deleteCreatedList (id) {
      api.deletePlaylistFn(id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            Toast({
              position: 'bottom',
              message: '已删除'
            })
          }
        })
        .catch(err => {
          console.log(err)
        })
    },
    commentsItem (id) {
      const djId = id
      const title = this.title
      const author = this.author
      const imgUrl = this.imgUrl
      this.$router.push({ name: 'comments', params: { djId, imgUrl, title, author } })
    },
    showSlider () {
      console.log('kkk')
      this.drawer = true
    },
    itemHandle (name, id) {
      switch (name) {
        case '删除':
          this.deleteItem(id)
          break
        case '评论':
          this.commentsItem(id)
          break
        default:
          break
      }
    }
  },
  components: {
    [Dialog.Component.name]: Dialog.Component
  }
}
</script>

<style lang="less">
@import url("~styles/global");
@import url("//at.alicdn.com/t/font_1439316_22v2kqpozkoh.css");
.item {
  height: 0.9rem;
  line-height: 0.9rem;
  .slider {
    margin-right: 0.2rem;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/sliderNav.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-10-01 14:30:50
 * @Update: 2019-10-29 13:07:19
 * @Update log: 可左右滑动的标签导航
 -->
<template>
  <div class="wrapper pd23" ref="navs">
    <!-- 使用 replace 它不会向 history 添加新记录 -->
    <router-link
      class="nav-list"
      :style="{minWidth}"
      v-for="(item, index) in list"
      :key="index"
      :to="item.path"
      replace
    >{{ item.text }}{{item.num | empty}}</router-link>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    list: {
      type: Array
    },
    minWidth: {
      type: String,
      default: '1.2rem'
    }
  },
  filters: {
    empty: function (val) {
      if (val) {
        return ' ' + val
      } else {
        return ''
      }
    }
  },
  methods: {
    /**
     * 该方法用来滚动跳转到对应的标签项,
     * 传入要跳转标签项的index值和每个标签项的宽度来使得标签行滚动
     */
    handleScroll (index, num) {
      this.$refs.navs.scrollLeft = index * num
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
.wrapper {
  display: flex;
  margin-top: 0.16rem;
  overflow-x: auto;
  flex-wrap: nowrap;
  border-bottom: 1px solid #ddd;
  .nav-list {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 0.7rem;
    box-sizing: border-box;
    padding: 0.03rem;
    margin: 0 0.2rem;
    color: #000;
    &::before {
      content: "";
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 2px;
      background-color: @bgcolor;
      transform-origin: bottom right;
      transform: scaleX(0);
      transition: transform 0.5s ease;
    }
    &.ac {
      color: @bgcolor;
    }
    &.ac::before {
      transform-origin: bottom left;
      transform: scaleX(1);
    }
  }
}
::-webkit-scrollbar {
  display: none;
}
</style>


================================================
FILE: musicPlayer/src/base/song.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-07 12:09:22
 * @Update: 2019-11-08 13:45:23
 * @Update log: 更新日志
 -->
<template>
  <div class="list-item" @click="startSong">
    <div class="img-info" v-if="imgUrl">
      <img v-lazy="imgUrl + '?param=50y50'" :key="imgUrl" v-show="!nowSong" />
      <i class="result yinliang" v-show="nowSong"></i>
    </div>
    <div class="index" v-if="num">
      <span v-show="!nowSong">{{ num }}</span>
      <i class="result yinliang" v-show="nowSong"></i>
    </div>
    <div class="song-info">
      <p class="song-name" :class="{twoLine}">
        <!-- {{songName | setKeyWords}} -->
        <!-- 注意 如果使用 v-html 显示内容可能会把子节点内容覆盖 -->
        <span v-html="songName"></span>
        <span class="alia" v-show="alia">({{alia}})</span>
      </p>
      <p class="song-art" v-if="type==='songList'">
        <span>
          <span class="artist" v-for="(item, index) in artists" :key="index">{{ item.name }}</span>
        </span>
        <span class="album-name">{{ albumName }}</span>
      </p>
      <p class="dj-info" v-if="type==='djList'">
        <span class="data">{{createTime | setMonth}}</span>
        <span class="count">
          <i class="result bofang1"></i>
          {{listenerCount | setNum}}
        </span>
        <span class="time">
          <i class="result shijian"></i>
          {{duration | setTime}}
        </span>
      </p>
    </div>
    <div class="icon" @click.stop="showSlider(itemId)">
      <i class="result diandiandian"></i>
    </div>
  </div>
</template>

<script>
import { filterSetMonth, filterSetPlayCount, filterSetTime } from 'utils/filters'

export default {
  name: '',
  props: {
    songName: {
      type: String
    },
    artists: {
      type: Array
    },
    albumName: {
      type: String
    },
    alia: {
      type: String
    },
    imgUrl: {
      type: String
    },
    num: {
      type: Number
    },
    type: {
      type: String,
      default: 'songList'
    },
    itemId: {
      type: Number
    },
    createTime: {
      type: Number
    },
    listenerCount: {
      type: Number
    },
    duration: {
      type: Number
    },
    nowSong: {
      type: Boolean,
      default: false
    },
    twoLine: {
      type: Boolean
    },
    keywords: {
      type: String
    }
  },
  filters: {
    setMonth: function (value) {
      return filterSetMonth(value)
    },
    setNum: function (value) {
      return filterSetPlayCount(value)
    },
    setTime: function (value) {
      return filterSetTime(value)
    }
  },
  methods: {
    startSong () {
      this.$emit('beginSong')
    },
    showSlider (id) {
      this.$emit('showSlider', id)
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
@import url("//at.alicdn.com/t/font_1380711_cftenqb5flc.css");

.list-item {
  .flex-between();
  align-items: center;
  height: 1.2rem;
  .index {
    color: #999;
  }
  .yinliang {
    color: @bgcolor;
  }
  .img-info {
    width: 0.7rem;
    height: 0;
    padding-bottom: 0.7rem;
    margin-right: 0.2rem;
    position: relative;
    img {
      border-radius: 0.1rem;
      width: 0.7rem;
      height: 0.7rem;
    }
    .yinliang {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate3d(-50%, -50%, 0);
    }
  }
  .song-info {
    display: flex;
    flex-direction: column;
    .song-name {
      width: 75vw;
      max-height: 0.4rem;
      line-height: 0.4rem;
      .ellipsis();
      .alia {
        color: #7c7b7d;
      }
      &.twoLine {
        max-height: 0.8rem;
        white-space: normal;
        .twoLinesEllipsis();
      }
    }
    .song-art {
      height: 0.4rem;
      line-height: 0.4rem;
      font-size: 0.23rem;
      color: #dacdcd;
      width: 75vw;
      .ellipsis();
      .artist {
        &::after {
          content: "/";
        }
        &:last-child::after {
          content: "";
        }
      }
      .album-name {
        &::before {
          content: "-";
        }
      }
    }
    .dj-info {
      font-size: 0.2rem;
      line-height: 1.5;
      color: #ccc;
      .result {
        font-size: 0.2rem;
      }
      .data,
      .count {
        margin-right: 0.2rem;
      }
    }
  }
  .icon {
    color: #ccc;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/songListPage/index.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-06 11:33:42
 * @Update: 2020-05-04 17:41:34
 * @Update log: 这是一个用来展示歌曲列表的基础组件
 -->
<template>
  <!-- 页面需要监听滚动事件,滚动到某个位置时标题栏要固定 -->
  <!-- 这里需要增加动态的改变样式信息,不是只有显示隐藏!!!!!!!!! -->
  <div class="wrapper" @scroll="scrollList">
    <!-- 由于歌单页和今日推荐页面的顶部展示区域高度不同,所以通过动态的 height 进行设置 -->
    <div class="container-top" :style="{height}">
      <!-- 通过传值 isAlbum 的布尔值进行判断,因为在今日推荐页面的页面标题是通过滚动显示隐藏的 -->
      <global-nav class="fixed pd23" v-if="!isAlbum" @returnPage="returnPage">
        <!-- 通过改变 listFixed 来控制 title 的显示与否-->
        <span class="text" v-show="listFixed">{{iTitle}}</span>
      </global-nav>
      <!-- 这里是在歌单页面时,页面标题是一直显示的 -->
      <global-nav class="fixed pd23" v-if="isAlbum" @returnPage="returnPage">
        <span class="text">{{iTitle}}</span>
      </global-nav>
      <!-- 这里包裹的是每日推荐页面额外显示的日期信息 -->
      <div class="pd23" v-if="!isAlbum">
        <div class="date">
          <span class="day">{{day}}</span>
          <span class="month">{{month}}</span>
        </div>
        <div class="info">查收属于您的今日推荐</div>
      </div>
      <!-- 这里包裹的是歌单页面的图片,作者,介绍等信息 -->
      <div class="album-info pd23" v-if="isAlbum">
        <div class="info-top">
          <div class="img-info">
            <img v-lazy="imgUrl + '?param=200y200'" :key="imgUrl" alt />
            <span class="play-count">
              <i class="date-song bofang"></i>
              {{playCount | setPlay}}
            </span>
          </div>
          <div class="info-con">
            <p class="album-title">{{iAlbumTitle}}</p>
            <div class="creator">
              <div class="img-info">
                <img v-lazy="creatorImgUrl + '?param=200y200'" :key="imgUrl" alt />
              </div>
              <span class="authorName">
                <div class="authorNameCon ellipsis">{{author}}</div>
                <i class="date-song iconfontjiantou5"></i>
              </span>
            </div>
            <div class="desc-wrapper">
              <span class="desc">{{description}}</span>
              <i class="date-song iconfontjiantou5"></i>
            </div>
          </div>
        </div>
        <div class="icons">
          <div class="comments" @click="goComments">
            <i class="date-song pinglun"></i>
            <span>{{commentCount | setCom}}</span>
          </div>
          <div class="comments">
            <i class="date-song fenxiang"></i>
            <span>{{shareCount | setShare}}</span>
          </div>
          <div class="comments">
            <i class="date-song xiazai"></i>
            <span>下载</span>
          </div>
          <div class="comments">
            <i class="date-song duoxuankuang"></i>
            <span>多选</span>
          </div>
        </div>
      </div>
    </div>
    <!-- 这里是 播放全部 那一行的信息,因为要操作是否固定,所以需要单独设置 -->
    <div class="title pd23" :class="{listFixed}">
      <span>
        <span @click="beginAudio">
          <i class="date-song cbofang"></i>
          播放全部
        </span>
        <!-- 当歌单组件时,需要显示当前歌单总共有多少首歌曲的信息 -->
        <span class="count" v-if="isAlbum">(共{{trackCount}}首)</span>
      </span>
      <!-- 当在每日推荐界面时,这里显示的是一个多选按钮 -->
      <!-- 这里的多选需要设置一个多选页面的组件!!!!!!!!! -->
      <span v-if="!isAlbum">
        <i class="date-song caidan"></i>
        多选
      </span>
      <!-- 当时歌单组件时有收藏歌单的按钮选项 -->
      <!-- 这里需要添加判断用户是否已经收藏歌单!!!!来显示不同的样式 -->
      <div class="collection" :class="{ 'bg': !isSubInItem }" ref="collection" v-if="isAlbum">
        <span v-show="!isSubInItem" @click="addPlaylist">+ 收藏({{subscribedCountItem | setCol}})</span>
        <span v-show="isSubInItem" @click="deletePlaylist">
          <i class="date-song wenjianjia"></i>
          {{subscribedCountItem | setCol}}
        </span>
      </div>
    </div>
    <!-- 这里将列表进行包裹统一的通过 load 属性进行判断是否展示 -->
    <div class="list-info" v-show="!load" :style="{ marginTop: top}">
      <slot></slot>
    </div>
    <!-- 页面 loading 组件 -->
    <page-loading v-show="load"></page-loading>
  </div>
</template>

<script>
import globalNav from 'base/generalNav'
import pageLoading from 'base/pageLoading'
import api from 'api'

const setNum = function (val) {
  if (!val) {
    return ''
  }
  if (val > 100000000) {
    val = ((val / 100000000).toFixed(1)) + '亿'
  } else if (val > 10000) {
    val = ((val / 10000).toFixed(1)) + '万'
  }
  return val
}

export default {
  name: '',
  components: {
    globalNav,
    pageLoading
  },
  /**
   * 这里使用 data 把 props 接收的值进行存储,因为 vue 不允许子组件直接修改父组件传过来的值
   */
  data () {
    return {
      iTitle: this.title,
      iAlbumTitle: this.albumTitle,
      listFixed: false,
      top: '0.5rem',
      isSubInItem: false,
      subscribedCountItem: 0
    }
  },
  /**
   * 当已经加载了一次页面后,再次进入页面时标题信息不能更新
   *
   * 这里需要监听 props 的改变,如果改变了将新值接受进行改变
   */
  watch: {
    title: function (val) {
      this.iTitle = val
    },
    albumTitle: function (val) {
      this.iAlbumTitle = val
    },
    subscribedCount: function (val) {
      this.subscribedCountItem = val
      if (val === 0 || !val) {
        this.$refs.collection.style.display = 'none'
      } else {
        this.$refs.collection.style.display = ''
      }
    },
    isSubIn: function (val) {
      this.isSubInItem = val
    }
  },
  /**
   * 所有的 props 值信息
   */
  props: {
    albumId: {
      type: Number
    },
    idxId: {
      type: Number
    },
    // 排行榜id,用来正确获取评论信息
    idxComId: {
      type: Number
    },
    dishId: {
      type: Number
    },
    playCount: {
      type: Number
    },
    height: {
      // 顶部展示区域高度
      type: String,
      default: '6rem'
    },
    subscribedCount: {
      type: Number
    },
    subscribed: {
      type: Boolean
    },
    trackCount: {
      type: Number
    },
    shareCount: {
      type: Number
    },
    commentCount: {
      type: Number
    },
    description: {
      type: String
    },
    author: {
      type: String
    },
    creatorImgUrl: {
      type: String
    },
    albumTitle: {
      type: String
    },
    imgUrl: {
      type: String
    },
    title: {
      type: String
    },
    isAlbum: {
      type: Boolean,
      default: true
    },
    load: {
      type: Boolean,
      default: true
    },
    isSubIn: {
      type: Boolean,
      default: false
    }
  },
  filters: {
    setCom: function (val) {
      if (!val) {
        return '评论'
      }
      return setNum(val)
    },
    setShare: function (val) {
      if (!val) {
        return '分享'
      }
      return setNum(val)
    },
    setCol: function (val) {
      if (!val) {
        return ''
      }
      return setNum(val)
    },
    setPlay: function (val) {
      if (!val) {
        return ''
      }
      return setNum(val)
    }
  },
  // 对日期信息提取展示
  computed: {
    /**
     * 返回日
     */
    day: function () {
      const day = new Date().getDate() < 10
        ? '0' + new Date().getDate()
        : new Date().getDate()
      return day
    },
    /**
     * 返回月份
     */
    month: function () {
      const month = new Date().getMonth() + 1 < 10
        ? '0' + (new Date().getMonth() + 1)
        : new Date().getMonth() + 1
      return month
    }
  },
  methods: {
    /**
     * 收藏歌单
     */
    addPlaylist () {
      const id = this.albumId || this.dishId || this.idxId
      api.addOrDeletePlaylistFn(1, id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.isSubInItem = true
            ++this.subscribedCountItem
          }
        })
    },
    /**
     * 取消收藏歌单
     */
    deletePlaylist () {
      const id = this.albumId || this.dishId || this.idxId
      api.addOrDeletePlaylistFn(2, id)
        .then(res => {
          const data = res.data
          if (data.code === 200) {
            this.isSubInItem = false
            --this.subscribedCountItem
          }
        })
    },
    /**
     * 播放全部,向父组件传递事件
     */
    beginAudio () {
      this.$emit('startPlayAll')
    },
    /**
     * 去评论页面
     * 通过传不同的params的属性来判断资源是歌单还是专辑
     */
    goComments () {
      const playlistId = this.albumId ? this.albumId : this.idxComId
      const albumId = this.dishId
      const imgUrl = this.imgUrl
      const title = this.albumTitle
      const author = this.author
      this.$router.push({ name: 'comments', params: { playlistId, albumId, imgUrl, title, author } })
    },
    /**
     * 返回上一页
     */
    returnPage () {
      this.$router.go(-1)
    },
    /**
     * 定义页面滚动事件,
     * 这里需要添加在滚动过程中样式的变化
     */
    scrollList (e) {
      // 获取到 top 值
      let top = this.$el.scrollTop
      // 当当前组件不是歌单组件时,就是每日推荐页面
      if (!this.isAlbum) {
        if (top >= 148) {
          this.listFixed = true
          this.top = '1.3rem'
        } else {
          this.listFixed = false
          this.top = '0.5rem'
        }
      } else {
        // 当是歌单组件时,当页面滚动到一定位置的时候顶部的标题会变
        if (top >= 148) {
          // 这里使用data存下了props的值进行修改,子组件不能直接修改props传过来的值
          this.iTitle = this.albumTitle
        } else {
          this.iTitle = this.title
        }
        // 当 top 到了 250 的时候会改变标题行的是否固定样式
        if (top >= 250) {
          this.listFixed = true
          this.top = '1.3rem'
        } else {
          this.listFixed = false
          this.top = '0.5rem'
        }
      }
    }
  }
}
</script>

<style lang='less' scoped>
@import url("//at.alicdn.com/t/font_1394963_t6jt71rtm9.css");
@import url("~styles/global.less");
.topFixed {
  position: fixed;
  width: 100%;
  height: 1rem;
  background-color: #ee5253;
  z-index: 9;
}
@textColor: #ccc;
.fixed {
  .topFixed();
  top: 0;
}
.listFixed {
  .topFixed();
  top: 1rem;
}

.wrapper {
  height: 100vh;
  position: relative;
  overflow: scroll;
  // 组件的title
  .title {
    font-size: 0.3rem;
    height: 1rem;
    line-height: 1rem;
    .flex-between();
    background-color: #fff;
    .count {
      color: #999;
      font-size: small;
    }
    .collection {
      .pd23();
      font-size: smaller;
      margin-top: 3px;
      height: 0.7rem;
      line-height: 0.7rem;
      border-radius: 0.4rem;
      color: #999;
      &.bg {
        background-color: @bgcolor;
        color: #fff;
      }
    }
  }
  .container-top {
    width: 100%;
    color: #fff;
    background-color: #ee5253;
    .date {
      padding-top: 1.5rem;
      .day {
        font-size: 0.7rem;
      }
      .month {
        font-size: 0.4rem;
        color: #c8d6e5;
        &::before {
          content: "/";
        }
      }
    }
    .info {
      margin-top: 0.7rem;
    }
    .text {
      font-size: 0.4rem;
      vertical-align: 5px;
      width: 8rem;
      .ellipsis();
    }
    .album-info {
      padding-top: 1.5rem;
      .info-top {
        height: 3rem;
        .flex-between();
        overflow: hidden;
        .img-info {
          @size: 2.6rem;
          width: @size;
          height: 0;
          position: relative;
          padding-bottom: @size;
          border-radius: @imgBorderRadius;
          overflow: hidden;
          img {
            width: @size;
            height: @size;
          }
          .play-count {
            position: absolute;
            top: 0.1rem;
            right: 0.1rem;
            .bofang {
              font-size: 0.24rem;
            }
          }
        }
        .info-con {
          width: 3.6rem;
          height: 2.6rem;
          display: flex;
          flex-direction: column;
          overflow: hidden;
          .album-title {
            font-size: 0.36rem;
            line-height: 1.5;
            .twoLinesEllipsis();
          }
          .creator {
            height: 1rem;
            color: @textColor;
            display: flex;
            align-items: center;
            .img-info {
              @size: 0.6rem;
              width: @size;
              height: 0;
              padding-bottom: @size;
              margin-right: 8px;
              border-radius: 50%;
              overflow: hidden;
              img {
                width: @size;
                height: @size;
              }
            }
          }
          .desc-wrapper {
            display: flex;
            align-items: center;
            color: @textColor;
            .desc {
              width: 3rem;
              .twoLinesEllipsis();
            }
          }
        }
      }
      .icons {
        margin-top: 8px;
        .flex-around();
        width: 100%;
        .comments {
          display: flex;
          justify-content: center;
          align-items: center;
          flex-direction: column;
          .date-song {
            font-size: 0.4rem;
            margin-bottom: 5px;
          }
        }
      }
    }
  }
  .list-info {
    width: 100%;
    box-sizing: border-box;
    padding: 0.1rem 0.23rem;
    background-color: #fff;
    transform: translate3d(0, -0.5rem, 0);
  }
}

.authorName {
  display: flex;
  .authorNameCon {
    max-width: 2.2rem;
  }
}
</style>


================================================
FILE: musicPlayer/src/base/titleFooter.vue
================================================
<!--
 * @Author: 李浩栋
 * @Begin: 2019-09-02 18:07:09
 * @Update: 2019-09-03 18:00:27
 * @Update log: 搜索展示页面公共的标题和底部组件
 -->
<template>
  <div class="list-global">
    <div class="title">
      <span class="title-content">{{title}}</span>
      <span class="smallTag" v-if="songList">
        <i class="result cbofang"></i>
        播放全部
      </span>
    </div>
    <slot></slot>
    <div class="more" v-if="more" @click="toLInk">
      {{moreText}}
      <i class="result youjiantou"></i>
    </div>
  </div>
</template>

<script>
export default {
  name: '',
  props: {
    title: {
      type: String
    },
    more: {
      type: Boolean,
      default: false
    },
    moreText: {
      type: String
    },
    songList: {
      type: Boolean
    },
    linkPage: {
      type: String
    },
    keyW: {
      type: String
    }
  },
  methods: {
    toLInk () {
      /**
       * 点击的时候把要跳转的页面传到state中,
       * 标签导航组件获取到当前的state。根据index值计算出要滚动的距离
       */
      this.$store.commit('SET_LINK_PAGE', this.linkPage)
      this.$router.push({
        path: `/${this.linkPage}/${this.keyW}`
      })
    }
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");

.list-global {
  margin-top: 0.36rem;
  .title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    .title-content {
      font-weight: 700;
      height: 0.3rem;
      line-height: 0.3rem;
    }
  }
  .more {
    height: 1rem;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 0.26rem;
    color: #ccc;
    .youjiantou {
      color: #aaa;
    }
  }
}
</style>


================================================
FILE: musicPlayer/src/components/detailPage/index.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-10-13 12:03:28
 * @Update: 2019-11-30 13:27:53
 * @Update log: 更新日志
 -->
<template>
  <div class="wrapper" @scroll="scrollList">
    <detail-nav @returnPage="returnPage" class="fixed pd23" style="color:#fff;">
      <span class="text">{{$title}}</span>
    </detail-nav>
    <div
      class="container-top"
      :class="{coverFixed, position}"
      :style="{backgroundImage: 'url(' + coverImgUrl + ')'}"
    >
      <div class="cover" :style="{backgroundColor: `rgba(0, 0, 0, ${cover})`}"></div>
      <div class="data" v-show="!listFixed" :style="{opacity}">
        <slot name="data">上半部区域</slot>
      </div>
    </div>
    <div :class="{listFixed}">
      <slot name="nav-list">这里显示吸顶标签行</slot>
    </div>
    <div class="container-bottom" :style="{ marginTop: top}">
      <slot name="bottom">下边展示音乐列表</slot>
    </div>
    <slot name="slider">这里是显示下方滑块</slot>
  </div>
</template>

<script>
import detailNav from 'base/generalNav'

export default {
  name: '',
  data () {
    return {
      cover: '0.2',
      listFixed: false,
      coverFixed: false,
      position: true,
      top: '0rem',
      opacity: 1,
      $title: ''
    }
  },
  props: {
    title: {
      type: String
    },
    name: {
      type: String
    },
    coverImgUrl: {
      type: String
    }
  },
  methods: {
    returnPage () {
      this.$router.go(-1)
    },
    /**
     * 定义页面滚动事件,
     * 这里需要添加在滚动过程中样式的变化
     */
    scrollList (e) {
      // 获取到 top 值
      let top = this.$el.scrollTop
      this.cover = top / 1000 + 0.3
      this.opacity = 1 - top / 500
      if (this.cover > 0.6) {
        this.cover = 0.6
        this.opacity = 0.4
      } else {
        this.cover = top / 1000 + 0.3
        this.opacity = 1 - top / 500
      }
      if (top >= 282) {
        this.$title = this.name
        this.listFixed = true
        this.coverFixed = true
        this.position = false
        this.top = '7.8rem'
      } else {
        this.$title = this.title
        this.listFixed = false
        this.coverFixed = false
        this.position = true
        this.top = '0'
      }
    }
  },
  components: {
    detailNav
  }
}
</script>

<style lang='less' scoped>
@import url("~styles/global.less");
.topFixed {
  position: fixed;
  width: 100%;
  height: 0.8rem;
  z-index: 9;
}
.fixed {
  .topFixed();
  top: 0;
}
.listFixed {
  .topFixed();
  top: 1.6rem;
}
.coverFixed {
  position: fixed;
  width: 100%;
  z-index: 3;
  top: 0;
  transform: translateY(-5.4rem);
}
.wrapper {
  width: 100vw;
  height: 100vh;
  position: relative;
  overflow-y: scroll;
  overflow-x: hidden;
  .text {
    font-size: 0.4rem;
    vertical-align: 5px;
  }
  .container-top {
    width: 100%;
    height: 0;
    padding-bottom: 7rem;
    background-size: 100%;
    background-repeat: no-repeat;
    &.position {
      position: relative;
    }
    .cover {
      position: absolute;
      left: 0;
      top: 0;
      right: 0;
      bottom: 0;
      width: 100%;
      height: 100%;
    }
    .data {
      position: absolute;
      bottom: 1rem;
      left: 0.3rem;
      .flex-between();
      width: 100%;
      box-sizing: border-box;
      padding-right: 0.8rem;
    }
  }
  .container-bottom {
    transform: translate3d(0, -0.8rem, 0);
  }
}
</style>


================================================
FILE: musicPlayer/src/components/top-tip/index.vue
================================================
<!--
 * @Author: Lambda
 * @Begin: 2019-11-24 11:11:37
 * @Update: 2019-11-24 12:19:31
 * @Update log: 更新日志
 -->
<template>
  <transition name="tip-show" mode="out-in">
    <div class="tip" v-show="isTip">
      <slot></slot>
    </div>
  </transition>
</template>

<script>
let timer = null
export default {
  name: '',
  data () {
    return {
      isTip: false
    }
  },
  methods: {
    showTip () {
      this.isTip = true
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        this.hideTip()
      }, 3000)
    },
    hideTip () {
      this.isTip = false
    }
  }
}
</script>

<style lang='less' scoped>
.tip-show-enter,
.tip-show-leave-to {
  transform: translateY(-1rem);
}

.tip-show-enter-active,
.tip-show-leave-active {
  transition: transform linear 0.3s;
}
.tip {
  position: fixed;
  top: 1.2rem;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
}
</style>


================================================
FILE: musicPlayer/src/getInfos/getData.js
================================================
/*
 * @Author: 李浩栋
 * @Begin: 2019-07-30 16:42:30
 * @Update: 2019-07-30 16:42:30
 * @Update log: 更新日志
 */
import Icons from './icons'

/**
 * 获取我的页面上边的图标
 */
export const homeIcons = () => {
  return Icons.homeIcons
}

/**
 * 获取我的页面列表图标
 */
export const homeList = () => {
  return Icons.homeList
}

/**
 * 获取发现页面上边的图标
 */
export const findIcons = () => {
  return Icons.findIcons
}

/**
 * 获取登陆页面上边的图标
 */
export const loginIcons = () => {
  return Icons.loginIcons
}

/**
 * 获取登陆列表上边的图标
 */
export const loginIconsTop = () => {
  return Icons.loginIconsTop
}

/**
 * 获取登陆列表下边的图标
 */
export const loginIconsBottom = () => {
  return Icons.loginIconsBottom
}

/**
 * 获取电台页面图标数据
 */
export const djIcons = () => {
  return Icons.djIcons
}


================================================
FILE: musicPlayer/src/getInfos/icons.js
================================================
/*
 * @Author: Lambda
 * @Begin: 2019-07-30 16:42:30
 * @Update: 2019-10-18 11:25:41
 * @Update log: 更新日志
 */
let homeIcons = [{
  text: '私藏推荐',
  icon: 'home iconxindian'
}, {
  text: '私人FM',
  icon: 'home iconshouyinji'
}, {
  text: 'Sati空间',
  icon: 'home iconyueliang'
}, {
  text: '最新电音',
  icon: 'home iconduodian'
}, {
  text: '因乐交友',
  icon: 'home iconjiaoyou'
}, {
  text: '亲子频道',
  icon: 'home iconertong'
}, {
  text: '古典专区',
  icon: 'home icongangqin'
}, {
  text: '跑步FM',
  icon: 'home iconorder-received'
}, {
  text: '小冰电台',
  icon: 'home iconbingjiling'
}, {
  text: '爵士电台',
  icon: 'home iconyandou'
}, {
  text: '驾驶模式',
  icon: 'home iconqiche'
}, {
  text: '编辑',
  icon: 'home icontoggle'
}]

let findIcons = [{
  text: '每日推荐',
  icon: 'find rili',
  linkTo: 'dateRecommend'
}, {
  text: '歌单',
  icon: 'find gedan',
  linkTo: 'recommend'
}, {
  text: '排行榜',
  icon: 'find paixingbang',
  linkTo: 'idx'
}, {
  text: '电台',
  icon: 'find diantai',
  linkTo: 'dj'
}, {
  text: '私人FM',
  icon: 'find shouyin',
  linkTo: 'personalFm'
}]

let djIcons = [{
  text: '电台分类',
  icon: 'dj dj-caidan1',
  linkTo: 'classification'
}, {
  text: '电台排行',
  icon: 'dj dj-paixing',
  linkTo: 'ranking'
}, {
  text: '付费精品',
  icon: 'dj dj-gerenzhongxin_wodeguizu',
  linkTo: 'pay_fine'
}, {
  text: '主播学院',
  icon: 'dj dj-shucopy',
  linkTo: 'anchor_college'
}]

let homeList = [{
  icon: 'iconyinyue',
  text: '本地音乐',
  num: 0
}, {
  icon: 'iconzuijinbofang',
  text: '最近播放',
  num: 0
}, {
  icon: 'iconxiazai',
  text: '下载管理',
  num: 0
}, {
  icon: 'icondiantai',
  text: '我的电台',
  num: 0
}, {
  icon: 'iconicon-31',
  text: '我的收藏',
  num: 0
}]

let loginIcons = [{
  icon: 'login iconwodexiaoxi',
  text: '我的消息'
}, {
  icon: 'login iconyonghu',
  text: '我的好友'
}, {
  icon: 'login iconpifu',
  text: '个性皮肤'
}, {
  icon: 'login icontinggeshiqu40x40',
  text: '听歌识曲'
}]

let loginIconsTop = [{
  icon: 'iconhuopiaotongxing',
  text: '演出'
}, {
  icon: 'icongouwuche',
  text: '商城'
}, {
  icon: 'icondibiao',
  text: '附近的人'
}, {
  icon: 'iconicon--',
  text: '口袋铃声'
}, {
  icon: 'icondingdan',
  text: '我的订单'
}]

let loginIconsBottom = [{
  icon: 'iconicon--2',
  text: '定时停止播放'
}, {
  icon: 'iconsaoyisao',
  text: '扫一扫'
}, {
  icon: 'iconicon--1',
  text: '音乐闹钟'
}, {
  icon: 'iconhezi501',
  text: '在线听歌免流量'
}, {
  icon: 'iconyouxi',
  text: '游戏推荐'
}, {
  icon: 'iconCoupon',
  text: '优惠券'
}, {
  icon: 'iconmaikefeng',
  text: '我要直播'
}]

export default {
  homeIcons,
  findIcons,
  homeList,
  loginIcons,
  loginIconsTop,
  loginIconsBottom,
  djIcons
}


================================================
FILE: musicPlayer/src/main.js
================================================
/*
 * @Author: Lambda
 * @Begin: 2019-08-30 19:47:55
 * @Update: 2020-03-13 22:29:57
 * @Update log: 更新日志
 */
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import fastclick from 'fastclick'
import VueLazyload from 'vue-lazyload'
import topTip from '@/components/top-tip'
import {
  Search,
  ActionSheet,
  Swipe,
  SwipeItem,
  Button,
  Checkbox,
  CheckboxGroup,
  PasswordInput,
  NumberKeyboard,
  PullRefresh
} from 'vant'

import 'styles/reset.css'
import 'styles/border.css'

fastclick.attach(document.body)
// import ""
Vue.use(Search)
Vue.use(Button)
Vue.use(ActionSheet)
Vue.use(PullRefresh)
Vue.use(Swipe).use(SwipeItem)
Vue.use(Checkbox).use(CheckboxGroup)
Vue.use(PasswordInput).use(NumberKeyboard)

Vue.config.productionTip = false

Vue.use(VueLazyload, {
  preLoad:
Download .txt
gitextract_0ydqwdr6/

├── .vscode/
│   └── settings.json
├── README.md
└── musicPlayer/
    ├── .babelrc
    ├── .editorconfig
    ├── .eslintignore
    ├── .eslintrc.js
    ├── .gitignore
    ├── .postcssrc.js
    ├── README.md
    ├── build/
    │   ├── build.js
    │   ├── check-versions.js
    │   ├── utils.js
    │   ├── vue-loader.conf.js
    │   ├── webpack.base.conf.js
    │   ├── webpack.dev.conf.js
    │   └── webpack.prod.conf.js
    ├── config/
    │   ├── dev.env.js
    │   ├── index.js
    │   └── prod.env.js
    ├── index.html
    ├── package.json
    ├── src/
    │   ├── App.vue
    │   ├── api/
    │   │   ├── config.js
    │   │   └── index.js
    │   ├── assets/
    │   │   ├── Bus.js
    │   │   ├── Mixins.js
    │   │   ├── styles/
    │   │   │   ├── border.css
    │   │   │   ├── global.less
    │   │   │   └── reset.css
    │   │   └── utils/
    │   │       ├── cookie.js
    │   │       ├── filters.js
    │   │       ├── getAstro.js
    │   │       ├── getPhone.js
    │   │       ├── getRandomArrayElements.js
    │   │       ├── modalScroll.js
    │   │       ├── scrollStopVideo.js
    │   │       └── setKeyWords.js
    │   ├── base/
    │   │   ├── albumPage/
    │   │   │   ├── index.vue
    │   │   │   └── index2.vue
    │   │   ├── alert.vue
    │   │   ├── audioAllTitle.vue
    │   │   ├── button.vue
    │   │   ├── circleLoading.vue
    │   │   ├── comments.vue
    │   │   ├── djDetailPage/
    │   │   │   ├── components/
    │   │   │   │   └── changeNav.vue
    │   │   │   ├── index.vue
    │   │   │   └── index2.vue
    │   │   ├── djSublistCard.vue
    │   │   ├── generalNav.vue
    │   │   ├── icon.vue
    │   │   ├── idxCard.vue
    │   │   ├── imgCard.vue
    │   │   ├── interchangeable.vue
    │   │   ├── loading.vue
    │   │   ├── loginPageIsShow.vue
    │   │   ├── pageErrorInfo.vue
    │   │   ├── pageLoading.vue
    │   │   ├── searchInput.vue
    │   │   ├── shouldLogin.vue
    │   │   ├── slider.vue
    │   │   ├── sliderNav.vue
    │   │   ├── song.vue
    │   │   ├── songListPage/
    │   │   │   └── index.vue
    │   │   └── titleFooter.vue
    │   ├── components/
    │   │   ├── detailPage/
    │   │   │   └── index.vue
    │   │   └── top-tip/
    │   │       └── index.vue
    │   ├── getInfos/
    │   │   ├── getData.js
    │   │   └── icons.js
    │   ├── main.js
    │   ├── pages/
    │   │   ├── audioIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── audioList.vue
    │   │   │   │   ├── bar.vue
    │   │   │   │   ├── functionButton.vue
    │   │   │   │   ├── lyricPage.vue
    │   │   │   │   ├── playIcons.vue
    │   │   │   │   ├── playing.vue
    │   │   │   │   └── small.vue
    │   │   │   └── index.vue
    │   │   ├── commentsIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── albumListInfo.vue
    │   │   │   │   └── centerMenu.vue
    │   │   │   └── index.vue
    │   │   ├── dateRecommend/
    │   │   │   └── index.vue
    │   │   ├── dj/
    │   │   │   ├── childrenPage/
    │   │   │   │   ├── class.vue
    │   │   │   │   ├── classRecommend.vue
    │   │   │   │   ├── djPayGift.vue
    │   │   │   │   ├── ranking-anchor.vue
    │   │   │   │   ├── ranking-program.vue
    │   │   │   │   ├── ranking-radio.vue
    │   │   │   │   ├── ranking.vue
    │   │   │   │   └── topConDetail.vue
    │   │   │   ├── components/
    │   │   │   │   ├── boutiqueRecom.vue
    │   │   │   │   ├── icons.vue
    │   │   │   │   ├── radioRecom.vue
    │   │   │   │   └── swiper.vue
    │   │   │   ├── index.vue
    │   │   │   ├── public.vue
    │   │   │   ├── publicClass.vue
    │   │   │   ├── publicImgWrap.vue
    │   │   │   └── titleAndThree.vue
    │   │   ├── djSublist/
    │   │   │   └── index.vue
    │   │   ├── findIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── chinese.vue
    │   │   │   │   ├── europe.vue
    │   │   │   │   ├── icons.vue
    │   │   │   │   ├── japan.vue
    │   │   │   │   ├── korea.vue
    │   │   │   │   ├── moreNewDish.vue
    │   │   │   │   ├── moreNewSongs.vue
    │   │   │   │   ├── newDish.vue
    │   │   │   │   ├── personalizedSongList.vue
    │   │   │   │   └── swiper.vue
    │   │   │   └── index.vue
    │   │   ├── friendIndex/
    │   │   │   ├── index.vue
    │   │   │   └── public.vue
    │   │   ├── homeIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── addNewPlayList.vue
    │   │   │   │   ├── homeList.vue
    │   │   │   │   ├── icons.vue
    │   │   │   │   └── songList.vue
    │   │   │   └── index.vue
    │   │   ├── idx/
    │   │   │   └── index.vue
    │   │   ├── loginIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── accountLogin.vue
    │   │   │   │   ├── phoneAccount.vue
    │   │   │   │   ├── phonePwd.vue
    │   │   │   │   ├── phoneVerify.vue
    │   │   │   │   ├── verifyCode.vue
    │   │   │   │   └── verifyInfo.vue
    │   │   │   └── index.vue
    │   │   ├── myFavorite/
    │   │   │   ├── components/
    │   │   │   │   ├── albums.vue
    │   │   │   │   ├── artists.vue
    │   │   │   │   ├── column.vue
    │   │   │   │   ├── mlog.vue
    │   │   │   │   └── videos.vue
    │   │   │   └── index.vue
    │   │   ├── nav/
    │   │   │   ├── components/
    │   │   │   │   ├── login-bottom.vue
    │   │   │   │   ├── login-icons-bottom.vue
    │   │   │   │   ├── login-icons-top.vue
    │   │   │   │   ├── login-icons.vue
    │   │   │   │   ├── login-top.vue
    │   │   │   │   └── login.vue
    │   │   │   └── index.vue
    │   │   ├── recentlyPlayed/
    │   │   │   └── index.vue
    │   │   ├── recommend/
    │   │   │   ├── fine/
    │   │   │   │   └── index.vue
    │   │   │   ├── general/
    │   │   │   │   └── index.vue
    │   │   │   ├── index.vue
    │   │   │   ├── navIndex/
    │   │   │   │   └── navList.vue
    │   │   │   └── recommended/
    │   │   │       └── index.vue
    │   │   ├── searchIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── history.vue
    │   │   │   │   └── hotSearch.vue
    │   │   │   └── index.vue
    │   │   ├── searchResults/
    │   │   │   ├── albumIndex/
    │   │   │   │   └── album.vue
    │   │   │   ├── artistIndex/
    │   │   │   │   └── artist.vue
    │   │   │   ├── composite/
    │   │   │   │   ├── components/
    │   │   │   │   │   ├── album.vue
    │   │   │   │   │   ├── artist.vue
    │   │   │   │   │   ├── djRadio.vue
    │   │   │   │   │   ├── playList.vue
    │   │   │   │   │   ├── simQuery.vue
    │   │   │   │   │   ├── song.vue
    │   │   │   │   │   ├── user.vue
    │   │   │   │   │   └── video.vue
    │   │   │   │   └── composite.vue
    │   │   │   ├── djRadioIndex/
    │   │   │   │   └── djRadio.vue
    │   │   │   ├── index.vue
    │   │   │   ├── navIndex/
    │   │   │   │   └── index.vue
    │   │   │   ├── playListIndex/
    │   │   │   │   └── playList.vue
    │   │   │   ├── singerIndex/
    │   │   │   │   ├── select.vue
    │   │   │   │   └── singer.vue
    │   │   │   ├── songIndex/
    │   │   │   │   └── song.vue
    │   │   │   ├── userIndex/
    │   │   │   │   └── user.vue
    │   │   │   └── videoIndex/
    │   │   │       └── video.vue
    │   │   ├── userInfoIndex/
    │   │   │   ├── components/
    │   │   │   │   ├── userDynamic.vue
    │   │   │   │   └── userHome.vue
    │   │   │   └── index.vue
    │   │   └── videoIndex/
    │   │       ├── components/
    │   │       │   ├── acg.vue
    │   │       │   ├── animation.vue
    │   │       │   ├── dance.vue
    │   │       │   ├── game.vue
    │   │       │   ├── listenBGM.vue
    │   │       │   ├── musicFestival.vue
    │   │       │   ├── rock.vue
    │   │       │   ├── scene.vue
    │   │       │   └── singing.vue
    │   │       ├── index.vue
    │   │       ├── public.vue
    │   │       └── videoComments/
    │   │           ├── components/
    │   │           │   ├── video.vue
    │   │           │   ├── videoCreator.vue
    │   │           │   └── videoInfo.vue
    │   │           └── index.vue
    │   ├── router/
    │   │   └── index.js
    │   └── store/
    │       ├── actions.js
    │       ├── getters.js
    │       ├── index.js
    │       ├── mutation-types.js
    │       ├── mutations.js
    │       └── state.js
    └── static/
        └── .gitkeep
Download .txt
SYMBOL INDEX (141 symbols across 13 files)

FILE: musicPlayer/build/check-versions.js
  function exec (line 7) | function exec (cmd) {

FILE: musicPlayer/build/utils.js
  function generateLoaders (line 33) | function generateLoaders (loader, loaderOptions) {

FILE: musicPlayer/build/webpack.base.conf.js
  function resolve (line 13) | function resolve (dir) {

FILE: musicPlayer/build/webpack.dev.conf.js
  constant HOST (line 13) | const HOST = process.env.HOST
  constant PORT (line 14) | const PORT = process.env.PORT && Number(process.env.PORT)

FILE: musicPlayer/build/webpack.prod.conf.js
  method minChunks (line 84) | minChunks (module) {

FILE: musicPlayer/src/api/index.js
  method bannerSwiperFn (line 111) | bannerSwiperFn () {
  method recSongsFn (line 117) | recSongsFn () {
  method albumDetailFn (line 125) | albumDetailFn (id, s = 5) {
  method topListFn (line 136) | topListFn () {
  method recSongListFn (line 147) | recSongListFn (limit = 30, order = 'hot', cat) {
  method highqualityFn (line 162) | highqualityFn (limit = 30, before, cat) {
  method idxListFn (line 175) | idxListFn (idx) {
  method dateRecSongListFn (line 185) | dateRecSongListFn () {
  method newDishFn (line 194) | newDishFn (limit = 10, offset) {
  method getDishInfoFn (line 206) | getDishInfoFn (id) {
  method phoneRegisteredFn (line 217) | phoneRegisteredFn (phone) {
  method sendVerifyFn (line 228) | sendVerifyFn (phone) {
  method verifyFn (line 241) | verifyFn (phone, captcha) {
  method phoneLoginFn (line 254) | phoneLoginFn (phone, password) {
  method loginStatusFn (line 265) | loginStatusFn () {
  method userRecordFn (line 274) | userRecordFn (uid, type = 1) {
  method userInfoFn (line 296) | userInfoFn (timestamp) {
  method userDetailFn (line 307) | userDetailFn (uid) {
  method userEventFn (line 320) | userEventFn (uid, limit = 20, lasttime) {
  method signInFn (line 334) | signInFn (type) {
  method registerFn (line 348) | registerFn (captcha, phone, password, nickname) {
  method playlistFn (line 363) | playlistFn (uid, timestamp) {
  method userDjFn (line 375) | userDjFn (uid) {
  method hotSearchListFn (line 385) | hotSearchListFn () {
  method searchFn (line 398) | searchFn (keywords, limit = 30, offset = 0, type = 1018) {
  method defaultSearchFn (line 411) | defaultSearchFn () {
  method suggestSearchFn (line 421) | suggestSearchFn (keywords, type = 'mobile') {
  method songUrlFn (line 436) | songUrlFn (id, br) {
  method checkSongFn (line 449) | checkSongFn (id, br) {
  method songLyricFn (line 461) | songLyricFn (id) {
  method addOrDeletePlaylistFn (line 473) | addOrDeletePlaylistFn (t, id) {
  method addPlaylistFn (line 486) | addPlaylistFn (name, privacy) {
  method deletePlaylistFn (line 498) | deletePlaylistFn (id) {
  method heartModeFn (line 511) | heartModeFn (id, pid, sid) {
  method likeMusicFn (line 525) | likeMusicFn (id, like) {
  method likeMusicListFn (line 537) | likeMusicListFn (uid) {
  method favoriteAlbumsFn (line 551) | favoriteAlbumsFn (limit, offset) {
  method favoriteArtistsFn (line 562) | favoriteArtistsFn () {
  method favoriteVideosFn (line 568) | favoriteVideosFn () {
  method getVideoDetailFn (line 575) | getVideoDetailFn (id) {
  method getVideoRelatedFn (line 586) | getVideoRelatedFn (id) {
  method getVideoCommentsFn (line 600) | getVideoCommentsFn (id, limit = 20, offset, before) {
  method djSublistFn (line 615) | djSublistFn (timestamp) {
  method newSongsFn (line 627) | newSongsFn (type = 0) {
  method personalFmFn (line 638) | personalFmFn () {
  method singerClassFn (line 656) | singerClassFn (cat = 1001, limit = 30, offset = 0, initial) {
  method logoutFn (line 669) | logoutFn () {
  method djBannerFn (line 675) | djBannerFn () {
  method radioRecomFn (line 681) | radioRecomFn () {
  method boutiqueRecomFn (line 689) | boutiqueRecomFn (limit = 3, offset = 0) {
  method djClassificationFn (line 700) | djClassificationFn () {
  method djClassificationInfoFn (line 708) | djClassificationInfoFn (type) {
  method djProgramFn (line 725) | djProgramFn (rid, limit = 30, offset = 0, asc) {
  method djDetailFn (line 739) | djDetailFn (rid) {
  method djPayGiftFn (line 751) | djPayGiftFn (limit = 30, offset = 0) {
  method djSubFn (line 764) | djSubFn (rid, t) {
  method djToplistFn (line 777) | djToplistFn (limit = 100, offset = 0) {
  method djHotToplistFn (line 791) | djHotToplistFn (limit = 100, offset = 0, type = 'hot') {
  method djProgramTopHoursFn (line 804) | djProgramTopHoursFn (limit) {
  method djToplistHoursFn (line 815) | djToplistHoursFn (limit) {
  method djToplistNewComersFn (line 826) | djToplistNewComersFn (limit) {
  method djToplistPopularsFn (line 837) | djToplistPopularsFn (limit) {
  method djToplistPaysFn (line 848) | djToplistPaysFn (limit) {
  method friendFn (line 863) | friendFn (pagesize = 20, lasttime = -1) {
  method getVideoTagFn (line 874) | getVideoTagFn () {
  method getVideoGroupFn (line 881) | getVideoGroupFn (id) {
  method getVideoUrlFn (line 892) | getVideoUrlFn (id) {
  method commentPlaylistFn (line 907) | commentPlaylistFn (id, limit = 20, offset, before) {
  method commentAlbumFn (line 927) | commentAlbumFn (id, limit = 20, offset, before) {
  method commentDjFn (line 939) | commentDjFn (id, limit = 20, offset, before) {
  method commentLikeFn (line 961) | commentLikeFn (id, cid, t, type) {
  method pushComFn (line 981) | pushComFn (type, id, content, commentId, threadId) {
  method delComFn (line 1003) | delComFn (type, id, commentId, threadId) {
  method resourceLikeInFn (line 1018) | resourceLikeInFn (type, id) {
  method resourceLikeOutFn (line 1033) | resourceLikeOutFn (type, id) {

FILE: musicPlayer/src/assets/Mixins.js
  method changeMode (line 38) | changeMode () {
  method showAudioList (line 41) | showAudioList () {
  method data (line 50) | data () {
  method created (line 57) | created () {
  method _getVideoDetail (line 61) | _getVideoDetail (id) {
  method hideVideo (line 78) | hideVideo () {
  method stopVideo (line 88) | stopVideo (self) {
  method getIndex (line 101) | getIndex (index) {
  method mounted (line 118) | mounted () {
  method setPagePb (line 135) | setPagePb () {

FILE: musicPlayer/src/assets/utils/getPhone.js
  function getPhone (line 7) | function getPhone () {

FILE: musicPlayer/src/assets/utils/modalScroll.js
  class ModalHelper (line 9) | class ModalHelper {
    method afterOpen (line 11) | static afterOpen () {
    method beforeClose (line 21) | static beforeClose () {

FILE: musicPlayer/src/assets/utils/scrollStopVideo.js
  function isInSport (line 8) | function isInSport (ele, wra) {

FILE: musicPlayer/src/store/actions.js
  function getRandomIndex (line 19) | function getRandomIndex (min, max) {
  function shuffle (line 25) | function shuffle (arr) {
  function findIndex (line 36) | function findIndex (list, song) {
  method selectPlay (line 46) | selectPlay ({
  method startPlayAll (line 69) | startPlayAll ({
  method addToAudioList (line 85) | addToAudioList ({
  method deleteSong (line 127) | deleteSong ({

FILE: musicPlayer/src/store/mutation-types.js
  constant SHOW_LOGIN (line 7) | const SHOW_LOGIN = 'SHOW_LOGIN' // 显示侧边栏
  constant HIDE_LOGIN (line 8) | const HIDE_LOGIN = 'HIDE_LOGIN' // 隐藏侧边栏
  constant TOGGLE_MODE (line 9) | const TOGGLE_MODE = 'TOGGLE_MODE' // 日夜模式切换
  constant TOGGLE_MODE_TEXT (line 10) | const TOGGLE_MODE_TEXT = 'TOGGLE_MODE_TEXT' // 日夜文字切换
  constant TO_SUN (line 11) | const TO_SUN = 'TO_SUN' // 由夜转换到日
  constant TO_YUE (line 12) | const TO_YUE = 'TO_YUE' // 由日转换到夜
  constant LOGIN_STATE (line 13) | const LOGIN_STATE = 'LOGIN_STATE' // 登陆状态
  constant SET_LEVEL (line 14) | const SET_LEVEL = 'SET_LEVEL' // 设置用户等级
  constant ACCOUNT_UID (line 15) | const ACCOUNT_UID = 'ACCOUNT_UID' // 存取用户id
  constant SET_LOAD (line 16) | const SET_LOAD = 'SET_LOAD' // 设置当前已经返回数据,显示界面
  constant RETURN_LOAD (line 17) | const RETURN_LOAD = 'RETURN_LOAD' // 将load返回默认的true
  constant SET_LINK_PAGE (line 18) | const SET_LINK_PAGE = 'SET_LINK_PAGE' // 将load返回默认的true
  constant SET_PLAY_SATE (line 19) | const SET_PLAY_SATE = 'SET_PLAY_SATE' // 设置当前的播放状态
  constant SET_FULL_SCREEN (line 20) | const SET_FULL_SCREEN = 'SET_FULL_SCREEN' // 设置当前的播放器的状态,是大还是小
  constant SET_AUDIO_LIST (line 21) | const SET_AUDIO_LIST = 'SET_AUDIO_LIST' // 设置当前的播放列表的展示
  constant SET_PLAY_LIST (line 22) | const SET_PLAY_LIST = 'SET_PLAY_LIST' // 设置当前的播放列表
  constant SET_AUDIO_INDEX (line 23) | const SET_AUDIO_INDEX = 'SET_AUDIO_INDEX' // 设置当前的播放歌曲是第几个,索引值
  constant SET_AUDIO_MODE (line 24) | const SET_AUDIO_MODE = 'SET_AUDIO_MODE' // 设置当前的播放模式
  constant SET_PLAYING_SHOW (line 25) | const SET_PLAYING_SHOW = 'SET_PLAYING_SHOW' // 设置当前是否显示转盘播放页面

FILE: musicPlayer/src/store/mutations.js
  method [SHOW_LOGIN] (line 34) | [SHOW_LOGIN] (state) {
  method [HIDE_LOGIN] (line 39) | [HIDE_LOGIN] (state) {
  method [TO_YUE] (line 44) | [TO_YUE] (state) {
  method [TO_SUN] (line 51) | [TO_SUN] (state) {
  method [TOGGLE_MODE] (line 58) | [TOGGLE_MODE] (state) {
  method [TOGGLE_MODE_TEXT] (line 66) | [TOGGLE_MODE_TEXT] (state) {
  method [LOGIN_STATE] (line 74) | [LOGIN_STATE] (state, num) {
  method [SET_LEVEL] (line 77) | [SET_LEVEL] (state, num) {
  method [ACCOUNT_UID] (line 81) | [ACCOUNT_UID] (state, id) {
  method [SET_LOAD] (line 85) | [SET_LOAD] (state) {
  method [RETURN_LOAD] (line 88) | [RETURN_LOAD] (state) {
  method [SET_LINK_PAGE] (line 94) | [SET_LINK_PAGE] (state, page) {
  method [SET_PLAY_SATE] (line 102) | [SET_PLAY_SATE] (state, flag) {
  method [SET_FULL_SCREEN] (line 108) | [SET_FULL_SCREEN] (state, flag) {
  method [SET_AUDIO_LIST] (line 119) | [SET_AUDIO_LIST] (state, list) {
  method [SET_AUDIO_INDEX] (line 125) | [SET_AUDIO_INDEX] (state, index) {
  method [SET_PLAY_LIST] (line 131) | [SET_PLAY_LIST] (state, list) {
  method [SET_AUDIO_MODE] (line 139) | [SET_AUDIO_MODE] (state, mode) {
  method [SET_PLAYING_SHOW] (line 145) | [SET_PLAYING_SHOW] (state, boolean) {
Condensed preview — 193 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (710K chars).
[
  {
    "path": ".vscode/settings.json",
    "chars": 111,
    "preview": "{\n  \"cSpell.words\": [\n    \"RCVD\",\n    \"autofocus\",\n    \"blogaaa\",\n    \"flexbox\",\n    \"vmax\",\n    \"zhidao\"\n  ]\n}"
  },
  {
    "path": "README.md",
    "chars": 4631,
    "preview": "<h1 align=\"center\">Welcome to Music-player 👋</h1>\n\n<p align=\"center\">\n<img src=\"https://img.shields.io/badge/webpack-^3."
  },
  {
    "path": "musicPlayer/.babelrc",
    "chars": 467,
    "preview": "{\n  \"presets\": [\n    [\"env\", {\n      \"modules\": false,\n      \"targets\": {\n        \"browsers\": [\"> 1%\", \"last 2 versions\""
  },
  {
    "path": "musicPlayer/.editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": "musicPlayer/.eslintignore",
    "chars": 30,
    "preview": "/build/\n/config/\n/dist/\n/*.js\n"
  },
  {
    "path": "musicPlayer/.eslintrc.js",
    "chars": 823,
    "preview": "// https://eslint.org/docs/user-guide/configuring\n\nmodule.exports = {\n  root: true,\n  parserOptions: {\n    parser: 'babe"
  },
  {
    "path": "musicPlayer/.gitignore",
    "chars": 160,
    "preview": ".DS_Store\nnode_modules/\n/dist/\nsave/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.ide"
  },
  {
    "path": "musicPlayer/.postcssrc.js",
    "chars": 246,
    "preview": "// https://github.com/michael-ciniawsky/postcss-load-config\n\nmodule.exports = {\n  \"plugins\": {\n    \"postcss-import\": {},"
  },
  {
    "path": "musicPlayer/README.md",
    "chars": 460,
    "preview": "# music-player\n\n> 高仿网易云音乐\n\n## Build Setup\n\n``` bash\n# install dependencies\nnpm install\n\n# serve with hot reload at local"
  },
  {
    "path": "musicPlayer/build/build.js",
    "chars": 1198,
    "preview": "'use strict'\nrequire('./check-versions')()\n\nprocess.env.NODE_ENV = 'production'\n\nconst ora = require('ora')\nconst rm = r"
  },
  {
    "path": "musicPlayer/build/check-versions.js",
    "chars": 1290,
    "preview": "'use strict'\nconst chalk = require('chalk')\nconst semver = require('semver')\nconst packageConfig = require('../package.j"
  },
  {
    "path": "musicPlayer/build/utils.js",
    "chars": 2587,
    "preview": "'use strict'\nconst path = require('path')\nconst config = require('../config')\nconst ExtractTextPlugin = require('extract"
  },
  {
    "path": "musicPlayer/build/vue-loader.conf.js",
    "chars": 553,
    "preview": "'use strict'\nconst utils = require('./utils')\nconst config = require('../config')\nconst isProduction = process.env.NODE_"
  },
  {
    "path": "musicPlayer/build/webpack.base.conf.js",
    "chars": 2725,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-18 19:13:26\n * @Update log: 更新日志\n */\n'use strict'\n"
  },
  {
    "path": "musicPlayer/build/webpack.dev.conf.js",
    "chars": 3004,
    "preview": "'use strict'\nconst utils = require('./utils')\nconst webpack = require('webpack')\nconst config = require('../config')\ncon"
  },
  {
    "path": "musicPlayer/build/webpack.prod.conf.js",
    "chars": 5055,
    "preview": "'use strict'\nconst path = require('path')\nconst utils = require('./utils')\nconst webpack = require('webpack')\nconst conf"
  },
  {
    "path": "musicPlayer/config/dev.env.js",
    "chars": 156,
    "preview": "'use strict'\nconst merge = require('webpack-merge')\nconst prodEnv = require('./prod.env')\n\nmodule.exports = merge(prodEn"
  },
  {
    "path": "musicPlayer/config/index.js",
    "chars": 2665,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-16 19:49:41\n * @Update: 2019-08-18 19:16:52\n * @Update log: 更新日志\n */\n'use strict'\n"
  },
  {
    "path": "musicPlayer/config/prod.env.js",
    "chars": 61,
    "preview": "'use strict'\nmodule.exports = {\n  NODE_ENV: '\"production\"'\n}\n"
  },
  {
    "path": "musicPlayer/index.html",
    "chars": 4484,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-04 13:34:22\n * @Update log: 更新日志\n -->\n<!DOCTYPE "
  },
  {
    "path": "musicPlayer/package.json",
    "chars": 2355,
    "preview": "{\n  \"name\": \"music-player\",\n  \"version\": \"1.0.0\",\n  \"description\": \"高仿网易云音乐\",\n  \"author\": \"powerdong <m19893170798@163.c"
  },
  {
    "path": "musicPlayer/src/App.vue",
    "chars": 1027,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-08-30 19:47:55\n * @Update: 2020-03-13 22:38:04\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/api/config.js",
    "chars": 4971,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-08-19 13:42:17\n * @Update: 2019-12-03 13:37:22\n * @Update log: 更新日志\n */\nconst api = p"
  },
  {
    "path": "musicPlayer/src/api/index.js",
    "chars": 20435,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-08-19 13:47:19\n * @Update: 2019-12-08 12:02:19\n * @Update log: 更新日志\n */\nimport axios "
  },
  {
    "path": "musicPlayer/src/assets/Bus.js",
    "chars": 154,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-08-29 10:48:10\n * @Update: 2019-08-29 10:50:16\n * @Update log: 更新日志\n */\nimport Vue fr"
  },
  {
    "path": "musicPlayer/src/assets/Mixins.js",
    "chars": 2486,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-09-21 15:14:40\n * @Update: 2019-11-24 12:59:18\n * @Update log: 更新日志\n */\n\nimport {\n  m"
  },
  {
    "path": "musicPlayer/src/assets/styles/border.css",
    "chars": 4632,
    "preview": "@charset \"utf-8\";\n.border,\n.border-top,\n.border-right,\n.border-bottom,\n.border-left,\n.border-topbottom,\n.border-rightlef"
  },
  {
    "path": "musicPlayer/src/assets/styles/global.less",
    "chars": 2517,
    "preview": "// 主题色\n@bgcolor: #dd001b;\n// 图片圆角\n@imgBorderRadius: 0.11rem;\n// 图标宽度\n@iconWidth: 1rem;\n// 图标下方文本大小\n@iconText: 0.24rem;\n\n"
  },
  {
    "path": "musicPlayer/src/assets/styles/reset.css",
    "chars": 2079,
    "preview": "@charset \"utf-8\";\n\nhtml {\n  background-color: #fff;\n  color: #000\n}\n\nbody,\nul,\nol,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nfigur"
  },
  {
    "path": "musicPlayer/src/assets/utils/cookie.js",
    "chars": 927,
    "preview": "/*\n * @Author: Lambda\n * @Begin: 2019-10-24 11:00:35\n * @Update: 2019-10-24 11:03:02\n * @Update log: 更新日志\n */\nexport con"
  },
  {
    "path": "musicPlayer/src/assets/utils/filters.js",
    "chars": 1996,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-10-14 14:08:38\n * @Update: 2019-12-10 13:40:19\n * @Update log: 更新日志\n */\nexport const "
  },
  {
    "path": "musicPlayer/src/assets/utils/getAstro.js",
    "chars": 250,
    "preview": "/*\n * @Author: Lambda\n * @Begin: 2019-10-28 12:05:42\n * @Update: 2019-10-28 12:06:27\n * @Update log: 更新日志\n */\nexport con"
  },
  {
    "path": "musicPlayer/src/assets/utils/getPhone.js",
    "chars": 316,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-08-26 13:35:48\n * @Update: 2019-08-26 13:47:13\n * @Update log: 更新日志\n */\nexport functi"
  },
  {
    "path": "musicPlayer/src/assets/utils/getRandomArrayElements.js",
    "chars": 627,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-10-11 13:17:17\n * @Update: 2019-10-11 13:18:07\n * @Update log: 更新日志\n */\n/**\n * 随机取出数组"
  },
  {
    "path": "musicPlayer/src/assets/utils/modalScroll.js",
    "chars": 772,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-27 17:08:42\n * @Update: 2019-08-10 16:42:32\n * @Update log: 更新日志\n */\n// 解决遮罩层滚动穿透问"
  },
  {
    "path": "musicPlayer/src/assets/utils/scrollStopVideo.js",
    "chars": 502,
    "preview": "/*\n * @Author: Lambda\n * @Begin: 2019-11-19 12:37:10\n * @Update: 2019-11-19 13:48:22\n * @Update log: 更新日志\n */\n// 判断一个元素是"
  },
  {
    "path": "musicPlayer/src/assets/utils/setKeyWords.js",
    "chars": 678,
    "preview": "/*\n * @Author: Lambda\n * @Begin: 2019-11-01 13:07:59\n * @Update: 2019-11-04 13:40:35\n * @Update log: 更新日志\n */\n/**\n * 传入关"
  },
  {
    "path": "musicPlayer/src/base/albumPage/index.vue",
    "chars": 4799,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-06 11:47:11\n * @Update: 2019-11-02 14:11:21\n * @Update log: 这个是歌单展示的通用组件\n -->\n<t"
  },
  {
    "path": "musicPlayer/src/base/albumPage/index2.vue",
    "chars": 10469,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-13 12:03:28\n * @Update: 2019-11-30 13:55:02\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/base/alert.vue",
    "chars": 771,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-17 17:10:07\n * @Update: 2019-09-23 17:38:12\n * @Update log: 底部错误信息提示组件\n -->\n<tem"
  },
  {
    "path": "musicPlayer/src/base/audioAllTitle.vue",
    "chars": 964,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-28 12:56:39\n * @Update: 2019-09-28 12:56:39\n * @Update log: 播放全部的那一行\n *\n * 使用时传入"
  },
  {
    "path": "musicPlayer/src/base/button.vue",
    "chars": 831,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-14 16:21:48\n * @Update: 2019-08-18 13:43:11\n * @Update log: 通用 button 登录页按钮\n *\n "
  },
  {
    "path": "musicPlayer/src/base/circleLoading.vue",
    "chars": 1417,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-11-04 13:58:11\n * @Update: 2019-11-07 19:55:14\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/base/comments.vue",
    "chars": 3201,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-27 09:14:42\n * @Update: 2019-11-12 19:11:06\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/base/djDetailPage/components/changeNav.vue",
    "chars": 1632,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-14 13:34:16\n * @Update: 2019-10-28 09:17:00\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/base/djDetailPage/index.vue",
    "chars": 9334,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-13 12:03:28\n * @Update: 2019-11-29 13:28:44\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/base/djDetailPage/index2.vue",
    "chars": 12124,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-13 12:03:28\n * @Update: 2019-11-14 14:03:58\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/base/djSublistCard.vue",
    "chars": 4374,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 15:14:42\n * @Update: 2019-10-04 13:38:23\n * @Update log: 长卡片组件\n -->\n<template"
  },
  {
    "path": "musicPlayer/src/base/generalNav.vue",
    "chars": 1100,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-14 15:44:26\n * @Update: 2019-11-19 22:01:42\n * @Update log: 登录页通用顶部导航\n -->\n<temp"
  },
  {
    "path": "musicPlayer/src/base/icon.vue",
    "chars": 1443,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-30 20:21:12\n * @Update: 2019-10-11 13:10:30\n * @Update log: 公共图标组件\n -->\n<templat"
  },
  {
    "path": "musicPlayer/src/base/idxCard.vue",
    "chars": 2465,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-09 13:31:02\n * @Update: 2019-10-08 12:45:15\n * @Update log: 排行榜中官方榜展示组件\n -->\n<te"
  },
  {
    "path": "musicPlayer/src/base/imgCard.vue",
    "chars": 6611,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-08 14:37:08\n * @Update: 2019-11-12 21:42:40\n * @Update log: 通用的方形展示组件\n -->\n<temp"
  },
  {
    "path": "musicPlayer/src/base/interchangeable.vue",
    "chars": 6733,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-02 13:36:42\n * @Update: 2019-11-16 13:54:29\n * @Update log: 这是一个公共的组件,用来展示搜索展示页面"
  },
  {
    "path": "musicPlayer/src/base/loading.vue",
    "chars": 969,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-17 17:10:07\n * @Update: 2019-08-17 18:46:40\n * @Update log: 页面加载样式\n -->\n<templat"
  },
  {
    "path": "musicPlayer/src/base/loginPageIsShow.vue",
    "chars": 1130,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-11 07:56:48\n * @Update: 2019-10-11 08:10:27\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/base/pageErrorInfo.vue",
    "chars": 469,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-05 09:21:33\n * @Update: 2019-10-03 20:01:31\n * @Update log: 搜索展示页如果没有搜索结果展示的信息页面"
  },
  {
    "path": "musicPlayer/src/base/pageLoading.vue",
    "chars": 1310,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:19:23\n * @Update: 2019-10-24 08:50:52\n * @Update log: 展示页面加载动画\n -->\n<templ"
  },
  {
    "path": "musicPlayer/src/base/searchInput.vue",
    "chars": 6841,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-27 12:42:24\n * @Update: 2019-11-14 13:38:05\n * @Update log: 搜索框\n -->\n<template>\n"
  },
  {
    "path": "musicPlayer/src/base/shouldLogin.vue",
    "chars": 501,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-11 07:51:34\n * @Update: 2019-10-11 08:12:58\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/base/slider.vue",
    "chars": 4144,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-27 12:49:09\n * @Update: 2019-11-29 13:18:49\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/base/sliderNav.vue",
    "chars": 1799,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 14:30:50\n * @Update: 2019-10-29 13:07:19\n * @Update log: 可左右滑动的标签导航\n -->\n<tem"
  },
  {
    "path": "musicPlayer/src/base/song.vue",
    "chars": 4263,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-07 12:09:22\n * @Update: 2019-11-08 13:45:23\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/base/songListPage/index.vue",
    "chars": 12888,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-06 11:33:42\n * @Update: 2020-05-04 17:41:34\n * @Update log: 这是一个用来展示歌曲列表的基础组件\n -"
  },
  {
    "path": "musicPlayer/src/base/titleFooter.vue",
    "chars": 1622,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-02 18:07:09\n * @Update: 2019-09-03 18:00:27\n * @Update log: 搜索展示页面公共的标题和底部组件\n --"
  },
  {
    "path": "musicPlayer/src/components/detailPage/index.vue",
    "chars": 3288,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-13 12:03:28\n * @Update: 2019-11-30 13:27:53\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/components/top-tip/index.vue",
    "chars": 935,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-11-24 11:11:37\n * @Update: 2019-11-24 12:19:31\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/getInfos/getData.js",
    "chars": 739,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-07-30 16:42:30\n * @Update log: 更新日志\n */\nimport Icons "
  },
  {
    "path": "musicPlayer/src/getInfos/icons.js",
    "chars": 2557,
    "preview": "/*\n * @Author: Lambda\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-18 11:25:41\n * @Update log: 更新日志\n */\nlet homeIc"
  },
  {
    "path": "musicPlayer/src/main.js",
    "chars": 62969,
    "preview": "/*\n * @Author: Lambda\n * @Begin: 2019-08-30 19:47:55\n * @Update: 2020-03-13 22:29:57\n * @Update log: 更新日志\n */\n// The Vue"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/audioList.vue",
    "chars": 4401,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-21 14:37:20\n * @Update: 2019-11-07 12:02:35\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/bar.vue",
    "chars": 3088,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-13 14:01:18\n * @Update: 2019-11-05 13:59:06\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/functionButton.vue",
    "chars": 1724,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-13 13:33:12\n * @Update: 2019-11-07 11:42:11\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/lyricPage.vue",
    "chars": 1789,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-16 13:26:47\n * @Update: 2020-03-14 13:47:25\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/playIcons.vue",
    "chars": 1551,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-13 13:33:12\n * @Update: 2019-10-24 09:42:28\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/playing.vue",
    "chars": 73175,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-13 12:44:14\n * @Update: 2019-10-11 11:40:19\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/components/small.vue",
    "chars": 2998,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-14 17:51:26\n * @Update: 2019-11-07 12:10:02\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/audioIndex/index.vue",
    "chars": 13319,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-12 13:02:20\n * @Update: 2020-03-14 13:56:09\n * @Update log: 点击歌单中的某一项,将歌单列表信息传入v"
  },
  {
    "path": "musicPlayer/src/pages/commentsIndex/components/albumListInfo.vue",
    "chars": 1311,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-27 08:29:46\n * @Update: 2019-10-29 21:38:43\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/commentsIndex/components/centerMenu.vue",
    "chars": 2019,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-11-09 08:32:08\n * @Update: 2019-11-09 09:32:15\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/commentsIndex/index.vue",
    "chars": 6704,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-27 07:48:55\n * @Update: 2019-11-12 19:35:25\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dateRecommend/index.vue",
    "chars": 1951,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-06 11:33:42\n * @Update: 2019-11-25 15:43:52\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/class.vue",
    "chars": 1936,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-13 10:32:53\n * @Update: 2019-10-26 08:18:44\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/classRecommend.vue",
    "chars": 1638,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-12-07 13:04:21\n * @Update: 2019-12-08 12:58:10\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/djPayGift.vue",
    "chars": 2541,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-13 10:59:26\n * @Update: 2019-10-18 11:19:53\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/ranking-anchor.vue",
    "chars": 1880,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-17 13:04:41\n * @Update: 2019-12-07 13:27:46\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/ranking-program.vue",
    "chars": 1378,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-17 13:04:59\n * @Update: 2019-12-03 18:38:16\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/ranking-radio.vue",
    "chars": 1406,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-17 13:05:15\n * @Update: 2019-12-07 13:18:14\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/ranking.vue",
    "chars": 1092,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-17 12:44:36\n * @Update: 2019-10-26 08:16:59\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dj/childrenPage/topConDetail.vue",
    "chars": 3412,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-12-07 13:04:21\n * @Update: 2019-12-07 14:01:20\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/dj/components/boutiqueRecom.vue",
    "chars": 1391,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-11 13:11:46\n * @Update: 2019-12-10 18:59:09\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/components/icons.vue",
    "chars": 911,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-11 13:10:20\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/components/radioRecom.vue",
    "chars": 1251,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-11 13:11:46\n * @Update: 2019-10-13 12:07:08\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/components/swiper.vue",
    "chars": 1411,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-03 12:07:07\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/index.vue",
    "chars": 6283,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-06 11:39:29\n * @Update: 2019-10-24 08:41:43\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/public.vue",
    "chars": 6506,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-12 13:19:59\n * @Update: 2019-12-07 13:56:18\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/publicClass.vue",
    "chars": 1889,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-12 14:24:51\n * @Update: 2019-12-08 12:24:40\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/publicImgWrap.vue",
    "chars": 1025,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-12 18:39:19\n * @Update: 2019-10-13 11:59:19\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/dj/titleAndThree.vue",
    "chars": 3162,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-12-03 12:34:55\n * @Update: 2019-12-10 13:09:16\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/djSublist/index.vue",
    "chars": 1275,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 15:08:21\n * @Update: 2019-10-16 13:29:19\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/chinese.vue",
    "chars": 1964,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-08 11:28:42\n * @Update: 2019-10-08 12:37:13\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/europe.vue",
    "chars": 1965,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-08 11:29:02\n * @Update: 2019-10-08 11:29:02\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/icons.vue",
    "chars": 1835,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-09 12:16:40\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/japan.vue",
    "chars": 1854,
    "preview": "<template>\n  <div>\n    <div class=\"load\" v-show=\"loading\">\n      <page-loading></page-loading>\n    </div>\n    <div v-sho"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/korea.vue",
    "chars": 1855,
    "preview": "<template>\n  <div>\n    <div class=\"load\" v-show=\"loading\">\n      <page-loading></page-loading>\n    </div>\n    <div v-sho"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/moreNewDish.vue",
    "chars": 4653,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-06 12:57:04\n * @Update: 2019-10-26 08:21:52\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/moreNewSongs.vue",
    "chars": 1258,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-08 11:12:45\n * @Update: 2019-11-02 12:30:05\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/newDish.vue",
    "chars": 3930,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-27 17:08:42\n * @Update: 2019-12-05 15:50:04\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/personalizedSongList.vue",
    "chars": 2948,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-12-05 15:47:01\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/components/swiper.vue",
    "chars": 1464,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-12 21:36:15\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/findIndex/index.vue",
    "chars": 1701,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2020-03-13 22:37:42\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/friendIndex/index.vue",
    "chars": 1495,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-26 10:32:15\n * @Update: 2019-11-12 21:55:07\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/friendIndex/public.vue",
    "chars": 9738,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-26 10:49:45\n * @Update: 2019-11-07 19:57:21\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/homeIndex/components/addNewPlayList.vue",
    "chars": 3024,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-26 13:09:11\n * @Update: 2019-11-03 13:02:37\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/homeIndex/components/homeList.vue",
    "chars": 2248,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-14 13:34:02\n * @Update log: 我的页面的列表项\n -->\n<templ"
  },
  {
    "path": "musicPlayer/src/pages/homeIndex/components/icons.vue",
    "chars": 2211,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-03 19:52:18\n * @Update log: 我的页面图标展示\n *         "
  },
  {
    "path": "musicPlayer/src/pages/homeIndex/components/songList.vue",
    "chars": 8567,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-14 13:45:17\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/homeIndex/index.vue",
    "chars": 3892,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-12 13:17:54\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/idx/index.vue",
    "chars": 2704,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-06 11:39:05\n * @Update: 2019-10-24 08:44:46\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/components/accountLogin.vue",
    "chars": 719,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-25 13:23:44\n * @Update: 2019-12-04 18:37:14\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/components/phoneAccount.vue",
    "chars": 4800,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-12 18:16:59\n * @Update: 2019-12-04 18:32:14\n * @Update log: 手机号登陆账号页面\n -->\n<temp"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/components/phonePwd.vue",
    "chars": 6156,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-14 15:42:41\n * @Update: 2020-03-13 22:05:44\n * @Update log: 手机号登录密码页面\n -->\n<temp"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/components/phoneVerify.vue",
    "chars": 621,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-18 13:45:09\n * @Update: 2019-12-04 18:35:37\n * @Update log: 手机号验证\n -->\n<template"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/components/verifyCode.vue",
    "chars": 1369,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-18 14:11:40\n * @Update: 2019-11-05 19:21:06\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/components/verifyInfo.vue",
    "chars": 2018,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-18 13:55:05\n * @Update: 2019-08-26 14:30:37\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/loginIndex/index.vue",
    "chars": 894,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 10:40:39\n * @Update: 2019-12-04 18:36:37\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/myFavorite/components/albums.vue",
    "chars": 525,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 14:47:32\n * @Update: 2019-10-04 13:05:28\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/myFavorite/components/artists.vue",
    "chars": 468,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 14:47:43\n * @Update: 2019-10-04 13:22:14\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/myFavorite/components/column.vue",
    "chars": 229,
    "preview": "<template>\n  <div>专栏页面</div>\n</template>\n\n<script>\nexport default {\n  name: '',\n  props: {\n    columnInfo: {\n      type:"
  },
  {
    "path": "musicPlayer/src/pages/myFavorite/components/mlog.vue",
    "chars": 338,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 14:48:35\n * @Update: 2019-10-01 14:48:35\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/myFavorite/components/videos.vue",
    "chars": 507,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 14:48:10\n * @Update: 2019-10-04 13:36:59\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/myFavorite/index.vue",
    "chars": 2972,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-01 14:23:14\n * @Update: 2019-10-04 12:48:02\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/nav/components/login-bottom.vue",
    "chars": 1302,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-30 13:05:17\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/nav/components/login-icons-bottom.vue",
    "chars": 972,
    "preview": "<template>\n  <div class=\"wrapper pd23 border-bottom\">\n    <ul>\n      <li class=\"list\" v-for=\"(item, index) in loginIcons"
  },
  {
    "path": "musicPlayer/src/pages/nav/components/login-icons-top.vue",
    "chars": 954,
    "preview": "<template>\n  <div class=\"wrapper pd23 border-bottom\">\n    <ul>\n      <li class=\"list\" v-for=\"(item, index) in loginIcons"
  },
  {
    "path": "musicPlayer/src/pages/nav/components/login-icons.vue",
    "chars": 1489,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-03 19:51:17\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/nav/components/login-top.vue",
    "chars": 5768,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-12-04 18:47:09\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/nav/components/login.vue",
    "chars": 1741,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-05 18:46:36\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/nav/index.vue",
    "chars": 2572,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-11-02 12:49:39\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/recentlyPlayed/index.vue",
    "chars": 2288,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-27 21:51:00\n * @Update: 2019-10-26 08:12:47\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/recommend/fine/index.vue",
    "chars": 1523,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-08 13:17:12\n * @Update: 2019-09-08 16:10:05\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/recommend/general/index.vue",
    "chars": 1637,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-08 13:17:34\n * @Update: 2019-09-17 10:32:47\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/recommend/index.vue",
    "chars": 874,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-06 11:38:30\n * @Update: 2019-09-13 14:52:55\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/recommend/navIndex/navList.vue",
    "chars": 2514,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 19:54:27\n * @Update: 2019-11-12 21:44:53\n * @Update log: 因为存在 url地址自动将中文转换为编码"
  },
  {
    "path": "musicPlayer/src/pages/recommend/recommended/index.vue",
    "chars": 1957,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-09-08 13:17:01\n * @Update: 2019-11-03 14:15:34\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchIndex/components/history.vue",
    "chars": 2282,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-27 13:29:51\n * @Update: 2019-08-31 10:06:52\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchIndex/components/hotSearch.vue",
    "chars": 2381,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-28 10:41:47\n * @Update: 2019-08-28 12:29:24\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchIndex/index.vue",
    "chars": 1203,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-27 12:37:42\n * @Update: 2019-09-01 21:49:23\n * @Update log: 这里用到了父组件调用子组件方法\n -->"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/albumIndex/album.vue",
    "chars": 2197,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:26:25\n * @Update: 2019-11-02 12:45:02\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/artistIndex/artist.vue",
    "chars": 2005,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:26:07\n * @Update: 2019-11-14 15:29:23\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/album.vue",
    "chars": 1083,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:58:10\n * @Update: 2019-11-16 13:44:29\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/artist.vue",
    "chars": 1032,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:57:52\n * @Update: 2019-11-26 13:01:35\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/djRadio.vue",
    "chars": 998,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:58:24\n * @Update: 2019-09-03 10:50:45\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/playList.vue",
    "chars": 1164,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:56:55\n * @Update: 2019-11-01 13:31:54\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/simQuery.vue",
    "chars": 1136,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:57:31\n * @Update: 2019-11-26 12:50:04\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/song.vue",
    "chars": 2265,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:56:42\n * @Update: 2019-11-01 13:25:31\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/user.vue",
    "chars": 859,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:58:35\n * @Update: 2019-11-02 12:48:36\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/components/video.vue",
    "chars": 1373,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 10:57:13\n * @Update: 2019-11-01 13:30:26\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/composite/composite.vue",
    "chars": 4582,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:17:07\n * @Update: 2020-03-13 23:04:01\n * @Update log: 综合页面展示\n -->\n<templat"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/djRadioIndex/djRadio.vue",
    "chars": 2208,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:25:40\n * @Update: 2019-11-14 13:57:40\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/index.vue",
    "chars": 1655,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-30 12:05:38\n * @Update: 2019-11-15 13:48:06\n * @Update log: 更新日志\n * 建立各个组件,将对象传给"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/navIndex/index.vue",
    "chars": 2112,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 19:54:27\n * @Update: 2019-10-29 13:04:54\n * @Update log: 因为存在 url地址自动将中文转换为编码"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/playListIndex/playList.vue",
    "chars": 2247,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:25:20\n * @Update: 2019-11-14 13:53:22\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/singerIndex/select.vue",
    "chars": 1692,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-10 12:40:09\n * @Update: 2019-11-03 13:59:40\n * @Update log: 这是一个歌手分类页面的选择组件\n  通过"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/singerIndex/singer.vue",
    "chars": 3382,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-10-10 08:19:51\n * @Update: 2019-11-03 13:58:32\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/songIndex/song.vue",
    "chars": 3959,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:24:50\n * @Update: 2019-11-01 13:28:52\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/userIndex/user.vue",
    "chars": 1841,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:24:13\n * @Update: 2019-11-02 12:47:57\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/searchResults/videoIndex/video.vue",
    "chars": 2194,
    "preview": "<!--\n * @Author: 李浩栋\n * @Begin: 2019-08-31 11:23:42\n * @Update: 2019-11-14 15:30:47\n * @Update log: 更新日志\n -->\n<template>"
  },
  {
    "path": "musicPlayer/src/pages/userInfoIndex/components/userDynamic.vue",
    "chars": 2970,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-28 13:07:27\n * @Update: 2019-12-10 18:56:39\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/userInfoIndex/components/userHome.vue",
    "chars": 1040,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-28 09:05:41\n * @Update: 2019-10-28 13:15:05\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/userInfoIndex/index.vue",
    "chars": 9104,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-28 08:56:16\n * @Update: 2019-12-10 18:55:16\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/acg.vue",
    "chars": 727,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:32:12\n * @Update: 2019-11-23 11:49:13\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/animation.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:32:38\n * @Update: 2019-11-23 11:49:40\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/dance.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:32:06\n * @Update: 2019-11-23 11:49:54\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/game.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:32:24\n * @Update: 2019-11-23 11:50:09\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/listenBGM.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:31:56\n * @Update: 2019-11-23 11:50:21\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/musicFestival.vue",
    "chars": 1998,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:31:37\n * @Update: 2019-11-23 11:47:13\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/rock.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:32:17\n * @Update: 2019-11-23 11:50:36\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/scene.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:31:45\n * @Update: 2019-11-23 11:50:47\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/components/singing.vue",
    "chars": 726,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 13:32:01\n * @Update: 2019-11-23 11:51:00\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/index.vue",
    "chars": 2038,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-25 12:55:37\n * @Update: 2019-11-24 12:22:25\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/public.vue",
    "chars": 10341,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-10-26 08:48:50\n * @Update: 2019-11-19 13:51:47\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/videoComments/components/video.vue",
    "chars": 5535,
    "preview": "<template>\n  <div class=\"play-video\" ref=\"playVideo\" @touchstart=\"hideFnBtn\">\n    <circle-loading type=\"absolute\" width="
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/videoComments/components/videoCreator.vue",
    "chars": 1049,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-11-12 13:30:45\n * @Update: 2019-11-12 20:59:21\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/videoComments/components/videoInfo.vue",
    "chars": 2245,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-11-12 13:28:45\n * @Update: 2019-11-12 19:57:30\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/pages/videoIndex/videoComments/index.vue",
    "chars": 8400,
    "preview": "<!--\n * @Author: Lambda\n * @Begin: 2019-11-11 11:49:45\n * @Update: 2019-11-12 22:07:06\n * @Update log: 更新日志\n -->\n<templa"
  },
  {
    "path": "musicPlayer/src/router/index.js",
    "chars": 12761,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-27 17:08:42\n * @Update: 2019-12-07 13:17:07\n * @Update log: 更新日志\n */\nimport Vue fr"
  },
  {
    "path": "musicPlayer/src/store/actions.js",
    "chars": 3278,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-09-12 14:11:33\n * @Update: 2019-09-22 12:15:54\n * @Update log: 更新日志\n */\nimport {\n  SE"
  },
  {
    "path": "musicPlayer/src/store/getters.js",
    "chars": 928,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-08-24 11:44:49\n * @Update: 2019-09-15 12:29:49\n * @Update log: 更新日志\n */\nconst getters"
  },
  {
    "path": "musicPlayer/src/store/index.js",
    "chars": 396,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-09-12 14:23:38\n * @Update log: 更新日志\n */\nimport Vue fr"
  },
  {
    "path": "musicPlayer/src/store/mutation-types.js",
    "chars": 1168,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-08-11 18:00:46\n * @Update log: 更新日志\n */\nexport const "
  },
  {
    "path": "musicPlayer/src/store/mutations.js",
    "chars": 2736,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-09-12 13:48:12\n * @Update log: 更新日志\n */\nimport {\n  SH"
  },
  {
    "path": "musicPlayer/src/store/state.js",
    "chars": 712,
    "preview": "/*\n * @Author: 李浩栋\n * @Begin: 2019-07-30 16:42:30\n * @Update: 2019-10-09 12:15:26\n * @Update log: 更新日志\n */\nexport defaul"
  },
  {
    "path": "musicPlayer/static/.gitkeep",
    "chars": 0,
    "preview": ""
  }
]

About this extraction

This page contains the full source code of the powerdong/Music-player GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 193 files (616.8 KB), approximately 250.2k tokens, and a symbol index with 141 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!