Compare commits

...

3 Commits

Author SHA1 Message Date
ed7e8a7768
修复播放器拖拽进度图标显示跳变问题
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 23:37:25 +08:00
83a814c386
处理vue子组件emit事件警告
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 22:32:04 +08:00
c55b75686e
播放器组件重构&歌词加载问题修复
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 22:15:57 +08:00
7 changed files with 684 additions and 708 deletions

View File

@ -34,6 +34,7 @@ export interface MusicPlayerItem {
src: string src: string
pic: string pic: string
lrc?: string lrc?: string
lyricId?: string
} }
export interface StatType { export interface StatType {

View File

@ -169,13 +169,13 @@
</el-table> </el-table>
</el-drawer> </el-drawer>
<el-drawer v-model="musicPlaying" :close-on-click-modal="false" size="40%" title="播放音乐"> <el-drawer v-model="musicPlaying" :close-on-click-modal="false" size="40%" title="播放音乐">
<a-player v-if="musicPlaying" ref="player" autoplay showLrc :list="musicList" v-model:music="currentMusic" @play="musicPlay"/> <a-player v-if="musicPlaying && currentMusic" ref="player" autoplay showLrc :list="musicList" v-model:music="currentMusic" @play="musicPlay"/>
</el-drawer> </el-drawer>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, watch } from 'vue'
import { useStore } from 'vuex' import { useStore } from 'vuex'
import { useBaseList } from '@/model/baselist' import { useBaseList } from '@/model/baselist'
import { MsgResult, Page } from '@/model/common.dto' import { MsgResult, Page } from '@/model/common.dto'
@ -225,7 +225,7 @@ const musicList = ref<MusicPlayerItem[]>([])
const currentMusic = ref<MusicPlayerItem>() const currentMusic = ref<MusicPlayerItem>()
const lyricForm = ref<VForm>() const lyricForm = ref<VForm>()
const musicUpload = ref<UploadInstance>() const musicUpload = ref<UploadInstance>()
const player = ref<any>() const player = ref<InstanceType<typeof APlayer>>()
const lyricLoading = ref(false) const lyricLoading = ref(false)
const libDrawerVisible = ref(false) const libDrawerVisible = ref(false)
const libTableData = ref<(MusicLibModel & { _isNew?: boolean })[]>([]) const libTableData = ref<(MusicLibModel & { _isNew?: boolean })[]>([])
@ -245,6 +245,13 @@ const lyricRuleValidate = {
let selectedIds: string[] = [] let selectedIds: string[] = []
watch(() => currentMusic.value, async (newMusic) => {
if (newMusic && !newMusic.lrc) {
const { lyric } = await http.get<any, any>('/music/lyric/get', {params: {lyricId: newMusic.lyricId}})
newMusic.lrc = lyric
}
})
async function loadData() { async function loadData() {
loading.value = true loading.value = true
const data = await http.get<MusicPage, any>('/music/list', {params: search}) const data = await http.get<MusicPage, any>('/music/list', {params: search})
@ -273,9 +280,7 @@ async function playMusic() {
album: item.album, album: item.album,
src: `${apiBase}/common/music/load/${item._id}`, src: `${apiBase}/common/music/load/${item._id}`,
pic: `${apiBase}/common/music/album/${item._id}`, pic: `${apiBase}/common/music/album/${item._id}`,
} lyricId: item.lyricId,
if (item.lyricId) {
musicItem.lrc = `${apiBase}/common/music/lyric/${item.lyricId}`
} }
return musicItem return musicItem
}) })
@ -373,23 +378,23 @@ function musicPlay() {
artwork: [{src: location.origin + currentMusic.value.pic}] artwork: [{src: location.origin + currentMusic.value.pic}]
}) })
navigator.mediaSession.setActionHandler('play', () => { navigator.mediaSession.setActionHandler('play', () => {
player.value.play() void player.value?.play()
}) })
navigator.mediaSession.setActionHandler('pause', () => { navigator.mediaSession.setActionHandler('pause', () => {
player.value.pause() player.value?.pause()
}) })
navigator.mediaSession.setActionHandler('previoustrack', () => { navigator.mediaSession.setActionHandler('previoustrack', () => {
if (currentId === 0) { if (currentId === 0) {
player.value.switch(musicList.value.length - 1) player.value?.switch(musicList.value.length - 1)
} else { } else {
player.value.switch(currentId - 1) player.value?.switch(currentId - 1)
} }
}) })
navigator.mediaSession.setActionHandler('nexttrack', () => { navigator.mediaSession.setActionHandler('nexttrack', () => {
if (currentId === musicList.value.length - 1) { if (currentId === musicList.value.length - 1) {
player.value.switch(0) player.value?.switch(0)
} else { } else {
player.value.switch(currentId + 1) player.value?.switch(currentId + 1)
} }
}) })
} }

View File

@ -3,7 +3,7 @@
<icon-button <icon-button
:class="`aplayer-icon-${volumeIcon}`" :class="`aplayer-icon-${volumeIcon}`"
:icon="volumeIcon" :icon="volumeIcon"
@click="$emit('togglemute')" @click="emit('togglemute')"
/> />
<div <div
class="aplayer-volume-bar-wrap" class="aplayer-volume-bar-wrap"
@ -33,7 +33,7 @@ const props = defineProps<{
muted: boolean muted: boolean
theme: string theme: string
}>() }>()
const emit = defineEmits(['setvolume']) const emit = defineEmits(['setvolume', 'togglemute'])
const barHeight = 40 const barHeight = 40
const volumeIcon = computed(() => { const volumeIcon = computed(() => {

View File

@ -4,9 +4,9 @@
:loadProgress="loadProgress" :loadProgress="loadProgress"
:playProgress="playProgress" :playProgress="playProgress"
:theme="theme" :theme="theme"
@dragbegin="val => $emit('dragbegin', val)" @dragbegin="val => emit('dragbegin', val)"
@dragend="val => $emit('dragend', val)" @dragend="val => emit('dragend', val)"
@dragging="val => $emit('dragging', val)" @dragging="val => emit('dragging', val)"
/> />
<div class="aplayer-time"> <div class="aplayer-time">
<div class="aplayer-time-inner"> <div class="aplayer-time-inner">
@ -18,26 +18,26 @@
:volume="volume" :volume="volume"
:theme="theme" :theme="theme"
:muted="muted" :muted="muted"
@togglemute="$emit('togglemute')" @togglemute="emit('togglemute')"
@setvolume="(v: number) => $emit('setvolume', v)" @setvolume="(v: number) => emit('setvolume', v)"
/> />
<icon-button <icon-button
class="aplayer-icon-mode" class="aplayer-icon-mode"
icon="shuffle" icon="shuffle"
:class="{ 'inactive': !shuffle }" :class="{ 'inactive': !shuffle }"
@click="$emit('toggleshuffle')" @click="emit('toggleshuffle')"
/> />
<icon-button <icon-button
class="aplayer-icon-mode" class="aplayer-icon-mode"
:icon="repeat === 'repeat_one' ? 'repeat_one' : 'repeat_all'" :icon="repeat === 'repeat_one' ? 'repeat_one' : 'repeat_all'"
:class="{ 'inactive': repeat === 'no_repeat'}" :class="{ 'inactive': repeat === 'no_repeat'}"
@click="$emit('nextmode')" @click="emit('nextmode')"
/> />
<icon-button <icon-button
class="aplayer-icon-menu" class="aplayer-icon-menu"
icon="menu" icon="menu"
:class="{ 'inactive': !showList }" :class="{ 'inactive': !showList }"
@click="$emit('togglelist')" @click="emit('togglelist')"
/> />
</div> </div>
</div> </div>
@ -60,6 +60,7 @@ const props = defineProps<{
showList: boolean showList: boolean
isMobile: boolean isMobile: boolean
}>() }>()
const emit = defineEmits(['dragbegin', 'dragend', 'dragging', 'togglemute', 'setvolume', 'toggleshuffle', 'nextmode', 'togglelist'])
const loadProgress = computed(() => { const loadProgress = computed(() => {
if (props.stat.duration === 0) return 0 if (props.stat.duration === 0) return 0
return props.stat.loadedTime / props.stat.duration return props.stat.loadedTime / props.stat.duration

View File

@ -11,7 +11,7 @@
v-for="(aMusic, index) of musicList" v-for="(aMusic, index) of musicList"
:key="index" :key="index"
:class="{'aplayer-list-light': aMusic === currentMusic}" :class="{'aplayer-list-light': aMusic === currentMusic}"
@click="$emit('selectsong', aMusic)" @click="emit('selectsong', aMusic)"
> >
<span class="aplayer-list-cur" :style="{background: theme}"></span> <span class="aplayer-list-cur" :style="{background: theme}"></span>
<span class="aplayer-list-index">{{ index + 1}}</span> <span class="aplayer-list-index">{{ index + 1}}</span>
@ -34,6 +34,7 @@ const props = defineProps<{
theme?: string theme?: string
listMaxHeight?: string listMaxHeight?: string
}>() }>()
const emit = defineEmits(['selectsong'])
const listHeightStyle = computed(() => { const listHeightStyle = computed(() => {
return { return {
height: `${33 * props.musicList.length - 1}px`, height: `${33 * props.musicList.length - 1}px`,

View File

@ -25,10 +25,9 @@ const props = defineProps<{
playStat: StatType playStat: StatType
}>() }>()
const displayLrc = ref('')
const currentLineIndex = ref(0) const currentLineIndex = ref(0)
const lrcLines = computed(() => parseLrc(displayLrc.value)) const lrcLines = computed(() => parseLrc(props.lrcSource))
const transformStyle = computed(() => { const transformStyle = computed(() => {
return { return {
@ -37,17 +36,6 @@ const transformStyle = computed(() => {
} }
}) })
watch(() => props.lrcSource, (lrcSource: string) => {
currentLineIndex.value = 0
if (/^https?:\/\//.test(lrcSource)) {
fetch(lrcSource)
.then(response => response.text())
.then(lrc => displayLrc.value = lrc)
} else {
displayLrc.value = lrcSource
}
}, { immediate: true })
watch(() => props.playStat.playedTime, (playedTime: number) => { watch(() => props.playStat.playedTime, (playedTime: number) => {
for (let i = 0; i < lrcLines.value.length; i++) { for (let i = 0; i < lrcLines.value.length; i++) {
const line = lrcLines.value[i] const line = lrcLines.value[i]

File diff suppressed because it is too large Load Diff