API数据表更新

This commit is contained in:
2025-08-30 15:26:27 +08:00
parent 3b6b4fda42
commit 694e045569
7 changed files with 1555 additions and 492 deletions

View File

@@ -14,48 +14,54 @@
}
body {
background: linear-gradient(135deg, #f8f9fc 0%, #eef2f6 100%);
background: linear-gradient(135deg, #e6f7ff 0%, #f0f7ff 100%);
min-height: 100vh;
padding: 20px;
padding: 10px; /* 核心距离页面两边10px */
color: #2c3e50;
overflow: hidden; /* 禁止页面整体滚动 */
overflow: hidden;
}
.container {
max-width: 1400px;
margin: 20px auto;
max-width: 100%;
margin: 0 auto; /* 水平居中同时借助body的10px padding实现边缘距离 */
display: flex;
gap: 25px;
height: calc(100vh - 40px); /* 让容器占用整个屏幕高度 */
gap: 15px;
height: calc(100vh - 20px); /* 减去body上下10px padding占满垂直空间 */
}
.panel {
background: white;
border-radius: 16px;
box-shadow: 0 8px 30px rgba(0, 0, 100, 0.08);
box-shadow: 0 4px 20px rgba(0, 120, 255, 0.08);
overflow: hidden;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
height: 100%; /* 面板高度铺满容器,避免内容溢出 */
}
.panel:hover {
transform: translateY(-5px);
transform: translateY(-3px);
}
.panel-header {
background: linear-gradient(135deg, #4a69bd 0%, #1e3799 100%);
background: linear-gradient(135deg, #4096ff 0%, #165dff 100%);
color: white;
padding: 18px 25px;
font-size: 1.3rem;
padding: 16px 20px;
font-size: 1.2rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0; /* 表头不压缩 */
}
.panel-content {
padding: 25px;
flex: 1;
padding: 20px;
flex: 1; /* 内容区占据剩余高度 */
display: flex;
flex-direction: column;
overflow: hidden; /* 关键:父容器隐藏溢出,确保子滚动容器生效 */
}
.left-panel {
@@ -67,23 +73,25 @@
}
.info-card {
background: #f8f9ff;
background: #eef7ff;
border-radius: 12px;
padding: 20px;
margin-bottom: 22px;
border-left: 4px solid #4a69bd;
padding: 18px;
margin-bottom: 18px;
border-left: 4px solid #4096ff;
flex-shrink: 0; /* 信息卡片不压缩 */
}
.info-row {
display: flex;
margin-bottom: 14px;
margin-bottom: 12px;
align-items: center;
line-height: 1.5;
}
.info-label {
font-weight: 600;
color: #4a69bd;
width: 120px;
color: #165dff;
width: 110px;
position: relative;
}
@@ -100,33 +108,48 @@
.button-panel {
display: flex;
gap: 12px;
margin-bottom: 25px;
border-bottom: 1px solid #e9ecef;
padding-bottom: 20px;
flex-shrink: 0; /* 防止按钮区随内容滚动 */
gap: 10px;
margin-bottom: 15px;
border-bottom: 1px solid #e6f0ff;
padding-bottom: 12px;
flex-shrink: 0; /* 按钮区不压缩,固定高度 */
flex-wrap: wrap; /* 小屏幕按钮换行 */
}
.button {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
padding: 10px 22px;
background: linear-gradient(135deg, #f0f7ff 0%, #e6f0ff 100%);
padding: 9px 18px;
border-radius: 8px;
font-weight: 600;
color: #4a69bd;
color: #165dff;
cursor: pointer;
border: 1px solid #dee2e6;
transition: all 0.3s ease;
border: 1px solid #d1e0ff;
transition: all 0.25s ease;
display: flex;
align-items: center;
gap: 6px;
}
.button:hover {
background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
background: linear-gradient(135deg, #e6f0ff 0%, #d1e0ff 100%);
box-shadow: 0 3px 6px rgba(64, 150, 255, 0.1);
}
.button.active {
background: linear-gradient(135deg, #4a69bd 0%, #1e3799 100%);
background: linear-gradient(135deg, #4096ff 0%, #165dff 100%);
color: white;
border-color: #1e3799;
border-color: #165dff;
}
/* 1. 字段信息面板:滚动容器修复(确保滚动条必现) */
.table-scroll-container {
flex: 1; /* 占据内容区剩余高度 */
overflow-y: auto; /* 内容溢出时显示垂直滚动条 */
overflow-x: auto; /* 小屏幕列宽不足时显示水平滚动条 */
border-radius: 12px;
margin-bottom: 10px;
box-shadow: 0 3px 12px rgba(0, 120, 255, 0.05);
min-height: 100px; /* 最小高度,避免内容过少时容器塌陷 */
}
table {
@@ -135,131 +158,386 @@
border-spacing: 0;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
background: white;
table-layout: fixed; /* 固定列宽,避免表头错位 */
min-width: 600px; /* 表格最小宽度,确保小屏幕触发水平滚动 */
}
th {
background: linear-gradient(135deg, #4a69bd 0%, #1e3799 100%);
background: linear-gradient(135deg, #4096ff 0%, #165dff 100%);
color: white;
font-weight: 600;
padding: 16px 15px;
padding: 14px 15px;
text-align: left;
border-right: 1px solid rgba(255, 255, 255, 0.15);
position: sticky;
top: 0;
z-index: 10;
z-index: 20;
background-clip: padding-box; /* 防止背景透色 */
/* 列宽分配:适配内容优先级 */
width: 100px;
}
th:nth-child(1) {
width: 80px;
}
/* 序号 */
th:nth-child(2) {
width: 180px;
}
/* 字段名称 */
th:nth-child(3) {
width: 150px;
}
/* 字段类型 */
th:nth-child(4) {
width: 200px;
}
/* 描述(增加宽度,减少换行) */
th:nth-child(5) {
width: 120px;
}
/* 键类型 */
th:last-child {
border-right: none;
}
td {
padding: 14px 15px;
border-bottom: 1px solid #e9ecef;
padding: 12px 15px;
border-bottom: 1px solid #e6f0ff;
color: #495057;
width: 100px;
word-wrap: break-word; /* 长文本换行,避免列宽异常 */
word-break: break-all;
}
td:nth-child(1) {
width: 80px;
}
td:nth-child(2) {
width: 180px;
}
td:nth-child(3) {
width: 150px;
}
td:nth-child(4) {
width: 200px;
}
td:nth-child(5) {
width: 120px;
}
tr:nth-child(even) {
background-color: #f8f9ff;
background-color: #f7fbff;
}
tr:hover td {
background-color: rgba(74, 105, 189, 0.08);
background-color: rgba(64, 150, 255, 0.08);
}
.primary-key {
background-color: #e3f2fd;
color: #0d47a1;
background-color: #e6f4ff;
color: #165dff;
font-weight: 600;
padding: 3px 10px;
padding: 2px 8px;
border-radius: 50px;
font-size: 0.85rem;
font-size: 0.8rem;
}
.index-key {
background-color: #e8f5e9;
color: #1b5e20;
padding: 3px 10px;
color: #00864e;
padding: 2px 8px;
border-radius: 50px;
font-size: 0.85rem;
font-size: 0.8rem;
}
.stats-row {
display: flex;
gap: 20px;
margin-top: 15px;
gap: 15px;
margin-top: 12px;
color: #6c757d;
font-size: 0.9rem;
font-size: 0.85rem;
flex-shrink: 0; /* 统计行不压缩 */
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
gap: 5px;
}
.time-ago {
color: #e74c3c;
color: #ff4d4f;
font-weight: 500;
font-size: 0.85rem;
}
/* 2. SQL面板滚动容器修复同字段面板逻辑 */
.sql-container {
flex: 1; /* 占据剩余高度 */
display: flex;
flex-direction: column;
margin-bottom: 10px;
border-radius: 12px;
box-shadow: 0 3px 12px rgba(0, 120, 255, 0.05);
overflow: hidden;
min-height: 100px; /* 最小高度,避免塌陷 */
}
.sql-toolbar {
background: #f0f7ff;
padding: 8px 15px;
border-bottom: 1px solid #e6f0ff;
display: flex;
justify-content: flex-end;
gap: 8px;
flex-shrink: 0; /* 工具条不压缩 */
}
.sql-copy-btn {
background: #4096ff;
color: white;
border: none;
border-radius: 6px;
padding: 6px 12px;
font-size: 0.85rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 5px;
transition: background 0.2s ease;
}
.sql-copy-btn:hover {
background: #165dff;
}
/* SQL输入框确保滚动条生效 */
.sql-input {
flex: 1; /* 占据SQL容器剩余高度 */
background: white;
padding: 15px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.9rem;
color: #333;
white-space: pre-wrap;
line-height: 1.6;
overflow-y: auto; /* 垂直滚动 */
overflow-x: auto; /* 水平滚动长SQL语句 */
outline: none;
border: none;
contenteditable: true;
min-height: 80px; /* 输入框最小高度 */
}
/* 移除pre标签默认样式干扰适配动态SQL */
.sql-input pre {
margin: 0;
padding: 0;
border: none;
background: transparent;
font-family: inherit;
font-size: inherit;
line-height: inherit;
white-space: inherit; /* 继承sql-input的换行规则 */
}
/* SQL高亮样式不破坏格式 */
.sql-keyword { color: #d73a49; font-weight: 600; }
.sql-table { color: #005cc5; }
.sql-column { color: #6f42c1; }
.sql-comment { color: #6a737d; }
/* 复制提示 */
.copy-toast {
position: fixed;
bottom: 30px;
right: 30px;
background: rgba(0, 120, 255, 0.9);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-size: 0.85rem;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 100;
}
.copy-toast.show {
opacity: 1;
}
/* 统一滚动条样式(确保所有滚动容器外观一致) */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background: #4096ff;
border-radius: 4px;
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: #165dff;
}
::-webkit-scrollbar-track {
background: rgba(64, 150, 255, 0.1);
border-radius: 4px;
}
/* 内容面板切换基础样式 */
.content-panel {
display: none;
flex: 1;
flex-direction: column;
overflow: hidden; /* 子容器溢出控制 */
}
.content-panel.active {
display: flex;
}
/* 3. 小屏幕/页面缩小适配(确保滚动条正常触发) */
@media (max-width: 1200px) {
.container {
gap: 10px;
}
.panel-content {
padding: 15px;
}
th, td {
padding: 12px 10px;
font-size: 0.85rem;
}
th:nth-child(4) {
width: 180px;
}
/* 小屏幕压缩描述列宽 */
td:nth-child(4) {
width: 180px;
}
}
@media (max-width: 1000px) {
.container {
flex-direction: column;
height: auto;
height: auto; /* 垂直布局时容器高度自适应 */
max-height: calc(100vh - 20px); /* 但不超过屏幕高度 */
overflow: hidden;
}
.table-scroll-container,
.sql-container {
max-height: 300px; /* 移动端限制最大高度,确保滚动 */
}
/* 移动端表格列宽进一步压缩 */
th:nth-child(2) {
width: 150px;
}
th:nth-child(3) {
width: 120px;
}
th:nth-child(4) {
width: 150px;
}
td:nth-child(2) {
width: 150px;
}
td:nth-child(3) {
width: 120px;
}
td:nth-child(4) {
width: 150px;
}
}
.table-scroll-container {
flex: 1;
overflow-y: auto;
border-radius: 12px;
max-height: calc(100vh - 320px); /* 确保独立滚动 */
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}
@media (max-width: 600px) {
.button {
padding: 8px 12px;
font-size: 0.85rem;
}
/* 自定义滚动条样式 */
.table-scroll-container::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.info-label {
width: 90px;
}
.table-scroll-container::-webkit-scrollbar-thumb {
background: #4a69bd;
border-radius: 4px;
}
/* 超小屏幕表格列宽最小化 */
th {
padding: 10px 8px;
}
.table-scroll-container::-webkit-scrollbar-track {
background: rgba(74, 105, 189, 0.1);
border-radius: 4px;
td {
padding: 10px 8px;
}
th:nth-child(2) {
width: 120px;
}
th:nth-child(4) {
width: 120px;
}
td:nth-child(2) {
width: 120px;
}
td:nth-child(4) {
width: 120px;
}
}
</style>
</head>
<body>
<div class="container">
<!-- 左侧面板:表基础信息 -->
<div class="panel left-panel">
<div class="panel-header">
<i class="fas fa-database"></i> 表基础信息
<i class="fas fa-database"></i>
<span>表基础信息</span>
</div>
<div class="panel-content">
<div class="panel-content" th:each="t : ${data}">
<div class="info-card">
<div class="info-row">
<div class="info-label">数据库</div>
<div class="info-value">work</div>
<div class="info-value"><p th:text="${t.dbName}"></p></div>
</div>
<div class="info-row">
<div class="info-label">表名称</div>
<div class="info-value">biz_cities</div>
<div class="info-value" th:text="${t.tableName}"><strong></strong></div>
</div>
<div class="info-row">
<div class="info-label">表描述</div>
<div class="info-value">市区信息表</div>
<div class="info-value"><p th:text="${t.tableDesc}"></p></div>
</div>
<div class="info-row">
<div class="info-label">存储量</div>
<div class="info-value">333条</div>
<div class="info-value"><p th:text="${t.dataLength}"></p></div>
</div>
</div>
@@ -267,183 +545,226 @@
<div class="info-row">
<div class="info-label">创建时间</div>
<div class="info-value">
2025-08-29 15:54:41
<span class="time-ago">2小时前</span>
<p th:text="${t.createTime}"></p>
</div>
</div>
<div class="info-row">
<div class="info-label">更新时间</div>
<div class="info-value">
2025-08-29 15:54:41
<span class="time-ago">2小时前</span>
<p th:text="${t.updateTime}"></p>
</div>
</div>
</div>
<div class="stats-row">
<div class="stat-item">
<i class="fas fa-key" style="color: #0d47a1;"></i>
<i class="fas fa-key" style="color: #165dff;"></i>
1 个主键
</div>
<div class="stat-item">
<i class="fas fa-sitemap" style="color: #1b5e20;"></i>
<i class="fas fa-sitemap" style="color: #00864e;"></i>
2 个索引
</div>
<div class="stat-item">
<i class="fas fa-list" style="color: #4a69bd;"></i>
13 个字段
<i class="fas fa-list" style="color: #4096ff;"></i>
20 个字段
</div>
</div>
</div>
</div>
<!-- 右侧面板:功能切换区 -->
<div class="panel right-panel">
<div class="panel-header">
<i class="fas fa-columns"></i> 表结构信息
<i class="fas fa-columns"></i>
<span>表结构操作</span>
</div>
<div class="panel-content">
<!-- 切换按钮 -->
<div class="button-panel">
<div class="button active">
<i class="fas fa-list-ul"></i> 字段信息
<div class="button active" data-target="field-info">
<i class="fas fa-list-ul"></i>
<span>字段信息</span>
</div>
<div class="button">
<i class="fas fa-code"></i> 生成SELECT
<div class="button" data-target="generate-select">
<i class="fas fa-code"></i>
<span>生成SELECT</span>
</div>
<div class="button">
<i class="fas fa-database"></i> 生成DDL
<div class="button" data-target="generate-ddl">
<i class="fas fa-database"></i>
<span>生成DDL</span>
</div>
</div>
<div class="table-scroll-container">
<table>
<thead>
<tr>
<th>序号</th>
<th>字段名称</th>
<th>字段类型</th>
<th>描述</th>
<th>类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>id</strong></td>
<td>datetime</td>
<td></td>
<td><span class="primary-key">主键</span></td>
</tr>
<tr>
<td>2</td>
<td>create_time</td>
<td>varchar</td>
<td>记录时间</td>
<td></td>
</tr>
<tr>
<td>3</td>
<td>province_code</td>
<td>varchar</td>
<td>省份编码</td>
<td><span class="index-key">索引</span></td>
</tr>
<tr>
<td>4</td>
<td>city_code</td>
<td>varchar</td>
<td>市区编码</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td>city_name</td>
<td>varchar</td>
<td>市区名称</td>
<td></td>
</tr>
<tr>
<td>6</td>
<td>area_code</td>
<td>varchar</td>
<td>市区区号</td>
<td></td>
</tr>
<tr>
<td>7</td>
<td>area_type</td>
<td>varchar</td>
<td>市区级别</td>
<td></td>
</tr>
<tr>
<td>8</td>
<td>update_time</td>
<td>varchar</td>
<td>更新时间</td>
<td></td>
</tr>
<tr>
<td>9</td>
<td>data_status</td>
<td>varchar</td>
<td>数据状态</td>
<td></td>
</tr>
<tr>
<td>10</td>
<td>f_tenant_id</td>
<td>varchar</td>
<td>租户id</td>
<td></td>
</tr>
<tr>
<td>11</td>
<td>f_flow_id</td>
<td>datetime</td>
<td>流程id</td>
<td><span class="index-key">索引</span></td>
</tr>
<tr>
<td>12</td>
<td>f_flow_task_id</td>
<td>datetime</td>
<td>流程任务主键</td>
<td></td>
</tr>
<tr>
<td>13</td>
<td>f_flow_state</td>
<td>bigint</td>
<td>流程任务状态</td>
<td></td>
</tr>
<!-- 额外行模拟更多字段 -->
<tr>
<td>14</td>
<td>f_created_by</td>
<td>varchar</td>
<td>创建人</td>
<td></td>
</tr>
<tr>
<td>15</td>
<td>f_updated_by</td>
<td>varchar</td>
<td>更新人</td>
<td></td>
</tr>
<tr>
<td>16</td>
<td>f_version</td>
<td>int</td>
<td>版本号</td>
<td></td>
</tr>
</tbody>
</table>
<!-- 1. 字段信息面板(滚动条修复) -->
<div id="field-info" class="content-panel active">
<div class="table-scroll-container">
<table>
<thead>
<tr>
<th>序号</th>
<th>字段名称</th>
<th>字段类型</th>
<th>描述</th>
<th>键类型</th>
</tr>
</thead>
<tbody th:each="c : ${columns}">
<tr>
<td th:text="${c.sort}"></td>
<td><strong th:text="${c.colName}"></strong></td>
<td th:text="${c.colType}"></td>
<td th:text="${c.colDesc}"></td>
<td><span class="primary-key" th:text="${c.keyType}"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 2. 生成SELECT语句面板滚动条修复 -->
<div id="generate-select" class="content-panel">
<div class="sql-container">
<div class="sql-toolbar">
<button class="sql-copy-btn" data-target="select-sql">
<i class="fas fa-copy"></i> 复制SQL
</button>
</div>
<div id="select-sql" class="sql-input">
<pre th:text="${selectSql}"></pre>
</div>
</div>
</div>
<!-- 3. 生成DDL语句面板滚动条修复 -->
<div id="generate-ddl" class="content-panel">
<div class="sql-container">
<div class="sql-toolbar">
<button class="sql-copy-btn" data-target="ddl-sql">
<i class="fas fa-copy"></i> 复制SQL
</button>
</div>
<div id="ddl-sql" class="sql-input">
<pre th:text="${ddlSql}"></pre>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 复制成功提示框 -->
<div class="copy-toast" id="copyToast">复制成功!</div>
<script>
// 1. 功能切换逻辑
const buttons = document.querySelectorAll('.button');
const contentPanels = document.querySelectorAll('.content-panel');
buttons.forEach(button => {
button.addEventListener('click', () => {
buttons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
const targetId = button.getAttribute('data-target');
contentPanels.forEach(panel => panel.classList.remove('active'));
const activePanel = document.getElementById(targetId);
activePanel.classList.add('active');
// 切换后重置滚动条位置(可选,提升体验)
const scrollContainer = activePanel.querySelector('.table-scroll-container, .sql-input');
if (scrollContainer) scrollContainer.scrollTop = 0;
});
});
// 2. SQL复制功能
const copyButtons = document.querySelectorAll('.sql-copy-btn');
const copyToast = document.getElementById('copyToast');
copyButtons.forEach(btn => {
btn.addEventListener('click', () => {
const targetId = btn.getAttribute('data-target');
const sqlInput = document.getElementById(targetId);
// 适配contenteditable复制
const range = document.createRange();
range.selectNodeContents(sqlInput);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
document.execCommand('copy');
selection.removeAllRanges();
// 显示提示
copyToast.classList.add('show');
setTimeout(() => copyToast.classList.remove('show'), 2000);
});
});
// 3. SQL编辑高亮逻辑
const sqlInputs = document.querySelectorAll('.sql-input');
const sqlKeywords = ['SELECT', 'FROM', 'WHERE', 'ORDER BY', 'GROUP BY', 'LIMIT', 'JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'ON', 'AND', 'OR', 'NOT', 'IN', 'NOT IN', 'BETWEEN', 'LIKE', 'CREATE TABLE', 'PRIMARY KEY', 'KEY', 'ENGINE', 'DEFAULT', 'CHARSET', 'COMMENT', 'NOT NULL', 'NULL', 'INT', 'VARCHAR', 'DATETIME', 'DECIMAL', 'TEXT', 'TINYINT', 'BIGINT'];
const sqlTables = ['work.biz_cities', 'idx_province_code', 'idx_f_flow_id', 'idx_tenant_delete'];
function highlightSQL(inputElement) {
let sqlText = inputElement.innerText;
// 高亮关键字(按长度排序,避免短关键字覆盖长关键字)
const sortedKeywords = [...sqlKeywords].sort((a, b) => b.length - a.length);
sortedKeywords.forEach(keyword => {
const regex = new RegExp(`\\b(${keyword})\\b`, 'gi');
sqlText = sqlText.replace(regex, '<span class="sql-keyword">$1</span>');
});
// 高亮表名
sqlTables.forEach(table => {
const regex = new RegExp(`\\b(${table})\\b`, 'g');
sqlText = sqlText.replace(regex, '<span class="sql-table">$1</span>');
});
// 高亮字段名(排除已高亮内容)
const originalText = inputElement.innerText;
const columnMatches = originalText.match(/\b([a-z_]+)\b/g) || [];
const validColumns = columnMatches.filter(col =>
!sqlKeywords.some(key => key.toUpperCase() === col.toUpperCase()) &&
!sqlTables.includes(col)
);
validColumns.forEach(column => {
const regex = new RegExp(`(?<!<span class="[^"]+">)\\b(${column})\\b(?!<\/span>)`, 'g');
sqlText = sqlText.replace(regex, '<span class="sql-column">$1</span>');
});
// 高亮注释
sqlText = sqlText.replace(/(-- .*)/g, '<span class="sql-comment">$1</span>');
// 保留光标位置
const selection = window.getSelection();
const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
const startOffset = range ? range.startOffset : 0;
inputElement.innerHTML = sqlText;
// 恢复光标
if (range && inputElement.firstChild) {
const newRange = document.createRange();
newRange.setStart(inputElement.firstChild, Math.min(startOffset, inputElement.firstChild.length));
newRange.setEnd(inputElement.firstChild, Math.min(startOffset, inputElement.firstChild.length));
selection.removeAllRanges();
selection.addRange(newRange);
}
}
// 初始化高亮
sqlInputs.forEach(input => highlightSQL(input));
// 编辑实时高亮
let highlightTimer;
sqlInputs.forEach(input => {
input.addEventListener('input', () => {
clearTimeout(highlightTimer);
highlightTimer = setTimeout(() => highlightSQL(input), 300);
});
});
</script>
</body>
</html>

View File

@@ -23,13 +23,20 @@
};
</script>
<style>
/* 弹窗占 80% 宽、60% 高 */
/* 弹窗尺寸配置 */
#detailDialog {
width: 65vw;
height: 80vh;
max-width: none;
max-height: none;
}
/* 表格卡片hover交互效果补充体验优化 */
.card-hover {
transition: box-shadow 0.2s ease;
}
.card-hover:hover {
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
}
</style>
</head>
<body class="bg-gray-50 font-inter text-neutral-dark">
@@ -63,9 +70,10 @@
</div>
</header>
<!-- 主内容两侧间距改为15px -->
<!-- 主内容 -->
<main class="pt-20 pb-8">
<div class="max-w-7xl mx-auto px-[30px]">
<div class="w-full px-[15px]">
<!-- 统计卡片 -->
<div class="bg-white rounded-xl shadow-md px-4 py-2 flex items-center justify-end space-x-1">
<span class="text-gray-500 text-sm"></span>
@@ -74,7 +82,7 @@
</div>
<!-- 表列表 -->
<div id="tableList" class="space-y-6 mt-6"></div>
<div id="tableList" class="space-y-6 mt-[5px]"></div>
</div>
</main>
@@ -90,7 +98,7 @@
const $ = sel => document.querySelector(sel);
const $$ = sel => document.querySelectorAll(sel);
/* Toast */
/* Toast 提示组件 */
function showToast(msg) {
const t = document.createElement('div');
t.className = 'fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg z-50';
@@ -100,7 +108,7 @@
}
/*****************************************************************
* 1. 加载数据库下拉框
* 1. 加载数据库下拉框数据
*****************************************************************/
fetch('getDbList')
.then(r => r.json())
@@ -117,7 +125,7 @@
.catch(() => showToast('获取数据库列表失败'));
/*****************************************************************
* 2. 加载/渲染表格卡片
* 2. 加载/渲染表格卡片列表
*****************************************************************/
function loadTableList() {
const dbId = $('#dbSelect').value;
@@ -171,7 +179,7 @@
$('#searchBtn').addEventListener('click', loadTableList);
/*****************************************************************
* 4. 表详情弹窗
* 4. 表详情弹窗控制
*****************************************************************/
function openDetailDialog(taskId) {
const dialog = $('#detailDialog');
@@ -180,7 +188,7 @@
dialog.showModal();
}
// 点击遮罩关闭弹窗
// 点击弹窗遮罩关闭弹窗
$('#detailDialog').addEventListener('click', e => {
if (e.target === $('#detailDialog')) $('#detailDialog').close();
});

View File

@@ -1,4 +1,3 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
<head>
<meta charset="UTF-8">
@@ -9,17 +8,24 @@
<!-- 引入Font Awesome图标库 -->
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 配置Tailwind自定义颜色 -->
<!-- 配置Tailwind自定义颜色和字体 -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#0FC6C2',
neutral: '#F5F7FA',
dark: '#1D2129',
light: '#F2F3F5'
primary: '#165DFF', // 深蓝色 - 主色调
'primary-light': '#E8F0FE', // 淡蓝色 - 辅助色
secondary: '#0FC6C2', // 青色 - 次要强调色
neutral: '#F5F7FA', // 浅灰 - 背景色
dark: '#1D2129', // 深灰 - 文本色
light: '#F2F3F5', // 更浅的灰 - 悬停背景
'tab-border': '#DADCE0', // 标签边框色
'tab-separator': '#E8EAED' // 标签分隔线色
},
fontSize: {
'sm': '0.875rem',
'base': '0.9375rem',
}
}
}
@@ -37,7 +43,7 @@
}
.menu-item {
@apply flex items-center gap-2 px-4 py-3 rounded-lg cursor-pointer transition-all duration-200;
@apply flex items-center gap-2 px-4 py-2.5 rounded-lg cursor-pointer transition-all duration-200 text-sm;
}
.menu-item-active {
@@ -56,137 +62,250 @@
animation: fadeOut 0.3s ease-in forwards;
}
.tab-active {
@apply bg-white text-primary border-b-2 border-primary;
/* 标签样式 */
.google-tab {
@apply relative px-3.5 py-1.5 cursor-pointer flex items-center space-x-1 text-xs transition-all duration-200 rounded-t-md border-y border-l border-transparent;
min-width: 80px;
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 1px;
height: 36px;
box-sizing: border-box;
}
.tab-inactive {
@apply bg-gray-100 text-gray-600 hover:bg-gray-200;
.google-tab-active {
@apply bg-primary text-white border-primary font-medium z-10;
box-shadow: 0 -2px 0 0 rgba(255,255,255,0.3) inset;
}
.google-tab-inactive {
@apply bg-primary-light text-primary/80 hover:bg-primary-light/80 border-transparent;
}
.google-tab-inactive:hover .close-tab {
@apply opacity-100;
}
.close-tab {
@apply w-4 h-4 flex items-center justify-center rounded-full hover:bg-white/20 text-xs opacity-0 transition-opacity duration-200;
}
.google-tab-active .close-tab {
@apply opacity-100 text-white;
}
.google-tab-active .close-tab:hover {
@apply bg-white/20;
}
/* 标签右键菜单样式 */
.tab-contextmenu {
@apply absolute bg-white rounded-lg shadow-lg py-1 z-50 border border-gray-200 w-48 hidden text-sm;
}
.tab-contextmenu-item {
@apply flex items-center gap-2 px-4 py-1.5 hover:bg-light transition-colors duration-200 cursor-pointer;
}
/* 提示对话框样式 */
.info-toast {
@apply fixed bg-white shadow-lg rounded-lg p-4 border border-gray-200 z-50 flex items-center gap-3 transform transition-all duration-300 opacity-0 translate-y-[-20px];
min-width: 280px;
max-width: 400px;
}
.info-toast i {
@apply text-primary;
}
/* 用户菜单样式 */
.user-menu {
@apply absolute right-0 mt-2 w-56 bg-white rounded-lg shadow-lg z-50 border border-gray-100 overflow-hidden;
}
.user-menu-actions {
@apply py-2;
}
.user-menu-action-item {
@apply flex items-center gap-3 px-5 py-2.5 text-sm hover:bg-light transition-colors duration-200 cursor-pointer w-full text-left;
}
/* 核心:彻底隐藏滚动条(跨浏览器兼容) */
.scrollbar-hide {
/* Firefox */
scrollbar-width: none;
/* IE/Edge */
-ms-overflow-style: none;
/* 禁止横向/纵向滚动 */
overflow-x: hidden !important;
overflow-y: hidden !important;
}
.scrollbar-hide::-webkit-scrollbar {
/* Chrome/Safari/Opera */
display: none !important;
width: 0 !important;
height: 0 !important;
}
/* 主内容区容器:精确设置四周边距 */
.main-content-wrapper {
@apply w-full h-[calc(100%-25px)] mt-4 mb-2.5 px-2.5; /* 上15px 下10px 左右10px精确匹配 */
}
.main-content-container {
@apply w-full h-full overflow-hidden bg-white rounded-lg shadow-sm border border-gray-100; /* 容器样式 */
}
/* 全局容器样式 */
.app-container {
@apply h-screen flex flex-col overflow-hidden;
}
/* 对话框菜单按钮样式 */
.modal-menu-btn {
@apply flex items-center gap-2 px-4 py-3 border border-gray-100 rounded-lg hover:border-primary hover:bg-primary/5 transition-colors cursor-pointer w-full justify-start;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-10px) scale(0.98);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
from { opacity: 0; transform: translateY(-10px) scale(0.98); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes fadeOut {
from {
opacity: 1;
transform: translateY(0) scale(1);
}
to {
opacity: 0;
transform: translateY(-10px) scale(0.98);
}
from { opacity: 1; transform: translateY(0) scale(1); }
to { opacity: 0; transform: translateY(-10px) scale(0.98); }
}
}
</style>
</head>
<body class="bg-neutral min-h-screen font-sans text-dark overflow-x-hidden">
<!-- 顶部导航栏 -->
<header class="bg-gradient-to-r from-blue-100 to-sky-200 shadow-sm sticky top-0 z-40 h-16">
<div class="flex items-center h-full px-6">
<!-- 1. 系统 Logo + 名称:最左侧固定 -->
<div class="flex items-center space-x-3 flex-shrink-0">
<div class="w-10 h-10 rounded-lg bg-primary flex items-center justify-center">
<i class="fa fa-cubes text-white text-xl"></i>
<!-- 全局容器:限制高度为屏幕高度,禁止滚动 -->
<body class="bg-neutral font-sans text-dark text-sm overflow-hidden">
<div class="app-container">
<!-- 顶部导航栏 -->
<header class="bg-primary-light shadow-sm sticky top-0 z-30 h-14 border-b border-gray-200">
<!-- 其余内容保持不变 -->
<div class="flex items-center h-full px-6">
<!-- 系统 Logo + 名称 -->
<div class="flex items-center space-x-2.5 flex-shrink-0">
<div class="w-9 h-9 rounded-lg bg-primary flex items-center justify-center">
<i class="fa fa-cubes text-white text-base"></i>
</div>
<h1 class="text-base font-bold whitespace-nowrap">智能管理平台</h1>
</div>
<!-- 模块导航 -->
<nav class="flex-1 flex justify-center items-center space-x-1" id="moduleContainer">
<div class="menu-item menu-item-inactive" id="loadingModules">
<i class="fa fa-spinner fa-spin"></i>
<span>加载模块中...</span>
</div>
</nav>
<!-- 用户信息 -->
<div class="relative flex-shrink-0" id="adminContainer">
<div class="menu-item menu-item-inactive flex items-center cursor-pointer" id="adminUser">
<i class="fa fa-user-circle"></i>
<span class="ml-2">加载用户中...</span>
<i class="fa fa-angle-down ml-1"></i>
</div>
<!-- 用户下拉菜单 -->
<div class="user-menu hidden" id="adminMenu">
<div class="user-menu-actions">
<div class="user-menu-action-item" id="logoutBtn">
<i class="fa fa-sign-out text-gray-500 w-4 text-center"></i>
<span>退出系统</span>
</div>
</div>
</div>
</div>
<h1 class="text-xl font-bold whitespace-nowrap">智能管理平台</h1>
</div>
</header>
<!-- 2. 模块导航:中间自适应 -->
<nav class="flex-1 flex justify-center items-center space-x-1" id="moduleContainer">
<!-- 动态加载的模块 -->
<div class="menu-item menu-item-inactive" id="loadingModules">
<i class="fa fa-spinner fa-spin text-lg"></i>
<span>加载模块中...</span>
<!-- 标签栏(无滚动条) -->
<div class="bg-white border-b border-tab-border relative z-20 h-9">
<div class="scrollbar-hide h-full w-full">
<div class="flex items-center space-x-0.5 px-3 min-w-max h-full" id="tabContainer">
<!-- 默认控制台标签 -->
<div class="google-tab google-tab-active" data-tab-id="console">
<i class="fa fa-home text-xs mr-1"></i>
<span>控制台</span>
<i class="fa fa-times close-tab ml-1" onclick="event.stopPropagation();"></i>
</div>
<!-- 新建标签按钮 -->
<button id="newTabBtn" class="w-8 h-8 flex items-center justify-center text-gray-500 hover:bg-gray-200 rounded-md transition-colors mx-0.5">
<i class="fa fa-plus text-xs"></i>
</button>
</div>
</nav>
<!-- 3. 用户信息:最右侧固定 -->
<div class="relative flex-shrink-0" id="adminContainer">
<div class="menu-item menu-item-inactive flex items-center cursor-pointer">
<i class="fa fa-user-circle text-xl"></i>
<span class="ml-2">加载用户中...</span>
<i class="fa fa-angle-down ml-1"></i>
</div>
<!-- 标签右键菜单 -->
<div class="tab-contextmenu" id="tabContextMenu">
<div class="tab-contextmenu-item" id="closeOtherTabs">
<i class="fa fa-window-close-o text-gray-500 text-xs"></i>
<span>关闭其他标签</span>
</div>
<!-- 下拉菜单内容 -->
<div class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg py-2 hidden z-50 border border-gray-100" id="adminMenu">
<a href="#" id="logoutBtn" class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-light transition-colors duration-200">
<i class="fa fa-sign-out text-gray-500"></i>
<span>退出系统</span>
</a>
<div class="tab-contextmenu-item" id="closeAllTabs">
<i class="fa fa-times-circle text-gray-500 text-xs"></i>
<span>关闭全部标签</span>
</div>
</div>
</div>
</header>
<!-- 标签栏 -->
<div class="bg-gradient-to-r from-blue-50 to-sky-100 border-b">
<div class="flex items-center">
<div class="flex-1 flex items-center space-x-1 px-4" id="tabContainer">
<!-- 默认显示控制台标签 -->
<div class="tab-active px-4 py-2 cursor-pointer flex items-center space-x-2" data-tab-id="console">
<span>控制台</span>
<i class="fa fa-home"></i>
<!-- 主内容区(精确设置四周边距) -->
<main class="flex-1 bg-neutral overflow-hidden p-0">
<!-- 外层容器实现精确的10px左右间距和其他边距 -->
<div class="main-content-wrapper">
<div class="main-content-container">
<iframe id="contentFrame" src="home" class="w-full border-0" style="overflow: hidden;"></iframe>
</div>
</div>
<button id="closeAllTabs" class="px-4 py-2 text-red-600 hover:bg-red-50 transition-colors">
<i class="fa fa-times-circle"></i> 关闭全部
</button>
</div>
</div>
</main>
<!-- 主内容区 -->
<main class="flex-1 bg-white">
<div class="h-full">
<iframe id="contentFrame" src="home" class="w-full h-[calc(100vh-120px)] border-0"></iframe>
<!-- 信息提示框 -->
<div id="infoToast" class="info-toast">
<i class="fa fa-info-circle text-primary text-lg"></i>
<p>请从顶部模块菜单选择要打开的功能</p>
</div>
</main>
<!-- 弹窗背景遮罩 -->
<div id="modalOverlay" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden opacity-0 transition-opacity duration-300"></div>
<!-- 弹窗背景遮罩 -->
<div id="modalOverlay" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden opacity-0 transition-opacity duration-300"></div>
<!-- 动态菜单弹窗 -->
<div id="dynamicModal" class="fixed bg-white rounded-xl shadow-xl z-50 hidden" style="width: 70%; left: 15%; top: 10%;">
<div class="p-6 border-b flex justify-between items-center">
<h3 class="text-lg font-semibold flex items-center" id="modalTitle">
<i class="modal-icon fa text-primary mr-2"></i>
<span id="modalModuleName"></span>
</h3>
<button class="modal-close text-gray-400 hover:text-gray-600">
<i class="fa fa-times"></i>
</button>
</div>
<div class="p-6" id="menuContainer">
<!-- 菜单将在这里动态加载 -->
<div class="flex justify-center items-center h-40">
<i class="fa fa-spinner fa-spin text-2xl text-gray-400"></i>
</div>
</div>
</div>
<!-- 退出确认弹窗 -->
<div id="logoutModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-[60] hidden flex items-center justify-center">
<div class="bg-white rounded-xl shadow-xl w-96 p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fa fa-exclamation-triangle text-yellow-500 mr-2"></i>
确认退出
</h3>
<p class="text-gray-600 mb-6">您确定要退出系统吗?</p>
<div class="flex justify-end space-x-3">
<button id="cancelLogout" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors">
取消
<!-- 动态菜单弹窗 -->
<div id="dynamicModal" class="fixed bg-white rounded-xl shadow-xl z-50 hidden" style="width: 70%; left: 15%; top: 10%;">
<div class="p-5 border-b flex justify-between items-center">
<h3 class="text-base font-semibold flex items-center" id="modalTitle">
<i class="modal-icon fa text-primary mr-2 text-sm"></i>
<span id="modalModuleName"></span>
</h3>
<button class="modal-close text-gray-400 hover:text-gray-600">
<i class="fa fa-times text-sm"></i>
</button>
<button id="confirmLogout" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors">
</div>
<div class="p-5" id="menuContainer">
<div class="flex justify-center items-center h-40">
<i class="fa fa-spinner fa-spin text-xl text-gray-400"></i>
</div>
</div>
</div>
<!-- 退出确认弹窗 -->
<div id="logoutModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-[60] hidden flex items-center justify-center">
<div class="bg-white rounded-xl shadow-xl w-96 p-5">
<h3 class="text-base font-semibold mb-4 flex items-center">
<i class="fa fa-exclamation-triangle text-yellow-500 mr-2 text-sm"></i>
确认退出
</button>
</h3>
<p class="text-gray-600 mb-5 text-sm">您确定要退出系统吗?</p>
<div class="flex justify-end space-x-3">
<button id="cancelLogout" class="px-3.5 py-1.5 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors text-sm">
取消
</button>
<button id="confirmLogout" class="px-3.5 py-1.5 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm">
确认退出
</button>
</div>
</div>
</div>
</div>
@@ -194,28 +313,23 @@
<script>
// 模拟API请求函数
const api = {
getModules: () =>
fetch('Sys/login/modules').then(res => res.json()),
getMenusByModuleCode: (moduleCode) =>
fetch(`Sys/login/menus?moduleCode=${moduleCode}`).then(res => res.json()),
getCurrentUser: () =>
fetch('Sys/login/me').then(res => res.json())
getModules: () => fetch('Sys/login/modules').then(res => res.json()),
getMenusByModuleCode: (moduleCode) => fetch(`Sys/login/menus?moduleCode=${moduleCode}`).then(res => res.json()),
getCurrentUser: () => fetch('Sys/login/me').then(res => res.json())
};
// 标签管理
let tabs = [
{ id: 'console', name: '控制台', url: 'home', closable: false }
];
// 标签管理核心数据
let tabs = [{ id: 'console', name: '控制台', url: 'home', closable: false, icon: 'fa-home' }];
let activeTabId = 'console';
let currentRightClickTabId = 'console';
// 页面逻辑
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function () {
// 元素获取
const moduleContainer = document.getElementById('moduleContainer');
const adminContainer = document.getElementById('adminContainer');
const adminUser = document.getElementById('adminUser');
const adminMenu = document.getElementById('adminMenu');
const menuContainer = document.getElementById('menuContainer');
const modalTitle = document.getElementById('modalTitle');
const modalModuleName = document.getElementById('modalModuleName');
const modalIcon = document.querySelector('.modal-icon');
const dynamicModal = document.getElementById('dynamicModal');
@@ -223,66 +337,140 @@
const modalCloseButtons = document.querySelectorAll('.modal-close');
const tabContainer = document.getElementById('tabContainer');
const contentFrame = document.getElementById('contentFrame');
const closeAllTabsBtn = document.getElementById('closeAllTabs');
const logoutBtn = document.getElementById('logoutBtn');
const logoutModal = document.getElementById('logoutModal');
const cancelLogout = document.getElementById('cancelLogout');
const confirmLogout = document.getElementById('confirmLogout');
const newTabBtn = document.getElementById('newTabBtn');
const infoToast = document.getElementById('infoToast');
const tabContextMenu = document.getElementById('tabContextMenu');
const closeOtherTabsBtn = document.getElementById('closeOtherTabs');
const closeAllTabsBtn = document.getElementById('closeAllTabs');
const logoutBtn = document.getElementById('logoutBtn');
// 加载模块列表
// 初始化功能
loadModules();
// 加载当前用户信息
loadCurrentUser();
initTabContextMenu();
bindModalEvents();
bindLogoutEvents();
initContentHeight(); // 初始化iframe高度考虑所有边距
window.addEventListener('resize', initContentHeight); // 窗口 resize 时重新计算高度
// 关闭全部标签页事件
closeAllTabsBtn.addEventListener('click', closeAllTabs);
// 新建标签按钮事件
newTabBtn.addEventListener('click', () => showInfoToast('请从顶部模块菜单选择要打开的功能'));
// 退出系统相关事件
logoutBtn.addEventListener('click', function(e) {
e.preventDefault();
document.getElementById('adminMenu').classList.add('hidden');
logoutModal.classList.remove('hidden');
// 用户菜单切换
adminUser.addEventListener('click', (e) => {
e.stopPropagation();
adminMenu.classList.toggle('hidden');
});
// 点击页面其他区域关闭用户菜单
document.addEventListener('click', () => adminMenu.classList.add('hidden'));
cancelLogout.addEventListener('click', function() {
logoutModal.classList.add('hidden');
});
/**
* 初始化主内容区高度(精确计算所有边距影响)
*/
function initContentHeight() {
// 强制设置body高度为窗口高度
document.body.style.height = `${window.innerHeight}px`;
confirmLogout.addEventListener('click', function() {
window.location.href = 'userLogout';
});
const headerHeight = document.querySelector('header').offsetHeight; // 顶部导航栏高度
const tabBarHeight = document.querySelector('.border-tab-border').offsetHeight; // 标签栏高度
const topSpacing = 16; // 与标签栏的顶部间距px
const bottomSpacing = 10; // 与底部的间距px
// 主内容区高度 = 窗口高度 - 顶部导航栏高度 - 标签栏高度 - 顶部间距 - 底部间距
const contentHeight = window.innerHeight - headerHeight - tabBarHeight - topSpacing - bottomSpacing;
// 强制设置iframe高度禁止内部滚动
contentFrame.style.height = `${contentHeight}px`;
contentFrame.style.overflow = 'hidden';
}
/**
* 显示信息提示框
*/
function showInfoToast(message, duration = 2000) {
infoToast.querySelector('p').textContent = message;
infoToast.style.left = '50%';
infoToast.style.top = '20%';
infoToast.style.transform = 'translate(-50%, 0)';
infoToast.classList.remove('opacity-0', 'translate-y-[-20px]');
infoToast.classList.add('opacity-100', 'translate-y-0');
setTimeout(() => {
infoToast.classList.remove('opacity-100', 'translate-y-0');
infoToast.classList.add('opacity-0', 'translate-y-[-20px]');
}, duration);
}
/**
* 初始化标签右键菜单功能
*/
function initTabContextMenu() {
tabContainer.addEventListener('contextmenu', function (e) {
const targetTab = e.target.closest('[data-tab-id]');
if (targetTab) {
e.preventDefault();
currentRightClickTabId = targetTab.getAttribute('data-tab-id');
const menuWidth = tabContextMenu.offsetWidth;
const menuHeight = tabContextMenu.offsetHeight;
tabContextMenu.style.left = `${Math.min(e.clientX, window.innerWidth - menuWidth)}px`;
tabContextMenu.style.top = `${Math.min(e.clientY, window.innerHeight - menuHeight)}px`;
tabContextMenu.classList.remove('hidden');
}
});
document.addEventListener('click', () => tabContextMenu.classList.add('hidden'));
tabContextMenu.addEventListener('click', (e) => e.stopPropagation());
closeOtherTabsBtn.addEventListener('click', function () {
tabs = tabs.filter(tab => tab.id === currentRightClickTabId || tab.id === 'console');
if (!tabs.some(tab => tab.id === activeTabId)) {
activeTabId = 'console';
contentFrame.src = 'home';
}
renderTabs();
tabContextMenu.classList.add('hidden');
});
closeAllTabsBtn.addEventListener('click', function () {
tabs = [{ id: 'console', name: '控制台', url: 'home', closable: false, icon: 'fa-home' }];
activeTabId = 'console';
contentFrame.src = 'home';
renderTabs();
tabContextMenu.classList.add('hidden');
});
}
/**
* 加载顶部模块导航
*/
async function loadModules() {
try {
const modules = await api.getModules();
moduleContainer.innerHTML = '';
modules.forEach(module => {
const moduleElement = document.createElement('div');
moduleElement.className = 'menu-item menu-item-inactive';
moduleElement.setAttribute('data-module-code', module.moduleCode);
moduleElement.setAttribute('data-module-name', module.moduleName);
moduleElement.setAttribute('data-module-icon', module.cicon);
moduleElement.innerHTML = `
<i class="fa ${module.cicon} text-lg"></i>
<i class="fa ${module.cicon}"></i>
<span>${module.moduleName}</span>
`;
moduleElement.addEventListener('click', function () {
const moduleCode = this.getAttribute('data-module-code');
const moduleName = this.getAttribute('data-module-name');
const moduleIcon = this.getAttribute('data-module-icon');
showModuleMenus(moduleCode, moduleName, moduleIcon, this);
const code = this.getAttribute('data-module-code');
const name = this.getAttribute('data-module-name');
const icon = this.getAttribute('data-module-icon');
showModuleMenus(code, name, icon, this);
});
moduleContainer.appendChild(moduleElement);
});
} catch (error) {
moduleContainer.innerHTML = `
<div class="menu-item menu-item-inactive text-red-500">
<i class="fa fa-exclamation-circle text-lg"></i>
<i class="fa fa-exclamation-circle"></i>
<span>模块加载失败</span>
</div>
`;
@@ -290,84 +478,64 @@
}
}
/**
* 加载当前用户信息
*/
async function loadCurrentUser() {
try {
const user = await api.getCurrentUser();
const usernameElement = adminUser.querySelector('span');
usernameElement.textContent = user.uname;
adminContainer.innerHTML = `
<div class="menu-item menu-item-inactive flex items-center justify-end cursor-pointer" id="adminUser">
<i class="fa fa-user-circle text-xl"></i>
<span class="ml-2">${user.uname}</span>
<i class="fa fa-angle-down ml-1"></i>
</div>
<div class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg py-2 hidden z-50 border border-gray-100" id="adminMenu">
<a href="#" id="logoutBtn" class="flex items-center gap-2 px-4 py-2 text-sm hover:bg-light transition-colors duration-200">
<i class="fa fa-sign-out text-gray-300"></i>
<span>退出系统</span>
</a>
</div>
`;
const adminUser = document.getElementById('adminUser');
const adminMenu = document.getElementById('adminMenu');
const newLogoutBtn = document.getElementById('logoutBtn');
adminUser.addEventListener('click', function (e) {
e.stopPropagation();
adminMenu.classList.toggle('hidden');
});
newLogoutBtn.addEventListener('click', function(e) {
// 绑定退出按钮事件
logoutBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
adminMenu.classList.add('hidden');
logoutModal.classList.remove('hidden');
});
} catch (error) {
adminContainer.innerHTML = `
<div class="menu-item menu-item-inactive flex items-center justify-end text-red-500">
<i class="fa fa-user-circle text-xl"></i>
<span class="ml-2">用户信息加载失败</span>
<i class="fa fa-angle-down ml-1"></i>
</div>
`;
const usernameElement = adminUser.querySelector('span');
usernameElement.textContent = '用户信息加载失败';
console.error('加载用户信息失败:', error);
}
}
/**
* 显示模块菜单弹窗
*/
async function showModuleMenus(moduleCode, moduleName, moduleIcon, menuItemElement) {
try {
modalModuleName.textContent = `${moduleName}菜单`;
modalIcon.className = `modal-icon fa ${moduleIcon} text-primary mr-2`;
modalIcon.className = `modal-icon fa ${moduleIcon} text-primary mr-2 text-sm`;
menuContainer.innerHTML = `
<div class="flex justify-center items-center h-40">
<i class="fa fa-spinner fa-spin text-2xl text-gray-400"></i>
<i class="fa fa-spinner fa-spin text-xl text-gray-400"></i>
</div>
`;
const menus = await api.getMenusByModuleCode(moduleCode);
if (menus.length > 0) {
// 菜单布局:图标在前、名称在后,使用网格布局排列
let menuHtml = '<div class="grid grid-cols-3 gap-4">';
menus.forEach(menu => {
menuHtml += `
<button class="flex flex-col items-center justify-center p-4 border border-gray-100 rounded-lg hover:border-primary hover:bg-primary/5 transition-colors"
onclick="openTab('${menu.menuCode}', '${menu.menuName}', '${menu.chref}')">
<i class="fa ${menu.cicon} text-primary text-2xl mb-2"></i>
<span>${menu.menuName}</span>
<button class="modal-menu-btn"
onclick="openTab('${menu.menuCode}', '${menu.menuName}', '${menu.chref}', '${menu.cicon}')">
<i class="fa ${menu.cicon} text-primary text-base"></i>
<span class="text-sm">${menu.menuName}</span>
</button>
`;
});
menuHtml += '</div>';
menuContainer.innerHTML = menuHtml;
} else {
menuContainer.innerHTML = `
<div class="flex flex-col justify-center items-center h-40 text-gray-500">
<i class="fa fa-folder-open-o text-3xl mb-2"></i>
<p>该模块暂无菜单</p>
<i class="fa fa-folder-open-o text-xl mb-2"></i>
<p class="text-sm">该模块暂无菜单</p>
</div>
`;
}
@@ -382,56 +550,39 @@
dynamicModal.classList.remove('hidden');
dynamicModal.classList.add('modal-enter');
modalOverlay.classList.remove('hidden');
setTimeout(() => {
modalOverlay.style.opacity = '1';
}, 10);
document.body.style.overflow = 'hidden';
} catch (error) {
menuContainer.innerHTML = `
<div class="flex flex-col justify-center items-center h-40 text-red-500">
<i class="fa fa-exclamation-circle text-3xl mb-2"></i>
<p>菜单加载失败</p>
<i class="fa fa-exclamation-circle text-xl mb-2"></i>
<p class="text-sm">菜单加载失败</p>
</div>
`;
console.error('加载菜单失败:', error);
}
}
function openTab(id, name, url) {
// 检查是否已存在该标签
/**
* 打开新标签
*/
function openTab(id, name, url, icon) {
const existingTab = tabs.find(tab => tab.id === id);
if (existingTab) {
switchTab(id);
} else {
// 添加新标签
tabs.push({ id, name, url, closable: true });
tabs.push({ id, name, url, closable: true, icon });
renderTabs();
switchTab(id);
}
closeModal();
}
function renderTabs() {
tabContainer.innerHTML = '';
tabs.forEach(tab => {
const tabElement = document.createElement('div');
tabElement.className = `px-4 py-2 cursor-pointer flex items-center space-x-2 border-b-2 transition-colors ${
tab.id === activeTabId ? 'bg-white text-primary border-primary' : 'bg-gray-100 text-gray-600 hover:bg-gray-200 border-transparent'
}`;
tabElement.setAttribute('data-tab-id', tab.id);
tabElement.innerHTML = `
<span>${tab.name}</span>
${tab.closable ? `<i class="fa fa-times text-xs hover:bg-red-100 hover:text-red-600 rounded-full p-1" onclick="event.stopPropagation(); closeTab('${tab.id}')"></i>` : `<i class="fa fa-home"></i>`}
`;
tabElement.addEventListener('click', () => switchTab(tab.id));
tabContainer.appendChild(tabElement);
});
}
/**
* 切换标签页
*/
function switchTab(id) {
activeTabId = id;
const activeTab = tabs.find(tab => tab.id === id);
@@ -441,70 +592,112 @@
}
}
/**
* 关闭指定标签页
*/
function closeTab(id) {
if (id === 'console') return; // 控制台不能关闭
if (id === 'console') return;
const tabIndex = tabs.findIndex(tab => tab.id === id);
if (tabIndex !== -1) {
tabs.splice(tabIndex, 1);
let newActiveTabId = activeTabId;
if (activeTabId === id) {
// 如果关闭的是当前标签,切换到控制台
activeTabId = 'console';
contentFrame.src = 'home';
newActiveTabId = tabIndex > 0 ? tabs[tabIndex - 1].id : 'console';
}
tabs.splice(tabIndex, 1);
activeTabId = newActiveTabId;
const activeTab = tabs.find(tab => tab.id === activeTabId);
if (activeTab) {
contentFrame.src = activeTab.url;
}
renderTabs();
}
}
function closeAllTabs() {
tabs = tabs.filter(tab => tab.id === 'console');
activeTabId = 'console';
contentFrame.src = 'home';
renderTabs();
/**
* 重新渲染所有标签
*/
function renderTabs() {
const tabContainer = document.getElementById('tabContainer');
const newTabBtn = document.getElementById('newTabBtn');
tabContainer.innerHTML = '';
tabs.forEach(tab => {
const tabElement = document.createElement('div');
tabElement.className = `google-tab ${tab.id === activeTabId ? 'google-tab-active' : 'google-tab-inactive'}`;
tabElement.setAttribute('data-tab-id', tab.id);
tabElement.innerHTML = `
<i class="fa ${tab.icon} text-xs mr-1"></i>
<span>${tab.name}</span>
<i class="fa fa-times close-tab ml-1" onclick="event.stopPropagation(); closeTab('${tab.id}')"></i>
`;
tabElement.addEventListener('click', () => switchTab(tab.id));
tabContainer.appendChild(tabElement);
});
tabContainer.appendChild(newTabBtn);
}
/**
* 关闭模态框
*/
function closeModal() {
const dynamicModal = document.getElementById('dynamicModal');
const modalOverlay = document.getElementById('modalOverlay');
dynamicModal.classList.add('hidden');
dynamicModal.classList.remove('modal-enter');
modalOverlay.classList.add('hidden');
modalOverlay.style.opacity = '0';
document.body.style.overflow = 'auto';
document.body.style.overflow = 'hidden'; // 保持全局无滚动
// 移除所有模块的激活状态
document.querySelectorAll('.menu-item').forEach(item => {
item.classList.remove('menu-item-active');
item.classList.add('menu-item-inactive');
});
}
// 将函数暴露到全局作用域
/**
* 绑定弹窗关闭事件
*/
function bindModalEvents() {
modalCloseButtons.forEach(button => {
button.addEventListener('click', closeModal);
});
modalOverlay.addEventListener('click', closeModal);
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
closeModal();
logoutModal.classList.add('hidden');
}
});
}
/**
* 绑定退出相关事件
*/
function bindLogoutEvents() {
cancelLogout.addEventListener('click', function () {
logoutModal.classList.add('hidden');
});
confirmLogout.addEventListener('click', function () {
window.location.href = 'userLogout';
});
}
// 全局函数暴露
window.openTab = openTab;
window.closeTab = closeTab;
// 弹窗关闭事件
modalCloseButtons.forEach(button => {
button.addEventListener('click', closeModal);
});
modalOverlay.addEventListener('click', closeModal);
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
closeModal();
logoutModal.classList.add('hidden');
}
});
// 下拉菜单关闭事件
document.addEventListener('click', function () {
const adminMenu = document.getElementById('adminMenu');
if (adminMenu) {
adminMenu.classList.add('hidden');
}
});
window.showInfoToast = showInfoToast;
});
</script>
</body>
</html>
</html>

View File

@@ -0,0 +1,312 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Font Awesome 4.7.0 所有图标</title>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#2563eb',
secondary: '#64748b',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.icon-card {
@apply bg-white rounded-lg shadow-md p-4 transition-all duration-300 hover:shadow-lg hover:scale-[1.02] border border-gray-100;
}
.icon-display {
@apply text-3xl mb-2 text-primary;
}
.icon-code {
@apply text-sm bg-gray-100 p-2 rounded font-mono text-secondary break-all;
}
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<header class="bg-primary text-white py-8 px-4 shadow-lg">
<div class="container mx-auto">
<h1 class="text-3xl md:text-4xl font-bold mb-2">Font Awesome 4.7.0 图标库</h1>
<p class="text-blue-100">完整展示所有图标及其代码</p>
</div>
</header>
<main class="container mx-auto py-8 px-4">
<div class="mb-8">
<div class="relative">
<input type="text" id="search" placeholder="搜索图标..."
class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary">
<i class="fa fa-search absolute right-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
</div>
<div class="mt-4 flex flex-wrap gap-2">
<button class="category-btn px-3 py-1 bg-primary text-white rounded-full text-sm" data-category="all">全部</button>
<button class="category-btn px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded-full text-sm" data-category="web-application">Web应用</button>
<button class="category-btn px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded-full text-sm" data-category="text-editor">文本编辑</button>
<button class="category-btn px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded-full text-sm" data-category="directional">方向</button>
<button class="category-btn px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded-full text-sm" data-category="video-player">视频播放</button>
<button class="category-btn px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded-full text-sm" data-category="brand">品牌</button>
<button class="category-btn px-3 py-1 bg-gray-200 hover:bg-gray-300 rounded-full text-sm" data-category="medical">医疗</button>
</div>
</div>
<div id="icons-container" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
<!-- 图标将通过JavaScript动态加载 -->
</div>
</main>
<footer class="bg-gray-800 text-white py-6 px-4 mt-12">
<div class="container mx-auto text-center">
<p>Font Awesome 4.7.0 图标展示 | 使用 <a href="https://fontawesome.com/v4/" class="text-blue-300 hover:underline">Font Awesome</a></p>
</div>
</footer>
<script>
// Font Awesome 4.7.0 所有图标数据(按类别分组)
const fontAwesomeIcons = {
"web-application": [
"fa-adjust", "fa-anchor", "fa-archive", "fa-area-chart", "fa-arrows", "fa-arrows-alt",
"fa-arrows-h", "fa-arrows-v", "fa-asterisk", "fa-at", "fa-ban", "fa-bar-chart",
"fa-bar-chart-o", "fa-bars", "fa-bath", "fa-bathtub", "fa-battery-0", "fa-battery-1",
"fa-battery-2", "fa-battery-3", "fa-battery-4", "fa-battery-empty", "fa-battery-full",
"fa-battery-half", "fa-battery-quarter", "fa-battery-three-quarters", "fa-bed",
"fa-beer", "fa-bell", "fa-bell-o", "fa-bell-slash", "fa-bell-slash-o", "fa-bicycle",
"fa-binoculars", "fa-birthday-cake", "fa-bolt", "fa-bomb", "fa-book", "fa-bookmark",
"fa-bookmark-o", "fa-briefcase", "fa-bullhorn", "fa-bullseye", "fa-calendar",
"fa-calendar-check-o", "fa-calendar-o", "fa-calendar-plus-o", "fa-calendar-times-o",
"fa-camera", "fa-camera-retro", "fa-car", "fa-caret-down", "fa-caret-left",
"fa-caret-right", "fa-caret-up", "fa-cart-arrow-down", "fa-cart-plus", "fa-cc",
"fa-certificate", "fa-check", "fa-check-circle", "fa-check-circle-o", "fa-check-square",
"fa-check-square-o", "fa-child", "fa-circle", "fa-circle-o", "fa-circle-o-notch",
"fa-circle-thin", "fa-clipboard", "fa-clock-o", "fa-clone", "fa-cloud", "fa-cloud-download",
"fa-cloud-upload", "fa-code", "fa-code-fork", "fa-coffee", "fa-cog", "fa-cogs", "fa-columns",
"fa-comment", "fa-comment-o", "fa-comments", "fa-comments-o", "fa-compass", "fa-credit-card",
"fa-cutlery", "fa-dashboard", "fa-database", "fa-diamond", "fa-download", "fa-edit",
"fa-ellipsis-h", "fa-ellipsis-v", "fa-envelope", "fa-envelope-o", "fa-envelope-open",
"fa-envelope-open-o", "fa-envelope-square", "fa-eraser", "fa-exclamation", "fa-exclamation-circle",
"fa-exclamation-triangle", "fa-external-link", "fa-external-link-square", "fa-eye", "fa-eye-slash",
"fa-fax", "fa-file", "fa-file-archive-o", "fa-file-audio-o", "fa-file-code-o", "fa-file-excel-o",
"fa-file-image-o", "fa-file-pdf-o", "fa-file-powerpoint-o", "fa-file-text", "fa-file-text-o",
"fa-file-video-o", "fa-file-word-o", "fa-film", "fa-filter", "fa-fire", "fa-fire-extinguisher",
"fa-flag", "fa-flag-checkered", "fa-flag-o", "fa-flask", "fa-folder", "fa-folder-o",
"fa-folder-open", "fa-folder-open-o", "fa-frown-o", "fa-gamepad", "fa-gavel", "fa-gear",
"fa-gears", "fa-gift", "fa-glass", "fa-globe", "fa-graduation-cap", "fa-hand-lizard-o",
"fa-hand-paper-o", "fa-hand-peace-o", "fa-hand-pointer-o", "fa-hand-rock-o", "fa-hand-scissors-o",
"fa-hand-spock-o", "fa-hand-stop-o", "fa-hdd-o", "fa-headphones", "fa-heart", "fa-heart-o",
"fa-history", "fa-home", "fa-horoscope", "fa-hospital-o", "fa-hourglass", "fa-hourglass-1",
"fa-hourglass-2", "fa-hourglass-3", "fa-hourglass-end", "fa-hourglass-half", "fa-hourglass-o",
"fa-hourglass-start", "fa-i-cursor", "fa-inbox", "fa-indent", "fa-info", "fa-info-circle",
"fa-institution", "fa-key", "fa-keyboard-o", "fa-language", "fa-laptop", "fa-legal",
"fa-leaf", "fa-leanpub", "fa-legal", "fa-leaf", "fa-leanpub", "fa-level-down", "fa-level-up",
"fa-life-ring", "fa-lightbulb-o", "fa-line-chart", "fa-link", "fa-list", "fa-list-alt",
"fa-list-ol", "fa-list-ul", "fa-lock", "fa-magic", "fa-magnet", "fa-mail-forward",
"fa-mail-reply", "fa-mail-reply-all", "fa-male", "fa-map", "fa-map-marker", "fa-map-o",
"fa-map-pin", "fa-map-signs", "fa-meh-o", "fa-microchip", "fa-microphone", "fa-microphone-slash",
"fa-minus", "fa-minus-circle", "fa-minus-square", "fa-minus-square-o", "fa-mobile",
"fa-mobile-phone", "fa-money", "fa-moon-o", "fa-motorcycle", "fa-mouse-pointer", "fa-music",
"fa-navicon", "fa-newspaper-o", "fa-ellipsis-h", "fa-ellipsis-v", "fa-envelope",
"fa-envelope-o", "fa-envelope-open", "fa-envelope-open-o", "fa-envelope-square", "fa-eraser",
"fa-exclamation", "fa-exclamation-circle", "fa-exclamation-triangle", "fa-external-link",
"fa-external-link-square", "fa-eye", "fa-eye-slash", "fa-fax", "fa-file",
"fa-file-archive-o", "fa-file-audio-o", "fa-file-code-o", "fa-file-excel-o",
"fa-file-image-o", "fa-file-pdf-o", "fa-file-powerpoint-o", "fa-file-text",
"fa-file-text-o", "fa-file-video-o", "fa-file-word-o", "fa-film", "fa-filter",
"fa-fire", "fa-fire-extinguisher", "fa-flag", "fa-flag-checkered", "fa-flag-o",
"fa-flask", "fa-folder", "fa-folder-o", "fa-folder-open", "fa-folder-open-o",
"fa-frown-o", "fa-gamepad", "fa-gavel", "fa-gear", "fa-gears", "fa-gift",
"fa-glass", "fa-globe", "fa-graduation-cap", "fa-hand-lizard-o", "fa-hand-paper-o",
"fa-hand-peace-o", "fa-hand-pointer-o", "fa-hand-rock-o", "fa-hand-scissors-o",
"fa-hand-spock-o", "fa-hand-stop-o", "fa-hdd-o", "fa-headphones", "fa-heart",
"fa-heart-o", "fa-history", "fa-home", "fa-horoscope", "fa-hospital-o", "fa-hourglass",
"fa-hourglass-1", "fa-hourglass-2", "fa-hourglass-3", "fa-hourglass-end",
"fa-hourglass-half", "fa-hourglass-o", "fa-hourglass-start", "fa-i-cursor",
"fa-inbox", "fa-indent", "fa-info", "fa-info-circle", "fa-institution", "fa-key",
"fa-keyboard-o", "fa-language", "fa-laptop", "fa-legal", "fa-leaf", "fa-leanpub",
"fa-legal", "fa-leaf", "fa-leanpub", "fa-level-down", "fa-level-up", "fa-life-ring",
"fa-lightbulb-o", "fa-line-chart", "fa-link", "fa-list", "fa-list-alt", "fa-list-ol",
"fa-list-ul", "fa-lock", "fa-magic", "fa-magnet", "fa-mail-forward", "fa-mail-reply",
"fa-mail-reply-all", "fa-male", "fa-map", "fa-map-marker", "fa-map-o", "fa-map-pin",
"fa-map-signs", "fa-meh-o", "fa-microchip", "fa-microphone", "fa-microphone-slash",
"fa-minus", "fa-minus-circle", "fa-minus-square", "fa-minus-square-o", "fa-mobile",
"fa-mobile-phone", "fa-money", "fa-moon-o", "fa-motorcycle", "fa-mouse-pointer",
"fa-music", "fa-navicon"
],
"text-editor": [
"fa-align-center", "fa-align-justify", "fa-align-left", "fa-align-right",
"fa-bold", "fa-italic", "fa-underline", "fa-strikethrough", "fa-subscript",
"fa-superscript", "fa-eraser", "fa-font", "fa-header", "fa-indent", "fa-outdent",
"fa-list", "fa-list-ol", "fa-list-ul", "fa-paperclip", "fa-paragraph", "fa-pencil",
"fa-pencil-square", "fa-pencil-square-o", "fa-repeat", "fa-rotate-left", "fa-rotate-right",
"fa-save", "fa-strikethrough", "fa-undo"
],
"directional": [
"fa-angle-double-down", "fa-angle-double-left", "fa-angle-double-right",
"fa-angle-double-up", "fa-angle-down", "fa-angle-left", "fa-angle-right",
"fa-angle-up", "fa-arrow-circle-down", "fa-arrow-circle-left", "fa-arrow-circle-o-down",
"fa-arrow-circle-o-left", "fa-arrow-circle-o-right", "fa-arrow-circle-o-up",
"fa-arrow-circle-right", "fa-arrow-circle-up", "fa-arrow-down", "fa-arrow-left",
"fa-arrow-right", "fa-arrow-up", "fa-arrows", "fa-arrows-alt", "fa-arrows-h",
"fa-arrows-v", "fa-backward", "fa-chevron-circle-down", "fa-chevron-circle-left",
"fa-chevron-circle-right", "fa-chevron-circle-up", "fa-chevron-down", "fa-chevron-left",
"fa-chevron-right", "fa-chevron-up", "fa-hand-o-down", "fa-hand-o-left", "fa-hand-o-right",
"fa-hand-o-up", "fa-level-down", "fa-level-up", "fa-long-arrow-down", "fa-long-arrow-left",
"fa-long-arrow-right", "fa-long-arrow-up", "fa-refresh", "fa-repeat", "fa-rotate-left",
"fa-rotate-right", "fa-step-backward", "fa-step-forward"
],
"video-player": [
"fa-backward", "fa-pause", "fa-pause-circle", "fa-pause-circle-o", "fa-play",
"fa-play-circle", "fa-play-circle-o", "fa-step-backward", "fa-step-forward",
"fa-stop", "fa-stop-circle", "fa-stop-circle-o", "fa-forward", "fa-eject",
"fa-volume-down", "fa-volume-off", "fa-volume-up", "fa-music", "fa-film"
],
"brand": [
"fa-500px", "fa-amazon", "fa-android", "fa-angellist", "fa-apple", "fa-archive",
"fa-area-chart", "fa-atlassian", "fa-automobile", "fa-bank", "fa-bandcamp",
"fa-btc", "fa-buffer", "fa-buysellads", "fa-cc-amex", "fa-cc-discover",
"fa-cc-mastercard", "fa-cc-paypal", "fa-cc-stripe", "fa-cc-visa", "fa-chrome",
"fa-cloudflare", "fa-codepen", "fa-connectdevelop", "fa-contao", "fa-css3",
"fa-dashcube", "fa-delicious", "fa-deviantart", "fa-digg", "fa-dribbble",
"fa-dropbox", "fa-drupal", "fa-edge", "fa-empire", "fa-envira", "fa-etsy",
"fa-eur", "fa-facebook", "fa-facebook-f", "fa-facebook-official", "fa-facebook-square",
"fa-firefox", "fa-flickr", "fa-font-awesome", "fa-forumbee", "fa-foursquare",
"fa-github", "fa-github-alt", "fa-github-square", "fa-gittip", "fa-glide",
"fa-glide-g", "fa-google", "fa-google-plus", "fa-google-plus-circle",
"fa-google-plus-official", "fa-google-plus-square", "fa-google-wallet", "fa-gratipay",
"fa-grav", "fa-hacker-news", "fa-hacker-news-square", "fa-hipchat", "fa-hooli",
"fa-html5", "fa-instagram", "fa-internet-explorer", "fa-ioxhost", "fa-joomla",
"fa-jpy", "fa-js", "fa-js-square", "fa-jsfiddle", "fa-leanpub", "fa-linkedin",
"fa-linkedin-square", "fa-linux", "fa-maxcdn", "fa-medium", "fa-meetup", "fa-microsoft",
"fa-minus-square", "fa-minus-square-o", "fa-mixcloud", "fa-modx", "fa-monero",
"fa-usd", "fa-reddit", "fa-reddit-alien", "fa-reddit-square", "fa-renren",
"fa-rmb", "fa-rocketchat", "fa-rotate-right", "fa-rss", "fa-rss-square",
"fa-rub", "fa-ruble", "fa-safari", "fa-steam", "fa-steam-square", "fa-stumbleupon",
"fa-stumbleupon-circle", "fa-superpowers", "fa-telegram", "fa-television", "fa-tencent-weibo",
"fa-thumb-tack", "fa-try", "fa-tumblr", "fa-tumblr-square", "fa-twitch", "fa-twitter",
"fa-twitter-square", "fa-usd", "fa-ubuntu", "fa-umbrella", "fa-underline", "fa-unsplash",
"fa-upload", "fa-usb", "fa-user-md", "fa-venus", "fa-venus-double", "fa-vimeo",
"fa-vimeo-square", "fa-vine", "fa-vk", "fa-vuejs", "fa-wechat", "fa-weibo",
"fa-weixin", "fa-whatsapp", "fa-whatsapp-square", "fa-wikipedia-w", "fa-window-close",
"fa-window-close-o", "fa-window-maximize", "fa-window-minimize", "fa-window-restore",
"fa-windows", "fa-wordpress", "fa-wpbeginner", "fa-wpforms", "fa-wrench", "fa-xing",
"fa-xing-square", "fa-y-combinator", "fa-yahoo", "fa-yelp", "fa-yen", "fa-youtube",
"fa-youtube-play", "fa-youtube-square"
],
"medical": [
"fa-ambulance", "fa-heartbeat", "fa-medkit", "fa-stethoscope", "fa-user-md",
"fa-medkit", "fa-heartbeat", "fa-plus-square", "fa-minus-square", "fa-thermometer",
"fa-thermometer-0", "fa-thermometer-1", "fa-thermometer-2", "fa-thermometer-3",
"fa-thermometer-4", "fa-thermometer-empty", "fa-thermometer-full", "fa-thermometer-half",
"fa-thermometer-quarter", "fa-thermometer-three-quarters", "fa-medkit"
]
};
// 生成所有图标HTML
function renderIcons(icons, category) {
const container = document.getElementById('icons-container');
container.innerHTML = '';
icons.forEach(icon => {
const iconCard = document.createElement('div');
iconCard.className = 'icon-card';
iconCard.setAttribute('data-category', category);
iconCard.setAttribute('data-name', icon);
iconCard.innerHTML = `
<i class="fa ${icon} icon-display"></i>
<p class="font-medium mb-2">${icon.replace('fa-', '')}</p>
<div class="icon-code">&lt;i class="fa ${icon}"&gt;&lt;/i&gt;</div>
`;
container.appendChild(iconCard);
});
}
// 初始渲染所有图标
function renderAllIcons() {
const allIcons = [];
for (const category in fontAwesomeIcons) {
fontAwesomeIcons[category].forEach(icon => {
allIcons.push({icon, category});
});
}
const container = document.getElementById('icons-container');
container.innerHTML = '';
allIcons.forEach(item => {
const iconCard = document.createElement('div');
iconCard.className = 'icon-card';
iconCard.setAttribute('data-category', item.category);
iconCard.setAttribute('data-name', item.icon);
iconCard.innerHTML = `
<i class="fa ${item.icon} icon-display"></i>
<p class="font-medium mb-2">${item.icon.replace('fa-', '')}</p>
<div class="icon-code">&lt;i class="fa ${item.icon}"&gt;&lt;/i&gt;</div>
`;
container.appendChild(iconCard);
});
}
// 搜索功能
document.getElementById('search').addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
const iconCards = document.querySelectorAll('.icon-card');
iconCards.forEach(card => {
const iconName = card.getAttribute('data-name');
if (iconName.includes(searchTerm)) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
});
// 分类筛选
document.querySelectorAll('.category-btn').forEach(btn => {
btn.addEventListener('click', function() {
// 更新按钮样式
document.querySelectorAll('.category-btn').forEach(b => {
b.classList.remove('bg-primary', 'text-white');
b.classList.add('bg-gray-200', 'hover:bg-gray-300');
});
this.classList.remove('bg-gray-200', 'hover:bg-gray-300');
this.classList.add('bg-primary', 'text-white');
const category = this.getAttribute('data-category');
const iconCards = document.querySelectorAll('.icon-card');
if (category === 'all') {
iconCards.forEach(card => {
card.style.display = 'block';
});
} else {
iconCards.forEach(card => {
if (card.getAttribute('data-category') === category) {
card.style.display = 'block';
} else {
card.style.display = 'none';
}
});
}
});
});
// 页面加载完成后渲染所有图标
window.addEventListener('DOMContentLoaded', renderAllIcons);
</script>
</body>
</html>