feat(music): 添加点赞功能并优化音乐解析

- 在 MusicPlayer 组件中添加点赞功能,使用 Check 和 Pointer 图标表示点赞状态
- 引入 toggleLikeMusic API 接口
- 优化歌词解析逻辑,提高代码可读性和性能
- 修改 UserLikeMusic 实体类,使用复合主键设计
This commit is contained in:
ikmkj
2025-07-26 00:44:34 +08:00
parent 9b54cda795
commit ce33571719
2 changed files with 33 additions and 35 deletions

View File

@@ -1,31 +1,28 @@
package com.test.musichouduan.entity; package com.test.musichouduan.entity;
import jakarta.persistence.*;
import lombok.Data; import lombok.Data;
import jakarta.persistence.*;
import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.CreationTimestamp;
import java.io.Serializable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/**
* 用户点赞音乐实体类
*/
@Data
@Entity
@Table(name = "user_like_music", uniqueConstraints = {
@UniqueConstraint(columnNames = {"userId", "musicId"})
})
public class UserLikeMusic {
@Id @Entity
@GeneratedValue(strategy = GenerationType.IDENTITY) @Data
@Table(name = "user_like_music")
@IdClass(UserLikeMusic.class)
public class UserLikeMusic implements Serializable {
private Long id; private Long id;
@Column(nullable = false) @Id
@Column(name = "user_id")
private Long userId; private Long userId;
@Column(nullable = false) @Id
@Column(name = "music_id")
private Long musicId; private Long musicId;
@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime createTime; private LocalDateTime createTime;
} }

View File

@@ -105,7 +105,7 @@
<el-tooltip :content="currentMusic.liked ? '取消点赞' : '点赞'" placement="top"> <el-tooltip :content="currentMusic.liked ? '取消点赞' : '点赞'" placement="top">
<el-button circle @click="toggleLike"> <el-button circle @click="toggleLike">
<el-icon> <el-icon>
<component :is="currentMusic.liked ? 'Pointer' : 'Pointer'" /> <component :is="currentMusic.liked ? 'Check' : 'Pointer'" />
</el-icon> </el-icon>
</el-button> </el-button>
</el-tooltip> </el-tooltip>
@@ -296,11 +296,11 @@ import { ref, computed, watch, nextTick } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { usePlayerStore } from '../stores/player' import { usePlayerStore } from '../stores/player'
import { useUserStore } from '../stores/user' import { useUserStore } from '../stores/user'
import { processMusicUrl, toggleCollectMusic, getMusicStatus } from '../api/music' import { processMusicUrl, toggleCollectMusic, getMusicStatus, toggleLikeMusic } from '../api/music'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { import {
VideoPlay, VideoPause, Back, Right, List, Close, Delete, VideoPlay, VideoPause, Back, Right, List, Close, Delete,
Mute, VideoCamera, Microphone, Star, StarFilled, ArrowDown, Pointer Mute, VideoCamera, Microphone, Star, StarFilled, ArrowDown, Pointer, Check
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
const router = useRouter() const router = useRouter()
@@ -380,26 +380,27 @@ const loopModeText = computed(() => {
// 解析歌词 // 解析歌词
const parsedLyric = computed(() => { const parsedLyric = computed(() => {
if (!currentMusic.value || !currentMusic.value.lyric) return [] if (!currentMusic.value || !currentMusic.value.lyric) return [];
const lyric = currentMusic.value.lyric const lyric = currentMusic.value.lyric;
const lines = lyric.split('\n') const lines = lyric.split('\n');
const timeRegExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/g const result = [];
const result = [] const timeRegExp = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/g;
for (const line of lines) { for (const line of lines) {
const timeMatches = [...line.matchAll(timeRegExp)] const text = line.replace(timeRegExp, '').trim();
if (timeMatches.length > 0) { if (text) {
const match = timeMatches timeRegExp.lastIndex = 0;
const min = parseInt(match) let match;
const sec = parseInt(match) while ((match = timeRegExp.exec(line)) !== null) {
const ms = parseInt(match) const min = parseInt(match, 10);
const time = min * 60 + sec + ms / 1000 const sec = parseInt(match, 10);
let text = line.replace(timeRegExp, '').trim() const ms = parseInt((match || '0').padEnd(3, '0'), 10);
if (text) { const time = min * 60 + sec + ms / 1000;
result.push({ time, text }) result.push({ time, text });
} }
} }
} }
return result.sort((a, b) => a.time - b.time) return result.sort((a, b) => a.time - b.time);
}) })
// 格式化时间 // 格式化时间