- 优化界面全屏、视频上传进度条、视频自动裁切铺满屏幕
This commit is contained in:
@@ -14,7 +14,8 @@
|
|||||||
{
|
{
|
||||||
"title": "昆明大学生创业园展播系统",
|
"title": "昆明大学生创业园展播系统",
|
||||||
"width": 1200,
|
"width": 1200,
|
||||||
"height": 800
|
"height": 800,
|
||||||
|
"fullscreen": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
+20
-2
@@ -156,6 +156,7 @@ export default function Admin() {
|
|||||||
const [playState, setPlayState] = useState({ status: 'idle' });
|
const [playState, setPlayState] = useState({ status: 'idle' });
|
||||||
const [previewItem, setPreviewItem] = useState(null);
|
const [previewItem, setPreviewItem] = useState(null);
|
||||||
const [toasts, setToasts] = useState([]);
|
const [toasts, setToasts] = useState([]);
|
||||||
|
const [uploadProgress, setUploadProgress] = useState(null);
|
||||||
const toastIdRef = useRef(0);
|
const toastIdRef = useRef(0);
|
||||||
const volTimerRef = useRef(null);
|
const volTimerRef = useRef(null);
|
||||||
|
|
||||||
@@ -259,10 +260,17 @@ export default function Admin() {
|
|||||||
const inp = document.getElementById('fileInput');
|
const inp = document.getElementById('fileInput');
|
||||||
const fileList = Array.from(inp.files);
|
const fileList = Array.from(inp.files);
|
||||||
if (fileList.length === 0) { addToast('请选择文件', 'error'); return; }
|
if (fileList.length === 0) { addToast('请选择文件', 'error'); return; }
|
||||||
await api.uploadFiles(fileList);
|
try {
|
||||||
|
setUploadProgress({ percent: 0, fileName: fileList[0].name, current: 1, total: fileList.length });
|
||||||
|
await api.uploadFilesWithProgress(fileList, setUploadProgress);
|
||||||
|
setUploadProgress(null);
|
||||||
inp.value = '';
|
inp.value = '';
|
||||||
addToast('上传完成');
|
addToast('上传完成');
|
||||||
loadAll();
|
loadAll();
|
||||||
|
} catch (err) {
|
||||||
|
setUploadProgress(null);
|
||||||
|
addToast(err.message || '上传失败', 'error');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddUrl = async () => {
|
const handleAddUrl = async () => {
|
||||||
@@ -403,8 +411,18 @@ export default function Admin() {
|
|||||||
<label>本地上传</label>
|
<label>本地上传</label>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<input type="file" id="fileInput" accept=".mp4,.mkv,.avi,.jpg,.jpeg,.png" multiple />
|
<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>
|
</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>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label>远程 URL</label>
|
<label>远程 URL</label>
|
||||||
|
|||||||
@@ -381,6 +381,9 @@ export default function Screen() {
|
|||||||
<span className="sb-text">{currentItem ? `${index + 1} / ${list.length}` : '0 / 0'}</span>
|
<span className="sb-text">{currentItem ? `${index + 1} / ${list.length}` : '0 / 0'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Watermark */}
|
||||||
|
<div className="watermark">云南派音人工智能科技提供技术支持</div>
|
||||||
|
|
||||||
{/* Sound Hint */}
|
{/* Sound Hint */}
|
||||||
<div id="soundHint" style={{ display: soundBlocked && loaded ? 'block' : 'none' }} onClick={enableSound}>
|
<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;
|
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 ============ */
|
/* ============ Responsive ============ */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.login-card { padding: var(--space-24) var(--space-16) var(--space-32); margin: var(--space-12); max-width: none; }
|
.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;
|
inset: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
object-fit: contain;
|
object-fit: cover;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
object-fit: contain;
|
object-fit: cover;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
display: none;
|
display: none;
|
||||||
@@ -429,3 +429,18 @@
|
|||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
font-family: "SF Mono", "JetBrains Mono", "Consolas", monospace;
|
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) {
|
for (const file of files) {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('file', file);
|
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