feat: 播放控制移入页眉 + 媒体铺满智能裁切
页眉播放控制(仅媒体轮播页): - 新增 MediaHeaderControls:播放/暂停、上一段/下一段、序号、当前文件名(点击进后台)、声音解锁 - 新增 mediaControl 共享状态桥:媒体页同步播放状态,页眉订阅渲染并下发指令 - 移除媒体页底部控制区,媒体区保持纯净全屏 媒体展示: - .ms-media 改为 object-fit: cover,填满整个容器并智能居中裁切(原 contain 留黑边) 修复: - MediaScreen useEffect 依赖数组引用后声明函数导致的 TDZ 崩溃 - 清理 MediaScreen 中不再使用的 Icon/cleanName/togglePlayPause
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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)}>
|
||||
|
||||
Reference in New Issue
Block a user