cloud-music
非常感谢那些无私开源的程序员,希望我也能够有能力像你们那样,开源很多很有意思的东西~~
//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>
作者:jser_dimple
-------------------------------------------
个性签名:一个人在年轻的时候浪费自己的才华与天赋是一件非常可惜的事情
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏5毛买辣条行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!
微信
支付宝