Trace Tabs

适用于 Trace 调试的tabs 样式展示
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Trace Tabs</title>
  <style>

#tools_trace {
    position: fixed;
    z-index: 99999999;
    font-size: 14px!important;
}

/* Logo 图标右下角 */
#tools_trace .trace-logo {
    position: fixed;
    bottom: 0px;
    right: 0px;
    height: 18px;
    cursor: pointer;
    z-index: 1001;
    transition: transform 0.3s;
    font-size: 14px;
    background-color: #eb4432;
    color: gold;
    padding: 0;
    border: 1px solid gold;
    box-sizing: content-box;
}

#tools_trace .trace-logo .logo {
    float: left;
}

#tools_trace .trace-logo .title {
    margin: 0 4px;
}

/* Tabs 容器 */
#tools_trace .tabs-container {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 250px;
    background: #333;
    border-top: 1px solid #444;
    display: none;
    flex-direction: column;
}

/* Tabs 头部 */
#tools_trace .tabs-header {
    display: flex;
    align-items: center;
    padding: 0;
    border-bottom: 1px solid #444;
    background: #222;
}

#tools_trace .tabs-logo-small {
    height: 22px;
    margin: 0 15px;
    cursor: pointer;
}

#tools_trace .tabs-menu {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
}

#tools_trace .tabs-item {
    padding: 2px 15px;
    cursor: pointer;
    font-weight: bold;
    color: #ccc;
    transition: background-color 0.3s, color 0.3s;
}

#tools_trace .tabs-item.active {
    background-color: #eb4432;
    color: #fff;
    border-radius: 0;
}

#tools_trace .tabs-close {
    margin-left: auto;
    padding: 2px 15px;
    cursor: pointer;
    color: #ccc;
    transition: color 0.3s;
}

#tools_trace .tabs-close:hover {
    color: #fff;
}

/* Tabs 内容 */
#tools_trace .tabs-content {
    flex: 1;
    padding: 0;
    background: #252a37;
    color: #edf2f7;
    overflow-y: auto;
    display: none;
}

#tools_trace .tabs-content.active {
    display: block;
}

#tools_trace ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

#tools_trace li {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    padding: 2px 0 2px 10px;
}

/*#tools_trace li:nth-child(odd) {
  background-color: #252a37;
}*/

#tools_trace li:nth-child(even) {
    background-color: #18181b;
    color: white;
}

#tools_trace li .json-label {
    /*      flex: 0 0 25%;*/
    flex: 1;
    /*      max-width: 25%;*/
    line-height: 1.6;
    word-break: break-word;
}

#tools_trace li .json-arrow-pre-wrapper {
    flex: 0 0 70%;
    max-width: 70%;
    display: flex;
    flex-direction: column;
}

#tools_trace li .json-string-content {
    flex: 0 0 70%;
    max-width: 70%;
    display: flex;
    flex-direction: column;
}

#tools_trace li .json-right {
    /*flex: 0 0 5%;*/
    /*max-width: 5%;*/
    width: 70px;
    display: flex;
    flex-direction: column;
}

#tools_trace .json-arrow {
    display: inline-block;
    font-weight: bold;
    cursor: pointer;
    margin-right: 10px;
}

#tools_trace pre {
    display: none;
    background-color: #f4f4f4;
    color: #333;
    padding: 2px 8px;
    margin: 5px 0 0 0;
    font-family: monospace;
    white-space: pre-wrap;
    word-wrap: break-word;
    border-radius: 3px;
    font-size: 14px;
}

#tools_trace pre.show {
    display: block;
}

  </style>
</head>
<body>
  <div id="tools_trace">
    <div class="trace-logo">
      <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAT9JREFUOE+tVFuOwjAMtMtqF06FtOVc/eBcFCmn2oegWU1Su7ZJ6X4QCakJycx4/OCc845euHgT8Ho4Fr7Pr+R4cR7PiGgdcNwNlPORCD8sTnSa+vJZ/psGdzazPQKCefoeFQQf3cdZVSpYIGkCWjDuztTfK5BdF775A07EnOSuVyiXu32v/oAk/9aw+T0t6gPRKb+VK5oUCcUqu3SjeojzNUDzZgEUdTOTGi9AyKjzT0zz1lRA8U6Y4t5mVggk4lA6bUBRIl5GO2AFMi9gZu8BI4DdQxGyHtUH8uDhXLytkCXENfUPWS4ZJdJukAxbz1A+6BBXCajLpYuel00rq60WbJYN1ImqZ4UtiVixxXfKf1rPDQdYVDtEVnva2FARjrbdz1AfYgKZ6bMJqCrs+FINydVgaOntARsebG1fDvgH5+lCJDZyUmAAAAAASUVORK5CYII=" alt="Logo" class="logo">
      <span class="title">调试</span>
    </div>
    

    <!-- Tabs 容器 -->
    <div class="tabs-container">
      <div class="tabs-header">
        <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAAXNSR0IArs4c6QAAAT9JREFUOE+tVFuOwjAMtMtqF06FtOVc/eBcFCmn2oegWU1Su7ZJ6X4QCakJycx4/OCc845euHgT8Ho4Fr7Pr+R4cR7PiGgdcNwNlPORCD8sTnSa+vJZ/psGdzazPQKCefoeFQQf3cdZVSpYIGkCWjDuztTfK5BdF775A07EnOSuVyiXu32v/oAk/9aw+T0t6gPRKb+VK5oUCcUqu3SjeojzNUDzZgEUdTOTGi9AyKjzT0zz1lRA8U6Y4t5mVggk4lA6bUBRIl5GO2AFMi9gZu8BI4DdQxGyHtUH8uDhXLytkCXENfUPWS4ZJdJukAxbz1A+6BBXCajLpYuel00rq60WbJYN1ImqZ4UtiVixxXfKf1rPDQdYVDtEVnva2FARjrbdz1AfYgKZ6bMJqCrs+FINydVgaOntARsebG1fDvgH5+lCJDZyUmAAAAAASUVORK5CYII=" alt="Logo" class="tabs-logo-small">
        <div class="tabs-menu">
          <div class="tabs-item active" data-tab="tab1">Tab 1</div>
          <div class="tabs-item" data-tab="tab2">Tab 2</div>
        </div>
        <div class="tabs-close">关闭</div>
      </div>
      <div id="tab1" class="tabs-content active">
        <ul>
          <li>
            <span class="json-label">只有一行文字</span>
          </li>
          <li>
            <span class="json-label">键名</span>
            <div class="json-string-content">字符串内容</div>
          </li>
          <li>
            <span class="json-label">键名</span>
            <div class="json-string-content">字符串内容</div>
            <div class="json-right">时间</div>
          </li>
          <li>
            <span class="json-label">_data:</span>
            <div class="json-arrow-pre-wrapper">
              <span class="json-arrow" onclick="toggleJson(this)"></span>
              <pre class="json">{"product":"Laptop","price":1200}</pre>
            </div>
            <div class="json-right">
              1000ms
            </div>
          </li>
          <li>
            <div class="json-arrow-pre-wrapper">
              <span class="json-arrow" onclick="toggleJson(this)"></span>
              <pre class="json">{"product":"Laptop","price":1200}</pre>
            </div>
          </li>
          <li>
            <span class="json-label">_info:</span>
            <div class="json-arrow-pre-wrapper">
              <span class="json-arrow" onclick="toggleJson(this)"></span>
              <pre class="json">{"name":"John","age":30}</pre>
            </div>
          </li>
        </ul>
      </div>
      <div id="tab2" class="tabs-content">
        <ul>
          <li>
            <span class="json-label">Tab 2 内容项 2</span>
            <div class="json-arrow-pre-wrapper">
              <span class="json-arrow" onclick="toggleJson(this)"></span>
              <pre class="json">{"old":[],"new":[]}</pre>
            </div>
          </li>
          <li>
            <span class="json-label">_data:</span>
            <div class="json-arrow-pre-wrapper">
              <span class="json-arrow" onclick="toggleJson(this)"></span>
              <pre class="json">{"product":"Laptop","price":1200}</pre>
            </div>
          </li>
          <li>
            <span class="json-label">_info:</span>
            <div class="json-arrow-pre-wrapper">
              <span class="json-arrow" onclick="toggleJson(this)"></span>
              <pre class="json">{"name":"John","age":30}</pre>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>

  <script>
(function (window) {
    const tabsLogo = document.querySelector("#tools_trace .trace-logo");
    const tabsContainer = document.querySelector("#tools_trace .tabs-container");
    const closeButton = document.querySelector("#tools_trace .tabs-close");
    const tabItems = document.querySelectorAll("#tools_trace .tabs-item");
    const tabContents = document.querySelectorAll("#tools_trace .tabs-content");

    // 点击 Logo 展开 Tabs
    tabsLogo.addEventListener("click", () => {
        tabsLogo.style.display = "none"; // 隐藏 Logo
        tabsContainer.style.display = "flex"; // 显示 Tabs
    });

    // 点击关闭按钮隐藏 Tabs
    closeButton.addEventListener("click", () => {
        tabsContainer.style.display = "none"; // 隐藏 Tabs
        tabsLogo.style.display = "block"; // 显示 Logo
    });

    // 激活指定 Tab
    function activateTab(index) {
        tabItems.forEach((item, i) => {
            item.classList.toggle("active", i === index);
            tabContents[i].classList.toggle("active", i === index);
        });
    }

    // Tab 切换逻辑
    tabItems.forEach((tab, index) => {
        tab.addEventListener("click", () => activateTab(index));
    });

    // 初始化默认激活第一个 Tab
    activateTab(0);


    // ========================================

    // 初始化 JSON 显示
    function initializeJsonDisplay(jsonElement, arrowElement, labelElement) {
        try {
            const jsonText = jsonElement.textContent.trim();
            const jsonData = JSON.parse(jsonText);

            // 计算 JSON 对象的长度
            const arrayLength = Array.isArray(jsonData) ? jsonData.length : Object.keys(jsonData).length;

            // 格式化 JSON 数据
            // jsonElement.textContent = `array:${arrayLength} [\n` +
            jsonElement.textContent = `[\n` +
                Object.entries(jsonData).map(([key, value]) => {
                    const valueStr = typeof value === "object" && value !== null
                        ? JSON.stringify(value, null, 2)
                        : JSON.stringify(value);
                    return `  "${key}" => ${valueStr}`;
                }).join(",\n") +
                "\n]";

            // 设置箭头的初始文本
            // arrowElement.textContent = `▶`;
            // labelElement.textContent += ` array:${arrayLength}`;

            arrowElement.textContent = `▶ array:${arrayLength}`;
            // labelElement.textContent += ` array:${arrayLength}`;
        } catch (e) {
            console.error("初始化 JSON 数据时出错:", e);
        }
    }

    // 初始化 JSON 列表
    document.addEventListener("DOMContentLoaded", function () {
        const jsonElements = document.querySelectorAll("#tools_trace .json");
        const arrowElements = document.querySelectorAll("#tools_trace .json-arrow");
        const labelElements = document.querySelectorAll("#tools_trace .json-label");

        jsonElements.forEach((jsonElement, index) => {
            const jsonText = jsonElement.textContent.trim();
            jsonElement.setAttribute("data-original", jsonText);
            initializeJsonDisplay(jsonElement, arrowElements[index], labelElements[index]);
        });
    });
})(window);

// 展开/收起 JSON
function toggleJson(arrowElement) {
    const preElement = arrowElement.nextElementSibling;
    const labelElement = arrowElement.parentNode.previousElementSibling;
    const jsonData = JSON.parse(preElement.getAttribute("data-original"));
    const arrayLength = Array.isArray(jsonData) ? jsonData.length : Object.keys(jsonData).length;

    if (preElement.classList.contains("show")) {
        // 收起
        // arrowElement.textContent = `▶`;
        // labelElement.textContent = labelElement.textContent.replace(/▼.+/, ` array:${arrayLength}`);
        arrowElement.textContent = `▶ array:${arrayLength}`;
        // labelElement.textContent = labelElement.text/Content.replace(/▼.+/, ` array:${arrayLength}`);
        preElement.classList.remove("show");
    } else {
        // 展开
        // arrowElement.textContent = `▼`;
        arrowElement.textContent = `▼ array:${arrayLength}`;
        // labelElement.textContent = labelElement.textContent.replace(/array:\d+/, '');
        preElement.classList.add("show");
    }
}

  </script>

</body>
</html>