221 lines
8.7 KiB
HTML
221 lines
8.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<style>
|
|
/* 1. 强力隐藏原生元素,解决重叠和点击失效问题 */
|
|
html body h1, html body pre, html body hr {
|
|
display: none !important;
|
|
}
|
|
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
max-width: 1000px; margin: 0 auto; padding: 20px;
|
|
background: #f8fafc; color: #1e293b;
|
|
display: block !important;
|
|
}
|
|
|
|
/* 管理栏样式 */
|
|
.app-header {
|
|
background: white; padding: 1rem 1.5rem; border-radius: 10px;
|
|
margin-bottom: 1rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
}
|
|
.app-header h2 { margin: 0; font-size: 1.1rem; }
|
|
.btn-group { display: flex; gap: 10px; }
|
|
|
|
.btn {
|
|
cursor: pointer; padding: 8px 16px; border: none; border-radius: 6px;
|
|
font-weight: 500; font-size: 14px; transition: 0.2s;
|
|
}
|
|
.btn-primary { background: #3b82f6; color: white; }
|
|
.btn-success { background: #10b981; color: white; }
|
|
|
|
/* 搜索框 */
|
|
#search-input {
|
|
width: 100%; padding: 12px; margin-bottom: 1rem; box-sizing: border-box;
|
|
border: 2px solid #e2e8f0; border-radius: 8px; outline: none;
|
|
}
|
|
|
|
/* 列表表格 */
|
|
.file-table { background: white; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); overflow: hidden; }
|
|
.table-header {
|
|
display: flex; background: #f1f5f9; padding: 12px 20px;
|
|
font-weight: 600; font-size: 13px; color: #475569;
|
|
}
|
|
.file-row {
|
|
display: flex; align-items: center; padding: 10px 20px;
|
|
border-bottom: 1px solid #f1f5f9; transition: background 0.2s;
|
|
}
|
|
.file-row:hover { background: #f8fafc; }
|
|
.col-name { flex: 1; text-decoration: none; color: #1e293b; font-size: 14px; font-weight: 500; display: flex; align-items: center; gap: 8px; }
|
|
.col-size { width: 80px; text-align: right; color: #64748b; font-size: 13px; }
|
|
.col-time { width: 150px; text-align: right; color: #64748b; font-size: 13px; }
|
|
.col-actions { width: 120px; text-align: right; }
|
|
|
|
.action-btn {
|
|
font-size: 12px; padding: 4px 8px; margin-left: 5px;
|
|
border-radius: 4px; border: 1px solid #e2e8f0;
|
|
background: white; cursor: pointer;
|
|
}
|
|
.action-btn:hover { border-color: #3b82f6; color: #3b82f6; }
|
|
.btn-del { color: #ef4444; }
|
|
|
|
#toast {
|
|
position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%);
|
|
background: rgba(30, 41, 59, 0.9); color: white; padding: 8px 20px;
|
|
border-radius: 20px; font-size: 14px; display: none; z-index: 10000;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="app">
|
|
<div class="app-header">
|
|
<h2 id="current-path">📂 文件列表 /</h2>
|
|
<div class="btn-group">
|
|
<button class="btn btn-success" onclick="mkFolder()">新建文件夹</button>
|
|
<button class="btn btn-primary" onclick="document.getElementById('file-el').click()">上传文件</button>
|
|
<input type="file" id="file-el" style="display:none" onchange="uploadFile(this)">
|
|
</div>
|
|
</div>
|
|
|
|
<input type="text" id="search-input" placeholder="输入关键词搜索...">
|
|
|
|
<div class="file-table">
|
|
<div class="table-header">
|
|
<div style="flex:1">名称</div>
|
|
<div style="width:80px; text-align:right">大小</div>
|
|
<div style="width:150px; text-align:right">修改时间</div>
|
|
<div style="width:120px; text-align:right">操作</div>
|
|
</div>
|
|
<div id="list-body"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="toast">提示信息</div>
|
|
|
|
<script>
|
|
const toast = document.getElementById('toast');
|
|
function showMsg(m) {
|
|
toast.innerText = m; toast.style.display = 'block';
|
|
setTimeout(() => toast.style.display = 'none', 2000);
|
|
}
|
|
|
|
// 复制功能兼容逻辑
|
|
function copyToClipboard(text) {
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
navigator.clipboard.writeText(text).then(() => showMsg("✅ 已复制到剪贴板"));
|
|
} else {
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = text;
|
|
textArea.style.position = "fixed";
|
|
textArea.style.left = "-9999px";
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
try {
|
|
document.execCommand('copy');
|
|
showMsg("✅ 链接已复制");
|
|
} catch (err) {
|
|
alert('复制失败: ' + text);
|
|
}
|
|
document.body.removeChild(textArea);
|
|
}
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const pre = document.querySelector('pre');
|
|
if (!pre) return;
|
|
|
|
const path = window.location.pathname;
|
|
document.getElementById('current-path').innerText = "📂 " + decodeURIComponent(path);
|
|
|
|
const files = [];
|
|
const regex = /<a href="([^"]*)"[^>]*>([^<]*)<\/a>\s*([^<]*?)(?=<a|$)/g;
|
|
let match;
|
|
|
|
while ((match = regex.exec(pre.innerHTML)) !== null) {
|
|
const [_, href, name, meta] = match;
|
|
// 只要不是 ../ 就放入文件列表数据
|
|
if (name !== '../') {
|
|
const metaParts = meta.trim().split(/\s+/);
|
|
files.push({
|
|
href, name: decodeURIComponent(name),
|
|
date: metaParts[0] + ' ' + (metaParts[1] || ''),
|
|
size: (metaParts.length >= 3) ? metaParts[2] : '-',
|
|
isDir: name.endsWith('/')
|
|
});
|
|
}
|
|
}
|
|
|
|
function render(data, isSearch = false) {
|
|
const body = document.getElementById('list-body');
|
|
let html = '';
|
|
|
|
// 如果不是根目录,且不是在搜索状态,显示“返回上一级”
|
|
if (path !== '/' && !isSearch) {
|
|
html += `
|
|
<div class="file-row">
|
|
<a href="../" class="col-name">↩️ .. (返回上一级)</a>
|
|
<div class="col-size">-</div>
|
|
<div class="col-time">-</div>
|
|
<div class="col-actions"></div>
|
|
</div>`;
|
|
}
|
|
|
|
html += data.map(f => `
|
|
<div class="file-row">
|
|
<a href="${f.href}" class="col-name"><span>${f.isDir ? '📁' : '📄'}</span> ${f.name}</a>
|
|
<div class="col-size">${f.size}</div>
|
|
<div class="col-time">${f.date}</div>
|
|
<div class="col-actions">
|
|
<button class="action-btn" onclick="copyLink('${f.href}')">链接</button>
|
|
<button class="action-btn btn-del" onclick="deleteItem('${f.href}')">删除</button>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
|
|
body.innerHTML = html || '<div style="padding:20px;text-align:center;color:#999;">该目录下没有文件</div>';
|
|
}
|
|
|
|
window.copyLink = (href) => {
|
|
const baseUrl = window.location.origin + window.location.pathname;
|
|
const fullUrl = (baseUrl.endsWith('/') ? baseUrl : baseUrl + '/') + href.replace(/^\//, '');
|
|
copyToClipboard(fullUrl);
|
|
};
|
|
|
|
render(files);
|
|
|
|
document.getElementById('search-input').oninput = (e) => {
|
|
const k = e.target.value.toLowerCase();
|
|
const filtered = files.filter(f => f.name.toLowerCase().includes(k));
|
|
render(filtered, k.length > 0);
|
|
};
|
|
});
|
|
|
|
// WebDAV 操作
|
|
async function mkFolder() {
|
|
const n = prompt("请输入文件夹名称:");
|
|
if (!n) return;
|
|
const res = await fetch(encodeURIComponent(n) + '/', { method: 'MKCOL' });
|
|
if (res.ok) location.reload(); else alert("错误: " + res.status + " (检查权限)");
|
|
}
|
|
|
|
async function uploadFile(el) {
|
|
const file = el.files[0];
|
|
if (!file) return;
|
|
showMsg("正在上传...");
|
|
const res = await fetch(encodeURIComponent(file.name), { method: 'PUT', body: file });
|
|
if (res.ok) location.reload(); else alert("上传失败,检查大小限制或权限");
|
|
}
|
|
|
|
async function deleteItem(href) {
|
|
if (!confirm("确定删除吗?此操作不可撤销。")) return;
|
|
const res = await fetch(href, { method: 'DELETE' });
|
|
if (res.ok) location.reload(); else alert("删除失败");
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|