通知中心(优化窗口碰撞和移动体验)edit icon

Fork(复制)
下载
嵌入
设置
BUG反馈
index.html
现在支持上传本地图片了!
            
            <!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>优化版系统通知中心</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
        }

        body {
            min-height: 100vh;
            background: linear-gradient(135deg, #1a202c, #2d3748);
            color: #e2e8f0;
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: flex-start;
        }

        .container {
            width: 100%;
            max-width: 1200px;
            display: grid;
            grid-template-columns: 280px 1fr;
            gap: 30px;
        }

        header {
            grid-column: 1 / -1;
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .brand {
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .brand i {
            font-size: 1.8rem;
            color: #63b3ed;
        }

        .brand h1 {
            font-weight: 600;
            font-size: 1.5rem;
            letter-spacing: -0.5px;
        }

        .controls {
            display: flex;
            gap: 15px;
        }

        .btn {
            background: rgba(74, 85, 104, 0.6);
            backdrop-filter: blur(10px);
            padding: 8px 16px;
            border-radius: 8px;
            color: #e2e8f0;
            display: flex;
            align-items: center;
            gap: 8px;
            cursor: pointer;
            transition: all 0.2s ease;
            border: none;
            font-weight: 500;
        }

        .btn:hover {
            background: rgba(90, 103, 128, 0.8);
            transform: translateY(-2px);
        }

        .btn-primary {
            background: rgba(56, 178, 172, 0.8);
        }

        .btn-primary:hover {
            background: rgba(56, 178, 172, 1);
        }

        /* 通知中心样式 */
        .notification-center {
            background: rgba(30, 41, 59, 0.6);
            backdrop-filter: blur(20px);
            border-radius: 16px;
            padding: 20px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            border: 1px solid rgba(255, 255, 255, 0.08);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .notification-center-header {
            margin-bottom: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .section-title {
            font-size: 1.4rem;
            font-weight: 600;
        }

        .notification-list {
            display: flex;
            flex-direction: column;
            gap: 16px;
            max-height: 75vh;
            overflow-y: auto;
            padding: 5px;
            scrollbar-width: thin;
            scrollbar-color: rgba(113, 128, 150, 0.5) transparent;
        }

        .notification-list::-webkit-scrollbar {
            width: 8px;
        }

        .notification-list::-webkit-scrollbar-thumb {
            background: rgba(113, 128, 150, 0.5);
            border-radius: 4px;
        }

        .notification-list::-webkit-scrollbar-thumb:hover {
            background: rgba(113, 128, 150, 0.7);
        }

        .notification {
            background: rgba(26, 32, 44, 0.8);
            border-radius: 12px;
            padding: 18px;
            cursor: pointer;
            transition: all 0.2s ease;
            border: 1px solid rgba(74, 85, 104, 0.4);
            position: relative;
            overflow: hidden;
        }

        .notification::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 3px;
            height: 100%;
            background: #63b3ed;
            border-radius: 3px 0 0 3px;
        }

        .notification:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
            border-color: rgba(99, 179, 237, 0.4);
        }

        .notification-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .notification-icon {
            width: 40px;
            height: 40px;
            background: rgba(56, 178, 172, 0.15);
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #38b2ac;
        }

        .notification-title {
            font-weight: 600;
            font-size: 1.1rem;
            margin-bottom: 5px;
        }

        .notification-time {
            color: #a0aec0;
            font-size: 0.85rem;
        }

        .notification-content {
            color: #cbd5e0;
            line-height: 1.5;
            font-size: 0.95rem;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }

        .notification-unread::after {
            content: '';
            position: absolute;
            top: 18px;
            right: 18px;
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: #63b3ed;
            animation: pulse 1.5s infinite;
        }

        @keyframes pulse {
            0% { opacity: 0.8; }
            50% { opacity: 0.3; }
            100% { opacity: 0.8; }
        }

        .notification-type {
            display: inline-block;
            padding: 3px 8px;
            font-size: 0.8rem;
            border-radius: 6px;
            background: rgba(99, 179, 237, 0.15);
            color: #90cdf4;
            margin-top: 10px;
        }

        /* 详情弹窗 */
        .notification-detail {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(30, 41, 59, 0.95);
            backdrop-filter: blur(25px);
            width: 500px;
            max-width: 90%;
            border-radius: 16px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
            z-index: 100;
            overflow: hidden;
            border: 1px solid rgba(255, 255, 255, 0.1);
            display: none;
            opacity: 0;
            transition: opacity 0.3s ease;
        }

        .notification-detail.draggable .detail-header {
            cursor: move;
        }

        .notification-detail.dragging .detail-header {
            cursor: grabbing;
        }

        .detail-header {
            padding: 20px;
            background: rgba(23, 32, 49, 0.8);
            border-bottom: 1px solid rgba(74, 85, 104, 0.4);
            user-select: none;
        }

        .detail-title {
            font-size: 1.3rem;
            font-weight: 600;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .detail-title i {
            color: #63b3ed;
        }

        .detail-content {
            padding: 25px;
            max-height: 50vh;
            overflow-y: auto;
            scrollbar-width: thin;
            scrollbar-color: rgba(113, 128, 150, 0.5) transparent;
        }

        .detail-content::-webkit-scrollbar {
            width: 8px;
        }

        .detail-content::-webkit-scrollbar-thumb {
            background: rgba(113, 128, 150, 0.5);
            border-radius: 4px;
        }

        .detail-content p {
            line-height: 1.7;
            margin-bottom: 15px;
            color: #e2e8f0;
        }

        .detail-footer {
            padding: 15px 25px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            background: rgba(23, 32, 49, 0.8);
            border-top: 1px solid rgba(74, 85, 104, 0.4);
        }

        .detail-time {
            font-size: 0.9rem;
            color: #a0aec0;
        }

        .close-btn {
            background: rgba(74, 85, 104, 0.4);
            padding: 8px 16px;
            border-radius: 8px;
            color: #e2e8f0;
            border: none;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .close-btn:hover {
            background: rgba(90, 103, 128, 0.6);
        }

        .sent-by {
            display: flex;
            align-items: center;
            gap: 10px;
            margin: 20px 0;
            background: rgba(26, 32, 44, 0.6);
            padding: 12px 15px;
            border-radius: 10px;
        }

        .avatar {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            background: linear-gradient(45deg, #38b2ac, #4299e1);
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: 600;
        }

        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            backdrop-filter: blur(5px);
            z-index: 99;
            display: none;
        }

        .empty-message {
            text-align: center;
            color: #a0aec0;
            padding: 40px 20px;
            font-size: 0.95rem;
        }

        .feedback {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            padding: 12px 24px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            z-index: 1000;
            animation: fadeInOut 2.5s forwards;
        }

        @keyframes fadeInOut {
            0% { opacity: 0; transform: translateX(-50%) translateY(-20px); }
            15% { opacity: 1; transform: translateX(-50%) translateY(0); }
            85% { opacity: 1; transform: translateX(-50%) translateY(0); }
            100% { opacity: 0; transform: translateX(-50%) translateY(-20px); }
        }

        @media (max-width: 768px) {
            .container {
                grid-template-columns: 1fr;
            }
            
            .notification-center {
                padding: 15px;
            }
            
            .controls {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <div class="brand">
                <i class="fas fa-bell"></i>
                <h1>系统通知中心</h1>
            </div>
            <div class="controls">
                <button class="btn" id="mark-read">
                    <i class="fas fa-check-circle"></i> 全部已读
                </button>
                <button class="btn btn-primary" id="new-notification">
                    <i class="fas fa-plus"></i> 添加通知
                </button>
            </div>
        </header>
        
        <section class="notification-center">
            <div class="notification-center-header">
                <h2 class="section-title">未读通知</h2>
                <span class="notification-count">3 条未读</span>
            </div>
            <div class="notification-list" id="unread-list">
              
            </div>
        </section>
        
        <section class="notification-center">
            <div class="notification-center-header">
                <h2 class="section-title">历史通知</h2>
            </div>
            <div class="notification-list" id="history-list">
                <!-- 历史通知将通过JS动态生成 -->
            </div>
        </section>
    </div>
    
    <!-- 通知详情弹窗 -->
    <div class="notification-detail" id="detail-window">
        <div class="detail-header">
            <h3 class="detail-title">
                <i class="fas fa-info-circle"></i>
                <span id="detail-title">通知标题</span>
            </h3>
        </div>
        <div class="detail-content">
            <div class="sent-by">
                <div class="avatar">JS</div>
                <div>
                    <div>发送者:系统通知服务</div>
                    <div class="detail-time" id="detail-send-time">2023年10月15日 14:30</div>
                </div>
            </div>
            <p id="detail-message">通知消息内容...</p>
            <p>这是通知的详细内容。当您收到重要系统更新、安全提醒或需要关注的任务时,我们会在这里通知您。</p>
            <p>您可以看到现在窗口有边缘检测功能:当靠近浏览器窗口边缘时,窗口会自动调整位置保持在视口中。</p>
            <p>调整浏览器窗口大小会实时更新位置,确保窗口始终可见。</p>
        </div>
        <div class="detail-footer">
            <div class="detail-time">ID: NS-2023-0012</div>
            <button class="close-btn" id="close-detail">关闭</button>
        </div>
    </div>
    
    <div class="overlay" id="overlay"></div>
    
    <script>
        // 模拟通知数据
        const notifications = [
            {
                id: 1,
                title: "系统更新可用",
                content: "新版本的系统更新已经可用,请安装以获取最新功能和安全更新。安装过程需要约10分钟,请确保您的设备已接通电源。",
                time: "2 分钟前",
                datetime: "2023-10-25T11:25:15",
                icon: "fas fa-sync-alt",
                color: "#63b3ed",
                type: "更新",
                unread: true,
                category: "unread",
                sender: "系统更新服务",
                details: "更新版本号:v2.4.3。包括性能改进和安全补丁,修复了上一版本中的内存泄漏问题,建议所有用户尽快更新。"
            },
            {
                id: 2,
                title: "安全扫描完成",
                content: "系统安全扫描已完成,未检测到威胁。所有核心系统文件均通过验证,系统状态良好。",
                time: "1 小时前",
                datetime: "2023-10-25T10:20:45",
                icon: "fas fa-shield-alt",
                color: "#48bb78",
                type: "安全",
                unread: true,
                category: "unread",
                sender: "安全防护中心",
                details: "本次扫描范围包括系统文件、第三方应用程序和注册表项。未发现恶意软件或可疑活动。扫描耗时12分45秒,覆盖了系统关键区域。"
            },
            {
                id: 3,
                title: "存储空间不足",
                content: "系统盘仅剩 5.2GB 可用空间,请清理一些文件以释放空间。长期低存储空间可能导致系统性能下降。",
                time: "3 小时前",
                datetime: "2023-10-25T08:45:22",
                icon: "fas fa-hdd",
                color: "#ed64a6",
                type: "警告",
                unread: true,
                category: "unread",
                sender: "存储管理系统",
                details: "您的系统分区(C:)剩余空间不足总容量的10%。大文件:下载文件夹(1.8GB), 应用程序缓存(1.2GB)。建议使用磁盘清理工具或删除不再需要的文件。"
            },
            {
                id: 4,
                title: "备份已完成",
                content: "系统自动备份已完成。耗时 25 分钟,所有用户文件和系统设置都已安全存储在备份设备中。",
                time: "昨天",
                datetime: "2023-10-24T17:30:10",
                icon: "fas fa-save",
                color: "#9f7aea",
                type: "任务",
                unread: false,
                category: "history",
                sender: "备份服务",
                details: "本次备份成功归档了256个文件和7个系统配置项。总备份大小: 3.7GB。使用增量备份技术节省了35%的存储空间。备份位置:D:\\Backup\\System\\2023-10-24。"
            },
            {
                id: 5,
                title: "登录通知",
                content: "您的帐户在 2023年10月24日 13:45 从新的设备登录。如非本人操作,请立即更改密码。",
                time: "2023年10月24日",
                datetime: "2023-10-24T13:45:20",
                icon: "fas fa-user-check",
                color: "#f6ad55",
                type: "安全",
                unread: false,
                category: "history",
                sender: "账户安全服务",
                details: "登录设备型号:Samsung SM-G998U (Android 13)。位置:上海市。如果这并非您本人的设备,建议您检查账户安全设置并更新密码。"
            }
        ];

        // 初始化
        document.addEventListener('DOMContentLoaded', () => {
            renderNotifications();
            setupEventListeners();
            
            // 添加窗口大小变化监听
            window.addEventListener('resize', handleWindowResize);
        });
        
        // 当前弹窗位置状态
        let currentPosition = {
            x: 0,
            y: 0,
            visible: false
        };

        // 渲染通知列表
        function renderNotifications() {
            const unreadList = document.getElementById('unread-list');
            const historyList = document.getElementById('history-list');
            
            // 过滤通知
            const unreadNotifications = notifications.filter(n => n.category === 'unread');
            const historyNotifications = notifications.filter(n => n.category === 'history');
            
            // 更新未读计数
            const unreadCount = unreadNotifications.filter(n => n.unread).length;
            document.querySelector('.notification-count').textContent = `${unreadCount} 条未读`;
            
            // 清空列表并添加内容
            renderList(unreadList, unreadNotifications, '无未读通知');
            renderList(historyList, historyNotifications, '无历史通知');
        }
        
        function renderList(listElement, notifications, emptyMessage) {
            // 清空列表
            listElement.innerHTML = '';
            
            if (notifications.length === 0) {
                // 添加空状态提示
                const emptyMsg = document.createElement('div');
                emptyMsg.className = 'empty-message';
                emptyMsg.textContent = emptyMessage;
                listElement.appendChild(emptyMsg);
                return;
            }
            
            // 添加通知到列表
            notifications.forEach(notification => {
                const element = createNotificationElement(notification);
                listElement.appendChild(element);
            });
        }

        // 创建单个通知DOM元素
        function createNotificationElement(notification) {
            const element = document.createElement('div');
            element.className = `notification ${notification.unread ? 'notification-unread' : ''}`;
            element.dataset.id = notification.id;
            
            element.innerHTML = `
                <div class="notification-header">
                    <div>
                        <div class="notification-title">${notification.title}</div>
                        <div class="notification-time">${notification.time}</div>
                    </div>
                    <div class="notification-icon" style="background: rgba(${hexToRgb(notification.color)}, 0.15); color: ${notification.color}">
                        <i class="${notification.icon}"></i>
                    </div>
                </div>
                <div class="notification-content">
                    ${notification.content}
                </div>
                <div class="notification-type">${notification.type}</div>
            `;
            
            return element;
        }

        // HEX颜色转RGB
        function hexToRgb(hex) {
            const r = parseInt(hex.slice(1, 3), 16);
            const g = parseInt(hex.slice(3, 5), 16);
            const b = parseInt(hex.slice(5, 7), 16);
            return `${r}, ${g}, ${b}`;
        }

        // 设置事件监听
        function setupEventListeners() {
            // 点击通知显示详情
            document.addEventListener('click', (e) => {
                const notification = e.target.closest('.notification');
                if (notification) {
                    const id = parseInt(notification.dataset.id);
                    const notificationData = notifications.find(n => n.id === id);
                    if (notificationData) {
                        showNotificationDetail(notificationData);
                        
                        // 标记为已读
                        if (notificationData.unread) {
                            notificationData.unread = false;
                            notification.classList.remove('notification-unread');
                            renderNotifications();
                        }
                    }
                }
            });

            // 关闭详情窗口
            document.getElementById('close-detail').addEventListener('click', closeDetailWindow);
            
            // 点击遮罩关闭窗口
            document.getElementById('overlay').addEventListener('click', closeDetailWindow);
            
            // 添加新通知
            document.getElementById('new-notification').addEventListener('click', addNewNotification);
            
            // 全部已读
            document.getElementById('mark-read').addEventListener('click', markAllAsRead);
            
            // 设置拖拽功能
            setupDraggable();
            
            // 按ESC关闭详情窗口
            document.addEventListener('keydown', (e) => {
                if (e.key === 'Escape') {
                    closeDetailWindow();
                }
            });
        }

        // 添加新通知
        function addNewNotification() {
            const titles = [
                '系统优化完成',
                '新应用可用',
                '账户安全提示',
                '性能报告就绪',
                '连接新设备'
            ];
            
            const contents = [
                '系统已完成后台优化,您的设备运行更快更流畅。',
                '您订阅的应用程序现已可供下载安装。',
                '检测到异常登录活动,请确认是否为本人操作。',
                '月度系统性能报告已生成,请查看详细信息。',
                '发现一个新设备连接到您的网络,请确认身份。'
            ];
            
            const icons = [
                'fas fa-rocket', 
                'fas fa-download', 
                'fas fa-exclamation-triangle',
                'fas fa-chart-line',
                'fas fa-wifi'
            ];
            
            const colors = [
                '#63b3ed',
                '#9f7aea',
                '#f6ad55',
                '#48bb78',
                '#ed64a6'
            ];
            
            const randomIndex = Math.floor(Math.random() * titles.length);
            
            const newNotification = {
                id: Date.now(), // 使用时间戳作为唯一ID
                title: titles[randomIndex],
                content: contents[randomIndex],
                time: "刚刚",
                datetime: new Date().toISOString(),
                icon: icons[randomIndex],
                color: colors[randomIndex],
                type: ["信息", "提示", "警告"][Math.floor(Math.random() * 3)],
                unread: true,
                category: "unread",
                sender: "系统通知服务",
                details: '这是系统自动生成的通知内容。您可以在此通知详情中查看更具体的信息。此功能用于测试系统通知系统的各项功能。'
            };
            
            notifications.unshift(newNotification);
            renderNotifications();
            
            // 显示操作反馈
            showFeedback(`已成功添加新通知: ${titles[randomIndex]}`, '#38b2ac');
        }

        // 全部标记为已读
        function markAllAsRead() {
            notifications.forEach(n => {
                if (n.category === 'unread') {
                    n.unread = false;
                }
            });
            renderNotifications();
            
            // 显示操作反馈
            showFeedback('所有通知已标记为已读', '#48bb78');
        }
        
        function showFeedback(message, color) {
            // 移除已有的反馈
            const existingFeedback = document.querySelector('.feedback');
            if (existingFeedback) {
                existingFeedback.remove();
            }
            
            const feedback = document.createElement('div');
            feedback.textContent = message;
            feedback.className = 'feedback';
            feedback.style.background = color;
            
            document.body.appendChild(feedback);
            
            setTimeout(() => {
                if (document.body.contains(feedback)) {
                    document.body.removeChild(feedback);
                }
            }, 2500);
        }

        // 显示通知详情
        function showNotificationDetail(notification) {
            const detailWindow = document.getElementById('detail-window');
            const overlay = document.getElementById('overlay');
            const title = document.getElementById('detail-title');
            const sendTime = document.getElementById('detail-send-time');
            const message = document.getElementById('detail-message');
            
            title.textContent = notification.title;
            
            // 格式化日期
            const date = new Date(notification.datetime);
            const formattedDate = date.toLocaleDateString('zh-CN', { 
                year: 'numeric', 
                month: 'long', 
                day: 'numeric',
                hour: '2-digit',
                minute: '2-digit'
            });
            sendTime.textContent = formattedDate;
            
            message.textContent = notification.content;
            
            // 更新详情内容
            const detailContent = document.querySelector('.detail-content');
            if (notification.details) {
                const contentParagraphs = detailContent.querySelectorAll('p:nth-child(n+2)');
                if (contentParagraphs[1]) {
                    contentParagraphs[1].textContent = notification.details;
                }
            }
            
            // 更新发送者
            if (notification.sender) {
                const senderElement = document.querySelector('.sent-by div > div:first-child');
                if (senderElement) {
                    senderElement.textContent = `发送者: ${notification.sender}`;
                }
            }
            
            // 显示窗口并居中
            detailWindow.style.display = 'block';
            overlay.style.display = 'block';
            
            // 重置窗口位置状态
            currentPosition.visible = true;
            
            // 居中显示
            positionCenter(detailWindow);
            
            // 动画效果
            setTimeout(() => {
                detailWindow.style.opacity = 1;
                overlay.style.opacity = 1;
            }, 10);
        }

        // 居中定位
        function positionCenter(element) {
            const elementRect = element.getBoundingClientRect();
            const x = Math.max(20, Math.min(window.innerWidth - elementRect.width - 20, (window.innerWidth - elementRect.width) / 2));
            const y = Math.max(20, Math.min(window.innerHeight - elementRect.height - 20, (window.innerHeight - elementRect.height) / 2));
            
            element.style.left = `${x}px`;
            element.style.top = `${y}px`;
            element.style.transform = 'none';
            
            // 保存当前窗口位置
            currentPosition = { 
                x: x, 
                y: y,
                visible: true
            };
        }
        
        // 窗口大小变化处理
        function handleWindowResize() {
            if (currentPosition.visible) {
                const detailWindow = document.getElementById('detail-window');
                
                if (detailWindow.style.display === 'block') {
                    // 重新调整位置
                    adjustWindowPosition();
                }
            }
        }
        
        // 调整窗口位置以确保在视口内
        function adjustWindowPosition() {
            const detailWindow = document.getElementById('detail-window');
            const elementRect = detailWindow.getBoundingClientRect();
            const viewportWidth = window.innerWidth;
            const viewportHeight = window.innerHeight;
            
            // 边界安全距离(10px)
            const margin = 10;
            
            // 计算新位置,确保在边界内
            let newX = currentPosition.x;
            let newY = currentPosition.y;
            
            // 右侧边界检查
            if (currentPosition.x + elementRect.width > viewportWidth - margin) {
                newX = viewportWidth - elementRect.width - margin;
            }
            
            // 左侧边界检查
            if (currentPosition.x < margin) {
                newX = margin;
            }
            
            // 底部边界检查
            if (currentPosition.y + elementRect.height > viewportHeight - margin) {
                newY = viewportHeight - elementRect.height - margin;
            }
            
            // 顶部边界检查
            if (currentPosition.y < margin) {
                newY = margin;
            }
            
            // 应用新位置
            detailWindow.style.left = `${newX}px`;
            detailWindow.style.top = `${newY}px`;
            detailWindow.style.transform = 'none';
            
            // 更新当前位置
            currentPosition.x = newX;
            currentPosition.y = newY;
        }

        // 关闭详情窗口
        function closeDetailWindow() {
            const detailWindow = document.getElementById('detail-window');
            const overlay = document.getElementById('overlay');
            
            detailWindow.style.opacity = 0;
            overlay.style.opacity = 0;
            
            setTimeout(() => {
                detailWindow.style.display = 'none';
                overlay.style.display = 'none';
                
                // 更新状态
                currentPosition.visible = false;
            }, 300);
        }

        // 设置窗口拖拽功能
        function setupDraggable() {
            const detailWindow = document.getElementById('detail-window');
            const header = document.querySelector('.detail-header');
            
            let isDragging = false;
            let currentX, currentY;
            let initialX, initialY;
            let xOffset = 0, yOffset = 0;
            
            // 开始拖拽
            header.addEventListener('mousedown', (e) => {
                if (e.target.closest('.detail-header')) {
                    detailWindow.classList.add('draggable');
                    isDragging = true;
                    
                    // 获取初始鼠标位置
                    initialX = e.clientX - currentPosition.x;
                    initialY = e.clientY - currentPosition.y;
                    
                    // 应用拖拽样式
                    detailWindow.style.transition = 'none';
                    detailWindow.classList.add('dragging');
                    
                    e.preventDefault();
                }
            });
            
            // 拖拽中
            document.addEventListener('mousemove', (e) => {
                if (isDragging) {
                    // 计算新位置
                    currentX = e.clientX - initialX;
                    currentY = e.clientY - initialY;
                    
                    // 边界检查 (带有10px安全距离)
                    const windowRect = detailWindow.getBoundingClientRect();
                    const viewportWidth = window.innerWidth;
                    const viewportHeight = window.innerHeight;
                    
                    // 确保窗口不会移出屏幕 (带有安全距离)
                    if (currentX < 10) currentX = 10;
                    if (currentY < 10) currentY = 10;
                    if (currentX > viewportWidth - windowRect.width - 10) 
                        currentX = viewportWidth - windowRect.width - 10;
                    if (currentY > viewportHeight - windowRect.height - 20) 
                        currentY = viewportHeight - windowRect.height - 20;
                    
                    // 更新偏移量
                    xOffset = currentX;
                    yOffset = currentY;
                    
                    // 保存当前位置
                    currentPosition.x = currentX;
                    currentPosition.y = currentY;
                    
                    // 应用新位置
                    detailWindow.style.left = `${currentX}px`;
                    detailWindow.style.top = `${currentY}px`;
                    detailWindow.style.transform = 'none';
                }
            });
            
            // 停止拖拽
            document.addEventListener('mouseup', () => {
                if (isDragging) {
                    isDragging = false;
                    
                    // 移除拖拽样式
                    detailWindow.classList.remove('draggable');
                    detailWindow.classList.remove('dragging');
                    
                    // 恢复动画效果
                    detailWindow.style.transition = '';
                }
            });
        }
    </script>
</body>
</html>
        
预览
控制台