新版百度网盘共享文件库目录导出工具

用于导出百度网盘共享文件库目录和文件列表

// ==UserScript==
// @name         新版百度网盘共享文件库目录导出工具
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  用于导出百度网盘共享文件库目录和文件列表
// @author       superzhang
// @license      MIT
// @match        https://pan.baidu.com/disk*
// @icon         https://nd-static.bdstatic.com/m-static/v20-main/favicon-main.ico
// @grant        GM_xmlhttpRequest
// @require      https://unpkg.com/xlsx/dist/xlsx.full.min.js
// ==/UserScript==

(function () {
    'use strict';

    // 添加调试日志函数
    const DEBUG = true;
    function debugLog(message, data) {
        if (DEBUG) {
            console.log(`[NBNT Debug] ${message}`, data || '');
        }
    }

    let directories = []; // 存储解析后的目录数据
    let depthSetting = 1; // 默认层数设置

    // 添加并发控制池
    class RequestPool {
        constructor(maxConcurrent = 2, requestInterval = 3000) {
            this.maxConcurrent = maxConcurrent;
            this.currentRequests = 0;
            this.queue = [];
            this.requestInterval = requestInterval;
            this.lastRequestTime = 0;
        }

        async add(fn) {
            if (this.currentRequests >= this.maxConcurrent) {
                await new Promise(resolve => this.queue.push(resolve));
            }

            const now = Date.now();
            const timeSinceLastRequest = now - this.lastRequestTime;
            if (timeSinceLastRequest < this.requestInterval) {
                await new Promise(resolve =>
                    setTimeout(resolve, this.requestInterval - timeSinceLastRequest)
                );
            }

            this.currentRequests++;
            this.lastRequestTime = Date.now();

            try {
                return await fn();
            } finally {
                this.currentRequests--;
                if (this.queue.length > 0) {
                    const next = this.queue.shift();
                    next();
                }
            }
        }
    }

    // 添加进度条组件
    function createProgressBar() {
        const progressContainer = document.createElement('div');
        progressContainer.id = 'directory-progress';
        progressContainer.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            z-index: 9999;
            display: none;
            width: 350px;
            font-family: "Microsoft YaHei", sans-serif;
        `;

        const titleDiv = document.createElement('div');
        titleDiv.style.cssText = `
            font-weight: bold;
            margin-bottom: 15px;
            color: #333;
            font-size: 14px;
        `;
        titleDiv.textContent = '目录获取进度';

        const progressText = document.createElement('div');
        progressText.id = 'progress-text';
        progressText.style.cssText = `
            margin-bottom: 10px;
            color: #666;
            font-size: 13px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            max-width: 280px;
        `;
        progressText.textContent = '正在获取目录信息...';

        const progressBarOuter = document.createElement('div');
        progressBarOuter.style.cssText = `
            width: 100%;
            height: 6px;
            background: #f0f0f0;
            border-radius: 3px;
            overflow: hidden;
        `;

        const progressBarInner = document.createElement('div');
        progressBarInner.id = 'progress-bar';
        progressBarInner.style.cssText = `
            width: 0%;
            height: 100%;
            background: linear-gradient(90deg, #2196F3, #00BCD4);
            transition: width 0.3s ease;
            border-radius: 3px;
        `;

        progressBarOuter.appendChild(progressBarInner);
        progressContainer.appendChild(titleDiv);
        progressContainer.appendChild(progressText);
        progressContainer.appendChild(progressBarOuter);
        document.body.appendChild(progressContainer);

        return {
            show: () => progressContainer.style.display = 'block',
            hide: () => progressContainer.style.display = 'none',
            remove: () => {
                if (progressContainer.parentNode) {
                    progressContainer.parentNode.removeChild(progressContainer);
                }
            },
            updateProgress: (current, total) => {
                const percentage = Math.min((current / total) * 100, 100);
                progressBarInner.style.width = `${percentage}%`;
                progressText.textContent = `进度:${current}/${total} (${percentage.toFixed(1)}%)`;
            },
            updateText: (text) => {
                progressText.textContent = text;
            }
        };
    }

    // 等待文件库按钮和标题加载,并添加点击事件监听
    function waitForLibraryElements() {
        let isProcessing = false;

        // 检查并添加按钮到操作栏
        function checkAndAddOperationButtons() {
            if (isProcessing) return;
            isProcessing = true;

            try {
                const operateDiv = document.querySelector('.im-file-nav__operate');
                const downloadButton = operateDiv?.querySelector('.u-icon-download')?.closest('button');

                if (!operateDiv || !downloadButton) {
                    isProcessing = false;
                    return;
                }

                const existingCheckButton = document.querySelector('#check-dir-button');
                const existingFetchButton = document.querySelector('#fetch-dir-button');

                if (existingCheckButton || existingFetchButton) {
                    isProcessing = false;
                    return;
                }

                // 添加样式
                const style = document.createElement('style');
                style.textContent = `
                    .export-dropdown {
                        position: relative;
                        display: inline-flex;
                        align-items: center;
                        cursor: pointer;
                        height: 24px;
                        line-height: 24px;
                    }
                    .export-dropdown::after {
                        content: '';
                        position: absolute;
                        right: -12px;
                        top: 6px;
                        width: 1px;
                        height: 12px;
                        background-color: rgb(217, 217, 217);
                    }
                    .export-dropdown-menu {
                        display: none;
                        position: absolute;
                        top: 100%;
                        left: 50%;
                        transform: translateX(-50%);
                        background: white;
                        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
                        padding: 4px;
                        z-index: 99999;
                        margin-top: 2px;
                        border: 1px solid #e8e8e8;
                        white-space: nowrap;
                        flex-direction: row;
                    }
                    .export-dropdown-menu.show {
                        display: flex;
                    }
                    .export-item {
                        padding: 4px 12px;
                        cursor: pointer;
                        color: #333;
                        font-size: 12px;
                        line-height: 1.5;
                        border-right: 1px solid #e8e8e8;
                    }
                    .export-item:last-child {
                        border-right: none;
                    }
                    .export-item:hover {
                        background: #f5f5f5;
                    }
                `;
                document.head.appendChild(style);

                const checkButton = document.createElement('button');
                checkButton.id = 'check-dir-button';
                checkButton.type = 'button';
                checkButton.className = 'u-button u-button--default u-button--mini';
                checkButton.innerHTML = `
                    <i class="u-icon-search"></i>
                    <span>检查目录</span>
                `;

                const exportDropdown = document.createElement('div');
                exportDropdown.className = 'export-dropdown u-button u-button--default u-button--mini';
                exportDropdown.innerHTML = `
                    <i class="u-icon-folder"></i>
                    <span>导出目录</span>
                    <i class="u-icon-arrow-down" style="margin-left: 4px;"></i>
                    <div class="export-dropdown-menu">
                        <div class="export-item" data-type="txt">导出为TXT</div>
                        <div class="export-item" data-type="xlsx">导出为Excel</div>
                    </div>
                `;

                const fetchAllDropdown = document.createElement('div');
                fetchAllDropdown.className = 'export-dropdown u-button u-button--default u-button--mini';
                fetchAllDropdown.innerHTML = `
                    <i class="u-icon-download-bold"></i>
                    <span>导出全部</span>
                    <i class="u-icon-arrow-down" style="margin-left: 4px;"></i>
                    <div class="export-dropdown-menu">
                        <div class="export-item" data-type="txt">导出为TXT</div>
                        <div class="export-item" data-type="xlsx">导出为Excel</div>
                    </div>
                `;

                checkButton.onclick = function() {
                    const selectedDirs = getSelectedDirectories();
                    console.log(selectedDirs);
                    if (selectedDirs.length === 0) {
                        alert('请至少选中一个目录!');
                        return;
                    }

                    // 如果选中了多个目录,则显示所有选中目录的信息
                    if (selectedDirs.length > 1) {
                        let infoText = `已选中 ${selectedDirs.length} 个目录:\n\n`;
                        selectedDirs.forEach((selected, index) => {
                            const { dirInfo, title } = selected;
                            infoText += `${index + 1}. ${title}\n`;
                            infoText += `   fs_id: ${dirInfo.fs_id}\n`;
                            infoText += `   group_id: ${dirInfo.group_id}\n`;
                            infoText += `   uk: ${dirInfo.uk}\n\n`;
                        });
                        alert(infoText);
                    } else {
                        // 如果只选中了一个目录,则使用原来的方式显示
                        const { dirInfo, title } = selectedDirs[0];
                        checkDirectoryInfo(dirInfo.msg_id, title);
                    }
                };

                // 处理导出选项点击
                exportDropdown.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const menu = this.querySelector('.export-dropdown-menu');
                    menu.classList.toggle('show');
                });

                // 点击其他地方关闭菜单
                document.addEventListener('click', function() {
                    const menus = document.querySelectorAll('.export-dropdown-menu');
                    menus.forEach(menu => menu.classList.remove('show'));
                });

                // 防止菜单项点击事件冒泡
                exportDropdown.querySelector('.export-dropdown-menu').addEventListener('click', function(e) {
                    e.stopPropagation();
                });

                exportDropdown.querySelector('.export-dropdown-menu').addEventListener('click', async function(e) {
                    const exportType = e.target.dataset.type;
                    if (!exportType) return;

                    try {
                        const selectedDirs = getSelectedDirectories();
                        if (selectedDirs.length === 0) {
                            alert('请至少选中一个目录!');
                            return;
                        }

                        depthSetting = parseInt(prompt("请输入要获取的子目录层数:", "1"), 10);
                        if (isNaN(depthSetting) || depthSetting < 1) {
                            alert("请输入有效的层数!");
                            return;
                        }

                        // 创建进度条
                        const progressBar = createProgressBar();
                        progressBar.show();
                        progressBar.updateText(`准备处理 ${selectedDirs.length} 个选中的目录...`);

                        // 存储所有目录的结果
                        const allResults = [];
                        let processedCount = 0;

                        // 逐个处理选中的目录
                        for (const selected of selectedDirs) {
                            const { dirInfo, title } = selected;
                            console.log(`处理目录 ${processedCount + 1}/${selectedDirs.length}: ${title}`);
                            progressBar.updateText(`正在处理目录 ${processedCount + 1}/${selectedDirs.length}: ${title}`);

                            const uk = dirInfo.uk;
                            const fsId = dirInfo.fs_id;
                            const gid = dirInfo.group_id;
                            const msgId = dirInfo.msg_id;

                            try {
                                const result = await fetchSubdirectories(uk, msgId, fsId, gid, title, depthSetting);
                                if (!window.cancelOperation && result) {
                                    allResults.push({
                                        title: title,
                                        result: result
                                    });
                                }
                            } catch (error) {
                                console.error(`处理目录 "${title}" 时出错:`, error);
                            }

                            processedCount++;
                            progressBar.updateProgress(processedCount, selectedDirs.length);
                        }

                        // 处理完成后导出文件
                        if (allResults.length > 0) {
                            progressBar.updateText('正在生成导出文件...');

                            if (exportType === 'txt') {
                                // 生成合并的文本内容
                                const combinedContent = formatMultipleDirectoryTrees(allResults);
                                const fileName = selectedDirs.length === 1 ? selectedDirs[0].title : '多个目录';
                                saveAsTxt(combinedContent, fileName);
                            } else if (exportType === 'xlsx') {
                                // 生成Excel文件
                                const fileName = selectedDirs.length === 1 ? selectedDirs[0].title : '多个目录';
                                saveMultipleAsExcel(allResults, fileName);
                            }

                            progressBar.updateText('导出完成!');
                            setTimeout(() => progressBar.hide(), 2000);
                        } else {
                            progressBar.updateText('没有成功处理任何目录');
                            setTimeout(() => progressBar.hide(), 2000);
                        }
                    } finally {
                        cleanup();
                    }
                });

                // 处理导出全部按钮的点击事件
                fetchAllDropdown.addEventListener('click', function(e) {
                    e.stopPropagation();
                    const menu = this.querySelector('.export-dropdown-menu');
                    menu.classList.toggle('show');
                });

                // 防止菜单项点击事件冒泡
                fetchAllDropdown.querySelector('.export-dropdown-menu').addEventListener('click', function(e) {
                    e.stopPropagation();
                });

                // 修改导出全部选项的点击处理
                fetchAllDropdown.querySelector('.export-dropdown-menu').addEventListener('click', async function(e) {
                    const exportType = e.target.dataset.type;
                    if (!exportType) return;

                    try {
                        const selectedDirs = getSelectedDirectories();
                        if (selectedDirs.length === 0) {
                            alert('请至少选中一个目录!');
                            return;
                        }

                        depthSetting = parseInt(prompt("请输入要获取的层数:", "1"), 10);
                        if (isNaN(depthSetting) || depthSetting < 1) {
                            alert("请输入有效的层数!");
                            return;
                        }

                        // 创建进度条
                        const progressBar = createProgressBar();
                        progressBar.show();
                        progressBar.updateText(`准备处理 ${selectedDirs.length} 个选中的目录...`);

                        // 存储所有目录的结果
                        const allResults = [];
                        let processedCount = 0;

                        // 逐个处理选中的目录
                        for (const selected of selectedDirs) {
                            const { dirInfo, title } = selected;
                            console.log(`处理目录 ${processedCount + 1}/${selectedDirs.length}: ${title}`);
                            progressBar.updateText(`正在处理目录 ${processedCount + 1}/${selectedDirs.length}: ${title}`);

                            const uk = dirInfo.uk;
                            const fsId = dirInfo.fs_id;
                            const gid = dirInfo.group_id;
                            const msgId = dirInfo.msg_id;

                            try {
                                const result = await fetchAllContent(uk, msgId, fsId, gid, title, depthSetting);
                                if (!window.cancelOperation && result) {
                                    allResults.push({
                                        title: title,
                                        result: result
                                    });
                                }
                            } catch (error) {
                                console.error(`处理目录 "${title}" 时出错:`, error);
                            }

                            processedCount++;
                            progressBar.updateProgress(processedCount, selectedDirs.length);
                        }

                        // 处理完成后导出文件
                        if (allResults.length > 0) {
                            progressBar.updateText('正在生成导出文件...');

                            if (exportType === 'txt') {
                                // 生成合并的文本内容
                                const combinedContent = formatMultipleAllContent(allResults);
                                const fileName = selectedDirs.length === 1 ? selectedDirs[0].title + "_完整" : '多个目录_完整';
                                saveAsTxt(combinedContent, fileName);
                            } else if (exportType === 'xlsx') {
                                // 生成Excel文件
                                const fileName = selectedDirs.length === 1 ? selectedDirs[0].title + "_完整" : '多个目录_完整';
                                saveMultipleAllAsExcel(allResults, fileName);
                            }

                            progressBar.updateText('导出完成!');
                            setTimeout(() => progressBar.hide(), 2000);
                        } else {
                            progressBar.updateText('没有成功处理任何目录');
                            setTimeout(() => progressBar.hide(), 2000);
                        }
                    } finally {
                        cleanup();
                    }
                });

                // 修改按钮插入顺序
                requestAnimationFrame(() => {
                    downloadButton.after(fetchAllDropdown);
                    downloadButton.after(document.createTextNode(' ')); // 添加空格
                    downloadButton.after(exportDropdown);
                    downloadButton.after(document.createTextNode(' ')); // 添加空格
                    downloadButton.after(checkButton);
                });

            } finally {
                isProcessing = false;
            }
        }

        // 创建一个防抖函数
        function debounce(func, wait) {
            let timeout;
            return function executedFunction(...args) {
                const later = () => {
                    clearTimeout(timeout);
                    func(...args);
                };
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
            };
        }

        // 使用防抖包装检查函数
        const debouncedCheck = debounce(checkAndAddOperationButtons, 200);

        // 修改 MutationObserver 的配置
        const observer = new MutationObserver((mutations) => {
            // 只在有相关变化时触发检查
            const hasRelevantChanges = mutations.some(mutation => {
                return mutation.addedNodes.length > 0 &&
                       Array.from(mutation.addedNodes).some(node => {
                           return node.classList?.contains('im-file-nav__operate') ||
                                  node.querySelector?.('.im-file-nav__operate');
                       });
            });

            if (hasRelevantChanges) {
                debouncedCheck();
            }
        });

        // 使用更具体的观察配置
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: false,
            characterData: false
        });

        // 初始检查
        checkAndAddOperationButtons();

        // 拦截请求(只需要执行一次)
        interceptNetworkRequests();
    }

    // 获取当前选中的目录(支持多选)
    function getSelectedDirectories() {
        debugLog('开始获取选中的目录');
        const selectedDirs = document.querySelectorAll('.im-pan-table__body-row.selected, .im-pan-list__item.selected');
        debugLog('找到的选中元素:', selectedDirs);

        if (selectedDirs.length === 0) {
            debugLog('没有选中任何目录');
            return [];
        }

        const results = [];

        for (const selectedDir of selectedDirs) {
            const title = selectedDir.querySelector('.im-pan-list__file-name-title-text')?.innerText;
            debugLog('获取到的目录标题:', title);

            if (!title) {
                debugLog('未能获取到目录标题,跳过此项');
                continue;
            }

            debugLog('当前目录列表:', directories);
            const matchedDir = directories.find(dir => {
                debugLog('比对目录:', {
                    current: dir.server_filename,
                    target: title,
                    match: dir.server_filename === title
                });
                return dir.server_filename === title;
            });

            if (!matchedDir) {
                console.error(`未找到目录 "${title}" 的记录`);
                debugLog('尝试模糊匹配...');
                // 尝试模糊匹配
                const fuzzyMatch = directories.find(dir =>
                    dir.server_filename.includes(title) ||
                    title.includes(dir.server_filename)
                );
                if (fuzzyMatch) {
                    debugLog('找到模糊匹配结果:', fuzzyMatch);
                    results.push({
                        element: selectedDir,
                        title: title,
                        dirInfo: fuzzyMatch
                    });
                } else {
                    debugLog(`模糊匹配也未找到结果,跳过 "${title}"`);
                }
            } else {
                debugLog('成功匹配到目录:', matchedDir);
                results.push({
                    element: selectedDir,
                    title: title,
                    dirInfo: matchedDir
                });
            }
        }

        debugLog('获取到的所有选中目录:', results);
        return results;
    }

    // 检查目录信息并显示相关信息
    function checkDirectoryInfo(msgId, title) {
        console.log(`检查目录: ${title}, msgId: ${msgId}`);

        const matchedDir = directories.find(dir => dir.msg_id === msgId);
        console.log("当前目录数据:", directories);

        if (matchedDir) {
            alert(`匹配到目录: ${title}\nfs_id: ${matchedDir.fs_id}\ngroup_id: ${matchedDir.group_id}\nuk: ${matchedDir.uk}`);
            console.log("匹配的目录信息:", matchedDir);
        } else {
            alert(`未找到与目录 "${title}" 匹配的记录。`);
        }
    }

    // 拦截 XMLHttpRequest 请求
    function interceptNetworkRequests() {
        const originalOpen = XMLHttpRequest.prototype.open; // 保存原始 XMLHttpRequest.open

        XMLHttpRequest.prototype.open = function (method, url, ...rest) {
            if (url.includes('mbox/group/listshare')) {
                console.log("准备拦截 XMLHttpRequest 请求:", url);
                this.addEventListener('load', function () {
                    try {
                        const data = this.responseType === 'json' ? this.response : JSON.parse(this.responseText);
                        console.debug("完整的响应数据:", data); // 调试输出完整数据
                        processLibraryData(data);
                    } catch (e) {
                        console.error("解析响应失败:", e);
                    }
                });
            }

            // 拦截进入目录的请求
            if (url.includes('mbox/msg/shareinfo')) {
                console.log("准备拦截进入目录的 XMLHttpRequest 请求:", url);
                this.addEventListener('load', function () {
                    try {
                        const data = this.responseType === 'json' ? this.response : JSON.parse(this.responseText);
                        console.debug("完整的响应数据:", data); // 调试输出完整数据
                        processDirectoryData(data);
                    } catch (e) {
                        console.error("解析响应失败:", e);
                    }
                });
            }

            return originalOpen.apply(this, [method, url, ...rest]);
        };
    }

    // 处理文件库数据:取需要的信息并存储
    function processLibraryData(data) {
        debugLog('收到文件库数据:', data);
        if (!data || data.errno !== 0) {
            console.error("文件库数据获取失败,错误码:", data?.errno);
            return;
        }

        directories = [];
        const msgList = data.records?.msg_list || [];
        debugLog('解析到的消息列表:', msgList);

        msgList.forEach((msg, index) => {
            const group_id = msg.group_id;
            const uk = msg.uk;
            debugLog(`处理第 ${index + 1} 条消息:`, { group_id, uk });

            msg.file_list.forEach(file => {
                if (parseInt(file.isdir) === 1) {
                    directories.push({
                        fs_id: file.fs_id,
                        server_filename: file.server_filename,
                        group_id: group_id,
                        msg_id: msg.msg_id,
                        uk: uk
                    });
                    debugLog('添加目录:', file.server_filename);
                }
            });
        });

        debugLog('当前所有目录:', directories);
    }

    // 处理目录数据:提取需要的信息并存储
    function processDirectoryData(data) {
        if (!data || data.errno !== 0) {
            console.error("目录数据获取失败,错误码:", data?.errno);
            return;
        }

        const records = data.records || [];

        records.forEach(record => {
            // 保存所有目录信息,包括子目录
            if (parseInt(record.isdir) === 1) {
                // 处理路径,移除"我的资源"前缀
                let processedPath = record.path;
                if (processedPath.startsWith('/我的资源/')) {
                    processedPath = processedPath.substring('/我的资源'.length);
                }

                // 从处理后的路径中提取各级目录
                const pathParts = processedPath.split('/').filter(p => p);
                const rootName = pathParts[0];

                // 查找根目录信息
                const rootDir = directories.find(d => d.server_filename === rootName);

                if (rootDir) {
                    // 检查是否已存在相同的记录
                    const existingRecord = directories.find(d => d.fs_id === record.fs_id);
                    if (!existingRecord) {
                        // 构建完整的目录信息
                        const dirInfo = {
                            fs_id: record.fs_id,
                            server_filename: record.server_filename,
                            path: processedPath,
                            group_id: rootDir.group_id,
                            msg_id: rootDir.msg_id,
                            uk: rootDir.uk,
                            parent_path: pathParts.slice(0, -1).join('/'),
                            level: pathParts.length - 1  // 添加层级信息
                        };

                        // 添加到目录列表
                        directories.push(dirInfo);
                    }
                } else {
                    // 如果是根目录级别的分享,直接添加
                    if (pathParts.length === 1) {
                        const dirInfo = {
                            fs_id: record.fs_id,
                            server_filename: record.server_filename,
                            path: processedPath,
                            group_id: record.group_id,
                            msg_id: record.msg_id,
                            uk: record.uk,
                            level: 0
                        };
                        directories.push(dirInfo);
                    }
                }
            }
        });

        // 按层级排序,方便调试查看
        directories.sort((a, b) => (a.level || 0) - (b.level || 0));
    }

    // 获取子目录信息
    async function fetchSubdirectories(uk, msgId, fsId, gid, title, depth) {
        debugLog('开始获取子目录', {
            uk, msgId, fsId, gid, title, depth
        });

        const startTime = performance.now();
        const progressBar = createProgressBar();
        progressBar.show();

        let result = {
            name: title,
            children: [],
            level: 0,
            isRoot: true,
            startTime: startTime
        };

        let totalDirectories = 0;
        let processedDirectories = 0;

        async function fetchDirContent(parentDir, currentDepth) {
            if (currentDepth >= depth) return;

            debugLog(`获取目录内容:${parentDir.name},当前深度:${currentDepth}`);

            let page = 1;
            let hasMore = true;
            const allRecords = [];

            while (hasMore) {
                progressBar.updateText(`正在获取 "${parentDir.name}" 的第 ${page} 页数据...`);
                debugLog(`[${parentDir.name}] 获取第 ${page} 页`);

                const url = `https://pan.baidu.com/mbox/msg/shareinfo?from_uk=${encodeURIComponent(uk)}&msg_id=${encodeURIComponent(msgId)}&type=2&num=100&page=${page}&fs_id=${encodeURIComponent(parentDir.fs_id || fsId)}&gid=${encodeURIComponent(gid)}&limit=100&desc=1&clienttype=0&app_id=250528&web=1`;

                try {
                    const response = await fetch(url, { timeout: 10000 });
                    const data = await response.json();
                    debugLog(`[${parentDir.name}] 第 ${page} 页响应:`, data);

                    if (data.errno !== 0) {
                        console.error(`[${parentDir.name}] 获取第 ${page} 页失败:`, data);
                        return;
                    }

                    allRecords.push(...data.records);
                    hasMore = data.has_more === 1;

                    debugLog(`[${parentDir.name}] 第 ${page} 页获取成功,记录数:${data.records.length},是否还有更多:${hasMore}`);
                    page++;

                    await new Promise(resolve => setTimeout(resolve, 1000));
                } catch (error) {
                    console.error(`[${parentDir.name}] 获取第 ${page} 页时发生错误:`, error);
                    return;
                }
            }

            // 分离目录和文件
            const directories = allRecords.filter(record => parseInt(record.isdir) === 1);
            const files = allRecords.filter(record => parseInt(record.isdir) === 0);
            totalDirectories += directories.length;

            debugLog(`[${parentDir.name}] 内容获取完成,总页数:${page - 1},总记录数:${allRecords.length},目录数:${directories.length},文件数:${files.length}`);

            // 先添加文件到父目录
            files.forEach(record => {
                const childFile = {
                    name: record.server_filename,
                    fs_id: record.fs_id,
                    size: record.size || 0,
                    children: [],
                    level: currentDepth + 1,
                    parentLevel: currentDepth,
                    isDir: false
                };
                debugLog(`添加文件:${childFile.name},大小:${childFile.size}`);
                parentDir.children.push(childFile);
            });

            // 然后处理目录
            const promises = directories.map(async record => {
                const childDir = {
                    name: record.server_filename,
                    fs_id: record.fs_id,
                    children: [],
                    level: currentDepth + 1,
                    parentLevel: currentDepth,
                    isDir: true
                };
                debugLog(`创建子目录:${childDir.name},层级:${childDir.level}`);
                parentDir.children.push(childDir);

                if (currentDepth + 1 < depth) {
                    await fetchDirContent(childDir, currentDepth + 1);
                }

                processedDirectories++;
                progressBar.updateProgress(processedDirectories, totalDirectories);
            });

            await Promise.all(promises);
        }

        try {
            await fetchDirContent(result, 0);
            debugLog('目录获取完成', result);
            progressBar.updateText('目录获取完成!');
            setTimeout(() => progressBar.hide(), 2000);

            return {
                tree: result,
                startTime: startTime
            };
        } finally {
            progressBar.remove();
            result = null;
            cleanup();
        }
    }

    // 添加清理文件名的函数
    function cleanFileName(name) {
        // 移除零宽空格和其他不可见字符
        return name.replace(/[\u200b\u200c\u200d\u200e\u200f\ufeff]/g, '');
    }

    // 格式化目录树
    function formatDirItem(node, prefix = '', isLastArray = []) {
        if (node.isRoot) {
            result += `${cleanFileName(node.name)}/\n`;
            if (node.children && node.children.length > 0) {
                node.children.forEach((child, index) => {
                    const isLast = index === node.children.length - 1;
                    formatDirItem(child, '', [isLast]);
                });
            }
        } else {
            const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
            const cleanName = cleanFileName(node.name);
            result += `${prefix}${connector}${cleanName}\n`;

            if (node.children && node.children.length > 0) {
                node.children.forEach((child, index) => {
                    const isLast = index === node.children.length - 1;
                    const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
                    formatDirItem(child, newPrefix, [...isLastArray, isLast]);
                });
            }
        }
    }

    function formatDirectoryTree(dir) {
        const formatStartTime = performance.now();
        const SYMBOLS = {
            space:  '    ',
            branch: '│   ',
            tee:    '├──',
            last:   '└──'
        };

        let result = '';
        const currentTime = new Date().toLocaleString();

        // 添加标题和信息头
        result += `目录结构导出清单\n`;
        result += `导出时间:${currentTime}\n`;
        result += `根目录:${dir.name}\n`;
        result += `${'='.repeat(50)}\n\n`;

        // 内部函数,用于格式化目录
        function formatDir(node, prefix = '', isLastArray = []) {
            if (node.isRoot) {
                result += `${cleanFileName(node.name)}\n`;
                if (node.children && node.children.length > 0) {
                    node.children.forEach((child, index) => {
                        const isLast = index === node.children.length - 1;
                        formatDir(child, '', [isLast]);
                    });
                }
            } else {
                const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
                const itemName = node.isDir ? `${cleanFileName(node.name)}/` : cleanFileName(node.name);
                const sizeInfo = !node.isDir && node.size ? ` (${formatSize(node.size)})` : '';
                result += `${prefix}${connector}${itemName}${sizeInfo}\n`;

                if (node.children && node.children.length > 0) {
                    node.children.forEach((child, index) => {
                        const isLast = index === node.children.length - 1;
                        const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
                        formatDir(child, newPrefix, [...isLastArray, isLast]);
                    });
                }
            }
        }

        formatDir(dir, '', []);

        const endTime = performance.now();
        const formatTime = ((endTime - formatStartTime) / 1000).toFixed(2); // 格式化耗时
        const totalTime = ((endTime - (dir.startTime || formatStartTime)) / 1000).toFixed(2); // 总耗时

        // 统计文件和目录数量
        let fileCount = 0;
        let dirCount = 0;
        let totalSize = 0;

        function countItems(node) {
            if (!node.isRoot) {
                if (node.isDir) {
                    dirCount++;
                } else {
                    fileCount++;
                    totalSize += node.size || 0;
                }
            }
            if (node.children && node.children.length > 0) {
                node.children.forEach(countItems);
            }
        }

        countItems(dir);

        // 添加页脚和统计信息
        result += `\n${'='.repeat(50)}\n`;
        result += `统计信息:\n`;
        result += `目录数量:${dirCount} 个\n`;
        result += `文件数量:${fileCount} 个\n`;
        if (fileCount > 0) {
            result += `文件大小:${formatSize(totalSize)}\n`;
        }
        result += `总项目数:${dirCount + fileCount} 个\n`;
        result += `格式化耗时:${formatTime} 秒\n`;
        if (dir.startTime) {
            result += `总处理耗时:${totalTime} 秒\n`;
        }

        return result;
    }

    // 格式化多个目录树
    function formatMultipleDirectoryTrees(dirResults) {
        const formatStartTime = performance.now();
        const SYMBOLS = {
            space:  '    ',
            branch: '│   ',
            tee:    '├──',
            last:   '└──'
        };

        let result = '';
        const currentTime = new Date().toLocaleString();

        // 添加标题和信息头
        result += `多目录结构导出清单\n`;
        result += `导出时间:${currentTime}\n`;
        result += `目录数量:${dirResults.length} 个\n`;
        result += `${'='.repeat(50)}\n\n`;

        let totalDirCount = 0;
        let totalProcessingTime = 0;

        // 逐个处理每个目录
        dirResults.forEach((dirResult, index) => {
            const { title, result: dirData } = dirResult;
            const dir = dirData.tree;

            // 添加目录标题
            result += `${index + 1}. ${title}\n`;
            result += `${'-'.repeat(50)}\n`;

            // 内部函数,用于格式化目录
            function formatDir(node, prefix = '', isLastArray = []) {
                if (node.isRoot) {
                    if (node.children && node.children.length > 0) {
                        node.children.forEach((child, index) => {
                            const isLast = index === node.children.length - 1;
                            formatDir(child, '', [isLast]);
                        });
                    } else {
                        result += `(空目录)\n`;
                    }
                } else {
                    const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
                    const itemName = node.isDir ? `${cleanFileName(node.name)}/` : cleanFileName(node.name);
                    const sizeInfo = !node.isDir && node.size ? ` (${formatSize(node.size)})` : '';
                    result += `${prefix}${connector}${itemName}${sizeInfo}\n`;

                    if (node.children && node.children.length > 0) {
                        node.children.forEach((child, index) => {
                            const isLast = index === node.children.length - 1;
                            const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
                            formatDir(child, newPrefix, [...isLastArray, isLast]);
                        });
                    }
                }
            }

            formatDir(dir, '', []);

            // 添加当前目录的统计信息
            let dirCount = 0;
            let fileCount = 0;

            function countItems(node) {
                if (!node.isRoot) {
                    if (node.isDir) {
                        dirCount++;
                    } else {
                        fileCount++;
                    }
                }
                if (node.children && node.children.length > 0) {
                    node.children.forEach(countItems);
                }
            }

            countItems(dir);
            totalDirCount += dirCount;

            if (dir.startTime) {
                const processingTime = (performance.now() - dir.startTime) / 1000;
                totalProcessingTime += processingTime;
            }

            result += `\n`;

            // 如果不是最后一个目录,添加分隔线
            if (index < dirResults.length - 1) {
                result += `${'-'.repeat(50)}\n\n`;
            }
        });

        const endTime = performance.now();
        const formatTime = ((endTime - formatStartTime) / 1000).toFixed(2);

        // 添加总统计信息
        result += `\n${'='.repeat(50)}\n`;
        result += `总统计信息:\n`;
        result += `处理目录数:${dirResults.length} 个\n`;
        result += `总子目录数:${totalDirCount} 个\n`;
        result += `格式化耗时:${formatTime} 秒\n`;
        result += `总处理耗时:${(totalProcessingTime + parseFloat(formatTime)).toFixed(2)} 秒\n`;

        return result;
    }

    // 添加统计目录数量的辅助函数
    function countDirectories(dir) {
        let count = 0;

        function traverse(node) {
            if (!node.isRoot && node.isDir) {
                count++;
            }
            if (node.children && node.children.length > 0) {
                node.children.forEach(traverse);
            }
        }

        traverse(dir);
        return count;
    }

    // 保存为 TXT 文件
    function saveAsTxt(content, title) {
        const blob = new Blob([content], { type: 'text/plain' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = `${title}.txt`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        console.log(`已保存文件: ${title}.txt`);
    }

    // 添加获取全部内容的函数
    async function fetchAllContent(uk, msgId, fsId, gid, title, depth) {
        const startTime = performance.now();
        const progressBar = createProgressBar();
        progressBar.show();

        let result = {
            name: title,
            children: [],
            level: 0,
            isRoot: true,
            startTime: startTime
        };

        let totalItems = 0;
        let processedItems = 0;

        async function fetchContent(parentDir, currentDepth) {
            if (currentDepth >= depth) return;

            let page = 1;
            let hasMore = true;
            const allRecords = [];
            const maxRetries = 3;
            const requestPool = new RequestPool(2, 3000);

            while (hasMore) {
                progressBar.updateText(`正在获取 "${parentDir.name}" 的第 ${page} 页数据...`);
                console.log(`[${parentDir.name}] 正在获取第 ${page} 页数据...`);

                const url = `https://pan.baidu.com/mbox/msg/shareinfo?from_uk=${encodeURIComponent(uk)}&msg_id=${encodeURIComponent(msgId)}&type=2&num=100&page=${page}&fs_id=${encodeURIComponent(parentDir.fs_id || fsId)}&gid=${encodeURIComponent(gid)}&limit=100&desc=1&clienttype=0&app_id=250528&web=1`;

                let retryCount = 0;
                let success = false;

                while (retryCount < maxRetries && !success) {
                    try {
                        const data = await requestPool.add(async () => {
                            const response = await fetch(url, {
                                timeout: 30000,
                                headers: {
                                    'Cache-Control': 'no-cache',
                                    'Pragma': 'no-cache'
                                }
                            });
                            if (!response.ok) {
                                throw new Error(`HTTP error! status: ${response.status}`);
                            }
                            return response.json();
                        });

                        if (data.errno !== 0) {
                            throw new Error(`API error: ${data.errno}`);
                        }

                        allRecords.push(...data.records);
                        hasMore = data.has_more === 1;
                        success = true;

                        console.log(`[${parentDir.name}] 第 ${page} 页获取成功,本页记录数: ${data.records.length},hasMore: ${hasMore}`);
                    } catch (error) {
                        retryCount++;
                        console.error(`[${parentDir.name}] 页面 ${page} 获取失败 (${retryCount}/${maxRetries})`);

                        if (retryCount < maxRetries) {
                            const delay = Math.min(1000 * Math.pow(2, retryCount), 10000); // 指数退避策略
                            progressBar.updateText(`请求失败,${delay/1000}秒后重试...`);
                            await new Promise(resolve => setTimeout(resolve, delay));
                        } else {
                            progressBar.updateText(`获取 "${parentDir.name}" 第 ${page} 页失败,跳过...`);
                            console.error(`[${parentDir.name}] 达到重试上限,跳过`);
                            hasMore = false;
                        }
                    }
                }

                if (success) {
                    page++;
                    // 成功后也适当延迟,避免请求过快
                    await new Promise(resolve => setTimeout(resolve, 2000));
                }
            }

            // 处理所有记录(包括文件和目录)
            totalItems += allRecords.length;

            const promises = allRecords.map(async record => {
                const childItem = {
                    name: record.server_filename,
                    fs_id: record.fs_id,
                    isDir: parseInt(record.isdir) === 1,
                    size: record.size,
                    children: [],
                    level: currentDepth + 1,
                    parentLevel: currentDepth
                };
                parentDir.children.push(childItem);

                if (childItem.isDir && currentDepth + 1 < depth) {
                    await fetchContent(childItem, currentDepth + 1);
                }

                processedItems++;
                progressBar.updateProgress(processedItems, totalItems);
            });

            await Promise.all(promises);
        }

        try {
            await fetchContent(result, 0);
            progressBar.updateText('内容获取完成!');
            setTimeout(() => progressBar.hide(), 2000);

            return {
                tree: result,
                startTime: startTime
            };
        } finally {
            progressBar.remove();
            result = null;
            cleanup();
        }
    }

    function formatAllContent(dir) {
        const formatStartTime = performance.now();
        let result = '';
        const currentTime = new Date().toLocaleString();

        const SYMBOLS = {
            space:  '    ',
            branch: '│   ',
            tee:    '├──',
            last:   '└──'
        };

        result += `完整目录结构导出清单\n`;
        result += `导出时间:${currentTime}\n`;
        result += `根目录:${dir.name}\n`;
        result += `${'='.repeat(50)}\n\n`;

        let fileCount = 0;
        let dirCount = 0;
        let totalSize = 0;

        function formatItem(node, prefix = '', isLastArray = []) {
            if (node.isRoot) {
                result += `${cleanFileName(node.name)}/\n`;
                if (node.children && node.children.length > 0) {
                    node.children.forEach((child, index) => {
                        const isLast = index === node.children.length - 1;
                        formatItem(child, '', [isLast]);
                    });
                }
            } else {
                const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
                const cleanName = cleanFileName(node.name);
                const itemName = node.isDir ? `${cleanName}/` : cleanName;
                const size = !node.isDir ? ` (${formatSize(node.size)})` : '';

                result += `${prefix}${connector}${itemName}${size}\n`;

                if (node.isDir) {
                    dirCount++;
                } else {
                    fileCount++;
                    totalSize += node.size || 0;
                }

                if (node.children && node.children.length > 0) {
                    node.children.forEach((child, index) => {
                        const isLast = index === node.children.length - 1;
                        const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
                        formatItem(child, newPrefix, [...isLastArray, isLast]);
                    });
                }
            }
        }

        formatItem(dir, '', []);

        const endTime = performance.now();
        const formatTime = ((endTime - formatStartTime) / 1000).toFixed(2);
        const totalTime = ((endTime - dir.startTime) / 1000).toFixed(2);

        result += `\n${'='.repeat(50)}\n`;
        result += `统计信息:\n`;
        result += `目录数量:${dirCount}\n`;
        result += `文件数量:${fileCount}\n`;
        result += `文件大小:${formatSize(totalSize)}\n`;
        result += `处理总计:${dirCount + fileCount} 个项目\n`;
        result += `格式化耗时:${formatTime} 秒\n`;
        result += `总处理耗时:${totalTime} 秒\n`;

        return result;
    }

    // 格式化多个目录的所有内容
    function formatMultipleAllContent(dirResults) {
        const formatStartTime = performance.now();
        let result = '';
        const currentTime = new Date().toLocaleString();

        const SYMBOLS = {
            space:  '    ',
            branch: '│   ',
            tee:    '├──',
            last:   '└──'
        };

        result += `多目录完整结构导出清单\n`;
        result += `导出时间:${currentTime}\n`;
        result += `目录数量:${dirResults.length} 个\n`;
        result += `${'='.repeat(50)}\n\n`;

        let totalFileCount = 0;
        let totalDirCount = 0;
        let totalSize = 0;
        let totalProcessingTime = 0;

        // 逐个处理每个目录
        dirResults.forEach((dirResult, index) => {
            const { title, result: dirData } = dirResult;
            const dir = dirData.tree;

            // 添加目录标题
            result += `${index + 1}. ${title}\n`;
            result += `${'-'.repeat(50)}\n`;

            let fileCount = 0;
            let dirCount = 0;
            let dirSize = 0;

            function formatItem(node, prefix = '', isLastArray = []) {
                if (node.isRoot) {
                    if (node.children && node.children.length > 0) {
                        node.children.forEach((child, index) => {
                            const isLast = index === node.children.length - 1;
                            formatItem(child, '', [isLast]);
                        });
                    } else {
                        result += `(空目录)\n`;
                    }
                } else {
                    const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
                    const cleanName = cleanFileName(node.name);
                    const itemName = node.isDir ? `${cleanName}/` : cleanName;
                    const size = !node.isDir ? ` (${formatSize(node.size)})` : '';

                    result += `${prefix}${connector}${itemName}${size}\n`;

                    if (node.isDir) {
                        dirCount++;
                    } else {
                        fileCount++;
                        dirSize += node.size || 0;
                    }

                    if (node.children && node.children.length > 0) {
                        node.children.forEach((child, index) => {
                            const isLast = index === node.children.length - 1;
                            const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
                            formatItem(child, newPrefix, [...isLastArray, isLast]);
                        });
                    }
                }
            }

            formatItem(dir, '', []);

            // 添加当前目录的统计信息
            result += `\n当前目录统计: 目录 ${dirCount} 个, 文件 ${fileCount} 个, 总大小 ${formatSize(dirSize)}\n`;

            totalDirCount += dirCount;
            totalFileCount += fileCount;
            totalSize += dirSize;

            if (dir.startTime) {
                const processingTime = (performance.now() - dir.startTime) / 1000;
                totalProcessingTime += processingTime;
            }

            // 如果不是最后一个目录,添加分隔线
            if (index < dirResults.length - 1) {
                result += `\n${'-'.repeat(50)}\n\n`;
            }
        });

        const endTime = performance.now();
        const formatTime = ((endTime - formatStartTime) / 1000).toFixed(2);

        // 添加总统计信息
        result += `\n${'='.repeat(50)}\n`;
        result += `总统计信息:\n`;
        result += `处理目录数:${dirResults.length} 个\n`;
        result += `总目录数量:${totalDirCount} 个\n`;
        result += `总文件数量:${totalFileCount} 个\n`;
        result += `总文件大小:${formatSize(totalSize)}\n`;
        result += `总项目数量:${totalDirCount + totalFileCount} 个\n`;
        result += `格式化耗时:${formatTime} 秒\n`;
        result += `总处理耗时:${(totalProcessingTime + parseFloat(formatTime)).toFixed(2)} 秒\n`;

        return result;
    }

    function formatSize(bytes) {
        if (bytes === 0) return '0 B';
        const k = 1024;
        const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }

    function cleanup() {
        const progressBar = document.getElementById('directory-progress');
        if (progressBar && progressBar.parentNode) {
            progressBar.parentNode.removeChild(progressBar);
        }
    }

    function saveAsExcel(data, title) {
        const wb = XLSX.utils.book_new();

        const excelData = [];

        excelData.push(['目录结构导出清单']);
        excelData.push([`导出时间: ${new Date().toLocaleString()}`]);

        let fileCount = 0;
        let dirCount = 0;
        let totalSize = 0;

        function countItems(node) {
            if (!node.isRoot) {
                if (node.isDir !== false) { // 目录或者没有isDir属性的节点(兼容旧数据)
                    dirCount++;
                } else {
                    fileCount++;
                    totalSize += node.size || 0;
                }
            }
            if (node.children && node.children.length > 0) {
                node.children.forEach(countItems);
            }
        }

        countItems(data.tree);

        excelData.push(['统计信息']);
        excelData.push([`目录数量: ${dirCount}`]);
        if (fileCount > 0) {
            excelData.push([`文件数量: ${fileCount}`]);
            excelData.push([`文件大小: ${formatSize(totalSize)}`]);
            excelData.push([`处理总计: ${dirCount + fileCount} 个项目`]);
        }
        excelData.push([`格式化耗时: ${((performance.now() - data.startTime) / 1000).toFixed(2)} 秒`]);
        excelData.push(['']);

        function getMaxDepth(node, currentDepth = 0) {
            if (!node.children || node.children.length === 0) {
                return currentDepth;
            }
            return Math.max(...node.children.map(child =>
                getMaxDepth(child, currentDepth + 1)
            ));
        }

        const actualDepth = Math.min(depthSetting, getMaxDepth(data.tree) + 1);

        // 改为显示项目名称、类型和大小的表格格式
        excelData.push(['项目名称', '类型', '大小', '路径']);

        const allRows = [];

        function extractNumber(str) {
            const match = str.match(/^(\d+)\./);
            return match ? parseInt(match[1]) : Infinity;
        }

        function compareItems(a, b) {
            const numA = extractNumber(a.name);
            const numB = extractNumber(b.name);

            if (numA !== numB) {
                return numA - numB;
            }

            return a.name.localeCompare(b.name, 'zh-CN');
        }

        function processNode(node, path = '') {
            if (!node.isRoot) {
                const currentPath = path ? `${path}/${node.name}` : node.name;
                const itemType = node.isDir !== false ? '目录' : '文件';
                const itemSize = node.isDir !== false ? '' : formatSize(node.size || 0);

                allRows.push([node.name, itemType, itemSize, currentPath]);

                if (node.children && node.children.length > 0) {
                    const sortedChildren = [...node.children].sort(compareItems);
                    sortedChildren.forEach(child => {
                        processNode(child, currentPath);
                    });
                }
            } else {
                if (node.children && node.children.length > 0) {
                    const sortedChildren = [...node.children].sort(compareItems);
                    sortedChildren.forEach(child => {
                        processNode(child, '');
                    });
                }
            }
        }

        processNode(data.tree);

        excelData.push(...allRows);

        const ws = XLSX.utils.aoa_to_sheet(excelData);

        // 设置列宽:项目名称、类型、大小、路径
        ws['!cols'] = [
            { wch: 40 },  // 项目名称
            { wch: 10 },  // 类型
            { wch: 15 },  // 大小
            { wch: 60 }   // 路径
        ];

        ws['!merges'] = [
            { s: { r: 0, c: 0 }, e: { r: 0, c: 3 } },  // 标题行
            { s: { r: 1, c: 0 }, e: { r: 1, c: 3 } },  // 时间行
            { s: { r: 2, c: 0 }, e: { r: 2, c: 3 } },  // 统计信息标题
            { s: { r: 3, c: 0 }, e: { r: 3, c: 3 } },  // 目录数量
        ];

        if (fileCount > 0) {
            ws['!merges'].push(
                { s: { r: 4, c: 0 }, e: { r: 4, c: 3 } },  // 文件数量
                { s: { r: 5, c: 0 }, e: { r: 5, c: 3 } },  // 文件大小
                { s: { r: 6, c: 0 }, e: { r: 6, c: 3 } },  // 处理总计
                { s: { r: 7, c: 0 }, e: { r: 7, c: 3 } },  // 格式化耗时
                { s: { r: 8, c: 0 }, e: { r: 8, c: 3 } }   // 空行
            );
        } else {
            ws['!merges'].push(
                { s: { r: 4, c: 0 }, e: { r: 4, c: 3 } },  // 格式化耗时
                { s: { r: 5, c: 0 }, e: { r: 5, c: 3 } }   // 空行
            );
        }

        XLSX.utils.book_append_sheet(wb, ws, '目录结构');

        XLSX.writeFile(wb, `${title}.xlsx`);
    }

    // 将多个目录导出为Excel
    function saveMultipleAsExcel(dirResults, title) {
        const wb = XLSX.utils.book_new();
        const startTime = performance.now();

        // 创建汇总表格
        const summaryData = [];
        const currentTime = new Date().toLocaleString();

        summaryData.push(['多目录结构导出清单']);
        summaryData.push([`导出时间: ${currentTime}`]);
        summaryData.push([`目录数量: ${dirResults.length} 个`]);
        summaryData.push(['']);

        // 添加目录汇总表头
        summaryData.push(['序号', '目录名称', '子目录数量', '处理耗时(秒)']);

        let totalDirCount = 0;
        let totalProcessingTime = 0;

        // 逐个处理每个目录,并添加到汇总表
        dirResults.forEach((dirResult, index) => {
            const { title, result: dirData } = dirResult;
            const dir = dirData.tree;

            // 统计子目录数量
            const dirCount = countDirectories(dir);
            totalDirCount += dirCount;

            // 计算处理时间
            let processingTime = 0;
            if (dir.startTime) {
                processingTime = (performance.now() - dir.startTime) / 1000;
                totalProcessingTime += processingTime;
            }

            // 添加到汇总表
            summaryData.push([index + 1, title, dirCount, processingTime.toFixed(2)]);

            // 为每个目录创建一个单独的工作表
            createDirectoryWorksheet(wb, dir, title);
        });

        // 添加总统计信息
        summaryData.push(['']);
        summaryData.push(['统计信息']);
        summaryData.push([`总目录数: ${dirResults.length} 个`]);
        summaryData.push([`总子目录数: ${totalDirCount} 个`]);

        const endTime = performance.now();
        const formatTime = ((endTime - startTime) / 1000).toFixed(2);

        summaryData.push([`格式化耗时: ${formatTime} 秒`]);
        summaryData.push([`总处理耗时: ${(totalProcessingTime + parseFloat(formatTime)).toFixed(2)} 秒`]);

        // 创建汇总工作表
        const ws = XLSX.utils.aoa_to_sheet(summaryData);

        // 设置列宽
        ws['!cols'] = [
            { wch: 8 },   // 序号
            { wch: 40 },  // 目录名称
            { wch: 15 },  // 子目录数量
            { wch: 15 }   // 处理耗时
        ];

        // 设置合并单元格
        ws['!merges'] = [
            { s: { r: 0, c: 0 }, e: { r: 0, c: 3 } },  // 标题行
            { s: { r: 1, c: 0 }, e: { r: 1, c: 3 } },  // 时间行
            { s: { r: 2, c: 0 }, e: { r: 2, c: 3 } },  // 目录数量行
            { s: { r: 3, c: 0 }, e: { r: 3, c: 3 } },  // 空行
            { s: { r: summaryData.length - 6, c: 0 }, e: { r: summaryData.length - 6, c: 3 } }, // 空行
            { s: { r: summaryData.length - 5, c: 0 }, e: { r: summaryData.length - 5, c: 3 } }, // 统计信息标题
            { s: { r: summaryData.length - 4, c: 0 }, e: { r: summaryData.length - 4, c: 3 } }, // 总目录数
            { s: { r: summaryData.length - 3, c: 0 }, e: { r: summaryData.length - 3, c: 3 } }, // 总子目录数
            { s: { r: summaryData.length - 2, c: 0 }, e: { r: summaryData.length - 2, c: 3 } }, // 格式化耗时
            { s: { r: summaryData.length - 1, c: 0 }, e: { r: summaryData.length - 1, c: 3 } }  // 总处理耗时
        ];

        // 将汇总工作表添加到工作簿
        XLSX.utils.book_append_sheet(wb, ws, '目录汇总');

        // 导出Excel文件
        XLSX.writeFile(wb, `${title}.xlsx`);

        // 创建单个目录的工作表的内部函数
        function createDirectoryWorksheet(wb, dir, sheetTitle) {
            const excelData = [];

            excelData.push([`目录: ${dir.name}`]);

            let dirCount = 0;

            function countItems(node) {
                if (!node.isRoot && node.isDir) {
                    dirCount++;
                }
                if (node.children && node.children.length > 0) {
                    node.children.forEach(countItems);
                }
            }

            countItems(dir);

            excelData.push([`子目录数量: ${dirCount}`]);
            excelData.push(['']);

            function getMaxDepth(node, currentDepth = 0) {
                if (!node.children || node.children.length === 0) {
                    return currentDepth;
                }
                return Math.max(...node.children.map(child =>
                    getMaxDepth(child, currentDepth + 1)
                ));
            }

            const actualDepth = Math.min(depthSetting, getMaxDepth(dir) + 1);

            const headers = [];
            for (let i = 1; i <= actualDepth; i++) {
                headers.push(`${i}级目录`);
            }
            excelData.push(headers);

            const allRows = [];

            function processNode(node, level = 0, parentRow = []) {
                if (level >= actualDepth) return;

                const currentRow = [...parentRow];

                if (!node.isRoot) {
                    currentRow[level] = node.name;
                    allRows.push([...currentRow]);
                }

                if (node.children && node.children.length > 0) {
                    if (node.isRoot) {
                        node.children.sort(compareItems);
                        node.children.forEach(child => {
                            const newRow = new Array(actualDepth).fill('');
                            newRow[0] = child.name;
                            allRows.push([...newRow]);

                            if (child.children && child.children.length > 0) {
                                child.children.sort(compareItems);
                                child.children.forEach(grandChild => {
                                    processNode(grandChild, 1, newRow);
                                });
                            }
                        });
                    } else {
                        node.children.sort(compareItems);
                        node.children.forEach(child => {
                            processNode(child, level + 1, currentRow);
                        });
                    }
                }
            }

            processNode(dir, 0, new Array(actualDepth).fill(''));

            excelData.push(...allRows);

            const ws = XLSX.utils.aoa_to_sheet(excelData);

            const colWidths = [];
            for (let i = 0; i < actualDepth; i++) {
                colWidths.push({ wch: 45 });
            }
            ws['!cols'] = colWidths;

            ws['!merges'] = [
                { s: { r: 0, c: 0 }, e: { r: 0, c: actualDepth - 1 } },  // 目录名称
                { s: { r: 1, c: 0 }, e: { r: 1, c: actualDepth - 1 } },  // 子目录数量
                { s: { r: 2, c: 0 }, e: { r: 2, c: actualDepth - 1 } }   // 空行
            ];

            // 工作表名称不能超过31个字符,如果超过需要截断
            let safeSheetName = sheetTitle;
            if (safeSheetName.length > 28) {
                safeSheetName = safeSheetName.substring(0, 25) + '...';
            }

            // 添加工作表到工作簿
            XLSX.utils.book_append_sheet(wb, ws, safeSheetName);
        }
    }

    // 将多个目录的所有内容导出为Excel
    function saveMultipleAllAsExcel(dirResults, title) {
        const wb = XLSX.utils.book_new();
        const startTime = performance.now();

        // 创建汇总表格
        const summaryData = [];
        const currentTime = new Date().toLocaleString();

        summaryData.push(['多目录完整结构导出清单']);
        summaryData.push([`导出时间: ${currentTime}`]);
        summaryData.push([`目录数量: ${dirResults.length} 个`]);
        summaryData.push(['']);

        // 添加目录汇总表头
        summaryData.push(['序号', '目录名称', '子目录数量', '文件数量', '总大小', '处理耗时(秒)']);

        let totalDirCount = 0;
        let totalFileCount = 0;
        let totalSize = 0;
        let totalProcessingTime = 0;

        // 逐个处理每个目录,并添加到汇总表
        dirResults.forEach((dirResult, index) => {
            const { title, result: dirData } = dirResult;
            const dir = dirData.tree;

            // 统计目录和文件数量
            let dirCount = 0;
            let fileCount = 0;
            let dirSize = 0;

            function countItems(node) {
                if (!node.isRoot) {
                    if (node.isDir) {
                        dirCount++;
                    } else {
                        fileCount++;
                        dirSize += node.size || 0;
                    }
                }
                if (node.children && node.children.length > 0) {
                    node.children.forEach(countItems);
                }
            }

            countItems(dir);

            totalDirCount += dirCount;
            totalFileCount += fileCount;
            totalSize += dirSize;

            // 计算处理时间
            let processingTime = 0;
            if (dir.startTime) {
                processingTime = (performance.now() - dir.startTime) / 1000;
                totalProcessingTime += processingTime;
            }

            // 添加到汇总表
            summaryData.push([
                index + 1,
                title,
                dirCount,
                fileCount,
                formatSize(dirSize),
                processingTime.toFixed(2)
            ]);

            // 为每个目录创建一个单独的工作表
            createAllContentWorksheet(wb, dir, title);
        });

        // 添加总统计信息
        summaryData.push(['']);
        summaryData.push(['统计信息']);
        summaryData.push([`总目录数: ${dirResults.length} 个`]);
        summaryData.push([`总子目录数: ${totalDirCount} 个`]);
        summaryData.push([`总文件数: ${totalFileCount} 个`]);
        summaryData.push([`总文件大小: ${formatSize(totalSize)}`]);

        const endTime = performance.now();
        const formatTime = ((endTime - startTime) / 1000).toFixed(2);

        summaryData.push([`格式化耗时: ${formatTime} 秒`]);
        summaryData.push([`总处理耗时: ${(totalProcessingTime + parseFloat(formatTime)).toFixed(2)} 秒`]);

        // 创建汇总工作表
        const ws = XLSX.utils.aoa_to_sheet(summaryData);

        // 设置列宽
        ws['!cols'] = [
            { wch: 8 },   // 序号
            { wch: 30 },  // 目录名称
            { wch: 12 },  // 子目录数量
            { wch: 12 },  // 文件数量
            { wch: 15 },  // 总大小
            { wch: 15 }   // 处理耗时
        ];

        // 设置合并单元格
        ws['!merges'] = [
            { s: { r: 0, c: 0 }, e: { r: 0, c: 5 } },  // 标题行
            { s: { r: 1, c: 0 }, e: { r: 1, c: 5 } },  // 时间行
            { s: { r: 2, c: 0 }, e: { r: 2, c: 5 } },  // 目录数量行
            { s: { r: 3, c: 0 }, e: { r: 3, c: 5 } },  // 空行
            { s: { r: summaryData.length - 8, c: 0 }, e: { r: summaryData.length - 8, c: 5 } }, // 空行
            { s: { r: summaryData.length - 7, c: 0 }, e: { r: summaryData.length - 7, c: 5 } }, // 统计信息标题
            { s: { r: summaryData.length - 6, c: 0 }, e: { r: summaryData.length - 6, c: 5 } }, // 总目录数
            { s: { r: summaryData.length - 5, c: 0 }, e: { r: summaryData.length - 5, c: 5 } }, // 总子目录数
            { s: { r: summaryData.length - 4, c: 0 }, e: { r: summaryData.length - 4, c: 5 } }, // 总文件数
            { s: { r: summaryData.length - 3, c: 0 }, e: { r: summaryData.length - 3, c: 5 } }, // 总文件大小
            { s: { r: summaryData.length - 2, c: 0 }, e: { r: summaryData.length - 2, c: 5 } }, // 格式化耗时
            { s: { r: summaryData.length - 1, c: 0 }, e: { r: summaryData.length - 1, c: 5 } }  // 总处理耗时
        ];

        // 将汇总工作表添加到工作簿
        XLSX.utils.book_append_sheet(wb, ws, '目录汇总');

        // 导出Excel文件
        XLSX.writeFile(wb, `${title}.xlsx`);

        // 创建单个目录的完整内容工作表的内部函数
        function createAllContentWorksheet(wb, dir, sheetTitle) {
            const excelData = [];

            excelData.push([`目录: ${dir.name}`]);

            let dirCount = 0;
            let fileCount = 0;
            let totalSize = 0;

            function countItems(node) {
                if (!node.isRoot) {
                    if (node.isDir) {
                        dirCount++;
                    } else {
                        fileCount++;
                        totalSize += node.size || 0;
                    }
                }
                if (node.children && node.children.length > 0) {
                    node.children.forEach(countItems);
                }
            }

            countItems(dir);

            excelData.push([`子目录数量: ${dirCount}`]);
            excelData.push([`文件数量: ${fileCount}`]);
            excelData.push([`总大小: ${formatSize(totalSize)}`]);
            excelData.push(['']);

            // 添加表头
            excelData.push(['项目名称', '类型', '大小']);

            const allRows = [];

            function processNode(node, prefix = '') {
                if (!node.isRoot) {
                    const itemName = prefix + node.name;
                    const itemType = node.isDir ? '目录' : '文件';
                    const itemSize = node.isDir ? '' : formatSize(node.size || 0);
                    allRows.push([itemName, itemType, itemSize]);
                }

                if (node.children && node.children.length > 0) {
                    const newPrefix = node.isRoot ? '' : prefix + '  ';
                    node.children.forEach(child => {
                        processNode(child, newPrefix);
                    });
                }
            }

            processNode(dir);

            excelData.push(...allRows);

            const ws = XLSX.utils.aoa_to_sheet(excelData);

            // 设置列宽
            ws['!cols'] = [
                { wch: 60 },  // 项目名称
                { wch: 10 },  // 类型
                { wch: 15 }   // 大小
            ];

            ws['!merges'] = [
                { s: { r: 0, c: 0 }, e: { r: 0, c: 2 } },  // 目录名称
                { s: { r: 1, c: 0 }, e: { r: 1, c: 2 } },  // 子目录数量
                { s: { r: 2, c: 0 }, e: { r: 2, c: 2 } },  // 文件数量
                { s: { r: 3, c: 0 }, e: { r: 3, c: 2 } },  // 总大小
                { s: { r: 4, c: 0 }, e: { r: 4, c: 2 } }   // 空行
            ];

            // 工作表名称不能超过31个字符,如果超过需要截断
            let safeSheetName = sheetTitle;
            if (safeSheetName.length > 28) {
                safeSheetName = safeSheetName.substring(0, 25) + '...';
            }

            // 添加工作表到工作簿
            XLSX.utils.book_append_sheet(wb, ws, safeSheetName);
        }
    }

    waitForLibraryElements();
})();