接下来实现将歌曲信息添加到数据库
1.我们先要定义好结构,在我们之前通过express xxx构建的项目中定义一个models文件夹,创建一个music.js文件,写入以下代码
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// 定义表结构
var musicObj = new Schema({
song_id: String,
author: String,
title: String,
hot: String
});
module.exports = mongoose.model('musics',musicObj);//暴露出去
2.在我们的routes文件夹下创建一个music.js文件,写入以下代码
var express = require('express');
var Router = express.Router();
//引入刚刚暴露出来的表模型
var musicModel = require('../models/music');
//定义添加到收藏的接口
Router.post('/collect',function(req,res,next){
// 这里是post请求,使用req.body来获取前端传过来的数据
let musiCollect = new musicModel({
song_id: req.body.song_id,
author: req.body.author,
title: req.body.title,
hot: req.body.hot
});
// 保存
musiCollect.save(function(err,doc){
if(err) {
res.json({
states: 0,
msg: err.message
});
}else {
res.json({
states: 1,
msg: '保存成功'
});
}
});
});
//查询收藏的数据
Router.post('/list',function(req,res,next){
musicModel.find({},function(err,doc){
if(err) {
res.json({
states: 0,
msg: err.message
});
}else {
res.json({
states: 1,
msg: doc //查询到的数据,以json格式返回
});
}
});
});
module.exports = Router;//暴露出路由
3.在app.js里面引入暴露出的路由var musicRouter = require('./routes/music');
4.使用该路由app.use('/music', musicRouter);
5.这样我们就可以在前端通过/music/collect来请求我们的添加收藏接口
6.这里需要注意的是前端的端口是8080,后端是3000所以存在跨域问题,在vue项目的config文件夹下有个index.js文件,将反向代理加上
//这里的意思是凡是以/music开头的接口都会指向3000的端口
proxyTable: {
'/music/*':{
target: 'http://localhost:3000'
}
},
7.注意的是这里的配置文件修改后的重启服务
8.先引入import axios from "axios",这里不知道的可以先了解下vue的基础知识,你也可以用vuex来封装这些请求,这里就不做介绍
// 添加到收藏
addMusic(item) {
let datas = {
song_id: item.song_id,
author: item.author,
title: item.title,
hot: item.hot
}
axios.post('/music/collect',datas).then(data=>{
this.$message({
message: '收藏成功',
type: 'success'
});
})
},
9.如果你使用mongoVUE就可以看到数据已经保存成功
22.png
这样首页的列表和查询接口实现完了,添加到数据库也实现了,下面是music.vue的全部代码
<template>
<div>
<div class="bg_player" style="background-image: url("https://y.gtimg.cn/music/photo_new/T002R300x300M000000eilSQ2dYUzX.jpg?max_age=2592000");"></div>
<div id="container">
<!-- <div class="logo" title="♫ 老D在线音乐播放器">
♫ 莫沫达在线音乐播放器
</div> -->
<div class="left-col">
<div class="overlay" style="background-color: rgb(255, 148, 92); opacity: 0.3;"></div>
<div class="intrude-less">
<header class="inner" id="header">
<a href="/" class="profilepic">
<img src="http://www.shawnzeng.com/img/me.JPG" class="animated zoomIn">
</a>
<hgroup>
<h1 class="header-author" style="width:100%"><a href="/">莫沫达</a></h1>
</hgroup>
<p class="header-subtitle">♫ 在线音乐播放器</p>
<p v-for="(item,index) in myType" :key="index" @click="personMusic(item.name)" :class="myChoose === item.name ? 'collectActive' : '' " class="header-subtitle collect">
<i :class="item.icon" style="padding-right:5px"></i>{{item.name}}
</p>
<!-- <audio src="http://sc1.111ttt.com/2017/4/05/10/298101104389.mp3" id="audio"></audio> collectActive-->
<!-- <div id="switch-area" class="switch-area">
<div class="switch-wrap">
<section class="switch-part switch-part1">
<nav class="header-menu">
<a href="javascript:void(0)" title="全部">全部</a>
<a href="javascript:void(0)" title="语言">语言</a>
<a href="javascript:void(0)" title="流派">流派</a>
<a href="javascript:void(0)" title="主题">主题</a>
<a href="javascript:void(0)" title="心情">心情</a>
<a href="javascript:void(0)" title="场景">场景</a>
</nav>
</section>
</div>
</div> -->
</header>
</div>
</div>
<div class="pop-music" title="音乐"></div>
<div class="mid-col" v-if="myChoose === '我的首页'">
<div class="myIndex">
<div class="mod_search" style="background-image:url(https://y.gtimg.cn/mediastyle/yqq/img/bg_search.jpg);">
<div class="search">
<el-input placeholder="请输入内容" v-model="searchParams.query" class="search-input">
<el-button slot="append" icon="el-icon-search" @click="searchMusic"></el-button>
</el-input>
</div>
</div>
<!-- <div class="index__hd">
<h3 class="index__tit"><i class="icon_txt">歌单推荐</i></h3>
</div> -->
<div style="margin-top: 20px;text-align: center;">
<el-radio-group v-model="musicType" size="small" @change="typeChange">
<el-radio-button label="1">新歌榜</el-radio-button>
<el-radio-button label="2">热歌榜</el-radio-button>
<el-radio-button label="11">摇滚榜</el-radio-button>
<el-radio-button label="12">爵士</el-radio-button>
<el-radio-button label="16">流行</el-radio-button>
<el-radio-button label="21">欧美金曲榜</el-radio-button>
<el-radio-button label="22">经典老歌榜</el-radio-button>
<el-radio-button label="23">情歌对唱榜</el-radio-button>
<el-radio-button label="24">影视金曲榜</el-radio-button>
<el-radio-button label="25">网络歌曲榜</el-radio-button>
</el-radio-group>
</div>
<div class="htmleaf-container">
<div class="full-length" v-loading="loading">
<div class="container">
<div class="row" v-if="ifSearch">
<div v-for="(item,index) in searchData" :key="index" class="col-md-4 col-sm-6" style="width:22%;float:left;margin-left:2%;margin-top:10px">
<div class="pricingTable">
<h3 class="title">{{item.songname}}</h3>
<div class="price-value">
<span class="month">{{item.artistname}}</span>
</div>
<ul class="pricing-content">
<li>热度:{{item.weight}}</li>
</ul>
<a href="#" class="pricingTable-signup red"><span>播放</span></a>
<a href="#" class="pricingTable-signup red"><span>添加</span></a>
<!-- <a href="#" class="pricingTable-signup red"><span>下载</span></a> -->
</div>
</div>
</div>
<!-- <h1 class="player_logo">QQ音乐</h1> -->
<!-- <el-table :data="tableData" style="width: 100%" height="500">
<el-table-column fixed prop="name" align="center" label="歌曲" width="150"> </el-table-column>
<el-table-column label="歌手" align="center" width="120">
<template slot-scope="scope">
<span>{{ scope.row.singer[0].name }}</span>
</template>
</el-table-column>
<el-table-column label="专辑" align="center" width="120">
<template slot-scope="scope">
<span>{{ scope.row.album.name }}</span>
</template>
</el-table-column>
<el-table-column prop="time_public" label="发布日期" align="center" width="120"></el-table-column>
<el-table-column align="center" label="时长">
<template slot-scope="scope">
<span>{{ scope.row.interval }}</span>
</template>
</el-table-column>
</el-table> -->
<ul v-if="!ifSearch" class="geBan">
<li v-for="(item,index) in jsonpData" :key="index">
<div class="port-1 effect-1">
<div class="image-box">
<img :src="item.pic_premium" alt="pic_premium">
</div>
<div class="text-desc">
<h3>{{item.title}}</h3>
<p>歌手:{{item.author}} <span style="padding-left:5px">语言:{{item.language}}</span> </p>
<p>热度:{{item.hot}}</p>
<a href="javascript:void(0)" @click="playMusic(item.song_id)" class="btn">播放</a>
<a href="javascript:void(0)" @click="addMusic(item)" class="btn">添加</a>
<a href="javascript:void(0)" @click="playMusic(item.song_id)" class="btn">下载</a>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<!-- 列表结束 -->
<div class="pageMusic" v-if="jsonpData.length > 0 && !ifSearch">
<!-- <el-button-group>
<el-button type="primary" icon="el-icon-arrow-left" style="margin-right:10px" >上一页</el-button>
<el-button type="primary">下一页<i class="el-icon-arrow-right el-icon--right"></i></el-button>
</el-button-group> -->
<el-pagination
background
layout="prev, pager, next"
:current-page.sync="currentPage"
@current-change="handleCurrentChange"
:page-size="commonParams.size"
:total="totalMusic">
</el-pagination>
</div>
<div class="audioUrl">
<audio :src="musicUrl" controls="controls" autoplay="autoplay"></audio>
</div>
</div>
</div>
<!-- 我的收藏 -->
<div class="mid-col mycollectList" v-if="myChoose === '我的收藏'" style="background: #4A424A;">
<my-collect></my-collect>
</div>
</div>
</div>
</template>
<style>
.mod_search .el-input-group__append{
text-align: center;
}
.container .el-table::before{
height: 0px;
}
.container .el-table__row:hover{
cursor: pointer;
}
</style>
<style scoped>
/* .mycollectList{
background: #4A424A;
opacity: .9;
} */
@import '../../../static/css/hover-effects.css';
.audioUrl{
position: fixed;
bottom:20px;
right:5px;
background:white;
z-index: 5;
}
.bg_player {
display: block;
}
.bg_player {
display: none;
background-repeat: no-repeat;
background-size: cover;
background-position: 50%;
-webkit-filter: blur(65px);
filter: blur(65px);
opacity: .6;
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
.bg_player, .bg_player_mask {
position: absolute;
top: 0;
left: 0;
}
.mod_search {
position: relative;
height: 147px;
background-position: 50%;
background-size: cover;
}
.mod_search .mod_search_input, .mod_search .search_input__input {
width: 554px;
height: 50px;
}
.search-input{
position: absolute;
width: 500px;
left: 50%;
margin-left: -250px;
top: 50px;
}
.pageMusic{
clear: both;
text-align: center;
margin-bottom: 80px;
}
.pageMusic .el-pager{
padding: 10px;
line-height: 12px;
font-size: 15px;
}
.index__tit {
display: block;
margin: 0 auto;
width: 196px;
height: 40px;
font-size: 28px
}
#container .mid-col {
position: absolute;
right: 0;
min-height: 100%;
/* background-color: #292a2b; */
left: 310px;
width: auto;
}
.pop-music {
width: 36px;
height: 36px;
background: url(http://www.shawnzeng.com/img/player.png);
cursor: pointer;
position: absolute;
top: 10px;
left: 10px;
border-bottom: none;
-webkit-animation: spin 3s infinite linear;
-moz-animation: spin 3s infinite linear;
-ms-animation: spin 3s infinite linear;
animation: spin 3s infinite linear;
-moz-animation: spin 3s infinite linear;
-ms-animation: spin 3s infinite linear;
-webkit-animation: spin 3s infinite linear;
}
#header .switch-area {
position: relative;
width: 100%;
overflow: hidden;
min-height: 260px;
font-size: 0.875rem;
}
#header .header-menu {
margin-top: 20px;
width: 100%;
position: absolute;
-webkit-transition: -webkit-transform 0.3s ease-in;
-moz-transition: -moz-transform 0.3s ease-in;
-ms-transition: -ms-transform 0.3s ease-in;
transition: transform 0.3s ease-in;
}
a {
text-decoration: none;
outline-width: 0;
color: #0bf;
outline: none;
cursor: pointer;
}
#header .header-menu a{
display: inline-block;
border-radius: 50%;
width: 40px;
height: 40px;
line-height: 40px;
font-size: 14px;
margin: 2px;
color: rgba(0,0,0,0.5);
border: 2px solid rgba(0,0,0,0.1);
-webkit-transition: all 0.3s ease-in-out;
-webkit-transition: all 0.3s ease-in-out;
-moz-transition: all 0.3s ease-in-out;
-ms-transition: all 0.3s ease-in-out;
transition: all 0.3s ease-in-out;
font-style: normal;
}
#header .switch-area .switch-wrap {
-webkit-transition: -webkit-transform 0.3s ease-in;
-moz-transition: -moz-transform 0.3s ease-in;
-ms-transition: -ms-transform 0.3s ease-in;
transition: transform 0.3s ease-in;
position: relative;
}
#header .switch-part1 {
left: 0;
}
#header .switch-part {
width: 100%;
position: absolute;
}
#header .header-subtitle {
text-align: center;
color: #333;
font-size: 0.875rem;
line-height: 1.75;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.header-author {
text-align: center;
margin: 13px 0;
font-family: Georgia, "Nimbus Roman No9 L", "Songti SC", "Courier New", Times, Georgia, serif;
font-size: 30px;
-webkit-transition: 0.3s;
-moz-transition: 0.3s;
-ms-transition: 0.3s;
transition: 0.3s;
}
#header .profilepic {
text-align: center;
display: block;
border: 5px solid #fff;
border-radius: 50%;
width: 128px;
height: 128px;
margin: 0 auto;
position: relative;
overflow: hidden;
background: #88acdb;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
-webkit-box-align: center;
text-align: center;
}
#header a {
color: #000;
}
#header .profilepic img {
width: 100%;
height: 100%;
border-radius: 50%;
}
#container .left-col .intrude-less {
width: 76%;
text-align: center;
margin: 112px auto 0;
}
#header {
width: 100%;
}
.inner {
width: 1000px;
margin: 0 auto;
}
#container .left-col .overlay {
width: 100%;
height: 180px;
background-color: #000;
position: absolute;
opacity: 0.7;
}
#container .left-col {
background: rgba(255,255,255,0.85);
width: 310px;
position: fixed;
opacity: 1;
-webkit-transition: all 0.2s ease-in;
-moz-transition: all 0.2s ease-in;
-ms-transition: all 0.2s ease-in;
transition: all 0.2s ease-in;
height: 100%;
/* overflow-y: auto; */
-webkit-box-shadow: 3px 2px 8px #333 !important;
-webkit-box-shadow: 3px 2px 8px #333 !important;
box-shadow: 3px 2px 8px #333 !important;
}
#header .collect{
font-size: 16px;
cursor: pointer;
border: 1px solid #ccc;
}
#header .collectActive{
background: #ccc;
border: 1px solid #4d92d9
}
</style>
<script>
import myCollect from './myCollect'
import axios from "axios"
export default{
components: {
myCollect
},
data(){
return{
ifSearch: false,
autoplay:true,
tableData: [],
myChoose: '我的首页',
myType: [
{
name: '我的首页',
icon: 'iconfont icon-shouyefill'
},
{
name: '我的收藏',
icon: 'iconfont icon-xiangqufill'
},
{
name: '我的喜欢',
icon: 'iconfont icon-zanfill'
}
],
auObj: null,
loading: false,
musicUrl: '',
musicType: '1',
api: 'http://tingapi.ting.baidu.com/v1/restserver/ting',
jsonpData: [],
searchData: [],
currentPage: 1,
totalMusic: 0,
dataObj: {
format: 'json',
calback: '',
from: 'webapp_music'
},
commonParams: {
method: 'baidu.ting.billboard.billList',
type: '',
size: 20,
offset: 0
},
searchParams: {
method: 'baidu.ting.search.catalogSug',
query: ''
},
playParams: {
method: 'baidu.ting.song.play',
songid: ''
}
}
},
methods: {
// 喜欢与收藏
personMusic(type) {
this.myChoose = type;
},
// 添加到收藏
addMusic(item) {
let datas = {
song_id: item.song_id ? item.song_id : item.songid,
author: item.author ? item.author : item.artistname,
title: item.title ? item.title : item.songname,
hot: item.hot ? item.hot : item.weight
}
axios.post('/music/collect',datas).then(data=>{
this.$message({
message: '收藏成功',
type: 'success'
});
})
},
//播放音乐
playMusic(songId){
this.playParams.songid = songId;
const dataMusic = Object.assign({}, this.playParams, this.dataObj)
this.$jsonp(this.api, dataMusic).then(json => {
this.musicUrl = json.bitrate.file_link;
})
},
// 音乐类型改变
typeChange(val) {
this.ifSearch = false;
this.commonParams.offset = 0;
this.currentPage = 1;
this.getMusicList();
},
// 页数改变
handleCurrentChange(val) {
window.scrollTo(0,0);// 请求成功滚动条滚到顶部
this.commonParams.offset = (parseInt(val)-1)*(this.commonParams.size);
this.getMusicList();
},
searchMusic() {
this.ifSearch = true;
this.loading = true;
const dataMusic = Object.assign({}, this.searchParams, this.dataObj)
this.$jsonp(this.api, dataMusic).then(json => {
this.searchData = json.song;
this.loading = false;
// this.totalMusic = parseInt(json.billboard.billboard_songnum);
})
},
// 获取音乐列表
getMusicList() {
this.loading = true;
this.commonParams.type = this.musicType;
const dataMusic = Object.assign({}, this.commonParams, this.dataObj)
this.$jsonp(this.api, dataMusic).then(json => {
this.jsonpData = json.song_list;
this.loading = false;
this.totalMusic = parseInt(json.billboard.billboard_songnum);
})
}
},
mounted(){
this.getMusicList();
}
}
</script>
中间引入了一个hover-effects.css
.container{margin: 0 auto; max-width: 1160px;}
h2{color: #fff; float: left; width: 100%; font-size: 24px; font-weight: 400; text-align: center; padding: 50px 0 40px; position: relative; z-index: 50;}
h2 span{position: relative; padding-bottom: 10px;}
h2 span:after{content: ""; width: 50%; height: 3px; background-color: #fff; position: absolute; left: 25%; bottom: 0;}
*{margin: 0; padding: 0; box-sizing: border-box;}
.full-length img{max-width: 100%; vertical-align: middle;}
.full-length{width: 100%; float: left; padding-bottom: 30px;overflow: hidden;padding-left: 5%;}
ul{margin: 0 -1.5%;}
.geBan li[data-v-7c5ce25b] {
float: left;
width: 22%;
margin: 10px 1%;
list-style: none;
}
h3{font-size: 14px; margin: 5px 0 10px;}
p{font-weight: 300; line-height: 20px; font-size: 14px; margin-bottom: 15px;}
.btn{display: inline-block; padding: 5px 10px; font-size: 14px; color: #fff; border: 2px solid #4d92d9; background-color: #4d92d9; text-decoration: none; transition: 0.4s;}
.btn:hover{background-color: transparent; color: #4d92d9; transition: 0.4s;}
.text-desc{position: absolute; left: 0; top: 0; background-color: #fff; height: 100%; opacity: 0; width: 100%; padding: 20px;}
/*= Reset CSS End
================= *
/* effect-1 css */
.port-1{float: left; width: 100%; position: relative; overflow: hidden; text-align: center; border: 4px solid rgba(255, 255, 255, 0.9);}
.port-1 .text-desc{opacity: 0.9; top: -100%; transition: 0.5s; color: #000; padding: 55px 20px 20px;}
.port-1 img{transition: 0.5s;}
.port-1:hover img{transform: scale(1.2);}
.port-1.effect-1:hover .text-desc{top: 0;}
.port-1.effect-2 .text-desc{top: auto; bottom: -100%;}
.port-1.effect-2:hover .text-desc{bottom: 0;}
.port-1.effect-3 .text-desc{top: 50%; left: 50%; width: 0; height: 0; overflow: hidden; padding: 0;}
.port-1.effect-3:hover .text-desc{width: 100%; top: 0; left: 0; height: 100%; padding: 15px 20px 20px;}
/*= Media Screen CSS
==================== */
@media only screen and (max-width: 1090px){
ul{width: 340px; margin: 0 auto;}
li{width: 100%; margin: 20px 0;}
.port-5.effect-1 {z-index: 19;}
}
@media only screen and (max-width: 360px){
ul{width: 300px;}
.port-1 .text-desc,
.port-1.effect-3:hover .text-desc,
.port-3.effect-1 .text-desc,
.port-3.effect-3 .text-desc,
.port-4.effect-1 .text-desc,
.port-4.effect-2 .text-desc,
.port-4.effect-3 .text-desc, .port-8 .text-desc{padding: 20px;}
.text-desc{padding: 7px;}
.port-5.effect-1 .text-desc{padding: 13px 20px 20px 90px;}
.port-5.effect-2 .text-desc{padding: 10px;}
.port-5.effect-3 .text-desc{padding: 16px 90px 20px 20px;}
.port-6.effect-1 .text-desc .btn,
.port-6.effect-2 .text-desc .btn,
.port-6.effect-3 .text-desc .btn,
.port-7.effect-1 .text-desc .btn,
.port-7.effect-2 .text-desc .btn,
.port-7.effect-3 .text-desc .btn,
.port-8.effect-3 .text-desc .btn{display: none;}
.port-6.effect-2 .text-desc{padding: 20px 120px 20px 20px;}
.port-6.effect-3 .text-desc{padding: 75px 20px 10px;}
.port-7.effect-1 .text-desc{padding: 12px 10px;}
.port-8.effect-3 .text-desc{padding: 28px 70px 20px;}
}
/*= Media Screen CSS End
======================== */
.pricingTable{
padding: 100px 0 20px;
border: 1px solid #ddd;
text-align: center;
position: relative;
transition: all 0.5s ease 0s;
}
.pricingTable .title{
width: 100%;
padding: 10px 0;
margin: 0;
background: #f7f2f0;
border: 1px solid #ddd;
font-size: 22px;
font-weight: 800;
color: #25283d;
text-transform: uppercase;
position: absolute;
top: 30px;
left: -15px;
transition: all 0.5s ease 0s;
}
.pricingTable:hover,
.pricingTable:hover .title{ border: 1px solid #25283d; }
.pricingTable .title:after{
content: "";
border-top: 15px solid #d2d2d2;
border-left: 15px solid transparent;
border-bottom: 15px solid transparent;
position: absolute;
bottom: -30px;
left: -1px;
transition: all 0.5s ease 0s;
}
.pricingTable:hover .title:after{ border-top: 15px solid #000; }
.pricingTable .price-value{
font-size: 50px;
color: #25283d;
margin-bottom: 40px;
}
.pricingTable .month{
display: block;
font-size: 14px;
color: #bb69a2;
line-height: 0;
text-transform: uppercase;
}
.pricingTable .pricing-content{
list-style: none;
padding: 0;
margin: 0 0 30px 0;
}
.pricingTable .pricing-content li{
font-size: 17px;
color: #848484;
line-height: 45px;
border-bottom: 1px solid #ddd;
}
.pricingTable .pricing-content li:nth-child(odd){ background: #f7f2f0; }
.pricingTable .pricing-content li:first-child{ border-top: 1px solid #ddd; }
.pricingTable .pricingTable-signup{
display: inline-block;
font-size: 18px;
font-weight: 600;
color: #25283d;
text-transform: uppercase;
position: relative;
transition: all 0.3s ease 0s;
}
.pricingTable .pricingTable-signup:hover{ color: #bb69a2; }
.pricingTable .pricingTable-signup span{
display: block;
padding: 7px 30px;
}
.pricingTable .pricingTable-signup:before,
.pricingTable .pricingTable-signup:after,
.pricingTable .pricingTable-signup span:before,
.pricingTable .pricingTable-signup span:after{
content: "";
background: #bb69a2;
position: absolute;
top: 0;
left: 0;
transition: all 0.15s ease-in-out 0s;
}
.pricingTable .pricingTable-signup:before,
.pricingTable .pricingTable-signup:after{
width: 2px;
height: 0;
}
.pricingTable .pricingTable-signup span:before,
.pricingTable .pricingTable-signup span:after{
width: 0;
height: 2px;
transition-delay: 0.15s;
}
.pricingTable .pricingTable-signup:after{
top: auto;
left: auto;
right: 0;
bottom: 0;
}
.pricingTable .pricingTable-signup span:before{
right: 0;
left: auto;
}
.pricingTable .pricingTable-signup span:after{
top: auto;
bottom: 0;
}
.pricingTable .pricingTable-signup:hover:before,
.pricingTable .pricingTable-signup:hover:after{
height: 100%;
transition-delay: 0.15s;
}
.pricingTable .pricingTable-signup:hover span:before,
.pricingTable .pricingTable-signup:hover span:after{
width: 100%;
transition-delay: 0s;
}
@media only screen and (max-width: 990px){
.pricingTable{ margin-bottom: 30px; }
}
下节将介绍收藏页的数据展示以及歌词滚动,进度条样式控制等
网友评论