271 lines
11 KiB
PHP
Executable File
271 lines
11 KiB
PHP
Executable File
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MCSManager 文件管理</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: '#165DFF',
|
|
secondary: '#36D399',
|
|
danger: '#F87272',
|
|
warning: '#FBBD23',
|
|
dark: '#1E293B',
|
|
light: '#F8FAFC'
|
|
},
|
|
fontFamily: {
|
|
inter: ['Inter', 'system-ui', 'sans-serif'],
|
|
},
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style type="text/tailwindcss">
|
|
@layer utilities {
|
|
.file-card {
|
|
@apply bg-white rounded-lg shadow-md p-4 mb-4 transition-all duration-300 hover:shadow-lg;
|
|
}
|
|
.btn {
|
|
@apply inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2;
|
|
}
|
|
.btn-primary {
|
|
@apply bg-primary text-white hover:bg-primary/90 focus:ring-primary/50;
|
|
}
|
|
.btn-danger {
|
|
@apply bg-danger text-white hover:bg-danger/90 focus:ring-danger/50;
|
|
}
|
|
.loading-overlay {
|
|
@apply fixed inset-0 bg-black/50 flex items-center justify-center z-50;
|
|
}
|
|
.notification {
|
|
@apply fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg z-50 transition-all duration-300 transform translate-y-20 opacity-0;
|
|
}
|
|
.notification-visible {
|
|
@apply translate-y-0 opacity-100;
|
|
}
|
|
.notification-success {
|
|
@apply bg-green-500 text-white;
|
|
}
|
|
.notification-error {
|
|
@apply bg-red-500 text-white;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-gray-50 font-inter text-gray-800 min-h-screen flex flex-col">
|
|
<!-- 顶部导航栏 -->
|
|
<header class="bg-primary text-white shadow-md sticky top-0 z-50">
|
|
<div class="container mx-auto px-4 py-3 flex justify-between items-center">
|
|
<div class="flex items-center space-x-2">
|
|
<i class="fa fa-server text-xl"></i>
|
|
<h1 class="text-xl font-bold">CCS文件管理</h1>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- 主内容区 -->
|
|
<main class="flex-grow container mx-auto px-4 py-6">
|
|
<!-- 文件上传表单 -->
|
|
<div class="mb-6">
|
|
<form id="uploadForm" class="bg-white rounded-xl shadow-lg p-6 mb-6">
|
|
<h3 class="text-lg font-semibold mb-4">上传文件</h3>
|
|
<input type="file" id="fileInput" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-primary file:text-white hover:file:bg-primary/90">
|
|
<button type="button" id="uploadBtn" class="btn btn-primary mt-4">上传文件</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- 文件列表 -->
|
|
<div class="bg-white rounded-xl shadow-lg p-6">
|
|
<h3 class="text-lg font-semibold mb-4">文件列表</h3>
|
|
<div id="fileList" class="divide-y divide-gray-200">
|
|
<div class="file-card flex justify-between items-center">
|
|
<span class="text-gray-800">文件名</span>
|
|
<div class="flex space-x-2">
|
|
<button class="btn btn-primary text-xs">下载</button>
|
|
<button class="btn btn-danger text-xs">删除</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- 加载遮罩层 -->
|
|
<div class="loading-overlay hidden" id="loading-overlay">
|
|
<div class="bg-white p-6 rounded-lg shadow-xl flex flex-col items-center">
|
|
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary mb-4"></div>
|
|
<p class="text-lg font-medium" id="loading-message">正在处理...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 通知组件 -->
|
|
<div class="notification notification-info" id="notification">
|
|
<p id="notification-message">这是一条通知消息</p>
|
|
</div>
|
|
|
|
<script>
|
|
// 从URL获取参数
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const uuid = urlParams.get('uuid') || 'default-uuid';
|
|
const daemonId = urlParams.get('daemonId') || 'default-daemon-id';
|
|
|
|
const uploadBtn = document.getElementById('uploadBtn');
|
|
const fileInput = document.getElementById('fileInput');
|
|
const fileList = document.getElementById('fileList');
|
|
|
|
uploadBtn.addEventListener('click', () => {
|
|
const file = fileInput.files[0];
|
|
if (!file) {
|
|
showNotification('请选择一个文件', 'error');
|
|
return;
|
|
}
|
|
showLoading('正在上传文件...');
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
fetch(`proxy.php?path=files/upload&uuid=${uuid}&daemonId=${daemonId}`, {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 200) {
|
|
showNotification('文件上传成功', 'success');
|
|
fetchFileList();
|
|
} else {
|
|
throw new Error(data.message || '上传失败');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('上传失败:', error);
|
|
showNotification('上传失败: ' + error.message, 'error');
|
|
})
|
|
.finally(() => {
|
|
hideLoading();
|
|
});
|
|
});
|
|
|
|
function fetchFileList() {
|
|
showLoading('正在获取文件列表...');
|
|
fetch(`proxy.php?path=files/list&uuid=${uuid}&daemonId=${daemonId}&target=/&page=1&page_size=50`)
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 200) {
|
|
fileList.innerHTML = '';
|
|
data.data.items.forEach(file => {
|
|
const fileCard = document.createElement('div');
|
|
fileCard.className = 'file-card flex justify-between items-center';
|
|
fileCard.innerHTML = `
|
|
<span class="text-gray-800">${file.name}</span>
|
|
<div class="flex space-x-2">
|
|
<button class="btn btn-primary text-xs" onclick="downloadFile('${file.name}')">下载</button>
|
|
<button class="btn btn-danger text-xs" onclick="deleteFile('${file.name}')">删除</button>
|
|
</div>
|
|
`;
|
|
fileList.appendChild(fileCard);
|
|
});
|
|
} else {
|
|
throw new Error(data.message || '获取文件列表失败');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('获取文件列表失败:', error);
|
|
showNotification('获取文件列表失败: ' + error.message, 'error');
|
|
})
|
|
.finally(() => {
|
|
hideLoading();
|
|
});
|
|
}
|
|
|
|
function downloadFile(fileName) {
|
|
showLoading('正在下载文件...');
|
|
fetch(`proxy.php?path=files/download&uuid=${uuid}&daemonId=${daemonId}&file=${fileName}`)
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
throw new Error('下载失败');
|
|
}
|
|
return response.blob();
|
|
})
|
|
.then(blob => {
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = fileName;
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
showNotification('文件下载成功', 'success');
|
|
})
|
|
.catch(error => {
|
|
console.error('下载失败:', error);
|
|
showNotification('下载失败: ' + error.message, 'error');
|
|
})
|
|
.finally(() => {
|
|
hideLoading();
|
|
});
|
|
}
|
|
|
|
function deleteFile(fileName) {
|
|
if (!confirm('确定要删除文件 ' + fileName + ' 吗?')) return;
|
|
showLoading('正在删除文件...');
|
|
fetch(`proxy.php?path=files/delete&uuid=${uuid}&daemonId=${daemonId}&file=${fileName}`, {
|
|
method: 'POST'
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 200) {
|
|
showNotification('文件删除成功', 'success');
|
|
fetchFileList();
|
|
} else {
|
|
throw new Error(data.message || '删除失败');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('删除失败:', error);
|
|
showNotification('删除失败: ' + error.message, 'error');
|
|
})
|
|
.finally(() => {
|
|
hideLoading();
|
|
});
|
|
}
|
|
|
|
function showLoading(message) {
|
|
const loadingOverlay = document.getElementById('loading-overlay');
|
|
const loadingMessage = document.getElementById('loading-message');
|
|
if (loadingOverlay && loadingMessage) {
|
|
loadingMessage.textContent = message;
|
|
loadingOverlay.classList.remove('hidden');
|
|
}
|
|
}
|
|
|
|
function hideLoading() {
|
|
const loadingOverlay = document.getElementById('loading-overlay');
|
|
if (loadingOverlay) {
|
|
loadingOverlay.classList.add('hidden');
|
|
}
|
|
}
|
|
|
|
function showNotification(message, type = 'info') {
|
|
const notification = document.getElementById('notification');
|
|
const notificationMessage = document.getElementById('notification-message');
|
|
if (!notification || !notificationMessage) {
|
|
console.error('Notification elements not found');
|
|
return;
|
|
}
|
|
notificationMessage.textContent = message;
|
|
notification.classList.remove('notification-success', 'notification-error', 'notification-info');
|
|
notification.classList.add(`notification-${type}`);
|
|
notification.classList.add('notification-visible');
|
|
setTimeout(() => {
|
|
notification.classList.remove('notification-visible');
|
|
}, 3000);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
fetchFileList();
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |