diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 32a22f5..81ce157 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -14,7 +14,8 @@ { "title": "昆明大学生创业园展播系统", "width": 1200, - "height": 800 + "height": 800, + "fullscreen": true } ], "security": { diff --git a/src/pages/Admin.jsx b/src/pages/Admin.jsx index e4e57c7..32c54b3 100644 --- a/src/pages/Admin.jsx +++ b/src/pages/Admin.jsx @@ -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() {
- +
+ {uploadProgress && ( +
+
+
+
+ + {uploadProgress.fileName} ({uploadProgress.current}/{uploadProgress.total}) — {uploadProgress.percent}% + +
+ )}
diff --git a/src/pages/Screen.jsx b/src/pages/Screen.jsx index 5a22126..97a0b0a 100644 --- a/src/pages/Screen.jsx +++ b/src/pages/Screen.jsx @@ -381,6 +381,9 @@ export default function Screen() { {currentItem ? `${index + 1} / ${list.length}` : '0 / 0'}
+ {/* Watermark */} +
云南派音人工智能科技提供技术支持
+ {/* Sound Hint */}
声音 diff --git a/src/styles/admin.css b/src/styles/admin.css index 7e4cc3f..0fa3245 100644 --- a/src/styles/admin.css +++ b/src/styles/admin.css @@ -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; } diff --git a/src/styles/screen.css b/src/styles/screen.css index a484898..f00a340 100644 --- a/src/styles/screen.css +++ b/src/styles/screen.css @@ -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; +} diff --git a/src/utils/api.js b/src/utils/api.js index 38f09b6..15cceac 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -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); + }); } }