Files
auto-index/ui.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>