feat: 歌单加了个顶部栏

This commit is contained in:
539943419 committed 2024-09-29 17:14:02 +08:00
1 parent ae476413df
commit 2e75a35d10
9 files changed
+306 -87

No files matched your search

@@ -39,7 +39,7 @@ const useAplayerMiao = (ap: Reactive<apType>, options?: aplayerMiaoOption) => {
const app = createApp(aplayerListNew, { ap, ...(options ?? {}) });
app.mount(new_list_dom);
console.log({ap})
// console.log({ap})
ap.on("destroy", () => {
app.unmount();
@@ -1,113 +1,145 @@
<template>
<miao-drop-handler @on-virtual-files="handleDropVFiles">
<miao-alertTip-provider ref="miaoMessageRef">
<miao-alert-tip-provider ref="miaoMessageRef">
<div class="miaoMusic-container">
<NScrollbar>
<div class="miaoMusic-main">
</div>
<miao-music-main v-if="ap" :ap="ap"></miao-music-main>
</NScrollbar>
<div ref="aplayer" />
<miao-mask
:show="isAPlayerListShow"
style="z-index: 5"
@click="() => ap?.list.hide()"></miao-mask>
<div ref="aplayer" style="z-index: 10" />
</div>
</miao-alertTip-provider>
</miao-alert-tip-provider>
</miao-drop-handler>
</template>
<script setup lang="ts">
import useAplayerMiao from './hooks/aplayerMiao'
import { VirtualFile } from '@/class/VirtualDirectory'
import { App, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import { NScrollbar } from 'naive-ui'
import './src/APlayer.fix.css'
// @ts-ignore
import APlayer from 'APlayer'
import config from '@/config'
import difference from 'lodash/difference'
import uniq from 'lodash/uniq'
import apType from './types/ap'
import miaoDropHandler from '@/components/miaoDropHandler.vue'
import miaoAlertTipProvider from '@/components/miaoAlertTipProvider.vue'
import useAplayerMiao from './hooks/aplayerMiao';
import { VirtualFile } from '@/class/VirtualDirectory';
import { App, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { NScrollbar } from 'naive-ui';
import "./src/APlayer.fix.css"
// @ts-ignore
import APlayer from 'APlayer';
import config from '@/config';
import difference from 'lodash/difference';
import uniq from 'lodash/uniq'
import apType from './types/ap';
import usePluginCenter from '@/hooks/usePluginCenter';
import usePluginCenter from '@/hooks/usePluginCenter'
import VirtualPage from '@/class/VirtualPage'
import miaoMask from '@/components/miaoMask.vue'
import miaoMusicMain from './src/main.vue'
const pluginCenter = usePluginCenter()
let ap = ref<apType>()
let vueApp: App<Element>
const miaoMessageRef = ref<InstanceType<typeof miaoAlertTipProvider>>()
const aplayer = ref<any>(null)
const isAPlayerListShow = ref(true)
const currentFiles = defineModel<VirtualFile[]>('currentFiles', {
required: true
});
watch(() => currentFiles.value, (value, oldValue) => {
const diff = difference(value, oldValue)
if (diff.length > 0) {
const _s = `等${diff.length}个资源`
miaoMessageRef.value?.alertTip(`已添加 ${diff[0].name}${diff.length !== 1 ? _s : ''}`, {
type: 'success',
timeout: 3000
})
ap.list.add(getMusicList(diff))
}
})
const getMusicList = (mp3VFiles: VirtualFile[]) => mp3VFiles.map((v, index) => ({
url: `${config.baseUrl}${v.url}`,
name: v.name,
artist: ' ',
index,
}));
const props = defineProps<{
view: VirtualPage
}>()
watch(
() => currentFiles.value,
(value, oldValue) => {
const diff = difference(value, oldValue)
if (diff.length > 0) {
const _s = `等${diff.length}个资源`
miaoMessageRef.value?.alertTip(
`已添加 ${diff[0].name}${diff.length !== 1 ? _s : ''}`,
{
type: 'success',
timeout: 3000
}
)
ap?.value?.list.add(getMusicList(diff))
}
}
)
const getMusicList = (mp3VFiles: VirtualFile[]) =>
mp3VFiles.map((v, index) => ({
url: `${config.baseUrl}${v.url}`,
name: v.name,
artist: ' ',
index
}))
const handleDropVFiles = (files: VirtualFile[]) => {
currentFiles.value = uniq([...currentFiles.value, ...files]);
};
currentFiles.value = uniq([...currentFiles.value, ...files])
}
const aplayer = ref<any>(null);
let ap: apType
let vueApp: App<Element>
onMounted(() => {
currentFiles.value = [...currentFiles.value.reverse()]
const initAPlayerMiao = () => {
const aplayerOption = {
container: aplayer.value,
audio: getMusicList(currentFiles.value),
autoplay: true,
volume: 1,
theme: "#60e8a2",
theme: '#60e8a2',
listMaxHeight: '500px'
}
ap = reactive<apType>(new APlayer(aplayerOption))
vueApp = useAplayerMiao(ap, {
onDragItemStart: () => {
},
onDragItemEnd: () => {
},
onPlayVideo: element => {
pluginCenter.usePlugin('miaoVideoPlayer', [], [currentFiles.value.find(v=>v.name === element.name) as VirtualFile])
ap.value = reactive<apType>(new APlayer(aplayerOption))
vueApp = useAplayerMiao(ap.value, {
onDragItemStart: () => {},
onDragItemEnd: () => {},
onPlayVideo: (element) => {
pluginCenter.usePlugin(
'miaoVideoPlayer',
[],
[
currentFiles.value.find(
(v) => v.name === element.name
) as VirtualFile
]
)
}
})
ap.on('listremove', (...args: any) => {
ap.value.on('listremove', (...args: any) => {
const { index } = args[0] as { index: number }
const _name = ap.list.audios[index].name
currentFiles.value = currentFiles.value.filter(v => v.name !== _name)
const _name = ap?.value?.list.audios[index].name
currentFiles.value = currentFiles.value.filter((v) => v.name !== _name)
})
});
ap.value.on('play', () => {
const name = ap.value?.list.audios[ap.value.list.index].name as string
props.view.setTitle(name)
})
ap.value.on('listshow', () => {
isAPlayerListShow.value = true
})
ap.value.on('listhide', () => {
isAPlayerListShow.value = false
})
}
onMounted(() => {
currentFiles.value = [...currentFiles.value.reverse()]
initAPlayerMiao()
})
onUnmounted(() => {
vueApp.unmount()
ap.destroy()
ap?.value?.destroy()
})
</script>
<style scoped lang="scss">
.miaoMusic-container {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.miaoMusic-main {
padding-bottom: 88px;
}
}
</style>
<style lang="scss">
@@ -117,5 +149,9 @@ onUnmounted(() => {
position: absolute;
bottom: 10px;
width: -webkit-fill-available;
.aplayer-icon-order,
.aplayer-icon-loop {
display: none !important;
}
}
</style>
</style>
@@ -3,7 +3,7 @@
font-family: Arial, Helvetica, sans-serif;
margin: 5px;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .07), 0 1px 5px 0 rgba(0, 0, 0, .1);
border-radius: 2px;
border-radius: 5px;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
@@ -22,10 +22,10 @@
height: 100%
}
.aplayer svg circle,
/* .aplayer svg circle,
.aplayer svg path {
fill: #fff
}
} */
.aplayer.aplayer-withlist .aplayer-info {
border-bottom: 1px solid #e9e9e9
@@ -1,4 +1,18 @@
<template>
<div class="aplayer-list-top">
<div @click="loopModeToggle" class="aplayer-list-top-loopMode">
<component
:is="loopModeMap[loopMode].icon"
class="aplayer-list-top-loopMode-icon"></component>
<span class="aplayer-list-top-loopMode-name">
{{ loopModeMap[loopMode].name }}
</span>
</div>
<div class="aplayer-list-top-buttons">
<component :is="AddCircleOutline" class="aplayer-list-top-buttons-icon"></component>
</div>
<span></span>
</div>
<ol
:style="{ maxHeight: ap.options.listMaxHeight }"
:class="[
@@ -35,7 +49,13 @@
<div
class="aplayer-list-control-btn aplayer-list-control-btn-hover aplayer-list-control-btn-play"
@click.stop="switchAudio(index)">
<PlayOutline class="aplayer-list-control-btn-svg" />
<component
:is="
activeAudioIndex == index && !isPaused
? PauseOutline
: PlayOutline
"
class="aplayer-list-control-btn-svg"></component>
</div>
<div
class="aplayer-list-control-btn aplayer-list-control-btn-hover"
@@ -88,8 +108,15 @@ import {
ReorderFourOutline,
PlayOutline,
HeartOutline,
VideocamOutline
VideocamOutline,
PauseOutline,
ListCircleOutline,
ListOutline,
HeadsetOutline,
LogoOctocat,
AddCircleOutline
} from '@vicons/ionicons5'
import { VueDraggable } from 'vue-draggable-plus'
// 与vue-draggable冲突
// import miaoLazyDiv from '@/components/miaoLazyDiv.vue';
@@ -102,6 +129,8 @@ const props = defineProps<{
onPlayVideo: (element: audioType) => void
}>()
const apRef = ref(props.ap)
const ol = ref<HTMLOListElement>()
const refreshKey = ref(Math.random())
@@ -115,6 +144,17 @@ const apTheme = computed(
)
const activeAudioIndex = computed(() => props.ap.list.index)
const isPaused = ref(false)
props.ap.on('play', () => {
nextTick(() => {
isPaused.value = false
})
})
props.ap.on('pause', () => {
isPaused.value = true
})
props.ap.on('listswitch', (...args: any) => {
// 手动触发更新,不然监听不到
@@ -136,6 +176,12 @@ props.ap.on('listadd', refresh)
const switchAudio = (index: number) => {
if (index === activeAudioIndex.value) {
if (props.ap.paused) {
props.ap.play()
} else {
props.ap.pause()
}
props.ap.paused = props.ap.paused
return
}
props.ap.list.switch(index)
@@ -207,9 +253,52 @@ const isVideo = (element: audioType) => {
}
return false
}
const loopModeMap = {
0: { name: '列表循环', icon: ListCircleOutline },
1: { name: '单曲循环', icon: HeadsetOutline },
2: { name: '列表播放', icon: ListOutline },
3: { name: '随机播放', icon: LogoOctocat }
}
const loopMode = computed(() => {
if (apRef.value.options.order === 'list') {
switch (apRef.value.options.loop) {
case 'all':
return 0
case 'one':
return 1
case 'none':
return 2
}
}
return 3
})
const loopModeToggle = () => {
const ops = apRef.value.options
if (ops.order === 'random') {
ops.order = 'list'
} else {
switch (ops.loop) {
case 'all': {
ops.loop = 'one'
break
}
case 'one': {
ops.loop = 'none'
break
}
case 'none': {
ops.loop = 'all'
ops.order = 'random'
break
}
}
}
}
onMounted(() => {
// console.log(props.ap)
console.log(props.ap)
})
</script>
@@ -259,6 +348,60 @@ onMounted(() => {
}
}
}
.aplayer-list-top {
height: 30px;
// width: auto;
// max-width: 100%;
position: relative;
border-bottom: 1px solid #e9e9e9;
display: flex;
justify-items: baseline;
align-items: center;
padding: 0 10px;
.aplayer-list-top-loopMode {
text-align: center;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
float: left;
// background-color: #f9f9f9;
scale: 1;
transition: scale 0.15s ease;
&-icon {
width: 20px;
aspect-ratio: 1;
transform: translateY(1px);
fill: #000;
path {
fill: #000 !important;
}
}
&-name {
margin-left: 5px;
}
&:hover {
scale: 1.1;
}
}
.aplayer-list-top-buttons {
display: inline-flex;
position: absolute;
right: 10px;
align-items: center;
justify-content: center;
scale: 1;
transition: scale 0.15s ease;
&-icon {
width: 20px;
aspect-ratio: 1;
}
&:hover {
scale: 1.1;
}
}
}
</style>
<style lang="scss">
@@ -0,0 +1,11 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 29 32">
<path
d="M9.333 9.333h13.333v4l5.333-5.333-5.333-5.333v4h-16v8h2.667v-5.333zM22.667 22.667h-13.333v-4l-5.333 5.333 5.333 5.333v-4h16v-8h-2.667v5.333z"></path>
</svg>
</template>
<script setup lang="ts">
</script>
<style scoped></style>
+25
View File
@@ -0,0 +1,25 @@
<template>
<div class="miaoMusic-main">
<div class="fd" v-if="false">
{{ fakeData }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import apType from '../types/ap'
const props = defineProps<{
ap: apType
}>()
const fakeData = ref(new Array(10000).fill('我会一直盯着你'))
console.log(props.ap)
</script>
<style lang="scss" scoped>
.miaoMusic-main {
padding-bottom: 88px;
}
</style>
+18 -14
View File
@@ -9,6 +9,7 @@ export type audioType = {
}
type apType = {
paused: boolean
list: {
audios: audioType[]
index: number
@@ -16,25 +17,28 @@ type apType = {
remove: (index: number) => void
add: (audios: audioType[]) => void
refreshAudioIndex: () => void
hide: () => void
}
container: HTMLDivElement
on: (event: string, callback: () => void) => void
options: {
audio: audioType[],
autoplay: boolean,
"volume": number,
"theme": string,
"mini": boolean,
"fixed": boolean,
"mutex": boolean,
"lrcType": number,
"preload": "auto",
"loop": "all",
"order": "list",
"listMaxHeight": string,
"storageName": "aplayer-setting"
},
audio: audioType[]
autoplay: boolean
volume: number
theme: string
mini: boolean
fixed: boolean
mutex: boolean
lrcType: number
preload: 'auto'
loop: 'all' | 'one' | 'none'
order: 'list' | 'random'
listMaxHeight: string
storageName: 'aplayer-setting'
}
destroy: () => {}
pause: () => void
play: () => void
}
export default apType