2025-10-07 12:38:53 +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 > 数据库管理工具 - Axis Innovators Box< / title >
< style >
/* ---------- 主题变量 & 基本样式 ---------- */
:root{
--primary-color: #2b8bef;
--accent-color: #49c28a;
--bg: #ffffff;
--surface: #f7fbff;
--text: #222;
--muted: #6b6b6b;
--border: #e6e9ee;
--shadow: 0 6px 20px rgba(34,34,34,0.06);
--hover: #f4f7fb;
}
[data-theme="dark"]{
--primary-color: #49a6ff;
--accent-color: #2ecc71;
--bg: #0f1115;
--surface: #121317;
--text: #e6edf3;
--muted: #9aa3ad;
--border: #24272b;
--shadow: 0 6px 20px rgba(0,0,0,0.6);
--hover: rgba(255,255,255,0.02);
}
[data-theme="light"]{
--primary-color: #2b8bef;
--accent-color: #49c28a;
--bg: #ffffff;
--surface: #f7fbff;
--text: #222;
--muted: #6b6b6b;
--border: #e6e9ee;
--shadow: 0 6px 20px rgba(34,34,34,0.06);
--hover: #f4f7fb;
}
*{box-sizing:border-box;margin:0;padding:0;transition:background-color .18s,color .18s,border-color .18s}
html,body{height:100%}
body{
font-family:Inter, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background:var(--bg);
color:var(--text);
display:flex;
flex-direction:column;
height:100vh;
overflow:hidden;
}
/* ---------- Header ---------- */
header{
display:flex;
align-items:center;
justify-content:space-between;
padding:12px 18px;
background:var(--surface);
border-bottom:1px solid var(--border);
box-shadow:var(--shadow);
z-index:100;
}
.logo{display:flex;gap:12px;align-items:center}
.logo .mark{width:36px;height:36px;border-radius:10px;background:linear-gradient(180deg,var(--primary-color),#1f7ad8);display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:18px}
.logo .title{font-weight:700;font-size:18px}
.header-controls{display:flex;gap:12px;align-items:center}
.theme-toggle{background:transparent;border:1px solid var(--border);padding:6px 8px;border-radius:8px;cursor:pointer}
.user-profile{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:10px;background:transparent;border:1px solid transparent}
.avatar{width:34px;height:34px;border-radius:8px;background:var(--primary-color);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:700}
/* ---------- 主区布局 ---------- */
.main-container{display:flex;flex:1;overflow:hidden}
/* 侧栏 */
.sidebar{width:280px;display:flex;flex-direction:column;background:var(--surface);border-right:1px solid var(--border);padding:12px;gap:12px}
.sidebar-section{background:transparent}
.sidebar-title{font-size:12px;color:var(--muted);text-transform:uppercase;padding:4px 8px;font-weight:600}
.sidebar-item{display:flex;align-items:center;gap:10px;padding:8px;border-radius:8px;color:var(--text);text-decoration:none;cursor:pointer}
.sidebar-item:hover{background:var(--hover)}
.sidebar-icon{width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center}
/* 数据库/表 列表区域 */
.database-list{display:flex;flex-direction:column;gap:8px;padding:6px 4px;max-height:160px;overflow:auto}
.tables-list{flex:1;overflow:auto;padding:6px 4px;border-radius:8px}
.table-item{display:flex;align-items:center;justify-content:space-between;padding:8px;border-radius:8px}
.table-item .meta{display:flex;flex-direction:column}
.table-item .name{font-weight:600}
.table-item .sub{font-size:12px;color:var(--muted)}
2025-10-07 15:35:33 +08:00
/* 工具区域(新设计) */
.tools-panel {
position: fixed;
bottom: 20px;
left: 20px;
width: 250px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
box-shadow: var(--shadow);
z-index: 1000;
display: none;
padding: 12px;
flex-direction: column;
gap: 8px;
}
.tools-panel.active {
display: flex;
}
.tools-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}
2025-10-07 12:38:53 +08:00
.tool-btn{display:flex;align-items:center;gap:8px;padding:8px;border-radius:8px;background:transparent;border:1px solid transparent;cursor:pointer}
.tool-btn:hover{background:var(--hover)}
2025-10-07 15:35:33 +08:00
.tools-toggle {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
border-radius: 8px;
background: transparent;
border: 1px solid var(--border);
cursor: pointer;
margin-top: 8px;
}
.tools-toggle:hover {
background: var(--hover);
}
2025-10-07 12:38:53 +08:00
/* 内容区 */
.content-area{flex:1;display:flex;flex-direction:column;overflow:hidden}
.toolbar{display:flex;gap:10px;padding:12px;border-bottom:1px solid var(--border);background:var(--surface)}
.btn{padding:8px 12px;border-radius:10px;border:1px solid transparent;cursor:pointer;font-weight:600}
.btn-primary{background:linear-gradient(180deg,var(--primary-color),#1f7ad8);color:#fff;box-shadow:0 8px 20px rgba(33,120,210,0.12)}
.btn-outline{background:transparent;border:1px solid var(--border);color:var(--text)}
.query-editor-container{display:flex;gap:12px;padding:12px;flex:1;overflow:hidden}
.query-editor{width:45%;display:flex;flex-direction:column;border-radius:10px;border:1px solid var(--border);background:var(--bg);overflow:hidden}
.editor-toolbar{padding:10px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center}
.editor-content{flex:1;padding:12px;font-family:monospace;font-size:13px;border:none;outline:none;background:transparent;resize:none;min-height:220px}
.results-container{flex:1;display:flex;flex-direction:column;border-radius:10px;border:1px solid var(--border);background:var(--bg);overflow:auto}
.results-header{padding:12px;border-bottom:1px solid var(--border);font-weight:700}
.results-content{padding:12px;overflow:auto}
/* 表格 */
table{width:100%;border-collapse:collapse}
th,td{padding:10px;border-bottom:1px solid var(--border);text-align:left;font-size:13px}
th{background:var(--surface);position:sticky;top:0}
2025-10-07 15:35:33 +08:00
/* 表格操作按钮 */
.table-actions{display:flex;gap:6px;margin-bottom:12px}
.table-actions .btn{font-size:12px;padding:6px 10px}
/* 编辑行样式 */
.editable-row{background:rgba(73,194,138,0.1) !important}
.editable-row td input{width:100%;padding:4px 6px;border:1px solid var(--border);border-radius:4px;background:var(--bg);color:var(--text)}
.editable-row td select{width:100%;padding:4px 6px;border:1px solid var(--border);border-radius:4px;background:var(--bg);color:var(--text)}
.action-cell{display:flex;gap:6px;justify-content:center}
.action-cell .btn{font-size:11px;padding:4px 8px}
/* 表设计器样式 */
.table-designer{display:flex;flex-direction:column;gap:12px}
.designer-section{background:var(--surface);border-radius:8px;padding:12px;border:1px solid var(--border)}
.designer-section h3{margin-bottom:12px;font-size:14px}
.columns-list{display:flex;flex-direction:column;gap:8px}
.column-item{display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:8px;align-items:center;padding:8px;border-radius:6px;border:1px solid var(--border)}
.column-actions{display:flex;gap:4px}
.column-actions .btn{font-size:11px;padding:4px 8px}
.indexes-list{display:flex;flex-direction:column;gap:8px}
.index-item{display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:8px;align-items:center;padding:8px;border-radius:6px;border:1px solid var(--border)}
.constraints-list{display:flex;flex-direction:column;gap:8px}
.constraint-item{display:grid;grid-template-columns:1fr 1fr 1fr auto;gap:8px;align-items:center;padding:8px;border-radius:6px;border:1px solid var(--border)}
2025-10-07 12:38:53 +08:00
.status-bar{padding:8px 12px;border-top:1px solid var(--border);background:var(--surface);display:flex;justify-content:space-between;font-size:13px;color:var(--muted)}
/* 弹窗(通用) */
.overlay{position:fixed;inset:0;background:rgba(0,0,0,0.45);display:none;z-index:2000}
.modal{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);background:var(--bg);border-radius:12px;padding:16px;box-shadow:var(--shadow);z-index:2001;min-width:360px;max-width:90%;display:none}
.modal .header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;font-weight:700}
.modal .body{max-height:60vh;overflow:auto;padding-top:8px}
.modal .footer{display:flex;justify-content:flex-end;gap:8px;margin-top:12px}
2025-10-07 15:35:33 +08:00
/* 表单控件 */
.form-control{width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--bg);color:var(--text);margin-bottom:12px}
.form-label{display:block;margin-bottom:4px;font-weight:600;font-size:14px}
.form-row{display:flex;gap:12px;margin-bottom:12px}
.form-row .form-group{flex:1}
/* 搜索框 */
.search-box{position:sticky;top:0;background:var(--surface);padding:8px;border-bottom:1px solid var(--border);z-index:10}
.search-input{width:100%;padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--bg);color:var(--text)}
.search-results{font-size:12px;color:var(--muted);margin-top:4px}
2025-10-07 12:38:53 +08:00
/* 响应式小屏 */
@media (max-width:900px){
.sidebar{display:none}
.query-editor{width:100%}
.query-editor-container{flex-direction:column}
2025-10-07 15:35:33 +08:00
.column-item{grid-template-columns:1fr;gap:4px}
.index-item{grid-template-columns:1fr;gap:4px}
.constraint-item{grid-template-columns:1fr;gap:4px}
2025-10-07 12:38:53 +08:00
}
/* 小图标样式( SVG 作为图标) */
.svg-icon{width:18px;height:18px;display:inline-block;vertical-align:middle;fill:currentColor}
2025-10-07 15:35:33 +08:00
/* 快速创建表按钮 */
.quick-create-table{background:var(--primary-color);color:white;border:none;padding:8px 12px;border-radius:8px;cursor:pointer;font-weight:600;margin-top:8px;width:100%}
.quick-create-table:hover{opacity:0.9}
2025-10-07 12:38:53 +08:00
< / style >
< / head >
< body data-theme = "light" >
< header >
< div class = "logo" >
< div class = "mark" > AI< / div >
< div >
< div class = "title" > Axis Innovators Box< / div >
< div style = "font-size:12px;color:var(--muted)" > 数据库管理工具< / div >
< / div >
< / div >
< div class = "header-controls" >
< button id = "themeToggle" class = "theme-toggle" title = "切换主题" > ☀️/🌙< / button >
< div class = "user-profile" >
< div class = "avatar" > JD< / div >
< div style = "font-size:13px" > 开发者< / div >
< / div >
< / div >
< / header >
< div class = "main-container" >
<!-- 侧栏 -->
< aside class = "sidebar" role = "navigation" >
< div class = "sidebar-section" >
< div class = "sidebar-title" > 连接< / div >
< div class = "sidebar-item" id = "openConnect" >
< div class = "sidebar-icon" >
<!-- plug svg -->
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M10 13a5 5 0 0 1 7.07 0l1.41 1.41 1.41-1.41a3 3 0 0 0-4.24-4.24L14.24 9.17" / > < / svg >
< / div >
< div > 连接 / 创建 本地数据库< / div >
< / div >
< / div >
< div class = "sidebar-section" >
< div class = "sidebar-title" > 数据库< / div >
< div class = "database-list" id = "databaseList" >
<!-- 动态填充 -->
< / div >
< / div >
< div class = "sidebar-section" style = "display:flex;flex-direction:column;flex:1;min-height:0" >
< div class = "sidebar-title" > 表< / div >
2025-10-07 15:35:33 +08:00
< button class = "quick-create-table" id = "quickCreateTableBtn" > + 快速创建表< / button >
< div class = "search-box" >
< input type = "text" id = "tableSearch" class = "search-input" placeholder = "搜索表..." >
< div class = "search-results" id = "tableSearchResults" > < / div >
< / div >
2025-10-07 12:38:53 +08:00
< div class = "tables-list" id = "tablesList" >
< div style = "padding:15px;text-align:center;color:var(--muted)" > 请连接数据库< / div >
< / div >
< / div >
< div class = "sidebar-section" >
< div class = "sidebar-title" > 工具< / div >
2025-10-07 15:35:33 +08:00
< button class = "tools-toggle" id = "toolsToggle" >
< span > ...< / span >
< / button >
2025-10-07 12:38:53 +08:00
< / div >
< / aside >
2025-10-07 15:35:33 +08:00
<!-- 工具面板 -->
< div class = "tools-panel" id = "toolsPanel" >
< div style = "display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;" >
< div style = "font-weight: 600;" > 工具< / div >
< button onclick = "document.getElementById('toolsPanel').classList.remove('active')" style = "background: transparent; border: none; cursor: pointer;" > ✕< / button >
< / div >
< div class = "tools-grid" >
< button class = "tool-btn" data-action = "queryAnalyzer" title = "查询分析器" >
< span class = "sidebar-icon" >
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M3 3h18v4H3zM3 10h12v4H3zM3 17h6v4H3z" / > < / svg >
< / span >
< span > 查询分析器< / span >
< / button >
< button class = "tool-btn" data-action = "exportData" title = "导出数据" >
< span class = "sidebar-icon" >
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M12 3v12M5 10l7-7 7 7M5 21h14" / > < / svg >
< / span >
< span > 导出数据< / span >
< / button >
< button class = "tool-btn" data-action = "importCsv" title = "CSV 导入" >
< span class = "sidebar-icon" >
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M12 3v12M5 10l7-7 7 7M21 21H3" / > < / svg >
< / span >
< span > CSV 导入< / span >
< / button >
< button class = "tool-btn" data-action = "erDiagram" title = "ER 图" >
< span class = "sidebar-icon" >
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M4 6h16v12H4zM8 6v12M16 6v12" / > < / svg >
< / span >
< span > ER 图< / span >
< / button >
< button class = "tool-btn" data-action = "performance" title = "性能监控" >
< span class = "sidebar-icon" >
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M3 12h3l3-8 4 16 4-10 3 4h4" / > < / svg >
< / span >
< span > 性能监控< / span >
< / button >
< button class = "tool-btn" data-action = "userMgmt" title = "用户管理" >
< span class = "sidebar-icon" >
< svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm-8 9a8 8 0 0 1 16 0" / > < / svg >
< / span >
< span > 用户管理< / span >
< / button >
< / div >
< / div >
2025-10-07 12:38:53 +08:00
<!-- 内容区 -->
< main class = "content-area" >
< div class = "toolbar" >
< button id = "executeQueryBtn" class = "btn btn-primary" > < svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M5 3v18l15-9z" / > < / svg > 执行查询< / button >
< button id = "refreshTablesBtn" class = "btn btn-outline" > < svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M21 12a9 9 0 1 0-2.6 6.12" / > < / svg > 刷新表< / button >
< button id = "newQueryBtn" class = "btn btn-outline" > < svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M12 5v14M5 12h14" / > < / svg > 新建查询< / button >
< button id = "formatQueryBtn" class = "btn btn-outline" > < svg class = "svg-icon" viewBox = "0 0 24 24" > < path d = "M3 6h18M3 12h18M3 18h18" / > < / svg > 格式化< / button >
< div style = "flex:1" > < / div >
< div id = "statusSmall" style = "font-size:13px;color:var(--muted)" > 状态: 就绪< / div >
< / div >
< div class = "query-editor-container" >
< div class = "query-editor" >
< div class = "editor-toolbar" >
< div > 查询编辑器< / div >
< div style = "display:flex;gap:8px" >
< button class = "btn btn-outline" id = "saveQueryBtn" > 保存< / button >
< button class = "btn btn-outline" id = "explainQueryBtn" > 解释< / button >
< / div >
< / div >
< textarea id = "queryEditor" class = "editor-content" placeholder = "输入SQL查询语句..." > SELECT * FROM users;< / textarea >
< / div >
< div class = "results-container" >
< div class = "results-header" >
< div > 查询结果< / div >
< div id = "resultsInfo" style = "font-size:13px;color:var(--muted)" > < / div >
< / div >
2025-10-07 15:35:33 +08:00
< div class = "search-box" id = "resultsSearchBox" style = "display:none" >
< input type = "text" id = "resultsSearchInput" class = "search-input" placeholder = "在结果中搜索..." >
< div class = "search-results" id = "resultsSearchInfo" > < / div >
< / div >
2025-10-07 12:38:53 +08:00
< div class = "results-content" id = "resultsContent" >
< div style = "text-align:center;padding:40px;color:var(--muted)" >
< p > 请连接数据库并执行查询以查看结果< / p >
< / div >
< / div >
< / div >
< / div >
< div class = "status-bar" >
< div id = "statusMessage" > 就绪< / div >
< div id = "rowColumnInfo" > 行: 0, 列: 0< / div >
< / div >
< / main >
< / div >
<!-- 事件日志按钮 -->
< button id = "showEventsBtn" style = "position:fixed;right:20px;bottom:20px;border-radius:50%;width:52px;height:52px;background:var(--primary-color);color:#fff;border:none;box-shadow:var(--shadow);cursor:pointer;z-index:1001" > 📋< / button >
<!-- 弹窗遮罩 -->
< div id = "overlay" class = "overlay" > < / div >
<!-- 连接/创建对话框(原有功能,稍微美化) -->
< div id = "connectionDialog" class = "modal" style = "min-width:520px" >
< div class = "header" > 数据库连接
< button onclick = "hideModal('connectionDialog')" style = "background:transparent;border:none;cursor:pointer" > ✕< / button >
< / div >
< div class = "body" >
< div style = "display:flex;gap:12px" >
< div style = "flex:1" >
< div style = "margin-bottom:8px" > < label > 操作< / label > < / div >
< select id = "dialogAction" class = "form-control" style = "width:100%;padding:8px;border-radius:6px;border:1px solid var(--border)" >
< option value = "connect" > 连接数据库< / option >
< option value = "create" > 创建本地数据库< / option >
< / select >
< div id = "connectFields" style = "margin-top:12px" >
< div style = "margin-bottom:8px" > < label > 数据库类型< / label > < / div >
< select id = "dbDriver" style = "width:100%;padding:8px;border-radius:6px;border:1px solid var(--border)" >
< option value = "mysql" > MySQL< / option >
< option value = "postgresql" > PostgreSQL< / option >
< option value = "sqlite" > SQLite< / option >
< option value = "oracle" > Oracle< / option >
< option value = "h2" > H2< / option >
< / select >
< div style = "display:flex;gap:8px;margin-top:8px" >
< input id = "dbHost" placeholder = "主机" style = "flex:1;padding:8px;border-radius:6px;border:1px solid var(--border)" value = "localhost" >
< input id = "dbPort" placeholder = "端口" style = "width:120px;padding:8px;border-radius:6px;border:1px solid var(--border)" value = "3306" >
< / div >
< div style = "display:flex;gap:8px;margin-top:8px" >
< input id = "dbName" placeholder = "数据库名" style = "flex:1;padding:8px;border-radius:6px;border:1px solid var(--border)" value = "test" >
< input id = "dbUsername" placeholder = "用户名" style = "width:160px;padding:8px;border-radius:6px;border:1px solid var(--border)" value = "root" >
< / div >
< div style = "margin-top:8px" > < input id = "dbPassword" placeholder = "密码" type = "password" style = "width:100%;padding:8px;border-radius:6px;border:1px solid var(--border)" > < / div >
< / div >
< div id = "createFields" style = "margin-top:12px;display:none" >
< div style = "margin-bottom:8px" > < label > 本地数据库类型< / label > < / div >
< select id = "localDbDriver" style = "width:100%;padding:8px;border-radius:6px;border:1px solid var(--border)" >
< option value = "sqlite" > SQLite< / option >
< option value = "h2" > H2 Database< / option >
< / select >
< div style = "margin-top:8px" > < input id = "localDbName" placeholder = "数据库名称" style = "width:100%;padding:8px;border-radius:6px;border:1px solid var(--border)" value = "my_database" > < / div >
< div style = "margin-top:8px" > < label > 存储路径< / label > < input id = "localDbPath" readonly style = "width:100%;padding:8px;border-radius:6px;border:1px solid var(--border);background:var(--hover)" > < / div >
< div style = "margin-top:8px" > < label > < input type = "checkbox" id = "includeSampleData" checked > 包含示例数据< / label > < / div >
< / div >
< / div >
< / div >
< / div >
< div class = "footer" >
< button class = "btn btn-outline" onclick = "hideModal('connectionDialog')" > 取消< / button >
< button class = "btn btn-primary" id = "confirmConnectionBtn" > 确认< / button >
< / div >
< / div >
<!-- 通用工具模态(动态使用) -->
< div id = "toolModal" class = "modal" style = "min-width:520px" >
< div class = "header" id = "toolModalTitle" > 工具< / div >
< div class = "body" id = "toolModalBody" > < / div >
< div class = "footer" id = "toolModalFooter" > < button class = "btn btn-outline" onclick = "hideModal('toolModal')" > 关闭< / button > < / div >
< / div >
2025-10-07 15:35:33 +08:00
<!-- 表设计器模态 -->
< div id = "tableDesignerModal" class = "modal" style = "min-width:800px;max-width:95%" >
< div class = "header" id = "tableDesignerTitle" > 表设计器< / div >
< div class = "body" id = "tableDesignerBody" >
< div class = "table-designer" >
< div class = "designer-section" >
< h3 > 基本信息< / h3 >
< div class = "form-row" >
< div class = "form-group" >
< label class = "form-label" > 表名< / label >
< input type = "text" id = "tableName" class = "form-control" placeholder = "输入表名" >
< / div >
< div class = "form-group" >
< label class = "form-label" > 引擎< / label >
< select id = "tableEngine" class = "form-control" >
< option value = "InnoDB" > InnoDB< / option >
< option value = "MyISAM" > MyISAM< / option >
< option value = "MEMORY" > MEMORY< / option >
< / select >
< / div >
< / div >
< div class = "form-row" >
< div class = "form-group" >
< label class = "form-label" > 字符集< / label >
< select id = "tableCharset" class = "form-control" >
< option value = "utf8mb4" > utf8mb4< / option >
< option value = "utf8" > utf8< / option >
< option value = "latin1" > latin1< / option >
< / select >
< / div >
< div class = "form-group" >
< label class = "form-label" > 排序规则< / label >
< select id = "tableCollation" class = "form-control" >
< option value = "utf8mb4_unicode_ci" > utf8mb4_unicode_ci< / option >
< option value = "utf8mb4_general_ci" > utf8mb4_general_ci< / option >
< option value = "utf8_general_ci" > utf8_general_ci< / option >
< / select >
< / div >
< / div >
< div class = "form-group" >
< label class = "form-label" > 注释< / label >
< textarea id = "tableComment" class = "form-control" placeholder = "表注释" rows = "2" > < / textarea >
< / div >
< / div >
< div class = "designer-section" >
< h3 > 列定义< / h3 >
< div class = "columns-list" id = "columnsList" >
<!-- 动态生成的列 -->
< / div >
< button class = "btn btn-outline" id = "addColumnBtn" > + 添加列< / button >
< / div >
< div class = "designer-section" >
< h3 > 索引< / h3 >
< div class = "indexes-list" id = "indexesList" >
<!-- 动态生成的索引 -->
< / div >
< button class = "btn btn-outline" id = "addIndexBtn" > + 添加索引< / button >
< / div >
< div class = "designer-section" >
< h3 > 约束< / h3 >
< div class = "constraints-list" id = "constraintsList" >
<!-- 动态生成的约束 -->
< / div >
< button class = "btn btn-outline" id = "addConstraintBtn" > + 添加约束< / button >
< / div >
< / div >
< / div >
< div class = "footer" id = "tableDesignerFooter" >
< button class = "btn btn-outline" onclick = "hideModal('tableDesignerModal')" > 取消< / button >
< button class = "btn btn-primary" id = "saveTableDesignBtn" > 保存表结构< / button >
< / div >
< / div >
2025-10-07 12:38:53 +08:00
<!-- 事件日志面板 -->
< div id = "eventLog" class = "modal" style = "right:20px;bottom:20px;left:auto;top:auto;transform:none;display:none;min-width:320px" >
< div class = "header" > 事件日志 < button onclick = "document.getElementById('eventLog').style.display='none'" > ✕< / button > < / div >
< div class = "body" id = "eventLogContent" style = "max-height:260px;overflow:auto" > < / div >
< / div >
< script >
// Java 通信对象(与你的 Java 交互)
const JavaBridge = {
sendRequest: function(request, callback) {
if (window.cefQuery) {
// 保持原有行为
window.cefQuery({
request: JSON.stringify(request),
onSuccess: function(response) {
if (callback) {
try { callback(JSON.parse(response), null); } catch(e) { callback(response, null); }
}
},
onFailure: function(error_code, error_message) {
if (callback) callback(null, {code: error_code, message: error_message});
}
});
} else {
2025-10-07 15:35:33 +08:00
// 开发环境模拟 - 更新模拟数据以匹配新的Java结构
2025-10-07 12:38:53 +08:00
console.log("Java Request:", request);
setTimeout(() => {
if (request.type === 'createLocalDatabase') {
callback({status:'success', connectionId:'mock_conn_' + Date.now(), database: request.dbName, driver: request.driver}, null);
} else if (request.type === 'connectDatabase') {
callback({status:'success', connectionId:'mock_conn_' + Date.now(), database: request.database, driver: request.driver}, null);
2025-10-07 15:35:33 +08:00
} else if (request.type === 'getTables') {
callback({status:'success', tables:[
{name:'users', type:'TABLE', rows:5},
{name:'products', type:'TABLE', rows:12},
{name:'orders', type:'TABLE', rows:8}
]}, null);
} else if (request.type === 'getTableData') {
callback({status:'success', columns:['id','name','email','created_at'], data:[
{id:1, name:'张三', email:'zhang@example.com', created_at:'2023-01-15'},
{id:2, name:'李四', email:'li@example.com', created_at:'2023-02-20'},
{id:3, name:'王五', email:'wang@example.com', created_at:'2023-03-10'}
], total:3, offset:0, limit:50}, null);
} else if (request.type === 'insertRow') {
callback({status:'success', message:'插入成功', newId: Math.floor(Math.random()*1000)+3}, null);
} else if (request.type === 'updateRow') {
callback({status:'success', message:'更新成功'}, null);
} else if (request.type === 'deleteRow') {
callback({status:'success', message:'删除成功'}, null);
} else if (request.type === 'createTable') {
callback({status:'success', message:'表创建成功'}, null);
} else if (request.type === 'alterTable') {
callback({status:'success', message:'表结构修改成功'}, null);
} else if (request.type === 'getTableStructure') {
// 更新模拟数据以匹配新的Java结构
callback({status:'success',
tableName: request.tableName,
engine: 'InnoDB',
charset: 'utf8mb4',
collation: 'utf8mb4_unicode_ci',
comment: '用户表',
columns: [
{name: 'id', type: 'INT', size: 11, nullable: false, defaultValue: null, autoIncrement: true},
{name: 'name', type: 'VARCHAR(255)', size: 255, nullable: false, defaultValue: null, autoIncrement: false},
{name: 'email', type: 'VARCHAR(255)', size: 255, nullable: false, defaultValue: null, autoIncrement: false},
{name: 'created_at', type: 'DATETIME', size: null, nullable: true, defaultValue: 'CURRENT_TIMESTAMP', autoIncrement: false}
],
indexes: [
{name: 'idx_email', type: 'UNIQUE', columns: 'email'},
{name: 'idx_name', type: 'INDEX', columns: 'name'}
],
constraints: [
{name: 'PRIMARY', type: 'PRIMARY KEY', definition: 'PRIMARY KEY (id)'}
]
}, null);
2025-10-07 12:38:53 +08:00
} else {
callback({status:'success', message:'模拟响应'}, null);
}
}, 350);
}
}
};
// UI 工具函数
function showModal(id){
document.getElementById('overlay').style.display='block';
document.getElementById(id).style.display='block';
}
function hideModal(id){
document.getElementById('overlay').style.display='none';
document.getElementById(id).style.display='none';
}
function addEventLog(title,desc){
const el = document.createElement('div');
const now = new Date();
el.innerHTML = `< div style = "padding:8px;border-bottom:1px dashed var(--border)" > < div style = "font-weight:700" > ${title}< / div > < div style = "font-size:12px;color:var(--muted)" > ${desc}< / div > < div style = "font-size:11px;color:var(--muted)" > ${now.toLocaleString()}< / div > < / div > `;
const content = document.getElementById('eventLogContent');
content.prepend(el);
// 保留最多 40 条
while (content.children.length > 40) content.removeChild(content.lastChild);
}
// 主题支持( auto / light / dark)
(function themeInit(){
const body = document.body;
let mode = localStorage.getItem('dbToolTheme') || 'light';
applyTheme(mode);
document.getElementById('themeToggle').addEventListener('click', () => {
mode = (mode === 'light') ? 'dark' : 'light';
localStorage.setItem('dbToolTheme', mode);
applyTheme(mode);
});
function applyTheme(m){
body.setAttribute('data-theme', m);
document.getElementById('themeToggle').textContent = m === 'light' ? '🌙' : '☀️';
}
})();
2025-10-07 15:35:33 +08:00
// 绑定工具面板切换
document.getElementById('toolsToggle').addEventListener('click', () => {
document.getElementById('toolsPanel').classList.toggle('active');
});
2025-10-07 12:38:53 +08:00
// 绑定打开连接对话框
document.getElementById('openConnect').addEventListener('click', () => {
showModal('connectionDialog');
// 默认显示 connect 区块
document.getElementById('dialogAction').value = 'connect';
toggleConnectionDialog();
});
// connection dialog toggle fields
document.getElementById('dialogAction').addEventListener('change', toggleConnectionDialog);
function toggleConnectionDialog(){
const action = document.getElementById('dialogAction').value;
const connectFields = document.getElementById('connectFields');
const createFields = document.getElementById('createFields');
if(action === 'connect'){
connectFields.style.display='block';
createFields.style.display='none';
} else {
connectFields.style.display='none';
createFields.style.display='block';
// 更新默认路径
const dbName = document.getElementById('localDbName').value || 'my_database';
const driver = document.getElementById('localDbDriver').value;
const extension = driver === 'h2' ? '' : '.db';
document.getElementById('localDbPath').value = `~/.axis_innovators_box/databases/${dbName}${extension}`;
}
}
// localDbName 和 localDbDriver 监听更新路径
document.getElementById('localDbName').addEventListener('input', () => {
const dbName = document.getElementById('localDbName').value || 'my_database';
const driver = document.getElementById('localDbDriver').value;
const extension = driver === 'h2' ? '' : '.db';
document.getElementById('localDbPath').value = `~/.axis_innovators_box/databases/${dbName}${extension}`;
});
document.getElementById('localDbDriver').addEventListener('change', () => {
const dbName = document.getElementById('localDbName').value || 'my_database';
const driver = document.getElementById('localDbDriver').value;
const extension = driver === 'h2' ? '' : '.db';
document.getElementById('localDbPath').value = `~/.axis_innovators_box/databases/${dbName}${extension}`;
});
// 确认连接 / 创建
document.getElementById('confirmConnectionBtn').addEventListener('click', () => {
const action = document.getElementById('dialogAction').value;
if(action === 'connect'){
const driver = document.getElementById('dbDriver').value;
const host = document.getElementById('dbHost').value;
const port = document.getElementById('dbPort').value;
const database = document.getElementById('dbName').value;
const username = document.getElementById('dbUsername').value;
const password = document.getElementById('dbPassword').value;
setStatus('正在连接数据库...');
JavaBridge.sendRequest({
type: 'connectDatabase',
driver, host, port, database, username, password
}, (resp, err) => {
if(err){ setStatus('连接失败'); addEventLog('连接失败', err.message); alert('连接失败: ' + err.message); return; }
if(resp & & resp.status === 'success'){
setStatus('连接成功');
addEventLog('连接', `已连接 ${resp.database} (${resp.driver})`);
hideModal('connectionDialog');
// 更新列表
onConnected(resp.connectionId, resp.database, resp.driver);
} else {
setStatus('连接失败'); alert('连接失败: ' + (resp & & resp.message || '未知错误'));
}
});
} else {
// 创建本地数据库
const driver = document.getElementById('localDbDriver').value;
const dbName = document.getElementById('localDbName').value || 'my_database';
const includeSampleData = document.getElementById('includeSampleData').checked;
setStatus('正在创建本地数据库...');
JavaBridge.sendRequest({
type: 'createLocalDatabase',
driver, dbName, includeSampleData
}, (resp, err) => {
if(err){ setStatus('创建失败'); addEventLog('创建失败', err.message); alert('创建失败: ' + err.message); return; }
if(resp & & resp.status === 'success'){
setStatus('创建成功');
addEventLog('创建数据库', `${dbName} (${driver})`);
hideModal('connectionDialog');
onConnected(resp.connectionId, resp.database, resp.driver);
} else {
setStatus('创建失败'); alert('创建失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
});
// 当连接成功后更新 UI( 填充数据库与表占位)
function onConnected(connectionId, database, driver){
window.isConnected = true;
window.currentConnectionId = connectionId;
2025-10-07 15:35:33 +08:00
window.currentDatabase = database;
2025-10-07 12:38:53 +08:00
// 更新数据库列表(这是简化示例:仅显示当前)
const dbList = document.getElementById('databaseList');
dbList.innerHTML = `< div style = "padding:8px;border-radius:8px;background:var(--hover)" > < div style = "font-weight:700" > ${database}< / div > < div style = "font-size:12px;color:var(--muted)" > ${driver}< / div > < / div > `;
// 加载表(请求 Java)
loadTables();
}
// 加载表
function loadTables(){
if(!window.currentConnectionId) return;
setStatus('加载表列表...');
JavaBridge.sendRequest({type:'getTables', connectionId: window.currentConnectionId}, (resp, err) => {
if(err){ setStatus('加载表失败'); addEventLog('加载表失败', err.message); return; }
if(resp & & resp.status === 'success'){
renderTables(resp.tables || []);
setStatus('表列表已加载');
} else {
setStatus('加载表失败');
}
});
}
function renderTables(tables){
const list = document.getElementById('tablesList');
list.innerHTML = '';
if(!tables || tables.length===0){
list.innerHTML = '< div style = "padding:15px;text-align:center;color:var(--muted)" > 数据库中没有表< / div > ';
return;
}
2025-10-07 15:35:33 +08:00
// 存储所有表数据用于搜索
window.allTables = tables;
2025-10-07 12:38:53 +08:00
tables.forEach(t => {
const item = document.createElement('div');
item.className = 'table-item';
item.innerHTML = `< div class = "meta" > < div class = "name" > ${t.name}< / div > < div class = "sub" > ${t.type} • ${t.rows} 行< / div > < / div >
2025-10-07 15:35:33 +08:00
< div style = "display:flex;gap:6px" >
< button class = "btn btn-outline" data-action = "view" data-table = "${t.name}" > 查看< / button >
< button class = "btn btn-outline" data-action = "structure" data-table = "${t.name}" > 结构< / button >
< button class = "btn btn-outline" data-action = "design" data-table = "${t.name}" disabled > 设计(维修中)< / button >
< / div > `;
2025-10-07 12:38:53 +08:00
list.appendChild(item);
});
// 绑定内部按钮
list.querySelectorAll('button[data-action="view"]').forEach(b=>{
b.addEventListener('click',(e)=>{
e.stopPropagation();
const table = b.dataset.table;
loadTableData(table);
});
});
list.querySelectorAll('button[data-action="structure"]').forEach(b=>{
b.addEventListener('click',(e)=>{
e.stopPropagation();
const table = b.dataset.table;
showTableStructure(table);
});
});
2025-10-07 15:35:33 +08:00
list.querySelectorAll('button[data-action="design"]').forEach(b=>{
b.addEventListener('click',(e)=>{
e.stopPropagation();
const table = b.dataset.table;
openTableDesigner(table);
});
});
// 应用搜索过滤
applyTableSearch();
}
// 表搜索功能
document.getElementById('tableSearch').addEventListener('input', applyTableSearch);
function applyTableSearch() {
const searchTerm = document.getElementById('tableSearch').value.toLowerCase();
const tableItems = document.querySelectorAll('.table-item');
let visibleCount = 0;
tableItems.forEach(item => {
const tableName = item.querySelector('.name').textContent.toLowerCase();
if (tableName.includes(searchTerm)) {
item.style.display = 'flex';
visibleCount++;
} else {
item.style.display = 'none';
}
});
document.getElementById('tableSearchResults').textContent =
searchTerm ? `找到 ${visibleCount} 个表` : '';
2025-10-07 12:38:53 +08:00
}
// 加载表数据
function loadTableData(tableName){
if(!window.currentConnectionId) { alert('未连接数据库'); return; }
setStatus(`正在加载表 ${tableName} 数据...`);
JavaBridge.sendRequest({type:'getTableData', connectionId: window.currentConnectionId, tableName, limit:50, offset:0}, (resp, err) => {
if(err){ setStatus('加载表数据失败'); alert('加载表数据失败:' + err.message); return; }
if(resp & & resp.status === 'success'){
renderTableData({tableName, columns: resp.columns, data: resp.data, total: resp.total, offset: resp.offset, limit: resp.limit});
setStatus('已加载数据');
document.getElementById('queryEditor').value = `SELECT * FROM ${tableName} LIMIT 50 OFFSET 0;`;
} else {
setStatus('加载表数据失败');
alert('加载表数据失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
function renderTableData(data){
const content = document.getElementById('resultsContent');
if(!data.columns || !data.data){
content.innerHTML = `< div style = "padding:20px;color:var(--muted)" > 没有数据< / div > `;
return;
}
2025-10-07 15:35:33 +08:00
// 添加表操作按钮
let html = `< div class = "table-actions" >
< button class = "btn btn-primary" id = "addRowBtn" > + 添加行< / button >
< button class = "btn btn-outline" id = "refreshTableBtn" > 刷新< / button >
< / div > `;
html += '< div class = "table-container" > < table > < thead > < tr > ';
2025-10-07 12:38:53 +08:00
data.columns.forEach(c => html += `< th > ${c}< / th > `);
2025-10-07 15:35:33 +08:00
html += '< th > 操作< / th > < / tr > < / thead > < tbody > ';
data.data.forEach((row, index)=>{
html += `< tr data-row-index = "${index}" > `;
2025-10-07 12:38:53 +08:00
data.columns.forEach(col=>{
const v = row[col];
html += `< td > ${v === null || v === undefined ? '< span style = "color:var(--muted);font-style:italic" > NULL< / span > ' : escapeHtml(String(v))}< / td > `;
});
2025-10-07 15:35:33 +08:00
html += `< td class = "action-cell" >
< button class = "btn btn-outline edit-row" data-row-index = "${index}" > 编辑< / button >
< button class = "btn btn-outline delete-row" data-row-index = "${index}" > 删除< / button >
< / td > `;
2025-10-07 12:38:53 +08:00
html += '< / tr > ';
});
html += '< / tbody > < / table > < / div > ';
content.innerHTML = html;
document.getElementById('rowColumnInfo').textContent = `行: ${data.data.length}, 列: ${data.columns.length}`;
2025-10-07 15:35:33 +08:00
// 绑定增删改按钮事件
bindTableActions(data.tableName, data.columns, data.data);
}
// 绑定表操作按钮事件
function bindTableActions(tableName, columns, data){
// 添加行按钮
document.getElementById('addRowBtn').addEventListener('click', () => {
openAddRowModal(tableName, columns);
});
// 刷新按钮
document.getElementById('refreshTableBtn').addEventListener('click', () => {
loadTableData(tableName);
});
// 编辑按钮
document.querySelectorAll('.edit-row').forEach(btn => {
btn.addEventListener('click', (e) => {
const rowIndex = parseInt(e.target.dataset.rowIndex);
const rowData = data[rowIndex];
openEditRowModal(tableName, columns, rowData);
});
});
// 删除按钮
document.querySelectorAll('.delete-row').forEach(btn => {
btn.addEventListener('click', (e) => {
const rowIndex = parseInt(e.target.dataset.rowIndex);
const rowData = data[rowIndex];
deleteRow(tableName, columns, rowData);
});
});
}
// 打开添加行模态框
function openAddRowModal(tableName, columns){
let formHtml = `< form id = "addRowForm" > `;
columns.forEach(col => {
formHtml += `
< div style = "margin-bottom:12px" >
< label class = "form-label" > ${col}< / label >
< input type = "text" class = "form-control" name = "${col}" placeholder = "输入 ${col} 值" >
< / div >
`;
});
formHtml += `< / form > `;
openToolModal(`添加行到 ${tableName}`, formHtml);
// 修改模态框底部按钮
document.getElementById('toolModalFooter').innerHTML = `
< button class = "btn btn-outline" onclick = "hideModal('toolModal')" > 取消< / button >
< button class = "btn btn-primary" id = "confirmAddRow" > 确认添加< / button >
`;
document.getElementById('confirmAddRow').addEventListener('click', () => {
const form = document.getElementById('addRowForm');
const formData = new FormData(form);
const rowData = {};
columns.forEach(col => {
rowData[col] = formData.get(col) || null;
});
insertRow(tableName, rowData);
});
}
// 打开编辑行模态框
function openEditRowModal(tableName, columns, rowData){
let formHtml = `< form id = "editRowForm" > `;
columns.forEach(col => {
const value = rowData[col] === null || rowData[col] === undefined ? '' : rowData[col];
formHtml += `
< div style = "margin-bottom:12px" >
< label class = "form-label" > ${col}< / label >
< input type = "text" class = "form-control" name = "${col}" value = "${escapeHtml(String(value))}" >
< / div >
`;
});
formHtml += `< / form > `;
openToolModal(`编辑 ${tableName} 的行`, formHtml);
// 修改模态框底部按钮
document.getElementById('toolModalFooter').innerHTML = `
< button class = "btn btn-outline" onclick = "hideModal('toolModal')" > 取消< / button >
< button class = "btn btn-primary" id = "confirmEditRow" > 确认更新< / button >
`;
document.getElementById('confirmEditRow').addEventListener('click', () => {
const form = document.getElementById('editRowForm');
const formData = new FormData(form);
const updatedData = {};
columns.forEach(col => {
updatedData[col] = formData.get(col) || null;
});
updateRow(tableName, rowData, updatedData);
});
}
// 插入行
function insertRow(tableName, rowData){
if(!window.currentConnectionId) { alert('未连接数据库'); return; }
setStatus(`正在插入行到 ${tableName}...`);
JavaBridge.sendRequest({
type: 'insertRow',
connectionId: window.currentConnectionId,
tableName,
rowData
}, (resp, err) => {
if(err){
setStatus('插入失败');
addEventLog('插入失败', err.message);
alert('插入失败: ' + err.message);
return;
}
if(resp & & resp.status === 'success'){
setStatus('插入成功');
addEventLog('插入行', `表 ${tableName}`);
hideModal('toolModal');
// 刷新表数据
loadTableData(tableName);
} else {
setStatus('插入失败');
alert('插入失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
// 更新行
function updateRow(tableName, originalData, updatedData){
if(!window.currentConnectionId) { alert('未连接数据库'); return; }
setStatus(`正在更新 ${tableName} 的行...`);
JavaBridge.sendRequest({
type: 'updateRow',
connectionId: window.currentConnectionId,
tableName,
originalData,
updatedData
}, (resp, err) => {
if(err){
setStatus('更新失败');
addEventLog('更新失败', err.message);
alert('更新失败: ' + err.message);
return;
}
if(resp & & resp.status === 'success'){
setStatus('更新成功');
addEventLog('更新行', `表 ${tableName}`);
hideModal('toolModal');
// 刷新表数据
loadTableData(tableName);
} else {
setStatus('更新失败');
alert('更新失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
// 删除行
function deleteRow(tableName, columns, rowData){
if(!window.currentConnectionId) { alert('未连接数据库'); return; }
if(!confirm('确定要删除这行数据吗?此操作不可撤销。')) {
return;
}
setStatus(`正在从 ${tableName} 删除行...`);
JavaBridge.sendRequest({
type: 'deleteRow',
connectionId: window.currentConnectionId,
tableName,
rowData
}, (resp, err) => {
if(err){
setStatus('删除失败');
addEventLog('删除失败', err.message);
alert('删除失败: ' + err.message);
return;
}
if(resp & & resp.status === 'success'){
setStatus('删除成功');
addEventLog('删除行', `表 ${tableName}`);
// 刷新表数据
loadTableData(tableName);
} else {
setStatus('删除失败');
alert('删除失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
// 打开表设计器 - 修复:只在新建表时重置表单
function openTableDesigner(tableName = null) {
if(!window.currentConnectionId) { alert('未连接数据库'); return; }
const isNewTable = !tableName;
document.getElementById('tableDesignerTitle').textContent =
isNewTable ? '创建新表' : `设计表: ${tableName}`;
// 只有在新建表时才重置表单
if (isNewTable) {
resetTableDesignerForm();
}
// 如果是编辑现有表,加载表结构
if (!isNewTable) {
setStatus(`正在加载表 ${tableName} 结构...`);
JavaBridge.sendRequest({
type: 'getTableStructure',
connectionId: window.currentConnectionId,
tableName
}, (resp, err) => {
if (err) {
setStatus('加载表结构失败');
alert('加载表结构失败: ' + err.message);
return;
}
if (resp & & resp.status === 'success') {
// 填充表结构到设计器
populateTableDesigner(resp);
setStatus('表结构已加载');
} else {
setStatus('加载表结构失败');
alert('加载表结构失败');
}
});
}
showModal('tableDesignerModal');
// 绑定保存按钮事件
document.getElementById('saveTableDesignBtn').onclick = () => {
saveTableDesign(isNewTable ? null : tableName);
};
}
// 重置表设计器表单(只在新建表时调用)
function resetTableDesignerForm() {
document.getElementById('tableName').value = '';
document.getElementById('tableEngine').value = 'InnoDB';
document.getElementById('tableCharset').value = 'utf8mb4';
document.getElementById('tableCollation').value = 'utf8mb4_unicode_ci';
document.getElementById('tableComment').value = '';
// 清空列列表
document.getElementById('columnsList').innerHTML = '';
document.getElementById('indexesList').innerHTML = '';
document.getElementById('constraintsList').innerHTML = '';
// 添加默认列
addColumnRow();
}
// 添加列行 - 修改: 根据新的Java数据结构处理列属性
function addColumnRow(columnData = null) {
const columnsList = document.getElementById('columnsList');
const columnId = 'column_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
// 获取列数据,如果没有数据则使用默认值
const typeValue = columnData ? columnData.type : 'VARCHAR(255)';
const nullableValue = columnData ? columnData.nullable : true;
const autoIncrementValue = columnData ? columnData.autoIncrement : false;
// 根据nullable和autoIncrement构建attributes值
let attributesValue = '';
if (columnData) {
if (columnData.autoIncrement) {
attributesValue = 'AUTO_INCREMENT';
}
if (!columnData.nullable) {
if (attributesValue) attributesValue += ' NOT NULL';
else attributesValue = 'NOT NULL';
}
}
// 定义所有可用的列类型
const columnTypes = [
'INT', 'VARCHAR(255)', 'TEXT', 'DATE', 'DATETIME', 'DECIMAL(10,2)', 'BOOLEAN',
'BIGINT', 'SMALLINT', 'TINYINT', 'FLOAT', 'DOUBLE', 'CHAR(1)', 'BLOB', 'LONGTEXT'
];
// 生成类型选项
let typeOptions = '';
columnTypes.forEach(type => {
const selected = type === typeValue ? 'selected' : '';
typeOptions += `< option value = "${type}" $ { selected } > ${type}< / option > `;
});
// 如果类型不在预定义列表中,添加自定义选项
if (columnData & & !columnTypes.includes(typeValue)) {
typeOptions += `< option value = "${typeValue}" selected > ${typeValue}< / option > `;
}
const columnHtml = `
< div class = "column-item" id = "${columnId}" >
< div >
< input type = "text" class = "form-control column-name" placeholder = "列名" value = "${columnData ? columnData.name : ''}" >
< / div >
< div >
< select class = "form-control column-type" >
${typeOptions}
< / select >
< / div >
< div >
< select class = "form-control column-attributes" >
< option value = "" > 无< / option >
< option value = "NOT NULL" $ { attributesValue . includes ( ' NOT NULL ' ) ? ' selected ' : ' ' } > 非空< / option >
< option value = "AUTO_INCREMENT" $ { attributesValue . includes ( ' AUTO_INCREMENT ' ) ? ' selected ' : ' ' } > 自增< / option >
< option value = "PRIMARY KEY" $ { attributesValue . includes ( ' PRIMARY KEY ' ) ? ' selected ' : ' ' } > 主键< / option >
< option value = "UNIQUE" $ { attributesValue . includes ( ' UNIQUE ' ) ? ' selected ' : ' ' } > 唯一< / option >
< option value = "NOT NULL AUTO_INCREMENT" $ { attributesValue . includes ( ' NOT NULL ' ) & & attributesValue . includes ( ' AUTO_INCREMENT ' ) ? ' selected ' : ' ' } > 非空+自增< / option >
< / select >
< / div >
< div class = "column-actions" >
< button class = "btn btn-outline move-up" type = "button" > ↑< / button >
< button class = "btn btn-outline move-down" type = "button" > ↓< / button >
< button class = "btn btn-outline remove-column" type = "button" > 删除< / button >
< / div >
< / div >
`;
columnsList.insertAdjacentHTML('beforeend', columnHtml);
// 绑定事件
const columnElement = document.getElementById(columnId);
columnElement.querySelector('.remove-column').addEventListener('click', () => {
columnElement.remove();
});
columnElement.querySelector('.move-up').addEventListener('click', () => {
const prev = columnElement.previousElementSibling;
if (prev) {
columnsList.insertBefore(columnElement, prev);
}
});
columnElement.querySelector('.move-down').addEventListener('click', () => {
const next = columnElement.nextElementSibling;
if (next) {
columnsList.insertBefore(next, columnElement);
}
});
}
// 添加索引行
function addIndexRow(indexData = null) {
const indexesList = document.getElementById('indexesList');
const indexId = 'index_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
const indexHtml = `
< div class = "index-item" id = "${indexId}" >
< div >
< input type = "text" class = "form-control index-name" placeholder = "索引名" value = "${indexData ? indexData.name : ''}" >
< / div >
< div >
< select class = "form-control index-type" >
< option value = "INDEX" $ { ( ! indexData | | indexData . type = == ' INDEX ' ) ? ' selected ' : ' ' } > 普通索引< / option >
< option value = "UNIQUE" $ { indexData & & indexData . type = == ' UNIQUE ' ? ' selected ' : ' ' } > 唯一索引< / option >
< option value = "FULLTEXT" $ { indexData & & indexData . type = == ' FULLTEXT ' ? ' selected ' : ' ' } > 全文索引< / option >
< / select >
< / div >
< div >
< input type = "text" class = "form-control index-columns" placeholder = "列名(逗号分隔)" value = "${indexData ? indexData.columns : ''}" >
< / div >
< div class = "column-actions" >
< button class = "btn btn-outline remove-index" type = "button" > 删除< / button >
< / div >
< / div >
`;
indexesList.insertAdjacentHTML('beforeend', indexHtml);
// 绑定事件
const indexElement = document.getElementById(indexId);
indexElement.querySelector('.remove-index').addEventListener('click', () => {
indexElement.remove();
});
}
// 添加约束行
function addConstraintRow(constraintData = null) {
const constraintsList = document.getElementById('constraintsList');
const constraintId = 'constraint_' + Date.now() + '_' + Math.random().toString(36).substr(2, 5);
const constraintHtml = `
< div class = "constraint-item" id = "${constraintId}" >
< div >
< input type = "text" class = "form-control constraint-name" placeholder = "约束名" value = "${constraintData ? constraintData.name : ''}" >
< / div >
< div >
< select class = "form-control constraint-type" >
< option value = "FOREIGN KEY" $ { ( ! constraintData | | constraintData . type = == ' FOREIGN KEY ' ) ? ' selected ' : ' ' } > 外键< / option >
< option value = "PRIMARY KEY" $ { constraintData & & constraintData . type = == ' PRIMARY KEY ' ? ' selected ' : ' ' } > 主键< / option >
< option value = "CHECK" $ { constraintData & & constraintData . type = == ' CHECK ' ? ' selected ' : ' ' } > 检查约束< / option >
< / select >
< / div >
< div >
< input type = "text" class = "form-control constraint-definition" placeholder = "约束定义" value = "${constraintData ? constraintData.definition : ''}" >
< / div >
< div class = "column-actions" >
< button class = "btn btn-outline remove-constraint" type = "button" > 删除< / button >
< / div >
< / div >
`;
constraintsList.insertAdjacentHTML('beforeend', constraintHtml);
// 绑定事件
const constraintElement = document.getElementById(constraintId);
constraintElement.querySelector('.remove-constraint').addEventListener('click', () => {
constraintElement.remove();
});
}
// 填充表设计器 - 修改: 根据新的Java数据结构处理所有字段
function populateTableDesigner(tableStructure) {
// 设置表基本信息 - 处理可能的JSONObject.NULL值
document.getElementById('tableName').value = tableStructure.tableName || '';
document.getElementById('tableEngine').value = (tableStructure.engine & & tableStructure.engine !== null & & tableStructure.engine !== 'null') ? tableStructure.engine : 'InnoDB';
document.getElementById('tableCharset').value = (tableStructure.charset & & tableStructure.charset !== null & & tableStructure.charset !== 'null') ? tableStructure.charset : 'utf8mb4';
document.getElementById('tableCollation').value = (tableStructure.collation & & tableStructure.collation !== null & & tableStructure.collation !== 'null') ? tableStructure.collation : 'utf8mb4_unicode_ci';
document.getElementById('tableComment').value = (tableStructure.comment & & tableStructure.comment !== null & & tableStructure.comment !== 'null') ? tableStructure.comment : '';
// 清空列列表
document.getElementById('columnsList').innerHTML = '';
document.getElementById('indexesList').innerHTML = '';
document.getElementById('constraintsList').innerHTML = '';
// 添加列
if (tableStructure.columns & & tableStructure.columns.length > 0) {
tableStructure.columns.forEach(column => {
addColumnRow(column);
});
} else {
addColumnRow();
}
// 添加索引
if (tableStructure.indexes & & tableStructure.indexes.length > 0) {
tableStructure.indexes.forEach(index => {
addIndexRow(index);
});
}
// 添加约束
if (tableStructure.constraints & & tableStructure.constraints.length > 0) {
tableStructure.constraints.forEach(constraint => {
addConstraintRow(constraint);
});
}
2025-10-07 12:38:53 +08:00
}
2025-10-07 15:35:33 +08:00
// 保存表设计
function saveTableDesign(existingTableName) {
const tableName = document.getElementById('tableName').value.trim();
if (!tableName) {
alert('请输入表名');
return;
}
// 收集列信息
const columns = [];
const columnElements = document.querySelectorAll('.column-item');
columnElements.forEach(colElement => {
const name = colElement.querySelector('.column-name').value.trim();
const type = colElement.querySelector('.column-type').value;
const attributes = colElement.querySelector('.column-attributes').value;
if (name) {
columns.push({
name: name,
type: type,
attributes: attributes
});
}
});
if (columns.length === 0) {
alert('请至少定义一个列');
return;
}
// 收集索引信息
const indexes = [];
const indexElements = document.querySelectorAll('.index-item');
indexElements.forEach(indexElement => {
const name = indexElement.querySelector('.index-name').value.trim();
const type = indexElement.querySelector('.index-type').value;
const columns = indexElement.querySelector('.index-columns').value.trim();
if (name & & columns) {
indexes.push({
name: name,
type: type,
columns: columns
});
}
});
// 收集约束信息
const constraints = [];
const constraintElements = document.querySelectorAll('.constraint-item');
constraintElements.forEach(constraintElement => {
const name = constraintElement.querySelector('.constraint-name').value.trim();
const type = constraintElement.querySelector('.constraint-type').value;
const definition = constraintElement.querySelector('.constraint-definition').value.trim();
if (name & & definition) {
constraints.push({
name: name,
type: type,
definition: definition
});
}
});
// 收集表选项
const tableOptions = {
engine: document.getElementById('tableEngine').value,
charset: document.getElementById('tableCharset').value,
collation: document.getElementById('tableCollation').value,
comment: document.getElementById('tableComment').value
};
setStatus(existingTableName ? `正在修改表 ${tableName}...` : `正在创建表 ${tableName}...`);
JavaBridge.sendRequest({
type: existingTableName ? 'alterTable' : 'createTable',
connectionId: window.currentConnectionId,
tableName: existingTableName || tableName,
newTableName: existingTableName ? tableName : null,
columns: columns,
indexes: indexes,
constraints: constraints,
tableOptions: tableOptions
}, (resp, err) => {
if (err) {
setStatus('操作失败');
alert('操作失败: ' + err.message);
return;
}
if (resp & & resp.status === 'success') {
setStatus('操作成功');
addEventLog(existingTableName ? '修改表' : '创建表',
existingTableName ? `表 ${existingTableName} 已更新为 ${tableName}` : `表 ${tableName} 已创建`);
hideModal('tableDesignerModal');
// 刷新表列表
loadTables();
} else {
setStatus('操作失败');
alert('操作失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
// 查看表结构 - 修改:显示更详细的结构信息
2025-10-07 12:38:53 +08:00
function showTableStructure(tableName){
if(!window.currentConnectionId) return alert('未连接数据库');
setStatus('查询表结构...');
JavaBridge.sendRequest({type:'getTableStructure', connectionId:window.currentConnectionId, tableName}, (resp, err) => {
if(err){ setStatus('获取失败'); alert('获取表结构失败:'+err.message); return; }
if(resp & & resp.status === 'success'){
openToolModal(`表结构: ${tableName}`, renderStructureHtml(resp));
} else {
alert('获取表结构失败: ' + (resp & & resp.message || '未知错误'));
}
});
}
function renderStructureHtml(resp){
2025-10-07 15:35:33 +08:00
let html = `< div style = "margin-bottom:16px" >
< div > < strong > 表名:< / strong > ${escapeHtml(resp.tableName || '')}< / div >
< div > < strong > 引擎:< / strong > ${escapeHtml(resp.engine || '')}< / div >
< div > < strong > 字符集:< / strong > ${escapeHtml(resp.charset || '')}< / div >
< div > < strong > 排序规则:< / strong > ${escapeHtml(resp.collation || '')}< / div >
< div > < strong > 注释:< / strong > ${escapeHtml(resp.comment || '')}< / div >
< / div > `;
2025-10-07 12:38:53 +08:00
if(resp.columns & & resp.columns.length>0){
2025-10-07 15:35:33 +08:00
html += '< h3 style = "margin:16px 0 8px 0" > 列信息< / h3 > ';
html += '< table > < thead > < tr > < th > 列名< / th > < th > 类型< / th > < th > 大小< / th > < th > 可空< / th > < th > 默认值< / th > < th > 自增< / th > < / tr > < / thead > < tbody > ';
2025-10-07 12:38:53 +08:00
resp.columns.forEach(c=>{
2025-10-07 15:35:33 +08:00
html += `< tr >
< td > ${escapeHtml(c.name)}< / td >
< td > ${escapeHtml(c.type)}< / td >
< td > ${c.size||''}< / td >
< td > ${c.nullable ? '是' : '否'}< / td >
< td > ${escapeHtml(c.defaultValue||'')}< / td >
< td > ${c.autoIncrement ? '是' : '否'}< / td >
< / tr > `;
2025-10-07 12:38:53 +08:00
});
html += '< / tbody > < / table > ';
} else {
html += `< div style = "padding:12px;color:var(--muted)" > 没有列信息< / div > `;
}
2025-10-07 15:35:33 +08:00
if(resp.indexes & & resp.indexes.length>0){
html += '< h3 style = "margin:16px 0 8px 0" > 索引信息< / h3 > ';
html += '< table > < thead > < tr > < th > 索引名< / th > < th > 类型< / th > < th > 列< / th > < / tr > < / thead > < tbody > ';
resp.indexes.forEach(idx=>{
html += `< tr >
< td > ${escapeHtml(idx.name)}< / td >
< td > ${escapeHtml(idx.type)}< / td >
< td > ${escapeHtml(idx.columns)}< / td >
< / tr > `;
});
html += '< / tbody > < / table > ';
}
if(resp.constraints & & resp.constraints.length>0){
html += '< h3 style = "margin:16px 0 8px 0" > 约束信息< / h3 > ';
html += '< table > < thead > < tr > < th > 约束名< / th > < th > 类型< / th > < th > 定义< / th > < / tr > < / thead > < tbody > ';
resp.constraints.forEach(con=>{
html += `< tr >
< td > ${escapeHtml(con.name)}< / td >
< td > ${escapeHtml(con.type)}< / td >
< td > ${escapeHtml(con.definition)}< / td >
< / tr > `;
});
html += '< / tbody > < / table > ';
}
return html;
}
function renderErDiagram(erData) {
if(!erData.tables || !Array.isArray(erData.tables)) {
return '< div > 无效的ER数据< / div > ';
}
let html = '< div class = "er-diagram" style = "display: flex; flex-wrap: wrap; gap: 20px;" > ';
erData.tables.forEach(table => {
html += `
< div class = "er-table" style = "border: 2px solid #333; border-radius: 8px; min-width: 200px;" >
< div class = "table-header" style = "background: #f0f0f0; padding: 8px; font-weight: bold; border-bottom: 1px solid #ccc;" >
${escapeHtml(table.name)}
< / div >
< div class = "table-columns" style = "padding: 8px;" >
`;
if(table.columns & & Array.isArray(table.columns)) {
table.columns.forEach(column => {
const type = column.type || 'VARCHAR';
const size = column.size ? `(${column.size})` : '';
const nullable = column.nullable ? 'NULL' : 'NOT NULL';
html += `
< div style = "padding: 4px 0; border-bottom: 1px dashed #eee;" >
< span style = "font-weight: bold;" > ${escapeHtml(column.name)}< / span >
< br >
< small style = "color: #666;" > ${type}${size} ${nullable}< / small >
< / div >
`;
});
}
html += `
< / div >
< / div >
`;
});
html += '< / div > ';
2025-10-07 12:38:53 +08:00
return html;
}
// 工具按钮统一处理
document.querySelectorAll('.tool-btn').forEach(btn=>{
btn.addEventListener('click', ()=>{
const action = btn.dataset.action;
switch(action){
2025-10-07 15:35:33 +08:00
case 'tableDesigner':
openTableDesigner();
break;
2025-10-07 12:38:53 +08:00
case 'queryAnalyzer':
openToolModal('查询分析器', `< div > 查询分析器(本地模拟)< div style = "margin-top:12px" > < textarea id = "analyzerSql" style = "width:100%;min-height:120px;padding:8px;border:1px solid var(--border)" > ${document.getElementById('queryEditor').value}< / textarea > < / div > < div style = "margin-top:8px" > < button class = "btn btn-primary" id = "analyzeBtn" > 分析< / button > < / div > < / div > `);
// 绑定分析按钮
setTimeout(()=>document.getElementById('analyzeBtn').addEventListener('click', ()=>{
const sql = document.getElementById('analyzerSql').value;
// 这里演示调用 Java 后端接口(实际需要后端支持)
JavaBridge.sendRequest({type:'analyzeQuery', connectionId: window.currentConnectionId, query: sql}, (resp,err)=>{
if(err) return alert('分析失败: ' + err.message);
openToolModal('查询分析结果', `< pre style = "white-space:pre-wrap" > ${escapeHtml(JSON.stringify(resp, null, 2))}< / pre > `);
});
}),60);
break;
case 'exportData':
openToolModal('导出数据', `< div > 选择导出格式:< div style = "margin-top:8px" > < select id = "exportFormat" style = "padding:8px;border:1px solid var(--border)" > < option value = "csv" > CSV< / option > < option value = "json" > JSON< / option > < / select > < / div > < div style = "margin-top:8px" > 表名:< input id = "exportTable" style = "width:100%;padding:8px;border:1px solid var(--border)" > < / div > < div style = "margin-top:8px" > < button class = "btn btn-primary" id = "doExport" > 开始导出< / button > < / div > < / div > `);
setTimeout(()=>document.getElementById('doExport').addEventListener('click', ()=>{
const format = document.getElementById('exportFormat').value;
const table = document.getElementById('exportTable').value;
if(!table) return alert('请输入表名');
JavaBridge.sendRequest({type:'exportData', connectionId: window.currentConnectionId, table, format}, (resp,err)=>{
if(err) return alert('导出失败: ' + err.message);
alert('导出触发成功(请在 Java 层实现具体保存)');
addEventLog('导出', `表 ${table} 导出为 ${format}`);
});
}),60);
break;
case 'importCsv':
openToolModal('CSV 导入', `< div > CSV 导入(请先确保文件已存在于应用可访问位置)< div style = "margin-top:8px" > 目标表:< input id = "importTable" style = "width:100%;padding:8px;border:1px solid var(--border)" > < / div > < div style = "margin-top:8px" > 文件路径:< input id = "importPath" placeholder = "C:/path/file.csv 或 /home/.../file.csv" style = "width:100%;padding:8px;border:1px solid var(--border)" > < / div > < div style = "margin-top:8px" > < button class = "btn btn-primary" id = "doImport" > 开始导入< / button > < / div > < / div > `);
setTimeout(()=>document.getElementById('doImport').addEventListener('click', ()=>{
const table = document.getElementById('importTable').value;
const path = document.getElementById('importPath').value;
if(!table || !path) return alert('请填写表名和文件路径');
JavaBridge.sendRequest({type:'importCsv', connectionId: window.currentConnectionId, table, path}, (resp,err)=>{
if(err) return alert('导入失败: ' + err.message);
alert('导入触发成功(请在 Java 层实现)');
addEventLog('导入', `从 ${path} 到 ${table}`);
});
}),60);
break;
case 'erDiagram':
openToolModal('ER 图', `< div > 生成 ER 图(演示)< div style = "margin-top:12px;color:var(--muted)" > 请在后端实现 ER 图生成并返回 SVG/图片。< / div > < div style = "margin-top:12px" > < button class = "btn btn-primary" id = "genEr" > 生成 ER 图< / button > < / div > < / div > `);
setTimeout(()=>document.getElementById('genEr').addEventListener('click', ()=>{
JavaBridge.sendRequest({type:'generateEr', connectionId: window.currentConnectionId}, (resp,err)=>{
if(err) return alert('生成失败: '+err.message);
2025-10-07 15:35:33 +08:00
openToolModal('ER 图', renderErDiagram(resp.er));
2025-10-07 12:38:53 +08:00
});
}),60);
break;
case 'performance':
openToolModal('性能监控', `< div > 性能监控(实时)< div id = "perfContent" style = "margin-top:8px;color:var(--muted)" > 等待数据...< / div > < div style = "margin-top:8px" > < button class = "btn btn-primary" id = "refreshPerf" > 刷新< / button > < / div > < / div > `);
setTimeout(()=>document.getElementById('refreshPerf').addEventListener('click', ()=>{
JavaBridge.sendRequest({type:'analyzePerformance', connectionId: window.currentConnectionId}, (resp,err)=>{
if(err) return alert('获取失败: ' + err.message);
document.getElementById('perfContent').innerHTML = `< pre style = "white-space:pre-wrap" > ${escapeHtml(JSON.stringify(resp, null, 2))}< / pre > `;
});
}),60);
break;
case 'userMgmt':
openToolModal('用户管理', `< div > 用户管理(示例)< div style = "margin-top:8px" > < button class = "btn btn-primary" id = "listUsers" > 列出用户< / button > < / div > < div id = "usersList" style = "margin-top:8px" > < / div > < / div > `);
setTimeout(()=>document.getElementById('listUsers').addEventListener('click', ()=>{
JavaBridge.sendRequest({type:'listUsers', connectionId: window.currentConnectionId}, (resp,err)=>{
if(err) return alert('失败: '+err.message);
document.getElementById('usersList').innerHTML = `< pre style = "white-space:pre-wrap" > ${escapeHtml(JSON.stringify(resp,null,2))}< / pre > `;
});
}),60);
break;
default:
alert('未实现的工具: ' + action);
}
2025-10-07 15:35:33 +08:00
// 关闭工具面板
document.getElementById('toolsPanel').classList.remove('active');
2025-10-07 12:38:53 +08:00
});
});
2025-10-07 15:35:33 +08:00
// 绑定添加列按钮
document.getElementById('addColumnBtn').addEventListener('click', () => {
addColumnRow();
});
// 绑定添加索引按钮
document.getElementById('addIndexBtn').addEventListener('click', () => {
addIndexRow();
});
// 绑定添加约束按钮
document.getElementById('addConstraintBtn').addEventListener('click', () => {
addConstraintRow();
});
// 快速创建表按钮
document.getElementById('quickCreateTableBtn').addEventListener('click', () => {
openTableDesigner();
});
2025-10-07 12:38:53 +08:00
function openToolModal(title, bodyHtml){
document.getElementById('toolModalTitle').textContent = title;
document.getElementById('toolModalBody').innerHTML = bodyHtml;
document.getElementById('toolModalFooter').innerHTML = `< button class = "btn btn-outline" onclick = "hideModal('toolModal')" > 关闭< / button > `;
showModal('toolModal');
}
// 查询相关按钮
document.getElementById('executeQueryBtn').addEventListener('click', ()=>{
const q = document.getElementById('queryEditor').value.trim();
if(!q) return alert('请输入 SQL');
if(!window.currentConnectionId) return alert('请先连接数据库');
setStatus('正在执行查询...');
JavaBridge.sendRequest({type:'executeQuery', connectionId:window.currentConnectionId, query:q}, (resp,err)=>{
if(err){ setStatus('查询失败'); alert('查询失败:'+err.message); return; }
if(resp & & resp.status === 'success'){
renderQueryResults(resp);
setStatus('查询成功');
addEventLog('查询', q.slice(0,120));
} else {
setStatus('查询失败');
alert('查询失败: ' + (resp & & resp.message || '未知错误'));
}
});
});
function renderQueryResults(resp){
if(resp.columns & & resp.data){
// 重用 renderTableData 风格(临时对象)
renderTableData({tableName:'QueryResult', columns: resp.columns, data: resp.data, total: resp.rowCount || resp.data.length, offset:0, limit:resp.data.length});
document.getElementById('resultsInfo').textContent = `行: ${resp.rowCount || resp.data.length} 耗时: ${resp.executionTime || 'N/A'}`;
} else {
document.getElementById('resultsContent').innerHTML = `< div style = "padding:12px" > 操作完成,影响行数:${resp.affectedRows || 0}< / div > `;
}
}
// 格式化按钮(保留你原有简单逻辑)
document.getElementById('formatQueryBtn').addEventListener('click', ()=>{
let query = document.getElementById('queryEditor').value;
query = query.replace(/\bSELECT\b/gi, '\nSELECT').replace(/\bFROM\b/gi, '\nFROM')
.replace(/\bWHERE\b/gi, '\nWHERE').replace(/\bORDER BY\b/gi, '\nORDER BY')
.replace(/\bGROUP BY\b/gi, '\nGROUP BY').replace(/\bHAVING\b/gi, '\nHAVING')
.replace(/\bJOIN\b/gi, '\nJOIN').replace(/\bON\b/gi, '\nON').replace(/,/g, ',\n ');
document.getElementById('queryEditor').value = query.trim();
});
// 刷新表
document.getElementById('refreshTablesBtn').addEventListener('click', ()=> loadTables());
// 事件日志按钮
document.getElementById('showEventsBtn').addEventListener('click', ()=> {
const el = document.getElementById('eventLog');
el.style.display = el.style.display === 'block' ? 'none' : 'block';
});
// 状态函数
function setStatus(msg){
document.getElementById('statusMessage').textContent = msg;
document.getElementById('statusSmall').textContent = '状态: ' + msg;
}
// 工具:转义 HTML( 避免 XSS)
function escapeHtml(str){
return String(str).replace(/&/g,'& ').replace(/< /g,'< ').replace(/>/g,'> ').replace(/"/g,'" ');
}
2025-10-07 15:35:33 +08:00
// 搜索功能实现
document.addEventListener('keydown', (e) => {
// Ctrl+F 在结果区域搜索
if (e.ctrlKey & & e.key === 'f') {
e.preventDefault();
const searchBox = document.getElementById('resultsSearchBox');
const searchInput = document.getElementById('resultsSearchInput');
if (searchBox.style.display === 'none') {
searchBox.style.display = 'block';
searchInput.focus();
} else {
searchBox.style.display = 'none';
}
}
});
// 结果搜索功能
document.getElementById('resultsSearchInput').addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const table = document.querySelector('.results-content table');
if (!table) return;
const rows = table.querySelectorAll('tbody tr');
let matchCount = 0;
rows.forEach(row => {
const text = row.textContent.toLowerCase();
if (text.includes(searchTerm)) {
row.style.display = '';
matchCount++;
// 高亮匹配的文本
if (searchTerm) {
const cells = row.querySelectorAll('td');
cells.forEach(cell => {
const originalText = cell.textContent;
const highlightedText = originalText.replace(
new RegExp(searchTerm, 'gi'),
match => `< mark style = "background-color: yellow; color: black;" > ${match}< / mark > `
);
cell.innerHTML = highlightedText;
});
}
} else {
row.style.display = 'none';
}
});
document.getElementById('resultsSearchInfo').textContent =
searchTerm ? `找到 ${matchCount} 个匹配项` : '';
});
2025-10-07 12:38:53 +08:00
// 初始化(模拟触发字体加载回调)
document.addEventListener('DOMContentLoaded', ()=>{
// 初始化 localDbPath
const dbName = document.getElementById('localDbName').value || 'my_database';
const driver = document.getElementById('localDbDriver').value;
const extension = driver === 'h2' ? '' : '.db';
document.getElementById('localDbPath').value = `~/.axis_innovators_box/databases/${dbName}${extension}`;
// 触发 fonts loaded( 兼容旧逻辑)
setTimeout(()=> {
JavaBridge.sendRequest({type:'getFonts'}, (resp,err)=>{
if(!err & & resp & & resp.status === 'success') addEventLog('字体加载', `已加载 ${resp.fonts.length} 字体`);
});
}, 600);
});
< / script >
< / body >
2025-10-07 15:35:33 +08:00
< / html >