Files
c-api/src/main/resources/templates/views/data/detail.html

780 lines
24 KiB
HTML
Raw Normal View History

2025-08-29 18:08:32 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>市区信息表结构</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
body {
2025-08-30 15:26:27 +08:00
background: linear-gradient(135deg, #e6f7ff 0%, #f0f7ff 100%);
2025-08-29 18:08:32 +08:00
min-height: 100vh;
2025-08-30 15:26:27 +08:00
padding: 10px; /* 核心距离页面两边10px */
2025-08-29 18:08:32 +08:00
color: #2c3e50;
2025-08-30 15:26:27 +08:00
overflow: hidden;
2025-08-29 18:08:32 +08:00
}
.container {
2025-08-30 15:26:27 +08:00
max-width: 100%;
margin: 0 auto; /* 水平居中同时借助body的10px padding实现边缘距离 */
2025-08-29 18:08:32 +08:00
display: flex;
2025-08-30 15:26:27 +08:00
gap: 15px;
height: calc(100vh - 20px); /* 减去body上下10px padding占满垂直空间 */
2025-08-29 18:08:32 +08:00
}
.panel {
background: white;
border-radius: 16px;
2025-08-30 15:26:27 +08:00
box-shadow: 0 4px 20px rgba(0, 120, 255, 0.08);
2025-08-29 18:08:32 +08:00
overflow: hidden;
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
2025-08-30 15:26:27 +08:00
height: 100%; /* 面板高度铺满容器,避免内容溢出 */
2025-08-29 18:08:32 +08:00
}
.panel:hover {
2025-08-30 15:26:27 +08:00
transform: translateY(-3px);
2025-08-29 18:08:32 +08:00
}
.panel-header {
2025-08-30 15:26:27 +08:00
background: linear-gradient(135deg, #4096ff 0%, #165dff 100%);
2025-08-29 18:08:32 +08:00
color: white;
2025-08-30 15:26:27 +08:00
padding: 16px 20px;
font-size: 1.2rem;
2025-08-29 18:08:32 +08:00
border-bottom: 1px solid rgba(255, 255, 255, 0.15);
2025-08-30 15:26:27 +08:00
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0; /* 表头不压缩 */
2025-08-29 18:08:32 +08:00
}
.panel-content {
2025-08-30 15:26:27 +08:00
padding: 20px;
flex: 1; /* 内容区占据剩余高度 */
2025-08-29 18:08:32 +08:00
display: flex;
flex-direction: column;
2025-08-30 15:26:27 +08:00
overflow: hidden; /* 关键:父容器隐藏溢出,确保子滚动容器生效 */
2025-08-29 18:08:32 +08:00
}
.left-panel {
flex: 1;
}
.right-panel {
flex: 2;
}
.info-card {
2025-08-30 15:26:27 +08:00
background: #eef7ff;
2025-08-29 18:08:32 +08:00
border-radius: 12px;
2025-08-30 15:26:27 +08:00
padding: 18px;
margin-bottom: 18px;
border-left: 4px solid #4096ff;
flex-shrink: 0; /* 信息卡片不压缩 */
2025-08-29 18:08:32 +08:00
}
.info-row {
display: flex;
2025-08-30 15:26:27 +08:00
margin-bottom: 12px;
2025-08-29 18:08:32 +08:00
align-items: center;
2025-08-30 15:26:27 +08:00
line-height: 1.5;
2025-08-29 18:08:32 +08:00
}
.info-label {
font-weight: 600;
2025-08-30 15:26:27 +08:00
color: #165dff;
width: 110px;
2025-08-29 18:08:32 +08:00
position: relative;
}
.info-label::after {
content: ":";
position: absolute;
right: 8px;
}
.info-value {
flex: 1;
color: #343a40;
}
.button-panel {
display: flex;
2025-08-30 15:26:27 +08:00
gap: 10px;
margin-bottom: 15px;
border-bottom: 1px solid #e6f0ff;
padding-bottom: 12px;
flex-shrink: 0; /* 按钮区不压缩,固定高度 */
flex-wrap: wrap; /* 小屏幕按钮换行 */
2025-08-29 18:08:32 +08:00
}
.button {
2025-08-30 15:26:27 +08:00
background: linear-gradient(135deg, #f0f7ff 0%, #e6f0ff 100%);
padding: 9px 18px;
2025-08-29 18:08:32 +08:00
border-radius: 8px;
font-weight: 600;
2025-08-30 15:26:27 +08:00
color: #165dff;
2025-08-29 18:08:32 +08:00
cursor: pointer;
2025-08-30 15:26:27 +08:00
border: 1px solid #d1e0ff;
transition: all 0.25s ease;
display: flex;
align-items: center;
gap: 6px;
2025-08-29 18:08:32 +08:00
}
.button:hover {
2025-08-30 15:26:27 +08:00
background: linear-gradient(135deg, #e6f0ff 0%, #d1e0ff 100%);
box-shadow: 0 3px 6px rgba(64, 150, 255, 0.1);
2025-08-29 18:08:32 +08:00
}
.button.active {
2025-08-30 15:26:27 +08:00
background: linear-gradient(135deg, #4096ff 0%, #165dff 100%);
2025-08-29 18:08:32 +08:00
color: white;
2025-08-30 15:26:27 +08:00
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; /* 最小高度,避免内容过少时容器塌陷 */
2025-08-29 18:08:32 +08:00
}
table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: 12px;
overflow: hidden;
background: white;
2025-08-30 15:26:27 +08:00
table-layout: fixed; /* 固定列宽,避免表头错位 */
min-width: 600px; /* 表格最小宽度,确保小屏幕触发水平滚动 */
2025-08-29 18:08:32 +08:00
}
th {
2025-08-30 15:26:27 +08:00
background: linear-gradient(135deg, #4096ff 0%, #165dff 100%);
2025-08-29 18:08:32 +08:00
color: white;
font-weight: 600;
2025-08-30 15:26:27 +08:00
padding: 14px 15px;
2025-08-29 18:08:32 +08:00
text-align: left;
border-right: 1px solid rgba(255, 255, 255, 0.15);
position: sticky;
top: 0;
2025-08-30 15:26:27 +08:00
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;
2025-08-29 18:08:32 +08:00
}
2025-08-30 15:26:27 +08:00
/* 描述(增加宽度,减少换行) */
th:nth-child(5) {
width: 120px;
}
/* 键类型 */
2025-08-29 18:08:32 +08:00
th:last-child {
border-right: none;
}
td {
2025-08-30 15:26:27 +08:00
padding: 12px 15px;
border-bottom: 1px solid #e6f0ff;
2025-08-29 18:08:32 +08:00
color: #495057;
2025-08-30 15:26:27 +08:00
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;
2025-08-29 18:08:32 +08:00
}
tr:nth-child(even) {
2025-08-30 15:26:27 +08:00
background-color: #f7fbff;
2025-08-29 18:08:32 +08:00
}
tr:hover td {
2025-08-30 15:26:27 +08:00
background-color: rgba(64, 150, 255, 0.08);
2025-08-29 18:08:32 +08:00
}
.primary-key {
2025-08-30 15:26:27 +08:00
background-color: #e6f4ff;
color: #165dff;
2025-08-29 18:08:32 +08:00
font-weight: 600;
2025-08-30 15:26:27 +08:00
padding: 2px 8px;
2025-08-29 18:08:32 +08:00
border-radius: 50px;
2025-08-30 15:26:27 +08:00
font-size: 0.8rem;
2025-08-29 18:08:32 +08:00
}
.index-key {
background-color: #e8f5e9;
2025-08-30 15:26:27 +08:00
color: #00864e;
padding: 2px 8px;
2025-08-29 18:08:32 +08:00
border-radius: 50px;
2025-08-30 15:26:27 +08:00
font-size: 0.8rem;
2025-08-29 18:08:32 +08:00
}
.stats-row {
display: flex;
2025-08-30 15:26:27 +08:00
gap: 15px;
margin-top: 12px;
2025-08-29 18:08:32 +08:00
color: #6c757d;
2025-08-30 15:26:27 +08:00
font-size: 0.85rem;
flex-shrink: 0; /* 统计行不压缩 */
2025-08-29 18:08:32 +08:00
}
.stat-item {
display: flex;
align-items: center;
2025-08-30 15:26:27 +08:00
gap: 5px;
2025-08-29 18:08:32 +08:00
}
.time-ago {
2025-08-30 15:26:27 +08:00
color: #ff4d4f;
2025-08-29 18:08:32 +08:00
font-weight: 500;
2025-08-30 15:26:27 +08:00
font-size: 0.85rem;
2025-08-29 18:08:32 +08:00
}
2025-08-30 15:26:27 +08:00
/* 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; /* 最小高度,避免塌陷 */
2025-08-29 18:08:32 +08:00
}
2025-08-30 15:26:27 +08:00
.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;
2025-08-29 18:08:32 +08:00
}
2025-08-30 15:26:27 +08:00
/* 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高亮样式不破坏格式 */
2025-08-30 15:38:11 +08:00
.sql-keyword {
color: #d73a49;
font-weight: 600;
}
.sql-table {
color: #005cc5;
}
.sql-column {
color: #6f42c1;
}
.sql-comment {
color: #6a737d;
}
2025-08-30 15:26:27 +08:00
/* 复制提示 */
.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 {
2025-08-29 18:08:32 +08:00
width: 8px;
height: 8px;
}
2025-08-30 15:26:27 +08:00
::-webkit-scrollbar-thumb {
background: #4096ff;
2025-08-29 18:08:32 +08:00
border-radius: 4px;
2025-08-30 15:26:27 +08:00
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: #165dff;
2025-08-29 18:08:32 +08:00
}
2025-08-30 15:26:27 +08:00
::-webkit-scrollbar-track {
background: rgba(64, 150, 255, 0.1);
2025-08-29 18:08:32 +08:00
border-radius: 4px;
}
2025-08-30 15:26:27 +08:00
/* 内容面板切换基础样式 */
.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; /* 垂直布局时容器高度自适应 */
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;
}
}
@media (max-width: 600px) {
.button {
padding: 8px 12px;
font-size: 0.85rem;
}
.info-label {
width: 90px;
}
/* 超小屏幕表格列宽最小化 */
th {
padding: 10px 8px;
}
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;
}
}
2025-08-29 18:08:32 +08:00
</style>
</head>
<body>
<div class="container">
2025-08-30 15:26:27 +08:00
<!-- 左侧面板:表基础信息 -->
2025-08-29 18:08:32 +08:00
<div class="panel left-panel">
<div class="panel-header">
2025-08-30 15:26:27 +08:00
<i class="fas fa-database"></i>
<span>表基础信息</span>
2025-08-29 18:08:32 +08:00
</div>
2025-08-30 15:26:27 +08:00
<div class="panel-content" th:each="t : ${data}">
2025-08-29 18:08:32 +08:00
<div class="info-card">
<div class="info-row">
<div class="info-label">数据库</div>
2025-08-30 15:26:27 +08:00
<div class="info-value"><p th:text="${t.dbName}"></p></div>
2025-08-29 18:08:32 +08:00
</div>
<div class="info-row">
<div class="info-label">表名称</div>
2025-08-30 15:26:27 +08:00
<div class="info-value" th:text="${t.tableName}"><strong></strong></div>
2025-08-29 18:08:32 +08:00
</div>
<div class="info-row">
<div class="info-label">表描述</div>
2025-08-30 15:26:27 +08:00
<div class="info-value"><p th:text="${t.tableDesc}"></p></div>
2025-08-29 18:08:32 +08:00
</div>
<div class="info-row">
<div class="info-label">存储量</div>
2025-08-30 15:26:27 +08:00
<div class="info-value"><p th:text="${t.dataLength}"></p></div>
2025-08-29 18:08:32 +08:00
</div>
</div>
<div class="info-card">
<div class="info-row">
<div class="info-label">创建时间</div>
<div class="info-value">
2025-08-30 15:26:27 +08:00
<p th:text="${t.createTime}"></p>
2025-08-29 18:08:32 +08:00
</div>
</div>
<div class="info-row">
<div class="info-label">更新时间</div>
<div class="info-value">
2025-08-30 15:26:27 +08:00
<p th:text="${t.updateTime}"></p>
2025-08-29 18:08:32 +08:00
</div>
</div>
</div>
<div class="stats-row">
<div class="stat-item">
2025-08-30 15:26:27 +08:00
<i class="fas fa-key" style="color: #165dff;"></i>
2025-08-30 15:38:11 +08:00
<p>主键:<span th:text="${pkCnt}">0</span></p>
2025-08-29 18:08:32 +08:00
</div>
<div class="stat-item">
2025-08-30 15:26:27 +08:00
<i class="fas fa-sitemap" style="color: #00864e;"></i>
2025-08-30 15:38:11 +08:00
<p>索引:<span th:text="${idxCnt}">0</span></p>
2025-08-29 18:08:32 +08:00
</div>
<div class="stat-item">
2025-08-30 15:26:27 +08:00
<i class="fas fa-list" style="color: #4096ff;"></i>
2025-08-30 15:38:11 +08:00
<p>字段:<span th:text="${colCnt}">0</span></p>
2025-08-29 18:08:32 +08:00
</div>
</div>
</div>
</div>
2025-08-30 15:26:27 +08:00
<!-- 右侧面板:功能切换区 -->
2025-08-29 18:08:32 +08:00
<div class="panel right-panel">
<div class="panel-header">
2025-08-30 15:26:27 +08:00
<i class="fas fa-columns"></i>
<span>表结构操作</span>
2025-08-29 18:08:32 +08:00
</div>
<div class="panel-content">
2025-08-30 15:26:27 +08:00
<!-- 切换按钮 -->
2025-08-29 18:08:32 +08:00
<div class="button-panel">
2025-08-30 15:26:27 +08:00
<div class="button active" data-target="field-info">
<i class="fas fa-list-ul"></i>
<span>字段信息</span>
</div>
<div class="button" data-target="generate-select">
<i class="fas fa-code"></i>
<span>生成SELECT</span>
2025-08-29 18:08:32 +08:00
</div>
2025-08-30 15:26:27 +08:00
<div class="button" data-target="generate-ddl">
<i class="fas fa-database"></i>
<span>生成DDL</span>
</div>
</div>
<!-- 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>
2025-08-29 18:08:32 +08:00
</div>
2025-08-30 15:26:27 +08:00
</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>
2025-08-30 15:34:11 +08:00
<div id="select-sql" class="sql-input" th:text="${selectSql}">
2025-08-30 15:26:27 +08:00
</div>
2025-08-29 18:08:32 +08:00
</div>
</div>
2025-08-30 15:26:27 +08:00
<!-- 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>
2025-08-30 15:34:11 +08:00
<div id="ddl-sql" class="sql-input" th:text="${ddlSql}">
2025-08-30 15:26:27 +08:00
</div>
</div>
2025-08-29 18:08:32 +08:00
</div>
</div>
</div>
</div>
2025-08-30 15:26:27 +08:00
<!-- 复制成功提示框 -->
<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>
2025-08-29 18:08:32 +08:00
</body>
</html>