QQ邮箱附件管理助手

批量下载QQ邮箱附件,支持筛选、排序和批量操作

// ==UserScript==
// @name         QQ邮箱附件管理助手
// @namespace    https://gf.qytechs.cn/zh-CN/scripts/535160
// @version      1.0
// @description  批量下载QQ邮箱附件,支持筛选、排序和批量操作
// @author       XHXIAIEIN
// @homepage     https://github.com/XHXIAIEIN/Auto-Download-QQMail-Attach/
// @match        https://wx.mail.qq.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_download
// @grant        GM_notification
// @connect      mail.qq.com
// @connect      wx.mail.qq.com
// @license      MIT
// @connect      gzc-dfsdown.mail.ftn.qq.com
// ==/UserScript==

(function () {
    'use strict';

    class StyleManager {
        static getStyles() {
            return {
                base: {
                    panel: `position: fixed; background: var(--bg_white_web, #FFFFFF); border-radius: 8px; box-shadow: var(--shadow_4, 0 8px 12px 0 rgba(19, 24, 29, 0.14)); z-index: 10000; display: flex; flex-direction: column;`,
                    overlay: `position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: var(--mask_gray_030, rgba(0, 0, 0, 0.3)); z-index: 9999; display: flex; align-items: center; justify-content: center;`,
                    toolbar: `display: flex; align-items: center; padding: 16px 20px; border-bottom: 1px solid var(--base_gray_007, rgba(21, 46, 74, 0.07)); background: var(--bg_white_web, #FFFFFF); border-radius: 8px 8px 0 0;`,
                    content: `flex: 1; min-height: 0; overflow: auto; padding: 20px;`
                },
                buttons: {
                    primary: `padding: 8px 16px; background: var(--theme_primary, #0F7AF5); color: var(--base_white_100, #FFFFFF); border: 1px solid var(--theme_darken_2, #0E66CB); border-radius: 4px; cursor: pointer; font-size: 13px; display: flex; align-items: center; gap: 4px; transition: all 0.2s; box-shadow: var(--material_BlueButton_Small);`,
                    secondary: `padding: 8px 16px; background: var(--bg_white_web, #FFFFFF); color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); border-radius: 4px; cursor: pointer; font-size: 13px; display: flex; align-items: center; gap: 4px; transition: all 0.2s;`,
                    icon: `width: 32px; height: 32px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); border-radius: 4px; background: transparent; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s;`
                },
                cards: {
                    attachment: `background: var(--bg_white_web, #FFFFFF); border: 1px solid var(--border_gray_010, rgba(22, 46, 74, 0.05)); border-radius: 8px; overflow: hidden; cursor: pointer; transition: box-shadow 0.2s, border 0.2s; position: relative; aspect-ratio: 1; box-shadow: none;`,
                    mail: `background: var(--bg_white_web, #FFFFFF); border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); border-radius: 8px; margin-bottom: 16px; overflow: hidden; transition: box-shadow 0.2s;`
                },
                controls: {
                    checkbox: `position: absolute; top: 8px; left: 8px; width: 20px; height: 20px; z-index: 10; accent-color: var(--theme_primary, #0F7AF5); cursor: pointer; background: rgba(255,255,255,0.9); border-radius: 4px; box-shadow: 0 1px 4px 0 rgba(21,46,74,0.08); opacity: 0.9; transition: opacity 0.2s, box-shadow 0.2s; border: 1px solid var(--border_gray_020, #e0e6ed);`
                },
                menus: {
                    dropdown: `position: fixed; background: var(--bg_white_web, #FFFFFF); border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.08)); border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); padding: 8px 0; min-width: 160px; z-index: 1001;`,
                    item: `padding: 8px 16px; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--base_gray_100, #13181D); transition: background 0.2s;`
                },
                states: {
                    loading: `position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: var(--mask_white_095, rgba(255, 255, 255, 0.95)); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 1000;`,
                    empty: `text-align: center; padding: 40px 20px; color: var(--base_gray_030, rgba(25, 38, 54, 0.3)); font-size: 14px;`,
                    spinner: `width: 32px; height: 32px; border: 2px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); border-top: 2px solid var(--theme_primary, #0F7AF5); border-radius: 50%; animation: spin 1s linear infinite; margin-bottom: 12px;`
                },
                toasts: {
                    base: `position: fixed; top: 20px; right: 20px; padding: 12px 16px; border-radius: 6px; color: white; font-size: 13px; z-index: 10002; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); display: flex; align-items: center; gap: 8px; max-width: 320px; word-wrap: break-word; opacity: 0; transform: translateX(100%); transition: all 0.3s ease;`,
                    info: `background: var(--theme_primary, #0F7AF5);`,
                    success: `background: var(--chrome_green, #00A755);`,
                    warning: `background: var(--chrome_yellow, #FFA500);`,
                    error: `background: var(--chrome_red, #F73116);`
                },
                media: {
                    preview: `width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); overflow: hidden;`,
                    image: `width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease;`,
                    icon: `font-size: 24px; color: var(--theme_primary, #0F7AF5); display: flex; align-items: center; justify-content: center; height: 100%; transition: transform 0.3s ease;`
                },
                layouts: {
                    grid: `display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 16px; padding: 16px; background: transparent;`,
                    floatingWindow: `position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 75%; max-width: 900px; height: 95%; max-height: 95vh; min-width: 600px; min-height: 700px; background: #fff; border-radius: 12px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15), 0 8px 32px rgba(0, 0, 0, 0.1); z-index: 10000; display: flex; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; overflow: hidden; resize: both;`,
                    floatingOverlay: `position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(4px); z-index: 9999; opacity: 0; transition: opacity 0.3s ease;`,
                    windowHeader: `display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; background: #fff; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: move; user-select: none; flex-shrink: 0;`,
                    windowContent: `flex: 1; overflow: hidden; display: flex; flex-direction: column; background: #f5f5f5;`,
                    windowControls: `display: flex; align-items: center; gap: 8px;`,
                    windowButton: `width: 12px; height: 12px; border-radius: 50%; cursor: pointer; transition: all 0.2s ease;`,
                    closeButton: `background: #ff5f57; border: 1px solid #e0443e;`,
                    minimizeButton: `background: #ffbd2e; border: 1px solid #dea123;`,
                    maximizeButton: `background: #28ca42; border: 1px solid #1aab29;`,
                    bentoGrid: `display: flex; flex-direction: column; gap: 24px; padding: 32px; max-width: 800px; margin: 0 auto; min-height: 100%; background: #f8f9fa;`,
                    bentoCard: `background: var(--bg_white_web, #FFFFFF); border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); border-radius: 12px; padding: 24px; transition: all 0.2s ease; position: relative; overflow: hidden; min-height: 120px; display: flex; flex-direction: column;`,
                    bentoCardPrimary: `background: rgba(25, 118, 210, 0.05); color: #1976d2; border: 2px solid #1976d2; border-radius: 12px; padding: 24px; position: relative; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(25, 118, 210, 0.08); min-height: 160px; display: flex; flex-direction: column;`,
                    bentoCardWarning: `background: rgba(245, 124, 0, 0.05); color: #f57c00; border: 2px solid #f57c00; border-radius: 12px; padding: 24px; position: relative; transition: all 0.3s ease; box-shadow: 0 2px 8px rgba(245, 124, 0, 0.08); min-height: 160px; display: flex; flex-direction: column;`,
                    bentoCardHeader: `background: var(--bg_white_web, #FFFFFF); border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); border-radius: 12px; padding: 20px; display: flex; justify-content: space-between; align-items: center; min-height: 80px;`,
                    bentoRow: `display: grid; grid-template-columns: 1fr 1fr; gap: 20px; width: 100%;`,
                    bentoRowResponsive: `display: grid; grid-template-columns: 1fr 1fr; gap: 20px; width: 100%;`,
                    bentoRowThree: `display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; width: 100%;`,
                    bentoRowFour: `display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; width: 100%;`,
                    bentoStatsGrid: `display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 20px; width: 100%;`,
                    bentoStatsResponsive: `display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 20px; width: 100%;`
                },
                dialogs: {
                    compareDialog: `padding: 24px; max-width: 600px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); position: relative;`,
                    detailDialog: `position: relative; width: 90%; max-width: 1000px; max-height: 90vh; transform: scale(0.95); transition: transform 0.3s ease;`,
                    settingsOverlay: `position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 10000; display: flex; align-items: center; justify-content: center;`,
                    settingsContent: `background: white; border-radius: 16px; padding: 32px; max-width: 800px; width: 90%; max-height: 80vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.15);`,
                    settingsSection: `margin-bottom: 32px; padding-bottom: 24px; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1));`,
                    settingsTitle: `font-size: 24px; font-weight: 700; color: var(--base_gray_100, #13181D); margin-bottom: 24px;`,
                    settingsSectionTitle: `font-size: 18px; font-weight: 600; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); margin-bottom: 16px;`,
                    settingsItem: `margin-bottom: 20px;`,
                    settingsLabel: `font-size: 14px; font-weight: 600; color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); margin-bottom: 8px; display: block;`,
                    settingsInput: `width: 100%; padding: 12px 16px; border: 1px solid var(--base_gray_020, rgba(22, 46, 74, 0.2)); border-radius: 8px; font-size: 14px; transition: all 0.2s ease; box-sizing: border-box;`,
                    settingsSelect: `width: 100%; padding: 12px 16px; border: 1px solid var(--base_gray_020, rgba(22, 46, 74, 0.2)); border-radius: 8px; font-size: 14px; background: white; transition: all 0.2s ease; box-sizing: border-box;`,
                    settingsCheckbox: `margin-right: 8px; transform: scale(1.2);`,
                    settingsDescription: `font-size: 13px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); margin-top: 4px; line-height: 1.4;`,
                    settingsButtonGroup: `display: flex; gap: 12px; justify-content: flex-end; margin-top: 32px; padding-top: 24px; border-top: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1));`,
                    settingsRow: `display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;`,
                    settingsCheckboxItem: `display: flex; align-items: center; margin-bottom: 12px;`
                },
                comparison: {
                    summaryTitle: `display: flex; align-items: center; gap: 12px; margin-bottom: 20px;`,
                    titleAccent: `width: 4px; height: 24px; background: linear-gradient(135deg, #0F7AF5, #40a9ff); border-radius: 2px;`,
                    statsGrid: `display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 24px;`,
                    statCard: `border-radius: 12px; padding: 20px; color: white; position: relative; overflow: hidden;`,
                    statCardDecor: `position: absolute; top: -10px; right: -10px; width: 60px; height: 60px; background: rgba(255,255,255,0.1); border-radius: 50%; opacity: 0.3;`,
                    statNumber: `font-size: 28px; font-weight: 800; margin-bottom: 4px;`,
                    statLabel: `font-size: 13px; opacity: 0.9;`,
                    statExtra: `font-size: 11px; opacity: 0.7; margin-top: 4px;`,
                    statusCard: `border-radius: 12px; padding: 20px; position: relative; overflow: hidden;`,
                    statusIcon: `position: absolute; top: -20px; right: -20px; font-size: 80px; opacity: 0.1;`,
                    statusHeader: `display: flex; align-items: center; gap: 12px; margin-bottom: 16px;`,
                    statusBadge: `width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 20px; font-weight: bold;`,
                    statusGrid: `display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;`,
                    statusSubCard: `background: rgba(255,255,255,0.6); border-radius: 8px; padding: 16px;`,
                    sectionCard: `background: #fff; border-radius: 12px; overflow: hidden; margin-bottom: 20px;`,
                    sectionHeader: `padding: 16px 20px; display: flex; align-items: center; justify-content: space-between;`,
                    sectionIcon: `width: 36px; height: 36px; background: rgba(255,255,255,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px;`,
                    sectionTitle: `margin: 0; font-size: 18px; font-weight: 700;`,
                    sectionSubtitle: `font-size: 13px; opacity: 0.9; margin-top: 2px;`,
                    sectionCount: `text-align: right;`,
                    sectionNumber: `font-size: 24px; font-weight: 800;`,
                    sectionUnit: `font-size: 11px; opacity: 0.8;`,
                    itemList: `max-height: 300px; overflow-y: auto;`,
                    itemRow: `padding: 16px 20px; display: flex; align-items: center; gap: 16px; transition: background 0.2s;`,
                    itemDot: `width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;`,
                    itemContent: `flex: 1; min-width: 0;`,
                    itemName: `font-weight: 600; color: #13181D; margin-bottom: 4px; word-break: break-all;`,
                    itemMeta: `display: flex; align-items: center; gap: 12px; font-size: 12px; color: #666;`,
                    itemTag: `font-size: 11px; padding: 4px 8px; background: #f8f9fa; border-radius: 4px; margin-top: 4px;`,
                    emptyState: `border-radius: 12px; padding: 20px; text-align: center; margin-bottom: 20px;`,
                    emptyIcon: `width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; color: white; font-size: 24px;`,
                    emptyTitle: `margin: 0 0 8px 0; font-size: 18px; font-weight: 700;`,
                    emptyDesc: `font-size: 14px;`
                },
                progress: {
                    area: `display: block; padding: 16px 20px; border-top: 1px solid var(--base_gray_010, #e9e9e9); background: var(--bg_white_web, #FFFFFF); position: fixed; bottom: 0; left: 0; right: 0; z-index: 999;`,
                    text: `color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); font-size: 13px;`
                },
                errors: {
                    centered: `position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center;`
                },
                interactions: {
                    menuItemHover: `background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); font-weight: 500;`,
                    cardHover: `transform: translateY(-2px); box-shadow: 0 4px 12px 0 rgba(19, 24, 29, 0.12); border-color: var(--base_gray_020, rgba(22, 46, 74, 0.2));`,
                    cardPrimaryHover: `transform: translateY(-2px); box-shadow: 0 4px 16px rgba(25, 118, 210, 0.15); background: rgba(25, 118, 210, 0.08); border-color: #1565c0;`,
                cardWarningHover: `transform: translateY(-2px); box-shadow: 0 4px 16px rgba(245, 124, 0, 0.15); background: rgba(245, 124, 0, 0.08); border-color: #ef6c00;`,
                    buttonHover: `background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); border-color: var(--base_gray_015, rgba(23, 46, 71, 0.15));`,
                    actionButtonHover: `background: var(--theme_primary, #0F7AF5); color: white; transform: translateY(-1px); box-shadow: 0 4px 12px 0 rgba(15, 122, 245, 0.3);`,
                    actionButtonSecondaryHover: `background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); border-color: var(--base_gray_030, rgba(22, 46, 74, 0.3)); transform: translateY(-1px);`,
                    primaryButtonHover: `background: var(--theme_lighten_1, #328DF6);`,
                    downloadButtonHover: `background: #0e66cb; box-shadow: 0 4px 16px 0 rgba(15,122,245,0.13);`,
                    checkboxHover: `opacity: 1; box-shadow: 0 2px 8px 0 rgba(15,122,245,0.12);`,
                    iconHover: `transform: scale(1.05);`,
                    imageHover: `transform: scale(1.02);`
                },
                toolbars: {
                    overlay: `height: 56px; flex-shrink: 0; box-shadow: 0 1px 3px rgba(0,0,0,0.1); position: relative; z-index: 1000; background: var(--bg_white_web, #FFFFFF); border-bottom: 1px solid var(--base_gray_007, rgba(21, 46, 74, 0.07));`,
                    section: `display: flex; align-items: center;`,
                    leftSection: `flex: 1;`,
                    rightSection: `gap: 12px;`,
                    middleSection: `gap: 8px;`
                },
                texts: {
                    title: `margin: 0; font-size: 18px; font-weight: 600; color: #333;`,
                    subtitle: `margin-left: 12px; font-size: 14px; color: #666;`,
                    errorIcon: `color: var(--chrome_red, #F73116); font-size: 20px; margin-bottom: 8px;`,
                    errorText: `color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); font-size: 13px;`,
                    headerTitle: `font-size: 24px; font-weight: 700; color: var(--base_gray_100, #13181D); margin: 0; line-height: 1.2;`,
                    headerSubtitle: `font-size: 14px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); margin: 4px 0 0 0; line-height: 1.3;`,
                    bentoNumber: `font-size: 32px; font-weight: 700; color: var(--base_gray_100, #13181D); margin: 0; line-height: 1.1; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif; letter-spacing: -0.02em;`,
                    bentoNumberLarge: `font-size: 42px; font-weight: 800; color: inherit; margin: 0; line-height: 1; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif; letter-spacing: -0.02em;`,
                    bentoNumberWarning: `font-size: 38px; font-weight: 800; color: inherit; margin: 0; line-height: 1; font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif; letter-spacing: -0.02em;`,
                    bentoLabel: `font-size: 14px; font-weight: 600; color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); margin: 6px 0 0 0; line-height: 1.3;`,
                    bentoLabelLarge: `font-size: 15px; font-weight: 600; color: inherit; opacity: 0.8; margin: 8px 0 0 0; line-height: 1.3;`,
                    bentoLabelWarning: `font-size: 14px; font-weight: 600; color: inherit; opacity: 0.8; margin: 6px 0 0 0; line-height: 1.3;`,
                    bentoDesc: `font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); line-height: 1.4; margin: 4px 0 0 0;`,
                    bentoDescLarge: `font-size: 13px; color: inherit; opacity: 0.7; line-height: 1.4; margin: 6px 0 0 0;`,
                    bentoDescWarning: `font-size: 12px; color: inherit; opacity: 0.7; line-height: 1.4; margin: 4px 0 0 0;`,
                    bentoTitle: `font-size: 16px; font-weight: 600; color: var(--base_gray_100, #13181D); margin: 0 0 12px 0; line-height: 1.3;`,
                    actionButton: `font-size: 13px; font-weight: 600; color: var(--theme_primary, #0F7AF5); text-decoration: none; padding: 8px 16px; border: 1px solid var(--theme_primary, #0F7AF5); border-radius: 6px; transition: all 0.2s ease; display: inline-flex; align-items: center; gap: 6px;`,
                    actionButtonSecondary: `font-size: 13px; font-weight: 600; color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); text-decoration: none; padding: 8px 16px; border: 1px solid var(--base_gray_020, rgba(22, 46, 74, 0.2)); border-radius: 6px; transition: all 0.2s ease; display: inline-flex; align-items: center; gap: 6px; background: var(--bg_white_web, #FFFFFF);`
                },
                toastExtensions: {
                    custom: `background: var(--bg_white_web, #FFFFFF); color: var(--base_gray_100, #13181D); bottom: 20px; animation: slideIn 0.3s ease-out;`
                },
                layout: {
                    overlay: `position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 10000; background: #f5f5f5;`
                }
            };
        }

        static applyStyle(element, styleKey, subKey) {
            const styles = this.getStyles();
            if (subKey) {
                element.style.cssText = styles[styleKey][subKey];
            } else {
                element.style.cssText = styles[styleKey];
            }
        }

        static addSpinnerAnimation() {
            if (!document.getElementById('attachment-spinner-style')) {
                const style = document.createElement('style');
                style.id = 'attachment-spinner-style';
                style.textContent = `
                    @keyframes spin {
                        0% { transform: rotate(0deg); }
                        100% { transform: rotate(360deg); }
                    }
                `;
                document.head.appendChild(style);
            }
        }

        static addLayoutStyles() {
            if (!document.getElementById('attachment-layout-style')) {
                const style = document.createElement('style');
                style.id = 'attachment-layout-style';
                const layouts = this.getStyles().layouts;
                style.textContent = `
                    .attachment-floating-overlay {
                        ${layouts.floatingOverlay}
                    }
                    .attachment-floating-overlay.show {
                        opacity: 1;
                    }
                    .attachment-floating-window {
                        ${layouts.floatingWindow}
                        opacity: 0;
                        transform: translate(-50%, -50%) scale(0.9);
                        transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
                    }
                    .attachment-floating-window.show {
                        opacity: 1;
                        transform: translate(-50%, -50%) scale(1);
                    }
                    .attachment-window-header {
                        ${layouts.windowHeader}
                    }
                    .attachment-window-content {
                        ${layouts.windowContent}
                    }
                    .attachment-window-controls {
                        ${layouts.windowControls}
                    }
                    .attachment-window-button {
                        ${layouts.windowButton}
                    }
                    .attachment-window-button.close {
                        ${layouts.closeButton}
                    }
                    .attachment-window-button.close:hover {
                        background: #e0443e;
                        transform: scale(1.1);
                    }
                    .attachment-window-button.minimize {
                        ${layouts.minimizeButton}
                    }
                    .attachment-window-button.minimize:hover {
                        background: #dea123;
                        transform: scale(1.1);
                    }
                    .attachment-window-button.maximize {
                        ${layouts.maximizeButton}
                    }
                    .attachment-window-button.maximize:hover {
                        background: #1aab29;
                        transform: scale(1.1);
                    }
                    .attachment-floating-window.maximized {
                        top: 0 !important;
                        left: 0 !important;
                        transform: none !important;
                        width: 100% !important;
                        height: 100% !important;
                        border-radius: 0 !important;
                        max-width: none !important;
                        max-height: none !important;
                    }
                    .attachment-floating-window.minimized {
                        height: 60px !important;
                        overflow: hidden;
                    }
                    .attachment-floating-window.minimized .attachment-window-content {
                        display: none;
                    }
                    @keyframes attachmentWindowSlideIn {
                        from {
                            opacity: 0;
                            transform: translate(-50%, -60%) scale(0.8);
                        }
                        to {
                            opacity: 1;
                            transform: translate(-50%, -50%) scale(1);
                        }
                    }
                    
                    /* 响应式布局 */
                    @media (max-width: 1024px) {
                        .attachment-floating-window {
                            width: 85% !important;
                            height: 95% !important;
                            min-width: 500px !important;
                        }
                    }
                    
                    @media (max-width: 768px) {
                        .attachment-floating-window {
                            width: 100% !important;
                            height: 100% !important;
                            min-width: 320px !important;
                            border-radius: 0 !important;
                            top: 0 !important;
                            left: 0 !important;
                            transform: none !important;
                        }
                        
                        /* 移动端卡片布局调整 */
                        .bento-row-responsive {
                            grid-template-columns: 1fr !important;
                            gap: 16px !important;
                        }
                        
                        .bento-stats-responsive {
                            grid-template-columns: repeat(2, 1fr) !important;
                            gap: 12px !important;
                        }
                    }
                    
                    @media (max-width: 480px) {
                        .bento-stats-responsive {
                            grid-template-columns: 1fr !important;
                        }
                    }
                `;
                document.head.appendChild(style);
            }
        }

        static applyInteractionStyle(element, styleKey, reset = false) {
            const styles = this.getStyles();
            if (reset) {
                element.style.cssText = element.getAttribute('data-original-style') || '';
            } else {
                if (!element.getAttribute('data-original-style')) {
                    element.setAttribute('data-original-style', element.style.cssText);
                }
                if (styles.interactions && styles.interactions[styleKey]) {
                    const originalStyle = element.style.cssText;
                    element.style.cssText = originalStyle + '; ' + styles.interactions[styleKey];
                }
            }
        }
    }

    const MAIL_CONSTANTS = {
        BASE_URL: 'https://wx.mail.qq.com',
        API_ENDPOINTS: {
            MAIL_LIST: '/list/maillist',
            ATTACH_DOWNLOAD: '/attach/download',
            ATTACH_THUMBNAIL: '/attach/thumbnail',
            ATTACH_PREVIEW: '/attach/preview'
        }
    };

    class AttachmentManager {
        constructor(downloader) {
            this.downloader = downloader;
            this.isVisible = false;
            this.selectedAttachments = new Set();
            this.downloadQueue = [];
            this.downloading = false;
            this.retryCount = 3;
            this.attachments = [];
            this.filters = {
                date: 'all',
                dateRange: {
                    start: null,
                    end: null
                },
                minSize: 0,
                maxSize: 0,
                allowedTypes: [],
                excludedTypes: []
            };
            this.sortBy = {
                field: 'date',
                direction: 'desc'
            };
            this.groupBy = 'mail';
            this.isLoading = false;
            this.currentFilter = 'all';
            this.currentSort = 'date';
            
            // 面板状态管理
            this.isViewActive = false;
            this.toggleInProgress = false;
            this.currentFolderId = null;
            this.overlayPanel = null;
            this.smartDownloadButton = null;
            this.downloadProgressTimer = null;
            this.globalKeyHandler = null;
            this.buttonObserver = null;
            this.isMinimized = false;
            this.isMaximized = false;
            this.dragState = {
                isDragging: false,
                startX: 0,
                startY: 0,
                startLeft: 0,
                startTop: 0
            };
            this.totalMailCount = 0;
            this.detailData = {
                noAttachmentMails: [],
                invalidNamingAttachments: []
            };

            this.downloadSettings = {
                fileNaming: {
                    prefix: '',
                    suffix: '',
                    includeMailId: false,
                    includeAttachmentId: false,
                    includeMailSubject: false,
                    includeFileType: false,
                    separator: '_',
                    useCustomPattern: false,
                    customPattern: '{date}_{subject}_{fileName}',
                    validation: {
                        enabled: true,
                        pattern: '\\d{6,}',
                        fallbackPattern: 'auto',
                        fallbackTemplate: '{subject}_{fileName}',
                        replacementChar: '_',
                        removeInvalidChars: true
                    }
                },
                folderStructure: 'flat',
                dateFormat: 'YYYY-MM-DD',
                createDateSubfolders: false,
                folderNaming: {
                    customTemplate: '{date}/{senderName}'
                },
                conflictResolution: 'rename',
                downloadBehavior: {
                    showProgress: true,
                    retryOnFail: true,
                    verifyDownloads: true,
                    notifyOnComplete: true,
                    concurrentDownloads: 'auto',
                    autoCompareAfterDownload: true
                },
                smartGrouping: {
                    enabled: false,
                    maxGroupSize: 5,
                    groupByType: true,
                    groupByDate: true
                }
            };

            this.downloadQueue = { high: [], normal: [], low: [] };
            this.downloadStats = { startTime: null, completedSize: 0, totalSize: 0, speed: 0, lastUpdate: null };
            this.concurrentControl = { minConcurrent: 2, maxConcurrent: 5, currentConcurrent: 3, successCount: 0, failCount: 0, lastAdjustTime: null, adjustInterval: 10000 };
            this.totalTasksForProgress = 0;
            this.completedTasksForProgress = 0;
            this.autoSaveTimeout = null;
            this.currentComparisonResult = null;
            this.init();
        }


        init() {
            window.attachmentManager = this;
            this.loadSavedSettings();
            this.initUrlChangeListener();

            // 清理可能存在的旧按钮
            const existingButtons = document.querySelectorAll('#attachment-downloader-btn, [data-attachment-manager-btn="true"], .attachment-floating-btn');
            existingButtons.forEach(btn => btn.remove());

            // 延迟创建按钮,确保页面元素已加载
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', () => {
                    setTimeout(() => this.createAndInjectButton(), 500);
                });
            } else {
                setTimeout(() => this.createAndInjectButton(), 500);
            }
        }

        initUrlChangeListener() {
            window.addEventListener('hashchange', () => {
                const newFolderId = this.downloader.getCurrentFolderId();
                if (this.currentFolderId !== newFolderId) {
                    console.log('[URL变化] 检测到文件夹变化:', this.currentFolderId, '->', newFolderId);
                    this.currentFolderId = newFolderId;
                    
                    // 立即清理旧按钮
                    const existingButtons = document.querySelectorAll('#attachment-downloader-btn, [data-attachment-manager-btn="true"], .attachment-floating-btn');
                    existingButtons.forEach(btn => btn.remove());
                    
                    // 延迟创建按钮,等待页面更新完成
                    setTimeout(() => this.createAndInjectButton(), 500);
                    
                    // 只有在面板激活时才重新初始化,避免冲突
                    if (this.isViewActive) {
                        console.log('[URL变化] 面板处于激活状态,准备重新初始化');
                        // 添加延迟,确保页面完全加载
                        setTimeout(() => {
                            if (this.isViewActive) { // 再次检查状态
                                this.reinitializeForFolderChange();
                            }
                        }, 1000);
                    }
                }
            });
        }



        hidePanel() {
            try {
                this.hideAttachmentView();
                    } catch (error) {
            this.cleanupAttachmentManager(true);
        }
        }

        // 获取文件夹显示名称
        getFolderDisplayName() {
            // 获取文件夹名称
            const nativeFolderName = document.querySelector('.toolbar-folder-name');
            if (nativeFolderName && nativeFolderName.textContent.trim()) {
                return nativeFolderName.textContent.trim();
            }

        }


    // 显示设置对话框
    async showSettingsDialog() {
        const dialog = this.createUI('div', {
            styles: StyleManager.getStyles().dialogs.settingsOverlay,
            content: `
                <div style="${StyleManager.getStyles().dialogs.settingsContent}">
                    <div style="${StyleManager.getStyles().dialogs.settingsTitle}">
                        <span>下载设置</span>
                        <button id="settings-close-x" style="position: absolute; right: 24px; top: 24px; background: none; border: none; font-size: 24px; cursor: pointer; color: #666; padding: 0; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: all 0.2s;">×</button>
                    </div>
                    
                    <!-- 选项卡导航 -->
                    <div style="display: flex; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); margin: 0 -32px; padding: 0 32px; margin-top: 20px;">
                        <div class="settings-tab active" data-tab="basic" style="padding: 12px 24px; cursor: pointer; border-bottom: 2px solid var(--theme_primary, #0F7AF5); color: var(--theme_primary, #0F7AF5); font-weight: 600; transition: all 0.2s;">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 8px;">
                                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
                                <polyline points="14 2 14 8 20 8"/>
                                <line x1="16" y1="13" x2="8" y2="13"/>
                                <line x1="16" y1="17" x2="8" y2="17"/>
                                <polyline points="10 9 9 9 8 9"/>
                            </svg>
                            基础设置
                        </div>
                        <div class="settings-tab" data-tab="advanced" style="padding: 12px 24px; cursor: pointer; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); transition: all 0.2s;">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 8px;">
                                <circle cx="12" cy="12" r="3"/>
                                <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
                            </svg>
                            高级设置
                        </div>
                        <div class="settings-tab" data-tab="download" style="padding: 12px 24px; cursor: pointer; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); transition: all 0.2s;">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 8px;">
                                <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
                                <polyline points="7 10 12 15 17 10"/>
                                <line x1="12" y1="15" x2="12" y2="3"/>
                            </svg>
                            下载设置
                        </div>
                        <div class="settings-tab" data-tab="filter" style="padding: 12px 24px; cursor: pointer; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); transition: all 0.2s;">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 8px;">
                                <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
                            </svg>
                            过滤规则
                        </div>
                    </div>
                    
                    <!-- 选项卡内容区域 -->
                    <div style="margin-top: 24px; max-height: calc(80vh - 200px); overflow-y: auto; padding-right: 8px;">
                        <!-- 基础设置面板 -->
                        <div id="settings-panel-basic" class="settings-panel" style="display: block;">
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
                                        <polyline points="14 2 14 8 20 8"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">文件命名</div>
                                </div>
                        
                                <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                    <label style="${StyleManager.getStyles().dialogs.settingsLabel}">命名模式</label>
                                    <select id="naming-mode" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                        <option value="original">原始文件名</option>
                                        <option value="custom">自定义文件名</option>
                                    </select>
                                </div>
                                
                                <div id="custom-naming-options" style="display: none; background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); padding: 16px; border-radius: 8px; margin-top: 12px;">
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">命名模板</label>
                                        <div style="position: relative;">
                                            <input type="text" id="naming-pattern" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                                   placeholder="{date}_{subject}_{fileName}" value="{date}_{subject}_{fileName}">
                                            <button type="button" id="show-variables-btn" style="position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: var(--theme_primary, #0F7AF5); color: white; border: none; border-radius: 4px; padding: 4px 12px; font-size: 12px; cursor: pointer; font-weight: 500;">选择变量</button>
                                        </div>
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                            使用变量构建自定义文件名,点击按钮查看所有可用变量
                                        </div>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsRow}; margin-top: 16px;">
                                        <div>
                                            <label style="${StyleManager.getStyles().dialogs.settingsLabel}">前缀</label>
                                            <input type="text" id="naming-prefix" style="${StyleManager.getStyles().dialogs.settingsInput}" placeholder="可选前缀">
                                        </div>
                                        <div>
                                            <label style="${StyleManager.getStyles().dialogs.settingsLabel}">后缀</label>
                                            <input type="text" id="naming-suffix" style="${StyleManager.getStyles().dialogs.settingsInput}" placeholder="可选后缀">
                                        </div>
                                    </div>
                                </div>
                            </div>
                            
                            <!-- 文件夹结构 -->
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">文件夹结构</div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                    <label style="${StyleManager.getStyles().dialogs.settingsLabel}">组织方式</label>
                                    <select id="folder-structure" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                        <option value="flat" selected>平铺(所有文件在同一文件夹)</option>
                                        <option value="subject">主题</option>
                                        <option value="sender">发件人</option>
                                        <option value="date">日期</option>
                                        <option value="custom">自定义文件夹结构</option>
                                    </select>
                                </div>
                                
                                <!-- 自定义文件夹结构配置 -->
                                <div id="folder-naming-options" style="display: none; background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); padding: 16px; border-radius: 8px; margin-top: 12px;">
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">文件夹结构模板</label>
                                        <div style="position: relative;">
                                            <input type="text" id="folder-custom-template" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                                   placeholder="{date:YYYY-MM}/{senderName}" value="{date:YYYY-MM}/{senderName}">
                                            <button type="button" id="show-folder-custom-variables-btn" style="position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: var(--theme_primary, #0F7AF5); color: white; border: none; border-radius: 4px; padding: 4px 12px; font-size: 12px; cursor: pointer; font-weight: 500;">选择变量</button>
                                        </div>
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                            使用"/"分隔创建多级文件夹。常用模板:<br>
                                            • {date}/{senderName} - 按日期和发件人<br>
                                            • {subject}/{fileType} - 按主题和文件类型<br>
                                            • {senderName}/{date} - 按发件人和日期
                                        </div>
                                    </div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                    <input type="checkbox" id="smart-grouping" style="${StyleManager.getStyles().dialogs.settingsCheckbox}">
                                    <label for="smart-grouping" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">启用智能分组</label>
                                </div>
                                <div style="${StyleManager.getStyles().dialogs.settingsDescription}">根据文件名模式自动分组相关文件</div>
                            </div>
                        </div>

                        <!-- 高级设置面板 -->
                        <div id="settings-panel-advanced" class="settings-panel" style="display: none;">
                            <!-- 文件名规范检查 -->
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <path d="M9 11l3 3L22 4"/>
                                        <path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">命名规则验证</div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                    <label style="${StyleManager.getStyles().dialogs.settingsLabel}">启用验证</label>
                                    <select id="filename-validation" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                        <option value="enabled">启用</option>
                                        <option value="disabled">禁用</option>
                                    </select>
                                    <div style="${StyleManager.getStyles().dialogs.settingsDescription}">使用正则表达式检查文件名是否符合规范</div>
                                </div>
                        
                                <div id="filename-validation-options" style="display: block; background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); padding: 16px; border-radius: 8px; margin-top: 12px;">
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">验证正则表达式</label>
                                        <input type="text" id="validation-pattern" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                               placeholder="\\d{6,}" value="\\d{6,}">
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                            用于验证文件名的正则表达式。例如:\\d{6,} 表示至少6位数字
                                        </div>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">不符合规则时的处理</label>
                                        <select id="fallback-pattern" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                            <option value="auto" selected>自动分析</option>
                                            <option value="mailSubject">添加邮件主题</option>
                                            <option value="senderEmail">添加发件人邮箱</option>
                                            <option value="toTime">添加时间戳</option>
                                            <option value="customTemplate">自定义模板</option>
                                        </select>
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">当文件名不符合验证规则时的处理方式</div>
                                    </div>
                                    
                                    <div id="fallback-template-container" style="${StyleManager.getStyles().dialogs.settingsItem}; display: none;">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">备用命名模板</label>
                                        <div style="position: relative;">
                                            <input type="text" id="fallback-template" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                                   placeholder="{date}_{subject}_{fileName}" value="{subject}_{fileName}">
                                            <button type="button" id="show-fallback-variables-btn" style="position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: var(--theme_primary, #0F7AF5); color: white; border: none; border-radius: 4px; padding: 4px 12px; font-size: 12px; cursor: pointer; font-weight: 500;">选择变量</button>
                                        </div>
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                            不符合规则时使用的命名模板
                                        </div>
                                    </div>
                                </div>
                            </div>
                            
                            <!-- 内容替换 -->
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">内容替换</div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                    <input type="checkbox" id="enable-content-replacement" style="${StyleManager.getStyles().dialogs.settingsCheckbox}">
                                    <label for="enable-content-replacement" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">启用内容替换</label>
                                </div>
                                <div style="${StyleManager.getStyles().dialogs.settingsDescription}">对文件名进行自定义内容替换,支持字符串和正则表达式</div>
                            
                                <div id="content-replacement-options" style="display: none; background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); padding: 16px; border-radius: 8px; margin-top: 12px;">
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">匹配模式</label>
                                        <select id="replacement-mode" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                            <option value="string" selected>字符串匹配</option>
                                            <option value="regex">正则表达式</option>
                                        </select>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">查找内容</label>
                                        <input type="text" id="replacement-search" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                               placeholder="要替换的内容或正则表达式">
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                            字符串模式:直接输入要替换的文字 | 正则模式:如 \\d{4} 匹配4位数字
                                        </div>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">替换内容</label>
                                        <div style="position: relative;">
                                            <input type="text" id="replacement-replace" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                                   placeholder="替换后的内容,支持变量">
                                            <button type="button" id="show-replacement-variables-btn" style="position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: var(--theme_primary, #0F7AF5); color: white; border: none; border-radius: 4px; padding: 4px 12px; font-size: 12px; cursor: pointer; font-weight: 500;">选择变量</button>
                                        </div>
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                            支持变量如 {subject}、{date} 等,正则模式还支持捕获组 $1、$2
                                        </div>
                                    </div>
                                    
                                    <div style="display: flex; gap: 24px; margin-top: 12px;">
                                        <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                            <input type="checkbox" id="replacement-global" style="${StyleManager.getStyles().dialogs.settingsCheckbox}" checked>
                                            <label for="replacement-global" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">全局替换</label>
                                        </div>
                                        
                                        <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                            <input type="checkbox" id="replacement-case-sensitive" style="${StyleManager.getStyles().dialogs.settingsCheckbox}">
                                            <label for="replacement-case-sensitive" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">区分大小写</label>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <!-- 文件名字符处理 -->
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
                                        <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">字符处理</div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                    <label style="${StyleManager.getStyles().dialogs.settingsLabel}">无效字符处理方式</label>
                                    <div style="${StyleManager.getStyles().dialogs.settingsRow}">
                                        <div style="flex: 1;">
                                            <select id="invalid-char-handling" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                                <option value="replace" selected>替换无效字符</option>
                                                <option value="remove">移除无效字符</option>
                                                <option value="keep">保留原字符</option>
                                            </select>
                                        </div>
                                        <div style="flex: 0 0 120px; margin-left: 10px;">
                                            <input type="text" id="invalid-char-replacement" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                                   value="_" placeholder="_" maxlength="3">
                                        </div>
                                    </div>
                                    <div style="${StyleManager.getStyles().dialogs.settingsDescription}">
                                        自动处理系统不允许的文件名字符(如 &lt; &gt; : " | ? * 等)
                                    </div>
                                </div>
                            </div>
                        </div>

                    
                        <!-- 下载设置面板 -->
                        <div id="settings-panel-download" class="settings-panel" style="display: none;">
                            <!-- 下载行为 -->
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
                                        <polyline points="7 10 12 15 17 10"/>
                                        <line x1="12" y1="15" x2="12" y2="3"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">下载行为</div>
                                </div>
                        
                                <div style="${StyleManager.getStyles().dialogs.settingsRow}">
                                    <div>
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">并发下载数</label>
                                        <select id="concurrent-downloads" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                            <option value="auto" selected>自动调节 (推荐)</option>
                                            <option value="1">1个文件</option>
                                            <option value="2">2个文件</option>
                                            <option value="3">3个文件</option>
                                            <option value="5">5个文件</option>
                                        </select>
                                        <div style="${StyleManager.getStyles().dialogs.settingsDescription}">自动调节模式会根据下载成功率动态调整并发数(2-5个文件),提供最佳下载性能</div>
                                    </div>
                                    <div>
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">冲突处理</label>
                                        <select id="conflict-resolution" style="${StyleManager.getStyles().dialogs.settingsSelect}">
                                            <option value="rename" selected>自动重命名</option>
                                            <option value="skip">跳过已存在</option>
                                            <option value="overwrite">覆盖文件</option>
                                        </select>
                                    </div>
                                </div>
                                
                                <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px;">
                                    <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                        <input type="checkbox" id="show-progress" style="${StyleManager.getStyles().dialogs.settingsCheckbox}" checked>
                                        <label for="show-progress" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">显示下载进度</label>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                        <input type="checkbox" id="retry-on-fail" style="${StyleManager.getStyles().dialogs.settingsCheckbox}" checked>
                                        <label for="retry-on-fail" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">失败时自动重试</label>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                        <input type="checkbox" id="verify-downloads" style="${StyleManager.getStyles().dialogs.settingsCheckbox}" checked>
                                        <label for="verify-downloads" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">验证下载完整性</label>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                        <input type="checkbox" id="notify-complete" style="${StyleManager.getStyles().dialogs.settingsCheckbox}" checked>
                                        <label for="notify-complete" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">完成时通知</label>
                                    </div>
                                    
                                    <div style="${StyleManager.getStyles().dialogs.settingsCheckboxItem}">
                                        <input type="checkbox" id="auto-compare" style="${StyleManager.getStyles().dialogs.settingsCheckbox}" checked>
                                        <label for="auto-compare" style="${StyleManager.getStyles().dialogs.settingsLabel}; margin-bottom: 0;">下载完成后自动对比本地</label>
                                    </div>
                                </div>
                            </div>
                        </div>
                    
                        <!-- 过滤规则面板 -->
                        <div id="settings-panel-filter" class="settings-panel" style="display: none;">
                            <div style="${StyleManager.getStyles().dialogs.settingsSection}">
                                <div style="display: flex; align-items: center; margin-bottom: 20px;">
                                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--theme_primary, #0F7AF5)" stroke-width="2" style="margin-right: 12px;">
                                        <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/>
                                    </svg>
                                    <div style="${StyleManager.getStyles().dialogs.settingsSectionTitle}; margin-bottom: 0;">文件过滤</div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsRow}">
                                    <div>
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">最小文件大小 (KB)</label>
                                        <input type="number" id="min-file-size" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                               placeholder="0" min="0">
                                    </div>
                                    <div>
                                        <label style="${StyleManager.getStyles().dialogs.settingsLabel}">最大文件大小 (MB)</label>
                                        <input type="number" id="max-file-size" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                               placeholder="无限制" min="0">
                                    </div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                    <label style="${StyleManager.getStyles().dialogs.settingsLabel}">允许的文件类型</label>
                                    <input type="text" id="allowed-types" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                           placeholder="jpg,png,pdf,doc,zip (留空表示允许所有类型)">
                                    <div style="${StyleManager.getStyles().dialogs.settingsDescription}">用逗号分隔多个文件扩展名,留空表示允许所有类型</div>
                                </div>
                                
                                <div style="${StyleManager.getStyles().dialogs.settingsItem}">
                                    <label style="${StyleManager.getStyles().dialogs.settingsLabel}">排除的文件类型</label>
                                    <input type="text" id="excluded-types" style="${StyleManager.getStyles().dialogs.settingsInput}" 
                                           placeholder="tmp,log,cache">
                                    <div style="${StyleManager.getStyles().dialogs.settingsDescription}">用逗号分隔多个文件扩展名</div>
                                </div>
                            </div>
                        </div>
                    </div>
                    
                    <!-- 按钮组 -->
                    <div style="${StyleManager.getStyles().dialogs.settingsButtonGroup}">
                        <button id="settings-reset" style="${StyleManager.getStyles().texts.actionButtonSecondary}">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 6px;">
                                <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
                                <path d="M3 3v5h5"/>
                            </svg>
                            重置默认
                        </button>
                        <button id="settings-save" style="${StyleManager.getStyles().texts.actionButton}">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display: inline-block; vertical-align: middle; margin-right: 6px;">
                                <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
                                <polyline points="17 21 17 13 7 13 7 21"/>
                                <polyline points="7 3 7 8 15 8"/>
                            </svg>
                            保存设置
                        </button>
                    </div>
                </div>
            `,
            events: {
                click: (e) => {
                    if (e.target === dialog) {
                        document.body.removeChild(dialog);
                    } else if (e.target.id === 'settings-close-x' || e.target.closest('#settings-close-x')) {
                        // 更新文件夹结构预览(如果当前有附件数据)
                        if (this.attachments && this.attachments.length > 0) {
                            this.updateFolderStructurePreview(this.attachments);
                        }
                        document.body.removeChild(dialog);
                    } else if (e.target.id === 'settings-save' || e.target.closest('#settings-save')) {
                        this.saveSettings(dialog);
                        document.body.removeChild(dialog);
                    } else if (e.target.id === 'settings-reset' || e.target.closest('#settings-reset')) {
                        this.resetSettings(dialog);
                    } else if (e.target.classList.contains('settings-tab')) {
                        this.switchSettingsTab(e.target.dataset.tab);
                    }
                }
            }
        });
        
        document.body.appendChild(dialog);
        
        // 添加事件监听器(避免CSP限制)
        const namingModeSelect = dialog.querySelector('#naming-mode');
        const fallbackPatternSelect = dialog.querySelector('#fallback-pattern');
        const showVariablesBtn = dialog.querySelector('#show-variables-btn');
        const showFallbackVariablesBtn = dialog.querySelector('#show-fallback-variables-btn');
        const filenameValidationSelect = dialog.querySelector('#filename-validation');
        
        if (namingModeSelect) {
            namingModeSelect.addEventListener('change', () => {

                if (window.toggleNamingOptions) {
                    window.toggleNamingOptions();
                }
            });

        }
        
        if (fallbackPatternSelect) {
            fallbackPatternSelect.addEventListener('change', () => {

                if (window.toggleFallbackTemplate) {
                    window.toggleFallbackTemplate();
                }
            });

        }
        
        if (showVariablesBtn) {
            showVariablesBtn.addEventListener('click', () => {

                this.showVariableSelector('naming-pattern');
            });

        }
        
        if (showFallbackVariablesBtn) {
            showFallbackVariablesBtn.addEventListener('click', () => {

                this.showVariableSelector('fallback-template');
            });

        }
        
        if (filenameValidationSelect) {
            filenameValidationSelect.addEventListener('change', () => {

                if (window.toggleFilenameValidationOptions) {
                    window.toggleFilenameValidationOptions();
                }
            });

        }
        
        const charHandlingSelect = dialog.querySelector('#invalid-char-handling');
        if (charHandlingSelect) {
            charHandlingSelect.addEventListener('change', () => {
                if (window.toggleCharHandlingInput) {
                    window.toggleCharHandlingInput();
                }
            });
        }
        
        const enableContentReplacementCheckbox = dialog.querySelector('#enable-content-replacement');
        if (enableContentReplacementCheckbox) {
            enableContentReplacementCheckbox.addEventListener('change', () => {
                if (window.toggleContentReplacementOptions) {
                    window.toggleContentReplacementOptions();
                }
            });
        }
        
        const showReplacementVariablesBtn = dialog.querySelector('#show-replacement-variables-btn');
        if (showReplacementVariablesBtn) {
            showReplacementVariablesBtn.addEventListener('click', () => {
                this.showVariableSelector('replacement-replace');
            });
        }
        
        // 定义全局函数
        window.toggleFallbackTemplate = () => {
            // 使用document.querySelector而不是dialog.querySelector
            const fallbackPattern = document.querySelector('#fallback-pattern').value;
            const templateContainer = document.querySelector('#fallback-template-container');
            
            if (fallbackPattern === 'customTemplate') {
                templateContainer.style.display = 'block';
            } else {
                templateContainer.style.display = 'none';
            }
        };
        
        window.toggleFilenameValidationOptions = () => {
            const filenameValidation = document.querySelector('#filename-validation');
            const validationOptions = document.querySelector('#filename-validation-options');
            
            if (filenameValidation && validationOptions) {
                if (filenameValidation.value === 'enabled') {
                    validationOptions.style.display = 'block';
                } else {
                    validationOptions.style.display = 'none';
                }
            }
        };
        
        window.toggleCharHandlingInput = () => {
            const charHandling = document.querySelector('#invalid-char-handling');
            const replacementInput = document.querySelector('#invalid-char-replacement');
            
            if (charHandling && replacementInput) {
                if (charHandling.value === 'replace') {
                    replacementInput.style.display = 'block';
                    replacementInput.disabled = false;
                } else {
                    replacementInput.style.display = 'none';
                    replacementInput.disabled = true;
                }
            }
        };
        
        window.toggleContentReplacementOptions = () => {
            const enableContentReplacement = document.querySelector('#enable-content-replacement');
            const optionsContainer = document.querySelector('#content-replacement-options');
            
            if (enableContentReplacement && optionsContainer) {
                if (enableContentReplacement.checked) {
                    optionsContainer.style.display = 'block';
                } else {
                    optionsContainer.style.display = 'none';
                }
            }
        };
        
        window.toggleNamingOptions = () => {

            
            // 使用document.querySelector而不是dialog.querySelector
            const namingMode = document.querySelector('#naming-mode');
            const customOptions = document.querySelector('#custom-naming-options');
            

            
            if (namingMode && customOptions) {
                const mode = namingMode.value;

                
                // 记录修改前的状态
                const beforeStyle = window.getComputedStyle(customOptions);

                
                if (mode === 'custom') {
                    customOptions.style.display = 'block';
                    customOptions.style.visibility = 'visible';
                    customOptions.style.opacity = '1';
                    customOptions.style.height = 'auto';
                    customOptions.style.overflow = 'visible';

                } else {
                    customOptions.style.display = 'none';
                    customOptions.style.visibility = 'hidden';
                    customOptions.style.opacity = '0';

                }
                
                // 强制重绘
                customOptions.offsetHeight;
                
                // 验证结果
                setTimeout(() => {
                    const afterStyle = window.getComputedStyle(customOptions);

                    
                    if (mode === 'custom' && afterStyle.display === 'block') {

                    } else if (mode === 'original' && afterStyle.display === 'none') {

                    } else {
                        console.error('❌ 状态验证失败!', {
                            期望模式: mode,
                            实际display: afterStyle.display,
                            期望display: mode === 'custom' ? 'block' : 'none'
                        });
                    }
                }, 10);
                
            } else {
                console.error('❌ 错误:找不到元素', { 
                    namingMode: namingMode ? '找到' : '未找到',
                    customOptions: customOptions ? '找到' : '未找到',
                    allElements: document.querySelectorAll('[id]').length + '个带ID的元素'
                });
                
                // 列出所有带ID的元素用于调试
                const allIds = Array.from(document.querySelectorAll('[id]')).map(el => el.id);

            }
        };
        
        // 加载当前设置
        this.loadSettings(dialog);
        
        // 设置文件夹结构事件
        this.setupFolderStructureEvents(dialog);
        
        // 添加自动保存功能
        this.setupAutoSave(dialog);
        
        // 添加选项卡切换样式
        this.addSettingsTabStyles();
    }

    // 切换设置选项卡
    switchSettingsTab(tabName) {
        // 切换选项卡激活状态
        document.querySelectorAll('.settings-tab').forEach(tab => {
            if (tab.dataset.tab === tabName) {
                tab.classList.add('active');
                tab.style.borderBottom = '2px solid var(--theme_primary, #0F7AF5)';
                tab.style.color = 'var(--theme_primary, #0F7AF5)';
                tab.style.fontWeight = '600';
            } else {
                tab.classList.remove('active');
                tab.style.borderBottom = 'none';
                tab.style.color = 'var(--base_gray_070, rgba(22, 30, 38, 0.7))';
                tab.style.fontWeight = 'normal';
            }
        });
        
        // 切换面板显示
        document.querySelectorAll('.settings-panel').forEach(panel => {
            panel.style.display = 'none';
        });
        const activePanel = document.getElementById(`settings-panel-${tabName}`);
        if (activePanel) {
            activePanel.style.display = 'block';
        }
    }

    // 添加选项卡样式
    addSettingsTabStyles() {
        if (document.getElementById('settings-tab-styles')) return;
        
        const style = document.createElement('style');
        style.id = 'settings-tab-styles';
        style.textContent = `
            .settings-tab {
                position: relative;
                user-select: none;
            }
            .settings-tab:hover:not(.active) {
                color: var(--theme_primary, #0F7AF5) !important;
                background: var(--base_gray_005, rgba(20, 46, 77, 0.05));
            }
            .settings-tab:active {
                transform: translateY(1px);
            }
            .settings-tab svg {
                transition: transform 0.2s;
            }
            .settings-tab:hover svg {
                transform: scale(1.1);
            }
            #settings-close-x:hover {
                background: var(--base_gray_005, rgba(20, 46, 77, 0.05));
                color: var(--base_gray_100, #13181D);
            }
            .settings-panel {
                animation: fadeIn 0.3s ease-out;
            }
            @keyframes fadeIn {
                from { opacity: 0; transform: translateY(10px); }
                to { opacity: 1; transform: translateY(0); }
            }
        `;
        document.head.appendChild(style);
    }

    loadSettings(dialog) {

        const settings = this.downloadSettings;
        
        // 文件命名设置
        const namingModeValue = settings.fileNaming.useCustomPattern ? 'custom' : 'original';

        dialog.querySelector('#naming-mode').value = namingModeValue;
        dialog.querySelector('#naming-prefix').value = settings.fileNaming.prefix || '';
        dialog.querySelector('#naming-suffix').value = settings.fileNaming.suffix || '';
        dialog.querySelector('#naming-pattern').value = settings.fileNaming.customPattern || '{date}_{subject}_{fileName}';
        
        // 文件名规范检查设置
        dialog.querySelector('#filename-validation').value = settings.fileNaming.validation?.enabled !== false ? 'enabled' : 'disabled';
        dialog.querySelector('#validation-pattern').value = settings.fileNaming.validation?.pattern || '\\d{6,}';
        dialog.querySelector('#fallback-pattern').value = settings.fileNaming.validation?.fallbackPattern || 'auto';
        dialog.querySelector('#fallback-template').value = settings.fileNaming.validation?.fallbackTemplate || '{subject}_{fileName}';
        
        // 内容替换设置
        const contentReplacement = settings.fileNaming.validation?.contentReplacement;
        dialog.querySelector('#enable-content-replacement').checked = contentReplacement?.enabled || false;
        dialog.querySelector('#replacement-mode').value = contentReplacement?.mode || 'string';
        dialog.querySelector('#replacement-search').value = contentReplacement?.search || '';
        dialog.querySelector('#replacement-replace').value = contentReplacement?.replace || '';
        dialog.querySelector('#replacement-global').checked = contentReplacement?.global !== false;
        dialog.querySelector('#replacement-case-sensitive').checked = contentReplacement?.caseSensitive || false;
        
        // 文件名字符处理设置
        const removeInvalidChars = settings.fileNaming.validation?.removeInvalidChars !== false;
        const replacementChar = settings.fileNaming.validation?.replacementChar || '_';
        
        if (removeInvalidChars && replacementChar) {
            dialog.querySelector('#invalid-char-handling').value = 'replace';
        } else if (removeInvalidChars && !replacementChar) {
            dialog.querySelector('#invalid-char-handling').value = 'remove';
        } else {
            dialog.querySelector('#invalid-char-handling').value = 'keep';
        }
        dialog.querySelector('#invalid-char-replacement').value = replacementChar;
        
        // 文件夹结构设置
        dialog.querySelector('#folder-structure').value = settings.folderStructure || 'flat';
        dialog.querySelector('#smart-grouping').checked = settings.smartGrouping?.enabled || false;
        
        // 自定义文件夹模板设置
        const folderNaming = settings.folderNaming || {};
        dialog.querySelector('#folder-custom-template').value = folderNaming.customTemplate || '{date:YYYY-MM}/{senderName}';
        
        // 下载行为设置
        dialog.querySelector('#concurrent-downloads').value = settings.downloadBehavior?.concurrentDownloads || 'auto';
        dialog.querySelector('#conflict-resolution').value = settings.conflictResolution || 'rename';
        dialog.querySelector('#show-progress').checked = settings.downloadBehavior?.showProgress !== false;
        dialog.querySelector('#retry-on-fail').checked = settings.downloadBehavior?.retryOnFail !== false;
        dialog.querySelector('#verify-downloads').checked = settings.downloadBehavior?.verifyDownloads !== false;
        dialog.querySelector('#notify-complete').checked = settings.downloadBehavior?.notifyOnComplete !== false;
        dialog.querySelector('#auto-compare').checked = settings.downloadBehavior?.autoCompareAfterDownload !== false;
        
        // 过滤设置
        dialog.querySelector('#min-file-size').value = this.filters?.minSize || '';
        dialog.querySelector('#max-file-size').value = this.filters?.maxSize ? this.filters.maxSize / (1024 * 1024) : '';
        dialog.querySelector('#allowed-types').value = this.filters?.allowedTypes?.join(',') || '';
        dialog.querySelector('#excluded-types').value = this.filters?.excludedTypes?.join(',') || '';
        
        // 手动触发显示状态更新(因为程序设置value不会触发onchange事件)
        setTimeout(() => {
            if (window.toggleNamingOptions) {
                window.toggleNamingOptions();
            }

            if (window.toggleFallbackTemplate) {
                window.toggleFallbackTemplate();
            }
            
            if (window.toggleFilenameValidationOptions) {
                window.toggleFilenameValidationOptions();
            }
            
            if (window.toggleCharHandlingInput) {
                window.toggleCharHandlingInput();
            }
            
            if (window.toggleContentReplacementOptions) {
                window.toggleContentReplacementOptions();
            }
        }, 50);
    }

    setupFolderStructureEvents(dialog) {
        const folderStructureSelect = dialog.querySelector('#folder-structure');
        const folderNamingOptions = dialog.querySelector('#folder-naming-options');
        
        if (!folderStructureSelect || !folderNamingOptions) return;
        
        // 显示/隐藏文件夹命名选项
        const toggleFolderNamingOptions = () => {
            const selectedValue = folderStructureSelect.value;
            const shouldShow = selectedValue === 'custom';
            folderNamingOptions.style.display = shouldShow ? 'block' : 'none';
        };
        
        // 初始化显示状态
        toggleFolderNamingOptions();
        
        // 监听选择器变化
        folderStructureSelect.addEventListener('change', toggleFolderNamingOptions);
        
        // 为自定义文件夹模板按钮添加事件监听器
        const customVariableBtn = dialog.querySelector('#show-folder-custom-variables-btn');
        if (customVariableBtn) {
            customVariableBtn.addEventListener('click', () => {
                this.showVariableSelector('folder-custom-template');
            });
        }
    }

    setupAutoSave(dialog) {
        // 获取所有需要监听的表单元素
        const formElements = dialog.querySelectorAll('input, select, textarea');
        
        // 为每个表单元素添加自动保存监听器
        formElements.forEach(element => {
            const eventType = element.type === 'checkbox' ? 'change' : 
                             element.tagName === 'SELECT' ? 'change' : 'input';
            
            element.addEventListener(eventType, () => {
                // 延迟保存,避免频繁保存
                clearTimeout(this.autoSaveTimeout);
                this.autoSaveTimeout = setTimeout(() => {
                    this.saveSettings(dialog);
                    this.showToast('设置已自动保存', 'success', 1500);
                }, 500);
            });
        });
    }

    showVariableSelector(targetInputId) {
        // 定义可用变量,按类别分组
        const variableGroups = [
            {
                title: '✉️ 邮件信息',
                variables: [
                    { key: '{subject}', name: '邮件主题', description: '邮件的主题内容', example: '重要文件' },
                    { key: '{sender}', name: '发件人', description: '发件人姓名', example: '张三' },
                    { key: '{senderEmail}', name: '发件人邮箱', description: '发件人邮箱地址', example: '[email protected]' },
                    { key: '{senderName}', name: '发件人名称', description: '发件人显示名称', example: '张三' },
                    { key: '{mailIndex}', name: '邮件编号', description: '邮件在文件夹中的编号(根据总数动态补零,最少2位)', example: '0001' },
                    { key: '{mailId}', name: '邮件ID', description: '邮件唯一标识符', example: 'ZL0012_wnrNBwSMZGQu72gAZv4Zkf6' }
                ]
            },
            {
                title: '📁 文件夹信息',
                variables: [
                    { key: '{folderName}', name: '文件夹名称', description: '当前文件夹的名称', example: '我的文件夹' },
                    { key: '{folderID}', name: '文件夹ID', description: '当前文件夹的ID', example: '2001' }
                ]
            },
            {
                title: '📎 附件信息',
                variables: [
                    { key: '{fileName}', name: '完整文件名', description: '附件的原始文件名', example: '报告.pdf' },
                    { key: '{fileNameNoExt}', name: '文件名', description: '不含扩展名的文件名', example: '报告' },
                    { key: '{fileType}', name: '文件扩展名', description: '文件的扩展名', example: 'pdf' },
                    { key: '{size}', name: '文件大小', description: '文件大小 (字节)', example: '1024' },
                                    { key: '{fileIndex}', name: '附件编号', description: '附件在文件夹中的编号(根据总数动态补零,最少2位)', example: '0001' },
                { key: '{attachIndex}', name: '邮件内序号', description: '附件在本邮件中的序号(根据总数动态补零,最少2位)', example: '01' },
                    { key: '{fileId}', name: '附件ID', description: '附件的唯一标识符', example: 'ZF0012_wnrNBwSMZGQu72gAYvkZSf6' }
                ]
            },
            {
                title: '📅 常用日期时间',
                variables: [
                    { key: '{date}', name: '标准日期', description: '邮件日期 (YYYY-MM-DD)', example: '2024-01-15' },
                    { key: '{time}', name: '标准时间', description: '邮件时间 (HH-mm-ss)', example: '14-30-25' },
                    { key: '{datetime}', name: '日期时间', description: '完整日期时间', example: '2024-01-15_14-30-25' },
                    { key: '{timestamp}', name: '时间戳', description: 'Unix时间戳 (秒)', example: '1705312225' },
                    { key: '{year}', name: '年份', description: '四位年份 (2024)', example: '2024' },
                    { key: '{month}', name: '月份', description: '两位月份 (01-12)', example: '01' },
                    { key: '{day}', name: '日期', description: '两位日期 (01-31)', example: '15' }
                ]
            },
            {
                title: '🔧 自定义日期格式',
                variables: [
                    { key: '{date:YYYY-MM-DD}', name: '完整日期', description: '年-月-日 (2024-01-15)', example: '2024-01-15' },
                    { key: '{date:YYYY-MM}', name: '年月', description: '年-月 (2024-01)', example: '2024-01' },
                    { key: '{date:MM-DD}', name: '月日', description: '月-日 (01-15)', example: '01-15' },
                    { key: '{date:YYYY}', name: '年份', description: '四位年份 (2024)', example: '2024' },
                    { key: '{date:MM}', name: '月份', description: '两位月份 (01)', example: '01' },
                    { key: '{date:DD}', name: '日期', description: '两位日期 (15)', example: '15' },
                    { key: '{date:HH-mm-ss}', name: '完整时间', description: '时-分-秒 (14-30-25)', example: '14-30-25' },
                    { key: '{date:HH-mm}', name: '时分', description: '时-分 (14-30)', example: '14-30' },
                    { key: '{date:HH}', name: '24小时制', description: '24小时制 (14)', example: '14' },
                    { key: '{date:hh}', name: '12小时制', description: '12小时制 (02)', example: '02' },
                    { key: '{date:mm}', name: '分钟', description: '分钟 (30)', example: '30' },
                    { key: '{date:ss}', name: '秒数', description: '秒数 (25)', example: '25' }
                ]
            },
            {
                title: '🌐 中文时间',
                variables: [
                    { key: '{weekday}', name: '星期', description: '中文星期 (星期一)', example: '星期一' },
                    { key: '{weekdayName}', name: '周', description: '简写星期 (周一)', example: '周一' },
                    { key: '{monthName}', name: '月份', description: '中文月份 (一月)', example: '一月' },
                    { key: '{ampm}', name: '上下午', description: '中文上下午', example: '下午' },
                    { key: '{AMPM}', name: 'AM/PM', description: '英文AM/PM', example: 'PM' }
                ]
            }
        ];

        // 创建弹出层
        const overlay = document.createElement('div');
        overlay.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.6);
            z-index: 10001;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
            box-sizing: border-box;
        `;

        const popup = document.createElement('div');
        popup.style.cssText = `
            background: white;
            border-radius: 16px;
            width: 90%;
            max-width: 1000px;
            max-height: 100vh;
            box-shadow: 0 12px 48px rgba(0,0,0,0.25);
            position: relative;
            animation: slideIn 0.3s ease-out;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        `;
        
        // 确保变量选择器样式已添加
        this.ensureVariableSelectorStyles();

        popup.innerHTML = `
            <!-- 弹窗头部 -->
            <div class="popup-header">
                <div style="display: flex; justify-content: space-between; align-items: center;">
                    <div>
                        <div style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 2px;">变量选择器</div>
                        <div style="color: #666; font-size: 13px;">点击变量插入到模板中</div>
                    </div>
                    <button id="close-variable-selector" style="background: #f8f8f8; border: 1px solid #ddd; border-radius: 6px; width: 28px; height: 28px; cursor: pointer; color: #666; font-size: 16px; display: flex; align-items: center; justify-content: center;">×</button>
                </div>
            </div>
            
            <!-- 模板编辑区域 -->
            <div class="template-section">
                <label style="display: block; font-size: 14px; font-weight: 500; color: #333; margin-bottom: 8px;">模板编辑</label>
                <div style="position: relative;">
                    <input type="text" id="template-preview" style="width: 100%; padding: 10px 80px 10px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; font-family: monospace; background: white; box-sizing: border-box; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
                    <button id="apply-template" style="position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: #007bff; color: white; border: none; border-radius: 4px; padding: 6px 12px; font-size: 12px; cursor: pointer; font-weight: 500;">应用</button>
                </div>
                <div style="margin-top: 8px; font-size: 12px; color: #666;">
                    <span style="color: #999;">预览:</span>
                    <span id="template-preview-example" style="font-family: monospace; color: #333; background: #f0f0f0; padding: 2px 6px; border-radius: 3px;">请输入模板</span>
                </div>
                <div id="filename-length-warning" style="margin-top: 4px; font-size: 11px; display: none;"></div>
            </div>
            
            <!-- 变量选择区域 -->
            <div class="variables-section">
                <div style="margin-bottom: 12px;">
                    <div style="font-size: 14px; font-weight: 500; color: #333; margin-bottom: 4px;">可用变量</div>
                    <div style="font-size: 12px; color: #666;">点击任意变量插入到模板中,模板不包含最终文件扩展名</div>
                </div>
                <div id="variables-container">
                    ${variableGroups.map(group => `
                        <div class="variable-group">
                            <div class="variable-group-title">${group.title}</div>
                            <div class="variables-grid">
                                ${group.variables.map(variable => `
                                    <div class="variable-item" data-variable="${variable.key}">
                                        <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 3px;">
                                            <span class="variable-key">${variable.key}</span>
                                            <span class="variable-name">${variable.name}</span>
                                        </div>
                                        <div class="variable-desc">${variable.description}</div>
                                    </div>
                                `).join('')}
                            </div>
                        </div>
                    `).join('')}
                </div>
            </div>
            
            <!-- 快捷操作区域 -->
            <div class="popup-footer">
                <div style="display: flex; justify-content: center; gap: 8px; flex-wrap: wrap;">
                    <button id="clear-template" style="padding: 8px 16px; border: 1px solid #ccc; background: white; border-radius: 4px; cursor: pointer; font-size: 12px; color: #666; font-weight: 500;">清空模板</button>
                    <button id="insert-common-pattern" style="padding: 8px 16px; border: 1px solid #007bff; background: #007bff; color: white; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500;">常用模式</button>
                    <button id="insert-detailed-pattern" style="padding: 8px 16px; border: 1px solid #28a745; background: #28a745; color: white; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500;">详细模式</button>
                </div>
            </div>
        `;

        overlay.appendChild(popup);
        document.body.appendChild(overlay);

        // 添加事件监听器
        const targetInput = document.querySelector(`#${targetInputId}`);
        const templatePreview = popup.querySelector('#template-preview');
        const previewExample = popup.querySelector('#template-preview-example');
        
        // 更新预览示例
        const updatePreviewExample = () => {
            if (previewExample && templatePreview) {
                const template = templatePreview.value;
                if (!template || template.trim() === '') {
                    previewExample.textContent = '请输入模板';
                    previewExample.title = '';
                    return;
                }
                
                // 从变量定义中提取示例数据
                const sampleData = {};
                variableGroups.forEach(group => {
                    group.variables.forEach(variable => {
                        sampleData[variable.key] = variable.example;
                    });
                });
                
                // 为预览创建一个简化的替换函数
                let example = template;
                Object.keys(sampleData).forEach(key => {
                    const value = sampleData[key];
                    if (value !== undefined && value !== null) {
                        // 使用全局替换,确保所有实例都被替换
                        example = example.replaceAll(key, String(value));
                    }
                });
                
                // 只在自定义文件名模式下添加扩展名示例
                if (targetInputId === 'naming-pattern') {
                    // 检查最后几个字符是否已经包含扩展名,避免重复添加
                    const lastPart = example.slice(-10);
                    if (!lastPart.includes('.')) {
                        example += '.pdf';
                    }
                }

                previewExample.textContent = example;
                previewExample.title = '';
                
                // 检查文件名长度并显示警告
                const warningElement = popup.querySelector('#filename-length-warning');
                if (warningElement) {
                    const nameLength = example.length;
                    if (nameLength > 255) {
                        warningElement.style.display = 'block';
                        warningElement.style.color = '#F73116';
                        warningElement.innerHTML = '⚠️ 已超过系统文件名最大长度! (' + nameLength + ' 字符)'
                    } else if (nameLength > 140) {
                        warningElement.style.display = 'block';
                        warningElement.style.color = '#F7A316';
                        warningElement.innerHTML = '⚠️ 文件名太长 (' + nameLength + ' 字符),可能会导致保存失败';
                    } else if (nameLength > 80) {
                        warningElement.style.display = 'block';
                        warningElement.style.color = '#F7A316';
                        warningElement.innerHTML = '⚠️ 文件名较长 (' + nameLength + ' 字符),建议简化模板';
                    } else if (nameLength > 50) {
                        warningElement.style.display = 'block';
                        warningElement.style.color = '#3498db';
                        warningElement.innerHTML = 'ℹ️ 文件名长度:' + nameLength + ' 字符';
                    } else {
                        warningElement.style.display = 'none';
                    }
                }
            }
        };
        
        // 初始化模板预览
        if (targetInput && templatePreview) {
            templatePreview.value = targetInput.value;
            updatePreviewExample();
        }
        
        // 监听模板输入变化
        templatePreview.addEventListener('input', updatePreviewExample);
        
        // 变量点击事件
        popup.querySelectorAll('.variable-item').forEach(item => {
            item.addEventListener('click', () => {
                const variable = item.dataset.variable;
                if (templatePreview) {
                    // 在模板预览中插入变量
                    const cursorPos = templatePreview.selectionStart || templatePreview.value.length;
                    const currentValue = templatePreview.value;
                    const newValue = currentValue.slice(0, cursorPos) + variable + currentValue.slice(cursorPos);
                    templatePreview.value = newValue;
                    
                    // 设置新的光标位置
                    setTimeout(() => {
                        templatePreview.focus();
                        templatePreview.setSelectionRange(cursorPos + variable.length, cursorPos + variable.length);
                    }, 10);
                    
                    this.showToast(`已插入: ${variable}`, 'success', 1000);
                    updatePreviewExample(); // 更新预览
                }
            });
        });
        
        // 应用模板按钮
        popup.querySelector('#apply-template').addEventListener('click', () => {
            if (targetInput && templatePreview) {
                targetInput.value = templatePreview.value;
                targetInput.focus();
                this.showToast('模板已应用', 'success', 1500);
                document.body.removeChild(overlay);
            }
        });
        
        // 让模板预览可编辑
        templatePreview.removeAttribute('readonly');

        // 关闭按钮
        popup.querySelector('#close-variable-selector').addEventListener('click', () => {
            document.body.removeChild(overlay);
        });

        // 清空模板按钮
        popup.querySelector('#clear-template').addEventListener('click', () => {
            if (templatePreview) {
                templatePreview.value = '';
                templatePreview.focus();
                updatePreviewExample();
                this.showToast('模板已清空', 'info', 1000);
            }
        });

        // 插入常用模式按钮
        popup.querySelector('#insert-common-pattern').addEventListener('click', () => {
            if (templatePreview) {
                templatePreview.value = '{date:YYYY-MM-DD}_{subject}_{fileName}';
                templatePreview.focus();
                updatePreviewExample();
                this.showToast('已插入常用模式', 'success', 1000);
            }
        });

        // 插入详细模式按钮
        popup.querySelector('#insert-detailed-pattern').addEventListener('click', () => {
            if (templatePreview) {
                templatePreview.value = '{date:YYYY-MM-DD}_{date:HH-mm}_{senderName}_{subject}_{fileIndex}_{fileName}';
                templatePreview.focus();
                updatePreviewExample();
                this.showToast('已插入详细模式', 'success', 1000);
            }
        });

        // 点击背景关闭
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                document.body.removeChild(overlay);
            }
        });

        // ESC键关闭
        const handleEsc = (e) => {
            if (e.key === 'Escape') {
                document.body.removeChild(overlay);
                document.removeEventListener('keydown', handleEsc);
            }
        };
        document.addEventListener('keydown', handleEsc);
    }

    saveSettings(dialog) {
        try {
            // 基础设置 - 文件命名
            const namingMode = dialog.querySelector('#naming-mode').value;
            this.downloadSettings.fileNaming.useCustomPattern = (namingMode === 'custom');
            this.downloadSettings.fileNaming.prefix = dialog.querySelector('#naming-prefix').value;
            this.downloadSettings.fileNaming.suffix = dialog.querySelector('#naming-suffix').value;
            this.downloadSettings.fileNaming.customPattern = dialog.querySelector('#naming-pattern').value;
            
            // 基础设置 - 文件夹结构
            this.downloadSettings.folderStructure = dialog.querySelector('#folder-structure').value;
            if (!this.downloadSettings.smartGrouping) this.downloadSettings.smartGrouping = {};
            this.downloadSettings.smartGrouping.enabled = dialog.querySelector('#smart-grouping').checked;
            
            // 保存自定义文件夹模板设置
            if (!this.downloadSettings.folderNaming) this.downloadSettings.folderNaming = {};
            this.downloadSettings.folderNaming.customTemplate = dialog.querySelector('#folder-custom-template').value || '{date:YYYY-MM}/{senderName}';
            
            // 文件名规范检查设置
            if (!this.downloadSettings.fileNaming.validation) this.downloadSettings.fileNaming.validation = {};
            this.downloadSettings.fileNaming.validation.enabled = dialog.querySelector('#filename-validation').value === 'enabled';
            this.downloadSettings.fileNaming.validation.pattern = dialog.querySelector('#validation-pattern').value || '\\d{6,}';
            this.downloadSettings.fileNaming.validation.fallbackPattern = dialog.querySelector('#fallback-pattern').value || 'auto';
            this.downloadSettings.fileNaming.validation.fallbackTemplate = dialog.querySelector('#fallback-template').value || '{subject}_{fileName}';
            
            // 内容替换设置
            if (!this.downloadSettings.fileNaming.validation.contentReplacement) {
                this.downloadSettings.fileNaming.validation.contentReplacement = {};
            }
            this.downloadSettings.fileNaming.validation.contentReplacement.enabled = dialog.querySelector('#enable-content-replacement').checked;
            this.downloadSettings.fileNaming.validation.contentReplacement.mode = dialog.querySelector('#replacement-mode').value || 'string';
            this.downloadSettings.fileNaming.validation.contentReplacement.search = dialog.querySelector('#replacement-search').value || '';
            this.downloadSettings.fileNaming.validation.contentReplacement.replace = dialog.querySelector('#replacement-replace').value || '';
            this.downloadSettings.fileNaming.validation.contentReplacement.global = dialog.querySelector('#replacement-global').checked;
            this.downloadSettings.fileNaming.validation.contentReplacement.caseSensitive = dialog.querySelector('#replacement-case-sensitive').checked;
            // 文件名字符处理设置
            const charHandling = dialog.querySelector('#invalid-char-handling').value;
            const replacementChar = dialog.querySelector('#invalid-char-replacement').value || '_';
            
            switch (charHandling) {
                case 'replace':
                    this.downloadSettings.fileNaming.validation.removeInvalidChars = true;
                    this.downloadSettings.fileNaming.validation.replacementChar = replacementChar;
                    break;
                case 'remove':
                    this.downloadSettings.fileNaming.validation.removeInvalidChars = true;
                    this.downloadSettings.fileNaming.validation.replacementChar = '';
                    break;
                case 'keep':
                default:
                    this.downloadSettings.fileNaming.validation.removeInvalidChars = false;
                    this.downloadSettings.fileNaming.validation.replacementChar = '';
                    break;
            }
            

            
            // 下载行为设置
            if (!this.downloadSettings.downloadBehavior) this.downloadSettings.downloadBehavior = {};
            const concurrentValue = dialog.querySelector('#concurrent-downloads').value;
            this.downloadSettings.downloadBehavior.concurrentDownloads = concurrentValue === 'auto' ? 'auto' : parseInt(concurrentValue);
            this.downloadSettings.conflictResolution = dialog.querySelector('#conflict-resolution').value;
            this.downloadSettings.downloadBehavior.showProgress = dialog.querySelector('#show-progress').checked;
            this.downloadSettings.downloadBehavior.retryOnFail = dialog.querySelector('#retry-on-fail').checked;
            this.downloadSettings.downloadBehavior.verifyDownloads = dialog.querySelector('#verify-downloads').checked;
            this.downloadSettings.downloadBehavior.notifyOnComplete = dialog.querySelector('#notify-complete').checked;
        this.downloadSettings.downloadBehavior.autoCompareAfterDownload = dialog.querySelector('#auto-compare').checked;
            
            // 过滤设置
            const minSize = parseInt(dialog.querySelector('#min-file-size').value) || 0;
            const maxSize = parseInt(dialog.querySelector('#max-file-size').value) || 0;
            const allowedTypes = dialog.querySelector('#allowed-types').value.split(',').map(t => t.trim()).filter(t => t);
            const excludedTypes = dialog.querySelector('#excluded-types').value.split(',').map(t => t.trim()).filter(t => t);
            
            this.filters.minSize = minSize * 1024; // 转换为字节
            this.filters.maxSize = maxSize > 0 ? maxSize * 1024 * 1024 : 0; // 转换为字节
            this.filters.allowedTypes = allowedTypes;
            this.filters.excludedTypes = excludedTypes;
            
            // 保存到本地存储
            localStorage.setItem('qqmail-downloader-settings', JSON.stringify({
                downloadSettings: this.downloadSettings,
                filters: this.filters
            }));
            
            this.showToast('设置已保存', 'success');
            
            // 更新文件夹结构预览(如果当前有附件数据)
            if (this.attachments && this.attachments.length > 0) {
                this.updateFolderStructurePreview(this.attachments);
            }
        } catch (error) {
            console.error('保存设置失败:', error);
            this.showToast('保存设置失败', 'error');
        }
    }

    resetSettings(dialog) {
        // 重置为默认设置
        this.downloadSettings = {
            fileNaming: {
                prefix: '',
                suffix: '',
                includeMailId: false,
                includeAttachmentId: false,
                includeMailSubject: false,
                includeFileType: false,
                separator: '_',
                useCustomPattern: false,
                customPattern: '{date}_{subject}_{fileName}',
                validation: {
                    enabled: true,
                    pattern: '\\d{6,}',
                    fallbackPattern: 'auto',
                    fallbackTemplate: '{subject}_{fileName}',
                    replacementChar: '_',
                    removeInvalidChars: true,
                    contentReplacement: {
                        enabled: false,
                        mode: 'string',
                        search: '',
                        replace: '',
                        global: true,
                        caseSensitive: false
                    }
                }
            },
            folderStructure: 'flat',
            folderNaming: {
                mailTemplate: '{subject}',
                dateTemplate: '{date:YYYY-MM-DD}',
                senderTemplate: '{senderName}',
                customTemplate: '{date:YYYY-MM}/{senderName}'
            },
            conflictResolution: 'rename',
            downloadBehavior: {
                showProgress: true,
                retryOnFail: true,
                verifyDownloads: true,
                notifyOnComplete: true,
                concurrentDownloads: 'auto',
                autoCompareAfterDownload: true
            },
            smartGrouping: {
                enabled: false
            }
        };
        
        this.filters = {
            date: 'all',
            dateRange: {
                start: null,
                end: null
            },
            minSize: 0,
            maxSize: 0,
            allowedTypes: [],
            excludedTypes: []
        };
        
        // 重新加载设置到界面
        this.loadSettings(dialog);
        
        // 自动保存重置后的设置
        this.saveSettings(dialog);
        
        this.showToast('设置已重置为默认值', 'info');
    }

    // 判断是否应该使用自定义命名
    shouldUseCustomNaming() {
        return this.downloadSettings.fileNaming.useCustomPattern || false;
    }

    // 在初始化时加载保存的设置
    loadSavedSettings() {
        try {
            const saved = localStorage.getItem('qqmail-downloader-settings');
            if (saved) {
                const settings = JSON.parse(saved);
                if (settings.downloadSettings) {
                    this.downloadSettings = { ...this.downloadSettings, ...settings.downloadSettings };
                }
                if (settings.filters) {
                    this.filters = { ...this.filters, ...settings.filters };
                }
            }
        } catch (error) {
            console.warn('加载保存的设置失败:', error);
        }
    }

    async showCompareDialog() {

        try {
            // 检查邮件附件数据
            const emailAttachments = this.attachments;
            if (emailAttachments.length === 0) {
                this.showToast('当前文件夹没有邮件附件数据,请确保已打开附件管理器并加载了邮件数据', 'warning');
                return;
            }

            // 直接弹出文件夹选择器
            const dirHandle = await window.showDirectoryPicker();

            
            // 创建比对结果对话框
            await this.showComparisonResults(dirHandle);
            
        } catch (error) {
            if (error.name !== 'AbortError') {

                this.showToast('比对功能执行失败: ' + error.message, 'error');
            }
        }
    }

    // 显示比对结果对话框
    async showComparisonResults(dirHandle) {
        // 创建比对结果对话框
        const dialog = this.createUI('div', {
            className: 'compare-results-dialog',
            styles: {
                position: 'fixed',
                top: '0',
                left: '0',
                width: '100%',
                height: '100%',
                backgroundColor: 'rgba(0, 0, 0, 0.5)',
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                zIndex: '100000'
            }
        });

        const dialogContent = this.createUI('div', {
            styles: {
                backgroundColor: 'white',
                borderRadius: '12px',
                padding: '24px',
                maxWidth: '900px',
                width: '95%',
                maxHeight: '85vh',
                overflow: 'auto',
                boxShadow: '0 20px 60px rgba(0, 0, 0, 0.15)'
            }
        });

        dialogContent.innerHTML = `
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                <h3 style="margin: 0; font-size: 18px; color: #13181D;">文件完整性比对结果</h3>
                <button class="close-dialog-btn" style="background: none; border: none; font-size: 24px; cursor: pointer; color: #666; padding: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;">×</button>
            </div>
            <div id="compare-results" style="display: none;">
                <div id="compare-summary" style="background: #f8f9fa; padding: 16px; border-radius: 6px; margin-bottom: 16px;">
                    <div style="text-align: center; padding: 20px;">正在比对文件...</div>
                </div>
                <div id="missing-files" style="margin-bottom: 16px;"></div>
                <div id="duplicate-files" style="margin-bottom: 16px;"></div>
                <div id="matched-files" style="margin-bottom: 16px;"></div>
            </div>
        `;

        dialog.appendChild(dialogContent);
        document.body.appendChild(dialog);

        const closeDialog = () => document.body.removeChild(dialog);
        
        // 添加事件监听器
        dialogContent.querySelector('.close-dialog-btn').addEventListener('click', closeDialog);
        dialog.addEventListener('click', (e) => e.target === dialog && closeDialog());

        // 添加下载按钮事件监听器
        dialog.addEventListener('click', (e) => {
            if (e.target.classList.contains('download-single-btn')) {
                const fileid = e.target.dataset.fileid;
                if (fileid) {
                    this.downloadSingleAttachment(fileid);
                }
            }
        });

        // 执行比对
        await this.performComparison(dirHandle, dialogContent);
    }

    // 执行比对
    async performComparison(dirHandle, dialogContent) {
        const resultsDiv = dialogContent.querySelector('#compare-results');
        const summaryDiv = dialogContent.querySelector('#compare-summary');
        const missingDiv = dialogContent.querySelector('#missing-files');
        const duplicateDiv = dialogContent.querySelector('#duplicate-files');
        const matchedDiv = dialogContent.querySelector('#matched-files');

        // 创建进度显示界面
        const createProgressUI = () => {
            return `
                <div style="text-align: center; padding: 20px;">
                    <div style="margin-bottom: 16px;">
                        <div id="progress-title" style="font-size: 16px; font-weight: 500; color: #13181D; margin-bottom: 8px;">正在比对文件...</div>
                        <div id="progress-subtitle" style="font-size: 14px; color: #666; margin-bottom: 12px;">准备中...</div>
                    </div>
                    <div style="background: #f5f5f5; border-radius: 8px; padding: 4px; margin-bottom: 12px;">
                        <div id="progress-bar" style="height: 8px; background: linear-gradient(90deg, #0F7AF5, #40a9ff); border-radius: 4px; width: 0%; transition: width 0.3s ease;"></div>
                    </div>
                    <div id="progress-details" style="font-size: 12px; color: #999; display: flex; justify-content: space-between;">
                        <span id="progress-current">0</span>
                        <span id="progress-total">0</span>
                    </div>
                </div>
            `;
        };

        // 更新进度显示
        const updateProgress = (title, subtitle, current, total) => {
            const progressTitle = summaryDiv.querySelector('#progress-title');
            const progressSubtitle = summaryDiv.querySelector('#progress-subtitle');
            const progressBar = summaryDiv.querySelector('#progress-bar');
            const progressCurrent = summaryDiv.querySelector('#progress-current');
            const progressTotal = summaryDiv.querySelector('#progress-total');

            if (progressTitle) progressTitle.textContent = title;
            if (progressSubtitle) progressSubtitle.textContent = subtitle;
            if (progressBar) progressBar.style.width = total > 0 ? `${(current / total) * 100}%` : '0%';
            if (progressCurrent) progressCurrent.textContent = current;
            if (progressTotal) progressTotal.textContent = total;
        };

        // 显示初始进度界面
        summaryDiv.innerHTML = createProgressUI();
        resultsDiv.style.display = 'block';

        try {
            // 步骤1: 获取本地文件信息
            updateProgress('正在扫描本地文件', '读取文件夹结构...', 0, 4);
            await new Promise(resolve => setTimeout(resolve, 100)); // 让UI更新

            console.log('开始扫描本地文件...');
            const localFiles = await this.getLocalFilesWithProgress(dirHandle, updateProgress);
            console.log(`扫描完成,找到 ${localFiles.length} 个本地文件`);

            // 步骤2: 获取邮件附件信息
            const emailAttachments = this.attachments;
            console.log(`获取邮件附件信息,共 ${emailAttachments.length} 个附件`);
            updateProgress('准备比对数据', `本地文件: ${localFiles.length} 个,邮件附件: ${emailAttachments.length} 个`, 2, 4);
            await new Promise(resolve => setTimeout(resolve, 200));

            if (emailAttachments.length === 0) {
                throw new Error('当前邮件文件夹中没有附件数据,请确保已打开附件管理器并加载了邮件数据');
            }

            // 步骤3: 执行比对分析
            console.log('开始执行比对分析...');
            const comparisonResult = await this.compareFilesWithProgress(localFiles, emailAttachments, updateProgress);
            console.log('比对分析完成:', comparisonResult.summary);

            // 步骤4: 显示比对结果
            updateProgress('生成比对报告', '正在整理结果...', 4, 4);
            await new Promise(resolve => setTimeout(resolve, 100));

            this.displayComparisonResults(comparisonResult, summaryDiv, missingDiv, duplicateDiv, matchedDiv);
        
        // 添加事件监听器
        this.setupComparisonDialogEvents(dialogContent);

        } catch (error) {
            console.error('比对过程中出错:', error);
            summaryDiv.innerHTML = `<div style="color: #e74c3c; text-align: center; padding: 20px;">
                <div style="font-size: 16px; font-weight: 500; margin-bottom: 8px;">比对失败</div>
                <div style="font-size: 14px; margin-bottom: 12px;">${error.message}</div>
                <div style="font-size: 12px; color: #999;">
                    请检查:<br>
                    1. 是否已选择正确的文件夹<br>
                    2. 文件夹是否有访问权限<br>
                    3. 浏览器控制台是否有错误信息
                </div>
            </div>`;
        }
    }

    // 获取本地文件信息
    async getLocalFiles(dirHandle, path = '') {
        const files = [];

        for await (const [name, handle] of dirHandle.entries()) {
            const fullPath = path ? `${path}/${name}` : name;

            if (handle.kind === 'file') {
                try {
                    const file = await handle.getFile();
                    files.push({
                        name: name,
                        path: fullPath,
                        size: file.size,
                        type: this.processFileName(name, 'getExtension')?.toLowerCase() || '',
                        lastModified: file.lastModified,
                        handle: handle
                    });
                } catch (error) {
                    // 静默忽略文件读取错误
                }
            } else if (handle.kind === 'directory') {
                // 递归读取子文件夹
                const subFiles = await this.getLocalFiles(handle, fullPath);
                files.push(...subFiles);
            }
        }

        return files;
    }

    // 带进度显示的获取本地文件信息
    async getLocalFilesWithProgress(dirHandle, updateProgress, path = '') {
        const files = [];
        let processedCount = 0;
        let totalCount = 0;
        const visitedPaths = new Set(); // 防止循环引用
        const maxDepth = 10; // 最大递归深度
        const maxFiles = 10000; // 最大文件数限制
        const startTime = Date.now();
        const maxTime = 30000; // 30秒超时

        try {
            // 首先统计总文件数(带限制)
            const countFiles = async (handle, currentPath = '', depth = 0) => {
                // 检查超时
                if (Date.now() - startTime > maxTime) {
                    throw new Error('文件扫描超时,请选择文件数量较少的文件夹');
                }

                // 检查深度限制
                if (depth > maxDepth) {
                    console.warn(`文件夹层级过深,跳过: ${currentPath}`);
                    return 0;
                }

                // 检查路径循环
                const normalizedPath = currentPath.toLowerCase();
                if (visitedPaths.has(normalizedPath)) {
                    console.warn(`检测到循环引用,跳过: ${currentPath}`);
                    return 0;
                }
                visitedPaths.add(normalizedPath);

                let count = 0;
                try {
                    const entries = [];
                    for await (const [name, subHandle] of handle.entries()) {
                        entries.push([name, subHandle]);
                        // 限制单个文件夹的条目数
                        if (entries.length > 1000) {
                            console.warn(`文件夹条目过多,仅处理前1000个: ${currentPath}`);
                            break;
                        }
                    }

                    for (const [name, subHandle] of entries) {
                        if (count > maxFiles) {
                            console.warn(`文件数量超过限制(${maxFiles}),停止扫描`);
                            break;
                        }

                        if (subHandle.kind === 'file') {
                            count++;
                        } else if (subHandle.kind === 'directory') {
                            try {
                                const subPath = currentPath ? `${currentPath}/${name}` : name;
                                count += await countFiles(subHandle, subPath, depth + 1);
                            } catch (error) {
                                console.warn(`无法访问子文件夹: ${currentPath}/${name}`, error);
                            }
                        }
                    }
                } catch (error) {
                    console.warn(`无法读取文件夹内容: ${currentPath}`, error);
                }

                visitedPaths.delete(normalizedPath);
                return count;
            };

            updateProgress('正在扫描本地文件', '统计文件数量...', 0, 1);
            console.log('开始统计文件数量...');
            
            totalCount = await countFiles(dirHandle);
            console.log(`统计完成,发现 ${totalCount} 个文件`);
            
            if (totalCount === 0) {
                updateProgress('扫描完成', '未找到任何文件', 1, 1);
                return files;
            }

            if (totalCount > maxFiles) {
                throw new Error(`文件数量过多(${totalCount}),请选择包含文件较少的文件夹(建议少于${maxFiles}个)`);
            }

            updateProgress('正在扫描本地文件', `发现 ${totalCount} 个文件,开始处理...`, 0, totalCount);

            // 重置访问路径记录
            visitedPaths.clear();

            // 递归处理文件(带限制)
            const processFiles = async (handle, currentPath = '', depth = 0) => {
                // 检查超时
                if (Date.now() - startTime > maxTime) {
                    throw new Error('文件处理超时');
                }

                // 检查深度限制
                if (depth > maxDepth) {
                    return;
                }

                // 检查路径循环
                const normalizedPath = currentPath.toLowerCase();
                if (visitedPaths.has(normalizedPath)) {
                    return;
                }
                visitedPaths.add(normalizedPath);

                try {
                    const entries = [];
                    for await (const [name, subHandle] of handle.entries()) {
                        entries.push([name, subHandle]);
                        if (entries.length > 1000) break;
                    }

                    for (const [name, subHandle] of entries) {
                        if (files.length >= maxFiles) {
                            console.warn(`已达到文件数量限制(${maxFiles}),停止处理`);
                            break;
                        }

                        const fullPath = currentPath ? `${currentPath}/${name}` : name;

                        if (subHandle.kind === 'file') {
                            try {
                                const file = await subHandle.getFile();
                                files.push({
                                    name: name,
                                    path: fullPath,
                                    size: file.size,
                                    type: this.processFileName(name, 'getExtension')?.toLowerCase() || '',
                                    lastModified: file.lastModified,
                                    handle: subHandle
                                });
                                processedCount++;
                                
                                // 更新进度
                                if (processedCount % 50 === 0 || processedCount === totalCount) {
                                    updateProgress('正在扫描本地文件', `已处理 ${processedCount}/${totalCount} 个文件`, processedCount, totalCount);
                                    await new Promise(resolve => setTimeout(resolve, 10)); // 让UI更新
                                }
                            } catch (error) {
                                console.warn(`无法读取文件: ${fullPath}`, error);
                                processedCount++;
                            }
                        } else if (subHandle.kind === 'directory') {
                            try {
                                await processFiles(subHandle, fullPath, depth + 1);
                            } catch (error) {
                                console.warn(`无法处理子文件夹: ${fullPath}`, error);
                            }
                        }
                    }
                } catch (error) {
                    console.warn(`处理文件夹时出错: ${currentPath}`, error);
                }

                visitedPaths.delete(normalizedPath);
            };

            await processFiles(dirHandle, path);
            updateProgress('扫描完成', `成功处理 ${files.length} 个文件`, files.length, totalCount);
            console.log(`文件扫描完成,用时: ${Date.now() - startTime}ms`);
            
        } catch (error) {
            console.error('扫描本地文件时出错:', error);
            throw new Error(`扫描本地文件失败: ${error.message}`);
        }

        return files;
    }

    // 比对文件
    // 优化的文件比对算法(减少重复计算和内存占用)
    compareFiles(localFiles, emailAttachments) {
        const result = {
            missing: [], duplicates: [], matched: [], localOnly: [],
            summary: { 
                totalEmail: emailAttachments.length, 
                totalLocal: localFiles.length, 
                missingCount: 0, 
                duplicateCount: 0, 
                matchedCount: 0,
                emailTotalSize: 0,
                localTotalSize: 0,
                matchedTotalSize: 0,
                missingTotalSize: 0
            }
        };

        // 预处理本地文件映射(合并两个映射表逻辑)
        const localFileMap = new Map();
        const usedLocalFiles = new Set();

        localFiles.forEach(file => {
            const normalizedKey = this.normalizeFileName(file.name);
            const sizeTypeKey = `${file.size}_${file.type}`;

            if (!localFileMap.has(normalizedKey)) localFileMap.set(normalizedKey, []);
            if (!localFileMap.has(sizeTypeKey)) localFileMap.set(sizeTypeKey, []);

            localFileMap.get(normalizedKey).push({ file, type: 'exact' });
            localFileMap.get(sizeTypeKey).push({ file, type: 'fuzzy' });
        });

        // 比对邮件附件(合并精确和模糊匹配逻辑)
        emailAttachments.forEach(attachment => {
            const normalizedName = this.normalizeFileName(attachment.name);
            const sizeTypeKey = `${attachment.size}_${attachment.type}`;
            let bestMatch = null;

            // 优先精确匹配
            const exactCandidates = localFileMap.get(normalizedName) ?? [];
            for (const { file } of exactCandidates.filter(c => c.type === 'exact')) {
                if (!usedLocalFiles.has(file) && file.size === attachment.size && file.type === attachment.type) {
                    bestMatch = { file, type: 'exact', similarity: 1.0 };
                    break;
                }
            }

            // 模糊匹配
            if (!bestMatch) {
                const fuzzyCandidates = localFileMap.get(sizeTypeKey) ?? [];
                let bestSimilarity = 0;

                for (const { file } of fuzzyCandidates.filter(c => c.type === 'fuzzy')) {
                    if (usedLocalFiles.has(file)) continue;

                    const similarity = this.calculateNameSimilarity(attachment.name, file.name);
                    if (similarity > 0.6 && similarity > bestSimilarity) {
                        bestMatch = { file, type: 'renamed', similarity };
                        bestSimilarity = similarity;
                    }
                }
            }

            if (bestMatch) {
                result.matched.push({ email: attachment, local: bestMatch.file, matchType: bestMatch.type, similarity: bestMatch.similarity });
                usedLocalFiles.add(bestMatch.file);
            } else {
                result.missing.push(attachment);
            }
        });

        // 检查重复和本地独有文件(合并处理逻辑)
        const localFileUsage = new Map();
        result.matched.forEach(match => {
            const key = `${match.local.path}_${match.local.size}`;
            (localFileUsage.get(key) || localFileUsage.set(key, []).get(key)).push(match);
        });

        // 批量处理重复文件和本地独有文件
        for (const matches of localFileUsage.values()) {
            if (matches.length > 1) {
                result.duplicates.push({ localFile: matches[0].local, emailAttachments: matches.map(m => m.email) });
            }
        }
        result.localOnly = localFiles.filter(file => !usedLocalFiles.has(file));

        // 计算总大小
        result.summary.emailTotalSize = emailAttachments.reduce((sum, att) => sum + att.size, 0);
        result.summary.localTotalSize = localFiles.reduce((sum, file) => sum + file.size, 0);
        result.summary.matchedTotalSize = result.matched.reduce((sum, match) => sum + match.email.size, 0);
        result.summary.missingTotalSize = result.missing.reduce((sum, att) => sum + att.size, 0);

        // 更新统计
        Object.assign(result.summary, {
            matchedCount: result.matched.length,
            missingCount: result.missing.length,
            duplicateCount: result.duplicates.length
        });

        return result;
    }

    // 带进度显示的文件比对
    async compareFilesWithProgress(localFiles, emailAttachments, updateProgress) {
        try {
            const result = {
                missing: [], duplicates: [], matched: [], localOnly: [],
                summary: { 
                    totalEmail: emailAttachments.length, 
                    totalLocal: localFiles.length, 
                    missingCount: 0, 
                    duplicateCount: 0, 
                    matchedCount: 0,
                    emailTotalSize: 0,
                    localTotalSize: 0,
                    matchedTotalSize: 0,
                    missingTotalSize: 0
                }
            };

            // 步骤1: 预处理本地文件映射
            updateProgress('分析本地文件', '建立文件索引...', 3, 4);
            await new Promise(resolve => setTimeout(resolve, 50));

            const localFileMap = new Map();
            const usedLocalFiles = new Set();

            localFiles.forEach(file => {
                const normalizedKey = this.normalizeFileName(file.name);
                const sizeTypeKey = `${file.size}_${file.type}`;

                if (!localFileMap.has(normalizedKey)) localFileMap.set(normalizedKey, []);
                if (!localFileMap.has(sizeTypeKey)) localFileMap.set(sizeTypeKey, []);

                localFileMap.get(normalizedKey).push({ file, type: 'exact' });
                localFileMap.get(sizeTypeKey).push({ file, type: 'fuzzy' });
            });

            console.log(`建立文件索引完成,本地文件映射条目: ${localFileMap.size}`);

            // 步骤2: 比对邮件附件
            updateProgress('比对文件匹配', '正在匹配邮件附件...', 3, 4);
            await new Promise(resolve => setTimeout(resolve, 50));

            for (let i = 0; i < emailAttachments.length; i++) {
                const attachment = emailAttachments[i];
                const normalizedName = this.normalizeFileName(attachment.name);
                const sizeTypeKey = `${attachment.size}_${attachment.type}`;
                let bestMatch = null;

                // 优先精确匹配
                const exactCandidates = localFileMap.get(normalizedName) ?? [];
                for (const { file } of exactCandidates.filter(c => c.type === 'exact')) {
                    if (!usedLocalFiles.has(file) && file.size === attachment.size && file.type === attachment.type) {
                        bestMatch = { file, type: 'exact', similarity: 1.0 };
                        break;
                    }
                }

                // 模糊匹配
                if (!bestMatch) {
                    const fuzzyCandidates = localFileMap.get(sizeTypeKey) ?? [];
                    let bestSimilarity = 0;

                    for (const { file } of fuzzyCandidates.filter(c => c.type === 'fuzzy')) {
                        if (usedLocalFiles.has(file)) continue;

                        const similarity = this.calculateNameSimilarity(attachment.name, file.name);
                        if (similarity > 0.6 && similarity > bestSimilarity) {
                            bestMatch = { file, type: 'renamed', similarity };
                            bestSimilarity = similarity;
                        }
                    }
                }

                if (bestMatch) {
                    result.matched.push({ email: attachment, local: bestMatch.file, matchType: bestMatch.type, similarity: bestMatch.similarity });
                    usedLocalFiles.add(bestMatch.file);
                } else {
                    result.missing.push(attachment);
                }

                // 每处理20个文件更新一次进度
                if ((i + 1) % 20 === 0 || i === emailAttachments.length - 1) {
                    updateProgress('比对文件匹配', `已处理 ${i + 1}/${emailAttachments.length} 个附件`, 3, 4);
                    await new Promise(resolve => setTimeout(resolve, 10));
                }
            }

            console.log(`文件匹配完成,匹配: ${result.matched.length}, 缺失: ${result.missing.length}`);

            // 步骤3: 检查重复文件和本地独有文件
            updateProgress('完成分析', '检查重复文件和整理结果...', 3, 4);
            await new Promise(resolve => setTimeout(resolve, 50));

            // 改进的重复文件检测逻辑
            result.duplicates = this.detectDuplicateFiles(result.matched, localFiles, emailAttachments);

            result.localOnly = localFiles.filter(file => !usedLocalFiles.has(file));

            // 计算总大小
                    result.summary.emailTotalSize = emailAttachments.reduce((sum, att) => sum + att.size, 0);
        result.summary.localTotalSize = localFiles.reduce((sum, file) => sum + file.size, 0);
        result.summary.matchedTotalSize = result.matched.reduce((sum, match) => sum + match.email.size, 0);
        result.summary.missingTotalSize = result.missing.reduce((sum, att) => sum + att.size, 0);

            // 更新统计
            Object.assign(result.summary, {
                matchedCount: result.matched.length,
                missingCount: result.missing.length,
                duplicateCount: result.duplicates.length,
                localOnlyCount: result.localOnly.length
            });

            console.log('比对分析完成:', result.summary);
            return result;

        } catch (error) {
            console.error('比对过程中出错:', error);
            throw new Error(`文件比对失败: ${error.message}`);
        }
    }

    // 改进的重复文件检测方法
    detectDuplicateFiles(matchedFiles, localFiles, emailAttachments) {
        const duplicates = [];
        
        // 1. 检测一个本地文件匹配多个邮件附件的情况
        const localFileToEmails = new Map();
        matchedFiles.forEach(match => {
            const localFileKey = this.generateFileKey(match.local);
            if (!localFileToEmails.has(localFileKey)) {
                localFileToEmails.set(localFileKey, []);
            }
            localFileToEmails.get(localFileKey).push(match);
        });

        // 找出一对多的匹配
        for (const [fileKey, matches] of localFileToEmails.entries()) {
            if (matches.length > 1) {
                duplicates.push({
                    type: 'oneToMany',
                    localFile: matches[0].local,
                    emailAttachments: matches.map(m => m.email),
                    reason: '一个本地文件匹配多个邮件附件',
                    severity: 'high'
                });
            }
        }

        // 2. 检测多个本地文件匹配同一个邮件附件的情况
        const emailToLocalFiles = new Map();
        matchedFiles.forEach(match => {
            const emailKey = this.generateEmailKey(match.email);
            if (!emailToLocalFiles.has(emailKey)) {
                emailToLocalFiles.set(emailKey, []);
            }
            emailToLocalFiles.get(emailKey).push(match);
        });

        // 找出多对一的匹配
        for (const [emailKey, matches] of emailToLocalFiles.entries()) {
            if (matches.length > 1) {
                duplicates.push({
                    type: 'manyToOne',
                    emailAttachment: matches[0].email,
                    localFiles: matches.map(m => m.local),
                    reason: '多个本地文件匹配同一个邮件附件',
                    severity: 'medium'
                });
            }
        }

        // 3. 检测本地文件夹中的重复文件(相同内容但不同名称)
        const localDuplicates = this.findLocalDuplicates(localFiles);
        localDuplicates.forEach(dup => {
            duplicates.push({
                type: 'localDuplicate',
                localFiles: dup.files,
                reason: dup.reason,
                severity: 'low'
            });
        });

        // 4. 检测邮件附件中的重复(相同内容但来自不同邮件)
        const emailDuplicates = this.findEmailDuplicates(emailAttachments);
        emailDuplicates.forEach(dup => {
            duplicates.push({
                type: 'emailDuplicate',
                emailAttachments: dup.files,
                reason: dup.reason,
                severity: 'info'
            });
        });

        return duplicates;
    }

    // 生成文件唯一标识键
    generateFileKey(file) {
        // 使用文件大小、修改时间和标准化文件名生成唯一键
        const normalizedName = this.normalizeFileName(file.name);
        return `${normalizedName}_${file.size}_${file.lastModified ?? 0}`;
    }

    // 生成邮件附件唯一标识键
    generateEmailKey(attachment) {
        // 使用文件大小和标准化文件名生成唯一键
        const normalizedName = this.normalizeFileName(attachment.name);
        return `${normalizedName}_${attachment.size}`;
    }

    // 查找本地文件中的重复
    findLocalDuplicates(localFiles) {
        const duplicates = [];
        const sizeGroups = new Map();

        // 按大小分组
        localFiles.forEach(file => {
            const size = file.size;
            if (!sizeGroups.has(size)) {
                sizeGroups.set(size, []);
            }
            sizeGroups.get(size).push(file);
        });

        // 检查每个大小组中的重复
        for (const [size, files] of sizeGroups.entries()) {
            if (files.length > 1) {
                // 按标准化文件名进一步分组
                const nameGroups = new Map();
                files.forEach(file => {
                    const normalizedName = this.normalizeFileName(file.name);
                    if (!nameGroups.has(normalizedName)) {
                        nameGroups.set(normalizedName, []);
                    }
                    nameGroups.get(normalizedName).push(file);
                });

                // 找出同名同大小的文件
                for (const [normalizedName, sameNameFiles] of nameGroups.entries()) {
                    if (sameNameFiles.length > 1) {
                        duplicates.push({
                            files: sameNameFiles,
                            reason: `相同大小(${this.formatData(size, 'size')})和相似文件名的文件`
                        });
                    }
                }

                // 检查不同名但相同大小的可疑重复
                const nameGroupsArray = Array.from(nameGroups.values());
                if (nameGroupsArray.length > 1 && size > 1024) { // 只检查大于1KB的文件
                    const allFiles = nameGroupsArray.flat();
                    if (allFiles.length > 1) {
                        // 检查文件名相似度
                        const similarFiles = [];
                        for (let i = 0; i < allFiles.length; i++) {
                            for (let j = i + 1; j < allFiles.length; j++) {
                                const similarity = this.calculateNameSimilarity(allFiles[i].name, allFiles[j].name);
                                if (similarity > 0.7) {
                                    if (!similarFiles.includes(allFiles[i])) similarFiles.push(allFiles[i]);
                                    if (!similarFiles.includes(allFiles[j])) similarFiles.push(allFiles[j]);
                                }
                            }
                        }
                        
                        if (similarFiles.length > 1) {
                            duplicates.push({
                                files: similarFiles,
                                reason: `相同大小(${this.formatData(size, 'size')})和相似文件名的可疑重复文件`
                            });
                        }
                    }
                }
            }
        }

        return duplicates;
    }

    // 查找邮件附件中的重复
    findEmailDuplicates(emailAttachments) {
        const duplicates = [];
        const sizeNameGroups = new Map();

        // 按大小和标准化文件名分组
        emailAttachments.forEach(attachment => {
            const normalizedName = this.normalizeFileName(attachment.name);
            const key = `${normalizedName}_${attachment.size}`;
            
            if (!sizeNameGroups.has(key)) {
                sizeNameGroups.set(key, []);
            }
            sizeNameGroups.get(key).push(attachment);
        });

        // 找出重复的邮件附件
        for (const [key, attachments] of sizeNameGroups.entries()) {
            if (attachments.length > 1) {
                // 检查是否来自不同邮件
                const uniqueEmails = new Set(attachments.map(att => att.mailSubject || att.mailId));
                if (uniqueEmails.size > 1) {
                    duplicates.push({
                        files: attachments,
                        reason: `相同文件在${uniqueEmails.size}封不同邮件中出现`
                    });
                }
            }
        }

        return duplicates;
    }

    // 标准化文件名(移除常见的重命名后缀)
    normalizeFileName(fileName) {
        // 移除扩展名
        const nameWithoutExt = this.processFileName(fileName, 'removeExtension');

        // 移除常见的重命名后缀,如 (1), (2), _1, _2 等
        const normalized = nameWithoutExt
            .replace(/\s*\(\d+\)$/, '')  // 移除 (1), (2) 等
            .replace(/\s*_\d+$/, '')     // 移除 _1, _2 等
            .replace(/\s*-\d+$/, '')     // 移除 -1, -2 等
            .replace(/\s*副本$/, '')      // 移除"副本"
            .replace(/\s*copy$/, '')      // 移除"copy"
            .replace(/\s*Copy$/, '')      // 移除"Copy"
            .replace(/\s*复制$/, '')      // 移除"复制"
            .trim();

        return normalized.toLowerCase();
    }

    // 计算文件名相似度
    calculateNameSimilarity(name1, name2) {
        const norm1 = this.normalizeFileName(name1);
        const norm2 = this.normalizeFileName(name2);

        // 如果标准化后完全相同,返回高相似度
        if (norm1 === norm2) {
            return 0.95;
        }

        // 使用编辑距离计算相似度
        const distance = this.levenshteinDistance(norm1, norm2);
        const maxLength = Math.max(norm1.length, norm2.length);

        if (maxLength === 0) return 1;

        return 1 - (distance / maxLength);
    }

    // 优化的编辑距离算法(使用滚动数组减少内存占用)
    levenshteinDistance(str1, str2) {
        if (str1 === str2) return 0;
        if (!str1.length) return str2.length;
        if (!str2.length) return str1.length;

        // 使用两个数组而不是二维矩阵,节省内存
        let prev = Array(str2.length + 1).fill(0).map((_, i) => i);
        let curr = Array(str2.length + 1);

        for (let i = 1; i <= str1.length; i++) {
            curr[0] = i;
            for (let j = 1; j <= str2.length; j++) {
                curr[j] = str1[i - 1] === str2[j - 1]
                    ? prev[j - 1]
                    : Math.min(prev[j - 1], prev[j], curr[j - 1]) + 1;
            }
            [prev, curr] = [curr, prev];
        }

        return prev[str2.length];
    }

    // 显示比对结果
    displayComparisonResults(result, summaryDiv, missingDiv, duplicateDiv, matchedDiv) {
        // 保存当前比对结果,供"保留最新"功能使用
        this.currentComparisonResult = result;
        
        // 为显示区域添加数据属性,便于后续更新
        summaryDiv.setAttribute('data-comparison-summary', '');
        missingDiv.setAttribute('data-comparison-missing', '');
        duplicateDiv.setAttribute('data-comparison-duplicates', '');
        matchedDiv.setAttribute('data-comparison-matched', '');

        // 计算统计数据
        const emailSize = result.summary.emailTotalSize;
        const localSize = result.summary.localTotalSize;
        const matchedSize = result.summary.matchedTotalSize;
        const missingSize = result.summary.missingTotalSize;
        const sizeMatchPercentage = emailSize > 0 ? Math.round((matchedSize / emailSize) * 100) : 100;
        const sizeDifference = localSize - emailSize;
        const sizeDifferenceAbs = Math.abs(sizeDifference);
        
        // 判断大小匹配状态
        const getSizeMatchStatus = () => {
            if (sizeDifferenceAbs === 0) {
                return { 
                    color: 'linear-gradient(135deg, #d4edda, #f8f9fa)', 
                    borderColor: '#c3e6cb', 
                    textColor: '#155724', 
                    icon: '✓', 
                    text: '文件夹大小完全匹配' 
                };
            }
            if (sizeDifferenceAbs < emailSize * 0.01) {
                return { 
                    color: 'linear-gradient(135deg, #e2f3ff, #f8f9fa)', 
                    borderColor: '#b3d9ff', 
                    textColor: '#0d47a1', 
                    icon: '≈', 
                    text: '文件夹大小基本匹配(差异小于1%)' 
                };
            }
            if (sizeDifferenceAbs < emailSize * 0.05) {
                return { 
                    color: 'linear-gradient(135deg, #fff3cd, #f8f9fa)', 
                    borderColor: '#ffeaa7', 
                    textColor: '#856404', 
                    icon: '⚠', 
                    text: '文件夹大小存在轻微差异(小于5%)' 
                };
            }
            return { 
                color: 'linear-gradient(135deg, #f8d7da, #f8f9fa)', 
                borderColor: '#f5c6cb', 
                textColor: '#721c24', 
                icon: '✗', 
                text: '文件夹大小存在明显差异' 
            };
        };
        
        const sizeStatus = getSizeMatchStatus();

        // 优化摘要区域 - 增加色彩层次和视觉引导
        summaryDiv.innerHTML = `
            <div style="margin-bottom: 24px;">
                <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 20px;">
                    <div style="width: 4px; height: 24px; background: linear-gradient(135deg, #007bff, #0056b3); border-radius: 2px;"></div>
                    <h4 style="margin: 0; font-size: 20px; font-weight: 700; color: #13181D;">比对摘要</h4>
                </div>
                
                <!-- 核心统计卡片 - 增加色彩层次 -->
                <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 24px;">
                    <div style="background: linear-gradient(135deg, #f8f9fa, #ffffff); border: 1px solid #dee2e6; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.04);">
                        <div style="font-size: 28px; font-weight: 700; margin-bottom: 4px; color: #495057;">${result.summary.totalEmail}</div>
                        <div style="font-size: 13px; color: #6c757d; font-weight: 600;">邮件附件总数</div>
                        <div style="font-size: 11px; color: #6c757d; margin-top: 4px;">${this.formatData(emailSize, 'size')}</div>
                    </div>
                    
                    <div style="background: linear-gradient(135deg, #e8f5e8, #ffffff); border: 1px solid #c3e6cb; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.04);">
                        <div style="font-size: 28px; font-weight: 700; margin-bottom: 4px; color: #155724;">${result.summary.matchedCount}</div>
                        <div style="font-size: 13px; color: #155724; font-weight: 600;">匹配文件</div>
                        <div style="font-size: 11px; color: #6c757d; margin-top: 4px;">${sizeMatchPercentage}% 大小匹配</div>
                    </div>
                    
                    <div style="background: linear-gradient(135deg, #fff3cd, #ffffff); border: 2px solid #ffeaa7; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 2px 6px rgba(255,193,7,0.15);">
                        <div style="font-size: 28px; font-weight: 700; margin-bottom: 4px; color: #856404;">${result.summary.missingCount}</div>
                        <div style="font-size: 13px; color: #856404; font-weight: 600;">缺失文件</div>
                        <div style="font-size: 11px; color: #6c757d; margin-top: 4px;">${this.formatData(missingSize, 'size')}</div>
                    </div>
                    
                    <div style="background: linear-gradient(135deg, #e3f2fd, #ffffff); border: 1px solid #b3d9ff; border-radius: 8px; padding: 20px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.04);">
                        <div style="font-size: 28px; font-weight: 700; margin-bottom: 4px; color: #0d47a1;">${result.summary.totalLocal}</div>
                        <div style="font-size: 13px; color: #0d47a1; font-weight: 600;">本地文件总数</div>
                        <div style="font-size: 11px; color: #6c757d; margin-top: 4px;">${this.formatData(localSize, 'size')}</div>
                    </div>
                </div>

                <!-- 大小比对状态卡片 - 增加状态色彩 -->
                <div style="background: linear-gradient(135deg, #f8f9fa, #ffffff); border: 1px solid #dee2e6; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.04);">
                    <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
                        <div style="width: 40px; height: 40px; background: ${sizeStatus.color}; border: 1px solid ${sizeStatus.borderColor}; border-radius: 6px; display: flex; align-items: center; justify-content: center; color: ${sizeStatus.textColor}; font-size: 16px; font-weight: bold;">${sizeStatus.icon}</div>
                        <div>
                            <h5 style="margin: 0; font-size: 18px; font-weight: 700; color: #13181D;">文件夹大小比对</h5>
                            <div style="font-size: 14px; color: #6c757d; margin-top: 2px;">${sizeStatus.text}</div>
                        </div>
                    </div>
                    
                    <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px;">
                        <div style="background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 6px; padding: 16px;">
                            <div style="font-size: 12px; color: #6c757d; margin-bottom: 6px;">已匹配文件大小</div>
                            <div style="font-size: 18px; font-weight: 700; color: #13181D;">${this.formatData(matchedSize, 'size')}</div>
                            <div style="font-size: 11px; color: #6c757d; margin-top: 2px;">${sizeMatchPercentage}% 匹配率</div>
                        </div>
                        
                        ${sizeDifference !== 0 ? `
                            <div style="background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 6px; padding: 16px;">
                                <div style="font-size: 12px; color: #6c757d; margin-bottom: 6px;">大小差异</div>
                                <div style="font-size: 18px; font-weight: 700; color: #13181D;">
                                    ${sizeDifference > 0 ? '+' : ''}${this.formatData(sizeDifferenceAbs, 'size')}
                                </div>
                                <div style="font-size: 11px; color: #6c757d; margin-top: 2px;">
                                    ${sizeDifference > 0 ? '本地文件更多' : '本地文件更少'}
                                </div>
                            </div>
                        ` : ''}
                    </div>
                </div>
            </div>
        `;

        // 重新设计缺失文件区域 - 增加完整信息展示
        if (result.missing.length > 0) {
            // 统计文件类型分布
            const typeStats = {};
            let totalSize = 0;
            result.missing.forEach(attachment => {
                const type = attachment.type || '未知';
                typeStats[type] = (typeStats[type] || 0) + 1;
                totalSize += attachment.size || 0;
            });
            
            missingDiv.innerHTML = `
                <div style="background: #ffffff; border: 1px solid #dee2e6; border-radius: 6px; margin-bottom: 20px;">
                    <!-- 头部概览 -->
                    <div style="padding: 16px 20px; border-bottom: 1px solid #e9ecef;">
                        <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px;">
                            <div>
                                <h4 style="margin: 0 0 4px 0; font-size: 16px; font-weight: 600; color: #13181D;">缺失文件</h4>
                                <div style="font-size: 13px; color: #6c757d;">需要下载 ${result.missing.length} 个文件,总大小 ${this.formatData(totalSize, 'size')}</div>
                            </div>
                            <div style="display: flex; gap: 8px;">
                                <button class="batch-download-missing-btn" 
                                        style="padding: 6px 12px; background: #007bff; color: white; border: none; border-radius: 4px; font-size: 12px; font-weight: 500; cursor: pointer; transition: background 0.2s;"
                                        onmouseover="this.style.background='#0056b3'" onmouseout="this.style.background='#007bff'">
                                    批量下载
                                </button>
                            </div>
                        </div>
                        
                        <!-- 文件类型统计 -->
                        <div style="display: flex; gap: 12px; flex-wrap: wrap;">
                            ${Object.entries(typeStats).map(([type, count]) => `
                                <div style="background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px; padding: 4px 8px; font-size: 11px;">
                                    <span style="color: #6c757d;">${type}:</span>
                                    <span style="color: #13181D; font-weight: 600; margin-left: 4px;">${count}</span>
                                </div>
                            `).join('')}
                        </div>
                    </div>
                    
                    <!-- 缺失文件列表 -->
                    <div style="max-height: 400px; overflow-y: auto;">
                        ${result.missing.map((attachment, index) => `
                            <div style="border-bottom: ${index === result.missing.length - 1 ? 'none' : '1px solid #f8f9fa'}; padding: 16px 20px; transition: background 0.2s;" 
                                 onmouseover="this.style.background='#f8f9fa'" 
                                 onmouseout="this.style.background='transparent'">
                                
                                <!-- 文件信息 -->
                                <div style="display: flex; align-items: flex-start; gap: 12px; margin-bottom: 8px;">
                                    <div style="width: 4px; height: 16px; background: #ffc107; border-radius: 2px; flex-shrink: 0; margin-top: 2px;"></div>
                                    <div style="flex: 1; min-width: 0;">
                                        <div style="font-weight: 600; color: #13181D; margin-bottom: 4px; word-break: break-all; font-size: 14px;">
                                            ${this.processData(attachment.name, 'escapeHtml')}
                                        </div>
                                        <div style="display: flex; align-items: center; gap: 16px; font-size: 12px; color: #6c757d; margin-bottom: 6px;">
                                            <span>${this.formatData(attachment.size, 'size')}</span>
                                            <span>${attachment.type || '未知类型'}</span>
                                            ${attachment.fileid ? `<span>ID: ${attachment.fileid}</span>` : ''}
                                        </div>
                                        
                                        <!-- 来源邮件信息 -->
                                        ${attachment.mailSubject || attachment.mailSender || attachment.mailDate ? `
                                            <div style="background: #f8f9fa; border-radius: 4px; padding: 6px 10px; margin-top: 6px;">
                                                <div style="font-size: 11px; color: #6c757d; margin-bottom: 2px;">来源邮件:</div>
                                                <div style="font-size: 12px; color: #13181D;">
                                                    ${attachment.mailSubject ? `
                                                        <div style="font-weight: 500; margin-bottom: 2px; word-break: break-all;">
                                                            ${this.processData(attachment.mailSubject, 'escapeHtml')}
                                                        </div>
                                                    ` : ''}
                                                    <div style="display: flex; gap: 12px; font-size: 11px; color: #6c757d;">
                                                        ${attachment.mailSender ? `<span>发件人: ${this.processData(attachment.mailSender, 'escapeHtml')}</span>` : ''}
                                                        ${attachment.mailDate ? `<span>日期: ${this.formatDate(attachment.mailDate, 'YYYY-MM-DD HH:mm')}</span>` : ''}
                                                    </div>
                                                </div>
                                            </div>
                                        ` : ''}
                                    </div>
                                    
                                    <!-- 操作按钮 -->
                                    <div style="display: flex; flex-direction: column; gap: 4px; flex-shrink: 0;">
                                        <button data-fileid="${attachment.fileid}" class="download-single-btn" 
                                                style="padding: 6px 12px; background: #28a745; color: white; border: none; border-radius: 4px; font-size: 11px; font-weight: 500; cursor: pointer; transition: background 0.2s; white-space: nowrap;"
                                                onmouseover="this.style.background='#218838'" onmouseout="this.style.background='#28a745'">
                                            下载
                                        </button>
                                        ${attachment.mailId ? `
                                            <button onclick="window.attachmentManager.openMail('${attachment.mailId}')" 
                                                    style="padding: 4px 8px; background: transparent; color: #6c757d; border: 1px solid #dee2e6; border-radius: 4px; font-size: 10px; cursor: pointer; transition: all 0.2s;"
                                                    onmouseover="this.style.background='#f8f9fa'; this.style.color='#13181D'" onmouseout="this.style.background='transparent'; this.style.color='#6c757d'">
                                                查看邮件
                                            </button>
                                        ` : ''}
                                    </div>
                                </div>
                            </div>
                        `).join('')}
                    </div>
                </div>
            `;
        } else {
            missingDiv.innerHTML = `
                <div style="background: linear-gradient(135deg, #f0fff4, #ffffff); border: 1px solid #c3e6cb; border-radius: 8px; padding: 20px; text-align: center; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(40,167,69,0.1);">
                    <div style="width: 60px; height: 60px; background: linear-gradient(135deg, #c3e6cb, #d4edda); border: 1px solid #28a745; border-radius: 8px; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; color: #155724; font-size: 20px;">✓</div>
                    <h4 style="margin: 0 0 8px 0; color: #155724; font-size: 18px; font-weight: 700;">文件完整</h4>
                    <div style="color: #155724; font-size: 14px;">所有邮件附件都已存在于本地文件夹中</div>
                </div>
            `;
        }

        // 重新设计重复文件检测面板 - 简洁清晰的信息架构
        if (result.duplicates.length > 0) {
            const duplicatesByType = this.groupDuplicatesByType(result.duplicates);
            const canKeepLatestCount = result.duplicates.filter(dup => this.canKeepLatestFile(dup)).length;
            
            // 统计各类型数量
            const typeStats = Object.entries(duplicatesByType).map(([type, duplicates]) => ({
                type,
                count: duplicates.length,
                title: this.getDuplicateTypeTitle(type).replace(/[⚠️🔄📁📧❓]\s*/, '') // 移除表情符号
            }));
            
            duplicateDiv.innerHTML = `
                <div style="background: #ffffff; border: 1px solid #dee2e6; border-radius: 6px; margin-bottom: 20px;">
                    <!-- 头部概览 -->
                    <div style="padding: 16px 20px; border-bottom: 1px solid #e9ecef;">
                        <div style="display: flex; align-items: center; justify-content: between; gap: 16px;">
                            <div>
                                <h4 style="margin: 0 0 4px 0; font-size: 16px; font-weight: 600; color: #13181D;">重复文件检测</h4>
                                <div style="font-size: 13px; color: #6c757d;">发现 ${result.duplicates.length} 个重复项,需要处理</div>
                            </div>
                            ${canKeepLatestCount > 0 ? `
                                <button class="batch-keep-latest-btn" data-count="${canKeepLatestCount}"
                                        style="padding: 6px 12px; background: #28a745; color: white; border: none; border-radius: 4px; font-size: 12px; font-weight: 500; cursor: pointer; transition: background 0.2s; white-space: nowrap;"
                                        onmouseover="this.style.background='#218838'" onmouseout="this.style.background='#28a745'">
                                    批量保留最新 (${canKeepLatestCount})
                                </button>
                            ` : ''}
                        </div>
                        
                        <!-- 类型统计 -->
                        <div style="display: flex; gap: 16px; margin-top: 12px; flex-wrap: wrap;">
                            ${typeStats.map(stat => `
                                <div style="background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 4px; padding: 6px 10px; font-size: 12px;">
                                    <span style="color: #6c757d;">${stat.title}:</span>
                                    <span style="color: #13181D; font-weight: 600; margin-left: 4px;">${stat.count}</span>
                                </div>
                            `).join('')}
                        </div>
                    </div>
                    
                    <!-- 重复项列表 -->
                    <div style="max-height: 400px; overflow-y: auto;">
                        ${Object.entries(duplicatesByType).map(([type, duplicates], typeIndex) => `
                            ${duplicates.map((dup, dupIndex) => this.renderDuplicateItem(dup, this.getDuplicateGlobalIndex(result.duplicates, dup))).join('')}
                        `).join('')}
                    </div>
                </div>
            `;
        } else {
            duplicateDiv.innerHTML = `
                <div style="background: linear-gradient(135deg, #f0fff4, #ffffff); border: 1px solid #c3e6cb; border-radius: 8px; padding: 20px; text-align: center; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(40,167,69,0.1);">
                    <div style="width: 60px; height: 60px; background: linear-gradient(135deg, #c3e6cb, #d4edda); border: 1px solid #28a745; border-radius: 8px; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; color: #155724; font-size: 20px;">✓</div>
                    <h4 style="margin: 0 0 8px 0; color: #155724; font-size: 18px; font-weight: 700;">无重复文件</h4>
                    <div style="color: #155724; font-size: 14px;">未检测到重复或相似的文件</div>
                </div>
            `;
        }

        // 优化匹配文件区域 - 增加成功色彩提示
        if (result.matched.length > 0) {
            const exactMatches = result.matched.filter(match => match.type === 'exact');
            const renamedMatches = result.matched.filter(match => match.type === 'renamed');
            
            matchedDiv.innerHTML = `
                <div style="background: linear-gradient(135deg, #f0fff4, #ffffff); border: 1px solid #c3e6cb; border-radius: 8px; overflow: hidden; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(40,167,69,0.1);">
                    <div style="background: linear-gradient(135deg, #d4edda, #ffffff); border-bottom: 1px solid #c3e6cb; padding: 16px 20px;">
                        <div style="display: flex; align-items: center; justify-content: space-between;">
                            <div style="display: flex; align-items: center; gap: 12px;">
                                <div style="width: 36px; height: 36px; background: linear-gradient(135deg, #c3e6cb, #d4edda); border: 1px solid #28a745; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 16px; color: #155724;">✓</div>
                                <div>
                                    <h4 style="margin: 0; font-size: 18px; font-weight: 700; color: #155724;">匹配文件</h4>
                                    <div style="font-size: 13px; color: #155724; margin-top: 2px;">已成功匹配的文件</div>
                                </div>
                            </div>
                            <div style="text-align: right;">
                                <div style="font-size: 24px; font-weight: 700; color: #155724;">${result.matched.length}</div>
                                <div style="font-size: 11px; color: #155724;">个文件</div>
                            </div>
                        </div>
                    </div>
                    <div style="max-height: 300px; overflow-y: auto;">
                        ${exactMatches.length > 0 ? `
                            <div style="padding: 12px 20px; background: #f8f9fa; color: #13181D; font-weight: 600; font-size: 14px; border-bottom: 1px solid #e9ecef;">
                                精确匹配 (${exactMatches.length})
                            </div>
                            ${exactMatches.map((match, index) => `
                                <div style="padding: 16px 20px; border-bottom: ${index === exactMatches.length - 1 && renamedMatches.length === 0 ? 'none' : '1px solid #f8f9fa'}; display: flex; align-items: center; gap: 16px; transition: background 0.2s;" onmouseover="this.style.background='#f8f9fa'" onmouseout="this.style.background='transparent'">
                                    <div style="width: 6px; height: 6px; background: #13181D; border-radius: 50%; flex-shrink: 0;"></div>
                                    <div style="flex: 1; min-width: 0;">
                                        <div style="font-weight: 600; color: #13181D; margin-bottom: 4px; word-break: break-all;">${this.processData(match.email.name, 'escapeHtml')}</div>
                                        <div style="display: flex; align-items: center; gap: 12px; font-size: 12px; color: #6c757d;">
                                            <span>${this.formatData(match.email.size, 'size')}</span>
                                            <span>•</span>
                                            <span>${match.email.type}</span>
                                        </div>
                                    </div>
                                </div>
                            `).join('')}
                        ` : ''}
                        
                        ${renamedMatches.length > 0 ? `
                            <div style="padding: 12px 20px; background: #f8f9fa; color: #13181D; font-weight: 600; font-size: 14px; border-bottom: 1px solid #e9ecef;">
                                重命名匹配 (${renamedMatches.length})
                            </div>
                            ${renamedMatches.map((match, index) => `
                                <div style="padding: 16px 20px; border-bottom: ${index === renamedMatches.length - 1 ? 'none' : '1px solid #f8f9fa'}; display: flex; align-items: center; gap: 16px; transition: background 0.2s;" onmouseover="this.style.background='#f8f9fa'" onmouseout="this.style.background='transparent'">
                                    <div style="width: 6px; height: 6px; background: #6c757d; border-radius: 50%; flex-shrink: 0;"></div>
                                    <div style="flex: 1; min-width: 0;">
                                        <div style="font-weight: 600; color: #13181D; margin-bottom: 4px; word-break: break-all;">${this.processData(match.email.name, 'escapeHtml')}</div>
                                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">→ ${this.processData(match.local.name, 'escapeHtml')}</div>
                                        <div style="display: flex; align-items: center; gap: 12px; font-size: 12px; color: #6c757d;">
                                            <span>${this.formatData(match.email.size, 'size')}</span>
                                            <span>•</span>
                                            <span>${match.email.type}</span>
                                        </div>
                                    </div>
                                </div>
                            `).join('')}
                        ` : ''}
                    </div>
                </div>
            `;
        } else {
            matchedDiv.innerHTML = `
                <div style="background: linear-gradient(135deg, #f8f9fa, #ffffff); border: 1px solid #dee2e6; border-radius: 8px; padding: 20px; text-align: center; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.04);">
                    <div style="width: 60px; height: 60px; background: linear-gradient(135deg, #dee2e6, #f8f9fa); border: 1px solid #adb5bd; border-radius: 8px; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; color: #6c757d; font-size: 20px;">○</div>
                    <h4 style="margin: 0 0 8px 0; color: #495057; font-size: 18px; font-weight: 700;">无匹配文件</h4>
                    <div style="color: #6c757d; font-size: 14px;">未找到匹配的文件</div>
                </div>
            `;
        }

        // 设置事件监听器
        this.setupComparisonDialogEvents(summaryDiv.parentNode);
    }

    // 按类型分组重复文件
    groupDuplicatesByType(duplicates) {
        const grouped = {};
        duplicates.forEach(dup => {
            if (!grouped[dup.type]) {
                grouped[dup.type] = [];
            }
            grouped[dup.type].push(dup);
        });
        return grouped;
    }

    // 获取重复文件类型的颜色配置
    getDuplicateTypeColor(type) {
        const colors = {
            'oneToMany': { bg: '#fff3cd', text: '#856404' },
            'manyToOne': { bg: '#f8d7da', text: '#721c24' },
            'localDuplicate': { bg: '#d1ecf1', text: '#0c5460' },
            'emailDuplicate': { bg: '#e2e3e5', text: '#383d41' }
        };
        return colors[type] || { bg: '#f8f9fa', text: '#495057' };
    }

    // 获取重复文件类型的标题
    getDuplicateTypeTitle(type) {
        const titles = {
            'oneToMany': '⚠️ 一对多匹配',
            'manyToOne': '🔄 多对一匹配',
            'localDuplicate': '📁 本地重复文件',
            'emailDuplicate': '📧 邮件重复附件'
        };
        return titles[type] || '❓ 未知类型';
    }

    // 渲染重复文件项 - 重新设计为简洁统一的格式
    renderDuplicateItem(dup, index) {
        const canKeepLatest = this.canKeepLatestFile(dup);
        const typeInfo = this.getDuplicateTypeInfo(dup.type);
        
        return `
            <div style="border-bottom: 1px solid #f8f9fa; padding: 16px 20px; transition: background 0.2s;" 
                 data-dup-index="${index}" 
                 onmouseover="this.style.background='#f8f9fa'" 
                 onmouseout="this.style.background='transparent'">
                
                <!-- 重复类型标识 -->
                <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">
                    <div style="width: 4px; height: 16px; background: ${typeInfo.color}; border-radius: 2px;"></div>
                    <div style="font-weight: 600; color: #13181D; font-size: 14px;">${typeInfo.title}</div>
                    <div style="font-size: 12px; color: #6c757d; background: #f8f9fa; padding: 2px 6px; border-radius: 3px;">
                        ${dup.reason}
                    </div>
                </div>
                
                <!-- 重复文件详情 -->
                ${this.renderDuplicateDetails(dup)}
                
                <!-- 操作按钮 -->
                ${canKeepLatest ? `
                    <div style="margin-top: 12px; text-align: right;">
                        <button class="keep-latest-btn" data-index="${index}"
                                style="background: #28a745; color: white; border: none; border-radius: 4px; padding: 6px 12px; font-size: 12px; font-weight: 500; cursor: pointer; transition: background 0.2s;"
                                onmouseover="this.style.background='#218838'" onmouseout="this.style.background='#28a745'">
                            保留最新
                        </button>
                    </div>
                ` : ''}
            </div>
        `;
    }
    
    // 获取重复类型信息
    getDuplicateTypeInfo(type) {
        const typeMap = {
            'oneToMany': { title: '一对多匹配', color: '#ffc107' },
            'manyToOne': { title: '多对一匹配', color: '#dc3545' },
            'localDuplicate': { title: '本地重复文件', color: '#17a2b8' },
            'emailDuplicate': { title: '邮件重复附件', color: '#6c757d' }
        };
        return typeMap[type] || { title: '未知类型', color: '#6c757d' };
    }
    
    // 渲染重复文件详情
    renderDuplicateDetails(dup) {
        switch (dup.type) {
            case 'oneToMany':
                return `
                    <!-- 本地文件 -->
                    <div style="margin-bottom: 12px;">
                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">本地文件:</div>
                        <div style="background: #f8f9fa; border-radius: 4px; padding: 8px 12px;">
                            <div style="font-weight: 600; color: #13181D; margin-bottom: 2px; word-break: break-all;">
                                ${this.processData(dup.localFile.name, 'escapeHtml')}
                            </div>
                            <div style="font-size: 12px; color: #6c757d;">
                                ${this.formatData(dup.localFile.size, 'size')}
                            </div>
                        </div>
                    </div>
                    
                    <!-- 匹配的邮件附件 -->
                    <div>
                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">匹配的邮件附件 (${dup.emailAttachments.length}):</div>
                        <div style="display: flex; flex-direction: column; gap: 4px;">
                            ${dup.emailAttachments.map(att => `
                                <div style="background: #f8f9fa; border-radius: 4px; padding: 6px 10px; font-size: 12px;">
                                    <div style="color: #13181D; font-weight: 500; margin-bottom: 2px; word-break: break-all;">
                                        ${this.processData(att.name, 'escapeHtml')}
                                    </div>
                                    <div style="color: #6c757d; display: flex; gap: 12px;">
                                        <span>${this.formatData(att.size, 'size')}</span>
                                        ${att.date ? `<span>${this.formatDate(att.date, 'MM-DD HH:mm')}</span>` : ''}
                                    </div>
                                </div>
                            `).join('')}
                        </div>
                    </div>
                `;
                
            case 'manyToOne':
                return `
                    <!-- 邮件附件 -->
                    <div style="margin-bottom: 12px;">
                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">邮件附件:</div>
                        <div style="background: #f8f9fa; border-radius: 4px; padding: 8px 12px;">
                            <div style="font-weight: 600; color: #13181D; margin-bottom: 2px; word-break: break-all;">
                                ${this.processData(dup.emailAttachment.name, 'escapeHtml')}
                            </div>
                            <div style="font-size: 12px; color: #6c757d;">
                                ${this.formatData(dup.emailAttachment.size, 'size')}
                            </div>
                        </div>
                    </div>
                    
                    <!-- 匹配的本地文件 -->
                    <div>
                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">匹配的本地文件 (${dup.localFiles.length}):</div>
                        <div style="display: flex; flex-direction: column; gap: 4px;">
                            ${dup.localFiles.map(file => `
                                <div style="background: #f8f9fa; border-radius: 4px; padding: 6px 10px; font-size: 12px;">
                                    <div style="color: #13181D; font-weight: 500; margin-bottom: 2px; word-break: break-all;">
                                        ${this.processData(file.name, 'escapeHtml')}
                                    </div>
                                    <div style="color: #6c757d; display: flex; gap: 12px; flex-wrap: wrap;">
                                        <span>${this.formatData(file.size, 'size')}</span>
                                        ${file.path ? `<span>路径: ${file.path}</span>` : ''}
                                        ${file.lastModified ? `<span>修改: ${this.formatDate(file.lastModified, 'MM-DD HH:mm')}</span>` : ''}
                                    </div>
                                </div>
                            `).join('')}
                        </div>
                    </div>
                `;
                
            case 'localDuplicate':
                return `
                    <div>
                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">重复的本地文件 (${dup.localFiles.length}):</div>
                        <div style="display: flex; flex-direction: column; gap: 4px;">
                            ${dup.localFiles.map(file => `
                                <div style="background: #f8f9fa; border-radius: 4px; padding: 6px 10px; font-size: 12px;">
                                    <div style="color: #13181D; font-weight: 500; margin-bottom: 2px; word-break: break-all;">
                                        ${this.processData(file.name, 'escapeHtml')}
                                    </div>
                                    <div style="color: #6c757d; display: flex; gap: 12px; flex-wrap: wrap;">
                                        <span>${this.formatData(file.size, 'size')}</span>
                                        ${file.path ? `<span>路径: ${file.path}</span>` : ''}
                                        ${file.lastModified ? `<span>修改: ${this.formatDate(file.lastModified, 'MM-DD HH:mm')}</span>` : ''}
                                    </div>
                                </div>
                            `).join('')}
                        </div>
                    </div>
                `;
                
            case 'emailDuplicate':
                return `
                    <div>
                        <div style="font-size: 12px; color: #6c757d; margin-bottom: 4px;">重复的邮件附件 (${dup.emailAttachments.length}):</div>
                        <div style="display: flex; flex-direction: column; gap: 4px;">
                            ${dup.emailAttachments.map(att => `
                                <div style="background: #f8f9fa; border-radius: 4px; padding: 6px 10px; font-size: 12px;">
                                    <div style="color: #13181D; font-weight: 500; margin-bottom: 2px; word-break: break-all;">
                                        ${this.processData(att.name, 'escapeHtml')}
                                    </div>
                                    <div style="color: #6c757d; display: flex; gap: 12px; flex-wrap: wrap;">
                                        <span>${this.formatData(att.size, 'size')}</span>
                                        <span>来自: ${att.mailSubject || '未知邮件'}</span>
                                        ${att.date ? `<span>${this.formatDate(att.date, 'MM-DD HH:mm')}</span>` : ''}
                                    </div>
                                </div>
                            `).join('')}
                        </div>
                    </div>
                `;
                
            default:
                return `<div style="font-size: 12px; color: #6c757d;">未知重复类型</div>`;
        }
    }

    // 获取重复项在全局数组中的索引
    getDuplicateGlobalIndex(allDuplicates, targetDup) {
        return allDuplicates.findIndex(dup => dup === targetDup);
    }

    // 判断是否可以执行"保留最新"操作
    canKeepLatestFile(dup) {
        switch (dup.type) {
            case 'localDuplicate':
                return dup.localFiles && dup.localFiles.length > 1 && 
                       dup.localFiles.some(file => file.lastModified);
            case 'emailDuplicate':
                return dup.emailAttachments && dup.emailAttachments.length > 1 && 
                       dup.emailAttachments.some(att => att.date);
            case 'manyToOne':
                return dup.localFiles && dup.localFiles.length > 1 && 
                       dup.localFiles.some(file => file.lastModified);
            default:
                return false;
        }
    }

    // 保留最新文件,删除旧文件
    async keepLatestFile(duplicateIndex) {
        if (!this.currentComparisonResult || !this.currentComparisonResult.duplicates) {
            this.showToast('比对结果不存在', 'error');
            return;
        }

        const dup = this.currentComparisonResult.duplicates[duplicateIndex];
        if (!dup) {
            this.showToast('重复文件项不存在', 'error');
            return;
        }

        try {
            let result;
            switch (dup.type) {
                case 'localDuplicate':
                    result = await this.keepLatestLocalFiles(dup);
                    break;
                case 'emailDuplicate':
                    result = await this.keepLatestEmailAttachments(dup);
                    break;
                case 'manyToOne':
                    result = await this.keepLatestInManyToOne(dup);
                    break;
                default:
                    this.showToast('此类型不支持保留最新操作', 'warning');
                    return;
            }

            if (result.success) {
                this.showToast(`已保留最新文件,删除了 ${result.deletedCount} 个旧文件`, 'success');
                // 从重复列表中移除已处理的项
                this.currentComparisonResult.duplicates.splice(duplicateIndex, 1);
                // 重新渲染比对结果
                this.refreshComparisonDisplay();
            } else {
                this.showToast(`操作失败: ${result.error}`, 'error');
            }
        } catch (error) {
            console.error('保留最新文件时出错:', error);
            this.showToast(`操作失败: ${error.message}`, 'error');
        }
    }

    // 保留最新的本地文件
    async keepLatestLocalFiles(dup, silent = false) {
        const files = dup.localFiles;
        if (!files || files.length <= 1) {
            return { success: false, error: '文件数量不足' };
        }

        // 按修改时间排序,最新的在前
        const sortedFiles = files.sort((a, b) => {
                    const timeA = a.lastModified ?? 0;
        const timeB = b.lastModified ?? 0;
            return timeB - timeA;
        });

        const latestFile = sortedFiles[0];
        const filesToDelete = sortedFiles.slice(1);

        // 确认操作(非静默模式)
        if (!silent) {
            const confirmMessage = `确定要保留最新文件并删除 ${filesToDelete.length} 个旧文件吗?\n\n保留: ${latestFile.name}\n删除: ${filesToDelete.map(f => f.name).join(', ')}`;
            if (!confirm(confirmMessage)) {
                return { success: false, error: '用户取消操作' };
            }
        }

        let deletedCount = 0;
        const errors = [];

        // 删除旧文件
        for (const file of filesToDelete) {
            try {
                if (file.handle) {
                    await file.handle.remove();
                    deletedCount++;
                } else {
                    errors.push(`无法删除 ${file.name}: 文件句柄不存在`);
                }
            } catch (error) {
                errors.push(`删除 ${file.name} 失败: ${error.message}`);
            }
        }

        if (errors.length > 0) {
            console.warn('删除文件时出现部分错误:', errors);
        }

        return { 
            success: deletedCount > 0, 
            deletedCount, 
            error: errors.length > 0 ? errors.join('; ') : null 
        };
    }

    // 保留最新的邮件附件(标记为下载,其他忽略)
    async keepLatestEmailAttachments(dup, silent = false) {
        const attachments = dup.emailAttachments;
        if (!attachments || attachments.length <= 1) {
            return { success: false, error: '附件数量不足' };
        }

        // 按邮件日期排序,最新的在前
        const sortedAttachments = attachments.sort((a, b) => {
                    const timeA = new Date(a.date ?? 0).getTime();
        const timeB = new Date(b.date ?? 0).getTime();
            return timeB - timeA;
        });

        const latestAttachment = sortedAttachments[0];
        const oldAttachments = sortedAttachments.slice(1);

        // 确认操作(非静默模式)
        if (!silent) {
            const confirmMessage = `确定要标记最新附件为下载,忽略 ${oldAttachments.length} 个旧附件吗?\n\n下载: ${latestAttachment.name} (${this.formatDate(latestAttachment.date, 'YYYY-MM-DD')})\n忽略: ${oldAttachments.map(att => `${att.name} (${this.formatDate(att.date, 'YYYY-MM-DD')})`).join(', ')}`;
            if (!confirm(confirmMessage)) {
                return { success: false, error: '用户取消操作' };
            }
        }

        // 这里可以实现将旧附件从下载列表中移除的逻辑
        // 或者添加到忽略列表中
        return { 
            success: true, 
            deletedCount: oldAttachments.length,
            message: `已标记保留最新附件: ${latestAttachment.name}` 
        };
    }

    // 在多对一匹配中保留最新的本地文件
    async keepLatestInManyToOne(dup, silent = false) {
        return await this.keepLatestLocalFiles(dup, silent);
    }

    // 批量保留最新文件
    async batchKeepLatest() {
        if (!this.currentComparisonResult || !this.currentComparisonResult.duplicates) {
            this.showToast('比对结果不存在', 'error');
            return;
        }

        const duplicates = this.currentComparisonResult.duplicates;
        const processableDuplicates = duplicates
            .map((dup, index) => ({ dup, index }))
            .filter(({ dup }) => this.canKeepLatestFile(dup));

        if (processableDuplicates.length === 0) {
            this.showToast('没有可以批量处理的重复文件', 'warning');
            return;
        }

        // 确认操作
        const confirmMessage = `确定要批量处理 ${processableDuplicates.length} 个重复文件组吗?\n\n此操作将:\n- 保留每组中最新的文件\n- 删除其他旧版本文件\n\n此操作不可撤销!`;
        if (!confirm(confirmMessage)) {
            return;
        }

        this.showProgress('正在批量处理重复文件...');
        
        let processedCount = 0;
        let totalDeleted = 0;
        const errors = [];

        // 按索引降序处理,避免索引变化问题
        const sortedDuplicates = processableDuplicates.sort((a, b) => b.index - a.index);

        for (const { dup, index } of sortedDuplicates) {
            try {
                let result;
                switch (dup.type) {
                    case 'localDuplicate':
                        result = await this.keepLatestLocalFiles(dup, true); // 静默模式
                        break;
                    case 'emailDuplicate':
                        result = await this.keepLatestEmailAttachments(dup, true); // 静默模式
                        break;
                    case 'manyToOne':
                        result = await this.keepLatestInManyToOne(dup, true); // 静默模式
                        break;
                    default:
                        continue;
                }

                if (result.success) {
                    totalDeleted += result.deletedCount;
                    // 从重复列表中移除已处理的项
                    this.currentComparisonResult.duplicates.splice(index, 1);
                    processedCount++;
                } else {
                    errors.push(`处理失败: ${result.error}`);
                }
            } catch (error) {
                errors.push(`处理重复文件时出错: ${error.message}`);
            }
        }

        this.hideProgress();

        // 显示结果
        if (processedCount > 0) {
            this.showToast(`批量处理完成!处理了 ${processedCount} 个重复文件组,删除了 ${totalDeleted} 个旧文件`, 'success');
            // 重新渲染比对结果
            this.refreshComparisonDisplay();
        } else {
            this.showToast('批量处理失败', 'error');
        }

        if (errors.length > 0) {
            console.warn('批量处理过程中的错误:', errors);
            this.showToast(`处理过程中出现 ${errors.length} 个错误,请查看控制台`, 'warning');
        }
    }

    // 设置比较对话框事件监听器
    setupComparisonDialogEvents(dialogContent) {
        // 批量保留最新按钮
        const batchKeepLatestBtns = dialogContent.querySelectorAll('.batch-keep-latest-btn');
        batchKeepLatestBtns.forEach(btn => {
            btn.addEventListener('click', () => this.batchKeepLatest());
            btn.addEventListener('mouseenter', (e) => e.target.style.background = '#138496');
            btn.addEventListener('mouseleave', (e) => e.target.style.background = '#17a2b8');
        });

        // 单个保留最新按钮
        const keepLatestBtns = dialogContent.querySelectorAll('.keep-latest-btn');
        keepLatestBtns.forEach(btn => {
            const index = parseInt(btn.dataset.index);
            btn.addEventListener('click', () => this.keepLatestFile(index));
            btn.addEventListener('mouseenter', (e) => e.target.style.background = '#218838');
            btn.addEventListener('mouseleave', (e) => e.target.style.background = '#28a745');
        });

        // 验证设置按钮
        const validationSettingsBtns = dialogContent.querySelectorAll('.validation-settings-btn');
        validationSettingsBtns.forEach(btn => {
            btn.addEventListener('click', () => this.openValidationSettings());
            btn.addEventListener('mouseenter', (e) => e.target.style.background = 'var(--theme_primary_hover, #0056b3)');
            btn.addEventListener('mouseleave', (e) => e.target.style.background = 'var(--theme_primary, #007bff)');
        });
    }

    // 刷新比对结果显示
    refreshComparisonDisplay() {
        if (!this.currentComparisonResult) return;

        const summaryDiv = document.querySelector('[data-comparison-summary]');
        const missingDiv = document.querySelector('[data-comparison-missing]');
        const duplicateDiv = document.querySelector('[data-comparison-duplicates]');
        const matchedDiv = document.querySelector('[data-comparison-matched]');

        if (summaryDiv && missingDiv && duplicateDiv && matchedDiv) {
            this.displayComparisonResults(
                this.currentComparisonResult, 
                summaryDiv, 
                missingDiv, 
                duplicateDiv, 
                matchedDiv
            );
            // 重新设置事件监听器
            this.setupComparisonDialogEvents(summaryDiv.closest('.comparison-dialog'));
        }
    }

    // 下载单个附件
    async downloadSingleAttachment(fileid) {
        const attachment = this.attachments.find(att => att.fileid === fileid);
        if (!attachment) {
            this.showToast('附件不存在', 'error');
            return;
        }

        try {
            const dirHandle = await window.showDirectoryPicker();
            await this.downloadAttachment(attachment, dirHandle);
            this.showToast('下载完成', 'success');
        } catch (error) {
            if (error.name !== 'AbortError') {
                this.showToast('下载失败: ' + error.message, 'error');
            }
        }
    }




    // 优化的过滤器应用(减少重复过滤操作)
    applyFilters() {
        const { fileTypes, timeRange, searchKeyword } = this.filters;

        // 预定义文件类型映射
        const fileTypeMap = {
            '文档': new Set(['doc', 'docx', 'pdf', 'txt', 'rtf']),
            '图片': new Set(['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']),
            '压缩包': new Set(['zip', 'rar', '7z', 'tar', 'gz']),
            '其他': new Set(['doc', 'docx', 'pdf', 'txt', 'rtf', 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'zip', 'rar', '7z', 'tar', 'gz'])
        };

        // 预计算时间范围
        const timeFilter = timeRange !== '全部' ? (() => {
            const now = new Date();
            const ranges = {
                '今天': new Date(now.getFullYear(), now.getMonth(), now.getDate()),
                '本周': new Date(now.getFullYear(), now.getMonth(), now.getDate() - now.getDay()),
                '本月': new Date(now.getFullYear(), now.getMonth(), 1),
                '今年': new Date(now.getFullYear(), 0, 1)
            };
            const threshold = ranges[timeRange];
            return threshold ? (att => new Date(att.date) >= threshold) : null;
        })() : null;

        // 预处理搜索关键词
        const keyword = searchKeyword?.toLowerCase();

        // 单次遍历应用所有过滤器
        const filteredAttachments = this.attachments.filter(attachment => {
            // 文件类型过滤
            if (fileTypes.length > 0 && !fileTypes.includes('全部')) {
                const ext = this.processFileName(attachment.name, 'getExtension')?.toLowerCase();
                const matchesType = fileTypes.some(type => {
                    const typeSet = fileTypeMap[type];
                    return type === '其他' ? !typeSet.has(ext) : typeSet?.has(ext);
                });
                if (!matchesType) return false;
            }

            // 时间过滤
            if (timeFilter && !timeFilter(attachment)) return false;

            // 关键词过滤
            if (keyword && !attachment.name.toLowerCase().includes(keyword) &&
                !attachment.mailSubject?.toLowerCase().includes(keyword)) return false;

            return true;
        });

        this.updateAttachmentList(filteredAttachments);
    }

    // 文件类型常量
    static FILE_TYPES = {
        '图片': ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'],
        '文档': ['doc', 'docx', 'pdf', 'txt', 'rtf', 'xls', 'xlsx', 'csv', 'ppt', 'pptx'],
        '压缩包': ['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz'],
        '视频': ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm'],
        '音频': ['mp3', 'wav', 'flac', 'aac', 'ogg']
    };

    getFileType(filename) {
        const ext = filename?.split('.').pop()?.toLowerCase() || '';
        for (const [type, extensions] of Object.entries(AttachmentManager.FILE_TYPES)) {
            if (extensions.includes(ext)) return type;
        }
        return '其他';
    }

    // Toast图标常量
    static TOAST_ICONS = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' };

    // 选择器常量
    static SELECTORS = {
        CONTENT_AREA: '#attachment-content-area',
        OVERLAY_PANEL: '#attachment-manager-overlay',
        MANAGER_PANEL: '.attachment-manager-panel',
        OVERLAY_CONTENT: '.attachment-overlay-content',
        OVERLAY_TOOLBAR: '.attachment-overlay-toolbar',
        DOWNLOADER_BTN: '#attachment-downloader-btn',
        DATA_ATTR: '[data-attachment-manager]',
        CUSTOM_STYLES: 'style[data-attachment-manager]'
    };



    async processMailsInBatches(mails, folderId) {
        const batchSize = 5;
        const totalBatches = Math.ceil(mails.length / batchSize);
        let mailIndex = 1;
        
        for (let i = 0; i < totalBatches; i++) {
            const start = i * batchSize;
            const batch = mails.slice(start, start + batchSize);
            
            for (const mail of batch) {
                if (mail?.normal_attach?.length > 0) {
                    const mailAttachments = this.processMailAttachments(mail);
                    // 为每个邮件的附件设置mailIndex和attachIndex
                    mailAttachments.forEach((attachment, attachIndex) => {
                        attachment.mailIndex = mailIndex;
                        attachment.attachIndex = attachIndex + 1;
                    });
                    this.attachments.push(...mailAttachments);
                    mailIndex++;
                }
            }
            
            // 每处理5封邮件让出主线程
            if (i < totalBatches - 1) {
                await new Promise(resolve => setTimeout(resolve, 1));
            }
            
            // 更新进度(减少频率)
            if (i % 2 === 0 || i === totalBatches - 1) {
                const progress = Math.round(((i + 1) / totalBatches) * 100);
                this.updateStatus(`正在处理邮件... ${progress}%`);
            }
        }
        
        // 处理完所有附件后,为每个附件设置fileIndex
        this.attachments.forEach((attachment, index) => {
            attachment.fileIndex = index + 1;
        });
    }

    processMailAttachments(mail) {
        const sid = this.downloader.sid;
        const mailData = {
            mailId: mail.emailid,
            mailSubject: mail.subject,
            totime: mail.totime,
            sender: mail.senders?.item?.[0]?.email,
            senderName: mail.senders?.item?.[0]?.nick,
            date: mail.totime
        };

        return mail.normal_attach
            .filter(attach => attach?.name && attach?.download_url)
            .map(attach => {
                const dotIndex = attach.name.lastIndexOf('.');
                const nameWithoutExt = dotIndex > 0 ? attach.name.substring(0, dotIndex) : attach.name;
                const ext = dotIndex > 0 ? attach.name.substring(dotIndex + 1) : '';

                let downloadUrl = attach.download_url;
                if (!downloadUrl.startsWith('http')) {
                    downloadUrl = MAIL_CONSTANTS.BASE_URL + downloadUrl;
                }
                if (!downloadUrl.includes('sid=')) {
                    downloadUrl += `${downloadUrl.includes('?') ? '&' : '?'}sid=${sid}`;
                }

                return {
                    ...attach,
                    ...mailData,
                    nameWithoutExt,
                    ext,
                    type: ext?.toLowerCase() || '', // 添加type字段,与本地文件保持一致
                    download_url: downloadUrl
                };
            });
    }

    // 字符串和数据处理工具方法
    // 统一数据处理工具
    processData(data, operation) {
        switch (operation) {
            case 'escapeHtml':
                const div = document.createElement('div');
                div.textContent = data;
                return div.innerHTML;
            case 'createSafeDate':
                if (!data) return null;
                let date;
                if (typeof data === 'string') {
                    date = new Date(data);
                } else if (typeof data === 'number') {
                    date = new Date(data < 10000000000 ? data * 1000 : data);
                } else {
                    date = new Date(data);
                }
                return isNaN(date.getTime()) ? null : date;
            default:
                return data;
        }
    }

    // 统一UI工厂
    createUI(type, config = {}) {
        const { className, styles, content, events, children, attributes, id } = config;
        let element;

        switch (type) {
            case 'button':
                element = document.createElement('button');
                if (content) element.innerHTML = content;
                break;
            case 'div':
                element = document.createElement('div');
                if (content) element.innerHTML = content;
                break;
            case 'span':
                element = document.createElement('span');
                if (content) element.textContent = content;
                break;
            case 'dialog':
                element = document.createElement('div');
                StyleManager.applyStyle(element, 'dialogs', 'compareDialog');
                break;
            case 'menu':
                element = document.createElement('div');
                StyleManager.applyStyle(element, 'menus', 'dropdown');
                break;
            case 'menuItem':
                element = document.createElement('div');
                StyleManager.applyStyle(element, 'menus', 'item');
                if (content) element.textContent = content;
                break;
            case 'card':
                element = document.createElement('div');
                StyleManager.applyStyle(element, 'cards', 'attachment');
                break;
            case 'toolbar':
                element = document.createElement('div');
                StyleManager.applyStyle(element, 'toolbars', 'overlay');
                break;
            default:
                element = document.createElement(type);
        }

        if (id) element.id = id;
        if (className) element.className = className;
        if (attributes) {
            Object.entries(attributes).forEach(([key, value]) => {
                element.setAttribute(key, value);
            });
        }
        if (styles) {
            if (typeof styles === 'string') {
                // 如果styles是字符串,直接设置cssText
                element.style.cssText = styles;
            } else if (typeof styles === 'object' && styles !== null) {
                // 如果styles是对象,逐个设置属性
                Object.entries(styles).forEach(([key, value]) => {
                    // 过滤掉数字索引和无效的CSS属性名
                    if (typeof key === 'string' &&
                        !key.match(/^\d+$/) &&
                        (typeof value === 'string' || typeof value === 'number')) {
                        try {
                            element.style[key] = value;
                        } catch (error) {
                            // 静默忽略CSS属性设置错误
                        }
                    }
                });
            }
        }
        if (events) {
            Object.entries(events).forEach(([event, handler]) => {
                if (event === 'hover') {
                    element.onmouseover = handler.enter;
                    element.onmouseout = handler.leave;
                } else {
                    element.addEventListener(event, handler);
                }
            });
        }
        if (children) {
            children.forEach(child => element.appendChild(child));
        }

        return element;
    }

    // DOM操作工具
    domHelper(operation, ...args) {
        switch (operation) {
            case 'query': return document.querySelector(args[0]);
            case 'queryAll': return document.querySelectorAll(args[0]);
            case 'remove':
                const elements = typeof args[0] === 'string' ? document.querySelectorAll(args[0]) : [args[0]];
                elements.forEach(el => el?.remove());
                break;
            default: return null;
        }
    }





    getFileIcon(filename) {
        const ext = filename?.split('.').pop()?.toLowerCase() || '';
        const icons = {
            // 图片
            'jpg': '🖼️', 'jpeg': '🖼️', 'png': '🖼️', 'gif': '🖼️', 'bmp': '🖼️', 'webp': '🖼️',
            // 文档
            'doc': '📄', 'docx': '📄', 'pdf': '📄', 'txt': '📄', 'rtf': '📄',
            // 表格
            'xls': '📊', 'xlsx': '📊', 'csv': '📊',
            // 演示文稿
            'ppt': '📑', 'pptx': '📑',
            // 压缩包
            'zip': '🗜️', 'rar': '🗜️', '7z': '🗜️', 'tar': '🗜️', 'gz': '🗜️',
            // 其他
            'default': '📎'
        };
        return icons[ext] || icons.default;
    }

    // 获取文件缩略图URL
    getThumbnailUrl(attachment) {
        if (!attachment || !attachment.name || !attachment.fileid || !attachment.mailId) {
            return null;
        }

        const ext = attachment.name.split('.').pop()?.toLowerCase() || '';
        const supportedImageTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
        const supportedDocTypes = ['pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx'];
        
        // 只为支持预览的文件类型生成缩略图URL
        if (!supportedImageTypes.includes(ext) && !supportedDocTypes.includes(ext)) {
            return null;
        }

        // 获取当前的sid
        const sid = this.downloader?.getSid?.() || '';
        if (!sid) {
            return null;
        }

        // 构建缩略图URL,参考您提供的格式
        const thumbnailUrl = `https://wx.mail.qq.com/attach/thumbnail?mailid=${attachment.mailId}&fileid=${attachment.fileid}&name=${encodeURIComponent(attachment.name)}&sid=${sid}`;
        
        return thumbnailUrl;
    }

    // 判断文件是否支持缩略图预览
    supportsThumbnail(filename) {
        const ext = filename?.split('.').pop()?.toLowerCase() || '';
        const supportedTypes = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'pdf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx'];
        return supportedTypes.includes(ext);
    }

    getFilteredAttachments() {
        if (!Array.isArray(this.attachments)) {
            return [];
        }

        let filtered = this.attachments.filter(attachment => {
            if (!attachment) {
                return false;
            }

            // 新的文件类型筛选
            if (this.currentFilter !== 'all') {
                const fileType = this.getFileType(attachment.name);
                switch (this.currentFilter) {
                    case 'images':
                        if (fileType !== '图片') return false;
                        break;
                    case 'documents':
                        if (fileType !== '文档') return false;
                        break;
                    case 'archives':
                        if (fileType !== '压缩包') return false;
                        break;
                    case 'others':
                        if (['图片', '文档', '压缩包', '视频', '音频'].includes(fileType)) return false;
                        break;
                }
            }

            // 原有的日期筛选
            if (this.filters.date !== 'all') {
                const date = this.processData(attachment.date || attachment.totime, 'createSafeDate');
                if (!date) return false; // 如果日期无效,过滤掉

                const now = new Date();
                switch (this.filters.date) {
                    case 'today':
                        if (date.toDateString() !== now.toDateString()) return false;
                        break;
                    case 'week':
                        const weekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
                        if (date < weekAgo) return false;
                        break;
                    case 'month':
                        if (date.getMonth() !== now.getMonth() || date.getFullYear() !== now.getFullYear()) return false;
                        break;
                    case 'custom':
                        if (this.filters.dateRange.start && date < this.filters.dateRange.start) return false;
                        if (this.filters.dateRange.end && date > this.filters.dateRange.end) return false;
                        break;
                }
            }

            // 文件大小过滤(使用设置中的配置)
            const fileSize = attachment.size;
            if (this.filters.minSize > 0 && fileSize < this.filters.minSize) return false;
            if (this.filters.maxSize > 0 && fileSize > this.filters.maxSize) return false;

            // 文件类型过滤(使用设置中的配置)
                    const fileName = attachment.name;
            const fileExt = fileName?.split('.').pop()?.toLowerCase() || '';

            // 允许的文件类型
            if (this.filters.allowedTypes && this.filters.allowedTypes.length > 0) {
                if (!this.filters.allowedTypes.includes(fileExt)) return false;
            }

            // 排除的文件类型
            if (this.filters.excludedTypes && this.filters.excludedTypes.length > 0) {
                if (this.filters.excludedTypes.includes(fileExt)) return false;
            }

            return true;
        });

        // 应用排序
        return this.getSortedAttachments(filtered);
    }

    getSortedAttachments(attachments) {
        return attachments.sort((a, b) => {
            let comparison = 0;
            switch (this.currentSort) {
                case 'name':
                    comparison = a.name.localeCompare(b.name);
                    break;
                case 'size':
                    comparison = a.size - b.size;
                    break;
                case 'date':
                    const dateA = a.date || a.totime;
                    const dateB = b.date || b.totime;
                    comparison = dateA && dateB ? dateA - dateB : 0;
                    break;
                case 'type':
                    const typeA = this.getFileType(a.name);
                    const typeB = this.getFileType(b.name);
                    comparison = typeA.localeCompare(typeB);
                    break;
                default:
                    // 默认按日期降序
                    const defaultDateA = a.date || a.totime;
                    const defaultDateB = b.date || b.totime;
                    comparison = defaultDateB - defaultDateA;
                    break;
            }
            // 默认降序排列,除非是名称排序
            return this.currentSort === 'name' ? comparison : -comparison;
        });
    }

    showSortMenu(button) {
        const rect = button.getBoundingClientRect();
        const menu = this.createUI('menu', {
            styles: {
                top: (rect.bottom + 4) + 'px',
                left: rect.left + 'px'
            }
        });

        const sortOptions = [
            { value: 'date', label: '按日期' },
            { value: 'size', label: '按大小' },
            { value: 'name', label: '按名称' }
        ];

        sortOptions.forEach(option => {
            const item = this.createUI('menuItem', { content: option.label });

            if (this.filters.sortBy === option.value) {
                item.style.background = 'var(--base_gray_005, rgba(20, 46, 77, 0.05))';
                const orderIcon = this.createUI('span', {
                    content: this.filters.sortOrder === 'asc' ? '↑' : '↓'
                });
                item.appendChild(orderIcon);
            }

            item.addEventListener('click', () => {
                if (this.filters.sortBy === option.value) {
                    this.filters.sortOrder = this.filters.sortOrder === 'asc' ? 'desc' : 'asc';
                } else {
                    this.filters.sortBy = option.value;
                    this.filters.sortOrder = 'desc';
                }
                this.applyFilters();
                menu.remove();
            });

            menu.appendChild(item);
        });

        document.body.appendChild(menu);

        const closeMenu = (e) => {
            if (!menu.contains(e.target) && e.target !== button) {
                menu.remove();
                document.removeEventListener('click', closeMenu);
            }
        };

        setTimeout(() => {
            document.addEventListener('click', closeMenu);
        }, 0);
    }



    applySearch(keyword) {
        if (!keyword) {
            this.displayAttachments(this.attachments);
            return;
        }

        keyword = keyword.toLowerCase();
        const filteredAttachments = this.attachments.filter(attachment =>
            attachment.name.toLowerCase().includes(keyword) ||
            (attachment.mailSubject && attachment.mailSubject.toLowerCase().includes(keyword)) ||
            (attachment.senderName && attachment.senderName.toLowerCase().includes(keyword))
        );
        this.displayAttachments(filteredAttachments);
    }

    // 统一选择管理
    manageSelection(action = 'toggle') {
        const checkboxes = this.domHelper('queryAll', `${AttachmentManager.SELECTORS.CONTENT_AREA} input[type="checkbox"]`);
        const allSelected = this.selectedAttachments.size === checkboxes.length;

        switch (action) {
            case 'toggle':
                if (allSelected) {
                    this.selectedAttachments.clear();
                    checkboxes.forEach(cb => cb.checked = false);
                } else {
                    checkboxes.forEach(cb => {
                        cb.checked = true;
                        this.selectedAttachments.add(cb.dataset.attachmentId);
                    });
                }
                break;
            case 'clear':
                this.selectedAttachments.clear();
                checkboxes.forEach(cb => cb.checked = false);
                break;
            case 'selectAll':
                checkboxes.forEach(cb => {
                    cb.checked = true;
                    this.selectedAttachments.add(cb.dataset.attachmentId);
                });
                break;
        }
        this.updateSmartDownloadButton();
    }

    // 创建标准按钮
    createButton(type, config = {}) {
        const { text, onClick, className = '', styles = {} } = config;
        const buttonStyles = {
            primary: {
                padding: '0 24px', height: '32px', background: '#0F7AF5', color: '#fff',
                border: 'none', borderRadius: '4px', fontSize: '12px', fontWeight: '700',
                cursor: 'pointer', transition: 'all 0.2s', boxShadow: '0 2px 8px 0 rgba(15,122,245,0.08)'
            },
            secondary: {
                padding: '0 24px', height: '32px', background: '#fff', color: '#0F7AF5',
                border: '1.5px solid #0F7AF5', borderRadius: '4px', fontSize: '12px', fontWeight: '600',
                cursor: 'pointer', transition: 'all 0.2s'
            },
            outline: {
                padding: '0 16px', height: '32px', background: '#fff', color: '#666',
                border: '1px solid #d9d9d9', borderRadius: '4px', fontSize: '12px', fontWeight: '500',
                cursor: 'pointer', transition: 'all 0.2s'
            }
        };

        return this.createUI('button', {
            content: text,
            className,
            styles: { ...buttonStyles[type], ...styles },
            events: {
                click: onClick,
                hover: {
                    enter: () => {
                        if (type === 'primary') {
                            event.target.style.background = '#0e66cb';
                            event.target.style.boxShadow = '0 4px 16px 0 rgba(15,122,245,0.13)';
                        } else if (type === 'outline') {
                            event.target.style.backgroundColor = '#f5f5f5';
                            event.target.style.borderColor = '#40a9ff';
                        }
                    },
                    leave: () => {
                        if (type === 'primary') {
                            event.target.style.background = '#0F7AF5';
                            event.target.style.boxShadow = '0 2px 8px 0 rgba(15,122,245,0.08)';
                        } else if (type === 'outline') {
                            event.target.style.backgroundColor = '#fff';
                            event.target.style.borderColor = '#d9d9d9';
                        }
                    }
                }
            }
        });
    }

    // 统一下载方法
    async downloadAll() {
        const filteredAttachments = this.getFilteredAttachments();
        await this.performDownload(filteredAttachments, '全部');
    }

    async downloadSelected() {
        if (this.selectedAttachments.size === 0) {
            this.showToast('请先选择要下载的附件', 'warning');
            return;
        }
        const selectedAttachments = this.attachments.filter(att => this.selectedAttachments.has(att.name_md5));
        await this.performDownload(selectedAttachments, '选中', true);
    }

    async performDownload(attachments, type, clearSelection = false) {
        if (this.downloading) {
            this.showToast('已有下载任务正在进行中', 'warning');
            return;
        }

        if (attachments.length === 0) {
            this.showToast(`当前没有可下载的${type}附件`, 'warning');
            return;
        }

        try {
            const dirHandle = await this.selectDownloadFolder();

            this.downloading = true;
            this.showToast(`准备下载${type} ${attachments.length} 个附件...`, 'info');

            this.totalTasksForProgress = attachments.length;
            this.completedTasksForProgress = 0;
            this.showProgress();
            this.updateDownloadProgress();

            const results = await this.downloadWithConcurrency(attachments, dirHandle);
            const successCount = results.filter(r => !r.error).length;
            const failCount = results.filter(r => r.error).length;

            this.showToast(
                failCount > 0
                    ? `${type}下载完成。成功: ${successCount},失败: ${failCount}`
                    : `${type} ${successCount} 个附件下载成功完成。`,
                failCount > 0 ? 'warning' : 'success'
            );
            this.updateStatus('下载处理完毕');

            if (clearSelection) {
                this.manageSelection('clear');
            }

            // 自动启动对比功能(如果下载成功且支持文件系统API)
            if (successCount > 0 && window.showDirectoryPicker && this.downloadSettings.downloadBehavior?.autoCompareAfterDownload) {
                this.autoCompareAfterDownload(dirHandle, successCount, failCount, type);
            }

        } catch (error) {
            if (error.name === 'AbortError') {
                this.showToast('已取消下载', 'info');
            } else {
                this.showToast(`下载过程中发生错误: ${error.message}`, 'error');
            }
        } finally {
            this.downloading = false;
            this.hideProgress();
        }
    }

    // 下载完成后自动对比
    async autoCompareAfterDownload(dirHandle, successCount, failCount, type) {
        try {
            // 延迟一点时间,确保文件系统写入完成
            await new Promise(resolve => setTimeout(resolve, 1000));
            
            this.showToast(`${type}下载完成,正在自动对比本地文件...`, 'info', 2000);
            
            // 直接使用下载时的目录句柄进行对比
            await this.showComparisonResults(dirHandle);
            
        } catch (error) {
            console.error('自动对比失败:', error);
            // 根据错误类型给出不同的提示
            if (error.name === 'NotAllowedError') {
                this.showToast('自动对比需要文件系统权限,请手动点击"对比本地"按钮', 'warning');
            } else if (error.name === 'AbortError') {
                // 用户取消了对比,不需要显示错误
                return;
            } else {
                this.showToast('自动对比失败,可手动点击"对比本地"按钮进行对比', 'warning');
            }
        }
    }

    async downloadAttachment(attachment, dirHandle, namingStrategy = null) {
        const attachmentName = attachment.name || 'unknown_attachment';

        try {
            // 获取附件内容
            const response = await this.downloader.fetchAttachment(attachment);

            // 确定目标文件夹
            const targetFolderHandle = await this.getTargetFolder(dirHandle, attachment);

            // 检查是否支持文件系统访问API
            if (window.showDirectoryPicker) {
                try {
                    const fileNamingConfig = this.downloadSettings.fileNaming;
                    const baseFileName = this.generateFileName(attachment, fileNamingConfig, this.attachments, namingStrategy);
                    const finalFileName = await this.handleFileConflict(targetFolderHandle, baseFileName);

                    const fileHandle = await targetFolderHandle.getFileHandle(finalFileName, { create: true });
                    const writable = await fileHandle.createWritable();
                    await writable.write(response.response);
                    await writable.close();

                    // 验证下载完整性
                    if (this.downloadSettings.downloadBehavior.verifyDownloads) {
                        const isValid = await this.verifyDownload(fileHandle, response.response.size);
                        if (!isValid) {
                            console.warn(`文件 ${finalFileName} 下载验证失败`);
                        }
                    }

                    return true;
                } catch (error) {
                    throw error;
                }
            } else {
                try {
                    // 使用GM_download
                    const fileNamingConfig = this.downloadSettings.fileNaming;
                    const blob = response.response;
                    const url = URL.createObjectURL(blob);
                    const fileName = this.generateFileName(attachment, fileNamingConfig, this.attachments, namingStrategy);

                    await new Promise((resolve, reject) => {
                        GM_download({
                            url: url,
                            name: fileName,
                            saveAs: true,
                                onload: function () {
                                URL.revokeObjectURL(url);
                                resolve();
                            },
                                onerror: function (error) {
                                URL.revokeObjectURL(url);
                                reject(error);
                            }
                        });
                    });
                    return true;
                } catch (error) {
                    throw error;
                }
            }
        } catch (error) {
            throw error;
        }
    }

    // 验证下载完整性
    async verifyDownload(fileHandle, expectedSize) {
        try {
            const file = await fileHandle.getFile();
            const actualSize = file.size;
            return actualSize === expectedSize;
        } catch (error) {
            return false;
        }
    }

    updateDownloadProgress() {
        if (this.totalTasksForProgress === 0) {
            const progressElement = document.querySelector('#download-progress-bar');
            if (progressElement) {
                progressElement.style.width = '0%';
            }
            return;
        }

        const progress = (this.completedTasksForProgress / this.totalTasksForProgress) * 100;
        const [progressElement, statusElement] = [
            this.domHelper('query', '#download-progress-bar'),
            this.domHelper('query', '#download-status')
        ];

        if (progressElement) {
            progressElement.style.width = `${progress}%`;

            if (progress < 100) {
                progressElement.style.transition = 'width 0.3s ease-in-out';
            } else {
                progressElement.style.transition = 'none';
            }
        }

        if (statusElement) {
            // 确保统计数据有效
            const completedSize = this.downloadStats.completedSize || 0;
            const totalSize = this.downloadStats.totalSize || 0;
            const speed = this.downloadStats.speed || 0;
            
            // 计算剩余时间
            const remainingSize = Math.max(0, totalSize - completedSize);
            const remainingTime = speed > 0 ? remainingSize / speed : 0;

            // 格式化时间
            const formatTime = (seconds) => {
                if (!seconds || seconds <= 0) return '计算中...';
                if (seconds < 60) return `${Math.round(seconds)}秒`;
                if (seconds < 3600) return `${Math.round(seconds / 60)}分钟`;
                return `${Math.round(seconds / 3600)}小时`;
            };

            // 更新状态文本
            statusElement.innerHTML = `
                ${Math.round(progress)}% -
                ${this.formatData(completedSize, 'size')} / ${this.formatData(totalSize, 'size')} -
                ${this.formatData(speed, 'size')}/s -
                剩余时间: ${formatTime(remainingTime)}
            `;
        }
    }

    // 统一状态管理
    updateStatus(message, showProgress = false) {
        // 更新工具栏副标题状态
        const countInfo = document.getElementById('attachment-count-info');
        if (countInfo) {
            countInfo.textContent = message;
        }

        // 更新进度条区域状态(用于下载过程)
        const status = document.getElementById('download-status');
        if (status) {
            status.textContent = message;
            status.style.opacity = '0';
            setTimeout(() => {
                status.style.transition = 'opacity 0.3s ease';
                status.style.opacity = '1';
            }, 10);
        }

        if (showProgress) this.showProgress(message);
    }

    showProgress(message = '正在下载附件...') {
        const progressArea = document.getElementById('attachment-progress-area');
        if (progressArea) {
            StyleManager.applyStyle(progressArea, 'progress', 'area');
            progressArea.innerHTML = `
                <div style="margin-bottom: 8px; font-weight: 500; color: var(--base_gray_100, #13181D);">${message}</div>
                <div style="background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); border-radius: 4px; height: 8px; overflow: hidden; margin-bottom: 8px;">
                    <div id="download-progress-bar" style="background: var(--theme_primary, #0F7AF5); height: 100%; width: 0%; transition: width 0.3s ease;"></div>
                </div>
                <div id="download-status" style="font-size: 12px; color: var(--base_gray_050, #888);">准备开始...</div>
            `;
        }
    }

    hideProgress() {
        const progressArea = document.getElementById('attachment-progress-area');
        if (progressArea) {
            progressArea.style.display = 'none';
            progressArea.innerHTML = '';
        }
    }

    showToast(message, type = 'info', duration = 3000) {
        const icon = AttachmentManager.TOAST_ICONS[type];

        const toast = this.createUI('div', {
            content: `${icon} ${message}`,
            styles: {
                ...StyleManager.getStyles().toasts.base,
                ...StyleManager.getStyles().toastExtensions.custom,
                borderLeft: `3px solid ${this.getToastColor(type)}`
            }
        });

        // 确保动画样式已添加
        this.ensureToastAnimations();

        document.body.appendChild(toast);

        setTimeout(() => {
            if (toast.parentElement) {
                toast.style.animation = 'slideOut 0.3s ease-out';
                setTimeout(() => toast.remove(), 300);
            }
        }, duration);
    }

    ensureToastAnimations() {
        // 检查是否已经添加了动画样式
        if (document.getElementById('toast-animations')) return;
        
        const style = document.createElement('style');
        style.id = 'toast-animations';
        style.textContent = `
            @keyframes slideIn { 
                from { transform: translateX(100%); opacity: 0; } 
                to { transform: translateX(0); opacity: 1; } 
            }
            @keyframes slideOut { 
                from { transform: translateX(0); opacity: 1; } 
                to { transform: translateX(100%); opacity: 0; } 
            }
        `;
        document.head.appendChild(style);
    }

    ensureVariableSelectorStyles() {
        // 检查是否已经添加了变量选择器样式
        if (document.getElementById('variable-selector-styles')) return;
        
        const style = document.createElement('style');
        style.id = 'variable-selector-styles';
        style.textContent = `
            @keyframes slideIn {
                from {
                    opacity: 0;
                    transform: scale(0.9) translateY(-20px);
                }
                to {
                    opacity: 1;
                    transform: scale(1) translateY(0);
                }
            }
            .popup-header {
                flex-shrink: 0;
                padding: 24px 28px 16px 28px;
                border-bottom: 1px solid #f0f0f0;
            }
            .template-section {
                flex-shrink: 0;
                padding: 20px 28px;
                background: #fafbfc;
                border-bottom: 1px solid #e8eaed;
            }
            .variables-section {
                flex: 1;
                overflow-y: auto;
                padding: 20px 28px;
                min-height: 0;
            }
            .variables-section::-webkit-scrollbar {
                width: 6px;
            }
            .variables-section::-webkit-scrollbar-track {
                background: #f1f1f1;
                border-radius: 3px;
            }
            .variables-section::-webkit-scrollbar-thumb {
                background: #c1c1c1;
                border-radius: 3px;
            }
            .variables-section::-webkit-scrollbar-thumb:hover {
                background: #a8a8a8;
            }
            .popup-footer {
                flex-shrink: 0;
                padding: 16px 28px 24px 28px;
                border-top: 1px solid #f0f0f0;
                background: #fafbfc;
            }
            .variable-group {
                margin-bottom: 18px;
            }
            .variable-group:last-child {
                margin-bottom: 0;
            }
            .variable-group-title {
                font-size: 14px;
                font-weight: 600;
                color: #333;
                margin-bottom: 8px;
                padding-bottom: 4px;
                border-bottom: 1px solid #e0e0e0;
            }
            .variables-grid {
                display: grid;
                grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
                gap: 8px;
            }
            .variable-item {
                padding: 10px 12px;
                border: 1px solid #e0e0e0;
                border-radius: 6px;
                cursor: pointer;
                transition: all 0.15s ease;
                background: white;
                box-shadow: 0 1px 2px rgba(0,0,0,0.05);
            }
            .variable-item:hover {
                background: #f0f8ff;
                border-color: #007bff;
                box-shadow: 0 2px 4px rgba(0,123,255,0.1);
                transform: translateY(-1px);
            }
            .variable-key {
                font-weight: 600;
                color: #007bff;
                font-family: 'Consolas', 'Monaco', monospace;
                font-size: 12px;
            }
            .variable-name {
                font-size: 10px;
                color: #666;
                background: #f5f5f5;
                padding: 1px 4px;
                border-radius: 2px;
                display: inline-block;
            }
            .variable-desc {
                font-size: 11px;
                color: #666;
                margin-top: 2px;
                line-height: 1.2;
            }
        `;
        document.head.appendChild(style);
    }

    getToastColor(type) {
        const colors = {
            success: 'var(--chrome_green, #16F761)',
            error: 'var(--chrome_red, #F73116)',
            warning: 'var(--chrome_orange, #F7A316)',
            info: 'var(--theme_primary, #0F7AF5)'
        };
        return colors[type] || colors.info;
    }

    // 统一格式化工具
    formatData(data, type) {
        switch (type) {
            case 'size':
                if (!data || data === 0) return '0 B';
                const units = ['B', 'KB', 'MB', 'GB'];
                let size = data;
                let unitIndex = 0;
                while (size >= 1024 && unitIndex < units.length - 1) {
                    size /= 1024;
                    unitIndex++;
                }
                return `${size.toFixed(2)} ${units[unitIndex]}`;
            case 'totalSize':
                const totalSize = data.reduce((sum, att) => sum + att.size, 0);
                return this.formatData(totalSize, 'size');
            default:
                return data;
        }
    }

    groupAttachmentsByMail(attachments) {
        const groups = new Map();
        for (const attachment of attachments) {
            const key = attachment.mailId;
            if (!groups.has(key)) {
                groups.set(key, {
                    mailId: attachment.mailId,
                    subject: attachment.mailSubject,
                    date: attachment.totime,
                    sender: attachment.sender,
                    senderName: attachment.senderName,
                    attachments: []
                });
            }
            groups.get(key).attachments.push(attachment);
        }
        return Array.from(groups.values());
    }

    // 下载分组附件
    async downloadGroupAttachments(group, groupContainer) {
        if (this.downloading) {
            this.showToast('已有下载任务正在进行中', 'warning');
            return;
        }

        const checkboxes = groupContainer.querySelectorAll('input[type="checkbox"]');
        const selectedIds = Array.from(checkboxes).filter(cb => cb.checked).map(cb => cb.dataset.attachmentId);
        const attachmentsToDownload = selectedIds.length > 0
            ? group.attachments.filter(a => selectedIds.includes(a.name_md5))
            : group.attachments;

        if (!attachmentsToDownload?.length) {
            this.showToast('此邮件组没有可下载的附件', 'info');
            return;
        }

        try {
            const dirHandle = await this.selectDownloadFolder();
            this.downloading = true;
            this.showToast(`准备并发下载邮件组中的 ${attachmentsToDownload.length} 个附件...`, 'info');
            this.totalTasksForProgress = attachmentsToDownload.length;
            this.completedTasksForProgress = 0;
            this.showProgress();
            this.updateDownloadProgress();

            const results = await this.downloadWithConcurrency(attachmentsToDownload, dirHandle);
            const successCount = results.filter(r => !r.error).length;
            const failCount = results.filter(r => r.error).length;

            this.showToast(
                failCount > 0
                    ? `邮件组下载完成。成功: ${successCount},失败: ${failCount}`
                    : `邮件组中 ${successCount} 个附件下载成功。`,
                failCount > 0 ? 'warning' : 'success'
            );
            this.updateStatus('下载处理完毕');

            // 自动启动对比功能(如果下载成功且支持文件系统API)
            if (successCount > 0 && window.showDirectoryPicker && this.downloadSettings.downloadBehavior?.autoCompareAfterDownload) {
                this.autoCompareAfterDownload(dirHandle, successCount, failCount, '邮件组');
            }
        } catch (error) {
            this.showToast(
                error.name === 'AbortError'
                    ? '已取消选择文件夹进行分组下载'
                    : `邮件组下载过程中发生错误: ${error.message}`,
                error.name === 'AbortError' ? 'info' : 'error'
            );
        } finally {
            this.downloading = false;
            this.hideProgress();
        }
    }

    // 统一状态显示方法
    showState(type, message = '', container = null) {
        // 移除已存在的状态
        const existingState = document.getElementById('attachment-state');
        if (existingState) {
            existingState.remove();
        }

        const stateDiv = this.createUI('div', {
            styles: { id: 'attachment-state' }
        });
        stateDiv.id = 'attachment-state';

        if (type === 'loading') {
            const loadingStyles = StyleManager.getStyles().states.loading;
            stateDiv.style.cssText = loadingStyles;
            const spinner = this.createUI('div', {
                styles: StyleManager.getStyles().states.spinner
            });
            const text = this.createUI('div', {
                content: message || '正在加载附件...',
                styles: StyleManager.getStyles().progress.text
            });
            stateDiv.appendChild(spinner);
            stateDiv.appendChild(text);
            StyleManager.addSpinnerAnimation();
        } else if (type === 'error') {
            const errorStyles = {
                ...StyleManager.getStyles().toasts.base,
                ...StyleManager.getStyles().errors.centered,
                borderLeft: '3px solid var(--chrome_red, #F73116)'
            };
            Object.entries(errorStyles).forEach(([key, value]) => {
                if (typeof value === 'string' || typeof value === 'number') {
                    stateDiv.style[key] = value;
                }
            });
            const icon = this.createUI('div', {
                content: '!',
                styles: StyleManager.getStyles().texts.errorIcon
            });
            const text = this.createUI('div', {
                content: message,
                styles: StyleManager.getStyles().texts.errorText
            });
            stateDiv.appendChild(icon);
            stateDiv.appendChild(text);
            setTimeout(() => stateDiv.remove(), 5000);
        }

        if (container) {
            container.style.position = 'relative';
            container.appendChild(stateDiv);
        }
    }



    // 获取目标文件夹
    async getTargetFolder(baseDirHandle, attachment) {
        let currentDirHandle = baseDirHandle;

        switch (this.downloadSettings.folderStructure) {
            case 'subject':
                // 按主题组织
                const subjectFolderName = this.sanitizeFileName(attachment.mailSubject || attachment.subject || '未知主题', attachment);
                if (subjectFolderName) {
                    currentDirHandle = await this.getOrCreateFolder(currentDirHandle, subjectFolderName);
                }
                break;

            case 'sender':
                // 按发件人组织
                const senderFolderName = this.sanitizeFileName(attachment.senderEmail || attachment.sender || '未知发件人', attachment);
                if (senderFolderName) {
                    currentDirHandle = await this.getOrCreateFolder(currentDirHandle, senderFolderName);
                }
                break;

            case 'date':
                // 按日期组织 (MM-DD格式)
                const mailDate = attachment.date || attachment.totime ? 
                    new Date(typeof (attachment.date || attachment.totime) === 'number' && (attachment.date || attachment.totime) < 10000000000 ? 
                        (attachment.date || attachment.totime) * 1000 : (attachment.date || attachment.totime)) : new Date();
                const dateFolderName = this.formatDate(mailDate, 'MM-DD');
                if (dateFolderName) {
                    currentDirHandle = await this.getOrCreateFolder(currentDirHandle, dateFolderName);
                }
                break;

            case 'custom':
                // 自定义文件夹结构 - 使用自定义模板
                const customTemplate = this.downloadSettings.folderNaming?.customTemplate || '{date:YYYY-MM}/{senderName}';
                const folderPaths = this.generateCustomFolderPath(customTemplate, attachment);
                for (const folderPath of folderPaths) {
                    if (folderPath) {
                        currentDirHandle = await this.getOrCreateFolder(currentDirHandle, folderPath);
                    }
                }
                break;

            case 'flat':
                // 不创建子文件夹
                return currentDirHandle;
        }

        return currentDirHandle;
    }

    // 生成文件夹名称
    generateFolderName(template, attachment) {
        const variableData = this.buildVariableData(attachment);
        let folderName = this.replaceVariablesInTemplate(template, variableData);
        
        // 清理文件夹名中的无效字符
        folderName = this.sanitizeFileName(folderName, attachment);
        
        // 如果文件夹名为空或只包含无效字符,使用默认名称
        if (!folderName || folderName.trim() === '') {
            folderName = '未知文件夹';
        }
        
        return folderName;
    }

    // 生成自定义文件夹路径
    generateCustomFolderPath(template, attachment) {
        const variableData = this.buildVariableData(attachment);
        let fullPath = this.replaceVariablesInTemplate(template, variableData);
        
        // 按 / 分割路径
        const folderPaths = fullPath.split('/').map(path => {
            const cleanPath = this.sanitizeFileName(path.trim(), attachment);
            return cleanPath || '未知文件夹';
        }).filter(path => path && path !== '');
        
        return folderPaths;
    }

    // 根据总数计算补零位数(最少2位)
    calculatePaddingDigits(total) {
        return Math.max(2, total.toString().length);
    }

    // 格式化索引号,根据总数动态补零
    formatIndex(index, total) {
        const digits = this.calculatePaddingDigits(total);
        return String(index || 1).padStart(digits, '0');
    }

    // 构建变量数据
    buildVariableData(attachment) {
        const now = new Date();
        const mailDate = attachment.date || attachment.totime ? 
            new Date(typeof (attachment.date || attachment.totime) === 'number' && (attachment.date || attachment.totime) < 10000000000 ? 
                (attachment.date || attachment.totime) * 1000 : (attachment.date || attachment.totime)) : now;
        
        // 获取总邮件数和总附件数用于动态补零
        const totalMails = this.totalMailCount || (this.attachments ? new Set(this.attachments.map(att => att.mailId)).size : 1);
        const totalAttachments = this.attachments ? this.attachments.length : 1;
        
        return {
            // 邮件信息(对可能包含非法字符的字段进行预清理)
            subject: this.sanitizeFileName(attachment.mailSubject || attachment.subject || '未知主题', attachment),
            sender: this.sanitizeFileName(attachment.sender || '未知发件人', attachment),
            senderEmail: attachment.senderEmail || '',
            senderName: this.sanitizeFileName(attachment.senderName || attachment.sender || '未知发件人', attachment),
            mailIndex: this.formatIndex(attachment.mailIndex, totalMails),
            folderID: attachment.folderId || '',
            folderName: this.sanitizeFileName(this.getFolderDisplayName() || '未知文件夹', attachment),
            mailId: attachment.mailId || '',
            
            // 附件信息
            fileName: attachment.name || '未知文件',
            fileNameNoExt: this.processFileName(attachment.name || '未知文件', 'removeExtension'),
            fileType: this.processFileName(attachment.name || '', 'getExtension'),
            fileId: attachment.fileid || '',
            fileIndex: this.formatIndex(attachment.fileIndex, totalAttachments),
            attachIndex: this.formatIndex(attachment.attachIndex, totalAttachments),
            size: attachment.size || 0,
            
            // 日期时间(保留原始Date对象和格式化字符串)
            date: mailDate, // 保留原始Date对象用于自定义格式化
            time: this.formatDate(mailDate, 'HH-mm-ss'),
            datetime: this.formatDate(mailDate, 'YYYY-MM-DD_HH-mm-ss'),
            timestamp: Math.floor(mailDate.getTime() / 1000),
            year: this.formatDate(mailDate, 'YYYY'),
            month: this.formatDate(mailDate, 'MM'),
            monthName: this.formatDate(mailDate, 'MMMM'),
            day: this.formatDate(mailDate, 'DD'),
            weekday: this.formatDate(mailDate, 'dddd'),
            weekdayName: this.formatDate(mailDate, 'ddd'),
            hour: this.formatDate(mailDate, 'HH'),
            hour12: this.formatDate(mailDate, 'hh'),
            minute: this.formatDate(mailDate, 'mm'),
            second: this.formatDate(mailDate, 'ss'),
            ampm: this.formatDate(mailDate, 'A'),
            AMPM: this.formatDate(mailDate, 'A')
        };
    }

    // 处理文件冲突
    async handleFileConflict(folderHandle, fileName) {
        try {
            // 检查文件是否存在
        try {
            await folderHandle.getFileHandle(fileName);
                // 文件已存在,需要处理冲突
                const conflictResolution = this.downloadSettings.conflictResolution;
                switch (conflictResolution) {
                    case 'rename':
                        return this.generateUniqueFileName(folderHandle, fileName);
                    case 'overwrite':
                        return fileName;
                    case 'skip':
                        throw new Error(`文件 ${fileName}已存在,跳过下载`);
                    default:
                        // 默认使用重命名
                        return this.generateUniqueFileName(folderHandle, fileName);
                }
            } catch (error) {
                // 文件不存在,直接返回原文件名
                return fileName;
            }
        } catch (error) {
            throw error;
        }
    }

    async generateUniqueFileName(folderHandle, fileName) {
        const ext = fileName.split('.').pop();
        const baseName = fileName.slice(0, -(ext.length + 1));
        let counter = 1;
        let newFileName = fileName;

        while (true) {
            try {
                await folderHandle.getFileHandle(newFileName);
                // 文件存在,尝试下一个名称
                newFileName = `${baseName}_${counter}.${ext}`;
                counter++;
        } catch (error) {
                // 文件不存在,可以使用这个名称
                return newFileName;
            }
        }
    }

    // 并发下载控制
    async downloadWithConcurrency(attachments, dirHandle) {
        // 预先分析命名策略(如果启用了auto模式)0
        let namingStrategy = null;
        const validation = this.downloadSettings.fileNaming.validation;
        if (validation?.enabled && validation.fallbackPattern === 'auto') {
            namingStrategy = this.analyzeAttachmentNaming(attachments, validation.pattern);
        }

        const results = [];
        const tasks = attachments.map(attachment => ({
            attachment, status: 'pending', retries: 0, namingStrategy
        }));

        this.totalTasksForProgress = tasks.length;
        this.completedTasksForProgress = 0;
        this.downloadStats = {
            startTime: Date.now(),
            completedSize: 0,
            totalSize: attachments.reduce((sum, att) => sum + (att.size || 0), 0),
            speed: 0,
            lastUpdate: Date.now()
        };

        // 根据用户设置决定使用动态并发还是固定并发
        const concurrentSetting = this.downloadSettings.downloadBehavior.concurrentDownloads;
        const useAutoMode = concurrentSetting === 'auto';
        const maxConcurrent = useAutoMode ? this.concurrentControl.currentConcurrent : parseInt(concurrentSetting) || 3;
        
        const activeDownloads = new Set();

        const processNext = async () => {
            // 检查是否超过并发限制
            const currentLimit = useAutoMode ? this.concurrentControl.currentConcurrent : maxConcurrent;
            if (activeDownloads.size >= currentLimit) {
                return;
            }
            
            const pendingTask = tasks.find(t => t.status === 'pending');
            if (!pendingTask) return;

            pendingTask.status = 'processing';
            const downloadPromise = this.downloadAttachment(pendingTask.attachment, dirHandle, pendingTask.namingStrategy)
                .then(() => {
                    pendingTask.status = 'completed';
                    results.push({ attachment: pendingTask.attachment, error: null });
                    this.completedTasksForProgress++;
                    
                    // 更新下载统计数据
                    const attachmentSize = pendingTask.attachment.size || 0;
                    this.updateDownloadStats(attachmentSize);
                    this.updateDownloadProgress();
                    
                    // 更新成功统计并调整并发数(仅在自动模式下)
                    if (useAutoMode) {
                        this.concurrentControl.successCount++;
                        this.adjustConcurrency();
                    }
                    
                    activeDownloads.delete(downloadPromise);
                    
                    // 尝试启动更多任务
                    const currentLimit = useAutoMode ? this.concurrentControl.currentConcurrent : maxConcurrent;
                    while (activeDownloads.size < currentLimit && 
                           tasks.some(t => t.status === 'pending')) {
                        processNext();
                    }
                })
                .catch(error => {
                    if (pendingTask.retries < this.retryCount) {
                        pendingTask.retries++;
                        pendingTask.status = 'pending';
                        activeDownloads.delete(downloadPromise);
                        
                        // 重试时也要考虑并发限制
                        const currentLimit = useAutoMode ? this.concurrentControl.currentConcurrent : maxConcurrent;
                        while (activeDownloads.size < currentLimit && 
                               tasks.some(t => t.status === 'pending')) {
                            processNext();
                        }
                    } else {
                        pendingTask.status = 'failed';
                        results.push({ attachment: pendingTask.attachment, error });
                        this.completedTasksForProgress++;
                        this.updateDownloadProgress();
                        
                        // 更新失败统计并调整并发数(仅在自动模式下)
                        if (useAutoMode) {
                            this.concurrentControl.failCount++;
                            this.adjustConcurrency();
                        }
                        
                        activeDownloads.delete(downloadPromise);
                        
                        // 尝试启动更多任务
                        const currentLimit = useAutoMode ? this.concurrentControl.currentConcurrent : maxConcurrent;
                        while (activeDownloads.size < currentLimit && 
                               tasks.some(t => t.status === 'pending')) {
                            processNext();
                        }
                    }
                });

            activeDownloads.add(downloadPromise);
        };

        // 启动初始下载任务
        const initialConcurrent = Math.min(maxConcurrent, tasks.length);
        for (let i = 0; i < initialConcurrent; i++) {
            processNext();
        }

        // 等待所有下载完成
        while (activeDownloads.size > 0) {
            await Promise.race(activeDownloads);
        }

        return results;
    }



    // 动态调整并发数
    adjustConcurrency() {
        const now = Date.now();
        if (!this.concurrentControl.lastAdjustTime ||
            now - this.concurrentControl.lastAdjustTime >= this.concurrentControl.adjustInterval) {

            const totalCount = this.concurrentControl.successCount + this.concurrentControl.failCount;
            if (totalCount === 0) return; // 没有足够的数据进行调整

            const successRate = this.concurrentControl.successCount / totalCount;
            const oldConcurrent = this.concurrentControl.currentConcurrent;

            if (successRate > 0.9) {
                // 成功率很高,可以增加并发
                this.concurrentControl.currentConcurrent = Math.min(
                    this.concurrentControl.currentConcurrent + 1,
                    this.concurrentControl.maxConcurrent
                );
            } else if (successRate < 0.7) {
                // 成功率较低,减少并发
                this.concurrentControl.currentConcurrent = Math.max(
                    this.concurrentControl.currentConcurrent - 1,
                    this.concurrentControl.minConcurrent
                );
            }

            // 如果并发数发生变化,显示调整信息
            if (this.concurrentControl.currentConcurrent !== oldConcurrent) {
                console.log(`[动态并发] 成功率: ${(successRate * 100).toFixed(1)}%, 并发数: ${oldConcurrent} → ${this.concurrentControl.currentConcurrent}`);
            }

            // 重置计数器
            this.concurrentControl.successCount = 0;
            this.concurrentControl.failCount = 0;
            this.concurrentControl.lastAdjustTime = now;
        }
    }

    // 更新下载统计
    updateDownloadStats(completedSize) {
        const now = Date.now();
        
        // 初始化时间戳
        if (!this.downloadStats.lastUpdate) {
            this.downloadStats.lastUpdate = this.downloadStats.startTime || now;
        }
        
        const timeDiff = (now - this.downloadStats.lastUpdate) / 1000; // 转换为秒
        const totalElapsed = (now - this.downloadStats.startTime) / 1000; // 总耗时

        this.downloadStats.completedSize += completedSize;
        this.downloadStats.lastUpdate = now;

        // 计算平均速度(基于总体进度,更稳定)
        if (totalElapsed > 0) {
            this.downloadStats.speed = this.downloadStats.completedSize / totalElapsed;
        }

        // 更新状态显示
        this.updateStatus(`下载中: ${this.formatData(this.downloadStats.completedSize, 'size')} / ${this.formatData(this.downloadStats.totalSize, 'size')} ` +
        `(${this.formatData(this.downloadStats.speed, 'size')}/s)`
        );
    }

    // 智能分组附件
    // 优化的智能分组算法(减少重复计算)
    groupAttachmentsSmartly(attachments) {
        const smartGroupingConfig = this.downloadSettings.smartGrouping;
        if (!smartGroupingConfig.enabled) {
            return this.groupAttachmentsByMail(attachments);
        }

        const groups = new Map();
        const { minGroupSize, maxGroupSize, groupBy } = smartGroupingConfig;

        // 预计算分组键生成器
        const keyGenerators = groupBy.map(criteria => {
            switch (criteria) {
                case 'type': return att => this.getFileType(att.name);
                case 'date': return att => {
                    const timestamp = att.date || att.totime;
                    if (timestamp) {
                        const date = new Date(typeof timestamp === 'number' && timestamp < 10000000000 ? timestamp * 1000 : timestamp);
                        return !isNaN(date.getTime()) ? this.formatDate(date, 'YYYYMMDD') : '';
                    }
                    return '';
                };
                case 'sender': return att => att.sender;
                default: return () => '';
            }
        });

        // 单次遍历生成分组
        attachments.forEach(attachment => {
            const groupKey = keyGenerators.map(gen => gen(attachment)).filter(Boolean).join('_');

            if (!groups.has(groupKey)) {
                groups.set(groupKey, {
                    key: groupKey,
                    attachments: [],
                    type: this.getFileType(attachment.name),
                    date: attachment.date,
                    sender: attachment.sender
                });
            }
            groups.get(groupKey).attachments.push(attachment);
        });

        // 批量处理分组大小
        const result = [];
        for (const group of groups.values()) {
            if (group.attachments.length >= minGroupSize && group.attachments.length <= maxGroupSize) {
                result.push(group);
            } else {
                result.push(...this.groupAttachmentsByMail(group.attachments));
            }
        }

        return result;
    }



    // 优化的命名模式解析
    parseNamingPattern(pattern, attachment) {
        if (!pattern || !attachment) return '';

        const replacements = {
            name: attachment.name,
            fileName: attachment.name,
            mailSubject: this.sanitizeFileName(attachment.mailSubject || '', attachment),
            subject: this.sanitizeFileName(attachment.mailSubject || '', attachment),
            sender: this.sanitizeFileName(attachment.senderName || attachment.sender || '', attachment),
            senderEmail: attachment.sender || '',
            mailId: attachment.mailId,
            attachmentId: attachment.fid,
            date: attachment.totime ? this.formatDate(new Date(typeof attachment.totime === 'number' && attachment.totime < 10000000000 ? attachment.totime * 1000 : attachment.totime), 'YYYYMMDD') : '',
            toTime: attachment.totime ? this.formatDate(new Date(typeof attachment.totime === 'number' && attachment.totime < 10000000000 ? attachment.totime * 1000 : attachment.totime), 'YYYYMMDDHHmmss') : '',
            fileType: this.processFileName(attachment.name, 'getExtension'),
            size: attachment.size ? this.formatData(attachment.size, 'size') : ''
        };

        return pattern.replace(/\{(\w+)\}/g, (match, key) => replacements[key] || match);
    }

    // 生成备用命名前缀
    generateFallbackPrefix(fallbackPattern, attachment) {
        if (!fallbackPattern || !attachment) return '';

        switch (fallbackPattern) {
            case 'mailSubject':
                return this.sanitizeFileName(attachment.mailSubject || '', attachment);
            case 'senderEmail':
                return this.sanitizeFileName(attachment.sender || '', attachment);
            case 'toTime':
                return attachment.totime ? this.formatDate(new Date(typeof attachment.totime === 'number' && attachment.totime < 10000000000 ? attachment.totime * 1000 : attachment.totime), 'YYYYMMDDHHmmss') : '';
            case 'customTemplate':
                // 这个会在后面的逻辑中处理
                return '';
            case 'auto':
            default:
                return '';
        }
    }

    // 优化的智能命名分析(减少重复过滤和计算)
    analyzeAttachmentNaming(attachments, validationPattern) {
        if (!attachments?.length) return { strategy: 'default', prefix: '' };

        // 创建正则表达式
        let regex;
        try {
            regex = new RegExp(validationPattern);
        } catch (error) {
            return { strategy: 'default', prefix: '' };
        }

        // 单次遍历分析附件
        const validAttachments = [];
        for (const att of attachments) {
            if (regex.test(att.name)) validAttachments.push(att);
        }

        const validCount = validAttachments.length;

        // 情况1:只有1个附件,或者全部不满足正则
        if (attachments.length === 1 || validCount === 0) {
            return { strategy: 'mailSubject', prefix: '' };
        }

        // 情况2:数量大于2张,且大于1张满足匹配
        if (attachments.length >= 2 && validCount > 1) {
            const commonPrefix = this.findCommonPrefix(validAttachments.map(att => att.name));
            if (commonPrefix?.length > 0) {
                return { strategy: 'commonPrefix', prefix: commonPrefix };
            }
        }

        // 情况3:只有1个满足匹配
        if (validCount === 1) {
            const extractedPrefix = this.extractNamingPattern(validAttachments[0].name);
            if (extractedPrefix) {
                return { strategy: 'extractedPattern', prefix: extractedPrefix };
            }
        }

        // 默认策略
        return { strategy: 'mailSubject', prefix: '' };
    }

    // 优化的公共前缀查找算法
    findCommonPrefix(fileNames) {
        if (!fileNames?.length || fileNames.length < 2) return '';

        const separators = new Set(['+', '-', '_', ' ', '.', '(', ')', '[', ']']);
        let prefix = fileNames[0];

        // 逐个比较,提前退出优化
        for (let i = 1; i < fileNames.length && prefix.length > 0; i++) {
            const current = fileNames[i];
            const minLen = Math.min(prefix.length, current.length);
            let j = 0;

            while (j < minLen && prefix[j] === current[j]) j++;
            prefix = prefix.substring(0, j);
        }

        // 找到最后一个分隔符位置
        const lastSepIndex = [...prefix].findLastIndex(char => separators.has(char));
        return lastSepIndex > 0 ? prefix.substring(0, lastSepIndex + 1) : prefix;
    }

    // 从单个文件名中提取命名模式
    extractNamingPattern(fileName) {
        // 匹配模式:{任意?}{分隔符?}{数字}{分隔符?}{数字?}{分隔符?}{任意?}
        // 例如:作者+123456+123456789+作品1.jpg -> 作者+123456+123456789+

        const patterns = [
            // 模式1: 前缀+数字+数字+后缀 (如: 作者+123456+123456789+作品1.jpg)
            /^(.+?[+\-_\s])(\d+)([+\-_\s])(\d+)([+\-_\s]).*/,
            // 模式2: 前缀+数字+后缀 (如: 作者+123456+作品1.jpg)
            /^(.+?[+\-_\s])(\d+)([+\-_\s]).*/,
            // 模式3: 前缀+数字 (如: 作者123456作品1.jpg)
            /^(.+?)(\d{6,}).*/
        ];

        for (const pattern of patterns) {
            const match = fileName.match(pattern);
            if (match) {
                if (pattern === patterns[0]) {
                    // 包含两个数字的情况:返回到第二个数字后的分隔符
                    return match[1] + match[2] + match[3] + match[4] + match[5];
                } else if (pattern === patterns[1]) {
                    // 包含一个数字的情况:返回到数字后的分隔符
                    return match[1] + match[2] + match[3];
                } else {
                    // 简单数字匹配:尝试找到数字前的合理分割点
                    const beforeNumber = match[1];
                    const number = match[2];
                    // 寻找最后一个可能的分隔符
                    const separators = ['+', '-', '_', ' '];
                    for (let i = beforeNumber.length - 1; i >= 0; i--) {
                        if (separators.includes(beforeNumber[i])) {
                            return beforeNumber.substring(0, i + 1) + number;
                        }
                    }
                    return beforeNumber + number;
                }
            }
        }

        return '';
    }

    // 生成文件名(支持智能auto模式)
    generateFileName(attachment, fileNamingConfig, allAttachments = null, namingStrategy = null) {
        // 如果没有配置,直接返回清理后的原文件名
        if (!fileNamingConfig) {
            return this.processFileName(attachment.name, 'sanitize', attachment);
        }

        let fileName = attachment.name;
        let needsFallback = false;

        // 检查是否启用了验证功能
        if (fileNamingConfig.validation && fileNamingConfig.validation.enabled && fileNamingConfig.validation.pattern) {
            try {
                const regex = new RegExp(fileNamingConfig.validation.pattern);
                const isValid = regex.test(attachment.name);
                needsFallback = !isValid;
            } catch (error) {
                // 静默忽略正则表达式错误
                needsFallback = false;
            }
        }

        // 按照指定顺序构建文件名:1. 命名策略 2. 备用命名前缀 3. 前缀、后缀
        const parts = [];
        const ext = this.processFileName(attachment.name, 'getExtension');
        let baseFileName = this.processFileName(attachment.name, 'removeExtension');

        // 1. 命名策略(自定义模式或原始文件名)
        if (fileNamingConfig.useCustomPattern && fileNamingConfig.customPattern) {
            // 使用自定义命名模板
            baseFileName = this.parseNamingPattern(fileNamingConfig.customPattern, attachment);
        } else if (needsFallback && fileNamingConfig.validation.fallbackPattern) {
            // 需要使用备用命名策略
            if (fileNamingConfig.validation.fallbackPattern === 'auto') {
                // 使用智能auto模式
                return this.generateAutoFileName(attachment, fileNamingConfig, allAttachments, namingStrategy);
            } else if (fileNamingConfig.validation.fallbackPattern === 'customTemplate' && fileNamingConfig.validation.fallbackTemplate) {
                // 使用自定义备用模板
                baseFileName = this.parseNamingPattern(fileNamingConfig.validation.fallbackTemplate, attachment);
            } else {
                // 使用其他备用策略
                const fallbackPrefix = this.generateFallbackPrefix(fileNamingConfig.validation.fallbackPattern, attachment);
                if (fallbackPrefix) {
                    baseFileName = fallbackPrefix + '_' + baseFileName;
                }
            }
        }
        // 如果不使用自定义模式且不需要备用策略,则使用原始文件名(已在上面设置)

        // 2. 备用命名前缀(如果启用了验证且需要备用策略,但不是auto或customTemplate)
        if (needsFallback && fileNamingConfig.validation.fallbackPattern && 
            fileNamingConfig.validation.fallbackPattern !== 'auto' && 
            fileNamingConfig.validation.fallbackPattern !== 'customTemplate') {
            const fallbackPrefix = this.generateFallbackPrefix(fileNamingConfig.validation.fallbackPattern, attachment);
            if (fallbackPrefix && !baseFileName.startsWith(fallbackPrefix)) {
                parts.push(fallbackPrefix);
            }
        }

        // 3. 前缀、后缀
        if (fileNamingConfig.prefix) {
            parts.push(fileNamingConfig.prefix);
        }

        // 添加基础文件名
        if (baseFileName) {
            parts.push(baseFileName);
        }

        // 后缀
        if (fileNamingConfig.suffix) {
            parts.push(fileNamingConfig.suffix);
        }

        // 合并文件名
        if (parts.length > 0) {
            const separator = fileNamingConfig.separator || '_';
            const finalBaseName = parts.join(separator);
            fileName = ext ? `${finalBaseName}.${ext}` : finalBaseName;
        } else {
            // 如果没有任何部分,使用原始文件名
            fileName = attachment.name;
        }

        return this.processFileName(fileName, 'sanitize', attachment);
    }

    // 生成智能auto模式文件名
    generateAutoFileName(attachment, fileNamingConfig, allAttachments, namingStrategy) {
        if (!namingStrategy) {
            // 如果没有提供策略,需要分析所有附件
            if (!allAttachments) {
                // 按照新的命名顺序构建默认文件名
                const parts = [];
                const ext = this.processFileName(attachment.name, 'getExtension');
                
                // 备用命名前缀(邮件主题)
                if (attachment.mailSubject) {
                    parts.push(attachment.mailSubject);
                }
                
                // 前缀
                if (fileNamingConfig.prefix) {
                    parts.push(fileNamingConfig.prefix);
                }
                
                // 基础文件名
                const baseFileName = this.processFileName(attachment.name, 'removeExtension');
                if (baseFileName) {
                    parts.push(baseFileName);
                }
                
                // 后缀
                if (fileNamingConfig.suffix) {
                    parts.push(fileNamingConfig.suffix);
                }
                
                const separator = fileNamingConfig.separator || '_';
                const finalBaseName = parts.join(separator);
                const finalName = ext ? `${finalBaseName}.${ext}` : finalBaseName;
                
                return this.processFileName(finalName, 'sanitize', attachment);
            }
            namingStrategy = this.analyzeAttachmentNaming(allAttachments, fileNamingConfig.validation.pattern);
        }

        // 确保附件对象有预计算的字段(兼容性检查)
        if (!attachment.nameWithoutExt) {
            attachment.nameWithoutExt = this.processFileName(attachment.name, 'removeExtension');
        }
        if (!attachment.ext) {
            attachment.ext = this.processFileName(attachment.name, 'getExtension');
        }

        // 按照新的命名顺序构建文件名
        const parts = [];
        let baseFileName = '';

        // 1. 命名策略(智能分析结果)
        switch (namingStrategy.strategy) {
            case 'mailSubject':
                // 使用邮件主题+文件名
                baseFileName = `${attachment.mailSubject}_${attachment.nameWithoutExt}`;
                break;

            case 'commonPrefix':
                // 使用公共前缀+原文件名去掉公共部分
                const remainingName = attachment.name.startsWith(namingStrategy.prefix)
                    ? attachment.name.substring(namingStrategy.prefix.length)
                    : attachment.name;
                const remainingWithoutExt = this.processFileName(remainingName, 'removeExtension');
                baseFileName = `${namingStrategy.prefix}${remainingWithoutExt}`;
                break;

            case 'extractedPattern':
                // 使用提取的模式+原文件名的后缀部分
                if (attachment.nameWithoutExt.startsWith(namingStrategy.prefix)) {
                    const suffix = attachment.nameWithoutExt.substring(namingStrategy.prefix.length);
                    baseFileName = `${namingStrategy.prefix}${suffix}`;
                } else {
                    baseFileName = `${namingStrategy.prefix}${attachment.nameWithoutExt}`;
                }
                break;

            default:
                // 默认使用邮件主题+文件名
                baseFileName = `${attachment.mailSubject}_${attachment.nameWithoutExt}`;
                break;
        }

        // 2. 备用命名前缀(已在策略中处理)
        // 3. 前缀、后缀
        if (fileNamingConfig.prefix) {
            parts.push(fileNamingConfig.prefix);
        }

        // 添加基础文件名
        if (baseFileName) {
            parts.push(baseFileName);
        }

        // 后缀
        if (fileNamingConfig.suffix) {
            parts.push(fileNamingConfig.suffix);
        }

        // 合并文件名
        const separator = fileNamingConfig.separator || '_';
        const finalBaseName = parts.length > 0 ? parts.join(separator) : baseFileName;
        const finalName = attachment.ext ? `${finalBaseName}.${attachment.ext}` : finalBaseName;
        const sanitizedName = this.processFileName(finalName, 'sanitize', attachment);
        
        // 验证文件名是否符合正则表达式
        if (!this.validateFileName(sanitizedName)) {
            console.log(`文件名 "${sanitizedName}" 不符合验证规则,使用备用策略`);
            return this.generateFallbackFileName(sanitizedName, attachment);
        }
        
        return sanitizedName;
    }

    // 文件工具方法集合
    // 统一文件名处理工具
    processFileName(fileName, operation, attachment = null) {
        switch (operation) {
            case 'removeExtension':
                const lastDotIndex = fileName.lastIndexOf('.');
                return lastDotIndex > 0 ? fileName.substring(0, lastDotIndex) : fileName;
            case 'getExtension':
                const match = fileName.match(/\.([^.]+)$/);
                return match ? match[1].toLowerCase() : null;
            case 'sanitize':
                return this.sanitizeFileName(fileName, attachment);
            default:
                return fileName;
        }
    }

    // 文件名规范检查和清理方法
    sanitizeFileName(fileName, attachment = null) {
        const validation = this.downloadSettings.fileNaming.validation;
        
        // 如果未启用验证,使用基本清理
        if (!validation?.enabled) {
            return fileName.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_').replace(/\s+/g, ' ').trim();
        }

        let cleanName = fileName;
        
        // 应用内容替换规则
        cleanName = this.applyContentReplacement(cleanName, attachment);

        const replacementChar = validation.replacementChar || '_';
        const removeInvalidChars = validation.removeInvalidChars !== false;

        // 移除系统禁用字符
        if (removeInvalidChars) {
            cleanName = cleanName.replace(/[<>:"/\\|?*\x00-\x1f]/g, replacementChar);
        }

        // 清理多余的空格和替换字符
        cleanName = cleanName.replace(/\s+/g, ' ').trim();
        
        // 移除连续的替换字符
        if (replacementChar && replacementChar !== ' ') {
            const escapedChar = replacementChar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            const duplicatePattern = new RegExp(`${escapedChar}{2,}`, 'g');
            cleanName = cleanName.replace(duplicatePattern, replacementChar);
        }

        // 清理首尾的特殊字符
        cleanName = cleanName.replace(/^[._\-\s]+|[._\-\s]+$/g, '');

        // 确保文件名不为空
        if (!cleanName || cleanName.trim() === '') {
            cleanName = 'unnamed_file';
        }

        return cleanName;
    }

    // 应用内容替换规则
    applyContentReplacement(fileName, attachment = null) {
        const validation = this.downloadSettings.fileNaming.validation;
        const contentReplacement = validation?.contentReplacement;
        
        // 如果未启用内容替换或没有搜索内容,直接返回原文件名
        if (!contentReplacement?.enabled || !contentReplacement.search) {
            return fileName;
        }
        
        try {
            let result = fileName;
            let searchPattern = contentReplacement.search;
            let replaceContent = contentReplacement.replace ?? '';
            
            // 如果有附件信息,替换变量
            if (attachment && replaceContent.includes('{')) {
                replaceContent = this.parseNamingPattern(replaceContent, attachment);
            }
            
            if (contentReplacement.mode === 'regex') {
                // 正则表达式模式
                const flags = contentReplacement.global ? 'g' : '';
                const caseFlags = contentReplacement.caseSensitive ? '' : 'i';
                const regex = new RegExp(searchPattern, flags + caseFlags);
                result = result.replace(regex, replaceContent);
            } else {
                // 字符串模式
                if (contentReplacement.global) {
                    // 全局替换
                    if (contentReplacement.caseSensitive) {
                        // 区分大小写
                        while (result.includes(searchPattern)) {
                            result = result.replace(searchPattern, replaceContent);
                        }
                    } else {
                        // 不区分大小写
                        const regex = new RegExp(searchPattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
                        result = result.replace(regex, replaceContent);
                    }
                } else {
                    // 只替换第一个匹配项
                    if (contentReplacement.caseSensitive) {
                        const index = result.indexOf(searchPattern);
                        if (index !== -1) {
                            result = result.substring(0, index) + replaceContent + result.substring(index + searchPattern.length);
                        }
                    } else {
                        const lowerResult = result.toLowerCase();
                        const lowerSearch = searchPattern.toLowerCase();
                        const index = lowerResult.indexOf(lowerSearch);
                        if (index !== -1) {
                            result = result.substring(0, index) + replaceContent + result.substring(index + searchPattern.length);
                        }
                    }
                }
            }
            
            return result;
        } catch (error) {
            console.warn('内容替换失败:', error);
            return fileName; // 如果替换失败,返回原文件名
        }
    }

    // 验证文件名是否符合正则表达式
    validateFileName(fileName) {
        const validation = this.downloadSettings.fileNaming.validation;
        
        if (!validation?.enabled || !validation.pattern) {
            return true;
        }

        try {
            const regex = new RegExp(validation.pattern);
            const nameWithoutExt = this.processFileName(fileName, 'removeExtension');
            return regex.test(nameWithoutExt);
        } catch (error) {
            console.warn('正则表达式验证失败:', error);
            return true; // 如果正则表达式有问题,默认通过验证
        }
    }

    // 根据备用策略生成文件名
    generateFallbackFileName(originalFileName, attachment) {
        const validation = this.downloadSettings.fileNaming.validation;
        const fallbackPattern = validation?.fallbackPattern || 'auto';
        const ext = this.processFileName(originalFileName, 'getExtension');
        
        let newName;
        
        switch (fallbackPattern) {
            case 'mailSubject':
                newName = attachment.mailSubject || attachment.subject || 'untitled';
                break;
            case 'senderEmail':
                newName = attachment.sender || 'unknown_sender';
                break;
            case 'toTime':
                newName = attachment.totime ? this.formatDate(new Date(typeof attachment.totime === 'number' && attachment.totime < 10000000000 ? attachment.totime * 1000 : attachment.totime), 'YYYYMMDDHHmmss') : Date.now().toString();
                break;
            case 'customTemplate':
                // 使用自定义模板
                const template = validation?.fallbackTemplate || '{subject}_{fileName}';
                newName = this.parseNamingPattern(template, attachment);
                break;
            case 'auto':
            default:
                // 智能分析:尝试提取有意义的部分
                const nameWithoutExt = this.processFileName(originalFileName, 'removeExtension');
                const numbers = nameWithoutExt.match(/\d+/g);
                const letters = nameWithoutExt.match(/[a-zA-Z\u4e00-\u9fff]+/g);
                
                if (numbers && numbers.length > 0) {
                    newName = numbers.join('_');
                } else if (letters && letters.length > 0) {
                    newName = letters.slice(0, 3).join('_');
                } else {
                    newName = attachment.mailSubject || attachment.subject || `file_${Date.now()}`;
                }
                break;
        }
        
        // 清理生成的文件名
        newName = this.sanitizeFileName(newName, attachment);
        
        return ext ? `${newName}.${ext}` : newName;
    }

    // 统一文件夹选择方法
    async selectDownloadFolder() {
        const dirHandle = await window.showDirectoryPicker({ mode: 'readwrite', startIn: 'downloads' });
        const permissionStatus = await dirHandle.requestPermission({ mode: 'readwrite' });
        if (permissionStatus !== 'granted') {
            throw new Error('需要文件夹写入权限才能下载文件');
        }
        return dirHandle;
    }



    // 通用的变量替换函数,用于预览和实际文件名生成
    replaceVariablesInTemplate(template, variableData) {
        if (!template || !variableData) return template;
        let result = template;
        
        // 处理带格式的变量,如 {date:YYYY-MM}
        result = result.replace(/\{(\w+):([^}]+)\}/g, (match, varName, format) => {
            if (varName === 'date' && variableData.date) {
                // 处理日期格式化
                const mailDate = variableData.date instanceof Date ? variableData.date : 
                    new Date(typeof variableData.date === 'number' && variableData.date < 10000000000 ? 
                        variableData.date * 1000 : variableData.date);
                if (mailDate && !isNaN(mailDate.getTime())) {
                    return this.formatDate(mailDate, format);
                }
            }
            return match; // 如果无法处理,保持原样
        });
        
        // 处理普通变量,如 {senderName}
        result = result.replace(/\{(\w+)\}/g, (match, varName) => {
            const value = variableData[varName];
            return (value !== undefined && value !== null) ? String(value) : match;
        });
        
        return result;
    }

    // 优化的自定义变量处理(减少正则表达式创建)
    processCustomVariables(variables, attachment) {
        if (!variables?.length) return {};

        const result = {};
        const attachmentEntries = Object.entries(attachment);

        variables.forEach(variable => {
            if (variable.name && variable.value) {
                let value = variable.value;
                // 批量替换,避免重复创建正则表达式
                attachmentEntries.forEach(([key, val]) => {
                    if (value.includes(`{${key}}`)) {
                        value = value.replaceAll(`{${key}}`, val ?? '');
                    }
                });
                result[variable.name] = value;
            }
        });
        return result;
    }

    async getOrCreateFolder(parentHandle, folderName) {
        try {
            return await parentHandle.getDirectoryHandle(folderName, { create: true });
        } catch (error) {
            throw error;
        }
    }

    formatDate(dateOrTimestamp, format) {
        if (arguments.length === 1) {
            const date = this.processData(dateOrTimestamp, 'createSafeDate');
            if (!date) return '';
                return date.toLocaleDateString('zh-CN') + ' ' + date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
        }

        const pad = (num) => String(num).padStart(2, '0');
        let date = dateOrTimestamp instanceof Date ? dateOrTimestamp : this.processData(dateOrTimestamp, 'createSafeDate');

        if (!date || isNaN(date.getTime())) {
            return '';
        }

        if (!format || typeof format !== 'string') {
            return '';
        }

        const year = date.getFullYear();
        const month = pad(date.getMonth() + 1);
        const day = pad(date.getDate());
        const hour24 = pad(date.getHours());
        const hour12 = pad(date.getHours() % 12 || 12);
        const minute = pad(date.getMinutes());
        const second = pad(date.getSeconds());

        return format
            .replace('YYYY', year)
            .replace('MM', month)
            .replace('DD', day)
            .replace('HH', hour24)
            .replace('hh', hour12)
            .replace('mm', minute)
            .replace('ss', second);
    }


    getSmartGroupName(attachment) {
        const parts = [];
        const smartGroupingConfig = this.downloadSettings.smartGrouping;

        if (smartGroupingConfig.groupByType) {
            const fileType = this.getFileType(attachment.name);
            if (fileType) {
                parts.push(fileType.toUpperCase());
            }
        }

        if (smartGroupingConfig.groupByDate) {
            const timestamp = attachment.date || attachment.totime || attachment.mailDate;
            if (timestamp) {
                const date = this.normalizeTimestamp(timestamp);
                if (date && !isNaN(date.getTime())) {
                    parts.push(this.formatDate(date, 'YYYY-MM'));
                }
            }
        }

        return parts.length > 0 ? parts.join('_') : null;
    }

    static async _asyncRetry(asyncFn, argsArray, maxRetries, delayMs, retryIdentifier = 'Unnamed Task') {
        let attempts = 0;
        while (attempts <= maxRetries) { // Note: attempts <= maxRetries for initial + maxRetries
            try {
                if (attempts > 0) { // Delay only for actual retries
                    console.log(`[AsyncRetry] Retrying ${retryIdentifier}, attempt ${attempts} of ${maxRetries} after ${delayMs}ms delay...`);
                    await new Promise(resolve => setTimeout(resolve, delayMs));
                }
                return await asyncFn(...argsArray);
            } catch (error) {
                    console.warn(`[AsyncRetry] Attempt ${attempts + 1} for ${retryIdentifier} failed:`, error.message);
                attempts++; // Increment after logging the current attempt that failed
                if (attempts > maxRetries) {
                    console.error(`[AsyncRetry] All ${maxRetries + 1} attempts for ${retryIdentifier} (initial + ${maxRetries} retries) failed.`);
                    throw error; // Re-throw the last error
                }
            }
        }
    }

    updateMailCount(filteredAttachments) {
        const attachmentList = filteredAttachments || this.attachments;
        const { mailIds, totalSize } = attachmentList.reduce((acc, att) => {
            if (att.mailId) acc.mailIds.add(att.mailId);
            if (att.size) acc.totalSize += att.size;
            return acc;
        }, { mailIds: new Set(), totalSize: 0 });

        // 优先使用API返回的总邮件数,如果没有则使用计算的邮件数
        const mailCount = this.totalMailCount || mailIds.size;
        const totalAttachmentCount = this.attachments ? this.attachments.length : 0;
        const statsText = `${mailCount} 封邮件 · ${totalAttachmentCount} 个附件${totalSize > 0 ? ` · ${this.formatData(totalSize, 'size')}` : ''}`;

        // 更新标题栏中的统计信息
        const headerInfo = document.getElementById('attachment-count-info');
        if (headerInfo) {
            headerInfo.textContent = statsText;
        }

        // 兼容旧的元素ID
        ['folder-stats'].forEach(id => {
            const elem = document.getElementById(id);
            if (elem) elem.textContent = statsText;
        });

        const mailCountElem = document.getElementById('mail-count');
        if (mailCountElem) mailCountElem.textContent = `${mailCount} 封邮件`;
    }

    updateAttachmentList(attachments) {
        if (!this.attachmentList) return;

        this.attachmentList.innerHTML = '';

        // 更新统计信息
        this.updateMailCount(attachments);

        if (attachments.length === 0) {
            const emptyState = this.createUI('div', {
                content: `
                    <svg width="48" height="48" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-bottom: 16px;">
                        <path d="M14 2H6C4.89543 2 4 2.89543 4 4V20C4 21.1046 4.89543 22 6 22H18C19.1046 22 20 21.1046 20 20V8L14 2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                        <path d="M14 2V8H20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
                    </svg>
                    <div>暂无附件</div>
                `,
                styles: StyleManager.getStyles().states.empty
            });
            this.attachmentList.appendChild(emptyState);
            return;
        }

        attachments.forEach(attachment => {
            const card = this.createAttachmentCard(attachment);
            this.attachmentList.appendChild(card);
        });
    }

    toggleAttachmentManager() {
        // 防抖机制,避免快速重复点击
        if (this.toggleInProgress) {
            console.log('[切换面板] 操作正在进行中,忽略重复点击');
            return;
        }
        
        this.toggleInProgress = true;
        console.log('[切换面板] 开始切换附件管理面板,当前状态:', this.isViewActive ? '激活' : '未激活');
        
        try {
            if (this.isViewActive) {
                this.hideAttachmentView();
            } else {
                this.initializeAttachmentView();
            }
        } finally {
            // 延迟重置防抖标志,避免操作过快
            setTimeout(() => {
                this.toggleInProgress = false;
                console.log('[切换面板] 防抖标志已重置');
            }, 1000);
        }
    }

    async initializeAttachmentView() {
        if (this.isViewActive) {
            console.log('[初始化] 面板已激活,跳过重复初始化');
            return;
        }

        console.log('[初始化] 开始初始化附件视图');
        
        try {
            await this.prepareForInitialization();
            await this.setupUserInterface();
            await this.loadAttachmentData();
            await this.completeInitialization();
            console.log('[初始化] 附件视图初始化完成');
        } catch (error) {
            console.error('[初始化] 初始化失败:', error);
            this.handleInitializationError(error);
        }
    }

    // 准备初始化 - 检查前置条件和清理状态
    async prepareForInitialization() {
        console.log('[准备初始化] 开始准备初始化');
        
        // 检查前置条件
        const sid = this.downloader.sid;
        if (!sid) {
            throw new Error('SID为空,请确保已正确登录(不可用)');
        }

        const folderId = this.downloader.getCurrentFolderId();
        if (!folderId) {
            throw new Error('无法获取当前文件夹ID');
        }

        // 清理之前的状态
        this.cleanupPreviousState();

        // 设置基础状态
        this.isViewActive = true;
        this.currentFolderId = folderId;
        this.isLoading = true;
        this.attachments = [];
        this.selectedAttachments.clear();
        
        console.log('[准备初始化] 初始化准备完成,文件夹ID:', folderId);
    }

    // 界面初始化 - 创建UI和显示加载状态
    async setupUserInterface() {
        console.log('[界面初始化] 开始创建用户界面');
        
        // 创建覆盖面板
        this.createOverlayPanel();

        // 显示初始加载状态
        const container = document.querySelector('#attachment-content-area');
        if (container) {
            this.showState('loading', '正在初始化附件管理器...', container);
        }

        // 设置全局键盘监听
        this.addGlobalKeyListener();

        // 更新工具栏状态
        this.updateStatus('正在准备加载附件...');
        
        console.log('[界面初始化] 用户界面创建完成');
    }

    async loadAttachmentData() {
        const container = document.querySelector('#attachment-content-area');
        this.showState('loading', '正在获取邮件列表...', container);
        
        // 首先获取第一页,同时获取总邮件数
        const firstPageResult = await this.downloader.fetchMailList(this.currentFolderId, 0, 50);
        const totalMailCount = firstPageResult.total;
        const firstPageMails = firstPageResult.mails;
        
        if (!firstPageMails.length) {
            this.attachments = [];
            this.totalMailCount = 0;
            return;
        }

        // 保存总邮件数
        this.totalMailCount = totalMailCount;
        
        // 如果总邮件数大于50,需要获取剩余的邮件
        let allMails = [...firstPageMails];
        
        if (totalMailCount > 50) {
            this.showState('loading', `正在获取 ${totalMailCount} 封邮件...`, container);
            
            // 计算需要获取的页数
            const totalPages = Math.ceil(totalMailCount / 50);
            
            // 并行获取剩余页面
            const pagePromises = [];
            for (let page = 1; page < totalPages; page++) {
                pagePromises.push(this.downloader.fetchMailList(this.currentFolderId, page, 50));
            }
            
            try {
                const results = await Promise.all(pagePromises);
                results.forEach(result => {
                    if (result.mails && result.mails.length > 0) {
                        allMails.push(...result.mails);
                    }
                });
            } catch (error) {
                console.error('[获取邮件] 获取剩余页面失败:', error);
                this.showToast('部分邮件获取失败,将处理已获取的邮件', 'warning');
            }
        }

        this.showState('loading', `正在处理 ${allMails.length} 封邮件...`, container);
        this.attachments = [];
        await this.processMailsInBatches(allMails, this.currentFolderId);
    }

    async completeInitialization() {
        this.displayAttachments(this.attachments);
        this.updateMailCount(this.attachments);
        this.updateStatus(`共 ${this.totalMailCount} 封邮件,包含 ${this.attachments.length} 个附件。`);
        this.updateSmartDownloadButton();
        this.isLoading = false;
    }

    // 清理之前的状态
    cleanupPreviousState() {
        // 清理选中状态
        this.selectedAttachments.clear();

        // 清理引用
        this.smartDownloadButton = null;
        this.overlayPanel = null;

        // 清理定时器
        if (this.downloadProgressTimer) {
            clearInterval(this.downloadProgressTimer);
            this.downloadProgressTimer = null;
        }

        // 移除可能遗留的UI元素
        const existingOverlays = document.querySelectorAll('#attachment-manager-overlay');
        existingOverlays.forEach(overlay => overlay.remove());

        // 移除Toast和菜单
        const toasts = document.querySelectorAll('.attachment-toast, .attachment-menu, .attachment-dialog');
        toasts.forEach(toast => toast.remove());
    }

    // 处理初始化错误
    handleInitializationError(error) {
        console.error('[错误处理] 处理初始化错误:', error);
        
        // 重置状态
        this.isViewActive = false;
        this.isLoading = false;

        // 清理资源
        this.cleanupAttachmentManager(true);

        // 显示错误信息
        const errorMessage = '附件管理器初始化失败: ' + error.message;
        this.showToast(errorMessage, 'error', 5000);
        
        console.log('[错误处理] 错误处理完成');
    }

    hideAttachmentView() {
        console.log('[隐藏视图] 开始隐藏附件视图');
        
        try {
            // 设置状态
            this.isViewActive = false;
            this.isLoading = false;

            // 按顺序清理
            this.cleanupAttachmentManager();
            this.removeOverlayPanel();
            this.restorePageState();
            
            // 确保按钮仍然存在,如果不存在则重新创建
            setTimeout(() => {
                const existingButton = document.querySelector('#attachment-downloader-btn, [data-attachment-manager-btn="true"], .attachment-floating-btn');
                if (!existingButton) {
                    console.log('[隐藏视图] 按钮丢失,重新创建');
                    this.createAndInjectButton();
                }
            }, 500);
            
            console.log('[隐藏视图] 附件视图隐藏完成');
        } catch (error) {
            console.error('[隐藏视图] 隐藏过程中出现错误:', error);
            this.cleanupAttachmentManager(true);
            try {
                this.showToast('关闭附件视图时出现问题,已强制清理', 'warning', 3000);
            } catch (toastError) {
                console.error('[隐藏视图] 无法显示错误提示:', toastError);
            }
        }
    }



    // 文件夹变化时的重新初始化
    async reinitializeForFolderChange() {
        console.log('[重新初始化] 开始文件夹变化重新初始化');
        
        try {
            // 更新当前文件夹ID
            this.currentFolderId = this.downloader.getCurrentFolderId();
            this.isLoading = true;

            // 清理当前数据
            this.attachments = [];
            this.selectedAttachments.clear();
            this.totalMailCount = 0;

            // 更新工具栏标题
            const titleElement = document.querySelector('#attachment-manager-overlay h1');
            if (titleElement) {
                titleElement.textContent = this.getFolderDisplayName();
            }

            // 重新加载数据
            await this.loadAttachmentData();
            await this.completeInitialization();
            
            console.log('[重新初始化] 文件夹变化重新初始化完成');
        } catch (error) {
            console.error('[重新初始化] 重新初始化失败:', error);
            this.showToast('切换文件夹失败: ' + error.message, 'error');

            // 显示错误状态
            const container = document.querySelector('#attachment-content-area');
            if (container) {
                this.showState('error', '切换文件夹失败: ' + error.message, container);
            }
        } finally {
            this.isLoading = false;
        }
    }

    // 统一清理方法
    cleanupAttachmentManager(force = false) {
        try {
            // 清理选中状态和引用
            this.selectedAttachments.clear();
            this.smartDownloadButton = null;
            this.overlayPanel = null;

            // 清理定时器
            if (this.downloadProgressTimer) {
                clearInterval(this.downloadProgressTimer);
                this.downloadProgressTimer = null;
            }

            // 移除全局键盘事件监听器
            this.removeGlobalKeyListener();

            // 断开按钮重复检查观察器
            if (this.buttonObserver) {
                this.buttonObserver.disconnect();
                this.buttonObserver = null;
            }

            // 移除响应式监听器
            if (this.resizeListener) {
                window.removeEventListener('resize', this.resizeListener);
                this.resizeListener = null;
            }

            // 清理浮动按钮样式
            const floatingBtnStyles = document.getElementById('attachment-floating-btn-styles');
            if (floatingBtnStyles) {
                floatingBtnStyles.remove();
            }

            // 清理DOM元素
            const elementsToRemove = force ? [
                '#attachment-manager-overlay',
                '.attachment-manager-panel',
                '.attachment-toast',
                '.attachment-menu',
                '.attachment-dialog',
                '[data-attachment-manager]'
            ] : [
                '.attachment-toast',
                '.attachment-menu',
                '.attachment-dialog',
                '[data-attachment-manager]'
            ];

            elementsToRemove.forEach(selector => {
                const elements = document.querySelectorAll(selector);
                elements.forEach(element => {
                    // 保护按钮不被删除
                    if (element.hasAttribute('data-attachment-manager-btn')) {
                        return;
                    }
                    
                    if (selector === '[data-attachment-manager]') {
                        element.removeAttribute('data-attachment-manager');
                    } else {
                        element.remove();
                    }
                });
            });

            // 清理样式和进度
            const customStyles = document.querySelectorAll('style[data-attachment-manager], #attachment-layout-style');
            customStyles.forEach(style => style.remove());
            this.hideProgress();

            // 重置内部状态
            this.isLoading = false;
            this.isViewActive = false;

            // 恢复页面状态
            if (force) {
                this.restorePageState();
            }

        } catch (error) {
            // 静默忽略清理错误
        }
    }

    createWindowHeader() {
        const header = this.createUI('div', {
            className: 'attachment-window-header'
        });

        // 窗口标题
        const title = this.createUI('div', {
            styles: 'display: flex; align-items: center; gap: 12px;',
            content: `
                <div style="font-size: 16px; font-weight: 600; color: var(--base_gray_100, #13181D);">
                ${this.getFolderDisplayName()}
                </div>
            `
        });

        // 窗口控制按钮
        const controls = this.createUI('div', {
            className: 'attachment-window-controls'
        });

        // 最小化按钮
        const minimizeBtn = this.createUI('div', {
            className: 'attachment-window-button minimize',
            title: '最小化',
            events: {
                click: (e) => {
                    e.stopPropagation();
                    this.toggleMinimize();
                }
            }
        });

        // 最大化按钮
        const maximizeBtn = this.createUI('div', {
            className: 'attachment-window-button maximize',
            title: '最大化/还原',
            events: {
                click: (e) => {
                    e.stopPropagation();
                    this.toggleMaximize();
                }
            }
        });

        // 关闭按钮
        const closeBtn = this.createUI('div', {
            className: 'attachment-window-button close',
            title: '关闭',
            events: {
                click: (e) => {
                    e.stopPropagation();
                    this.hideAttachmentView();
                }
            }
        });

        controls.appendChild(minimizeBtn);
        controls.appendChild(maximizeBtn);
        controls.appendChild(closeBtn);

        header.appendChild(title);
        header.appendChild(controls);

        return header;
    }

    createOverlayPanel() {
        this.removeOverlayPanel();

        // 添加布局样式
        StyleManager.addLayoutStyles();

        // 创建背景遮罩
        this.overlayBackground = this.createUI('div', {
            className: 'attachment-floating-overlay',
            events: { 
                click: (e) => {
                    if (e.target === this.overlayBackground) {
                        this.hideAttachmentView();
                    }
                }
            }
        });

        // 创建浮动窗口
        this.overlayPanel = this.createUI('div', {
            className: 'attachment-floating-window',
            events: { keydown: (e) => e.key === 'Escape' && (e.preventDefault(), this.hideAttachmentView()) }
        });
        this.overlayPanel.id = AttachmentManager.SELECTORS.OVERLAY_PANEL.slice(1);
        this.overlayPanel.tabIndex = -1;

        // 创建窗口标题栏
        const header = this.createWindowHeader();
        
        // 创建窗口内容
        const content = this.createUI('div', {
            className: 'attachment-window-content',
            content: this.createNativeAttachmentView()
        });

        this.overlayPanel.appendChild(header);
        this.overlayPanel.appendChild(content);
        this.overlayBackground.appendChild(this.overlayPanel);
        document.body.appendChild(this.overlayBackground);

        // 添加拖拽功能
        this.addWindowDragFunctionality(header);

        // 显示动画
        requestAnimationFrame(() => {
            this.overlayBackground.classList.add('show');
            this.overlayPanel.classList.add('show');
            this.overlayPanel.focus();
        });
    }

    addWindowDragFunctionality(header) {
        let isDragging = false;
        let startX, startY, startLeft, startTop;

        header.addEventListener('mousedown', (e) => {
            if (e.target.classList.contains('attachment-window-button')) return;
            if (this.overlayPanel.classList.contains('maximized')) return;

            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            
            const rect = this.overlayPanel.getBoundingClientRect();
            startLeft = rect.left;
            startTop = rect.top;

            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
            
            header.style.cursor = 'grabbing';
            e.preventDefault();
        });

        const handleMouseMove = (e) => {
            if (!isDragging) return;

            const deltaX = e.clientX - startX;
            const deltaY = e.clientY - startY;
            
            const newLeft = startLeft + deltaX;
            const newTop = startTop + deltaY;

            // 限制窗口不能拖出屏幕
            const maxLeft = window.innerWidth - this.overlayPanel.offsetWidth;
            const maxTop = window.innerHeight - this.overlayPanel.offsetHeight;
            
            const constrainedLeft = Math.max(0, Math.min(newLeft, maxLeft));
            const constrainedTop = Math.max(0, Math.min(newTop, maxTop));

            this.overlayPanel.style.left = constrainedLeft + 'px';
            this.overlayPanel.style.top = constrainedTop + 'px';
            this.overlayPanel.style.transform = 'none';
        };

        const handleMouseUp = () => {
            isDragging = false;
            header.style.cursor = 'move';
            document.removeEventListener('mousemove', handleMouseMove);
            document.removeEventListener('mouseup', handleMouseUp);
        };

        // 双击标题栏最大化/还原
        header.addEventListener('dblclick', (e) => {
            if (e.target.classList.contains('attachment-window-button')) return;
            this.toggleMaximize();
        });
    }

    toggleMinimize() {
        this.overlayPanel.classList.toggle('minimized');
        this.isMinimized = this.overlayPanel.classList.contains('minimized');
    }

    toggleMaximize() {
        this.overlayPanel.classList.toggle('maximized');
        this.isMaximized = this.overlayPanel.classList.contains('maximized');
        
        if (this.isMaximized) {
            // 保存当前位置和大小
            this.windowState = {
                left: this.overlayPanel.style.left,
                top: this.overlayPanel.style.top,
                width: this.overlayPanel.style.width,
                height: this.overlayPanel.style.height,
                transform: this.overlayPanel.style.transform
            };
        } else {
            // 恢复之前的位置和大小
            if (this.windowState) {
                this.overlayPanel.style.left = this.windowState.left;
                this.overlayPanel.style.top = this.windowState.top;
                this.overlayPanel.style.width = this.windowState.width;
                this.overlayPanel.style.height = this.windowState.height;
                this.overlayPanel.style.transform = this.windowState.transform;
            } else {
                // 如果没有保存的状态,回到中心位置
                this.overlayPanel.style.left = '';
                this.overlayPanel.style.top = '';
                this.overlayPanel.style.width = '';
                this.overlayPanel.style.height = '';
                this.overlayPanel.style.transform = 'translate(-50%, -50%)';
            }
        }
    }

    removeOverlayPanel() {
        // 移除浮动窗口
        if (this.overlayBackground) {
            // 添加关闭动画
            this.overlayBackground.classList.remove('show');
            if (this.overlayPanel) {
                this.overlayPanel.classList.remove('show');
            }

            // 延迟移除元素,等待动画完成
            setTimeout(() => {
                if (this.overlayBackground && this.overlayBackground.parentNode) {
                    this.overlayBackground.remove();
                }
                this.overlayBackground = null;
                this.overlayPanel = null;
            }, 300);
        }

        // 移除可能遗留的浮动窗口
        const existingOverlays = document.querySelectorAll('.attachment-floating-overlay');
        existingOverlays.forEach(overlay => overlay.remove());

        // 清理引用
        this.smartDownloadButton = null;
        this.windowState = null;
        this.isMinimized = false;
        this.isMaximized = false;

        // 移除可能的高z-index元素
        const highZIndexElements = document.querySelectorAll('[style*="z-index: 10000"], [style*="z-index: 99999"]');
        highZIndexElements.forEach(element => {
            // 保护按钮不被删除
            if (element.hasAttribute('data-attachment-manager-btn') || 
                element.classList.contains('attachment-floating-btn')) {
                return;
            }
            
            if (element.classList.contains('attachment-floating-overlay') ||
                element.classList.contains('attachment-floating-window') ||
                element.classList.contains('attachment-toast') ||
                element.classList.contains('attachment-menu') ||
                element.classList.contains('attachment-dialog')) {
                element.remove();
            }
        });
    }



    restorePageState() {
        try {
            // 恢复页面滚动
            document.documentElement.style.overflow = '';
            document.body.style.overflow = '';
            document.documentElement.style.position = '';
            document.body.style.position = '';

            // 移除可能的遮罩层
            const overlays = document.querySelectorAll('[style*="position: fixed"][style*="z-index"]');
            overlays.forEach(overlay => {
                if (overlay.id === 'attachment-manager-overlay' ||
                    overlay.classList.contains('attachment-manager-panel') ||
                    overlay.classList.contains('attachment-overlay')) {
                    overlay.remove();
                }
            });

            // 确保原生页面元素可见和可交互
            const mainApp = document.querySelector('#mailMainApp, .mail-main-app, .main-content');
            if (mainApp) {
                mainApp.style.display = '';
                mainApp.style.visibility = '';
                mainApp.style.pointerEvents = '';
            }

            // 恢复工具栏状态
            const toolbar = document.querySelector('.xmail-ui-ellipsis-toolbar');
            if (toolbar) {
                toolbar.style.display = '';
                toolbar.style.visibility = '';
                toolbar.style.pointerEvents = '';
            }

            // 重新启用页面交互
            document.body.style.pointerEvents = '';

            // 触发窗口resize事件,确保页面布局正确
            setTimeout(() => {
                window.dispatchEvent(new Event('resize'));
            }, 100);

        } catch (error) {
            // 静默忽略页面状态恢复错误
        }
    }

    addGlobalKeyListener() {
        // 移除可能存在的旧监听器
        this.removeGlobalKeyListener();

        // 创建新的键盘事件处理器
        this.globalKeyHandler = (e) => {
            // 只有在面板激活且不在加载状态时才响应ESC键
            if (this.isViewActive && !this.isLoading && e.key === 'Escape') {
                // 检查是否有其他弹窗或输入框处于焦点状态
                const activeElement = document.activeElement;
                const isInputFocused = activeElement && (
                    activeElement.tagName === 'INPUT' || 
                    activeElement.tagName === 'TEXTAREA' || 
                    activeElement.contentEditable === 'true'
                );
                
                // 检查是否有其他模态框打开
                const hasOtherModals = document.querySelector('.attachment-dialog, .variable-selector-overlay');
                
                if (!isInputFocused && !hasOtherModals) {
                    console.log('[键盘监听] ESC键被按下,关闭附件面板');
                    e.preventDefault();
                    e.stopPropagation();
                    this.hideAttachmentView();
                }
            }
        };

        // 添加全局键盘事件监听器
        document.addEventListener('keydown', this.globalKeyHandler, true);
        console.log('[键盘监听] 全局键盘监听器已添加');
    }

    removeGlobalKeyListener() {
        if (this.globalKeyHandler) {
            document.removeEventListener('keydown', this.globalKeyHandler, true);
            this.globalKeyHandler = null;
        }
    }

    updateSmartDownloadButton() {
        if (!this.smartDownloadButton) return;
        
        const selectedCount = this.selectedAttachments.size;
        const text = selectedCount > 0 ? `下载选中 (${selectedCount})` : '下载全部';
        
        if (this.smartDownloadButton.querySelector('svg')) {
            this.smartDownloadButton.lastChild.textContent = text;
        } else {
            this.smartDownloadButton.textContent = text;
        }
    }



        // 简化的原生附件视图,适配浮动窗口
    createNativeAttachmentView() {
        return `
            <div class="mail-list-page-items" style="border: none; box-shadow: none; height: 100%; overflow: hidden;">
                <div class="mail-list-page-items-inner" style="border: none; border-radius: 0; box-shadow: none; margin: 0; height: 100%; display: flex; flex-direction: column;">
                    <div class="xmail-ui-float-scroll" style="border: none; flex: 1; overflow: hidden;">
                        <div class="ui-float-scroll-body" tabindex="0" style="padding: 0; height: 100%; overflow-y: auto;">
                            <div id="attachment-content-area" style="padding: 20px; min-height: 100%;">
                                <!-- Bento Grid统计信息将在这里显示 -->
                            </div>
                        </div>
                    </div>
                    <div id="attachment-progress-area" style="display: none; border: none; margin: 0; position: absolute; bottom: 0; left: 0; right: 0; background: #fff; border-top: 1px solid var(--base_gray_010, #e9e9e9); z-index: 1001;"></div>
                </div>
            </div>
        `;
    }



    displayAttachments(attachments) {
        const container = document.querySelector('#attachment-manager-overlay #attachment-content-area') || document.querySelector('#attachment-content-area');
        if (!container) return;

        container.innerHTML = '';
        document.getElementById('attachment-state')?.remove();

        if (!attachments?.length) {
            container.innerHTML = `<div style="text-align: center; padding: 40px; color: #888;">当前文件夹暂无附件</div>`;
            this.updateMailCount([]);
            return;
        }

            this.displayBentoGrid(attachments, container);
            this.updateMailCount(attachments);
            this.updateSmartDownloadButton();
        }

            displayBentoGrid(attachments, container) {
        const bentoGrid = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoGrid
        });

        // 立即显示的基础统计
        const basicStats = this.calculateBasicStats(attachments);
        
        // 1. 标题栏
        const headerCard = this.createHeaderCard();
        bentoGrid.appendChild(headerCard);
        
        // 2. 主要功能行(下载卡片 + 快速操作)
        const mainFunctionRow = this.createMainFunctionRow();
        bentoGrid.appendChild(mainFunctionRow);
        
        // 添加分隔线
        const divider1 = this.createUI('div', {
            styles: 'height: 1px; background: linear-gradient(90deg, transparent, var(--base_gray_010, rgba(22, 46, 74, 0.1)), transparent); margin: 8px 0;'
        });
        bentoGrid.appendChild(divider1);
        
        // 3. 邮件统计卡片
        const mailStatsCard = this.createMailStatsCard(basicStats.totalMails);
        bentoGrid.appendChild(mailStatsCard);
        
        // 4. 无附件邮件详细列表
        const noAttachmentListCard = this.createNoAttachmentListCard();
        bentoGrid.appendChild(noAttachmentListCard);
        
        // 5. 文件类型和问题文件统计行
        const fileTypeRow = this.createFileTypeRow();
        bentoGrid.appendChild(fileTypeRow);
        
        // 6. 命名异常附件详细列表
        const invalidNamingListCard = this.createInvalidNamingListCard();
        bentoGrid.appendChild(invalidNamingListCard);
        
        // 7. 重复附件详细列表
        const duplicateAttachmentsListCard = this.createDuplicateAttachmentsListCard();
        bentoGrid.appendChild(duplicateAttachmentsListCard);
        
        container.appendChild(bentoGrid);
        
        // 更新文件夹结构预览
        this.updateFolderStructurePreview(attachments);

        // 延迟计算详细统计
        const cards = [
            headerCard,                     // 索引0 - 标题栏
            mainFunctionRow,                // 索引1 - 主要功能行
            mailStatsCard,                  // 索引2 - 邮件统计
            noAttachmentListCard,           // 索引3 - 无附件邮件列表
            fileTypeRow,                    // 索引4 - 文件类型和问题文件
            invalidNamingListCard,          // 索引5 - 命名异常列表
            duplicateAttachmentsListCard    // 索引6 - 重复附件列表
        ];
        this.calculateDetailedStatsAsync(attachments, cards);
    }

            // 基础统计 - 立即计算
    calculateBasicStats(attachments) {
        return {
            totalMails: new Set(attachments.map(a => a.mailId)).size,
            totalMailsInFolder: this.totalMailCount
        };
    }

    // 详细统计 - 异步计算
    async calculateDetailedStatsAsync(attachments, cards) {
        // 初始化详细数据
        this.detailData = {
            noAttachmentMails: [],
            invalidNamingAttachments: [],
            duplicateAttachments: []
        };
        
        // 分批计算,避免阻塞UI
        const batchSize = 50;
        const stats = {
            totalAttachments: attachments.length,
            senderCount: 0,
            noAttachmentMails: 0,
            duplicateMails: 0,
            duplicateAttachments: 0,
            totalSize: 0,
            largeAttachments: 0,
            imageCount: 0,
            archiveCount: 0,
            documentCount: 0,
            otherCount: 0,
            invalidNaming: 0
        };

        // 更新附件数量
        this.updateElementById('attachment-count', this.attachments ? this.attachments.length : attachments.length);

        // 分批处理附件
        for (let i = 0; i < attachments.length; i += batchSize) {
            const batch = attachments.slice(i, i + batchSize);
            await this.processBatch(batch, stats);
            
            // 让出主线程
            await new Promise(resolve => setTimeout(resolve, 1));
        }

        // 计算发件人数量
        const senders = new Set();
        const mailSubjects = new Map();
        const mailStats = await this.getAllMailsForStats();
        const allMails = mailStats.mails;

        for (const mail of allMails) {
            if (mail.senders?.item?.[0]?.email) {
                senders.add(mail.senders.item[0].email);
            }
            
            // 统计重复邮件
            const subject = mail.subject?.trim().toLowerCase();
            if (subject) {
                mailSubjects.set(subject, (mailSubjects.get(subject) ?? 0) + 1);
            }
        }

        stats.senderCount = senders.size;
        stats.totalMailsInFolder = mailStats.totalMails;
        
        // 保存无附件邮件详细数据
        this.detailData.noAttachmentMails = allMails.filter(mail => !mail.attach || mail.attach != 1);
        stats.noAttachmentMails = this.detailData.noAttachmentMails.length;
        stats.duplicateMails = Array.from(mailSubjects.values()).filter(count => count > 1).length;
        
        // 计算重复附件数量(基于文件名和大小)
        const attachmentMap = new Map();
        const attachmentGroups = new Map();
        
        attachments.forEach(attachment => {
            const key = `${attachment.name}_${attachment.size}`;
            attachmentMap.set(key, (attachmentMap.get(key) ?? 0) + 1);
            
            if (!attachmentGroups.has(key)) {
                attachmentGroups.set(key, []);
            }
            attachmentGroups.get(key).push(attachment);
        });
        
        // 收集重复文件详细信息
        this.detailData.duplicateAttachments = [];
        for (const [key, attachments] of attachmentGroups.entries()) {
            if (attachments.length > 1) {
                this.detailData.duplicateAttachments.push(...attachments.map(attachment => ({
                    ...attachment,
                    duplicateKey: key,
                    duplicateCount: attachments.length
                })));
            }
        }
        
        stats.duplicateAttachments = Array.from(attachmentMap.values()).filter(count => count > 1).length;

        // 更新主下载卡片
        this.updateElementById('attachment-count', this.attachments ? this.attachments.length : attachments.length);
        this.updateElementById('total-size', `总大小 ${this.formatFileSize(stats.totalSize)}`);
        
        // 更新统计概览卡片
        this.updateElementById('quick-mail-count', stats.totalMailsInFolder || this.totalMailCount || '计算中...');
        this.updateElementById('quick-sender-count', stats.senderCount || '计算中...');
        this.updateElementById('quick-no-attachment-mails', stats.noAttachmentMails || '计算中...');
        
        // 更新警告卡片
        this.updateElementById('no-attachment-mails', stats.noAttachmentMails);
        
        // 更新邮件统计卡片
        this.updateElementById('sender-count', stats.senderCount);
        
        // 更新文件类型统计
        this.updateElementById('image-files', stats.imageCount);
        this.updateElementById('archive-files', stats.archiveCount);
        this.updateElementById('document-files', stats.documentCount);
        this.updateElementById('other-files', stats.otherCount);
        
        // 更新问题文件统计
        this.updateElementById('invalid-naming', stats.invalidNaming);
        this.updateElementById('large-files', stats.largeAttachments);
        this.updateElementById('duplicate-attachments', stats.duplicateAttachments);
        this.updateElementById('duplicate-mails', stats.duplicateMails);
        
        // 更新详细列表
        this.updateNoAttachmentList();
        this.updateInvalidNamingList();
        this.updateDuplicateAttachmentsList();
    }

    async processBatch(batch, stats) {
        const archiveExts = ['zip', 'rar', '7z', 'tar', 'gz', 'bz2'];
        const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'ico'];
        const documentExts = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'rtf'];

        // 确保详细数据数组存在
        if (!this.detailData.invalidNamingAttachments) {
            this.detailData.invalidNamingAttachments = [];
        }

        batch.forEach(attachment => {
            // 计算文件大小
            const size = parseInt(attachment.size) || parseInt(attachment.filesize);
            stats.totalSize += size;

            // 超大附件 (>10MB)
            if (size > 10 * 1024 * 1024) {
                stats.largeAttachments++;
            }

            // 文件类型统计
            const ext = attachment.ext || this.processFileName(attachment.name, 'getExtension');
            const extLower = ext?.toLowerCase() || '';
            
            if (imageExts.includes(extLower)) {
                stats.imageCount++;
            } else if (archiveExts.includes(extLower)) {
                stats.archiveCount++;
            } else if (documentExts.includes(extLower)) {
                stats.documentCount++;
            } else {
                stats.otherCount++;
            }

            // 命名异常检查 - 使用设置中的验证规则
            const fileName = attachment.name;
            const validationResult = this.checkFileNameValidation(fileName);
            
            if (!validationResult.isValid) {
                stats.invalidNaming++;
                this.detailData.invalidNamingAttachments.push({
                    ...attachment,
                    invalidReason: validationResult.reason
                });
            }
        });
    }

    // 检查文件名是否符合验证规则
    checkFileNameValidation(fileName) {
        const validation = this.downloadSettings.fileNaming.validation;
        
        // 如果验证未开启,则认为所有文件名都是有效的
        if (!validation?.enabled) {
            return { isValid: true, reason: '' };
        }

        // 如果没有设置验证模式,使用默认的基本检查
        if (!validation.pattern) {
            // 基本的文件名检查
            if (!fileName || fileName.trim() === '') {
                return { isValid: false, reason: '文件名为空' };
            }
            if (!fileName.includes('.')) {
                return { isValid: false, reason: '没有文件扩展名' };
            }
            return { isValid: true, reason: '' };
        }

        try {
            const regex = new RegExp(validation.pattern);
            const nameWithoutExt = this.processFileName(fileName, 'removeExtension');
            
            if (!regex.test(nameWithoutExt)) {
                return { 
                    isValid: false, 
                    reason: `不符合验证规则: ${validation.pattern}` 
                };
            }
            
            return { isValid: true, reason: '' };
        } catch (error) {
            console.warn('正则表达式验证失败:', error);
            // 如果正则表达式有问题,使用基本检查
            if (!fileName.includes('.')) {
                return { isValid: false, reason: '没有文件扩展名' };
            }
            return { isValid: true, reason: '' };
        }
    }

    async getAllMailsForStats() {
        try {
            const folderId = this.downloader.getCurrentFolderId();
            // 获取更多邮件数据以提高统计准确性,最多获取前5页(250封邮件)
            const allMails = await this.downloader.getMailsWithPagination(folderId, 5);
            // 同时获取总数信息
            const firstPageResult = await this.downloader.fetchMailList(folderId, 0, 50);
            return {
                mails: allMails,
                totalMails: firstPageResult.total,
                unreadMails: firstPageResult.unread
            };
        } catch (error) {
            console.warn('获取邮件统计失败:', error);
            return { mails: [], totalMails: 0, unreadMails: 0 };
        }
    }

    // 更新无附件邮件列表
    updateNoAttachmentList() {
        const container = document.getElementById('no-attachment-list-container');
        const listCard = container?.closest('.bento-card');
        
        if (!container) return;
        
        const data = this.detailData.noAttachmentMails;
        
        if (data.length === 0) {
            container.innerHTML = `
                <div style="text-align: center; padding: 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                    暂无无附件邮件
                </div>
            `;
            if (listCard) listCard.style.display = 'none';
            return;
        }
        
        // 显示列表卡片
        if (listCard) listCard.style.display = 'block';
        
        // 只显示前5条,超过5条显示"查看更多"
        const displayData = data.slice(0, 5);
        const hasMore = data.length > 5;
        
        // 清空容器
        container.innerHTML = '';
        
        // 创建主容器
        const mainContainer = document.createElement('div');
        mainContainer.style.cssText = 'display: flex; flex-direction: column; gap: 8px;';
        
        // 添加邮件项目
        displayData.forEach((mail, index) => {
            const mailElement = this.renderCompactMailItem(mail, index);
            mainContainer.appendChild(mailElement);
        });
        
        // 添加"更多"提示
        if (hasMore) {
            const moreDiv = document.createElement('div');
            moreDiv.style.cssText = 'padding: 12px; text-align: center; border-top: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); margin-top: 8px;';
            moreDiv.innerHTML = `
                <span style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                    还有 ${data.length - 5} 封邮件未显示
                </span>
            `;
            mainContainer.appendChild(moreDiv);
        }
        
        container.appendChild(mainContainer);
    }

    // 更新命名异常附件列表
    updateInvalidNamingList() {
        const container = document.getElementById('invalid-naming-list-container');
        const listCard = container?.closest('.bento-card');
        
        if (!container) return;
        
        const data = this.detailData.invalidNamingAttachments;
        const validation = this.downloadSettings.fileNaming.validation;
        
        // 检查验证规则状态
        const validationStatus = this.getValidationStatus();
        
        if (data.length === 0) {
            container.innerHTML = `
                <div style="text-align: center; padding: 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                    <div style="margin-bottom: 8px;">暂无命名异常附件</div>
                    <div style="font-size: 11px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5));">
                        ${validationStatus}
                    </div>
                </div>
            `;
            if (listCard) listCard.style.display = 'none';
            return;
        }
        
        // 显示列表卡片
        if (listCard) listCard.style.display = 'block';
        
        // 只显示前5条,超过5条显示"查看更多"
        const displayData = data.slice(0, 5);
        const hasMore = data.length > 5;
        
        // 清空容器
        container.innerHTML = '';
        
        // 创建验证规则提示
        const validationInfo = document.createElement('div');
        validationInfo.style.cssText = 'margin-bottom: 12px; padding: 8px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); border-radius: 6px; border-left: 3px solid var(--theme_primary, #007bff);';
        validationInfo.innerHTML = `
            <div style="font-size: 12px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); margin-bottom: 2px;">当前验证规则</div>
            <div style="font-size: 11px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">${validationStatus}</div>
        `;
        
        // 创建主容器
        const mainContainer = document.createElement('div');
        mainContainer.style.cssText = 'display: flex; flex-direction: column; gap: 8px;';
        
        // 添加附件项目
        displayData.forEach((attachment, index) => {
            const attachmentElement = this.renderCompactAttachmentItem(attachment, index);
            mainContainer.appendChild(attachmentElement);
        });
        
        // 添加"更多"提示
        if (hasMore) {
            const moreDiv = document.createElement('div');
            moreDiv.style.cssText = 'padding: 12px; text-align: center; border-top: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); margin-top: 8px;';
            moreDiv.innerHTML = `
                <span style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                    还有 ${data.length - 5} 个异常附件未显示
                </span>
            `;
            mainContainer.appendChild(moreDiv);
        }
        
        container.appendChild(validationInfo);
        container.appendChild(mainContainer);
        
        // 验证设置按钮事件处理已在卡片创建时添加
    }

    // 获取验证规则状态描述
    getValidationStatus() {
        const validation = this.downloadSettings.fileNaming.validation;
        
        if (!validation?.enabled) {
            return '验证规则已关闭 - 仅检查基本文件名格式';
        }
        
        if (!validation.pattern) {
            return '验证规则已开启 - 使用默认基本检查';
        }
        
        return `验证规则: ${validation.pattern}`;
    }

    // 更新重复附件列表
    updateDuplicateAttachmentsList() {
        const container = document.getElementById('duplicate-attachments-list-container');
        const listCard = container?.closest('.bento-card');
        
        if (!container) return;
        
        const data = this.detailData.duplicateAttachments;
        
        if (data.length === 0) {
            container.innerHTML = `
                <div style="text-align: center; padding: 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                    暂无重复附件
                </div>
            `;
            if (listCard) listCard.style.display = 'none';
            return;
        }
        
        // 显示列表卡片
        if (listCard) listCard.style.display = 'block';
        
        // 按重复组分组显示
        const groupedData = this.groupDuplicateAttachments(data);
        const displayGroups = groupedData.slice(0, 3); // 只显示前3组
        const hasMore = groupedData.length > 3;
        
        // 清空容器
        container.innerHTML = '';
        
        // 创建主容器
        const mainContainer = document.createElement('div');
        mainContainer.style.cssText = 'display: flex; flex-direction: column; gap: 12px;';
        
        // 添加组元素
        displayGroups.forEach((group, index) => {
            const groupElement = this.renderDuplicateGroup(group, index);
            mainContainer.appendChild(groupElement);
        });
        
        // 添加"更多"提示
        if (hasMore) {
            const moreDiv = document.createElement('div');
            moreDiv.style.cssText = 'padding: 12px; text-align: center; border-top: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); margin-top: 8px;';
            moreDiv.innerHTML = `
                <span style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                    还有 ${groupedData.length - 3} 组重复文件未显示
                </span>
            `;
            mainContainer.appendChild(moreDiv);
        }
        
        container.appendChild(mainContainer);
    }

    // 将重复附件按组分组
    groupDuplicateAttachments(duplicateAttachments) {
        const groups = new Map();
        
        duplicateAttachments.forEach(attachment => {
            const key = attachment.duplicateKey;
            if (!groups.has(key)) {
                groups.set(key, []);
            }
            groups.get(key).push(attachment);
        });
        
        return Array.from(groups.values());
    }

    // 渲染重复文件组
    renderDuplicateGroup(group, groupIndex) {
        if (group.length === 0) return document.createElement('div');
        
        const fileName = group[0].name;
        const fileSize = this.formatFileSize(parseInt(group[0].size) || parseInt(group[0].filesize));
        const duplicateCount = group.length;
        
        // 找出最新的附件(按时间排序)
        const sortedGroup = [...group].sort((a, b) => {
            const dateA = this.processData(a.date || a.totime, 'createSafeDate');
            const dateB = this.processData(b.date || b.totime, 'createSafeDate');
            if (!dateA && !dateB) return 0;
            if (!dateA) return 1;
            if (!dateB) return -1;
            return dateB.getTime() - dateA.getTime();
        });
        
        // 标记最新的附件
        const latestAttachment = sortedGroup[0];
        const groupWithLatestFlag = group.map(attachment => ({
            ...attachment,
            isLatest: attachment === latestAttachment
        }));
        
        const groupDiv = document.createElement('div');
        groupDiv.style.cssText = 'padding: 12px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); border-radius: 6px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1));';
        
        // 创建头部信息
        const headerDiv = document.createElement('div');
        headerDiv.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;';
        headerDiv.innerHTML = `
            <div style="flex: 1; min-width: 0;">
                <div style="font-weight: 500; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px;">${fileName}</div>
                <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); display: flex; align-items: center; gap: 8px;">
                    <span style="background: var(--theme_warning_light, rgba(255, 193, 7, 0.1)); color: var(--theme_warning, #ffc107); padding: 1px 4px; border-radius: 3px; font-size: 11px;">重复 ${duplicateCount} 次</span>
                    <span>${fileSize}</span>
                </div>
            </div>
        `;
        
        // 创建附件列表容器
        const itemsContainer = document.createElement('div');
        itemsContainer.style.cssText = 'display: flex; flex-direction: column; gap: 4px; max-height: 120px; overflow-y: auto;';
        
        // 添加附件项目
        groupWithLatestFlag.forEach((attachment, index) => {
            const itemElement = this.renderCompactDuplicateAttachmentItem(attachment, index);
            itemsContainer.appendChild(itemElement);
        });
        
        groupDiv.appendChild(headerDiv);
        groupDiv.appendChild(itemsContainer);
        
        return groupDiv;
    }

    // 渲染紧凑的重复附件项目
    renderCompactDuplicateAttachmentItem(attachment, index) {
        const mailSubject = attachment.mailSubject;
        const mailId = attachment.mailId;
        const attachmentDate = this.processData(attachment.date || attachment.totime, 'createSafeDate');
        const dateStr = attachmentDate ? this.formatDate(attachmentDate, 'MM-DD HH:mm') : '未知时间';
        const isLatest = attachment.isLatest;
        const senderName = attachment.senderName || '未知发件人';
        const senderEmail = attachment.senderEmail || attachment.sender || '';
        
        const div = document.createElement('div');
        div.style.cssText = `padding: 8px; background: white; border-radius: 4px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: pointer; transition: all 0.2s;`;
        div.innerHTML = `
            <div style="display: flex; align-items: center; justify-content: space-between; gap: 8px;">
                <div style="flex: 1; min-width: 0;">
                    <div style="font-size: 12px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: flex; align-items: center; gap: 4px; margin-bottom: 2px;">
                        <span>来自邮件: ${mailSubject}</span>
                        ${isLatest ? '<span style="background: var(--theme_success, #28a745); color: white; padding: 1px 4px; border-radius: 3px; font-size: 10px; font-weight: 500;">最新</span>' : ''}
                    </div>
                    <div style="font-size: 10px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); margin-bottom: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                        发件人: ${senderName}${senderEmail ? ` (${senderEmail})` : ''}
                    </div>
                    <div style="font-size: 10px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                        时间: ${dateStr}
                    </div>
                </div>
                <div style="color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); font-size: 10px;">
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                        <polyline points="15 3 21 3 21 9"/>
                        <line x1="10" y1="14" x2="21" y2="3"/>
                    </svg>
                </div>
            </div>
        `;
        
        // 添加事件监听器
        div.addEventListener('click', () => this.openMail(mailId));
        div.addEventListener('mouseenter', (e) => e.target.style.background = 'var(--base_gray_005, rgba(22, 46, 74, 0.05))');
        div.addEventListener('mouseleave', (e) => e.target.style.background = 'white');
        
        return div;
    }

    // 渲染紧凑的邮件项目
    renderCompactMailItem(mail, index) {
        const subject = mail.subject;
        const senderData = mail.senders?.item?.[0];
        const senderName = senderData?.name || senderData?.nick || '未知发件人';
        const senderEmail = senderData?.email || '';
        // 修复时间字段:使用totime而不是date
        const timestamp = mail.totime || mail.date;
        const date = timestamp ? this.formatDate(this.processData(timestamp, 'createSafeDate'), 'MM-DD HH:mm') : '未知时间';
        // 修复邮件ID字段:使用emailid而不是id
        const mailId = mail.emailid || mail.id;
        
        const div = document.createElement('div');
        div.style.cssText = `padding: 12px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); border-radius: 6px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: pointer; transition: all 0.2s;`;
        div.innerHTML = `
            <div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
                <div style="flex: 1; min-width: 0;">
                    <div style="font-weight: 500; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); margin-bottom: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px;">${subject}</div>
                    <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">发件人: ${senderName}${senderEmail ? ` (${senderEmail})` : ''}</div>
                    <div style="font-size: 11px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">时间: ${date}</div>
                </div>
                <div style="color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); font-size: 12px;">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                        <polyline points="15 3 21 3 21 9"/>
                        <line x1="10" y1="14" x2="21" y2="3"/>
                    </svg>
                </div>
            </div>
        `;
        
        // 添加事件监听器
        div.addEventListener('click', () => this.openMail(mailId));
        div.addEventListener('mouseenter', (e) => e.target.style.background = 'var(--base_gray_010, rgba(22, 46, 74, 0.1))');
        div.addEventListener('mouseleave', (e) => e.target.style.background = 'var(--base_gray_005, rgba(22, 46, 74, 0.05))');
        
        return div;
    }

    // 渲染紧凑的附件项目
    renderCompactAttachmentItem(attachment, index) {
        const fileName = attachment.name;
        const fileSize = this.formatFileSize(parseInt(attachment.size) || parseInt(attachment.filesize));
        const invalidReason = attachment.invalidReason;
        const mailSubject = attachment.mailSubject;
        const mailId = attachment.mailId;
        const senderName = attachment.senderName || '未知发件人';
        const senderEmail = attachment.senderEmail || attachment.sender || '';
        const attachmentDate = this.processData(attachment.date || attachment.totime, 'createSafeDate');
        const dateStr = attachmentDate ? this.formatDate(attachmentDate, 'MM-DD HH:mm') : '未知时间';
        
        // 获取文件图标和缩略图
        const fileIcon = this.getFileIcon(fileName);
        const thumbnailUrl = this.getThumbnailUrl(attachment);
        const supportsThumbnail = this.supportsThumbnail(fileName);
        
        const div = document.createElement('div');
        div.style.cssText = `padding: 12px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); border-radius: 6px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: pointer; transition: all 0.2s;`;
        div.innerHTML = `
            <div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
                <!-- 文件图标/缩略图 -->
                <div class="file-preview-container" style="width: 36px; height: 36px; border-radius: 6px; background: var(--base_gray_010, rgba(22, 46, 74, 0.1)); display: flex; align-items: center; justify-content: center; flex-shrink: 0; overflow: hidden; position: relative;">
                    ${supportsThumbnail && thumbnailUrl ? 
                        `<img class="file-thumbnail" src="${thumbnailUrl}" alt="${fileName}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 5px; opacity: 0; transition: opacity 0.3s;" data-fallback-icon="${fileIcon}">
                         <span class="file-icon-fallback" style="font-size: 14px; display: none; position: absolute; width: 100%; height: 100%; align-items: center; justify-content: center;">${fileIcon}</span>` : 
                        `<span style="font-size: 14px;">${fileIcon}</span>`
                    }
                </div>
                
                <div style="flex: 1; min-width: 0;">
                    <div style="font-weight: 500; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); margin-bottom: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; display: flex; align-items: center; gap: 6px;">
                        <span>${fileName}</span>
                        ${invalidReason ? `<span style="background: var(--theme_danger_light, rgba(220, 53, 69, 0.1)); color: var(--theme_danger, #dc3545); padding: 1px 4px; border-radius: 3px; font-size: 10px;">${invalidReason}</span>` : ''}
                    </div>
                    <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">大小: ${fileSize} · 时间: ${dateStr}</div>
                    <div style="font-size: 11px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); margin-bottom: 1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">来自邮件: ${mailSubject}</div>
                    <div style="font-size: 11px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">发件人: ${senderName}${senderEmail ? ` (${senderEmail})` : ''}</div>
                </div>
                <div style="color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); font-size: 12px;">
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                        <polyline points="15 3 21 3 21 9"/>
                        <line x1="10" y1="14" x2="21" y2="3"/>
                    </svg>
                </div>
            </div>
        `;
        
        // 添加事件监听器
        div.addEventListener('click', () => this.openMail(mailId));
        div.addEventListener('mouseenter', (e) => e.target.style.background = 'var(--base_gray_010, rgba(22, 46, 74, 0.1))');
        div.addEventListener('mouseleave', (e) => e.target.style.background = 'var(--base_gray_005, rgba(22, 46, 74, 0.05))');
        
        // 为缩略图添加事件监听器
        const thumbnailImg = div.querySelector('.file-thumbnail');
        const fallbackIcon = div.querySelector('.file-icon-fallback');
        
        if (thumbnailImg && fallbackIcon) {
            // 图片加载成功
            thumbnailImg.addEventListener('load', () => {
                thumbnailImg.style.opacity = '1';
            });
            
            // 图片加载失败,显示fallback图标
            thumbnailImg.addEventListener('error', () => {
                thumbnailImg.style.display = 'none';
                fallbackIcon.style.display = 'flex';
            });
        }
        
        return div;
    }

    // 显示详细列表
    showDetailList(type) {
        let title = '';
        let data = [];
        let itemRenderer = null;
        
        switch (type) {
            case 'no-attachment':
            case 'no-attachment-mails':
                title = '无附件邮件完整列表';
                data = this.detailData?.noAttachmentMails || [];
                itemRenderer = this.renderMailItem.bind(this);
                break;
            case 'invalid-naming':
                title = '命名异常附件完整列表';
                data = this.detailData?.invalidNamingAttachments || [];
                itemRenderer = this.renderAttachmentItem.bind(this);
                break;
            case 'duplicate-attachments':
                title = '重复附件完整列表';
                // 将重复附件按组分组显示
                const duplicateAttachments = this.detailData?.duplicateAttachments || [];
                data = this.groupDuplicateAttachments(duplicateAttachments);
                itemRenderer = this.renderDuplicateGroupItem.bind(this);
                break;
            default:
                console.warn('未知的列表类型:', type);
                return;
        }
        
        if (data.length === 0) {
            this.showToast(`暂无${title.replace('完整列表', '')}数据`, 'info');
            return;
        }
        
        this.createDetailListDialog(title, data, itemRenderer);
    }

    // 为重复附件添加最新标记
    addLatestFlagToDuplicateAttachments(duplicateAttachments) {
        if (!duplicateAttachments || duplicateAttachments.length === 0) {
            return [];
        }

        // 按重复组分组
        const groups = new Map();
        duplicateAttachments.forEach(attachment => {
            const key = attachment.duplicateKey;
            if (!groups.has(key)) {
                groups.set(key, []);
            }
            groups.get(key).push(attachment);
        });

        const result = [];
        // 为每个组标记最新的附件
        for (const [key, group] of groups.entries()) {
            // 按时间排序找出最新的
            const sortedGroup = [...group].sort((a, b) => {
                const dateA = this.processData(a.date || a.totime, 'createSafeDate');
                const dateB = this.processData(b.date || b.totime, 'createSafeDate');
                if (!dateA && !dateB) return 0;
                if (!dateA) return 1;
                if (!dateB) return -1;
                return dateB.getTime() - dateA.getTime();
            });

            const latestAttachment = sortedGroup[0];
            // 为每个附件添加isLatest标记
            const groupWithLatestFlag = group.map(attachment => ({
                ...attachment,
                isLatest: attachment === latestAttachment
            }));

            result.push(...groupWithLatestFlag);
        }

        return result;
    }
    
    // 创建详细列表对话框
    createDetailListDialog(title, data, itemRenderer) {
        const dialog = this.createUI('div', {
            styles: 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.6); z-index: 10000; display: flex; align-items: center; justify-content: center; backdrop-filter: blur(4px);',
            content: `
                <div style="background: white; border-radius: 12px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); width: 95%; max-width: 1000px; max-height: 90vh; display: flex; flex-direction: column; overflow: hidden;">
                    <!-- 头部区域 -->
                    <div style="padding: 24px; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); display: flex; align-items: center; justify-content: space-between;">
                        <div style="flex: 1;">
                            <h3 style="margin: 0; font-size: 20px; font-weight: 600; color: var(--base_gray_090, rgba(22, 30, 38, 0.9));">${title}</h3>
                            <p id="list-stats" style="margin: 4px 0 0 0; font-size: 14px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                                ${title.includes('重复附件') ? 
                                    `共 <span id="total-count">${data.length}</span> 组重复文件 · 显示 <span id="showing-count">${Math.min(data.length, 20)}</span> 组` :
                                    `共 <span id="total-count">${data.length}</span> 项 · 显示 <span id="showing-count">${Math.min(data.length, 20)}</span> 项`
                                }
                            </p>
                        </div>
                        <button id="close-detail-dialog" style="background: none; border: none; font-size: 24px; cursor: pointer; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); padding: 8px; border-radius: 6px; transition: all 0.2s; margin-left: 16px;">×</button>
                    </div>
                    
                    <!-- 工具栏区域 -->
                    <div style="padding: 16px 24px; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); background: var(--base_gray_005, rgba(22, 46, 74, 0.05));">
                        <div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
                            <!-- 搜索框 -->
                            <div style="flex: 1; min-width: 200px; position: relative;">
                                <input id="detail-search" type="text" placeholder="搜索..." style="width: 70%; padding: 8px 12px 8px 36px; border: 1px solid var(--base_gray_020, rgba(22, 46, 74, 0.2)); border-radius: 6px; font-size: 14px; outline: none; transition: all 0.2s;">
                                <svg style="position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5));" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <circle cx="11" cy="11" r="8"></circle>
                                    <path d="m21 21-4.35-4.35"></path>
                                </svg>
                            </div>
                            
                            <!-- 排序选择 -->
                            <select id="detail-sort" style="padding: 8px 12px; border: 1px solid var(--base_gray_020, rgba(22, 46, 74, 0.2)); border-radius: 6px; font-size: 14px; background: white; cursor: pointer;">
                                <option value="default">默认排序</option>
                                <option value="name">按名称排序</option>
                                <option value="time">按时间排序</option>
                                <option value="size">按大小排序</option>
                            </select>
                            
                            <!-- 显示数量选择 -->
                            <select id="detail-page-size" style="padding: 8px 12px; border: 1px solid var(--base_gray_020, rgba(22, 46, 74, 0.2)); border-radius: 6px; font-size: 14px; background: white; cursor: pointer;">
                                <option value="20">显示 20 项</option>
                                <option value="50">显示 50 项</option>
                                <option value="100">显示 100 项</option>
                                <option value="all">显示全部</option>
                            </select>
                            
                            <!-- 刷新按钮 -->
                            <button id="detail-refresh" style="padding: 8px 12px; background: var(--theme_primary, #0F7AF5); color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; display: flex; align-items: center; gap: 6px; transition: all 0.2s;">
                                <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <polyline points="23 4 23 10 17 10"></polyline>
                                    <polyline points="1 20 1 14 7 14"></polyline>
                                    <path d="m3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
                                </svg>
                                刷新
                            </button>
                        </div>
                    </div>
                    
                    <!-- 列表内容区域 -->
                    <div style="flex: 1; overflow-y: auto; padding: 16px 24px;">
                        <div id="detail-list-container" style="display: flex; flex-direction: column; gap: 8px;">
                            <!-- 内容将通过JavaScript动态加载 -->
                        </div>
                        
                        <!-- 加载更多按钮 -->
                        <div id="load-more-section" style="padding: 16px; text-align: center; margin-top: 16px; display: none;">
                            <button id="load-more-items" style="background: var(--theme_primary, #0F7AF5); color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 14px; transition: all 0.2s;">
                                加载更多
                                    </button>
                                </div>
                        
                        <!-- 空状态提示 -->
                        <div id="empty-state" style="text-align: center; padding: 40px 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); display: none;">
                            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="margin-bottom: 16px; color: var(--base_gray_040, rgba(22, 30, 38, 0.4));">
                                <circle cx="11" cy="11" r="8"></circle>
                                <path d="m21 21-4.35-4.35"></path>
                            </svg>
                            <div style="font-size: 16px; margin-bottom: 8px;">未找到匹配项</div>
                            <div style="font-size: 14px;">尝试调整搜索条件或清除筛选</div>
                        </div>
                    </div>
                </div>
            `
        });
        
        document.body.appendChild(dialog);
        
        // 初始化对话框功能
        this.initializeDetailDialog(dialog, title, data, itemRenderer);
        
        // 设置焦点以便键盘事件生效
        dialog.tabIndex = -1;
        dialog.focus();
    }
    
    // 初始化详细列表对话框功能
    initializeDetailDialog(dialog, title, originalData, itemRenderer) {
        // 存储对话框状态
        const state = {
            originalData: originalData,
            filteredData: [...originalData],
            displayedData: [],
            itemRenderer: itemRenderer,
            currentPage: 0,
            pageSize: 20,
            searchKeyword: '',
            sortBy: 'default'
        };
        
        // 获取DOM元素
        const elements = {
            container: dialog.querySelector('#detail-list-container'),
            searchInput: dialog.querySelector('#detail-search'),
            sortSelect: dialog.querySelector('#detail-sort'),
            pageSizeSelect: dialog.querySelector('#detail-page-size'),
            refreshBtn: dialog.querySelector('#detail-refresh'),
            loadMoreBtn: dialog.querySelector('#load-more-items'),
            loadMoreSection: dialog.querySelector('#load-more-section'),
            emptyState: dialog.querySelector('#empty-state'),
            totalCount: dialog.querySelector('#total-count'),
            showingCount: dialog.querySelector('#showing-count'),
            closeBtn: dialog.querySelector('#close-detail-dialog')
        };
        
        // 更新显示统计
        const updateStats = () => {
            elements.totalCount.textContent = state.filteredData.length;
            elements.showingCount.textContent = state.displayedData.length;
        };
        
        // 渲染列表项
        const renderItems = (items, append = false) => {
            if (!append) {
                elements.container.innerHTML = '';
                state.displayedData = [];
            }
            
            const startIndex = state.displayedData.length;
            const itemsToRender = items.slice(startIndex, startIndex + (state.pageSize === 'all' ? items.length : parseInt(state.pageSize)));
            
            if (itemsToRender.length === 0) {
                if (state.displayedData.length === 0) {
                    elements.emptyState.style.display = 'block';
                }
                elements.loadMoreSection.style.display = 'none';
                return;
            }
            
            elements.emptyState.style.display = 'none';
            
            itemsToRender.forEach((item, index) => {
                const itemHtml = state.itemRenderer(item, startIndex + index);
                elements.container.insertAdjacentHTML('beforeend', itemHtml);
                
                // 获取刚刚插入的最后一个元素
                const lastElement = elements.container.lastElementChild;
                
                // 如果是重复附件组,需要添加事件监听器
                if (Array.isArray(item)) {
                    const duplicateItems = lastElement.querySelectorAll('.duplicate-item');
                    
                    duplicateItems.forEach(duplicateItem => {
                        const mailId = duplicateItem.getAttribute('data-mail-id');
                        
                        // 点击事件  
                        duplicateItem.addEventListener('click', ((mailId) => {
                            return (e) => {
                                e.preventDefault();
                                e.stopPropagation();
                                this.openMail(mailId);
                            };
                        })(mailId));
                        
                        // 悬停效果
                        duplicateItem.addEventListener('mouseenter', () => {
                            duplicateItem.style.background = 'var(--base_gray_010, rgba(22, 46, 74, 0.1))';
                        });
                        
                        duplicateItem.addEventListener('mouseleave', () => {
                            duplicateItem.style.background = 'var(--base_gray_005, rgba(22, 46, 74, 0.05))';
                        });
                    });
                } else {
                    // 为单个附件项目添加缩略图事件监听器
                    const thumbnailImg = lastElement.querySelector('.file-thumbnail');
                    const fallbackIcon = lastElement.querySelector('.file-icon-fallback');
                    
                    if (thumbnailImg && fallbackIcon) {
                        // 图片加载成功
                        thumbnailImg.addEventListener('load', () => {
                            thumbnailImg.style.opacity = '1';
                        });
                        
                        // 图片加载失败,显示fallback图标
                        thumbnailImg.addEventListener('error', () => {
                            thumbnailImg.style.display = 'none';
                            fallbackIcon.style.display = 'flex';
                        });
                    }
                }
                
                // 为所有项目的缩略图添加事件监听器(包括重复附件组的主图标)
                const groupThumbnailImg = lastElement.querySelector('.file-preview-container .file-thumbnail');
                const groupFallbackIcon = lastElement.querySelector('.file-preview-container .file-icon-fallback');
                
                if (groupThumbnailImg && groupFallbackIcon) {
                    // 图片加载成功
                    groupThumbnailImg.addEventListener('load', () => {
                        groupThumbnailImg.style.opacity = '1';
                    });
                    
                    // 图片加载失败,显示fallback图标
                    groupThumbnailImg.addEventListener('error', () => {
                        groupThumbnailImg.style.display = 'none';
                        groupFallbackIcon.style.display = 'flex';
                    });
                }
            });
            
            state.displayedData.push(...itemsToRender);
            
            // 显示/隐藏加载更多按钮
            const hasMore = state.displayedData.length < state.filteredData.length && state.pageSize !== 'all';
            elements.loadMoreSection.style.display = hasMore ? 'block' : 'none';
            
            updateStats();
        };
        
        // 应用搜索过滤
        const applySearch = () => {
            const keyword = state.searchKeyword.toLowerCase();
            if (!keyword) {
                state.filteredData = [...state.originalData];
            } else {
                state.filteredData = state.originalData.filter(item => {
                    // 判断是重复附件组还是单个项目
                    if (Array.isArray(item)) {
                        // 重复附件组:搜索组内所有附件的信息
                        return item.some(attachment => {
                            const searchText = [
                                attachment.name,
                                attachment.mailSubject,
                                attachment.senderName,
                                attachment.senderEmail,
                                attachment.sender
                            ].filter(Boolean).join(' ').toLowerCase();
                            return searchText.includes(keyword);
                        });
                    } else {
                        // 单个项目:搜索邮件主题、发件人、附件名等
                        const searchText = [
                            item.subject,
                            item.name,
                            item.mailSubject,
                            item.senderName,
                            item.senderEmail,
                            item.sender,
                            item.invalidReason
                        ].filter(Boolean).join(' ').toLowerCase();
                        return searchText.includes(keyword);
                    }
                });
            }
        };
        
        // 应用排序
        const applySort = () => {
            switch (state.sortBy) {
                case 'name':
                    state.filteredData.sort((a, b) => {
                        // 处理重复附件组和单个项目
                        const nameA = Array.isArray(a) ? (a[0]?.name || '') : (a.name || a.subject || '');
                        const nameB = Array.isArray(b) ? (b[0]?.name || '') : (b.name || b.subject || '');
                        return nameA.toLowerCase().localeCompare(nameB.toLowerCase());
                    });
                    break;
                case 'time':
                    state.filteredData.sort((a, b) => {
                        // 处理重复附件组和单个项目
                        let timeA, timeB;
                        if (Array.isArray(a)) {
                            // 重复附件组:取最新时间
                            timeA = Math.max(...a.map(item => item.totime || item.date || 0));
                        } else {
                            timeA = a.totime || a.date || 0;
                        }
                        if (Array.isArray(b)) {
                            // 重复附件组:取最新时间
                            timeB = Math.max(...b.map(item => item.totime || item.date || 0));
                        } else {
                            timeB = b.totime || b.date || 0;
                        }
                        return timeB - timeA; // 新的在前
                    });
                    break;
                case 'size':
                    state.filteredData.sort((a, b) => {
                        // 处理重复附件组和单个项目
                        const sizeA = Array.isArray(a) ? parseInt(a[0]?.size || a[0]?.filesize || 0) : parseInt(a.size || a.filesize || 0);
                        const sizeB = Array.isArray(b) ? parseInt(b[0]?.size || b[0]?.filesize || 0) : parseInt(b.size || b.filesize || 0);
                        return sizeB - sizeA; // 大的在前
                    });
                    break;
                default:
                    // 保持原始顺序
                    break;
            }
        };
        
        // 刷新列表
        const refreshList = () => {
            applySearch();
            applySort();
            state.currentPage = 0;
            renderItems(state.filteredData, false);
        };
        
        // 事件监听器
        elements.searchInput.addEventListener('input', (e) => {
            state.searchKeyword = e.target.value;
            refreshList();
        });
        
        elements.sortSelect.addEventListener('change', (e) => {
            state.sortBy = e.target.value;
            refreshList();
        });
        
        elements.pageSizeSelect.addEventListener('change', (e) => {
            state.pageSize = e.target.value;
            refreshList();
        });
        
        elements.refreshBtn.addEventListener('click', () => {
            // 重新获取数据(如果需要的话)
            refreshList();
            this.showToast('列表已刷新', 'success', 1500);
        });
        
        elements.loadMoreBtn.addEventListener('click', () => {
            renderItems(state.filteredData, true);
        });
        
        elements.closeBtn.addEventListener('click', () => {
                         document.body.removeChild(dialog);
        });
        
        // 点击背景关闭
        dialog.addEventListener('click', (e) => {
            if (e.target === dialog) {
                document.body.removeChild(dialog);
            }
        });
        
        // ESC键关闭
        dialog.addEventListener('keydown', (e) => {
                     if (e.key === 'Escape') {
                         document.body.removeChild(dialog);
            }
        });
        
        // 搜索框焦点样式
        elements.searchInput.addEventListener('focus', () => {
            elements.searchInput.style.borderColor = 'var(--theme_primary, #0F7AF5)';
            elements.searchInput.style.boxShadow = '0 0 0 2px rgba(15, 122, 245, 0.2)';
        });
        
        elements.searchInput.addEventListener('blur', () => {
            elements.searchInput.style.borderColor = 'var(--base_gray_020, rgba(22, 46, 74, 0.2))';
            elements.searchInput.style.boxShadow = 'none';
        });
        
        // 初始化列表
        refreshList();
    }
    
    // 加载更多项目(保留向后兼容)
    loadMoreItems(data, itemRenderer) {
        const container = document.getElementById('detail-list-container');
        if (!container) return;
        
        // 清空容器,显示所有项目
        container.innerHTML = data.map((item, index) => itemRenderer(item, index)).join('');
    }
    
    // 渲染邮件项目
    renderMailItem(mail, index) {
        const subject = mail.subject;
        const senderData = mail.senders?.item?.[0];
        const senderName = senderData?.name || senderData?.nick || '未知发件人';
        const senderEmail = senderData?.email || '';
        const timestamp = mail.totime || mail.date;
        const date = timestamp ? this.formatDate(this.processData(timestamp, 'createSafeDate'), 'MM-DD HH:mm') : '未知时间';
        const mailId = mail.emailid || mail.id;
        
        const element = this.createUI('div', {
            styles: `padding: 12px; background: white; border-radius: 8px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: pointer; transition: all 0.2s; margin-bottom: 8px;`,
            content: `
                <div style="display: flex; align-items: center; justify-content: space-between;">
                    <div style="flex: 1; min-width: 0;">
                        <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px;">
                            <div style="font-weight: 600; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; flex: 1; margin-right: 12px;">
                                ${subject}
                        </div>
                            <div style="font-size: 12px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); white-space: nowrap;">
                                ${date}
                        </div>
                    </div>
                        <div style="display: flex; align-items: center; justify-content: space-between;">
                            <div style="font-size: 13px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;">
                                ${senderName}
                            </div>
                        </div>
                    </div>
                    <div style="color: var(--base_gray_040, rgba(22, 30, 38, 0.4)); margin-left: 12px;">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                            <polyline points="15 3 21 3 21 9"/>
                            <line x1="10" y1="14" x2="21" y2="3"/>
                        </svg>
                    </div>
                </div>
            `,
            events: {
                click: () => this.openMail(mailId),
                mouseenter: (e) => e.target.style.background = 'var(--base_gray_005, rgba(22, 46, 74, 0.05))',
                mouseleave: (e) => e.target.style.background = 'white'
            }
        });
        
        return element.outerHTML;
    }
    
    // 渲染附件项目
    renderAttachmentItem(attachment, index) {
        const fileName = attachment.name;
        const fileSize = this.formatFileSize(parseInt(attachment.size) || parseInt(attachment.filesize));
        const invalidReason = attachment.invalidReason;
        const mailSubject = attachment.mailSubject;
        const mailId = attachment.mailId;
        const isLatest = attachment.isLatest;
        const attachmentDate = this.processData(attachment.date || attachment.totime, 'createSafeDate');
        const dateStr = attachmentDate ? this.formatDate(attachmentDate, 'MM-DD HH:mm') : '未知时间';
        const senderName = attachment.senderName || '未知发件人';
        
        // 获取文件图标和缩略图
        const fileIcon = this.getFileIcon(fileName);
        const thumbnailUrl = this.getThumbnailUrl(attachment);
        const supportsThumbnail = this.supportsThumbnail(fileName);
        
        const element = this.createUI('div', {
            styles: `padding: 12px; background: white; border-radius: 8px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: pointer; transition: all 0.2s; margin-bottom: 8px;`,
            content: `
                <div style="display: flex; align-items: center; justify-content: space-between;">
                    <div style="display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0;">
                        <!-- 文件图标/缩略图 -->
                        <div class="file-preview-container" style="width: 48px; height: 48px; border-radius: 6px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); display: flex; align-items: center; justify-content: center; flex-shrink: 0; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); overflow: hidden; position: relative;">
                            ${supportsThumbnail && thumbnailUrl ? 
                                `<img class="file-thumbnail" src="${thumbnailUrl}" alt="${fileName}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 5px; opacity: 0; transition: opacity 0.3s;" data-fallback-icon="${fileIcon}">
                                 <span class="file-icon-fallback" style="font-size: 16px; display: none; position: absolute; width: 100%; height: 100%; align-items: center; justify-content: center;">${fileIcon}</span>` : 
                                `<span style="font-size: 16px;">${fileIcon}</span>`
                            }
                        </div>
                        
                        <!-- 文件信息 -->
                    <div style="flex: 1; min-width: 0;">
                            <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
                                <div style="font-weight: 600; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; flex: 1;">
                                    ${fileName}
                        </div>
                                ${isLatest ? '<span style="background: var(--theme_success, #28a745); color: white; padding: 2px 6px; border-radius: 4px; font-size: 10px; font-weight: 500; white-space: nowrap;">最新</span>' : ''}
                                ${invalidReason ? `<span style="background: var(--theme_danger_light, rgba(220, 53, 69, 0.1)); color: var(--theme_danger, #dc3545); padding: 2px 6px; border-radius: 4px; font-size: 10px; white-space: nowrap;">${invalidReason}</span>` : ''}
                        </div>
                            <div style="display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                                <span>${fileSize}</span>
                                <span>•</span>
                                <span>${dateStr}</span>
                                <span>•</span>
                                <span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${senderName}</span>
                    </div>
                            <div style="font-size: 11px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                                来自: ${mailSubject}
                            </div>
                        </div>
                    </div>
                    <div style="color: var(--base_gray_040, rgba(22, 30, 38, 0.4)); margin-left: 12px;">
                        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                            <polyline points="15 3 21 3 21 9"/>
                            <line x1="10" y1="14" x2="21" y2="3"/>
                        </svg>
                    </div>
                </div>
            `,
            events: {
                click: () => this.openMail(mailId),
                mouseenter: (e) => e.target.style.background = 'var(--base_gray_005, rgba(22, 46, 74, 0.05))',
                mouseleave: (e) => e.target.style.background = 'white'
            }
        });
        
        return element.outerHTML;
    }

    // 渲染重复附件组项目(用于详细列表对话框)
    renderDuplicateGroupItem(group, index) {
        if (!group || group.length === 0) return '';
        
        const fileName = group[0].name;
        const fileSize = this.formatFileSize(parseInt(group[0].size) || parseInt(group[0].filesize));
        const duplicateCount = group.length;
        const fileIcon = this.getFileIcon(fileName);
        
        // 获取缩略图信息(使用组中第一个附件)
        const firstAttachment = group[0];
        const thumbnailUrl = this.getThumbnailUrl(firstAttachment);
        const supportsThumbnail = this.supportsThumbnail(fileName);
        
        // 找出最新的附件(按时间排序)
        const sortedGroup = [...group].sort((a, b) => {
            const dateA = this.processData(a.date || a.totime, 'createSafeDate');
            const dateB = this.processData(b.date || b.totime, 'createSafeDate');
            if (!dateA && !dateB) return 0;
            if (!dateA) return 1;
            if (!dateB) return -1;
            return dateB.getTime() - dateA.getTime();
        });
        
        const latestAttachment = sortedGroup[0];
        const groupWithLatestFlag = group.map(attachment => ({
            ...attachment,
            isLatest: attachment === latestAttachment
        }));
        
        const element = this.createUI('div', {
            styles: `padding: 16px; background: white; border-radius: 8px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); margin-bottom: 8px;`,
            content: `
                <div style="display: flex; align-items: flex-start; gap: 12px;">
                    <!-- 文件图标/缩略图 -->
                    <div class="file-preview-container" style="width: 40px; height: 40px; border-radius: 8px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); display: flex; align-items: center; justify-content: center; flex-shrink: 0; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); overflow: hidden; position: relative;">
                        ${supportsThumbnail && thumbnailUrl ? 
                            `<img class="file-thumbnail" src="${thumbnailUrl}" alt="${fileName}" style="width: 100%; height: 100%; object-fit: cover; border-radius: 7px; opacity: 0; transition: opacity 0.3s;" data-fallback-icon="${fileIcon}">
                             <span class="file-icon-fallback" style="font-size: 16px; display: none; position: absolute; width: 100%; height: 100%; align-items: center; justify-content: center;">${fileIcon}</span>` : 
                            `<span style="font-size: 16px;">${fileIcon}</span>`
                        }
                    </div>
                    
                    <!-- 文件信息 -->
                    <div style="flex: 1; min-width: 0;">
                        <!-- 文件头部信息 -->
                        <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
                            <div style="font-weight: 600; color: var(--base_gray_090, rgba(22, 30, 38, 0.9)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 16px; flex: 1;">
                                ${fileName}
                            </div>
                            <span style="background: var(--theme_warning_light, rgba(255, 193, 7, 0.1)); color: var(--theme_warning, #ffc107); padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; white-space: nowrap;">
                                重复 ${duplicateCount} 次
                            </span>
                        </div>
                        
                        <!-- 文件基本信息 -->
                        <div style="font-size: 13px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); margin-bottom: 12px;">
                            文件大小: ${fileSize}
                        </div>
                        
                        <!-- 重复附件详情列表 -->
                        <div style="border-top: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); padding-top: 12px;">
                            <div style="font-size: 12px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); margin-bottom: 8px; font-weight: 500;">
                                重复附件详情:
                            </div>
                            <div id="duplicate-items-container-${index}" style="display: flex; flex-direction: column; gap: 6px; max-height: 200px; overflow-y: auto;">
                                ${groupWithLatestFlag.map((attachment, idx) => {
                                    const attachmentDate = this.processData(attachment.date || attachment.totime, 'createSafeDate');
                                    const dateStr = attachmentDate ? this.formatDate(attachmentDate, 'MM-DD HH:mm') : '未知时间';
                                    const senderName = attachment.senderName || '未知发件人';
                                    const mailSubject = attachment.mailSubject || '未知邮件';
                                    const isLatest = attachment.isLatest;
                                    
                                    return `
                                        <div class="duplicate-item" data-mail-id="${attachment.mailId}" style="padding: 8px 12px; background: var(--base_gray_005, rgba(22, 46, 74, 0.05)); border-radius: 6px; border: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1)); cursor: pointer; transition: all 0.2s;">
                                            <div style="display: flex; align-items: center; justify-content: space-between; gap: 8px;">
                                                <div style="flex: 1; min-width: 0;">
                                                    <div style="display: flex; align-items: center; gap: 6px; margin-bottom: 2px;">
                                                        <span style="font-size: 11px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;">
                                                            ${mailSubject}
                                                        </span>
                                                        ${isLatest ? '<span style="background: var(--theme_success, #28a745); color: white; padding: 1px 4px; border-radius: 3px; font-size: 9px; font-weight: 500;">最新</span>' : ''}
                                                    </div>
                                                    <div style="font-size: 10px; color: var(--base_gray_050, rgba(22, 30, 38, 0.5)); display: flex; align-items: center; gap: 8px;">
                                                        <span>${senderName}</span>
                                                        <span>•</span>
                                                        <span>${dateStr}</span>
                                                    </div>
                                                </div>
                                                <div style="color: var(--base_gray_040, rgba(22, 30, 38, 0.4)); font-size: 10px;">
                                                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                                        <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                                                        <polyline points="15 3 21 3 21 9"/>
                                                        <line x1="10" y1="14" x2="21" y2="3"/>
                                                    </svg>
                                                </div>
                                            </div>
                                        </div>
                                    `;
                                }).join('')}
                            </div>
                        </div>
                    </div>
                </div>
            `
        });
        
        return element.outerHTML;
    }
    

    
    // 打开邮件
    openMail(mailId) {
        if (!mailId) {
            this.showToast('无法打开邮件:邮件ID无效', 'error');
            return;
        }
        
        try {
            // 构建邮件URL - 使用正确的QQ邮箱URL格式
            const currentUrl = window.location.href;
            const baseUrl = currentUrl.split('#')[0];
            const mailUrl = `${baseUrl}#/read/${mailId}`;
            
            // 在新标签页中打开
            window.open(mailUrl, '_blank');
            this.showToast('正在新标签页中打开邮件...', 'info');
        } catch (error) {
            console.error('打开邮件失败:', error);
            this.showToast('打开邮件失败', 'error');
        }
    }

    // 打开验证设置
    openValidationSettings() {
        try {
            // 直接调用设置对话框,并切换到文件命名标签页
            this.showSettingsDialog().then(() => {
                // 等待对话框创建完成后切换到文件命名标签页
                setTimeout(() => {
                    this.switchSettingsTab('file-naming');
                    
                    // 滚动到验证规则部分
                    setTimeout(() => {
                        const validationSection = document.querySelector('#validation-enabled');
                        if (validationSection) {
                            validationSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
                            // 高亮显示验证规则区域
                            const validationContainer = validationSection.closest('.setting-group');
                            if (validationContainer) {
                                validationContainer.style.transition = 'all 0.3s ease';
                                validationContainer.style.background = 'rgba(0, 123, 255, 0.1)';
                                validationContainer.style.borderRadius = '8px';
                                validationContainer.style.padding = '16px';
                                
                                // 3秒后移除高亮
                                setTimeout(() => {
                                    validationContainer.style.background = '';
                                    validationContainer.style.padding = '';
                                }, 3000);
                            }
                        }
                    }, 200);
                }, 100);
            });
            
            this.showToast('正在打开验证规则设置...', 'info');
        } catch (error) {
            console.error('打开设置失败:', error);
            this.showToast('打开设置失败', 'error');
        }
    }

    // 生成文件夹结构预览
    generateFolderStructurePreview(attachments) {
        const folderStructure = this.downloadSettings.folderStructure;
        
        if (folderStructure === 'flat') {
            return null; // 平铺模式不显示文件夹结构
        }
        
        // 取前几个附件作为示例
        const sampleAttachments = attachments.slice(0, 3);
        const folders = new Set();
        
        sampleAttachments.forEach(attachment => {
            let folderPath = '';
            
            switch (folderStructure) {
                case 'subject':
                    folderPath = this.sanitizeFileName(attachment.mailSubject || attachment.subject || '未知主题', attachment);
                    break;
                case 'sender':
                    folderPath = attachment.senderEmail || attachment.sender || '未知发件人';
                    break;
                case 'date':
                    const mailDate = attachment.date || attachment.totime ? 
                        new Date(typeof (attachment.date || attachment.totime) === 'number' && (attachment.date || attachment.totime) < 10000000000 ? 
                            (attachment.date || attachment.totime) * 1000 : (attachment.date || attachment.totime)) : new Date();
                    folderPath = this.formatDate(mailDate, 'MM-DD');
                    break;
                case 'custom':
                    const customTemplate = this.downloadSettings.folderNaming?.customTemplate || '{date:YYYY-MM-DD}/{senderName}';
                    const variableData = this.buildVariableData(attachment);
                    folderPath = this.replaceVariablesInTemplate(customTemplate, variableData);
                    // 确保预览显示正确,如果变量替换失败则使用示例数据
                    if (folderPath === customTemplate || folderPath.includes('{') || folderPath.includes('}')) {
                        const now = new Date();
                        const exampleData = {
                            date: now,
                            senderName: attachment.senderName || attachment.sender || '示例发件人',
                            subject: attachment.mailSubject || attachment.subject || '示例主题',
                            senderEmail: attachment.senderEmail || '[email protected]'
                        };
                        folderPath = this.replaceVariablesInTemplate(customTemplate, exampleData);
                    }
                    break;
            }
            
            if (folderPath) {
                folders.add(folderPath);
            }
        });
        
        const folderArray = Array.from(folders);
        const maxDisplay = 3;
        const hasMore = folderArray.length > maxDisplay;
        const displayFolders = folderArray.slice(0, maxDisplay);
        
        return {
            folders: displayFolders,
            hasMore,
            totalCount: folderArray.length,
            structure: folderStructure
        };
    }

    // 更新文件夹结构预览显示
    updateFolderStructurePreview(attachments) {
        const previewCard = document.getElementById('folder-structure-preview-card');
        const previewContent = document.getElementById('folder-structure-preview-content');
        
        if (!previewCard || !previewContent) return;
        
        const preview = this.generateFolderStructurePreview(attachments);
        
        if (preview) {
            previewCard.style.display = 'block';
            previewContent.innerHTML = preview.folders.slice(0, 3).map(folder => `📁 ${folder}`).join('<br>') + 
                (preview.folders.length > 3 ? `<br><span style="color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">... 共 ${preview.folders.length} 个文件夹</span>` : '');
        } else {
            previewCard.style.display = 'none';
        }
    }

    createHeaderCard() {
        const folderName = this.getFolderDisplayName();
        const headerCard = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoCardHeader,
            content: `
                <div style="display: flex; align-items: center; gap: 16px;">
                    <button style="background: none; border: none; padding: 8px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); cursor: pointer; border-radius: 6px; transition: all 0.2s ease;" id="back-btn" title="返回邮件列表">
                        <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
                            <path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/>
                        </svg>
                    </button>
                    <div style="flex: 1;">
                        <div style="${StyleManager.getStyles().texts.headerTitle}">${folderName}</div>
                        <div style="${StyleManager.getStyles().texts.headerSubtitle}" id="attachment-count-info">实时分析邮箱附件分布情况</div>
                    </div>
                </div>
                <div style="display: flex; gap: 12px; align-items: center;">
                    <button style="${StyleManager.getStyles().texts.actionButtonSecondary}" id="settings-btn">
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <circle cx="12" cy="12" r="3"/>
                            <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1 1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
                        </svg>
                        设置
                    </button>
                    <button style="${StyleManager.getStyles().texts.actionButtonSecondary}${!window.showDirectoryPicker ? '; opacity: 0.5; cursor: not-allowed;' : ''}" id="compare-btn" ${!window.showDirectoryPicker ? 'disabled title="需要 Chrome 86+ 或 Edge 86+ 浏览器支持"' : ''}>
                        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                            <path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2z"/>
                            <path d="M19 11h-4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h4a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2z"/>
                            <path d="M12 2v9"/>
                            <path d="M8 6l4-4 4 4"/>
                        </svg>
                        对比本地
                    </button>
                </div>
            `,
            events: {
                click: (e) => {
                    if (e.target.id === 'back-btn' || e.target.closest('#back-btn')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.hideAttachmentView();
                    } else if (e.target.id === 'settings-btn' || e.target.closest('#settings-btn')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.showSettingsDialog();
                    } else if (e.target.id === 'compare-btn' || e.target.closest('#compare-btn')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.showCompareDialog();
                    }
                }
            }
        });

        // 添加按钮悬停效果
        const backBtn = headerCard.querySelector('#back-btn');
        const settingsBtn = headerCard.querySelector('#settings-btn');
        const compareBtn = headerCard.querySelector('#compare-btn');
        
        if (backBtn) {
            backBtn.addEventListener('mouseenter', () => {
                backBtn.style.background = 'var(--base_gray_005, rgba(20, 46, 77, 0.05))';
                backBtn.style.color = 'var(--base_gray_080, rgba(22, 30, 38, 0.8))';
            });
            backBtn.addEventListener('mouseleave', () => {
                backBtn.style.background = 'none';
                backBtn.style.color = 'var(--base_gray_060, rgba(22, 30, 38, 0.6))';
            });
        }
        
        if (settingsBtn) {
            settingsBtn.addEventListener('mouseenter', () => {
                StyleManager.applyInteractionStyle(settingsBtn, 'actionButtonSecondaryHover');
            });
            settingsBtn.addEventListener('mouseleave', () => {
                StyleManager.applyInteractionStyle(settingsBtn, 'actionButtonSecondaryHover', true);
            });
        }
        
        if (compareBtn) {
            compareBtn.addEventListener('mouseenter', () => {
                StyleManager.applyInteractionStyle(compareBtn, 'actionButtonSecondaryHover');
            });
            compareBtn.addEventListener('mouseleave', () => {
                StyleManager.applyInteractionStyle(compareBtn, 'actionButtonSecondaryHover', true);
            });
        }

        return headerCard;
    }

    createMainFunctionRow() {
        const row = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoRowResponsive,
            className: 'bento-row-responsive',
            content: `
                <div style="${StyleManager.getStyles().layouts.bentoCardPrimary}">
                    <div style="display: flex; flex-direction: column; height: 100%;">
                        <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoLabelLarge}">📎 附件下载</div>
                            <div style="opacity: 0.6;">
                                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
                                    <polyline points="7,10 12,15 17,10"/>
                                    <line x1="12" y1="15" x2="12" y2="3"/>
                                </svg>
                            </div>
                        </div>
                        <div style="flex: 1; display: flex; flex-direction: column; justify-content: center; text-align: center;">
                            <div style="${StyleManager.getStyles().texts.bentoNumberLarge}" id="attachment-count">计算中...</div>
                            <div style="${StyleManager.getStyles().texts.bentoDescLarge}; margin-top: 6px;" id="total-size">计算中...</div>
                            <div id="folder-structure-preview-card" style="margin-top: 12px; display: none;">
                                <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6)); margin-bottom: 6px;">文件夹结构预览</div>
                                <div id="folder-structure-preview-content" style="background: var(--base_gray_005, rgba(20, 46, 77, 0.05)); padding: 8px 12px; border-radius: 6px; font-size: 11px; font-family: monospace; color: var(--base_gray_080, rgba(22, 30, 38, 0.8)); text-align: left; max-height: 80px; overflow-y: auto;">
                                </div>
                            </div>
                        </div>
                        <div style="text-align: center; margin-top: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoDescLarge}; font-weight: 600;">点击下载全部附件</div>
                        </div>
                    </div>
                </div>
                <div style="${StyleManager.getStyles().layouts.bentoCard}">
                    <div style="display: flex; flex-direction: column; height: 100%;">
                        <div style="margin-bottom: 16px;">
                            <div style="${StyleManager.getStyles().texts.bentoTitle}">📊 统计概览</div>
                        </div>
                        <div style="flex: 1; display: flex; flex-direction: column; justify-content: space-between;">
                            <div style="display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1));">
                                <span style="font-size: 14px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7));">邮件总数</span>
                                <span style="font-size: 16px; font-weight: 600; color: var(--base_gray_100, #13181D);" id="quick-mail-count">计算中...</span>
                            </div>
                            <div style="display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid var(--base_gray_010, rgba(22, 46, 74, 0.1));">
                                <span style="font-size: 14px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7));">发件人数量</span>
                                <span style="font-size: 16px; font-weight: 600; color: var(--base_gray_100, #13181D);" id="quick-sender-count">计算中...</span>
                            </div>
                            <div style="display: flex; justify-content: space-between; align-items: center; padding: 12px 0;">
                                <span style="font-size: 14px; color: var(--base_gray_070, rgba(22, 30, 38, 0.7));">无附件邮件</span>
                                <span style="font-size: 16px; font-weight: 600; color: var(--base_gray_100, #13181D);" id="quick-no-attachment-mails">计算中...</span>
                            </div>
                        </div>
                    </div>
                </div>
            `
        });
        
        // 添加下载点击事件和悬停效果到第一个卡片
        const downloadCard = row.children[0];
        const operationCard = row.children[1];
        
        downloadCard.addEventListener('click', () => {
            this.downloadAll();
        });
        
        // 添加悬停效果
        downloadCard.addEventListener('mouseenter', () => {
            StyleManager.applyInteractionStyle(downloadCard, 'cardPrimaryHover');
        });
        downloadCard.addEventListener('mouseleave', () => {
            StyleManager.applyInteractionStyle(downloadCard, 'cardPrimaryHover', true);
        });
        
        // 移除统计卡片的悬停效果,因为它不可点击
        
        return row;
    }

    createMailStatsCard(totalMails) {
        const card = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoCard,
            content: `
                <div style="margin-bottom: 16px;">
                    <div style="${StyleManager.getStyles().texts.bentoTitle}">邮件统计概览</div>
                </div>
                <div style="${StyleManager.getStyles().layouts.bentoStatsResponsive}" class="bento-stats-responsive">
                    <div style="text-align: center; padding: 16px;">
                        <div style="${StyleManager.getStyles().texts.bentoNumber}">${totalMails}</div>
                        <div style="${StyleManager.getStyles().texts.bentoLabel}">有附件邮件</div>
                        <div style="${StyleManager.getStyles().texts.bentoDesc}">包含附件的邮件总数</div>
                    </div>
                    <div style="text-align: center; padding: 16px;">
                        <div style="${StyleManager.getStyles().texts.bentoNumber}" id="sender-count">计算中...</div>
                        <div style="${StyleManager.getStyles().texts.bentoLabel}">发件人数量</div>
                        <div style="${StyleManager.getStyles().texts.bentoDesc}">不同发件人总数</div>
                    </div>
                                        <div style="text-align: center; padding: 16px;">
                        <div style="${StyleManager.getStyles().texts.bentoNumber}" id="no-attachment-mails">计算中...</div>
                        <div style="${StyleManager.getStyles().texts.bentoLabel}">无附件邮件</div>
                        <div style="${StyleManager.getStyles().texts.bentoDesc}">需要检查的邮件</div>
                    </div>
                </div>
            `
        });
        return card;
    }

    createNoAttachmentListCard() {
        const card = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoCard + '; display: none;',
            className: 'bento-card no-attachment-list-card',
            content: `
                <div style="margin-bottom: 16px;">
                    <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
                    <div style="${StyleManager.getStyles().texts.bentoTitle}">无附件邮件列表</div>
                        <button id="view-all-no-attachment-mails" style="padding: 6px 12px; font-size: 12px; background: var(--theme_primary, #007bff); color: white; border: none; border-radius: 4px; cursor: pointer; display: flex; align-items: center; gap: 4px; transition: all 0.2s;">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
                                <circle cx="12" cy="12" r="3"/>
                            </svg>
                            查看完整列表
                        </button>
                    </div>
                    <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">点击邮件可在新标签页中打开</div>
                </div>
                <div id="no-attachment-list-container" style="max-height: 300px; overflow-y: auto;">
                    <div style="text-align: center; padding: 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                        加载中...
                    </div>
                </div>
            `,
            events: {
                click: (e) => {
                    if (e.target.id === 'view-all-no-attachment-mails' || e.target.closest('#view-all-no-attachment-mails')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.showDetailList('no-attachment');
                    }
                }
            }
        });
        return card;
    }

    createInvalidNamingListCard() {
        const card = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoCard + '; display: none;',
            className: 'bento-card invalid-naming-list-card',
            content: `
                <div style="margin-bottom: 16px;">
                    <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
                        <div style="${StyleManager.getStyles().texts.bentoTitle}">命名异常附件列表</div>
                        <div style="display: flex; gap: 8px;">
                            <button id="view-all-invalid-naming" style="padding: 6px 12px; font-size: 12px; background: var(--theme_success, #28a745); color: white; border: none; border-radius: 4px; cursor: pointer; display: flex; align-items: center; gap: 4px; transition: all 0.2s;">
                                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                    <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
                                    <circle cx="12" cy="12" r="3"/>
                                </svg>
                                查看完整列表
                            </button>
                            <button id="open-validation-settings" class="validation-settings-btn" style="padding: 6px 12px; font-size: 12px; background: var(--theme_primary, #007bff); color: white; border: none; border-radius: 4px; cursor: pointer; display: flex; align-items: center; gap: 4px; transition: all 0.2s;">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <circle cx="12" cy="12" r="3"/>
                                <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1 1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
                            </svg>
                            修改验证规则
                        </button>
                        </div>
                    </div>
                    <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">点击附件可在新标签页中打开对应邮件</div>
                </div>
                <div id="invalid-naming-list-container" style="max-height: 300px; overflow-y: auto;">
                    <div style="text-align: center; padding: 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                        加载中...
                    </div>
                </div>
            `,
            events: {
                click: (e) => {
                    if (e.target.id === 'view-all-invalid-naming' || e.target.closest('#view-all-invalid-naming')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.showDetailList('invalid-naming');
                    } else if (e.target.id === 'open-validation-settings' || e.target.closest('#open-validation-settings')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.openValidationSettings();
                    }
                }
            }
        });
        return card;
    }

    // 创建重复附件列表卡片
    createDuplicateAttachmentsListCard() {
        const card = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoCard + '; display: none;',
            className: 'bento-card duplicate-attachments-list-card',
            content: `
                <div style="margin-bottom: 16px;">
                    <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px;">
                        <div style="${StyleManager.getStyles().texts.bentoTitle}">重复附件列表</div>
                        <button id="view-all-duplicate-attachments" style="padding: 6px 12px; font-size: 12px; background: var(--theme_warning, #ffc107); color: #212529; border: none; border-radius: 4px; cursor: pointer; display: flex; align-items: center; gap: 4px; transition: all 0.2s; font-weight: 500;">
                            <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
                                <circle cx="12" cy="12" r="3"/>
                            </svg>
                            查看完整列表
                        </button>
                    </div>
                    <div style="font-size: 12px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">点击附件可在新标签页中打开对应邮件</div>
                </div>
                <div id="duplicate-attachments-list-container" style="max-height: 300px; overflow-y: auto;">
                    <div style="text-align: center; padding: 20px; color: var(--base_gray_060, rgba(22, 30, 38, 0.6));">
                        加载中...
                    </div>
                </div>
            `,
            events: {
                click: (e) => {
                    if (e.target.id === 'view-all-duplicate-attachments' || e.target.closest('#view-all-duplicate-attachments')) {
                        e.preventDefault();
                        e.stopPropagation();
                        this.showDetailList('duplicate-attachments');
                    }
                }
            }
        });
        return card;
    }

    createFileTypeRow() {
        const row = this.createUI('div', {
            styles: StyleManager.getStyles().layouts.bentoRowResponsive,
            className: 'bento-row-responsive',
            content: `
                <div style="${StyleManager.getStyles().layouts.bentoCard}">
                    <div style="margin-bottom: 16px;">
                        <div style="${StyleManager.getStyles().texts.bentoTitle}">文件类型分布</div>
                    </div>
                    <div style="${StyleManager.getStyles().layouts.bentoStatsResponsive}" class="bento-stats-responsive">
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="image-files">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">图片</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">JPG, PNG, GIF等</div>
                        </div>
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="archive-files">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">压缩包</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">ZIP, RAR, 7Z等</div>
                        </div>
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="document-files">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">文档</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">PDF, DOC, XLS等</div>
                        </div>
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="other-files">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">其他</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">其他格式文件</div>
                        </div>
                    </div>
                </div>
                <div style="${StyleManager.getStyles().layouts.bentoCard}">
                    <div style="margin-bottom: 16px;">
                        <div style="${StyleManager.getStyles().texts.bentoTitle}">问题文件检测</div>
                    </div>
                    <div style="${StyleManager.getStyles().layouts.bentoStatsResponsive}" class="bento-stats-responsive">
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="invalid-naming">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">命名异常</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">无扩展名、空文件、特殊字符等</div>
                        </div>
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="large-files">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">超大文件</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">&gt;10MB</div>
                        </div>
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="duplicate-attachments">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">重复文件</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">相同名称且相同大小的文件</div>
                        </div>
                        <div style="text-align: center; padding: 12px;">
                            <div style="${StyleManager.getStyles().texts.bentoNumber}" id="duplicate-mails">0</div>
                            <div style="${StyleManager.getStyles().texts.bentoLabel}">重复邮件</div>
                            <div style="${StyleManager.getStyles().texts.bentoDesc}">相同发件人的相同主题且相同附件的邮件</div>
                        </div>
                    </div>
                </div>
            `
        });
        return row;
    }



    updateCardValue(card, value) {
        const numberElement = card.children[1];
        if (numberElement) {
            numberElement.textContent = value;
        }
    }

    updateElementById(id, value) {
        const element = document.getElementById(id);
        if (element) {
            element.textContent = value;
        }
    }

        createBentoCard(label, value, description, size = 'normal', icon = '', action = '') {
            const isPrimary = size === 'primary';
            const sizeClass = isPrimary ? 'bentoCardPrimary' :
                size === 'wide' ? 'bentoCardWide' :
                size === 'tall' ? 'bentoCardTall' :
                size === 'small' ? 'bentoCardSmall' : '';

            const isClickable = action === 'download';
            let cardStyle = StyleManager.getStyles().layouts.bentoCard;
            if (sizeClass) {
                cardStyle += '; ' + StyleManager.getStyles().layouts[sizeClass];
            }
            // 只有可点击的卡片才添加cursor: pointer
            if (isClickable) {
                cardStyle += '; cursor: pointer;';
            }

            const numberStyle = isPrimary ? StyleManager.getStyles().texts.bentoNumberPrimary : StyleManager.getStyles().texts.bentoNumber;
            const labelStyle = isPrimary ? StyleManager.getStyles().texts.bentoLabelPrimary : StyleManager.getStyles().texts.bentoLabel;
            const descStyle = isPrimary ? StyleManager.getStyles().texts.bentoDescPrimary : StyleManager.getStyles().texts.bentoDesc;

            const card = this.createUI('div', {
                styles: cardStyle,
                content: `
                <div>
                    <div style="${numberStyle}">${value}</div>
                    <div style="${labelStyle}">${label}</div>
                </div>
                <div style="${descStyle}">${description}</div>
                ${isClickable ? `<div style="position: absolute; top: 16px; right: 16px;">
                    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="opacity: 0.7;">
                        <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
                        <polyline points="7,10 12,15 17,10"/>
                        <line x1="12" y1="15" x2="12" y2="3"/>
                    </svg>
                </div>` : ''}
            `,
                events: isClickable ? {
                    mouseenter: (e) => {
                        if (isPrimary) {
                            StyleManager.applyInteractionStyle(e.target, 'cardPrimaryHover');
                        } else {
                            StyleManager.applyInteractionStyle(e.target, 'cardHover');
                        }
                    },
                    mouseleave: (e) => {
                        StyleManager.applyInteractionStyle(e.target, isPrimary ? 'cardPrimaryHover' : 'cardHover', true);
                    },
                    click: () => {
                        if (action === 'download') {
                            this.downloadAll();
                        }
                    }
                } : {}
            });

            return card;
        }

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

        // 保留这些方法以防其他地方需要使用,但简化实现
        async renderGroupsInBatches(groups, container) {
            // 简化版本,不再使用
            return;
        }

        createGroupContainer(group, index) {
            // 简化版本,不再使用
            return this.createUI('div');
    }

    createAndInjectButton() {
        console.log('[按钮创建] 开始创建附件管理按钮');
        
        // 先清理已存在的按钮 - 使用更全面的清理方式
        const existingButtons = document.querySelectorAll('#attachment-downloader-btn, [data-attachment-manager-btn="true"], .attachment-floating-btn');
        if (existingButtons.length > 0) {
            console.log('[按钮创建] 清理已存在的按钮,数量:', existingButtons.length);
            existingButtons.forEach(btn => btn.remove());
        }

        // 检查屏幕宽度决定显示方式
        const screenWidth = window.innerWidth;
        console.log('[按钮创建] 屏幕宽度:', screenWidth);
        
        try {
            if (screenWidth < 1340) {
                this.createFloatingButton();
            } else {
                this.createToolbarButton();
            }
            
            // 设置按钮重复检查机制
            this.setupButtonDuplicationCheck();
            
            // 设置窗口大小变化监听
            this.setupResponsiveListener();
            
            console.log('[按钮创建] 附件管理按钮创建完成');
        } catch (error) {
            console.error('[按钮创建] 创建按钮时出现错误:', error);
            // 延迟重试
            setTimeout(() => {
                console.log('[按钮创建] 重试创建按钮');
                this.createAndInjectButton();
            }, 2000);
        }
    }

    createToolbarButton() {
            // 尝试多个可能的工具栏选择器
            const toolbarSelectors = [
                '.xmail-ui-ellipsis-toolbar .ui-ellipsis-toolbar-btns',
                '.ui-ellipsis-toolbar-btns',
                '.toolbar-btns',
                '.mail-toolbar .toolbar-btns',
                '.xmail-ui-toolbar .ui-toolbar-btns',
                '.toolbar-right',
                '.toolbar-actions'
            ];

            let toolbar = null;
            for (const selector of toolbarSelectors) {
                toolbar = document.querySelector(selector);
                if (toolbar) break;
            }

            if (!toolbar) {
                // 如果找不到工具栏,延迟重试
                setTimeout(() => this.createAndInjectButton(), 1000);
                return;
            }

            const button = this.createUI('button', {
                id: 'attachment-downloader-btn',
                className: 'xmail-ui-btn ui-btn-size32 ui-btn-border ui-btn-them-clear-gray',
                attributes: { 'data-attachment-manager-btn': 'true' },
                content: `
                    <span class="xmail-ui-icon ui-btn-icon" style="width: 16px; height: 16px; margin-right: 4px;">
                <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
                    <path d="M768 810.7H256c-44.2 0-80-35.8-80-80V547.2c0-17.7 14.3-32 32-32s32 14.3 32 32v183.5c0 8.8 7.2 16 16 16h512c8.8 0 16-7.2 16-16V547.2c0-17.7 14.3-32 32-32s32 14.3 32 32v183.5c0 44.2-35.8 80-80 80zM480 614.4c-8.2 0-16.4-3.1-22.6-9.4L234.8 382.3c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L480 536.7l199.9-199.7c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L502.6 605c-6.3 6.3-14.4 9.4-22.6 9.4z" fill="#2c2c2c"></path>
                    <path d="M512 646.4c-17.7 0-32-14.3-32-32V172.8c0-17.7 14.3-32 32-32s32 14.3 32 32v441.6c0 17.7-14.3 32-32 32z" fill="#2c2c2c"></path>
                </svg>
                    </span>
                    <span>附件管理</span>
                `,
                styles: { marginLeft: '8px' },
                events: {
                    click: (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        this.toggleAttachmentManager();
                    }
                }
            });

            toolbar.appendChild(button);
    }

    createFloatingButton() {
            // 添加浮动按钮样式
            this.addFloatingButtonStyles();

            const floatingButton = this.createUI('div', {
                className: 'attachment-floating-btn',
                attributes: { 'data-attachment-manager-btn': 'true' },
                content: `
                    <div class="floating-btn-icon">
                        <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
                            <path d="M768 810.7H256c-44.2 0-80-35.8-80-80V547.2c0-17.7 14.3-32 32-32s32 14.3 32 32v183.5c0 8.8 7.2 16 16 16h512c8.8 0 16-7.2 16-16V547.2c0-17.7 14.3-32 32-32s32 14.3 32 32v183.5c0 44.2-35.8 80-80 80zM480 614.4c-8.2 0-16.4-3.1-22.6-9.4L234.8 382.3c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L480 536.7l199.9-199.7c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3L502.6 605c-6.3 6.3-14.4 9.4-22.6 9.4z" fill="currentColor"></path>
                            <path d="M512 646.4c-17.7 0-32-14.3-32-32V172.8c0-17.7 14.3-32 32-32s32 14.3 32 32v441.6c0 17.7-14.3 32-32 32z" fill="currentColor"></path>
                        </svg>
                    </div>
                    <div class="floating-btn-tooltip">附件管理</div>
                `,
                events: {
                    click: (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        this.toggleAttachmentManager();
                    }
                }
            });

            document.body.appendChild(floatingButton);
    }

    addFloatingButtonStyles() {
            // 检查是否已经添加了样式
            if (document.getElementById('attachment-floating-btn-styles')) {
                return;
            }

            const styles = document.createElement('style');
            styles.id = 'attachment-floating-btn-styles';
            styles.textContent = `
                .attachment-floating-btn {
                    position: fixed;
                    right: 20px;
                    bottom: 60px;
                    width: 56px;
                    height: 56px;
                    background: var(--theme_primary, #0F7AF5);
                    border-radius: 50%;
                    box-shadow: 0 4px 12px rgba(15, 122, 245, 0.3);
                    cursor: pointer;
                    z-index: 1000;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
                    user-select: none;
                }

                .attachment-floating-btn:hover {
                    transform: translateY(-2px);
                    box-shadow: 0 6px 16px rgba(15, 122, 245, 0.4);
                    background: var(--theme_darken_1, #0E6FD9);
                }

                .attachment-floating-btn:active {
                    transform: translateY(0);
                    box-shadow: 0 2px 8px rgba(15, 122, 245, 0.3);
                }

                .floating-btn-icon {
                    width: 24px;
                    height: 24px;
                    color: white;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                }

                .floating-btn-icon svg {
                    width: 100%;
                    height: 100%;
                }

                .floating-btn-tooltip {
                    position: absolute;
                    right: 64px;
                    top: 50%;
                    transform: translateY(-50%);
                    background: rgba(0, 0, 0, 0.8);
                    color: white;
                    padding: 8px 12px;
                    border-radius: 4px;
                    font-size: 14px;
                    white-space: nowrap;
                    opacity: 0;
                    visibility: hidden;
                    transition: all 0.2s;
                    pointer-events: none;
                }

                .floating-btn-tooltip::after {
                    content: '';
                    position: absolute;
                    left: 100%;
                    top: 50%;
                    transform: translateY(-50%);
                    border: 6px solid transparent;
                    border-left-color: rgba(0, 0, 0, 0.8);
                }

                .attachment-floating-btn:hover .floating-btn-tooltip {
                    opacity: 1;
                    visibility: visible;
                }

                /* 响应式隐藏规则 */
                @media (min-width: 1300px) {
                    .attachment-floating-btn {
                        display: none !important;
                    }
                }

                @media (max-width: 1299px) {
                    #attachment-downloader-btn {
                        display: none !important;
                    }
                }
            `;
            document.head.appendChild(styles);
    }

    setupResponsiveListener() {
            // 防抖函数
            let resizeTimeout;
            const handleResize = () => {
                clearTimeout(resizeTimeout);
                resizeTimeout = setTimeout(() => {
                    const screenWidth = window.innerWidth;
                    const hasToolbarBtn = document.getElementById('attachment-downloader-btn');
                    const hasFloatingBtn = document.querySelector('.attachment-floating-btn');

                    if (screenWidth < 1300 && hasToolbarBtn && !hasFloatingBtn) {
                        // 需要切换到浮动按钮
                        this.createAndInjectButton();
                    } else if (screenWidth >= 1300 && !hasToolbarBtn && hasFloatingBtn) {
                        // 需要切换到工具栏按钮
                        this.createAndInjectButton();
                    }
                }, 200);
            };

            // 移除旧的监听器
            if (this.resizeListener) {
                window.removeEventListener('resize', this.resizeListener);
            }

            this.resizeListener = handleResize;
            window.addEventListener('resize', this.resizeListener);
    }

    setupButtonDuplicationCheck() {
        // 防止重复按钮的观察器
        if (this.buttonObserver) {
            this.buttonObserver.disconnect();
        }
        
        this.buttonObserver = new MutationObserver((mutations) => {
            // 避免在面板初始化过程中进行检查
            if (this.isLoading) {
                return;
            }
            
            const buttons = document.querySelectorAll('#attachment-downloader-btn, [data-attachment-manager-btn="true"], .attachment-floating-btn');
            if (buttons.length > 1) {
                console.log('[按钮检查] 发现重复按钮,数量:', buttons.length);
                // 保留第一个,移除其他的
                for (let i = 1; i < buttons.length; i++) {
                    console.log('[按钮检查] 移除重复按钮:', buttons[i]);
                    buttons[i].remove();
                }
            }
        });
        
        this.buttonObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
}

class QQMailDownloader {
    constructor() {
        this.sid = null;
        this.headers = this._getDefaultHeaders();
        this.attachmentManager = null;
    }

    _getDefaultHeaders() {
        return {
            'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
            'accept-language': 'en,zh-CN;q=0.9,zh;q=0.8',
            'priority': 'u=0, i',
            'sec-ch-ua': '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
            'sec-ch-ua-mobile': '?0',
            'sec-ch-ua-platform': '"Windows"',
            'sec-fetch-dest': 'iframe',
            'sec-fetch-mode': 'navigate',
            'sec-fetch-site': 'same-origin',
            'sec-fetch-user': '?1',
            'upgrade-insecure-requests': '1'
        };
    }

    async fetchAttachment(attachment) {
        try {
            // 构建初始URL
            const downloadUrlObj = new URL(attachment.download_url, MAIL_CONSTANTS.BASE_URL);
            const params = new URLSearchParams(downloadUrlObj.search);
            const pageUrl = new URL(window.location.href);
            const sid = pageUrl.searchParams.get('sid') || this.sid;
            const initialUrl = `${MAIL_CONSTANTS.BASE_URL}${MAIL_CONSTANTS.API_ENDPOINTS.ATTACH_DOWNLOAD}?mailid=${params.get('mailid')}&fileid=${params.get('fileid')}&name=${encodeURIComponent(attachment.name)}&sid=${sid}`;

            let finalDownloadUrl = null;

            try {
                // 尝试获取重定向URL
                finalDownloadUrl = await this._fetchRedirectUrl(initialUrl, attachment.name);
            } catch (redirectError) {

                // 回退方案1: 尝试直接使用原始下载URL
                if (attachment.download_url) {
                    let directUrl = attachment.download_url;
                    if (!directUrl.startsWith('http')) {
                        directUrl = MAIL_CONSTANTS.BASE_URL + directUrl;
                    }
                    // 确保包含SID参数
                    if (!directUrl.includes('sid=')) {
                        const separator = directUrl.includes('?') ? '&' : '?';
                        directUrl += `${separator}sid=${sid}`;
                    }
                    finalDownloadUrl = directUrl;
                } else {
                    // 回退方案2: 使用初始URL
                    finalDownloadUrl = initialUrl;
                }
            }

            // 获取文件内容
            const response = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: finalDownloadUrl,
                    headers: {
                        ...this.headers,
                        'Referer': MAIL_CONSTANTS.BASE_URL + '/'
                    },
                    responseType: 'blob',
                        onload: function (response) {
                        if (response.status === 200) {
                            resolve(response);
                        } else {
                            reject(new Error(`Failed to fetch content: ${response.status} ${response.statusText}`));
                        }
                    },
                        onerror: function (error) {
                        reject(error);
                    }
                });
            });

            return response;
        } catch (error) {
            throw error;
        }
    }

    async _fetchRedirectUrl(initialUrl, attachmentName) {
        try {
            const response = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: initialUrl,
                    headers: {
                        ...this.headers,
                        'Referer': MAIL_CONSTANTS.BASE_URL + '/'
                    },
                        onload: function (response) {
                        if (response.status === 200) {
                            resolve(response);
                        } else {
                            reject(new Error(`Failed to fetch redirect: ${response.status}`));
                        }
                    },
                        onerror: function (error) {
                        reject(error);
                    }
                });
            });

            // 方法1: 检查是否已经重定向到最终URL
            if (response.finalUrl && response.finalUrl !== initialUrl) {
                return response.finalUrl;
            }

            // 方法2: 从响应中提取JavaScript重定向URL
            const responseText = response.responseText;

            // 尝试多种JavaScript重定向模式
            const redirectPatterns = [
                /window\.location\.href\s*=\s*['"]([^'"]+)['"]/,
                /location\.href\s*=\s*['"]([^'"]+)['"]/,
                /window\.location\s*=\s*['"]([^'"]+)['"]/,
                /location\s*=\s*['"]([^'"]+)['"]/,
                /window\.location\.replace\(['"]([^'"]+)['"]\)/,
                /location\.replace\(['"]([^'"]+)['"]\)/,
                /document\.location\s*=\s*['"]([^'"]+)['"]/,
                /document\.location\.href\s*=\s*['"]([^'"]+)['"]/
            ];

            for (const pattern of redirectPatterns) {
                const match = responseText.match(pattern);
                if (match && match[1]) {
                    return match[1];
                }
            }

            // 方法3: 查找HTML meta refresh重定向
            const metaRefreshMatch = responseText.match(/<meta[^>]+http-equiv=['"]refresh['"][^>]+content=['"][^'"]*url=([^'"]+)['"]/i);
            if (metaRefreshMatch && metaRefreshMatch[1]) {
                return metaRefreshMatch[1];
            }

            // 方法4: 查找可能的下载链接
            const downloadLinkPatterns = [
                /href=['"]([^'"]*download[^'"]*)['"]/i,
                /url\(['"]([^'"]*download[^'"]*)['"]\)/i,
                /(https?:\/\/[^'">\s]+download[^'">\s]*)/i
            ];

            for (const pattern of downloadLinkPatterns) {
                const match = responseText.match(pattern);
                if (match && match[1]) {
                    return match[1];
                }
            }

            // 方法5: 如果响应是JSON格式,尝试解析
            try {
                const jsonData = JSON.parse(responseText);
                if (jsonData.url || jsonData.download_url || jsonData.redirect_url) {
                    const foundUrl = jsonData.url || jsonData.download_url || jsonData.redirect_url;
                    return foundUrl;
                }
            } catch (e) {
                // 不是JSON格式,继续其他方法
            }

            throw new Error(`No redirect URL found in response for ${attachmentName}`);

        } catch (error) {
            throw error;
        }
    }

    async init() {
        this.sid = this.getSid();
        if (!this.sid) {
            return;
        }

        // 等待页面加载完成
        if (document.readyState === 'loading') {
            await new Promise(resolve => {
                document.addEventListener('DOMContentLoaded', resolve);
            });
        }

        // 检查是否在登录(不可用)页面
        if (window.location.pathname.includes('/login')) {
            this.handleLoginPage();
            return;
        }

        // 处理主页面
        this.attachmentManager = new AttachmentManager(this);
        // 设置全局引用,供HTML中的onclick使用
        window.attachmentManager = this.attachmentManager;
    }

    handleLoginPage() {
        // 可以添加登录(不可用)页面的特定处理逻辑
    }

    handleMainPage() {
        // 初始化附件管理器
        this.attachmentManager = new AttachmentManager(this);
    }

    getSid() {
        // 优先从URL参数获取
        const urlParams = new URLSearchParams(window.location.search);
        let sid = urlParams.get('sid');
        if (sid) {
            return sid;
        }

        // 从URL hash中获取
        const hash = window.location.hash;
        const hashMatch = hash.match(/sid=([^&]+)/);
        if (hashMatch) {
            sid = hashMatch[1];
            return sid;
        }

        return '';
    }

    getCookie(name) {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
        return '';
    }

    cleanup() {
        if (this.manager) {
            if (this.manager.container) {
                this.manager.container.remove();
            }
            this.manager = null;
        }
        if (this.boundFolderChangeHandler) {
            window.removeEventListener('hashchange', this.boundFolderChangeHandler, false);
            this.boundFolderChangeHandler = null;
        }
        this.isLoggedIn = false;
        this.currentFolderId = null;
        this.sid = null;
    }

    getCurrentFolderId() {
        const hash = window.location.hash;
        const listMatch = hash.match(/\/list\/(\d+)/);
        return listMatch ? listMatch[1] : '1';
    }

    async fetchMailList(folderId, pageNow = 0, pageSize = 50) {
        const requestUrl = this.buildMailListUrl(folderId, pageNow, pageSize);

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: requestUrl,
                headers: this._getDefaultHeaders(),
                timeout: 15000,
                onload: (response) => this.handleMailListResponse(response, resolve, reject),
                onerror: () => reject(new Error('网络连接失败')),
                ontimeout: () => reject(new Error('请求超时'))
            });
        });
    }

    buildMailListUrl(folderId, pageNow, pageSize) {
        const params = new URLSearchParams({
            r: Date.now(),
            sid: this.sid,
            dir: folderId,
            page_now: pageNow,
            page_size: pageSize,
            sort_type: 1,
            sort_direction: 1,
            func: 1,
            tag: '',
            enable_topmail: true
        });
        return `${MAIL_CONSTANTS.BASE_URL}${MAIL_CONSTANTS.API_ENDPOINTS.MAIL_LIST}?${params}`;
    }

    handleMailListResponse(response, resolve, reject) {
        const statusErrorMap = {
            302: '需要重新登录(不可用)', 301: '需要重新登录(不可用)', 403: '权限被拒绝,请重新登录(不可用)'
        };

        if (response.status !== 200) {
            return reject(new Error(statusErrorMap[response.status] || `HTTP错误: ${response.status}`));
        }

        try {
            const data = JSON.parse(response.responseText);
            if (!data?.head) return reject(new Error('响应格式错误'));
            if (data.head.ret !== 0) return reject(new Error(`API错误: ${data.head.msg || data.head.ret}`));

            const result = data.body?.list ? {
                mails: data.body.list,
                total: data.body.total_num || data.body.list.length,
                unread: data.body.unread_num
            } : { mails: [], total: 0, unread: 0 };

            resolve(result);
        } catch {
            reject(new Error('响应不是有效的JSON格式'));
        }
    }

    async getAllMails(folderId) {
        const result = await this.fetchMailList(folderId, 0, 50);
                    return result.mails;
    }

    async getMailsWithPagination(folderId, maxPages = 5) {
        const allMails = [];
        let page = 0;
        const pageSize = 50;

        while (page < maxPages) {
            const result = await this.fetchMailList(folderId, page, pageSize);
            if (!result.mails?.length) break;

            allMails.push(...result.mails);
            if (result.mails.length < pageSize) break;
            
            page++;
            await new Promise(resolve => setTimeout(resolve, 100));
        }

        return allMails;
    }
}

// 初始化下载器
let downloader = null;
const observer = new MutationObserver(mutationCallback);

function initDownloader() {
        if (downloader?.attachmentManager) return;

        downloader = new QQMailDownloader();
        observer.disconnect();
        downloader.init();
}

function mutationCallback(mutationsList, obs) {
        if (document.querySelector('#mailMainApp') && !downloader?.attachmentManager) {
            initDownloader();
    }
}

function startObserver() {
        observer.observe(document.body, { childList: true, subtree: true });
}

(function initializeScript() {
    const initialize = () => {
        if (document.querySelector('#mailMainApp')) {
            initDownloader();
        } else {
            startObserver();
        }
    };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initialize, { once: true });
    } else {
        initialize();
    }
})();

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址