ow
This commit is contained in:
1 parent
adcf94df4a
commit
428c94951b
9 files changed
+622
-263
No files matched your search
@@ -19,13 +19,19 @@
|
||||
</div>
|
||||
|
||||
<div v-else class="vap-body">
|
||||
<!-- 收起 -->
|
||||
<button class="vap-collapse" type="button" title="收起播放器" @click="emit('close')">
|
||||
<!-- 收起(控制区收起时换成下面的"显示控制区",右上角同一位置只放一个) -->
|
||||
<button v-if="controlsVisible" class="vap-collapse" type="button" title="收起播放器" @click="emit('close')">
|
||||
<DownOutlined />
|
||||
</button>
|
||||
<!-- 控制区收起后的恢复入口:整页用法没有弹窗右上角按钮,否则收起就点不回来了 -->
|
||||
<button v-if="!controlsVisible" class="vap-restore" type="button" title="显示控制区"
|
||||
@click.stop="toggleControls">
|
||||
<EyeOutlined />
|
||||
</button>
|
||||
|
||||
<!-- 唱片 + 唱臂 -->
|
||||
<div class="vap-stage">
|
||||
<!-- 唱片 + 唱臂:整片区域单击即可暂停/播放,
|
||||
这样页内全屏(控制区已收起)时也还能控制播放 -->
|
||||
<div class="vap-stage" :title="isPlaying ? '单击暂停' : '单击播放'" @click="toggle">
|
||||
<div class="vap-turntable">
|
||||
<div class="vap-disc" :class="{ 'is-spinning': isPlaying }">
|
||||
<img v-if="current.cover && !coverError" class="vap-disc-cover" :src="current.cover" alt=""
|
||||
@@ -66,10 +72,12 @@
|
||||
</div>
|
||||
|
||||
<!-- 功能图标行 -->
|
||||
<div class="vap-actions">
|
||||
<button class="vap-act" :class="{ 'is-fav': isFaved }" type="button" title="喜欢" @click="onFav">
|
||||
<HeartFilled v-if="isFaved" />
|
||||
<HeartOutlined v-else />
|
||||
<div v-if="controlsVisible" class="vap-actions">
|
||||
<!-- 隐藏/显示控制区:整页用法时父组件会顺带藏掉 layout 顶栏,做到整页全屏 -->
|
||||
<button class="vap-act" type="button" :class="{ 'is-on': !controlsVisible }"
|
||||
:title="controlsVisible ? '隐藏控制区' : '显示控制区'" @click.stop="toggleControls">
|
||||
<EyeInvisibleOutlined v-if="controlsVisible" />
|
||||
<EyeOutlined v-else />
|
||||
</button>
|
||||
<button class="vap-act" type="button" title="下载" @click="onDownload">
|
||||
<DownloadOutlined />
|
||||
@@ -117,8 +125,8 @@
|
||||
</div>
|
||||
|
||||
<!-- 进度 -->
|
||||
<div class="vap-progress" ref="barRef" @pointerdown="onBarDown" @pointermove="onBarMove" @pointerup="onBarUp"
|
||||
@pointercancel="onBarUp">
|
||||
<div v-if="controlsVisible" class="vap-progress" ref="barRef" @pointerdown="onBarDown"
|
||||
@pointermove="onBarMove" @pointerup="onBarUp" @pointercancel="onBarUp">
|
||||
<div class="vap-bar">
|
||||
<span class="vap-bar-fill" :style="{ width: displayRatio * 100 + '%' }"></span>
|
||||
<span class="vap-bar-thumb" :style="{ left: displayRatio * 100 + '%' }"></span>
|
||||
@@ -130,7 +138,7 @@
|
||||
</div>
|
||||
|
||||
<!-- 主控制 -->
|
||||
<div class="vap-controls">
|
||||
<div v-if="controlsVisible" class="vap-controls">
|
||||
<button class="vap-ctl" type="button" :title="modeTitle" @click="cycleMode">
|
||||
<RetweetOutlined v-if="mode !== 'random'" />
|
||||
<SwapOutlined v-else />
|
||||
@@ -180,12 +188,13 @@
|
||||
import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import { request } from "@/utils/request";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import {
|
||||
DownOutlined, HeartOutlined, HeartFilled, DownloadOutlined, FieldTimeOutlined,
|
||||
DownOutlined, DownloadOutlined, FieldTimeOutlined,
|
||||
AudioOutlined, AudioMutedOutlined, EllipsisOutlined, InfoCircleOutlined,
|
||||
RetweetOutlined, SwapOutlined, StepBackwardFilled, StepForwardFilled,
|
||||
CaretRightFilled, PauseOutlined, UnorderedListOutlined, CloseOutlined,
|
||||
CustomerServiceOutlined,
|
||||
CustomerServiceOutlined, EyeOutlined, EyeInvisibleOutlined,
|
||||
} from "@ant-design/icons-vue";
|
||||
|
||||
/** 一首歌 */
|
||||
@@ -202,8 +211,31 @@ export interface VinylTrack {
|
||||
lrc?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{ tracks?: VinylTrack[] }>(), { tracks: () => [] });
|
||||
const emit = defineEmits<{ (e: "close"): void }>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
tracks?: VinylTrack[];
|
||||
/** 控制区(功能图标行 / 进度 / 主控制)是否显示;页面可用它做「整页全屏」 */
|
||||
controls?: boolean;
|
||||
}>(),
|
||||
{ tracks: () => [], controls: true }
|
||||
);
|
||||
const emit = defineEmits<{
|
||||
(e: "close"): void;
|
||||
(e: "update:controls", visible: boolean): void;
|
||||
}>();
|
||||
|
||||
/**
|
||||
* 控制区是否显示:由功能图标行的眼睛图标切换。
|
||||
* 收起后唱片与歌词吃掉这部分空间;父组件可用 v-model:controls 拿到状态做整页全屏。
|
||||
* passive: true(与 VideoPlayer / PhotoSlidePlayer 一致):父组件不绑 v-model 时
|
||||
* 按钮也不会"点了没反应"。
|
||||
*/
|
||||
const controlsVisible = useVModel(props, "controls", emit, { passive: true });
|
||||
|
||||
const toggleControls = () => {
|
||||
menu.value = ""; // 顺手收起可能打开的弹层(定时 / 音量 / 倍速)
|
||||
controlsVisible.value = !controlsVisible.value;
|
||||
};
|
||||
|
||||
/* ---------------- 基础状态 ---------------- */
|
||||
const audioARef = ref<HTMLAudioElement | null>(null);
|
||||
@@ -580,25 +612,7 @@ const onSleep = (v: number) => {
|
||||
menu.value = "";
|
||||
};
|
||||
|
||||
/* ---------------- 喜欢 / 下载 / 信息 ---------------- */
|
||||
const FAV_KEY = "vap-favs";
|
||||
const loadFavs = (): Set<string> => {
|
||||
try {
|
||||
return new Set<string>(JSON.parse(localStorage.getItem(FAV_KEY) || "[]"));
|
||||
} catch {
|
||||
return new Set<string>();
|
||||
}
|
||||
};
|
||||
const favs = ref<Set<string>>(loadFavs());
|
||||
const isFaved = computed(() => !!current.value && favs.value.has(current.value.url));
|
||||
const onFav = () => {
|
||||
if (!current.value) return;
|
||||
const s = new Set(favs.value);
|
||||
if (s.has(current.value.url)) s.delete(current.value.url);
|
||||
else s.add(current.value.url);
|
||||
favs.value = s;
|
||||
localStorage.setItem(FAV_KEY, JSON.stringify([...s]));
|
||||
};
|
||||
/* ---------------- 下载 / 信息 ---------------- */
|
||||
const onDownload = async () => {
|
||||
if (!current.value) return;
|
||||
// 参考 DmsFileEnquiry.vue:先取回字节流,再用 Blob + <a download> 落盘
|
||||
@@ -781,7 +795,33 @@ onBeforeUnmount(() => {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
/* 控制区收起后的恢复入口:与"收起"按钮同一位置(右上角),整页用法下没有弹窗右上角按钮 */
|
||||
.vap-restore {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
z-index: 30;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
background: rgba(0, 0, 0, 0.38);
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
opacity: 0.55;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.vap-restore:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ---------- 唱片区 ---------- */
|
||||
/* 单击整片区域即可暂停/播放(全屏收起控制区后唯一的播放开关),所以给可点的光标 */
|
||||
.vap-stage {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
@@ -789,6 +829,7 @@ onBeforeUnmount(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 唱盘方框:唱片与唱臂共用同一坐标系(尺寸即唱片直径) */
|
||||
@@ -1040,10 +1081,6 @@ onBeforeUnmount(() => {
|
||||
color: #7ff3e8;
|
||||
}
|
||||
|
||||
.vap-act.is-fav {
|
||||
color: #ff5b7f;
|
||||
}
|
||||
|
||||
.vap-act.is-muted {
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user