feat(user): 为收藏音乐页面添加更多操作功能
- 添加下拉菜单,支持多种操作 - 实现添加到播放列表、下一首播放、下载音乐、下载歌词等功能 - 优化用户交互,增加确认对话框和提示消息
This commit is contained in:
@@ -1,12 +1,27 @@
|
|||||||
package com.test.musichouduan.controller;
|
package com.test.musichouduan.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||||
|
import com.test.musichouduan.entity.Music;
|
||||||
|
import com.test.musichouduan.service.MusicService;
|
||||||
import com.test.musichouduan.dto.Result;
|
import com.test.musichouduan.dto.Result;
|
||||||
import com.test.musichouduan.service.FileService;
|
import com.test.musichouduan.service.FileService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.core.io.InputStreamResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件控制器
|
* 文件控制器
|
||||||
*/
|
*/
|
||||||
@@ -16,7 +31,8 @@ public class FileController {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FileService fileService;
|
private FileService fileService;
|
||||||
|
@Autowired
|
||||||
|
private MusicService musicService;
|
||||||
/**
|
/**
|
||||||
* 上传图片
|
* 上传图片
|
||||||
*
|
*
|
||||||
@@ -68,4 +84,73 @@ public class FileController {
|
|||||||
boolean result = fileService.deleteFile(url);
|
boolean result = fileService.deleteFile(url);
|
||||||
return Result.success("删除成功", result);
|
return Result.success("删除成功", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载音乐
|
||||||
|
*
|
||||||
|
* @param musicId 音乐ID
|
||||||
|
* @return 响应实体
|
||||||
|
*/
|
||||||
|
@GetMapping("/download/music/{musicId}")
|
||||||
|
public ResponseEntity<Resource> downloadMusic(@PathVariable Integer musicId) throws IOException {
|
||||||
|
// 1. 根据 musicId 获取音乐信息
|
||||||
|
Music music = musicService.findMusicById(musicId);
|
||||||
|
if (music == null || music.getUrl() == null || music.getUrl().isEmpty()) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 获取文件路径
|
||||||
|
String fileUrl = music.getUrl(); // 格式: /upload/musics/xxxxxxxx.mp3
|
||||||
|
String filePath = "music-houduan" + fileUrl; // 转换为相对项目路径
|
||||||
|
Path path = Paths.get(filePath);
|
||||||
|
Resource resource = new InputStreamResource(Files.newInputStream(path));
|
||||||
|
|
||||||
|
// 3. 设置响应头
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
String filename = music.getName() + ".mp3"; // 使用音乐名作为文件名
|
||||||
|
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
|
||||||
|
headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
||||||
|
headers.add(HttpHeaders.PRAGMA, "no-cache");
|
||||||
|
headers.add(HttpHeaders.EXPIRES, "0");
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.headers(headers)
|
||||||
|
.contentLength(Files.size(path))
|
||||||
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.body(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载歌词
|
||||||
|
*
|
||||||
|
* @param musicId 音乐ID
|
||||||
|
* @return 响应实体
|
||||||
|
*/
|
||||||
|
@GetMapping("/download/lyric/{musicId}")
|
||||||
|
public ResponseEntity<Resource> downloadLyric(@PathVariable Integer musicId) throws IOException {
|
||||||
|
// 1. 根据 musicId 获取音乐信息
|
||||||
|
Music music = musicService.findMusicById(musicId);
|
||||||
|
if (music == null || music.getLyric() == null || music.getLyric().isEmpty()) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 将歌词文本转换为输入流
|
||||||
|
String lyricContent = music.getLyric();
|
||||||
|
byte[] lyricBytes = lyricContent.getBytes(StandardCharsets.UTF_8);
|
||||||
|
Resource resource = new InputStreamResource(new ByteArrayInputStream(lyricBytes));
|
||||||
|
|
||||||
|
// 3. 设置响应头
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
String filename = music.getName() + ".lrc"; // 使用音乐名作为文件名
|
||||||
|
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
|
||||||
|
headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
|
||||||
|
headers.add(HttpHeaders.PRAGMA, "no-cache");
|
||||||
|
headers.add(HttpHeaders.EXPIRES, "0");
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.headers(headers)
|
||||||
|
.contentLength(lyricBytes.length)
|
||||||
|
.contentType(MediaType.TEXT_PLAIN)
|
||||||
|
.body(resource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,4 +161,12 @@ public interface MusicService {
|
|||||||
* @return 音乐状态DTO
|
* @return 音乐状态DTO
|
||||||
*/
|
*/
|
||||||
MusicStatusDTO getMusicStatus(Long musicId, Long userId);
|
MusicStatusDTO getMusicStatus(Long musicId, Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID查找音乐实体
|
||||||
|
*
|
||||||
|
* @param musicId 音乐ID
|
||||||
|
* @return 音乐实体
|
||||||
|
*/
|
||||||
|
Music findMusicById(Integer musicId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -615,6 +615,11 @@ public class MusicServiceImpl implements MusicService {
|
|||||||
return new MusicStatusDTO(isCollected, isLiked);
|
return new MusicStatusDTO(isCollected, isLiked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Music findMusicById(Integer musicId) {
|
||||||
|
return musicRepository.findById(musicId.longValue()).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将实体转换为DTO
|
* 将实体转换为DTO
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -99,8 +99,9 @@
|
|||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item command="addToPlaylist">添加到播放列表</el-dropdown-item>
|
<el-dropdown-item command="addToPlaylist">添加到播放列表</el-dropdown-item>
|
||||||
<el-dropdown-item command="playNext">下一首播放</el-dropdown-item>
|
<el-dropdown-item command="playNext">下一首播放</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="downloadMusic">下载音乐</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="downloadLyric">下载歌词</el-dropdown-item>
|
||||||
<el-dropdown-item command="share" v-if="false">分享</el-dropdown-item>
|
<el-dropdown-item command="share" v-if="false">分享</el-dropdown-item>
|
||||||
<el-dropdown-item command="download" v-if="false">下载</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
@@ -128,7 +129,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { VideoPlay, VideoPause, Star, More, InfoFilled, Pointer } from '@element-plus/icons-vue'
|
import { VideoPlay, VideoPause, Star, More, InfoFilled, Pointer } from '@element-plus/icons-vue'
|
||||||
import { usePlayerStore } from '../stores/player'
|
import { usePlayerStore } from '../stores/player'
|
||||||
import { toggleCollectMusic, toggleLikeMusic } from '../api/music'
|
import { toggleCollectMusic, toggleLikeMusic } from '../api/music'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
list: {
|
list: {
|
||||||
@@ -258,12 +259,31 @@ const handleCommand = (command, music) => {
|
|||||||
case 'share':
|
case 'share':
|
||||||
ElMessage.info('分享功能开发中')
|
ElMessage.info('分享功能开发中')
|
||||||
break
|
break
|
||||||
case 'download':
|
case 'downloadMusic':
|
||||||
ElMessage.info('下载功能开发中')
|
downloadFile('music', music)
|
||||||
|
break
|
||||||
|
case 'downloadLyric':
|
||||||
|
downloadFile('lyric', music)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const downloadFile = (type, music) => {
|
||||||
|
const typeName = type === 'music' ? '音乐' : '歌词'
|
||||||
|
ElMessageBox.confirm(`确定要下载《${music.name}》的${typeName}吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'info'
|
||||||
|
}).then(() => {
|
||||||
|
const url = `/api/file/download/${type}/${music.id}`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
ElMessage.success(`${typeName}开始下载...`)
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.info(`已取消下载${typeName}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化时长
|
// 格式化时长
|
||||||
const formatDuration = (seconds) => {
|
const formatDuration = (seconds) => {
|
||||||
if (!seconds) return '00:00'
|
if (!seconds) return '00:00'
|
||||||
|
|||||||
@@ -331,6 +331,54 @@ const formatPlayCount = (count) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理下拉菜单命令
|
||||||
|
const handleCommand = (command, music) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'addToPlaylist':
|
||||||
|
playerStore.addToPlaylist(music)
|
||||||
|
ElMessage.success('已添加到播放列表')
|
||||||
|
break
|
||||||
|
case 'playNext':
|
||||||
|
const currentIndex = playerStore.getCurrentIndex
|
||||||
|
if (currentIndex !== -1) {
|
||||||
|
const playlist = [...playerStore.getPlaylist]
|
||||||
|
const existingIndex = playlist.findIndex(item => item.id === music.id)
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
playlist.splice(existingIndex, 1)
|
||||||
|
}
|
||||||
|
playlist.splice(currentIndex + 1, 0, music)
|
||||||
|
playerStore.$patch({ playlist })
|
||||||
|
ElMessage.success('已添加到下一首播放')
|
||||||
|
} else {
|
||||||
|
playerStore.addToPlaylist(music)
|
||||||
|
ElMessage.success('已添加到播放列表')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'downloadMusic':
|
||||||
|
downloadFile('music', music)
|
||||||
|
break
|
||||||
|
case 'downloadLyric':
|
||||||
|
downloadFile('lyric', music)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const downloadFile = (type, music) => {
|
||||||
|
const typeName = type === 'music' ? '音乐' : '歌词'
|
||||||
|
ElMessageBox.confirm(`确定要下载《${music.name}》的${typeName}吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'info'
|
||||||
|
}).then(() => {
|
||||||
|
const url = `/api/file/download/${type}/${music.id}`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
ElMessage.success(`${typeName}开始下载...`)
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.info(`已取消下载${typeName}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// WebSocket 订阅实例
|
// WebSocket 订阅实例
|
||||||
let commentSubscription = null;
|
let commentSubscription = null;
|
||||||
|
|
||||||
@@ -484,14 +532,21 @@ onUnmounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" width="100" align="center">
|
<el-table-column label="操作" width="120" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-dropdown trigger="click" @command="handleCommand($event, scope.row)">
|
||||||
icon="Plus"
|
<el-button circle size="small">
|
||||||
circle
|
<el-icon><More /></el-icon>
|
||||||
size="small"
|
</el-button>
|
||||||
@click.stop="addToPlaylist(scope.row)"
|
<template #dropdown>
|
||||||
></el-button>
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="addToPlaylist">添加到播放列表</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="playNext">下一首播放</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="downloadMusic">下载音乐</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="downloadLyric">下载歌词</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ const playAll = () => {
|
|||||||
|
|
||||||
// 播放第一首
|
// 播放第一首
|
||||||
playerStore.setCurrentMusic({
|
playerStore.setCurrentMusic({
|
||||||
...songs.value[0],
|
...songs.value,
|
||||||
singer: singer.value.name
|
singer: singer.value.name
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -317,6 +317,52 @@ const deleteComment = async (commentId) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理下拉菜单命令
|
||||||
|
const handleCommand = (command, music) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'addToPlaylist':
|
||||||
|
addToPlaylist(music)
|
||||||
|
break
|
||||||
|
case 'playNext':
|
||||||
|
const currentIndex = playerStore.getCurrentIndex
|
||||||
|
if (currentIndex !== -1) {
|
||||||
|
const playlist = [...playerStore.getPlaylist]
|
||||||
|
const existingIndex = playlist.findIndex(item => item.id === music.id)
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
playlist.splice(existingIndex, 1)
|
||||||
|
}
|
||||||
|
playlist.splice(currentIndex + 1, 0, music)
|
||||||
|
playerStore.$patch({ playlist })
|
||||||
|
ElMessage.success('已添加到下一首播放')
|
||||||
|
} else {
|
||||||
|
playerStore.addToPlaylist(music)
|
||||||
|
ElMessage.success('已添加到播放列表')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'downloadMusic':
|
||||||
|
downloadFile('music', music)
|
||||||
|
break
|
||||||
|
case 'downloadLyric':
|
||||||
|
downloadFile('lyric', music)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const downloadFile = (type, music) => {
|
||||||
|
const typeName = type === 'music' ? '音乐' : '歌词'
|
||||||
|
ElMessageBox.confirm(`确定要下载《${music.name}》的${typeName}吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'info'
|
||||||
|
}).then(() => {
|
||||||
|
const url = `/api/file/download/${type}/${music.id}`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
ElMessage.success(`${typeName}开始下载...`)
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.info(`已取消下载${typeName}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
@@ -497,14 +543,21 @@ onUnmounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
<el-table-column label="操作" width="100" align="center">
|
<el-table-column label="操作" width="120" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-dropdown trigger="click" @command="handleCommand($event, scope.row)">
|
||||||
icon="Plus"
|
<el-button circle size="small">
|
||||||
circle
|
<el-icon><More /></el-icon>
|
||||||
size="small"
|
</el-button>
|
||||||
@click.stop="addToPlaylist(scope.row)"
|
<template #dropdown>
|
||||||
></el-button>
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="addToPlaylist">添加到播放列表</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="playNext">下一首播放</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="downloadMusic">下载音乐</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="downloadLyric">下载歌词</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|||||||
@@ -99,6 +99,56 @@ const playAll = () => {
|
|||||||
ElMessage.success('开始播放全部收藏音乐')
|
ElMessage.success('开始播放全部收藏音乐')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理下拉菜单命令
|
||||||
|
const handleCommand = (command, music) => {
|
||||||
|
switch (command) {
|
||||||
|
case 'addToPlaylist':
|
||||||
|
addToPlaylist(music)
|
||||||
|
break
|
||||||
|
case 'playNext':
|
||||||
|
const currentIndex = playerStore.getCurrentIndex
|
||||||
|
if (currentIndex !== -1) {
|
||||||
|
const playlist = [...playerStore.getPlaylist]
|
||||||
|
const existingIndex = playlist.findIndex(item => item.id === music.id)
|
||||||
|
if (existingIndex !== -1) {
|
||||||
|
playlist.splice(existingIndex, 1)
|
||||||
|
}
|
||||||
|
playlist.splice(currentIndex + 1, 0, music)
|
||||||
|
playerStore.$patch({ playlist })
|
||||||
|
ElMessage.success('已添加到下一首播放')
|
||||||
|
} else {
|
||||||
|
playerStore.addToPlaylist(music)
|
||||||
|
ElMessage.success('已添加到播放列表')
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'downloadMusic':
|
||||||
|
downloadFile('music', music)
|
||||||
|
break
|
||||||
|
case 'downloadLyric':
|
||||||
|
downloadFile('lyric', music)
|
||||||
|
break
|
||||||
|
case 'cancelCollect':
|
||||||
|
cancelCollect(music.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载文件
|
||||||
|
const downloadFile = (type, music) => {
|
||||||
|
const typeName = type === 'music' ? '音乐' : '歌词'
|
||||||
|
ElMessageBox.confirm(`确定要下载《${music.name}》的${typeName}吗?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'info'
|
||||||
|
}).then(() => {
|
||||||
|
const url = `/api/file/download/${type}/${music.id}`
|
||||||
|
window.open(url, '_blank')
|
||||||
|
ElMessage.success(`${typeName}开始下载...`)
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.info(`已取消下载${typeName}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
const formatTime = (seconds) => {
|
const formatTime = (seconds) => {
|
||||||
const mins = Math.floor(seconds / 60)
|
const mins = Math.floor(seconds / 60)
|
||||||
@@ -167,20 +217,20 @@ onMounted(() => {
|
|||||||
|
|
||||||
<el-table-column label="操作" width="150" align="center">
|
<el-table-column label="操作" width="150" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-dropdown trigger="click" @command="handleCommand($event, scope.row)">
|
||||||
icon="Delete"
|
<el-button circle size="small">
|
||||||
circle
|
<el-icon><More /></el-icon>
|
||||||
size="small"
|
</el-button>
|
||||||
type="danger"
|
<template #dropdown>
|
||||||
@click="cancelCollect(scope.row.id)"
|
<el-dropdown-menu>
|
||||||
></el-button>
|
<el-dropdown-item command="addToPlaylist">添加到播放列表</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="playNext">下一首播放</el-dropdown-item>
|
||||||
<el-button
|
<el-dropdown-item command="downloadMusic">下载音乐</el-dropdown-item>
|
||||||
icon="Plus"
|
<el-dropdown-item command="downloadLyric">下载歌词</el-dropdown-item>
|
||||||
circle
|
<el-dropdown-item command="cancelCollect" divided>取消收藏</el-dropdown-item>
|
||||||
size="small"
|
</el-dropdown-menu>
|
||||||
@click="addToPlaylist(scope.row)"
|
</template>
|
||||||
></el-button>
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|||||||
Reference in New Issue
Block a user