- 优化界面全屏、视频上传进度条、视频自动裁切铺满屏幕
This commit is contained in:
+23
-5
@@ -156,6 +156,7 @@ export default function Admin() {
|
||||
const [playState, setPlayState] = useState({ status: 'idle' });
|
||||
const [previewItem, setPreviewItem] = useState(null);
|
||||
const [toasts, setToasts] = useState([]);
|
||||
const [uploadProgress, setUploadProgress] = useState(null);
|
||||
const toastIdRef = useRef(0);
|
||||
const volTimerRef = useRef(null);
|
||||
|
||||
@@ -259,10 +260,17 @@ export default function Admin() {
|
||||
const inp = document.getElementById('fileInput');
|
||||
const fileList = Array.from(inp.files);
|
||||
if (fileList.length === 0) { addToast('请选择文件', 'error'); return; }
|
||||
await api.uploadFiles(fileList);
|
||||
inp.value = '';
|
||||
addToast('上传完成');
|
||||
loadAll();
|
||||
try {
|
||||
setUploadProgress({ percent: 0, fileName: fileList[0].name, current: 1, total: fileList.length });
|
||||
await api.uploadFilesWithProgress(fileList, setUploadProgress);
|
||||
setUploadProgress(null);
|
||||
inp.value = '';
|
||||
addToast('上传完成');
|
||||
loadAll();
|
||||
} catch (err) {
|
||||
setUploadProgress(null);
|
||||
addToast(err.message || '上传失败', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddUrl = async () => {
|
||||
@@ -403,8 +411,18 @@ export default function Admin() {
|
||||
<label>本地上传</label>
|
||||
<div className="form-row">
|
||||
<input type="file" id="fileInput" accept=".mp4,.mkv,.avi,.jpg,.jpeg,.png" multiple />
|
||||
<button className="btn-accent" onClick={handleUpload}>上传</button>
|
||||
<button className="btn-accent" onClick={handleUpload} disabled={uploadProgress !== null}>上传</button>
|
||||
</div>
|
||||
{uploadProgress && (
|
||||
<div className="upload-progress">
|
||||
<div className="progress-bar">
|
||||
<div className="progress-fill" style={{ width: `${uploadProgress.percent}%` }}></div>
|
||||
</div>
|
||||
<span className="progress-text">
|
||||
{uploadProgress.fileName} ({uploadProgress.current}/{uploadProgress.total}) — {uploadProgress.percent}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label>远程 URL</label>
|
||||
|
||||
@@ -381,6 +381,9 @@ export default function Screen() {
|
||||
<span className="sb-text">{currentItem ? `${index + 1} / ${list.length}` : '0 / 0'}</span>
|
||||
</div>
|
||||
|
||||
{/* Watermark */}
|
||||
<div className="watermark">云南派音人工智能科技提供技术支持</div>
|
||||
|
||||
{/* Sound Hint */}
|
||||
<div id="soundHint" style={{ display: soundBlocked && loaded ? 'block' : 'none' }} onClick={enableSound}>
|
||||
声音
|
||||
|
||||
@@ -949,6 +949,37 @@ button { font-family: "Poppins", "PingFang SC", sans-serif; cursor: pointer; tra
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ============ Upload Progress ============ */
|
||||
.upload-progress {
|
||||
margin-top: var(--space-12);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-12);
|
||||
}
|
||||
.progress-bar {
|
||||
flex: 1;
|
||||
height: 6px;
|
||||
background: rgba(0,0,0,0.06);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: var(--primary);
|
||||
border-radius: 3px;
|
||||
transition: width 0.25s ease;
|
||||
box-shadow: 0 0 8px rgba(0, 189, 125, 0.3);
|
||||
}
|
||||
.progress-text {
|
||||
font-family: "JetBrains Mono", "SF Mono", "Consolas", monospace;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--text-dim);
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ============ Responsive ============ */
|
||||
@media (max-width: 768px) {
|
||||
.login-card { padding: var(--space-24) var(--space-16) var(--space-32); margin: var(--space-12); max-width: none; }
|
||||
|
||||
+17
-2
@@ -121,7 +121,7 @@
|
||||
inset: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
object-fit: contain;
|
||||
object-fit: cover;
|
||||
background: transparent;
|
||||
z-index: 3;
|
||||
}
|
||||
@@ -130,7 +130,7 @@
|
||||
inset: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
object-fit: contain;
|
||||
object-fit: cover;
|
||||
background: transparent;
|
||||
z-index: 3;
|
||||
display: none;
|
||||
@@ -429,3 +429,18 @@
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: "SF Mono", "JetBrains Mono", "Consolas", monospace;
|
||||
}
|
||||
|
||||
/* ============ Watermark ============ */
|
||||
.watermark {
|
||||
position: fixed;
|
||||
bottom: 28px;
|
||||
left: 28px;
|
||||
z-index: 100;
|
||||
font-size: 10px;
|
||||
font-weight: 400;
|
||||
color: rgba(255,255,255,0.2);
|
||||
letter-spacing: 0.08em;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
font-family: "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
+40
-1
@@ -59,7 +59,46 @@ export async function uploadFiles(files) {
|
||||
for (const file of files) {
|
||||
const fd = new FormData();
|
||||
fd.append('file', file);
|
||||
await fetch(`${BASE}/upload`, { method: 'POST', body: fd });
|
||||
const res = await fetch(`${BASE}/upload`, { method: 'POST', body: fd });
|
||||
if (!res.ok) throw new Error(`上传失败 (${res.status})`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function uploadFilesWithProgress(files, onProgress) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
await new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', `${BASE}/upload`);
|
||||
|
||||
xhr.upload.onprogress = (e) => {
|
||||
if (e.lengthComputable) {
|
||||
onProgress({
|
||||
percent: Math.round((e.loaded / e.total) * 100),
|
||||
loaded: e.loaded,
|
||||
total: e.total,
|
||||
fileName: file.name,
|
||||
current: i + 1,
|
||||
total: files.length,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`上传失败 (${xhr.status})`));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => reject(new Error('网络错误,请检查连接'));
|
||||
xhr.ontimeout = () => reject(new Error('上传超时'));
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append('file', file);
|
||||
xhr.send(fd);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user