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>