Files

190 lines
5.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ========== 初始化二维码 ==========
document.addEventListener('DOMContentLoaded', function () {
new QRCode(document.getElementById('qrcode'), {
text: SERVER_URL,
width: 140,
height: 140,
colorDark: '#333333',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.M
});
loadFiles();
});
// ========== 复制链接 ==========
function copyUrl() {
const url = document.getElementById('urlValue').textContent;
navigator.clipboard.writeText(url).then(() => {
const btn = document.querySelector('.btn-copy');
const original = btn.textContent;
btn.textContent = '已复制 ✓';
setTimeout(() => btn.textContent = original, 1500);
}).catch(() => {
// 兼容旧浏览器
const input = document.createElement('input');
input.value = url;
document.body.appendChild(input);
input.select();
document.execCommand('copy');
document.body.removeChild(input);
});
}
// ========== 上传相关 ==========
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const progressContainer = document.getElementById('progressContainer');
const progressFill = document.getElementById('progressFill');
const progressName = document.getElementById('progressName');
const progressPercent = document.getElementById('progressPercent');
// 点击触发文件选择
dropZone.addEventListener('click', () => fileInput.click());
// 文件选择
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
uploadFiles(Array.from(e.target.files));
fileInput.value = '';
}
});
// 拖拽事件
['dragenter', 'dragover'].forEach(evt => {
dropZone.addEventListener(evt, (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.add('dragover');
});
});
['dragleave', 'drop'].forEach(evt => {
dropZone.addEventListener(evt, (e) => {
e.preventDefault();
e.stopPropagation();
dropZone.classList.remove('dragover');
});
});
dropZone.addEventListener('drop', (e) => {
const files = Array.from(e.dataTransfer.files);
if (files.length) uploadFiles(files);
});
// 批量上传
function uploadFiles(files) {
let index = 0;
const total = files.length;
function next() {
if (index >= total) {
progressContainer.style.display = 'none';
loadFiles();
return;
}
const file = files[index];
index++;
uploadSingle(file, () => next());
}
next();
}
// 单个文件上传
function uploadSingle(file, callback) {
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
progressContainer.style.display = 'block';
progressName.textContent = file.name;
progressFill.style.width = '0%';
progressPercent.textContent = '0%';
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100);
progressFill.style.width = percent + '%';
progressPercent.textContent = percent + '%';
}
});
xhr.addEventListener('load', () => {
progressFill.style.width = '100%';
progressPercent.textContent = '100%';
setTimeout(callback, 300);
});
xhr.addEventListener('error', () => {
alert('上传失败:' + file.name);
callback();
});
xhr.open('POST', '/upload');
xhr.send(formData);
}
// ========== 文件列表 ==========
function loadFiles() {
fetch('/files')
.then(res => res.json())
.then(files => renderFiles(files))
.catch(err => console.error('加载文件失败', err));
}
function renderFiles(files) {
const list = document.getElementById('fileList');
if (!files.length) {
list.innerHTML = '<li class="empty">暂无文件</li>';
return;
}
list.innerHTML = files.map(f => `
<li>
<div class="file-info">
<span class="file-icon">${getIcon(f.name)}</span>
<div class="file-meta">
<div class="file-name" title="${escapeHtml(f.name)}">${escapeHtml(f.name)}</div>
<div class="file-size">${f.size}</div>
</div>
</div>
<div class="file-actions">
<a class="btn-download" href="/download/${encodeURIComponent(f.name)}">下载</a>
<button class="btn-delete" onclick="deleteFile('${escapeJs(f.name)}')">删除</button>
</div>
</li>
`).join('');
}
function deleteFile(name) {
if (!confirm('确定删除 ' + name + ' ?')) return;
fetch('/delete/' + encodeURIComponent(name), { method: 'DELETE' })
.then(res => res.json())
.then(() => loadFiles())
.catch(err => alert('删除失败'));
}
// ========== 工具函数 ==========
function getIcon(name) {
const ext = name.split('.').pop().toLowerCase();
const map = {
jpg: '🖼️', jpeg: '🖼️', png: '🖼️', gif: '🖼️', webp: '🖼️', svg: '🖼️',
mp4: '🎬', mov: '🎬', avi: '🎬', mkv: '🎬',
mp3: '🎵', wav: '🎵', flac: '🎵',
pdf: '📕', doc: '📘', docx: '📘', xls: '📗', xlsx: '📗', ppt: '📙', pptx: '📙',
zip: '🗜️', rar: '🗜️', '7z': '🗜️', tar: '🗜️', gz: '🗜️',
txt: '📄', md: '📝', json: '📋', xml: '📋',
exe: '⚙️', apk: '📱', dmg: '💿', iso: '💿'
};
return map[ext] || '📄';
}
function escapeHtml(str) {
return String(str).replace(/[&<>"']/g, s => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
}[s]));
}
function escapeJs(str) {
return String(str).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"');
}