76 lines
3.0 KiB
JavaScript
76 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 大文件分片上传常量 (见 app.js uploadChunked/uploadOneChunk)
|
|
// ---------------------------------------------------------------------------
|
|
const CHUNK_THRESHOLD = 5 * 1024 * 1024 * 1024; // >5GB 走分片
|
|
const CHUNK_SIZE = 1 * 1024 * 1024 * 1024; // 每片 1GB
|
|
|
|
function uuid() {
|
|
// crypto.randomUUID 仅在 secure context (HTTPS / localhost) 可用, HTTP 非 localhost 会抛.
|
|
// 用 crypto.getRandomValues (所有现代浏览器均支持, 不要求 secure context) 手动拼一个 v4.
|
|
const b = new Uint8Array(16);
|
|
crypto.getRandomValues(b);
|
|
b[6] = (b[6] & 0x0f) | 0x40; // version 4
|
|
b[8] = (b[8] & 0x3f) | 0x80; // variant RFC 4122
|
|
const h = [...b].map(x => x.toString(16).padStart(2, '0')).join('');
|
|
return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20)}`;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 纯函数 + 倒计时 interval
|
|
// 跨文件共享 (classic <script> 加载顺序: app.js → helpers.js → batch.js).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function formatRemain(iso) {
|
|
const ms = new Date(iso).getTime() - Date.now();
|
|
if (ms <= 0) return '已过期';
|
|
const s = Math.floor(ms / 1000);
|
|
if (s < 60) return `剩 ${s} 秒`;
|
|
const m = Math.floor(s / 60);
|
|
if (m < 60) return `剩 ${m} 分钟`;
|
|
const h = Math.floor(m / 60);
|
|
if (h < 24) return `剩 ${h} 小时${m % 60 ? ' ' + (m % 60) + ' 分' : ''}`;
|
|
const d = Math.floor(h / 24);
|
|
return `剩 ${d} 天${h % 24 ? ' ' + (h % 24) + ' 小时' : ''}`;
|
|
}
|
|
|
|
function isAlmostExpired(iso) {
|
|
const ms = new Date(iso).getTime() - Date.now();
|
|
return ms > 0 && ms < 60 * 60 * 1000; // < 1h
|
|
}
|
|
|
|
setInterval(() => {
|
|
document.querySelectorAll('[data-expires]').forEach(el => {
|
|
el.textContent = formatRemain(el.dataset.expires);
|
|
el.classList.toggle('danger', isAlmostExpired(el.dataset.expires));
|
|
});
|
|
}, 1000);
|
|
|
|
function iconFor(ext) {
|
|
const map = { pdf:'📄', zip:'📦', rar:'📦', '7z':'📦', txt:'📄', md:'📄',
|
|
png:'🖼', jpg:'🖼', jpeg:'🖼', gif:'🖼', webp:'🖼', svg:'🖼',
|
|
mp4:'🎬', mov:'🎬', avi:'🎬', mkv:'🎬',
|
|
mp3:'🎵', wav:'🎵', flac:'🎵',
|
|
xls:'📊', xlsx:'📊', csv:'📊',
|
|
doc:'📃', docx:'📃', ppt:'📽', pptx:'📽' };
|
|
return map[ext] || '📁';
|
|
}
|
|
|
|
function formatSize(b) {
|
|
if (b == null || isNaN(b)) return '-';
|
|
if (b < 1024) return b + ' B';
|
|
if (b < 1024 * 1024) return (b / 1024).toFixed(1) + ' KB';
|
|
if (b < 1024 * 1024 * 1024) return (b / 1024 / 1024).toFixed(2) + ' MB';
|
|
return (b / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
}
|
|
|
|
function formatTime(iso) {
|
|
const d = new Date(iso);
|
|
const pad = n => String(n).padStart(2, '0');
|
|
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
}
|
|
|
|
function escapeHtml(s) {
|
|
return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
} |