非常感谢那些无私开源的程序员,希望我也能够有能力像你们那样,开源很多很有意思的东西~~

//index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>cloud-music</title>
<meta http-equiv=X-UA-Compatible content="IE=edge">
<meta name=format-detection content="telephone=no">
<meta name=format-detection content="email=no">
<meta name=apple-mobile-web-app-capable content=yes>
<meta name=apple-mobile-web-app-status-bar-style content=black>
<meta name=full-screen content=yes>
<meta name=browsermode content=application>
<meta name=x5-orientation content=portrait>
<meta name=x5-fullscreen content=true>
<meta name=x5-page-mode content=app>
<!--清除缓存-->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,minimal-ui">
<link rel="icon" href="static/logo.ico" type="image/x-icon" />
<link rel="shortcut icon" href="static/logo.ico" type="image/x-icon" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic">
<link href="http://cdn.bootcss.com/material-design-icons/3.0.1/iconfont/material-icons.min.css" rel="stylesheet">
<script>
;(function (doc, win, undefined) {
let docEl = doc.documentElement,
resizeEvt = 'orientationchange' in win? 'orientationchange' : 'resize',
recalc = function () {
let clientWidth = docEl.clientWidth;
if (clientWidth === undefined) return;
docEl.style.fontSize = 20 * (clientWidth / 320) + 'px';
};
if (doc.addEventListener === undefined) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false)
})(document, window);
</script>
<style>
* {
margin: 0;
padding: 0;
}
a {
text-decoration: none;
}
p, span {
font-size: 12px;
}
</style>
</head>
<body>
<div id="app">
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
<script src="//cdn.bootcss.com/vue/2.2.5/vue.min.js"></script>
<script src="//cdn.bootcss.com/vue-router/2.3.0/vue-router.min.js"></script>
<script src="//cdn.bootcss.com/vuex/2.2.1/vuex.min.js"></script>
<script src="//cdn.bootcss.com/axios/0.15.3/axios.min.js"></script>
</body>
</html>
//app.vue
<template>
<div>
<!-- 主界面部分 -->
<loading :show="loadingShow"></loading>
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>
<player v-show="songList.length > 0 && !showDetail"></player>
</div>
</template>
<script>
import player from './components/playerBar/playerBar';
import loading from './components/loading/overall-loading';
import { mapGetters } from 'vuex';
export default {
name: 'app',
mounted () {
console.log('%c 浅滩戏虾', 'background-image:-webkit-gradient( linear, left top,right top, color-stop(0, #00a419),color-stop(0.15, #f44336), color-stop(0.29, #ff4300),color-stop(0.3, #AA00FF),color-stop(0.4, #8BC34A), color-stop(0.45, #607D8B),color-stop(0.6, #4096EE), color-stop(0.75, #D50000),color-stop(0.9, #4096EE), color-stop(1, #FF1A00));color:transparent;-webkit-background-clip:text;font-size:13px;');
},
computed: {
...mapGetters([
'songList',
'showDetail',
'loadingShow'
])
},
components: {
player,
loading
}
};
</script>
//main.js
import Vue from 'vue';
import store from './vuex';
import VueRouter from 'vue-router';
import VueLazyload from 'vue-lazyload'; // 引入图片懒加载模块
import App from './App';
import routes from './routers';
// import {loadFromlLocal} from './common/js/store'; // 公共方法:本地缓存
// 注册为全局组件
Vue.use(VueRouter);
// error,loading是图片路径, 用require引入
Vue.use(VueLazyload, {
error: require('./assets/404.png'),
loading: require('./assets/loading.jpg'),
attempt: 1
}
); const scrollBehavior = (to, from, savedPosition) => {
if (savedPosition) {
// savedPosition is only available for popstate navigations.
return savedPosition;
} else {
let position = {};
// new navigation.
// scroll to anchor by returning the selector
if (to.hash) {
position.selector = to.hash;
}
// check if any matched route config has meta that requires scrolling to top
if (to.matched.some(m => m.meta.scrollToTop)) {
// cords will be used if no selector is provided,
// or if the selector didn't match any element.
position.x = 0;
position.y = 0;
}
// if the returned position is falsy or an empty object,
// will retain current scroll position.
return position;
}
}; const router = new VueRouter({
// mode: 'history',
'linkActiveClass': 'active',
routes, // (缩写)相当于 routes: routes
scrollBehavior
}); /**
* 创建和挂载根实例。
* 记得要通过 router 配置参数注入路由,
* 从而让整个应用都有路由功能
*/
const routerApp = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app'); /**
* loadFromlLocal()是读取本地缓存数据,具体common/js/store.js 查看
*/
// if (!loadFromlLocal('music', 'find', false)) {
// router.push('/find');
// }
export default routerApp;
//router.js
/**
* 整个app的路由设置
*/
const router = [{
path: '/find', // 引导页
name: 'index',
component (resolve) {
require.ensure(['./views/index'], () => {
resolve(require('./views/index'));
});
},
children: [{
path: '/find', // 发现
name: 'find',
component (resolve) {
require.ensure(['./views/find/find'], () => {
resolve(require('./views/find/find'));
});
},
meta: { keepAlive: true }
}],
meta: { keepAlive: true }
}, {
path: '/search', // 搜索页
name: 'search',
component (resolve) {
require.ensure(['./views/search/search'], () => {
resolve(require('./views/search/search'));
});
},
meta: { keepAlive: true }
}, {
path: '/player/:id', // 单曲播放页
name: 'player',
component (resolve) {
require.ensure(['./views/detail/player/player'], () => {
resolve(require('./views/detail/player/player'));
});
},
meta: { keepAlive: false }
}, {
path: '/playLists/:id', // 歌单详情页
name: 'playLists',
component (resolve) {
require.ensure(['./views/detail/playList/playlists'], () => {
resolve(require('./views/detail/playList/playlists'));
});
},
meta: { keepAlive: false }
}, {
path: '/singer/:id', // 歌手详情页
name: 'singer',
component (resolve) {
require.ensure(['./views/detail/singer/singer'], () => {
resolve(require('./views/detail/singer/singer'));
});
},
meta: { keepAlive: false }
}, {
path: '/album/:id', // 专辑详情页
name: 'album',
component (resolve) {
require.ensure(['./views/detail/album/album'], () => {
resolve(require('./views/detail/album/album'));
});
},
meta: { keepAlive: false }
}, {
path: '/user/:id', // 用户详情页
name: 'user',
component (resolve) {
require.ensure(['./views/detail/user/user'], () => {
resolve(require('./views/detail/user/user'));
});
},
meta: { keepAlive: false }
}, {
path: '/ranking/:idx', // 榜单详情页
name: 'ranking',
component (resolve) {
require.ensure(['./views/detail/ranking/ranking'], () => {
resolve(require('./views/detail/ranking/ranking'));
});
},
meta: { keepAlive: false }
}, {
path: '/mv/:id', // 视频播放
name: 'mv',
component (resolve) {
require.ensure(['./views/detail/mvPlay/mvPlay'], () => {
resolve(require('./views/detail/mvPlay/mvPlay'));
});
},
meta: { keepAlive: false }
}, {
path: '/playListComment/:id', // 歌单评论
name: 'playListComment',
component (resolve) {
require.ensure(['./views/detail/playList/playListComment'], () => {
resolve(require('./views/detail/playList/playListComment'));
});
},
meta: { keepAlive: false }
}, {
path: '/albumComment/:id', // 专辑评论
name: 'albumComment',
component (resolve) {
require.ensure(['./views/detail/album/albumComment'], () => {
resolve(require('./views/detail/album/albumComment'));
});
},
meta: { keepAlive: false }
}, {
path: '/rankingComment/:id', // 排行榜歌单评论
name: 'rankingComment',
component (resolve) {
require.ensure(['./views/detail/ranking/rankingComment'], () => {
resolve(require('./views/detail/ranking/rankingComment'));
});
},
meta: { keepAlive: false }
}, {
path: '*', redirect: '/find' // url错误重回定向
}];
export default router;
//find.vue
<template>
<transition name="fade">
<div class="find-page">
<tab :line-width=2 active-color='#b72712' defaultColor='#666' bar-active-color='#b72712'
v-model="index">
<!-- 切换 -->
<tab-item class="vux-center" :selected="type === item" v-for="(item, index) in tabList"
@click="type = item" :key="index" style="background-color: #fdfffe;">{{item}}
</tab-item>
</tab>
<!-- 轮播切换的 -->
<!-- 这个是左右的那种切换 -->
<swiper v-model="index" height="100%" :show-dots="false" class="swiper-container" style="width:100%;height: 100%;padding-bottom: 90px;background-color: #eef2f1;">
<swiper-item :key="1">
<div class="tab-swiper vux-center">
<v-recommend></v-recommend>
</div>
</swiper-item>
<swiper-item :key="2">
<div class="tab-swiper vux-center">
<v-play-lists></v-play-lists>
</div>
</swiper-item>
<swiper-item :key="3">
<div class="tab-swiper vux-center">
<v-ranking></v-ranking>
</div>
</swiper-item>
</swiper>
</div>
</transition>
</template>
<script>
import { Tab, TabItem, Swiper, SwiperItem } from 'vux';
import vRecommend from './recommend/recommend';
import vPlayLists from './playLists/playLists';
import vRanking from './ranking/ranking';
//tabList:list()
const list = () => ['个性推荐', '歌单', '排行榜'];
export default {
name: 'find',
data () {
return {
index: 0,
tabList: list(),
type: '个性推荐'
};
},
components: {
vPlayLists,
vRecommend,
vRanking,
Tab,
TabItem,
Swiper,
SwiperItem
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'find.styl';
</style>
//activitysList.vue
<template>
<ul class="activitys-area">
<li class="activity-card-find" v-for="(data, index) in activitys" :key="index">
<img v-lazy="data.picUrl + '?param=400y200'" lazy="loading">
<h2 style="-webkit-box-orient: vertical;">{{data.name}}</h2>
</li>
</ul>
</template>
<script>
export default {
name: 'v-activity-list',
props: {
activitys: {
type: Array,
default: []
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import 'activitysList.styl';
</style>
//mvList.vue
<template>
<ul class="MV-area">
<li class="mv-card-find" v-for="(data, index) in MVs" style="flex: 0 0 49.5%" @click="jumpMvDetail(data.id)" :key="index">
<img v-lazy="data.picUrl + '?param=400y200'" lazy="loading">
<h2 style="-webkit-box-orient: vertical;">{{data.name}}</h2>
</li>
</ul>
</template>
<script>
export default {
name: 'v-mv-list',
props: {
MVs: {
type: Array,
default: []
}
},
methods: {
jumpMvDetail(id) {
this.$router.push({
path: '/mv/' + id
});
}
}
};
</script>
//newSongList.vue
<template>
<ul class="newSongList-area">
<li class="newSongList-card-find" v-for="(data, index) in newSong" @click="jumpAlbumDetail(data.song.album.id)" :key="index">
<img v-lazy="data.song.album.picUrl+ '?param=200y200'" lazy="loading">
<h2 style="-webkit-box-orient: vertical;-webkit-line-clamp: 1">{{data.name}}</h2>
<p style="-webkit-box-orient: vertical;">{{data.song.artists[0].name}}</p>
</li>
</ul>
</template>
<script>
export default {
name: 'v-new-song-lists',
props: {
newSong: {
type: Array,
default: []
}
},
methods: {
jumpAlbumDetail(id) {
this.$router.push({
path: '/album/' + id
});
}
}
};
</script>
//djProgram.vue
<template>
<ul class="djProgram-area">
<li class="djProgram-card-find" v-for="(data, index) in djProgram" :key="index">
<img v-lazy="data.picUrl+ '?param=200y200'" lazy="loading">
<h2 style="-webkit-box-orient: vertical;">{{data.name}}</h2>
</li>
</ul>
</template>
<script>
export default {
name: 'v-dj-program-lists',
props: {
djProgram: {
type: Array,
default: []
}
},
methods: {
jumpPlayListsDetail(id) {
this.$router.push({
path: '/playLists/' + id
});
}
}
};
</script>
//recommend.vue
<template>
<div class="recommend-area">
<div id="slider">
<swiper :options="swiperOption" style="height: 100%;">
<swiper-slide v-for="(item, index) in slide_list" :key="index"><img :src="item" class="banner-item" alt="" style="width: 100%; height: 100%;"></swiper-slide>
<div class="swiper-pagination swiper-pagination-white" slot="pagination"></div>
</swiper>
</div>
<div class="recommend-playLists-area">
<h1 class="title">推荐歌单</h1>
<!-- 推荐歌单,有与后端交互 -->
<v-play-lists :playlists="playlists"></v-play-lists>
</div>
<div class="recommend-activitys-area">
<h1 class="title">独家放送</h1>
<v-activitys-list :activitys="activitys"></v-activitys-list>
</div>
<div class="recommend-mv-area">
<h1 class="title">最新音乐</h1>
<v-new-song-list :newSong="newSong"></v-new-song-list>
</div>
<div class="recommend-mv-area">
<h1 class="title">推荐MV</h1>
<v-mv-list :MVs="MVs"></v-mv-list>
</div>
<div class="recommend-mv-area">
<h1 class="title">主播电台</h1>
<v-dj-program-list :djProgram="djProgram"></v-dj-program-list>
</div>
</div>
</template>
<script>
import api from '../../../api/index';
import { swiper, swiperSlide } from 'vue-awesome-swiper';
// v-for
import vPlayLists from '../../../components/list/find/recommend/playLists';
// v-for
import vActivitysList from '../../../components/list/find/recommend/activitysList'; import vMvList from '../../../components/list/find/recommend/mvList';
import vNewSongList from '../../../components/list/find/recommend/newSongList';
import vDjProgramList from '../../../components/list/find/recommend/djProgram';
const imgList = ['/static/banner1.jpg', '/static/banner2.jpg', '/static/banner3.jpg', '/static/banner4.jpg'];
export default {
name: 'v-recommend',
data () {
return {
swiperOption: {
pagination: '.swiper-pagination',
paginationClickable: true,
autoplay: 2500
},
slide_list: imgList,
playlists: [],
activitys: [],
MVs: [],
newSong: [],
djProgram: []
};
},
mounted () {
this.getPersonalizedResource();
this.getPrivatecontentResource();
this.getPersonalizedMvResource();
this.getNewSongResource();
this.getDjProgramResource();
},
methods: {
getPersonalizedResource() {
api.getPersonalized().then((response) => {
this.playlists = response.data.result;
console.log('getPersonalizedResource',response);
})
.catch((response) => {
console.log(response);
});
},
getPrivatecontentResource() {
api.getPrivatecontent().then((response) => {
this.activitys = response.data.result;
})
.catch((response) => {
console.log(response);
});
},
getPersonalizedMvResource() {
api.getPersonalizedMv().then((response) => {
this.MVs = response.data.result;
})
.catch((response) => {
console.log(response);
});
},
getNewSongResource() {
api.getNewSong().then((response) => {
this.newSong = response.data.result.slice(0, 6);
})
.catch((response) => {
console.log(response);
});
},
getDjProgramResource() {
api.getDjProgram().then((response) => {
this.djProgram = response.data.result.slice(0, 6);
})
.catch((response) => {
console.log(response);
});
}
},
components: {
swiper,
swiperSlide,
vPlayLists,
vActivitysList,
vMvList,
vNewSongList,
vDjProgramList
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import 'recommend.styl';
</style>

//主界面部分代码
<template>
<!-- 主界面部分 -->
<transition name="fade">
<div class="index">
<!-- 侧边栏 -->
<asideMenu v-show="isShowAsideMenu"></asideMenu>
<!-- 头部 -->
<v-header></v-header>
<router-view></router-view>
</div>
</transition>
</template>
<script>
import vHeader from '../components/header/header';
import asideMenu from '../components/aside/aside'; export default {
computed: {
isShowAsideMenu() {
return this.$store.state.isShowAsideMenu;
}
},
components: {
vHeader,
asideMenu
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'index.styl';
</style>
//header.vue
<template>
<div class="header">
<div class="name">
<span @click="showAsideMenu(true)" class="func"><i class="func-icon"></i></span>
<router-link to="/find" class="item">
<span class="music"><i class="music-icon"></i></span>
</router-link>
<router-link to="/search" class="item">
<span class="personal"><i class="personal-icon"></i></span>
</router-link>
<span class="search"><i @click="toSearch" class="search-icon"></i></span>
</div>
</div>
</template> <script>
export default {
name: 'header',
methods: {
toSearch () {
this.$router.push('/search');
},
showAsideMenu (flag) {
this.$store.commit('showAsideMenu', flag);
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import "header.styl";
</style>
//src/components/aside/aside.vue
<template>
<transition name="fadeIn">
<div class="aside-menu">
<i @click="showAsideMenu" class="back"></i>
<div class="aside">
<div class="info">
<img src="https://avatars2.githubusercontent.com/u/16521402?v=3&u=225ef33c491d879294c4cb06621ec15f5b01f02a&s=400">
<p class="author">浅滩戏虾</p>
</div>
</div>
<div @click.stop.prevent="showAsideMenu" class="mask"></div>
</div>
</transition>
</template>
<script>
export default {
name: 'aside',
data () {
return {
isSignIn: false
};
},
methods: {
showAsideMenu () {
this.$store.commit('showAsideMenu', false);
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import "aside.styl";
</style>



//src/views/find/playLists/playLists.vue
<template>
<div class="playLists-area">
<button-tab v-model="index">
<button-tab-item @on-item-click="selectType()">最新</button-tab-item>
<button-tab-item @on-item-click="selectType()">最热</button-tab-item>
</button-tab>
<div class="playLists">
<ul>
<li v-for="(data, index) in playlists" :key="index">
<v-play-list :data="data"></v-play-list>
</li>
</ul>
</div>
</div>
</template>
<script>
import api from '../../../api/index';
import { ButtonTab, ButtonTabItem } from 'vux';
import vPlayList from '../../../components/card/findCard/playList/playList';
export default {
name: 'v-play-lists',
data () {
return {
index: 0,
keys: 'new',
playlists: []
};
},
mounted: function() {
this.getTopPlaylistResource();
},
methods: {
selectType () {
this.keys = this.index ? 'hot' : 'new';
//点击切换,从后端获取数据
this.getTopPlaylistResource();
},
getTopPlaylistResource() {
this.$store.commit('update_loading', true);
api.getTopPlaylistResource(this.keys, 20, 0).then((response) => {
this.playlists = response.data.playlists;
//数据会先渲染出来,所以要重新渲染完后执行
// $nextTick() 在dom 重新渲染完后执行
this.$nextTick(() => {
this.$store.commit('update_loading', false);
});
})
.catch((response) => {
console.log(response);
});
}
},
components: {
vPlayList,
ButtonTab,
ButtonTabItem
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'playLists.styl';
</style>
//src/components/list/find/ranking/songsList.vue
<template>
<ul class="ranking-songsList">
<li style="-webkit-box-orient: vertical;" v-for="(item, index) in data" :key="index">{{index + 1}}.{{item.name}}-{{item.artists[0].name}}</li>
</ul>
</template>
<script>
export default {
name: 'v-songs-list',
props: {
data: {
type: Array,
default: []
//如果有和后端交互的数据,页面会展示
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus">
@import 'songsList.styl';
</style>

//search.vue
<template lang="html">
<transition name="fade">
<div class="search-page">
<div class='header-other'>
<span @click="goBack" class="back"><i class="back-icon"></i></span>
<div class="input">
<input v-model="keywords" @keyup.enter="toSearch(keywords)" type="text" placeholder='搜素音乐、歌手、歌词、用户'>
<i @click="keywords=''" v-show="keywords!==''&&!isShowHot" class="icon-cancel"></i>
</div>
</div>
<div class="hot" v-if="isShowHot">
<p>热门搜索</p>
<ul class="keywords">
<li v-for="item of hotKeywords" v-text="item" @click="toSearch(item)" class="keyword"></li>
</ul>
</div>
<div v-else class="search-list">
<tab :line-width=2 active-color='#b72712' defaultColor='#666' bar-active-color='#b72712'
v-model="index">
<tab-item class="vux-center" :selected="type === item" v-for="(item, index) in tabList"
@click="type = item" :key="index">{{item}}
</tab-item>
</tab>
<swiper v-model="index" height="100%" :show-dots="false" class="swiper-container">
<swiper-item :key="1">
<div class="tab-swiper vux-center search-area">
<v-single-list :songs="songs"></v-single-list>
</div>
</swiper-item>
<swiper-item :key="2">
<div class="tab-swiper vux-center search-area">
<v-singer-list :singer="singer"></v-singer-list>
</div>
</swiper-item>
<swiper-item :key="3">
<div class="tab-swiper vux-center search-area">
<v-album-list :albums="albums"></v-album-list>
</div>
</swiper-item>
<swiper-item :key="4">
<div class="tab-swiper vux-center search-area">
<v-play-lists :playlist="playlist"></v-play-lists>
</div>
</swiper-item>
<swiper-item :key="5">
<div class="tab-swiper vux-center search-area">
<v-user-list :user="user"></v-user-list>
</div>
</swiper-item>
<swiper-item :key="6">
<div class="tab-swiper vux-center search-area">
<v-mv-list :MVs="mvs"></v-mv-list>
</div>
</swiper-item>
</swiper>
</div>
</div>
</transition>
</template> <script>
import api from '../../api/index';
import { Tab, TabItem, Swiper, SwiperItem } from 'vux';
import vSingleList from '../../components/list/search/singleList';
import vSingerList from '../../components/list/search/singerList';
import vAlbumList from '../../components/list/search/albumList';
import vPlayLists from '../../components/list/search/playLists';
import vUserList from '../../components/list/search/userList';
import vMvList from '../../components/list/search/mvList';
const list = () => ['单曲', '歌手', '专辑', '歌单', '用户', 'MV'];
const hotKeywordsList = () => ['清白之年', '我喜欢上你时的内心活动', '我想和你唱',
'hyukoh', '童话镇', '陈奕迅', '漂洋过海来看你', '许嵩', '成都', '林俊杰'];
export default {
name: 'search',
data () {
return {
index: 0,
tabList: list(),
hotKeywords: hotKeywordsList(),
type: '单曲',
keywords: '',
isShowHot: true,
songs: [],
singer: [],
albums: [],
playlist: [],
user: [],
mvs: []
};
},
// watch $route 决定是否清除关键词
watch: {
'$route' (to, from) {
if (from.name === 'find') {
this.keywords = '';
this.isShowHot = true;
}
}
},
methods: {
initSearchList () {
this.getSingleResource(); // 获取搜索单曲
this.getAlbumResource(); // 获取搜索专辑
this.getSingerResource(); // 获取搜索歌手
this.getPlayListResource(); // 获取搜索歌单
this.getUserResource(); // 获取搜索用户
this.getMvResource(); // 获取搜索MV
},
goBack () {
//返回
this.$router.push({
path: '/find'
});
},
// 关键词搜索
toSearch (keywords) {
//关键词搜索
this.keywords = keywords;
if (this.keywords.trim()) {
this.isShowHot = false;
this.$router.push({
path: '/search',
query: {
keywords: keywords
}
});
this.initSearchList();
}
},
// 获取搜索单曲
getSingleResource() {
this.$store.commit('update_loading', true);
//获取搜索单曲
api.getSearchResource(this.$route.query.keywords, 1, 30, 0)
.then((response) => {
this.songs = response.data.result.songs;
// $nextTick() 在dom 重新渲染完后执行
this.$nextTick(() => {
this.$store.commit('update_loading', false);
});
})
.catch((response) => {
console.log(response);
});
},
// 获取搜索专辑
getSingerResource() {
//与后端交互
api.getSearchResource(this.$route.query.keywords, 100, 30, 0)
.then((response) => {
this.singer = response.data.result.artists;
})
.catch((response) => {
console.log(response);
});
},
// 获取搜索歌手
getAlbumResource() {
api.getSearchResource(this.$route.query.keywords, 10, 30, 0)
.then((response) => {
this.albums = response.data.result.albums;
})
.catch((response) => {
console.log(response);
});
},
// 获取搜索歌单
getPlayListResource() {
api.getSearchResource(this.$route.query.keywords, 1000, 30, 0)
.then((response) => {
this.playlist = response.data.result.playlists;
})
.catch((response) => {
console.log(response);
});
},
// 获取搜索用户
getUserResource() {
api.getSearchResource(this.$route.query.keywords, 1002, 30, 0)
.then((response) => {
this.user = response.data.result.userprofiles;
})
.catch((response) => {
console.log(response);
});
},
// 获取搜索MV
getMvResource() {
api.getSearchResource(this.$route.query.keywords, 1004, 30, 0)
.then((response) => {
this.mvs = response.data.result.mvs;
})
.catch((response) => {
console.log(response);
});
}
},
components: {
Tab,
TabItem,
Swiper,
SwiperItem,
vSingleList,
vSingerList,
vAlbumList,
vPlayLists,
vUserList,
vMvList
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import "search.styl";
</style>

//singerList.vue
<template>
<ul class="singer-list">
<li class="singer-card" v-for="(data, index) in singer" @click="jumpSingerDetail(data.id)" :key="index">
<img v-lazy="data.picUrl + '?param=200y200'" lazy="loading" class="avatar">
<p class="singer-name">
<span class="name" style="-webkit-box-orient: vertical;">{{data.name}}</span>
<span class="trans" v-show="data.trans">({{data.trans}})</span>
</p>
</li>
</ul>
</template>
<script>
export default {
name: 'v-singer-list',
props: {
singer: {
type: Array,
default: []
}
},
methods: {
jumpSingerDetail(id) {
this.$router.push({
path: '/singer/' + id
});
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'singerList.styl';
</style>
//albumList.vue
<template>
<ul class="album-list">
<v-album-card :data="data" v-for="(data, index) in albums" :key="index"></v-album-card>
</ul>
</template>
<script>
import vAlbumCard from '../../card/searchCard/albumCard';
export default {
name: 'v-album-list',
components: {
vAlbumCard
},
props: {
albums: {
type: Array,
default: []
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'albumList.styl';
</style>
//userList.vue
<template>
<ul class="user-list">
<li v-for="(data, index) in user" class="user-card" @click="jumpUserDetail(data.userId)" :key="index">
<img v-lazy="data.avatarUrl + '?param=200y200'" lazy="loading" class="avatarImage">
<div class="avatar-info">
<p class="avatar-name">
{{data.nickname}}
<span class="gender-man" v-if="data.gender === 1"><i class="man-icon"></i></span>
<span class="gender-female" v-else><i class="female-icon"></i></span>
</p>
<p class="avatar-intro" style="-webkit-box-orient: vertical;">{{data.signature}}</p>
</div>
</li>
</ul>
</template>
<!-- v-if v-else的使用 -->
<script>
export default {
name: 'v-user-card',
props: {
user: {
type: Array,
default: []
}
},
methods: {
jumpUserDetail(id) {
this.$router.push({
path: '/user/' + id
});
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'userList.styl';
</style>
//mvList.vue
<template>
<ul class="mv-list">
<li class="mv-card" v-for="(data, index) in MVs" style="flex: 0 0 49.5%" @click="jumpMvDetail(data.id)" :key="index">
<img v-lazy="data.cover + '?param=400y200'" lazy="loading" class="mv-image">
<h2 style="-webkit-box-orient: vertical;">{{data.name}}</h2>
<p style="-webkit-box-orient: vertical;">{{data.artistName}}</p>
</li>
</ul>
</template>
<script>
export default {
name: 'v-mv-list',
props: {
MVs: {
type: Array,
default: []
}
},
methods: {
jumpMvDetail(id) {
this.$router.push({
path: '/mv/' + id
});
}
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import 'mvList.styl';
</style>

//user.vue
<template>
<transition name="fade">
<div class="user-detail">
<div class="user-info" :style="{'background-image': 'url(' + backgroundImage + ')'}">
<x-header :left-options="{backText: ''}" style="background-color:inherit; width: 100%;">{{userInfo.nickname}}</x-header>
<img v-lazy="avatarImage + '?param=200y200'" lazy="loading">
<p class="user-name">
{{userInfo.nickname}}
<span class="gender-man" v-if="userInfo.gender === 1"><i class="man-icon"></i></span>
<span class="gender-female" v-else><i class="female-icon"></i></span>
</p>
</div>
<div class="tab-list">
<tab :line-width=2 active-color='#b72712' defaultColor='#666' bar-active-color='#b72712' v-model="index">
<tab-item class="vux-center" :selected="type === item" v-for="(item, index) in tabList" @click="type = item" :key="index">{{item}}</tab-item>
</tab>
<swiper v-model="index" height="100%" :show-dots="false">
<swiper-item :key="0">
<div class="tab-swiper vux-center">
<div class="play-lists-detail">
<ul style="list-style: none;">
<li v-for="(data, index) in playlist" :key="index">
<v-play-lists-card :data="data"></v-play-lists-card>
</li>
</ul>
</div>
</div>
</swiper-item>
<swiper-item :key="1">
<!--<div class="tab-swiper vux-center">-->
<!--<div class="hot-single-list">-->
<!--<ul>-->
<!--<li v-for="(data, order) in playlist">-->
<!--<v-single-card :data="data" :order="order"></v-single-card>-->
<!--</li>-->
<!--</ul>-->
<!--</div>-->
<!--</div>-->
</swiper-item>
</swiper>
</div>
</div>
</transition>
</template>
<script type="text/ecmascript-6">
import api from '../../../api';
import { XHeader, Tab, TabItem, Swiper, SwiperItem } from 'vux';
import vPlayListsCard from '../../../components/card/detail/playlists';
const list = () => ['歌单', '关于TA'];
export default {
data () {
return {
tName: '歌单',
type: '歌单',
tabList: list(),
index: 0,
backgroundColor: '',
userInfo: {},
playlist: {}
};
},
mounted: function() {
this.getUserInfo();
},
methods: {
back () {
this.$router.go(-1);
},
getUserInfo () {
this.$store.commit('update_loading', true);
//与后端交互获取资源
api.getUserPlaylistResource(this.$route.params.id)
.then((response) => {
this.playlist = response.data.playlist;
this.userInfo = response.data.playlist[0].creator;
// $nextTick() 在dom 重新渲染完后执行
this.$nextTick(() => {
this.$store.commit('update_loading', false);
});
})
.catch((response) => {
console.log(response);
});
}
},
computed: {
backgroundImage() {
return '' || (this.userInfo.backgroundUrl + '?param=500y500');
},
avatarImage() {
return '' || this.userInfo.avatarUrl;
}
},
components: {
Tab,
TabItem,
Swiper,
SwiperItem,
XHeader,
vPlayListsCard
}
};
</script>
<style lang="less" scoped>
.vux-swiper {
height: 100%;
} .vux-slider {
height: 100%;
} .tab-swiper {
background-color: #fff;
height: 100%;
}
</style>
<style lang="stylus" rel="stylesheet/stylus">
@import "user.styl";
</style>
//playlists.vue
<template>
<transition name="fade">
<div class="playlist">
<div class="fixed-title" :style="{'background': 'rgba(183, 39, 18, '+ opacity +')'}" style="transition: opacity .1s;" v-show="!isShowDetail">
<x-header :left-options="{backText: ''}" style="background-color:transparent">{{tName}}</x-header>
</div>
<div class="playlist-info" :style="{'background-image': 'url(' + playListImage + '?param=500y500'+ ')'}" v-show="!isShowDetail">
<div class="playlist-info-blur">
<div class="playlist-intro">
<img v-lazy="playListImage" class="playlist-image" lazy="loading" alt="photo" @click="showDetail()">
<div class="playlist-intro-other">
<p class="playlist-title" style="-webkit-box-orient: vertical;">{{playlist.name}}</p>
<div class="playlist-creator" @click="jumpUserDetail(creator.userId)">
<img v-lazy="creatorImage + '?param=100y100'" lazy="loading">
<span class="playlist-nickname" style="-webkit-box-orient: vertical;">{{creator.nickname}}</span>
<span class="more"> > </span>
</div>
</div>
</div>
<div class="playlist-status">
<div class="playCount">
<span class="file"><i class="icon-file"></i></span>
<span>{{playlist.playCount}}</span>
</div>
<div class="commentCount">
<span class="comment" @click="jumpCommentDetail()"><i class="icon-comment"></i></span>
<span>{{playlist.commentCount}}</span>
</div>
<div class="shareCount">
<span class="share"><i class="icon-share"></i></span>
<span>{{playlist.shareCount}}</span>
</div>
</div>
</div>
</div>
<div class="play-list" v-show="!isShowDetail">
<v-play-all :data="commonSongs"></v-play-all>
<ul>
<li v-for="(data, index) in list" :key='index'>
<v-single-card :data="data" :index="index"></v-single-card>
</li>
</ul>
</div>
<v-play-list-detail :data="playlist" v-show="isShowDetail"></v-play-list-detail>
</div>
</transition>
</template>
<script>
import api from '../../../api';
import { XHeader } from 'vux';
import vPlayAll from '../../../components/playAll/playAll.vue';
import vSingleCard from '../../../components/card/detail/singleCard.vue';
import vPlayListDetail from './playListDetail';
export default {
data () {
return {
playlist: {},
tName: '歌单',
creator: {},
data: [],
index: '',
list: [],
commonSongs: [],
backgroundColor: '',
opacity: 0,
isShowDetail: false
};
},
// 解除keep-alive的缓存
beforeRouteEnter: (to, from, next) => {
next(vm => {
window.onscroll = () => {
let opa = window.pageYOffset / 222;
if (opa > 1) {
vm.tName = vm.playlist.name;
vm.opacity = 1;
} else {
vm.tName = '歌单';
vm.opacity = 0;
}
};
});
},
// 路由离开时清除onscroll事件
beforeRouteLeave: (to, from, next) => {
window.onscroll = null;
next();
},
mounted: function() {
let self = this;
this.getPlayListDetail();
this.$root.$on('close-detail', (condition) => {
self.isShowDetail = condition;
});
},
methods: {
showDetail () {
this.isShowDetail = true;
},
jumpUserDetail(id) {
this.$router.push({
path: '/user/' + id
});
},
jumpCommentDetail() {
this.$router.push({
path: '/playListComment/' + this.$route.params.id
});
},
getPlayListDetail () {
//与后端交互获取数据
this.$store.commit('update_loading', true);
api.getPlaylistDetailResource(this.$route.params.id).then((response) => {
this.playlist = response.data.playlist;
this.list = response.data.playlist.tracks;
this.creator = response.data.playlist.creator;
this.songsToCommon(this.list);
// $nextTick() 在dom 重新渲染完后执行
this.$nextTick(() => {
this.$store.commit('update_loading', false);
});
}).catch((error) => {
console.log('加载歌单信息出错:' + error);
});
},
songsToCommon (items) {
let vm = this;
this.commonSongs = items.map(function (item) {
return {
'id': item.id,
'name': item.name,
'singer': vm.getAuthorList(item.ar),
'albumPic': item.al.picUrl,
'location': '',
'album': item.al.id
};
});
},
getAuthorList(authorInfo) {
return authorInfo.map(function (item) {
return item.name;
}).toString();
}
},
computed: {
playListImage() {
return '' || (this.playlist.picUrl);
},
creatorImage() {
return '' || this.creator.avatarUrl;
}
},
components: {
XHeader,
vPlayAll,
vSingleCard,
vPlayListDetail
}
};
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
@import "playlists.styl";
</style>

本文学习自这位非常美丽的小姐姐,表白你哇

cloud-music的更多相关文章

  1. On cloud, be cloud native

    本来不想起一个英文名,但是想来想去都没能想出一个简洁地表述该意思的中文释义,所以就用了一个英文名称,望见谅. Cloud Native是一个刚刚由VMware所提出一年左右的名词.其表示在设计并实现一 ...

  2. 在公有云AZURE上部署私有云AZUREPACK以及WEBSITE CLOUD(六)

    (六)在Website Cloud中添加site 1新建Website,并打开 使用前面创建的用户 newbee@waplab.com 登录租户Portal,新建一个website 新建完成后, 可以 ...

  3. 在公有云AZURE上部署私有云AZUREPACK以及WEBSITE CLOUD(五)

    (五)注册Website Cloud 1 注册Website Cloud 添加Website Cloud   连接Website Cloud 注意, endpoint 是使用Management Se ...

  4. 在公有云AZURE上部署私有云AZUREPACK以及WEBSITE CLOUD(四)

    (四)搭建Website Cloud环境 1安装CONTROLLER主机 在开始安装Web site Cloud之前,读者应该对该服务的拓扑结构有个大概了解. 如图: Controller是非常重要的 ...

  5. 在公有云AZURE上部署私有云AZUREPACK以及WEBSITE CLOUD(二)

    前言 (二)建立虚拟网络环境,以及域控和DNS服务器   1搭建虚拟网络环境 在Azure上创建虚拟网络.本例选择的是东南亚数据中心.后面在创建虚机的时候,也选择这个数据中心. VNet Name: ...

  6. spring/spring boot/spring cloud开发总结

    背景        针对RPC远程调用,都在使用dubbo.dubbox等,我们也是如此.由于社区暂停维护.应对未来发展,我们准备尝试新技术(或许这时候也不算什么新技术了吧),选择使用了spring ...

  7. Oracle Sales Cloud:管理沙盒(定制化)小细节2——使用对象触发器更新数字字段

    在上一篇 "管理沙盒(定制化)小细节1" 的随笔中,我们使用公式法在 "业务机会" 对象(单头)上建立了 "利润合计" 字段,并将它等于 & ...

  8. Oracle Sales Cloud:管理沙盒(定制化)小细节1——利用公式创建字段并显示在前端页面

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的CRM管理系统.由于 Oracle 销售云是基于 Oracle 云环境的,它与传统的管理系统相比,显著特点之一便 ...

  9. Oracle Sales Cloud:报告和分析(BIEE)小细节2——利用变量和过滤器传参(例如,根据提示展示不同部门的数据)

    在上一篇随笔中,我们建立了部门和子部门的双提示,并将部门和子部门做了关联.那么,本篇随笔我们重点介绍利用建好的双提示进行传参. 在操作之前,我们来看一个报告和分析的具体需求: [1] 两个有关联的提示 ...

  10. Oracle Sales Cloud:报告和分析(BIEE)小细节1——创建双提示并建立关联(例如,部门和子部门提示)

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的客户商机管理系统,通过提供丰富的功能来帮助提高销售效率,更好地去了解客户,发现和追踪商机,为最终的销售成交 (d ...

随机推荐

  1. MyBatis映射器(一)--多参数传递方式

    在mybatis映射器的接口中,一般在查询时需要传递一些参数作为查询条件,有时候是一个,有时候是多个.当只有一个参数时,我们只要在sql中使用接口中的参数名称即可,但是如果是多个呢,就不能直接用参数名 ...

  2. Activiti实战03_Hello World

    Hello World如此经典,以至于几乎学习没一门新的技术都是从Hello World开始,可能意味着开启了新世界的大门吧,接下来就让我们一起步入到Activiti的世界中吧! 本文所使用开发环境 ...

  3. Mysql指定部分数据同步

    一.需求背景 朋友的公司需要每天定时从源端定时同步一部分数据到目标端,库中存在company_id列的表,只将指定的company_id列导入到目标端数据库:存在company_id列的表,将表中所有 ...

  4. 901. Online Stock Span [短于线性的时间统计单个元素的Span ]

    Span 指这个元素之前连续的小于这个元素的值有多少个 原理: 维护递减栈 这个栈内的元素是递减的序列 新到一个元素x 依次出栈比x小的(也就是这个元素的Span) 这种问题的关键在于 新来的元素如果 ...

  5. TZOJ 4267 An Easy Puz(深搜)

    描述 Wddpdh find an interesting mini-game in the BBS of WHU, called “An easy PUZ”. It’s a 6 * 6 chess ...

  6. PHP苹果推送实现(APNS)

    以下资料网上收集整理得来 1.在ios dev center制作相关证书和文件用客户端实现(不再赘述,网上很多,) 网上教程: http://blog.csdn.net/lizhenning87/ar ...

  7. 微服务开源生态报告 No.10

    「微服务开源生态报告」,汇集各个开源项目近期的社区动态,帮助开发者们更高效的了解到各开源项目的最新进展. 社区动态包括,但不限于:版本发布.人员动态.项目动态和规划.培训和活动. 非常欢迎国内其他微服 ...

  8. Linq 之 Where操作

    适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句. Where操作包括3种形式,分别为简单形式.关系条件形式 ...

  9. ObservableCollection类

    https://blog.csdn.net/GongchuangSu/article/details/48832721 https://blog.csdn.net/hyman_c/article/de ...

  10. Javascript-正则表达式常用字符集及方法

    正则表达式修饰符(修饰符 可以在全局搜索中不区分大小写) i(ignoreCase)执行对大小写不敏感的匹配 g (global)     执行全局匹配(查找所有匹配而非在找到第一个匹配后停止) m( ...