Files
srdb/webui
bourdon 30c3e74bd2 feat: 完善 WebUI basePath 支持并简化示例代码
主要改动:

1. WebUI basePath 逻辑完善
   - NewWebUI 支持可变参数 basePath
   - 新增 path() 辅助方法统一路径处理
   - handleTableAPI 正确处理 basePath 前缀
   - handleIndex 根据 basePath 替换占位符

2. 简化示例代码
   - 删除反向代理实现(111行)
   - 直接使用带 basePath 的 WebUI
   - 代码量减少 33%,架构更清晰

3. 前端优化
   - 新增 api.js 统一 API 服务层
   - 所有组件使用统一的 API 调用
   - 支持通过 window.API_BASE 配置 basePath

4. 修复 .gitignore
   - 使用通用模式支持 commands 目录
   - 无需为新示例项目修改配置
2025-10-14 22:23:30 +08:00
..
2025-10-13 01:36:49 +08:00

SRDB WebUI

基于 Preact.js 的 SRDB WebUI使用无构建工具工作流No Build Workflow

技术栈

  • Preact 10.19.3 - 轻量级 React 替代品3KB
  • HTM 3.1.1 - JSX 替代方案,直接在浏览器中使用
  • dom-align - 智能元素定位(用于 Popover 和 Tooltip
  • ESM.sh - ES Modules CDN

特性

零构建工具 - 无需 Webpack、Vite 等构建工具 原生 ES Modules - 直接使用 import 语法 HTM 语法 - 使用 html 标签模板字符串代替 JSX 主题切换 - 支持深色/浅色主题,持久化保存 智能 Popover - 鼠标悬停单元格显示完整数据 字段 Tooltip - 悬停表头显示字段注释,悬停表名显示表注释 响应式设计 - 适配不同屏幕尺寸 数据分页 - 支持自定义每页显示行数20/50/100/200+ 跳页功能 列选择器 - 下拉菜单选择要显示的列,支持持久化保存 详情模态框 - 点击眼睛图标查看单条记录的完整数据 Manifest 弹窗 - 点击按钮打开弹窗,实时显示 LSM-Tree 结构和 Compaction 统计 索引标识 - 有索引的字段显示 🔍 图标 悬浮加载提示 - 加载时顶部显示悬浮提示,不影响布局

目录结构

webui/
├── static/
│   ├── index.html          # 主 HTML 文件
│   ├── css/
│   │   └── styles.css      # 全局样式(支持主题)
│   │
│   └── js/
│       ├── main.js         # 应用入口
│       ├── hooks/          # 自定义 Hooks
│       │   ├── useCellPopover.js    # 单元格 Popover Hook
│       │   └── useTooltip.js        # Tooltip Hook
│       │
│       └── components/     # Preact 组件
│           ├── App.js                  # 主应用组件
│           ├── Sidebar.js              # 侧边栏(表列表)
│           ├── TableItem.js            # 表列表项(可展开字段)
│           ├── FieldList.js            # 字段列表
│           ├── TableView.js            # 表视图(主容器)
│           ├── ColumnSelector.js       # 列选择器(下拉菜单)
│           ├── DataTable.js            # 数据表格
│           ├── TableRow.js             # 表格行
│           ├── TableCell.js            # 表格单元格
│           ├── Pagination.js           # 分页器sticky 底部)
│           ├── PageJumper.js           # 跳页输入框
│           ├── RowDetailModal.js       # 记录详情模态框
│           ├── ManifestModal.js        # Manifest 弹窗
│           ├── ManifestView.js         # Manifest 视图
│           ├── LevelCard.js            # 层级卡片
│           ├── FileCard.js             # 文件卡片
│           ├── StatCard.js             # 统计卡片
│           └── CompactionStats.js      # Compaction 统计
│
├── webui.go                    # Go 后端处理器
└── README.md                   # 本文件

快速开始

1. 启动后端服务

cd /path/to/srdb/examples/webui
go run main.go serve --db ./data --addr :8080

2. 访问 WebUI

浏览器打开:http://localhost:8080/

使用技巧

  1. 查看字段说明:鼠标悬停在表头字段名上,会显示该字段的 comment
  2. 查看表说明:鼠标悬停在表名上,会显示该表的 comment
  3. 选择显示列:点击右上角的 "Columns" 按钮,勾选要显示的列,选择会自动保存
  4. 查看完整数据:鼠标悬停在表格单元格上,会显示该单元格的完整数据
  5. 查看记录详情:鼠标悬停在行上,点击出现的眼睛图标查看完整记录
  6. 跳转到指定页:在分页器输入页码,点击"跳转"按钮
  7. 查看 LSM-Tree 结构:点击右上角的 "📊 Manifest" 按钮打开弹窗
  8. 展开表字段:在侧边栏点击表名前的 ▶ 图标展开查看所有字段
  9. 识别索引字段:带有 🔍 图标的字段表示该字段已建立索引
  10. 主题切换:点击左上角的 ☀️/🌙 图标切换深色/浅色主题

核心组件说明

App.js - 主应用

负责:

  • 主题管理(深色/浅色切换LocalStorage 持久化)
  • 表列表加载和选择
  • 侧边栏 + 主内容区布局

Sidebar.js - 侧边栏

特性:

  • 可展开的表列表(点击 ▶ 展开字段)
  • 表统计信息(字段数)
  • 字段列表显示(字段名、类型、索引图标)
  • 选中状态高亮

TableView.js - 表视图容器

包含:

  • 表名标题(悬停显示表注释)
  • 行数统计(格式化显示 K/M
  • Manifest 按钮(打开弹窗)
  • 列选择器按钮
  • DataTable 组件
  • ManifestModal 组件

ColumnSelector.js - 列选择器

特性:

  • 下拉菜单式选择器
  • 多选复选框
  • 显示字段类型和索引图标
  • LocalStorage 持久化保存
  • 选中数量徽章显示

DataTable.js - 数据表格

功能:

  • 响应式表格渲染
  • 表头 Tooltip显示字段注释
  • 单元格 Popover悬停显示完整数据
  • 时间格式化(_time 字段)
  • 悬浮加载提示(顶部居中)
  • Sticky 分页器(粘在底部)

TableRow.js - 表格行

特性:

  • 悬停高亮效果
  • 操作列眼睛图标(仅悬停时显示,使用 opacity 动画)
  • 点击查看详情

Pagination.js - 分页组件

功能:

  • 上一页/下一页导航
  • 每页行数选择20/50/100/200
  • 跳页功能PageJumper
  • Sticky 定位在底部
  • 显示当前页范围和总数

RowDetailModal.js - 记录详情

特性:

  • 全屏模态框
  • 显示单条记录的所有字段
  • 系统字段标记(_seq, _time
  • JSON 格式化显示
  • ESC 键和点击遮罩层关闭

ManifestModal.js - Manifest 弹窗

特性:

  • 90vw 宽度弹窗(最大 1200px
  • 包装 ManifestView 组件
  • ESC 键和点击外部关闭
  • 阻止背景滚动

ManifestView.js - Manifest 视图

展示:

  • 统计卡片(总文件数、总大小、下一个文件号、最后序列号)
  • 各层级详情L0-L6+
    • 层级标识(彩色徽章)
    • 文件数和总大小
    • Compaction Score带进度条
    • 文件列表文件号、大小、行数、Key 范围)
  • Compaction 统计(合并次数、读写字节等)
  • 只显示有文件的层级

自定义 Hooks

useCellPopover.js

用于单元格数据预览的 Popover

  • 使用 dom-align 智能定位
  • 自动处理溢出和边界
  • 延迟隐藏避免闪烁
  • 支持 JSON 格式化显示

useTooltip.js

用于表头和表名的 Tooltip

  • 显示字段 comment 或表 comment
  • 使用 dom-align 定位在元素下方
  • 无延迟即时显示
  • 最大宽度 300px自动换行

Preact + HTM 语法示例

基本组件

import { html } from 'htm/preact';

export function MyComponent({ name }) {
    return html`
        <div class="hello">
            <h1>Hello, ${name}!</h1>
        </div>
    `;
}

使用 Hooks

import { html } from 'htm/preact';
import { useState, useEffect } from 'preact/hooks';

export function Counter() {
    const [count, setCount] = useState(0);

    useEffect(() => {
        console.log('Count changed:', count);
    }, [count]);

    return html`
        <div>
            <p>Count: ${count}</p>
            <button onClick=${() => setCount(count + 1)}>
                Increment
            </button>
        </div>
    `;
}

条件渲染

${loading
    ? html`<div class="loading">Loading...</div>`
    : html`<div class="content">${data}</div>`
}

列表渲染

${items.map(item => html`
    <div key=${item.id}>${item.name}</div>
`)}

主题切换

主题通过修改 <html> 元素的 data-theme 属性实现:

// 切换到浅色主题
document.documentElement.setAttribute('data-theme', 'light');

// 切换回深色主题
document.documentElement.removeAttribute('data-theme');

CSS 变量会根据主题自动切换:

:root {
    --bg-main: #0f0f1a;  /* 深色主题 */
}

:root[data-theme="light"] {
    --bg-main: #f5f5f7;  /* 浅色主题 */
}

API 接口

WebUI v2 使用的 API 端点:

  • GET /api/tables - 获取表列表(含行数统计)
  • GET /api/tables/:name/schema - 获取表 Schema
  • GET /api/tables/:name/data?limit=100&offset=0 - 获取表数据(支持分页)
  • GET /api/tables/:name/data/:seq - 获取单条记录详情(完整数据)
  • GET /api/tables/:name/manifest - 获取 Manifest 信息LSM-Tree 结构)

开发建议

添加新组件

  1. static/js/components/ 创建新文件
  2. 使用 html 标签模板语法
  3. 导出函数组件
import { html } from 'htm/preact';

export function NewComponent({ prop1, prop2 }) {
    return html`
        <div>Your content here</div>
    `;
}

内联样式 vs CSS

推荐使用内联样式(通过 styles 对象)以获得更好的组件隔离性和主题支持:

const styles = {
    container: {
        padding: '20px',
        background: 'var(--bg-elevated)',
        color: 'var(--text-primary)'
    }
};

return html`<div style=${styles.container}>Content</div>`;

状态管理

使用 useStateuseEffect 管理组件状态:

import { useState, useEffect } from 'preact/hooks';

// 本地状态
const [count, setCount] = useState(0);

// 副作用处理
useEffect(() => {
    // 组件挂载时执行
    console.log('Component mounted');

    // 清理函数
    return () => {
        console.log('Component unmounted');
    };
}, []); // 空依赖数组表示只在挂载/卸载时执行

参考资料

License

MIT