|
目录
【个人练习】网易云音乐PC端仿站 Vue 2(一)
【个人练习】网易云音乐PC端仿站 Vue 2(二)
【个人练习】网易云音乐PC端仿站 Vue 2(三)
【个人练习】网易云音乐PC端仿站 Vue 2(四)
【个人练习】网易云音乐PC端仿站 Vue 2(五)
【个人练习】网易云音乐PC端仿站 Vue 2(六)
【个人练习】网易云音乐PC端仿站 Vue 2(七)
7 根组件
根组件包含:头部导航栏、路由出口、返回顶部组件、音乐播放器组件、底部组件
注:在登录状态下,“我的音乐”页面不包含底部组件
<template>
<div id=&#34;app&#34;>
<!-- 头部导航 -->
<Header />
<!-- 路由出口 -->
<router-view></router-view>
<!-- 返回顶部组件 -->
<GoTop />
<!-- 音乐播放器组件 -->
<Music />
<!-- 底部组件 -->
<Footer v-show=&#34;isShowFooter&#34;/>
</div>
</template>
<script>
import Header from &#39;@/components/Header.vue&#39;
import GoTop from &#39;@/components/GoTop.vue&#39;
import Music from &#39;@/components/Music.vue&#39;
import Footer from &#39;@/components/Footer.vue&#39;
export default {
name: &#39;App&#39;,
components: { Header, GoTop, Music, Footer },
data() {
return {
isShowFooter: true
}
},
computed: {
path() {
return this.$route.path
}
},
watch: {
path() {
if (this.$route.path.indexOf(&#39;/my&#39;) !== -1 && localStorage.getItem(&#39;COOKIE&#39;)) this.isShowFooter = false
else this.isShowFooter = true
}
}
}
</script>7-1 头部导航组件 Header

- 通过this.$route.path获取当前路由地址,判断当前选中的导航栏项目,赋予选中样式。
<template>
<div class=&#34;header&#34;>
<!-- 导航栏 -->
<ul class=&#34;nav&#34;>
<li class=&#34;nav-item&#34; :class=&#34;[curIndex === 0 ? &#39;is-active&#39; : &#39;&#39;]&#34; @click=&#34;curIndex = 0&#34;>
<router-link to=&#34;/&#34;>发现音乐</router-link>
<sub :class=&#34;[curIndex === 0 ? &#39;cur&#39; : &#39;&#39;]&#34;></sub>
</li>
<li class=&#34;nav-item&#34; :class=&#34;[curIndex === 1 ? &#39;is-active&#39; : &#39;&#39;]&#34; @click=&#34;curIndex = 1&#34;>
<router-link to=&#34;/my&#34;>我的音乐</router-link>
<sub :class=&#34;[curIndex === 1 ? &#39;cur&#39; : &#39;&#39;]&#34;></sub>
</li>
<li class=&#34;nav-item&#34; :class=&#34;[curIndex === 2 ? &#39;is-active&#39; : &#39;&#39;]&#34; @click=&#34;curIndex = 2&#34;>
<router-link to=&#34;/friend&#34;>关注</router-link>
<sub :class=&#34;[curIndex === 2 ? &#39;cur&#39; : &#39;&#39;]&#34;></sub>
</li>
<li class=&#34;nav-item&#34;>
<a href=&#34;https://music.163.com/store/product&#34; target=&#34;_blank&#34;>商城</a>
</li>
<li class=&#34;nav-item&#34;>
<a href=&#34;https://music.163.com/st/musician&#34; target=&#34;_blank&#34;>音乐人</a>
</li>
<li class=&#34;nav-item&#34;>
<a href=&#34;https://music.163.com/#/download&#34;>下载客户端</a>
<sup class=&#34;hot&#34;></sup>
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
isFixed: false, // 头部导航栏是否固定
}
}
computed: {
path() {
return this.$route.path // 获取当前路径
},
},
watch: {
path: {
handler() {
// 当前url
this.getCurIndex() // path改变时,更新导航栏item序号
// Header在&#39;我的音乐&#39;页面固定
if (this.$route.path.indexOf(&#39;/my&#39;) !== -1) this.isFixed = true
else this.isFixed = false
},
immediate: true,
}
},
methods: {
// 获取导航栏当前展示的item序号
getCurIndex() {
let path = this.$route.path
if (path.indexOf(&#39;/home&#39;) != -1) this.curIndex = 0
else if (path.indexOf(&#39;/my&#39;) != -1) this.curIndex = 1
else if (path.indexOf(&#39;/friend&#39;) != -1) this.curIndex = 2
else this.curIndex = 0
}
}
}
</script>2. 搜索框:

- 搜索框中输入的关键字使用v-model属性双向绑定,使用变量searchText记录搜索关键字;
- 当搜索框获得焦点,且关键字不为空时,展示搜索建议列表,使用布尔变量isOpenSearch控制搜索建议列表展示与否;在失去焦点时,使用定时器延后将变量isOpenSearch变为false,这样是为了防止点击搜索列表中的歌曲、歌手、专辑、歌单等页面无法跳转。
- 调用接口获取搜索建议:
搜索建议
说明 : 调用此接口 , 传入搜索关键词可获得搜索建议 , 搜索结果同时包含单曲 , 歌手 , 歌单信息
必选参数 :keywords : 关键词
可选参数 :type : 如果传 &#39;mobile&#39; 则返回移动端数据
接口地址 :/search/suggest // api/index.js
// 获取搜索建议
export function reqSearchSuggest(keywords) {
return request(&#39;/search/suggest&#39;, &#39;get&#39;, { &#39;keywords&#39;: keywords })
}
<template>
<div class=&#34;header&#34;>
<!-- 搜索栏 -->
<div class=&#34;search&#34;>
<i class=&#34;el-icon-search&#34;></i>
<input
v-model=&#34;searchText&#34;
type=&#34;text&#34;
placeholder=&#34;音乐/视频/电台/用户&#34;
onfocus=&#34;this.placeholder=&#39;&#39;&#34;
onblur=&#34;this.placeholder=&#39;音乐/视频/电台/用户&#39;&#34;
@focus=&#34;isOpenSearch = true&#34;
@blur=&#34;searchBlur&#34;
/>
</div>
<!-- 搜索建议列表 -->
<div class=&#34;searchList&#34; v-show=&#34;isOpenSearch && searchText&#34;>
<p class=&#34;note&#34;>
<a href=&#34;javascript:;&#34;>搜&#34;{{ searchText }}&#34; 相关用户</a>&gt;
</p>
<div class=&#34;rap&#34;>
<div class=&#34;songs&#34; v-if=&#34;suggestList.songs&#34;>
<h3 class=&#34;hd&#34;>
<i class=&#34;icn&#34;></i>单曲
</h3>
<ul class=&#34;bd&#34;>
<li v-for=&#34;item in suggestList.songs&#34; :key=&#34;item.id&#34;>
<router-link :to=&#34;{ path: &#39;/song&#39;, query: { id: `${item.id}` } }&#34; class=&#34;ellipsis&#34;>{{item.name}}</router-link>
</li>
</ul>
</div>
<div class=&#34;artists&#34; v-if=&#34;suggestList.artists&#34;>
<h3 class=&#34;hd&#34;>
<i class=&#34;icn&#34;></i>歌手
</h3>
<ul class=&#34;bd odd&#34;>
<li v-for=&#34;item in suggestList.artists&#34; :key=&#34;item.id&#34;>
<router-link :to=&#34;{ path: &#39;/artist&#39;, query: { id: `${item.id}` } }&#34; class=&#34;ellipsis&#34;>{{item.name}}</router-link>
</li>
</ul>
</div>
<div class=&#34;albums&#34; v-if=&#34;suggestList.albums&#34;>
<h3 class=&#34;hd&#34;>
<i class=&#34;icn&#34;></i>专辑
</h3>
<ul class=&#34;bd&#34;>
<li v-for=&#34;item in suggestList.albums&#34; :key=&#34;item.id&#34;>
<router-link :to=&#34;{ path: &#39;/album&#39;, query: { id: `${item.id}` } }&#34; class=&#34;ellipsis&#34;>{{item.name}}</router-link>
</li>
</ul>
</div>
<div class=&#34;playlists&#34; v-if=&#34;suggestList.playlists&#34;>
<h3 class=&#34;hd&#34;>
<i class=&#34;icn&#34;></i>歌单
</h3>
<ul class=&#34;bd odd&#34;>
<li v-for=&#34;item in suggestList.playlists&#34; :key=&#34;item.id&#34;>
<router-link :to=&#34;{ path: &#39;/playlist&#39;, query: { id: `${item.id}` } }&#34; class=&#34;ellipsis&#34;>{{item.name}}</router-link>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
export default{
data () {
return {
searchText: &#39;&#39;, // 搜索内容
isOpenSearch: true, // 是否展示搜索建议列表
suggestList: {}, // 搜索建议
}
},
watch: {
searchText() {
// 搜索栏搜索内容
if (this.searchText !== &#39;&#39;) {
this.getSearchSuggest()
}
}
},
methods: {
// 搜索栏失去焦点
searchBlur() {
setTimeout(() => {
this.isOpenSearch = false
}, 100)
},
// 获取搜索建议
async getSearchSuggest() {
let result = await this.$api.reqSearchSuggest(this.searchText)
this.suggestList = result.result
},
}
}
</script>
3. 判断本地localStorage是否保存有cookie,如果有cookie,根据cookie获取用户信息,当前处于登录状态,头部导航栏中显示用户头像;否则为未登录状态,头部导航栏中显示登录按钮。
获取账号信息
说明 : 登录后调用此接口 ,可获取用户账号信息
接口地址 :/user/account // 根据Cookie获取账号信息
export function reqUserAccount() {
return request(&#39;/user/account&#39;, &#39;post&#39;)
}
<template>
<div class=&#34;header&#34;>
<!-- 登录按钮 -->
<div class=&#34;login&#34; v-if=&#34;!isLogin&#34; @click=&#34;openLoginBox&#34;>
<a href=&#34;javascript:;&#34;>登录</a>
</div>
<!-- 用户头像 -->
<div class=&#34;user&#34; v-if=&#34;isLogin&#34;>
<img :src=&#34;userImg&#34; @mouseenter=&#34;isShowUserBox = true&#34; />
</div>
</div>
</template>
<script>
export default {
data () {
return {
isLogin: false, // 是否登录
userImg: &#39;&#39;, // 用户头像
isShowUserBox: false, // 是否展示用户属性框
}
},
mounted() {
// 获取用户信息
try {
if (this.$store.state.user.cookie) {
this.$store
.dispatch(&#39;user/getUserInfo&#39;, this.$store.state.user.cookie)
.then(() => {
this.userImg = this.$store.state.user.userInfo.avatarUrl
this.isLogin = true
})
}
} catch (err) {
console.log(err)
this.isLogin = false
}
}
}
</script>4. 在登录状态下,当鼠标移动到用户头像时,展示用户属性框。这里只实现了退出登录的功能,当点击退出登录时,清除本地保存的cookie以及用户ID,清除用户信息,展示登录按钮。

退出登录
说明 : 调用此接口 , 可退出登录
接口地址:/logout // 退出登录
export function reqLogout() {
return request(&#39;/logout&#39;, &#39;get&#39;)
}
<template>
<div class=&#34;header&#34;>
<!-- 用户属性框 -->
<div class=&#34;user-box&#34; v-show=&#34;isShowUserBox&#34; @mouseleave=&#34;isShowUserBox = false&#34;>
<i class=&#34;arrow&#34;></i>
<ul>
<li>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-user&#34;></i>我的主页</a>
</li>
<li>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-message&#34;></i>我的消息</a>
</li>
<li>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-level&#34;></i>我的等级</a>
</li>
<li>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-vip&#34;></i>VIP会员</a>
</li>
</ul>
<ul>
<li>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-settings&#34;></i>个人设置</a>
</li>
<li>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-validation&#34;></i>实名认证</a>
</li>
</ul>
<ul>
<li @click=&#34;logout&#34;>
<a href=&#34;javascript:;&#34;><i class=&#34;icon-logout&#34;></i>退出登录</a>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
methods: {
// 退出登录
logout() {
this.isLogin = false
this.isShowUserBox = false
this.$store.dispatch(&#39;user/logout&#39;).then(() => {
location.reload()
})
},
}
}
</script> |
|