Files
window-axis-innovators-box/javascript/DatabaseTool.html
tzdwindows 7 efc73c935d feat(browser): 实现主题和字体动态更新功能
- 移除重复的字体信息注入逻辑
- 添加 updateTheme 方法统一处理主题和字体更新
- 在 setVisible 方法中调用 updateTheme 确保显示时更新
-优化 JavaScript 中的主题应用逻辑,增强兼容性
- 增强 HTML 页面中的主题监听和字体应用功能
- 添加事件计数器和调试信息用于追踪主题变化
2025-10-07 17:07:15 +08:00

2015 lines
93 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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)}
/* 工具区域(新设计) */
.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}
.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)}
.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);
}
/* 内容区 */
.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}
/* 表格操作按钮 */
.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)}
.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}
/* 表单控件 */
.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}
/* 响应式小屏 */
@media (max-width:900px){
.sidebar{display:none}
.query-editor{width:100%}
.query-editor-container{flex-direction:column}
.column-item{grid-template-columns:1fr;gap:4px}
.index-item{grid-template-columns:1fr;gap:4px}
.constraint-item{grid-template-columns:1fr;gap:4px}
}
/* 小图标样式SVG 作为图标) */
.svg-icon{width:18px;height:18px;display:inline-block;vertical-align:middle;fill:currentColor}
/* 快速创建表按钮 */
.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}
</style>
</head>
<body>
<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>
<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>
<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>
<button class="tools-toggle" id="toolsToggle">
<span>...</span>
</button>
</div>
</aside>
<!-- 工具面板 -->
<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>
<!-- 内容区 -->
<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>
<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>
<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>
<!-- 表设计器模态 -->
<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>
<!-- 事件日志面板 -->
<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>
window.eventCounter = {
javaFontsLoaded: 0,
javaThemeChanged: 0
};
document.addEventListener('javaThemeChanged', function(event) {
window.eventCounter.javaThemeChanged++;
console.log('事件详情:', event.detail);
if (typeof applyJavaTheme === 'function') {
applyJavaTheme(event.detail);
}
}, true);
// 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 {
// 开发环境模拟 - 更新模拟数据以匹配新的Java结构
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);
} 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);
} 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;
// const themeToggle = document.getElementById('themeToggle');
//
// let currentTheme = localStorage.getItem('dbToolTheme') || 'light';
//
// applyTheme(currentTheme);
//
// themeToggle.addEventListener('click', () => {
// const currentTheme = body.getAttribute('data-theme');
// const newTheme = currentTheme === 'light' ? 'dark' : 'light';
//
// localStorage.setItem('dbToolTheme', newTheme);
// applyTheme(newTheme);
// });
//
// function applyTheme(theme){
// body.setAttribute('data-theme', theme);
// themeToggle.textContent = theme === 'light' ? '🌙' : '☀️';
// }
//})();
// 绑定工具面板切换
document.getElementById('toolsToggle').addEventListener('click', () => {
document.getElementById('toolsPanel').classList.toggle('active');
});
// 绑定打开连接对话框
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;
window.currentDatabase = database;
// 更新数据库列表(这是简化示例:仅显示当前)
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;
}
// 存储所有表数据用于搜索
window.allTables = tables;
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>
<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>`;
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);
});
});
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} 个表` : '';
}
// 加载表数据
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;
}
// 添加表操作按钮
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>';
data.columns.forEach(c => html += `<th>${c}</th>`);
html += '<th>操作</th></tr></thead><tbody>';
data.data.forEach((row, index)=>{
html += `<tr data-row-index="${index}">`;
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>`;
});
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>`;
html += '</tr>';
});
html += '</tbody></table></div>';
content.innerHTML = html;
document.getElementById('rowColumnInfo').textContent = `行: ${data.data.length}, 列: ${data.columns.length}`;
// 绑定增删改按钮事件
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);
});
}
}
// 保存表设计
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 || '未知错误'));
}
});
}
// 查看表结构 - 修改:显示更详细的结构信息
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){
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>`;
if(resp.columns && resp.columns.length>0){
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>';
resp.columns.forEach(c=>{
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>`;
});
html += '</tbody></table>';
} else {
html += `<div style="padding:12px;color:var(--muted)">没有列信息</div>`;
}
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>';
return html;
}
// 工具按钮统一处理
document.querySelectorAll('.tool-btn').forEach(btn=>{
btn.addEventListener('click', ()=>{
const action = btn.dataset.action;
switch(action){
case 'tableDesigner':
openTableDesigner();
break;
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);
openToolModal('ER 图', renderErDiagram(resp.er));
});
}),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);
}
// 关闭工具面板
document.getElementById('toolsPanel').classList.remove('active');
});
});
// 绑定添加列按钮
document.getElementById('addColumnBtn').addEventListener('click', () => {
addColumnRow();
});
// 绑定添加索引按钮
document.getElementById('addIndexBtn').addEventListener('click', () => {
addIndexRow();
});
// 绑定添加约束按钮
document.getElementById('addConstraintBtn').addEventListener('click', () => {
addConstraintRow();
});
// 快速创建表按钮
document.getElementById('quickCreateTableBtn').addEventListener('click', () => {
openTableDesigner();
});
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,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
// 搜索功能实现
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} 个匹配项` : '';
});
// 监听Java字体加载完成事件
document.addEventListener('javaFontsLoaded', function(event) {
const fontInfo = event.detail;
console.log('接收到Java字体信息:', fontInfo);
// 应用Java字体到编辑器
applyJavaFonts(fontInfo);
});
// 应用Java字体的函数 - 修复版本
function applyJavaFonts(fontInfo) {
const uiFonts = fontInfo.uiFonts || {};
const defaultFont = fontInfo.defaultFont || uiFonts['Label.font'] || {};
if (defaultFont && defaultFont.family) {
const fontFamily = defaultFont.family;
const fontSize = defaultFont.size || 14;
const fontWeight = defaultFont.bold ? 'bold' : 'normal';
const fontStyle = defaultFont.italic ? 'italic' : 'normal';
// 移除之前可能存在的字体样式
const existingStyle = document.getElementById('java-fonts-style');
if (existingStyle) {
existingStyle.remove();
}
// 创建字体样式 - 增加优先级和更全面的覆盖
const style = document.createElement('style');
style.id = 'java-fonts-style';
style.textContent = `
/* 强制应用Java字体到所有元素 */
* {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
}
/* 特定元素覆盖 */
body, html {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
font-size: ${fontSize}px !important;
}
.toolbar, button, select, input {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
font-size: ${fontSize}px !important;
}
.log-item {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
font-size: ${fontSize}px !important;
}
/* CodeMirror 编辑器字体 */
.CodeMirror, .CodeMirror * {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
font-size: ${fontSize}px !important;
}
.CodeMirror pre, .CodeMirror-code, .CodeMirror-line {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
font-size: ${fontSize}px !important;
}
.query-editor, .editor-content, textarea {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
font-size: ${fontSize}px !important;
}
table, th, td {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
}
.sidebar-item, .sidebar-title {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
}
.modal, .form-control {
font-family: '${fontFamily}', 'Fira Code', 'JetBrains Mono', monospace !important;
}
`;
// 添加到文档头
document.head.appendChild(style);
console.log('Java字体已应用到HTML界面:', fontFamily, fontSize + 'px');
// 强制刷新CodeMirror编辑器
if (window.editor) {
setTimeout(() => {
editor.refresh();
// 重新设置内容以触发完全重绘
const content = editor.getValue();
editor.setValue('');
editor.setValue(content);
// 额外的刷新确保字体应用
setTimeout(() => {
editor.refresh();
}, 100);
}, 50);
}
// 记录字体应用事件
addEventLog('字体应用', `已应用字体: ${fontFamily} ${fontSize}px`);
}
}
// 应用Java主题的函数
function applyJavaTheme(themeInfo) {
console.log('🎨 开始应用Java主题原始数据:', themeInfo);
// 复用现有的主题处理逻辑
let isDarkTheme;
if (typeof themeInfo.isDarkTheme === 'boolean') {
isDarkTheme = themeInfo.isDarkTheme;
} else if (typeof themeInfo.isDarkTheme === 'string') {
// 处理字符串类型的布尔值
isDarkTheme = themeInfo.isDarkTheme === 'true' || themeInfo.isDarkTheme === '1';
console.log('🔄 转换字符串布尔值:', themeInfo.isDarkTheme, '->', isDarkTheme);
} else {
// 默认值
isDarkTheme = false;
console.warn('⚠️ 无法识别isDarkTheme值使用默认值false');
}
console.log('🎯 最终isDarkTheme值:', isDarkTheme);
// 直接调用页面现有的主题切换函数
const theme = isDarkTheme ? 'dark' : 'light';
const body = document.body;
// 添加强制刷新CSS的函数
function forceThemeRefresh() {
// 强制重新计算CSS
const body = document.body;
const currentTheme = body.getAttribute('data-theme');
// 临时移除再重新添加data-theme属性
body.removeAttribute('data-theme');
setTimeout(() => {
body.setAttribute('data-theme', currentTheme);
console.log('强制刷新主题:', currentTheme);
}, 10);
}
function applyTheme(m){
body.setAttribute('data-theme', m);
document.getElementById('themeToggle').textContent = m === 'light' ? '🌙' : '☀️';
}
// 方法1: 调用现有的 applyTheme 函数
if (typeof applyTheme === 'function') {
applyTheme(theme);
forceThemeRefresh();
console.log('✅ 调用现有applyTheme函数:', theme);
}
const codeMirrorTheme = isDarkTheme ? 'material-darker' : 'nord';
if (window.editor) {
editor.setOption('theme', codeMirrorTheme);
}
const themeSelector = document.getElementById('theme-selector');
if (themeSelector) {
themeSelector.value = theme;
}
console.log('✅ Java主题已应用到HTML界面:', isDarkTheme ? '深色主题' : '浅色主题');
addEventLog('主题应用', `已应用${isDarkTheme ? '深色' : '浅色'}主题`);
}
// 辅助函数:添加事件日志
function addEventLog(type, message) {
const output = document.getElementById('output');
if (output) {
const timestamp = new Date().toLocaleTimeString();
const logItem = document.createElement('div');
logItem.className = `log-item ${type === 'error' ? 'error' : ''}`;
logItem.innerHTML = `
<i class="fas fa-info-circle"></i>
${timestamp}: ${message}
`;
output.appendChild(logItem);
output.scrollTop = output.scrollHeight;
}
}
// 初始化(模拟触发字体加载回调)
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);
});
document.addEventListener('DOMContentLoaded', function() {
// 监听所有对data-theme的修改
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.type === 'attributes' && mutation.attributeName === 'data-theme') {
console.log('主题被修改:', mutation.oldValue, '->', document.body.getAttribute('data-theme'));
console.trace('调用堆栈');
}
});
});
observer.observe(document.body, {
attributes: true,
attributeFilter: ['data-theme'],
attributeOldValue: true
});
console.log('主题调试已启动');
});
</script>
</body>
</html>