feat: 播放控制移入页眉 + 媒体铺满智能裁切

页眉播放控制(仅媒体轮播页):
- 新增 MediaHeaderControls:播放/暂停、上一段/下一段、序号、当前文件名(点击进后台)、声音解锁
- 新增 mediaControl 共享状态桥:媒体页同步播放状态,页眉订阅渲染并下发指令
- 移除媒体页底部控制区,媒体区保持纯净全屏

媒体展示:
- .ms-media 改为 object-fit: cover,填满整个容器并智能居中裁切(原 contain 留黑边)

修复:
- MediaScreen useEffect 依赖数组引用后声明函数导致的 TDZ 崩溃
- 清理 MediaScreen 中不再使用的 Icon/cleanName/togglePlayPause
This commit is contained in:
Pine
2026-08-17 22:45:46 +08:00
parent 07dbd404a9
commit 29bdad316f
6 changed files with 266 additions and 209 deletions
+78
View File
@@ -0,0 +1,78 @@
import { useState, useEffect } from 'react';
import Icon from './Icons';
import { onMediaUiState, mediaCommand } from '../utils/mediaControl';
/* =========================================================
页眉媒体控制区 —— 仅媒体轮播页显示
订阅媒体页播放状态(paused/index/total/name/soundBlocked),
点击通过 mediaCommand() 下发指令,由 MediaScreen 执行
========================================================= */
/** 展示名清洗:URL 只取文件名,不显示完整播放地址 */
function cleanName(name) {
if (!name) return '';
const text = String(name);
try {
const u = new URL(text);
const last = decodeURIComponent(u.pathname.split('/').filter(Boolean).pop() || '');
return last ? `${last}${u.search ? ' …' : ''}` : text;
} catch {
return text;
}
}
export default function MediaHeaderControls() {
const [st, setSt] = useState(null);
useEffect(() => onMediaUiState(setSt), []);
if (!st || !st.loaded) {
return (
<div className="bd-hd-media" title="媒体播放控制">
<Icon name="player-play" size={13} />
</div>
);
}
const showName = cleanName(st.name) || '等待播放…';
return (
<div className="bd-hd-media">
{/* 播放 / 暂停 */}
<button
className={`bd-hd-media-btn ${st.paused ? 'primary' : ''}`}
onClick={() => mediaCommand(st.paused ? 'play' : 'pause')}
title={st.paused ? '播放' : '暂停'}
>
<Icon name={st.paused ? 'play' : 'pause'} size={14} />
</button>
{/* 上一段 */}
<button className="bd-hd-media-btn" onClick={() => mediaCommand('prev')} title="上一段">
<Icon name="chevron-left" size={13} />
</button>
{/* 序号 */}
<span className="bd-hd-media-num">
<b>{st.index + 1}</b>/<em>{st.total}</em>
</span>
{/* 下一段 */}
<button className="bd-hd-media-btn" onClick={() => mediaCommand('next')} title="下一段">
<Icon name="chevron-right" size={13} />
</button>
{/* 当前播放名(点击进入后台) */}
<button
className="bd-hd-media-name"
onClick={() => mediaCommand('open_login')}
title="点击进入后台"
>
<span className="bd-hd-media-dot" />
<span>{showName}</span>
</button>
{/* 声音解锁 */}
{st.soundBlocked && (
<button className="bd-hd-media-btn warn" onClick={() => mediaCommand('enable_sound')} title="点击开启声音">
<Icon name="volume" size={14} />
</button>
)}
</div>
);
}
+3
View File
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import Icon from './Icons';
import MediaHeaderControls from './MediaHeaderControls';
import { PAGE_ORDER, usePageIndex } from '../utils/pageNav';
import { Avatar, AvatarImage, AvatarFallback } from '@appica/ui-react/avatar'
@@ -52,6 +53,8 @@ export default function PageHeader() {
</div>
</div>
<div className="bd-header-right">
{/* 媒体轮播页:页眉内嵌播放控制 */}
{location.pathname === '/screen' && <MediaHeaderControls />}
<div className="bd-nav-keys" title="左右方向键或点击切换页面">
{left && (
<button className="bd-nav-key" onClick={() => navigate(left.path)}>