mirror of
https://github.com/qaiu/netdisk-fast-download.git
synced 2025-12-16 12:23:03 +00:00
693 lines
22 KiB
HTML
693 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>网盘目录管理系统</title>
|
|
<!-- 本地引用Font Awesome -->
|
|
<link rel="stylesheet" href="./css/all.min.css">
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
|
}
|
|
|
|
body {
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
color: #333;
|
|
}
|
|
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
header {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
padding: 20px;
|
|
}
|
|
|
|
h1 {
|
|
color: #2c3e50;
|
|
font-size: 2.5rem;
|
|
margin-bottom: 10px;
|
|
position: relative;
|
|
display: inline-block;
|
|
}
|
|
|
|
h1:after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -10px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 80px;
|
|
height: 4px;
|
|
background: linear-gradient(90deg, #3498db, #2c3e50);
|
|
border-radius: 2px;
|
|
}
|
|
|
|
.subtitle {
|
|
color: #7f8c8d;
|
|
font-size: 1.1rem;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.dashboard {
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.breadcrumb {
|
|
background: #f8f9fa;
|
|
padding: 16px 24px;
|
|
border-bottom: 1px solid #eaeaea;
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.breadcrumb-item {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 0.95rem;
|
|
color: #7f8c8d;
|
|
cursor: pointer;
|
|
transition: color 0.2s;
|
|
}
|
|
|
|
.breadcrumb-item:hover {
|
|
color: #3498db;
|
|
}
|
|
|
|
.breadcrumb-item i {
|
|
margin: 0 8px;
|
|
font-size: 0.8rem;
|
|
color: #bdc3c7;
|
|
}
|
|
|
|
.content {
|
|
padding: 24px;
|
|
min-height: 500px;
|
|
}
|
|
|
|
.grid-view {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.item {
|
|
background: #fff;
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
box-shadow: 0 3px 15px rgba(0, 0, 0, 0.05);
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
padding: 20px 10px;
|
|
border: 2px solid transparent;
|
|
}
|
|
|
|
.item:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
|
|
border-color: #3498db;
|
|
}
|
|
|
|
.item-icon {
|
|
font-size: 3.5rem;
|
|
margin-bottom: 15px;
|
|
transition: transform 0.3s;
|
|
}
|
|
|
|
.item:hover .item-icon {
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
.folder .item-icon {
|
|
color: #3498db;
|
|
}
|
|
|
|
.image .item-icon {
|
|
color: #e74c3c;
|
|
}
|
|
|
|
.document .item-icon {
|
|
color: #f39c12;
|
|
}
|
|
|
|
.archive .item-icon {
|
|
color: #9b59b6;
|
|
}
|
|
|
|
.audio .item-icon {
|
|
color: #1abc9c;
|
|
}
|
|
|
|
.video .item-icon {
|
|
color: #d35400;
|
|
}
|
|
|
|
.code .item-icon {
|
|
color: #27ae60;
|
|
}
|
|
|
|
.item-name {
|
|
font-weight: 500;
|
|
font-size: 0.95rem;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.item-meta {
|
|
font-size: 0.8rem;
|
|
color: #95a5a6;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 50px 20px;
|
|
color: #7f8c8d;
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.empty-state i {
|
|
font-size: 5rem;
|
|
margin-bottom: 20px;
|
|
color: #bdc3c7;
|
|
}
|
|
|
|
.empty-state h3 {
|
|
font-size: 1.5rem;
|
|
margin-bottom: 10px;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.loading {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
min-height: 300px;
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.spinner {
|
|
width: 50px;
|
|
height: 50px;
|
|
border: 5px solid rgba(52, 152, 219, 0.2);
|
|
border-top: 5px solid #3498db;
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
.action-bar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 15px 24px;
|
|
background: #f8f9fa;
|
|
border-top: 1px solid #eaeaea;
|
|
}
|
|
|
|
.btn {
|
|
background: #3498db;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-weight: 500;
|
|
transition: background 0.3s;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.btn:hover {
|
|
background: #2980b9;
|
|
}
|
|
|
|
.btn i {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.stats {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
color: #7f8c8d;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.stat-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.grid-view {
|
|
grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
|
|
gap: 15px;
|
|
}
|
|
|
|
.item {
|
|
padding: 15px 8px;
|
|
}
|
|
|
|
.item-icon {
|
|
font-size: 3rem;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.grid-view {
|
|
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
|
|
gap: 10px;
|
|
}
|
|
|
|
h1 {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.action-bar {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1><i class="fas fa-cloud"></i> 网盘目录管理系统</h1>
|
|
<p class="subtitle">管理您的文件与文件夹,操作简单直观</p>
|
|
</header>
|
|
|
|
<div class="dashboard">
|
|
<div class="breadcrumb" id="breadcrumb">
|
|
<!-- 面包屑导航会通过JS动态生成 -->
|
|
</div>
|
|
|
|
<div class="content">
|
|
<div class="grid-view" id="file-grid">
|
|
<!-- 文件列表会通过JS动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="action-bar">
|
|
<button class="btn" id="back-btn">
|
|
<i class="fas fa-arrow-left"></i> 返回上一级
|
|
</button>
|
|
<div class="stats">
|
|
<div class="stat-item">
|
|
<i class="fas fa-folder"></i> <span id="folder-count">0</span> 个文件夹
|
|
</div>
|
|
<div class="stat-item">
|
|
<i class="fas fa-file"></i> <span id="file-count">0</span> 个文件
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// 文件类型映射
|
|
const fileTypeIcons = {
|
|
// 图片
|
|
'jpg': { icon: 'fa-file-image', type: 'image' },
|
|
'jpeg': { icon: 'fa-file-image', type: 'image' },
|
|
'png': { icon: 'fa-file-image', type: 'image' },
|
|
'gif': { icon: 'fa-file-image', type: 'image' },
|
|
'bmp': { icon: 'fa-file-image', type: 'image' },
|
|
'svg': { icon: 'fa-file-image', type: 'image' },
|
|
'webp': { icon: 'fa-file-image', type: 'image' },
|
|
|
|
// 文档
|
|
'pdf': { icon: 'fa-file-pdf', type: 'document' },
|
|
'doc': { icon: 'fa-file-word', type: 'document' },
|
|
'docx': { icon: 'fa-file-word', type: 'document' },
|
|
'xls': { icon: 'fa-file-excel', type: 'document' },
|
|
'xlsx': { icon: 'fa-file-excel', type: 'document' },
|
|
'ppt': { icon: 'fa-file-powerpoint', type: 'document' },
|
|
'pptx': { icon: 'fa-file-powerpoint', type: 'document' },
|
|
'txt': { icon: 'fa-file-alt', type: 'document' },
|
|
'rtf': { icon: 'fa-file-alt', type: 'document' },
|
|
|
|
// 压缩文件
|
|
'zip': { icon: 'fa-file-archive', type: 'archive' },
|
|
'rar': { icon: 'fa-file-archive', type: 'archive' },
|
|
'7z': { icon: 'fa-file-archive', type: 'archive' },
|
|
'tar': { icon: 'fa-file-archive', type: 'archive' },
|
|
'gz': { icon: 'fa-file-archive', type: 'archive' },
|
|
|
|
// 音频
|
|
'mp3': { icon: 'fa-file-audio', type: 'audio' },
|
|
'wav': { icon: 'fa-file-audio', type: 'audio' },
|
|
'ogg': { icon: 'fa-file-audio', type: 'audio' },
|
|
'flac': { icon: 'fa-file-audio', type: 'audio' },
|
|
|
|
// 视频
|
|
'mp4': { icon: 'fa-file-video', type: 'video' },
|
|
'avi': { icon: 'fa-file-video', type: 'video' },
|
|
'mov': { icon: 'fa-file-video', type: 'video' },
|
|
'wmv': { icon: 'fa-file-video', type: 'video' },
|
|
'mkv': { icon: 'fa-file-video', type: 'video' },
|
|
'flv': { icon: 'fa-file-video', type: 'video' },
|
|
|
|
// 代码
|
|
'html': { icon: 'fa-file-code', type: 'code' },
|
|
'htm': { icon: 'fa-file-code', type: 'code' },
|
|
'css': { icon: 'fa-file-code', type: 'code' },
|
|
'js': { icon: 'fa-file-code', type: 'code' },
|
|
'json': { icon: 'fa-file-code', type: 'code' },
|
|
'php': { icon: 'fa-file-code', type: 'code' },
|
|
'py': { icon: 'fa-file-code', type: 'code' },
|
|
'java': { icon: 'fa-file-code', type: 'code' },
|
|
'c': { icon: 'fa-file-code', type: 'code' },
|
|
'cpp': { icon: 'fa-file-code', type: 'code' },
|
|
'h': { icon: 'fa-file-code', type: 'code' },
|
|
'sh': { icon: 'fa-file-code', type: 'code' },
|
|
'bat': { icon: 'fa-file-code', type: 'code' },
|
|
'md': { icon: 'fa-file-code', type: 'code' },
|
|
|
|
// 默认
|
|
'default': { icon: 'fa-file', type: 'document' }
|
|
};
|
|
|
|
const obj = new URL(window.location.href);
|
|
// 获取 URL 参数
|
|
const params = obj.searchParams;
|
|
const shareUrl = params.get('url');
|
|
const pwd = params.get('pwd');
|
|
// 动态拼接并编码参数
|
|
const apiUrl = `${window.location.origin}/v2/getFileList?url=${encodeURIComponent(shareUrl)}&pwd=${encodeURIComponent(pwd)}`;
|
|
|
|
// 当前目录状态
|
|
let currentDir = {
|
|
// url: 'http://192.168.101.227:6401/v2/getFileList?url=https://share.feijipan.com/s/3pMsofZd&pwd=qaiu',
|
|
// 动态获取url encode 参数
|
|
url: apiUrl,
|
|
name: '全部文件'
|
|
};
|
|
const pathStack = [currentDir];
|
|
|
|
// DOM 元素
|
|
const breadcrumbEl = document.getElementById('breadcrumb');
|
|
const fileGridEl = document.getElementById('file-grid');
|
|
const backBtn = document.getElementById('back-btn');
|
|
const folderCountEl = document.getElementById('folder-count');
|
|
const fileCountEl = document.getElementById('file-count');
|
|
|
|
// 初始化
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
renderBreadcrumb();
|
|
fetchFileList(currentDir.url);
|
|
|
|
// 返回上一级按钮事件
|
|
backBtn.addEventListener('click', goBack);
|
|
});
|
|
|
|
// 渲染面包屑导航
|
|
function renderBreadcrumb() {
|
|
breadcrumbEl.innerHTML = '';
|
|
|
|
pathStack.forEach((item, index) => {
|
|
const itemEl = document.createElement('div');
|
|
itemEl.className = 'breadcrumb-item';
|
|
itemEl.textContent = item.name;
|
|
|
|
if (index < pathStack.length - 1) {
|
|
itemEl.addEventListener('click', () => {
|
|
// 点击面包屑项返回对应目录
|
|
goToDirectory(index);
|
|
});
|
|
} else {
|
|
itemEl.style.cursor = 'default';
|
|
itemEl.style.fontWeight = '600';
|
|
itemEl.style.color = '#2c3e50';
|
|
}
|
|
|
|
breadcrumbEl.appendChild(itemEl);
|
|
|
|
// 如果不是最后一个,添加分隔符
|
|
if (index < pathStack.length - 1) {
|
|
const separator = document.createElement('i');
|
|
separator.className = 'fas fa-chevron-right';
|
|
breadcrumbEl.appendChild(separator);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 获取文件列表
|
|
async function fetchFileList(url) {
|
|
try {
|
|
// 显示加载状态
|
|
fileGridEl.innerHTML = `
|
|
<div class="loading">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
`;
|
|
|
|
const response = await fetch(url);
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
|
|
if (data.code === 200 && data.success) {
|
|
renderFileList(data.data);
|
|
} else {
|
|
throw new Error(data.msg || '获取文件列表失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('获取文件列表失败:', error);
|
|
fileGridEl.innerHTML = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
<h3>加载失败</h3>
|
|
<p>${error.message}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// 渲染文件列表
|
|
function renderFileList(files) {
|
|
fileGridEl.innerHTML = '';
|
|
|
|
if (!files || files.length === 0) {
|
|
fileGridEl.innerHTML = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-folder-open"></i>
|
|
<h3>此文件夹为空</h3>
|
|
<p>暂无文件或文件夹</p>
|
|
</div>
|
|
`;
|
|
folderCountEl.textContent = '0';
|
|
fileCountEl.textContent = '0';
|
|
return;
|
|
}
|
|
|
|
let folderCount = 0;
|
|
let fileCount = 0;
|
|
|
|
files.forEach(file => {
|
|
const item = document.createElement('div');
|
|
|
|
if (file.fileType === 'folder') {
|
|
// 文件夹
|
|
item.className = 'item folder';
|
|
item.innerHTML = `
|
|
<div class="item-icon">
|
|
<i class="fas fa-folder"></i>
|
|
</div>
|
|
<div class="item-name">${file.fileName || '未命名文件夹'}</div>
|
|
<div class="item-meta">
|
|
${file.sizeStr || '0B'} · ${formatDate(file.createTime)}
|
|
</div>
|
|
`;
|
|
folderCount++;
|
|
|
|
// 添加点击事件
|
|
item.addEventListener('click', () => {
|
|
enterFolder(file);
|
|
});
|
|
} else {
|
|
// 文件
|
|
const fileExt = getFileExtension(file.fileName);
|
|
const fileTypeInfo = fileTypeIcons[fileExt.toLowerCase()] || fileTypeIcons['default'];
|
|
|
|
item.className = `item ${fileTypeInfo.type}`;
|
|
item.innerHTML = `
|
|
<div class="item-icon">
|
|
<i class="fas ${fileTypeInfo.icon}"></i>
|
|
</div>
|
|
<div class="item-name">${file.fileName}</div>
|
|
<div class="item-meta">
|
|
${file.sizeStr || '0B'} · ${formatDate(file.createTime)}
|
|
</div>
|
|
`;
|
|
fileCount++;
|
|
|
|
// 添加点击事件
|
|
item.addEventListener('click', () => {
|
|
handleFileClick(file);
|
|
});
|
|
}
|
|
|
|
fileGridEl.appendChild(item);
|
|
});
|
|
|
|
// 更新统计信息
|
|
folderCountEl.textContent = folderCount;
|
|
fileCountEl.textContent = fileCount;
|
|
}
|
|
|
|
// 获取文件扩展名
|
|
function getFileExtension(filename) {
|
|
if (!filename) return '';
|
|
return filename.split('.').pop();
|
|
}
|
|
|
|
// 进入文件夹
|
|
function enterFolder(folder) {
|
|
if (!folder.parserUrl) {
|
|
alert('无法进入该文件夹,缺少访问链接');
|
|
return;
|
|
}
|
|
|
|
const newDir = {
|
|
url: folder.parserUrl,
|
|
name: folder.fileName || '未命名文件夹'
|
|
};
|
|
|
|
pathStack.push(newDir);
|
|
currentDir = newDir;
|
|
|
|
fetchFileList(currentDir.url);
|
|
renderBreadcrumb();
|
|
}
|
|
|
|
// 下载文件
|
|
function handleFileClick(file) {
|
|
if (!file.parserUrl) {
|
|
alert('无法操作该文件,缺少必要链接');
|
|
return;
|
|
}
|
|
|
|
// 更友好的选择对话框
|
|
const modal = document.createElement('div');
|
|
modal.style.position = 'fixed';
|
|
modal.style.top = '0';
|
|
modal.style.left = '0';
|
|
modal.style.width = '100%';
|
|
modal.style.height = '100%';
|
|
modal.style.backgroundColor = 'rgba(0,0,0,0.5)';
|
|
modal.style.display = 'flex';
|
|
modal.style.justifyContent = 'center';
|
|
modal.style.alignItems = 'center';
|
|
modal.style.zIndex = '1000';
|
|
|
|
const dialog = document.createElement('div');
|
|
dialog.style.backgroundColor = 'white';
|
|
dialog.style.padding = '20px';
|
|
dialog.style.borderRadius = '8px';
|
|
dialog.style.width = '300px';
|
|
dialog.style.textAlign = 'center';
|
|
|
|
dialog.innerHTML = `
|
|
<p style="margin-bottom: 20px;">${file.fileName || '未命名文件'}</p>
|
|
<div style="display: flex; justify-content: center; gap: 15px;">
|
|
<button id="preview-btn" style="padding: 8px 15px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
预览文件
|
|
</button>
|
|
<button id="download-btn" style="padding: 8px 15px; background: #2ecc71; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
|
下载文件
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
modal.appendChild(dialog);
|
|
document.body.appendChild(modal);
|
|
|
|
// 预览按钮事件
|
|
dialog.querySelector('#preview-btn').addEventListener('click', () => {
|
|
document.body.removeChild(modal);
|
|
const previewUrl = file.previewUrl || file.parserUrl;
|
|
window.open(previewUrl, '_blank');
|
|
});
|
|
|
|
// 下载按钮事件
|
|
dialog.querySelector('#download-btn').addEventListener('click', () => {
|
|
document.body.removeChild(modal);
|
|
const iframe = document.createElement('iframe');
|
|
iframe.style.display = 'none';
|
|
iframe.src = file.parserUrl;
|
|
document.body.appendChild(iframe);
|
|
setTimeout(() => {
|
|
document.body.removeChild(iframe);
|
|
}, 3000);
|
|
});
|
|
|
|
// 点击蒙层关闭
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) {
|
|
document.body.removeChild(modal);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 返回上一级
|
|
function goBack() {
|
|
if (pathStack.length > 1) {
|
|
pathStack.pop();
|
|
currentDir = pathStack[pathStack.length - 1];
|
|
|
|
fetchFileList(currentDir.url);
|
|
renderBreadcrumb();
|
|
}
|
|
}
|
|
|
|
// 跳转到指定目录
|
|
function goToDirectory(index) {
|
|
pathStack.splice(index + 1);
|
|
currentDir = pathStack[pathStack.length - 1];
|
|
|
|
fetchFileList(currentDir.url);
|
|
renderBreadcrumb();
|
|
}
|
|
|
|
// 格式化日期
|
|
function formatDate(dateString) {
|
|
if (!dateString) return '未知日期';
|
|
|
|
try {
|
|
const date = new Date(dateString);
|
|
return isNaN(date.getTime())
|
|
? '未知日期'
|
|
: `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
|
|
} catch {
|
|
return '未知日期';
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |