windows经典页面

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FaLayoutContainer 复刻 - 顶部/底部显隐 + 半圆三角按钮</title>
    <style>
        /* ============================================================
   一、设计变量(沿用 Fantastic-admin 风格)
   ============================================================ */
        :root {
            --g-app-bg: #ffffff;
            --g-main-bg: #f5f7f9;
            --g-border-color: #ebeef5;
            --g-title-color: #1f2d3d;
            --g-text-color: #5a5e66;
            --g-icon-color: #8c939d;
            --g-primary: #409eff;
            --g-radius: 8px;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
            height: 100vh;
            overflow: hidden;
            background: linear-gradient(135deg, #1e3c72, #2a5298, #4b6cb7);
        }

        /* ============================================================
   二、窗口外壳(保持)
   ============================================================ */
        .window-shell {
            position: absolute;
            width: 90vw;
            max-width: 1400px;
            height: 85vh;
            max-height: 900px;
            min-width: 720px;
            min-height: 500px;
            background: var(--g-app-bg);
            border-radius: 10px;
            box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1), 0 20px 60px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.25);
            display: flex;
            flex-direction: column;
            overflow: hidden;
            transition: opacity .3s, transform .3s, border-radius .3s, width .3s, height .3s, left .3s, top .3s;
        }

        .window-shell.is-maximized {
            left: 0 !important;
            top: 0 !important;
            width: 100vw !important;
            height: 100vh !important;
            max-width: none;
            max-height: none;
            border-radius: 0;
        }

        .window-shell.is-minimized {
            left: 16px !important;
            top: auto !important;
            bottom: 16px !important;
            width: 260px !important;
            height: 40px !important;
            min-width: 0;
            min-height: 0;
            border-radius: 8px;
            cursor: pointer;
        }

        .window-shell.is-minimized .window-body,
        .window-shell.is-minimized .window-controls {
            display: none;
        }

        .window-shell.is-minimized .window-titlebar {
            cursor: pointer;
            justify-content: center;
        }

        .window-shell.is-closed {
            opacity: 0;
            transform: scale(.92);
            pointer-events: none;
        }

        .window-titlebar {
            flex-shrink: 0;
            height: 40px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            background: #f3f3f3;
            border-bottom: 1px solid #e5e5e5;
            padding-left: 12px;
            user-select: none;
            cursor: default;
        }

        .window-title {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 13px;
            font-weight: 500;
            color: #303133;
        }

        .window-title .app-icon {
            width: 16px;
            height: 16px;
        }

        .window-controls {
            display: flex;
            height: 100%;
        }

        .win-btn {
            width: 46px;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            background: transparent;
            border: none;
            cursor: pointer;
            color: #333;
            transition: background .15s, color .15s;
        }

        .win-btn:hover {
            background: #e5e5e5;
        }

        .win-btn.close:hover {
            background: #e81123;
            color: #fff;
        }

        .win-btn svg {
            width: 10px;
            height: 10px;
        }

        .window-body {
            flex: 1;
            min-height: 0;
            display: flex;
            flex-direction: column;
            padding: 12px;
            background: var(--g-main-bg);
            overflow: hidden;
        }

        /* ============================================================
   三、核心布局:官方 FaLayoutContainer 结构(左 | 中(上中下) | 右)
   ============================================================ */
        .layout-container {
            display: flex;
            flex: 1;
            min-height: 0;
            border: 1px solid var(--g-border-color);
            border-radius: var(--g-radius);
            overflow: hidden;
            background: var(--g-app-bg);
            --left-width: 240px;
            --right-width: 240px;
            --top-height: 44px;
            --bottom-height: 44px;
        }

        /* ---- 所有面板/区域的基础样式 ---- */
        .side-panel,
        .main-content,
        .inner-panel,
        .top-bar,
        .bottom-bar {
            background: var(--g-app-bg);
            border: none;
            position: relative;
            display: flex;
            flex-direction: column;
            overflow: visible;
            transition: width .3s cubic-bezier(.4, 0, .2, 1), height .3s cubic-bezier(.4, 0, .2, 1),
                opacity .3s;
        }

        /* ---- 左侧面板 ---- */
        .left-panel {
            width: var(--left-width);
            flex-shrink: 0;
            border-right: 1px solid var(--g-border-color);
        }

        /* ---- 右侧面板 ---- */
        .right-panel {
            width: var(--right-width);
            flex-shrink: 0;
            border-left: 1px solid var(--g-border-color);
        }

        /* ---- 中部区域:垂直排列(上 | 中 | 下) ---- */
        .middle-area {
            flex: 1;
            min-width: 0;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        /* 顶部栏 */
        .top-bar {
            height: var(--top-height);
            flex-shrink: 0;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 14px;
            border-bottom: 1px solid var(--g-border-color);
            position: relative;
        }

        .top-bar .bar-title {
            font-size: 13px;
            font-weight: 600;
            color: var(--g-title-color);
            display: flex;
            align-items: center;
            gap: 8px;
        }

        /* 中部主内容区(官方 default slot) */
        .main-content {
            flex: 1;
            min-height: 0;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        /* 底部栏 */
        .bottom-bar {
            height: var(--bottom-height);
            flex-shrink: 0;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 0 14px;
            border-top: 1px solid var(--g-border-color);
            position: relative;
        }

        .bottom-bar .bar-title {
            font-size: 13px;
            font-weight: 600;
            color: var(--g-title-color);
            display: flex;
            align-items: center;
            gap: 8px;
        }

        /* ---- 中部内容排版 ---- */
        .panel-header {
            padding: 10px 14px;
            font-weight: 600;
            font-size: 13px;
            border-bottom: 1px solid var(--g-border-color);
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-shrink: 0;
            color: var(--g-title-color);
        }

        .panel-body {
            padding: 14px;
            overflow-y: auto;
            font-size: 13px;
            line-height: 1.7;
            color: var(--g-text-color);
            flex: 1;
        }

        .panel-body ul {
            list-style: none;
            padding: 0;
        }

        .panel-body li {
            padding: 6px 10px;
            border-radius: 6px;
            display: flex;
            align-items: center;
            gap: 8px;
            cursor: default;
        }

        .panel-body li:hover {
            background: var(--g-main-bg);
        }

        .panel-body li svg {
            width: 16px;
            height: 16px;
            color: var(--g-icon-color);
            flex-shrink: 0;
        }

        /* ============================================================
   四、显隐控制(核心修复:顶部/底部栏的独立显隐)
   ============================================================ */

        /* 所有边栏通用的隐藏态:高度/宽度归零,内部内容隐藏,但保留按钮 */
        .side-panel.is-hidden .panel-header,
        .side-panel.is-hidden .panel-body,
        .side-panel.is-hidden .resize-handle {
            display: none !important;
        }

        /* 左侧/右侧面板隐藏:宽度归零 */
        .left-panel.is-hidden,
        .right-panel.is-hidden {
            width: 0 !important;
            min-width: 0;
            padding: 0;
            overflow: visible;
        }

        /* 顶部/底部栏隐藏:高度归零(官方行为) */
        .top-bar.is-hidden,
        .bottom-bar.is-hidden {
            height: 0 !important;
            min-height: 0;
            padding: 0;
            border: none !important;
            overflow: visible;
        }

        .top-bar.is-hidden .bar-title,
        .bottom-bar.is-hidden .bar-title {
            display: none !important;
        }

        /* 折叠后去掉面板自身的边框,避免残留线条 */
        .left-panel.is-hidden {
            border-right: none !important;
        }

        .right-panel.is-hidden {
            border-left: none !important;
        }

        /* ============================================================
   五、折叠按钮(半圆 + 三角,贴合边缘)
   -------------------------------------------------------------
   设计思路(贴近官方):
   - 按钮是边线的一部分,而非浮起的独立卡片。
   - 使用 SVG 绘制“半圆 + 三角”组合图标。
   - 直边无边框,白色背景与面板无缝融合。
   - 圆弧侧有 1px 边框,颜色与边线一致。
   ============================================================ */
        .toggle-btn {
            position: absolute;
            z-index: 40;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0;
            outline: none;
            cursor: pointer;
            background: var(--g-app-bg);
            border: 1px solid var(--g-border-color);
            transition: width .22s cubic-bezier(.4, 0, .2, 1),
                height .22s cubic-bezier(.4, 0, .2, 1),
                background .2s, border-color .2s, box-shadow .2s;
        }

        /* 扩大点击热区(不显示) */
        .toggle-btn::after {
            content: '';
            position: absolute;
            top: -4px;
            bottom: -4px;
            left: -6px;
            right: -6px;
        }

        .toggle-btn svg {
            width: 14px;
            height: 14px;
            flex-shrink: 0;
            color: var(--g-icon-color);
            opacity: .35;
            transition: opacity .15s, color .15s, transform .3s;
        }

        /* ---- 按钮位置与形状(水平模式) ---- */

        /* 左侧面板按钮:贴在右边界,圆弧向右鼓出 */
        .left-panel .toggle-btn {
            left: 100%;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 12px;
            height: 44px;
            border-radius: 0 22px 22px 0;
            border-left: none;
        }

        .left-panel .toggle-btn:hover,
        .left-panel.is-hidden .toggle-btn {
            width: 20px;
        }

        /* 右侧面板按钮:贴在左边界,圆弧向左鼓出 */
        .right-panel .toggle-btn {
            left: 0;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 12px;
            height: 44px;
            border-radius: 22px 0 0 22px;
            border-right: none;
        }

        .right-panel .toggle-btn:hover,
        .right-panel.is-hidden .toggle-btn {
            width: 20px;
        }

        /* 顶部栏按钮:贴在下边界,圆弧向下鼓出 */
        .top-bar .toggle-btn {
            left: 50%;
            top: 100%;
            transform: translate(-50%, -50%);
            width: 44px;
            height: 12px;
            border-radius: 0 0 22px 22px;
            border-top: none;
        }

        .top-bar .toggle-btn:hover,
        .top-bar.is-hidden .toggle-btn {
            height: 20px;
        }

        /* 底部栏按钮:贴在上边界,圆弧向上鼓出 */
        .bottom-bar .toggle-btn {
            left: 50%;
            top: 0;
            transform: translate(-50%, -50%);
            width: 44px;
            height: 12px;
            border-radius: 22px 22px 0 0;
            border-bottom: none;
        }

        .bottom-bar .toggle-btn:hover,
        .bottom-bar.is-hidden .toggle-btn {
            height: 20px;
        }

        /* ---- 悬停与折叠态的颜色反馈 ---- */
        .toggle-btn:hover,
        .side-panel.is-hidden .toggle-btn,
        .top-bar.is-hidden .toggle-btn,
        .bottom-bar.is-hidden .toggle-btn {
            background: var(--g-primary);
            border-color: var(--g-primary);
            box-shadow: 0 2px 8px rgba(64, 158, 255, .35);
        }

        .toggle-btn:hover svg,
        .side-panel.is-hidden .toggle-btn svg,
        .top-bar.is-hidden .toggle-btn svg,
        .bottom-bar.is-hidden .toggle-btn svg {
            opacity: 1;
            color: #fff;
        }

        /* ---- 折叠后图标翻转 ---- */
        .side-panel.is-hidden .toggle-btn svg,
        .top-bar.is-hidden .toggle-btn svg,
        .bottom-bar.is-hidden .toggle-btn svg {
            transform: rotate(180deg);
        }

        /* ============================================================
   六、拖拽手柄
   ============================================================ */
        .resize-handle {
            position: absolute;
            z-index: 20;
            background: transparent;
            transition: background .2s;
        }

        .resize-handle:hover,
        .resize-handle.active {
            background: var(--g-primary);
        }

        .left-panel .resize-handle {
            right: -2px;
            top: 0;
            bottom: 0;
            width: 5px;
            cursor: col-resize;
        }

        .right-panel .resize-handle {
            left: -2px;
            top: 0;
            bottom: 0;
            width: 5px;
            cursor: col-resize;
        }

        /* ============================================================
   七、重新打开按钮
   ============================================================ */
        .reopen-btn {
            position: fixed;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            padding: 12px 28px;
            background: #fff;
            border: 1px solid var(--g-border-color);
            border-radius: 8px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, .3);
            font-size: 14px;
            color: var(--g-primary);
            cursor: pointer;
            display: none;
            align-items: center;
            gap: 8px;
            z-index: 9999;
        }

        .reopen-btn:hover {
            background: #ecf5ff;
            border-color: var(--g-primary);
        }
    </style>
</head>

<body>

    <div class="window-shell" id="windowShell">
        <div class="window-titlebar" id="windowTitlebar">
            <div class="window-title">
                <svg class="app-icon" viewBox="0 0 16 16">
                    <rect x="1" y="1" width="6" height="6" rx="1" fill="#409eff" />
                    <rect x="9" y="1" width="6" height="6" rx="1" fill="#67c23a" />
                    <rect x="1" y="9" width="6" height="6" rx="1" fill="#e6a23c" />
                    <rect x="9" y="9" width="6" height="6" rx="1" fill="#f56c6c" />
                </svg>
                <span>FaLayoutContainer — 顶部/底部显隐 + 半圆三角按钮</span>
            </div>
            <div class="window-controls">
                <button class="win-btn" id="btnMinimize"><svg viewBox="0 0 10 10">
                        <path d="M0 5h10" stroke="currentColor" />
                    </svg></button>
                <button class="win-btn" id="btnMaximize"><svg viewBox="0 0 10 10">
                        <rect x=".5" y=".5" width="9" height="9" fill="none" stroke="currentColor" />
                    </svg></button>
                <button class="win-btn close" id="btnClose"><svg viewBox="0 0 10 10">
                        <path d="M0 0l10 10M10 0L0 10" stroke="currentColor" />
                    </svg></button>
            </div>
        </div>

        <div class="window-body">
            <div class="layout-container" id="layoutContainer">

                <!-- ================= 左侧面板(导航) ================= -->
                <aside class="side-panel left-panel" id="leftPanel">
                    <div class="panel-header"><span>导航</span></div>
                    <div class="panel-body">
                        <ul>
                            <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                                    <rect x="3" y="3" width="7" height="7" rx="1" />
                                    <rect x="14" y="3" width="7" height="7" rx="1" />
                                    <rect x="3" y="14" width="7" height="7" rx="1" />
                                    <rect x="14" y="14" width="7" height="7" rx="1" />
                                </svg>仪表盘</li>
                            <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                                    <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
                                    <circle cx="12" cy="7" r="4" />
                                </svg>用户管理</li>
                            <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                                    <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 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-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 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" />
                                </svg>系统设置</li>
                            <li><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
                                    <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" />
                                </svg>日志中心</li>
                        </ul>
                    </div>
                    <div class="resize-handle" data-resize="left"></div>
                    <button class="toggle-btn" data-toggle="left" aria-label="切换左侧导航">
                        <svg viewBox="0 0 24 24">
                            <path d="M15 6l-6 6 6 6V6z" fill="currentColor" />
                            <path d="M3 4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h2V4H3z" fill="currentColor" opacity="0.3" />
                        </svg>
                    </button>
                </aside>

                <!-- ================= 中部区域(上 | 中 | 下) ================= -->
                <div class="middle-area">

                    <!-- 顶部栏 -->
                    <div class="top-bar" id="topBar">
                        <span class="bar-title">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                                stroke-width="2">
                                <path d="M3 12h18M3 6h18M3 18h18" />
                            </svg>
                            顶部栏(topSide)
                        </span>
                        <span style="font-size:12px;color:var(--g-icon-color);">高度可调</span>
                        <button class="toggle-btn" data-toggle="top" aria-label="切换顶部栏">
                            <svg viewBox="0 0 24 24">
                                <path d="M6 15l6-6 6 6H6z" fill="currentColor" />
                                <path d="M4 3a2 2 0 0 0-2 2v2h20V5a2 2 0 0 0-2-2H4z" fill="currentColor"
                                    opacity="0.3" />
                            </svg>
                        </button>
                    </div>

                    <!-- 中部主内容区(default slot) -->
                    <main class="main-content">
                        <div class="panel-header"><span>主内容区(default 插槽)</span></div>
                        <div class="panel-body">
                            <p><strong>顶部栏和底部栏现已支持独立显隐。</strong>点击顶部栏边缘的半圆三角按钮,可以将其折叠;再次点击即可展开。</p>
                            <p>左侧和右侧面板的折叠按钮同样是半圆与三角的组合,贴在边缘线上。</p>
                            <p>拖动左右面板的边缘可以调整宽度,拖动顶部/底部栏的边缘可以调整高度。</p>
                        </div>
                    </main>

                    <!-- 底部栏 -->
                    <div class="bottom-bar" id="bottomBar">
                        <span class="bar-title">
                            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"
                                stroke-width="2">
                                <path d="M3 12h18M3 6h18M3 18h18" />
                            </svg>
                            底部栏(bottomSide)
                        </span>
                        <span style="font-size:12px;color:var(--g-icon-color);">高度可调</span>
                        <button class="toggle-btn" data-toggle="bottom" aria-label="切换底部栏">
                            <svg viewBox="0 0 24 24">
                                <path d="M6 9l6 6 6-6H6z" fill="currentColor" />
                                <path d="M4 21a2 2 0 0 1-2-2v-2h20v2a2 2 0 0 1-2 2H4z" fill="currentColor"
                                    opacity="0.3" />
                            </svg>
                        </button>
                    </div>

                </div>

                <!-- ================= 右侧面板 ================= -->
                <aside class="side-panel right-panel" id="rightPanel">
                    <div class="panel-header"><span>右侧</span></div>
                    <div class="panel-body">
                        <p>这里是右侧面板,可以放置属性设置或详情内容。</p>
                        <p style="margin-top:10px;">拖左边缘调整宽度,边界上会浮现半圆三角把手。</p>
                    </div>
                    <div class="resize-handle" data-resize="right"></div>
                    <button class="toggle-btn" data-toggle="right" aria-label="切换右侧面板">
                        <svg viewBox="0 0 24 24">
                            <path d="M9 6l6 6-6 6V6z" fill="currentColor" />
                            <path d="M21 4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-2V4h2z" fill="currentColor" opacity="0.3" />
                        </svg>
                    </button>
                </aside>

            </div>
        </div>
    </div>

    <button class="reopen-btn" id="reopenBtn">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M3 12a9 9 0 1 0 3-6.7L3 8" />
            <path d="M3 3v5h5" />
        </svg>
        重新打开窗口
    </button>

    <script>
        /* ============================================================
           窗口逻辑(保持)
           ============================================================ */
        const windowShell = document.getElementById('windowShell');
        const windowTitlebar = document.getElementById('windowTitlebar');
        const btnMinimize = document.getElementById('btnMinimize');
        const btnMaximize = document.getElementById('btnMaximize');
        const btnClose = document.getElementById('btnClose');
        const reopenBtn = document.getElementById('reopenBtn');

        function centerWindow() {
            windowShell.style.left = (window.innerWidth - windowShell.offsetWidth) / 2 + 'px';
            windowShell.style.top = Math.max(20, (window.innerHeight - windowShell.offsetHeight) / 2) + 'px';
        }
        window.addEventListener('load', centerWindow);
        window.addEventListener('resize', () => {
            if (!windowShell.classList.contains('is-maximized') &&
                !windowShell.classList.contains('is-minimized') &&
                !windowShell.classList.contains('is-closed')) centerWindow();
        });

        let dragging = false, dsX = 0, dsY = 0, wsL = 0, wsT = 0;
        windowTitlebar.addEventListener('mousedown', e => {
            if (windowShell.classList.contains('is-maximized') ||
                windowShell.classList.contains('is-minimized') ||
                e.target.closest('.window-controls')) return;
            dragging = true;
            dsX = e.clientX; dsY = e.clientY;
            wsL = windowShell.offsetLeft; wsT = windowShell.offsetTop;
            document.body.style.userSelect = 'none';
        });
        document.addEventListener('mousemove', e => {
            if (!dragging) return;
            windowShell.style.left = wsL + e.clientX - dsX + 'px';
            windowShell.style.top = Math.max(0, wsT + e.clientY - dsY) + 'px';
        });
        document.addEventListener('mouseup', () => {
            if (dragging) { dragging = false; document.body.style.userSelect = ''; }
        });

        windowTitlebar.addEventListener('dblclick', e => {
            if (e.target.closest('.window-controls')) return;
            const willMax = !windowShell.classList.contains('is-maximized');
            windowShell.classList.toggle('is-maximized', willMax);
            btnMaximize.innerHTML = willMax
                ? `<svg viewBox="0 0 10 10"><rect x=".5" y="2.5" width="7" height="7" fill="none" stroke="currentColor"/><path d="M2.5 2.5V.5h7v7h-2" fill="none" stroke="currentColor"/></svg>`
                : `<svg viewBox="0 0 10 10"><rect x=".5" y=".5" width="9" height="9" fill="none" stroke="currentColor"/></svg>`;
            if (!willMax) centerWindow();
        });
        btnMaximize.addEventListener('click', () => windowTitlebar.dispatchEvent(new Event('dblclick')));
        btnMinimize.addEventListener('click', () => windowShell.classList.add('is-minimized'));
        windowShell.addEventListener('click', e => {
            if (windowShell.classList.contains('is-minimized') &&
                !e.target.closest('.window-controls')) {
                windowShell.classList.remove('is-minimized'); centerWindow();
            }
        });
        btnClose.addEventListener('click', () => {
            windowShell.classList.add('is-closed');
            reopenBtn.style.display = 'flex';
        });
        reopenBtn.addEventListener('click', () => {
            windowShell.classList.remove('is-closed', 'is-minimized');
            reopenBtn.style.display = 'none';
            centerWindow();
        });

        /* ============================================================
           布局容器逻辑(核心修复:顶部/底部栏的显隐)
           ============================================================ */
        const container = document.getElementById('layoutContainer');
        const panels = {
            left: document.getElementById('leftPanel'),
            right: document.getElementById('rightPanel'),
            top: document.getElementById('topBar'),
            bottom: document.getElementById('bottomBar')
        };

        /* ---- 折叠 / 展开(通用) ---- */
        function togglePanel(key) {
            const panel = panels[key];
            if (!panel) return;
            panel.classList.toggle('is-hidden');
            syncCtrlBtn(key);
        }

        /* ---- 同步第二行控制按钮的文字与状态 ---- */
        function syncCtrlBtn(key) {
            const ctrl = document.querySelector(`[data-ctrl="${key}"]`);
            if (!ctrl) return;
            const isHidden = panels[key].classList.contains('is-hidden');
            ctrl.classList.toggle('is-active', isHidden);
            ctrl.innerHTML = ctrl.innerHTML.replace(/(隐藏|展开)/, isHidden ? '展开' : '隐藏');
        }

        /* ---- 绑定边缘切换按钮 ---- */
        document.querySelectorAll('[data-toggle]').forEach(btn => {
            btn.addEventListener('click', e => {
                e.stopPropagation();
                togglePanel(btn.dataset.toggle);
            });
        });

        /* ---- 绑定第二行控制按钮(现在顶部/底部栏真正生效) ---- */
        document.querySelectorAll('[data-ctrl]').forEach(btn => {
            btn.addEventListener('click', () => {
                const key = btn.dataset.ctrl;
                togglePanel(key);
            });
        });

        /* ---- 拖拽调整尺寸(左右宽度 + 上下高度) ---- */
        document.querySelectorAll('.resize-handle').forEach(handle => {
            handle.addEventListener('mousedown', e => {
                e.preventDefault();
                const key = handle.dataset.resize;
                const panel = panels[key];
                if (!panel || panel.classList.contains('is-hidden')) return;

                const startX = e.clientX, startY = e.clientY;
                const computed = getComputedStyle(container);
                const startL = parseFloat(computed.getPropertyValue('--left-width')) || 240;
                const startR = parseFloat(computed.getPropertyValue('--right-width')) || 240;
                const startT = parseFloat(computed.getPropertyValue('--top-height')) || 44;
                const startB = parseFloat(computed.getPropertyValue('--bottom-height')) || 44;

                handle.classList.add('active');
                const btn = panel.querySelector('.toggle-btn');
                if (btn) btn.style.opacity = '0';

                function onMove(ev) {
                    const dx = ev.clientX - startX, dy = ev.clientY - startY;
                    let v;
                    if (key === 'left') {
                        v = Math.max(160, Math.min(startL + dx, 600));
                        container.style.setProperty('--left-width', v + 'px');
                    } else if (key === 'right') {
                        v = Math.max(160, Math.min(startR - dx, 600));
                        container.style.setProperty('--right-width', v + 'px');
                    } else if (key === 'top') {
                        v = Math.max(30, Math.min(startT + dy, 300));
                        container.style.setProperty('--top-height', v + 'px');
                    } else if (key === 'bottom') {
                        v = Math.max(30, Math.min(startB - dy, 300));
                        container.style.setProperty('--bottom-height', v + 'px');
                    }
                }
                function onUp() {
                    handle.classList.remove('active');
                    if (btn) btn.style.opacity = '';
                    document.removeEventListener('mousemove', onMove);
                    document.removeEventListener('mouseup', onUp);
                    document.body.style.cursor = '';
                    document.body.style.userSelect = '';
                }
                document.body.style.cursor = (key === 'left' || key === 'right') ? 'col-resize' : 'row-resize';
                document.body.style.userSelect = 'none';
                document.addEventListener('mousemove', onMove);
                document.addEventListener('mouseup', onUp);
            });
        });
    </script>
</body>

</html>

  

posted @ 2026-10-21 16:19  华腾智算  阅读(3)  评论(0)    收藏  举报
https://damo.alibaba.com/ https://tianchi.aliyun.com/course?spm=5176.21206777.J_3941670930.5.87dc17c9BZNvLL