很是感謝那些無私開源的程序員,但願我也可以有能力像大家那樣,開源不少頗有意思的東西~~css
//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>