更新数据同步
This commit is contained in:
@@ -0,0 +1,799 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>文档中心</title>
|
||||
<!-- Tailwind CSS v3 -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<!-- 统一的 Tailwind 配置 -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#e6f7ff',
|
||||
secondary: '#f0f7ff',
|
||||
accent: '#1890ff',
|
||||
'accent-light': '#e6f7ff',
|
||||
'accent-hover': '#bae7ff',
|
||||
'text-primary': '#333333',
|
||||
'text-secondary': '#666666',
|
||||
'border-color': '#d9d9d9'
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
boxShadow: {
|
||||
'card': '0 2px 8px rgba(0, 0, 0, 0.1)',
|
||||
'hover': '0 4px 12px rgba(0, 0, 0, 0.15)',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.transition-height {
|
||||
transition: max-height 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.slide-down {
|
||||
animation: slideDown 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
transform: translateY(-10px);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.pulse {
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 font-sans text-text-primary min-h-screen flex flex-col">
|
||||
<!-- 页面容器 -->
|
||||
<div class="flex-grow flex flex-col md:flex-row p-4 md:p-6 gap-6 h-[calc(100vh-5px)]">
|
||||
<!-- 左侧文件夹导航 -->
|
||||
<div class="w-full md:w-64 lg:w-72 bg-primary rounded-lg shadow-card flex flex-col overflow-hidden">
|
||||
<!-- 文件夹搜索 -->
|
||||
<div class="p-4 border-b border-border-color">
|
||||
<div class="relative">
|
||||
<input type="text" id="folderSearch" placeholder="搜索文件夹..."
|
||||
class="w-full pl-10 pr-4 py-2 rounded-lg border border-border-color focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent">
|
||||
<i class="fa fa-search absolute left-3 top-3 text-text-secondary"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件夹列表 -->
|
||||
<div class="flex-grow overflow-y-auto scrollbar-thin p-2">
|
||||
<div class="space-y-2">
|
||||
<!-- 文件夹组1 -->
|
||||
<div class="folder-group" th:each="folders : ${folderTrees}">
|
||||
<div class="flex items-center justify-between p-2 rounded-md hover:bg-accent-hover cursor-pointer"
|
||||
onclick="toggleFolderGroup(this)">
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-folder-o mr-2 text-accent"></i>
|
||||
<span th:text="${folders.getFolder().getFolderName()}"></span>
|
||||
</div>
|
||||
<i class="fa fa-chevron-right text-xs transition-transform duration-300"></i>
|
||||
</div>
|
||||
<div class="pl-6 mt-1 folder-content max-h-0 overflow-hidden transition-height"
|
||||
th:each="childFolders : ${folders.getChildFolders()}">
|
||||
<div class="space-y-1">
|
||||
<div class="p-2 rounded-md hover:bg-accent-hover cursor-pointer folder-item">
|
||||
<i class="fa fa-folder-o mr-2 text-accent"></i>
|
||||
<span th:text="${childFolders.getFolderName()}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 创建文件夹按钮 -->
|
||||
<div class="p-4 border-t border-border-color">
|
||||
<button id="createFolderBtn"
|
||||
class="w-full flex items-center justify-center p-2 bg-accent text-white rounded-lg hover:bg-blue-600 transition-colors">
|
||||
<i class="fa fa-plus mr-2"></i>
|
||||
<span>创建文件夹</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧文件操作和展示区 -->
|
||||
<div class="flex-grow flex flex-col bg-secondary rounded-lg shadow-card overflow-hidden">
|
||||
<!-- 顶部搜索和上传区域 -->
|
||||
<div class="p-4 border-b border-border-color flex flex-col sm:flex-row gap-4">
|
||||
<!-- 文件搜索 -->
|
||||
<div class="flex-grow relative">
|
||||
<input type="text" id="fileSearch" placeholder="搜索文件..."
|
||||
class="w-full pl-10 pr-4 py-2 rounded-lg border border-border-color focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent">
|
||||
<i class="fa fa-search absolute left-3 top-3 text-text-secondary"></i>
|
||||
</div>
|
||||
|
||||
<!-- 上传文件按钮 -->
|
||||
<button id="uploadFileBtn"
|
||||
class="flex items-center justify-center px-4 py-2 bg-accent text-white rounded-lg hover:bg-blue-600 transition-colors">
|
||||
<i class="fa fa-upload mr-2"></i>
|
||||
<span>上传文件</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 文件列表区域 -->
|
||||
<div class="flex-grow overflow-y-auto scrollbar-thin p-4">
|
||||
<div id="fileList" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<!-- 文件项1 -->
|
||||
<div class="bg-white rounded-lg shadow-card p-4 hover:shadow-hover transition-shadow"
|
||||
th:each="file : ${files}">
|
||||
<div class="flex items-center mb-3">
|
||||
<img src="https://p3-doubao-search-sign.byteimg.com/labis/fc16ba2cc6e083aacff2c3629bd48364~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1779099593&x-signature=ZOIIEOF4OonT%2B1U3qJMy0ggfOwM%3D"
|
||||
alt="文档" class="file-icon mr-3">
|
||||
<div class="flex-grow">
|
||||
<h3 class="font-medium truncate" th:text="${file.getFileName()}"></h3>
|
||||
<p class="text-xs text-text-secondary">2023-06-15 | 2.4MB</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button class="text-accent hover:text-blue-700 transition-colors"
|
||||
onclick="downloadFile('project-plan.docx')">
|
||||
<i class="fa fa-download mr-1"></i> 下载
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态提示 -->
|
||||
<div id="emptyState" class="hidden flex flex-col items-center justify-center py-10">
|
||||
<div class="w-24 h-24 mb-4 opacity-50">
|
||||
<i class="fa fa-folder-open-o text-6xl text-text-secondary"></i>
|
||||
</div>
|
||||
<p class="text-text-secondary text-lg">当前文件夹为空</p>
|
||||
<p class="text-text-secondary text-sm mt-2">点击"上传文件"按钮添加文件</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 创建文件夹弹窗 -->
|
||||
<div id="createFolderModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-md p-6 fade-in">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium">创建文件夹</h3>
|
||||
<button onclick="closeCreateFolderModal()" class="text-text-secondary hover:text-text-primary">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<form id="createFolderForm" onsubmit="createFolder(event)">
|
||||
<div class="mb-4">
|
||||
<label for="folderName" class="block text-sm font-medium text-text-secondary mb-1">文件夹名称</label>
|
||||
<input type="text" id="folderName" required
|
||||
class="w-full px-3 py-2 border border-border-color rounded-md focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent">
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
<label for="folderParent" class="block text-sm font-medium text-text-secondary mb-1">父文件夹</label>
|
||||
<select id="folderParent"
|
||||
class="w-full px-3 py-2 border border-border-color rounded-md focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent">
|
||||
<option value="">根目录</option>
|
||||
<option value="project-docs">项目文档</option>
|
||||
<option value="tech-docs">技术文档</option>
|
||||
<option value="test-docs">测试文档</option>
|
||||
<option value="meeting-notes">会议记录</option>
|
||||
<option value="training-materials">培训资料</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button type="button" onclick="closeCreateFolderModal()"
|
||||
class="px-4 py-2 border border-border-color rounded-md mr-2 hover:bg-gray-50 transition-colors">
|
||||
取消
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-accent text-white rounded-md hover:bg-blue-600 transition-colors">
|
||||
创建
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 上传文件弹窗 -->
|
||||
<div id="uploadFileModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
||||
<div class="bg-white rounded-lg shadow-lg w-full max-w-4xl p-6 fade-in">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-medium">上传文件</h3>
|
||||
<button onclick="closeUploadFileModal()" class="text-text-secondary hover:text-text-primary">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 上传区域 -->
|
||||
<div id="uploadArea"
|
||||
class="border-2 border-dashed border-border-color rounded-lg p-8 mb-6 text-center hover:border-accent transition-colors cursor-pointer"
|
||||
onclick="document.getElementById('fileInput').click()">
|
||||
<div class="w-16 h-16 mx-auto mb-4 opacity-50">
|
||||
<i class="fa fa-cloud-upload text-4xl text-text-secondary"></i>
|
||||
</div>
|
||||
<p class="text-text-secondary mb-2">拖拽文件到此处或点击上传</p>
|
||||
<p class="text-text-secondary text-sm">支持批量上传,单个文件不超过100MB</p>
|
||||
<input type="file" id="fileInput" multiple class="hidden" onchange="handleFiles(this.files)">
|
||||
</div>
|
||||
|
||||
<!-- 上传文件夹选择 -->
|
||||
<div class="mb-6">
|
||||
<label for="uploadFolder" class="block text-sm font-medium text-text-secondary mb-1">上传到文件夹</label>
|
||||
<select id="uploadFolder"
|
||||
class="w-full px-3 py-2 border border-border-color rounded-md focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent">
|
||||
<option value="project-plan">项目计划</option>
|
||||
<option value="requirements">需求文档</option>
|
||||
<option value="design">设计文档</option>
|
||||
<option value="architecture">架构文档</option>
|
||||
<option value="api">API文档</option>
|
||||
<option value="database">数据库文档</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 上传文件列表 -->
|
||||
<div class="mb-6 max-h-64 overflow-y-auto scrollbar-thin">
|
||||
<h4 class="text-sm font-medium text-text-secondary mb-2">待上传文件</h4>
|
||||
<div id="uploadFileList" class="space-y-2">
|
||||
<!-- 上传文件项将通过JavaScript动态添加 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="flex justify-end">
|
||||
<button type="button" onclick="closeUploadFileModal()"
|
||||
class="px-4 py-2 border border-border-color rounded-md mr-2 hover:bg-gray-50 transition-colors">
|
||||
取消
|
||||
</button>
|
||||
<button id="startUploadBtn" type="button" onclick="startUpload()"
|
||||
class="px-4 py-2 bg-accent text-white rounded-md hover:bg-blue-600 transition-colors">
|
||||
开始上传
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息提示 -->
|
||||
<div id="messageToast"
|
||||
class="fixed bottom-4 right-4 px-4 py-3 rounded-lg shadow-lg bg-white transform translate-y-20 opacity-0 transition-all duration-300 z-50 flex items-center">
|
||||
<i id="messageIcon" class="fa fa-check-circle text-green-500 mr-2"></i>
|
||||
<span id="messageText">操作成功</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 全局变量
|
||||
let currentFolder = null;
|
||||
let uploadFiles = [];
|
||||
|
||||
// 页面加载完成后执行
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// 绑定创建文件夹按钮事件
|
||||
document.getElementById('createFolderBtn').addEventListener('click', openCreateFolderModal);
|
||||
|
||||
// 绑定上传文件按钮事件
|
||||
document.getElementById('uploadFileBtn').addEventListener('click', openUploadFileModal);
|
||||
|
||||
// 绑定文件夹搜索事件
|
||||
document.getElementById('folderSearch').addEventListener('input', searchFolders);
|
||||
|
||||
// 绑定文件搜索事件
|
||||
document.getElementById('fileSearch').addEventListener('input', searchFiles);
|
||||
|
||||
// 绑定拖拽上传事件
|
||||
const uploadArea = document.getElementById('uploadArea');
|
||||
uploadArea.addEventListener('dragover', handleDragOver);
|
||||
uploadArea.addEventListener('drop', handleDrop);
|
||||
});
|
||||
|
||||
// 文件夹组展开/折叠
|
||||
function toggleFolderGroup(element) {
|
||||
const content = element.nextElementSibling;
|
||||
const icon = element.querySelector('.fa-chevron-right');
|
||||
|
||||
if (content.style.maxHeight) {
|
||||
content.style.maxHeight = null;
|
||||
icon.style.transform = 'rotate(0deg)';
|
||||
} else {
|
||||
content.style.maxHeight = content.scrollHeight + 'px';
|
||||
icon.style.transform = 'rotate(90deg)';
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索文件夹
|
||||
function searchFolders() {
|
||||
const searchTerm = document.getElementById('folderSearch').value.toLowerCase();
|
||||
const folderGroups = document.querySelectorAll('.folder-group');
|
||||
|
||||
folderGroups.forEach(group => {
|
||||
const groupTitle = group.querySelector('.flex.items-center span').textContent.toLowerCase();
|
||||
const folderItems = group.querySelectorAll('.folder-item');
|
||||
let hasVisibleItem = false;
|
||||
|
||||
// 检查文件夹组标题是否匹配搜索词
|
||||
if (groupTitle.includes(searchTerm)) {
|
||||
group.style.display = 'block';
|
||||
hasVisibleItem = true;
|
||||
} else {
|
||||
// 检查文件夹项是否匹配搜索词
|
||||
folderItems.forEach(item => {
|
||||
const itemTitle = item.querySelector('span').textContent.toLowerCase();
|
||||
if (itemTitle.includes(searchTerm)) {
|
||||
item.style.display = 'block';
|
||||
hasVisibleItem = true;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 如果有可见项则显示文件夹组,否则隐藏
|
||||
group.style.display = hasVisibleItem ? 'block' : 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 搜索文件
|
||||
function searchFiles() {
|
||||
const searchTerm = document.getElementById('fileSearch').value.toLowerCase();
|
||||
const fileItems = document.querySelectorAll('#fileList > div');
|
||||
const emptyState = document.getElementById('emptyState');
|
||||
let visibleCount = 0;
|
||||
|
||||
fileItems.forEach(item => {
|
||||
const fileName = item.querySelector('h3').textContent.toLowerCase();
|
||||
if (fileName.includes(searchTerm)) {
|
||||
item.style.display = 'block';
|
||||
visibleCount++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// 如果没有可见文件,显示空状态
|
||||
if (visibleCount === 0) {
|
||||
emptyState.classList.remove('hidden');
|
||||
} else {
|
||||
emptyState.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
function downloadFile(fileName) {
|
||||
showMessage(`正在准备下载 ${fileName}...`);
|
||||
|
||||
// 这里应该是实际的下载API调用
|
||||
setTimeout(() => {
|
||||
// 模拟下载完成
|
||||
showMessage(`${fileName} 下载成功`);
|
||||
|
||||
// 实际项目中,这里应该调用后端API进行文件下载
|
||||
// 例如:window.location.href = `/api/download/${fileName}`;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
// 打开创建文件夹弹窗
|
||||
function openCreateFolderModal() {
|
||||
document.getElementById('createFolderModal').classList.remove('hidden');
|
||||
document.getElementById('folderName').focus();
|
||||
}
|
||||
|
||||
// 关闭创建文件夹弹窗
|
||||
function closeCreateFolderModal() {
|
||||
document.getElementById('createFolderModal').classList.add('hidden');
|
||||
document.getElementById('createFolderForm').reset();
|
||||
}
|
||||
|
||||
// 创建文件夹
|
||||
function createFolder(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const folderName = document.getElementById('folderName').value;
|
||||
const folderParent = document.getElementById('folderParent').value;
|
||||
|
||||
if (!folderName) {
|
||||
showMessage('请输入文件夹名称', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
showMessage(`正在创建文件夹: ${folderName}...`);
|
||||
|
||||
// 这里应该是实际的API调用,现在使用模拟数据
|
||||
setTimeout(() => {
|
||||
// 模拟创建成功
|
||||
closeCreateFolderModal();
|
||||
showMessage(`文件夹 ${folderName} 创建成功`);
|
||||
|
||||
// 实际项目中,这里应该调用后端API创建文件夹
|
||||
// 例如:
|
||||
// fetch('/api/folders', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// name: folderName,
|
||||
// parentId: folderParent
|
||||
// }),
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .then(data => {
|
||||
// closeCreateFolderModal();
|
||||
// showMessage(`文件夹 ${folderName} 创建成功`);
|
||||
// // 刷新文件夹列表
|
||||
// })
|
||||
// .catch(error => {
|
||||
// showMessage('创建文件夹失败', 'error');
|
||||
// });
|
||||
}, 800);
|
||||
}
|
||||
|
||||
// 打开上传文件弹窗
|
||||
function openUploadFileModal() {
|
||||
document.getElementById('uploadFileModal').classList.remove('hidden');
|
||||
document.getElementById('uploadFileList').innerHTML = '';
|
||||
uploadFiles = [];
|
||||
}
|
||||
|
||||
// 关闭上传文件弹窗
|
||||
function closeUploadFileModal() {
|
||||
document.getElementById('uploadFileModal').classList.add('hidden');
|
||||
document.getElementById('fileInput').value = '';
|
||||
uploadFiles = [];
|
||||
}
|
||||
|
||||
// 处理拖拽文件
|
||||
function handleDragOver(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.getElementById('uploadArea').classList.add('border-accent');
|
||||
}
|
||||
|
||||
// 处理文件放置
|
||||
function handleDrop(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
document.getElementById('uploadArea').classList.remove('border-accent');
|
||||
|
||||
if (event.dataTransfer.files.length > 0) {
|
||||
handleFiles(event.dataTransfer.files);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理选择的文件
|
||||
function handleFiles(files) {
|
||||
if (files.length === 0) return;
|
||||
|
||||
// 添加文件到上传列表
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
|
||||
// 检查文件是否已在上传列表中
|
||||
const isDuplicate = uploadFiles.some(f => f.name === file.name && f.size === file.size);
|
||||
if (isDuplicate) continue;
|
||||
|
||||
// 添加文件到上传列表
|
||||
uploadFiles.push(file);
|
||||
|
||||
// 创建文件项
|
||||
const fileItem = document.createElement('div');
|
||||
fileItem.className = 'flex items-center justify-between p-2 border border-border-color rounded-md';
|
||||
fileItem.innerHTML = `
|
||||
<div class="flex items-center">
|
||||
<i class="fa fa-file-o mr-2 text-accent"></i>
|
||||
<div class="flex-grow">
|
||||
<p class="text-sm truncate">${file.name}</p>
|
||||
<p class="text-xs text-text-secondary">${formatFileSize(file.size)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="upload-progress w-32 h-2 bg-gray-200 rounded-full mr-2 hidden">
|
||||
<div class="upload-progress-bar h-full bg-accent rounded-full" style="width: 0%"></div>
|
||||
</div>
|
||||
<button class="text-text-secondary hover:text-red-500 transition-colors" onclick="removeUploadFile(this, ${i})">
|
||||
<i class="fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
document.getElementById('uploadFileList').appendChild(fileItem);
|
||||
}
|
||||
}
|
||||
|
||||
// 移除上传文件
|
||||
function removeUploadFile(element, index) {
|
||||
// 移除文件项
|
||||
element.closest('div.flex.items-center.justify-between').remove();
|
||||
|
||||
// 从上传列表中移除文件
|
||||
uploadFiles.splice(index, 1);
|
||||
|
||||
// 更新剩余文件的索引
|
||||
const fileItems = document.querySelectorAll('#uploadFileList button');
|
||||
fileItems.forEach((item, newIndex) => {
|
||||
item.setAttribute('onclick', `removeUploadFile(this, ${newIndex})`);
|
||||
});
|
||||
}
|
||||
|
||||
// 开始上传
|
||||
function startUpload() {
|
||||
if (uploadFiles.length === 0) {
|
||||
showMessage('请先选择要上传的文件', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
const uploadFolder = document.getElementById('uploadFolder').value;
|
||||
showMessage(`正在上传 ${uploadFiles.length} 个文件...`);
|
||||
|
||||
// 禁用上传按钮
|
||||
const uploadBtn = document.getElementById('startUploadBtn');
|
||||
uploadBtn.disabled = true;
|
||||
uploadBtn.innerHTML = '<i class="fa fa-spinner fa-spin mr-2"></i> 上传中...';
|
||||
|
||||
// 显示进度条
|
||||
const progressBars = document.querySelectorAll('.upload-progress');
|
||||
progressBars.forEach(bar => {
|
||||
bar.classList.remove('hidden');
|
||||
});
|
||||
|
||||
// 模拟上传进度
|
||||
let completedFiles = 0;
|
||||
uploadFiles.forEach((file, index) => {
|
||||
simulateUploadProgress(index, file, () => {
|
||||
completedFiles++;
|
||||
|
||||
// 检查是否所有文件都上传完成
|
||||
if (completedFiles === uploadFiles.length) {
|
||||
setTimeout(() => {
|
||||
// 启用上传按钮
|
||||
uploadBtn.disabled = false;
|
||||
uploadBtn.innerHTML = '开始上传';
|
||||
|
||||
// 清空上传列表
|
||||
document.getElementById('uploadFileList').innerHTML = '';
|
||||
uploadFiles = [];
|
||||
|
||||
// 关闭上传弹窗
|
||||
closeUploadFileModal();
|
||||
|
||||
// 显示成功消息
|
||||
showMessage(`${completedFiles} 个文件上传成功`);
|
||||
|
||||
// 刷新当前文件夹的文件列表
|
||||
if (currentFolder) {
|
||||
loadFiles(currentFolder);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 实际项目中,这里应该调用后端API上传文件
|
||||
// 例如:
|
||||
// const formData = new FormData();
|
||||
// uploadFiles.forEach(file => {
|
||||
// formData.append('files', file);
|
||||
// });
|
||||
// formData.append('folderId', uploadFolder);
|
||||
//
|
||||
// fetch('/api/upload', {
|
||||
// method: 'POST',
|
||||
// body: formData,
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .then(data => {
|
||||
// // 处理上传成功
|
||||
// })
|
||||
// .catch(error => {
|
||||
// // 处理上传失败
|
||||
// });
|
||||
}
|
||||
|
||||
// 模拟上传进度
|
||||
function simulateUploadProgress(index, file, callback) {
|
||||
const progressBar = document.querySelectorAll('.upload-progress-bar')[index];
|
||||
let progress = 0;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
progress += Math.random() * 20;
|
||||
if (progress >= 100) {
|
||||
progress = 100;
|
||||
clearInterval(interval);
|
||||
setTimeout(callback, 500);
|
||||
}
|
||||
progressBar.style.width = `${progress}%`;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
// 格式化文件大小
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
else if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
else return (bytes / 1048576).toFixed(1) + ' MB';
|
||||
}
|
||||
|
||||
// 显示消息提示
|
||||
function showMessage(message, type = 'success') {
|
||||
const toast = document.getElementById('messageToast');
|
||||
const icon = document.getElementById('messageIcon');
|
||||
const text = document.getElementById('messageText');
|
||||
|
||||
// 设置消息内容和类型
|
||||
text.textContent = message;
|
||||
|
||||
if (type === 'success') {
|
||||
icon.className = 'fa fa-check-circle text-green-500 mr-2';
|
||||
} else if (type === 'error') {
|
||||
icon.className = 'fa fa-exclamation-circle text-red-500 mr-2';
|
||||
} else if (type === 'warning') {
|
||||
icon.className = 'fa fa-exclamation-triangle text-yellow-500 mr-2';
|
||||
} else if (type === 'info') {
|
||||
icon.className = 'fa fa-info-circle text-blue-500 mr-2';
|
||||
}
|
||||
|
||||
// 显示消息提示
|
||||
toast.classList.remove('translate-y-20', 'opacity-0');
|
||||
|
||||
// 3秒后自动关闭
|
||||
setTimeout(() => {
|
||||
toast.classList.add('translate-y-20', 'opacity-0');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 后端API接口方法(与Spring Boot联调)
|
||||
const api = {
|
||||
// 获取文件夹列表
|
||||
getFolders: function () {
|
||||
// 实际项目中,这里应该调用后端API获取文件夹列表
|
||||
// return fetch('/api/folders')
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('获取文件夹列表失败', 'error');
|
||||
// console.error(error);
|
||||
// return [];
|
||||
// });
|
||||
},
|
||||
|
||||
// 获取文件列表
|
||||
getFiles: function (folderId) {
|
||||
// 实际项目中,这里应该调用后端API获取文件列表
|
||||
// return fetch(`/api/folders/${folderId}/files`)
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('获取文件列表失败', 'error');
|
||||
// console.error(error);
|
||||
// return [];
|
||||
// });
|
||||
},
|
||||
|
||||
// 创建文件夹
|
||||
createFolder: function (name, parentId) {
|
||||
// 实际项目中,这里应该调用后端API创建文件夹
|
||||
// return fetch('/api/folders', {
|
||||
// method: 'POST',
|
||||
// headers: {
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// body: JSON.stringify({
|
||||
// name: name,
|
||||
// parentId: parentId
|
||||
// }),
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('创建文件夹失败', 'error');
|
||||
// console.error(error);
|
||||
// throw error;
|
||||
// });
|
||||
},
|
||||
|
||||
// 上传文件
|
||||
uploadFiles: function (files, folderId) {
|
||||
// 实际项目中,这里应该调用后端API上传文件
|
||||
// const formData = new FormData();
|
||||
// files.forEach(file => {
|
||||
// formData.append('files', file);
|
||||
// });
|
||||
// formData.append('folderId', folderId);
|
||||
//
|
||||
// return fetch('/api/upload', {
|
||||
// method: 'POST',
|
||||
// body: formData,
|
||||
// })
|
||||
// .then(response => response.json())
|
||||
// .catch(error => {
|
||||
// showMessage('上传文件失败', 'error');
|
||||
// console.error(error);
|
||||
// throw error;
|
||||
// });
|
||||
},
|
||||
|
||||
// 下载文件
|
||||
downloadFile: function (fileId) {
|
||||
// 实际项目中,这里应该调用后端API下载文件
|
||||
// window.location.href = `/api/files/${fileId}/download`;
|
||||
},
|
||||
|
||||
// 删除文件
|
||||
deleteFile: function (fileId) {
|
||||
// 实际项目中,这里应该调用后端API删除文件
|
||||
// return fetch(`/api/files/${fileId}`, {
|
||||
// method: 'DELETE',
|
||||
// })
|
||||
// .then(response => {
|
||||
// if (!response.ok) {
|
||||
// throw new Error('删除文件失败');
|
||||
// }
|
||||
// })
|
||||
// .catch(error => {
|
||||
// showMessage('删除文件失败', 'error');
|
||||
// console.error(error);
|
||||
// throw error;
|
||||
// });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user