vue2+elementui 实现网盘功能(静态文件)

码云:一个简单的网盘项目 · jiongyou/jsDemo - 码云 - 开源中国

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue网盘系统 - 增强版</title>
    <!-- 引入Element UI样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入Vue和Element UI -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <!-- 引入图标 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            background-color: #f5f7fa;
            color: #333;
            font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
        }
        
        .cloud-app {
            min-height: 100vh;
            display: flex;
            flex-direction: column;
        }
        
        .app-header {
            background: linear-gradient(135deg, #3a7bd5, #00d2ff);
            color: white;
            padding: 15px 30px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        }
        
        .logo {
            display: flex;
            align-items: center;
            font-size: 22px;
            font-weight: bold;
        }
        
        .logo i {
            margin-right: 10px;
            font-size: 28px;
        }
        
        .user-info {
            display: flex;
            align-items: center;
        }
        
        .user-avatar {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background-color: rgba(255, 255, 255, 0.2);
            display: flex;
            align-items: center;
            justify-content: center;
            margin-left: 15px;
            font-weight: bold;
        }
        
        .clipboard-status {
            background-color: #e6f7ff;
            padding: 8px 15px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            border-bottom: 1px solid #91d5ff;
        }
        
        .clipboard-info {
            display: flex;
            align-items: center;
        }
        
        .clipboard-actions {
            display: flex;
            gap: 10px;
        }
        
        .downloads-panel {
            background-color: #f5f7fa;
            padding: 10px 15px;
            border-bottom: 1px solid #ebeef5;
            max-height: 200px;
            overflow-y: auto;
        }
        
        .download-item {
            display: flex;
            align-items: center;
            padding: 8px 0;
            border-bottom: 1px solid #ebeef5;
        }
        
        .download-item:last-child {
            border-bottom: none;
        }
        
        .download-icon {
            width: 32px;
            height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 10px;
            border-radius: 4px;
            background-color: #e6f7ff;
            color: #3a7bd5;
        }
        
        .download-info {
            flex: 1;
        }
        
        .download-name {
            font-size: 14px;
            margin-bottom: 4px;
        }
        
        .download-status {
            font-size: 12px;
            color: #909399;
        }
        
        .download-progress {
            width: 100px;
            margin-left: 10px;
        }
        
        .download-actions {
            margin-left: 10px;
        }
        
        .main-container {
            display: flex;
            flex: 1;
        }
        
        .sidebar {
            width: 220px;
            background-color: #304156;
            color: #fff;
            padding: 20px 0;
        }
        
        .menu-item {
            padding: 14px 20px;
            cursor: pointer;
            transition: background-color 0.3s;
            display: flex;
            align-items: center;
        }
        
        .menu-item i {
            margin-right: 10px;
            width: 20px;
            text-align: center;
        }
        
        .menu-item:hover, .menu-item.active {
            background-color: #263445;
        }
        
        .content {
            flex: 1;
            padding: 20px;
            overflow-y: auto;
        }
        
        .action-bar {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
            align-items: center;
        }
        
        .breadcrumb {
            margin-bottom: 20px;
            padding: 15px;
            background-color: #fff;
            border-radius: 4px;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        }
        
        .file-list {
            background-color: #fff;
            border-radius: 4px;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
            overflow: hidden;
        }
        
        .file-item {
            display: flex;
            align-items: center;
            padding: 12px 20px;
            border-bottom: 1px solid #ebeef5;
            cursor: pointer;
            transition: background-color 0.3s;
            position: relative;
        }
        
        .file-item:hover {
            background-color: #f5f7fa;
        }
        
        .file-item.selected {
            background-color: #ecf5ff;
        }
        
        .file-icon {
            width: 40px;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 15px;
            border-radius: 4px;
        }
        
        .folder-icon {
            background-color: #ffe7cc;
            color: #ff9426;
        }
        
        .file-info {
            flex: 1;
        }
        
        .file-name {
            font-weight: 500;
            margin-bottom: 5px;
        }
        
        .file-meta {
            font-size: 12px;
            color: #909399;
        }
        
        .file-actions {
            display: flex;
            gap: 10px;
            opacity: 0;
            transition: opacity 0.3s;
        }
        
        .file-item:hover .file-actions {
            opacity: 1;
        }
        
        .action-btn {
            padding: 5px;
            border-radius: 4px;
            cursor: pointer;
            color: #606266;
        }
        
        .action-btn:hover {
            background-color: #f5f7fa;
            color: #3a7bd5;
        }
        
        .upload-area {
            margin-top: 30px;
            border: 2px dashed #dcdfe6;
            border-radius: 6px;
            padding: 30px;
            text-align: center;
            background-color: rgba(58, 123, 213, 0.03);
            transition: all 0.3s;
        }
        
        .upload-area:hover {
            border-color: #3a7bd5;
            background-color: rgba(58, 123, 213, 0.08);
        }
        
        .upload-icon {
            font-size: 48px;
            color: #3a7bd5;
            margin-bottom: 15px;
        }
        
        .upload-text {
            margin-bottom: 20px;
        }
        
        .upload-text h3 {
            margin-bottom: 8px;
            color: #3a7bd5;
        }
        
        .upload-text p {
            color: #909399;
            font-size: 14px;
        }
        
        .empty-state {
            text-align: center;
            padding: 40px;
            color: #909399;
        }
        
        .empty-icon {
            font-size: 60px;
            margin-bottom: 20px;
            color: #dcdfe6;
        }
        
        .storage-info {
            margin-top: 20px;
            padding: 20px;
            background-color: rgba(255, 255, 255, 0.1);
            border-radius: 4px;
            margin: 20px;
        }
        
        .storage-title {
            margin-bottom: 10px;
            font-size: 14px;
        }
        
        .storage-progress {
            margin-bottom: 10px;
        }
        
        .storage-size {
            font-size: 12px;
        }
        
        .context-menu {
            position: fixed;
            background-color: #fff;
            border-radius: 4px;
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
            z-index: 1000;
            padding: 5px 0;
        }
        
        .context-menu-item {
            padding: 8px 20px;
            cursor: pointer;
            display: flex;
            align-items: center;
        }
        
        .context-menu-item i {
            margin-right: 10px;
            width: 16px;
            text-align: center;
        }
        
        .context-menu-item:hover {
            background-color: #f5f7fa;
            color: #3a7bd5;
        }
        
        .selection-actions {
            background-color: #ecf5ff;
            padding: 10px 15px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            border-bottom: 1px solid #b3d8ff;
        }
        
        .selection-count {
            display: flex;
            align-items: center;
        }
        
        .selection-buttons {
            display: flex;
            gap: 10px;
        }
    </style>
</head>
<body>
    <div id="app" class="cloud-app">
        <!-- 顶部导航 -->
        <header class="app-header">
            <div class="logo">
                <i class="fas fa-cloud"></i>
                <span>云端网盘</span>
            </div>
            <div class="user-info">
                <i class="fas fa-bell"></i>
                <div class="user-avatar">JS</div>
            </div>
        </header>
        
        <!-- 剪贴板状态栏 -->
        <div class="clipboard-status" v-if="clipboard.item">
            <div class="clipboard-info">
                <i class="fas fa-clipboard" style="margin-right: 10px;"></i>
                <span>已{{ clipboard.operation === 'copy' ? '复制' : '移动' }}: {{ clipboard.item.name }}</span>
            </div>
            <div class="clipboard-actions">
                <el-button size="mini" @click="pasteItem">粘贴到当前文件夹</el-button>
                <el-button size="mini" @click="clearClipboard">取消</el-button>
            </div>
        </div>
        
        <!-- 下载面板 -->
        <div class="downloads-panel" v-if="downloads.length > 0">
            <div class="download-item" v-for="download in downloads" :key="download.id">
                <div class="download-icon">
                    <i :class="getFileIcon(download)"></i>
                </div>
                <div class="download-info">
                    <div class="download-name">{{ download.name }}</div>
                    <div class="download-status">
                        <span v-if="download.status === 'downloading'">下载中...</span>
                        <span v-else-if="download.status === 'completed'">下载完成</span>
                        <span v-else-if="download.status === 'error'">下载失败</span>
                        <span v-else>等待下载</span>
                    </div>
                </div>
                <div class="download-progress">
                    <el-progress 
                        :percentage="download.progress" 
                        :status="download.status === 'error' ? 'exception' : (download.status === 'completed' ? 'success' : '')"
                        :show-text="false"
                        v-if="download.status !== 'completed' && download.status !== 'error'">
                    </el-progress>
                    <span v-else>{{ download.status === 'completed' ? '完成' : '失败' }}</span>
                </div>
                <div class="download-actions">
                    <i class="fas fa-times" v-if="download.status !== 'downloading'" @click="removeDownload(download.id)" style="cursor: pointer;"></i>
                </div>
            </div>
        </div>
        
        <!-- 选择操作栏 -->
        <div class="selection-actions" v-if="selectedItems.length > 0">
            <div class="selection-count">
                <i class="fas fa-check-circle" style="color: #3a7bd5; margin-right: 8px;"></i>
                <span>已选择 {{ selectedItems.length }} 个项目</span>
            </div>
            <div class="selection-buttons">
                <el-button size="mini" @click="downloadSelected">下载所选</el-button>
                <el-button size="mini" @click="clearSelection">取消选择</el-button>
            </div>
        </div>
        
        <div class="main-container">
            <!-- 侧边栏 -->
            <div class="sidebar">
                <div class="menu-item active">
                    <i class="fas fa-home"></i>
                    <span>首页</span>
                </div>
                <div class="menu-item">
                    <i class="fas fa-file"></i>
                    <span>我的文件</span>
                </div>
                <div class="menu-item">
                    <i class="fas fa-share-alt"></i>
                    <span>共享</span>
                </div>
                <div class="menu-item">
                    <i class="fas fa-clock"></i>
                    <span>最近</span>
                </div>
                <div class="menu-item">
                    <i class="fas fa-star"></i>
                    <span>收藏</span>
                </div>
                <div class="menu-item">
                    <i class="fas fa-trash"></i>
                    <span>回收站</span>
                </div>
                <div class="menu-item">
                    <i class="fas fa-cog"></i>
                    <span>设置</span>
                </div>
                
                <div class="storage-info">
                    <div class="storage-title">存储空间</div>
                    <el-progress class="storage-progress" :percentage="65" :show-text="false"></el-progress>
                    <div class="storage-size">6.5 GB / 10 GB 已使用</div>
                </div>
            </div>
            
            <!-- 主内容区 -->
            <div class="content">
                <!-- 面包屑导航 -->
                <el-breadcrumb class="breadcrumb" separator="/">
                    <el-breadcrumb-item v-for="(item, index) in breadcrumb" :key="index">
                        <a href="#" @click.prevent="navigateTo(item.id)">{{ item.name }}</a>
                    </el-breadcrumb-item>
                </el-breadcrumb>
                
                <!-- 操作栏 -->
                <div class="action-bar">
                    <h2 v-if="currentFolderId === 0">我的文件夹</h2>
                    <h2 v-else>{{ currentFolderName }}</h2>
                    <div>
                        <el-button type="primary" icon="el-icon-folder-add" @click="createFolder">新建文件夹</el-button>
                        <el-button icon="el-icon-upload" @click="uploadFile">上传文件</el-button>
                        <el-button icon="el-icon-document-copy" :disabled="!clipboard.item" @click="pasteItem">粘贴</el-button>
                        <el-button icon="el-icon-download" @click="downloadAllVisible">下载全部</el-button>
                    </div>
                </div>
                
                <!-- 文件列表 -->
                <div class="file-list">
                    <!-- 文件夹列表 -->
                    <div v-if="folders.length > 0">
                        <div class="file-item" v-for="folder in folders" :key="'folder-'+folder.id" 
                             @click="toggleSelect(folder, 'folder')"
                             @dblclick="enterFolder(folder)"
                             @contextmenu.prevent="showContextMenu($event, folder, 'folder')"
                             :class="{ selected: isSelected(folder.id, 'folder') }">
                            <div class="file-icon folder-icon">
                                <i class="fas fa-folder"></i>
                            </div>
                            <div class="file-info">
                                <div class="file-name">{{ folder.name }}</div>
                                <div class="file-meta">{{ folder.items }}个项目 · {{ folder.createdAt }}</div>
                            </div>
                            <div class="file-actions">
                                <div class="action-btn" @click.stop="copyItem(folder, 'folder')">
                                    <i class="fas fa-copy"></i>
                                </div>
                                <div class="action-btn" @click.stop="cutItem(folder, 'folder')">
                                    <i class="fas fa-cut"></i>
                                </div>
                                <div class="action-btn" @click.stop="downloadItem(folder, 'folder')">
                                    <i class="fas fa-download"></i>
                                </div>
                                <div class="action-btn" @click.stop="renameFolder(folder)">
                                    <i class="fas fa-pen"></i>
                                </div>
                                <div class="action-btn" @click.stop="deleteFolder(folder)">
                                    <i class="fas fa-trash"></i>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 文件列表 -->
                    <div v-if="files.length > 0">
                        <div class="file-item" v-for="file in files" :key="'file-'+file.id" 
                             @click="toggleSelect(file, 'file')"
                             @contextmenu.prevent="showContextMenu($event, file, 'file')"
                             :class="{ selected: isSelected(file.id, 'file') }">
                            <div class="file-icon" :class="getFileIconClass(file)">
                                <i :class="getFileIcon(file)"></i>
                            </div>
                            <div class="file-info">
                                <div class="file-name">{{ file.name }}</div>
                                <div class="file-meta">{{ file.size }} · {{ file.createdAt }}</div>
                            </div>
                            <div class="file-actions">
                                <div class="action-btn" @click.stop="downloadItem(file, 'file')">
                                    <i class="fas fa-download"></i>
                                </div>
                                <div class="action-btn" @click.stop="copyItem(file, 'file')">
                                    <i class="fas fa-copy"></i>
                                </div>
                                <div class="action-btn" @click.stop="cutItem(file, 'file')">
                                    <i class="fas fa-cut"></i>
                                </div>
                                <div class="action-btn" @click.stop="renameFile(file)">
                                    <i class="fas fa-pen"></i>
                                </div>
                                <div class="action-btn" @click.stop="deleteFile(file)">
                                    <i class="fas fa-trash"></i>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 空状态 -->
                    <div class="empty-state" v-if="folders.length === 0 && files.length === 0">
                        <div class="empty-icon">
                            <i class="fas fa-folder-open"></i>
                        </div>
                        <h3>当前文件夹为空</h3>
                        <p>上传文件或创建新文件夹</p>
                    </div>
                </div>
                
                <!-- 上传区域 -->
                <div class="upload-area" @drop="onDrop" @dragover="onDragOver" @dragleave="onDragLeave">
                    <div class="upload-icon">
                        <i class="fas fa-cloud-upload-alt"></i>
                    </div>
                    <div class="upload-text">
                        <h3>拖放文件到此处上传</h3>
                        <p>支持单个或多个文件上传,最大文件大小2GB</p>
                    </div>
                    <el-button type="primary" icon="el-icon-upload" @click="uploadFile">选择文件</el-button>
                    <input type="file" ref="fileInput" style="display: none" @change="onFileSelected" multiple>
                </div>
            </div>
        </div>
        
        <!-- 上下文菜单 -->
        <div class="context-menu" v-if="contextMenu.visible" :style="{ top: contextMenu.y + 'px', left: contextMenu.x + 'px' }">
            <div class="context-menu-item" @click="downloadItem(contextMenu.item, contextMenu.type)">
                <i class="fas fa-download"></i>
                <span>下载</span>
            </div>
            <div class="context-menu-item" @click="copyItem(contextMenu.item, contextMenu.type)">
                <i class="fas fa-copy"></i>
                <span>复制</span>
            </div>
            <div class="context-menu-item" @click="cutItem(contextMenu.item, contextMenu.type)">
                <i class="fas fa-cut"></i>
                <span>剪切</span>
            </div>
            <div class="context-menu-item" @click="pasteItem" v-if="clipboard.item">
                <i class="fas fa-paste"></i>
                <span>粘贴</span>
            </div>
            <el-divider></el-divider>
            <div class="context-menu-item" @click="contextMenu.type === 'folder' ? renameFolder(contextMenu.item) : renameFile(contextMenu.item)">
                <i class="fas fa-pen"></i>
                <span>重命名</span>
            </div>
            <div class="context-menu-item" @click="contextMenu.type === 'folder' ? deleteFolder(contextMenu.item) : deleteFile(contextMenu.item)">
                <i class="fas fa-trash"></i>
                <span>删除</span>
            </div>
        </div>
        
        <!-- 选择目标文件夹对话框 -->
        <el-dialog :title="'选择目标文件夹 - ' + (clipboard.operation === 'copy' ? '复制' : '移动')" :visible.sync="targetFolderDialogVisible" width="50%">
            <el-tree
                :data="folderTree"
                :props="treeProps"
                node-key="id"
                default-expand-all
                :expand-on-click-node="false"
                @node-click="handleFolderSelect"
                :highlight-current="true">
                <span class="custom-tree-node" slot-scope="{ node, data }">
                    <i class="fas fa-folder" style="color: #ff9426; margin-right: 8px;"></i>
                    <span>{{ node.label }}</span>
                </span>
            </el-tree>
            <span slot="footer" class="dialog-footer">
                <el-button @click="targetFolderDialogVisible = false">取消</el-button>
                <el-button type="primary" @click="confirmTargetFolder">确定</el-button>
            </span>
        </el-dialog>
    </div>

    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    // 当前文件夹ID(0表示根目录)
                    currentFolderId: 0,
                    currentFolderName: '',
                    // 面包屑导航
                    breadcrumb: [{ id: 0, name: '全部文件' }],
                    // 文件夹数据
                    folders: [],
                    // 文件数据
                    files: [],
                    // 剪贴板状态
                    clipboard: {
                        item: null,
                        type: null,
                        operation: null, // 'copy' or 'cut'
                        originalParentId: null
                    },
                    // 上下文菜单
                    contextMenu: {
                        visible: false,
                        x: 0,
                        y: 0,
                        item: null,
                        type: null
                    },
                    // 目标文件夹对话框
                    targetFolderDialogVisible: false,
                    selectedTargetFolder: null,
                    folderTree: [],
                    treeProps: {
                        children: 'children',
                        label: 'name'
                    },
                    // 下载相关
                    downloads: [],
                    // 选择相关
                    selectedItems: []
                };
            },
            mounted() {
                this.loadAllData();
                this.loadFolderContent();
                
                // 点击其他地方关闭上下文菜单
                document.addEventListener('click', () => {
                    this.contextMenu.visible = false;
                });
                
                // 加载下载历史
                this.loadDownloadHistory();
            },
            methods: {
                // 加载所有数据
                loadAllData() {
                    this.allFolders = JSON.parse(localStorage.getItem('cloudFolders')) || [
                        { id: 1, name: '工作文档', items: 5, createdAt: '2023-06-15', parentId: 0 },
                        { id: 2, name: '个人照片', items: 23, createdAt: '2023-05-22', parentId: 0 },
                        { id: 3, name: '学习资料', items: 12, createdAt: '2023-07-10', parentId: 0 },
                        { id: 4, name: '项目文件', items: 8, createdAt: '2023-08-01', parentId: 1 },
                        { id: 5, name: '会议记录', items: 3, createdAt: '2023-08-05', parentId: 1 }
                    ];
                    
                    this.allFiles = JSON.parse(localStorage.getItem('cloudFiles')) || [
                        { id: 1, name: '项目报告.pdf', size: '2.4 MB', type: 'pdf', createdAt: '2023-08-05', parentId: 0 },
                        { id: 2, name: 'vacation.jpg', size: '4.2 MB', type: 'image', createdAt: '2023-08-10', parentId: 0 },
                        { id: 3, name: '论文草稿.docx', size: '1.8 MB', type: 'doc', createdAt: '2023-08-12', parentId: 0 },
                        { id: 4, name: '演示视频.mp4', size: '86.5 MB', type: 'video', createdAt: '2023-08-15', parentId: 0 },
                        { id: 5, name: 'Q3规划.xlsx', size: '0.8 MB', type: 'excel', createdAt: '2023-08-18', parentId: 1 },
                        { id: 6, name: '需求文档.pdf', size: '3.1 MB', type: 'pdf', createdAt: '2023-08-20', parentId: 4 }
                    ];
                    
                    // 构建文件夹树
                    this.buildFolderTree();
                },
                
                // 构建文件夹树
                buildFolderTree() {
                    // 复制文件夹数据
                    const folders = JSON.parse(JSON.stringify(this.allFolders));
                    
                    // 创建根节点
                    const rootFolders = folders.filter(f => f.parentId === 0);
                    
                    // 递归构建树
                    const buildTree = (parentId) => {
                        const children = folders.filter(f => f.parentId === parentId);
                        for (const child of children) {
                            child.children = buildTree(child.id);
                        }
                        return children;
                    };
                    
                    this.folderTree = buildTree(0);
                },
                
                // 加载当前文件夹内容
                loadFolderContent() {
                    this.folders = this.allFolders.filter(f => f.parentId === this.currentFolderId);
                    this.files = this.allFiles.filter(f => f.parentId === this.currentFolderId);
                    
                    // 更新当前文件夹名称
                    if (this.currentFolderId !== 0) {
                        const folder = this.allFolders.find(f => f.id === this.currentFolderId);
                        this.currentFolderName = folder ? folder.name : '';
                    }
                    
                    // 清空选择
                    this.selectedItems = [];
                },
                
                // 进入文件夹
                enterFolder(folder) {
                    this.currentFolderId = folder.id;
                    this.breadcrumb.push({ id: folder.id, name: folder.name });
                    this.loadFolderContent();
                },
                
                // 导航到指定文件夹
                navigateTo(folderId) {
                    const index = this.breadcrumb.findIndex(item => item.id === folderId);
                    if (index !== -1) {
                        this.breadcrumb = this.breadcrumb.slice(0, index + 1);
                        this.currentFolderId = folderId;
                        this.loadFolderContent();
                    }
                },
                
                // 创建文件夹
                createFolder() {
                    this.$prompt('请输入文件夹名称', '创建文件夹', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        inputPattern: /.+/,
                        inputErrorMessage: '文件夹名称不能为空'
                    }).then(({ value }) => {
                        const newFolder = {
                            id: Date.now(),
                            name: value,
                            items: 0,
                            createdAt: new Date().toLocaleDateString(),
                            parentId: this.currentFolderId
                        };
                        
                        // 保存到本地存储
                        this.allFolders.push(newFolder);
                        localStorage.setItem('cloudFolders', JSON.stringify(this.allFolders));
                        
                        // 更新当前视图
                        this.folders = this.allFolders.filter(f => f.parentId === this.currentFolderId);
                        
                        this.$message({
                            type: 'success',
                            message: '文件夹创建成功'
                        });
                    }).catch(() => {
                        // 用户取消操作
                    });
                },
                
                // 重命名文件夹
                renameFolder(folder) {
                    this.$prompt('请输入新的文件夹名称', '重命名文件夹', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        inputValue: folder.name,
                        inputPattern: /.+/,
                        inputErrorMessage: '文件夹名称不能为空'
                    }).then(({ value }) => {
                        // 更新本地存储
                        const index = this.allFolders.findIndex(f => f.id === folder.id);
                        if (index !== -1) {
                            this.allFolders[index].name = value;
                            localStorage.setItem('cloudFolders', JSON.stringify(this.allFolders));
                            
                            // 更新当前视图
                            this.folders = this.allFolders.filter(f => f.parentId === this.currentFolderId);
                            
                            this.$message({
                                type: 'success',
                                message: '文件夹重命名成功'
                            });
                        }
                    }).catch(() => {
                        // 用户取消操作
                    });
                },
                
                // 删除文件夹
                deleteFolder(folder) {
                    this.$confirm(`确定要删除文件夹 "${folder.name}" 吗?此操作将删除文件夹内的所有内容。`, '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        // 递归删除文件夹及其内容
                        this.deleteFolderRecursively(folder.id);
                        
                        // 更新视图
                        this.loadFolderContent();
                        
                        this.$message({
                            type: 'success',
                            message: '删除成功'
                        });
                    }).catch(() => {
                        // 用户取消操作
                    });
                },
                
                // 递归删除文件夹及其内容
                deleteFolderRecursively(folderId) {
                    // 删除文件夹
                    this.allFolders = this.allFolders.filter(f => f.id !== folderId);
                    
                    // 删除文件夹内的文件
                    this.allFiles = this.allFiles.filter(f => f.parentId !== folderId);
                    
                    // 查找并删除子文件夹
                    const childFolders = this.allFolders.filter(f => f.parentId === folderId);
                    for (const child of childFolders) {
                        this.deleteFolderRecursively(child.id);
                    }
                    
                    // 保存到本地存储
                    localStorage.setItem('cloudFolders', JSON.stringify(this.allFolders));
                    localStorage.setItem('cloudFiles', JSON.stringify(this.allFiles));
                    
                    // 更新数据
                    this.folders = this.allFolders.filter(f => f.parentId === this.currentFolderId);
                    this.files = this.allFiles.filter(f => f.parentId === this.currentFolderId);
                },
                
                // 上传文件
                uploadFile() {
                    this.$refs.fileInput.click();
                },
                
                // 文件选择处理
                onFileSelected(event) {
                    const files = event.target.files;
                    if (files.length > 0) {
                        this.handleFileUpload(files);
                    }
                    // 重置input
                    event.target.value = '';
                },
                
                // 处理文件上传
                handleFileUpload(fileList) {
                    for (let i = 0; i < fileList.length; i++) {
                        const file = fileList[i];
                        const fileSize = (file.size / (1024 * 1024)).toFixed(1) + ' MB';
                        
                        // 获取文件类型
                        let fileType = 'file';
                        if (file.type.includes('image')) fileType = 'image';
                        else if (file.type.includes('pdf')) fileType = 'pdf';
                        else if (file.type.includes('word')) fileType = 'doc';
                        else if (file.type.includes('video')) fileType = 'video';
                        else if (file.type.includes('excel') || file.type.includes('sheet')) fileType = 'excel';
                        
                        const newFile = {
                            id: Date.now() + i,
                            name: file.name,
                            size: fileSize,
                            type: fileType,
                            createdAt: new Date().toLocaleDateString(),
                            parentId: this.currentFolderId
                        };
                        
                        this.allFiles.push(newFile);
                    }
                    
                    // 保存到本地存储
                    localStorage.setItem('cloudFiles', JSON.stringify(this.allFiles));
                    
                    // 更新当前视图
                    this.files = this.allFiles.filter(f => f.parentId === this.currentFolderId);
                    
                    this.$message({
                        type: 'success',
                        message: `成功上传 ${fileList.length} 个文件`
                    });
                },
                
                // 下载文件
                downloadItem(item, type) {
                    // 创建下载任务
                    const downloadId = Date.now();
                    const downloadTask = {
                        id: downloadId,
                        name: item.name,
                        type: type === 'file' ? item.type : 'folder',
                        progress: 0,
                        status: 'pending'
                    };
                    
                    this.downloads.unshift(downloadTask);
                    this.saveDownloadHistory();
                    
                    // 模拟下载过程
                    this.simulateDownload(downloadId, item, type);
                    
                    this.contextMenu.visible = false;
                },
                
                // 模拟下载过程
                simulateDownload(downloadId, item, type) {
                    // 更新状态为下载中
                    const downloadIndex = this.downloads.findIndex(d => d.id === downloadId);
                    if (downloadIndex !== -1) {
                        this.downloads[downloadIndex].status = 'downloading';
                        
                        // 模拟进度更新
                        let progress = 0;
                        const interval = setInterval(() => {
                            progress += Math.random() * 15;
                            if (progress >= 100) {
                                progress = 100;
                                clearInterval(interval);
                                
                                // 下载完成
                                this.downloads[downloadIndex].progress = 100;
                                this.downloads[downloadIndex].status = 'completed';
                                this.saveDownloadHistory();
                                
                                // 实际下载文件
                                this.performDownload(item, type);
                            } else {
                                this.downloads[downloadIndex].progress = Math.round(progress);
                            }
                        }, 200);
                    }
                },
                
                // 执行实际下载
                performDownload(item, type) {
                    // 创建文件内容
                    let content = '';
                    let fileName = item.name;
                    
                    if (type === 'file') {
                        // 根据文件类型生成不同的内容
                        switch (item.type) {
                            case 'pdf':
                                content = `%PDF-1.4\n模拟PDF文件内容: ${item.name}\n生成时间: ${new Date().toLocaleString()}`;
                                break;
                            case 'image':
                                content = `模拟图像文件: ${item.name}\n生成时间: ${new Date().toLocaleString()}`;
                                if (!fileName.endsWith('.jpg')) fileName += '.jpg';
                                break;
                            case 'doc':
                                content = `模拟Word文档: ${item.name}\n生成时间: ${new Date().toLocaleString()}`;
                                if (!fileName.endsWith('.docx')) fileName += '.docx';
                                break;
                            case 'video':
                                content = `模拟视频文件: ${item.name}\n生成时间: ${new Date().toLocaleString()}`;
                                if (!fileName.endsWith('.mp4')) fileName += '.mp4';
                                break;
                            case 'excel':
                                content = `模拟Excel文件: ${item.name}\n生成时间: ${new Date().toLocaleString()}`;
                                if (!fileName.endsWith('.xlsx')) fileName += '.xlsx';
                                break;
                            default:
                                content = `文件: ${item.name}\n大小: ${item.size}\n生成时间: ${new Date().toLocaleString()}`;
                        }
                    } else {
                        // 文件夹下载 - 创建ZIP文件(模拟)
                        content = `压缩文件夹: ${item.name}\n包含文件: ${item.items}个\n生成时间: ${new Date().toLocaleString()}`;
                        if (!fileName.endsWith('.zip')) fileName += '.zip';
                    }
                    
                    // 创建Blob对象
                    const blob = new Blob([content], { type: 'application/octet-stream' });
                    const url = URL.createObjectURL(blob);
                    
                    // 创建下载链接
                    const link = document.createElement('a');
                    link.href = url;
                    link.download = fileName;
                    document.body.appendChild(link);
                    link.click();
                    
                    // 清理
                    setTimeout(() => {
                        document.body.removeChild(link);
                        URL.revokeObjectURL(url);
                    }, 100);
                },
                
                // 移除下载任务
                removeDownload(downloadId) {
                    this.downloads = this.downloads.filter(d => d.id !== downloadId);
                    this.saveDownloadHistory();
                },
                
                // 保存下载历史
                saveDownloadHistory() {
                    // 只保存最近10条下载记录
                    const history = this.downloads.slice(0, 10);
                    localStorage.setItem('downloadHistory', JSON.stringify(history));
                },
                
                // 加载下载历史
                loadDownloadHistory() {
                    const history = JSON.parse(localStorage.getItem('downloadHistory')) || [];
                    this.downloads = history;
                },
                
                // 重命名文件
                renameFile(file) {
                    this.$prompt('请输入新的文件名称', '重命名文件', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        inputValue: file.name,
                        inputPattern: /.+/,
                        inputErrorMessage: '文件名称不能为空'
                    }).then(({ value }) => {
                        // 更新本地存储
                        const index = this.allFiles.findIndex(f => f.id === file.id);
                        if (index !== -1) {
                            this.allFiles[index].name = value;
                            localStorage.setItem('cloudFiles', JSON.stringify(this.allFiles));
                            
                            // 更新当前视图
                            this.files = this.allFiles.filter(f => f.parentId === this.currentFolderId);
                            
                            this.$message({
                                type: 'success',
                                message: '文件重命名成功'
                            });
                        }
                    }).catch(() => {
                        // 用户取消操作
                    });
                },
                
                // 删除文件
                deleteFile(file) {
                    this.$confirm(`确定要删除文件 "${file.name}" 吗?`, '提示', {
                        confirmButtonText: '确定',
                        cancelButtonText: '取消',
                        type: 'warning'
                    }).then(() => {
                        // 更新本地存储
                        this.allFiles = this.allFiles.filter(f => f.id !== file.id);
                        localStorage.setItem('cloudFiles', JSON.stringify(this.allFiles));
                        
                        // 更新当前视图
                        this.files = this.allFiles.filter(f => f.parentId === this.currentFolderId);
                        
                        this.$message({
                            type: 'success',
                            message: '删除成功'
                        });
                    }).catch(() => {
                        // 用户取消操作
                    });
                },
                
                // 复制项目
                copyItem(item, type) {
                    this.clipboard = {
                        item: item,
                        type: type,
                        operation: 'copy',
                        originalParentId: item.parentId
                    };
                    
                    this.contextMenu.visible = false;
                    this.$message({
                        type: 'success',
                        message: `已复制: ${item.name}`
                    });
                },
                
                // 剪切项目
                cutItem(item, type) {
                    this.clipboard = {
                        item: item,
                        type: type,
                        operation: 'cut',
                        originalParentId: item.parentId
                    };
                    
                    this.contextMenu.visible = false;
                    this.$message({
                        type: 'success',
                        message: `已剪切: ${item.name}`
                    });
                },
                
                // 粘贴项目
                pasteItem() {
                    if (!this.clipboard.item) return;
                    
                    // 如果目标是文件夹,需要选择目标文件夹
                    if (this.clipboard.type === 'folder' && this.clipboard.operation === 'cut') {
                        this.targetFolderDialogVisible = true;
                        return;
                    }
                    
                    // 直接粘贴到当前文件夹
                    this.performPaste(this.currentFolderId);
                },
                
                // 执行粘贴操作
                performPaste(targetFolderId) {
                    if (this.clipboard.operation === 'copy') {
                        this.copyToTarget(targetFolderId);
                    } else {
                        this.moveToTarget(targetFolderId);
                    }
                    
                    // 清空剪贴板
                    this.clearClipboard();
                    
                    // 重新加载内容
                    this.loadFolderContent();
                },
                
                // 复制到目标文件夹
                copyToTarget(targetFolderId) {
                    if (this.clipboard.type === 'file') {
                        // 复制文件
                        const newFile = {
                            ...this.clipboard.item,
                            id: Date.now(),
                            parentId: targetFolderId
                        };
                        this.allFiles.push(newFile);
                    } else {
                        // 复制文件夹
                        const newFolder = {
                            ...this.clipboard.item,
                            id: Date.now(),
                            parentId: targetFolderId
                        };
                        this.allFolders.push(newFolder);
                    }
                    
                    // 保存到本地存储
                    localStorage.setItem('cloudFolders', JSON.stringify(this.allFolders));
                    localStorage.setItem('cloudFiles', JSON.stringify(this.allFiles));
                    
                    this.$message({
                        type: 'success',
                        message: '复制成功'
                    });
                },
                
                // 移动到目标文件夹
                moveToTarget(targetFolderId) {
                    if (this.clipboard.type === 'file') {
                        // 移动文件
                        const index = this.allFiles.findIndex(f => f.id === this.clipboard.item.id);
                        if (index !== -1) {
                            this.allFiles[index].parentId = targetFolderId;
                        }
                    } else {
                        // 移动文件夹
                        const index = this.allFolders.findIndex(f => f.id === this.clipboard.item.id);
                        if (index !== -1) {
                            this.allFolders[index].parentId = targetFolderId;
                        }
                    }
                    
                    // 保存到本地存储
                    localStorage.setItem('cloudFolders', JSON.stringify(this.allFolders));
                    localStorage.setItem('cloudFiles', JSON.stringify(this.allFiles));
                    
                    this.$message({
                        type: 'success',
                        message: '移动成功'
                    });
                },
                
                // 清空剪贴板
                clearClipboard() {
                    this.clipboard = {
                        item: null,
                        type: null,
                        operation: null,
                        originalParentId: null
                    };
                },
                
                // 显示上下文菜单
                showContextMenu(event, item, type) {
                    this.contextMenu = {
                        visible: true,
                        x: event.pageX,
                        y: event.pageY,
                        item: item,
                        type: type
                    };
                },
                
                // 处理文件夹选择
                handleFolderSelect(data) {
                    this.selectedTargetFolder = data;
                },
                
                // 确认目标文件夹
                confirmTargetFolder() {
                    if (!this.selectedTargetFolder) {
                        this.$message.warning('请选择目标文件夹');
                        return;
                    }
                    
                    this.performPaste(this.selectedTargetFolder.id);
                    this.targetFolderDialogVisible = false;
                },
                
                // 选择/取消选择项目
                toggleSelect(item, type) {
                    const itemId = `${type}-${item.id}`;
                    const index = this.selectedItems.findIndex(i => i.id === itemId);
                    
                    if (index === -1) {
                        this.selectedItems.push({
                            id: itemId,
                            type: type,
                            item: item
                        });
                    } else {
                        this.selectedItems.splice(index, 1);
                    }
                },
                
                // 检查项目是否被选择
                isSelected(itemId, type) {
                    return this.selectedItems.some(i => i.id === `${type}-${itemId}`);
                },
                
                // 清空选择
                clearSelection() {
                    this.selectedItems = [];
                },
                
                // 下载所选项目
                downloadSelected() {
                    if (this.selectedItems.length === 0) return;
                    
                    // 批量下载
                    this.selectedItems.forEach(selected => {
                        this.downloadItem(selected.item, selected.type);
                    });
                    
                    this.$message({
                        type: 'success',
                        message: `开始下载 ${this.selectedItems.length} 个项目`
                    });
                },
                
                // 下载全部可见项目
                downloadAllVisible() {
                    const itemsToDownload = [
                        ...this.folders.map(f => ({ item: f, type: 'folder' })),
                        ...this.files.map(f => ({ item: f, type: 'file' }))
                    ];
                    
                    if (itemsToDownload.length === 0) return;
                    
                    itemsToDownload.forEach(({ item, type }) => {
                        this.downloadItem(item, type);
                    });
                    
                    this.$message({
                        type: 'success',
                        message: `开始下载 ${itemsToDownload.length} 个项目`
                    });
                },
                
                // 获取文件图标
                getFileIcon(file) {
                    switch (file.type) {
                        case 'pdf': return 'fas fa-file-pdf';
                        case 'image': return 'fas fa-file-image';
                        case 'doc': return 'fas fa-file-word';
                        case 'video': return 'fas fa-file-video';
                        case 'excel': return 'fas fa-file-excel';
                        case 'folder': return 'fas fa-folder';
                        default: return 'fas fa-file';
                    }
                },
                
                // 获取文件图标类
                getFileIconClass(file) {
                    switch (file.type) {
                        case 'pdf': return 'pdf-icon';
                        case 'image': return 'image-icon';
                        case 'doc': return 'doc-icon';
                        case 'video': return 'video-icon';
                        case 'excel': return 'excel-icon';
                        default: return 'file-icon';
                    }
                },
                
                // 拖放相关方法
                onDragOver(e) {
                    e.preventDefault();
                    e.currentTarget.style.borderColor = '#3a7bd5';
                    e.currentTarget.style.backgroundColor = 'rgba(58, 123, 213, 0.15)';
                },
                
                onDragLeave(e) {
                    e.preventDefault();
                    e.currentTarget.style.borderColor = '#dcdfe6';
                    e.currentTarget.style.backgroundColor = 'rgba(58, 123, 213, 0.03)';
                },
                
                onDrop(e) {
                    e.preventDefault();
                    e.currentTarget.style.borderColor = '#dcdfe6';
                    e.currentTarget.style.backgroundColor = 'rgba(58, 123, 213, 0.03)';
                    
                    const files = e.dataTransfer.files;
                    if (files.length > 0) {
                        this.handleFileUpload(files);
                    }
                }
            }
        });
    </script>
</body>
</html>