1、后端编写

1.1 Service服务层

  1. package com.gmw.musicserver.service;
  2. import com.gmw.musicserver.entity.Song;
  3. import com.baomidou.mybatisplus.extension.service.IService;
  4. import java.util.List;
  5. /**
  6. * <p>
  7. * 歌曲表 服务类
  8. * 歌曲service接口
  9. * </p>
  10. *
  11. * @author 未进化的程序猿
  12. * @since 2022-01-26
  13. */
  14. public interface SongService extends IService<Song> {
  15. /**
  16. *增加
  17. */
  18. public boolean insertSong(Song song);
  19. /**
  20. *修改
  21. */
  22. public boolean updateSong(Song song);
  23. /**
  24. * 删除
  25. */
  26. public boolean deleteSong(Integer id);
  27. /**
  28. * 根据主键查询整个对象
  29. */
  30. public Song selectByPrimaryKey(Integer id);
  31. /**
  32. * 查询所有歌曲
  33. */
  34. public List<Song> allSong();
  35. /**
  36. * 根据歌名模糊查询列表
  37. */
  38. public List<Song> songOfName(String name);
  39. /**
  40. * 根据歌手id查询
  41. */
  42. public List<Song> songOfSingerId(Integer singerId);
  43. }
  1. package com.gmw.musicserver.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.gmw.musicserver.entity.Song;
  4. import com.gmw.musicserver.mapper.SongMapper;
  5. import com.gmw.musicserver.service.SongService;
  6. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7. import org.springframework.stereotype.Service;
  8. import java.util.List;
  9. /**
  10. * <p>
  11. * 歌曲表 服务实现类
  12. * </p>
  13. *
  14. * @author 未进化的程序猿
  15. * @since 2022-01-26
  16. */
  17. @Service
  18. public class SongServiceImpl extends ServiceImpl<SongMapper, Song> implements SongService {
  19. /**
  20. * 添加歌曲
  21. * @param song
  22. * @return
  23. */
  24. @Override
  25. public boolean insertSong(Song song) {
  26. return this.baseMapper.insert(song) > 0;
  27. }
  28. /**
  29. * 更新歌曲
  30. * @param song
  31. * @return
  32. */
  33. @Override
  34. public boolean updateSong(Song song) {
  35. return this.baseMapper.updateById(song) > 0;
  36. }
  37. /**
  38. * 删除歌曲
  39. * @param id
  40. * @return
  41. */
  42. @Override
  43. public boolean deleteSong(Integer id) {
  44. return this.baseMapper.deleteById(id) > 0;
  45. }
  46. /**
  47. * 根据主键id查询歌曲对象
  48. * @param id
  49. * @return
  50. */
  51. @Override
  52. public Song selectByPrimaryKey(Integer id) {
  53. return this.baseMapper.selectById(id);
  54. }
  55. /**
  56. * 查询所有的歌曲
  57. * @return
  58. */
  59. @Override
  60. public List<Song> allSong() {
  61. return this.baseMapper.selectList(null);
  62. }
  63. /**
  64. * 根据歌曲名称模糊查询所有的歌曲
  65. * @param name
  66. * @return
  67. */
  68. @Override
  69. public List<Song> songOfName(String name) {
  70. QueryWrapper<Song> queryWrapper = new QueryWrapper<>();
  71. queryWrapper.like("name",name);
  72. return this.baseMapper.selectList(queryWrapper);
  73. }
  74. /**
  75. * 根据歌手名称查询所有的歌曲
  76. * @param singerId
  77. * @return
  78. */
  79. @Override
  80. public List<Song> songOfSingerId(Integer singerId) {
  81. QueryWrapper<Song> queryWrapper = new QueryWrapper<>();
  82. queryWrapper.eq("singer_id",singerId);
  83. return this.baseMapper.selectList(queryWrapper);
  84. }
  85. }

1.2 Controller控制层

  1. package com.gmw.musicserver.controller;
  2. import com.gmw.musicserver.commonutils.R;
  3. import com.gmw.musicserver.entity.Singer;
  4. import com.gmw.musicserver.entity.Song;
  5. import com.gmw.musicserver.service.SongService;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.web.bind.annotation.*;
  10. import org.springframework.web.multipart.MultipartFile;
  11. import javax.servlet.http.HttpServletRequest;
  12. import java.io.File;
  13. import java.io.IOException;
  14. import java.util.List;
  15. /**
  16. * <p>
  17. * 歌曲表 前端控制器
  18. * </p>
  19. *
  20. * @author 未进化的程序猿
  21. * @since 2022-01-26
  22. */
  23. @RestController
  24. @RequestMapping("/musicserver/song")
  25. public class SongController {
  26. private static final Logger logger = LoggerFactory.getLogger(SongController.class);
  27. @Autowired
  28. public SongService songService;
  29. @PostMapping(value = "/addSong")
  30. public R addSong(HttpServletRequest request, @RequestParam("file")MultipartFile file){
  31. //获取前端传来的参数
  32. String singerId = request.getParameter("singerId").trim(); //所属歌手id
  33. String name = request.getParameter("name").trim(); //歌名
  34. String introduction = request.getParameter("introduction").trim(); //简介
  35. String pic = "/img/songPic/tubiao.jpg"; //默认图片
  36. String lyric = request.getParameter("lyric").trim(); //歌词
  37. //上传歌曲文件
  38. //先判断上传的文件是否为空
  39. if(file.isEmpty()){
  40. return R.error().message("歌曲上传失败!!");
  41. }
  42. //文件名=当前时间到毫秒+原来的文件名
  43. String fileName = System.currentTimeMillis() + file.getOriginalFilename();
  44. logger.info("文件名: {}",fileName);
  45. //上传歌曲的文件路径
  46. String filePath = System.getProperty("user.dir") + System.getProperty("file.separator")
  47. + "audio" + System.getProperty("file.separator") + "song";
  48. logger.info("上传歌曲的文件地址: {}",filePath);
  49. //如果文件路径不存在,新增该路径
  50. File file1 = new File(filePath);
  51. if(!file1.exists()){
  52. file1.mkdir();
  53. }
  54. //实际的文件地址
  55. String fileDist = filePath + System.getProperty("file.separator") + fileName;
  56. logger.info("实际保存歌曲的文件地址: {}",fileDist);
  57. File fileDest = new File(fileDist);
  58. //存储到数据库里的相对文件地址
  59. String storeUrlPath = "/audio/song/"+fileName;
  60. logger.info("存储到数据库里的相对文件地址: {}",storeUrlPath);
  61. try {
  62. //保存上传图片
  63. file.transferTo(fileDest);
  64. //上传图片之后,保存歌曲的信息
  65. Song song = new Song();
  66. song.setSingerId(Integer.parseInt(singerId)); //歌手的ID
  67. song.setName(name); //歌曲的名称
  68. song.setIntroduction(introduction); //歌曲的专辑
  69. song.setPic(pic); //歌曲的图片
  70. song.setLyric(lyric); //歌曲的歌词
  71. song.setUrl(storeUrlPath); //歌曲的播放地址
  72. logger.info("歌曲对象: {}",song);
  73. boolean flag = this.songService.insertSong(song);
  74. if(flag){
  75. return R.ok().message("添加歌曲成功!!").data("storeUrlPath",storeUrlPath);
  76. }else {
  77. return R.error().message("添加歌曲失败!!");
  78. }
  79. } catch (IOException e) {
  80. return R.error().message("添加歌曲失败!!");
  81. }
  82. }
  83. /**
  84. * 根据歌手id查询歌曲
  85. */
  86. @GetMapping(value = "/singer/detail")
  87. public R songOfSingerId(HttpServletRequest request){
  88. String singerId = request.getParameter("singerId");
  89. List<Song> songList = this.songService.songOfSingerId(Integer.parseInt(singerId));
  90. logger.info("根据歌手id查询歌曲: {}",songList);
  91. return R.ok().data("list",songList);
  92. }
  93. /**
  94. * 修改歌曲
  95. */
  96. @PostMapping(value = "/updateSong")
  97. public Object updateSong(HttpServletRequest request){
  98. String id = request.getParameter("id").trim(); //主键
  99. String name = request.getParameter("name").trim(); //歌名
  100. String introduction = request.getParameter("introduction").trim();//专辑
  101. String lyric = request.getParameter("lyric").trim(); //歌词
  102. //保存到歌手的对象中
  103. Song song = new Song();
  104. song.setId(Integer.parseInt(id));
  105. song.setName(name);
  106. song.setIntroduction(introduction);
  107. song.setLyric(lyric);
  108. logger.info("编辑歌曲对象: {}",song);
  109. boolean flag = this.songService.updateSong(song);
  110. if(flag){ //保存成功
  111. return R.ok().message("歌曲修改成功!!");
  112. }
  113. return R.error().message("歌曲修改失败!!");
  114. }
  115. /**
  116. * 删除歌曲
  117. */
  118. @GetMapping(value = "/deleteSong")
  119. public R deleteSong(HttpServletRequest request){
  120. String id = request.getParameter("id").trim(); //主键
  121. //-TODO 先查询到数据库中对应的文件地址,删除掉它再进行下面的代码
  122. //通过ID主键查询歌手对象
  123. Song song = this.songService.selectByPrimaryKey(Integer.parseInt(id));
  124. //获取歌曲图片
  125. String songPic = song.getPic();
  126. logger.info("歌曲图片: {}",songPic);
  127. //如果文件的相对路径是/img/songPic/tubiao.jpg就没必要删除
  128. //因为它是当作默认相对路径的,需要用到
  129. if(!songPic.equals("/img/songPic/tubiao.jpg")){
  130. //获取歌曲保存的文件目录
  131. String songPath = System.getProperty("user.dir") + songPic;
  132. logger.info("获取歌曲保存的文件目录: {}",songPath);
  133. File file = new File(songPath);
  134. if(file.isFile()){
  135. //判断如果是文件的话,就删除文件
  136. boolean isDelete = file.delete();
  137. logger.info("是否删除成功: {}",isDelete);
  138. if(!isDelete){
  139. return R.error().message("删除歌曲失败!!");
  140. }
  141. }
  142. }
  143. //获取歌曲播放地址
  144. String songUrl = song.getUrl();
  145. logger.info("获取歌曲播放地址: {}",songUrl);
  146. //获取歌曲保存的文件目录
  147. String fileSongUrlPath = System.getProperty("user.dir") + songUrl;
  148. logger.info("获取歌曲播放地址: {}",fileSongUrlPath);
  149. File delUrlFile = new File(fileSongUrlPath);
  150. if(delUrlFile.isFile()){
  151. //判断如果是文件的话,就删除文件
  152. boolean isDelete = delUrlFile.delete();
  153. logger.info("是否删除成功: {}",isDelete);
  154. if(!isDelete){
  155. return R.error().message("删除歌曲失败!!");
  156. }
  157. }
  158. boolean flag = this.songService.deleteSong(Integer.parseInt(id));
  159. if(flag){
  160. return R.ok().message("删除歌曲成功!!");
  161. }
  162. return R.error().message("删除歌曲失败!!");
  163. }
  164. /**
  165. * 更新歌曲图片
  166. */
  167. @PostMapping(value = "/updateSongPic")
  168. public R updateSongPic(@RequestParam("file") MultipartFile file, @RequestParam("id")int id){
  169. /**
  170. * 更新歌曲之前,必须把原来的歌曲图片删除掉
  171. */
  172. //通过ID主键查询歌手对象
  173. Song song = this.songService.selectByPrimaryKey(id);
  174. //获取歌曲图片
  175. String songPic = song.getPic();
  176. logger.info("歌曲图片: {}",songPic);
  177. //如果文件的相对路径是/img/songPic/tubiao.jpg就没必要删除
  178. //因为它是当作默认相对路径的,需要用到
  179. if(!songPic.equals("/img/songPic/tubiao.jpg")){
  180. //获取歌曲保存的文件目录
  181. String songPath = System.getProperty("user.dir") + songPic;
  182. logger.info("获取歌曲保存的文件目录: {}",songPath);
  183. File file1 = new File(songPath);
  184. if(file1.isFile()){
  185. file1.delete();
  186. }
  187. }
  188. //开始更新歌曲图片
  189. if(file.isEmpty()){
  190. return R.error().message("上传歌曲失败!!");
  191. }
  192. //文件名=当前时间到毫秒+原来的文件名
  193. String fileName = System.currentTimeMillis()+file.getOriginalFilename();
  194. //文件路径
  195. String filePath = System.getProperty("user.dir")+System.getProperty("file.separator")+"img"
  196. +System.getProperty("file.separator")+"songPic";
  197. //如果文件路径不存在,新增该路径
  198. File file1 = new File(filePath);
  199. if(!file1.exists()){
  200. file1.mkdir();
  201. }
  202. //实际的文件地址
  203. File dest = new File(filePath+System.getProperty("file.separator")+fileName);
  204. //存储到数据库里的相对文件地址
  205. String storeAvatorPath = "/img/songPic/"+fileName;
  206. try {
  207. file.transferTo(dest);
  208. Song song1 = new Song();
  209. song1.setId(id); //歌曲ID
  210. song1.setPic(storeAvatorPath); //歌曲图片相对地址
  211. boolean flag = this.songService.updateSong(song1);
  212. if(flag){
  213. return R.ok().message("更新歌曲图片成功!!");
  214. }else {
  215. return R.error().message("更新歌曲图片失败!!");
  216. }
  217. } catch (IOException e) {
  218. return R.error().message("更新歌曲图片失败!!");
  219. }
  220. }
  221. /**
  222. * 更新歌曲
  223. */
  224. @PostMapping(value = "/updateSongUrl")
  225. public R updateSongUrl(@RequestParam("file") MultipartFile avatorFile, @RequestParam("id")int id){
  226. /**
  227. * 更新歌曲之前,必须把原来的歌曲文件删除掉
  228. */
  229. //通过ID主键查询歌手对象
  230. Song song = this.songService.selectByPrimaryKey(id);
  231. //获取歌曲播放地址
  232. String songUrl = song.getUrl();
  233. logger.info("获取歌曲播放地址: {}",songUrl);
  234. //获取歌曲保存的文件目录
  235. String fileSongUrlPath = System.getProperty("user.dir") + songUrl;
  236. logger.info("获取歌曲播放地址: {}",fileSongUrlPath);
  237. File delUrlFile = new File(fileSongUrlPath);
  238. if(delUrlFile.isFile()){
  239. delUrlFile.delete();
  240. }
  241. //开始文件上传
  242. if(avatorFile.isEmpty()){
  243. return R.error().message("文件上传失败!!");
  244. }
  245. //文件名=当前时间到毫秒+原来的文件名
  246. String fileName = System.currentTimeMillis()+avatorFile.getOriginalFilename();
  247. //文件路径
  248. String filePath = System.getProperty("user.dir")+System.getProperty("file.separator")
  249. + "audio" + System.getProperty("file.separator") + "song";
  250. //如果文件路径不存在,新增该路径
  251. File file1 = new File(filePath);
  252. if(!file1.exists()){
  253. file1.mkdir();
  254. }
  255. //实际的文件地址
  256. File dest = new File(filePath+System.getProperty("file.separator")+fileName);
  257. //存储到数据库里的相对文件地址
  258. String storeAvatorPath = "/audio/song/"+fileName;
  259. try {
  260. avatorFile.transferTo(dest);
  261. Song song1 = new Song();
  262. song1.setId(id);
  263. song1.setUrl(storeAvatorPath);
  264. boolean flag = this.songService.updateSong(song1);
  265. if(flag){
  266. return R.ok().message("更新歌曲播放文件成功!!");
  267. }else {
  268. return R.error().message("更新歌曲播放文件失败!!");
  269. }
  270. } catch (IOException e) {
  271. return R.error().message("更新歌曲播放文件失败!!");
  272. }
  273. }
  274. }

1.3 配置图片、音频定位各种文件或头像地址

image.png

  1. package com.gmw.musicserver.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  4. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  5. /**
  6. * 定位各种文件或头像地址
  7. */
  8. @Configuration
  9. public class FileConfig implements WebMvcConfigurer {
  10. @Override
  11. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  12. /**
  13. * 歌手头像地址
  14. */
  15. registry.addResourceHandler("/img/singerPic/**")
  16. .addResourceLocations(
  17. "file:" + System.getProperty("user.dir") + System.getProperty("file.separator")
  18. + "img" + System.getProperty("file.separator")
  19. + "singerPic" + System.getProperty("file.separator")
  20. );
  21. /**
  22. * 歌单图片地址
  23. */
  24. registry.addResourceHandler("/img/songListPic/**")
  25. .addResourceLocations(
  26. "file:" + System.getProperty("user.dir") + System.getProperty("file.separator")
  27. + "img" + System.getProperty("file.separator")
  28. + "songListPic" + System.getProperty("file.separator")
  29. );
  30. /**
  31. * 歌曲图片地址
  32. */
  33. registry.addResourceHandler("/img/songPic/**")
  34. .addResourceLocations(
  35. "file:" + System.getProperty("user.dir") + System.getProperty("file.separator")
  36. + "img" + System.getProperty("file.separator")
  37. + "songPic" + System.getProperty("file.separator")
  38. );
  39. /**
  40. * 歌曲播放地址
  41. */
  42. registry.addResourceHandler("/audio/song/**")
  43. .addResourceLocations(
  44. "file:" + System.getProperty("user.dir") + System.getProperty("file.separator")
  45. + "audio" + System.getProperty("file.separator")
  46. + "song" + System.getProperty("file.separator")
  47. );
  48. }
  49. }

2、前端编写

  1. <template>
  2. <div class="table">
  3. <div class="crumbs">
  4. <i class="el-icon-tickets"></i>歌曲信息
  5. </div>
  6. <div class="container">
  7. <div class="handle-box">
  8. <el-button type="primary" size="mini" @click="delAll">批量删除</el-button>
  9. <el-input v-model="select_word" size="mini" placeholder="请输入歌曲名" class="handle-input"></el-input>
  10. <el-button type="primary" size="mini" @click="centerDialogVisible = true">添加歌曲</el-button>
  11. </div>
  12. </div>
  13. <el-table size="mini" ref="multipleTable" border style="width:100%" height="680px" :data="data" @selection-change="handleSelectionChange">
  14. <el-table-column type="selection" width="40"></el-table-column>
  15. <el-table-column label="歌曲图片" width="110" align="center">
  16. <template slot-scope="scope">
  17. <div class="song-img">
  18. <img :src="getUrl(scope.row.pic)" style="width:100%"/>
  19. </div>
  20. <div class="play" @click="setSongUrl(scope.row.url,scope.row.name)">
  21. <div v-if="toggle == scope.row.name">
  22. <svg class="icon">
  23. <use xlink:href="#icon-zanting"></use>
  24. </svg>
  25. </div>
  26. <div v-if="toggle != scope.row.name">
  27. <svg class="icon">
  28. <use xlink:href="#icon-bofanganniu"></use>
  29. </svg>
  30. </div>
  31. </div>
  32. </template>
  33. </el-table-column>
  34. <el-table-column prop="name" label="歌手-歌名" width="120" align="center"></el-table-column>
  35. <el-table-column prop="introduction" label="专辑" width="150" align="center"></el-table-column>
  36. <el-table-column label="歌词" align="center">
  37. <template slot-scope="scope">
  38. <ul style="height:100px;overflow:scroll;">
  39. <li v-for="(item,index) in parseLyric(scope.row.lyric)" :key="index">
  40. {{item}}
  41. </li>
  42. </ul>
  43. </template>
  44. </el-table-column>
  45. <el-table-column label="资源更新" align="center" width="100">
  46. <template slot-scope="scope">
  47. <el-upload :action="uploadUrl(scope.row.id)" :before-upload="beforeAvatorUpload"
  48. :on-success="handleAvatorSuccess">
  49. <el-button size="mini">更新图片</el-button>
  50. </el-upload>
  51. <br/>
  52. <el-upload :action="uploadSongUrl(scope.row.id)" :before-upload="beforeSongUpload"
  53. :on-success="handleSongSuccess">
  54. <el-button size="mini">更新歌曲</el-button>
  55. </el-upload>
  56. </template>
  57. </el-table-column>
  58. <el-table-column label="操作" width="150" align="center">
  59. <template slot-scope="scope">
  60. <el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
  61. <el-button size="mini" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
  62. </template>
  63. </el-table-column>
  64. </el-table>
  65. <div class="pagination">
  66. <el-pagination
  67. background
  68. layout = "total,prev,pager,next"
  69. :current-page="currentPage"
  70. :page-size="pageSize"
  71. :total="tableData.length"
  72. @current-change="handleCurrentChange"
  73. >
  74. </el-pagination>
  75. </div>
  76. <el-dialog title="添加歌曲" :visible.sync="centerDialogVisible" width="400px" center>
  77. <el-form :model="registerForm" ref="registerForm" label-width="80px" action="" id="tf">
  78. <div>
  79. <label>歌名</label>
  80. <el-input type="text" name="name"></el-input>
  81. </div>
  82. <div>
  83. <label>专辑</label>
  84. <el-input type="text" name="introduction"></el-input>
  85. </div>
  86. <div>
  87. <label>歌词</label>
  88. <el-input type="textarea" name="lyric"></el-input>
  89. </div>
  90. <div>
  91. <label>歌曲上传</label>
  92. <input type="file" name="file">
  93. </div>
  94. </el-form>
  95. <span slot="footer">
  96. <el-button size="mini" @click="centerDialogVisible = false">取消</el-button>
  97. <el-button size="mini" @click="addSong">确定</el-button>
  98. </span>
  99. </el-dialog>
  100. <el-dialog title="修改歌曲" :visible.sync="editVisible" width="400px" center>
  101. <el-form :model="form" ref="form" label-width="80px">
  102. <el-form-item prop="name" label="歌手-歌名" size="mini">
  103. <el-input v-model="form.name" placeholder="歌手-歌名"></el-input>
  104. </el-form-item>
  105. <el-form-item prop="introduction" label="专辑" size="mini">
  106. <el-input v-model="form.introduction" placeholder="专辑"></el-input>
  107. </el-form-item>
  108. <el-form-item prop="lyric" label="歌词" size="mini">
  109. <el-input v-model="form.lyric" placeholder="歌词" type="textarea"></el-input>
  110. </el-form-item>
  111. </el-form>
  112. <span slot="footer">
  113. <el-button size="mini" @click="editVisible = false">取消</el-button>
  114. <el-button size="mini" @click="editSave">确定</el-button>
  115. </span>
  116. </el-dialog>
  117. <el-dialog title="删除歌曲" :visible.sync="delVisible" width="300px" center>
  118. <div align="center">删除不可恢复,是否确定删除?</div>
  119. <span slot="footer">
  120. <el-button size="mini" @click="delVisible = false">取消</el-button>
  121. <el-button size="mini" @click="deleteRow">确定</el-button>
  122. </span>
  123. </el-dialog>
  124. </div>
  125. </template>
  126. <script>
  127. import { mixin } from '../mixins/index';
  128. import {mapGetters} from 'vuex';
  129. import '@/assets/js/iconfont.js';
  130. import {songOfSingerId,updateSong,delSong} from '../api/song/index';
  131. export default {
  132. mixins: [mixin],
  133. data(){
  134. return{
  135. singerId: '', //歌手id
  136. singerName: '', //歌手名
  137. centerDialogVisible: false, //添加弹窗是否显示
  138. editVisible: false, //编辑弹窗是否显示
  139. delVisible: false, //删除弹窗是否显示
  140. registerForm:{ //添加框
  141. name: '',
  142. singerName: '',
  143. introduction: '',
  144. lyric: ''
  145. },
  146. form:{ //编辑框
  147. id: '',
  148. name: '',
  149. introduction: '',
  150. lyric: ''
  151. },
  152. tableData: [],
  153. tempData: [],
  154. select_word: '',
  155. pageSize: 5, //分页每页大小
  156. currentPage: 1, //当前页
  157. idx: -1, //当前选择项
  158. multipleSelection: [], //哪些项已经打勾
  159. toggle: false //播放器的图标状态
  160. }
  161. },
  162. computed:{
  163. ...mapGetters([
  164. 'isPlay'
  165. ]),
  166. //计算当前搜索结果表里的数据
  167. data(){
  168. return this.tableData.slice((this.currentPage - 1) * this.pageSize,this.currentPage * this.pageSize)
  169. }
  170. },
  171. watch:{
  172. //搜索框里面的内容发生变化的时候,搜索结果table列表的内容跟着它的内容发生变化
  173. select_word: function(){
  174. if(this.select_word == ''){
  175. this.tableData = this.tempData;
  176. }else{
  177. this.tableData = [];
  178. for(let item of this.tempData){
  179. if(item.name.includes(this.select_word)){
  180. this.tableData.push(item);
  181. }
  182. }
  183. }
  184. }
  185. },
  186. created(){
  187. this.singerId = this.$route.query.id;
  188. this.singerName = this.$route.query.name;
  189. this.getData();
  190. },
  191. destroyed() {
  192. this.$store.commit('setIsPlay',false);
  193. },
  194. methods:{
  195. //获取当前页
  196. handleCurrentChange(val){
  197. this.currentPage = val;
  198. },
  199. //查询所有歌手
  200. getData(){
  201. this.tempData = [];
  202. this.tableData = [];
  203. songOfSingerId(this.singerId).then(res => {
  204. this.tempData = res.data.list;
  205. this.tableData = res.data.list;
  206. this.currentPage = 1;
  207. })
  208. },
  209. //添加歌手
  210. addSong(){
  211. let _this = this;
  212. var form = new FormData(document.getElementById('tf'));
  213. form.append('singerId',this.singerId);
  214. form.set('name',this.singerName+'-'+form.get('name'));
  215. if(!form.get('lyric')){
  216. form.set('lyric','[00:00:00]暂无歌词');
  217. }
  218. var req = new XMLHttpRequest();
  219. req.onreadystatechange = function(){
  220. //req.readyState == 4 获取到返回的完整数据
  221. //req.status == 200 和后台正常交互完成
  222. if(req.readyState == 4 && req.status == 200){
  223. let res = JSON.parse(req.response);
  224. if(res.code){
  225. _this.getData();
  226. _this.registerForm = {};
  227. _this.notify(res.msg,'success');
  228. }else{
  229. _this.notify('保存失败','error');
  230. }
  231. }
  232. }
  233. req.open('post',`${_this.$store.state.HOST}/musicserver/song/addSong`,false);
  234. req.send(form);
  235. _this.centerDialogVisible = false;
  236. },
  237. //弹出编辑页面
  238. handleEdit(row){
  239. this.editVisible = true;
  240. this.form = {
  241. id: row.id,
  242. name: row.name,
  243. introduction: row.introduction,
  244. lyric: row.lyric
  245. }
  246. },
  247. //保存编辑页面修改的数据
  248. editSave(){
  249. let params = new URLSearchParams();
  250. params.append('id',this.form.id);
  251. params.append('name',this.form.name);
  252. params.append('introduction',this.form.introduction);
  253. params.append('lyric',this.form.lyric);
  254. updateSong(params)
  255. .then(res => {
  256. if(res.code == 20000){
  257. this.getData();
  258. this.notify("修改成功","success");
  259. }else{
  260. this.notify("修改失败","error");
  261. }
  262. })
  263. .catch(err => {
  264. console.log(err);
  265. });
  266. this.editVisible = false;
  267. },
  268. //更新图片
  269. uploadUrl(id){
  270. return `${this.$store.state.HOST}/musicserver/song/updateSongPic?id=${id}`
  271. },
  272. //删除一名歌手
  273. deleteRow(){
  274. delSong(this.idx)
  275. .then(res => {
  276. if(res.code == 20000){
  277. this.getData();
  278. this.notify("删除成功","success");
  279. }else{
  280. this.notify("删除失败","error");
  281. }
  282. })
  283. .catch(err => {
  284. console.log(err);
  285. });
  286. this.delVisible = false;
  287. },
  288. //解析歌词
  289. parseLyric(text){
  290. let lines = text.split("\n");
  291. let pattern = /\[\d{2}:\d{2}.(\d{3}|\d{2})\]/g;
  292. let result = [];
  293. for(let item of lines){
  294. let value = item.replace(pattern,'');
  295. result.push(value);
  296. }
  297. return result;
  298. },
  299. //上传歌曲之前的校验
  300. beforeSongUpload(file){
  301. var testMsg = file.name.substring(file.name.lastIndexOf('.') + 1);
  302. if(testMsg!='mp3'){
  303. this.$message({
  304. message: '上传文件只能是mp3格式',
  305. type: 'error'
  306. });
  307. return false;
  308. }
  309. return true;
  310. },
  311. //上传歌曲成功之后要做的工作
  312. handleSongSuccess(res){
  313. let _this = this;
  314. if(res.code == 20000){
  315. _this.getData();
  316. _this.$notify({
  317. title: '上传成功',
  318. type: 'success'
  319. });
  320. }else{
  321. _this.$notify({
  322. title: '上传失败',
  323. type: 'error'
  324. });
  325. }
  326. },
  327. //更新歌曲url
  328. uploadSongUrl(id){
  329. return `${this.$store.state.HOST}/musicserver/song/updateSongUrl?id=${id}`
  330. },
  331. //切换播放歌曲
  332. setSongUrl(url,name) {
  333. this.toggle = name;
  334. this.$store.commit('setUrl',this.$store.state.HOST + url);
  335. if(this.isPlay){
  336. this.$store.commit('setIsPlay',false);
  337. }else{
  338. this.$store.commit('setIsPlay',true);
  339. }
  340. }
  341. }
  342. }
  343. </script>
  344. <style scoped>
  345. .handle-box{
  346. margin-bottom: 20px;
  347. }
  348. .song-img{
  349. width: 100%;
  350. height: 80px;
  351. border-radius: 5px;
  352. margin-bottom: 5px;
  353. overflow: hidden;
  354. }
  355. .handle-input{
  356. width: 300px;
  357. display: inline-block;
  358. }
  359. .pagination{
  360. display: flex;
  361. justify-content: center;
  362. }
  363. .play {
  364. position: absolute;
  365. z-index: 100;
  366. width: 80px;
  367. height: 80px;
  368. display: flex;
  369. align-items: center;
  370. justify-content: center;
  371. cursor: pointer;
  372. top: 18px;
  373. left: 15px;
  374. }
  375. .icon {
  376. width: 2em;
  377. height: 2em;
  378. color: white;
  379. fill: currentColor;
  380. overflow: hidden;
  381. }
  382. </style>

2.1 添加歌曲

image.png
image.png

<el-dialog title="添加歌曲" :visible.sync="centerDialogVisible" width="400px" center>
  <el-form :model="registerForm" ref="registerForm" label-width="80px" action="" id="tf">
    <div>
      <label>歌名</label>
      <el-input type="text" name="name"></el-input>
    </div>
    <div>
      <label>专辑</label>
      <el-input type="text" name="introduction"></el-input>
    </div>
    <div>
      <label>歌词</label>
      <el-input type="textarea" name="lyric"></el-input>
    </div>
    <div>
      <label>歌曲上传</label>
      <input type="file" name="file">
    </div>
  </el-form>
  <span slot="footer">
    <el-button size="mini" @click="centerDialogVisible = false">取消</el-button>
    <el-button size="mini" @click="addSong">确定</el-button>                
  </span>
</el-dialog>

image.png

:visible.sync=”centerDialogVisible” 当centerDialogVisible为true时,会弹出文本输入框,否则隐藏

编写请求后端接口方法
image.png
image.png

import {get,post} from '../http'
//============歌曲相关================
//根据歌手id查询歌曲
export const songOfSingerId =(id) => get(`musicserver/song/singer/detail?singerId=${id}`);
//编辑歌曲
export const updateSong = (params) => post(`musicserver/song/updateSong`,params);
//删除歌曲
export const delSong = (id) => get(`musicserver/song/deleteSong?id=${id}`);

编写添加歌曲方法
image.png
image.png

 //添加歌手
addSong(){
  let _this = this;
  var form = new FormData(document.getElementById('tf'));
  form.append('singerId',this.singerId); //添加歌手的id到form中
  form.set('name',this.singerName+'-'+form.get('name')); //设置歌曲的名称格式为:歌手名称-歌曲名称
  if(!form.get('lyric')){
    form.set('lyric','[00:00:00]暂无歌词');
  }
  var req = new XMLHttpRequest();
  req.onreadystatechange = function(){
    //req.readyState == 4 获取到返回的完整数据
    //req.status == 200 和后台正常交互完成
    if(req.readyState == 4 && req.status == 200){
      let res = JSON.parse(req.response);
      if(res.code){
        _this.getData();
        _this.registerForm = {};
        _this.notify(res.msg,'success');
      }else{
        _this.notify('保存失败','error');
      }
    }
  }
  req.open('post',`${_this.$store.state.HOST}/musicserver/song/addSong`,false);
  req.send(form);
  _this.centerDialogVisible = false;
},

2.2 查询/修改/删除跟歌手管理模块一样(省略)

2.3 歌词显示

image.png
解析歌词(歌词前面带有[00:00.22]格式的替换为空字符串)

//解析歌词
parseLyric(text){
  let lines = text.split("\n");
  let pattern = /\[\d{2}:\d{2}.(\d{3}|\d{2})\]/g; //歌词前面带有[00:00.22]格式的替换为空字符串
  let result = [];
  for(let item of lines){
    let value = item.replace(pattern,'');
    result.push(value);
  }
  return result;
},

进行歌词的解析
image.png

2.4 更新歌曲图片/更新歌曲文件

image.png
image.png

<el-upload :action="uploadUrl(scope.row.id)" :before-upload="beforeAvatorUpload" 
           :on-success="handleAvatorSuccess">
  <el-button size="mini">更新图片</el-button>
</el-upload>
<br/>
<el-upload :action="uploadSongUrl(scope.row.id)" :before-upload="beforeSongUpload" 
           :on-success="handleSongSuccess">
  <el-button size="mini">更新歌曲</el-button>
</el-upload>

1)更新歌曲图片

:action=”uploadUrl(scope.row.id)” 表示要访问的后台地址,并携带歌曲的id,通过id修改歌曲图像
image.png

//更新图片
uploadUrl(id){
  return `${this.$store.state.HOST}/musicserver/song/updateSongPic?id=${id}`
},

:before-upload=”beforeAvatorUpload” 表示文件上传之前,进行哪些操作,比如:上传的文件类型、大小都可以做限制;

 //上传图片之前的校验
beforeAvatorUpload(file){
  const isJPG = (file.type === 'image/jpeg')||(file.type === 'image/png');
  if(!isJPG){
    this.$message.error('上传头像图片只能是jpg或png格式');
    return false;
  }
  const isLt2M = (file.size / 1024 /1024) < 2;
  if(!isLt2M){
    this.$message.error('上传头像图片大小不能超过2MB');
    return false;
  }
  return true;
},

:on-success=”handleAvatorSuccess” 表示上传成功后需要进行哪些操作,比如:提示上传成功或者失败

//上传图片成功之后要做的工作
handleAvatorSuccess(res){
  let _this = this;
  if(res.code == 20000){
    _this.getData();
    _this.$notify({
      title: '上传成功',
      type: 'success'
    });
  }else{
    _this.$notify({
      title: '上传失败',
      type: 'error'
    });
  }
},

2)更新歌曲文件

:action=”uploadSongUrl(scope.row.id)” 表示要访问的后台地址,并携带歌曲的id,通过id修改歌曲图像
image.png

//更新歌曲url
uploadSongUrl(id){
  return `${this.$store.state.HOST}/musicserver/song/updateSongUrl?id=${id}`
},

:before-upload=”beforeSongUpload” 表示文件上传之前,进行哪些操作,比如:上传的文件类型、大小都可以做限制;

//上传歌曲之前的校验
beforeSongUpload(file){
  var testMsg = file.name.substring(file.name.lastIndexOf('.') + 1);
  if(testMsg!='mp3'){
    this.$message({
      message: '上传文件只能是mp3格式',
      type: 'error'
    });
    return false;
  }
  return true;
},

:on-success=”handleSongSuccess” 表示上传成功后需要进行哪些操作,比如:提示上传成功或者失败

//上传歌曲成功之后要做的工作
handleSongSuccess(res){
  let _this = this;
  if(res.code == 20000){
    _this.getData();
    _this.$notify({
      title: '上传成功',
      type: 'success'
    });
  }else{
    _this.$notify({
      title: '上传失败',
      type: 'error'
    });
  }
},

2.5 页面播放歌曲

1)设置全局共享属性:id、url、isPlay

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
    state:{
        HOST: 'http://127.0.0.1:8888',
        isPlay: false,          //是否播放中
        url: '',                //歌曲地址
        id: ''                  //歌曲id
    },
    getters: { //获取共享属性的方法
        isPlay: state => state.isPlay,
        url: state => state.url,
        id: state => state.id
    },
    mutations: { //设置共享属性的方法
        setIsPlay: (state,isPlay) => {state.isPlay = isPlay},
        setUrl: (state,url) => {state.url = url},
        setId: (state,id) => {state.id = id}
    }
})

export default store

2)创建SongAudio.vue组件,专门用来播放音乐

<template>
    <div class="song-audio">
        <audio id="player" 
            :src="url" 
            controls = "controls" 
            preload = "true"
            @canplay="startPlay" 
            @ended="ended"
        ></audio>
    </div>
</template>
<script>
import {mapGetters} from 'vuex';
export default {
    name: 'song-audio',
    computed: {
        ...mapGetters([
            'id',
            'url',
            'isPlay'
        ])
    },
    watch:{
        //监听播放还是暂停
        isPlay: function(){
            this.togglePlay();
        }
    },
    methods:{
        //获取链接后准备播放
        startPlay(){
            let player = document.querySelector('#player');
            //开始播放
            player.play();
        },
        //播放完成之后触发
        ended(){
           this.isPlay = false
        },
        //开始、暂停
        togglePlay() {
            let player = document.querySelector('#player');
            if(this.isPlay){
                player.play();
            }else{
                player.pause();
            }
        }
    }
}
</script>

<style>
    .song-audio {
        display: none;
    }
</style>

3)在App.vue页面中,引入该组件

<template>
  <div id="app">
    <SongAudio />
    <router-view></router-view>
  </div>
</template>

<script>
import SongAudio from './components/SongAudio';
export default {
  name: 'App',
  components: {
    SongAudio
  }
}
</script>

<style>
</style>

4)在SongPage.vue歌曲页面中进行参数的设置
image.png
image.png
image.png

  //切换播放歌曲
setSongUrl(url,name) {
  this.toggle = name;
  this.$store.commit('setUrl',this.$store.state.HOST + url);
  if(this.isPlay){
    this.$store.commit('setIsPlay',false);
  }else{
    this.$store.commit('setIsPlay',true);
  }
}