This commit is contained in:
yumoqing 2025-10-12 17:59:59 +08:00
parent ebdb090e98
commit 53265bfa1d
83 changed files with 23927 additions and 234 deletions

205
docs/cn/accordion.md Normal file
View File

@ -0,0 +1,205 @@
# `bricks.Accordion` 技术文档
```markdown
# bricks.Accordion
`bricks.Accordion` 是一个基于 `bricks.VBox` 的可折叠面板组件,允许用户通过点击标题按钮切换显示不同的内容区域。常用于需要节省垂直空间、分组展示信息的界面设计中。
---
## 继承关系
- **继承自**: `bricks.VBox`
- **注册名称**: `'Accordion'`(通过 `bricks.Factory.register` 注册)
---
## 构造函数
### `new bricks.Accordion(opts)`
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项对象 |
#### `opts` 配置项
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `item_size` | String | `'25px'` | 每个标题项的高度CSS 尺寸字符串) |
| `item_css` | String | `'accordion-button'` | 标题按钮使用的 CSS 类名 |
| `content_css` | String | `'accordion-content'` | 内容区域使用的 CSS 类名 |
| `items` | Array\<Object\> | 必填 | 要显示的面板项列表 |
##### `items` 数组元素结构
每个 `item` 是一个对象,包含以下字段:
| 字段 | 类型 | 是否必需 | 说明 |
|------|------|----------|------|
| `name` | String | 是 | 唯一标识符,用于定位和缓存内容 |
| `icon` | String | 否 | 显示在按钮前的图标类名(如 Font Awesome 类) |
| `label` | String | 是 | 按钮上显示的文本标签 |
| `content` | Object | 是 | 描述子组件的配置对象(符合 `widgetBuild` 结构) |
| `refresh` | Boolean | 否 | 若为 `true`,每次点击都会重新构建内容(不使用缓存) |
---
## 成员属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `w_items` | Array\<bricks.Button\> | 存储所有标题按钮实例的数组 |
| `subcontents` | Object | 缓存已创建的内容组件,键为 `name`,值为对应的 widget 实例 |
| `content` | bricks.Filler | 包裹内容区域的容器 |
| `sub_container` | bricks.VScrollPanel | 实际承载内容的可滚动面板 |
| `key_select_items` | Array | 支持键盘导航的选择项集合(用于上下键选择) |
| `keyselectable` | Boolean | `true`,表示该组件支持键盘选择操作 |
---
## 方法
### `constructor(opts)`
初始化 Accordion 组件:
1. 调用父类构造函数。
2. 设置整体高度为 `100%`
3. 创建多个 `bricks.Button` 实例作为标题栏,并绑定 `click` 事件到 `change_content`
4. 初始化内容显示区域(`VScrollPanel`)并插入第一个项目的内容。
> ⚠️ 注意:第一个按钮会自动触发一次 `click` 事件以加载默认内容。
---
### `async change_content(event)`
处理标题按钮点击事件,动态切换显示内容。
#### 参数
- `event`: DOM 事件对象,`event.target.bricks_widget` 应指向被点击的 `bricks.Button` 实例。
#### 行为逻辑
1. 获取被点击按钮的 `name`
2. 在 `this.opts.items` 中查找对应项的位置与配置。
3. 判断是否需要刷新:
- 如果设置了 `refresh: true`
- 或者该内容尚未被缓存
4. 若需刷新或未缓存,则调用 `bricks.widgetBuild()` 异步生成新内容并缓存。
5. 清空当前内容区,并将新内容添加进 `sub_container`
6. 更新布局:移除旧的 `content` widget 并重新插入(确保位于正确位置)。
#### 错误处理
- 若找不到匹配的 `name`,会在控制台输出警告日志。
- 若 `item.content` 不存在,则打印错误信息并返回。
---
## 事件绑定
- 所有标题按钮均监听 `click` 事件,触发 `change_content` 回调。
- 使用 `.bind(this)` 确保回调中的 `this` 指向正确的 Accordion 实例。
---
## 示例配置
```javascript
var accordion = new bricks.Accordion({
item_size: '30px',
item_css: 'my-accordion-button',
content_css: 'my-accordion-content',
items: [
{
name: 'general',
icon: 'fa fa-home',
label: '通用设置',
content: {
widgettype: 'TextBlock',
text: '这里是通用设置内容...'
}
},
{
name: 'advanced',
icon: 'fa fa-cog',
label: '高级选项',
refresh: true,
content: {
widgettype: 'FormPanel',
fields: [ ... ]
}
}
]
});
```
---
## 样式建议CSS
```css
.accordion-button {
padding: 8px 12px;
background-color: #f0f0f0;
border-bottom: 1px solid #ddd;
cursor: pointer;
font-weight: bold;
}
.accordion-button:hover {
background-color: #e0e0e0;
}
.accordion-content {
padding: 10px;
background-color: #fff;
border: 1px solid #ddd;
min-height: 100px;
}
```
> 可根据实际需求覆盖默认类名或通过 `item_css` / `content_css` 自定义。
---
## 工厂注册
```javascript
bricks.Factory.register('Accordion', bricks.Accordion);
```
允许通过工厂方法创建实例:
```javascript
bricks.widgetBuild({ widgettype: 'Accordion', ... });
```
---
## 注意事项
- 内容组件是**懒加载**的,仅在首次点击时创建。
- 支持**缓存机制**,避免重复渲染提升性能;可通过 `refresh: true` 强制重载。
- 支持键盘导航(上下箭头选择按钮),前提是容器环境支持焦点管理。
- `widgetBuild` 返回的是 `Promise`,因此 `change_content` 定义为 `async` 函数。
---
## 调试信息
启用调试模式后,点击按钮时会输出类似日志:
```text
accordion: button= [Button Instance] name= general
```
可通过 `bricks.debug()` 控制开关。
---
```

226
docs/cn/aggrid.md Normal file
View File

@ -0,0 +1,226 @@
# `bricks.AG_Grid` 技术文档
> 基于 [ag-Grid](https://www.ag-grid.com/) 封装的可复用 JavaScript 网格组件,继承自 `bricks.JsWidget`
---
## 概述
`bricks.AG_Grid` 是一个基于 `ag-Grid` 的封装类,用于在网页中快速创建功能丰富的数据表格。它支持从远程 URL 动态加载数据,并提供列定义、排序、过滤、多选、单元格点击事件等基础功能。
该类继承自 `bricks.JsWidget`,遵循统一的组件初始化和生命周期管理机制。
---
## 依赖
- `ag-Grid`(必须全局可用,即 `agGrid` 对象已加载)
- `bricks.js` 核心库(包含 `bricks.JsWidget` 基类)
---
## 构造函数
```js
new bricks.AG_Grid(options)
```
### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `options` | `Object` | ✅ | 配置选项对象 |
#### `options` 属性
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `dataurl` | `String` | ✅ | - | 数据接口 URL返回 JSON 格式数组数据 |
| `fields` | `Array<Object>` | ✅ | - | 列字段配置列表,每个字段包含显示信息 |
##### `fields` 字段配置项
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| `name` | `String` | ✅ | - | 字段名(对应数据中的 key |
| `label` | `String` | ❌ | `name` | 表头显示名称 |
| `width` | `Number` | ❌ | `100` | 列宽度(像素) |
---
## 类定义
```js
var bricks = window.bricks || {};
bricks.AG_Grid = class extends bricks.JsWidget {
/*
opts 示例:
{
dataurl: "/api/data",
fields: [
{ name: "id", label: "ID", width: 80 },
{ name: "name", label: "姓名" },
{ name: "age", width: 60 }
]
}
*/
constructor(opts) {
super(opts);
this.ag_opts = {}; // ag-Grid 配置对象
}
init() {
// 初始化数据源
this.datasource = {
getRows: this.getRows.bind(this),
startRow: 0
};
// 设置 ag-Grid 配置项
this.ag_opts.columnDefs = [];
this.ag_opts.rowModelType = 'infinite';
this.ag_opts.maxConcurrentDatasourceRequests = 1;
this.ag_opts.datasource = this.datasource;
// 生成列定义
for (let i = 0; i < this.opts.fields.length; i++) {
const field = this.opts.fields[i];
const colDef = {
field: field.name,
headerName: field.label || field.name,
width: field.width || 100
};
this.ag_opts.columnDefs.push(colDef);
}
// 默认列行为:可排序、可过滤
this.ag_opts.defaultColDef = { sortable: true, filter: true };
// 启用多行选择
this.ag_opts.rowSelection = 'multiple';
// 启用行动画效果
this.ag_opts.animateRows = true;
// 绑定单元格点击事件
this.ag_opts.onCellClicked = this.cell_clicked.bind(this);
// 初始化 ag-Grid 实例
this.aggrid = new agGrid.Grid(this.dom_element, this.ag_opts);
// 加载初始数据(一次性加载全部)
fetch(this.opts.dataurl)
.then(response => response.json())
.then(data => {
this.ag_opts.api.setRowData(data); // 注意:此处与 infinite model 不符,见下方说明
})
.catch(err => console.error('Failed to load data:', err));
}
cell_clicked(params) {
bricks.debug('Cell clicked:', params);
}
}
```
---
## 方法说明
### `constructor(opts)`
调用父类构造函数并初始化 `this.ag_opts` 为空对象,用于后续存储 ag-Grid 配置。
### `init()`
组件初始化主逻辑:
1. 定义 `datasource` 对象(为无限滚动做准备,但当前实现未完全使用)。
2. 遍历 `opts.fields` 构建 `columnDefs`
3. 设置通用表格行为(排序、过滤、多选、动画)。
4. 创建 `ag-Grid` 实例并与 DOM 元素绑定。
5. 使用 `fetch` 请求 `dataurl` 获取初始数据,并通过 `setRowData` 填充表格。
> ⚠️ **注意**:虽然设置了 `rowModelType: 'infinite'`,但实际并未正确实现 `getRows` 方法,因此当前行为更像是“客户端模型”,而非真正的无限滚动。建议根据需求调整数据加载方式。
### `cell_clicked(params)`
单元格点击回调函数,输出调试信息到控制台(需 `bricks.debug` 存在)。
参数 `params` 包含:
- `value`: 当前单元格值
- `column`: 列对象
- `rowIndex`: 行索引
- `data`: 整行数据对象
- 更多详见 [ag-Grid 官方文档](https://www.ag-grid.com/javascript-data-grid/cell-events/)
---
## 使用示例
```html
<div id="myGrid" style="height: 400px;" class="ag-theme-alpine"></div>
<script>
new bricks.AG_Grid({
dom_element: document.getElementById('myGrid'),
dataurl: '/api/users',
fields: [
{ name: 'id', label: '用户ID', width: 80 },
{ name: 'name', label: '姓名', width: 120 },
{ name: 'email', label: '邮箱' },
{ name: 'age', label: '年龄', width: 60 }
]
});
</script>
```
---
## 已知问题与改进建议
| 问题 | 说明 | 建议 |
|------|------|------|
| `getRows` 未实现 | `datasource.getRows` 未定义,却赋值给了 `this.datasource.getRows` | 若使用 `infinite` 模式,应实现分页加载逻辑 |
| `startRow` 未绑定上下文 | `startRow``datasource` 中未正确定义为属性 | 应作为闭包变量或实例属性维护 |
| `setRowData` 不适用于 Infinite Model | `setRowData` 仅适用于客户端模型 | 改为调用 `api.setDatasource()` 并完整实现 `getRows` |
| 错误语法 | `maxConcurrentDatasourceRequests: 1,` 使用了冒号而非赋值 | 应为 `=` |
| `width``label` 的默认值写法错误 | 使用了位运算符 `|` 而非逻辑默认值 | 应使用 `||``??` |
### 修正建议代码片段
```js
// 正确的字段处理
width: field.width ?? 100,
headerName: field.label ?? field.name,
// 正确的属性赋值
this.ag_opts.maxConcurrentDatasourceRequests = 1;
// 实现 getRows若启用 infinite 模式)
getRows(params) {
fetch(`${this.opts.dataurl}?start=${params.startRow}&end=${params.endRow}`)
.then(res => res.json())
.then(data => {
params.successCallback(data, data.length === 100 ? 10000 : data.length);
});
}
```
---
## 总结
`bricks.AG_Grid` 提供了一个简洁的 ag-Grid 封装,适合快速集成静态或小规模动态数据表格。但在大数据场景下需完善 `infinite` 模式的数据源实现以提升性能。
建议后续优化方向:
- 完整支持分页/无限滚动
- 支持列类型、渲染器扩展
- 添加加载状态提示
- 支持外部刷新方法
---
📌 **版本**: 1.0
📅 **最后更新**: 2025-04

220
docs/cn/asr.md Normal file
View File

@ -0,0 +1,220 @@
# `bricks.ASRClient` 技术文档
> 基于 WebSocket 的语音识别客户端组件,用于实时音频流传输与文本识别结果接收。
---
## 概述
`bricks.ASRClient` 是一个基于 `bricks.VBox` 的类,封装了浏览器端的语音识别功能。它通过调用 Web Audio API 获取用户麦克风输入,并使用 WebSocket 将音频数据以 Base64 编码形式发送至后端 ASR自动语音识别服务。同时监听服务器返回的识别结果并触发相应事件。
该组件提供了一个可点击的图标按钮来控制录音的开始与停止支持自定义图标、WebSocket 地址和附加参数。
---
## 继承关系
- **继承自**`bricks.VBox`
- **注册名称**`ASRClient`(可通过 `bricks.Factory.create('ASRClient', options)` 创建)
```js
bricks.Factory.register('ASRClient', bricks.ASRClient);
```
---
## 构造函数
### `constructor(opts)`
#### 参数
| 参数名 | 类型 | 说明 |
|----------------|----------|------|
| `opts` | Object | 配置选项对象,见下表 |
##### `opts` 配置项
| 属性名 | 类型 | 默认值 | 说明 |
|----------------|----------|--------|------|
| `start_icon` | String | `'imgs/start_recording.svg'` | 开始录音时显示的图标路径SVG/PNG |
| `stop_icon` | String | `'imgs/stop_recording.svg'` | 停止录音时显示的图标路径SVG/PNG |
| `ws_url` | String | 必填 | WebSocket 服务器地址,例如:`wss://asr.example.com/ws` |
| `icon_options` | Object | `{}` | 传递给内部 `bricks.Svg` 图标组件的额外配置 |
| `ws_params` | Object | `{}` | 发送至 WebSocket 服务的附加参数,在每次发送音频时合并使用 |
---
## 事件
`ASRClient` 支持以下自定义事件,可通过 `.bind()` 方法监听:
| 事件名 | 触发时机 | 参数格式(`event.params` |
|--------------|------------------------------|----------------------------|
| `start` | 用户点击开始录音 | 无参数 |
| `stop` | 用户点击停止录音 | 无参数 |
| `transtext` | 接收到服务器返回的识别文本 | `{ content, speaker, start, end }` |
示例:
```js
asr.bind('transtext', function(e) {
console.log('识别结果:', e.params.content);
});
```
---
## 属性
| 属性名 | 类型 | 说明 |
|---------------|---------------|------|
| `status` | String | 当前状态:`'start'``'stop'` |
| `icon` | `bricks.Svg` | 控制按钮图标实例 |
| `socket` | `WebSocket` | 连接到 ASR 服务的 WebSocket 实例 |
| `stream` | `MediaStream` | 来自 `getUserMedia` 的音频流 |
| `mediaRecorder` | `MediaRecorder` | 浏览器原生媒体录制对象,每秒收集并发送音频块 |
---
## 方法
### `toggle_button()`
切换录音状态(开始 ↔ 停止),并更新按钮图标。
- 若当前为 `'stop'`,则切换为 `'start'` 并调用 `start_recording()`
- 若当前为 `'start'`,则切换为 `'stop'` 并调用 `stop_recording()`
绑定在图标的 `click` 事件上。
---
### `async start_recording()`
启动麦克风录音并开始向服务器发送音频数据。
#### 行为流程:
1. 调用 `navigator.mediaDevices.getUserMedia({ audio: true })` 请求麦克风权限。
2. 创建 `MediaRecorder` 实例处理音频流。
3. 设置 `ondataavailable` 回调:每 1 秒生成一段音频 Blob。
4. 使用 `blobToBase64()` 工具将 Blob 转为 Base64 字符串。
5. 构造消息体并发送至 WebSocket 服务器:
```json
{
"type": "audiobuffer",
"data": "base64string...",
...ws_params
}
```
> ⚠️ 注意:需确保页面运行在 HTTPS 环境或本地开发环境localhost否则无法获取麦克风权限。
---
### `stop_recording()`
停止 `MediaRecorder` 录音,关闭音频流轨道。
```js
this.mediaRecorder.stop();
// 同时会释放麦克风资源
```
---
### `response_data(event)`
处理来自 WebSocket 的服务器响应。
#### 参数
- `event.data`: 服务器返回的 JSON 字符串
#### 动作
1. 解析 JSON 数据
2. 触发 `transtext` 事件并将解析后的数据作为参数分发
```js
this.dispatch('transtext', parsedData);
```
---
### `response_log(event)`
调试方法:将接收到的识别结果输出到控制台。
```js
console.log('response data=', event.params);
```
可通过重写此方法实现日志收集或 UI 更新。
---
## 内部依赖
| 工具/函数 | 说明 |
|----------------------|------|
| `bricks_resource(path)` | 解析资源路径的工具函数,通常用于构建静态资源 URL |
| `blobToBase64(blob)` | 异步将 Blob 转换为 Base64 编码字符串 |
| `objcopy(obj)` | 深拷贝对象(防止修改原始 `ws_params` |
> 示例 `blobToBase64` 实现:
> ```js
> function blobToBase64(blob) {
> return new Promise((resolve, reject) => {
> const reader = new FileReader();
> reader.onload = () => resolve(reader.result.split(',')[1]);
> reader.onerror = reject;
> reader.readAsDataURL(blob);
> });
> }
> ```
---
## 使用示例
```js
var asr = new bricks.ASRClient({
ws_url: 'wss://your-asr-server.com/asr',
start_icon: 'imgs/mic-on.svg',
stop_icon: 'imgs/mic-off.svg',
ws_params: {
lang: 'zh-CN',
model: 'conversational'
}
});
// 监听识别结果
asr.bind('transtext', function(e) {
document.getElementById('output').innerHTML += '<p>' + e.params.content + '</p>';
});
// 插入到 DOM 容器
document.body.appendChild(asr.render());
```
---
## 注意事项
1. **安全性要求**必须在安全上下文HTTPS 或 localhost中运行才能访问麦克风。
2. **浏览器兼容性**:需要支持 `MediaRecorder` API 和 `WebSocket`
3. **性能建议**:每秒发送一次音频块,避免频繁请求;建议使用 Opus 编码压缩音频。
4. **错误处理**:未对 `getUserMedia` 失败做降级处理,建议外层捕获异常。
---
## 版本信息
- **作者**Bricks Framework Team
- **版本**1.0
- **最后更新**2025年4月5日
---
📌 *文档结束*

404
docs/cn/audio.md Normal file
View File

@ -0,0 +1,404 @@
# `bricks` 音频模块技术文档
本文档为 `bricks` 框架中的音频相关功能模块提供详细说明,包含以下核心类:
- `bricks.formatMs()` —— 时间格式化工具函数
- `bricks.AudioPlayer` —— 音频播放器组件
- `bricks.AudioRecorder` —— 音频录制与上传组件
- `bricks.TextedAudioPlayer` —— 带文本同步的流式音频播放器
- 工厂注册机制(通过 `bricks.Factory`
---
## 1. 工具函数:`bricks.formatMs(ms, all)`
将毫秒数转换为可读的时间字符串,支持自定义显示粒度。
### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `ms` | `number` | 时间(单位:毫秒) |
| `all` | `boolean?` | 是否强制显示所有时间单位即使高位为0 |
### 返回值
返回格式化的字符串,形式如:`"h:mm:ss″sss"``"mm:ss″sss"`
- 小时部分仅在非零时显示
- 分钟和秒根据上下文决定是否显示前导零
- 毫秒固定三位数字补全
### 示例
```js
bricks.formatMs(3661234); // 输出: "1:01:01″234"
bricks.formatMs(61234, true); // 输出: "01:01″0234"
```
---
## 2. 音频播放器:`bricks.AudioPlayer`
基于 HTML5 `<audio>` 元素封装的高级音频播放控制组件。
### 继承关系
```js
class AudioPlayer extends bricks.JsWidget
```
### 构造选项 (`options`)
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `url` | `string` | 否 | `null` | 初始音频源 URL |
| `autoplay` | `boolean` | 否 | `false` | 是否自动播放 |
### 实例属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `audio` | `HTMLAudioElement` | 内部使用的 `<audio>` DOM 节点 |
| `source` | `HTMLSourceElement` | 动态创建的 `<source>` 节点 |
| `playlist` | `Array<string>` | 待播放的 URL 列表(队列) |
| `url_generator` | `AsyncGenerator` | 流式加载 URL 的异步生成器 |
| `srcList` | `Array<{played: boolean, url: string}>` | 动态加载的音频源列表 |
### 方法
#### `get_status() → string`
获取当前播放状态。
**返回值:**
| 状态 | 说明 |
|------|------|
| `"error"` | 音频加载出错 |
| `"ended"` | 播放结束 |
| `"paused"` | 暂停中 |
| `"loading"` | 数据不足,正在缓冲 |
| `"playing"` | 正在播放 |
---
#### `add_url(url: string)`
向播放队列添加新音频。若当前状态为 `'error'``'ended'`,立即播放该音频;否则加入队列。
---
#### `set_source(url: string)`
设置音频源并更新 DOM。
> ⚠️ 注意:会同时设置 `this.audio.src` 和内部 `<source>` 元素。
---
#### `set_stream_urls(response: Response)`
从服务器流式接收多个音频 URL每行一个用于连续播放场景如语音合成流
##### 参数
- `response`: `fetch` 返回的 `Response` 对象,其 body 应为逐行输出的文本流。
##### 实现逻辑
1. 创建异步生成器 `dyn_urls()` 解析流数据。
2. 使用 `schedule_once()` 异步启动 `load_queue_url()`
3. 自动预加载并顺序播放音频片段。
---
#### `async load_queue_url()`
内部方法:持续从 `url_generator` 获取新的音频 URL并维护 `srcList`
`srcList.length < 2` 时触发首次播放。
---
#### `async play_srclist(event?)`
播放 `srcList` 中尚未播放的第一个音频项。
- 若传入事件对象且音频未结束,则不执行。
- 播放完成后通过 `'ended'` 事件继续下一首。
---
#### `play()` / `toggle_play()`
- `play()`: 异步调用原生 `audio.play()`
- `toggle_play()`: 切换播放/暂停状态。
---
#### `set_url(url)`
设置音频源并立即开始播放。
等价于:
```js
this.set_source(url);
this.audio.play();
```
---
## 3. 音频录制器:`bricks.AudioRecorder`
实现麦克风录音、可视化、文件上传与下载功能。
### 外部依赖
必须引入第三方库 [Recorder.js](https://gitee.com/xiangyuecn/Recorder),支持 WAV 格式录音。
```html
<script src="https://cdn.jsdelivr.net/npm/recorder-js"></script>
```
### 继承关系
```js
class AudioRecorder extends bricks.HBox
```
### 构造选项 (`opts`)
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `upload_url` | `string` | 否 | `null` | 录音结束后上传的目标地址 |
| `start_icon` | `string` | 否 | 内置 SVG | 开始按钮图标路径 |
| `stop_icon` | `string` | 否 | 内置 SVG | 停止按钮图标路径 |
| `icon_rate` | `number` | 否 | `undefined` | 图标缩放比例 |
### 事件系统
通过 `this.dispatch()` 触发以下自定义事件:
| 事件名 | 触发时机 | 携带参数 |
|--------|----------|---------|
| `record_started` | 开始录音 | 无 |
| `record_ended` | 结束录音 | `{data: Blob, url: string, duration: number}` |
| `uploaded` | 成功上传后 | 服务器返回结果 |
可通过 `bind()` 监听这些事件。
### 主要组件
- `rec_btn`: 使用 `bricks.Svg` 显示切换式按钮(开始/停止)
- `rec_time`: 显示已录制时长(调用 `bricks.formatMs()`
- `wave`: (预留)波形图显示区域(需额外包支持)
### 核心方法
#### `recOpen()` / `recClose()`
打开或关闭麦克风权限。
- 调用 `Recorder.open()` 请求用户授权。
- 成功后设置 `this.rec` 实例并派发 `record_started`
- 失败时打印调试信息。
---
#### `start_recording()` / `stop_recording()`
开始或停止录音。
- `start_recording`: 若已授权则直接开始,否则先调用 `recOpen()`
- `stop_recording`: 调用 `rec.stop()` 获取 `Blob` 和时长,生成本地 URL 并派发 `record_ended`
---
#### `on_process(buffers, powerLevel, ...)`
录音过程中的实时回调约每秒12次
当前用途:
- 更新录音时长显示(`bufferDuration`
- 可扩展用于绘制音量条或波形图(注释中示例)
---
#### `upload() → Promise<void>`
将录音文件以 `FormData` 形式上传至 `upload_url`
使用 `bricks.jpost()` 发送请求,附带运行中提示(`bricks.Running`)。
成功后派发 `uploaded` 事件。
---
#### `download()`
触发浏览器下载当前录音文件WAV 格式),文件名为 `recorder-{timestamp}.wav`
> ⚠️ 下载完成后自动释放 `ObjectURL` 防止内存泄漏。
---
## 4. 文本同步音频播放器:`bricks.TextedAudioPlayer`
专为“语音+字幕”场景设计的垂直布局播放器,支持流式 JSON 数据驱动。
### 继承关系
```js
class TextedAudioPlayer extends bricks.VBox
```
### 组件结构
- 上方:`AudioPlayer` 控件
- 下方:`VScrollPanel` 包含 `Text` 组件用于显示文本内容
### 核心特性
支持通过流式响应动态加载语音与对应文本,实现边生成边播放的效果(适用于 TTS + 字幕同步)。
### 方法
#### `set_stream_urls(response: Response)`
解析流式 JSON 响应,每一行为一个 `{audio: base64, text: string}` 对象。
使用辅助函数 `streamResponseJson(response, callback)` 逐条处理。
---
#### `load_stream_data(json)`
接收到单条流数据后的处理逻辑:
1. 存入 `streaming_buffer` 队列;
2. 若当前空闲(`wait_play === true`),立即触发 `playnext()`
---
#### `playnext()`
从缓冲区取出下一条数据:
- 提取 `audio` 字段并转为 URL`base64_to_url()`
- 设置播放器源并更新文本显示;
- 播放完毕后等待 `'ended'` 事件再次调用自身。
当数据耗尽时清空界面。
---
## 5. 工厂注册
所有组件均通过 `bricks.Factory` 注册,可在模板中使用标签方式实例化。
```js
bricks.Factory.register('AudioPlayer', bricks.AudioPlayer);
bricks.Factory.register('AudioRecorder', bricks.AudioRecorder);
bricks.Factory.register('TextedAudioPlayer', bricks.TextedAudioPlayer);
```
### 示例用法(假设支持声明式语法)
```html
<widget type="AudioPlayer" url="demo.mp3" autoplay="true"/>
<widget type="AudioRecorder" upload_url="/api/upload-audio"/>
<widget type="TextedAudioPlayer"/>
```
---
## 6. 辅助函数与约定
### `base64_to_url(base64str) → string`
将 Base64 编码的音频数据转换为 `ObjectURL`,供 `<audio>` 使用。
> ✅ 实际代码中需确保此函数存在。
---
### `schedule_once(fn, delay)`
延迟执行一次函数(通常为 `setTimeout(fn, delay * 1000)` 封装)。
---
### `bricks.debug(msg, ...args)`
框架级调试日志输出。
---
### `bricks.is_mobile() → boolean`
判断是否运行在移动设备上,影响下载行为处理。
---
## 7. 使用场景建议
| 场景 | 推荐组件 |
|------|----------|
| 普通音频播放 | `AudioPlayer` |
| 用户录音上传 | `AudioRecorder` |
| AI语音对话TTS+字幕) | `TextedAudioPlayer` + 流式 API |
| 连续播放多个短音频 | `AudioPlayer.set_stream_urls()` |
---
## 8. 注意事项
1. **跨域限制**:录音上传、音频源加载需注意 CORS。
2. **HTTPS 要求**:现代浏览器要求 `getUserMedia()` 在安全上下文中运行HTTPS 或 localhost
3. **移动端兼容性**iOS Safari 对自动播放和 `ObjectURL` 支持有限,建议用户交互后触发播放。
4. **内存管理**:使用 `URL.revokeObjectURL()` 及时释放资源。
5. **错误处理**:建议监听 `error` 事件并在 UI 中反馈。
---
## 9. 示例代码
### 初始化一个播放器
```js
const player = new bricks.AudioPlayer({
url: 'music.mp3',
autoplay: true
});
document.body.appendChild(player.dom_element);
```
### 创建录音器并绑定上传
```js
const recorder = new bricks.AudioRecorder({
upload_url: '/api/v1/audio-upload',
icon_rate: 1.2
});
recorder.bind('uploaded', (res) => {
console.log('Upload success:', res);
});
document.body.appendChild(recorder.dom_element);
```
### 流式语音播放AI 回复)
```js
fetch('/api/stream-tts', { method: 'POST', body: prompt })
.then(response => {
const tap = new bricks.TextedAudioPlayer();
tap.set_stream_urls(response);
document.body.appendChild(tap.dom_element);
});
```
---
> 📚 文档版本v1.0
> © 2025 bricks Framework Team

211
docs/cn/bar.md Normal file
View File

@ -0,0 +1,211 @@
# `bricks.ChartBar` 技术文档
> 基于 ECharts 的柱状图组件,用于快速生成基于数据配置的柱状图表。
---
## 概述
`bricks.ChartBar``bricks` 框架中的一个图表扩展类,继承自 `bricks.EchartsExt`。它封装了使用 [Apache ECharts](https://echarts.apache.org/) 绘制**柱状图Bar Chart**所需的基本逻辑,支持通过配置项动态加载数据并渲染图表。
该组件适用于需要展示分类维度下多个指标对比的场景,例如销量统计、用户增长等。
---
## 类定义
```javascript
bricks.ChartBar = class extends bricks.EchartsExt
```
- **命名空间**: `bricks.ChartBar`
- **父类**: `bricks.EchartsExt`
- **注册名称**: `'ChartBar'`(通过 `bricks.Factory.register` 注册)
---
## 配置参数
`ChartBar` 支持以下初始化配置项:
| 参数 | 类型 | 说明 |
|------|------|------|
| `data_url` | `String` (可选) | 数据请求的 URL 地址。若提供,则通过 AJAX 获取数据。 |
| `data_params` | `Object` (可选) | 发送请求时携带的参数(与 `data_url` 配合使用)。 |
| `method` | `String` (可选) | 请求方法,默认为 `'GET'`,可设为 `'POST'` 等。 |
| `data` | `Array<Object>` (可选) | 直接传入的数据数组,格式为对象列表。优先级高于 `data_url`。 |
| `nameField` | `String` | 指定作为 X 轴分类字段的键名(如:地区、时间等)。 |
| `valueFields` | `Array<String>` | 指定作为系列Series值字段的键名数组即多个指标列。 |
### 示例数据结构
```json
[
{ "category": "A", "sales": 120, "profit": 80 },
{ "category": "B", "sales": 150, "profit": 90 }
]
```
对应配置:
```js
{
data: [...],
nameField: "category",
valueFields: ["sales", "profit"]
}
```
---
## 方法说明
### `values_from_data(data, name)`
从数据集中提取指定字段的所有值。
#### 参数
- `data`: `Array<Object>` - 数据源数组。
- `name`: `String` - 要提取的字段名。
#### 返回值
- `Array` - 包含所有 `data[i][name]` 值的数组。
#### 示例
```js
this.values_from_data([
{ year: '2020', sales: 100 },
{ year: '2021', sales: 130 }
], 'sales');
// → [100, 130]
```
---
### `lineinfo_from_data(data, name)`
构建单个 ECharts Series 配置对象(类型为 bar
#### 参数
- `data`: `Array<Object>` - 数据源。
- `name`: `String` - 字段名,用作 series 名称和数据提取依据。
#### 返回值
```js
{
name: String,
type: 'bar',
data: Array // 提取自 data 中该字段的值
}
```
#### 示例
```js
this.lineinfo_from_data(data, 'sales');
// → { name: 'sales', type: 'bar', data: [100, 130] }
```
---
### `setup_options(data)`
根据输入数据生成完整的 ECharts 配置选项(`option` 对象)。
#### 参数
- `data`: `Array<Object>` - 处理后的数据集。
#### 返回值 (`Object`)
返回标准的 ECharts 配置对象,包含:
```js
{
tooltip: { trigger: 'axis' },
legend: { data: [...] }, // 分类名称列表
xAxis: {
type: 'category',
data: [...] // nameField 对应的值列表
},
yAxis: {
type: 'value'
},
series: [ ... ] // 每个 valueField 对应一个 bar series
}
```
#### 内部逻辑
1. 提取 `nameField` 的值作为 X 轴和图例数据;
2. 遍历 `valueFields`,为每个字段创建一个柱状图系列;
3. 整合成最终的 `options` 对象供 ECharts 渲染使用。
---
## 使用示例
```js
var chart = new bricks.ChartBar({
data_url: '/api/sales-data',
nameField: 'month',
valueFields: ['income', 'expense']
});
chart.renderTo('#chart-container');
```
或直接传入数据:
```js
var chart = new bricks.ChartBar({
data: [
{ month: 'Jan', income: 5000, expense: 3000 },
{ month: 'Feb', income: 6000, expense: 3500 }
],
nameField: 'month',
valueFields: ['income', 'expense']
});
chart.renderTo(document.getElementById('main'));
```
---
## 图表渲染效果
- **X 轴**:显示 `nameField` 的值(类别轴)。
- **Y 轴**:数值轴,自动适配最大最小值。
- **Tooltip**:开启轴触发提示,鼠标悬停显示各系列数值。
- **Legend**:图例显示所有 `valueFields` 名称。
- **Series**:每个 `valueField` 显示为一组柱子(支持多组并列柱状图)。
---
## 注册机制
```js
bricks.Factory.register('ChartBar', bricks.ChartBar);
```
允许通过工厂模式创建实例:
```js
bricks.Factory.create('ChartBar', config);
```
---
## 注意事项
1. 必须确保页面已引入 ECharts 库且 `bricks.EchartsExt` 已正确定义。
2. `nameField``valueFields` 必须存在于数据对象中。
3. 若同时设置了 `data``data_url`,将优先使用 `data`
4. 支持异步数据加载,`data_url` 返回的数据应为 JSON 数组。
---
## 版本信息
- **作者**: bricks framework team
- **版本**: 1.0.0
- **依赖**: `ECharts`, `bricks.EchartsExt`
---
📌 *文档生成时间2025-04-05*

276
docs/cn/binstreaming.md Normal file
View File

@ -0,0 +1,276 @@
# Bricks 流媒体传输模块技术文档
---
## 概述
`bricks.UpStreaming``bricks.down_streaming` 是用于实现浏览器端流式数据上传与下载的 JavaScript 工具类,基于现代 Web API`ReadableStream``fetch`)构建。该模块适用于需要实时上传二进制数据(如音频、视频流)或处理响应流的场景。
主要功能包括:
- **上行流UpStreaming**:通过 `POST` 请求将数据以 `application/octet-stream` 格式分块发送至指定 URL。
- **下行流解析器down_streaming**:异步生成器函数,用于消费 `Response.body` 的流式内容,并将其转换为字符串输出。
---
## 依赖说明
本模块依赖以下现代浏览器特性:
- `ReadableStream`
- `Headers`
- `fetch()` API支持 `duplex: 'full'`
- `async/await``async generator` 函数
- `TextDecoder`(隐含在 `String.fromCharCode` 使用中)
> ⚠️ 注意:`duplex: 'full'` 是处理流式请求所必需的,目前仅在部分现代浏览器(如 Chrome 105+)中支持。
---
## 命名空间初始化
```js
bricks = window.bricks || {};
```
确保 `bricks` 全局命名空间存在,避免覆盖已有定义。
---
## 类:`bricks.UpStreaming`
继承自 `bricks.JsWidget`,提供流式上传能力。
### 继承关系
```js
class UpStreaming extends bricks.JsWidget
```
> 需确保 `bricks.JsWidget` 已正确定义并可用。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 配置对象,必须包含 `url` 字段 |
示例配置:
```js
{
url: "https://example.com/upload"
}
```
#### 实现逻辑
调用父类构造函数 `super(opts)`,初始化组件基础属性。
---
### 方法
#### `async go()`
启动流式上传请求。
##### 返回值
- `{Promise<Response>}`:返回一个 resolve 为 `Response` 对象的 Promise。
##### 实现细节
1. 创建一个可读流 `this.body`使用当前实例作为其源source
2. 设置请求头:
- `Content-Type: application/octet-stream`
3. 使用 `fetch` 发起 POST 请求:
```js
fetch(this.url, {
method: 'POST',
headers: this.headers,
duplex: 'full',
body: this.body
})
```
4. 返回响应 `Response` 对象。
> 💡 `duplex: 'full'` 表示客户端可以同时写入请求体并读取响应,常用于流式接口。
---
#### `send(data)`
向流中写入一段数据。
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `data` | ArrayBuffer / Uint8Array 等 | 要发送的二进制数据块 |
##### 实现
调用内部控制器的 `enqueue` 方法将数据推入流队列:
```js
this.stream_ctlr.enqueue(data);
```
> 必须在调用 `start()` 后才能使用此方法。
---
#### `finish()`
关闭写入流,表示所有数据已发送完毕。
##### 实现
```js
this.stream_ctlr.close();
```
通知流结束,触发底层请求完成。
---
#### `start(controller)`
流控制器初始化钩子(由 `ReadableStream` 调用)。
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `controller` | ReadableStreamController | 浏览器提供的流控制对象 |
##### 实现
保存控制器引用以便后续调用:
```js
this.stream_ctlr = controller;
```
> 此方法是 `ReadableStream` 构造时自动调用的标准接口。
---
## 函数:`bricks.down_streaming`
异步生成器函数,用于逐段读取响应流并解码为字符串。
```js
bricks.down_streaming = async function*(response)
```
### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `response` | Response | 来自 `fetch` 的响应对象 |
### 返回值
- `{AsyncGenerator<string>}`:异步生成器,每次 `yield` 一个字符串片段。
### 实现逻辑
1. 若 `response` 为空,则直接返回。
2. 获取响应体的读取器:`response.body.getReader()`
3. 循环调用 `reader.read()` 直到流结束(`done === true`)。
4. 将每一块 `Uint8Array` 数据转换为字符串:
```js
String.fromCharCode(...value.value)
```
> ❌ 注意:这种方式对非 ASCII 字符(如中文)可能出错,推荐使用 `TextDecoder`
5. 输出日志并 `yield` 结果。
### 示例用法
```js
for await (const chunk of bricks.down_streaming(resp)) {
console.log('Received:', chunk);
}
```
---
## 使用示例
### 上行流发送数据
```js
const uploader = new bricks.UpStreaming({
url: '/api/stream-upload'
});
// 启动上传
const respPromise = uploader.go();
// 发送若干数据块
uploader.send(new TextEncoder().encode("Hello"));
uploader.send(new TextEncoder().encode("World"));
// 完成上传
uploader.finish();
// 获取响应
const resp = await respPromise;
console.log('Upload complete:', resp.status);
```
### 下行流接收数据
```js
const response = await fetch('/api/stream-download');
for await (const textChunk of bricks.down_streaming(response)) {
console.log('Chunk:', textChunk);
}
```
---
## 注意事项与优化建议
1. ✅ **兼容性警告**
`duplex: 'full'` 并非所有浏览器都支持,请在使用前检测环境支持情况。
2. ⚠️ **字符编码问题**
当前行内使用 `String.fromCharCode` 解码字节数组,**不支持 UTF-8 多字节字符**。
建议改进为使用 `TextDecoder`
```js
const decoder = new TextDecoder('utf-8');
let result = decoder.decode(value.value);
```
3. 🔒 **错误处理缺失**
当前代码未捕获 `fetch``reader.read()` 中可能出现的异常,建议添加 `try-catch`
4. 📦 **内存管理**
大量频繁调用 `send()` 可能导致内存堆积建议结合背压机制backpressure进行流控。
---
## 总结
| 功能 | 类/函数 | 用途 |
|------|--------|------|
| 流式上传 | `bricks.UpStreaming` | 实现双向流上传数据 |
| 流式下载解析 | `bricks.down_streaming` | 异步解析响应流为字符串 |
该模块适用于实时音视频通信、大文件分片上传、AI 流式推理等场景,具备良好的扩展潜力。
---
> 文档版本v1.0
> 最后更新2025-04-05

View File

@ -1,135 +1,498 @@
# widgetBuild
函数用于创建bricks框架的控件实例。
# Bricks.js 技术文档
## 语法
var w = widgetBuild(desc, widget)
> **Bricks.js** 是一个基于 JavaScript 的前端组件化框架,用于构建可扩展、模块化的 Web 应用程序。它支持动态组件加载、事件绑定、数据实时获取、弹窗管理、国际化i18n、媒体流控制等功能。
## 参数说明
### desc
desc是一个对象类型数据创建控件参数必须有"widgettype", "options"属性, 可选属性有"subwidegets""binds"
---
### widget
widget是一个控件实例用于解析desc中出现的url
## 目录
## 返回值
* null 出错可能1widgettype类型未注册或不存在2json文件格式不对
* 新创建的控件实体
- [1. 概述](#1-概述)
- [2. 核心对象 `bricks`](#2-核心对象-bricks)
- [3. 绑定动作Bind Actions](#3-绑定动作bind-actions)
- [通用属性](#通用属性)
- [各类型绑定的特定属性](#各类型绑定的特定属性)
- [4. 工具函数](#4-工具函数)
- [`uuid()`](#uuid)
- [`deviceid(appname)`](#deviceidappname)
- [`str2data(s, d)`](#str2datas-d)
- [`apply_data(desc, data)`](#apply_datadesc-data)
- [5. 组件构建系统](#5-组件构建系统)
- [`widgetBuild(desc, widget, data)`](#widgetbuilddesc-widget-data)
- [`buildBind(w, desc)`](#buildbindw-desc)
- [`buildEventBind(from_widget, widget, event, desc)`](#buildeventbindfrom_widget-widget-event-desc)
- [`universal_handler(from_widget, widget, desc, event)`](#universal_handlerfrom_widget-widget-desc-event)
- [6. 事件处理器生成器](#6-事件处理器生成器)
- [`buildEventHandler(w, desc, event)`](#buildeventhandlerw-desc-event)
- [`getRealtimeData(w, desc)`](#getrealtimedataw-desc)
- [7. 各类 Handler 构造函数](#7-各类-handler-构造函数)
- [`buildNewWindowHandler`](#buildnewwindowhandler)
- [`buildUrlwidgetHandler`](#buildurlwidgethandler)
- [`buildBricksHandler`](#buildbrickshandler)
- [`buildRegisterFunctionHandler`](#buildregisterfunctionhandler)
- [`buildMethodHandler`](#buildmethodhandler)
- [`buildScriptHandler`](#buildscripthandler)
- [`buildDispatchEventHandler`](#builddispatcheventhandler)
- [8. 辅助函数](#8-辅助函数)
- [`getWidgetById(id, from_widget)`](#getwidgetbyidid-from_widget)
- [9. App 类 (`bricks.App`)](#9-app-类-bricksapp)
- [构造函数参数 `opts`](#构造函数参数-opts)
- [主要方法](#主要方法)
## 例子
tree.json
---
## 1. 概述
`bricks` 是一个全局命名空间对象,封装了整个应用的核心逻辑:
```js
var bricks = window.bricks || {};
bricks.app = null;
```
{
"widgettype":"Tree",
"options":{
"idField":"id",
"textField":"text",
"data":[
{
"id":1,
"text":"node1",
"is_leaf":false,
"children":[
{
"id":11,
"text":"node11",
"is_leaf":false,
"children":[
{
"id":112,
"text":"node.12",
"is_leaf":false,
"children":[
{
"id":1112,
"text":"node1112",
"is_leaf":true
},
{
"id":1113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":113,
"text":"node113",
"is_leaf":true
}
]
},
{
"id":12,
"text":"node12",
"is_leaf":true
},
{
"id":13,
"text":"node13",
"is_leaf":true
}
]
},
{
"id":2,
"text":"node2",
"is_leaf":false,
"children":[
{
"id":21,
"text":"node21",
"is_leaf":true
},
{
"id":22,
"text":"node22",
"is_leaf":true
},
{
"id":23,
"text":"node23",
"is_leaf":true
}
]
},
{
"id":3,
"text":"node3",
"is_leaf":true
}
]
}
}
```
tree.html
```
<html>
<head>
<link rel="stylesheet" href="/bricks/css/bricks.css" />
</head>
<body>
<script src="/bricks/bricks.js"></script>
<script>
const opts =
{
"widget": {
"widgettype":"urlwidget",
"options":{
"url":"tree.json"
}
}
}
;
const app = new BricksApp(opts);
app.run();
</script>
</body>
</html>
所有功能均挂载在 `bricks` 对象下,包括:
- 组件工厂Factory
- 弹窗管理Popup / PopupWindow
- HTTP 请求客户端HttpJson
- 国际化支持I18n
- 设备标识与会话管理
- 动态组件构建与事件绑定机制
---
## 2. 核心对象 `bricks`
| 属性/方法 | 类型 | 描述 |
|------------------|-----------|------|
| `app` | App 实例 | 当前应用主实例 |
| `Body` | DOM 元素包装 | 页面 body 封装 |
| `bug` / `debug` | Boolean | 是否开启调试模式 |
| `Factory` | Class | 组件注册与创建工厂 |
| `RF` | RegisterFunction | 注册函数管理器 |
---
## 3. 绑定动作Bind Actions
通过 `desc.binds` 数组定义用户交互行为,每个绑定描述符包含以下结构。
### 通用属性
所有绑定动作都具备以下字段:
| 字段名 | 类型 | 必填 | 描述 |
|------------------|----------|------|------|
| `actiontype` | String | ✅ | 动作类型:`bricks`, `urlwidget`, `method`, `script`, `registerfunction`, `event`, `newwindow` |
| `wid` | String | ✅ | 触发事件的组件 ID |
| `event` | String | ✅ | 监听的事件名称,如 `'click'` |
| `target` | String 或 Widget | ✅ | 执行目标组件或特殊值(如 `'Popup'` |
| `datawidget` | String | ❌ | 获取运行时数据的源组件 ID |
| `datamethod` | String | ❌ | 数据获取方法,默认 `'getValue'` |
| `dataparams` | Object | ❌ | 调用 `datamethod` 的参数 |
| `datascript` | String | ❌ | 自定义脚本获取数据 |
| `rtdata` | Object | ❌ | 静态运行时数据 |
| `conform` | Object | ❌ | 确认对话框配置,在执行前弹出确认窗口 |
---
### 各类型绑定的特定属性
#### `urlwidget` action
从远程 URL 加载组件描述并渲染。
| 属性 | 类型 | 描述 |
|--------|---------|------|
| `mode` | String | `'replace'`, `'insert'`, `'append'`,默认 `'replace'` |
| `options.method` | String | HTTP 方法,默认 `'GET'` |
| `options.params` | Object | 请求参数 |
| `options.url` | String | 远程组件 JSON 地址 |
#### `bricks` action
本地创建 Bricks 组件。
| 属性 | 类型 | 描述 |
|--------|---------|------|
| `mode` | String | 同上 |
| `options.widgettype` | String | 组件类型,必须已注册 |
| `options.*` | Any | 组件初始化选项 |
#### `method` action
调用目标组件的方法。
| 属性 | 类型 | 描述 |
|---------|----------|------|
| `method` | String | 方法名 |
| `params` | Object | 方法参数kwargs |
#### `script` action
执行内联脚本。
| 属性 | 类型 | 描述 |
|---------|----------|------|
| `script` | String | 可执行 JS 代码字符串(异步函数体) |
| `params` | Object | 传入脚本的参数 |
#### `registerfunction` action
调用已注册的全局函数。
| 属性 | 类型 | 描述 |
|----------|----------|------|
| `rfname` | String | 注册函数名称 |
| `params` | Object | 函数参数 |
#### `event` action
派发自定义事件。
| 属性 | 类型 | 描述 |
|------------------|----------|------|
| `dispatch_event` | String | 要派发的事件名 |
| `params` | Object | 携带的数据 |
---
## 4. 工具函数
### `uuid()`
生成唯一标识符UUID优先使用 Web Crypto API降级为随机字符串。
```js
bricks.uuid(); // 返回类似 "a1b2c3d4e5f6..."
```
- 使用 `crypto.randomUUID()`(现代浏览器)
- 若不支持,则生成 30 位随机字符(来自字母数字集)
> ⚠️ 注意:降级版本非标准 UUID仅作唯一性参考。
---
### `deviceid(appname)`
为当前设备+应用组合生成持久化设备 ID存储于 `localStorage`
```js
bricks.deviceid('myapp'); // → myappdeviceid: abcdef...
```
- Key: `${appname}deviceid`
- 自动生成并缓存,避免重复访问时变化
---
### `str2data(s, d)`
模板字符串解析函数,支持变量插值和类型转换。
**语法格式:**
```
这是一个树形控件, 运行[这里](https://github.com/bricks/examples/tree.html)
更多的例子请看
[bricks控件例子](https://github.com/yumoqing/bricks/examples)
'my name is ${name}, age ${age:type}'
```
支持的类型:
- `int`: 转为整数
- `str`: 字符串(默认)
- `json`: JSON 序列化
**示例:**
```js
bricks.str2data('Hello ${user}', { user: 'Tom' });
// → "Hello Tom"
bricks.str2data('${data:json}', { data: { x: 1 } });
// → '{"x":1}'
```
返回 `null` 如果关键变量缺失。
---
### `apply_data(desc, data)`
将数据填充到任意对象结构中(递归模板替换)。
```js
const desc = { url: '/api/${id}' };
const filled = bricks.apply_data(desc, { id: 123 });
// → { url: '/api/123' }
```
内部使用 `str2data` 处理所有字符串字段。
---
## 5. 组件构建系统
### `widgetBuild(desc, widget, data)`
根据组件描述符异步构建组件树。
**参数:**
- `desc`: 组件描述对象(含 `widgettype`, `options`, `subwidgets`, `binds`
- `widget`: 父组件(上下文)
- `data`: 初始数据,用于模板填充
**流程:**
1. 若 `widgettype === 'urlwidget'`,先发起 HTTP 请求获取真实组件描述
2. 应用 `data` 填充模板
3. 查找组件类(通过 `Factory.get(type)`
4. 创建实例并设置 `id`
5. 递归构建子组件(`subwidgets`
6. 绑定事件(`binds`
**返回:** 构建完成的组件实例Promise
---
### `buildBind(w, desc)`
为指定组件建立事件绑定。
**步骤:**
1. 解析 `wid` 得到触发源组件
2. 调用 `buildEventBind(...)` 添加监听器
---
### `buildEventBind(from_widget, widget, event, desc)`
实际绑定事件处理器。
```js
widget.bind(event, handler);
```
处理器为 `bricks.universal_handler(from_widget, widget, desc)` 的柯里化函数。
---
### `universal_handler(from_widget, widget, desc, event)`
统一事件处理入口,支持前置确认(`conform`)。
**逻辑:**
- 若有 `conform` 配置,创建 `Conform` 弹窗,确认后再执行后续操作
- 否则直接调用 `buildEventHandler(...)`
若失败,输出调试信息。
---
## 6. 事件处理器生成器
### `buildEventHandler(w, desc, event)`
根据 `actiontype` 分发不同类型的处理器。
| actiontype | 返回处理器函数 |
|--------------------|----------------|
| `newwindow` | 新窗口打开链接 |
| `urlwidget` | 加载远程组件 |
| `bricks` | 创建本地组件 |
| `registerfunction` | 调用注册函数 |
| `method` | 调用组件方法 |
| `script` | 执行脚本 |
| `event` | 派发事件 |
同时处理:
- 实时数据提取(`rtdata` 来源:`datawidget`, `datamethod`, `datascript`
- 参数映射(`params_mapping`
- 事件参数合并
---
### `getRealtimeData(w, desc)`
从指定组件获取实时数据。
**方式:**
- `desc.method`: 调用组件方法
- `desc.script`: 执行脚本获取结果
**返回:** Promise 包裹的结果数据
---
## 7. 各类 Handler 构造函数
这些函数返回一个“延迟执行”的函数(通常用 `.bind()` 封装),供事件触发时调用。
### `buildNewWindowHandler`
打开新浏览器窗口。
- 支持参数拼接URL 查询参数)
- 使用 `_buildWidget` 渲染 `NewWindow` 组件
---
### `buildUrlwidgetHandler`
加载远程组件并在目标位置展示。
- 支持 `FormData` 特殊处理POST 提交)
- 参数自动注入 URL 或请求体
---
### `buildBricksHandler`
在目标容器中渲染本地 Bricks 组件。
- 支持 `mode`: `replace`, `insert`, `append`
- 自动处理弹窗打开Popup/PopupWindow
---
### `buildRegisterFunctionHandler`
调用通过 `bricks.RF.register(name, func)` 注册的函数。
- 参数合并策略:`desc.params` + `rtdata` + `event_params`
- 支持模板填充
---
### `buildMethodHandler`
调用目标组件上的某个方法。
```js
target[desc.method].bind(target, params)
```
适用于 `setValue`, `show`, `hide` 等组件 API。
---
### `buildScriptHandler`
动态创建异步函数并绑定执行环境。
```js
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
new AsyncFunction('params', 'event', scriptBody);
```
可用于复杂逻辑脚本注入。
---
### `buildDispatchEventHandler`
向目标组件派发自定义事件。
```js
target.dispatch(desc.dispatch_event, mergedParams);
```
常用于跨组件通信。
---
## 8. 辅助函数
### `getWidgetById(id, from_widget)`
根据 ID 查找组件实例,支持路径表达式。
**ID 支持语法:**
- `'self'`: 当前组件
- `'root'`: 根组件
- `'app'` / `'body'`: 应用主体
- `'window'`: 浏览器窗口根元素
- `'#id'`: DOM ID 选择器
- `'-id'`: 使用 `closest()` 向上查找
**示例:**
```js
bricks.getWidgetById('loginForm.usernameInput');
```
返回匹配的 `bricks_widget` 实例或原生 DOM。
---
## 9. App 类 (`bricks.App`)
继承自 `bricks.Layout`,是整个应用的根容器。
### 构造函数参数 `opts`
| 参数 | 类型 | 描述 |
|------|------|------|
| `appname` | String | 应用名,用于设备 ID 存储 |
| `debug` | Boolean/String | 是否启用调试日志 |
| `login_url` | String | 登录页地址,默认 `/rbac/userpassword_login.ui` |
| `charsize` | Number | 字体基础大小 |
| `language` | String | 初始语言(如 `'zh-CN'` |
| `i18n.url` | String | 国际化资源接口 |
| `i18n.default_lang` | String | 默认语言 |
| `widget` | Object | 根组件描述 |
### 主要方法
| 方法 | 描述 |
|------|------|
| `run()` | 启动应用:初始化语言、构建根组件、添加至 DOM |
| `build()` | 构建根组件树 |
| `change_language(lang)` | 切换语言并通知 i18n 系统 |
| `key_down_action(event)` | 全局键盘事件捕获 |
| `new_zindex()` | 获取新的 z-index 层级 |
| `screenWidth()` / `screenHeight()` | 获取视口尺寸 |
| `save_session(session)` / `get_session()` | 会话管理 |
| `start_camera(vpos)` | 打开指定摄像头 |
| `start_mic()` | 打开麦克风 |
| `getCameras()` | 获取可用视频输入设备列表 |
| `textsize_bigger()` / `textsize_smaller()` | 调整字体大小 |
| `show_windows_panel()` | 显示多窗口面板(实验性) |
---
## 附录 A内置组件类型常见 `widgettype`
| 类型 | 说明 |
|------|------|
| `Text` | 文本显示 |
| `Button` | 按钮 |
| `Input` | 输入框 |
| `Layout` | 布局容器 |
| `Popup` | 模态弹窗 |
| `PopupWindow` | 独立窗口弹窗 |
| `Conform` | 确认对话框 |
| `NewWindow` | 新浏览器窗口包装器 |
| `WindowsPanel` | 窗口管理面板 |
---
## 附录 B调试技巧
启用调试:
```js
bricks.bug = true;
```
查看日志:
```js
console.log(...args); // 内部使用 debug 输出
```
推荐配合 Chrome DevTools 使用。
---
## 许可证
MIT License假设项目允许
---
> 📝 文档版本v1.0
> ✍️ 最后更新2025年4月5日

View File

@ -60,4 +60,3 @@ bricks采用控件这一概念来描述web GUI的显示部件每个控件均
ui文件可以直接调试如在服务器根目录下的test目录下有一个hello.ui文件
就可以在浏览器中用urlhttps://sername/test/hello.ui调试

254
docs/cn/button.md Normal file
View File

@ -0,0 +1,254 @@
# `bricks.Button` 技术文档
> 继承自:`bricks.Layout`
`bricks.Button` 是一个可交互的按钮组件,支持图标、文本标签、自定义样式以及点击事件处理。它基于 Flexbox 布局进行内容排列,适用于水平或垂直方向的内容展示。
---
## 目录
- [概述](#概述)
- [配置选项Options](#配置选项options)
- [结构与布局](#结构与布局)
- [方法说明](#方法说明)
- [`constructor(opts)`](#constructoro)
- [`create()`](#create)
- [`opts_setup()`](#opts_setup)
- [`target_clicked(event)`](#target_clickedevent)
- [事件机制](#事件机制)
- [注册信息](#注册信息)
---
## 概述
`bricks.Button` 提供了一个封装良好的按钮控件,可用于触发操作或导航。该组件可以包含:
- 图标(通过 `bricks.Icon`
- 文本标签(通过 `bricks.Text`
- 自定义 CSS 样式
- 工具提示tooltip
- 可配置的布局方向(横向/纵向)
所有子元素使用 Flexbox 排列,并可通过 `item_rate` 控制尺寸比例。
---
## 配置选项Options
| 属性名 | 类型 | 描述 |
|----------------|----------|------|
| `orientation` | String | 布局方向,可选 `'horizontal'` 或默认为垂直 `'vertical'`。影响内部图标的排列方式。 |
| `height` | CSS value | 容器高度,默认 `100%`(由父级控制)。 |
| `width` | CSS value | 容器宽度,默认 `100%`。 |
| `item_rate` | Number | 子元素(图标/文字)的缩放比率,默认为 `1`。用于调整大小。 |
| `tooltip` | String | 鼠标悬停时显示的提示文本,设置到 DOM 的 `title` 属性上。 |
| `color` | String | 文字颜色(传递给 `Text` 组件)。 |
| `bgcolor` | String | 背景颜色(传递给 `Text` 组件)。 |
| `nonepack` | Boolean | 是否移除内边距和边框;若为 `true`,则 padding 设为 `0px`border 为 `0`。 |
| `name` | String | 组件名称,用作 DOM 元素 ID 的基础。 |
| `icon` | String | 图标资源 URL 或路径,用于创建 `bricks.Icon` 实例。 |
| `label` | String | 显示的文字标签支持国际化i18n。 |
| `css` | Object | 自定义 CSS 样式对象,将被合并到 DOM 元素的 `style` 中。 |
| `action` | Object | 点击按钮后执行的动作配置,详见下表。 |
### `action` 对象结构
| 子属性 | 类型 | 描述 |
|----------------|----------|------|
| `target` | String | 目标组件或模块名称。 |
| `datawidget` | String | 数据源控件名。 |
| `datamethod` | String | 数据获取方法名。 |
| `datascript` | String | 自定义脚本逻辑(如函数体字符串)。 |
| `dataparams` | Object | 发送至数据接口的参数。 |
| `rtdata` | Boolean | 是否实时刷新数据。 |
| `actiontype` | String | 动作类型,例如 `"navigate"``"submit"` 等。 |
| `...` | Any | 其他扩展字段,根据业务需求定义。 |
---
## 结构与布局
- 使用 `<button>` 元素作为根节点。
- 内部采用 `display: flex` 进行布局:
- 水平布局:`flex-direction: row`
- 垂直布局:`flex-direction: column`
- 支持图标在上、文字在下的垂直排布,或并排的水平排布。
- 默认具有 `0.5rem` 的内边距,除非设置了 `nonepack: true`
```css
{
display: flex;
justify-content: center;
align-items: center;
text-align: center;
padding: 0.5rem; /* 或 0px当 nonepack=true */
}
```
---
## 方法说明
### `constructor(opts)`
初始化按钮实例,继承自 `Layout` 并设置基本样式和属性。
#### 参数
- `opts` (Object): 配置选项对象。
#### 行为
1. 调用父类构造函数 `super(opts)`
2. 初始化默认样式对象Flex 布局相关)。
3. 根据 `nonepack` 设置是否去除 padding 和 border。
4. 根据 `orientation` 设置 `flexDirection` 并记录简写形式(`this.orient = 'h'|'v'`)。
5. 设置组件 ID 为 `opts.name`
6. 执行 `opts_setup()` 创建子组件(图标/文本)。
7. 合并默认样式与用户自定义样式到 `dom_element.style`
---
### `create()`
创建底层 DOM 元素。
```js
this.dom_element = document.createElement('button');
```
> 此方法在组件生命周期中自动调用,无需手动执行。
---
### `opts_setup()`
根据配置项创建子控件(图标和/或文本),并将它们添加到当前容器中。
#### 行为
- 如果存在 `opts.icon`,创建一个 `bricks.Icon` 实例,并绑定点击事件。
- 如果存在 `opts.label`,创建一个 `bricks.Text` 实例,启用 i18n 支持,并绑定点击事件。
- 所有子控件均通过 `add_widget()` 加入布局。
- 文本控件引用保存在 `this.text_w` 上,便于后续更新。
#### 示例代码片段
```js
if (this.opts.icon) {
const icon = new bricks.Icon({
rate: this.item_rate,
url: this.opts.icon
});
this.add_widget(icon);
icon.bind('click', this.target_clicked.bind(this));
}
```
---
### `target_clicked(event)`
处理按钮点击事件的核心方法。
#### 参数
- `event` (Event): 浏览器原生事件对象。
#### 行为
1. 输出调试日志:`bricks.debug('target_clicked() .... called ')`
2. 阻止事件冒泡(`event.stopPropagation()`
3. 触发组件自身的 `'click'` 事件,并携带完整 `opts` 数据。
4. 若配置了 `action`,且启用了 `debug` 模式,则输出完整的 `opts` 到控制台。
#### 示例输出debug 模式)
```text
debug:opts= { name: "btn_save", action: { target: "form", actiontype: "submit" }, ... }
```
---
## 事件机制
| 事件名 | 触发时机 | 携带数据 |
|----------|------------------------|--------------------|
| `click` | 用户点击按钮时 | `this.opts` 对象 |
可通过 `.bind('click', handler)` 监听此事件:
```js
button.bind('click', function(data) {
console.log('Button clicked:', data.action);
});
```
---
## 注册信息
```js
bricks.Factory.register('Button', bricks.Button);
```
使得可以通过工厂模式动态创建按钮:
```js
const btn = bricks.Factory.create('Button', {
name: 'my_button',
label: 'Submit',
icon: '/icons/save.svg',
action: {
actiontype: 'submit',
target: 'form1'
}
});
```
---
## 使用示例
```js
const myButton = new bricks.Button({
name: 'btn_ok',
label: 'OK',
icon: '/icons/check.png',
orientation: 'horizontal',
item_rate: 1.2,
color: '#fff',
bgcolor: '#007bff',
tooltip: 'Click to confirm',
action: {
actiontype: 'navigate',
target: 'page_home'
},
debug: true
});
document.body.appendChild(myButton.dom_element);
```
---
## 注意事项
- 推荐配合 `bricks.Icon``bricks.Text` 使用以保持 UI 一致性。
- 若需完全自定义外观,请使用 `css` 属性覆盖样式。
- `nonepack: true` 适合嵌入紧凑型 UI 区域。
- 所有文本自动支持国际化i18n前提是 `bricks.app.i18n` 已正确配置。
---
## 版本信息
- **框架**Bricks.js
- **组件路径**`bricks.Button`
- **最后更新**:根据代码注释推断为近期维护版本
---
✅ *文档完*

355
docs/cn/camera.md Normal file
View File

@ -0,0 +1,355 @@
# `bricks.Camera` 技术文档
> 一个基于 `bricks.Popup` 的摄像头操作组件,支持拍照和视频录制功能。
---
## 概述
`bricks.Camera``bricks` UI 框架中的一个类,继承自 `bricks.Popup`,用于在网页中实现摄像头访问、实时预览、拍照以及视频录制功能。它通过调用浏览器的 `MediaDevices.getUserMedia()` 接口获取摄像头流,并结合 HTML5 `<video>``<canvas>` 实现图像捕获与处理。
该组件支持两种模式:
- **拍照模式picture**:单张照片拍摄。
- **录像模式recorder**:支持开始/停止录制 WebM 格式的视频。
同时提供切换摄像头设备、删除取消等交互按钮。
---
## 继承关系
```
bricks.Popup
bricks.Camera
```
---
## 构造函数
```js
new bricks.Camera(options)
```
### 参数
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|-------|------|------|--------|------|
| `opts.fps` | Number | 否 | `60` | 视频预览帧率(每秒帧数),影响 `show_picture` 调用频率 |
| `opts.type` | String | 是 | `'picture'` | 模式类型:<br>`'picture'`: 拍照模式<br>`'recorder'`: 录像模式 |
| `opts.auto_dismiss` | Boolean | 否 | `false` | 是否在操作后自动关闭弹窗(内部强制设为 `false` |
> ⚠️ 注意:`auto_dismiss` 在构造函数中被强制设置为 `false`,以防止误关闭。
---
## 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `this.recordedChunks` | Array | 存储媒体录制过程中产生的数据块Blob |
| `this.mediaRecorder` | MediaRecorder \| null | 浏览器原生 `MediaRecorder` 实例,用于视频录制 |
| `this.stream` | MediaStream \| null | 来自摄像头的媒体流对象 |
| `this.video` | HTMLVideoElement | 用于播放摄像头视频流的 `<video>` 元素 |
| `this.imgw` | bricks.Image | 显示当前画面预览的图像控件 |
| `this.shot_btn` | bricks.Svg | 拍照或开始/停止录制的按钮图标 |
| `this.cur_camera_id` | Number | 当前使用的摄像头设备索引(未实际使用,建议优化) |
| `this.task_period` | Number | 帧刷新周期(单位:秒),由 `fps` 计算得出 |
| `this.task` | TaskHandle | 使用 `schedule_once` 创建的定时任务句柄 |
| `this.dataurl` | String | 最近一帧画面的 Data URLbase64 编码的 JPEG 图像) |
| `this.record_status` | String | 录制状态:<br>`''`(初始)<br>`'standby'`(待机)<br>`'recording'`(正在录制) |
---
## 方法
### `constructor(opts)`
初始化摄像头组件,创建 UI 布局并绑定事件。
#### 内部逻辑
1. 设置默认帧率和禁用自动关闭。
2. 调用父类 `super(opts)` 初始化弹窗。
3. 初始化录制相关变量。
4. 创建 UI 结构:
- 上方填充区域显示实时画面(`this.imgw`
- 下方水平工具栏包含:
- 切换摄像头按钮
- 拍照 / 录制按钮
- 删除(关闭)按钮
5. 绑定按钮点击事件:
- `shot_btn`:根据模式执行 `take_picture``switch_recording`
- `switch_btn`:切换摄像头
- `del_btn`:关闭弹窗
6. 延迟 0.1 秒启动摄像头(`startCamera`
---
### `async startCamera(vpos?)`
启动摄像头并开始视频流预览。
#### 参数
- `vpos` (Number, 可选):指定摄像头设备索引(前置/后置)
#### 流程
1. 调用全局应用对象 `bricks.app.start_camera(vpos)` 获取摄像头流。
2. 将流赋值给 `this.stream` 并绑定到 `<video>` 元素。
3. 开始播放视频。
4. 启动周期性任务 `show_picture`,按设定帧率更新预览图。
#### 示例
```js
await this.startCamera(0); // 使用第一个摄像头
```
> ✅ 依赖全局 `bricks.app` 提供 `video_devices``video_stream`
---
### `show_picture()`
将当前视频帧渲染到预览区(通过 Canvas 截图)。
#### 步骤
1. 创建临时 `<canvas>`,尺寸匹配视频分辨率。
2. 使用 `drawImage` 将视频当前帧绘制到 canvas。
3. 转换为 JPEG Data URL质量 95%)。
4. 更新 `this.imgw` 显示新图像。
5. 调用 `schedule_once` 自身,维持帧率循环。
#### 性能提示
- 若 `this.task_period === 0`,则退出循环(如拍照后停止)。
- 使用 `requestAnimationFrame` 类似的调度机制(`schedule_once`)。
---
### `async switch_camera(btn, event)`
切换摄像头设备(例如前后摄像头)。
#### 参数
- `btn`触发按钮SVG 图标)
- `event`:点击事件对象(未使用)
#### 行为
1. 检查是否有多个摄像头(`bricks.app.video_devices.length < 2`)。
2. 若只有一个摄像头,则禁用按钮。
3. 否则,递增 `vpos``bricks.app.vpos`),循环切换。
4. 调用 `startCamera(vpos)` 重新初始化摄像头。
> 🔄 支持循环切换0 → 1 → ... → n-1 → 0
---
### `switch_recording()`
控制录像的开始与停止。
#### 行为
| 当前状态 | 动作 |
|---------|------|
| `recording` | 停止录制,按钮变“开始”图标 |
| `standby` 或空 | 开始录制,按钮变“停止”图标 |
#### 触发方法
- `videorecorder_start()`:开始录制
- `videorecorder_stop()`:停止录制
---
### `videorecorder_start()`
使用 `MediaRecorder API` 开始录制视频。
#### 异常
- 如果 `this.stream` 未初始化,抛出错误。
#### 配置
- 使用 `new MediaRecorder(stream)`
- 数据格式:`video/webm`
- 监听 `ondataavailable`:收集每个 chunk 到 `recordedChunks`
- 监听 `onstop`
- 合并所有 chunks 成一个 Blob
- 创建 File 对象:`recorded_video.webm`
- 创建 Object URL仅供调试日志
- 触发 `recorded` 事件,携带生成的 `File`
#### 示例输出
```js
dispatch('recorded', file); // file.type === "video/webm"
```
---
### `videorecorder_stop()`
手动停止媒体录制。
```js
this.mediaRecorder.stop();
```
> 自动触发 `onstop` 回调,完成文件合成与事件派发。
---
### `take_picture(event)`
执行拍照操作。
#### 参数
- `event`:点击事件,调用 `stopPropagation()` 防止冒泡
#### 行为
1. 取消当前预览任务(`task.cancel()`)。
2. 停止帧更新(`task_period = 0`)。
3. 派发 `shot` 事件,携带最近一帧的 Data URL。
#### 示例响应
```js
camera.bind('shot', function(dataurl) {
const img = document.createElement('img');
img.src = dataurl;
document.body.appendChild(img);
});
```
---
## UI 结构
```text
+----------------------------------+
| [Preview] |
| (bricks.Image: imgw) |
+----------------------------------+
| [Switch] [Action] [Delete] |
| (SVG) (SVG) (SVG) |
+----------------------------------+
```
- 使用 `bricks.HBox` 水平布局底部按钮栏
- 使用 `bricks.Filler` 占位扩展空间
- 所有按钮均为 `bricks.Svg` 组件,加载 SVG 资源图标
---
## 事件
| 事件名 | 触发时机 | 携带数据 |
|-------|----------|---------|
| `shot` | 用户拍照完成 | `dataurl`JPEG base64 字符串) |
| `recorded` | 视频录制结束 | `File` 对象WebM 格式) |
可通过 `bind()` 方法监听:
```js
const camera = new bricks.Camera({ type: 'picture' });
camera.bind('shot', (dataurl) => {
console.log('Photo taken:', dataurl);
});
camera.open();
```
---
## 图标资源SVG
| 按钮 | 图标路径 | 提示文字 |
|------|--------|--------|
| 拍照 | `imgs/camera.svg` | Take a picture |
| 开始录制 | `imgs/start_recording.svg` | Start or stop record video |
| 停止录制 | `imgs/stop_recording.svg` | Recording... |
| 切换摄像头 | `imgs/switch-camera.svg` | switch camera |
| 删除/取消 | `imgs/delete.svg` | canel it应为 cancel |
> 图标路径通过 `bricks_resource()` 解析,通常指向 `/resources/...`
---
## 依赖项
| 依赖 | 说明 |
|------|------|
| `bricks.Popup` | 父类,提供弹窗基础功能 |
| `bricks.Svg` | SVG 图标按钮 |
| `bricks.Image` | 图像显示控件 |
| `bricks.HBox`, `bricks.Filler` | 布局容器 |
| `bricks.app` | 全局应用对象,需提供:<br>- `video_devices`: 摄像头设备列表<br>- `vpos`: 当前设备索引<br>- `start_camera(vpos)`: 启动摄像头方法<br>- `video_stream`: 摄像头媒体流 |
| `schedule_once(fn, delay)` | 自定义异步调度函数(类似 `setTimeout`,但可能集成任务队列) |
| `bricks_resource(path)` | 资源路径解析函数 |
---
## 注册
```js
bricks.Factory.register('Camera', bricks.Camera);
```
允许通过工厂方式创建实例:
```js
bricks.Factory.create('Camera', { type: 'recorder' });
```
---
## 使用示例
### 拍照模式
```js
const camera = new bricks.Camera({
type: 'picture',
fps: 30
});
camera.bind('shot', (dataurl) => {
document.getElementById('result').src = dataurl;
});
camera.open();
```
### 录像模式
```js
const recorder = new bricks.Camera({
type: 'recorder',
fps: 25
});
recorder.bind('recorded', (file) => {
const url = URL.createObjectURL(file);
const a = document.createElement('a');
a.href = url;
a.download = 'video.webm';
a.click();
});
recorder.open();
```
---
## 注意事项 & 改进建议
| 问题 | 建议 |
|------|------|
| `record_status == ''` 应为 `=` | 修复拼写错误,避免逻辑 bug |
| `task.cancel()``task` 应为 `this.task` | 修复变量引用错误 |
| `tip:'canel it'` 拼写错误 | 改为 `'cancel it'` |
| `this.cur_camera_id` 未更新 | 在 `switch_camera` 中同步更新 |
| 不支持分辨率配置 | 可扩展 `opts.constraints` 传入 `getUserMedia` |
| WebM 兼容性有限 | 可检测支持 MIME 类型,降级为 MP4若支持 |
| 缺少错误处理 UI | 添加摄像头权限失败提示 |
---
## 版本信息
- **作者**:未知(来自 `bricks` 框架)
- **最后修改**:根据代码推断为现代浏览器环境开发
- **兼容性**:需支持 `MediaDevices`, `MediaRecorder`, `Canvas` 的现代浏览器Chrome/Firefox/Edge
---
📌 **总结**
`bricks.Camera` 是一个功能完整、结构清晰的前端摄像头操作组件,适用于需要拍照或短时间录像的应用场景,如头像上传、短视频录制等。配合 `bricks` 框架可快速集成至项目中。

323
docs/cn/cols.md Normal file
View File

@ -0,0 +1,323 @@
# `bricks.Cols` 技术文档
> **模块**`bricks.Cols`
> **继承自**`bricks.VBox`
> **用途**:用于展示分页数据的列式布局组件,支持滚动加载、点击事件、动态渲染记录等特性。
---
## 概述
`bricks.Cols` 是一个基于 `VBox` 布局容器构建的数据展示控件,专为高效展示大量可分页数据而设计。它通过垂直滚动加载前后页内容(懒加载),并以列的形式排列每条记录,适用于移动端和桌面端的响应式布局。
该组件常用于新闻列表、商品展示、用户卡片墙等场景。
---
## 继承结构
```
bricks.Widget
└── bricks.Container
└── bricks.Box
└── bricks.VBox
└── bricks.Cols
```
---
## 配置选项Constructor Options
| 参数 | 类型 | 说明 |
|------|------|------|
| `data_url` | `String` | 获取数据的 API 地址,用于异步加载分页数据。 |
| `data_params` | `Object` | 初始请求参数对象,在每次请求时合并发送。 |
| `data_method` | `String` | 请求方法(如 `"GET"``"POST"`),默认由 `PageDataLoader` 决定。 |
| `page_rows` | `Number` | 每页显示的数据行数。 |
| `cache_limit` | `Number` | 缓存的最大页数,超出后自动清理旧页。 |
| `col_width` | `String/Number` | 单个列的宽度CSS 格式,如 `"200px"`)。 |
| `col_cwidth` | `String` | 列的弹性宽度CSS `flex` 值,如 `"1fr"`)。 |
| `mobile_cols` | `Number` | 移动端每行显示的列数,默认为 `2`。 |
| `record_view` | `Object` | 记录项的视图配置,定义如何使用 `widgetBuild` 渲染单条数据。 |
| `title` | `String` | 可选标题文本(支持国际化)。 |
| `description` | `String` | 可选描述文本(支持 Markdown。 |
| `toolbar` | `Object` | 工具栏配置对象,将被构造成 `bricks.Toolbar` 实例。 |
---
## 事件Events
| 事件名 | 触发条件 | 参数 |
|--------|----------|------|
| `record_click` | 用户点击某条记录时触发 | `(recordData)` — 当前记录的原始数据对象 |
| `command` | 工具栏发出命令时转发此事件(通过 `dispatch` | 命令名称及参数 |
> 使用 `.bind(event, handler)` 方法监听这些事件。
---
## 属性Properties
| 属性 | 类型 | 描述 |
|------|------|------|
| `loader` | `bricks.PageDataLoader` | 分页数据加载器实例,负责管理前后页数据获取与缓存。 |
| `container` | `bricks.VScrollPanel` | 外层垂直滚动容器,监听滚动阈值以触发分页加载。 |
| `main` | `bricks.DynamicColumn` | 实际存放记录项的动态列容器。 |
| `select_record` | `Widget` | 当前选中的记录对应的 widget 实例。 |
| `loading` | `Boolean` | 是否正在加载数据,防止重复请求。 |
| `title_w` | `bricks.Title4` | 标题组件实例(如果设置了 `title`)。 |
| `desc_w` | `bricks.MdWidget` | 描述组件实例(如果设置了 `description`)。 |
| `toolbar_w` | `bricks.Toolbar` | 工具栏组件实例(如果设置了 `toolbar`)。 |
---
## 方法Methods
### `constructor(opts)`
初始化 `Cols` 组件,并创建子组件结构。
#### 流程说明:
1. 调用父类构造函数。
2. 初始化 `PageDataLoader` 加载器。
3. 创建滚动容器 `VScrollPanel` 并绑定滚动边界事件。
4. 根据配置添加标题、描述、工具栏。
5. 创建主内容区域 `DynamicColumn`
6. 延迟 0.5 秒调用 `load_first_page()` 加载首屏数据。
---
### `command_handle(event)`
处理来自工具栏的命令事件,并将其转为 `dispatch` 派发出去。
```js
this.toolbar_w.bind('command', this.command_handle.bind(this));
```
---
### `async handle_click(rw, event)`
处理单个记录项的点击事件。
#### 功能:
- 阻止事件冒泡。
- 取消上一个选中项的高亮样式。
- 设置当前项为选中状态并添加 `selected_record` CSS 类。
- 打印日志并派发 `record_click` 事件,携带 `user_data`
> `rw.user_data` 存储的是该记录的原始数据对象。
---
### `async dataHandle(d)`
处理从 `loader` 返回的数据包,并渲染到界面。
#### 参数:
- `d`: 数据对象,格式如下:
```js
{
rows: [...], // 数据数组
add_page: Number, // 添加到哪一页
delete_page: Number, // (可选)需删除的页码(用于翻页替换)
pos_rate: Number // 滚动位置比例0~1
}
```
#### 行为逻辑:
- 若是向前翻页(非最大页),则反转数据顺序以便插入顶部。
- 使用 `widgetBuild(record_view, parent, data)` 动态生成每个记录 widget。
- 绑定点击事件。
- 设置 `data-page` 属性用于后续删除。
- 插入至 `main` 容器开头或末尾。
- 如有 `delete_page`,调用 `delete_page()` 清理旧页。
---
### `delete_page(page)`
删除指定页码的所有 DOM 元素及其 widget。
#### 实现方式:
- 查询所有 `[data-page="X"]` 的元素。
- 获取其关联的 `bricks_widget` 实例。
- 从 `main` 中移除对应 widget。
---
### `create_main_widget()`
重新创建主内容区域(`DynamicColumn`),通常在刷新或重置布局时调用。
```js
this.main = new bricks.DynamicColumn({
width: "100%",
col_cwidth: this.col_cwidth,
mobile_cols: this.mobile_cols || 2
});
```
---
### `async show_with_data(data)`
直接传入本地数据进行展示(绕过 `data_url` 请求)。
> ⚠️ 注意:目前代码中存在错误,应为 `await this.load_first_page(params);` 而不是 `await load_first_page(params);`
#### 正确实现建议:
```js
async show_with_data(data){
this.data = data;
this.data_url = null;
await this.load_first_page(); // 不需要参数
}
```
---
### `async load_first_page(params)`
加载第一页数据。
#### 流程:
1. 显示加载动画(`Running` 组件)。
2. 检查是否已在加载中,避免重复请求。
3. 合并 `data_params` 与传入的 `params`
4. 调用 `loader.loadData()` 获取数据。
5. 成功后清空主容器并调用 `dataHandle(d)`
6. 出错时打印调试信息。
7. 最终隐藏加载动画,释放锁。
---
### `async load_previous_page()`
向上滚动触底时加载前一页。
- 自动计算滚动位置并恢复。
- 支持 `pos_rate` 定位。
- 错误时仅输出 debug 日志。
---
### `async load_next_page()`
向下滚动触底时加载下一页。
- 类似于 `load_previous_page`,但加载下一页。
- 注释掉的 `scrollTop` 表示暂未启用精确滚动定位。
---
## 内部机制
### 分页加载策略
利用 `bricks.PageDataLoader` 实现智能分页:
- 支持双向分页(上一页 / 下一页)。
- 支持缓存控制(`cache_pages`)。
- 数据按“页”组织,可通过 `data-page` 属性追踪来源。
### 滚动加载触发
通过 `VScrollPanel` 的两个事件实现无限滚动:
| 事件 | 触发条件 |
|------|----------|
| `min_threshold` | 滚动到顶部附近 → 加载前一页 |
| `max_threshold` | 滚动到底部附近 → 加载下一页 |
绑定方式:
```js
this.container.bind('min_threshold', this.load_previous_page.bind(this));
this.container.bind('max_threshold', this.load_next_page.bind(this));
```
---
## 使用示例
### 基本用法
```js
var cols = new bricks.Cols({
title: "Latest Articles",
description: "A list of recent posts.",
data_url: "/api/articles",
data_params: { category: "tech" },
page_rows: 10,
cache_limit: 5,
col_cwidth: "1fr",
mobile_cols: 2,
record_view: {
type: "CardView",
fields: ["title", "summary", "image"]
}
});
cols.bind("record_click", function(data) {
console.log("Selected:", data);
});
document.body.appendChild(cols.dom_element);
```
### 手动加载本地数据
```js
cols.show_with_data({
rows: [
{id: 1, name: "Item 1"},
{id: 2, name: "Item 2"}
]
});
```
---
## 注意事项
1. **性能优化**:只保留可视区域附近的页面,其他页面可手动卸载。
2. **内存管理**:长时间运行应用应注意 `cache_limit` 设置,避免内存泄漏。
3. **事件绑定**:确保 `handle_click` 使用 `.bind(this, w)` 正确绑定上下文。
4. **CSS 样式**
- `.selected_record`:用于标记选中项,请在 CSS 中定义高亮样式。
- `.filler`:应用于 `container`,可能影响背景或间距。
---
## 注册信息
```js
bricks.Factory.register('Cols', bricks.Cols);
```
可在模板中通过 `<widget type="Cols" ...>` 方式声明使用。
---
## 版本信息
- **作者**Bricks Framework Team
- **最后更新**:根据代码推断为现代异步 JS 架构ES6+
- **兼容性**:需支持 Promise、async/await、Custom Elements
---
## 相关组件
| 组件 | 作用 |
|------|------|
| `bricks.PageDataLoader` | 提供分页数据加载能力 |
| `bricks.VScrollPanel` | 提供滚动检测与阈值事件 |
| `bricks.DynamicColumn` | 实现响应式多列布局 |
| `bricks.widgetBuild` | 动态构建子组件的核心函数 |
---
**文档完成度**:完整覆盖功能、接口、流程与使用方式。
🔧 **待修复问题**`show_with_data` 方法中对 `load_first_page` 的调用缺少 `this.` 上下文。

125
docs/cn/compoments.list Normal file
View File

@ -0,0 +1,125 @@
accordion.js:bricks.Factory.register('Accordion', bricks.Accordion);
asr.js:bricks.Factory.register('ASRClient', bricks.ASRClient);
audio.js:bricks.Factory.register('AudioPlayer', bricks.AudioPlayer);
audio.js:bricks.Factory.register('AudioRecorder', bricks.AudioRecorder);
audio.js:bricks.Factory.register('TextedAudioPlayer', bricks.TextedAudioPlayer);
bar.js:bricks.Factory.register('ChartBar', bricks.ChartBar);
bricks.js:registerfunction action:
bricks.js: 'not registered',
bricks.js: case 'registerfunction':
button.js:bricks.Factory.register('Button', bricks.Button);
camera.js:bricks.Factory.register('Camera', bricks.Camera);
cols.js:bricks.Factory.register('Cols', bricks.Cols);
conform.js:bricks.Factory.register('Conform', bricks.Conform);
continueaudio.js:bricks.Factory.register('ContinueAudioPlayer', bricks.ContinueAudioPlayer);
countdown.js:bricks.Factory.register('Countdown', bricks.Countdown);
countdown.js:bricks.Factory.register('TimePassed', bricks.TimePassed);
datagrid.js:bricks.Factory.register('DataGrid', bricks.DataGrid);
datarow.js:bricks.Factory.register('DataRow', bricks.DataRow);
dataviewer.js:bricks.Factory.register('DataViewer', bricks.DataViewer);
docxviewer.js:bricks.Factory.register('DOCXviewer', bricks.DOCXviewer);
docxviewer.js:bricks.Factory.register('EXCELviewer', bricks.EXCELviewer);
docxviewer.js:bricks.Factory.register('PDFviewer', bricks.PDFviewer);
dynamicaccordion.js:bricks.Factory.register('DynamicAccordion', bricks.DynamicAccordion);
dynamiccolumn.js:bricks.Factory.register('DynamicColumn', bricks.DynamicColumn);
factory.js: register(name, widget){
floaticonbar.js:bricks.Factory.register('IconBar', bricks.IconBar);
floaticonbar.js:bricks.Factory.register('IconTextBar', bricks.IconTextBar);
floaticonbar.js:bricks.Factory.register('FloatIconBar', bricks.FloatIconBar);
floaticonbar.js:bricks.Factory.register('FloatIconTextBar', bricks.FloatIconTextBar);
form.js:bricks.Factory.register('InlineForm', bricks.InlineForm);
form.js:bricks.Factory.register('Form', bricks.Form);
gobang.js:bricks.Factory.register('Gobang', bricks.Gobang);
html.js:bricks.Factory.register('Html', bricks.Html);
iconbarpage.js:bricks.Factory.register('IconbarPage', bricks.IconbarPage);
iframe.js:bricks.Factory.register('NewWindow', bricks.NewWindow);
iframe.js:bricks.Factory.register('Iframe', bricks.Iframe);
image.js:bricks.Factory.register('Image', bricks.Image);
image.js:bricks.Factory.register('StatedIcon', bricks.StatedIcon);
image.js:bricks.Factory.register('Icon', bricks.Icon);
image.js:bricks.Factory.register('BlankIcon', bricks.BlankIcon);
input.js: register(name, uitype,Klass){
input.js: bricks.Factory.register(name, Klass);
input.js:Input.register('UiAudio', 'audiorecorder', bricks.UiAudio);
input.js:Input.register('UiStr', 'str', bricks.UiStr);
input.js:Input.register('UiHide', 'hide', bricks.UiHide);
input.js:Input.register('UiTel', 'tel', bricks.UiTel);
input.js:Input.register('UiDate', 'date', bricks.UiDate);
input.js:Input.register('UiInt', 'int', bricks.UiInt);
input.js:Input.register('UiFloat', 'float', bricks.UiFloat);
input.js:Input.register('UiCheck', 'check', bricks.UiCheck);
input.js:Input.register('UiCheckBox', 'checkbox', bricks.UiCheckBox);
input.js:Input.register('UiEmail', 'email', bricks.UiEmail);
input.js:Input.register('UiFile', 'file', bricks.UiFile);
input.js:Input.register('UiImage', 'image', bricks.UiImage);
input.js:Input.register('UiCode', 'code', bricks.UiCode);
input.js:Input.register('UiText', 'text', bricks.UiText);
input.js:Input.register('UiPassword', 'password', bricks.UiPassword);
input.js:Input.register('UiAudio', 'audio', bricks.UiAudio);
input.js:Input.register('UiVideo', 'video', bricks.UiVideo);
input.js:Input.register('UiAudio', 'audiotext', bricks.UiAudio);
input.js:Input.register('UiSearch', 'search', bricks.UiSearch);
keypress.js:bricks.Factory.register('KeyPress', bricks.KeyPress);
layout.js:bricks.Factory.register('HBox', bricks.HBox);
layout.js:bricks.Factory.register('FHBox', bricks.FHBox);
layout.js:bricks.Factory.register('VBox', bricks.VBox);
layout.js:bricks.Factory.register('FVBox', bricks.FVBox);
layout.js:bricks.Factory.register('Filler', bricks.Filler);
layout.js:bricks.Factory.register('HFiller', bricks.Filler);
layout.js:bricks.Factory.register('VFiller', bricks.Filler);
layout.js:bricks.Factory.register('ResponsableBox', bricks.ResponsableBox);
line.js:bricks.Factory.register('ChartLine', bricks.ChartLine);
llm.js:bricks.Factory.register('LlmIO', bricks.LlmIO);
llmout.js:bricks.Factory.register('LlmOut', bricks.LlmOut);
markdown_viewer.js:bricks.Factory.register('MarkdownViewer', bricks.MarkdownViewer);
markdown_viewer.js:bricks.Factory.register('MdWidget', bricks.MdWidget);
menu.js:bricks.Factory.register('Menu', bricks.Menu);
message.js:bricks.Factory.register('Message', bricks.Message);
message.js:bricks.Factory.register('Error', bricks.Error);
miniform.js:bricks.Factory.register('MiniForm', bricks.MiniForm);
modal.js:bricks.Factory.register('Modal', bricks.Modal);
modal.js:bricks.Factory.register('ModalForm', bricks.ModalForm);
multiple_state_image.js:bricks.Factory.register('MultipleStateImage', bricks.MultipleStateImage);
period.js:bricks.Factory.register('PeriodDays', bricks.PeriodDays);
pie.js:bricks.Factory.register('ChartPie', bricks.ChartPie);
popup.js:bricks.Factory.register('Popup', bricks.Popup);
popup.js:bricks.Factory.register('PopupWindow', bricks.PopupWindow);
progressbar.js:bricks.Factory.register('ProgressBar', bricks.ProgressBar);
qaframe.js:bricks.Factory.register('QAFrame', bricks.QAFrame);
recorder.js:bricks.Factory.register('SysCamera', bricks.SysCamera);
recorder.js:bricks.Factory.register('WidgetRecorder', bricks.WidgetRecorder);
recorder.js:bricks.Factory.register('SysAudioRecorder', bricks.SysAudioRecorder);
recorder.js:bricks.Factory.register('SysVideoRecorder', bricks.SysVideoRecorder);
registerfunction.js: register(n, f){
running.js:bricks.Factory.register('Running', bricks.Running);
scroll.js:bricks.Factory.register('VScrollPanel', bricks.VScrollPanel);
scroll.js:bricks.Factory.register('HScrollPanel', bricks.HScrollPanel);
splitter.js:bricks.Factory.register('Splitter', bricks.Splitter);
streaming_audio.js:bricks.Factory.register('StreamAudio', bricks.StreamAudio);
streaming_audio.js:bricks.Factory.register('ASRText', bricks.StreamAudio);
svg.js:bricks.Factory.register('Svg', bricks.Svg);
svg.js:bricks.Factory.register('StatedSvg', bricks.StatedSvg);
svg.js:bricks.Factory.register('MultipleStateIcon', bricks.MultipleStateIcon);
tab.js:bricks.Factory.register('TabPanel', bricks.TabPanel);
tabular.js:bricks.Factory.register('Tabular', bricks.Tabular);
toolbar.js:bricks.Factory.register('Toolbar', bricks.Toolbar);
tree.js:bricks.Factory.register('Tree', bricks.Tree);
tree.js:bricks.Factory.register('EditableTree', bricks.EditableTree);
utils.js: register(n, icon){
vadtext.js:bricks.Factory.register('VadText', bricks.VadText);
videoplayer.js:bricks.Factory.register('Iptv', bricks.Iptv);
videoplayer.js:bricks.Factory.register('VideoPlayer', bricks.VideoPlayer);
videoplayer.js:bricks.Factory.register('Video', bricks.VideoPlayer);
websocket.js:bricks.Factory.register('WebSocket', bricks.WebSocket);
webspeech.js:bricks.Factory.register('WebTTS', bricks.WebTTS);
webspeech.js:bricks.Factory.register('WebASR', bricks.WebASR);
widget.js:bricks.Factory.register('Tooltip', bricks.Tooltip);
widget.js:bricks.Factory.register('Text', bricks.Text);
widget.js:bricks.Factory.register('KeyinText', bricks.KeyinText);
widget.js:bricks.Factory.register('Title1', bricks.Title1);
widget.js:bricks.Factory.register('Title2', bricks.Title2);
widget.js:bricks.Factory.register('Title3', bricks.Title3);
widget.js:bricks.Factory.register('Title4', bricks.Title4);
widget.js:bricks.Factory.register('Title5', bricks.Title5);
widget.js:bricks.Factory.register('Title6', bricks.Title6);
wterm.js:bricks.Factory.register('Wterm', bricks.Wterm);

249
docs/cn/conform.md Normal file
View File

@ -0,0 +1,249 @@
# `bricks.Conform` 技术文档
> 一个基于 `bricks.PopupWindow` 的确认对话框类,用于展示消息并提供“确认”与“取消”操作选项。
---
## 概述
`bricks.Conform``bricks` UI 框架中的一个模态弹窗组件,继承自 `bricks.PopupWindow`。它被设计用于在用户执行关键操作前进行确认,例如删除数据或提交表单。该组件自动显示,并包含可自定义的确认/取消按钮,支持国际化文本和事件回调。
---
## 类定义
```javascript
class bricks.Conform extends bricks.PopupWindow
```
---
## 构造函数
### `constructor(opts)`
初始化一个 `Conform` 实例。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 配置选项对象,继承自 `PopupWindow` 并扩展以下属性: |
##### 扩展配置项 (`opts`)
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `message` | String | 必填 | 要显示的消息内容支持换行和国际化i18n。 |
| `timeout` | Number | `0` (自动覆盖为 0) | 弹窗自动关闭时间(毫秒),此组件中强制设为 `0`,即不自动关闭。 |
| `auto_open` | Boolean | `true` (自动设置) | 是否在创建后立即打开弹窗,此处固定为 `true`。 |
| `conform` | Object | 可选 | “确认”按钮的自定义配置,如标签、事件等。 |
| `discard` | Object | 可选 | “取消”按钮的自定义配置。 |
> ⚠️ 注意:构造函数内部会强制设置 `opts.timeout = 0``opts.auto_open = true`,以确保用户必须手动选择操作。
#### 示例
```javascript
var confirmDialog = new bricks.Conform({
message: "确定要删除此文件吗?",
conform: {
label: "删除",
handler: function(){ console.log("已确认"); }
},
discard: {
label: "保留"
}
});
```
---
## 方法说明
### `create_conform()`
创建主容器布局并将消息区域与工具栏添加进去。
- 使用 `VBox` 布局占满整个窗口。
- 调用 `create_message()``create_toolbar()` 分别构建内容区与操作区。
- 将整体布局通过 `add_widget()` 添加到弹窗中。
---
### `create_message(widget)`
创建消息显示区域,支持长文本滚动和居中对齐。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `widget` | `bricks.VBox` | 容器部件,用于承载消息内容。 |
#### 内部结构
1. 创建一个 `Filler` 占位符,允许内容拉伸填充。
2. 在 `Filler` 中嵌入 `VScrollPanel` 支持垂直滚动。
3. 添加 `Text` 组件显示消息:
- 启用文本换行 (`wrap: true`)
- 水平居中对齐 (`halign: 'middle'`)
- 支持国际化 (`i18n: true`)
---
### `create_toolbar(widget)`
创建底部操作工具栏,包含“确认”和“取消”两个按钮。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `widget` | `bricks.VBox` | 容器部件,用于添加工具栏。 |
#### 工具栏配置
使用 `bricks.IconTextBar` 创建图标+文字按钮栏:
| 按钮名称 | 图标 | 默认标签 | 自定义来源 |
|---------|------|----------|------------|
| `conform` | `imgs/conform.svg` | "Conform" | `this.opts.conform` |
| `discard` | `imgs/cancel.svg` | "Discard" | `this.opts.discard` |
> ✅ 所有自定义属性将通过 `bricks.extend()` 合并到默认配置中。
#### 事件绑定
- `conform` → 触发 `conform_hndl`
- `discard` → 触发 `discard_hndl`
---
### `conform_hndl(event)`
“确认”按钮点击处理函数。
#### 行为
1. 调用 `this.dismiss()` 关闭弹窗。
2. 触发名为 `'conformed'` 的自定义事件,供外部监听。
#### 示例监听
```javascript
confirmDialog.on('conformed', function(){
// 执行确认逻辑
});
```
---
### `discard_hndl(event)`
“取消”按钮点击处理函数。
#### 行为
1. 调用 `this.dismiss()` 关闭弹窗。
2. 触发名为 `'cancelled'` 的自定义事件。
#### 示例监听
```javascript
confirmDialog.on('cancelled', function(){
// 执行取消后的逻辑
});
```
---
## 事件列表
| 事件名 | 触发时机 | 携带数据 |
|-------|----------|--------|
| `conformed` | 用户点击“确认”按钮后 | 无 |
| `cancelled` | 用户点击“取消”按钮后 | 无 |
可通过 `.on(event, handler)``.bind(event, handler)` 注册监听。
---
## 注册信息
```javascript
bricks.Factory.register('Conform', bricks.Conform);
```
- 通过工厂模式注册名称为 `'Conform'` 的可实例化组件。
- 可通过 `bricks.Factory.create('Conform', options)` 动态创建实例。
---
## 布局结构图
```plaintext
PopupWindow
└── VBox (100% x 100%)
├── Filler
│ └── VScrollPanel
│ └── Text (消息内容)
└── IconTextBar
├── [✔] Conform 按钮
└── [✖] Discard 按钮
```
---
## 样式与资源依赖
- **图标资源路径**
- 确认图标:`bricks_resource('imgs/conform.svg')`
- 取消图标:`bricks_resource('imgs/cancel.svg')`
- 使用 `i18n: true`,需配合国际化语言包解析多语言文本。
---
## 使用建议
✅ 推荐场景:
- 删除、覆盖、退出等危险操作前的二次确认。
- 需要用户明确响应的操作流程中断点。
❌ 不适用场景:
- 非阻塞性提示(应使用 Toast 或 Banner
- 需长时间停留的信息展示(考虑使用普通窗口)。
---
## 完整示例
```javascript
var dialog = new bricks.Conform({
message: "您确定要退出编辑模式吗?未保存的内容将会丢失。",
conform: { label: "退出", style: "danger" },
discard: { label: "继续编辑" }
});
dialog.on('conformed', function(){
window.location.href = "/home";
});
dialog.on('cancelled', function(){
console.log("用户选择保留");
});
```
---
## 版本信息
- **框架版本**`bricks.js`
- **组件作者**:未知(基于命名空间推断)
- **最后更新**:根据代码逻辑推断为现代 ES6+ 风格实现
---
📌 *文档生成于2025年4月*

314
docs/cn/continueaudio.md Normal file
View File

@ -0,0 +1,314 @@
# `ContinueAudioPlayer` 技术文档
> 一个基于 Web Audio API 的连续音频播放器组件,支持流式播放、音量控制、暂停/恢复、静音切换和事件回调。
---
## 概述
`bricks.ContinueAudioPlayer` 是一个继承自 `bricks.VBox` 的音频播放类,专为在浏览器中实现无缝连续音频播放而设计。它使用 Web Audio API 解码并播放 Base64 编码或 ArrayBuffer 格式的音频数据,并支持动态控制播放状态与音量。
该组件适用于需要精确时间控制的语音合成TTS、实时音频流处理等场景。
---
## 类定义
```js
class ContinueAudioPlayer extends bricks.VBox
```
注册名称:`'ContinueAudioPlayer'`
注册方式:
```js
bricks.Factory.register('ContinueAudioPlayer', bricks.ContinueAudioPlayer);
```
---
## 构造函数
### `constructor(options)`
初始化播放器实例。
#### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|------|
| `options.ws_url` | `string` | WebSocket 地址(当前未实际使用,保留字段) |
| `options.onStart` | `Function` | 音频开始播放时触发的回调函数 |
| `options.onEnd` | `Function` | 当前音频片段结束播放时触发 |
| `options.onPause` | `Function` | 暂停播放时触发 |
| `options.onResume` | `Function` | 恢复播放时触发 |
| `options.onVolumeChange` | `Function` | 音量变化时触发,参数为新音量值 |
#### 示例
```js
const player = new bricks.ContinueAudioPlayer({
ws_url: 'wss://example.com/audio',
onStart: () => console.log('Audio started'),
onEnd: () => console.log('Audio ended'),
onPause: () => console.log('Paused'),
onResume: () => console.log('Resumed'),
onVolumeChange: (vol) => console.log(`Volume: ${vol}`)
});
```
---
## 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `audioContext` | `AudioContext` | Web Audio 上下文对象 |
| `gainNode` | `GainNode` | 控制音量的增益节点 |
| `nextStartTime` | `Number` | 下一段音频应开始播放的时间(以秒为单位) |
| `started` | `Boolean` | 是否已成功初始化音频上下文 |
| `muted` | `Boolean` | 当前是否处于静音状态 |
| `volume` | `Number` | 当前音量范围0.0 - 1.0 |
| `ws_url` | `String` | 存储传入的 WebSocket URL目前仅作存储用途 |
| `options` | `Object` | 用户传入的配置选项 |
---
## 方法
### `initAudioContext()`
创建并初始化 `AudioContext``GainNode`,准备音频播放环境。
- 若上下文已被关闭,则可通过调用此方法重新激活。
- 自动设置初始增益值为 `this.volume`
- 更新 `nextStartTime` 为当前时间,确保后续音频从正确时间点开始。
> ⚠️ 注意:由于浏览器策略限制,**必须在用户交互(如点击)后才能创建或恢复 AudioContext**。
---
### `base64ToArrayBuffer(base64) → ArrayBuffer`
将 Base64 字符串转换为 `ArrayBuffer`,用于 Web Audio API 解码。
#### 参数
| 参数名 | 类型 | 描述 |
|-------|------|------|
| `base64` | `string` | Base64 编码的二进制音频数据 |
#### 返回值
- `{ArrayBuffer}`:可用于 `decodeAudioData` 的原始二进制缓冲区。
#### 示例
```js
const buffer = player.base64ToArrayBuffer("UklGRiQAAABXQVZFZm...");
```
---
### `handleAudioTrack(arrayBuffer)`
解码并播放一段音频数据。
#### 参数
| 参数名 | 类型 | 描述 |
|-------|------|------|
| `arrayBuffer` | `ArrayBuffer` | 包含编码音频数据的二进制缓冲区(如 WAV、MP3 等) |
#### 行为说明
1. 使用 `audioContext.decodeAudioData()` 异步解码音频。
2. 创建 `AudioBufferSourceNode` 并连接到增益节点。
3. 在合适的时间点开始播放(避免重叠):
- 开始时间为 `Math.max(currentTime, this.nextStartTime)`
4. 更新 `nextStartTime` 为本次播放结束时间,保证下段音频接续播放。
5. 触发 `onStart` 回调。
6. 播放结束后触发 `onEnd` 回调。
#### 错误处理
若解码失败,会在控制台输出错误日志:
```text
Error decoding audio data: [Error]
```
---
### `pauseAudio()`
暂停所有正在播放的音频。
- 调用 `audioContext.suspend()` 挂起上下文。
- 成功后触发 `onPause` 回调。
> 📝 仅当 `audioContext.state === 'running'` 时有效。
---
### `resumeAudio()`
恢复被暂停的音频播放。
- 调用 `audioContext.resume()` 恢复上下文运行。
- 成功后触发 `onResume` 回调。
> 📝 仅当 `audioContext.state === 'suspended'` 时有效。
---
### `restart()`
停止当前播放,关闭音频上下文,并重新初始化,实现“重新开始”。
#### 行为流程
1. 如果上下文存在且未关闭,先调用 `close()`
2. 关闭完成后调用 `initAudioContext()` 重建上下文。
3. 重置播放时间线。
> ✅ 可用于重置播放状态或应对长时间运行后的资源释放问题。
---
### `setVolume(value)`
设置播放音量。
#### 参数
| 参数名 | 类型 | 范围 | 描述 |
|-------|------|------|------|
| `value` | `number` | `0.0 ~ 1.0` | 目标音量值 |
#### 功能
- 自动限制输入值在 `[0, 1]` 范围内。
- 更新 `this.volume`
- 如果已初始化 `gainNode`,则更新其 `gain.value`(若已静音则保持为 0
- 触发 `onVolumeChange` 事件。
#### 示例
```js
player.setVolume(0.75); // 设置音量为 75%
```
---
### `toggleMute()`
切换静音状态。
- 若当前非静音,则进入静音状态(`gain.value = 0`)。
- 若当前静音,则恢复至原音量(`gain.value = this.volume`)。
- 切换后更新 `this.muted` 并触发 `onVolumeChange`
---
### `emit(eventName, ...args)`
触发指定事件回调(如果定义了对应函数)。
#### 参数
| 参数名 | 类型 | 描述 |
|-------|------|------|
| `eventName` | `string` | 回调函数名(对应 `options[eventName]` |
| `...args` | any | 传递给回调的参数 |
#### 示例
```js
this.emit('onStart'); // 调用 options.onStart()
```
---
## 使用示例
```js
// 创建播放器实例
const audioPlayer = new bricks.ContinueAudioPlayer({
onStart: () => console.log("▶️ 开始播放"),
onEnd: () => console.log("⏹️ 播放结束"),
onVolumeChange: (v) => console.log(`🔊 音量: ${v}`)
});
// 假设你有一段 Base64 音频数据
const base64Audio = "UklGRiQAAABXQVZFZm1...";
const buffer = audioPlayer.base64ToArrayBuffer(base64Audio);
// 播放音频
audioPlayer.handleAudioTrack(buffer);
// 控制操作
audioPlayer.setVolume(0.5); // 调整音量
audioPlayer.toggleMute(); // 切换静音
audioPlayer.pauseAudio(); // 暂停
audioPlayer.resumeAudio(); // 恢复
audioPlayer.restart(); // 重启播放器
```
---
## 浏览器兼容性
| 浏览器 | 支持情况 |
|--------|----------|
| Chrome | ✅ 支持 |
| Firefox | ✅ 支持 |
| Safari | ⚠️ 需要 `webkitAudioContext` 兼容写法(已内置处理) |
| Edge | ✅ 支持 |
| iOS Safari | ✅(注意自动播放策略) |
> 🔐 **注意**:出于安全策略,大多数现代浏览器要求 **用户手势(如点击)** 后才能启动音频上下文。
---
## 注意事项
1. **自动播放限制**
大多数浏览器禁止页面加载时自动播放音频。建议在用户点击事件中初始化或调用 `handleAudioTrack`
2. **内存管理**
长时间连续播放大量音频可能导致内存占用上升,请合理管理音频数据生命周期。
3. **跨域音频数据**
所有音频数据需满足同源策略或 CORS 要求,否则无法解码。
4. **精度同步**
利用 `AudioContext.currentTime` 实现高精度播放调度,适合多段连续播放场景。
---
## 图解工作流程
```
[Base64]
↓ base64ToArrayBuffer()
[ArrayBuffer]
↓ handleAudioTrack()
decodeAudioData() → AudioBuffer
↓ createBufferSource()
[Source Node] → gainNode → destination
startTime = max(now, nextStartTime)
nextStartTime += duration
```
---
## 许可与归属
© 2025 Bricks Framework. All rights reserved.
开源协议:请参考项目 LICENSE 文件。
---
📌 **提示**:结合 WebSocket 或 Fetch 流式传输,可实现 TTS 实时语音播报系统。

284
docs/cn/countdown.md Normal file
View File

@ -0,0 +1,284 @@
# Bricks 时间组件技术文档
本模块提供两个基于 `bricks` 框架的时间显示类组件:`TimePassed`(计时器)和 `Countdown`(倒计时器),并包含一个通用的时间格式化工具函数。这些组件可用于构建需要时间展示功能的 UI 界面。
---
## 目录
- [1. 核心工具函数](#1-核心工具函数)
- [`bricks.formatTime(seconds)`](#bricksformattimeseconds)
- [2. TimePassed 类(正向计时器)](#2-timepassed-类正向计时器)
- [继承关系](#继承关系)
- [构造函数](#构造函数)
- [方法](#方法)
- [3. Countdown 类(倒计时器)](#3-countdown-类倒计时器)
- [继承关系](#继承关系-1)
- [配置选项 (`opts`)](#配置选项-opts)
- [事件](#事件)
- [构造函数](#构造函数-1)
- [方法](#方法-1)
- [4. 工厂注册](#4-工厂注册)
- [5. 使用示例](#5-使用示例)
---
## 1. 核心工具函数
### `bricks.formatTime(seconds)`
将秒数转换为 `HH:MM:SS` 格式的字符串,不足两位用前导零填充。
#### 参数
| 参数名 | 类型 | 描述 |
|---------|--------|--------------|
| seconds | number | 时间(以秒为单位) |
#### 返回值
- `{string}`:格式化后的时间字符串,格式为 `HH:MM:SS`
#### 示例
```js
bricks.formatTime(3661); // 输出 "01:01:01"
bricks.formatTime(59); // 输出 "00:00:59"
```
#### 实现说明
- 小时 = `Math.floor(seconds / 3600)`
- 分钟 = `Math.floor((seconds % 3600) / 60)`
- 秒 = `seconds % 60`
- 每部分使用 `.padStart(2, '0')` 补齐至两位数字。
---
## 2. TimePassed 类(正向计时器)
用于从 0 开始递增显示已过去的时间如“00:00:05”表示运行了 5 秒)。
### 继承关系
```js
class TimePassed extends bricks.VBox
```
> 继承自 `bricks.VBox`,作为容器可包含子控件。
---
### 构造函数
```js
new bricks.TimePassed(opts)
```
#### 参数
| 参数名 | 类型 | 是否必需 | 描述 |
|----------|--------|----------|--------------------------|
| opts | object | 是 | 配置对象 |
| opts.text_rate | any | 否 | 文本渲染速率或样式参数,传递给内部 Text 组件 |
> ⚠️ 注意:当前代码中存在潜在 bug —— 使用了未定义变量 `this.t``this.text_rate`,应为 `opts.text_rate` 并初始化文本内容为 `formatTime(this.seconds)`
✅ **建议修复后的构造函数片段:**
```js
this.seconds = 0;
this.text_w = new bricks.Text({
text: bricks.formatTime(this.seconds),
rate: opts.text_rate
});
this.add_widget(this.text_w);
```
---
### 方法
| 方法名 | 描述 |
|------------|--------------------------------------------------------------|
| `start()` | 启动计时器,每秒调用一次 `add_one_second()`,开始递增时间。 |
| `stop()` | 停止计时器,取消当前调度任务。 |
#### `add_one_second()`
私有方法,由 `schedule_once` 调度执行:
- 每次使 `this.seconds += 1`
- 更新显示文本
- 自动重新调度下一次调用(形成循环)
> 使用 `bind(this)` 确保上下文正确。
⚠️ 若不停止,将持续运行。
---
## 3. Countdown 类(倒计时器)
实现一个可配置的倒计时组件,在时间归零时触发事件。
### 继承关系
```js
class Countdown extends bricks.VBox
```
> 同样继承自 `VBox` 容器组件。
---
### 配置选项 (`opts`)
| 参数名 | 类型 | 是否必需 | 描述 |
|-------------|--------|----------|----------------------------------------------------------------------|
| limit_time | string | 是 | 初始倒计时时间,格式支持 `"SS"``"MM:SS"``"HH:MM:SS"` |
| text_rate | any | 否 | 传入 Text 组件的渲染参数 |
---
### 事件
| 事件名 | 触发条件 | 数据参数 |
|-----------|----------------------------------|--------|
| `timeout` | 当倒计时结束(`this.seconds < 1`)时触发 | 无 |
可通过 `this.dispatch('timeout')` 触发事件,供外部监听。
---
### 构造函数
```js
new bricks.Countdown(opts)
```
#### 功能说明
1. 解析 `opts.limit_time` 字符串为小时、分钟、秒。
2. 支持以下格式:
- `"30"` → 30 秒
- `"5:30"` → 5 分 30 秒
- `"1:30:45"` → 1 小时 30 分 45 秒
3. 转换为总秒数存储在 `this.seconds`
4. 创建并添加 `Text` 组件用于显示时间。
> ✅ 支持任意长度分割,但仅处理 1~3 段,其余情况默认按三段解析。
---
### 方法
| 方法名 | 描述 |
|-------------------|----------------------------------------------------------------------|
| `start()` | 启动倒计时,延迟 1 秒后调用 `time_down_second()` 开始递减。 |
| `time_down_second()` | 私有方法:每次减少 1 秒,更新显示;若时间为 0则触发 `timeout` 事件并终止。 |
> 使用 `schedule_once(fn, delay)` 实现定时回调,形成递归倒计时。
---
## 4. 工厂注册
为了支持动态创建组件实例,通过工厂模式注册两个类:
```js
bricks.Factory.register('Countdown', bricks.Countdown);
bricks.Factory.register('TimePassed', bricks.TimePassed);
```
### 用途
允许通过字符串名称动态创建组件,例如:
```js
let timer = bricks.Factory.create('TimePassed', { text_rate: 'high' });
let cd = bricks.Factory.create('Countdown', { limit_time: '00:01:00' });
```
---
## 5. 使用示例
### 示例 1创建并启动正向计时器
```js
let timePassed = new bricks.TimePassed({
text_rate: 'normal'
});
// 添加到页面或其他容器
someContainer.add_widget(timePassed);
// 开始计时
timePassed.start();
// 停止计时
// timePassed.stop();
```
### 示例 2创建倒计时器并监听超时事件
```js
let countdown = new bricks.Countdown({
limit_time: '00:00:10', // 10 秒倒计时
text_rate: 'large'
});
countdown.on('timeout', function() {
console.log('倒计时结束!');
alert('时间到!');
});
someContainer.add_widget(countdown);
countdown.start(); // 启动倒计时
```
### 示例 3使用工厂创建组件
```js
let widget = bricks.Factory.create('Countdown', {
limit_time: '01:30:00'
});
widget.on('timeout', () => {
console.log('倒计时完成');
});
container.add_widget(widget);
widget.start();
```
---
## 注意事项与改进建议
1. **Bug 修复建议**
- 在 `TimePassed` 构造函数中,`text: this.t` 应改为 `text: bricks.formatTime(this.seconds)`
- `rate: this.text_rate` 应为 `rate: opts.text_rate`
2. **性能优化**
- 可考虑使用 `setInterval` 替代递归 `schedule_once`,但当前方式更灵活且兼容异步调度系统。
3. **扩展性建议**
- 可增加 `pause()` 方法支持暂停/恢复。
- 提供 `getFormattedTime()` 公共方法获取当前时间字符串。
4. **输入验证**
- 对 `limit_time` 的解析可加入非法值检测(如非数字)。
---
## 版本信息
- 模块名:`bricks.time`
- 作者Bricks Framework Team
- 最后更新2025-04-05
---
📌 *本文档适用于 Bricks UI 框架 v1.x+*

88
docs/cn/css.md Normal file
View File

@ -0,0 +1,88 @@
# CSS 多列布局配置文档
本技术文档描述了一个用于定义多列布局样式的 JavaScript 对象 `css`,其中包含多列布局的样式配置。
---
## 概述
该对象 `css` 用于存储与 CSS 多列布局相关的样式属性。当前仅定义了一个名为 `multicolumns` 的子对象,用于配置多列布局的宽度和间距。
---
## 配置结构
```javascript
var css = {
multicolumns: {
columnWidth: '350px',
colummGap: '10px'
}
}
```
> ⚠️ 注意:`colummGap` 存在拼写错误,应为 `columnGap`
---
## 属性说明
### `multicolumns`
一个包含多列布局相关 CSS 属性的对象。
| 属性名 | 类型 | 默认值 | 描述 |
|---------------|--------|------------|------|
| `columnWidth` | 字符串 | `'350px'` | 定义每列的宽度。此处设置为 `350px`,表示每列宽 350 像素。 |
| `colummGap` | 字符串 | `'10px'` | ⚠️ **拼写错误**:应为 `columnGap`。用于设置列之间的间距,当前设置为 `10px`。 |
---
## 使用建议
### 修正拼写错误
建议将 `colummGap` 更正为 `columnGap`,以确保与标准 CSS 属性名称一致:
```javascript
var css = {
multicolumns: {
columnWidth: '350px',
columnGap: '10px' // 已修正拼写
}
}
```
### 实际应用示例
可将此配置动态应用于 DOM 元素,例如:
```javascript
const element = document.getElementById('content');
element.style.columnWidth = css.multicolumns.columnWidth;
element.style.columnGap = css.multicolumns.columnGap;
```
或结合 CSSOM 使用:
```css
/* 等效的 CSS 写法 */
#content {
column-width: 350px;
column-gap: 10px;
}
```
---
## 浏览器兼容性
- `column-width``column-gap` 是 CSS Multi-column Layout Module 的一部分。
- 现代浏览器普遍支持,但在旧版浏览器(如 IE中不支持。
- 推荐在使用时添加必要的厂商前缀或进行兼容性检测。
---
## 总结
该配置对象提供了一种通过 JavaScript 管理多列布局样式的简便方式。建议修复属性名拼写错误,并在生产环境中结合实际需求进行扩展和封装。

434
docs/cn/datagrid.md Normal file
View File

@ -0,0 +1,434 @@
# `bricks.DataGrid` 技术文档
> **基于 `bricks.js` 框架的可冻结列数据表格组件**
---
## 概述
`bricks.DataGrid` 是一个功能丰富的前端数据网格组件,支持:
- 冻结列(固定左侧列)
- 虚拟滚动与分页加载
- 动态字段渲染(通过 `uitype``uioptions`
- 行选择、点击事件处理
- 工具栏、迷你表单集成
- 多语言支持i18n
该组件继承自 `bricks.VBox`,采用模块化设计,结合 `bricks.Row` 实现每行数据的封装和交互。
---
## 类结构
### 1. `bricks.Row`
表示数据网格中的一行记录。
#### 构造函数:`constructor(dg, rec)`
| 参数 | 类型 | 说明 |
|------|------|------|
| `dg` | `bricks.DataGrid` | 所属的数据网格实例 |
| `rec` | `Object` | 当前行的数据对象 |
**初始化内容:**
- 复制数据:`this.data = objcopy(rec)`
- 初始化列容器:`freeze_cols`, `normal_cols`
- 创建列控件:调用 `create_col_widgets()` 分别生成冻结列和普通列
- 设置宽度样式
- 绑定点击处理器
#### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `dg` | `DataGrid` | 所属的数据网格对象 |
| `data` | `Object` | 当前行原始数据副本 |
| `freeze_cols` | `Array<bricks.Widget>` | 冻结列中的控件列表 |
| `normal_cols` | `Array<bricks.Widget>` | 非冻结列中的控件列表 |
| `name_widgets` | `Object` | 字段名 → 控件映射表 |
| `click_handler` | `Function` | 点击事件处理器(绑定到所属 DataGrid |
| `freeze_row` | `bricks.HBox``null` | 包含所有冻结列控件的水平布局容器 |
| `normal_row` | `bricks.HBox``null` | 包含所有非冻结列控件的水平布局容器 |
#### 方法
##### `create_col_widgets(fields, cols)``bricks.HBox|null`
根据字段定义创建对应的 UI 控件,并放入指定数组中。
**参数:**
- `fields`: 字段配置数组
- `cols`: 输出控件数组(引用传递)
**逻辑流程:**
1. 遍历每个字段 `f`
2. 合并默认选项:
```js
{
name: f.name,
label: f.label,
uiype: f.uitype,
width: f.width,
required: true,
row_data: objcopy(this.data),
readonly: true
}
```
3. 根据 `uitype` 创建不同控件:
- `'button'`: 使用 `bricks.Button`,绑定 `button_click`
- 其他类型:使用 `bricks.viewFactory(opts, this.data)`
4. 为控件设置样式flex 布局、最小宽度)
5. 将控件加入 `cols` 数组及 `name_widgets` 映射
6. 最终返回一个包含这些控件的 `HBox`
**返回值:**
- 成功时返回 `HBox` 容器
- 若无字段则返回 `null`
---
##### `button_click(event)`
按钮点击回调函数。
**行为:**
- 临时重写 `getValue()` 方法,使其返回整行数据
- 准备动作描述符 `desc`(含 action、params、datawidget 等)
- 触发全局处理器 `universal_handler`
> ⚠️ 注意:此方法在 `Button` 上下文中执行(通过 `.bind(w)` 绑定)
---
##### `selected()`
将当前行设为“已选”状态(视觉高亮)。
**操作:**
- 遍历所有列控件,移除 `'selected'` CSS 类
##### `unselected()`
取消当前行的选择状态。
**操作:**
- 添加 `'selected'` CSS 类至所有列控件
##### `toogle_select(e, f)`
手动切换 DOM 元素的选中类。
| 参数 | 类型 | 说明 |
|------|------|------|
| `e` | `HTMLElement` | 要操作的元素 |
| `f` | `Boolean` | 是否添加 `'selected'` 类 |
---
### 2. `bricks.DataGrid` (主类)
继承自 `bricks.VBox`,实现完整的数据表格功能。
#### 构造函数:`constructor(opts)`
**参数 `opts` 支持以下属性:**
| 属性 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `dataurl` | `String` | 否 | - | 数据接口 URL用于异步加载 |
| `method` | `String` | 否 | `'GET'` | 请求方式 |
| `params` | `Object` | 否 | `{}` | 请求参数 |
| `title` | `String` | 否 | - | 表格标题(显示为 `Title1` |
| `description` | `String` | 否 | - | 表格描述文本 |
| `show_info` | `Boolean` | 否 | `false` | 是否显示信息栏 |
| `miniform` | `Object` | 否 | - | 查询条件迷你表单配置 |
| `toolbar` | `Object` | 否 | - | 工具栏按钮配置 |
| `row_height` | `Number/String` | 否 | `'auto'` | 表头行高度 |
| `header_css` | `String` | 否 | `'grid_header'` | 表头 CSS 类名 |
| `body_css` | `String` | 否 | `'grid_body'` | 表体 CSS 类名 |
| `fields` | `Array<Object>` | 是 | - | 字段定义数组(见下文) |
| `check` | `Boolean` | 否 | `false` | 是否显示复选框列 |
| `lineno` | `Boolean` | 否 | `false` | 是否显示序号列 |
| `admin` | `Any` | 否 | - | 管理员权限标志(预留扩展) |
#### 字段定义 (`fields`) 结构
每个字段对象包含:
| 属性 | 类型 | 说明 |
|------|------|------|
| `name` | `String` | 字段名(对应数据键) |
| `label` | `String` | 显示标签(支持 i18n |
| `datatype` | `String` | 数据类型(如 string, int, date |
| `uitype` | `String` | 控件类型text, number, button, checkbox 等) |
| `uioptions` | `Object` | 控件额外配置项 |
| `freeze` | `Boolean` | 是否冻结(固定在左侧) |
| `width` | `Number/String` | 列宽(单位 px |
> 特殊字段自动插入:
> - `_check`: 复选框列(当 `check=true`
> - `_lineno`: 序号列(当 `lineno=true`
---
#### 主要属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `loading` | `Boolean` | 是否正在加载数据 |
| `select_row` | `bricks.Row` | 当前选中行对象 |
| `dataurl`, `method`, `params` | `String/Object` | 数据请求配置 |
| `fields` | `Array` | 所有字段定义 |
| `freeze_fields` / `normal_fields` | `Array` | 分离后的冻结/非冻结字段列表 |
| `freeze_width` / `normal_width` | `Number` | 冻结区与主体区域总宽度px |
| `freeze_part`, `normal_part` | `VBox` | 左右两部分容器 |
| `freeze_header`, `normal_header` | `HBox` | 表头容器 |
| `freeze_body`, `normal_body` | `VScrollPanel` | 表体滚动容器 |
| `loader` | `BufferedDataLoader` | 缓冲式数据加载器 |
| `miniform` | `MiniForm` | 查询表单 |
| `toolbar` | `Toolbar` | 工具栏 |
---
#### 核心方法
##### `create_parts()`
初始化整个表格结构,包括:
1. 分离字段为 `freeze_fields``normal_fields`
2. 计算各区域宽度
3. 创建左右两个垂直容器(带滚动)
4. 构建表头(调用 `create_header()`
5. 设置同步滚动机制(`coscroll`
> ✅ 如果启用了 `check``lineno`,会自动插入特殊字段。
---
##### `create_header()`
创建表头单元格,使用 `bricks.Text` 显示字段 `label``name`
- 每个文本控件设置 `flex` 样式以匹配列宽
- 添加 `column_no` 属性到 DOM 元素(格式:`f0`, `n1`...),便于后续定位
---
##### `add_rows(records, direction)`
批量添加多行数据。
| 参数 | 类型 | 说明 |
|------|------|------|
| `records` | `Array<Object>` | 数据记录数组 |
| `direction` | `'up' \| 'down'` | 添加方向(默认 `'down'` |
内部循环调用 `add_row()`
---
##### `add_row(data, index)`
添加单行数据。
**步骤:**
1. 实例化 `new bricks.Row(this, data)`
2. 将其 `freeze_row``normal_row` 分别加入左右容器
3. 可指定插入位置(`index`
---
##### `clear_data()`
清空所有行数据并重置选中行。
- 清除 `freeze_body``normal_body` 中的控件
- 设置 `selected_row = null`
---
##### `del_old_rows(cnt, direction)`
删除旧的若干行(用于虚拟滚动优化)。
| 参数 | 说明 |
|------|------|
| `cnt` | 删除数量 |
| `direction` | `'up'`(顶部删除)或 `'down'`(底部删除) |
---
##### `loadData(params)`
重新加载数据(通常由外部触发)。
委托给 `this.loader.loadData(params)`
---
##### `miniform_input(event)`
监听迷你表单输入事件,自动调用 `loadData()`
---
##### `command_handle(event)`
工具栏命令处理钩子(需用户自行覆盖实现)。
---
##### `coscroll(event)`
实现左右两列区域的垂直滚动同步。
**原理:**
- 监听任一侧 `VScrollPanel``scroll` 事件
- 同步另一侧的 `scrollTop`
> 保证冻结列与主表体垂直对齐
---
##### `load_previous_data()` / `load_next_data()`
响应滚动到底部/顶部阈值时,加载前一页/后一页数据。
内部调用 `this.loader.previousPage()``nextPage()`
> 通过 `min_threshold` / `max_threshold` 事件绑定触发
---
##### `click_handler(row, event)`
行内控件点击统一处理器。
**行为:**
1. 取消之前选中行的高亮
2. 高亮当前行(调用 `row.selected()`
3. 触发 `row_click` 自定义事件
4. 输出调试日志
> 此函数作为 `bind` 上下文传入每一行控件
---
## 事件系统
| 事件名 | 触发时机 | 传递参数 |
|-------|---------|--------|
| `row_click` | 用户点击某一行 | `bricks.Row` 实例 |
| `input` (from miniform) | 迷你表单值变化 | event 对象 |
| `command` (from toolbar) | 工具栏按钮被点击 | command 名称等 |
---
## 样式类说明CSS Classes
| 类名 | 应用元素 | 用途 |
|------|----------|------|
| `datagrid` | 外层容器 | 主体样式 |
| `datagrid-grid` | 主 `HBox` | 网格布局容器 |
| `datagrid-left` | 冻结区 `VBox` | 左侧固定列容器 |
| `datagrid-right` | 主体区 `VBox` | 右侧可滚动列容器 |
| `grid_header` | 表头 `HBox` | 表头样式 |
| `datagrid-body` | `VScrollPanel` | 表体滚动区域样式 |
| `selected` | 单元格/行 | 高亮选中状态 |
---
## 注册与使用
```js
// 注册组件工厂
bricks.Factory.register('DataGrid', bricks.DataGrid);
```
### 示例用法
```js
var grid = new bricks.DataGrid({
title: "用户列表",
dataurl: "/api/users",
method: "POST",
params: { dept: "sales" },
check: true,
lineno: true,
row_height: 40,
fields: [
{ name: "name", label: "姓名", uitype: "text", freeze: true, width: 120 },
{ name: "age", label: "年龄", uitype: "int", width: 80 },
{ name: "edit", label: "编辑", uitype: "button", icon: "edit", action: { cmd: "edit_user" }, width: 60 }
],
miniform: { /* mini form config */ },
toolbar: { items: [ /* toolbar buttons */ ] }
});
container.add_widget(grid);
```
---
## 调试与日志
使用 `bricks.debug(...)` 输出关键流程日志,例如:
- 列宽计算结果
- 滚动事件触发
- 数据加载过程
可通过浏览器控制台查看。
---
## 依赖说明
本组件依赖以下 `bricks.js` 模块:
- `bricks.VBox`, `bricks.HBox`:布局容器
- `bricks.Text`, `bricks.Button`, `bricks.Title1`:基础控件
- `bricks.MiniForm`, `bricks.Toolbar`:功能控件
- `bricks.VScrollPanel`:滚动面板
- `bricks.BufferedDataLoader`:分页数据加载器
- `bricks.viewFactory`:动态视图工厂
- `objcopy()`:对象深拷贝工具
- `convert2int()`:安全转整数
- `schedule_once()`:延迟执行函数
---
## 设计亮点
**高性能虚拟滚动**
通过 `BufferedDataLoader` + `min_threshold/max_threshold` 实现大数据量流畅浏览。
**灵活 UI 扩展性**
支持任意 `uitype` 字段类型,可通过 `viewFactory` 扩展。
**双列同步滚动**
完美实现冻结列与主体列的垂直同步滚动。
**模块化架构**
`Row` 类独立封装行逻辑,易于维护和定制。
---
## 待改进点(建议)
- ❌ 错误:`self.toolbar` 应为 `this.toolbar`
- 🔧 增加 `destroy()` 方法清理事件监听
- 📈 支持横向滚动同步(如有必要)
- 💡 提供 `getValue()` 获取所有选中行数据的方法
- 🔄 支持列排序、过滤等高级功能扩展
---
## 版本信息
- 编写时间2025年4月
- 作者Bricks Framework 团队
- 文档版本v1.0
---
> 📘 提示:请确保引入完整 `bricks.js` 框架及其依赖资源CSS、图标等以正确渲染组件。

311
docs/cn/datarow.md Normal file
View File

@ -0,0 +1,311 @@
# `bricks.DataRow` 技术文档
> **模块**: `bricks.js`
> **类名**: `bricks.DataRow`
> **继承自**: `bricks.HBox`
---
## 概述
`bricks.DataRow` 是一个用于渲染表格行的 UI 组件通常用于数据浏览器Data Browser或列表视图中。它支持表头行和数据行两种渲染模式能够根据配置自动构建字段、工具栏并处理用户交互。
该组件继承自 `bricks.HBox`,因此具备水平布局能力,适用于构建可扩展的数据展示界面。
---
## 配置选项 (Options)
| 属性 | 类型 | 说明 |
|------|------|------|
| `toolbar` | Object / null | 工具栏配置对象,包含 `tools` 数组,定义操作图标按钮。在表头中将被替换为空白图标。 |
| `fields` | Array | 字段定义数组,每个字段描述一个列的显示方式(如名称、类型等)。 |
| `css` | String / Object | 自定义 CSS 样式,应用于整个 DataRow 容器。 |
| `browserfields` | Object | 浏览器专用配置:<br>- `exclouded`: 排除显示的字段名数组<br>- `cwidth`: 各字段的列宽映射(单位:字符宽度) |
| `editexclouded` | Array | (未使用)预留字段排除列表(当前未实现逻辑)。 |
| `header_css` | String / Object | 表头行的额外 CSS 样式。 |
| `checkField` | String | 可选字段名,用于启用复选框列(例如选择记录)。值会绑定到 `user_data[checkField]`。 |
### 字段定义 (`fields` 中的对象)
| 属性 | 类型 | 说明 |
|------|------|------|
| `name` | String | 字段名,对应数据中的键。 |
| `label` | String | 列标题;若未提供,则使用 `name` 作为标签。 |
| `uitype` | String | 控件类型(如 `'str'`, `'int'`, `'date'` 等),决定如何渲染该字段。 |
| `cwidth` | Number | 列宽(以字符为单位,默认为 10。优先级低于 `browserfields.cwidth`。 |
| `value` | Any | (仅用于表头)默认值或标签文本。 |
| 其他属性 | - | 支持传递给具体 ViewBuilder 的其他参数。 |
---
## 方法
### 构造函数 `constructor(opts)`
初始化 `DataRow` 实例并设置初始状态。
```js
new bricks.DataRow(options);
```
#### 参数:
- `opts` (Object): 配置选项对象,参见上文。
#### 内部初始化:
- 调用父类构造函数。
- 初始化 `this.record_w = null` —— 用于存放字段控件的容器。
---
### `render_header()`
渲染表头行(即列标题)。
```js
row.render_header();
```
内部调用 `this.render(true)`,指示进入“表头”模式。
---
### `render_data()`
渲染数据行(即实际记录内容)。
```js
row.render_data();
```
内部调用 `this.render(false)`,表示渲染真实数据。
---
### `render(header)`
主渲染方法,区分表头与数据行逻辑。
#### 参数:
- `header` (Boolean):是否渲染为表头。
#### 行为:
1. 若存在 `checkField`
- 表头:添加一个空图标(`bricks.BlankIcon`
- 数据行:添加一个可交互的复选框(`bricks.UiCheck`),绑定 `changed` 事件到 `get_check_state`
2. 调用 `build_fields(header)` 渲染所有字段。
---
### `renew(record)`
更新当前行的数据源并重新渲染字段内容。
#### 参数:
- `record` (Object): 新的数据记录对象。
#### 行为:
- 更新 `this.user_data = record`
- 清空原有字段容器 `record_w`
- 重建字段控件
> ⚠️ 注意:不会重建整个 DOM 结构,仅刷新字段内容,性能更优。
---
### `get_check_state(e)`
处理复选框状态变化事件。
#### 参数:
- `e` (Event): 来自 `UiCheck``changed` 事件。
#### 行为:
- 获取新值并同步回 `this.user_data[this.checkField]`
- 触发 `check_changed` 事件,携带自身实例作为参数
```js
this.dispatch('check_changed', this);
```
可用于外部监听选中状态变更。
---
### `build_toolbar(header)`
构建工具栏区域(如操作按钮:编辑、删除等)。
#### 参数:
- `header` (Boolean): 是否为表头模式。
#### 行为:
- 表头模式下,所有工具替换为占位图标(`blankicon`),保持对齐
- 数据行模式下,正常加载工具项
- 创建 `bricks.IconBar` 实例并添加至组件
- 为每个非空白工具绑定事件监听器,通过 `my_dispatch` 转发事件名
> ✅ 支持事件冒泡机制,便于外部监听工具点击事件。
---
### `my_dispatch(e)`
事件转发函数工厂,生成能正确派发事件的方法。
#### 参数:
- `e` (String): 事件名称(如 `'edit'`, `'delete'`
#### 返回:
- Function: 闭包函数,调用 `this.dispatch(e)`,确保上下文正确。
常用于绑定:
```js
w.bind(tools[i].name, this.my_dispatch(tools[i].name));
```
---
### `build_fields(header, cw?)`
创建字段容器并启动字段构建流程。
#### 参数:
- `header` (Boolean): 是否为表头
- `cw` (Widget, 可选): 外部传入的容器,默认新建 `HBox`
#### 行为:
- 创建新的 `HBox` 容器 `record_w`
- 设置样式类 `childrensize`
- 添加进主组件
- 调用 `_build_fields` 执行具体构建
---
### `_build_fields(header, cw)`
私有方法:逐个构建字段控件。
#### 参数:
- `header` (Boolean): 是否为表头
- `cw` (Container Widget): 目标容器
#### 流程:
1. 解析 `browserfields.exclouded``cwidths` 配置
2. 将 `checkField` 加入排除列表(避免重复显示)
3. 遍历 `this.fields`
- 跳过被排除的字段
- 构造字段控件参数 `opts`
- 表头:显示 `label``name`
- 数据行:从 `user_data` 提取值
- 使用 `bricks.get_ViewBuilder(uitype)` 获取对应的视图生成器
- 回退到 `'str'` 类型生成器以防不支持的类型
- 实例化控件并添加到容器
- 添加 CSS 类 `tabular-cell` 保证表格样式一致
---
## 事件系统
`DataRow` 支持以下事件分发:
| 事件名 | 触发时机 | 携带参数 |
|--------|----------|---------|
| `check_changed` | 复选框状态改变时 | 当前行实例 (`this`) |
| `[tool.name]` | 工具栏按钮被点击时(如 `'edit'`, `'delete'` | 由 `IconBar` 触发,可通过 `bind()` 监听 |
示例监听:
```js
row.bind('check_changed', function(sender){
console.log("选中状态变化:", sender.user_data);
});
```
---
## 注册信息
```js
bricks.Factory.register('DataRow', bricks.DataRow);
```
允许通过工厂方式创建实例:
```js
var row = bricks.Factory.create('DataRow', options);
```
---
## 使用示例
### 创建表头行
```js
var headerRow = new bricks.DataRow({
fields: [
{ name: 'id', label: 'ID', uitype: 'int', cwidth: 5 },
{ name: 'name', label: '姓名', uitype: 'str', cwidth: 15 },
{ name: 'age', label: '年龄', uitype: 'int' }
],
checkField: 'selected'
});
headerRow.render_header(); // 渲染列标题 + 复选框占位
container.add_widget(headerRow);
```
### 创建数据行
```js
var dataRow = new bricks.DataRow({
fields: [...],
checkField: 'selected',
browserfields: {
exclouded: ['internal_id'],
cwidths: { name: 20 }
},
toolbar: {
tools: [
{ name: 'edit', icon: 'pencil' },
{ name: 'delete', icon: 'trash' }
]
}
});
dataRow.renew({ id: 1, name: '张三', age: 25, selected: 1 });
container.add_widget(dataRow);
```
---
## 设计特点
- **双模式渲染**:同一组件支持表头与数据行,减少冗余代码。
- **灵活字段控制**:通过 `browserfields.exclouded` 动态隐藏字段。
- **列宽管理**:支持全局与字段级 `cwidth` 控制布局。
- **事件解耦**:通过 `dispatch` 实现松耦合通信。
- **可扩展性**:基于 `ViewBuilder` 插件机制支持多种字段类型。
---
## 注意事项
1. `editexclouded` 字段目前未在代码中使用,可能是遗留字段。
2. `toolbar.tools.forEach(...)` 应注意空指针风险,建议增加判空保护。
3. `_build_fields``cwidths` 键名为 `cwidths`,但配置写的是 `cwidth`,可能存在拼写错误(应统一为 `cwidth`)。
4. 建议在 `renew()` 中判断 `record_w` 是否已存在,避免重复创建。
---
## 版本信息
- **Author**: Bricks Framework Team
- **Version**: 1.0
- **Last Modified**: 2025-04-05
---
✅ 文档完

502
docs/cn/dataviewer.md Normal file
View File

@ -0,0 +1,502 @@
# `bricks.DataViewer` 技术文档
> **版本1.0**
> **继承自:`bricks.VBox`**
> **用途:通用数据展示组件,支持分页加载、滚动加载、工具栏操作与动态表单交互**
---
## 概述
`bricks.DataViewer` 是一个可扩展的数据视图组件,用于在 Web 界面中以可视化方式展示和管理结构化数据。它基于 `bricks.VBox` 容器构建,具备以下核心功能:
- 支持异步分页加载远程数据(通过 `PageDataLoader`
- 垂直滚动区域自动加载上一页/下一页
- 可配置的工具栏(支持增删改查等操作)
- 行选择与事件通知机制
- 内置编辑表单弹窗(新增、更新、克隆、删除)
- 高度可定制化(可通过子类重写关键方法实现自定义渲染)
该组件通常作为列表、表格或卡片式数据展示的基础容器使用。
---
## 组件结构
```js
var bricks = window.bricks || {};
bricks.DataViewer = class extends bricks.VBox { ... }
```
### 注册名称
```js
bricks.Factory.register('DataViewer', bricks.DataViewer);
```
可通过工厂创建:
```js
let viewer = bricks.Factory.build('DataViewer', opts);
```
---
## 构造函数
```js
constructor(opts)
```
### 参数说明
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.data_url` | String | 是 | 数据请求 URL |
| `opts.data_params` | Object | 否 | 请求附加参数 |
| `opts.page_rows` | Number | 否 | 每页行数,默认由 `PageDataLoader` 控制 |
| `opts.data_method` | String | 否 | HTTP 方法(如 `'GET'`, `'POST'`),默认 `'GET'` |
| `opts.cache_limit` | Number | 否 | 缓存页面数量限制 |
| `opts.editable` | Object | 否 | 编辑配置对象(见下文) |
| `opts.toolbar` | Object | 否 | 工具栏自定义项 |
| `opts.row_options` | Object | 否 | 行级选项(字段、排除字段等) |
### 初始化行为
1. 设置默认布局样式:
- 宽高为 `100%`
- 溢出隐藏(`overflow: hidden`
2. 创建 `PageDataLoader` 实例用于数据加载
3. 初始化状态变量(选中行、加载锁、偏移量等)
4. 绑定事件:`row_check_changed`
5. 延迟调用 `build_all()` 进行 UI 构建
---
## 核心属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `loader` | `PageDataLoader` | 负责数据分页加载 |
| `scrollpanel` | `VScrollPanel` | 主内容滚动容器 |
| `filler_widget` | `Filler` | 占位容器,容纳 `scrollpanel` |
| `toolbar_w` | `IconTextBar` | 工具栏组件 |
| `select_row` | Widget | 当前选中的记录行 widget |
| `active_item` | Widget | 当前激活项目(保留字段) |
| `loading` | Boolean | 是否正在加载数据 |
| `data_offset` | Number | 数据起始偏移位置(用于反向插入) |
| `old_params` | Object | 上次请求参数,防止重复加载 |
| `key_select_items` | Array | 支持键盘导航的选择项集合 |
| `check_changed_row` | Object | 最近一次变更的行数据 |
| `keyselectable` | Boolean | 是否允许键盘选择 |
---
## 生命周期方法
### `async build_all()`
主 UI 构建入口,按顺序执行以下步骤:
```js
await this.build_other();
this.scrollpanel.bind('min_threshold', this.load_previous_page.bind(this));
this.scrollpanel.bind('max_threshold', this.load_next_page.bind(this));
await this.render();
this.set_key_select_items();
```
#### 子构建方法:
| 方法 | 功能 |
|------|------|
| `build_title_widget()` | (预留)构建标题区 |
| `build_description_widget()` | (预留)构建描述区 |
| `build_toolbar_widget()` | 构建顶部工具栏 |
| `build_records_area()` | 创建滚动面板用于显示数据行 |
| `build_other()` | 子类可扩展的额外构建逻辑(空实现) |
---
### `async render(params)`
重新加载并渲染数据。
#### 参数
- `params`: 请求参数(合并到原始 `data_params`
#### 流程
1. 若参数未变化 → 返回
2. 使用 `loader.loadData(params)` 获取数据
3. 清空当前内容
4. 执行前置处理 `before_data_handle()`
5. 处理数据 `dataHandle(d)`
> ⚠️ 自动去重:若 `params === old_params` 则跳过。
---
### `async before_data_handle()`
钩子函数,在数据处理前调用。可用于预处理或状态清理。
> 默认为空,供子类覆盖。
---
### `async dataHandle(d)`
处理从 `loader` 返回的数据对象。
#### 输入格式示例
```json
{
"rows": [...],
"add_page": 1,
"delete_page": 2
}
```
#### 行为
- 调用 `renderPageData(rows, add_page)`
- 如果有 `delete_page`,调用 `delete_page(page_num)` 删除旧页
---
## 数据渲染相关
### `build_records_area()`
创建主数据显示区域:
```js
this.filler_widget = new bricks.Filler({});
this.add_widget(this.filler_widget);
this.scrollpanel = new bricks.VScrollPanel({});
this.filler_widget.add_widget(this.scrollpanel);
```
> 使用 `Filler + VScrollPanel` 结构确保布局适应。
---
### `async renderPageData(data, page)`
将一批数据渲染成可视行。
#### 参数
- `data`: 数组,每项是一个数据记录
- `page`: 页面编号
#### 特殊逻辑
- 如果不是最大页(历史页):数据逆序,并从前部插入(维护时间顺序)
- 否则:正常追加至末尾
内部循环调用 `build_row()`
---
### `async build_record_view(record)`
**抽象方法**:生成单条记录的 UI 组件。
#### 默认实现
```js
var w = new bricks.VBox({width: '100px', height:'100px'});
w.set_css('test_box');
return w;
```
> ✅ 必须由子类重写以实现具体展示样式(如表格行、卡片等)
---
### `async build_row(record, page, pos)`
将一条记录添加到 `scrollpanel` 中。
#### 参数
- `record`: 数据对象
- `page`: 所属页码
- `pos`: 插入位置null 表示末尾)
#### 步骤
1. 调用 `build_record_view(record)` 创建 widget
2. 设置属性 `data-page` 便于后续删除
3. 添加到 `scrollpanel` 指定位置
---
## 工具栏与用户交互
### `build_toolbar_widget()`
根据配置生成工具栏按钮。
#### 支持的操作(当 `editable` 存在时)
| 名称 | 图标 | 条件 | 提示 |
|------|------|------|------|
| `add` | `add_icon` 或默认图标 | 总是显示 | 新增记录 |
| `update` | `update_icon` | 需选中行 | 更新选中项 |
| `clone` | `clone_icon` | 需选中行 | 克隆选中项 |
| `delete` | `delete_icon` | 需选中行 | 删除选中项 |
> 图标路径通过 `bricks_resource()` 解析 SVG 资源。
此外,支持合并外部传入的 `toolbar.tools`
最终创建 `IconTextBar` 并绑定命令事件。
---
### `command_event_handle(event)`
处理工具栏点击事件。
#### 分发逻辑
| 命令名 | 行为 |
|--------|------|
| `add` | 调用 `add_record()` |
| `update` | 调用 `update_record(select_row)` |
| `clone` | 调用 `clone_record(select_row)` |
| `delete` | 调用 `delete_record(select_row)` |
| 其他 | 触发全局事件 `dispatch(name, data)` |
> 若操作需要选中行但无选中项,则提示错误。
---
## 编辑功能
### 字段控制
#### `get_edit_fields()`
提取可编辑字段,过滤掉 `editexclouded` 中指定的字段。
> 结果保存在 `this.fields` 数组中。
#### `get_hidefields()`
获取应隐藏提交的字段(来自 `data_params`),转换为 `{name, value, uitype: 'hide'}` 形式。
---
### 表单构建
| 方法 | 功能 |
|------|------|
| `build_add_form()` | 构建“新增”表单 |
| `build_update_form(data)` | 构建“更新”表单(带 id 隐藏域) |
| `build_clone_form(data)` | 构建“克隆”表单(不包含 id |
所有表单均基于 `bricks.Form`,并注入隐藏字段和编辑字段。
---
### 弹窗管理
#### `build_window(icon, title, form)`
创建通用弹窗(`PopupWindow`)封装表单。
##### 配置
- 居中定位 (`archor: "cc"`)
- 可移动、可缩放
- 尺寸:宽 90%,高 70%
- 绑定表单的 `cancel` 事件关闭窗口
---
### 编辑流程
#### `add_record()`
1. 创建新增表单
2. 弹出窗口
3. 监听 `submited` 事件 → 调用 `add_record_finish(win, event)`
#### `add_record_finish(f, event)`
1. 关闭窗口
2. 重新加载数据
3. 解析响应 JSON 并构建反馈组件(如消息提示)
#### `update_record()`
1. 获取当前选中行数据
2. 创建更新表单(含 `id` 隐藏域)
3. 弹窗并监听 `submited``update_record_finish()`
#### `update_record_finish(win, form, event)`
1. 调用 `renew_record_view(form, row)` 更新本地视图
2. 显示服务器返回结果组件
3. 关闭窗口
#### `clone_record()`
`add_record`,但初始值为原记录数据
#### `delete_record(row, record)`
弹出确认对话框(`Conform`),确认后调用 `delete_record_act()`
#### `delete_record_act()`
1. 发送 DELETE 请求POST with body
2. 接收响应并构建反馈组件
3. 若成功(返回 Message 类型),移除对应行并刷新
---
## 滚动加载机制
### `load_previous_page()`
加载前一页数据(向上滚动触底)
#### 流程
1. 检查是否已在加载 → 防抖
2. 显示 loading 指示器(`Running`
3. 调用 `loader.loadPreviousPage()`
4. 成功则调用 `dataHandle(d)`
5. 恢复滚动位置(按 `pos_rate`
6. 隐藏 loading
> 错误被捕获并打印 debug 日志。
---
### `load_next_page()`
加载下一页数据(向下滚动触底)
逻辑类似 `load_previous_page()`,但无需调整滚动位置。
---
## 辅助方法
### `set_key_select_items()`
设置支持键盘导航的元素集合(除去第一个 filler widget
用于后续方向键选择。
---
### `delete_page(page)`
批量删除属于某一页的所有 DOM 元素。
通过 `[data-page="X"]` 查询 selector 获取 widgets 并逐个移除。
---
### `record_check_changed(event)`
处理行内复选框变更事件。
转发事件为 `row_check_changed`,携带 `user_data`
---
### `renew_record_view(form, row)`
用表单最新值更新某行的 `user_data`
```js
row.user_data = { ...row.user_data, ...form._getValue() };
row.renew(data); // 视图刷新
```
> `renew()` 是 widget 的生命周期方法,需子类实现。
---
## 事件系统
| 事件名 | 触发时机 | 参数 |
|-------|---------|------|
| `row_check_changed` | 行内复选框改变 | `{user_data}` |
| `command` | 工具栏按钮点击 | `{name, selected_row}` |
| `submited` | 表单提交成功 | `{params: Response}` |
| `conformed` / `discard` | 删除确认框选择 | —— |
---
## 设计原则与扩展建议
### 可扩展点(推荐子类覆盖)
| 方法 | 用途 |
|------|------|
| `build_other()` | 添加自定义组件 |
| `build_title_widget()` | 自定义标题 |
| `build_record_view(record)` | 自定义行渲染模板 |
| `before_data_handle()` | 数据加载前准备 |
| `renew(record)` in row widget | 行内容更新逻辑 |
---
### 性能优化特性
- 数据缓存与懒加载
- 滚动阈值触发分页
- 请求去重(参数比对)
- 页面级删除释放内存
---
## 使用示例(伪代码)
```js
let viewer = new bricks.DataViewer({
data_url: '/api/users',
data_params: { dept_id: 101 },
page_rows: 20,
editable: {
add_icon: 'imgs/user_add.svg',
new_data_url: '/api/users/create',
update_data_url: '/api/users/update',
delete_data_url: '/api/users/delete'
},
row_options: {
fields: [
{ name: 'name', label: '姓名', uitype: 'text' },
{ name: 'age', label: '年龄', uitype: 'number' }
],
editexclouded: ['created_at']
}
});
// 自定义行渲染
viewer.build_record_view = function(record) {
let w = new bricks.HBox({ width: '100%', padding: 10 });
w.add_widget(new bricks.Label({ text: record.name }));
w.add_widget(new bricks.Label({ text: record.age }));
w.user_data = record;
return w;
};
```
---
## 调试信息
- `bricks.debug_obj = this.scrollpanel;` —— 方便调试滚动容器
- 所有关键操作均有 `bricks.debug()` 输出
- 支持 `bricks.show_error()` 提示用户错误
---
## 依赖组件
| 组件 | 作用 |
|------|------|
| `bricks.VBox` | 布局基类 |
| `bricks.VScrollPanel` | 滚动容器 |
| `bricks.PageDataLoader` | 分页数据加载器 |
| `bricks.IconTextBar` | 工具栏 |
| `bricks.Form` | 表单引擎 |
| `bricks.PopupWindow` | 弹窗容器 |
| `bricks.Conform` | 确认对话框 |
| `bricks.Running` | 加载指示器 |
| `bricks.HttpJson` | JSON 请求客户端 |
| `bricks.widgetBuild` | 动态组件构建 |
---
## 版本历史
| 版本 | 修改内容 |
|------|----------|
| 1.0 | 初始公开文档版本 |
---
> 📝 文档生成时间2025-04-05
> © 2025 Bricks Framework Team

262
docs/cn/docxviewer.md Normal file
View File

@ -0,0 +1,262 @@
# Bricks 文档查看器组件技术文档
本技术文档介绍了基于 `bricks.js` 框架的三种文档文件在线预览组件:`DOCXviewer``EXCELviewer``PDFviewer`。这些组件支持通过 URL 加载并渲染 `.docx``.xlsx/.xls``.pdf` 文件,适用于网页端轻量级文档展示场景。
---
## 🔧 依赖说明
以下第三方库必须在页面中提前加载:
```html
<!-- 必需Mammoth.js - 用于 DOCX 转 HTML -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.2/mammoth.browser.min.js"></script>
<!-- 必需SheetJS (xlsx) - 用于 Excel 解析 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
<!-- 必需PDF.js - 用于 PDF 渲染 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.4.120/pdf.min.js"></script>
```
> ⚠️ 注意:确保 `pdfjsLib` 已全局可用(通常由 PDF.js 提供)。
---
## 📦 组件概览
| 组件名 | 功能描述 | 支持格式 |
|----------------|------------------------------|--------------|
| `DOCXviewer` | 预览 Word 文档 (.docx) | `.docx` |
| `EXCELviewer` | 预览 Excel 表格文件 | `.xlsx`, `.xls` |
| `PDFviewer` | 预览 PDF 文档 | `.pdf` |
所有组件均继承自 `bricks.VBox`,可通过 `bricks.Factory.register()` 注册使用。
---
## 1. ✅ DOCXviewer
`.docx` 文件转换为 HTML 并嵌入页面显示。
### 类定义
```javascript
bricks.DOCXviewer = class extends bricks.VBox
```
### 构造函数
```javascript
constructor(opts)
```
#### 参数
- `opts` `{Object}`: 配置选项对象。
- `url` `{String}`: 指向 `.docx` 文件的远程或本地 URL。
#### 示例
```javascript
const docViewer = new bricks.DOCXviewer({
url: 'https://example.com/document.docx'
});
```
### 方法
#### `async set_url(url)`
从指定 URL 获取 `.docx` 文件,并使用 Mammoth.js 转换为 HTML 显示。
##### 流程
1. 使用 `HttpArrayBuffer` 异步获取文件二进制数据。
2. 调用 `mammoth.convertToHtml()` 将 ArrayBuffer 转为 HTML 字符串。
3. 插入到组件 DOM 容器内。
##### 参数
- `url` `{String}`: 可选,若未传则使用实例的 `this.url`
---
## 2. ✅ EXCELviewer
支持多工作表 Excel 文件的交互式浏览。
### 类定义
```javascript
bricks.EXCELviewer = class extends bricks.VBox
```
### 构造函数
```javascript
constructor(opts)
```
#### 参数
- `opts` `{Object}`:
- `url` `{String}`: Excel 文件地址。
- (自动设置)`height: "100%"`
#### UI 结构
- 上部:横向滚动条式工作表标签栏 (`HBox`)
- 下部:内容区域 (`Filler`) 显示当前选中的 sheet
#### 示例
```javascript
const excelViewer = new bricks.EXCELviewer({
url: 'https://example.com/data.xlsx'
});
```
### 方法
#### `async set_url(url)`
加载 Excel 文件并解析所有工作表名称,生成可点击标签。
##### 步骤
1. 获取文件 ArrayBuffer。
2. 使用 `XLSX.read(data, {type: 'array'})` 解析 workbook。
3. 遍历 `SheetNames` 创建文本按钮,绑定点击事件。
#### `show_sheet_by_name(sheetname, widget)`
切换显示指定工作表的内容。
##### 功能
- 更新选中状态样式(添加 `selected` CSS 类)
- 使用 `XLSX.utils.sheet_to_html()` 生成表格 HTML
- 嵌入 `VScrollPanel` 实现垂直滚动
- 替换内容容器中的子组件
##### 内部变量
- `this.cur_sheetname`: 当前显示的工作表名
- `this.workbook`: 缓存 workbook 对象以供后续访问
---
## 3. ✅ PDFviewer
逐页渲染 PDF 文件为 Canvas 图像。
### 类定义
```javascript
bricks.PDFviewer = class extends bricks.VBox
```
### 构造函数
```javascript
constructor(opts)
```
#### 参数
- `opts` `{Object}`:
- `url` `{String}`: PDF 文件路径
- (自动设置)`width: '100%'`
#### 示例
```javascript
const pdfViewer = new bricks.PDFviewer({
url: 'https://example.com/report.pdf'
});
```
### 方法
#### `async set_url(url)`
加载 PDF 并启动渲染流程。
##### 步骤
1. 使用 `HttpArrayBuffer` 获取二进制数据。
2. 调用 `pdfjsLib.getDocument({ data: ab })` 初始化文档。
3. 遍历每一页,调用 `getPage(i)` 并异步渲染。
#### `add_page_content(page)`
将单个 PDF 页面渲染为 `<canvas>` 元素并插入组件。
##### 渲染参数
- 缩放比例:`scale = 1.5`
- 使用 `page.getViewport()` 计算视口尺寸
- 创建 canvas设置宽高调用 `page.render()` 绘制
##### UI 布局
- 每页后插入一个 `Splitter` 分隔符(逻辑待修复)
- 使用 `JsWidget` 包裹 canvas 实现集成
> ❗注意:原代码中 `i < this.pdf.numPages``i` 未定义,应改为索引判断或移除条件。
##### 修复建议:
```js
// 修改 add_page_content 中的判断逻辑
if (page.pageNumber < this.pdf.numPages) {
const splitter = new bricks.Splitter();
this.add_widget(splitter);
}
```
---
## 🔗 工具函数
### `extractBodyContent(htmlString)`
提取 HTML 字符串中 `<body>...</body>` 标签之间的内容。
#### 参数
- `htmlString` `{String}`: 完整 HTML 字符串
#### 返回值
- `{String|null}`: 匹配到的 body 内容,否则返回 `null`
#### 正则表达式
```js
/<body[^>]*>([\s\S]*?)<\/body>/i
```
> 💡 当前代码中该函数被注释掉,可用于清理 XLSX 输出中的多余头尾信息。
---
## 🧱 组件注册
最后,三个组件通过 `bricks.Factory` 注册,可在模板或动态创建时使用字符串标识符:
```javascript
bricks.Factory.register('DOCXviewer', bricks.DOCXviewer);
bricks.Factory.register('EXCELviewer', bricks.EXCELviewer);
bricks.Factory.register('PDFviewer', bricks.PDFviewer);
```
### 使用示例(工厂模式)
```javascript
const viewer = bricks.Factory.create('PDFviewer', {
url: 'manual.pdf'
});
parentContainer.add_widget(viewer);
```
---
## 🛠️ 已知问题与改进建议
| 问题 | 描述 | 建议 |
|------|------|-------|
| `i` 未定义 | `add_page_content``i < numPages` 报错 | 改用 `page.pageNumber` 判断 |
| 性能问题 | 多页 PDF 同时请求可能导致卡顿 | 添加顺序加载或懒加载机制 |
| 样式控制弱 | 输出 HTML 缺乏定制化样式 | 提供 `customStyle` 回调或封装容器类 |
| 错误处理不足 | `pdfjsLib` catch 仅打印日志 | 提供错误 UI 反馈机制 |
---
## 📎 总结
这三个组件构成了一个轻量级、模块化的文档预览系统,适合集成于 Web 应用前端,实现无需下载即可查看常见办公文档的功能。
### 特性亮点
- 基于标准浏览器 API 与流行解析库
- 支持异步加载和动态渲染
- 组件化设计,易于扩展和复用
- 与 `bricks.js` UI 框架无缝集成
### 推荐用途
- 在线帮助手册预览
- 后台管理系统文档查看
- 教学平台课件展示
---
> 📚 **提示**请确保服务器允许跨域访问文档资源CORS否则 `HttpArrayBuffer.get()` 请求将失败。

399
docs/cn/dynamicaccordion.md Normal file
View File

@ -0,0 +1,399 @@
# `DynamicAccordion` 技术文档
> **模块名称:** `bricks.DynamicAccordion`
> **继承自:** `bricks.VScrollPanel`
> **用途:** 动态可折叠列表组件,支持分页加载、内容动态渲染、编辑操作(增删改)、工具栏扩展等高级功能。
---
## 目录
- [1. 概述](#1-概述)
- [2. 核心类结构](#2-核心类结构)
- [2.1 `bricks.AccordionItem`](#21-bricksaccordionitem)
- [2.2 `bricks.AccordionInfo`](#22-bricksaccordioninfo)
- [2.3 `bricks.DynamicAccordion`](#23-bricksdynamicaccordion)
- [3. 配置选项 (`opts`)](#3-配置选项-opts)
- [4. 生命周期与构建流程](#4-生命周期与构建流程)
- [5. 主要方法说明](#5-主要方法说明)
- [5.1 渲染控制](#51-渲染控制)
- [5.2 内容生成](#52-内容生成)
- [5.3 数据处理与分页](#53-数据处理与分页)
- [5.4 编辑功能](#54-编辑功能)
- [5.5 事件系统](#55-事件系统)
- [6. CSS 类名约定](#6-css-类名约定)
- [7. 使用示例](#7-使用示例)
- [8. 注意事项](#8-注意事项)
---
## 1. 概述
`bricks.DynamicAccordion` 是一个基于 `VScrollPanel` 的高级可折叠面板组件,用于展示大量条目数据。每个条目由标题区域和可展开的内容区组成,支持:
- 分页异步加载数据
- 可配置的记录视图模板(`record_view`
- 展开后动态加载详细内容(`content_view`
- 行内工具栏(支持自定义按钮及编辑操作)
- 增删改查CRUD交互支持
- 条件性内容显示(依赖字段值)
适用于需要高效展示并操作大量结构化数据的场景,如后台管理界面中的日志、订单、用户列表等。
---
## 2. 核心类结构
### 2.1 `bricks.AccordionItem`
```js
class extends bricks.VBox
```
表示单个可折叠项的容器,包含两个子部件:
- `AccordionInfo`:点击触发展开/收起的头部
- `VBox`:隐藏的内容区域(默认不显示)
#### 构造函数
```js
constructor(opts) {
super(opts);
this.set_css('accordion-item');
}
```
#### 特性
- 自动添加 CSS 类 `accordion-item`
- 继承 `VBox` 布局特性(垂直排列)
---
### 2.2 `bricks.AccordionInfo`
```js
class extends bricks.FHBox
```
代表折叠项的标题部分,通常用于显示摘要信息,并绑定点击事件以切换内容可见性。
#### 构造函数
```js
constructor(opts) {
super(opts);
this.set_css('accordion-item-info');
}
```
#### 特性
- 使用 `FHBox` 实现水平布局
- 添加 CSS 类 `accordion-item-info`
- 存储关联的数据记录在 `user_data` 属性中
---
### 2.3 `bricks.DynamicAccordion`
主控件类负责整体逻辑调度、数据加载、UI 构建与交互响应。
```js
class extends bricks.VScrollPanel
```
#### 注册方式
```js
bricks.Factory.register('DynamicAccordion', bricks.DynamicAccordion);
```
允许通过工厂方法创建实例:
```js
bricks.createWidget("DynamicAccordion", {...});
```
---
## 3. 配置选项 (`opts`)
| 参数 | 类型 | 说明 |
|------|------|------|
| `data_url` | String | 获取数据的 API 地址 |
| `data_method` | String (可选) | 请求方法,默认为 `'GET'` |
| `data_params` | Object (可选) | 发送请求时附加的参数 |
| `cache_limit` | Number (可选) | 缓存页数上限 |
| `page_rows` | Number (可选) | 每页返回的行数 |
| `row_cheight` | Number (可选, 默认: `1.5`) | 每行高度倍率(影响布局计算) |
| `record_view` | Widget 描述对象 | 定义每行摘要区域的 UI 结构 |
| `content_rely_on` | String (可选) | 控制是否展开内容的字段名 |
| `content_rely_value` | Any (可选) | 上述字段应匹配的值才能展开内容 |
| `editable` | Object (可选) | 启用编辑模式的相关配置 |
| &nbsp;&nbsp;`.add_icon`, `.update_icon`, `.delete_icon` | String | 图标路径或资源引用 |
| &nbsp;&nbsp;`.form_cheight` | Number | 表单高度系数 |
| &nbsp;&nbsp;`.new_data_url`, `.update_data_url`, `.delete_data_url` | String | 对应操作的提交 URL |
| `fields` | Array\<Field\> | 字段定义数组,用于表单和列头渲染 |
| `record_toolbar` | Object (可选) | 自定义行工具栏按钮配置 |
| `record_toolbar_collapsable` | Boolean (可选) | 工具栏是否可折叠(未完全实现) |
| `header` | Object (可选) | 头部固定行配置(暂未启用) |
| `content_view` | Widget 描述对象 | 展开后显示的详情内容模板 |
| `title` | String (可选) | 页面标题文本i18n 支持) |
| `description` | String (可选) | 页面描述文本 |
> ⚠️ 所有字符串文本均支持国际化(`i18n: true`)。
---
## 4. 生命周期与构建流程
`build_all()` 方法是初始化入口,在构造完成后延迟执行(`schedule_once(..., 0.1)`
```js
async build_all() {
if (this.title) this.build_title();
if (this.description) this.build_description();
await this.build_toolbar();
await this.build_header();
this.container = new bricks.VScrollPanel({});
this.add_widget(new bricks.Filler().add_widget(this.container));
this.container.bind('min_threshold', this.load_previous_page.bind(this));
this.container.bind('max_threshold', this.load_next_page.bind(this));
await this.render();
}
```
### 流程图解:
```
[初始化]
build_title() → 添加标题
build_description() → 添加描述
build_toolbar() → 添加全局工具栏
build_header() → 创建表头(模拟)
创建内部容器 container (VScrollPanel)
绑定滚动触底/触顶事件 → 分页加载
render() → 加载并渲染第一页数据
```
---
## 5. 主要方法说明
### 5.1 渲染控制
#### `render(params)`
根据参数重新加载数据并刷新 UI。
- 若参数未变,则跳过
- 调用 `loader.loadData(params)` 获取数据
- 成功后调用 `dataHandle(d)`
#### `dataHandle(d)`
处理服务器返回的数据包:
```js
{
rows: [...], // 当前页数据
add_page: 2, // 新增页码
delete_page: 1 // 应删除的旧页码(超出缓存限制)
}
```
调用 `renderAccordionItems(data, page)` 渲染新项,并清理过期页。
---
### 5.2 内容生成
#### `build_item(record)`
构建单个可折叠项:
- 创建 `AccordionItem`
- 调用 `build_info(item, record)` 构建标题区
- 创建隐藏的 `content` 区域
- 绑定点击事件:`line_clicked(info, content, record)`
- 返回完整 `item`
`record === null`,则作为“表头”使用。
#### `build_info(item, record)`
生成标题区域 UI
- 使用 `widgetBuild(this.record_view, info, record)` 渲染摘要内容
- 插入 `record_toolbar`(含编辑图标)
- 设置 `info.user_data = record`
- 返回 `AccordionInfo` 实例
当无 `record` 时,用于生成列头标签。
---
### 5.3 数据处理与分页
#### `load_previous_page() / load_next_page()`
滚动接近边界时自动加载前后页:
- 显示加载动画(`Running` 提示)
- 设置 `this.loading = true` 防止重复请求
- 调用 `loader.loadPreviousPage()``loadNextPage()`
- 成功后调用 `dataHandle()` 并恢复滚动位置
- 错误捕获并输出调试日志
#### `delete_page(page)`
移除指定页的所有 DOM 元素:
```js
querySelectorAll('[data-page="X"]')
→ 移除对应 widget
```
确保内存和 DOM 不泄漏。
---
### 5.4 编辑功能
#### 新增记录
- `add_record(info)`:打开内嵌表单
- `add_record_abort()`:取消新增
- `add_record_finish()`:提交成功后刷新列表
#### 更新记录
- `update_record(info, record)`:弹出编辑表单
- `update_cancel()`:取消编辑
- `update_record_finish()`:保存后更新视图或提示
#### 删除记录
- `delete_record(info, record)`:弹出确认框
- `delete_record_act()`:发送删除请求,删除成功则移除该项
#### 新建表单快捷入口
- `build_new_form()`:顶部添加“+”按钮和隐藏表单
- 点击按钮展开/收起新建表单
- 提交后自动调用 `render()` 刷新列表
---
### 5.5 事件系统
#### 内部事件绑定
- `info.bind('click', line_clicked)`:行点击展开内容
- `IconBar` 工具按钮绑定各种事件(`add`, `update`, `delete`
- 自定义工具按钮通过 `fire_event(name, data)` 派发事件
#### 外部可监听事件
| 事件名 | 触发时机 | 参数 |
|--------|----------|------|
| `row_selected` | 用户点击某一行 | `info` widget |
| `conformed` | 删除确认后 | —— |
| `submited` | 表单提交成功 | 响应对象 |
| `[custom_tool_name]` | 自定义工具按钮被点击 | `record` 数据 |
可通过 `bind(event, handler)` 监听。
---
## 6. CSS 类名约定
| 类名 | 作用 |
|------|------|
| `.accordion-item` | 整个折叠项外层容器 |
| `.accordion-item-info` | 折叠项标题区域 |
| `.accordion-item-info-selected` | 当前行被选中状态 |
| `.accordion-item-content` | 内容区域样式基类 |
| `.filler` | 占位填充容器(用于自适应高度) |
建议配合 SCSS 定义主题风格,例如圆角、阴影、过渡动画等。
---
## 7. 使用示例
```js
var accordion = new bricks.DynamicAccordion({
title: "User List",
description: "Manage all registered users.",
data_url: "/api/users",
page_rows: 10,
cache_limit: 5,
fields: [
{ name: "name", label: "Name" },
{ name: "email", label: "Email" },
{ name: "status", label: "Status" }
],
record_view: {
widgettype: "HBox",
options: { cheight: 1.5 },
subwidgets: [
{ widgettype: "Text", options: { otext: "{{name}}" } },
{ widgettype: "Text", options: { otext: "{{email}}", halign: "right" } }
]
},
content_view: {
widgettype: "Form",
options: {
fields: [
{ name: "name", uitype: "text" },
{ name: "email", uitype: "email" },
{ name: "bio", uitype: "textarea" }
]
}
},
editable: {
form_cheight: 8,
new_data_url: "/api/users/create",
update_data_url: "/api/users/update",
delete_data_url: "/api/users/delete",
add_icon: "/static/icons/add.svg"
},
record_toolbar: {
tools: [
{ name: "view_logs", tip: "View operation logs", icon: "/icons/logs.svg" }
]
}
});
// 监听自定义操作
accordion.bind("view_logs", function(record){
console.log("查看日志:", record);
});
```
---
## 8. 注意事项
1. **性能优化**
- 支持分页缓存,避免一次性加载过多数据
- 滚动加载机制减少初始等待时间
- `cache_limit` 应合理设置以防内存占用过高
2. **内容懒加载**
- `content_view` 仅在点击展开时构建,节省资源
- 再次点击会清除内容(`clear_widgets()`),防止状态残留
3. **国际化支持**
- 所有文本建议使用 `otext` + `i18n: true` 配合语言包
4. **DOM 清理**
- 删除页面时主动移除 widgets 和事件监听(依赖框架 GC
5. **扩展性**
- 可通过 `record_toolbar` 添加自定义操作按钮
- `fire_event()` 支持插件式开发
6. **兼容性**
- 依赖 `bricks.PageDataLoader`, `bricks.HttpJson`, `bricks.widgetBuild` 等基础模块
- 需确保这些模块已正确加载
---
✅ **推荐搭配组件:**
- `bricks.Form`:用于编辑表单
- `bricks.Conform`:删除确认对话框
- `bricks.Running`:加载指示器
- `bricks.IconBar`:工具栏按钮组
📦 此组件适合集成于管理系统、数据看板等复杂前端应用中。

173
docs/cn/dynamiccolumn.md Normal file
View File

@ -0,0 +1,173 @@
# `DynamicColumn` 组件技术文档
> **模块路径**: `bricks.DynamicColumn`
> **继承自**: `bricks.Layout`
> **用途**: 一个响应式网格布局组件,根据屏幕尺寸动态调整列数和列宽,适用于桌面与移动端。
---
## 概述
`DynamicColumn` 是 Bricks UI 框架中的一个布局类,用于创建基于 CSS Grid 的**动态列布局**。它能够根据设备类型(移动端或桌面端)、字符单位(`charsize`)以及配置参数自动计算并设置网格的列宽与间距,实现响应式设计。
该组件特别适合用于卡片列表、内容流等需要自适应列数的场景。
---
## 构造函数
```js
new bricks.DynamicColumn(options)
```
### 参数
| 参数名 | 类型 | 必填 | 默认值 | 描述 |
|----------------|----------|------|--------|------|
| `col_cwidth` | Number | 否 | 根据 `col_width` 推导 | 列宽度(以字符单位 `charsize` 为基准。例如20 表示 20 个字符宽度。 |
| `col_width` | Number | 否 | — | 固定列宽度(像素值)。若未提供 `col_cwidth`,则使用此值。 |
| `col_cgap` | Number | 否 | `0.5` | 列之间的间隙(以 `charsize` 为单位)。 |
| `mobile_cols` | Number | 否 | `1` | 在移动设备竖屏模式下强制显示的列数。 |
> ⚠️ 注意:
> - 若未传入 `col_cwidth` 且未传入 `col_width`,则默认 `col_cwidth = 20`
> - 所有参数均可通过构造选项对象传入。
### 示例
```js
const layout = new bricks.DynamicColumn({
col_cwidth: 25, // 每列宽 25ch
col_cgap: 0.8, // 列间距 0.8ch
mobile_cols: 1 // 移动端强制单列
});
```
---
## 方法说明
### `set_column_width()`
重新计算并设置当前容器的 `gridTemplateColumns``gap` 样式属性,实现动态列宽适配。
#### 逻辑流程
1. 获取当前应用的字符大小 `bricks.app.charsize`
2. 计算列间间隙:`gap = charsize × col_cgap`
3. 获取当前视口宽高:`screenWidth()``screenHeight()`
4. 判断是否为移动端竖屏:
- 如果是,则使用 `mobile_cols` 固定列数,并均分可用宽度;
- 否则:
- 优先使用 `col_cwidth` × `charsize` 作为最小列宽;
- 其次回退到 `col_width` 像素值。
5. 设置 CSS Grid 属性:
```css
grid-template-columns: repeat(auto-fill, minmax(cw + "px", 1fr));
gap: gap + "px";
```
#### 触发时机
该方法会在以下事件中被自动调用:
- 组件绑定到父元素时 (`on_parent`)
- 窗口尺寸变化时 (`resize`)
- 字符单位发生变化时 (`charsize` 事件,前提是实例具有 `cwidth` 属性)
---
## 样式行为
| CSS 属性 | 值 | 说明 |
|---------|-----|------|
| `display` | `grid` | 强制启用 CSS Grid 布局 |
| `grid-template-columns` | `repeat(auto-fill, minmax(cw, 1fr))` | 自动填充列,每列至少 `cw` 宽度,最多扩展至 1fr |
| `gap` | 动态计算的像素值 | 列与行之间的间距(由 `col_cgap` × `charsize` 决定) |
> ✅ 提示:使用 `auto-fill` 能确保在空间足够时自动添加新列,无需手动管理断点。
---
## 响应式策略
| 设备环境 | 判断条件 | 行为 |
|----------------|----------------------------------|------|
| 移动端竖屏 | `bricks.is_mobile() && width < height` | 使用 `mobile_cols` 固定列数,宽度均分 |
| 桌面/横屏设备 | 其他情况 | 使用 `col_cwidth``col_width` 作为最小列宽,启用 `minmax` 自动换行 |
---
## 注册信息
```js
bricks.Factory.register('DynamicColumn', bricks.DynamicColumn);
```
可通过工厂方式创建实例:
```js
bricks.Factory.create('DynamicColumn', { ...options });
```
---
## 使用建议
### 推荐搭配
- 配合 `bricks.Container` 或其他 `Layout` 子类作为子项使用。
- 结合 `charsize` 变化监听,实现字体缩放下的布局同步更新。
### 性能提示
- 避免频繁触发 `set_column_width`,组件已对 `resize``charsize` 做了事件绑定优化。
- 如需禁用字符单位监听,可避免设置 `this.cwidth` 属性。
---
## 示例代码
```js
// 创建一个动态列布局,每列约 20 字符宽,间隔 1ch
const dynamicCol = new bricks.DynamicColumn({
col_cwidth: 20,
col_cgap: 1,
mobile_cols: 1
});
// 添加一些内容
['Item 1', 'Item 2', 'Item 3'].forEach(text => {
const item = new bricks.Label({ text });
dynamicCol.append(item);
});
// 插入 DOM
document.body.appendChild(dynamicCol.dom_element);
```
---
## 浏览器兼容性
✅ 支持所有现代浏览器Chrome, Firefox, Safari, Edge
⚠️ 需要支持 CSS Grid Layout Level 1
---
## 版本历史
| 版本 | 修改内容 |
|------|----------|
| 1.0 | 初始实现,支持字符单位驱动的动态列布局 |
---
## 相关类
- [`bricks.Layout`](./Layout.md) - 基础布局类
- [`bricks.app`](../core/app.md) - 应用上下文,提供 `charsize`, `screenWidth()` 等工具
- [`bricks.is_mobile()`](../utils/is_mobile.md) - 设备检测工具函数
---
📝 **维护者**: Bricks UI Team
📅 **最后更新**: 2025-04-05

301
docs/cn/echartsext.md Normal file
View File

@ -0,0 +1,301 @@
# `EchartsExt` 技术文档
> **模块路径**: `bricks.EchartsExt`
> **继承自**: `bricks.VBox`
> **依赖库**: [Apache ECharts](https://echarts.apache.org/)(需全局引入)
---
## 概述
`bricks.EchartsExt` 是一个基于 `ECharts` 的可扩展图表组件,封装在 `bricks.js` 前端框架中。它继承自 `VBox` 容器类,支持动态数据加载、响应式布局以及点击事件处理,适用于展示多维数据的可视化图表(如柱状图、折线图、饼图等)。
该组件支持从本地数据或远程 URL 加载数据,并能自动将“长格式”数据透视为“宽格式”,便于多系列图表渲染。
---
## 初始化选项Constructor Options
```js
new bricks.EchartsExt({
title: String, // 图表标题
description: String, // 图表描述(可选)
data_url: String, // 远程数据接口地址
data_params: Object, // 请求参数(附加到 data_url
method: String, // HTTP 方法,默认 'GET'
user_data: Array, // 本地数据数组(优先级高于 data_url
nameField: String, // 分组字段名(如:类别名称),默认 'name'
valueField: String, // 数值字段名(单系列时使用),默认 'value'
serieField: String, // 系列字段名(用于区分多个数据系列)
valueFields: Array, // 多数值字段数组(若未设置,则根据 serieField 自动提取)
pie_options: Object, // 特定于饼图的配置项(可选)
idField: String // 唯一标识字段,默认 'id'
})
```
> ⚠️ 注意:`user_data``data_url` 二选一。若两者都存在,优先使用 `user_data`
---
## 属性说明
| 属性 | 类型 | 描述 |
|------|------|------|
| `idField` | String | 数据对象唯一标识字段,默认 `'id'` |
| `nameField` | String | 作为 X 轴或分类维度的字段,默认 `'name'` |
| `valueField` | String | 主数值字段,默认 `'value'`(仅单系列有效) |
| `serieField` | String | 区分不同数据系列的字段(例如:“类型”、“年份”) |
| `valueFields` | Array | 所有需要显示的数据系列名称列表(自动从 `serieField` 提取) |
| `user_data` | Array | 当前加载的数据集(原始格式) |
| `data_url` | String | 数据请求地址 |
| `data_params` | Object | 额外请求参数 |
| `holder` | `bricks.Filler` | 内部占位容器,用于承载 ECharts 实例 |
| `chart` | `echarts.ECharts` | ECharts 实例对象 |
| `method` | String | HTTP 请求方法,默认 `'GET'` |
---
## 方法详解
### `constructor(opts)`
初始化图表组件,执行以下操作:
- 设置默认字段;
- 创建标题和描述组件;
- 初始化内部容器 `holder`
- 初始化 ECharts 实例;
- 若存在 `user_data` 则立即渲染;否则尝试通过 `data_url` 异步加载数据;
- 绑定窗口/元素重绘事件以触发图表 resize。
```js
this.bind('element_resize', this.chart.resize.bind(this.chart));
```
---
### `pivotify(data) → Array`
将“长格式”数据转换为“宽格式”数据,便于多系列图表展示。
#### 输入数据示例(长格式):
```json
[
{ "name": "A", "type": "销量", "value": 100 },
{ "name": "A", "type": "利润", "value": 30 },
{ "name": "B", "type": "销量", "value": 150 }
]
```
#### 输出结果(宽格式):
```json
[
{ "name": "A", "销量": 100, "利润": 30 },
{ "name": "B", "销量": 150 }
]
```
#### 处理逻辑:
1. 提取所有唯一的 `serieField` 值作为新字段;
2. 按 `nameField` 排序;
3. 构建以 `nameField` 为键的对象字典;
4. 将每个记录映射到对应行并填充相应系列值;
5. 返回对象值数组并排序。
> ✅ 使用场景:当 `serieField` 存在时,自动启用透视功能。
---
### `get_series(data) → Array`
提取数据中所有唯一的系列名称(基于 `this.serieField`)。
#### 示例:
```js
data = [
{ type: 'sales', value: 100 },
{ type: 'profit', value: 20 },
{ type: 'sales', value: 80 }
]
// get_series(data) → ['sales', 'profit']
```
返回值用于生成图例legend或系列配置。
---
### `render_data()`
根据当前 `user_data` 渲染图表。
#### 步骤:
1. 若存在 `serieField`,则调用 `pivotify()` 转换数据;
2. 初始化新的 ECharts 实例(防止内存泄漏);
3. 调用 `setup_options(data)` 获取图表配置;
4. 补充 `grid` 布局配置;
5. 若有多系列且存在 `legend`,更新其 `data` 字段;
6. 应用配置到 ECharts 实例;
7. 绑定点击事件处理器 `click_handle`
> 🔁 支持热更新:每次调用此方法都会重新绘制图表。
---
### `click_handle(params)`
ECharts 点击事件回调函数。
#### 参数:
- `params`: ECharts 返回的点击事件参数对象
#### 动作:
- 打印调试信息至控制台;
- 触发自定义事件 `'element_click'`,携带原始数据项:
```js
this.dispatch('element_click', this.user_data[params.dataIndex]);
```
> 🎯 可用于外部监听数据项点击行为,实现联动交互。
---
### `async render_urldata(params = {})`
异步从远程 URL 加载数据并渲染图表。
#### 流程:
1. 合并默认参数 `this.data_params` 与传入参数;
2. 使用 `bricks.HttpJson` 发起 HTTP 请求;
3. 成功后保存响应数据至 `this.user_data`
4. 调用 `render_data()` 渲染图表。
#### 示例请求:
```js
await jc.httpcall(this.data_url, {
method: this.method || 'GET',
params: _params
});
```
> ⏱️ 默认延迟 0.1 秒调度,避免构造期间 DOM 未就绪。
---
## 事件Events
| 事件名 | 触发时机 | 携带数据 |
|--------|----------|---------|
| `element_click` | 用户点击图表中的某个数据点 | 对应的原始数据项(来自 `user_data` |
| `element_resize` | 元素尺寸变化时(由父容器触发) | —— |
可通过 `bind()` 监听这些事件:
```js
chart.bind('element_click', function(dataItem) {
console.log('Clicked:', dataItem);
});
```
---
## 数据结构要求
### 本地数据(`user_data`)格式
```json
[
{
"name": "类别A",
"value": 120,
"type": "线上"
},
{
"name": "类别A",
"value": 80,
"type": "线下"
},
...
]
```
- 必须是对象数组;
- 至少包含 `nameField``valueField` 字段;
- 若使用多系列,需提供 `serieField` 字段。
---
## 样式与布局
- 图表容器自动适应父元素大小;
- `grid` 配置留白 3%,确保标签不被裁剪;
- 支持响应式设计,绑定 `element_resize` 事件自动调整图表尺寸。
---
## 使用示例
### 示例 1使用本地数据创建多系列柱状图
```js
var chart = new bricks.EchartsExt({
title: "销售统计",
description: "各区域线上线下销售额对比",
user_data: [
{ name: "北京", type: "线上", value: 200 },
{ name: "北京", type: "线下", value: 150 },
{ name: "上海", type: "线上", value: 180 },
{ name: "上海", type: "线下", value: 170 }
],
nameField: "name",
valueField: "value",
serieField: "type"
});
chart.bind('element_click', function(item) {
alert("点击了:" + item.name);
});
```
### 示例 2从 API 加载数据
```js
var chart = new bricks.EchartsExt({
title: "实时订单量",
data_url: "/api/orders",
data_params: { period: "today" },
nameField: "hour",
valueField: "count",
method: "GET"
});
```
---
## 注意事项
1. **ECharts 必须已全局加载**,否则 `echarts.init` 会报错;
2. `setup_options(data)` 方法需子类实现,用于定义具体图表类型(如 bar、line、pie
3. 若未定义 `serieField`,则不会进行数据透视;
4. `valueFields` 若手动指定,则忽略自动提取逻辑;
5. 建议对大数据集做分页或聚合处理,避免性能问题。
---
## 调试提示
- 查看浏览器控制台输出 `opts=` 可检查最终传递给 ECharts 的配置;
- 确保 `this.holder.dom_element` 已正确挂载且非空;
- 检查网络请求是否成功返回合法 JSON 数据。
---
## 版本信息
- **Bricks Framework**: v1.x+
- **ECharts 支持版本**: ≥ 5.0
- **兼容性**: 支持现代浏览器Chrome / Firefox / Edge / Safari
---
> 💡 提示:建议结合 `bricks.Layout` 系统使用,实现复杂仪表盘布局。

162
docs/cn/factory.md Normal file
View File

@ -0,0 +1,162 @@
# Bricks 工厂模式技术文档
## 概述
`bricks.Factory` 是一个基于单例模式的工厂类,用于注册和获取组件(或“小部件”)对象。它提供了一个全局可访问的注册表,允许动态地注册命名的组件类或对象,并通过名称进行检索。
该实现是 `Bricks` 前端框架/库的一部分,旨在统一管理可复用的 UI 组件或其他功能模块。
---
## 代码结构
```javascript
var bricks = window.bricks || {};
class Factory_ {
constructor() {
this.widgets_kv = new Object();
this.widgets_kv['_t_'] = 1;
}
register(name, widget) {
this.widgets_kv[name] = widget;
}
get(name) {
if (this.widgets_kv.hasOwnProperty(name)) {
return this.widgets_kv[name];
}
return null;
}
}
bricks.Factory = new Factory_();
```
---
## 核心类:`Factory_`
### 构造函数 `constructor()`
初始化一个空的对象 `widgets_kv`,用于存储注册的组件。
#### 初始化内容:
- `this.widgets_kv = new Object()`
创建一个普通对象作为键值对存储容器。
- `this.widgets_kv['_t_'] = 1`
预留字段,可能用于调试、版本标识或检测对象是否已被初始化(用途未明确,但不影响主流程)。
> ⚠️ 注意:使用 `new Object()` 而非 `{}` 仅为风格选择,功能一致。
---
### 方法说明
#### `register(name: string, widget: any): void`
将指定名称与组件(类、函数或对象)注册到工厂中。
##### 参数:
| 参数名 | 类型 | 描述 |
|---------|--------|------|
| `name` | string | 注册的唯一名称(键),用于后续查找 |
| `widget`| any | 要注册的组件,通常为类构造函数或配置对象 |
##### 行为:
- 直接赋值:`this.widgets_kv[name] = widget`
- 若名称已存在,则覆盖旧值
##### 示例:
```js
class MyWidget { /* ... */ }
bricks.Factory.register('my-widget', MyWidget);
```
---
#### `get(name: string): any | null`
根据名称从工厂中获取已注册的组件。
##### 参数:
| 参数名 | 类型 | 描述 |
|---------|--------|------|
| `name` | string | 要查询的组件名称 |
##### 返回值:
- 如果存在该名称的组件,返回对应值;
- 否则返回 `null`
> 使用 `hasOwnProperty` 检查避免原型链污染风险。
##### 示例:
```js
const WidgetClass = bricks.Factory.get('my-widget');
if (WidgetClass) {
const instance = new WidgetClass();
}
```
---
## 全局实例:`bricks.Factory`
通过以下语句暴露为全局单例:
```js
bricks.Factory = new Factory_();
```
确保在整个应用中只有一个 `Factory` 实例,所有模块共享同一注册表。
> 利用 `window.bricks || {}` 确保命名空间安全,防止覆盖已有变量。
---
## 设计模式
- **单例模式Singleton Pattern**
`Factory_` 类仅被实例化一次,全局共享状态。
- **工厂模式Factory Pattern**
提供统一接口来创建对象,解耦对象的使用与创建过程。
---
## 使用场景
适用于需要动态加载或按需获取组件的系统,例如:
- UI 组件库注册与调用
- 插件系统
- 模块化前端架构中的依赖查找
---
## 注意事项
1. **名称冲突**
`register` 不检查重名,后注册者会覆盖前者,请确保命名唯一性。
2. **安全性**
当前未对输入参数做类型校验,建议在生产环境中添加防御性编程措施。
3. **性能**
使用普通对象作为 map在现代 JS 引擎中对于中等规模键集表现良好。若需大规模注册,可考虑改用 `Map`
4. **扩展建议**
- 可增加 `unregister(name)` 方法以支持注销组件
- 可增加 `list()` 方法返回所有注册项列表用于调试
---
## 版本信息
- 创建时间:未知(根据代码推断为早期 JS 模块化方案)
- 兼容性:支持 ES6+ 环境,可在浏览器中直接运行
---
## 总结
`bricks.Factory` 是一个轻量级、高效的组件注册与获取机制,适用于构建模块化、可扩展的前端系统。其简洁的设计使其易于理解和集成,是 `Bricks` 框架基础设施的重要组成部分。

321
docs/cn/floaticonbar.md Normal file
View File

@ -0,0 +1,321 @@
# `bricks.IconBar` 及相关组件技术文档
---
## 概述
`bricks.IconBar` 是一个基于 `bricks.HBox` 的 UI 组件类,用于创建水平排列的图标工具栏。它支持动态图标、点击事件分发、响应式尺寸调整等功能。该模块还包含多个扩展类:
- `bricks.IconTextBar`:带文本标签的图标栏。
- `bricks.FloatIconBar`:浮动式图标栏,鼠标悬停时显示完整图标组。
- `bricks.FloatIconTextBar`:结合浮动与图文混合的增强型工具栏。
这些组件常用于构建轻量级、可定制的工具栏或操作面板。
---
## 基础结构
```js
var bricks = window.bricks || {};
```
确保 `bricks` 全局命名空间存在,避免覆盖已有内容。
---
## 1. `bricks.IconBar`
### 类继承关系
```js
class extends bricks.HBox
```
继承自 `HBox`(水平布局容器),实现横向排列子控件。
### 配置参数 (`opts`)
| 参数名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `cheight` | Number | `2` | 子项高度倍率(相对于字符单位) |
| `rate` | Number | `1` | 缩放比例因子 |
| `margin` | String | `'10px'` | 图标之间的左右边距 |
| `tools` | Array | `[]` | 工具定义数组,每个元素为一个工具描述对象 |
#### `tools` 数组项结构
| 属性 | 类型 | 是否必需 | 说明 |
|------|------|----------|------|
| `name` | String | 否 | 图标的唯一标识名称(用于事件分发和 DOM ID |
| `icon` | String | 是(非 blankicon | SVG 图标 URL 或资源路径 |
| `rate` | Number | 否 | 单独设置该图标的缩放比例(优先于全局 rate |
| `dynsize` | Boolean | 否 | 是否启用动态尺寸调整 |
| `tip` | String | 否 | 提示文本(暂未在 IconBar 中直接使用) |
> 特殊情况:若 `name === 'blankicon'`,则生成空白占位符图标。
### 构造函数行为
```js
constructor(opts)
```
1. **默认值填充**
- 若未指定 `cheight`,默认设为 `2`
- 若未指定 `rate`,默认设为 `1`
2. **调用父类构造器**
```js
super(opts);
```
3. **样式设置**
- 添加 CSS 类名 `'childrensize'`
- 设置主轴对齐方式为居中(`alignItems: center`
4. **遍历 `tools` 创建子控件**
- 复制每个工具配置并补充通用属性(如 `cheight`, `cwidth`, `dynsize`
- 调用 `build_item(opts)` 创建具体控件
- 设置左右 margin 和 cursor 样式为 pointer
- 若有 `name`,设置对应 widget 的 ID
- 绑定点击事件并通过 `regen_event` 分发事件
- 将控件加入容器
5. **内部高度记录**
- 使用 `this.height_int` 记录所有子控件中的最大高度(像素值),便于外部布局参考。
---
### 方法详解
#### `build_item(opts) → Widget`
根据配置创建单个图标控件。
##### 行为逻辑:
- 计算实际高度:`h = bricks.app.charsize * rate * opts.cheight`
- 更新 `this.height_int` 以保持最大高度
- 判断是否为 `blankicon`
- 是 → 返回 `new bricks.BlankIcon({...})`
- 否 → 使用 `opts.icon` 作为 `url` 创建 `bricks.Svg` 实例
- 绑定点击事件到 `regen_event(desc, event)`
- 返回创建的控件
#### `regen_event(desc, event)`
处理图标点击事件,并触发两个事件:
```js
this.dispatch(desc.name, desc); // 以 name 为事件类型分发
this.dispatch('command', desc); // 统一命令事件
```
同时阻止事件冒泡和默认行为:
```js
event.preventDefault();
event.stopPropagation();
```
> 此设计适用于菜单/按钮点击后不希望影响页面其他部分的操作场景。
---
## 2. `bricks.IconTextBar`(图文混合图标栏)
### 类继承关系
```js
class extends bricks.IconBar
```
重写了 `build_item` 方法以支持图标+文字组合。
### `build_item(opts)` 实现细节
返回一个 `HBox` 容器,内含:
1. **SVG 图标(可选)**
- 来源:`opts.icon`
- 属性包括 `rate`, `dynsize`
2. **文本标签(可选)**
- 使用 `bricks.Text` 显示 `opts.label`
- 支持国际化(`i18n: true`)、自动换行(`wrap: true`)、左对齐(`haligh: 'left'`
3. **整体包装**
- 包裹在一个 `HBox` 中,宽度自适应,高度自动计算
- 绑定点击事件至 `regen_event(opts)`
- 更新 `height_int`(基于 `charsize * rate`
> ⚠️ 注意:图标与文本共存时垂直居中对齐由外层 HBox 控制。
---
## 3. `bricks.FloatIconBar`(浮动图标栏)
### 类继承关系
```js
class extends bricks.HBox
```
实现“仅显示浮出按钮,悬停时展开全部图标”的交互模式。
### 配置参数 (`opts`)
`IconBar`,但只显示一个小浮点按钮,其余图标隐藏。
### 构造函数逻辑
1. 创建浮出按钮:
```js
this.float_w = new bricks.Svg({ url: bricks_resource('imgs/float-out.png') });
```
- 绑定 `mousemove` 事件显示图标栏
- 添加到容器
2. 创建图标主体:
```js
this.icons_box = this.build_bar(opts);
```
- 默认调用 `new bricks.IconBar(opts)`
- 初始隐藏(`display: none`
3. 设置容器高度等于 `icons_box.height_int`
4. 绑定全局点击事件隐藏图标栏(通过 `Body.click`
### 方法说明
| 方法 | 功能 |
|------|------|
| `build_bar(opts)` | 工厂方法,创建内部图标栏实例(可被子类重写) |
| `show_icons()` | 显示图标栏(`display: flex` |
| `hide_icons()` | 隐藏图标栏(`display: none` |
> 📌 交互逻辑:鼠标移上浮点按钮 → 显示图标;点击任意空白区域 → 隐藏图标。
---
## 4. `bricks.FloatIconTextBar`(浮动图文栏)
### 类继承关系
```js
class extends bricks.FloatIconBar
```
扩展 `FloatIconBar`,使其内部使用 `IconTextBar` 替代 `IconBar`
### 核心重写方法
```js
build_bar(opts) {
return new bricks.IconTextBar(opts);
}
```
> ✅ 效果:浮出后显示带文字说明的图标按钮组,适合需要语义化提示的场景。
---
## 工厂注册
以下语句将各组件注册到 `bricks.Factory`,支持通过字符串名称动态创建实例:
```js
bricks.Factory.register('IconBar', bricks.IconBar);
bricks.Factory.register('IconTextBar', bricks.IconTextBar);
bricks.Factory.register('FloatIconBar', bricks.FloatIconBar);
bricks.Factory.register('FloatIconTextBar', bricks.FloatIconTextBar);
```
### 使用示例(工厂方式)
```js
var bar = bricks.Factory.create('IconTextBar', {
tools: [
{ name: 'save', icon: '/icons/save.svg', label: 'Save File' },
{ name: 'print', icon: '/icons/print.svg', label: 'Print' }
],
margin: '8px',
rate: 1.2
});
```
---
## 示例配置
### 简单图标栏
```js
new bricks.IconBar({
cheight: 2,
rate: 1,
margin: '12px',
tools: [
{ name: 'home', icon: '/icons/home.svg', tip: 'Go Home' },
{ name: 'settings', icon: '/icons/settings.svg' },
{ name: 'blankicon' }, // 占位符
{ name: 'logout', icon: '/icons/logout.svg' }
]
});
```
### 图文混合栏
```js
new bricks.IconTextBar({
rate: 1.1,
tools: [
{ name: 'new', icon: '/icons/new.svg', label: 'New', tip: 'Create new file' },
{ name: 'open', icon: '/icons/open.svg', label: 'Open' },
{ name: 'help', label: 'Help', icon: '/icons/help.svg' }
]
});
```
### 浮动图文工具栏
```js
new bricks.FloatIconTextBar({
tools: [
{ name: 'cut', icon: '/icons/cut.svg', label: 'Cut' },
{ name: 'copy', icon: '/icons/copy.svg', label: 'Copy' },
{ name: 'paste', icon: '/icons/paste.svg', label: 'Paste' }
],
rate: 1
});
```
---
## 设计特点总结
| 特性 | 描述 |
|------|------|
| 🔧 **模块化设计** | 所有功能拆分为可复用类,便于扩展 |
| 🖱️ **交互友好** | 支持点击、悬停展开、事件拦截 |
| 📏 **响应式尺寸** | 基于 `charsize``rate` 实现相对缩放 |
| 🔔 **事件驱动** | 通过 `dispatch` 发送命名事件,解耦 UI 与业务逻辑 |
| 🧩 **工厂模式支持** | 可通过字符串动态创建组件 |
---
## 注意事项
1. **依赖前提**
- 必须已加载 `bricks.HBox`, `bricks.Svg`, `bricks.Text`, `bricks.BlankIcon`, `bricks.Body`
- `bricks.app.charsize` 需已初始化
- `bricks_resource()` 函数需可用(用于资源路径解析)
2. **性能建议**
- 工具数量较多时建议使用 `FloatIconBar` 减少视觉干扰
- 避免频繁重建整个 `IconBar`,应缓存实例
3. **样式定制**
- 推荐通过 `set_style()` 或外部 CSS 修改外观
- 不推荐直接修改内部 DOM 结构
---
## 版本信息
- **作者**Bricks Framework Team
- **版本**1.0
- **最后更新**2025年4月5日
---
📌 **文档结束**

353
docs/cn/form.md Normal file
View File

@ -0,0 +1,353 @@
# Bricks.js 表单模块技术文档
> 本文档描述了 `bricks` 框架中表单相关组件的实现逻辑、结构与使用方式。该模块提供了灵活的表单构建能力,支持动态字段渲染、数据校验、提交处理及工具栏集成。
---
## 目录
- [概述](#概述)
- [核心变量](#核心变量)
- [核心方法](#核心方法)
- [类说明](#类说明)
- [`FieldGroup`](#fieldgroup)
- [`FormBody`](#formbody)
- [`FormBase`](#formbase)
- [`InlineForm`](#inlineform)
- [`Form`](#form)
- [工厂注册](#工厂注册)
- [使用示例](#使用示例)
---
## 概述
本模块为 `bricks.js` 提供了一套完整的表单系统,包含以下功能:
- 动态生成表单字段(支持嵌套分组)
- 支持文件上传(需使用 `FormData`
- 自动化工具栏(提交、重置、取消按钮)
- 数据获取与验证
- 提交前后事件分发
- 多布局支持(水平/垂直输入框)
主要适用于 Web 端复杂表单场景,如配置页、编辑器、数据录入等。
---
## 核心变量
### `bricks.need_formdata_fields`
```js
bricks.need_formdata_fields = ['file', 'video', 'audio'];
```
**说明:**
指定哪些字段类型需要通过 `FormData` 提交(例如文件上传类字段)。这些字段将触发表单启用 `multipart/form-data` 编码模式。
---
## 核心方法
### `show_resp_message_or_error(resp)`
```js
bricks.show_resp_message_or_error = async function(resp){...}
```
**参数:**
- `resp`: `Response` 对象(来自 `fetch` 请求)
**功能:**
1. 解析响应体为 JSON。
2. 调用 `widgetBuild` 将返回的 UI 描述渲染到页面主体中。
**用途:**
用于处理服务端返回的动态 UI 或错误信息并展示。
---
## 类说明
### `FieldGroup`
负责根据字段定义递归构建表单控件。
#### 构造函数
```js
new FieldGroup(opts)
```
- `opts`: 配置对象(目前未实际使用)
#### 方法:`build_fields(form, parent, fields)`
| 参数 | 类型 | 说明 |
|----------|------------|------|
| `form` | Form 实例 | 当前所属表单对象 |
| `parent` | Widget | 容器组件(如 VBox/HBox |
| `fields` | Array | 字段数组 |
**行为说明:**
- 使用 `DynamicColumn` 实现响应式列布局(移动端默认两列)。
- 若遇到 `uitype: 'group'` 的字段,则递归构建其内部字段,并插入新列。
- 非隐藏字段创建 `VBox``HBox` 包裹标签和输入控件。
- 所有输入控件由 `Input.factory(field)` 创建,并存入 `form.name_inputs`
- 若字段类型在 `need_formdata_fields` 中,设置 `form.need_formdata = true`
---
### `FormBody`
继承自 `VScrollPanel`,是表单内容区域的核心容器。
#### 构造函数
```js
new FormBody(form, opts)
```
**初始化操作:**
- 设置宽高为 100%
- 初始化 `name_inputs` 映射
- 创建 `FieldGroup` 渲染非文本字段
- 调用 `build_text_fields()` 渲染文本字段
#### 方法:`build_text_fields()`
遍历 `form.textfields`,对每个字段:
- 创建标签文字 (`Text`)
- 创建只读文本控件 (`UiText`)
- 包装成 `VBox` 并添加至容器
- 注册到 `form.name_inputs` 中以便后续取值
> **注意:** `text` 类型字段不参与编辑,仅用于显示。
---
### `FormBase`
表单基类,提供通用功能(继承自 `Layout`)。
#### 属性
| 属性名 | 类型 | 说明 |
|-------------------|--------|------|
| `name_inputs` | Object | 存储字段名 → 控件实例映射 |
| `submit_changed` | Boolean| 是否仅提交变更字段 |
| `origin_data` | Object | 初始数据快照(用于比对变化) |
| `submit_url` | String | 提交地址 |
| `method` | String | HTTP 方法,默认 `'POST'` |
| `toolbar` | Object | 工具栏配置扩展 |
#### 方法
##### `build_toolbar(widget)`
动态构建顶部工具栏(含“提交”、“重置”、“取消”按钮),支持自定义扩展。
- 默认图标路径由 `bricks_resource()` 加载。
- 使用 `IconTextBar` 组件呈现按钮条。
- 绑定 `command` 事件到 `command_handle`
##### `command_handle(event)`
处理工具栏命令事件:
| 命令名 | 行为 |
|-----------|------|
| `submit` | 触发 `validation()` |
| `reset` | 调用 `reset_data()` |
| `cancel` | 触发 `dispatch('cancel')` |
| 其他 | 若有 `action` 则执行事件处理器;否则直接 `dispatch(name)` |
##### `reset_data()`
重置所有输入控件为其初始值(调用各控件的 `reset()` 方法)。
##### `_getValue()`
收集所有字段值,进行必填校验:
- 忽略无权访问的属性(`hasOwnProperty`
- 调用 `getValue()` 获取控件值
- 必填项为空时弹出错误提示并聚焦
- 返回合并后的数据对象
> ⚠️ 若校验失败则中断并返回 `undefined`
##### `getValue()`
优先返回缓存的 `this.data`,否则调用 `get_formdata()`
##### `get_formdata()`
构建 `FormData` 实例:
- 遍历所有控件,调用 `set_formdata(data)` 写入数据
- 若启用了 `submit_changed`,跳过未修改字段
- 忽略值为 `null` 的字段
- 返回 `FormData``null`(无变更)
##### `validation()`
异步提交流程控制:
1. 显示加载动画(`Running` 组件)
2. 获取 `FormData`
3. 分发 `submit` 事件
4. 若设置了 `submit_url`,发起 HTTP 请求
5. 成功后分发 `submited` 事件,传入响应对象
6. 出错时捕获异常并输出日志
7. 隐藏加载动画
##### `save_origin_data()`
保存当前所有字段值作为原始状态,用于后续变更判断。
---
### `InlineForm`
轻量级内联表单,适合嵌入其他组件内部。
#### 特性:
- 不包含标题或描述
- 直接渲染字段 + 工具栏
- 自动保存初始数据
#### 构造函数
```js
new InlineForm(opts)
```
**行为:**
- 设置宽高 100%,开启滚动
- 调用 `FieldGroup.build_fields()` 渲染 `opts.fields`
- 添加工具栏到第一个子容器
- 保存初始数据
---
### `Form`
完整表单组件,具备标题、描述、分栏、工具栏等功能。
#### 配置选项(`opts`
| 参数 | 类型 | 说明 |
|----------------|----------|------|
| `title` | String | 表单标题(可国际化) |
| `description` | String | 表单描述文本 |
| `notoolbar` | Boolean | 是否隐藏工具栏,默认 `false` |
| `input_layout` | String | 输入框布局 `"VBox"`(默认)或 `"HBox"` |
| `fields` | Array | 字段定义数组 |
| `submit_url` | String | 提交 URL |
| `method` | String | HTTP 方法GET/POST |
| `toolbar` | Object | 自定义工具栏配置 |
#### 构造流程
1. 设置尺寸与滚动
2. 添加标题(若有)
3. 添加描述(若有)
4. 添加填充容器 `Filler`
5. 分离 `text` 与其他字段
6. 创建 `FormBody` 渲染主体内容
7. 条件性构建工具栏
8. 保存初始数据
---
## 工厂注册
```js
bricks.Factory.register('InlineForm', bricks.InlineForm);
bricks.Factory.register('Form', bricks.Form);
```
允许通过字符串名称动态创建组件实例,便于模板化渲染。
---
## 使用示例
### 定义一个简单表单
```js
var form = new bricks.Form({
title: "User Profile",
description: "Please fill in your information.",
submit_url: "/api/user/update",
method: "POST",
input_layout: "VBox",
fields: [
{ name: "username", label: "Username", uitype: "text_input", required: true },
{ name: "email", label: "Email", uitype: "email", required: true },
{ name: "avatar", label: "Avatar", uitype: "file" },
{ name: "bio", label: "Bio", uitype: "textarea" },
{
uitype: "group",
fields: [
{ name: "phone", label: "Phone", uitype: "tel" },
{ name: "age", label: "Age", uitype: "number" }
]
}
]
});
// 监听提交事件
form.bind('submit', function(data){
console.log("Submitting:", data);
});
form.bind('submited', function(resp){
alert("Saved successfully!");
});
```
### 内联表单示例
```js
var inline = new bricks.InlineForm({
fields: [
{ name: "name", label: "Name", uitype: "text_input" },
{ name: "status", label: "Status", uitype: "select", options: [...] }
],
toolbar: {
tools: [
{ icon: "...", name: "custom", label: "Custom Action", action: "doSomething" }
]
}
});
inline.bind('command:custom', function(){ ... });
```
---
## 附录:字段定义格式
```json
[
{
"name": "field_name",
"label": "Display Label",
"uitype": "text_input|email|file|group|hide|...",
"required": true,
"value": "default_value",
"removable": false,
"icon": "optional_icon_path"
}
]
```
其中:
- `group`: 可嵌套子字段
- `text`: 仅展示,不可编辑
- `hide`: 隐藏字段(但仍会渲染占位)
---
## 注意事项
- 文件上传字段必须使用 `FormData`,确保 `submit_url` 接口支持 `multipart/form-data`
- 所有文本标签建议启用 `i18n: true` 实现多语言支持。
- 自定义控件需实现 `getValue()``set_formdata(formData)` 接口。
- 错误提示依赖 `bricks.Error` 组件,请确保已加载。
---
> 📚 更多组件参考请查阅 [Bricks.js 官方文档](https://github.com/your-bricks-project)。

283
docs/cn/gobang.md Normal file
View File

@ -0,0 +1,283 @@
# `bricks.Gobang` 技术文档
> **五子棋游戏组件** —— 基于 `bricks.js` 框架实现的可交互五子棋界面控件。
---
## 目录
- [概述](#概述)
- [类结构](#类结构)
- [`bricks.GobangPoint`](#bricksgobangpoint)
- [`bricks.Gobang`](#bricksgobang)
- [API 参考](#api-参考)
- [`GobangPoint` 构造函数与方法](#gobangpoint-构造函数与方法)
- [`Gobang` 构造函数与方法](#gobang-构造函数与方法)
- [事件绑定](#事件绑定)
- [图像资源管理](#图像资源管理)
- [布局与自适应](#布局与自适应)
- [使用示例](#使用示例)
---
## 概述
`bricks.Gobang` 是一个基于 `bricks.js` UI 框架开发的五子棋Gomoku游戏界面组件。它包含两个核心类
- `bricks.GobangPoint`:表示棋盘上的单个格子,用于显示空、黑子或白子状态,并支持鼠标悬停反馈。
- `bricks.Gobang`:主控件,负责构建 15×15 的棋盘布局、管理棋子状态、响应尺寸变化并为后续 AI/玩家逻辑提供接口。
该组件具备良好的可扩展性,可用于人机对战、双人对战或多智能体博弈系统。
---
## 类结构
### `bricks.GobangPoint`
继承自 `bricks.Image`,代表棋盘上一个可交互的点位。
#### 属性说明
| 属性名 | 类型 | 描述 |
|-----------|--------|------|
| `p_status` | Number | 棋子状态:`0`=空, `1`=黑子, `2`=白子 |
| `p_x` | Number | 横向坐标(列),取值范围 `115` |
| `p_y` | Number | 纵向坐标(行),取值范围 `115` |
> 注:坐标系以左上角为 `(1,1)`,右下角为 `(15,15)`
#### 构造函数
```js
new bricks.GobangPoint({
p_status: 0,
p_x: x,
p_y: y,
tip: '(x,y)'
})
```
##### 参数
| 参数 | 类型 | 必填 | 说明 |
|----------|--------|------|------|
| `p_status` | Number | 是 | 初始状态 |
| `p_x` | Number | 是 | X 坐标 |
| `p_y` | Number | 是 | Y 坐标 |
| `tip` | String | 否 | 鼠标提示文本,默认为 `(x,y)` |
#### 方法
##### `.calc_url()`
根据当前 `p_status`, `p_x`, `p_y` 计算对应的图片路径。
- 边缘位置使用特定前缀:
- 上边:`t`
- 下边:`b`
- 左边:`l`
- 右边:`r`
- 中间区域:`c`
- 状态后缀:
- `empty`: 空格
- `black`: 黑子
- `white`: 白子
**输出格式:**
```
imgs/[one][two]_[state].png
```
例如:`imgs/tl_empty.png`, `imgs/cc_black.png`
> 使用 `bricks_resource(name)` 获取实际资源 URL。
##### `.set_current_position(flg, event)`
处理鼠标进入/离开事件,设置 CSS 类控制视觉高亮。
- `flg === true` → 移除 `curpos` 样式类(显示高亮)
- `flg === false` → 添加 `curpos` 类(取消高亮)
> 实际行为依赖于样式表中 `.curpos` 的定义。
##### `.getValue()`
返回当前点的状态对象。
```js
{
status: this.p_status,
x: this.p_x,
y: this.p_y
}
```
##### `.str()`
调试用字符串输出,格式为 `(status,x,y)`
---
### `bricks.Gobang`
主游戏容器,继承自 `bricks.VBox`,自动创建 15×15 棋盘。
#### 构造函数
```js
new bricks.Gobang(opts)
```
初始化棋盘,渲染空白格子,并开始第一轮回合(默认黑方先行)。
#### 内部属性
| 属性 | 类型 | 说明 |
|----------|----------|------|
| `filler` | Filler | 占位容器,用于监听尺寸变化 |
| `area` | Array[Array[GobangPoint]] | 二维数组,存储所有 `GobangPoint` 实例 |
| `player` | Object | (预留)玩家配置信息 |
##### 玩家配置结构(未来扩展)
```js
{
black_player: {
name: "Player1",
type: "user", // 或 "llm"
url: "", // AI 接口地址
delay: 1, // 响应延迟(秒)
params: {} // 自定义参数
},
white_player: { ... }
}
```
#### 方法
##### `.render_empty_area()`
创建 15×15 的棋盘网格:
- 创建外层垂直盒子 (`VBox`)
- 每行新建一个水平盒子 (`HBox`)
- 在每个格子中添加 `GobangPoint(p_status=0)`
- 将其组织成二维数组 `this.area[i][j]`,其中 `i = p_y - 1`, `j = p_x - 1`
最终将整个棋盘加入 `this.filler` 容器。
##### `.resize_area()`
响应容器大小变化,动态调整每个棋盘点的宽高,保持正方形且填满可用空间。
- 计算最小边长:`Math.min(clientWidth, clientHeight)`
- 单元格尺寸:`size = min_side / 15`
- 遍历 `this.area` 设置每个 `GobangPoint` 的样式宽高
> 绑定在 `filler``element_resize` 事件上。
##### `.inform_go(party)`
通知某一方落子(如 `'black'``'white'`)。此方法目前为空,作为钩子供子类或外部逻辑重写。
典型用途:
- 触发 AI 思考
- 更新 UI 提示
- 发送网络请求
---
## 事件绑定
| 元素 | 事件 | 回调 | 说明 |
|------|------|-------|------|
| `GobangPoint` | `mouseover` | `set_current_position(true)` | 显示悬停效果 |
| `GobangPoint` | `mouseout` | `set_current_position(false)` | 隐藏悬停效果 |
| `filler` | `element_resize` | `resize_area()` | 自动适配棋盘尺寸 |
---
## 图像资源管理
通过 `bricks_resource(name)` 函数加载图像资源。建议准备以下命名规则的 PNG 图片:
```
imgs/
├── tl_empty.png ── 左上角空格
├── tc_empty.png ── 上边缘中间空格
├── tr_empty.png ── 右上角空格
├── cl_empty.png ── 左边缘中间空格
├── cc_empty.png ── 中央空格
├── cr_empty.png ── 右边缘中间空格
├── bl_empty.png ── 左下角空格
├── bc_empty.png ── 下边缘中间空格
├── br_empty.png ── 右下角空格
├── **_black.png ── 所有位置的黑子版本
└── **_white.png ── 所有位置的白子版本
```
> 共需 9 种位置 × 3 种状态 = 27 张图(若四角合并可减少)。
---
## 布局与自适应
- 使用 `bricks.Filler` 包裹棋盘以监听 DOM 尺寸变化。
- `resize_area()` 方法确保棋盘始终居中并等比缩放。
- 所有点位统一设为正方形,避免拉伸失真。
- 支持响应式设计,在不同屏幕尺寸下正常显示。
---
## 使用示例
```js
// 创建五子棋组件并插入页面
var gobang = new bricks.Gobang({});
document.body.appendChild(gobang.dom_element);
// (可选)手动更改某个点状态
var point = gobang.area[7][7]; // 中心点
point.p_status = 1;
point.set_url(point.calc_url()); // 重新计算图像
```
> 若需集成 AI 对战,请扩展 `inform_go` 方法以调用远程服务或本地模型。
---
## 注册与框架集成
```js
bricks.Factory.register('Gobang', bricks.Gobang);
```
允许通过工厂模式动态创建实例:
```js
var game = bricks.Factory.create('Gobang', {});
```
---
## 待完善功能TODO
- ✅ 棋盘渲染与自适应
- ✅ 棋子状态切换
- ⬜ 落子逻辑检测(是否已占用)
- ⬜ 胜负判断(五连珠算法)
- ⬜ 回合控制与玩家切换
- ⬜ AI 对手集成LLM 或规则引擎)
- ⬜ 悔棋、重新开始等功能按钮
---
## 版权与许可
© 2025 bricks.js 项目组
本模块遵循 MIT 许可协议,欢迎自由使用与修改。

123
docs/cn/html.md Normal file
View File

@ -0,0 +1,123 @@
# `bricks.Html` 组件技术文档
## 概述
`bricks.Html` 是 Bricks 框架中的一个基础 UI 组件,用于将原始 HTML 内容动态插入到 DOM 中。它继承自 `bricks.JsWidget`,是构建更复杂组件或直接渲染静态/动态 HTML 内容的轻量级工具。
---
## 类定义
```javascript
bricks.Html = class extends bricks.JsWidget
```
- **继承自**: `bricks.JsWidget`
- **用途**: 渲染传入的 HTML 字符串到组件的根 DOM 元素中。
- **注册名称**: `'Html'`(通过 `bricks.Factory` 注册)
---
## 构造函数
### `constructor(opts)`
初始化 `Html` 组件实例。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 配置选项对象,继承自 `JsWidget` 并扩展以下字段。 |
#### `opts` 属性
| 属性名 | 类型 | 必填 | 说明 |
|--------|--------|------|------|
| `html` | String | 是 | 要插入到组件内部的 HTML 字符串内容。该内容将直接设置为 `innerHTML`。 |
#### 示例
```javascript
const htmlWidget = new bricks.Html({
html: '<div class="info">这是一段 <strong>HTML</strong> 内容</div>'
});
```
---
## 功能说明
- 在构造函数中调用 `super(opts)` 初始化父类 `JsWidget` 的基本功能(如 DOM 创建、事件绑定等)。
- 使用 `this.dom_element.innerHTML = opts.html` 将传入的 HTML 字符串注入组件的根元素。
> ⚠️ **安全提示**:由于使用了 `innerHTML`,若 `html` 字段包含用户输入,可能存在 XSS 安全风险。建议在使用前对 HTML 内容进行过滤或转义。
---
## 工厂注册
```javascript
bricks.Factory.register('Html', bricks.Html);
```
- 将 `Html` 组件注册到 Bricks 的工厂系统中,允许通过配置方式(如 JSON 描述)创建该组件。
- 注册名称为 `'Html'`,可在模板或布局定义中使用。
#### 工厂创建示例(假设支持 JSON 配置)
```json
{
"type": "Html",
"html": "<p>通过工厂创建的 HTML 组件</p>"
}
```
---
## 使用示例
### 基本用法
```javascript
// 创建一个包含 HTML 内容的组件
const myHtmlComponent = new bricks.Html({
html: `
<h3>欢迎使用 Bricks Html 组件</h3>
<ul>
<li>支持任意合法 HTML</li>
<li>可嵌入富文本内容</li>
</ul>
`
});
// 将组件添加到页面某容器中
document.getElementById('container').appendChild(myHtmlComponent.dom_element);
```
---
## 注意事项
1. **安全性**:避免将不可信的用户输入直接赋值给 `html` 字段。
2. **脚本执行**:通过 `innerHTML` 插入的 `<script>` 标签默认不会执行,如需执行需额外处理。
3. **样式隔离**:插入的内容会继承父级样式,建议使用 CSS 类名控制样式作用域。
---
## 相关类
- [`bricks.JsWidget`](./JsWidget.md) —— 所有 JavaScript 组件的基类。
- [`bricks.Factory`](./Factory.md) —— 组件工厂,用于注册和实例化组件。
---
## 版本信息
- **框架**: Bricks UI Framework
- **模块**: `bricks.Html`
- **最后更新**: _可根据实际项目填写_
---
✅ 适用于快速渲染静态 HTML 内容场景,是构建富文本展示、帮助文档、动态提示等模块的理想选择。

283
docs/cn/i18n.md Normal file
View File

@ -0,0 +1,283 @@
下面是为提供的 JavaScript 代码编写的 **Markdown 格式技术文档**,包含类的功能说明、构造函数参数、方法详解以及使用示例。
---
# `bricks.I18n` 国际化支持类
`bricks.I18n` 是一个轻量级的国际化i18n工具类用于在前端应用中实现多语言文本翻译和动态语言切换。它支持从远程 URL 加载语言包,并缓存已加载的语言资源以提高性能。
该类依赖于全局对象 `window.bricks`,并使用 `bricks.HttpJson` 进行异步请求,同时假设存在辅助函数 `objget``obj_fmtstr` 用于对象操作和字符串格式化。
---
## 基本结构
```javascript
var bricks = window.bricks || {};
bricks.I18n = class {
// 实现见下文
};
```
> ⚠️ 注意:此代码片段中存在一处变量名错误(`opts` 未定义),已在文档中指出并修正。
---
## 构造函数
### `new bricks.I18n(options)`
初始化 I18n 实例,配置语言资源路径与默认行为。
#### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `url` | `string` | 否 | 获取语言包的 API 地址(如 `/api/i18n`)。若不提供,则仅支持手动设置字典。 |
| `method` | `string` | 否 | 请求语言包时使用的 HTTP 方法,默认为 `'GET'`。 |
| `default_lang` | `string` | 否 | 默认语言代码(如 `'en'`, `'zh-CN'`),默认值为 `'en'`。 |
#### 示例
```js
const i18n = new bricks.I18n({
url: '/api/translations',
default_lang: 'zh-CN'
});
```
> ❌ **注意原始代码 Bug**
> 原始代码中使用了未定义的 `opts` 变量:
> ```js
> this.url = opts.url;
> ```
> 应改为传入的构造函数参数 `url`, `method`, `default_lang`。**正确实现应如下:**
```js
constructor(url, default_lang, method) {
this.url = url;
this.default_lang = default_lang || 'en';
this.method = method || 'GET';
this.lang_msgs = {}; // 缓存所有语言的消息字典
this.msgs = {}; // 当前激活语言的翻译映射
}
```
或者接受一个选项对象:
```js
constructor(options) {
this.url = options.url;
this.default_lang = options.default_lang || 'en';
this.method = options.method || 'GET';
this.lang_msgs = {};
this.msgs = {};
}
```
> ✅ 文档后续基于 **修正后的构造函数** 进行描述。
---
## 方法
### `_ (txt, obj) → string`
获取指定键的翻译文本,并可选地进行变量替换。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `txt` | `string` | 要翻译的键名key。 |
| `obj` | `Object``undefined` | 用于填充模板字符串的变量对象。 |
#### 返回值
- 如果当前语言中有对应的翻译,返回翻译后的内容;
- 若提供了 `obj`,则调用 `obj_fmtstr(obj, translatedStr)` 对结果进行格式化;
- 否则返回原始 `txt`
#### 示例
```js
i18n.setup_dict({
"hello": "Hello, {name}!",
"welcome": "Welcome aboard."
}, 'en');
i18n._("hello", {name: "Alice"});
// 输出: "Hello, Alice!"
i18n._("welcome");
// 输出: "Welcome aboard."
```
---
### `is_loaded(lang) → boolean`
检查某个语言包是否已被加载到内存中。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `lang` | `string` | 语言代码(如 `'zh-CN'`)。 |
#### 返回值
- `true`:该语言的消息字典已加载;
- `false`:尚未加载。
#### 示例
```js
if (!i18n.is_loaded('fr')) {
await i18n.change_lang('fr');
}
```
---
### `setup_dict(dic, lang)`
将给定的语言字典注册到实例中,并立即激活该语言。
> 此方法通常由 `change_lang` 内部调用,也可用于预加载或静态注入语言包。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `dic` | `Object` | 键值对形式的语言翻译表,例如 `{ "ok": "确定" }`。 |
| `lang` | `string` | 该字典对应的语言代码。 |
#### 行为
- 将 `dic` 存储至 `this.lang_msgs[lang]` 中;
- 设置 `this.msgs = dic`,即当前活跃语言为 `lang`
#### 示例
```js
i18n.setup_dict({
"title": "应用标题",
"save": "保存"
}, 'zh-CN');
```
---
### `async change_lang(lang) → Promise<void>`
切换当前语言环境。如果目标语言未加载,会自动通过网络请求获取。
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `lang` | `string` | 目标语言代码(如 `'es'`, `'ja'`)。 |
#### 流程
1. 检查是否已加载该语言:
- 是 → 直接激活;
- 否 → 发起 HTTP 请求获取语言包。
2. 使用 `bricks.HttpJson` 发送请求,携带参数 `{ lang }`
3. 成功响应后,调用 `setup_dict` 注册并激活新语言。
#### 异常处理
- 若未设置 `this.url`,则跳过请求,无法加载新语言。
- 错误需由 `HttpJson` 层自行抛出或捕获。
#### 示例
```js
await i18n.change_lang('zh-CN');
document.title = i18n._('app_title'); // 使用新语言更新界面
```
---
## 依赖说明
本类依赖以下外部模块或函数:
| 名称 | 说明 |
|------|------|
| `bricks.HttpJson` | 用于发送 JSON 格式的 HTTP 请求,需具备 `httpcall(url, config)` 方法。 |
| `objget(obj, path)` | 工具函数,安全访问嵌套对象属性(类似 lodash.get。 |
| `obj_fmtstr(obj, str)` | 字符串模板替换函数,支持 `{key}` 语法,用 `obj.key` 替换。 |
> 示例 `obj_fmtstr` 实现:
> ```js
> function obj_fmtstr(obj, str) {
> return str.replace(/\{([^}]+)\}/g, (m, key) => obj[key] || m);
> }
> ```
---
## 使用场景示例
### 初始化与语言切换
```js
// 初始化 i18n 实例
const i18n = new bricks.I18n({
url: '/api/translations',
default_lang: 'en'
});
// 切换语言
async function setLanguage(lang) {
await i18n.change_lang(lang);
updateUI(); // 重新渲染页面文本
}
function updateUI() {
document.getElementById('greeting').textContent = i18n._('welcome', { user: 'Tom' });
}
```
### 预加载语言包(离线模式)
```js
// 手动注入英文包
i18n.setup_dict({
"welcome": "Welcome!",
"goodbye": "Goodbye!"
}, 'en');
// 不发起请求,直接切换
i18n.change_lang('en').then(updateUI);
```
---
## 注意事项
1. ✅ **修复建议**:原始代码中的 `opts` 应为构造函数参数对象,请确保传参方式一致。
2. 🔐 安全性:确保服务端返回的语言包内容经过清洗,防止 XSS。
3. 🌐 性能优化:所有语言包会被缓存,避免重复请求。
4. ⏱ 异步加载:首次切换未加载语言时需要等待网络响应,请添加加载状态提示。
---
## 版本历史(示例)
| 版本 | 修改内容 |
|------|----------|
| 0.1 | 初始版本,支持基本翻译与远程加载 |
---
## 许可证
MIT License
---
✅ *文档结束*

218
docs/cn/iconbarpage.md Normal file
View File

@ -0,0 +1,218 @@
# `IconbarPage` 技术文档
> **模块路径**: `bricks.IconbarPage`
> **继承自**: `bricks.VBox`
> **用途**: 提供一个带有图标工具栏的页面容器,支持顶部或底部布局,并可动态加载内容组件。
---
## 概述
`bricks.IconbarPage` 是一个基于 `bricks.VBox` 的复合组件,用于构建具有图标文本工具栏(`IconTextBar`)和内容区域的页面。工具栏可置于页面顶部或底部,点击工具项时会触发对应的内容展示逻辑。
该组件适用于需要导航式界面的应用场景,如设置页、功能面板或多标签界面。
---
## 类定义
```javascript
bricks.IconbarPage = class extends bricks.VBox { ... }
```
---
## 构造函数
### `constructor(opts)`
初始化 `IconbarPage` 实例。
#### 参数
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `opts` | Object | 配置选项对象 |
##### `opts` 结构
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `bar_opts` | Object | 是 | - | 图标工具栏的配置选项,传递给 `bricks.IconTextBar` |
| `bar_at` | String | 否 | `'top'` | 工具栏位置,可选 `'top'``'bottom'` |
| `height` | String | 否 | `'100%'` | 自动设置为 100%,确保占满父容器高度 |
##### `bar_opts` 子属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `margin` | Number/String | 工具栏外边距 |
| `rate` | Number | 布局占比(在 VBox 中使用的伸缩比例) |
| `tools` | Array\<Tool\> | 工具项列表,每个工具项定义一个可点击的操作 |
##### `tools` 数组中的 `tool` 对象结构
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `name` | String | 是 | 工具名称,作为唯一标识 |
| `icon` | String | 是 | 图标类名(如 Font Awesome 类名) |
| `label` | String | 否 | 显示文本(可选) |
| `tip` | String | 是 | 鼠标悬停提示文本 |
| `dynsize` | Boolean | 否 | 是否动态调整大小 |
| `rate` | Number | 否 | 在布局中所占比例 |
| `context` | Object | 否 | 附加上下文数据,可用于命令处理 |
| `content` | WidgetConfig / Function / Promise | 是 | 要加载的内容组件配置或异步构造逻辑 |
---
## 成员属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `bar_at` | String | 存储工具栏位置(`'top'``'bottom'` |
| `content` | `bricks.Filler` | 内容容器,用于动态插入当前选中的工具对应的内容组件 |
| `bar` | `bricks.IconTextBar` | 创建的图标文本工具栏实例 |
---
## 方法
### `command_handle(event) → Promise<void>`
处理来自工具栏的命令事件(例如点击某个工具按钮),并加载对应的内容。
#### 参数
- `event`: 事件对象
- `params`: 触发事件的 `tool` 对象(即被点击的工具项)
#### 行为
1. 获取触发事件的 `tool`
2. 调用 `show_content(tool)` 异步加载内容
> ⚠️ 使用 `bind(this)` 绑定上下文,在构造时通过 `bar.bind('command', ...)` 注册。
---
### `show_content(tool) → Promise<void>`
根据传入的 `tool` 动态创建并显示其对应的内容组件。
#### 参数
- `tool`: 工具项对象(包含 `content` 字段)
#### 流程
1. 使用 `bricks.widgetBuild(tool.content, this)` 异步生成组件实例
2. 若组件存在且尚未添加到 DOM 树,则将其加入 `this.content` 容器
3. 替换当前内容区域的内容(旧内容会被自动移除?取决于 `Filler` 实现)
> ✅ 支持异步组件加载,适合懒加载或远程模块。
---
## 生命周期与初始化行为
1. 设置默认高度为 `'100%'`
2. 确定工具栏位置(默认为 `'top'`
3. 调用父类 `super(opts)` 初始化 VBox 布局
4. 创建 `IconTextBar` 实例并传入 `bar_opts`
5. 创建 `Filler` 作为内容占位符
6. 根据 `bar_at` 决定工具栏与内容的排列顺序:
- `'top'`: 先工具栏,后内容
- `'bottom'`: 先内容,后工具栏
7. 绑定 `command` 事件监听器到 `command_handle`
8. 使用 `schedule_once` 延迟 0.1 秒自动显示第一个工具项的内容
> 🕒 `schedule_once(..., 0.1)` 可避免渲染冲突,确保 DOM 就绪后再加载初始内容。
---
## 示例用法
```javascript
var page = new bricks.IconbarPage({
bar_at: 'top',
bar_opts: {
margin: 5,
rate: 0,
tools: [
{
name: 'dashboard',
icon: 'fa fa-tachometer',
label: '仪表盘',
tip: '查看系统状态',
rate: 1,
content: { type: 'DashboardWidget', opts: {} }
},
{
name: 'settings',
icon: 'fa fa-cog',
tip: '系统设置',
content: async function() {
let mod = await import('./SettingsPanel.js');
return new mod.SettingsPanel();
}
}
]
}
});
document.body.appendChild(page.root);
```
---
## 事件机制
- **事件类型**: `command`
- **触发源**: `bricks.IconTextBar`
- **携带参数**: `event.params` 包含完整的 `tool` 对象
- **监听方式**: 在 `IconTextBar` 上绑定 `command` 事件
---
## 注册信息
```javascript
bricks.Factory.register('IconbarPage', bricks.IconbarPage);
```
可通过工厂方法创建:
```javascript
bricks.Factory.create('IconbarPage', {...opts});
```
---
## 注意事项
1. **内容清理**:当前实现未显式清除旧内容,依赖 `Filler` 组件的行为。若需手动管理,请扩展 `show_content` 方法。
2. **性能优化**:首次加载延迟 0.1s,建议确认是否必要;可考虑使用 `requestAnimationFrame` 或更精确的时机控制。
3. **错误处理**`widgetBuild` 失败时无异常捕获,建议包裹 `try-catch` 增强健壮性。
---
## 依赖组件
- `bricks.VBox` —— 垂直布局容器
- `bricks.IconTextBar` —— 图标+文本工具栏
- `bricks.Filler` —— 内容填充容器
- `bricks.widgetBuild` —— 异步构建组件的工具函数
- `schedule_once` —— 延迟执行工具函数(通常为 `setTimeout` 封装)
---
## 版本历史
| 日期 | 修改人 | 说明 |
|------|--------|------|
| 2025-04-05 | AutoDoc Generator | 根据源码生成技术文档 |
---
**文档完成**
📌 推荐配合 `IconTextBar` 文档一起阅读以理解完整交互逻辑。

184
docs/cn/iframe.md Normal file
View File

@ -0,0 +1,184 @@
# Bricks 框架Iframe 与 NewWindow 组件技术文档
> **版本**1.0
> **作者**Bricks 团队
> **适用框架**Bricks.js基于类的前端组件架构
---
## 概述
本文档描述 `bricks` 命名空间下的两个核心组件:
- `bricks.Iframe`:用于在当前页面嵌入外部网页内容的 `<iframe>` 组件。
- `bricks.NewWindow`:用于打开新浏览器窗口并加载指定 URL 的组件。
这两个组件均继承自 `bricks` 框架的基础类,并通过 `bricks.Factory` 注册,支持动态实例化。
---
## 依赖说明
- 需确保全局存在 `window.bricks` 对象(或自动初始化)。
- 依赖基础类:
- `bricks.Layout`:用于 `Iframe`
- `bricks.JsWidget`:用于 `NewWindow`
- 工厂模式注册依赖:`bricks.Factory.register`
---
## 1. `bricks.Iframe`
### 继承关系
```js
class Iframe extends bricks.Layout
```
### 功能说明
创建一个内联框架(`<iframe>`),将指定 URL 的内容嵌入当前页面。默认高度为 `100%`,可自定义。
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.url` | `String` | ✅ | 要嵌入的网页 URL |
| `opts.height` | `String` | ❌ | iframe 高度(默认 `'100%'` |
| `opts` 中其他属性 | — | — | 将传递给父类 `bricks.Layout` |
#### 示例
```js
const iframe = new bricks.Iframe({
url: 'https://example.com',
height: '500px'
});
```
### 方法
#### `create()`
创建 DOM 元素 `<iframe>` 并赋值给 `this.dom_element`
```js
create()
```
- **行为**
- 创建一个新的 `HTMLIFrameElement`
- 不自动添加到 DOM需由父类或外部逻辑处理挂载。
---
## 2. `bricks.NewWindow`
### 继承关系
```js
class NewWindow extends bricks.JsWidget
```
### 功能说明
调用浏览器原生 `window.open()` 方法,在新窗口或标签页中打开指定 URL。
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.url` | `String` | ✅ | 要打开的网页地址 |
| `opts.name` | `String` | ❌ | 新窗口名称(参见 `window.open` 第二参数),默认 `'_blank'` |
| `opts` 中其他属性 | — | — | 传递给父类 `bricks.JsWidget` |
#### 示例
```js
new bricks.NewWindow({
url: 'https://example.com',
name: 'myWindow'
});
// 打开新窗口,名称为 'myWindow'
new bricks.NewWindow({
url: 'https://help.com'
});
// 使用默认设置打开新标签页
```
> ⚠️ 注意:现代浏览器可能阻止未用户触发的 `window.open()` 调用(如非点击事件中执行)。
---
## 3. 工厂注册
为支持动态创建组件,两个类均已注册到 `bricks.Factory`
```js
bricks.Factory.register('NewWindow', bricks.NewWindow);
bricks.Factory.register('Iframe', bricks.Iframe);
```
### 使用工厂创建实例(示例)
```js
const widget = bricks.Factory.create('Iframe', {
url: 'https://dashboard.example.com',
height: '600px'
});
document.body.appendChild(widget.getElement()); // 假设方法存在
```
---
## 使用场景建议
| 组件 | 推荐场景 |
|------|----------|
| `Iframe` | 嵌入第三方仪表盘、帮助文档、跨域内容等需要隔离但显示在当前页的内容 |
| `NewWindow` | 跳转外部链接、打开帮助中心、弹出独立操作界面等无需嵌入当前页面的操作 |
---
## 注意事项
1. **安全性**
- 使用 `<iframe>` 时注意 XSS 和点击劫持风险,建议对源站点设置 CSP 策略。
- 避免在 `iframe` 中加载不可信来源。
2. **兼容性**
- `window.open()` 受浏览器弹窗拦截机制影响,请确保在用户交互上下文中调用。
3. **响应式设计**
- `Iframe` 默认高度为 `100%`,请确保其容器具有明确高度,否则可能无法正确渲染。
4. **SEO 与可访问性**
- `<iframe>` 内容不被搜索引擎直接索引,重要信息应避免仅通过 iframe 展示。
- 添加 `title` 属性以提升可访问性(当前代码未实现,建议扩展)。
---
## 扩展建议
```js
// 示例:增强 Iframe 支持 title 和 sandbox 属性
class EnhancedIframe extends bricks.Iframe {
constructor(opts) {
super(opts);
const { title, sandbox } = opts;
if (title) this.dom_element.title = title;
if (sandbox) this.dom_element.sandbox = sandbox;
}
}
```
---
## 版本历史
- **v1.0**:初始版本,包含 `Iframe``NewWindow` 基础实现及工厂注册。
---
> 📌 提示:更多关于 `bricks.Layout``bricks.JsWidget``bricks.Factory` 的细节,请参考主框架文档。

350
docs/cn/image.md Normal file
View File

@ -0,0 +1,350 @@
# `bricks.Image` 及相关图标组件技术文档
> 基于 `bricks.js` 框架的图像与图标类组件实现
---
## 概述
本模块定义了基于 `bricks.JsWidget` 的一系列图像与图标类组件,用于在网页中动态加载、显示和管理图像资源。主要包括:
- `bricks.Image`:基础图像组件
- `bricks.Icon`:可缩放的图标组件(继承自 `Image`
- `bricks.StatedIcon`:支持状态切换的图标组件(继承自 `Icon`
- `bricks.BlankIcon`:占位图标组件(无图像内容)
所有组件均通过 `bricks.Factory` 注册,可在配置系统中通过类型名实例化。
---
## 1. `bricks.Image`
### 类定义
```js
class extends bricks.JsWidget
```
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 配置选项对象 |
##### `opts` 属性
| 属性 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `url` | String | 否 | 图像的 URL 地址 |
| `default_url` | String | 否 | 当主 `url` 加载失败时使用的备用图像地址 |
#### 行为
- 调用父类构造函数。
- 若传入 `url`,则自动调用 `set_url()` 设置图像源。
---
### 方法
#### `create()`
创建一个 `<img>` 元素作为组件的 DOM 根节点。
```js
create()
```
> **说明**:该方法由框架调用,在初始化时创建 DOM 结构。
---
#### `set_url(url)`
设置图像的 `src` 属性,并绑定错误处理以支持默认图像。
```js
set_url(url)
```
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `url` | String | 图像资源 URL |
##### 行为
- 将 `this.url` 设为传入值。
- 如果配置了 `default_url`,则为 `<img>` 绑定 `onerror` 事件处理器,加载失败时自动切换到默认图。
- 设置 `dom_element.src = url`
---
#### `set_default_url()`
当图像加载失败时触发,切换至默认图像。
```js
set_default_url()
```
##### 行为
- 移除 `onerror` 监听器,防止重复触发。
- 将 `src` 设置为 `this.opts.default_url`
- 输出日志:`console.log('default_url', this.opts.default_url)`
---
#### `base64()`
将当前图像绘制到 Canvas 并导出为 Base64 编码的 PNG 数据 URL。
```js
base64()
```
##### 返回值
- `{String}` - 图像的 Data URL格式如`data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...`
##### 实现逻辑
1. 创建临时 `<canvas>` 元素。
2. 获取上下文并设置画布尺寸等于图像实际宽高。
3. 使用 `drawImage()` 将图像绘制到画布。
4. 调用 `toDataURL('image/png')` 生成 Base64 字符串。
5. 返回结果(未去除头部前缀)。
> ⚠️ 注意:此方法依赖图像已完全加载,否则可能绘制空白或异常内容。
---
#### `removeBase64Header(base64String)`
移除 Base64 字符串中的 MIME 头部信息(例如 `data:image/png;base64,`)。
```js
removeBase64Header(base64String)
```
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `base64String` | String | 完整的 Data URL 字符串 |
##### 返回值
- `{String}` - 纯 Base64 编码内容
##### 示例
```js
const clean = removeBase64Header('data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==');
// → 'R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='
```
---
## 2. `bricks.Icon`
### 类定义
```js
class extends bricks.Image
```
扩展 `Image` 类,提供基于字符单位(`charsize`)的响应式尺寸控制。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
`bricks.Image`,额外支持以下选项:
##### `opts` 扩展属性
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `rate` | Number | `1` | 缩放倍率,相对于 `charsize` |
| `cwidth` | Number | `1` | 占据的字符宽度数 |
| `cheight` | Number | `1` | 占据的字符高度数 |
| `dynsize` | Boolean | `true` | 是否启用动态尺寸调整 |
#### 行为
- 调用父类构造函数。
- 自动执行 `options_parse()` 进行配置解析与尺寸设置。
---
### 方法
#### `options_parse()`
解析配置项并设置图标尺寸及图像源。
```js
options_parse()
```
##### 动作
1. 设置 `rate`(默认为 1
2. 计算样式尺寸:`siz = bricks.app.charsize * rate + 'px'`
3. 调用 `set_url(this.url)` 重新加载图像(确保尺寸生效后加载)
4. 设置 `cwidth`, `cheight`, `dynsize`
5. 调用 `charsize_sizing()` 应用尺寸策略
> ✅ 提示:`charsize_sizing()``JsWidget` 提供的方法,用于根据字符网格进行布局适配。
---
## 3. `bricks.StatedIcon`
### 类定义
```js
class extends bricks.Icon
```
支持多状态切换的图标组件,常用于表示不同 UI 状态(如开/关、在线/离线等)。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
| 属性 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `states` | Array<{state: String, url: String}> | 是 | 状态列表,每个状态包含名称和对应图像 URL |
| `state` | String | 否 | 初始状态名;若未指定,则使用第一个状态 |
#### 示例配置
```js
{
states: [
{ state: 'online', url: '/img/online.png' },
{ state: 'offline', url: '/img/offline.png' }
],
state: 'online'
}
```
#### 行为
- 调用父类构造函数。
- 自动执行 `options_parse()` 初始化状态。
---
### 方法
#### `options_parse()`
初始化状态机逻辑。
```js
options_parse()
```
##### 行为
- 若未定义 `states` 数组,则直接返回。
- 若未设置初始 `state`,则设为 `states[0].state`
- 调用 `set_state(this.state)` 激活初始状态
---
#### `set_state(state)`
切换当前图标状态,并更新图像。
```js
set_state(state)
```
##### 参数
| 参数 | 类型 | 描述 |
|------|------|------|
| `state` | String | 要切换到的状态名称 |
##### 行为
- 更新 `this.state`
- 遍历 `this.states` 查找匹配项
- 找到后设置 `this.url = s.url`
- 调用 `super.options_parse()` 重新应用图像和尺寸
> 💡 支持链式调用与运行时状态变更。
---
## 4. `bricks.BlankIcon`
### 类定义
```js
class extends bricks.JsWidget
```
一个不渲染图像的“空白图标”,仅保留图标布局结构,用作占位符。
---
### 构造函数
```js
constructor(opts)
```
#### 参数
`Icon`,支持:
- `rate`: 缩放比例
- `cwidth`, `cheight`: 字符单元占用
- `dynsize`: 是否动态调整大小
#### 行为
- 不创建图像元素。
- 仅调用 `charsize_sizing()` 实现布局占位。
---
## 工厂注册
所有组件均已注册至 `bricks.Factory`,可在模板或配置中通过字符串类型名实例化:
```js
bricks.Factory.register('Image', bricks.Image);
bricks.Factory.register('StatedIcon', bricks.StatedIcon);
bricks.Factory.register('Icon', bricks.Icon);
bricks.Factory.register('BlankIcon', bricks.BlankIcon);
```
### 使用示例(假设模板语法)
```json
{
"type": "Icon",
"url": "/icons/save.png",
"rate": 1.5,
"cwidth": 2,
"cheight": 2
}
```
---
## 总结
| 组件 | 用途 | 特性 |
|------|------|------|
| `Image` | 显示普通图像 | 支持错误 fallback、Base64 导出 |
| `Icon` | 可缩放图标 | 基于 `charsize` 自适应尺寸 |
| `StatedIcon` | 多状态图标 | 支持运行时状态切换 |
| `BlankIcon` | 布局占位符 | 无图像,仅占空间 |
---
## 注意事项
1. **图像跨域问题**:使用 `base64()` 方法时,若图像来自跨域服务器且未开启 CORSCanvas 将被污染导致无法导出数据。
2. **异步加载**`base64()` 应在图像 `load` 事件完成后调用,否则结果不可靠。
3. **内存泄漏风险**:频繁调用 `base64()` 会创建大量临时 Canvas建议缓存或节流。
4. **默认图像防循环**:确保 `default_url` 图像本身有效,避免 `onerror` 死循环。
---
## 版本信息
- 框架:`bricks.js`
- 模块:`image.js`
- 最后更新2025年4月5日
---
**建议使用场景**
- 图标按钮、状态指示器、用户头像、动态图像预览等需要灵活控制图像行为的 UI 元素。

View File

@ -27,7 +27,7 @@ bricks的开发理念是应用开发可分为底层控件的开发以及操
通过这样分工,让精通底层开发的人员专心开发底层控件,而精通操控控件的人员专心搭建应用,从而提高开发效率和开发质量,希望大家参与进来一起做。
关于控件更多的信息,请看[控件](widgettypes.md)
关于控件更多的信息,请看[控件](widgets.md)
## 依赖
如果要使用Markdown需要引用marked模块
@ -54,5 +54,5 @@ bricks 使用uglifyjs 压缩
```
## 例子
配置好本地服务器后可以使用build.sh将bricks项目内容复制到本地网站后台之后在网站的examples目录中查看bricks提供的例子程序
配置好本地服务器后可以使用build.sh将bricks项目内容复制到本地网站后台之后在网站的/bricks/examples目录中查看bricks提供的例子程序

621
docs/cn/input.md Normal file
View File

@ -0,0 +1,621 @@
# Bricks UI 组件技术文档
> **版本**: 1.0
> **作者**: Bricks Framework Team
> **描述**: `bricks.UiType` 及其子类构成了一套用于构建表单输入控件的组件体系,支持文本、密码、数字、日期、文件上传、选择框等多种输入类型。所有组件均继承自统一基类 `bricks.UiType`,并可通过工厂模式动态创建。
---
## 目录
- [概述](#概述)
- [核心基类](#核心基类)
- [`bricks.UiType`](#bricksuitype)
- [`bricks.UiHide`](#bricksuihide)
- [基础输入控件](#基础输入控件)
- [`bricks.UiStr`](#bricksuistr)
- [`bricks.UiPassword`](#bricksuipassword)
- [`bricks.UiInt`](#bricksuiint)
- [`bricks.UiFloat`](#bricksuifloat)
- [`bricks.UiTel`](#bricksuitel)
- [`bricks.UiEmail`](#bricksuiemail)
- [`bricks.UiDate`](#bricksuidate)
- [`bricks.UiText`](#bricksuitext)
- [`bricks.UiCode`](#bricksuicode)
- [复合与特殊控件](#复合与特殊控件)
- [`bricks.UiCheck`](#bricksuicheck)
- [`bricks.UiCheckBox`](#bricksuicheckbox)
- [`bricks.UiSearch`](#bricksuisearch)
- [多媒体输入控件](#多媒体输入控件)
- [`bricks.UiFile`](#bricksuifile)
- [`bricks.UiImage`](#bricksuiimage)
- [`bricks.UiAudio`](#bricksuiaudio)
- [`bricks.UiVideo`](#bricksuivideo)
- [工厂系统](#工厂系统)
- [`bricks._Input`](#bricks_input)
- [`Input` 工厂实例](#input-工厂实例)
---
## 概述
该模块提供了一个可扩展的 UI 输入组件框架,基于 JavaScript 类和 DOM 封装,实现以下特性:
- 统一接口:所有输入控件都实现了 `getValue()`, `setValue()`, `reset()`, `resultValue()` 等方法。
- 表单集成:支持通过 `set_formdata(formData)` 方法将值附加到 `FormData` 对象中。
- 值校验与格式化:通过正则表达式(`pattern`)进行输入过滤。
- 动态数据加载:如 `UiCode``UiCheckBox` 支持从 URL 异步加载选项数据。
- 用户交互事件:支持 `focus`, `blur`, `changed`, `keydown` 等事件绑定。
- 国际化支持:标签文本使用 `bricks.app.i18n._()` 进行翻译。
- 多媒体录制支持:图像、音频、视频可通过摄像头/麦克风直接采集。
---
## 核心基类
### `bricks.UiType`
**继承自**: `bricks.Layout`
**用途**: 所有输入控件的抽象基类,定义通用行为。
#### 构造函数
```js
new bricks.UiType(opts)
```
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.name` | String | 是 | 控件名称(对应表单字段名) |
| `opts.value` / `opts.defaultvalue` | Any | 否 | 初始值或默认值 |
| `opts.required` | Boolean | 否 | 是否为必填项 |
#### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `name` | String | 字段名称 |
| `value` | Any | 当前值 |
| `required` | Boolean | 是否必填 |
| `ctype` | String | 内部类型标识,默认 `'text'` |
| `dom_element` | HTMLElement | 渲染后的 DOM 元素 |
#### 方法
| 方法 | 说明 |
|------|------|
| `getValue()` → Object | 返回 `{ name: value }`,若存在用户数据则合并返回 |
| `set_formdata(formData)` | 将当前值添加到 `FormData` 中 |
| `resultValue()` → Any | 获取实际提交值(子类重写) |
| `focus()` | 聚焦输入元素 |
| `reset()` | 重置为初始值 |
| `setValue(v)` | 设置值并更新 DOM |
| `set_disabled(f)` | 设置禁用状态 |
| `set_readonly(f)` | 设置只读状态 |
| `set_required(f)` | 设置是否必填 |
---
### `bricks.UiHide`
**继承自**: `bricks.UiType`
**用途**: 隐藏字段,不显示但可携带数据。
#### 特性
- `uitype = 'hide'`
- 自动设置 `display: none` 样式
#### 示例
```js
new bricks.UiHide({ name: 'user_id', value: '12345' })
```
---
## 基础输入控件
### `bricks.UiStr`
**继承自**: `bricks.UiType`
**用途**: 单行文本输入框。
#### 支持配置项 (`opts`)
| 属性 | 类型 | 说明 |
|------|------|------|
| `align` | String | 文本对齐方式:`left`, `center`, `right` |
| `length` | Number | 最大字符数maxlength |
| `minlength` | Number | 最小字符数 |
| `tip` | String | 提示信息(暂未使用) |
| `width` | String | 宽度CSS 单位) |
| `readonly` | Boolean/String | 是否只读 |
| `required` | Boolean | 是否必填 |
| `placeholder` | String | 占位符文本(自动国际化) |
| `css` | String | 自定义 CSS 类名 |
| `dynsize` | Boolean | 是否启用动态字体适配 |
| `cfontsize` | Number | 字体缩放比例 |
#### 方法扩展
- `create()`:创建 `<input type="text">`
- `check_pattern(value)`:用正则校验输入
- `set_value_from_input(event)`:输入时触发值更新与事件派发
- `onfocus/onblur`:焦点处理,添加高亮样式
#### 示例
```js
new bricks.UiStr({
name: 'username',
placeholder: 'Enter your username',
required: true,
width: '200px'
})
```
---
### `bricks.UiPassword`
**继承自**: `bricks.UiStr`
**用途**: 密码输入框。
#### 特性
- `type="password"`
- `uitype='password'`
#### 示例
```js
new bricks.UiPassword({ name: 'password', placeholder: '******' })
```
---
### `bricks.UiInt`
**继承自**: `bricks.UiStr`
**用途**: 整数输入。
#### 特性
- `type="number"`
- `textAlign: right`
- 正则匹配:`\d*`
- `resultValue()` 返回 `parseInt(value)`
#### 示例
```js
new bricks.UiInt({ name: 'age', length: 3 })
```
---
### `bricks.UiFloat`
**继承自**: `bricks.UiInt`
**用途**: 浮点数输入。
#### 特性
- 正则匹配:`\d*\.?\d+`
- 支持小数位控制(`dec_len`
- 自动设置 `step` 属性以限制精度
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `dec_len` | 2 | 小数位数 |
#### 示例
```js
new bricks.UiFloat({ name: 'price', dec_len: 2 })
```
---
### `bricks.UiTel`
**继承自**: `bricks.UiStr`
**用途**: 电话号码输入。
#### 特性
- `type="tel"`
- 默认正则:`[+]?\\d+`
- 支持自定义 `pattern`
#### 示例
```js
new bricks.UiTel({ name: 'phone', pattern: '[0-9]{11}' })
```
---
### `bricks.UiEmail`
**继承自**: `bricks.UiStr`
**用途**: 邮箱地址输入。
#### 特性
- `type="email"`
- 可选自定义 `pattern`
#### 示例
```js
new bricks.UiEmail({ name: 'email', required: true })
```
---
### `bricks.UiDate`
**继承自**: `bricks.UiStr`
**用途**: 日期选择器。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `max_date` | 最大允许日期(字符串格式 YYYY-MM-DD |
| `min_date` | 最小允许日期 |
#### 特性
- `type="date"`
- 若值为空,`resultValue()` 返回 `null`
#### 示例
```js
new bricks.UiDate({
name: 'birth_date',
min_date: '1900-01-01',
max_date: '2024-12-31'
})
```
---
### `bricks.UiText`
**继承自**: `bricks.UiType`
**用途**: 多行文本域textarea
#### 支持配置
| 属性 | 类型 | 说明 |
|------|------|------|
| `rows` | Number | 行数 |
| `cols` | Number | 列数 |
| `height` | String | 高度(默认 `'200px'` |
#### 特性
- 支持 Tab 缩进(每 4 空格)
- Shift+Tab 删除缩进
- Enter 换行(阻止默认行为)
- 支持代码编辑风格缩进逻辑
#### 方法
| 方法 | 说明 |
|------|------|
| `handle_tab_indent(erase)` | 处理 Tab 缩进或反向删除 |
| `handle_enter()` | 插入换行并保持光标位置 |
#### 示例
```js
new bricks.UiText({ name: 'description', rows: 6, cols: 50 })
```
---
### `bricks.UiCode`
**继承自**: `bricks.UiType`
**用途**: 下拉选择框(类似 `<select>`),常用于枚举字段。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `data` | 本地数据数组 `[ { value: '', text: '' } ]` |
| `dataurl` | 数据源 URL异步加载 |
| `params` | 请求参数 |
| `method` | HTTP 方法GET/POST |
| `valueField` | 值字段名(默认 `'value'` |
| `textField` | 显示文本字段名(默认 `'text'` |
| `nullable` | 是否允许空选项 |
#### 特性
- 支持动态加载数据(`load_data()`
- 支持级联刷新(通过 `get_data(event)` 接收上级事件参数)
- 自动生成 `<option>` 元素
- 支持国际化文本显示
#### 方法
| 方法 | 说明 |
|------|------|
| `build_options(data)` | 构建下拉选项 |
| `load_data(params)` | 异步获取数据 |
| `get_data(event?)` | 触发数据加载(可带参数) |
#### 示例
```js
new bricks.UiCode({
name: 'status',
dataurl: '/api/statuses',
textField: 'label',
valueField: 'id'
})
```
---
## 复合与特殊控件
### `bricks.UiCheck`
**继承自**: `bricks.UiType`
**用途**: 单个复选图标SVG 图标切换)。
#### 特性
- 使用 `MultipleStateIcon` 显示 checked/unchecked 状态
- 图标资源路径:
- checked: `imgs/checkbox-checked.svg`
- unchecked: `imgs/checkbox-unchecked.svg`
- 值为布尔类型
#### 方法
| 方法 | 说明 |
|------|------|
| `setValue(true/false)` | 设置状态 |
| `set_value_from_input()` | 响应图标点击,派发 `changed` 事件 |
#### 示例
```js
new bricks.UiCheck({ name: 'agree', value: false })
```
---
### `bricks.UiCheckBox`
**继承自**: `bricks.UiType`
**用途**: 多选或单选组fieldset + legend + 多个 UiCheck
#### 支持配置
| 属性 | 说明 |
|------|------|
| `data` | 选项列表 `[ { value: '', text: '' } ]` |
| `dataurl` | 异步数据源 |
| `textField` | 显示字段 |
| `valueField` | 值字段 |
| `label` | 分组标题legend |
#### 特性
- 支持多选(数组)和单选(字符串)
- 动态构建多个 `UiCheck` 实例
- 支持异步加载数据(`load_data_onfly()`
#### 方法
| 方法 | 说明 |
|------|------|
| `build_checkboxs()` | 根据数据生成复选框列表 |
| `set_value_from_input()` | 更新选中值并广播 `changed` 事件 |
| `setValue(v)` | 设置选中项(支持数组或单值) |
#### 示例
```js
new bricks.UiCheckBox({
name: 'hobbies',
label: 'Your Hobbies',
data: [
{ value: 'reading', text: 'Reading' },
{ value: 'music', text: 'Music' }
],
multicheck: true
})
```
---
### `bricks.UiSearch`
**继承自**: `bricks.HBox`
**用途**: 搜索选择器,点击图标弹出窗口选择数据。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `search_url` | 弹窗内容页面 URL |
| `valueField` | 返回值字段 |
| `textField` | 显示字段 |
| `popup_options` | 弹窗配置 |
| `text` | 显示文本(非值) |
#### 特性
- 包含一个只读 `UiStr` 和一个搜索图标SVG
- 点击图标打开弹窗(`default_popupwindow`
- 弹窗内查找 `Tabular` 组件并绑定事件
- 选择后调用 `set_data()` 更新值
#### 方法
| 方法 | 说明 |
|------|------|
| `open_search_window()` | 打开搜索弹窗 |
| `set_data(event)` | 接收选择结果并填充 |
#### 示例
```js
new bricks.UiSearch({
name: 'customer_id',
search_url: '/pages/customer_picker.html',
valueField: 'id',
textField: 'name'
})
```
---
## 多媒体输入控件
### `bricks.UiFile`
**继承自**: `bricks.VBox`
**用途**: 文件上传控件,支持拖拽和点击选择。
#### 支持配置
| 属性 | 说明 |
|------|------|
| `accept` | MIME 类型过滤(如 `'image/'`, `'audio/'` |
| `multiple` | 是否允许多选 |
#### 特性
- 支持拖拽上传
- 自动监听 `dragover`, `drop` 等事件
- 内部包含隐藏 `<input type="file">`
- 支持 `set_input_file(files)` 手动设置文件列表
#### 方法
| 方法 | 说明 |
|------|------|
| `handleFileSelect()` | 处理文件选择 |
| `dropHandle()` | 处理拖放事件 |
| `resultValue()` | 返回 File 或 FileList |
#### 示例
```js
new bricks.UiFile({ name: 'attachment', accept: 'application/pdf', multiple: true })
```
---
### `bricks.UiImage`
**继承自**: `bricks.UiFile`
**用途**: 图像上传 + 拍照功能。
#### 特性
- `accept='image/'`
- 添加拍照按钮(调用 `SysCamera`
- 支持预览上传或拍摄的图片(`show_image()`
#### 方法
| 方法 | 说明 |
|------|------|
| `take_photo()` | 打开相机 |
| `accept_photo()` | 接收拍摄结果 |
| `_show_image(file)` | 显示图片预览URL 或 Blob |
#### 示例
```js
new bricks.UiImage({ name: 'avatar' })
```
---
### `bricks.UiAudio`
**继承自**: `bricks.UiFile`
**用途**: 音频上传 + 录音功能。
#### 特性
- `accept='audio/'`
- 添加录音按钮(调用 `SysAudioRecorder`
- 支持播放预览(`AudioPlayer`
#### 方法
| 方法 | 说明 |
|------|------|
| `open_recorder()` | 打开录音器 |
| `accept_audio()` | 接收录音文件 |
| `_show_audio(file)` | 显示音频播放器 |
#### 示例
```js
new bricks.UiAudio({ name: 'voice_note' })
```
---
### `bricks.UiVideo`
**继承自**: `bricks.UiFile`
**用途**: 视频上传 + 录制功能。
#### 特性
- `accept='video/'`
- 添加录像按钮(调用 `SysVideoRecorder`
- 支持视频预览(`VideoPlayer`
#### 方法
| 方法 | 说明 |
|------|------|
| `open_recorder()` | 打开录像器 |
| `accept_video()` | 接收录像文件 |
| `_show_video(file)` | 显示视频播放器 |
#### 示例
```js
new bricks.UiVideo({ name: 'demo_video' })
```
---
## 工厂系统
### `bricks._Input`
**用途**: 输入控件工厂类,用于注册和创建 UI 组件。
#### 方法
| 方法 | 说明 |
|------|------|
| `register(name, uitype, Klass)` | 注册组件类 |
| `factory(options)` | 根据 `uitype` 创建实例 |
#### 示例
```js
const factory = new bricks._Input();
factory.register('UiStr', 'str', bricks.UiStr);
const input = factory.factory({ uitype: 'str', name: 'title' });
```
---
### `Input` 工厂实例
全局已初始化的工厂对象,预注册了所有标准控件。
#### 已注册映射表
| 名称 (name) | uitype | 类 |
|------------|--------|-----|
| `UiStr` | `str` | `bricks.UiStr` |
| `UiPassword` | `password` | `bricks.UiPassword` |
| `UiInt` | `int` | `bricks.UiInt` |
| `UiFloat` | `float` | `bricks.UiFloat` |
| `UiTel` | `tel` | `bricks.UiTel` |
| `UiEmail` | `email` | `bricks.UiEmail` |
| `UiDate` | `date` | `bricks.UiDate` |
| `UiText` | `text` | `bricks.UiText` |
| `UiCode` | `code` | `bricks.UiCode` |
| `UiCheck` | `check` | `bricks.UiCheck` |
| `UiCheckBox` | `checkbox` | `bricks.UiCheckBox` |
| `UiFile` | `file` | `bricks.UiFile` |
| `UiImage` | `image` | `bricks.UiImage` |
| `UiAudio` | `audio`, `audiorecorder` | `bricks.UiAudio` |
| `UiVideo` | `video` | `bricks.UiVideo` |
| `UiSearch` | `search` | `bricks.UiSearch` |
| `UiHide` | `hide` | `bricks.UiHide` |
#### 使用方式
```js
const field = Input.factory({ uitype: 'email', name: 'contact_email' });
```
---
## 总结
本组件库提供了完整且可扩展的前端表单输入解决方案,具备以下优势:
✅ 统一 API 接口
✅ 支持异步数据加载
✅ 多媒体采集支持
✅ 可定制样式与行为
✅ 支持国际化
✅ 事件驱动架构
适用于构建复杂的表单页面、数据录入系统、移动端 Web 应用等场景。
---
> ✅ **提示**:建议结合 `bricks.Layout``bricks.HBox`/`VBox` 布局容器组织表单结构。
> 📁 **资源路径**:图标资源位于 `imgs/` 目录下,需确保正确部署。

391
docs/cn/jsoncall.md Normal file
View File

@ -0,0 +1,391 @@
# Bricks HTTP 模块技术文档
本模块为 `bricks` 前端框架提供了一套完整的 HTTP 请求处理工具集支持多种响应类型文本、JSON、二进制流等并封装了自动参数注入、错误处理、会话管理与登录重定向等功能。
---
## 目录
- [概述](#概述)
- [核心功能](#核心功能)
- [公共函数](#公共函数)
- [基类:`HttpText`](#基类httptext)
- [派生类](#派生类)
- [`HttpArrayBuffer`](#httparraybuffer)
- [`HttpBin`](#httpbin)
- [`HttpResponse`](#httpresponse)
- [`HttpResponseStream`](#httpresponsestream)
- [`HttpRaw`](#httpraw)
- [`HttpJson`](#httpjson)
- [快捷方法](#快捷方法)
- [使用示例](#使用示例)
---
## 概述
`bricks.Http*` 系列类基于 `fetch` API 构建,旨在统一前端网络请求行为。主要特性包括:
- 自动附加设备信息和会话参数
- 支持 GET/POST 请求及 FormData 或 JSON 数据格式
- 统一的错误提示机制401、403、其他错误
- 可扩展的响应处理器文本、JSON、流式解析等
- 登录拦截与跳转支持(待完善)
该模块适用于 Web 应用中需要与后端服务交互的所有场景。
---
## 核心功能
| 功能 | 描述 |
|------|------|
| 参数合并 | 自动注入 `_webbricks_`, 屏幕宽高, 移动端标识等参数 |
| 会话管理 | 从响应头读取 `Set-Cookie` 并保存会话;发送时自动携带 |
| 错误处理 | 对 401、403 和非 OK 状态码进行统一 UI 提示 |
| 登录重定向 | 当返回 401 且存在 `login_url` 时触发登录流程(目前未完全实现) |
| 流式响应处理 | 支持逐行处理 chunked 响应数据(如 Server-Sent Events |
---
## 公共函数
### `url_params(data)`
将对象转换为 URL 查询字符串。
#### 参数
- `data`: `{[key: string]: any}` — 要编码的对象
#### 返回值
- `string` — 格式化后的查询字符串(例如 `"a=1&b=2"`
#### 示例
```js
url_params({a: 1, b: 'hello'}) // → "a=1&b=hello"
```
> ⚠️ 所有值都会通过 `encodeURIComponent` 编码。
---
## 基类:`HttpText`
所有 HTTP 客户端类的基类。
### 构造函数 `constructor(headers?)`
#### 参数
- `headers?`: `{[key: string]: string}` — 自定义请求头,默认为 `{ Accept: "text/html" }`
#### 行为说明
- 若未传入 `headers`,默认使用:
```js
{ "Accept": "text/html" }
```
- 使用 `bricks.extend()` 合并默认头与用户头。
- 自动收集以下上下文参数作为请求参数:
- `_webbricks_`: 固定值 `1`
- `width`: 屏幕宽度(来自 `bricks.app.screenWidth()`
- `height`: 屏幕高度(来自 `bricks.app.screenHeight()`
- `_is_mobile`: 是否为移动端(`'1'``'0'`
> ✅ 这些参数会在每次请求中自动附加。
### 方法
#### `url_parse(url)`
解析 URL 中的查询参数,并将其合并到内部 `this.params` 中。
##### 参数
- `url`: `string` — 原始 URL含 query
##### 返回值
- `string` — 清除 query 的基础 URL
##### 示例
```js
const client = new bricks.HttpText();
client.url_parse("/api/data?a=1&b=2");
// 结果client.params 包含 a=1, b=2
// 返回 "/api/data"
```
#### `add_own_params(params)`
合并用户参数与内部参数(包括 session
##### 参数
- `params`: `Object | FormData | null`
##### 返回值
- `Object | FormData` — 合并后的参数对象
##### 行为逻辑
- 如果是 `FormData`,直接追加键值对
- 否则创建新对象,合并 `this.params``params`
- 若存在 `bricks.app.get_session()`,额外添加 `session` 字段
#### `add_own_headers(headers)`
合并自定义请求头与实例默认头。
##### 参数
- `headers`: `{[key: string]: string} | null`
##### 返回值
- `Object` — 合并后的 headers
> 使用 `Object.assign(this.headers, headers)` 实现浅合并。
#### `async bricks_fetch(url, {method, headers, params})`
底层 `fetch` 封装,负责构建最终请求选项。
##### 参数
- `url`: `string` — 请求地址
- `method`: `'GET'|'POST'|'HEAD'|...` — HTTP 方法(默认 `'GET'`
- `headers`: `Object` — 请求头
- `params`: `Object|FormData` — 请求参数
##### 处理逻辑
| 条件 | 行为 |
|------|------|
| `params instanceof FormData` | 强制设为 POST`body = params` |
| `GET/HEAD` 请求 | 将 `params` 转为 query string 拼接到 URL |
| 其他方法(如 POST | `body = JSON.stringify(params)` |
##### 返回值
- `Response` — fetch 返回的原生响应对象
#### `async httpcall(url, opts)`
高层封装,包含状态码判断与错误处理。
##### 参数
- `url`: `string`
- `opts`: `{method, headers, params}`
##### 流程
1. 调用 `bricks_fetch` 发起请求
2. 检查响应状态:
- **401** 且配置了 `bricks.app.login_url` → 触发 `withLoginInfo`
- **403** → 显示“禁止访问”弹窗
- 非 `ok` → 显示通用错误弹窗
3. 成功则调用 `get_result_data()` 获取结果
4. 解析 `Set-Cookie` 并调用 `bricks.app.save_session(session)` 保存会话
##### 返回值
- `string | null` — 成功返回响应内容,失败返回 `null`
#### `async withLoginInfo(url, params)`
处理 401 时尝试重新登录(当前仅为占位实现)。
> ❗ 当前仅创建一个 `urlwidget` 加载登录页,未完成回调或重试逻辑。
#### `async get_result_data(resp)`
提取响应体数据的方法(可被子类覆盖)。
##### 默认实现(`HttpText`
```js
return await resp.text();
```
---
## 派生类
### `HttpArrayBuffer`
继承自 `HttpText`,用于下载二进制文件(如字体、模型等)。
#### 重写方法
```js
async get_result_data(resp) {
return await resp.arrayBuffer();
}
```
---
### `HttpBin`
继承自 `HttpText`,以 Blob 形式获取响应(适合图片、音频等)。
#### 重写方法
```js
async get_result_data(resp) {
return await resp.blob();
}
```
---
### `HttpResponse`
继承自 `HttpText`,直接返回原始 `Response` 对象。
#### 重写方法
```js
async get_result_data(resp) {
return resp;
}
```
> ✅ 适用于需手动处理 header 或 stream 的高级场景。
---
### `HttpResponseStream`
继承自 `HttpResponse`,专用于处理流式响应(如 SSE
#### 方法:`async handle_chunk(resp, handler)`
逐行解析 chunked 文本流,并调用 `handler(line)`
##### 参数
- `resp`: `Response` — fetch 返回的响应对象
- `handler`: `(line: string) => void` — 每一行数据的处理器
##### 实现细节
- 使用 `ReadableStream.getReader()` + `TextDecoder`
- 按 `\n` 分割缓冲区,确保跨 chunk 边界正确切分
- 最终剩余内容也会传递给 `handler`
##### 示例用法
```js
const streamClient = new bricks.HttpResponseStream();
const resp = await streamClient.get('/stream-endpoint');
await streamClient.handle_chunk(resp, (line) => {
console.log('Received:', line);
});
```
---
### `HttpRaw`
`HttpBin` 相同,返回 `Blob`
> ⚠️ 注释中可能表示未来用途不同,但当前实现与 `HttpBin` 完全一致。
---
### `HttpJson`
专用于 JSON 接口通信,设置默认 Accept 头并解析 JSON 响应。
#### 构造函数增强
```js
this.headers = { "Accept": "application/json" };
bricks.extend(this.headers, headers); // 用户头可覆盖
```
#### 重写方法
```js
async get_result_data(resp) {
return await resp.json();
}
```
> ✅ 推荐用于 RESTful API 调用。
---
## 快捷方法
模块末尾导出多个常用快捷函数,绑定到全局命名空间:
| 变量名 | 类型 | 说明 |
|--------|------|------|
| `bricks.hc` | `new HttpText()` | 文本客户端实例 |
| `bricks.tget` | `Function` | `hc.get` 的绑定方法GET 文本) |
| `bricks.tpost` | `Function` | `hc.post` 的绑定方法POST 文本) |
| `bricks.jc` | `new HttpJson()` | JSON 客户端实例 |
| `bricks.jcall` | `Function` | `jc.httpcall` 的绑定方法(调用 JSON 接口) |
| `bricks.jget` | `Function` | `jc.get` 的绑定方法GET JSON |
| `bricks.jpost` | `Function` | `jc.post` 的绑定方法POST JSON |
### 示例
```js
// 获取 HTML 内容
const html = await bricks.tget('/page', { params: { id: 123 } });
// 调用 JSON 接口
const data = await bricks.jpost('/api/user', {
headers: { 'X-Token': 'abc' },
params: { name: 'Alice' }
});
```
---
## 使用示例
### 1. 发送普通 GET 请求
```js
const client = new bricks.HttpText();
const result = await client.get('/api/hello', {
params: { q: 'test' }
});
console.log(result); // 输出服务器返回的文本
```
### 2. 发送 JSON POST 请求
```js
const jsonClient = new bricks.HttpJson();
const user = await jsonClient.post('/api/users', {
params: { name: 'Bob', age: 30 }
});
// 自动设置 Content-Type 和 Accept并解析 JSON
```
### 3. 下载文件为 ArrayBuffer
```js
const bufferClient = new bricks.HttpArrayBuffer();
const resp = await bufferClient.get('/model.bin');
const arrayBuf = await resp; // 已是 arrayBuffer
```
### 4. 处理实时日志流
```js
const streamClient = new bricks.HttpResponseStream();
const resp = await streamClient.get('/logs/stream');
await streamClient.handle_chunk(resp, (line) => {
console.log('[LOG]', line);
});
```
---
## 注意事项
- 依赖 `bricks.app` 提供 `screenWidth()`, `screenHeight()`, `get_session()`, `save_session()` 等方法
- 依赖 `bricks.extend(obj, src)` 实现对象合并(类似 jQuery.extend
- `objget(headers, 'Set-Cookie')` 用于安全获取响应头(需外部定义)
- 登录流程尚未完整实现,`withLoginInfo` 仅为原型
---
## 待优化建议
| 问题 | 建议改进 |
|------|---------|
| `bufffer` 拼写错误 | 修复 `if (buffer != ''){ yield bufffer; }` 中的拼写 |
| `withLoginInfo` 无实际登录能力 | 应支持模态框输入并回调重试请求 |
| `handle_chunk` 正则 `/\\r?\\n/gm` 不必要 | 可简化为 `.split('\n')` |
| `bricks_fetch``method = 'POST'` 修改外层变量风险 | 应使用局部变量 |
---
## 版本信息
- 创建时间:未知
- 框架版本Bricks Framework v1.x+
- 作者Bricks Team
> 文档最后更新2025年4月5日

126
docs/cn/keypress.md Normal file
View File

@ -0,0 +1,126 @@
# `bricks.KeyPress` 组件技术文档
> 一个用于监听键盘按键并实时显示按键信息的 UI 组件。
---
## 概述
`bricks.KeyPress` 是基于 `bricks.VBox` 的扩展类,用于创建一个能够监听全局键盘事件(`keydown`)的组件。当用户按下任意键时,该组件会清除当前内容,并显示被按下的键名。
此组件常用于调试、快捷键提示或交互式输入反馈场景。
---
## 继承关系
- **继承自**: `bricks.VBox`
- **类型**: 类Class
- **注册名称**: `'KeyPress'`(通过 `bricks.Factory` 注册)
---
## 构造函数
```js
constructor(opts)
```
### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 传递给父类 `VBox` 的配置选项,如布局、样式等。 |
### 行为
1. 调用 `super(opts)` 初始化父类 `VBox`
2. 使用 `bricks.app.bind('keydown', ...)` 绑定全局键盘按下事件,将 `this.key_handler` 作为回调函数,并确保其上下文指向当前实例。
---
## 方法
### `key_handler(event)`
处理键盘按下事件的回调函数。
#### 参数
| 参数名 | 类型 | 说明 |
|---------|-------------|------------------------|
| `event` | KeyboardEvent | 浏览器原生键盘事件对象。 |
#### 实现逻辑
1. 获取 `event.key` 字符串表示(例如 `"a"`, `"Enter"`, `"Shift"` 等)。
2. 如果 `event.key` 不存在,则直接返回,不进行后续操作。
3. 调用 `this.clear_widgets()` 清除当前容器内的所有子控件。
4. 创建一个新的 `bricks.Text` 控件,显示文本:`"key press is: <key>"`
5. 使用 `this.add_widget(w)` 将新文本控件添加到当前容器中。
#### 示例输出
- 按下字母 A → 显示:`key press is: a`
- 按下回车键 → 显示:`key press is: Enter`
- 按下空格键 → 显示:`key press is: `(后面是一个空格)
---
## 工厂注册
```js
bricks.Factory.register('KeyPress', bricks.KeyPress);
```
注册后可通过工厂方法动态创建该组件:
```js
var keyPressWidget = bricks.Factory.create('KeyPress', { /* opts */ });
```
---
## 使用示例
```js
// 创建 KeyPress 组件实例
var keyDisplay = new bricks.KeyPress({
style: {
padding: '10px',
border: '1px solid #ccc'
}
});
// 添加到页面某个容器中
someContainer.add_widget(keyDisplay);
```
---
## 注意事项
- 依赖全局对象 `bricks.app` 必须实现 `.bind(eventType, handler)` 方法来绑定 DOM 事件。
- 仅响应 `keydown` 事件,不处理 `keyup``keypress`
- 每次按键都会清空原有内容,仅保留最新一次按键信息。
---
## 依赖项
- `bricks.VBox` —— 布局容器基类。
- `bricks.Text` —— 文本显示组件。
- `bricks.Factory` —— 组件工厂,用于注册和创建组件。
- `bricks.app` —— 应用级事件管理器。
---
## 版本信息
- **模块**: `bricks`
- **组件**: `KeyPress`
- **最后更新**: 请根据实际项目填写
---
✅ *可用于开发调试面板、快捷键教学模块等场景。*

359
docs/cn/layout.md Normal file
View File

@ -0,0 +1,359 @@
# `bricks.Layout` 及其子类技术文档
本技术文档详细描述了 `bricks.Layout` 类及其相关布局组件(如 `VBox`, `HBox`, `Filler`, `ResponsableBox` 等)的功能、结构和使用方式。这些类是基于 `bricks.JsWidget` 构建的 UI 容器组件,支持键盘导航、动态内容管理与响应式布局。
---
## 目录
- [概述](#概述)
- [核心模块](#核心模块)
- [`bricks.Layout`](#brickslayout)
- [`bricks.VBox`](#bricksvbox)
- [`bricks.HBox`](#brickshbox)
- [`bricks.FHBox`](#bricksfhbox)
- [`bricks.FVBox`](#bricksfvbox)
- [`bricks.Filler`](#bricksfiller)
- [`bricks.ResponsableBox`](#bricksresponsablebox)
- [键盘选择机制](#键盘选择机制)
- [事件处理](#事件处理)
- [方法说明](#方法说明)
- [工厂注册](#工厂注册)
- [示例用法](#示例用法)
---
## 概述
`bricks` 是一个前端 UI 组件框架,`Layout` 是其基础容器类用于组织和管理子控件widgets。所有布局类均继承自 `bricks.Layout`,并可通过 CSS 类实现水平或垂直排列。此外,该系统支持**键盘导航**,允许用户通过方向键在可聚焦元素之间切换,并通过 Enter 键触发操作。
主要特性包括:
- 支持嵌套布局结构
- 提供标题、描述、工具栏构建方法
- 内置键盘导航支持(上下左右 + Enter
- 基于栈的层级导航机制
- 动态添加/删除子控件
- 响应式设计支持(`ResponsableBox`
---
## 核心模块
### `bricks.Layout`
#### 继承关系
```js
class Layout extends bricks.JsWidget
```
#### 属性
| 属性名 | 类型 | 描述 |
|-------|------|------|
| `_container` | Boolean | 标记为容器组件 |
| `keyselectable` | Boolean | 是否启用键盘选择功能 |
| `children` | Array | 子控件列表 |
| `selected_item` | Widget | 当前选中的子控件 |
| `title` | String (可选) | 容器标题文本 |
| `description` | String (可选) | 描述文本 |
| `toolbar` | Object (可选) | 工具栏配置对象 |
| `use_key_select` | Boolean (可选) | 初始化时是否自动启用键盘选择 |
> ⚠️ 注意:`use_key_select` 在代码中未明确定义于构造函数参数,但被条件判断引用,建议作为选项传入。
#### 构造函数
```js
constructor(options)
```
- **参数**
- `options` `{Object}` 配置项,支持以下字段:
- `title`: 显示标题
- `description`: 显示描述
- `toolbar`: 工具栏配置
- `keyselectable`: 是否允许键盘选择(默认 `false`
- `use_key_select`: 若为真,则自动调用 `enable_key_select()`
- **行为**
- 调用父类构造函数
- 初始化内部状态
- 若设置了 `use_key_select`,则立即启用键盘选择模式
---
### `bricks.VBox`
垂直布局容器,子元素垂直堆叠。
```js
class VBox extends Layout
```
- **构造函数**
- 自动设置 CSS 类:`vcontainer`
- **用途**:创建纵向排列的内容区域。
---
### `bricks.HBox`
水平布局容器,子元素水平排列。
```js
class HBox extends Layout
```
- **构造函数**
- 设置 CSS 类:`hcontainer`
- **用途**:横向排布控件。
---
### `bricks.FHBox`
固定行水平盒子(不换行)。
```js
class FHBox extends HBox
```
- **构造函数**
- 调用 `super(options)`
- 设置样式:`flexWrap: 'nowrap'`
- **用途**:确保子元素始终在同一行显示。
---
### `bricks.FVBox`
固定列垂直盒子(不换行)。
```js
class FVBox extends VBox
```
- **构造函数**
- 设置样式:`flexWrap: 'nowrap'`
- **用途**:防止垂直方向上的内容换列(较少见,通常用于嵌套场景)。
---
### `bricks.Filler`
弹性填充占位符,常用于 Flex 布局中占据剩余空间。
```js
class Filler extends Layout
```
- **构造函数**
- 设置 CSS 类:`filler`
- 注释中包含潜在增强样式(如 `flexGrow: 1`),可用于扩展
- **用途**:在布局中插入空白区域以平衡界面。
---
### `bricks.ResponsableBox`
响应式布局容器,根据宽高比动态调整布局方向。
```js
class ResponsableBox extends Layout
```
- **事件绑定**
- 监听 `'element_resize'` 事件,自动调用 `reset_type()`
- **`reset_type(event)` 行为**
- 判断当前容器宽度与高度:
- 若 `width > height` → 视为横向,应用 `hcontainer` 并设 `height: 100%`
- 否则 → 视为纵向,应用 `vcontainer` 并设 `width: 100%`
- 输出调试日志到控制台
> 💡 此类适合移动端或窗口缩放频繁的场景实现“自适应”UI 排列。
---
## 键盘选择机制
### 全局栈管理
```js
bricks.key_selectable_stack = [];
```
- 所有启用了键盘选择的 `Layout` 实例按进入顺序压入栈。
- 最顶层的实例才响应按键事件(通过 `is_currkeyselectable()` 判断)。
### 关键方法
| 方法 | 说明 |
|------|------|
| `enable_key_select()` | 启用键盘选择,注册事件监听,推入栈 |
| `disable_key_select()` | 停止监听,从栈顶弹出自身(若为当前层) |
| `is_currkeyselectable()` | 判断自己是否处于栈顶且可选 |
### 导航逻辑
| 键盘按键 | 功能 |
|--------|------|
| `ArrowDown` | 选择下一个子项(循环) |
| `ArrowUp` | 选择上一个子项(循环) |
| `ArrowRight` | 进入下一级(进入当前选中项的子菜单) |
| `ArrowLeft` | 返回上一级(退出当前层级) |
| `Enter` | 触发当前选中项的 `click` 事件 |
> 🔄 循环选择:到达末尾后回到开头,反之亦然。
---
## 事件处理
### `key_handler(event)`
主键盘事件处理器,仅当实例位于 `key_selectable_stack` 栈顶时生效。
- 使用 `event.key` 匹配方向键与回车
- 分发对应操作(上下选择、进出层级、确认)
### 生命周期事件
- `on_parent`:当控件被添加或移除父容器时派发,通知父子关系变更
---
## 方法说明
### 控件管理
| 方法 | 参数 | 描述 |
|------|------|------|
| `add_widget(w, index)` | `w`: widget, `index?`: number | 将控件插入指定位置或追加至末尾 |
| `remove_widget(w)` | `w`: widget | 移除指定控件,清理 DOM 和事件 |
| `clear_widgets()` | 无 | 清空所有子控件及 DOM 子节点 |
| `remove_widgets_at_begin(cnt)` | `cnt`: number | 移除前 `cnt` 个子控件 |
| `remove_widgets_at_end(cnt)` | `cnt`: number | 移除后 `cnt` 个子控件 |
> ✅ `insertBefore` / `appendChild` 被合理使用以维持 DOM 结构一致性。
---
### UI 构建方法
| 方法 | 功能 |
|------|------|
| `build_title()` / `build_title_widget()` | 创建并添加标题控件(`Title3` |
| `build_description()` / `build_description_widget()` | 添加描述文本(`Text` |
| `build_toolbar_widget(ext_tools)` | 构建浮动图标工具栏(`FloatTextIconBar` |
> 🔔 多数构建方法检查是否存在对应属性(如 `this.title`)后再创建。
---
### 选择控制方法
| 方法 | 功能 |
|------|------|
| `select_item(w)` | 选中指定控件,取消之前的选择 |
| `select_default_item()` | 默认选择第一个子控件 |
| `select_next_item()` | 向下循环选择 |
| `select_previous_item()` | 向上循环选择 |
| `find_first_keyselectable_child()` | 深度优先查找首个支持键盘选择的后代 |
---
### 层级跳转
| 方法 | 功能 |
|------|------|
| `down_level()` | 进入当前选中控件的子层级(需支持 keyselectable |
| `up_level()` | 退出当前层级,返回上级 |
> 🧭 类似菜单树导航模型,利用栈维护路径。
---
## 工厂注册
以下类型已向 `bricks.Factory` 注册,可用于声明式创建:
```js
bricks.Factory.register('HBox', bricks.HBox);
bricks.Factory.register('FHBox', bricks.FHBox);
bricks.Factory.register('VBox', bricks.VBox);
bricks.Factory.register('FVBox', bricks.FVBox);
bricks.Factory.register('Filler', bricks.Filler);
bricks.Factory.register('HFiller', bricks.Filler); // 水平填充
bricks.Factory.register('VFiller', bricks.Filler); // 垂直填充(同 Fillers
bricks.Factory.register('ResponsableBox', bricks.ResponsableBox);
```
> ✅ 支持模板引擎或 JSON 配置动态生成界面。
---
## 示例用法
### 创建带标题的垂直布局
```js
let vbox = new bricks.VBox({
title: "用户设置",
description: "请选择要修改的选项",
keyselectable: true
});
// 添加子控件
let btn1 = new bricks.Button({ otext: "个人资料" });
let btn2 = new bricks.Button({ otext: "安全设置" });
vbox.add_widget(btn1);
vbox.add_widget(btn2);
document.body.appendChild(vbox.dom_element);
```
### 启用手动键盘导航
```js
vbox.enable_key_select(); // 激活键盘控制
```
此时可用 ↑↓ 键切换按钮Enter 执行点击,← 返回上级(如果有)。
### 响应式容器示例
```js
let responsive = new bricks.ResponsableBox();
responsive.add_widget(new bricks.Label({ otext: "此布局会随尺寸变化!" }));
document.body.appendChild(responsive.dom_element);
```
窗口大小改变时,布局方向将自动调整。
---
## 注意事项与最佳实践
1. **避免重复启用 `keyselectable`**
- 确保 `enable_key_select()` 不被多次调用导致重复绑定事件
2. **手动管理栈结构风险**
- `key_selectable_stack` 是全局状态,跨组件交互需谨慎
3. **DOM 更新同步**
- 所有 `add_widget` / `remove_widget` 都应及时更新 DOM 和 `children` 数组
4. **性能优化建议**
- `find_first_keyselectable_child()` 使用递归,深层嵌套可能影响性能,可缓存结果
5. **i18n 支持**
- 所有文本控件启用 `i18n: true`,便于国际化
---
## 总结
`bricks.Layout` 提供了一个强大而灵活的基础布局系统,结合键盘导航与响应式能力,非常适合构建复杂的桌面风格 Web 应用界面(如仪表盘、菜单系统、表单布局等)。其模块化设计使得扩展新布局类型变得简单,同时工厂模式支持运行时动态渲染。
> ✅ 推荐在需要高可访问性Accessibility和键盘操作支持的应用中使用此组件体系。
---
📌 **版本信息**:本文档基于提供的 JavaScript 代码片段撰写
📅 **最后更新**2025年4月5日

203
docs/cn/line.md Normal file
View File

@ -0,0 +1,203 @@
# `bricks.ChartLine` 技术文档
> 基于 ECharts 扩展的折线图组件,用于快速渲染多系列折线图。
---
## 概述
`bricks.ChartLine``bricks.EchartsExt` 的子类,封装了基于 ECharts 的折线图配置逻辑。它支持从远程数据源或本地数据动态生成折线图,并自动构建图表所需的 `options` 配置对象。
该组件通过工厂模式注册为 `'ChartLine'` 类型,可通过 `bricks.Factory.create('ChartLine', config)` 实例化。
---
## 依赖
- `bricks.EchartsExt`:基础 ECharts 扩展类。
- `bricks.Factory`:用于组件注册与创建的工厂类。
---
## 属性(配置项)
| 属性名 | 类型 | 必填 | 描述 |
|----------------|----------|------|------|
| `data_url` | String | 否 | 数据请求的 URL 地址。若提供,则通过 AJAX 获取数据。 |
| `data_params` | Object | 否 | 请求数据时携带的参数GET 或 POST。 |
| `method` | String | 否 | 请求方法,默认为 `'GET'`。可选 `'POST'`。 |
| `data` | Array | 否 | 直接传入的本地数据数组,格式为对象数组。例如:`[{name: 'A', value1: 10, value2: 20}]`。 |
| `line_options` | Object | 否 | 自定义 ECharts 配置的扩展选项,会合并到最终的 `options` 中。 |
| `nameField` | String | 是 | 用作 X 轴分类字段的键名(如时间、类别等)。 |
| `valueFields` | Array<String> | 是 | 一个字符串数组表示需要绘制为折线的字段名称Y 轴数据)。每个字段将生成一条折线。 |
> ⚠️ 注意:`data``data_url` 二选一。若两者都存在,优先使用 `data_url` 异步加载数据。
---
## 方法
### `values_from_data(data, name)`
从数据数组中提取指定字段的所有值。
#### 参数
- `data` (Array<Object>):数据对象数组。
- `name` (String):要提取的字段名。
#### 返回值
- Array包含所有 `data[i][name]` 值的数组。
#### 示例
```js
const data = [{x: 'A', y: 1}, {x: 'B', y: 2}];
this.values_from_data(data, 'y'); // 返回 [1, 2]
```
---
### `lineinfo_from_data(data, name)`
生成单条折线的 ECharts 系列配置项。
#### 参数
- `data` (Array<Object>):原始数据。
- `name` (String):对应 `valueFields` 中的字段名,作为该折线的名称和数据来源。
#### 返回值
```js
{
name: String,
type: 'line',
data: Array // 提取自 data 中对应字段的值
}
```
#### 示例
```js
this.lineinfo_from_data(data, 'sales');
// 返回:
// {
// name: 'sales',
// type: 'line',
// data: [100, 150, 200]
// }
```
---
### `setup_options(data)`
根据输入数据构建完整的 ECharts 配置对象。
#### 参数
- `data` (Array<Object>):待处理的数据数组。
#### 返回值
返回标准的 ECharts `options` 对象,结构如下:
```js
{
tooltip: { trigger: 'axis' },
legend: { data: valueFields },
xAxis: {
type: 'category',
data: [/* 所有 nameField 对应的值 */]
},
yAxis: {
type: 'value'
},
series: [/* 多个 lineinfo_from_data 生成的对象 */]
}
```
#### 内部流程
1. 提取 `nameField` 字段作为 X 轴数据。
2. 遍历 `valueFields`,每项生成一条折线系列。
3. 构建图例legend`valueFields` 数组。
4. 设置提示框为轴触发(`axis`)。
5. 返回完整配置。
---
## 使用示例
### HTML 结构
```html
<div id="chart-line" style="width: 800px; height: 400px;"></div>
```
### JavaScript 初始化
#### 方式一:使用本地数据
```js
const chart = new bricks.ChartLine({
data: [
{ month: 'Jan', sales: 100, profit: 60 },
{ month: 'Feb', sales: 130, profit: 70 },
{ month: 'Mar', sales: 145, profit: 65 }
],
nameField: 'month',
valueFields: ['sales', 'profit']
});
chart.renderTo('#chart-line');
```
#### 方式二:从接口加载数据
```js
const chart = new bricks.ChartLine({
data_url: '/api/chart-data',
data_params: { year: 2023 },
method: 'GET',
nameField: 'date',
valueFields: ['pv', 'uv', 'orders']
});
chart.renderTo('#chart-line');
```
---
## 图表效果说明
- 支持多条折线同时展示。
- X 轴为类目轴category显示 `nameField` 的值。
- Y 轴为数值轴value
- 鼠标悬停时显示坐标轴对齐的提示框tooltip
- 图例自动根据 `valueFields` 生成。
---
## 注册信息
```js
bricks.Factory.register('ChartLine', bricks.ChartLine);
```
可通过工厂方式创建:
```js
const chart = bricks.Factory.create('ChartLine', config);
```
---
## 注意事项
1. 数据必须是对象数组格式。
2. `nameField``valueFields` 中的字段必须存在于数据对象中。
3. 若使用 `data_url`,后端需返回 JSON 格式数组。
4. 可通过 `line_options` 进行高级定制(如颜色、样式、动画等),会深度合并至最终 options。
---
## 版本信息
- 创建者:`bricks` 框架团队
- 类型名称:`ChartLine`
- 继承自:`bricks.EchartsExt`
- 注册标识符:`'ChartLine'`
---
✅ 推荐用于快速搭建基于 ECharts 的多系列折线图场景。

414
docs/cn/llm.md Normal file
View File

@ -0,0 +1,414 @@
以下是针对您提供的 JavaScript 代码编写的 **Markdown 格式技术文档**,结构清晰、注释完整,适用于前端开发团队或项目维护者使用。
---
# 📘 `bricks.LlmIO` 模块技术文档
> 基于 `bricks.js` 框架实现的多模型大语言模型LLM交互系统
> 支持流式响应、文本转语音TTS、用户反馈评分等功能
---
## 🔧 模块结构概览
```js
bricks = window.bricks || {}
bricks.LlmMsgAudio // 音频播放与流式消息处理
bricks.ModelOutput // 模型输出 UI 组件
bricks.LlmModel // 单个 LLM 模型控制器
bricks.LlmIO // 主控容器:管理输入、多个模型和输出展示
```
所有组件均继承自 `bricks.js` 提供的基础 UI 类(如 `VBox`, `HBox`, `JsWidget` 等),并支持动态构建、事件绑定和异步数据流处理。
---
## 1. `bricks.LlmMsgAudio` —— 流式音频消息处理器
### 功能说明
用于处理来自后端的流式文本响应,并根据标点符号分段发送至音频播放器。自动检测语言以适配中英文标点。
### 继承关系
```js
class LlmMsgAudio extends bricks.UpStreaming
```
### 构造函数:`constructor(opts)`
| 参数 | 类型 | 描述 |
|------|------|------|
| `opts` | Object | 传递给父类的配置项 |
#### 初始化字段:
- `this.olddata`: 上一次接收到的数据(用于增量比较)
- `this.data`: 当前累积的完整文本
- `this.cn_p`: 中文标点符号数组 `[“。”,”,”,”!”,”?”,”\n”]`
- `this.other_p`: 英文标点符号数组 `[“.”,”,”,”!”, “?”, “\n”]`
- `this.audio`: 实例化的音频播放器(通过 `AudioPlayer({})` 创建)
### 方法列表
#### ✅ `detectLanguage(text)``String`
尝试使用浏览器内置的 `Intl.LocaleDetector` 推测文本语言。
- 返回示例:`'zh'`, `'en'`
- 若失败则返回 `'未知'`
> ⚠️ 注意:`Intl.LocaleDetector` 并非所有浏览器都支持,需注意兼容性降级处理。
#### ✅ `send(data)``void`
接收增量数据并进行分句处理,将已完成句子通过 `super.send()` 发送。
##### 工作流程:
1. 计算新增部分:`newdata = data.slice(this.olddata.length)`
2. 更新历史数据缓存
3. 调用 `detectLanguage(this.data)` 判断语言
4. 使用对应标点分割成句子数组(过滤空字符串)
5. 将除最后一句外的所有完整句子发送出去
6. 保留最后未完成句在 `this.data` 中等待下一批数据
> 💡 示例:
> 输入 `"你好!今天天气"` → 输出 `"你好!"`,缓存 `"今天天气"`
#### ✅ `async go()``Promise<Response>`
调用父类 `go()` 获取最终响应,并设置音频源为该响应内容。
```js
await super.go();
this.audio.set_source_from_response(resp);
```
通常用于非流式场景下的 TTS 音频播放。
---
## 2. `bricks.ModelOutput` —— 模型输出 UI 控件
### 功能说明
显示单个模型的输出结果,包含图标、加载动画、内容区域及满意度评价功能。
### 继承关系
```js
class ModelOutput extends bricks.VBox
```
### 构造函数:`constructor(opts)`
| 属性 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `modelname` | String | 是 | - | 显示的模型名称 |
| `icon` | URL/String | 否 | `llm.svg` | 自定义图标路径 |
| `response_mode` | String | 否 | - | 可选 `'stream'`, `'sync'`, `'async'` |
| `estimate_url` | URL | 否 | - | 用户反馈提交地址 |
#### 内部组件初始化:
- `img`: 模型图标 SVG
- `content`: 主体 HBox 容器
- `run`: 加载动画组件(`BaseRunning`
- `filler`: 实际内容显示区(`LlmOut` 组件)
- `estimate_w`: 满意度打分组件(点赞/踩)
### 核心方法
#### ✅ `build_estimate_widgets()``void`
创建“结果满意吗?”评分控件(仅当 `estimate_url` 存在时)。
包含:
- 文本提示:“结果满意吗?”
- 👍 点赞图标(绑定 `estimate_llm(1)`
- 👎 点踩图标(绑定 `estimate_llm(-1)`
初始状态为隐藏(`.hide()`),可在完成后显示。
#### ✅ `async estimate_llm(val, event)``Promise<void>`
用户点击点赞/点踩时触发,向服务端上报评分。
**请求参数:**
```json
{
"widgettype": "urlwidget",
"options": {
"url": this.estimate_url,
"params": {
"logid": this.logid,
"value": val // 1 或 -1
}
}
}
```
> 执行后禁用评分按钮防止重复提交。
#### ✅ `async update_data(data)``Promise<void>`
更新模型输出内容。
- 移除加载动画 `run`
- 调用 `filler.update(data)` 渲染内容
- 支持流式逐步更新
#### ✅ `finish()``void`
生命周期钩子,当前无实际逻辑,可扩展。
---
## 3. `bricks.LlmModel` —— 单个 LLM 模型控制器
### 功能说明
封装一个 LLM 模型的行为逻辑,包括请求发送、格式化、响应处理等。
### 继承关系
```js
class LlmModel extends bricks.JsWidget
```
### 构造函数:`constructor(llmio, opts)`
| 参数 | 类型 | 说明 |
|------|------|------|
| `llmio` | LlmIO 实例 | 上层控制容器引用 |
| `opts` | Object | 模型配置对象 |
#### 配置项 (`opts`) 支持字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| `model` | String | 模型标识符(如 `gpt-3.5-turbo` |
| `modelname` | String | 显示名 |
| `url` | URL | 请求接口地址 |
| `params` | Object | 固定请求参数 |
| `input_from` | String | 允许的数据来源标签 |
| `textvoice` | Boolean | 是否启用 TTS 语音播报 |
| `tts_url` | URL | TTS 接口地址 |
| `response_mode` | String | `'stream' \| 'sync' \| 'async'` |
| `icon` | URL | 图标路径 |
### 核心方法
#### ✅ `render_title()``HBox`
生成顶部标题栏(含图标),点击可打开设置面板(预留接口)。
#### ✅ `show_setup_panel(event)``void`
待实现:点击标题弹出模型配置面板。
#### ✅ `inputdata2uploaddata(data)``FormData \| Object`
将输入数据转换为适合上传的格式,并注入模型相关信息。
- 若是 `FormData`,使用 `.append()` 添加字段
- 否则直接赋值对象属性
- 注入 `model``llmid`
> 特殊逻辑:若 `llmio.model_inputed` 为真,则不添加 `model` 字段(避免重复)
#### ✅ `async model_inputed(data)``Promise<void>`
主入口方法:接收用户输入并发起模型请求。
**行为分支:**
| 模式 | 行为 |
|------|------|
| `'stream'` / `'async'` | 使用 `HttpResponseStream` 处理流式响应 |
| `'sync'` | 使用 `HttpJson` 发起同步 POST 请求 |
流程:
1. 创建 `ModelOutput` 显示组件并加入页面
2. 准备请求数据
3. 根据模式发起请求
4. 流式情况下逐块调用 `chunk_response(mout, line)`
#### ✅ `is_accept_source(source)``Boolean`
判断是否接受来自指定源的数据。
#### ✅ `llm_msg_format()``Object`
返回默认的消息结构模板:
```js
{ role: 'assistant', content: "${content}" }
```
可用于格式化聊天记录。
#### ✅ `chunk_response(mout, l)``void`
处理每一个流式响应片段(每行 JSON 字符串)。
- 去除空白字符
- 尝试解析 JSON
- 异步模式下只处理 `status === 'SUCCEEDED'` 的消息
- 调用 `mout.update_data(d)` 更新 UI
#### ✅ `chunk_ended()``void`
流结束回调(目前仅打印日志)
---
## 4. `bricks.LlmIO` —— 主控容器LLM 输入输出管理器)
### 功能说明
集成式组件,提供以下能力:
- 多模型管理
- 输入表单弹窗
- 模型选择弹窗
- 自动渲染模型输出
- 支持 TTS 和用户反馈
### 继承关系
```js
class LlmIO extends bricks.VBox
```
### 构造函数:`constructor(opts)`
| 属性 | 类型 | 说明 |
|------|------|------|
| `user_icon` | URL | 用户头像图标 |
| `list_models_url` | URL | 获取可用模型列表的接口 |
| `input_fields` | Array | 表单字段定义(参考 `bricks.Form` |
| `models` | Array | 初始已添加的模型列表 |
| `tts_url` | URL | TTS 接口地址 |
| `estimate_url` | URL | 用户反馈提交地址 |
#### 初始化行为:
- 创建标题栏 `title_w`
- 创建输出区域 `o_w`(滚动容器)
- 创建底部操作按钮:
- `i_w`: 输入按钮(打开输入表单)
- `nm_w`: 添加模型按钮(打开模型搜索窗口)
- 绑定点击事件
- 若模型少于 2 个且存在 `tts_url`,启用 `textvoice` 模式
- 遍历 `models` 初始化每个 `LlmModel`
### 核心方法
#### ✅ `async show_input(params)``Promise<void>`
显示用户输入内容在对话区域上方。
- 构建 `UserInputView` 显示输入数据
- 添加用户头像图标
- 插入到 `o_w` 容器中
#### ✅ `show_added_model(m)``void`
注册一个新的模型实例并将其标题添加到顶部工具栏。
- 若启用了 `textvoice`,自动注入 `tts_url`
- 实例化 `LlmModel`
- 调用 `.render_title()` 并加入 `title_w`
#### ✅ `async open_search_models(event)``Promise<void>`
打开“选择模型”弹窗,从远程获取模型列表。
使用 `PopupWindow + Cols` 组件展示表格形式的模型列表。
**表格列定义:**
- 图标 + 名称HBox
- 描述、启用时间VBox 内嵌 Text
点击某条记录会触发:
- `add_new_model(event)`
- 并关闭弹窗
#### ✅ `async add_new_model(event)``Promise<void>`
将选中的模型加入 `this.models` 数组,并调用 `show_added_model` 显示。
#### ✅ `async open_input_widget(event)``Promise<void>`
打开输入表单弹窗。
使用 `Form` 组件加载 `input_fields` 定义的字段。
提交后触发:
- `handle_input(event)`
- 并自动关闭弹窗
#### ✅ `async handle_input(event)``Promise<void>`
处理用户提交的输入数据。
1. 调用 `show_input(params)` 显示输入内容
2. 遍历所有 `llmmodels`,调度执行各自的 `model_inputed(params)`
- 使用 `schedule_once(fn, 0.01)` 实现微任务延迟执行
---
## 📦 注册组件
```js
bricks.Factory.register('LlmIO', bricks.LlmIO);
```
允许通过工厂方式动态创建此组件。
---
## 🎯 使用示例
```js
var llmio = new bricks.LlmIO({
user_icon: '/static/icons/user.png',
tts_url: '/api/tts',
estimate_url: '/api/feedback',
input_fields: [
{ name: 'prompt', label: '输入提示', type: 'textarea' }
],
models: [
{
model: 'qwen-plus',
modelname: '通义千问',
url: '/api/llm/stream',
response_mode: 'stream',
icon: '/icons/qwen.svg'
}
]
});
```
---
## 🛠️ 注意事项 & 待优化点
| 问题 | 建议修复 |
|------|----------|
| `detectLaguage` 拼写错误 | 应为 `detectLanguage` |
| `this.oter_p` 拼写错误 | 应为 `this.other_p` |
| `lang='zh'` 错误地使用了赋值而非比较 | 应改为 `lang === 'zh'` |
| `objcopy` 函数未定义 | 应替换为 `JSON.parse(JSON.stringify(data))` 或引入深拷贝工具 |
| `Intl.LocaleDetector` 兼容性差 | 建议降级为基于关键词的语言识别算法 |
| `schedule_once(..., 0.01)` 不标准 | 建议改用 `setTimeout(fn, 0)``queueMicrotask` |
---
## 📚 附录关键类图UML 简化版)
```
+------------------+
| LlmIO |
+------------------+
|
v
+------------------+
| LlmModel[x] |
+------------------+
|
v
+------------------+
| ModelOutput |
+------------------+---> LlmMsgAudio (用于音频)
|
v
+------------------+
| UserInput |
+------------------+
```
---
## 📝 总结
本模块实现了完整的 LLM 交互链路:
1. 用户输入 → 表单收集
2. 多模型并发调用 → 支持流式/同步
3. 分句播放音频 → 提升听觉体验
4. 用户反馈机制 → 支持效果评估闭环
适合集成于智能客服、AI 助手、教育平台等需要多模型对比或语音播报的应用场景。
---
**文档版本v1.0**
📅 最后更新2025年4月5日
👨‍💻 编写Bricks Framework Team

366
docs/cn/llm_dialog.md Normal file
View File

@ -0,0 +1,366 @@
# `bricks.js` 技术文档LlmDialog 模块
> **版本**1.0
> **模块名称**`LlmDialog`, `LlmMsgBox`, `UserMsgBox`, 工具函数 `escapeSpecialChars`
> **依赖框架**`bricks.js` 前端组件库(基于类的 UI 架构)
---
## 📚 概述
该模块提供了一套用于构建与 LLM大语言模型交互对话界面的组件系统支持多模型并行响应、流式/同步响应模式、Markdown 渲染以及消息内容转义。主要包含以下核心类:
- `bricks.escapeSpecialChars`:字符串特殊字符转义工具。
- `bricks.UserMsgBox`:用户消息展示组件。
- `bricks.LlmMsgBox`LLM 回应消息展示及请求处理组件。
- `bricks.LlmDialog`:主对话容器,管理整个对话流程。
此外,通过 `bricks.Factory.register` 注册了 `LlmDialog` 以便动态创建。
---
## 🔧 工具函数
### `bricks.escapeSpecialChars(s)`
对输入字符串中的特殊字符进行反斜杠转义,常用于防止 JSON 注入或确保文本安全输出。
#### 参数
| 参数 | 类型 | 描述 |
|------|--------|--------------|
| `s` | string | 待转义字符串 |
#### 返回值
- **类型**`string`
- 转义后的字符串,适用于嵌入 JSON 或 HTML 环境。
#### 支持转义的字符
| 字符 | 原始表示 | 转义后表示 | 说明 |
|------|----------|----------------|--------------------|
| `\` | `\` | `\\` | 反斜杠 |
| `"` | `"` | `\"` | 双引号 |
| `\n` | 换行 | `\n` | 换行符 |
| `\r` | 回车 | `\r` | 回车符 |
| `\t` | 制表符 | `\t` | Tab |
| `\f` | 换页 | `\f` | Form feed |
| `\v` | 垂直制表 | `\v` | Vertical tab |
| `\0` | 空字符 | `\0` | Null byte |
> ⚠️ 注意:单引号 `'` 的转义代码被注释,当前未启用。
#### 示例
```js
bricks.escapeSpecialChars('Hello "world"\n');
// 输出: 'Hello \\"world\\"\\n'
```
---
## 💬 用户消息组件:`UserMsgBox`
继承自 `bricks.HBox`,用于显示用户发送的消息。
### 继承关系
```js
class UserMsgBox extends bricks.HBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------|--------|------|----------------------------|------------------------------|
| `icon` | string | 否 | `imgs/chat-user.svg` | 用户头像 SVG 图标 URL |
| `prompt` | string | 是 | - | 用户输入的消息内容 |
| `msg_css` | string | 否 | `'user_msg'` | 应用到消息体的 CSS 类名 |
#### 内部结构
1. 创建一个 SVG 头像图标(使用 `bricks.Svg`)。
2. 使用 `MdWidget` 显示 Markdown 格式消息。
3. 添加左侧空白图标占位符(`BlankIcon`),保持布局对齐。
4. 消息排列顺序:`[BlankIcon] ← [消息内容] ← [用户头像]`
#### 示例实例化
```js
new bricks.UserMsgBox({
prompt: "你好,请介绍一下你自己。",
icon: "/icons/user.svg",
msg_css: "custom-user-style"
});
```
---
## 🤖 LLM 消息组件:`LlmMsgBox`
继承自 `bricks.HBox`,用于展示 LLM 的回复,并支持流式和同步两种响应方式。
### 继承关系
```js
class LlmMsgBox extends bricks.HBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------------|----------|------|-------------------------|----------------------------------------------------------------------|
| `model` | string | 是 | - | 使用的模型名称(如 `gpt-3.5-turbo` |
| `mapi` | string | 是 | - | API 类型标识(可用于后端路由判断) |
| `url` | string | 是 | - | 请求后端接口地址 |
| `icon` | string | 否 | `imgs/user.png` | LLM 头像图标 URL |
| `msg_css` | string | 否 | `'llm_msg'` | 消息区域应用的 CSS 类 |
| `response_mode` | string | 否 | `'stream'` | 响应模式:`'stream'`, `'sync'`, `'async'` |
| `user_msg` | object | 否 | `{role:'user', content:"${prompt}"}` | 用户消息模板,支持 `${prompt}` 插值 |
| `llm_msg` | object | 否 | `{role:'assistant', content:"${content}"}` | LLM 消息模板,支持 `${content}` 插值 |
#### 内部初始化逻辑
- 创建头像图标SVG
- 初始化 `MdWidget` 显示消息内容
- 加载 `BaseRunning` 动画组件(在响应中显示加载状态)
- 初始化消息历史数组 `this.messages = []`
#### 消息排列顺序
`[LLM头像] → [运行动画] → [消息内容] → [空白占位符]`
---
### 方法列表
#### `responsed()`
标记响应已开始,停止加载动画,移除运行指示器。
```js
this.run.stop_timepass();
this.remove_widget(this.run);
```
#### `user_msg_format()`
生成用户消息对象格式。
- 若设置了 `this.user_msg`,则返回其值;
- 否则返回默认模板:`{role: 'user', content: "${prompt}"}`
> 实际内容将在调用时通过 `bricks.apply_data()` 替换 `${prompt}`
#### `llm_msg_format()`
生成 LLM 消息对象格式。
- 若设置了 `this.llm_msg`,返回其值;
- 否则返回:`{role: 'assistant', content: "${content}"}`
#### `chunk_response(l)`
处理流式响应中的每一个数据块chunk
##### 参数
- `l`: JSON 字符串片段(通常来自 ReadableStream
##### 行为
1. 解析 JSON 数据。
2. 若无 `content` 字段或为空,忽略。
3. 累加内容至当前消息。
4. 更新 `MdWidget` 内容。
5. 触发 `updated` 事件。
#### `chunk_ended()`
流式响应结束后,将最终内容保存为结构化消息并推入 `messages` 数组。
- 调用 `escapeSpecialChars` 对内容转义
- 使用 `apply_data` 填充模板
- 推入 `this.messages`
#### `async set_prompt(prompt)`
发起对 LLM 的请求。
##### 参数
- `prompt`: 用户输入文本(自动转义)
##### 流程
1. 将用户消息按模板格式化并加入 `messages`
2. 准备请求参数:
```js
{
messages: this.messages,
mapi: this.mapi,
model: this.model
}
```
3. 根据 `response_mode` 执行不同请求策略:
| 模式 | 行为说明 |
|-----------|---------|
| `stream` | 使用 `HttpResponseStream` 发起 POST 请求,逐块接收并渲染 |
| `sync` | 使用 `HttpJson` 同步获取完整响应,一次性渲染结果 |
| 其他 | 不做任何操作(预留扩展) |
##### 示例调用
```js
await llmMsgBox.set_prompt("请写一首关于春天的诗");
```
---
## 🧩 主对话框组件:`LlmDialog`
继承自 `bricks.VBox`,是完整的聊天对话界面容器,支持多模型同时响应。
### 继承关系
```js
class LlmDialog extends bricks.VBox
```
### 构造函数:`constructor(opts)`
#### 参数 `opts`
| 属性 | 类型 | 必需 | 默认值 | 描述 |
|------------------|----------|------|----------------|----------------------------------------------------------------------|
| `models` | array | 是 | - | 模型配置数组,每个元素定义一个 LLM 模型 |
| `response_mode` | string | 否 | `'stream'` | 全局响应模式:`'stream'`, `'sync'`, `'async'` |
| `user_msg_css` | string | 否 | `'user_msg'` | 用户消息使用的 CSS 类 |
| `user_icon` | string | 否 | - | 用户头像图标路径 |
| `title_ccs` | string | 否 | `'llm_title'` | 标题栏 CSS 类(拼写疑似错误,应为 `title_css` |
| `height` | string | 否 | `'100%'` | 容器高度 |
#### `models` 数组项结构
```js
{
model: "gpt-3.5-turbo",
mapi: "openai",
url: "/api/openai/chat",
icon: "/icons/gpt.svg",
css: "gpt-response",
user_msg: { role: "user", content: "${prompt}" },
llm_msg: { role: "assistant", content: "${content}" }
}
```
---
### 方法列表
#### `show_models_info()`
遍历所有模型,调用 `show_model_info(model)` 在标题栏显示模型信息。
#### `show_model_info(model)`
在顶部标题区添加一个 HBox包含模型图标和名称。
- 使用 `Svg` 显示图标
- 使用 `Text` 显示模型名
- 存储引用到 `this.model_info_ws[model.model]`
#### `add_model(model)`
动态添加新模型配置,并立即显示在标题栏。
#### `delete_model(model)`
根据 `model.model` 名称删除模型及其视图。
#### `async set_prompt(prompt)`
主入口方法:发送用户消息并触发所有模型响应。
##### 步骤
1. 转义用户输入。
2. 创建并添加 `UserMsgBox` 到滚动面板。
3. 调用 `llm_request(prompt)` 并等待完成。
4. 自动滚动到底部。
#### `async llm_request(prompt)`
为每个注册的模型创建一个 `LlmMsgBox`,并异步启动请求。
- 使用 `schedule_once(fn, 0.1)` 延迟执行以避免阻塞 UI
- 所有模型并行响应
---
### 事件机制
#### `llm_answer` 事件
当某个模型完成响应后,可通过监听此事件获取回答内容。
> ❗ 当前代码中未显式触发 `llm_answer` 事件,需补充如下代码才能生效:
```js
// 在 chunk_ended 或 sync 响应结束处添加:
this.fire('llm_answer', { content: txt });
```
建议增强事件通知能力。
---
## 🏗️ 工厂注册
```js
bricks.Factory.register('LlmDialog', bricks.LlmDialog);
```
允许通过字符串标识动态创建组件:
```js
var dialog = bricks.Factory.create('LlmDialog', options);
```
---
## ✅ 使用示例
```js
var dialog = new bricks.LlmDialog({
height: '600px',
user_icon: '/icons/me.svg',
title_ccs: 'chat-title',
response_mode: 'stream',
models: [
{
model: 'gpt-3.5-turbo',
mapi: 'openai',
url: '/api/chat',
icon: '/icons/gpt.svg',
css: 'gpt-msg',
user_msg: { role: 'user', content: '${prompt}' },
llm_msg: { role: 'assistant', content: '${content}' }
},
{
model: 'claude-2',
mapi: 'anthropic',
url: '/api/anthropic',
icon: '/icons/claude.svg',
css: 'claude-msg'
}
]
});
document.body.appendChild(dialog.dom_element);
// 发送消息
dialog.set_prompt("解释什么是机器学习");
```
---
## 📝 注意事项与改进建议
| 项目 | 说明 |
|------|------|
| 🔐 安全性 | `escapeSpecialChars` 有助于防止注入,但建议结合 DOMPurify 或其他 sanitizer 进一步防护 XSS |
| 🧩 单引号转义 | 被注释掉,若需兼容 SQL 或属性字符串场景,建议启用 |
| 🎯 事件缺失 | `llm_answer` 事件未实际触发,建议在 `chunk_ended()` 中补全 |
| 🖼️ 图标资源 | 依赖 `bricks_resource()` 函数解析静态资源路径,需确保其存在 |
| ⏳ 异步控制 | `schedule_once(..., 0.1)` 是一种 Hack可考虑改为 `queueMicrotask``Promise.resolve().then()` |
| 📏 布局兼容 | 使用 FlexboxHBox/VBox确保父级容器设置正确尺寸 |
---
## 📚 总结
本模块提供了完整的前端 LLM 聊天对话解决方案,具备以下特性:
✅ 多模型支持
✅ 流式 & 同步响应
✅ Markdown 渲染
✅ 自动滚动与加载动画
✅ 可扩展模板机制
适合集成进低代码平台、AI 助手界面、多模型对比测试工具等场景。
---
> 文档版本1.0
> 最后更新2025年4月5日

297
docs/cn/llmout.md Normal file
View File

@ -0,0 +1,297 @@
# Bricks 框架技术文档
## 概述
本文档描述了 `bricks` 框架中两个核心视图类的实现与使用:`UserInputView``LlmOut`。这两个类用于处理用户输入和大模型输出内容的可视化展示,支持 Markdown 文本、图像、音频、视频等多种媒体类型。
---
## 1. `bricks.UserInputView`
### 类定义
```javascript
bricks.UserInputView = class extends bricks.VBox
```
### 功能说明
将用户的输入字段(如文本、图片、音视频等)转换为结构化的 Markdown 内容进行展示,并独立嵌入音视频播放器组件。
- 所有非音视频字段以代码块形式显示。
- 图像字段以 Markdown 图像语法渲染。
- 视频和音频字段通过专用播放器组件独立展示。
---
### 构造函数
```javascript
constructor(opts)
```
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项,继承自 `bricks.VBox` |
#### 初始化行为
- 调用父类构造函数 `super(opts)`
- 初始化内部变量:
- `this.v_w`: 视频播放器实例(初始为 `null`
- `this.a_w`: 音频播放器实例(初始为 `null`
- 调用 `show_input(this.data)` 显示数据
---
### 方法:`show_input(data)`
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `data` | Object | 包含用户输入数据的对象 |
#### 处理逻辑
遍历 `this.input_fields` 中定义的字段配置,根据字段名前缀判断类型并生成相应内容:
| 字段前缀 | 处理方式 |
|---------|----------|
| `video*` | 创建 `VideoPlayer` 组件,自动播放,宽度 100% |
| `audio*` | 创建 `AudioPlayer` 组件,自动播放,宽度 100% |
| `image*` | 在 Markdown 中添加图片语法:`![label](url)` |
| 其他 | 使用代码块包裹内容:`\`\`\`\nvalue\n\`\`\`` |
> **注意**:字段标签优先使用 `f.label`,若无则使用 `f.name`
#### 流程步骤
1. 初始化空字符串 `mdtext`
2. 遍历所有输入字段,拼接 Markdown 内容或创建媒体组件
3. 清除当前所有子组件(`clear_widgets()`
4. 创建新的 `MdWidget` 显示 Markdown 内容
5. 添加音视频组件(如有)
#### 示例输出 Markdown
```markdown
* 用户提问
```
这是用户的输入文本
```
* 示例图片
![示例图片](https://example.com/image.png)
```
---
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `v_w` | VideoPlayer 或 null | 当前绑定的视频播放器 |
| `a_w` | AudioPlayer 或 null | 当前绑定的音频播放器 |
| `input_fields` | Array | 字段定义数组,需在外部设置 |
| `data` | Object | 输入数据对象 |
---
## 2. `bricks.LlmOut`
### 类定义
```javascript
bricks.LlmOut = class extends bricks.VBox
```
### 功能说明
用于动态渲染大语言模型LLM返回的结果数据。支持以下内容类型
- 推理过程文本thinking / reasoning
- 最终回答内容
- 错误信息
- 音频响应URL 或 Base64
- 视频响应URL 或 Base64
- 单张或多张图像
该组件具备增量更新能力,可通过多次调用 `update()` 累积内容。
---
### 构造函数
```javascript
constructor(opts)
```
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项,继承自 `VBox` |
#### 初始化行为
- 调用父类构造函数
- 初始化各类媒体组件引用为 `null`
- 初始化数据缓存属性为空值或空数组
#### 初始状态
```js
this.rc_w = null; // reasoning widget
this.c_w = null; // content widget
this.v_w = null; // video player
this.i_w = null; // image widgets (not used directly)
this.a_w = null; // audio player
this.images = []; // 存储图片 URL 列表
this.reasoning_content = ''; // 缓存推理内容
this.content = ''; // 缓存应答内容
this.error = ''; // 缓存错误信息
```
---
### 方法:`update(data)`
#### 参数
| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `data` | Object | 是 | LLM 返回的 JSON 数据 |
#### 支持字段
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `reasoning_content` | String | 推理过程文本(可选) |
| `content` | String | 最终回复文本(可选) |
| `error` | String | 错误信息(可选) |
| `audio` | String | 音频资源路径或 Base64 编码(可选) |
| `video` | String | 视频资源路径或 Base64 编码(可选) |
| `image` | String 或 Array<String> | 图片资源地址(单个或多个) |
---
#### 处理逻辑详解
##### 🔊 音频处理
- 若 `audio` 不以 `http` 开头且不包含 `data:audio/` 前缀,则自动封装为 `data:audio/wav;base64,...`
- 第一次出现时创建 `AudioPlayer`
- 后续调用会追加新音频(`add_url`
##### 🎥 视频处理
- 第一次出现创建 `VideoPlayer`
- 后续更新追加新视频源(`add_url`
##### ❌ 错误处理
- 将 `data.error` 追加到本地缓存 `this.error`
- 展示时使用 `resp-error` CSS 类样式化
##### 💬 推理内容
- 追加至 `this.reasoning_content`
- 使用 `thinking-content` 样式及浅红色背景 (`#f0d0d0`) 区分显示
##### ✅ 正常响应内容
- 追加至 `this.content`
- 使用 `resp-content` 样式正常展示
##### 🖼️ 图像处理
- 支持单个 URL 或数组
- 所有图像 URL 被合并进 `this.images` 数组
- 每张图创建一个 `Image` 组件并添加到容器
---
#### 渲染顺序
组件按如下顺序依次添加到界面:
1. 错误信息(若有)
2. 推理内容(若有)
3. 正常响应内容(若有)
4. 视频播放器(若有)
5. 音频播放器(若有)
6. 所有图片(逐一添加)
> **注意**:每次 `update()` 都会先清除已有组件(`clear_widgets()`),再重新构建整个 UI。
---
### 注册信息
```js
bricks.Factory.register('LlmOut', bricks.LlmOut);
```
允许通过工厂模式以字符串 `'LlmOut'` 实例化此类。
---
## 使用示例
### 示例 1初始化 UserInputView
```js
const userInput = new bricks.UserInputView({
data: {
question: "你好吗?",
image_01: "https://example.com/photo.jpg"
},
input_fields: [
{ name: "question", label: "问题" },
{ name: "image_01", label: "上传图片" }
]
});
```
### 示例 2更新 LLM 输出
```js
const llmView = new bricks.LlmOut();
llmView.update({
reasoning_content: "正在分析用户的问题...",
audio: "base64encodedstring==",
image: ["img1.jpg", "img2.jpg"]
});
llmView.update({
content: "我已经完成分析,这是结果。",
video: "https://example.com/demo.mp4"
});
```
---
## 依赖说明
| 组件 | 用途 |
|------|------|
| `bricks.VBox` | 布局容器基类,垂直排列子组件 |
| `bricks.MdWidget` | 渲染 Markdown 内容 |
| `bricks.VideoPlayer` | 视频播放组件 |
| `bricks.AudioPlayer` | 音频播放组件 |
| `bricks.Image` | 图像显示组件 |
| `bricks.escapeSpecialChars()` | 工具函数,转义特殊字符防止 XSS |
---
## 注意事项
1. **Base64 音频格式假设**:默认非 HTTP 音频数据为 WAV 格式的 Base64 编码。
2. **图像数组合并问题**:当前 `concat()` 未正确赋值,应改为:
```js
this.images = this.images.concat(data.image);
```
3. **增量更新限制**:虽然内容是累积的,但每次都会重绘全部组件,可能影响性能。
4. **样式依赖**:需要预定义 CSS 类:
- `resp-error`
- `thinking-content`
- `resp-content`
---
## 待优化建议
| 项目 | 建议 |
|------|------|
| 图像去重 | 可增加 URL 去重机制 |
| 媒体并发 | 多个音视频同时播放可能干扰用户体验 |
| 安全性 | `escapeSpecialChars` 应确保防御 XSS 注入 |
| 性能 | 大量文本更新时避免频繁 DOM 重绘 |
---
## 版本信息
- 框架Bricks UI Framework
- 模块:`UserInputView`, `LlmOut`
- 作者:系统自动生成文档
- 时间2025年4月5日
---
✅ 文档结束

250
docs/cn/markdown_viewer.md Normal file
View File

@ -0,0 +1,250 @@
# Bricks Markdown 组件技术文档
> **项目依赖说明**:本组件基于 [marked.js](https://github.com/markedjs/marked) 实现 Markdown 渲染功能。在使用 `bricks.js` 前,需确保已引入 `marked.min.js`
```html
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
```
---
## 模块概述
`bricks.MdWidget``bricks.MarkdownViewer` 是 Bricks 框架中用于渲染和交互式浏览 Markdown 内容的两个核心组件:
- `MdWidget`:基础 Markdown 显示控件,支持本地文本或远程 URL 加载。
- `MarkdownViewer`:增强型 Markdown 浏览器,内置导航栈(前进/后退)功能,适合构建文档阅读器类应用。
---
## 1. `bricks.MdWidget`
### 类定义
```js
class MdWidget extends bricks.JsWidget
```
### 功能描述
一个轻量级的 Markdown 渲染控件,可从字符串 (`mdtext`) 或远程 URL (`md_url`) 加载内容,并使用 `marked.js` 进行解析渲染。支持动态更新内容和链接拦截处理。
### 构造函数参数:`options`
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `mdtext` | String | 否 | 要渲染的 Markdown 文本内容 |
| `md_url` | String | 否 | 远程 Markdown 文件 URL |
| `method` | String | 否(默认 `"GET"` | 请求方式(目前仅支持 GET |
| `params` | Object | 否 | 请求参数(暂未实际用于 GET 请求) |
> ⚠️ 注意:若同时提供 `mdtext``md_url`,优先使用 `mdtext` 并立即渲染。
### 方法列表
#### `constructor(options)`
初始化组件:
- 若提供了 `mdtext`,则直接调用 `_build1()` 渲染。
- 否则延迟加载 `md_url` 内容,并绑定滚动监听。
#### `set_content(content)`
动态设置 Markdown 内容并重新渲染。
- **参数**
- `content`: (String) 新的 Markdown 字符串
#### `show_scroll(event)`
滚动事件回调,输出当前 `window.scrollY` 到调试日志(通过 `bricks.debug`)。
#### `async build()`
异步加载并渲染来自 `opts.md_url` 的 Markdown 内容。若未指定 URL则不执行任何操作。
#### `async _build(md_url)`
私有方法:通过 `bricks.tget(md_url)` 获取远程 Markdown 内容并渲染。
- 触发事件:`loaded`,携带 `{ url: md_url }`
- 自动重写页面内所有 `<a>` 标签为内部跳转行为
#### `_build1()`
将当前 `this.md_content` 使用 `marked.parse()` 解析为 HTML并插入 DOM。
同时劫持所有链接点击事件,实现内部导航。
> 所有 `<a href="...">` 被改为 `href="#"`,并绑定 `onclick` 调用 `_build(原URL)`
#### `getname()`
返回控件名称:
- 若设置了 `this.name`,返回该值;
- 否则返回 `'mdtext'`
#### `getValue()`
获取当前控件的值,格式为对象:
```js
{ [name]: md_content }
```
常用于表单数据收集。
#### `setValue(v)`
设置 Markdown 内容(仅赋值,不触发渲染)。通常配合其他逻辑调用。
---
## 2. `bricks.MarkdownViewer`
### 类定义
```js
class MarkdownViewer extends bricks.VBox
```
### 功能描述
一个容器式 Markdown 浏览器,继承自 `VBox`,支持以下特性:
- 可选的“返回”按钮(导航栈)
- 支持点击 Markdown 中的链接进行页面跳转
- 记录访问历史back_stack支持回退至上一页
### 构造函数参数:`options`
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `navigator` | Boolean | 否(默认 `true` | 是否显示返回按钮 |
| `recommentable` | Boolean | 否 | (预留字段,当前未使用) |
| `md_url` | String | 否 | 初始加载的远程 Markdown URL |
| `mdtext` | String | 否 | 初始本地 Markdown 文本 |
| `method` | String | 否(默认 `"GET"` | HTTP 方法 |
| `params` | Object | 否 | 请求参数对象 |
> ✅ 提示:使用 `absurl()` 处理相对路径,确保 URL 正确解析。
### 方法列表
#### `constructor(options)`
初始化组件:
- 创建内部 `MdWidget` 实例负责渲染
- 注册 `loaded` 事件以维护导航栈
- 设置样式:自动滚动、占满父容器高度
#### `show_scroll(event)`
`MdWidget`,打印当前滚动位置。
#### `async createBackButton()`
动态创建顶部返回按钮HBox + Text 组合):
- 显示文本 `"<<<<<<<"`
- 点击触发 `go_back()`
- 使用 `widgetBuild` 异步生成 UI 元素(调试信息较多)
> 🛠️ 当前 UI 较原始,未来可替换为图标或更美观样式。
#### `add_back_stack(event)`
当新页面加载完成时,将当前 URL 推入 `back_stack` 数组。
- **事件参数**`event.params.url` 表示刚加载的 URL
#### `async go_back(event)`
实现“返回上一页”逻辑:
1. 弹出当前页 URL
2. 再弹出上一页 URL
3. 使用 `mdtext._build(url)` 加载上一页内容
> 🔒 若栈长度小于 2则禁止返回。
#### `async build()` / `async _build(md_url)`
> ❗注意:此处存在代码冗余 —— `_build` 方法在 `MarkdownViewer` 中重复定义但未被调用(应调用的是 `mdtext._build`)。
建议移除该方法或修正逻辑,避免混淆。
---
## 事件系统
### 自定义事件
| 事件名 | 触发时机 | 携带参数 |
|--------|----------|---------|
| `loaded` | Markdown 内容成功加载并渲染后 | `{ url: string }` |
| `scroll` | 容器发生滚动时(绑定到 DOM | 原生 Event 对象 |
可通过 `.bind('event', handler)` 监听。
---
## 工厂注册
```js
bricks.Factory.register('MarkdownViewer', bricks.MarkdownViewer);
bricks.Factory.register('MdWidget', bricks.MdWidget);
```
允许通过 JSON 描述符动态创建组件:
### 示例 JSON 配置
```json
{
"widgettype": "MarkdownViewer",
"options": {
"md_url": "/docs/intro.md",
"navigator": true
}
}
```
---
## 使用示例
### 示例 1直接渲染本地 Markdown
```js
const mdWidget = new bricks.MdWidget({
mdtext: "# Hello\nWelcome to Bricks Markdown Viewer!"
});
document.body.appendChild(mdWidget.dom_element);
```
### 示例 2加载远程 Markdown 并启用导航
```js
const viewer = new bricks.MarkdownViewer({
md_url: 'https://example.com/README.md',
navigator: true
});
document.body.appendChild(viewer.dom_element);
```
### 示例 3动态更新内容
```js
mdWidget.set_content("## New Content\nUpdated at " + new Date().toLocaleString());
```
---
## 注意事项 & 建议
1. **依赖必须提前加载**
```html
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
```
2. **安全性提示**
- `innerHTML = marked.parse(...)` 存在 XSS 风险
- 如需展示用户输入,请先对 HTML 输出做净化处理(如使用 DOMPurify
3. **性能优化建议**
- 避免频繁调用 `set_content``_build`
- 对大型文档考虑分页或懒加载
4. **代码改进建议**
- `MarkdownViewer._build` 方法冗余且可能错误,建议删除或重构
- `createBackButton` 应支持国际化或自定义文本
- 链接拦截逻辑可抽象成独立方法便于复用
---
## 版本信息
- **作者**Bricks Framework 团队
- **依赖库**[marked@latest](https://marked.js.org/)
- **兼容性**现代浏览器ES6+ 支持)
---
📌 **文档版本**v1.0
📅 **最后更新**2025年4月5日

367
docs/cn/menu.md Normal file
View File

@ -0,0 +1,367 @@
# `bricks.Menu` 技术文档
> 本文档为 `bricks.Menu` 类的详细说明,基于其继承自 `bricks.VBox` 的结构和功能设计。
---
## 概述
`bricks.Menu` 是一个用于构建可交互菜单界面组件的类,继承自 `bricks.VBox`。它支持嵌套子菜单、动态加载远程子菜单内容,并能响应菜单项点击事件以打开新窗口或更新目标组件内容。
该组件适用于构建侧边栏菜单、导航菜单或任何树形结构的交互式 UI。
---
## 继承关系
- **父类**: `bricks.VBox`
- **类型**: 自定义 UI 组件类Class-based
---
## 构造函数
```js
constructor(options)
```
### 参数
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `options` | Object | 配置选项对象,继承自 `VBox` 并扩展以下属性 |
#### `options` 支持字段:
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `items` | Array | ✅ | - | 菜单项数组,每个元素是一个菜单项配置对象(见下文) |
| `bgcolor` | String | ❌ | `"white"` | 背景颜色 |
| `target` | String | ❌ | - | 点击菜单项后内容加载的目标组件 ID 或特殊值 `'PopupWindow'` / `'Popup'` |
| `popup_options` | Object | ❌ | `{}` | 当目标为 PopupWindow 或 Popup 时使用的弹窗配置 |
> ⚠️ 注意:`target``popup_options` 可在单个菜单项中覆盖。
### 示例
```js
new bricks.Menu({
items: [
{ label: "首页", url: "/home" },
{ label: "用户管理", submenu: "/api/users" }
],
target: "main_content",
bgcolor: "#f5f5f5"
});
```
---
## 核心方法
### `create_submenu_container()`
创建一个用于容纳子菜单的容器(`VBox`),默认隐藏并带有缩进样式。
#### 返回值
- `{bricks.VBox}` —— 已配置好样式的子菜单容器
#### 样式设置
- `marginLeft: "15px"`
- `display: "none"`
---
### `async menu_clicked(event)`
处理菜单项被点击后的逻辑,根据配置打开页面、弹窗或注入到指定组件。
#### 参数
- `event`: DOM 事件对象,包含 `params` 字段,即菜单项数据。
#### 行为逻辑
1. 解析目标类型:
- 若 `target === 'PopupWindow'`:创建 `PopupWindow` 实例
- 若 `target === 'Popup'`:创建 `Popup` 实例
- 否则:查找对应 ID 的组件 (`bricks.getWidgetById`)
2. 使用 `widgetBuild` 动态生成 URL Widget 并插入目标组件
3. 触发全局 `command` 事件,携带原始菜单项参数
#### 触发事件
- `command`:携带菜单项信息,可用于外部监听执行命令
#### 日志输出
- 失败时打印错误日志:
- 目标组件未找到
- widget 构建失败
---
### `create_children(w, items)`
递归创建菜单项及其子菜单。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `w` | `bricks.Container` | 容器组件,用于添加生成的菜单项 |
| `items` | Array | 菜单项列表 |
#### 子菜单类型判断
| 条件 | 类型 | 说明 |
|------|------|------|
| `item.items` 存在 | 静态子菜单 | 直接递归创建 |
| `item.submenu` 存在 | 动态子菜单 | 点击时通过 URL 加载 |
| 其他 | 叶节点菜单项 | 绑定点击事件触发 `item_click` |
#### 内部行为
- 创建 `HBox` 表示菜单项
- 添加图标与文本
- 绑定点击/展开事件
- 支持国际化i18n、换行、左对齐等文本特性
---
### `async get_submenu_items(url)`
从远程 URL 获取子菜单数据JSON 格式)。
#### 参数
- `url` (String): 请求地址
#### 返回值
- `Promise<Array>`: 菜单项数组(`data.options.items`
#### 示例响应格式
```json
{
"options": {
"items": [
{ "label": "子项1", "url": "/sub1" },
{ "label": "子项2", "url": "/sub2" }
]
}
}
```
---
### `async load_submenu(container, event)`
懒加载动态子菜单内容。
#### 参数
- `container`: 子菜单容器(`VBox`
- `event`: 点击事件
#### 流程
1. 阻止事件冒泡
2. 若未加载过,则调用 `get_submenu_items()` 获取数据
3. 调用 `create_children()` 填充内容
4. 切换显示/隐藏状态
> ✅ 实现“点击展开 → 首次加载 → 缓存”机制
---
### `items_toggle_hide(w, event)`
切换静态子菜单的可见性。
#### 参数
- `w`: 子菜单容器
- `event`: 事件对象
#### 行为
- 执行 `toggle_hide()` 显示或隐藏
- 阻止事件冒泡
---
### `create_menuitem(item)`
创建单个菜单项 UI 组件(`HBox`)。
#### 参数
- `item`: 菜单项配置对象
| 属性 | 类型 | 说明 |
|------|------|------|
| `label` | String | 显示文本(支持 i18n |
| `icon` | String | 图标 URL |
| `url` | String | 导航链接 |
| `name` | String | 名称(备用标题) |
| 其他字段 | Any | 将直接挂载到返回的 widget 上 |
#### 返回值
- `{bricks.HBox}` 包含图标和文本的水平布局组件
#### 子组件
| 组件 | 类型 | 说明 |
|------|------|------|
| `iw` | `Icon``BlankIcon` | 图标区域,若无图标则占位 |
| `tw` | `Text` | 文本标签,启用自动换行、左对齐、填充样式 |
#### 样式类
- 应用 CSS 类名:`menuitem_css` 或默认 `'menuitem'`
---
### `regen_menuitem_event(item, event)`
菜单项点击事件处理器,用于非容器型菜单项。
#### 行为
- 记录日志
- 触发 `item_click` 事件并传递 `item`
- 阻止事件冒泡
---
## 事件系统
### 监听的事件
- `item_click`: 当菜单项被点击时触发,由 `regen_menuitem_event` 发出
### 分发的事件
| 事件名 | 数据 | 说明 |
|--------|------|------|
| `item_click` | `item` 对象 | 内部使用,通知上级处理点击 |
| `command` | `item` 对象 | 外部可监听,表示一个命令被执行 |
---
## 配置与扩展
### 默认弹窗选项获取
- `bricks.get_popupwindow_default_options()`
- `bricks.get_popup_default_options()`
可通过 `popup_options` 合并自定义配置(使用 `bricks.extend`
### 国际化支持
- `Text` 组件启用 `i18n: true`,支持多语言翻译
### 样式定制
- 可通过 `menuitem_css` 自定义菜单项样式类
- 使用 `set_css()``set_style()` 进行细粒度控制
---
## 注册与工厂模式
```js
bricks.Factory.register('Menu', bricks.Menu);
```
允许通过字符串标识符创建实例:
```js
bricks.createWidget("Menu", options);
```
---
## 使用场景示例
### 场景 1静态树形菜单
```js
const menu = new bricks.Menu({
items: [
{
label: "仪表盘",
icon: "/icons/dashboard.png",
url: "/dashboard"
},
{
label: "设置",
items: [
{ label: "账户", url: "/settings/account" },
{ label: "安全", url: "/settings/security" }
]
}
],
target: "main_panel"
});
```
### 场景 2动态加载子菜单
```js
const menu = new bricks.Menu({
items: [
{
label: "产品分类",
submenu: "/api/categories",
icon: "/icons/folder.png"
}
],
target: "PopupWindow",
popup_options: {
width: 800,
height: 600
}
});
```
---
## 注意事项
1. **性能优化**:动态子菜单仅在首次展开时请求数据,后续展开直接显示缓存内容。
2. **事件隔离**:所有点击事件均调用 `stopPropagation()` 防止意外冒泡。
3. **URL Widget 构建**:依赖 `bricks.widgetBuild()` 异步构建,需确保环境已初始化。
4. **目标组件存在性**:应确保 `target` 所指组件已注册且可用。
---
## 依赖模块
| 模块 | 用途 |
|------|------|
| `bricks.VBox`, `bricks.HBox` | 布局容器 |
| `bricks.Icon`, `bricks.BlankIcon` | 图标显示 |
| `bricks.Text` | 文本渲染与 i18n |
| `bricks.PopupWindow`, `bricks.Popup` | 弹窗组件 |
| `bricks.HttpJson` | JSON 数据请求 |
| `bricks.Factory`, `bricks.widgetBuild` | 组件工厂与异步构建 |
| `bricks.extend` | 对象合并工具函数 |
---
## 版本信息
- **作者**: Bricks Framework Team
- **版本**: v1.0(基础功能稳定)
- **最后更新**: 2025年4月5日
---
✅ *本组件适用于构建灵活、可扩展的菜单系统,推荐结合路由系统或 CMS 后台使用。*

218
docs/cn/message.md Normal file
View File

@ -0,0 +1,218 @@
# `bricks.Message``bricks.Error` 技术文档
> 基于 Bricks UI 框架的消息弹窗组件
---
## 概述
`bricks.Message``bricks.Error` 是基于 `bricks.PopupWindow` 构建的轻量级消息提示组件,用于在 Web 应用中显示文本信息或错误提示。该模块提供统一的 API 接口(`bricks.show_message``bricks.show_error`),便于快速调用。
- `bricks.Message`:通用消息弹窗。
- `bricks.Error`:专用于显示错误信息的弹窗,样式上更突出。
- 支持国际化i18n、自动换行、居中对齐等特性。
---
## 继承结构
```
bricks.PopupWindow
└── bricks.Message
└── bricks.Error
```
---
## 类定义
### `bricks.Message`
继承自 `bricks.PopupWindow`,用于创建可配置的消息提示窗口。
#### 构造函数
```js
new bricks.Message(opts)
```
##### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts` | Object | ✅ | 配置选项对象 |
###### `opts` 属性
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `title` | String | - | 弹窗标题 |
| `message` | String | - | 要显示的消息正文内容 |
| `auto_open` | Boolean | `true` | 是否在构造后自动打开窗口(由构造函数内部设置) |
| `cheight` | Number | `9` | 内容区域高度(单位:网格) |
| `cwidth` | Number | `16` | 内容区域宽度(单位:网格) |
> ⚠️ 注意:`auto_open` 在构造函数中被强制设为 `true`,因此所有 `Message` 实例创建后会立即显示。
#### 方法
##### `create_message_widget()`
私有方法,负责构建消息内容区域的 UI 结构。
**UI 结构如下:**
```
Filler (填充容器)
└── VScrollPanel (垂直滚动面板)
└── Text (文本控件,支持换行和居中)
```
- 使用 `bricks.Filler` 作为内容填充层。
- 使用 `bricks.VScrollPanel` 包裹文本以支持长文本滚动。
- 使用 `bricks.Text` 显示消息,启用:
- 自动换行 (`wrap: true`)
- 水平居中对齐 (`halign: 'middle'`)
- 国际化支持 (`i18n: true`)
##### `set_css(cssClass)`
设置弹窗的 CSS 样式类前缀为 `'message'`,用于应用主题样式。
---
### `bricks.Error`
继承自 `bricks.Message`,专用于显示错误信息。
#### 构造函数
```js
new bricks.Error(opts)
```
##### 参数
`bricks.Message``opts`
##### 行为差异
- 调用父类构造函数(即 `super(opts)`)完成初始化。
- 额外调用 `this.set_css('error')`,将样式类切换为 `'error'`,通常表现为红色边框/背景等视觉警示。
---
## 全局快捷函数
### `bricks.show_message(opts)`
快速显示一条普通消息。
```js
bricks.show_message({
title: "提示",
message: "操作已成功完成。"
});
```
#### 参数
`bricks.Message``opts`
> 若未指定 `cheight``cwidth`,则默认使用 `9``16`
---
### `bricks.show_error(opts)`
快速显示一条错误消息。
```js
bricks.show_error({
title: "出错了",
message: "无法连接到服务器,请检查网络。"
});
```
#### 参数
`bricks.Message``opts`
> 视觉样式通过 `set_css('error')` 强化,适合错误场景。
---
## 工厂注册
为支持动态创建机制,两类组件已在 `bricks.Factory` 中注册:
```js
bricks.Factory.register('Message', bricks.Message);
bricks.Factory.register('Error', bricks.Error);
```
这意味着可以通过工厂模式按名称实例化这些组件:
```js
var msg = bricks.Factory.create('Message', { title: "Hello", message: "World" });
```
---
## 使用示例
### 显示普通消息
```js
bricks.show_message({
title: "欢迎",
message: "欢迎使用 Bricks 框架!这是一个示例消息。",
cheight: 10,
cwidth: 20
});
```
### 显示错误消息
```js
bricks.show_error({
title: "加载失败",
message: "请求的数据未能获取,请稍后再试。长时间失败请联系管理员。",
cheight: 12
});
```
---
## 样式说明
| 组件 | CSS Class 前缀 | 典型用途 |
|------|----------------|----------|
| `bricks.Message` | `.message-*` | 一般通知、提示 |
| `bricks.Error` | `.error-*` | 错误、警告信息 |
开发者应在 CSS 中定义相应的样式规则以实现美观的视觉效果。
---
## 依赖项
确保以下组件已加载:
- `bricks.PopupWindow`
- `bricks.Filler`
- `bricks.VScrollPanel`
- `bricks.Text`
- `bricks.Factory`
---
## 版本信息
- 创建时间:未知
- 框架版本Bricks UI假设
- 作者Bricks 团队 / 开发者社区
---
✅ **推荐使用 `bricks.show_message()``bricks.show_error()` 快捷函数进行调用,避免手动管理窗口生命周期。**

314
docs/cn/miniform.md Normal file
View File

@ -0,0 +1,314 @@
# `bricks.MiniForm` 技术文档
> **MiniForm** 是一个基于 `bricks.HBox` 的轻量级表单组件,用于动态切换和输入不同字段的数据。它支持通过下拉选择器快速切换输入项,并实时触发输入事件。
---
## 概述
`bricks.MiniForm` 类继承自 `bricks.HBox`,提供了一个紧凑的表单界面,适用于需要在多个输入字段之间快速切换的场景(如搜索框、参数配置等)。该组件包含:
- 一个下拉选择器(用于选择当前字段)
- 一个动态输入控件(根据所选字段类型自动创建)
- 支持自定义字段属性和默认值
- 可扩展的输入事件机制
---
## 继承关系
```
bricks.HBox
└── bricks.MiniForm
```
---
## 构造函数
```js
new bricks.MiniForm(options)
```
### 参数
| 参数名 | 类型 | 必填 | 描述 |
|-------|------|------|------|
| `options` | Object | ✅ | 配置对象,结构如下 |
#### `options` 结构
| 属性 | 类型 | 必填 | 默认值 | 描述 |
|------|------|------|--------|------|
| `defaultname` | String | ❌ | 第一个字段的 `name` | 默认激活的字段名称 |
| `label_width` | String/Number | ❌ | - | 标签列宽度(可被字段级覆盖) |
| `input_width` | String/Number | ❌ | - | 输入框宽度(可被字段级覆盖) |
| `params` | Object | ❌ | `{}` | 固定附加参数,在 `getValue()` 中返回 |
| `fields` | Array&lt;Object&gt; | ✅ | - | 字段定义数组,每个字段包含以下子属性 |
##### `fields[i]` 字段定义
| 属性 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `name` | String | ✅ | 字段唯一标识符(如:`"username"` |
| `label` | String | ✅ | 显示名称(出现在下拉列表中) |
| `icon` | String | ❌ | 图标类名(可选,用于视觉增强) |
| `uitype` | String | ✅ | 控件类型(如 `"text"`, `"number"`, `"code"` 等),需与 `Input.factory` 兼容 |
| `uiparams` | Object | ❌ | 传递给具体输入控件的额外配置参数 |
---
## 示例配置
```js
var miniForm = new bricks.MiniForm({
defaultname: 'email',
params: { action: 'search' },
fields: [
{
name: 'username',
label: '用户名',
uitype: 'text',
uiparams: { placeholder: '请输入用户名' }
},
{
name: 'email',
label: '邮箱',
uitype: 'text',
uiparams: { type: 'email', placeholder: '请输入邮箱' }
},
{
name: 'count',
label: '数量',
uitype: 'number',
uiparams: { min: 1, max: 100 }
}
]
});
```
---
## 方法说明
### `constructor(opts)`
初始化 MiniForm 实例并调用 `build()`
#### 行为:
- 设置容器宽高为 `'auto'`
- 调用父类构造函数
- 执行 `build()` 构建 UI
---
### `build()`
构建整个组件的 UI包括选项选择器和初始输入控件。
#### 步骤:
1. 确定默认字段名(优先使用 `defaultname`,否则取第一个字段)
2. 调用 `build_options()` 创建下拉选择器
3. 调用 `build_widgets(name)` 初始化对应输入控件
---
### `build_options()`
创建一个下拉选择控件(`this.choose`),允许用户切换当前编辑的字段。
#### 使用 `Input.factory` 创建的选择器配置:
```js
{
width: "90px",
name: "name",
uiType: "code", // 假设为下拉或代码选择器
valueField: 'name', // 绑定值为字段的 name
textField: 'label', // 显示文本为 label
data: this.opts.fields
}
```
> ⚠️ 注:`uiType: "code"` 可能表示一种特殊下拉控件,实际应确保其行为符合预期(例如 ComboBox 或 Select
绑定事件:`changed``change_input(e)`
---
### `build_widgets(name)`
根据指定字段名动态创建对应的输入控件。
#### 参数:
- `name`: 字段名(必须存在于 `fields` 中)
#### 流程:
1. 若已有输入控件,则解绑 `input` 事件
2. 清空当前所有控件
3. 添加选择器控件 `this.choose`
4. 查找匹配字段定义 `f`
5. 复制字段描述并设置 `width: 'auto'`
6. 使用 `Input.factory(desc)` 创建输入控件 `i`
7. 绑定 `input` 事件到 `input_handle`
8. 将新控件加入布局
9. 保存引用至 `this.input`
---
### `change_input(e)`
当下拉选择器值改变时触发,重新构建输入控件。
#### 行为:
- 获取当前选择的字段名 `this.choose.value`
- 调用 `build_widgets(name)` 切换输入控件
---
### `input_handle(e)`
当输入内容发生变化时触发,用于广播事件。
#### 行为:
1. 调用 `getValue()` 获取完整数据
2. 输出调试信息(通过 `bricks.debug`
3. 触发 `input` 事件,携带数据 `d`
```js
this.dispatch('input', d);
```
> 可通过 `.bind('input', handler)` 监听此事件
---
### `getValue() → Object`
获取当前表单的合并值。
#### 返回值结构:
```js
{
// 来自 this.opts.params 的固定参数
action: 'search',
// 来自当前输入控件的动态值
username: 'john_doe'
}
```
#### 逻辑:
- 初始化为 `params` 对象(若未设置则为空 `{}`
- 合并当前输入控件的值(`this.input.getValue()`
- 使用 `bricks.extend(d, v)` 进行浅合并
---
### `show_options(e)`
显示下拉选择器(调试用途或外部调用)。
#### 行为:
- 输出调试日志
- 调用 `this.choose.show()` 显示选择器
> 主要用于开发调试或定制交互逻辑
---
### `clear_widgets()`
> 📌 注意:此方法未在代码中定义,但被调用。
假设其来自父类 `HBox`,功能为清空所有子控件。
---
## 事件系统
### 支持监听的事件
| 事件名 | 触发时机 | 携带数据 |
|--------|----------|---------|
| `input` | 输入值变化时 | 合并后的表单数据对象 |
#### 示例监听:
```js
miniForm.bind('input', function(data) {
console.log('Current form data:', data);
// { action: 'search', username: 'testuser' }
});
```
---
## 工厂注册
```js
bricks.Factory.register('MiniForm', bricks.MiniForm);
```
允许通过工厂模式创建实例:
```js
var form = bricks.Factory.create('MiniForm', options);
```
---
## 设计特点
| 特性 | 说明 |
|------|------|
| ✅ 动态输入切换 | 支持运行时更换输入控件类型 |
| ✅ 模块化结构 | 基于 `Input.factory` 实现控件解耦 |
| ✅ 可扩展性 | 支持任意 `uitype``uiparams` |
| ✅ 事件驱动 | 提供标准 `input` 事件接口 |
| ⚠️ 宽度控制有限 | 当前 `label_width` / `input_width` 未完全实现(可能依赖样式或其他机制) |
---
## 注意事项
1. **`Input.factory` 依赖**
必须确保 `Input.factory` 支持所有声明的 `uitype` 类型。
2. **`objcopy()` 函数**
代码中使用了 `objcopy(f)`,应确保全局存在此工具函数(通常为对象深拷贝或浅拷贝)。
3. **`bricks.extend`**
用于合并对象,类似 jQuery 的 `$.extend` 或 Lodash 的 `_.assign`
4. **UI 渲染顺序**
每次切换字段都会重建输入控件,适合字段较少的场景;高频切换可能影响性能。
5. **样式兼容性**
推荐配合 CSS 设置 `.bricks-miniform` 或相关类以优化布局。
---
## 调试支持
启用调试模式后,会输出以下信息:
- `show_options()` 调用记录
- `input_handle()` 中的实时数据
可通过 `bricks.debug = console.log` 启用。
---
## 总结
`bricks.MiniForm` 是一个简洁高效的动态表单组件,特别适用于:
- 多条件搜索栏
- 快捷参数输入
- 动态配置面板
结合 `bricks` 框架的输入控件体系,能够快速搭建灵活的用户界面。
---
**推荐使用场景**:工具条、查询过滤器、快捷操作面板
🔧 **可优化方向**:缓存输入控件实例、支持更多布局配置、增加验证机制

295
docs/cn/modal.md Normal file
View File

@ -0,0 +1,295 @@
# `bricks` 模态组件技术文档
> **版本1.0**
> **模块Modal, BaseModal, ModalForm**
本文档描述了 `bricks` 框架中与模态窗口相关的类,包括 `BaseModal``Modal``ModalForm`。这些类用于创建可重用的弹出层Modal组件支持自定义定位、样式、自动关闭、超时关闭等功能。
---
## 目录
- [概述](#概述)
- [核心类结构](#核心类结构)
- [`bricks.BaseModal`](#bricksbasemodal)
- [构造函数参数](#构造函数参数-base-modal)
- [属性](#属性-base-modal)
- [方法](#方法-base-modal)
- [`bricks.Modal`](#bricksmodal)
- [构造函数参数](#构造函数参数-modal)
- [属性](#属性-modal)
- [方法](#方法-modal)
- [`bricks.ModalForm`](#bricksmodalfom)
- [构造函数参数](#构造函数参数-modalfom)
- [属性](#属性-modalfom)
- [方法](#方法-modalfom)
- [注册与使用](#注册与使用)
- [示例代码](#示例代码)
---
## 概述
`bricks` 提供了一套基于布局系统的模态对话框系统:
- `BaseModal` 是所有模态组件的基础类。
- `Modal` 是一个带标题栏和内容区域的标准模态窗口。
- `ModalForm` 是一个用于展示表单的模态窗口,集成 `Form` 组件并处理提交逻辑。
- 所有模态窗口通过 Z-index 管理堆叠顺序,并支持锚点对齐(`archor`)、目标容器绑定等特性。
---
## 核心类结构
```text
bricks.Layout
└── bricks.BaseModal
├── bricks.Modal
└── bricks.ModalForm (extends PopupWindow)
```
> 注意:`ModalForm` 实际继承自 `PopupWindow` 而非 `Modal`,但功能上属于模态系列组件。
---
## `bricks.BaseModal`
基础模态类,提供通用的模态行为,如显示/隐藏、Z-index 控制、目标挂载等。
### 构造函数参数 (Base Modal)
| 参数 | 类型 | 描述 |
|------|------|------|
| `target` | `string``Layout` | 模态窗口挂载的目标容器。字符串表示 ID对象为 Layout 实例。默认为 `bricks.Body`。 |
| `auto_open` | `boolean` | 是否在添加子组件后自动打开模态。 |
| `auto_close` | `boolean` | (未启用)是否点击背景关闭模态。 |
| `org_index` | `number` | 初始 z-index 值(暂未使用)。 |
| `width` | `string` | 内容面板宽度(如 `'500px'`, `'80%'`)。 |
| `height` | `string` | 内容面板高度。 |
| `bgcolor` | `string` | 内容面板背景色,默认 `#e8e8e8`。 |
| `title` | `string` | 标题文本(仅被 `Modal` 使用)。 |
| `timeout` | `number` | 自动关闭延迟时间毫秒0 表示不自动关闭。 |
| `archor` | `string` | 锚点位置,控制内容居中方式:<br>`tl`, `tc`, `tr`, `cl`, `cc`, `cr`, `bl`, `bc`, `br`<br>默认值:`'cc'`(居中) |
### 属性 (Base Modal)
| 属性 | 类型 | 描述 |
|------|------|------|
| `panel` | `VBox` | 实际内容容器,内部垂直布局。 |
| `timeout` | `number` | 自动关闭延时ms。 |
| `timeout_task` | `Task` | 定时任务句柄,用于取消定时关闭。 |
| `target_w` | `Layout` | 解析后的目标挂载容器。 |
| `ancestor_add_widget` | `Function` | 保存父类 `add_widget` 方法的引用。 |
### 方法 (Base Modal)
#### `create()`
创建模态外层 DOM 元素:
- 使用 `<div>` 作为遮罩层。
- 设置 `position: fixed` 遮罩全屏。
- 背景颜色为半透明黑色 (`rgba(0,0,0,0.4)`)。
- 初始隐藏(`display: none`)。
- 自动分配递增的 `z-index`
#### `get_zindex() → number`
获取下一个可用的 `z-index` 值,从 `bricks.min_zindex` 开始递增。
> 默认起始值:`5000`
#### `add_widget(widget, index)`
`panel` 添加子控件。如果设置了 `auto_open: true`,则立即调用 `open()`
#### `open()`
显示模态窗口:
- 将 `dom_element.style.display` 设为空字符串(即显示)。
- 若设置了 `timeout > 0`,启动延迟关闭任务。
- 触发 `opened` 事件。
#### `dismiss()`
关闭并移除模态窗口:
- 隐藏元素。
- 取消超时任务。
- 从父容器中移除自身。
- 触发 `dismissed` 事件。
- 异常捕获防止中断。
---
## `bricks.Modal`
标准模态对话框,继承自 `BaseModal`,带有可关闭标题栏。
### 构造函数参数 (Modal)
`BaseModal`,额外支持:
| 参数 | 类型 | 描述 |
|------|------|------|
| `title` | `string` | 显示在标题栏中的文字。 |
### 属性 (Modal)
| 属性 | 类型 | 描述 |
|------|------|------|
| `title_box` | `HBox` | 水平布局的标题栏容器。 |
| `title_w` | `Filler` | 标题文本占位容器。 |
| `content` | `Filler` | 主体内容容器,填充剩余空间。 |
### 方法 (Modal)
#### `create_title()`
创建标题栏:
- 包含一个水平盒子 `HBox`
- 左侧显示标题文本(使用 `Text` 组件,支持国际化 `i18n` 和动态尺寸 `dynsize`)。
- 右侧放置关闭图标SVG 图标,绑定 `click → dismiss()`)。
- 添加 CSS 类名 `title`
#### `add_widget(widget, index)`
将组件添加到 `content` 区域。若 `auto_open` 为真,则自动打开模态。
#### `click_handler(event)`
点击事件处理器(当前注释状态):
- 如果点击的是模态遮罩层(非内容区域),触发 `dismiss()`
- 用于实现“点击背景关闭”功能(目前未启用)。
> ⚠️ 当前该功能已被注释,需手动启用。
---
## `bricks.ModalForm`
用于快速弹出表单的模态窗口,集成异步表单构建和提交处理。
### 构造函数参数 (ModalForm)
| 参数 | 类型 | 描述 |
|------|------|------|
| `title` | `string` | 表单标题。 |
| `description` | `string` | 表单描述信息。 |
| `fields` | `Array<FieldOptions>` | 表单字段定义数组。 |
| `binds` | `Array<BindConfig>` | 数据绑定配置。 |
| `user_data` | `any` | 用户自定义数据(可选)。 |
| `submit_url` | `string` | 提交 URL可通过实例设置。 |
| 其他 | 同 `PopupWindow` | 如 `width`, `height`, `archor` 等。 |
### 属性 (ModalForm)
| 属性 | 类型 | 描述 |
|------|------|------|
| `form` | `Form` | 动态生成的表单实例。 |
| `submit_url` | `string` | 表单提交地址(可选)。 |
### 方法 (ModalForm)
#### `build_form() → Promise<void>`
异步构建表单:
- 延迟 200ms 执行(确保 DOM 就绪)。
- 使用 `bricks.widgetBuild()` 动态创建 `Form` 组件。
- 添加至模态内容区。
- 绑定以下事件:
- `submit`: 触发 `form_submit`
- `submited`: 转发服务器响应
- `cancel`: 关闭模态
- 最后调用 `open()` 显示。
#### `_getValue() → any`
获取原始表单数据(未经格式化)。
#### `getValue() → any`
获取经过验证和处理的表单数据。
#### `form_submit()`
处理表单提交:
- 获取数据并派发 `submit` 事件。
- 立即关闭模态(`dismiss()`)。
#### `form_submited(event)`
当表单成功提交后,转发 `submited` 事件及其参数。
---
## 注册与使用
```js
// 注册工厂类,便于通过字符串创建
bricks.Factory.register('Modal', bricks.Modal);
bricks.Factory.register('ModalForm', bricks.ModalForm);
```
这意味着你可以使用如下方式动态创建:
```js
let modal = bricks.Factory.create('Modal', { title: '提示', width: '400px' });
```
---
## 示例代码
### 创建一个简单模态窗口
```js
let modal = new bricks.Modal({
title: '欢迎',
width: '500px',
height: '300px',
auto_open: true,
archor: 'cc'
});
modal.add_widget(new bricks.Text({ otext: '这是模态内容!' }));
```
### 创建一个表单模态
```js
let formModal = new bricks.ModalForm({
title: '用户信息',
description: '请填写以下信息',
fields: [
{ name: 'name', label: '姓名', type: 'text' },
{ name: 'age', label: '年龄', type: 'number' }
],
binds: [
['name', 'user.name'],
['age', 'user.age']
]
});
formModal.bind('submit', function(data) {
console.log('提交数据:', data);
});
```
---
## 注意事项
1. **Z-index 管理**:所有模态共享全局 `last_zindex` 计数器,确保新窗口总在最上层。
2. **锚点定位**:依赖外部函数 `archorize(el, pos)` 实现元素定位,请确保其存在。
3. **资源路径**:关闭图标使用 `bricks_resource('imgs/delete.svg')` 加载,需正确配置资源路径。
4. **事件解绑**`click_handler` 的绑定/解绑逻辑目前被注释,如需启用请解除注释并测试。
---
## 依赖说明
- `bricks.Layout`, `bricks.VBox`, `bricks.HBox`, `bricks.Filler`, `bricks.Text`, `bricks.Svg` —— 布局与基础组件。
- `bricks.widgetBuild()` —— 异步组件构建工具。
- `schedule_once(fn, delay)` —— 延迟执行工具函数。
- `objget(obj, key, default)` —— 安全取值工具。
- `archorize(element, position)` —— 锚点定位函数。
---
**建议用途**:适用于消息提示、确认框、登录弹窗、动态表单提交等场景。
🔧 **扩展建议**
- 支持键盘 ESC 关闭。
- 添加动画过渡效果。
- 支持拖拽移动(针对 `ModalForm`)。
---
📖 *文档版本1.0*
📅 *最后更新2025年4月5日*

View File

@ -0,0 +1,170 @@
# `MultipleStateImage` 技术文档
> **命名空间**: `bricks.MultipleStateImage`
> **继承自**: `bricks.Layout`
> **注册名称**: `'MultipleStateImage'`(可通过 `bricks.Factory` 创建)
---
## 概述
`MultipleStateImage` 是一个可交互的图像组件,支持在多个预定义状态之间切换。每个状态对应一张不同的图片 URL。用户点击图像时组件会自动切换到下一个状态并更新显示的图片。
该组件适用于需要通过点击切换不同视觉状态的场景,例如:开关按钮、多状态图标、轮播式图片控件等。
---
## 构造函数
```js
new bricks.MultipleStateImage(options)
```
### 参数
| 参数名 | 类型 | 必填 | 说明 |
|-------|------|------|------|
| `opts` | `Object` | ✅ | 配置选项对象 |
#### `opts` 配置项
| 属性 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `state` | `String` | ✅ | 初始状态名称,必须是 `urls` 中的一个键 |
| `urls` | `Object<String, String>` | ✅ | 状态与图片 URL 的映射表,格式为 `{ stateName: imageUrl }` |
| `width` | `Number` \| `String` | ❌ | 图像宽度(可选,传递给内部 `bricks.Image` 组件) |
| `height` | `Number` \| `String` | ❌ | 图像高度(可选,传递给内部 `bricks.Image` 组件) |
---
## 示例用法
```js
var image = new bricks.MultipleStateImage({
state: 'off',
urls: {
off: 'images/light_off.png',
on: 'images/light_on.png'
},
width: 100,
height: 100
});
// 添加到父容器
parent.add_widget(image);
// 监听状态变化
image.bind('state_changed', function(newState) {
console.log('当前状态:', newState);
});
```
---
## 方法
### `set_state(state)`
手动设置当前状态并更新图像。
- **参数**:
- `state` (`String`) - 要切换到的状态名,必须存在于 `opts.urls` 中。
- **行为**:
- 更新内部状态 `this.state`
- 调用内部图像的 `set_url()` 方法加载新图片
```js
image.set_state('on');
```
---
### `change_state(event)`
**私有方法 / 事件处理器** — 响应图像点击事件,自动切换到下一个状态。
- **触发方式**: 用户点击图像时自动调用
- **行为**:
- 阻止事件冒泡(`stopPropagation`
- 在 `urls` 的状态列表中循环切换至下一个状态
- 若已是最后一个状态,则回到第一个状态(循环切换)
- 触发 `state_changed` 事件
> ⚠️ 通常无需直接调用此方法,已绑定在图像的 `'click'` 事件上。
---
## 事件
### `state_changed`
当图像状态发生改变时触发。
- **回调参数**:
- `newState` (`String`) - 当前切换后的状态名
```js
image.bind('state_changed', function(state) {
console.log('状态已切换为:', state);
});
```
---
## 内部结构
- 使用 `bricks.Image` 实例作为核心图像渲染组件
- 将自身作为布局容器,通过 `add_widget(this.img)` 管理子组件
- 状态切换逻辑基于 `Object.keys(this.opts.urls)` 的顺序进行循环
---
## 注意事项
1. **状态顺序依赖对象键的枚举顺序**
JavaScript 中对象属性的遍历顺序在现代引擎中通常是插入顺序,建议确保 `urls` 对象的键按预期顺序定义。
2. **URL 合法性需提前校验**
组件不检查图片 URL 是否有效,错误需由外部处理。
3. **事件绑定自动完成**
构造函数中已自动绑定 `'click'` 事件,无需重复绑定。
4. **工厂注册**
已通过 `bricks.Factory.register('MultipleStateImage', ...)` 注册,可在声明式模板中使用类型名创建。
---
## 工厂注册
```js
bricks.Factory.register('MultipleStateImage', bricks.MultipleStateImage);
```
允许通过配置方式创建实例:
```js
bricks.Factory.create({
type: 'MultipleStateImage',
state: 'off',
urls: {
off: 'images/off.png',
on: 'images/on.png'
},
width: 50,
height: 50
});
```
---
## 版权与归属
- 所属模块: `bricks`
- 组件名: `MultipleStateImage`
- 作者: (根据项目上下文补充)
- 版本: 1.0.0
---
✅ *文档结束*

117
docs/cn/myoperator.md Normal file
View File

@ -0,0 +1,117 @@
# `Oper` 类技术文档
## 概述
`Oper` 是一个用于封装数值并支持自定义加法操作的 JavaScript 类。通过该类,可以创建包含数值的对象,并使用内置的方法执行对象间的加法运算。
> **注意**:该类中定义了两个功能几乎相同的加法方法 `__plus__``__add__`,可能是为了兼容不同命名习惯或未来扩展预留。
---
## 构造函数
### `constructor(v)`
初始化一个 `Oper` 实例,将传入的值保存在 `value` 属性中。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `v` | `number` | 初始化的数值 |
#### 示例
```javascript
const a = new Oper(5);
console.log(a.value); // 输出: 5
```
---
## 方法
### `__plus__(a, b)`
执行两个 `Oper` 对象的加法操作,返回一个新的 `Oper` 实例,其值为两个操作数 `value` 属性之和。该方法会输出两个操作数到控制台。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `a` | `Oper` | 第一个操作数 |
| `b` | `Oper` | 第二个操作数 |
#### 返回值
- 类型:`Oper`
- 说明:一个新的 `Oper` 实例,其 `value``a.value + b.value`
#### 示例
```javascript
const x = new Oper(3);
const y = new Oper(7);
const result = new Oper().__plus__(x, y);
// 控制台输出: Oper { value: 3 }, Oper { value: 7 }
console.log(result.value); // 输出: 10
```
---
### `__add__(a, b)`
`__plus__` 功能完全相同,执行两个 `Oper` 对象的加法操作并返回新实例,同时打印操作数到控制台。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `a` | `Oper` | 第一个操作数 |
| `b` | `Oper` | 第二个操作数 |
#### 返回值
- 类型:`Oper`
- 说明:一个新的 `Oper` 实例,其 `value``a.value + b.value`
#### 示例
```javascript
const m = new Oper(4);
const n = new Oper(6);
const sum = new Oper().__add__(m, n);
// 控制台输出: Oper { value: 4 }, Oper { value: 6 }
console.log(sum.value); // 输出: 10
```
---
## 注意事项
1. 所有方法均为实例方法,调用时需要先创建 `Oper` 实例。
2. `__plus__``__add__` 行为一致,可能存在冗余,建议根据实际需求保留其一或用于不同语义场景。
3. 当前实现未做类型检查,若传入非 `Oper` 实例或 `value` 非数字,可能导致运行时错误。
4. 方法设计为静态操作风格(接受两个操作数),而非基于当前实例的 `this.value` 进行计算,这可能与常规面向对象设计不符,使用时需注意。
---
## 建议改进
```js
// 改进建议:基于实例的加法
class Oper {
constructor(v) {
this.value = v;
}
add(other) {
return new Oper(this.value + other.value);
}
}
// 使用方式更自然
const a = new Oper(5);
const b = new Oper(3);
const c = a.add(b);
console.log(c.value); // 8
```
---
## 版本信息
- 语言JavaScript (ES6+)
- 兼容性:现代浏览器及 Node.js 环境

164
docs/cn/myvad.md Normal file
View File

@ -0,0 +1,164 @@
# VAD语音活动检测模块技术文档
> **基于 `@ricky0912/vad-web` 的 Web 端语音活动检测集成方案**
---
## 概述
本模块提供了一个轻量级的语音活动检测Voice Activity Detection, VAD功能封装用于在浏览器环境中实时检测用户语音的开始与结束并在语音结束后回调处理音频数据。该模块依赖于 [`vad-web`](https://www.npmjs.com/package/@ricky0123/vad-web) 库,底层使用 ONNX Runtime Web 实现高性能推理。
主要功能:
- 实时麦克风音频采集
- 自动检测语音段Speech Segment
- 在语音结束后触发回调,返回 `Float32Array` 格式的 16kHz 音频样本
- 支持动态启用/禁用 VAD
---
## 依赖引入
```html
<!-- 引入 ONNX Runtime WebVAD 依赖) -->
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
<!-- 引入 vad-web 库 -->
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.7/dist/bundle.min.js"></script>
```
> ⚠️ 注意:必须按顺序加载 `ort.js` 后再加载 `vad-web`,否则会报错。
---
## API 接口
### `bricks.enable_vad(func)`
启动语音活动检测,并设置语音结束时的回调函数。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `func` | `Function` | 回调函数,接收一个参数 `audio`,类型为 `Float32Array`,表示采样率为 16000Hz 的单声道音频数据 |
#### 示例
```javascript
bricks.enable_vad(function(audio) {
console.log("语音结束,音频长度:", audio.length);
// 可将 audio 发送到后端进行 ASR 识别等处理
});
```
#### 内部实现
```javascript
bricks.vad = await vad.MicVAD.new({
onSpeechEnd: func
});
bricks.vad.start();
```
---
### `bricks.disable_vad()`
停止当前运行的 VAD 实例,并释放相关资源。
#### 行为说明
- 调用 `stop()` 方法停止麦克风监听和模型推理
- 将 `bricks.vad` 设置为 `null`,便于垃圾回收
#### 示例
```javascript
// 停止语音检测
bricks.disable_vad();
```
---
## 音频数据格式
- **采样率**16000 Hz
- **位深**32-bit float
- **声道**单声道Mono
- **数据类型**`Float32Array`
- **应用场景**适用于大多数现代语音识别ASR模型输入要求
---
## 使用示例
```html
<!DOCTYPE html>
<html>
<head>
<title>VAD Demo</title>
</head>
<body>
<button onclick="startVAD()">开始语音检测</button>
<button onclick="stopVAD()">停止语音检测</button>
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.7/dist/bundle.min.js"></script>
<script>
var bricks = window.bricks || {};
bricks.enable_vad = async function(func){
bricks.vad = await vad.MicVAD.new({
onSpeechEnd: func
});
bricks.vad.start();
};
bricks.disable_vad = async function(){
if (bricks.vad) {
bricks.vad.stop();
bricks.vad = null;
}
};
function startVAD() {
bricks.enable_vad((audio) => {
console.log("捕获到语音片段,样本数:", audio.length);
// 此处可上传至 ASR 服务或进行本地处理
});
}
function stopVAD() {
bricks.disable_vad();
}
</script>
</body>
</html>
```
---
## 注意事项
1. **浏览器权限**:首次运行时需请求麦克风权限,请确保页面在 HTTPS 环境下运行或本地调试localhost
2. **性能优化**`vad-web` 使用 WASM 和 ONNX 模型进行推理,首次加载可能需要几秒预热时间。
3. **内存管理**:务必在不需要时调用 `disable_vad()` 以释放麦克风资源,避免持续占用。
4. **版本锁定**:当前固定使用 `@ricky0123/vad-web@0.0.7`,建议生产环境保持版本稳定。
---
## 参考链接
- [vad-web GitHub 仓库](https://github.com/ricky0123/vad-web)
- [ONNX Runtime Web](https://onnxruntime.ai/docs/tutorials/web/intro.html)
- [Web Audio API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)
---
## 版本信息
- **vad-web 版本**`0.0.7`
- **兼容性**支持现代主流浏览器Chrome、Edge、Firefox、Safari
---
📌 **提示**:此模块适合嵌入到低代码平台、语音助手、实时对话系统中,作为前端语音触发机制的核心组件。

317
docs/cn/page_data_loader.md Normal file
View File

@ -0,0 +1,317 @@
# `bricks.PageDataLoader` 技术文档
`PageDataLoader` 是一个用于分页加载数据的 JavaScript 类,支持缓存控制、参数合并、前后翻页等功能。它适用于需要从后端 API 按页获取大量数据的场景,并通过缓存机制优化性能。
---
## 目录
- [概述](#概述)
- [依赖说明](#依赖说明)
- [构造函数与配置选项](#构造函数与配置选项)
- [实例方法](#实例方法)
- [`loadData([params])`](#loaddataparams)
- [`loadNextPage()`](#loadnextpage)
- [`loadPreviousPage()`](#loadpreviouspage)
- [`loadPage(page)`](#loadpagepage)
- [`is_max_page(p)`](#is_max_pagep)
- [返回数据结构](#返回数据结构)
- [使用示例](#使用示例)
- [缓存策略](#缓存策略)
- [注意事项](#注意事项)
---
## 概述
`bricks.PageDataLoader` 提供了一个简洁的接口来管理分页数据的加载和缓存。其主要特性包括:
- 支持自定义请求 URL、分页大小、HTTP 方法等。
- 自动计算总页数。
- 内置页面缓存机制(可配置最大缓存页数)。
- 支持跳转到指定页、下一页、上一页。
- 可扩展的基础参数与动态参数合并。
---
## 依赖说明
该类依赖以下全局对象或模块:
| 依赖 | 说明 |
|------|------|
| `window.bricks` | 全局命名空间对象,提供工具函数 |
| `bricks.extend(obj1, obj2)` | 对象浅拷贝并合并属性(类似 jQuery 的 `$.extend` |
| `bricks.HttpJson()` | 发起 HTTP 请求的封装类,支持 `.httpcall(url, options)` 方法 |
| `bricks.debug(...)` | 调试日志输出函数 |
> ⚠️ 确保这些依赖在运行环境中已正确加载。
---
## 构造函数与配置选项
```js
var loader = new bricks.PageDataLoader(options);
```
### 参数:`options` (Object)
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|---------------|----------|------|--------|------|
| `url` | String | ✅ | - | 数据接口地址 |
| `pagerows` | Number | ❌ | 80 | 每页显示的数据条数 |
| `cache_pages` | Number | ❌ | 5 | 最多缓存多少个页面(超出则移除最远页) |
| `method` | String | ❌ | `'GET'` | HTTP 请求方法(如 `'POST'` |
| `params` | Object | ❌ | `{}` | 基础请求参数(每次请求都会携带) |
### 示例
```js
var p = new bricks.PageDataLoader({
url: '/api/data',
pagerows: 50,
cache_pages: 3,
method: 'GET',
params: {
category: 'news'
}
});
```
---
## 实例方法
### `loadData([params])`
初始化并加载第一页数据。
#### 参数
- `params` (Object, 可选):本次请求附加的参数,会与 `base_params` 合并。
#### 返回值
- Promise解析为第一页的响应数据包含分页信息
#### 行为说明
- 清空当前所有缓存页。
- 合并基础参数与传入参数。
- 调用 `loadPage(1)` 加载第一页。
#### 示例
```js
p.loadData({ search: 'keyword' }).then(data => {
console.log(data);
});
```
---
### `loadNextPage()`
加载下一页数据(即当前已加载页码的最大值 + 1
#### 返回值
- Promise若存在下一页则返回下一页数据否则返回 `undefined`
#### 条件判断
- 仅当目标页未加载且小于等于 `lastPage` 时才发起请求。
#### 示例
```js
p.loadNextPage().then(data => {
if (data) {
render(data.rows); // 渲染新页数据
}
});
```
---
### `loadPreviousPage()`
加载上一页数据(即当前已加载页码的最小值 - 1
#### 返回值
- Promise若存在上一页页码 > 0则返回对应数据否则返回 `undefined`
#### 示例
```js
p.loadPreviousPage().then(data => {
if (data) {
prependToView(data.rows); // 将数据插入视图开头
}
});
```
---
### `loadPage(page)`
加载指定页码的数据(核心方法)。
#### 参数
- `page` (Number):要加载的页码(从 1 开始)。
#### 返回值
- Promise返回该页的完整响应数据。
#### 行为说明
1. 若该页已加载(存在于 `this.pages` 中),不重复请求。
2. 使用 `bricks.HttpJson().httpcall()` 发起请求:
```js
{
page: page,
rows: this.rows
}
```
3. 自动更新 `lastPage`(根据 `total / rows` 计算)。
4. 将当前页加入 `pages` 缓存数组。
5. 若缓存超过 `cache_pages` 数量,则删除“最远”的一页(相对于当前页是首或尾)。
6. 在返回数据中添加元字段(如 `add_page`, `delete_page`, `pos_rate`)。
#### 扩展字段说明
| 字段名 | 类型 | 说明 |
|--------------|--------|------|
| `add_page` | Number | 当前新增的页码 |
| `delete_page`| Number | 因缓存溢出被删除的页码(如有) |
| `pos_rate` | Number | 当前位置占比(用于 UI 定位提示) |
| `last_page` | Number | 总页数(由后端 `total` 推导) |
---
### `is_max_page(p)`
判断某页是否为当前已知的最大页码。
#### 参数
- `p` (Number):待检测的页码。
#### 返回值
- Boolean如果是当前缓存中的最大页码返回 `true`
#### 示例
```js
if (p.is_max_page(5)) {
console.log("这是当前最大的页码");
}
```
---
## 返回数据结构
假设后端返回格式如下:
```json
{
"total": 230,
"rows": [...]
}
```
`PageDataLoader` 会在基础上增加以下字段:
```json
{
"total": 230,
"rows": [...],
"last_page": 3,
"add_page": 1,
"delete_page": 3,
"pos_rate": 0.5
}
```
| 字段 | 说明 |
|--------------|------|
| `last_page` | 根据 `Math.ceil(total / rows)` 得出的总页数 |
| `add_page` | 本次成功加载的页码 |
| `delete_page`| 如果触发了缓存清理,表示被清除的页码 |
| `pos_rate` | 当前页在已有页中的相对位置0 ~ 1便于 UI 定位 |
---
## 使用示例
```js
// 初始化加载器
var loader = new bricks.PageDataLoader({
url: '/api/list',
params: { type: 'article' },
pagerows: 20,
cache_pages: 4,
method: 'GET'
});
// 加载第一页
loader.loadData().then(data => {
displayData(data.rows);
console.log(`共 ${data.last_page} 页`);
});
// 下一页
document.getElementById('next').onclick = () => {
loader.loadNextPage().then(data => {
if (data) appendData(data.rows);
});
};
// 上一页
document.getElementById('prev').onclick = () => {
loader.loadPreviousPage().then(data => {
if (data) prependData(data.rows);
});
};
```
---
## 缓存策略
- 所有已成功加载的页码记录在 `this.pages` 数组中。
- 当 `pages.length > cache_pages` 时,自动删除“最远”的一页:
- 若当前加载的是最后一页 → 删除最早页(最小页码)
- 若当前加载的是较早页 → 删除最后页(最大页码)
> 此策略有助于保持用户浏览路径附近的页数据在内存中。
---
## 注意事项
1. **异步调用**:所有 `loadXxx` 方法均为 `async`,需使用 `await``.then()` 处理结果。
2. **页码从 1 开始**:符合常规分页习惯。
3. **无重复加载**:已加载的页不会再次请求,需手动调用 `loadData()` 重置。
4. **错误处理**:请求失败时会通过 `bricks.debug()` 输出调试信息,但不会抛出异常。
5. **total 必须存在**:后端响应必须包含 `total` 字段,否则无法计算 `lastPage`
---
## 版本信息
- 模块名称:`bricks.PageDataLoader`
- 创建时间:未知
- 维护者:`window.bricks` 团队
> 如需定制行为,建议继承此类或扩展原型方法。
---
**推荐用途**:表格分页、无限滚动、左右滑动浏览历史记录等场景。

272
docs/cn/paging.md Normal file
View File

@ -0,0 +1,272 @@
# `bricks.BufferedDataLoader` 技术文档
> 一个用于分页加载数据并支持缓冲机制的前端数据加载器。
---
## 概述
`bricks.BufferedDataLoader` 是一个 JavaScript 类,旨在为需要高效处理大量分页数据的 UI 组件(如表格、列表等)提供**带缓冲的数据加载能力**。它通过限制内存中缓存的页面数量来优化性能,并支持向前/向后翻页操作。
该类适用于与支持分页接口的后端服务配合使用,典型场景包括大数据量表格滚动加载、虚拟滚动列表等。
---
## 命名空间
```js
bricks.BufferedDataLoader
```
依赖于全局对象 `window.bricks`,若不存在则自动创建。
---
## 构造函数
### `new bricks.BufferedDataLoader(widget, options)`
#### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `widget` | Object | ✅ | 数据展示组件实例,必须实现 `.clear_data()`, `.add_rows(rows, direction)`, `.del_old_rows(count, direction)` 方法。 |
| `opts` | Object | ✅ | 配置选项对象,详见下表。 |
#### `opts` 配置项
| 属性 | 类型 | 可选 | 默认值 | 说明 |
|------|------|------|--------|------|
| `url` | String | ❌ | - | 请求数据的 API 地址。 |
| `method` | String | ✅ | `'GET'` | HTTP 请求方法,如 `'GET'``'POST'`。 |
| `params` | Object | ✅ | `{}` | 固定请求参数(例如过滤条件、排序字段等)。 |
| `buffer_pages` | Number | ✅ | `5` | 最大缓存页数,超出时将移除最旧或最新的一页以释放内存。 |
| `pagerows` | Number | ✅ | `60` | 每页返回的数据行数。 |
---
## 实例属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `cur_page` | Number | 当前正在加载/显示的页码(从 1 开始)。 |
| `total_records` | Number | 总记录数(由服务器返回填充)。 |
| `total_page` | Number | 总页数(根据 `total_records / pagerows` 计算得出)。 |
| `buffer` | Object | (预留扩展)当前用于存储已加载页面数据的缓存对象(本版本未实际使用)。 |
| `buffered_pages` | Number | 当前缓冲中的页面数量(影响是否触发旧数据清理)。 |
| `loading` | Boolean | 标志位,表示是否正在进行网络请求。 |
| `direction` | String | 上下方向标识:`'up'` 表示上一页,`'down'` 表示下一页,用于通知 widget 渲染策略。 |
| `cur_params` | Object | 合并后的当前请求参数(包含固定参数和动态传参)。 |
---
## 方法
### `initial()`
重置加载器状态,通常在初始化或重新加载前调用。
#### 功能:
- 重置当前页为 `-1`
- 清空缓冲计数
- 重置总记录数
- 清空当前参数副本
---
### `async loadData(params)`
初始化并加载第一页数据,清空现有数据。
#### 参数:
- `params` *(Object, 可选)*: 动态附加的请求参数(会与 `opts.params` 合并)
#### 返回值:
- `Promise<Object>`: 解析为服务器返回的数据结构,包含:
```js
{
total: 1000,
page: 1,
total_page: 17,
rows: [...]
}
```
#### 流程:
1. 调用 `initial()` 重置状态
2. 清空 widget 显示数据
3. 合并默认参数与传入参数
4. 设置每页行数 (`rows`) 和初始页码 (`page=1`)
5. 调用 `loadPage()` 加载第一页
#### 示例:
```js
const loader = new bricks.BufferedDataLoader(myWidget, {
url: '/api/data',
params: { category: 'A' },
pagerows: 50
});
await loader.loadData({ search: 'keyword' }); // 发送 {category:'A', search:'keyword', rows:50, page:1}
```
---
### `async loadPage(page)`
加载指定页码的数据(内部方法,也可手动调用)。
#### 参数:
- `page` *(Number, 可选)*: 指定要加载的页码,默认为当前 `this.cur_page + 1`(如果未设置)
> ⚠️ 注意:此方法内部管理 `cur_page`,外部应优先使用 `nextPage()` / `previousPage()`
#### 行为逻辑:
1. 若正在加载,则直接返回(防重复提交)
2. 若缓冲页数达到上限(`buffer_pages`),则调用 `widget.del_old_rows()` 删除旧行
3. 构造完整请求参数(含 `page`, `rows` 等)
4. 使用 `bricks.HttpJson` 发起异步请求
5. 更新总记录数和总页数
6. 调用 `widget.add_rows()` 添加新数据
7. 增加缓冲页计数,释放加载锁
#### 返回值:
- `Promise<Object>`: 服务器响应数据
---
### `async nextPage()`
加载下一页数据。
#### 条件判断:
- 如果已在最后一页或正在加载,则不执行任何操作。
#### 行为:
- 设置 `direction = 'down'`
- `cur_page += 1`
- 调用 `loadPage()`
#### 示例:
```js
await loader.nextPage(); // 加载第 2 页
```
---
### `async previousPage()`
加载上一页数据。
#### 条件判断:
- 如果已是第一页或正在加载,则不执行。
#### 行为:
- 设置 `direction = 'up'`
- `cur_page -= 1`
- 调用 `loadPage()`
#### 示例:
```js
await loader.previousPage(); // 回退到前一页
```
---
## widget 接口要求
`BufferedDataLoader` 依赖传入的 `widget` 实现以下三个方法:
| 方法 | 签名 | 用途 |
|------|------|------|
| `.clear_data()` | `function(): void` | 清空当前所有数据显示 |
| `.add_rows(rows, direction)` | `function(Array, String): void` | 添加一批数据行,`direction``'up'``'down'`,可用于决定插入位置 |
| `.del_old_rows(count, direction)` | `function(Number, String): void` | 删除旧数据行(例如顶部或底部若干行),用于维持缓冲大小 |
> 💡 提示这些方法可结合虚拟滚动、DOM 复用等技术实现高性能渲染。
---
## 使用示例
```js
// 定义 widget 对象(模拟组件)
const myWidget = {
data: [],
clear_data() {
this.data = [];
console.log("数据已清空");
},
add_rows(rows, direction) {
if (direction === 'up') {
this.data.unshift(...rows);
} else {
this.data.push(...rows);
}
console.log(`添加了 ${rows.length} 行数据,方向: ${direction}`);
},
del_old_rows(count, direction) {
if (direction === 'up') {
this.data.splice(-count); // 删除末尾
} else {
this.data.splice(0, count); // 删除开头
}
console.log(`删除了 ${count} 行旧数据`);
}
};
// 创建 BufferedDataLoader 实例
const loader = new bricks.BufferedDataLoader(myWidget, {
url: '/api/list',
method: 'POST',
params: { type: 'report' },
buffer_pages: 3,
pagerows: 20
});
// 初始加载第一页
await loader.loadData({ keyword: 'test' });
// 下一页
await loader.nextPage();
// 上一页
await loader.previousPage();
```
---
## 注意事项
1. **线程安全**:通过 `this.loading` 实现简单的并发控制,防止重复请求。
2. **内存控制**:通过 `buffer_pages` 控制最大缓存页数,避免内存溢出。
3. **方向感知**`direction` 字段帮助 widget 区分上下滑动行为,便于优化 UI 渲染。
4. **总页数计算修正**
```js
if (d.total_page * this.pagerows < this.total_record) {
d.total_page += 1;
}
```
此处逻辑存在潜在错误(应使用向上取整),建议改为:
```js
d.total_page = Math.ceil(d.total / this.pagerows);
```
---
## 改进建议
| 问题 | 建议修复 |
|------|---------|
| `total_page` 计算错误 | 改为 `Math.ceil(total / pagerows)` |
| `buffer` 成员未实际使用 | 应实现真正的页面数据缓存以支持快速回退 |
| 缺少错误处理 | 在 `httpcall` 外包裹 `try-catch` 并抛出异常 |
| 不支持取消请求 | 可引入 `AbortController` 实现请求中断 |
---
## 版本信息
- 所属库:`bricks.js`
- 类名:`BufferedDataLoader`
- 创建时间:未知
- 作者:未知
---
📖 **文档生成于2025年4月5日**

322
docs/cn/period.md Normal file
View File

@ -0,0 +1,322 @@
# `bricks` 日期工具与组件库技术文档
---
## 概述
`bricks` 是一个轻量级的 JavaScript 工具和 UI 组件库,提供常用的日期处理函数以及可交互的日期周期控件。本文档详细说明其核心功能模块:
- 日期字符串与 `Date` 对象之间的转换
- 日期增减操作(天、月、年)
- `PeriodDays` 可点击切换周期的 UI 控件
该库设计用于前端项目中快速构建时间范围选择器等交互式组件。
---
## 全局命名空间
```js
var bricks = window.bricks || {};
```
> 所有功能均挂载在全局 `bricks` 命名空间下,避免污染全局作用域。
---
## 1. `bricks.str2date(sdate)`
将格式为 `"YYYY-MM-DD"` 的字符串转换为 `Date` 对象。
### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|------------------------|
| `sdate` | string | 格式为 `"YYYY-MM-DD"` 的日期字符串 |
### 返回值
- `{Date}`:对应的 `Date` 对象(注意:月份从 0 开始)
### 示例
```js
bricks.str2date("2025-04-05"); // 返回 Date 对象,表示 2025年4月5日
```
### 注意事项
- 输入字符串必须符合 `YYYY-MM-DD` 格式。
- `new Date(year, month - 1, day)` 中月份需减 1因 JS 的 `Date` 对象月份从 0 起始。
---
## 2. `bricks.date2str(date)`
`Date` 对象格式化为 `"YYYY-MM-DD"` 字符串。
### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|--------------------|
| `date` | Date | 要格式化的日期对象 |
### 返回值
- `{string}`:格式化后的 `"YYYY-MM-DD"` 字符串,不足两位自动补零。
### 示例
```js
const d = new Date(2025, 3, 5); // 2025-04-05
bricks.date2str(d); // "2025-04-05"
```
---
## 3. `bricks.addMonths(dateObj, months)`
在指定日期上增加若干个月。
### 参数
| 参数名 | 类型 | 描述 |
|------------|--------|--------------------------|
| `dateObj` | Date | 原始日期对象 |
| `months` | number | 要增加的月数(可为负) |
### 返回值
- `{Date}`:新的 `Date` 对象,原对象不被修改
### 示例
```js
const d = new Date(2025, 0, 15); // 2025-01-15
bricks.addMonths(d, 3); // 2025-04-15
bricks.addMonths(d, -1); // 2024-12-15
```
---
## 4. `bricks.addYears(dateObj, years)`
在指定日期上增加若干年。
### 参数
| 参数名 | 类型 | 描述 |
|-----------|--------|--------------------------|
| `dateObj` | Date | 原始日期对象 |
| `years` | number | 要增加的年数(可为负) |
### 返回值
- `{Date}`:新的 `Date` 对象
### ⚠️ 注意
使用了 `setYear()` 方法,但请注意:
- `setYear()` 在现代 JS 中已被弃用,推荐使用 `setFullYear()` 替代。
- 当前实现可能存在问题:`getYear()` 返回的是相对于 1900 的偏移量,因此 `+ years` 实际上是错误逻辑!
### ✅ 修正建议
```js
bricks.addYears = function(dateObj, years){
const newDate = new Date(dateObj);
newDate.setFullYear(newDate.getFullYear() + years);
return newDate;
}
```
> 建议尽快替换以确保跨世纪正确性(如 2099 → 2100 年等问题)。
---
## 5. `bricks.addDays(dateObj, days)`
在指定日期上增加若干天。
### 参数
| 参数名 | 类型 | 描述 |
|----------|--------|--------------------------|
| `dateObj`| Date | 原始日期对象 |
| `days` | number | 要增加的天数(可为负) |
### 返回值
- `{Date}`:新的 `Date` 对象
### 示例
```js
const d = new Date(2025, 0, 1);
bricks.addDays(d, 10); // 2025-01-11
bricks.addDays(d, -1); // 2024-12-31
```
---
## 6. `bricks.PeriodDays` —— 周期日期选择控件
一个继承自 `bricks.HBox` 的 UI 组件,允许用户通过点击前后文本切换时间区间。
### 继承关系
```js
class PeriodDays extends bricks.HBox
```
### 配置选项 (`opts`)
| 属性名 | 类型 | 默认值 | 描述 |
|--------------|----------|-------------|------|
| `start_date` | string | 必填 | 起始日期 `"YYYY-MM-DD"` |
| `end_date` | string | 必填 | 结束日期 `"YYYY-MM-DD"` |
| `step_type` | string | `'days'` | 步进类型:`'days'`, `'months'`, `'years'` |
| `step_cnt` | number | `1` | 每次步进的数量 |
| `title` | string | `undefined` | 显示标题(支持国际化) |
| `splitter` | string | `' 至 '` | 分隔符文本,显示在起止日期之间 |
### 支持事件
- **`changed`**:当日期范围发生变化时触发,携带新范围数据。
#### 事件参数结构
```json
{
"start_date": "YYYY-MM-DD",
"end_date": "YYYY-MM-DD"
}
```
### 成员方法
#### `date_add(strdate, step_cnt, step_type)`
内部方法:对字符串日期执行加法运算并返回新字符串。
##### 参数
| 参数名 | 类型 | 描述 |
|--------------|----------|------|
| `strdate` | string | 输入日期字符串 |
| `step_cnt` | number | 步进步数 |
| `step_type` | string | `'days'`, `'months'`, `'years'` |
##### 返回值
- `{string}`:结果日期字符串(格式 `"YYYY-MM-DD"`
##### 示例
```js
this.date_add("2025-01-01", 1, "months"); // "2025-02-01"
```
---
#### `step_back()`
点击起始日期时触发,整体周期向前移动(减去步长)。
- 更新 `start_date``end_date`
- 刷新界面显示
- 触发 `changed` 事件
---
#### `step_forward()`
点击结束日期时触发,整体周期向后移动(加上步长)。
- 同上,方向相反
---
### 用户交互设计
| 元素 | 行为 |
|----------------|------------------------------|
| 起始日期文本 | 点击 → 周期整体前移 |
| 结束日期文本 | 点击 → 周期整体后移 |
> 文本添加了 `clickable` CSS 类,并绑定点击事件。
---
### 使用示例
```js
const period = new bricks.PeriodDays({
start_date: "2025-01-01",
end_date: "2025-01-31",
step_type: "months",
step_cnt: 1,
title: "统计周期",
splitter: " 至 "
});
// 监听变化
period.bind('changed', function(e){
console.log("新周期:", e.start_date, "到", e.end_date);
});
```
渲染效果类似:
```
统计周期 2025-01-01 至 2025-01-31
```
点击 “2025-01-01” → 变为 “2024-12-01 至 2024-12-31”
点击 “2025-01-31” → 变为 “2025-02-01 至 2025-02-28”
---
## 注册组件
```js
bricks.Factory.register('PeriodDays', bricks.PeriodDays);
```
> 使得可通过工厂模式动态创建此组件。
---
## 总结
| 功能 | 是否推荐使用 | 备注 |
|----------------------|--------------|------|
| `str2date` / `date2str` | ✅ 推荐 | 安全可靠 |
| `addDays` / `addMonths` | ✅ 推荐 | 正确封装 |
| `addYears` | ⚠️ 需修复 | 应改用 `setFullYear` |
| `PeriodDays` | ✅ 推荐 | 交互清晰,适合报表周期切换 |
---
## 附录:已知问题与改进建议
1. ❗ `addYears` 使用 `getYear/setYear` 存在兼容性和逻辑错误,应改为:
```js
bricks.addYears = function(dateObj, years) {
const newDate = new Date(dateObj);
newDate.setFullYear(newDate.getFullYear() + years);
return newDate;
};
```
2. ✅ 所有日期操作均创建副本,保护原始对象,良好实践。
3. 🔹 `PeriodDays` 未做边界校验(如非法日期),建议增加输入验证。
4. 🔹 支持更多 `step_type``'weeks'` 可扩展性更强。
---
> 文档版本v1.0
> 最后更新2025年4月5日

257
docs/cn/pie.md Normal file
View File

@ -0,0 +1,257 @@
# `bricks.ChartPie` 技术文档
## 概述
`bricks.ChartPie` 是基于 `bricks.EchartsExt` 扩展的类,用于创建可配置的饼图组件。它利用 ECharts 作为底层图表引擎,支持通过数据接口动态加载数据,并自动生成饼图及其图例。
该组件适用于需要展示分类占比数据的场景,如统计分析、仪表盘等。
---
## 继承关系
```js
class ChartPie extends bricks.EchartsExt
```
继承自 `bricks.EchartsExt`,具备基础的图表初始化、数据获取和事件处理能力。
---
## 构造函数
### `constructor(opts)`
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 配置选项对象,传递给父类及内部逻辑使用 |
#### 示例
```js
var pieChart = new bricks.ChartPie({
nameField: 'category',
valueFields: ['sales'],
data_url: '/api/pie-data',
pie_options: {
type: 'pie',
radius: '70%'
},
legend: {
show: true,
orient: 'vertical'
}
});
```
> ⚠️ 注意:构造函数中调用了 `super(opts)`,因此所有父类支持的配置项也适用。
---
## 配置选项Options
| 属性名 | 类型 | 必填 | 默认值 | 说明 |
|----------------|----------|------|--------|------|
| `title` | String | 否 | - | 图表标题 |
| `description` | String | 否 | - | 图表描述信息(可用于辅助提示) |
| `legend` | Object | 否 | `{}` | 图例配置项,将直接传入 ECharts 的 `legend` 配置 |
| `pie_options` | Object | 是 | - | 饼图系列的核心配置,如 `type`, `radius`, `center` 等 |
| `data_url` | String | 否 | - | 数据接口 URL若提供则自动异步加载数据 |
| `nameField` | String | 是 | - | 指定哪一字段作为饼图扇区的名称(即类别) |
| `valueFields` | Array | 是 | - | 数组形式指定值字段(目前仅取第一个字段用于数值) |
| `data_params` | Object | 否 | - | 请求 `data_url` 时携带的额外参数 |
| `data` | Array | 否 | `[]` | 直接传入的静态数据数组 |
---
## 核心方法
### `setup_options(data)`
根据传入的数据生成符合 ECharts 要求的图表配置项。
#### 参数
| 参数名 | 类型 | 说明 |
|-------|-------|------|
| `data` | Array | 原始数据数组,通常来自 API 或静态赋值 |
#### 返回值
返回一个符合 ECharts schema 的配置对象,结构如下:
```js
{
legend: { ... }, // 图例配置
tooltip: {
trigger: 'item' // 鼠标悬停显示单项详情
},
series: [
{ /* 处理后的饼图系列配置 */ }
]
}
```
#### 内部逻辑说明
1. **数据映射**
- 使用 `this.nameField` 映射为每个扇区的 `name`
- 使用 `this.valueFields[0]` 映射为 `value`
- 构建新的数据格式:`{ name: '类别A', value: 100 }`
2. **合并饼图配置**
- 使用 `bricks.extend({}, this.pie_options)` 深拷贝避免污染原始配置
- 将处理后的数据注入到 `series[0].data`
3. **图例处理**
- 若设置了 `legend`,则保留其配置
- 注释代码表明原计划支持多字段图例,当前未启用
#### 示例输出
假设输入数据为:
```js
[
{ category: "手机", sales: 60 },
{ category: "平板", sales: 40 }
]
```
且配置为:
```js
{
nameField: 'category',
valueFields: ['sales'],
pie_options: { type: 'pie', radius: '80%' }
}
```
`setup_options` 输出为:
```js
{
legend: {},
tooltip: { trigger: 'item' },
series: [{
type: 'pie',
radius: '80%',
data: [
{ name: '手机', value: 60 },
{ name: '平板', value: 40 }
]
}]
}
```
---
## 支持的事件
### `element_click`
当用户点击饼图中的某个扇区时触发。
#### 回调参数
ECharts 提供的标准事件参数对象,包含:
- `name`: 扇区名称
- `value`: 对应数值
- `data`: 原始数据项
- `seriesName`: 系列名称(如有)
#### 使用方式
```js
pieChart.on('element_click', function(e) {
console.log('Clicked:', e.name, e.value);
});
```
---
## 注册与使用
### 注册组件
```js
bricks.Factory.register('ChartPie', bricks.ChartPie);
```
通过工厂模式注册,后续可通过字符串标识符创建实例:
```js
var chart = bricks.Factory.create('ChartPie', options);
```
---
## 完整示例
```js
// 初始化饼图
var myPie = new bricks.ChartPie({
nameField: 'product',
valueFields: ['revenue'],
data_url: '/report/sales-by-product',
data_params: { year: 2024 },
pie_options: {
type: 'pie',
radius: ['50%', '70%'],
label: {
show: true,
formatter: '{b}: {d}%'
}
},
legend: {
show: true,
orient: 'horizontal',
bottom: '0%'
},
title: '2024 年产品收入分布'
});
// 渲染到 DOM 元素
myPie.renderTo('#chart-container');
// 绑定点击事件
myPie.on('element_click', function(e) {
alert(`您点击了:${e.name}`);
});
```
---
## 注意事项
1. **`valueFields` 仅支持单个值字段**
当前实现只取数组的第一个字段作为数值来源,不支持多维度饼图。
2. **数据格式要求**
从接口或手动传入的数据必须是对象数组,且包含 `nameField``valueFields[0]` 字段。
3. **依赖 `bricks.extend` 工具函数**
用于安全拷贝 `pie_options`,确保不影响原始配置。
4. **需提前引入 ECharts**
本组件依赖全局 ECharts 库,请确保页面已正确加载。
---
## 版本信息
- 创建时间:未知
- 最后更新:根据代码注释推断近期维护中
- 所属框架:`bricks.js` 可视化组件库
---
**建议扩展方向**
- 支持多层环形图(嵌套饼图)
- 添加动画配置开关
- 支持主题颜色自定义
- 增加空数据状态提示

502
docs/cn/popup.md Normal file
View File

@ -0,0 +1,502 @@
# Bricks UI Framework - Popup 组件技术文档
---
## 概述
`bricks.Popup` 是基于 `bricks.VBox` 的弹窗组件,用于创建可浮动、可移动、可调整大小的模态或非模态对话框。它支持自动打开/关闭、点击外部区域自动关闭、内容动态加载等功能。
此外,框架还提供了两个扩展类:
- `bricks.PopupWindow`:带标题栏和控制按钮(最小化、全屏、关闭)的窗口式弹窗。
- `bricks.WindowsPanel`:用于管理多个最小化的窗口列表面板。
该组件适用于构建现代化 Web 应用中的对话框、提示框、应用窗口等交互界面。
---
## 全局命名空间初始化
```js
var bricks = window.bricks || {};
```
确保 `bricks` 命名空间存在,避免覆盖已有对象。
---
## 默认配置方法
### `bricks.get_popup_default_options()`
返回一个包含默认选项的对象,供 `Popup` 使用。
#### 返回值
```js
{
timeout: 0,
archor: 'cc',
auto_open: true,
auto_dismiss: true,
auto_destroy: true,
movable: true,
resizable: false,
modal: true
}
```
| 参数 | 类型 | 描述 |
|------|------|------|
| `timeout` | Number | 自动关闭延迟时间(秒),设为 0 表示不自动关闭 |
| `archor` | String | 定位锚点,取值见下表 |
| `auto_open` | Boolean | 是否在实例化后自动打开弹窗 |
| `auto_dismiss` | Boolean | 是否允许点击外部区域关闭弹窗 |
| `auto_destroy` | Boolean | 关闭后是否自动销毁组件 |
| `movable` | Boolean | 是否可拖动 |
| `resizable` | Boolean | 是否可调整大小 |
| `modal` | Boolean | 是否为模态(遮罩其他控件) |
> **`archor` 取值说明**
> 支持九宫格定位:
> `'tl'`, `'tc'`, `'tr'`, `'cl'`, `'cc'`, `'cr'`, `'bl'`, `'bc'`, `'br'`
> 分别表示:左上、中上、右上、左中、居中、右中、左下、中下、右下
---
## 核心类:`bricks.Popup`
继承自 `bricks.VBox`,提供完整的弹窗功能。
### 构造函数:`constructor(opts)`
#### 参数
- `opts` (Object): 配置选项,合并默认配置使用。
#### 内部属性初始化
| 属性 | 类型 | 初始值 | 说明 |
|------|------|--------|------|
| `no_opened` | Boolean | `true` | 是否首次打开 |
| `auto_task` | Task | `null` | 超时任务句柄 |
| `issub` | Boolean | `false` | 是否依附于某个控件显示 |
| `opened` | Boolean | `false` | 当前是否已打开 |
| `content_box` | VBox | 实例 | 内容容器 |
| `content_w` | Widget | `content_box` | 内容承载部件 |
| `moving_w` | Widget | `this` | 可拖动目标(默认整个弹窗) |
| `target_w` | Widget | `bricks.Body` | 目标绑定控件 |
| `moving_status` | Boolean | `false` | 拖拽状态标志 |
| `resize_status` | Boolean | `false` | 缩放状态标志 |
#### 初始化流程
1. 设置 CSS 类名为 `popup`
2. 将自身置于顶层z-index
3. 创建内容容器并添加到内部
4. 绑定点击外部关闭事件(若启用 `auto_dismiss`
5. 启用拖动(`movable`)与缩放(`resizable`
6. 默认隐藏(`display: none`
7. 添加至 `Body` 容器
8. 绑定点击以提升层级
9. 若 `auto_open === true`,调用 `open()`
10. 若有 `content`,监听 `opened` 事件加载内容
---
### 方法详解
#### `load_content() → Promise<void>`
异步加载配置中的 `content` 并渲染到内容区。
```js
async load_content(){
var w = await bricks.widgetBuild(this.content, this);
if (w){
this.set_dismiss_events(w);
this.content_w.clear_widgets();
this.content_w.add_widget(w);
}
}
```
> ⚠️ 依赖 `bricks.widgetBuild()` 动态构建子控件树。
---
#### `set_dismiss_events(widget)`
为指定控件绑定关闭事件。
```js
this.dismiss_events.forEach(ename => {
w.bind(ename, this.destroy.bind(this));
});
```
> 示例:`dismiss_events: ['ok', 'cancel']` 会绑定这些事件触发销毁。
---
#### `bring_to_top()`
将当前弹窗置顶(最高 z-index更新全局 `toppopup` 引用。
```js
this.zindex = bricks.app.new_zindex();
this.set_style('zIndex', this.zindex);
bricks.app.toppopup = this;
```
---
#### `popup_from_widget(from_w)`
从指定控件位置智能定位弹窗(避开屏幕边缘)。
##### 算法逻辑
- 计算源控件中心坐标 `(ox, oy)`
- 若源在左侧,则弹窗出现在右侧;否则出现在左侧
- 若源在上方,则弹窗出现在下方;否则出现在上方
- 边界检测防止溢出屏幕
> 适用于 Tooltip 或 Context Menu 场景。
---
#### `setup_resizable()`
启用可缩放功能,在右下角添加 SVG 缩放手柄。
##### 子组件
- `resizable_w`: SVG 图标(右下三角)
##### 事件绑定
- `mousedown``resize_start_pos`
- `mousemove``resizing`
- `mouseup``stop_resizing`
---
#### `resize_start_pos(e)`
记录初始尺寸和鼠标位置,开始缩放。
```js
this.s_width = rect.width;
this.s_height = rect.height;
this.s_offsetX = e.clientX;
this.s_offsetY = e.clientY;
```
---
#### `resizing(e)`
实时调整宽度和高度。
```js
cx = this.s_width + e.clientX - this.s_offsetX;
cy = this.s_height + e.clientY - this.s_offsetY;
this.set_style('width', cx + 'px');
this.set_style('height', cy + 'px');
```
---
#### `stop_resizing(e)`
结束缩放操作,解绑全局事件。
```js
this.resize_status = false;
bricks.Body.unbind('mousemove', this.resizing.bind(this));
bricks.Body.unbind('mouseup', this.stop_resizing.bind(this));
```
---
#### `positify_tl()`
根据 `archor` 和屏幕尺寸计算并设置弹窗位置。
##### 尺寸解析优先级
1. `cwidth` / `cheight`(字符单位 × 字符大小)
2. `width` / `height`(百分比或 px
3. 默认占屏 80%
##### 锚点布局规则
| 第一位(垂直) | 第二位(水平) |
|----------------|----------------|
| `t`: 顶部对齐 | `l`: 左对齐 |
| `c`: 居中 | `c`: 居中 |
| `b`: 底部对齐 | `r`: 右对齐 |
> 返回 `{ top, left }` 像素坐标。
---
#### `setup_movable()`
启用拖动功能,绑定 `mousedown``touchstart`
---
#### `rec_start_pos(e)`
记录拖动起点偏移量,并绑定移动与释放事件。
```js
this.offsetX = e.clientX - rect.left;
this.offsetY = e.clientY - rect.top;
```
---
#### `moving(e)`
实时更新位置。
```js
cx = e.clientX - this.offsetX;
cy = e.clientY - this.offsetY;
this.set_style('left', cx + 'px');
this.set_style('top', cy + 'px');
```
---
#### `stop_moving(e)`
停止拖动,解绑所有相关事件。
---
#### `click_outside(event)`
点击弹窗外区域时关闭弹窗(仅当 `auto_dismiss``true` 时生效)。
```js
if (event.target != this.dom_element) this.dismiss();
```
---
#### `open()`
打开弹窗。
##### 流程
1. 若无父节点,加入 `app` 根容器
2. 若是首次打开:
- 设置目标控件(`widget` 参数)
- 执行定位 `positify_tl()`
3. 显示元素(`display: block`
4. 触发 `opened` 事件
5. 设置超时关闭任务(如需)
6. 若为模态,则禁用目标控件
7. 提升至顶层
---
#### `dismiss()`
关闭弹窗。
##### 流程
1. 若为模态,恢复目标控件可用状态
2. 清除超时任务
3. 隐藏元素(`display: none`
4. 触发 `dismissed` 事件
5. 若 `auto_destroy === true`,执行 `destroy()`
---
#### `destroy()`
彻底销毁组件。
```js
if (this.opened) this.dismiss();
if (this.parent) {
this.parent.remove_widget(this);
this.parent = null;
}
```
---
#### `add_widget(w, i)`
向内容区域添加控件,并自动绑定关闭事件。
> 若 `auto_open``true`,则立即打开弹窗。
---
#### `remove_widget(w)` / `clear_widgets()`
代理调用 `content_w` 的对应方法。
---
## 扩展类:`bricks.PopupWindow`
继承自 `Popup`,模拟操作系统窗口,带有标题栏和控制按钮。
### 构造函数特点
```js
opts.movable = true;
opts.resizable = true;
opts.auto_open = false; // 手动控制打开时机
```
#### 特有属性
| 属性 | 说明 |
|------|------|
| `title_bar` | 标题栏容器HBox |
| `tb_w` | 控制按钮栏IconBar |
| `content_w` | 主内容区Layout |
| `title_w` | 标题文本显示控件 |
#### 内部结构
1. **标题栏 (`titlebar`)**
- 图标SVG
- 控制按钮组(删除、最小化、全屏)
- 标题文本
2. **填充层 (`Filler`)**
- 包含主内容区
3. **内容区 (`Layout`)**
- 支持 Flex 布局
#### 控制按钮功能
| 按钮 | 功能 |
|------|------|
| `delete` | 调用 `destroy()` 销毁窗口 |
| `minimize` | 调用 `win_minimize()` 最小化 |
| `fullscreen` | 进入全屏模式 |
---
#### `win_minimize()`
关闭窗口并将自身推入 `bricks.app.mwins` 数组,供后续恢复。
```js
this.dismiss();
if (!this.auto_destroy) {
bricks.app.mwins.push(this);
}
```
---
#### `set_title(txt)`
动态修改窗口标题。
```js
this.title_w.set_text(txt);
```
---
## 工具类:`bricks.WindowsPanel`
用于展示所有最小化的窗口列表,点击可恢复。
### 构造函数行为
- 固定宽高为 `80%`
- 自动关闭、销毁
- 不自动打开
#### 数据绑定结构
使用 `Cols` 组件渲染表格:
```json
{
"total": 3,
"rows": [
{ "title": "Settings", "url": "...", "pos": 0 },
...
]
}
```
每行包含图标、标题,点击触发 `del_window()`
---
#### `del_window(event)`
恢复指定位置的窗口并从列表中移除。
```js
var pos = event.params.pos;
var w = bricks.app.mwins[pos];
w.open(); // 恢复显示
bricks.app.mwins.splice(pos, 1); // 删除记录
this.dismiss(); // 关闭面板
```
---
## 工厂注册
```js
bricks.Factory.register('Popup', bricks.Popup);
bricks.Factory.register('PopupWindow', bricks.PopupWindow);
```
支持通过字符串类型动态创建组件实例。
---
## 静态辅助函数
### `bricks.get_popupwindow_default_options()`
返回专用于 `PopupWindow` 的默认配置,与普通 `Popup` 的区别在于:
```diff
+ resizable: true
```
其余同 `get_popup_default_options()`
---
## 使用示例
### 创建简单弹窗
```js
let popup = new bricks.Popup({
content: {
widgettype: 'Text',
options: { text: 'Hello World!' }
},
archor: 'cc',
timeout: 3000
});
```
### 创建应用窗口
```js
let win = new bricks.PopupWindow({
title: '用户设置',
icon: '/icons/settings.svg',
content: { ... },
auto_open: true
});
```
### 显示窗口管理器
```js
let panel = new bricks.WindowsPanel();
panel.open();
```
---
## 注意事项
1. **事件作用域问题**:大量使用 `.bind(this)`,注意性能影响。
2. **内存泄漏风险**:未正确解绑事件可能导致残留监听器。
3. **触摸设备兼容性**:支持 `touchstart/move/end`,但需测试跨平台表现。
4. **z-index 管理**:依赖 `bricks.app.new_zindex()`,需保证唯一递增。
5. **异步内容加载**`load_content()` 为异步,注意加载顺序。
---
## 总结
`bricks.Popup` 系列组件提供了强大而灵活的弹窗系统,具备以下核心能力:
✅ 模态/非模态切换
✅ 自动定位与边界适配
✅ 拖拽移动 & 缩放
✅ 外部点击关闭
✅ 超时自动关闭
✅ 内容动态加载
✅ 窗口最小化管理
适合构建复杂的桌面风格 Web 应用界面。
---
> 文档版本v1.0
> 最后更新2025-04-05

150
docs/cn/progressbar.md Normal file
View File

@ -0,0 +1,150 @@
# `bricks.ProgressBar` 技术文档
```markdown
# bricks.ProgressBar
`bricks.ProgressBar` 是一个基于 `bricks.HBox` 的进度条组件,用于在用户界面中可视化显示任务的完成进度。它通过 CSS 样式控制外观,并支持动态设置当前进度值。
---
## 继承关系
- **继承自**: `bricks.HBox`
- **注册名称**: `'ProgressBar'`(通过 `bricks.Factory` 注册)
---
## 构造函数
### `new ProgressBar(opts)`
创建一个进度条实例。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts` | Object | 配置选项对象 |
##### 可选配置项 (`opts`)
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `total_value` | Number | `100`(隐式) | 进度条的总值,用于计算百分比 |
| `bar_cwidth` | Number | `2` | 进度条的高度(以字符行为单位),影响内部文本控件的高度 |
> ⚠️ 注意:`total_value` 在当前代码中未被实际读取或存储,逻辑上应传入并在 `set_value` 中使用。
#### 实现细节
- 调用父类构造函数 `super(opts)`
- 设置容器的 CSS 类名为 `progress-container`
- 创建一个 `bricks.Text` 实例作为进度条填充部分:
- 初始文本为 `'0%'`
- 高度由 `bar_cwidth` 决定(默认为 2 行高)
- 添加 CSS 类 `progress-bar`
- 将该文本控件添加到布局中。
---
## 方法
### `set_value(v)`
设置当前进度值并更新进度条的视觉表现。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `v` | Number | 当前进度值(应小于等于 `total_value` |
#### 实现逻辑
> ❗ 存在 Bug以下变量未定义或命名错误
```js
var pzt = (current / total) * 100;
pzt = Math.max(0, Math.min(100, percentage));
```
- `current``total` 未声明。
- `percentage` 变量不存在,应为 `pzt`
- 正确逻辑应基于输入参数 `v` 和预设的 `total_value` 计算百分比。
**建议修正版本**
```js
set_value(v) {
const total = this.total_value || 100;
let percentage = (v / total) * 100;
percentage = Math.max(0, Math.min(100, percentage));
this.text_w.set_style('width', percentage + '%');
}
```
此外,应在构造函数中保存 `this.total_value = opts.total_value || 100;`
---
## 事件
当前组件未定义任何自定义事件。
---
## 样式类
| CSS 类名 | 应用元素 | 用途 |
|---------|----------|------|
| `progress-container` | 容器HBox | 包裹整个进度条的外层样式 |
| `progress-bar` | 文本控件(填充条) | 控制进度条填充部分的样式,通常配合 `width` 动态调整 |
> 建议在 CSS 中定义 `.progress-bar` 为块级元素,具有背景色、过渡动画等效果。
---
## 使用示例
```js
const progressBar = new bricks.ProgressBar({
total_value: 200,
bar_cwidth: 3
});
// 添加到某个容器
someContainer.add_widget(progressBar);
// 更新进度
progressBar.set_value(50); // 显示 25%
progressBar.set_value(100); // 显示 50%
```
---
## 已知问题与改进建议
1. ✅ **Bug**: `set_value` 方法中使用了未定义的变量 `current`, `total`, `percentage`
2. ✅ **缺少属性初始化**`total_value` 应在构造函数中保存。
3. 🔧 **功能扩展建议**
- 支持显示百分比数字文本。
- 提供 `get_value()` 方法。
- 支持事件通知(如 `onchange`)。
4. 🎨 **样式建议**
- 使用 Flexbox 布局确保进度条拉伸一致。
- 添加边框和圆角提升视觉体验。
---
## 注册信息
通过工厂注册,可在模板或动态创建中使用字符串标识:
```js
bricks.Factory.register('ProgressBar', bricks.ProgressBar);
```
---
> **备注**:请修复 `set_value` 中的变量引用错误以确保组件正常工作。
```

343
docs/cn/qaframe.md Normal file
View File

@ -0,0 +1,343 @@
# `bricks.QAFrame` 技术文档
> 一个基于 WebSocket 的问答交互框架组件,支持多媒体课件展示、题目呈现与用户答案提交。
---
## 概述
`bricks.QAFrame``bricks` UI 框架中的一个复合组件类,继承自 `bricks.VBox`。它用于构建一个完整的**问答互动界面**,通过 WebSocket 与后端服务通信,支持以下功能:
- 展示多媒体课件视频、音频、图片、Markdown
- 显示问题并接收用户输入(文本或语音)
- 控制答题流程(开始确认、题目切换、结果反馈等)
该组件广泛适用于在线教育、智能测评和互动学习场景。
---
## 继承关系
```
bricks.QAFrame → bricks.VBox → bricks.Widget
```
---
## 构造函数
```js
new bricks.QAFrame(options)
```
### 参数:`options` (Object)
| 属性名 | 类型 | 必填 | 描述 |
|----------------|----------|------|------|
| `ws_url` | String | 是 | WebSocket 连接地址 |
| `ws_params` | Object | 否 | WebSocket 查询参数对象,将被序列化为 URL 查询字符串 |
| `title` | String | 否 | 页面标题(未在代码中使用,保留字段) |
| `description` | String | 否 | 描述信息(未在代码中使用,保留字段) |
| `courseware` | Object | 否 | 初始课件配置,结构见下文 |
#### `courseware` 子属性
| 属性名 | 类型 | 可选值 | 描述 |
|-----------|----------|----------------------------|------|
| `type` | String | `"audio"`, `"video"`, `"image"`, `"markdown"` | 媒体类型 |
| `url` | String | - | 资源 URL |
| `timeout` | Number | 秒数0 表示无超时 | 自动跳转时间 |
> 示例:
> ```js
> courseware: {
> type: 'video',
> url: '/media/intro.mp4',
> timeout: 30
> }
> ```
---
## 内部结构布局
`QAFrame` 使用垂直布局容器VBox包含三个主要区域
| 区域 | 组件 | 功能说明 |
|-----------|---------------|----------|
| `top_w` | `HBox` | 顶部工具栏区域(可显示题号) |
| `main_w` | `Filler` | 主内容区,动态加载课件或问题 |
| `bottom_w`| `HBox` | 底部操作区,放置按钮或输入控件 |
---
## WebSocket 通信协议
`QAFrame` 通过内置的 `bricks.WebSocket` 实例与服务器进行双向通信。
### WebSocket 初始化
自动拼接 `ws_params` 成查询字符串:
```js
const url = this.ws_url + '?' + new URLSearchParams(this.ws_params).toString();
this.ws = new bricks.WebSocket({ ws_url: url });
```
### 监听事件绑定
| 事件名 | 回调方法 | 触发时机 |
|------------------|----------------------|---------|
| `onopen` | `start_question_answer()` | WebSocket 连接建立时 |
| `onquestion` | `show_question(d)` | 收到新问题数据 |
| `oncourseware` | `show_courseware(d)` | 收到课件数据 |
| `onaskstart` | `show_conform(d)` | 请求用户确认开始 |
> ⚠️ 注意:`oncourseware` 被重复绑定三次 —— 此为冗余代码,建议优化。
---
## 接收消息格式Server → Client
由 WebSocket 推送的消息通过 `data` 字段传递,`type` 标识消息类型。
| 编号 | 类型 | `data` 结构 | 说明 |
|------|------------------|-------------|------|
| 1 | `courseware` | `{ type, url }` | 播放指定类型的媒体资源 |
| 2 | `askready` | `{ total_q, cur_q }` | 提示准备开始第几题 |
| 3 | `question` | `{ q_desc, total_q, cur_q }` | 显示具体问题 |
| 4 | `result` | `{ total_q, correct_cnt, error_cnt }` | 显示答题结果统计 |
| 5 | `error_list` | `{ error_cnt, rows: [...] }` | 错误详情列表 |
### `error_list.rows[]` 元素结构
| 字段 | 类型 | 说明 |
|-------------|--------|------|
| `pos` | Number | 题目位置索引 |
| `q_desc` | String | 题干描述 |
| `your_a` | String | 用户答案 |
| `corrent_a` | String | 正确答案 |
| `error_desc`| String | 错误原因说明 |
---
## 发送消息格式Client → Server
| 编号 | 类型 | `data` 值 | 触发方式 |
|------|-------------------|-----------|----------|
| 1 | `qa_start` | `null``{ d: "test data", v: 100 }` | 点击“开始”或连接打开时自动发送 |
| 2 | `conform_start` | `null` | 用户点击“Start?”按钮触发 |
| 3 | `text_answer` | `{ texta: "用户输入内容" }` | 文本输入框失去焦点时 |
| 4 | `audio_answer` | Base64 编码的音频 Blob | 录音结束时自动发送 |
> 示例:
> ```js
> { type: 'text_answer', data: 'Hello world' }
> { type: 'audio_answer', data: 'base64:...' }
> ```
---
## 核心方法说明
### `show_question(d)`
显示一道新问题。
#### 参数
- `d`: `{ q_desc, total_q, cur_q }`
#### 行为
- 更新顶部题号显示(需确保已创建 `qtotal_w``qcur_w`,当前代码缺失定义)
- 使用 `widgetBuild` 解析 `q_desc` 内容生成子组件
- 添加至主区域
> ✅ 支持富内容渲染(如 HTML、自定义组件
---
### `show_courseware(d)`
播放指定课件资源。
#### 参数
- `d`: `{ type, url }`
#### 支持类型
| 类型 | 组件 | 特性 |
|------------|--------------------|------|
| `video` | `bricks.Video` | 全屏自动播放 |
| `audio` | `bricks.AudioPlayer` | 自动播放 |
| `image` | `bricks.Image` | 全屏展示 |
| `markdown` | `bricks.MdWidget` | 加载远程 `.md` 文件 |
> 所有组件均设置宽高为 `100%`,适应容器。
---
### `show_conform(d)`
显示“是否开始”确认按钮。
#### 行为
- 清空主区域
- 添加一个标签为 `"Start ?"` 的按钮
- 绑定点击事件调用 `start_question_answer()`
---
### `start_question_answer()`
向服务端发送启动信号。
```js
this.ws.send({
type: 'qa_start',
data: { d: 'test data', v: 100 }
});
```
通常由 `WebSocket.onopen` 或用户点击触发。
---
### `conform_start()`
发送用户确认开始指令。
```js
this.ws.send({
type: 'conform_start',
data: null
});
```
---
### `send_text_answer(e)`
处理文本答案提交。
#### 参数
- `e.data.texta`: 用户输入文本
#### 流程
- 获取输入值
- 发送 `text_answer` 消息
> 绑定于 `StrInput``blur` 事件(失焦即提交)
---
### `send_audio_answer(e)`
异步处理录音答案提交。
#### 参数
- `e.data.audio`: Blob 音频数据
#### 流程
1. 将 Blob 转为 Base64 字符串(依赖 `blobToBase64` 函数)
2. 发送 `audio_answer` 消息
> ⚠️ 注意:`send_audio_data` 方法未定义,应为笔误,实际应为 `send_audio_answer`
---
### `build_input_widgets()`
构建底部输入控件区。
#### 创建组件
- `StrInput`: 文本输入框name=`texta`,样式填充)
- `AudioRecorder`: 语音录制按钮(带图标动画)
#### 事件绑定
- 文本框失焦 → `send_text_answer`
- 录音结束 → `send_audio_answer`(原写错为 `send_audio_data`
> ❗ 注释部分包含图像/视频采集功能预留接口SVG 图标),目前未启用。
---
### `play_course()`
【未使用】根据 `this.courseware` 播放初始课程内容。
> 当前逻辑中未调用此方法。若需启用,应在构造函数中补充调用。
---
### `build_startbtn()`
构建底部“press to start”按钮。
> 当前未在构造函数中调用,可能用于手动控制启动流程。
---
## 工厂注册
```js
bricks.Factory.register('QAFrame', bricks.QAFrame);
```
允许通过工厂模式按名称创建实例:
```js
bricks.create({ type: 'QAFrame', ws_url: 'ws://localhost:8080' });
```
---
## 使用示例
```html
<div id="qa-container"></div>
<script>
const qa = new bricks.QAFrame({
ws_url: 'ws://example.com/ws/qa',
ws_params: {
session_id: 'abc123',
user_id: 'u456'
},
courseware: {
type: 'video',
url: '/intro.mp4',
timeout: 15
}
});
document.getElementById('qa-container').appendChild(qa.root());
</script>
```
---
## 已知问题与改进建议
| 问题 | 描述 | 建议 |
|------|------|------|
| `qtotal_w` / `qcur_w` 未定义 | `show_question` 中尝试调用 `.set_text()`,但未初始化这些 widget | 应在构造函数中创建并加入 `top_w` |
| `oncourseware` 多次绑定 | 同一事件监听器重复注册三次 | 保留一次即可 |
| `send_audio_data` 调用错误 | `bind('record_ended')` 指向不存在的方法 | 改为 `this.send_audio_answer.bind(this)` |
| `play_course()` 未调用 | 初始化课件无法自动播放 | 在合适时机调用(如构造末尾判断是否存在 `courseware` |
| `StrInput` 工厂调用不明确 | `StrInput({...})` 不符合常规语法 | 应为 `new bricks.StrInput(...)` 或使用 `widgetBuild` |
---
## 总结
`bricks.QAFrame` 是一个功能完整、结构清晰的互动问答组件,具备以下优势:
✅ 支持多种媒体格式
✅ 实时双向通信
✅ 模块化设计便于扩展
✅ 适合嵌入式教学系统
建议修复上述问题以提升稳定性和可维护性。
---
*文档版本v1.0*
*最后更新2025年4月*

410
docs/cn/recorder.md Normal file
View File

@ -0,0 +1,410 @@
# `bricks.MediaRecorder` 技术文档
## 概述
`bricks.MediaRecorder` 是一个基于浏览器 `MediaRecorder API` 的通用媒体录制组件,继承自 `bricks.Popup`。它提供了统一的界面和控制逻辑,支持音频、视频以及特定 DOM 元素(如 `<video>`)的录制功能。
通过继承机制,该类派生出多个具体实现:
- `WidgetRecorder`:录制指定页面中的媒体元素(如 `<video>``<audio>`
- `SysAudioRecorder`:录制系统麦克风输入(音频流),并自动转换 WebM 为 WAV 格式
- `SysVideoRecorder`:录制摄像头及麦克风输入(音视频流),实时预览画面
- `SysCamera`:仅拍照模式,用于从摄像头抓取单帧图像
所有子类均通过 `bricks.Factory` 注册,便于在应用中动态创建。
---
## 基础类:`bricks.MediaRecorder`
### 继承关系
```js
class bricks.MediaRecorder extends bricks.Popup
```
### 构造函数
```js
constructor(opts)
```
#### 参数
| 参数名 | 类型 | 必需 | 默认值 | 说明 |
|-------|------|------|--------|------|
| `opts.fps` | Number | 否 | `30` | 帧率(每秒更新次数),用于时间显示刷新频率 |
#### 初始化内容
- 设置任务周期:`this.task_period = 1 / fps`
- 创建 UI 组件:
- `preview`: 预览区域VBox
- `controls`: 控制栏HBox
- `toggle_record`: 开始/停止按钮SVG 图标)
- `timepass`: 显示已录制时间(格式:`HH:MM:SS`
- `filler`: 弹性填充空间
- `cancel`: 取消按钮(删除图标)
- 绑定事件:
- `toggle_record.click``switch_record()`
- `cancel.click``cancel_record()`
- 初始化状态:
- `record_status = 'standby'`
- 初始禁用录制按钮
- 调度 `open_recorder()` 在 0.1 秒后执行(异步准备设备)
---
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `fps` | Number | 每秒帧数,决定 UI 更新频率 |
| `task_period` | Number | 定时任务间隔(秒) |
| `task` | TaskHandle | 当前运行的时间更新定时器 |
| `pic_task` | TaskHandle | (仅视频)图像捕获定时器 |
| `stream` | MediaStream | 获取到的媒体流 |
| `mediaRecorder` | MediaRecorder | 浏览器原生录制实例 |
| `recordedChunks` | Array<Blob> | 存储录制过程中产生的数据块 |
| `mimetype` | String | 媒体 MIME 类型(如 `'audio/wav'`, `'video/mp4'` |
| `normal_stop` | Boolean | 是否正常结束录制(防止重复触发) |
| `record_status` | String | 状态:`'standby'``'recording'` |
| `start_time` | Number | 录制开始的时间戳(毫秒) |
| `time_diff` | String | 当前录制时长字符串(`HH:MM:SS` |
---
### 方法
#### `tick_task()`
定时更新录制时间显示。
```js
tick_task()
```
- 使用 `schedule_once` 循环调用自身,间隔为 `this.task_period`
- 调用 `bricks.timeDiff(this.start_time)` 计算经过时间
- 更新 `this.timepass` 文本
> ⚠️ 注意:必须手动取消任务以避免内存泄漏。
---
#### `async switch_record()`
切换录制状态(开始或停止录制)。
```js
switch_record()
```
行为根据当前 `record_status` 决定:
| 状态 | 动作 |
|------|------|
| `'standby'` | 调用 `start_recorder()`,图标变为“停止” |
| `'recording'` | 调用 `stop_recorder()`,图标恢复为“开始” |
---
#### `cancel_record()`
取消当前录制操作,关闭资源并关闭弹窗。
```js
cancel_record()
```
等价于调用 `close_recorder()`
---
#### `async open_recorder()`
**虚方法**:由子类重写以打开具体的媒体源。
```js
open_recorder()
```
> 默认实现为空,仅输出调试日志。
子类应在此方法中:
- 请求用户权限(如麦克风/摄像头)
- 获取 `MediaStream`
- 设置 `this.stream`
- 解除录制按钮禁用状态
---
#### `async start_recorder()`
启动实际录制流程。
##### 步骤:
1. 设置 `normal_stop = false`
2. 实例化 `MediaRecorder` 并绑定事件:
- `ondataavailable`: 收集数据块到 `recordedChunks`
- `onstop`: 处理最终文件生成与事件分发
3. 记录 `start_time`
4. 启动定时器更新时间显示
5. 调用 `mediaRecorder.start()`
6. 触发 `record_started` 事件
##### 事件分发
- `record_started`: 录制开始时触发
---
#### `async blob_convert(blob)`
**可扩展方法**:允许子类对录制结果 Blob 进行格式转换。
```js
blob_convert(blob) → Promise<Blob>
```
默认直接返回原始 Blob。
> 示例:`SysAudioRecorder` 中将 WebM 转换为 WAV。
---
#### `stop_recorder()`
停止录制,并标记为正常终止。
##### 行为:
- 设置 `normal_stop = true`
- 更新时间显示
- 调用 `mediaRecorder.stop()`
- 调用 `close_recorder()`
---
#### `close_recorder()`
清理资源并关闭弹窗。
##### 清理项:
- 取消 `task``pic_task`(如有)
- 停止 `mediaRecorder`(若存在)
- 停止 `stream` 所有轨道
- 调用 `dismiss()` 关闭弹窗
> 若正在录制,会强制中断。
---
## 子类详解
---
### `bricks.WidgetRecorder`
> 录制页面中某个媒体元素(如 `<video>``<audio>`
#### 继承链
```js
class WidgetRecorder extends MediaRecorder
```
#### `open_recorder()`
- 根据 `opts.widgetid` 查找目标组件
- 检查其 DOM 标签类型:
- `<video>``mimetype = 'video/mp4'`
- `<audio>``mimetype = 'audio/wav'`
- 调用 `captureStream()` 获取流
- 启用录制按钮
⚠️ 错误处理:非媒体元素抛出异常
---
### `bricks.SysAudioRecorder`
> 录制麦克风输入,支持 WebM → WAV 自动转换
#### 继承链
```js
class SysAudioRecorder extends MediaRecorder
```
#### `open_recorder()`
- 请求麦克风权限(`getUserMedia({ audio: true })`
- 设置 `mimetype = 'audio/webm'`
- 获取音频流并启用按钮
#### `blob_convert(webmBlob)`
将 WebM 编码的音频解码为 PCM再编码为标准 WAV 格式 Blob。
##### 流程:
1. `webmBlob.arrayBuffer()` → 获取二进制数据
2. 使用 `AudioContext.decodeAudioData()` 解码为 `AudioBuffer`
3. 调用 `encodeWAV()` 生成 WAV 文件头 + 数据
4. 返回新的 `Blob(type='audio/wav')`
#### `encodeWAV(audioBuffer)`
手动构建 WAV 文件二进制结构,包含 RIFF 头信息。
| 参数 | 值 |
|------|----|
| 采样率 | 来自 `audioBuffer.sampleRate` |
| 声道数 | `audioBuffer.numberOfChannels` |
| 位深 | 16-bit PCM |
| 格式 | 小端序little-endian |
使用 `DataView` 写入头部字段。
#### `interleave(left, right)`
交错左右声道数据,生成立体声 PCM 数组。
#### `writeString(view, offset, string)`
辅助函数:向 `DataView` 写入 ASCII 字符串。
---
### `bricks.SysVideoRecorder`
> 录制摄像头+麦克风,带实时预览
#### 继承链
```js
class SysVideoRecorder extends MediaRecorder
```
#### `open_recorder()`
- 请求音视频权限:`{ audio: true, video: true }`
- 设置 `mimetype = 'video/mp4'`
- 创建 `ImageCapture(track)` 对象用于逐帧捕获
- 添加 `bricks.Image` 到预览区
- 启动 `show_picture()` 循环任务
#### `show_picture()`
- 调用 `imageCapture.takePhoto()` 获取当前帧 Blob
- 转换为 Data URL 并设置给 `imgw`
- 创建 `File` 对象供后续使用
- 递归调度下一次拍摄
> 实现了类似“摄像机取景器”的效果
#### `close_recorder()`
重写以额外清理 `pic_task`
---
### `bricks.SysCamera`
> 专用拍照工具,从摄像头抓拍一张照片
#### 继承链
```js
class SysCamera extends SysVideoRecorder
```
#### `switch_record()`
重写行为为“拍照”而非录制:
##### 动作:
- 阻止事件冒泡(`event.stopPropagation()`
- 停止 `pic_task`
- 设置 `task_period = 0` 防止继续更新
- 分发 `shot` 事件,携带:
```js
{
url: this.dataurl, // 图像 Data URL
file: this.imgfile // 图像 File 对象
}
```
- 调用 `close_recorder()` 关闭
> 不进行任何视频录制,仅抓取一帧
---
## 工厂注册
以下类通过 `bricks.Factory` 注册,可在配置中通过名称实例化:
```js
bricks.Factory.register('SysCamera', bricks.SysCamera);
bricks.Factory.register('WidgetRecorder', bricks.WidgetRecorder);
bricks.Factory.register('SysAudioRecorder', bricks.SysAudioRecorder);
bricks.Factory.register('SysVideoRecorder', bricks.SysVideoRecorder);
```
示例使用方式(假设框架支持):
```json
{
"type": "SysAudioRecorder",
"opts": {
"fps": 25
}
}
```
---
## 事件列表
| 事件名 | 触发时机 | 传递数据 |
|--------|----------|---------|
| `record_started` | 录制开始时 | 无 |
| `record_end` | 录制正常结束 | `{ url: string, file: File }` |
| `shot` | SysCamera拍照完成 | `{ url: string, file: File }` |
> 使用 `this.dispatch(eventName, data)` 触发
---
## 工具函数依赖
| 函数 | 来源 | 用途 |
|------|------|------|
| `bricks_resource(path)` | 全局函数 | 解析静态资源路径 |
| `schedule_once(fn, delay)` | 全局函数 | 延迟执行函数(支持取消) |
| `bricks.timeDiff(timestamp)` | 工具模块 | 将时间戳差值转为 `HH:MM:SS` 字符串 |
| `bricks.getWidgetById(id, app)` | 框架 API | 根据 ID 获取组件实例 |
---
## 使用示例
### 启动音频录制
```js
const recorder = new bricks.SysAudioRecorder({
fps: 30
});
recorder.bind('record_end', (data) => {
console.log('录音完成:', data.file);
const audio = document.createElement('audio');
audio.src = data.url;
audio.controls = true;
document.body.appendChild(audio);
});
recorder.present(); // 显示录制界面
```
### 抓拍摄像头照片
```js
const camera = new bricks.SysCamera();
camera.bind('shot', ({ file }) => {
console.log('照片已拍摄:', file);
});
camera.present();
```
---
## 注意事项
1. **权限请求**`getUserMedia` 需要在安全上下文HTTPS中运行且用户需授权。
2. **兼容性**`MediaRecorder` 在部分旧浏览器中不支持或需前缀。
3. **内存管理**:长时间录制可能导致大量 `recordedChunks` 占用内存,建议限制最大时长。
4. **格式支持**:不同浏览器支持的 `mimeType` 不同,应做检测回退。
5. **跨域问题**`captureStream()``<video>` 要求同源或 CORS 允许。
---
## 总结
`bricks.MediaRecorder` 提供了一个模块化、可扩展的媒体录制架构,适用于多种场景:
| 类 | 用途 | 输出格式 |
|-----|------|---------|
| `WidgetRecorder` | 录屏(特定元素) | MP4 / WAV |
| `SysAudioRecorder` | 录音 | WAV经转换 |
| `SysVideoRecorder` | 录像 | MP4 |
| `SysCamera` | 拍照 | JPEG |
结合 `bricks` 框架的组件系统与事件机制,可轻松集成至可视化编辑器或交互式应用中。

183
docs/cn/registerfunction.md Normal file
View File

@ -0,0 +1,183 @@
# Bricks RegisterFunction 模块技术文档
---
## 概述
`RegisterFunction``bricks` 命名空间下的一个轻量级函数注册与管理类,用于在全局环境中注册、存储和获取函数。它提供了一种集中式的方式来管理可复用或动态调用的函数,适用于插件系统、事件处理器注册、模块化功能扩展等场景。
该模块通过 `bricks.RF` 实例暴露给全局使用,确保函数注册的统一访问接口。
---
## 代码结构
```javascript
var bricks = window.bricks || {};
class RegisterFunction {
constructor() {
this.rfs = {};
}
register(n, f) {
this.rfs[n] = f;
}
get(n) {
try {
return this.rfs[n];
} catch (e) {
return null;
}
}
}
bricks.RF = new RegisterFunction();
```
---
## 核心类:`RegisterFunction`
### 构造函数 `constructor()`
初始化一个空的对象 `this.rfs`,用于存储注册的函数。
#### 示例:
```js
const rf = new RegisterFunction(); // this.rfs = {}
```
---
### 方法:`register(n, f)`
将一个函数 `f` 注册到名称 `n` 下。
#### 参数:
- `n` (string | number): 函数的唯一标识名称(键名)。
- `f` (Function): 要注册的函数。
#### 行为:
- 将 `f` 存储在 `this.rfs` 对象中,以 `n` 作为键。
- 若已存在同名键,则覆盖原值。
#### 示例:
```js
function greet() {
console.log("Hello!");
}
bricks.RF.register('greet', greet);
```
---
### 方法:`get(n)`
根据名称 `n` 获取已注册的函数。
#### 参数:
- `n` (string | number): 要获取的函数的名称。
#### 返回值:
- 如果存在对应的函数,返回该函数;
- 如果不存在或发生异常,返回 `null`
> ⚠️ 注意:尽管 `try-catch` 被使用,但在 JavaScript 中直接访问对象属性不会抛出异常(除非对象被冻结或有 getter 抛错)。因此这里的 `try-catch` 属于防御性编程,实际多数情况下非必需。
#### 示例:
```js
const greetFn = bricks.RF.get('greet');
if (greetFn) {
greetFn(); // 输出: Hello!
}
```
---
## 全局实例:`bricks.RF`
通过以下语句创建并暴露全局唯一的 `RegisterFunction` 实例:
```js
bricks.RF = new RegisterFunction();
```
此实例可在全局范围内通过 `bricks.RF` 访问,实现跨模块的函数注册与调用。
---
## 使用示例
### 注册多个函数
```js
bricks.RF.register('log', msg => console.log(msg));
bricks.RF.register('add', (a, b) => a + b);
```
### 调用已注册函数
```js
const log = bricks.RF.get('log');
log('This is a test message.');
const add = bricks.RF.get('add');
console.log(add(2, 3)); // 输出: 5
```
### 处理未注册函数
```js
const unknown = bricks.RF.get('unknown');
console.log(unknown); // 输出: null
```
---
## 设计特点
| 特性 | 说明 |
|------|------|
| **轻量** | 仅包含两个核心方法,无外部依赖。 |
| **易用** | 接口简洁,便于集成到现有项目中。 |
| **容错处理** | `get()` 方法包含异常捕获,增强健壮性。 |
| **可扩展** | 可基于此类构建更复杂的注册中心(如带命名空间、生命周期管理等)。 |
---
## 注意事项
1. **命名冲突**:注册时使用唯一名称,避免覆盖重要函数。
2. **类型检查缺失**:当前未对传入的 `f` 是否为函数进行校验,建议调用前自行验证。
3. **性能考虑**:底层基于普通对象存储,适合中小型注册场景;高频读写可考虑 `Map` 结构优化。
---
## 改进建议(可选)
```js
// 增强版 register增加函数类型校验
register(n, f) {
if (typeof f !== 'function') {
throw new Error(`Expected a function, but got ${typeof f}`);
}
this.rfs[n] = f;
}
// 或使用 Map 提升性能(尤其在大量条目时)
this.rfs = new Map();
register(n, f) { this.rfs.set(n, f); }
get(n) { return this.rfs.get(n) || null; }
```
---
## 版本信息
- 初始版本1.0
- 作者Bricks Framework Team
- 兼容环境:现代浏览器(支持 ES6 Class
---
> 📌 提示:本模块是 `bricks` 工具库的基础组件之一,推荐与其他模块配合使用以发挥最大效能。

435
docs/cn/rtc.md Normal file
View File

@ -0,0 +1,435 @@
# Bricks WebRTC 通信框架技术文档
本文档描述了 `bricks` 框架中用于实现点对点P2P实时音视频通信的核心模块包括
- `bricks.VideoBox`:用于播放媒体流的视频组件。
- `bricks.Signaling`:信令服务客户端,通过 WebSocket 与服务器通信。
- `bricks.RTCP2PConnect`:基于 WebRTC 的 P2P 连接管理器。
---
## 1. `bricks.VideoBox`
一个封装了 `<video>` 元素的 JavaScript 组件,用于显示媒体流(如摄像头或屏幕共享流)。
### 类定义
```js
class bricks.VideoBox extends bricks.JsWidget
```
### 方法
#### `create()`
创建并初始化 `<video>` DOM 元素,并设置自动播放和静音属性。
**行为:**
- 创建 `video` 元素。
- 设置 `autoplay``muted` 属性为 `true`
> ⚠️ 注意:由于现代浏览器的安全策略,`autoplay` 只能在用户交互后生效,且必须配合 `muted` 才能无提示播放。
#### `get_stream() → MediaStream`
获取当前绑定到该视频元素的媒体流。
**返回值:**
- `{MediaStream}` 当前播放的媒体流对象。
#### `set_stream(stream: MediaStream)`
将指定的媒体流绑定到 `<video>` 元素上进行播放。
**参数:**
- `stream`: `{MediaStream}` 要播放的媒体流。
**行为:**
- 将 `stream` 赋值给内部变量 `this.stream`
- 设置 `this.dom_element.srcObject = stream`,触发视频播放。
---
## 2. `bricks.Signaling`
信令客户端类,负责与信令服务器建立 WebSocket 连接,处理登录、心跳、消息收发及会话分发。
### 构造函数
```js
new bricks.Signaling(opts)
```
**参数 `opts`**
| 参数 | 类型 | 描述 |
|------|------|------|
| `signaling_url` | `String` | WebSocket 信令服务器地址 |
| `info` | `Object` | 客户端信息(如用户 ID、设备信息等在发送数据时附加为 `msgfrom` 字段 |
| `connect_opts` | `Object` | 传递给后续连接模块的通用配置选项 |
| `onclose` | `Function` | WebSocket 关闭时的回调函数 |
| `onopen` | `Function` | WebSocket 成功打开时的回调函数(可选) |
| `onlogin` | `Function(onlinePeers)` | 登录成功后接收在线用户列表的回调 |
| `heartbeat_period` | `Number` | 心跳周期(秒),若不设则不启用 |
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `socket` | `WebSocket` | 当前 WebSocket 实例 |
| `peers` | `Object` | 存储当前在线用户的映射表key: peer.id |
| `sessions` | `Object` | 已建立的会话记录(未使用) |
| `handlers` | `Object` | 每个会话 ID 对应的消息处理器 |
| `sessionhandlers` | `Object` | 按会话类型注册的处理器构造函数 |
| `hb_task` | `TaskHandle` | 心跳定时任务句柄(由 `schedule_once` 返回) |
| `heartbeat_period` | `Number` | 心跳间隔时间(秒) |
### 方法
#### `init_websocket()`
初始化 WebSocket 连接:
- 使用 `bricks.app.get_session()` 获取会话凭证作为子协议。
- 绑定事件监听器:
- `onmessage``signaling_recvdata`
- `onopen``login`
- `onclose` / `onerror``reconnect`
#### `reconnect()`
WebSocket 断开后的处理逻辑:
- 清除心跳任务。
- 触发 `onclose` 回调。
- **注意:当前未实现自动重连机制。**
#### `signaling_recvdata(event)`
异步处理从 WebSocket 接收到的数据包。
**流程:**
1. 解析 JSON 数据。
2. 若包含 `data.session`,查找对应会话处理器:
- 若首次收到,则根据 `sessiontype` 实例化对应的处理器类。
- 注册该会话的 `recvdata_handler`
3. 否则调用默认 `recvdata_handler` 处理全局消息。
#### `add_handler(key, handler)`
注册某个 `sessionid` 的消息处理器。
#### `add_sessionhandler(sessiontype, handlerClass)`
注册某种会话类型的处理器类(构造函数)。例如:
```js
signaling.add_sessionhandler('p2p', bricks.RTCP2PConnect);
```
#### `recvdata_handler(data)`
默认消息处理器,处理非会话级消息。
**支持的消息类型:**
- `online`: 更新在线用户列表,并触发 `onlogin(online)` 回调。
> ❗ 未处理其他消息类型时仅打印日志。
#### `new_session(sessiontype, peer)`
发起一个新的会话请求。
**步骤:**
1. 查找 `sessiontype` 对应的处理器类。
2. 生成唯一 `sessionid`
3. 发送 `{ type: 'new_session', session }` 到服务器。
4. 创建处理器实例并注册其 `recvdata_handler`
5. 返回处理器实例(通常是 `RTCP2PConnect` 实例)。
#### `login()`
向服务器发送登录请求。
**行为:**
- 发送 `{ type: 'login' }`
- 如果设置了 `heartbeat_period > 0`,启动周期性心跳任务。
#### `logout()`
发送登出请求 `{ type: 'logout' }`
#### `send_data(d)`
将对象序列化为 JSON 并通过 WebSocket 发送。
**附加字段:**
- `d.msgfrom = this.info`(标识发送者)
#### `socket_send(s)`
直接发送原始字符串(底层接口,一般不推荐直接使用)。
---
## 3. `bricks.RTCP2PConnect`
WebRTC P2P 连接控制器,处理音视频通话和数据通道通信。
### 构造函数
```js
new bricks.RTCP2PConnect(signaling, session, opts)
```
**参数:**
| 参数 | 类型 | 描述 |
|------|------|------|
| `signaling` | `bricks.Signaling` | 信令客户端实例 |
| `session` | `Object` | 会话元信息 `{ sessionid, sessiontype }` |
| `opts` | `Object` | 配置项,见下表 |
**`opts` 配置说明:**
| 选项 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `ice_servers` | `Array` | 必需 | ICE 服务器配置STUN/TURN |
| `peer_info` | `Object` | 必需 | 对端用户信息(含 `.id` |
| `auto_callaccept` | `Boolean` | `false` | 是否自动接受呼叫 |
| `media_options` | `Object` | `{ video: true, audio: true }` | 媒体采集选项 |
| `data_connect` | `Boolean` | `false` | 是否启用数据通道 |
| `on_pc_connected(peer)` | `Function` | - | 连接建立成功回调 |
| `on_pc_disconnected(peer)` | `Function` | - | 连接断开回调 |
| `on_dc_open(dc)` | `Function` | - | 数据通道开启回调 |
| `on_dc_close(dc)` | `Function` | - | 数据通道关闭回调 |
| `on_dc_message(dc, data)` | `Function` | - | 收到数据通道消息回调 |
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `id` | `String` | 本地唯一标识UUID |
| `signaling` | `Signaling` | 信令客户端引用 |
| `session` | `Object` | 当前会话信息 |
| `requester` | `Boolean` | 是否为主叫方 |
| `peers` | `Object` | 所有远程连接的 `PeerConnection` 管理对象 |
| `signal_handlers` | `Object` | 信令消息处理器映射 |
| `local_stream` | `MediaStream` | 本地摄像头流 |
| `localVideo` | `VideoBox` | 本地视频预览组件 |
| `local_screen` | `MediaStream` | 屏幕共享流(可选) |
---
### 核心方法
#### `add_handler(type, f)`
注册信令消息处理器(如 `'offer'`, `'answer'` 等)。
#### `get_handler(type) → Function`
获取指定类型的消息处理器。
#### `p2pconnect(peer)`
尝试与指定对端建立 P2P 连接。
**流程:**
1. 获取本地媒体流(如果尚未获取)。
2. 若已存在连接则跳过。
3. 调用 `createPeerConnection(peer)` 创建新的 `RTCPeerConnection`
#### `h_sessioncreated(data)`
当会话创建完成时触发。
**行为:**
- 自动调用 `p2pconnect`
- 若有 `peer_info`,主动发送 `callrequest` 请求呼叫。
#### `h_callrequest(data)`
收到呼叫请求。
**行为:**
- 若 `auto_callaccept === true`(或硬编码为 `true`),自动响应:
- 建立连接。
- 发送 `callaccepted` 消息。
#### `h_callaccepted(data)`
对方接受呼叫。
**行为:**
- 创建数据通道(如果启用)。
- 发送 SDP Offer。
#### `h_offer(data)`
收到远程 Offer。
**流程:**
1. 设置 `remoteDescription`
2. 创建 Answer。
3. 设置 `localDescription`
4. 发送 Answer 回去。
5. 如果不是主叫方,再发送一次 Offer可能存在设计问题需确认逻辑
#### `h_answer(data)`
收到远程 Answer。
**行为:**
- 设置 `remoteDescription`
#### `h_icecandidate(data)`
收到 ICE 候选地址。
**行为:**
- 添加到 `RTCPeerConnection` 中。
#### `h_sessionquit(data)`
收到会话结束通知。
**行为:**
- 关闭对应 `RTCPeerConnection`
#### `send_offer(peer, initial = false)`
创建并发送 SDP Offer。
**参数:**
- `peer`: 目标用户。
- `initial`: 是否是首次呼叫(决定角色:`requester` / `responser`
**流程:**
1. 创建 Offer。
2. 设置 `localDescription`
3. 通过信令发送 Offer。
#### `send_candidate(peer, event)`
ICE 候选事件触发时调用,转发候选地址给远端。
> ✅ 使用 `bind(this, peer)` 绑定上下文。
#### `signaling_send(d)`
封装信令消息,附带当前 `session` 信息后发送。
#### `recvdata_handler(data)`
统一入口处理所有信令消息:
- 查找对应处理器并执行。
- 未处理则输出警告日志。
---
### 连接状态监听
#### `ice_statechange(peer, event)`
ICE 连接状态变化时的日志输出。
#### `connection_statechange(peer, event)`
连接状态变化处理:
- `disconnected`: 触发 `peer_close()``on_pc_disconnected` 回调。
- `connected`: 触发 `on_pc_connected` 回调。
---
### 数据通道DataChannel
#### `dc_accepted(peer, event)`
收到远程数据通道请求。
**行为:**
- 保存 `event.channel`
- 调用 `dc_created()` 初始化。
#### `dc_created(peer, dc)`
初始化数据通道事件监听器:
- `onmessage``datachannel_message`
- `onopen``datachannel_open`
- `onclose``datachannel_close`
#### `datachannel_message(peer, event)`
收到数据通道消息。
**行为:**
- 触发 `on_dc_message(dc, event.data)` 回调。
#### `datachannel_open/close(peer)`
分别在数据通道打开/关闭时触发相应回调。
#### `createDataChannel(peer)`
主动创建数据通道(用于主叫方)。
---
### 媒体流控制
#### `createPeerConnection(peer)`
创建并配置 `RTCPeerConnection` 实例。
**关键操作:**
- 使用传入的 `iceServers`
- 添加本地媒体轨道(如有)。
- 绑定以下事件:
- `onicecandidate`
- `oniceconnectionstatechange`
- `onconnectionstatechange`
- `ondatachannel`(注意:拼写错误应为 `ondatachannel`
- `ontrack` → 将远端流绑定到 `VideoBox`
> 🛠️ Bug 提示:`pc.ondatachanel` 应为 `pc.ondatachannel`
#### `changeLocalVideoStream(peer, new_stream)`
动态更换本地视频流(如切换摄像头或屏幕共享)。
**流程:**
1. 移除旧的视频 Track。
2. 添加新流中的视频 Track。
3. 重新发送 Offer 以更新协商。
#### `getLocalStream()`
获取本地摄像头和屏幕共享流。
**行为:**
- 调用 `getUserMedia(media_options)` 获取摄像头流。
- 调用 `getDisplayMedia({ video: true })` 获取屏幕共享流(音频可选)。
- 分别赋值给 `this.local_stream``this.local_screen`
- 创建 `localVideo` 并绑定摄像头流用于预览。
> 🔐 权限提示:需要用户授权麦克风和摄像头权限。
---
#### `peer_close(peer)`
清理与指定对端的所有资源。
**释放内容:**
- 停止所有接收/发送的媒体 Track。
- 停止远端视频流。
- 关闭数据通道和 `RTCPeerConnection`
- 删除 `peers` 中的记录。
- 若无剩余连接,停止本地流并从信令中注销会话处理器。
---
## 使用示例
```js
// 初始化信令
var signaling = new bricks.Signaling({
signaling_url: 'wss://example.com/signaling',
info: { id: 'user123', name: 'Alice' },
heartbeat_period: 30,
onlogin: function(peers) {
console.log('Online users:', peers);
}
});
// 注册 P2P 处理器
signaling.add_sessionhandler('p2p', bricks.RTCP2PConnect);
// 登录(触发 login
signaling.login();
// 发起一个 P2P 会话
var p2p = signaling.new_session('p2p', { id: 'user456' });
```
---
## 注意事项与潜在问题
| 问题 | 描述 | 建议修复 |
|------|------|---------|
| `ondatachanel` 拼写错误 | 应为 `ondatachannel` | 更正拼写 |
| `auto_callaccept || true` | 条件恒真,失去意义 | 改为 `this.opts.auto_callaccept === true` |
| `newStream` 变量名错误 | `changeLocalVideoStream` 中误写为 `newStream` | 改为 `new_stream` |
| 缺少异常处理 | 多处 `await` 未包裹 `try/catch` | 增加错误捕获 |
| 心跳任务未清除 | `schedule_once` 可能导致内存泄漏 | 确保正确取消任务 |
| 未实现自动重连 | `reconnect()` 仅触发回调 | 应加入延迟重试机制 |
---
## 总结
`bricks` 提供了一套完整的 WebRTC 信令与连接管理方案,具有以下特点:
**模块化设计**:分离信令、会话、连接逻辑
**扩展性强**:支持多种会话类型
**易于集成**:提供 VideoBox 等 UI 组件封装
⚠️ **需优化点**:存在若干 bug 与安全限制需处理
适用于开发音视频会议、直播互动、远程协作等实时通信应用。

198
docs/cn/running.md Normal file
View File

@ -0,0 +1,198 @@
# `bricks.Running` 模块技术文档
---
## 概述
`bricks.Running` 是一个用于显示运行中状态(如加载、处理中)的模态窗口组件,通常用于提示用户当前系统正在执行某项任务。它基于 `bricks.BaseModal` 和自定义的 `bricks.BaseRunning` 类构建,提供了一个动态更新的时间计时器和一个可配置的动画图标(默认为 `running.gif`)。
该模块适用于需要可视化反馈长时间操作进度的场景。
---
## 依赖说明
- `bricks.FHBox`:水平布局容器基类。
- `bricks.BaseModal`:模态窗口基类。
- `bricks.Icon`:图标显示组件。
- `bricks.Text`:文本显示组件。
- `bricks.formatMs()`:格式化毫秒时间的工具函数。
- `schedule_once()`:定时调度函数,用于周期性执行任务。
- `bricks_resource()`:资源路径解析函数,用于获取静态资源 URL。
> ⚠️ 注意:以上依赖需在运行环境中提前定义并可用。
---
## 类结构
### 1. `bricks.BaseRunning`
继承自 `bricks.FHBox`,用于构建包含图标与运行时间显示的横向控件。
#### 构造函数:`constructor(opts)`
##### 参数
| 参数名 | 类型 | 是否必填 | 描述 |
|--------|--------|----------|------|
| `opts.icon` | String | 否 | 图标图片的 URL 路径。若未提供,则使用默认资源路径下的 `running.gif`。 |
##### 功能说明
1. 调用父类构造函数。
2. 创建一个 `Icon` 组件用于展示运行动画。
3. 创建一个 `Text` 组件用于实时显示已运行时间。
4. 初始化起始时间戳(`this.time_start`)。
5. 将图标和文本控件添加到容器中。
6. 启动定时任务,每 50ms 更新一次时间显示。
##### 示例代码
```js
const runner = new bricks.BaseRunning({
icon: '/custom/loading.svg'
});
```
#### 方法:`show_timepass()`
- **功能**:计算从开始到当前经过的时间,并格式化后更新到 `Text` 控件上。
- **触发机制**:通过 `schedule_once` 每 50ms 自调用一次,形成持续更新效果。
- **时间格式**:调用 `bricks.formatMs(t, 1)`,保留一位小数,单位为秒(例如:`3.2s`)。
#### 方法:`stop_timepass()`
- **功能**:停止时间更新任务。
- **行为**
- 如果存在正在进行的定时任务(`this.showtime_task`),则取消该任务。
- 清空任务引用,防止内存泄漏。
---
### 2. `bricks.Running`
继承自 `bricks.BaseModal`,封装 `BaseRunning` 成一个自动弹出的模态框。
#### 构造函数:`constructor(opts)`
##### 参数
| 参数名 | 类型 | 是否必填 | 描述 |
|------------|--------|----------|------|
| `opts.target` | Element | 否 | 模态框挂载的目标 DOM 元素。默认为 body 或由 BaseModal 决定。 |
| `opts.icon` | String | 否 | 使用的图标路径,将传递给 `BaseRunning`。 |
##### 特性设置
- `auto_open: true`:实例化后自动打开模态框。
- `archor: 'cc'`模态框居中对齐center-center
##### 内部逻辑
- 创建 `bricks.BaseRunning` 实例作为内容主体。
- 将其加入模态框内容区域。
#### 方法:`dismiss()`
- **功能**:关闭模态框前,先停止计时器。
- **步骤**
1. 调用 `this.w.stop_timepass()` 停止时间更新。
2. 调用父类 `BaseModal.dismiss()` 关闭模态框。
> ✅ 提示:此方法确保资源释放和定时器清理。
---
### 3. 工厂注册
```js
bricks.Factory.register('Running', bricks.Running);
```
- 将 `Running` 组件注册到 `bricks.Factory` 中,支持通过字符串名称动态创建实例:
```js
bricks.Factory.create('Running', { icon: 'imgs/custom.gif' });
```
---
## 使用示例
### 示例 1基本使用默认图标
```js
new bricks.Running();
// 弹出居中模态框,显示默认 running.gif 与运行时间
```
### 示例 2自定义图标
```js
new bricks.Running({
icon: '/assets/icons/spinner.svg'
});
```
### 示例 3手动关闭
```js
const runner = new bricks.Running();
// 在某个异步操作完成后关闭
setTimeout(() => {
runner.dismiss(); // 自动停止计时器并关闭模态框
}, 3000);
```
---
## 样式与外观
| 组件 | 属性 | 值 |
|------------|-------------|------------------------|
| 文本颜色 | color | `#222` |
| 文本换行 | wrap | `false` |
| 国际化支持 | i18n | `false` |
| 容器布局 | FHBox | 水平排列图标与时间文本 |
| 模态框锚点 | archor | `'cc'`(居中) |
> 可通过扩展 CSS 或子类进一步定制样式。
---
## 性能与注意事项
- **刷新频率**:每 50ms 更新一次时间,平衡流畅性与性能。
- **资源管理**
- 必须调用 `dismiss()` 正确关闭,避免定时器持续运行。
- 不手动调用可能导致内存泄漏或界面卡顿。
- **跨浏览器兼容性**:依赖 `Date.now()``bind()`,建议配合 polyfill 在旧浏览器中使用。
---
## API 总结
| 类名 | 方法 / 属性 | 类型 | 描述 |
|-------------------|------------------------|----------|------|
| `bricks.BaseRunning` | `constructor(opts)` | 构造函数 | 初始化带图标的运行状态条 |
| | `show_timepass()` | 方法 | 定时更新运行时间 |
| | `stop_timepass()` | 方法 | 停止时间更新任务 |
| `bricks.Running` | `constructor(opts)` | 构造函数 | 创建并自动打开模态框 |
| | `dismiss()` | 方法 | 停止计时并关闭模态框 |
| `bricks.Factory` | `.register(type, cls)` | 静态方法 | 注册组件以便工厂创建 |
---
## 版本信息
- **作者**Bricks Framework Team
- **版本**1.0
- **最后更新**2025年4月5日
> 更多文档请参考 [Bricks UI 官方文档](https://bricks.example.com/docs)
---
📌 **建议用途**:数据加载、文件上传、后台处理等耗时操作的状态提示。

293
docs/cn/scroll.md Normal file
View File

@ -0,0 +1,293 @@
# Bricks.js 滚动面板组件技术文档
> **项目名称**`bricks.js`
> **模块**`HScrollPanel``VScrollPanel`
> **功能描述**:提供可监听滚动位置阈值的水平和垂直滚动容器组件。
---
## 目录
- [概述](#概述)
- [依赖说明](#依赖说明)
- [核心类说明](#核心类说明)
- [`low_handle`](#low_handle)
- [`bricks.HScrollPanel`](#bricksHScrollPanel)
- [`bricks.VScrollPanel`](#bricksVScrollPanel)
- [事件机制](#事件机制)
- [工厂注册](#工厂注册)
- [使用示例](#使用示例)
- [注意事项](#注意事项)
---
## 概述
`bricks.HScrollPanel``bricks.VScrollPanel` 是基于 `bricks.HBox``bricks.VBox` 的扩展类,用于创建具有滚动能力的容器,并在用户滚动接近内容边界时触发自定义事件(如 `min_threshold``max_threshold`),适用于实现“无限滚动”、“触底加载”等功能。
该组件通过封装原生 DOM 滚动行为,在关键滚动位置触发语义化事件,便于上层逻辑响应。
---
## 依赖说明
本模块依赖以下环境或模块:
- 全局对象 `window.bricks`:框架主命名空间。
- `bricks.debug()`:调试日志输出函数(仅用于开发期)。
- `bricks.HBox` / `bricks.VBox`:基础布局容器类。
- `bricks.Widget.dispatch()`:事件派发方法。
- `bricks.Factory.register()`:组件工厂注册接口。
---
## 核心类说明
### `low_handle`
#### 功能
通用滚动阈值判断函数,根据当前滚动位置判断是否到达最小或最大阈值,并触发相应事件。
#### 函数签名
```js
function low_handle(widget, dim, last_pos, cur_pos, maxlen, winsize)
```
#### 参数说明
| 参数名 | 类型 | 描述 |
|-------------|----------|------|
| `widget` | Object | 触发事件的组件实例(需支持 `.dispatch()` 方法) |
| `dim` | String | 滚动方向标识(`'x'``'y'`,目前未实际使用但预留) |
| `last_pos` | Number | 上一次记录的滚动位置 |
| `cur_pos` | Number | 当前滚动位置(例如 `scrollLeft``scrollTop` |
| `maxlen` | Number | 整体滚动内容长度(`scrollWidth``scrollHeight` |
| `winsize` | Number | 可见区域大小(`clientWidth``clientHeight` |
#### 行为逻辑
1. 如果当前滚动位置接近右/下边缘差值小于5像素则触发 `max_threshold` 事件。
2. 如果当前滚动位置接近左/上边缘小于1像素则触发 `min_threshold` 事件。
3. 使用 `bricks.debug()` 输出调试信息。
#### 示例调用
```js
low_handle(this, 'x', this.last_scrollLeft, e.scrollLeft, e.scrollWidth, e.clientWidth);
```
---
### bricks.HScrollPanel
水平滚动面板组件,继承自 `bricks.HBox`
#### 继承关系
```js
class HScrollPanel extends bricks.HBox
```
#### 构造函数
```js
constructor(opts)
```
##### 参数
| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `opts.width` | String | 否 | `'100%'` | 宽度设置 |
| `opts.height` | String | 否 | `'100%'` | 高度设置 |
| `opts.css` | String | 否 | `''` | 自定义 CSS 类名,自动附加 `scrollpanel` |
| `opts.overflow` | String | 否 | `'auto'` | 溢出处理方式 |
| `opts.min_threshold` | Number | 否 | `0.01` | 最小滚动比例阈值(暂未使用) |
| `opts.max_threshold` | Number | 否 | `0.99` | 最大滚动比例阈值(暂未使用) |
> ⚠️ 注:虽然传入了 `min_threshold``max_threshold`,但在当前版本中并未参与计算,而是直接使用像素差值判断。
##### 初始化行为
- 设置默认宽高为 100%。
- 添加 `scrollpanel` CSS 类。
- 设置 `overflow: auto` 以启用滚动条。
- 注册 `scroll` 事件监听器。
- 记录初始 `scrollLeft` 值。
#### 方法
##### `scroll_handle(event)`
处理滚动事件的核心方法。
###### 参数
- `event`: DOM 滚动事件对象。
###### 逻辑流程
1. 确保事件源是当前组件的 DOM 元素。
2. 检查是否有实际可滚动空间(`scrollWidth > clientWidth + 1`)。
3. 调用 `low_handle` 判断是否触发边界事件。
4. 更新 `scroll_info` 状态对象及 `last_scrollLeft`
###### `scroll_info` 结构
```js
{
left: number, // 当前 scrollLeft
scroll_width: number, // scrollWidth
client_width: number // clientWidth
}
```
---
### bricks.VScrollPanel
垂直滚动面板组件,继承自 `bricks.VBox`
#### 继承关系
```js
class VScrollPanel extends bricks.VBox
```
#### 构造函数
```js
constructor(opts)
```
##### 参数
`HScrollPanel` 类似,区别如下:
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `min_threshold` | `0.02` | 垂直方向最小阈值 |
| `max_threshold` | `0.95` | 垂直方向最大阈值 |
> ⚠️ 当前版本中这些阈值仍未被算法使用。
##### 初始化行为
- 设置默认宽高。
- 添加 `scrollpanel` 类。
- 开启 `overflow: auto`
- 绑定 `scroll` 事件。
- 记录初始 `scrollTop`
#### 方法
##### `scroll_handle(event)`
处理垂直滚动事件。
###### 逻辑流程
1. 验证事件目标。
2. 检查是否存在可滚动高度。
3. 调用 `low_handle` 判断上下边界。
4. 更新 `scroll_info``last_scrollTop`
###### `scroll_info` 结构
```js
{
top: number, // scrollTop
scroll_height: number, // scrollHeight
client_height: number // clientHeight
}
```
---
## 事件机制
组件会在特定滚动条件下自动派发事件:
| 事件名 | 触发条件 |
|------------------|---------|
| `'min_threshold'` | 滚动位置接近顶部或左侧(`< 1px` |
| `'max_threshold'` | 滚动位置接近底部或右侧(距离末端 < 5px |
可通过 `.bind()` 方法监听这些事件:
```js
panel.bind('min_threshold', function(){
console.log('到达顶部/起点');
});
```
---
## 工厂注册
为了支持动态创建,两个组件均注册到 `bricks.Factory`
```js
bricks.Factory.register('VScrollPanel', bricks.VScrollPanel);
bricks.Factory.register('HScrollPanel', bricks.HScrollPanel);
```
这意味着可以通过工厂方式实例化:
```js
var vpanel = bricks.Factory.create('VScrollPanel', { ... });
```
---
## 使用示例
### 创建一个垂直滚动面板并监听边界事件
```js
var panel = new bricks.VScrollPanel({
css: 'my-scroll-container',
min_threshold: 0.02,
max_threshold: 0.95
});
panel.bind('min_threshold', function() {
console.log('已滚动到顶部,可加载更多...');
});
panel.bind('max_threshold', function() {
console.log('已滚动到底部,加载新数据...');
});
// 将 panel 添加到父容器中
parent.append(panel);
```
### 创建水平滚动面板
```js
var hpanel = new bricks.HScrollPanel({
css: 'horizontal-list'
});
hpanel.bind('max_threshold', function() {
alert('已滑动到最右侧!');
});
```
---
## 注意事项
1. **阈值未生效问题**:尽管构造函数接受 `min_threshold``max_threshold`,但当前 `low_handle` 使用的是固定像素判断(`<1``<5`),未来建议改为基于比例的动态判断以提升灵活性。
2. **性能优化建议**
- 可添加防抖debounce机制避免频繁触发。
- 在复杂场景下考虑使用 `requestAnimationFrame` 控制检测频率。
3. **兼容性要求**
- 支持现代浏览器。
- 依赖标准 DOM 属性如 `scrollLeft`, `scrollTop`, `scrollWidth`, `clientHeight` 等。
4. **调试模式**
- 使用 `bricks.debug()` 输出日志,上线前应关闭或重定向。
5. **CSS 要求**
- 推荐为 `.scrollpanel` 设置明确的尺寸和 `overflow` 样式,确保滚动正常工作。
---
## 版本信息
- 编写时间2025年4月
- 模块版本v1.0(基础功能版)
- 维护建议:增加对比例阈值的支持,提升可配置性。
---
✅ **文档完**

139
docs/cn/splitter.md Normal file
View File

@ -0,0 +1,139 @@
# `bricks.Splitter` 组件技术文档
## 概述
`bricks.Splitter` 是一个基于 `bricks.JsWidget` 的轻量级 UI 组件,用于在页面中创建分隔线(`<hr>` 元素)。它通常用于视觉上分隔不同内容区域,适用于构建模块化布局。
该组件通过 Bricks 框架的工厂模式注册,可通过 `bricks.Factory` 实例化并插入 DOM。
---
## 类定义
```javascript
bricks.Splitter = class extends bricks.JsWidget
```
- **继承自**: `bricks.JsWidget`
- **用途**: 创建水平分隔线(`<hr>`
- **注册名称**: `'Splitter'`
---
## 构造函数
### `constructor(ops)`
初始化 `Splitter` 实例。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `ops` | Object | 可选配置对象(当前未使用,传递给父类) |
#### 实现细节
```js
constructor(ops) {
super({});
}
```
- 调用父类 `bricks.JsWidget` 的构造函数。
- 当前不依赖任何配置项,传入空对象 `{}`
---
## 方法
### `create()`
创建并初始化组件的 DOM 结构。
#### 实现
```js
create() {
this.dom_element = this._create('hr');
}
```
#### 行为说明
- 使用父类提供的 `_create(tagName)` 工具方法创建一个 `<hr>` 元素。
- 将生成的 DOM 元素赋值给实例属性 `this.dom_element`,供后续操作使用。
- `<hr>` 是标准 HTML 水平分隔线元素,语义清晰,样式可通过 CSS 自定义。
---
## 注册与使用
### 注册组件
```js
bricks.Factory.register('Splitter', bricks.Splitter);
```
- 将 `Splitter` 类注册到 `bricks.Factory` 中,注册名为 `'Splitter'`
- 注册后可通过工厂方法动态创建实例。
---
## 使用示例
### 1. 手动实例化
```js
const splitter = new bricks.Splitter();
splitter.create();
document.body.appendChild(splitter.getElement()); // 假设 getElement() 返回 dom_element
```
### 2. 通过工厂创建(推荐)
```js
const splitter = bricks.Factory.create('Splitter');
splitter.create();
container.appendChild(splitter.dom_element);
```
---
## DOM 结构
生成的 DOM 元素:
```html
<hr>
```
> 样式可由外部 CSS 控制,例如:
>
> ```css
> hr {
> border: 1px solid #ccc;
> margin: 16px 0;
> }
> ```
---
## 注意事项
- `create()` 方法必须被显式调用以生成 DOM 元素。
- `dom_element` 属性在 `create()` 调用后才可用。
- 当前版本不支持自定义标签或复杂配置,如需扩展可继承此类或添加参数支持。
---
## 版本兼容性
- 依赖 `bricks.JsWidget` 基类和 `bricks.Factory` 系统。
- 需确保 `window.bricks` 已初始化。
---
## 总结
`bricks.Splitter` 是一个简洁、语义化的分隔线组件,适合在组件化界面中快速插入视觉分隔。其设计遵循 Bricks 框架规范,易于集成与扩展。

240
docs/cn/streaming_audio.md Normal file
View File

@ -0,0 +1,240 @@
# `bricks.StreamAudio` 技术文档
> **模块**`bricks.StreamAudio`
> **继承自**`bricks.VBox`
> **用途**实现基于麦克风音频流的实时语音识别ASR前端组件支持启动/停止录音、音频上传与实时文本反馈。
---
## 概述
`bricks.StreamAudio` 是一个用于浏览器端实时语音处理的 UI 组件。它封装了麦克风采集、语音活动检测VAD、音频编码上传以及后端流式响应接收的完整流程适用于在线语音识别ASR场景。
该组件通过按钮控制录音启停,使用 `vad.MicVAD` 进行语音端点检测,并将语音片段以 Base64 编码形式发送到指定服务端 URL同时接收并显示服务端返回的识别结果文本。
---
## 依赖说明
- `bricks.VBox`:布局容器,垂直排列子控件。
- `bricks.Button`:用于触发开始/停止操作。
- `bricks.Filler`:可伸缩填充容器,容纳内容区域。
- `bricks.Text`:显示识别出的文字内容,支持自动换行。
- `vad.MicVAD`:语音活动检测模块(假设为外部导入的 VAD 库)。
- `schedule_once`:延迟执行函数(通常为框架提供的异步调度工具)。
- `btoa`JavaScript 内置函数,用于二进制数据转 Base64 字符串。
- `TextDecoder` / `ReadableStream`:用于解析流式响应体中的 UTF-8 文本。
---
## 构造函数
```js
constructor(opts)
```
### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts` | Object | 是 | 配置选项对象 |
#### 支持的配置项:
| 属性 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `height` | String | `'100%'` | 容器高度(会被强制设置) |
| `name` | String | `'asr_text'` | 组件名称标识 |
| `url` | String | — | 流式上传的目标服务器地址(必须传入) |
> ⚠️ 注意:`opts.url` 必须在初始化时提供,否则上传无法进行。
### 初始化行为
1. 设置默认高度和名称;
2. 调用父类构造函数(创建 VBox 布局);
3. 创建内部控件:
- `this.button`:控制录音启停;
- `this.filler`:中间填充区;
- `this.text_w`:显示识别结果的文本控件;
4. 将 `text_w` 添加至 `filler` 中;
5. 将 `button``filler` 添加为子组件;
6. 绑定按钮点击事件到 `toggle_status` 方法。
---
## 核心方法
### `toggle_status()`
切换录音状态(开始 ↔ 停止)
```js
toggle_status()
```
根据当前是否正在上传 (`this.upstreaming`) 决定调用 `start()``stop()`
---
### `start()`
请求开始录音与上传流程。
```js
start()
```
#### 行为:
- 更新按钮文字为 "stop"
- 使用 `schedule_once` 延迟调用 `_start()`,避免同步阻塞 UI。
---
### `async _start()`
实际执行录音启动逻辑的异步方法。
#### 步骤:
1. 若存在全局 VAD 实例(`bricks.vad`),先停止它(防止冲突);
2. 初始化新的 `MicVAD` 实例:
- 配置 `onSpeechEnd` 回调:当检测到语音结束时,调用 `handle_audio(audio)` 发送音频;
3. 启动 VAD 监听;
4. 记录当前实例为全局唯一活跃 VAD`bricks.vad = this`
5. 初始化 `UpStreaming` 实例用于上传音频;
6. 发起连接请求(`.go()`
7. 开始接收服务端响应数据(`recieve_data()`)。
> ✅ 支持并发保护:确保同一时间只有一个录音流激活。
---
### `stop()`
请求停止录音与上传。
```js
stop()
```
#### 行为:
- 更新按钮文字为 "start"
- 延迟调用 `_stop()`
---
### `async _stop()`
实际停止逻辑。
#### 步骤:
1. 如果有活跃的上传连接(`upstreaming`),调用其 `finish()` 方法通知服务端结束;
2. 清理 `upstreaming` 引用;
3. 暂停 VAD 录音;
4. 解除全局引用(`bricks.vad = null`)。
---
### `async receive_data()`
从服务端流式响应中读取并处理识别结果。
```js
async receive_data()
```
#### 工作流程:
1. 获取响应体的 `reader`(假设 `resp` 已定义且为 `Response.body.getReader()`
2. 使用 `TextDecoder` 解码 UTF-8 数据;
3. 循环读取每一行文本(模拟逐行解析 SSE 或 NDJSON 流);
4. 尝试将每行解析为 JSON
- 成功 → 提取 `d.content` 并更新显示文本;
- 失败 → 输出错误日志;
5. 直到流结束。
> ❗⚠️ 当前代码中 `resp` 变量未定义或作用域错误,应为 `this.resp` 或来自 `this.upstreaming.go()` 的返回值。**此为潜在 bug**。
---
### `handle_audio(audio)`
处理捕获到的音频数据块(通常是 PCM 或 WAV 格式)。
```js
handle_audio(audio)
```
#### 功能:
- 打印调试信息;
- 将原始音频数据转换为 Base64 字符串;
- 调用 `this.upstreaming.send(b64audio)` 发送到服务端。
> 🔍 说明:音频格式需与服务端兼容(如小端 PCM、16kHz 单声道等)。
---
## 注册与别名
```js
bricks.Factory.register('StreamAudio', bricks.StreamAudio);
bricks.Factory.register('ASRText', bricks.StreamAudio);
```
允许通过两种标签创建该组件:
- `<widget type="StreamAudio">`
- `<widget type="ASRText">`
便于不同项目或历史兼容使用。
---
## 示例用法
```html
<widget type="ASRText" url="/api/asr/stream"></widget>
```
或通过 JS 创建:
```js
const asrWidget = new bricks.StreamAudio({
url: '/api/asr/upload',
name: 'my_asr'
});
document.body.appendChild(asrWidget.dom);
```
---
## 状态属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `upstreaming` | UpStreaming \| null | 当前上传连接实例 |
| `vad` | MicVAD \| null | 语音活动检测实例 |
| `resp_text` | String | 累积的响应文本(暂未使用) |
| `resp` | Response | 服务端响应对象(可能需要修正作用域) |
---
## 已知问题与改进建议
| 问题 | 描述 | 建议修复 |
|------|------|----------|
| `resp` 未定义 | `receive_data()` 中直接使用 `resp.body`,但无声明 | 应从 `this.upstreaming.go()` 返回值获取响应对象 |
| `reader.readline()` 不存在 | 原生 `ReadableStreamDefaultReader` 不提供 `.readline()` | 需自行实现逐行读取逻辑或引入辅助库 |
| 错误拼写 | `recieve_data` 函数名拼写错误(应为 `receive` | 重命名函数及所有调用处 |
| 异常处理不足 | 接收流过程中缺少中断和重连机制 | 添加网络错误监听与重试逻辑 |
---
## 总结
`bricks.StreamAudio` 是一个功能完整的语音输入前端组件,适合集成于语音助手、实时字幕、语音搜索等系统中。虽然目前存在少量代码瑕疵,但整体结构清晰、职责分明,易于扩展和维护。
建议结合后端 ASR 服务(如 WebSocket + Whisper 或自研引擎)共同部署使用。
---
📌 **版本**1.0
📅 **最后更新**2025-04-05

350
docs/cn/svg.md Normal file
View File

@ -0,0 +1,350 @@
# `bricks.Svg` 与相关组件技术文档
本文件为 `bricks.Svg` 及其派生类(`StatedSvg``MultipleStateIcon`)的详细技术说明文档。这些类用于在网页中动态加载、渲染和控制 SVG 图标的行为支持颜色设置、闪烁动画、URL 动态更新以及状态切换功能。
---
## 模块结构
```js
var bricks = window.bricks || {};
```
所有组件均挂载于全局命名空间 `bricks` 下,并通过 `bricks.Factory.register()` 注册为可实例化的 UI 组件。
---
## 1. `bricks.Svg`
### 简介
`bricks.Svg` 是一个继承自 `bricks.VBox` 的类,用于显示并控制单个 SVG 图标的可视化行为。它支持从远程 URL 加载 SVG 内容,动态着色、闪烁动画等特性。
### 继承关系
```js
class Svg extends bricks.VBox
```
---
### 构造函数:`constructor(opts)`
#### 参数
| 参数 | 类型 | 必需 | 默认值 | 描述 |
|------|------|------|--------|------|
| `opts.rate` | Number | 否 | `1` | 缩放比例,影响宽度和高度 |
| `opts.url` | String | 否 | - | SVG 文件的 URL 地址 |
| `opts.blink` | Boolean | 否 | `false` | 是否启用闪烁效果 |
| `opts.color` | String (CSS Color) | 否 | 由 `bricks.app.get_color()` 获取 | SVG 填充颜色 |
| `opts.blinkcolor` | String (CSS Color) | 否 | 由 `bricks.app.get_bgcolor()` 获取 | 闪烁时的颜色 |
| `opts.blinktime` | Number | 否 | `0.5` | 闪烁间隔时间(秒) |
> ⚠️ 注意:`opts.cwidth``opts.cheight` 将被自动设为 `opts.rate`,实现等比缩放。
#### 行为说明
- 调用父类 `super(opts)` 初始化容器。
- 若未指定 `color``blinkcolor`,则使用应用级默认前景/背景色。
- 若提供了 `url`,调用 `set_url(url)` 加载 SVG 内容。
- 支持动态尺寸(`dynsize: true`)。
---
### 方法列表
#### `set_url(url)`
从指定 URL 加载 SVG 内容并插入到 DOM。
##### 参数
- `url` (String | null)SVG 文件路径或网址;若为 `null` 或空,则清空内容。
##### 流程
1. 清除当前内容(若无 `url`)。
2. 使用 `fetch()` 请求 SVG 文本。
3. 验证响应是否以 `<svg ` 开头(防止 XSS
4. 存储原始 SVG 文本至 `this.svgText`
5. 调用 `set_colored_svg(color)` 渲染带颜色的 SVG。
6. 如果启用了 `blink`,启动闪烁动画。
> ✅ 安全提示:仅允许标准 `<svg ...>` 标签开头的内容,避免注入风险。
---
#### `set_color(color)`
设置 SVG 的填充颜色,并立即重新渲染。
##### 参数
- `color` (String):合法 CSS 颜色值(如 `"red"`, `"#00ff00"`, `"rgb(255,0,0)"`
##### 行为
- 更新 `this.color`
- 调用 `set_colored_svg(color)`
---
#### `set_blinkcolor(color)`
设置闪烁状态下的颜色。
##### 参数
- `color` (String):目标闪烁颜色
##### 行为
- 更新 `this.blinkcolor`
- 自动补全 `blinktime``0.5`(如果尚未设置)
---
#### `set_colored_svg(color)`
将模板中的 `{color}` 占位符替换为实际颜色值,并更新 DOM。
##### 参数
- `color` (String):要应用的颜色
##### 实现
```js
var svgText = bricks.obj_fmtstr({color: color}, this.svgText);
this.dom_element.innerHTML = svgText;
```
> 📌 依赖 `bricks.obj_fmtstr` 实现字符串模板替换(类似 `{color}``"#ff0000"`
---
#### `start_blink()`
启动周期性颜色闪烁动画。
##### 条件
- 必须设置了 `blinktime > 0`
- 必须定义了 `blinkcolor`
##### 行为
- 若当前没有运行任务(`!this.blink_task`),调用 `_blink()` 启动首次切换。
---
#### `_blink()`
私有方法:执行一次颜色切换,并安排下一次调用。
##### 逻辑
- 当前颜色是正常色 → 切换为 `blinkcolor`
- 当前颜色是 `blinkcolor` → 切换回正常 `color`
- 使用 `schedule_once(fn, delayInSeconds)` 安排下次执行(避免阻塞主线程)
> 🔁 此方法形成递归循环,直到 `end_blink()` 被调用。
---
#### `end_blink()`
停止闪烁动画。
##### 行为
- 设置 `this.blink_task = null`,中断 `_blink()` 的递归调度链。
---
#### `set_ancent_color(e)`
从父元素的计算样式中获取文本颜色,并设置为 SVG 颜色。
##### 应用场景
适用于希望图标跟随上下文主题色的情况。
##### 实现
```js
var pstyle = getComputedStyle(this.parent);
this.set_color(pstyle.color);
```
---
## 2. `bricks.StatedSvg`
### 简介
继承自 `bricks.Svg`支持多个状态state每个状态对应不同的 SVG 资源。点击可按顺序切换状态。
### 构造函数:`constructor(opts)`
#### 扩展参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.states` | Array<{state: String, url: String}> | 是 | 状态数组,每项包含状态名与对应的 SVG URL |
| `opts.state` | String | 否 | 初始状态;若不提供,默认取第一个状态 |
#### 初始化行为
- 设置初始状态 `this.curstate`
- 调用 `set_state(state)` 加载对应 SVG
- 绑定 `click` 事件处理器 `trigger`
---
### 方法列表
#### `trigger(event)`
处理点击事件,顺序切换到下一个状态(循环)。
##### 事件处理
- `stopPropagation()``preventDefault()` 阻止冒泡与默认行为
##### 切换逻辑
- 查找当前状态索引 `i`
- 下一状态索引:`k = i + 1`,若已达末尾则回到 `0`
- 调用 `set_state(states[k].state)`
- 触发 `state_changed` 事件
---
#### `set_state(state)`
切换到指定状态。
##### 参数
- `state` (String):目标状态名称
##### 行为
- 若已处于该状态,直接返回
- 遍历 `this.states` 查找匹配项
- 找到后调用 `set_url(s.url)`
- 广播 `state_changed` 事件
- 若未找到匹配项,清除 SVG 内容(`set_url(null)`
---
## 3. `bricks.MultipleStateIcon`
### 简介
另一种多状态图标实现方式,使用对象字典管理 URL 映射,适合命名清晰的状态切换(如开关、模式选择等)。
### 构造函数:`constructor(opts)`
#### 参数
| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `opts.urls` | Object<String, String> | 是 | 状态名到 SVG URL 的映射表,例如 `{on: 'on.svg', off: 'off.svg'}` |
| `opts.state` | String | 是 | 初始状态键名 |
#### 初始化流程
- 从 `urls[state]` 提取初始 URL
- 调用 `super(opts)` 创建基础 SVG
- 记录 `this.urls` 和当前 `this.state`
- 绑定 `click` 事件到 `change_state`
---
### 方法列表
#### `change_state(event)`
点击时切换到下一个状态(按 `Object.keys(urls)` 顺序循环)。
##### 流程
- 获取所有状态键名数组
- 查找当前 `state` 的索引
- 计算下一索引(越界则归零)
- 调用 `set_state(newState)`
- 触发 `state_changed` 事件
---
#### `set_state(state)`
设置当前状态并加载对应 SVG。
##### 参数
- `state` (String):目标状态键名
##### 行为
- 更新 `this.state`
- 调用 `set_url(this.urls[state])`
---
## 工厂注册
以下三类组件均已向 `bricks.Factory` 注册,可在声明式模板中使用:
```js
bricks.Factory.register('Svg', bricks.Svg);
bricks.Factory.register('StatedSvg', bricks.StatedSvg);
bricks.Factory.register('MultipleStateIcon', bricks.MultipleStateIcon);
```
这意味着可以通过如下方式创建实例(假设框架支持):
```html
<div data-widget="Svg" data-options='{"url": "icon.svg", "color": "blue"}'></div>
```
---
## 使用示例
### 示例 1基础彩色 SVG
```js
const icon = new bricks.Svg({
url: '/icons/home.svg',
color: 'green',
blink: true,
blinkcolor: 'yellow',
blinktime: 0.8
});
document.body.appendChild(icon.dom_element);
```
### 示例 2三态切换图标StatedSvg
```js
const light = new bricks.StatedSvg({
states: [
{ state: 'red', url: '/svg/red.svg' },
{ state: 'yellow', url: '/svg/yellow.svg' },
{ state: 'green', url: '/svg/green.svg' }
],
state: 'red'
});
light.on('state_changed', (state) => {
console.log('Current state:', state);
});
```
### 示例 3双态图标MultipleStateIcon
```js
const toggle = new bricks.MultipleStateIcon({
urls: {
on: '/icons/power-on.svg',
off: '/icons/power-off.svg'
},
state: 'off'
});
toggle.on('state_changed', s => console.log('Power is now:', s));
```
---
## 注意事项与最佳实践
| 项目 | 建议 |
|------|------|
| **安全性** | 不要加载不可信来源的 SVG尤其是含 `<script>` 或内联事件的文件 |
| **性能** | 多次调用 `set_color` 会触发重绘,建议批量操作 |
| **闪烁控制** | 使用 `start_blink()` / `end_blink()` 显式控制动画生命周期 |
| **异步加载** | `set_url()` 是异步操作,后续逻辑应放在监听器或 Promise 中处理 |
| **颜色同步** | 推荐使用 `set_ancent_color()` 实现主题适配 |
---
## 依赖说明
| 依赖项 | 来源 | 用途 |
|--------|------|------|
| `bricks.VBox` | 基础布局容器 | 提供 DOM 容器能力 |
| `bricks.app.get_color()` | 应用配置模块 | 获取默认前景色 |
| `bricks.app.get_bgcolor()` | 应用配置模块 | 获取默认背景色 |
| `bricks.obj_fmtstr()` | 字符串工具 | 替换 `{color}` 模板 |
| `schedule_once(fn, sec)` | 异步调度工具 | 实现非阻塞定时任务 |
---
## 版本信息
- 编写日期2025年4月5日
- 框架版本兼容性BricksJS v1.x+
- 维护者:前端组件团队
---
**文档完成**
如有扩展需求,请参考现有接口进行子类化开发。

334
docs/cn/tab.md Normal file
View File

@ -0,0 +1,334 @@
# `TabPanel` 组件技术文档
> **命名空间**: `bricks.TabPanel`
> **继承自**: `bricks.Layout`
> **注册名称**: `'TabPanel'`(可通过工厂创建)
---
## 概述
`TabPanel` 是一个可切换内容区域的布局组件,支持多标签页管理。每个标签页可以包含独立的内容组件(如其他 widget并支持动态加载、缓存和事件通知机制。
标签位置可配置为上下左右四种方向,内容区自动适配布局顺序。
---
## 构造函数
```js
new bricks.TabPanel(options)
```
### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `options` | Object | 配置选项对象,继承自 `bricks.Layout` 并扩展以下属性 |
### Options 配置项
```js
{
tab_pos: "top", // 标签栏位置: 'top', 'bottom', 'left', 'right'
tab_long: "100%", // 标签宽度或高度CSS 值字符串)
items: [ // 标签项数组
{
name: "tab1", // 标签唯一标识名(必填)
label: "Tab 1", // 显示文本
icon: "", // 图标类名(可选)
removable: false, // 是否可关闭(显示删除按钮)
refresh: false, // 是否每次点击都重新构建内容
content: { // 内容描述对象,用于构建子 Widget
widgettype: "..."// 如: "Text", "Grid" 等
// 其他 widget 特定参数
}
}
],
css: "" // 自定义 CSS 类名(附加到根元素)
}
```
> ⚠️ 注意:`content` 可以是标准 widget 描述对象,也可以直接是一个 `bricks.JsWidget` 实例。
---
## 布局结构
根据 `tab_pos` 不同,整体采用 `vbox``hbox` 布局:
| `tab_pos` | 主轴方向 | 子组件排列顺序 |
|-----------|----------|----------------|
| `"top"` / `"bottom"` | 垂直 (`vbox`) | 上/下:标签 → 内容;下/上:内容 → 标签 |
| `"left"` / `"right"` | 水平 (`hbox`) | 左/右:标签 → 内容;右/左:内容 → 标签 |
- **标签容器**`this.tab_container`VBox 容器)
- **内容容器**`this.content_container`Filler 容器,用于动态替换内容)
---
## 样式类CSS Classes
组件使用如下 CSS 类进行样式控制:
| 类名 | 作用 |
|------|------|
| `.tabpanel` | 根元素样式 |
| `.tab-button` | 单个标签按钮默认样式 |
| `.tab-button-active` | 当前激活标签样式 |
| `.tab-button-hover` | 鼠标悬停时标签样式 |
| `.tab-content` | 内容区域样式(已通过 `set_css('tabpanel-content')` 设置) |
| `.vbox`, `.hbox` | 布局方向控制 |
> 开发者可在主题 CSS 中覆盖这些类以自定义外观。
---
## 事件系统
### 1. `switch` 事件(在 TabPanel 上触发)
当标签切换导致内容变更时触发。
- **事件名**: `'switch'`
- **回调参数**: 被激活的内容 widget 实例
- **示例**:
```js
tabpanel.bind('switch', function(widget) {
console.log("当前显示内容组件:", widget);
});
```
### 2. `active` 事件(在内容 widget 上触发)
当该内容面板被切换为可见状态时,在其自身上触发 `active` 事件。
- **用途**: 用于内容初始化、数据刷新等操作。
- **示例**:
```js
contentWidget.bind('active', function() {
this.refreshData(); // 自定义刷新逻辑
});
```
### 3. Toolbar 内部事件(自动绑定)
- `'command'`: 用户点击标签时触发 → 调用 `show_tabcontent`
- `'remove'`: 用户关闭标签时触发 → 调用 `tab_removed`
- `'ready'`: Toolbar 初始化完成后 → 自动显示第一个标签
---
## 方法说明
### `constructor(options)`
初始化 TabPanel 实例,创建标签栏与内容容器,并根据配置设置布局方向。
#### 关键行为:
- 创建内部 `toolbar``content_container`
- 根据 `tab_pos` 设置主轴布局(`vbox` / `hbox`
- 调用 `createToolbar()` 构建标签工具栏
- 最后调用 `show_first_tab()` 显示首个标签页
---
### `show_first_tab()`
手动触发第一个标签页的显示。
```js
this.show_first_tab();
```
> 通常由 `'ready'` 事件自动调用。
---
### `createToolbar()`
基于 `options.items` 创建一个 `bricks.Toolbar` 作为标签栏。
#### 行为:
- 设置 `orientation`
- `top` / `bottom``horizontal`
- `left` / `right``vertical`
- 绑定事件:
- `'command'``show_tabcontent`
- `'remove'``tab_removed`
- `'ready'``show_first_tab`
- 添加至 `tab_container`
---
### `async show_tabcontent(event)`
处理标签点击事件,加载并显示对应内容。
#### 流程:
1. 若点击的是当前标签,忽略重复操作。
2. 查找匹配的 `item` 配置。
3. 尝试从缓存 `content_buffer` 获取已有 widget
- 若存在且 `refresh: false`,直接复用。
4. 否则异步构建新 widget
- 支持直接传入 `bricks.JsWidget` 实例
- 或通过 `bricks.widgetBuild()` 解析配置生成
5. 缓存 widget调用 `switch_content(w)` 切换显示
> 错误捕获:构建失败会输出 debug 日志。
---
### `switch_content(w)`
切换内容区域为指定 widget。
#### 动作:
- 清空当前内容容器:`clear_widgets()`
- 添加新 widget
- 触发 `switch` 事件(携带 widget 参数)
- 触发内容 widget 的 `active` 事件
```js
this.switch_content(widgetInstance);
```
---
### `add_tab(desc)`
动态添加一个新的标签页。
#### 参数:
- `desc`: 符合 `items` 结构的对象(必须含 `name`, `label` 等)
#### 行为:
- 调用 `toolbar.createTool(desc)` 添加按钮
- 若 `removable === true`,启用关闭功能(需手动实现 UI 支持)
> 示例:
```js
tabpanel.add_tab({
name: 'dynamic',
label: '动态标签',
content: { widgettype: 'Text', text: '这是动态添加的内容' },
removable: true
});
```
---
### `tab_removed(event)`
处理标签被移除后的逻辑。
#### 参数:
- `event.params.name`: 被删除标签的 `name`
#### 行为:
- 从 `content_buffer` 中清除缓存
- 如果当前正在显示该标签,则自动切换回第一个标签页
---
## 缓存机制
- 所有已加载的 content widget 缓存在 `this.content_buffer` 对象中(键为 `name`
- 默认不重复加载(除非设置 `refresh: true`
- 删除标签时自动清理缓存
> 提升性能,避免频繁重建复杂组件。
---
## 使用示例
### 基本用法
```js
var tabpanel = new bricks.TabPanel({
tab_pos: 'top',
items: [
{
name: 'home',
label: '首页',
content: { widgettype: 'Text', text: '欢迎来到首页' }
},
{
name: 'settings',
label: '设置',
icon: 'icon-gear',
removable: true,
content: { widgettype: 'Form', fields: [...] }
}
]
});
document.body.appendChild(tabpanel.get_dom());
```
### 监听切换事件
```js
tabpanel.bind('switch', function(activeWidget) {
console.log('切换到了:', activeWidget);
});
// 在内容组件中监听激活事件
var dashboardWidget = {
widgettype: 'Dashboard',
init: function() {
this.bind('active', function() {
this.loadData(); // 仅在显示时加载数据
});
}
};
```
---
## 注册信息
```js
bricks.Factory.register('TabPanel', bricks.TabPanel);
```
可通过工厂方式创建:
```js
bricks.widgetBuild({ widgettype: 'TabPanel', ... }, parent);
```
---
## 调试信息
- 重复点击相同标签:输出调试日志
- 内容构建失败:记录错误日志
- 未匹配的点击事件:提示“无响应”
> 使用 `bricks.debug()` 输出信息(生产环境建议关闭)
---
## 注意事项
1. **name 字段必须唯一**,否则缓存和查找将出错。
2. **content 必须合法**,确保能被 `widgetBuild` 正确解析。
3. 若需实时刷新内容,请设置 `refresh: true`
4. 移除标签不会销毁 widget但会清除缓存防止内存泄漏。
---
## 版本兼容性
- 依赖 `bricks.Layout`, `bricks.Toolbar`, `bricks.Filler`, `bricks.VBox`
- 需支持 ES6 class 语法
- 异步加载依赖 Promise 环境
---
**推荐场景**:后台管理系统、多文档界面、配置面板切换等需要标签式导航的 UI 场景。

304
docs/cn/tabular.md Normal file
View File

@ -0,0 +1,304 @@
# `bricks.Tabular` 技术文档
```markdown
# bricks.Tabular 类文档
`bricks.Tabular` 是一个基于 `bricks.DataViewer` 的表格数据展示组件,用于在 Web 界面中以表格形式渲染结构化数据。它支持行选择、复选框状态变更监听、动态内容展开(折叠面板)、字段过滤等功能,适用于需要交互式表格的前端应用场景。
---
## 继承关系
- **继承自**`bricks.DataViewer`
- **注册名称**`Tabular`(通过 `bricks.Factory.register` 注册)
---
## 构造函数
```js
constructor(opts)
```
### 参数
- `opts` (Object) - 配置选项,传递给父类 `DataViewer` 并用于初始化当前实例。
### 说明
调用父类构造函数后,绑定 `row_check_changed` 事件,触发时执行 `show_check_event_data` 方法(需确保该方法存在或已定义)。
> ⚠️ 注意:`this.show_check_event_data.bind(this)` 表明此方法应在外部实现或混入。
---
## 核心方法
### `async build_other()`
在数据构建阶段调用,用于初始化编辑字段。
#### 功能
调用 `get_edit_fields()` 方法,提取可编辑字段列表并存储到 `this.fields` 中。
---
### `async before_data_handle()`
在处理数据前异步执行的钩子函数。
#### 功能
1. 调用 `build_header_row()` 创建表头行;
2. 设置数据偏移量为 1可能用于跳过表头占位
#### 属性设置
- `this.data_offset = 1`
---
### `async build_header_row()`
创建并渲染表格的表头行。
#### 实现逻辑
1. 合并配置项:将 `cheight``row_options` 扩展为新的 `options`
2. 创建 `DataRow` 实例作为表头;
3. 渲染但不立即插入内容(`render(false)`
4. 添加 CSS 类名 `tabular-header-row`
5. 将其添加至滚动容器 `scrollpanel`
#### 示例代码
```js
var dr = new bricks.DataRow(options);
dr.set_css('tabular-header-row');
this.scrollpanel.add_widget(dr);
```
---
### `async build_record_view(record)`
根据记录数据构建单条记录的视图。
#### 参数
- `record` (Object) - 数据记录对象。
#### 返回值
- `Promise<Widget>` - 返回包装后的行控件VBox 或 DataRow
#### 分支逻辑
| 条件 | 行为 |
|------|------|
| `!this.content_view` | 直接返回 `DataRow`,绑定点击事件 |
| `this.content_view` | 使用 `VBox` 包裹主行和隐藏的内容区,支持展开/折叠 |
##### 内部结构(含 content_view 时)
- 主显示区域:`row.rec_widget`
- 折叠内容区域:`row.content_widget`(初始隐藏)
- 存储原始数据:`row.user_data = record`
##### 事件绑定
- 点击主行触发 `record_clicked(row, record, event)`
---
### `async record_clicked(row, record, event)`
处理行点击事件,实现单选高亮与内容展开控制。
#### 参数
- `row` - 当前行控件(可能是 `DataRow``VBox`
- `record` - 对应的数据记录
- `event` - 原生事件对象
#### 功能
1. 取消上一个选中行的高亮样式;
2. 若启用了 `content_view`,则关闭旧行的内容区;
3. 切换当前行为选中状态:
- 更新 `this.select_row`
- 显示高亮样式
- 如启用 `content_view`,调用 `toggle_content(true)` 展开内容
4. 触发 `row_selected` 事件,携带当前记录数据。
---
### `async toggle_content(row, flag)`
控制某一行的折叠内容区域的显示或隐藏。
#### 参数
- `row` (VBox) - 包含 `.content_widget` 的行容器
- `flag` (Boolean) - `true` 展开,`false` 收起
#### 行为
- `flag === true`:
- 显示内容区
- 清空现有子控件
- 复制 `this.content_view` 描述结构
- 应用当前行数据进行模板填充
- 异步构建新控件并插入
- `flag === false`:
- 隐藏内容区
- 清除所有子控件
> ✅ 支持动态内容渲染,常用于详情展示、表单嵌套等场景。
---
### `get_edit_fields()`
提取可用于编辑的字段列表,排除指定字段。
#### 参数来源
- `this.row_options.fields`: 字段定义数组
- `this.row_options.editexclouded`: 排除字段名数组(拼写疑似错误,应为 `excluded`
#### 流程
遍历所有字段,若字段名不在 `editexclouded` 列表中,则加入 `this.fields` 数组。
> ❗ 注意:`editexclouded` 拼写异常,建议检查是否应为 `editExcluded` 或类似命名。
---
### `async build_info(record)`
构建单条数据行的显示控件(`DataRow`)。
#### 参数
- `record` (Object | null) - 数据记录;若为空表示构建表头
#### 配置合并
使用 `bricks.extend` 合并默认高度与行选项。
#### 渲染逻辑
- 设置 CSS 类名为 `tabular-row`
- 调用 `dr.render(header)` 控制是否渲染为表头
- 绑定 `check_changed` 事件 → `record_check_changed`
> 🔤 注释掉的部分涉及工具栏事件代理,目前未启用。
---
### `record_check_changed(event)`
处理复选框状态变化事件。
#### 行为
1. 保存最后变更的行到 `this.check_changed_row`
2. 派发 `row_check_changed` 事件,携带用户数据
#### 派发事件
- 事件名:`row_check_changed`
- 参数:`event.params.user_data`
---
### `async renew_record_view(form, row)`
更新指定行的数据视图(如编辑后刷新显示)。
#### 参数
- `form` - 表单控件实例,用于获取最新值
- `row` - 要更新的行控件
#### 步骤
1. 获取表单数据:`form._getValue()`
2. 合并到原记录:`bricks.extend(row.user_data, d)`
3. 调用 `renew(record)` 更新 UI
- 若有 `content_view`:仅更新主显示部分 `rec_widget`
- 否则:直接更新整行
---
### `record_event_handle(event_name, record, row, item)`
通用事件处理器,用于转发来自 `DataRow` 工具栏的操作事件。
#### 参数
- `event_name` - 事件类型字符串
- `record` - 当前行数据
- `row` - 行控件实例
- `item` - 触发事件的具体元素(如按钮)
#### 行为
打印日志,并派发对应事件,携带 `record` 数据。
> 📝 日志输出便于调试,生产环境可考虑移除或降级。
---
### `get_hidefields()`
生成隐藏字段数组,通常用于向后端提交额外参数。
#### 返回值
- Array<{name, value, uitype}>: 每个字段设置 `uitype: 'hide'`
#### 数据源
`this.data_params` 对象提取键值对,转换为字段格式。
#### 示例输出
```js
[
{ name: "user_id", value: 123, uitype: "hide" },
{ name: "mode", value: "preview", uitype: "hide" }
]
```
---
## 事件系统
| 事件名 | 触发时机 | 携带参数 |
|-------|---------|--------|
| `row_check_changed` | 行复选框状态改变 | `record`(用户数据) |
| `row_selected` | 用户点击某行 | `record`(用户数据) |
| (转发)任意 `event_name` | 来自 `DataRow.toolbar_w` 的事件 | `record` |
---
## 样式类说明
| CSS 类名 | 用途 |
|--------|------|
| `tabular-header-row` | 表头行样式 |
| `tabular-row` | 普通行样式 |
| `tabular-row-selected` | 选中行高亮样式 |
---
## 工厂注册
```js
bricks.Factory.register('Tabular', bricks.Tabular);
```
允许通过描述符 `{type: 'Tabular', ...}` 动态创建实例。
---
## 使用示例(伪代码)
```js
var tabular = new bricks.Tabular({
cheight: 30,
row_options: {
fields: [
{ name: 'name', label: '姓名' },
{ name: 'age', label: '年龄' }
],
editexclouded: ['age'] // age 不参与编辑
},
content_view: {
type: 'Form',
items: [
{ field: 'detail', uitype: 'textarea' }
]
}
});
tabular.bind('row_selected', function(data) {
console.log('Selected:', data);
});
// 加载数据
tabular.setData(records);
```
---
## 注意事项
1. **拼写问题**`editexclouded` 应为 `editExcluded``excludedFields`,建议修正。
2. **依赖项**:依赖 `bricks.DataRow`, `bricks.VBox`, `bricks.widgetBuild`, `objcopy`, `bricks.apply_data` 等模块,请确保已加载。
3. **异步安全**:多数方法标记为 `async`,调用时需使用 `await`
4. **事件解绑**:未见销毁生命周期管理,长期运行可能导致内存泄漏。
---
```

368
docs/cn/toolbar.md Normal file
View File

@ -0,0 +1,368 @@
# `bricks.Toolbar` 技术文档
> **模块**`bricks.Toolbar`
> **继承自**`bricks.Layout`
> **用途**:创建可配置的工具栏组件,支持水平/垂直布局、动态工具项生成、点击事件分发及可移除按钮功能。
---
## 概述
`bricks.Toolbar` 是一个基于 `bricks.Layout` 的类,用于构建可视化工具栏。它支持横向或纵向排列的按钮工具项,并提供灵活的配置选项,包括图标、标签、样式、事件绑定等。工具栏中的每个工具项可以触发命令事件,并可选择性地添加“删除”图标以支持动态移除。
该组件使用异步方式创建工具项,确保渲染性能,并通过 `bricks.widgetBuild` 工厂方法统一创建子控件。
---
## 构造函数
### `constructor(options)`
初始化工具栏实例。
#### 参数
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `options` | Object | 配置对象,继承自 `bricks.Layout` 并扩展以下属性 |
#### `options` 属性
| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `orientation` | String | `'horizontal'` | 布局方向:`'horizontal'``'vertical'` |
| `target` | Any | `undefined` | 可选的目标引用,会在事件数据中传递 |
| `interval` | String 或 Number | `'10px'` | 工具项之间的间隔尺寸CSS 单位) |
| `tools` | Array&lt;Object&gt; | `[]` | 工具项描述数组,每项包含图标、名称、标签等信息 |
| `css` | String | `'toolbar'` | 自定义 CSS 前缀类名 |
#### 示例
```js
var toolbar = new bricks.Toolbar({
orientation: 'horizontal',
target: myComponent,
interval: '15px',
css: 'my-toolbar',
tools: [
{ name: 'save', label: '保存', icon: 'save-icon.svg' },
{ name: 'delete', label: '删除', icon: 'del-icon.svg', removable: true }
]
});
```
#### 实现细节
- 根据 `orientation` 创建 `HScrollPanel`(水平)或 `VScrollPanel`(垂直)作为内部容器。
- 添加对应 CSS 类:`.htoolbar``.vtoolbar`
- 启用键盘选择:`enable_key_select()`
- 使用 `schedule_once` 延迟调用 `createTools()`,避免阻塞主线程。
---
## 成员变量
| 变量 | 类型 | 描述 |
|------|------|------|
| `bar` | `bricks.HScrollPanel` / `bricks.VScrollPanel` | 内部滚动面板容器 |
| `toolList` | Array&lt;Widget&gt; | 存储所有已创建的工具按钮实例 |
| `clicked_btn` | Widget 或 null | 当前被激活(点击)的按钮 |
| `preffix_css` | String | CSS 类前缀,用于激活状态样式 |
---
## 方法说明
### `add_interval_box()`
在当前工具栏中插入一个间隔空白区域(`JsWidget`),其大小由 `options.interval` 控制,方向根据 `orientation` 决定。
#### 行为逻辑
- 若为垂直布局 → 设置高度
- 若为水平布局 → 设置宽度
#### 示例效果
```css
width: 10px; /* 水平间距 */
height: 10px; /* 垂直间距 */
```
> 调用时机:在 `createTools()` 中,两个工具之间自动插入。
---
### `async createTools()`
异步批量创建所有工具项。
#### 流程
1. 遍历 `this.opts.tools`
2. 调用 `createTool(desc)` 逐个创建
3. 在非最后一个工具后调用 `add_interval_box()`
#### 注意事项
- 使用 `await` 确保顺序执行(可用于动画或依赖加载)
- 初始延迟 10ms 执行(通过 `schedule_once(..., 0.01)`
---
### `async createTool(desc)`
创建单个工具按钮。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `desc` | Object | 工具描述对象,结构如下 |
##### `desc` 结构(工具项配置)
| 属性 | 类型 | 是否必需 | 说明 |
|------|------|----------|------|
| `name` | String | ✅ | 工具唯一标识符(用于事件名) |
| `label` | String | ❌ | 显示文本 |
| `icon` | String | ❌ | 图标 URL 或 class 名称 |
| `css` | String | ❌ | 自定义按钮 CSS 类 |
| `removable` | Boolean | ❌ | 是否显示删除图标,默认 `false` |
#### 返回值
- `{Promise<bricks.Widget>}`:成功时返回按钮控件;失败返回 `undefined`
#### 内部逻辑
1. 构造 `widgetBuild` 所需的选项
2. 调用 `bricks.widgetBuild(options)` 异步创建按钮
3. 绑定 `click` 事件到 `do_handle`
4. 将按钮加入 `toolList``bar` 容器
5. 若 `removable == true`,调用 `add_removable(w)`
6. 触发 `remove` 事件(当用户点击删除图标时)
---
### `remove_item(w, event)`
从工具栏中移除指定工具项。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `w` | Widget | 要移除的按钮控件 |
| `event` | Event | DOM 事件对象 |
#### 动作
- 从 `bar` 中移除控件
- 取消选中状态
- 从 `toolList` 数组中删除
- 解绑 `click` 事件监听器
- 分发 `remove` 事件,携带 `tool_opts`
- 阻止事件冒泡和默认行为
---
### `do_handle(tool, event)`
处理工具按钮点击事件。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `tool` | Widget | 被点击的按钮实例 |
| `event` | Event | 原始事件对象 |
#### 功能
1. 输出调试日志
2. 构建事件数据 `d`,合并 `tool.tool_opts` 和可选 `target`
3. 在 `bar` 上标记该按钮为选中状态
4. 分发两个事件:
- `'command'`:通用命令事件
- `[tool.name]`:以工具名命名的特定事件(如 `'save'`, `'delete'`
5. 更新激活按钮的 CSS 样式:
- 清除上一个激活按钮的样式
- 为当前按钮添加 `prefix-css + '-button-active'`
6. 记录当前点击按钮至 `this.clicked_btn`
#### 示例事件数据
```js
{
name: "save",
label: "保存",
icon: "save-icon.svg",
target: myComponent
}
```
---
### `add_removable(item)`
为指定工具项添加可删除的 SVG “×” 图标。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `item` | Widget | 支持移除的工具按钮 |
#### 条件判断
- 仅当 `item.tool_opts.removable === true` 时执行
#### 实现步骤
1. 加载 SVG 删除图标(路径来自 `bricks_resource('imgs/delete.svg')`
2. 创建 `bricks.Svg` 控件并插入按钮内
3. 绑定点击事件到 `remove_item`
4. 输出调试信息
#### 错误处理
- 若 `Svg` 创建失败,输出错误日志
---
### `click(name)`
模拟点击某个工具按钮(通过名称查找)。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `name` | String | 工具项的 `name` 字段 |
#### 行为
遍历 `toolList`,找到匹配 `name` 的按钮并触发其原生 DOM 的 `.click()` 方法,从而激活 `do_handle`
#### 应用场景
- 外部程序化触发某个命令
- 快捷键绑定
---
## 事件系统
`Toolbar` 支持以下事件分发:
| 事件名 | 触发条件 | 携带数据 |
|--------|----------|---------|
| `command` | 任意工具被点击 | 合并后的工具选项 + target |
| `[tool.name]` | 特定工具被点击 | 同上 |
| `remove` | 用户点击删除图标 | 被删除工具的原始 `tool_opts` |
可通过 `bind(event, handler)` 监听这些事件。
#### 示例监听
```js
toolbar.bind('save', function(data) {
console.log('保存操作触发:', data);
});
toolbar.bind('command', function(cmd) {
console.log('收到命令:', cmd.name);
});
```
---
## 样式类说明
| CSS 类 | 作用 |
|-------|------|
| `.htoolbar` | 水平工具栏根元素样式 |
| `.vtoolbar` | 垂直工具栏根元素样式 |
| `.toolbar-button-active` | 激活按钮状态(默认前缀) |
| `[custom]-button-active` | 自定义前缀激活样式(由 `css` 选项控制) |
> 推荐在主题 CSS 中定义相关样式。
---
## 注册与工厂模式
```js
bricks.Factory.register('Toolbar', bricks.Toolbar);
```
允许通过字符串标识符创建实例:
```js
bricks.create({
widgettype: 'Toolbar',
options: { ... }
});
```
---
## 使用示例
### 完整 HTML 示例
```html
<div id="toolbar-container"></div>
<script>
var toolbar = new bricks.Toolbar({
container: document.getElementById('toolbar-container'),
orientation: 'horizontal',
interval: '12px',
target: app.mainView,
css: 'app-toolbar',
tools: [
{ name: 'new', label: '新建', icon: 'icons/new.svg' },
{ name: 'open', label: '打开', icon: 'icons/open.svg' },
{ name: 'save', label: '保存', icon: 'icons/save.svg', removable: true }
]
});
// 监听事件
toolbar.bind('save', function(data) {
console.log('正在保存...', data.target);
});
// 程序触发
setTimeout(() => toolbar.click('new'), 1000);
</script>
```
---
## 调试信息
启用 `bricks.debug` 后,会输出以下信息:
- 工具构建失败
- 删除图标加载结果
- 点击事件详情
- 可移除功能启用状态
建议开发阶段开启调试模式。
---
## 总结
`bricks.Toolbar` 提供了一个现代化、可扩展的工具栏解决方案,具备以下特性:
✅ 支持横竖布局
✅ 动态工具生成
✅ 事件驱动架构
✅ 支持可删除项
✅ 样式可定制
✅ 兼容工厂创建模式
适合集成于富客户端应用、编辑器、IDE 工具条等场景。

356
docs/cn/tree.md Normal file
View File

@ -0,0 +1,356 @@
# `bricks.Tree``bricks.TreeNode` 技术文档
> **版本1.0**
> **模块Tree 组件(树形结构 UI 控件)**
---
## 概述
`bricks.Tree` 是一个基于 `bricks.js` 框架的可交互树形组件,支持异步数据加载、节点展开/折叠、选中/勾选操作、动态增删改节点等功能。它适用于展示层级数据结构(如文件系统、组织架构等),并提供完整的前端交互逻辑和后端通信能力。
该组件由两个核心类构成:
- `bricks.Tree`: 树的容器类,继承自 `bricks.VScrollPanel`,负责管理整体布局、数据获取、工具栏及事件分发。
- `bricks.TreeNode`: 单个树节点类,继承自 `bricks.VBox`,表示树中的一个节点,包含图标、文本、子节点区域等元素。
---
## 类定义
### `bricks.TreeNode`
#### 继承关系
```js
class TreeNode extends bricks.VBox
```
#### 构造函数
```js
constructor(tree, parent_node, data)
```
##### 参数说明:
| 参数 | 类型 | 描述 |
|--------------|----------|------|
| `tree` | `bricks.Tree` | 当前节点所属的树实例 |
| `parent_node`| `bricks.TreeNode``null` | 父节点引用;根节点为 `null` |
| `data` | `Object` | 节点绑定的数据对象 |
##### 初始化行为:
- 设置默认选项(宽度 100%,高度自适应)
- 初始化节点状态字段(是否为叶子节点、是否已加载子节点等)
- 设置图标 URL根据类型或默认配置
- 创建主内容行(`HBox`),绑定点击事件
- 异步创建节点内容(图标、复选框、文本或自定义视图)
- 若非叶子节点,则创建子节点容器(`VBox`),初始隐藏
---
### `bricks.Tree`
#### 继承关系
```js
class Tree extends bricks.VScrollPanel
```
#### 构造函数
```js
constructor(options)
```
##### 参数说明(`options` 对象):
| 属性名 | 类型 | 必需 | 默认值 | 描述 |
|--------|------|------|--------|------|
| `row_height` | String | 否 | `'35px'` | 每行节点的高度 |
| `idField` | String | 是 | - | 数据中标识唯一 ID 的字段名 |
| `textField` | String | 是 | - | 显示节点文本的字段名 |
| `is_leafField` | String | 否 | `'is_leaf'` | 判断是否为叶子节点的字段名 |
| `typeField` | String | 否 | - | 区分节点类型的字段名(用于图标定制) |
| `data` | Array | 否 | - | 静态数据源(本地 JSON 数组) |
| `dataurl` | String | 否 | - | 动态获取子节点数据的 API 地址 |
| `method` | String | 否 | `'GET'` | 请求数据时使用的 HTTP 方法 |
| `params` | Object | 否 | `{}` | 发送请求时携带的额外参数 |
| `node_view` | WidgetDesc \| Function | 否 | - | 自定义节点渲染模板(可通过 `widgetBuild` 解析) |
| `checkField` | String | 否 | - | 控制节点勾选状态的字段名 |
| `editable` | Object | 否 | - | 编辑功能配置(增删改 URL 和字段) |
| `node_typeicons` | Object | 否 | 内置默认图标 | 不同类型节点的图标配置 |
| `multitype_tree` | Boolean | 否 | `false` | 是否启用多类型树显示 |
##### 初始化行为:
- 创建滚动面板
- 构建标题与描述(预留接口)
- 创建工具栏(如果启用了编辑模式或自定义工具)
- 初始化容器用于放置节点
- 如果有 `dataurl`,则异步加载根节点数据;否则使用本地 `data` 构建树
---
## 主要方法
### `bricks.TreeNode` 方法
| 方法名 | 说明 |
|-------|------|
| `getValue()` | 返回当前节点及其所有子节点的完整数据结构(递归) |
| `get_id()` | 获取节点 ID通过 `idField` 字段) |
| `selected(flg)` | 设置节点选中状态样式 |
| `toggleExpandCollapse(event)` | 切换展开/收起状态(异步) |
| `expand()` | 展开节点(若未加载过子节点且存在 `dataurl`,会先请求数据) |
| `collapse()` | 收起节点 |
| `create_triple()` | 创建带状态切换功能的 SVG 图标(展开/收起) |
| `change_node_type()` | 根据是否为叶子节点设置图标行为 |
| `create_node_content(widget)` | 渲染节点内部内容(图标、复选框、文本或自定义组件) |
| `update_content()` | 更新节点显示内容(主要用于刷新文本或视图) |
| `setup_icon_urls()` | 根据节点类型配置图标路径(支持按 type 定制) |
---
### `bricks.Tree` 方法
| 方法名 | 说明 |
|-------|------|
| `getValue()` | 获取整棵树的数据结构(含所有子节点) |
| `get_id()` | 获取树根节点 ID |
| `create_toolbar()` | 创建顶部工具栏(添加、编辑、删除按钮及其他命令) |
| `toolbar_command(event)` | 工具栏命令处理器(响应用户操作) |
| `add_new_node()` | 打开模态窗添加新节点 |
| `new_node_inputed(event)` | 处理新增节点表单提交 |
| `append_new_subnode(node, data)` | 在指定节点下追加子节点 |
| `build_subnode(node, data)` | 构建并插入一个 `TreeNode` 实例 |
| `delete_node()` | 删除当前选中节点(弹出确认框) |
| `delete_node_conformed(event)` | 确认删除后的处理逻辑 |
| `delete_subnode(node, subnode)` | 从父节点中移除子节点(更新数据与 DOM |
| `update_node()` | 编辑当前选中节点(打开表单) |
| `update_node_inputed(event)` | 处理编辑表单提交 |
| `update_node_data(node, data)` | 应用新数据到节点,并刷新 UI |
| `get_children_data(node)` | 通过 AJAX 获取某节点的子节点数据 |
| `create_node_children(node, data)` | 使用给定数据批量创建子节点 |
| `node_click_handle(node, event)` | 节点点击处理(控制选中状态切换) |
| `node_selected(node, flag)` | 分发节点选中事件 |
| `node_checked(node, event)` | 处理节点勾选状态变化,维护 `checked_data` 列表 |
---
## 事件机制
### 内部触发事件(通过 `dispatch` 分发)
| 事件名 | 触发时机 | 参数说明 |
|--------|---------|----------|
| `node_selected` | 节点被选中或取消选中 | 包含节点原始数据 + `selected: true/false` |
| `check_changed` | 节点勾选状态改变 | 节点数据对象 |
| `[command]` | 工具栏执行命令(如 `add`, `delete` | 命令参数 + 元数据referer、title 等) |
> 示例监听方式:
```js
tree.bind('node_selected', function(e){
console.log('Selected node:', e.params);
});
```
---
## 图标与资源管理
### 图标策略
- 支持三种状态图标:
- `open`: 展开状态图标
- `close`: 收起状态图标
- `leaf`: 叶子节点图标
- 图标来源优先级:
1. `node_typeicons[type]` —— 按节点类型指定
2. 回退到 `default_type`
3. 使用内置默认图标集
```js
this.node_typeicons = {
folder: {
open: '/static/imgs/open-folder.svg',
close: '/static/imgs/close-folder.svg',
leaf: '/static/imgs/file.svg'
},
default_type: 'folder'
}
```
- 使用 `bricks_resource(path)` 解析静态资源路径。
---
## 编辑功能EditableTree
当配置 `editable` 选项时,启用增删改功能:
```js
editable: {
fields: [ /* 表单字段定义 */ ],
add_url: '/api/node/add',
delete_url: '/api/node/delete',
update_url: '/api/node/update'
}
```
### 功能流程
| 操作 | 流程 |
|------|------|
| 添加节点 | 弹出 `ModalForm` → 提交 → 调用 `add_url` → 成功后插入新节点 |
| 删除节点 | 确认对话框 → 调用 `delete_url` → 移除 DOM 与数据 |
| 修改节点 | 加载当前数据至表单 → 提交 → 调用 `update_url` → 局部刷新 UI |
> 注意:表单数据自动附加 `idField``parentField`(如有父节点)
---
## 数据结构要求
### 节点数据格式示例
```json
{
"id": "n1",
"name": "项目A",
"type": "project",
"is_leaf": false,
"children": [
{
"id": "n11",
"name": "子任务1",
"is_leaf": true
}
]
}
```
### 关键字段映射(可配置)
| 配置项 | 默认字段 |
|--------|----------|
| `idField` | `id` |
| `textField` | `name`(需在 `opts.textField` 中指定) |
| `is_leafField` | `is_leaf` |
| `typeField` | (可选)`type` |
---
## 使用示例
### 静态树(本地数据)
```js
var tree = new bricks.Tree({
idField: 'id',
textField: 'name',
is_leafField: 'is_leaf',
data: [
{ id: '1', name: '父节点', is_leaf: false, children: [
{ id: '2', name: '子节点', is_leaf: true }
]}
]
});
tree.open();
```
### 动态树(远程加载)
```js
var tree = new bricks.Tree({
idField: 'id',
textField: 'label',
dataurl: '/api/tree/nodes',
params: { app: 'doc' },
row_height: '40px'
});
document.body.appendChild(tree.dom_element);
```
### 可编辑树
```js
var editableTree = new bricks.Tree({
idField: 'id',
textField: 'title',
dataurl: '/api/tree',
editable: {
fields: [
{ name: 'title', label: '标题', type: 'text' },
{ name: 'type', label: '类型', type: 'select', options: [...] }
],
add_url: '/api/node/add',
delete_url: '/api/node/delete',
update_url: '/api/node/update'
}
});
```
---
## 异步加载机制
- 使用 `schedule_once(fn, delay)` 延迟执行以避免阻塞主线程
- 子节点数据仅在首次展开时请求一次(`children_loaded` 标志位控制)
- 支持无限层级嵌套
---
## 工具栏Toolbar
自动构建于树上方(如果启用了编辑或自定义工具):
- 支持图标+文字按钮
- 可扩展自定义命令
- 命令可通过以下方式注入数据:
- `selected_data: true` → 传入选中节点数据
- `checked_data: true` → 传入所有勾选节点
- 否则传入上下文数据(选中 > 勾选 > params
---
## 注册组件
```js
bricks.Factory.register('Tree', bricks.Tree);
bricks.Factory.register('EditableTree', bricks.EditableTree); // 假设存在别名
```
允许通过描述符动态创建:
```js
bricks.widgetBuild({ widgettype: 'Tree', ...options }, parent);
```
---
## 已知限制与注意事项
1. **性能优化建议**:对于超大数据集(>1000 节点),建议启用虚拟滚动或懒加载优化。
2. **复选框过滤问题**`checked_data` 过滤逻辑目前仅比较 `id`,应确保唯一性。
3. **FormData 处理**:对 `FormData` 类型做了特殊处理(`.append()`),兼容性强。
4. **图标路径错误排查**:注意 `bricks_resource()` 是否正确解析路径。
5. **循环引用风险**`TreeNode` 持有 `tree``parent_node` 引用,注意内存释放。
---
## 附录:依赖函数说明
| 函数 | 来源 | 用途 |
|------|------|------|
| `bricks.extend(a, b)` | utils | 对象浅合并 |
| `bricks.uuid()` | utils | 生成唯一 ID |
| `bricks_resource(path)` | global | 解析资源路径 |
| `schedule_once(fn, delay)` | scheduler | 延迟执行函数 |
| `formdata2object(fd)` | utils | 将 FormData 转为普通对象 |
| `new bricks.HttpJson()` | net | 封装 JSON 请求 |
---
## 版本历史
- v1.0:初版发布,支持基本树形结构、异步加载、编辑操作、事件系统
---
> 文档维护者Bricks Framework Team
> 最后更新2025年4月5日

106
docs/cn/tree_choose.md Normal file
View File

@ -0,0 +1,106 @@
# `bricks.TreeChoose` 技术文档
## 概述
`bricks.TreeChoose``bricks` 框架中的一个类,用于实现树形结构的选择组件。该组件继承自 `bricks.VBox`,提供了一个可扩展的容器结构,适用于展示和选择层级数据(如目录树、组织架构等)。
---
## 命名空间
```javascript
var bricks = window.bricks || {};
```
确保 `bricks` 命名空间在全局作用域中存在,若不存在则初始化为空对象,以避免命名冲突或引用错误。
---
## 类定义
```javascript
bricks.TreeChoose = class extends bricks.VBox {
/*
{
}
*/
constructor(opts) {
super(opts);
}
}
```
### 继承关系
- **父类**: `bricks.VBox`
- **说明**: `VBox` 通常表示一个垂直布局容器,`TreeChoose` 在其基础上扩展功能,用于构建树形选择界面。
---
## 构造函数
### `constructor(opts)`
初始化 `TreeChoose` 实例。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|--------|------|
| `opts` | Object | 配置选项对象,将传递给父类构造函数。目前未定义特定选项,继承自 `VBox` 的配置。 |
#### 实现
```js
constructor(opts) {
super(opts); // 调用父类构造函数
}
```
- 调用 `super(opts)` 以完成父类 `VBox` 的初始化。
- 当前版本未添加额外初始化逻辑,保留扩展空间。
---
## 当前功能状态
- ✅ 继承自 `VBox`,具备基本容器能力。
- 🚫 尚未实现树形渲染、节点选择、展开/收起等核心功能。
- 📌 注释块 `{}` 可能为未来配置项预留结构。
---
## 使用示例(待扩展)
```js
const treeChoose = new bricks.TreeChoose({
// 后续可支持的配置项(示例)
// data: [...], // 树形数据
// multiple: false, // 是否多选
// onSelect: function(node) { ... }
});
document.body.appendChild(treeChoose.getElement());
```
> ⚠️ 当前类为空壳结构,需进一步开发以支持实际功能。
---
## 未来开发建议
1. 添加树形数据绑定方法(如 `setData(data)`)。
2. 实现节点点击事件与选择状态管理。
3. 支持异步加载子节点(懒加载)。
4. 提供样式定制与事件回调机制。
---
## 版本信息
- 初始版本0.1.0(基础类结构)
- 所属框架:`bricks.js`
- 最后更新2025年4月5日
---
> 📝 注:本文档基于当前代码结构生成,实际功能需后续完善。

274
docs/cn/uitype.md Normal file
View File

@ -0,0 +1,274 @@
# `bricks.uiViewers` 模块技术文档
---
## 概述
`bricks.uiViewers` 是一个基于工厂模式的 UI 视图构建系统,用于根据不同的数据类型(`uitype`)动态创建对应的 UI 组件。该模块通过注册和获取视图构建器函数,实现对多种数据类型的可视化展示。
它适用于需要根据配置动态渲染字段内容的场景(如表单预览、数据展示等),支持字符串、图标、代码、密码、隐藏字段、音频等多种类型。
---
## 命名空间结构
```javascript
var bricks = window.bricks || {};
bricks.uiViewers = {};
```
- `bricks`:全局命名空间对象,防止污染全局作用域。
- `bricks.uiViewers`:存储所有已注册的视图构建器函数的对象,键为 `uitype`,值为构建函数。
---
## 核心方法
### `bricks.add_ViewBuilder(uitype, handler)`
注册一个新的视图构建器。
#### 参数
| 参数 | 类型 | 描述 |
|-----------|----------|------|
| `uitype` | `String` | 数据/字段类型的标识符(如 `'str'`, `'icon'` 等)。 |
| `handler` | `Function` | 构建 UI 组件的处理函数,接收 `opts` 配置对象并返回一个组件实例。 |
#### 示例
```js
bricks.add_ViewBuilder('str', function(opts){
return new bricks.Text({ otext: opts.value });
});
```
> 此方法将指定的 `handler` 存入 `bricks.uiViewers` 对象中,以 `uitype` 为键。
---
### `bricks.get_ViewBuilder(uitype)`
获取已注册的视图构建器函数。
#### 参数
| 参数 | 类型 | 描述 |
|-----------|----------|------|
| `uitype` | `String` | 要获取的构建器对应的类型名称。 |
#### 返回值
- `{Function|null}`:如果存在对应 `uitype` 的构建器,则返回该函数;否则返回 `undefined`
#### 示例
```js
var builder = bricks.get_ViewBuilder('str');
if (builder) {
var widget = builder({ value: 'Hello' });
}
```
---
## 内置视图构建器
以下为默认注册的几种常见类型及其行为说明。
---
### `'str'` - 字符串显示
使用 `bricks.Text` 组件显示普通文本并支持国际化i18n
#### 构建逻辑
```js
bricks.add_ViewBuilder('str', function(opts){
var options = bricks.extend({}, opts);
options.otext = opts.value;
options.i18n = true;
return new bricks.Text(options);
});
```
#### 说明
- 复制原始 `opts` 配置。
- 设置显示文本为 `opts.value`
- 启用国际化支持。
---
### `'icon'` - 图标显示
显示一个图标组件,通常基于 URL 或类名。
#### 构建逻辑
```js
bricks.add_ViewBuilder('icon', function(opts){
var options = bricks.extend({}, opts);
options.url = opts.value;
return new bricks.Icon(options);
});
```
#### 说明
- 将 `value` 字段作为图标的资源地址URL传入 `bricks.Icon`
---
### `'code'` - 代码/富文本显示
用于显示可配置字段的文本内容,优先从 `user_data` 中提取。
#### 构建逻辑
```js
bricks.add_ViewBuilder('code', function(opts){
var textField = opts.textField || 'text';
var valueField = opts.name;
var txt;
if (opts.user_data) {
txt = opts.user_data[textField] || opts.user_data[valueField] || '';
} else {
txt = opts.value || '';
}
var options = bricks.extend({}, opts);
options.otext = txt;
options.i18n = true;
return new bricks.Text(options);
});
```
#### 说明
- 支持从 `user_data` 对象中读取内容,优先级:`textField > valueField > fallback ''`
- 若无 `user_data`,则使用 `opts.value`
- 使用 `bricks.Text` 显示结果,并启用 i18n。
---
### `'password'` - 密码占位显示
安全地显示密码字段,仅展示掩码。
#### 构建逻辑
```js
bricks.add_ViewBuilder('password', function(opts){
var options = bricks.extend({}, opts);
options.otext = '******';
options.i18n = true;
return new bricks.Text(options);
});
```
#### 说明
- 实际值不暴露,统一显示为六个星号 `******`
- 仍使用 `bricks.Text` 组件进行渲染。
---
### `'hide'` - 隐藏字段
创建一个空的占位组件,不显示任何内容。
#### 构建逻辑
```js
bricks.add_ViewBuilder('hide', function(opts){
return new bricks.JsWidget({});
});
```
#### 说明
- 返回一个空的 `JsWidget` 实例,用于占位但不可见。
- 常用于逻辑控制或后台数据绑定。
---
### `'audio'` - 音频播放器
嵌入一个音频播放控件,支持自动播放开关。
#### 构建逻辑
```js
bricks.add_ViewBuilder('audio', function(opts){
var options = bricks.extend({}, opts);
var url = options.value;
return new bricks.AudioPlayer({ url: url, autoplay: false });
});
```
#### 说明
- 从 `opts.value` 提取音频文件 URL。
- 创建 `AudioPlayer` 实例,默认禁用自动播放。
---
## 扩展性与自定义
开发者可通过 `bricks.add_ViewBuilder` 方法扩展新的字段类型支持。
### 自定义示例
```js
bricks.add_ViewBuilder('image', function(opts) {
return new bricks.Image({
src: opts.value,
alt: opts.label || 'Image'
});
});
```
> 只需确保 `uitype` 不冲突即可无缝集成。
---
## 依赖说明
本模块依赖以下 `bricks` 子模块:
| 模块 | 用途 |
|------------------|------|
| `bricks.extend` | 对象浅拷贝工具函数(类似 jQuery.extend。 |
| `bricks.Text` | 文本显示组件。 |
| `bricks.Icon` | 图标显示组件。 |
| `bricks.JsWidget` | 基础 UI 容器组件。 |
| `bricks.AudioPlayer` | 音频播放组件。 |
> 确保这些组件在运行时已加载。
---
## 使用示例
```js
// 获取构建器
var builder = bricks.get_ViewBuilder('str');
// 创建组件
var textWidget = builder({ value: 'Hello World' });
// 插入 DOM
textWidget.renderTo(document.body);
```
---
## 总结
| 特性 | 说明 |
|--------------|------|
| **设计模式** | 工厂模式 + 注册中心 |
| **可扩展性** | 高,支持动态添加新类型 |
| **安全性** | 敏感信息(如密码)做了脱敏处理 |
| **灵活性** | 支持从不同数据源提取内容(如 `user_data` |
此模块是构建动态 UI 展示层的核心组件之一适合用于低代码平台、表单引擎、CMS 内容预览等场景。

428
docs/cn/uitypesdef.md Normal file
View File

@ -0,0 +1,428 @@
# Bricks UI 类型定义与构建系统技术文档
本文档描述了 `bricks.UiTypesDef` 类及其相关工厂函数和构建器,用于实现基于 `uitype` 的视图View和输入控件Input的动态创建机制。该系统支持灵活扩展不同数据类型的 UI 表现形式。
---
## 目录
- [1. 概述](#1-概述)
- [2. 核心类UiTypesDef](#2-核心类uitypesdef)
- [构造函数](#constructoropts)
- [set(uitype, viewBuilder, inputBuilder)](#setuitype-viewbuilder-inputbuilder)
- [get(uitype)](#getuitype)
- [getViewBuilder(uitype)](#getviewbuilderviewbuilder)
- [getInputBuilder(uitype)](#getinputbuilderinputbuilder)
- [setViewBuilder(uitype, Builder)](#setviewbuilderviewbuilder)
- [setInputBuilder(uitype, Builder)](#setinputbuilderinputbuilder)
- [3. 工厂函数](#3-工厂函数)
- [bricks.viewFactory(desc, rec)](#bricksviewfactorydesc-rec)
- [bricks.inputFactory(desc, rec)](#bricksinputfactorydesc-rec)
- [4. 内置构建器](#4-内置构建器)
- [通用辅助函数](#通用辅助函数)
- [buildText(text, halign)](#buildtexttext-halign)
- [字符串类型 (`str`)](#字符串类型-str)
- [strViewBuilder](#strviewbuilder)
- [strInputBuilder](#strinputbuilder)
- [密码类型 (`password`)](#密码类型-password)
- [passwordViewBuilder](#passwordviewbuilder)
- [passwordInputBuilder](#passwordinputbuilder)
- [整数类型 (`int`)](#整数类型-int)
- [intViewBuilder](#intviewbuilder)
- [浮点数类型 (`float`)](#浮点数类型-float)
- [floatViewBuilder](#floatviewbuilder)
- [编码/字典类型 (`code`)](#编码字典类型-code)
- [codeViewBuilder](#codeviewbuilder)
---
## 1. 概述
`bricks.UiTypesDef` 是一个管理 **UI 类型映射** 的核心类,它将特定的数据类型(如 `str`, `int`, `password` 等)与对应的 **视图构建器viewBuilder****输入构建器inputBuilder** 关联起来。
通过注册不同的构建器函数,可以实现对字段描述(`desc`)和记录数据(`rec`)自动生成 UI 控件的能力。
系统提供两个主要工厂函数:
- `viewFactory`: 创建只读文本显示控件
- `inputFactory`: 创建可编辑输入控件
默认情况下,所有未匹配的类型会尝试回退到 `'str'` 类型处理。
---
## 2. 核心类:`UiTypesDef`
```javascript
bricks.UiTypesDef = class {
constructor(opts)
}
```
### `constructor(opts)`
初始化 `UiTypesDef` 实例。
#### 参数
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `opts` | Object | 可选配置对象(当前未使用) |
#### 属性
- `this.uitypes`: 存储每种 `uitype` 对应的构建器对象,结构如下:
```js
{
uitype: {
viewBuilder: Function,
inputBuilder: Function
}
}
```
---
### `set(uitype, viewBuilder, inputBuilder)`
注册一个 UI 类型的视图和输入构建器。
#### 参数
| 参数名 | 类型 | 必需 | 说明 |
|--------|------|------|------|
| `uitype` | String | 是 | UI 类型名称,例如 `'str'`, `'int'` |
| `viewBuilder` | Function | 否 | 用于生成只读视图的函数 |
| `inputBuilder` | Function | 否 | 用于生成可编辑输入控件的函数 |
> 若对应 `uitype` 尚未存在,则自动创建容器对象。
---
### `get(uitype)`
获取指定 `uitype``[viewBuilder, inputBuilder]` 数组。
#### 返回值
- 成功时返回 `[viewBuilder, inputBuilder]`
- 失败时返回 `(null, null)`(注意:原代码有误,实际返回 `[null, null]` 更合理)
#### 示例
```js
const [viewFn, inputFn] = bricks.uitypesdef.get('str');
```
---
### `getViewBuilder(uitype)`
获取指定类型的视图构建器函数。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `uitype` | String | 要查询的 UI 类型 |
#### 返回值
- `Function`:视图构建器
- 若不存在则返回 `undefined`
> ⚠️ 注意:原代码中若找不到返回的是 `this.uitypes[uitype].viewBuilder`,即可能为 `undefined`,但注释写成返回 `Null`,可能是笔误。
---
### `getInputBuilder(uitype)`
获取指定类型的输入构建器函数。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `uitype` | String | 要查询的 UI 类型 |
#### 返回值
- `Function`:输入构建器
- 若不存在则返回 `Null`注意JavaScript 中应为 `null`,而非首字母大写的 `Null` —— 原代码此处可能存在错误)
---
### `setViewBuilder(uitype, Builder)`
仅设置某个 `uitype` 的视图构建器。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `uitype` | String | UI 类型 |
| `Builder` | Function | 视图构建函数 |
> 如果该 `uitype` 无条目,则先初始化空对象。
---
### `setInputBuilder(uitype, Builder)`
仅设置某个 `uitype` 的输入构建器。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `uitype` | String | UI 类型 |
| `Builder` | Function | 输入构建函数 |
> 同上,若不存在则先创建占位对象。
---
## 3. 工厂函数
### `bricks.viewFactory(desc, rec)`
根据字段描述和数据记录生成对应的只读视图组件。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `desc` | Object | 字段描述对象,必须包含 `.uitype``.name` |
| `rec` | Object | 数据记录对象,字段值从中提取 |
#### 流程
1. 提取 `desc.uitype`
2. 查找注册的 `viewBuilder`
3. 若未找到,尝试使用 `'str'` 类型作为默认 fallback
4. 若仍无 builder返回 `Null`(⚠️ 应为 `null`
5. 调用 builder 生成控件并返回
#### 返回值
- UI 控件实例(如 `Text` 组件),或 `null`
#### 示例
```js
const widget = bricks.viewFactory({ uitype: 'int', name: 'age' }, { age: 25 });
// 返回右对齐的文本 "25"
```
---
### `bricks.inputFactory(desc, rec)`
生成可编辑的输入控件。
#### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `desc` | Object | 字段描述 |
| `rec` | Object | 当前数据记录 |
#### 流程
1. 获取 `uitype`
2. 查找 `inputBuilder`,失败则 fallback 到 `'str'`
3. 若无 builder返回 `Null`(⚠️ 同样应为 `null`
4. 调用 builder 并返回结果
#### 返回值
- 输入控件实例(如 `UiStr`, `UiPassword`),或 `null`
#### 示例
```js
const input = bricks.inputFactory({ uitype: 'password', name: 'pwd' }, {});
// 返回 UiPassword 控件
```
---
## 4. 内置构建器
### 通用辅助函数
#### `buildText(text, halign)`
创建一个标准化的文本控件。
##### 参数
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `text` | String | 显示文本内容 |
| `halign` | String | 水平对齐方式,仅支持 `'left'``'right'`,其他值默认为 `'left'` |
##### 返回值
- `Text` 实例,具有以下属性:
```js
{
text: text || '',
overflow: 'hidden',
wrap: true,
halign: 'left' // 固定左对齐?注意这里硬编码了 left
}
```
> ❗ 注意:尽管参数允许传入 `halign`,但在构造 `Text` 时却固定写死为 `'left'`,这可能导致逻辑不一致!
建议修正为:
```js
halign: halign // 而非 'left'
```
---
### 字符串类型 (`str`)
#### `strViewBuilder(desc, rec)`
生成字符串类型的只读视图。
##### 实现
- 从 `rec[desc.name]` 取值
- 使用 `buildText(v, 'left')` 显示,左对齐
##### 注册
```js
bricks.uitypesdef.setViewBuilder('str', strViewBuilder);
```
#### `strInputBuilder(desc, rec)`
生成字符串输入控件。
##### 实现
- 设置 `desc.value` 为当前字段值(⚠️ 原代码中 `desc[value]` 存在语法错误!应为 `desc.value`
- 返回 `new UiStr(desc)`
##### 错误提示
```js
desc[value] = v; // ❌ 语法错误value 未定义
```
✅ 正确应为:
```js
desc.value = v;
```
##### 注册
```js
bricks.uitypesdef.setInputBuilder('str', strInputBuilder);
```
---
### 密码类型 (`password`)
#### `passwordViewBuilder(desc, rec)`
显示掩码文本 `******`,保护隐私。
##### 实现
- 忽略真实值,始终显示六个星号
- 调用 `buildText('******')`
##### 注册
```js
bricks.uitypesdef.setViewBuilder('password', passwordViewBuilder);
```
#### `passwordInputBuilder(desc, rec)`
生成密码输入框。
##### 实现
- 直接返回 `new UiPassword(desc)`
- 不依赖 `rec` 值(由组件自身管理)
##### 注册
```js
bricks.uitypesdef.setInputBuilder('password', passwordInputBuilder);
```
---
### 整数类型 (`int`)
#### `intViewBuilder(desc, rec)`
将数值转为字符串,并右对齐显示。
##### 实现
- `v = rec[desc.name] + ''`(强制转字符串)
- 调用 `buildText(v, 'right')`
##### 注册
```js
bricks.uitypesdef.setViewBuilder('int', intViewBuilder);
```
---
### 浮点数类型 (`float`)
#### `floatViewBuilder(desc, rec)`
格式化浮点数,保留小数位后显示。
##### 参数扩展
- `desc.dec_len`:指定小数位数,默认为 `2`
##### 实现
```js
v = rec[desc.name];
v = v.toFixed(desc.dec_len || 2); // 格式化
v = v + ''; // 转字符串
return buildText(v, 'right'); // 右对齐
```
##### 注册
```js
bricks.uitypesdef.setViewBuilder('float', floatViewBuilder);
```
---
### 编码/字典类型 (`code`)
适用于下拉选择、字典项等场景的只读显示。
#### `codeViewBuilder(desc, rec)`
##### 参数处理
- 克隆 `desc``opts`
- 若存在 `uiparams`,将其属性合并到 `opts`
- 支持两个字段名优先级:
1. `textFeild`(拼写疑似错误,应为 `textField`
2. `valueField`
##### 查找逻辑
```js
v = rec[desc.textFeild || 'text']
if (!v) v = rec[desc.valueField || 'value']
```
> ⚠️ 注意:`textFeild` 很可能是拼写错误,应为 `textField`
##### 显示
- 左对齐文本控件
##### 注册
```js
bricks.uitypesdef.setViewBuilder('code', codeViewBuilder);
```
---
## 已知问题与改进建议
| 问题 | 说明 | 建议修复 |
|------|------|---------|
| `desc[value] = v` | JavaScript 语法错误 | 改为 `desc.value = v` |
| `return Null` | 非法标识符(应为 `null` | 所有 `Null``null` |
| `halign:'left'` in `buildText` | 忽略参数,始终左对齐 | 改为 `halign: halign` |
| `textFeild` 拼写错误 | 可能导致字段无法正确识别 | 改为 `textField` |
| 默认 fallback 使用 `'str'` | 合理但需确保 `'str'` 已注册 | 初始化时必须保证基础类型存在 |
---
## 总结
本模块实现了基于 `uitype` 的 UI 构建策略模式,具备良好的扩展性:
- 新增类型只需调用 `setViewBuilder` / `setInputBuilder`
- 工厂函数统一接口调用
- 支持 fallback 机制增强健壮性
适合用于表单渲染、列表展示等需要动态 UI 生成的场景。
---
> ✅ **维护建议**:增加类型校验、异常捕获、单元测试以提升稳定性。

787
docs/cn/utils.md Normal file
View File

@ -0,0 +1,787 @@
# Bricks.js 技术文档
> **Bricks.js** 是一个轻量级的前端工具库,提供了一系列实用函数和类,用于处理 DOM 操作、事件监听、数据转换、网络请求、媒体流播放等常见任务。它支持响应式设计、动态组件构建,并兼容移动端与桌面端环境。
---
## 目录
- [初始化](#初始化)
- [核心配置](#核心配置)
- [时间与格式化](#时间与格式化)
- [DOM 与观察者](#dom-与观察者)
- [URL 与资源管理](#url-与资源管理)
- [数据处理与类型判断](#数据处理与类型判断)
- [颜色解析](#颜色解析)
- [异步与流式响应](#异步与流式响应)
- [Blob 与 Base64 转换](#blob-与-base64-转换)
- [表单与对象操作](#表单与对象操作)
- [调试系统](#调试系统)
- [设备检测](#设备检测)
- [UI 定位与布局](#ui-定位与布局)
- [扩展方法Array](#扩展方法array)
- [Cookie 操作](#cookie-操作)
- [高度同步](#高度同步)
- [深拷贝与对象操作](#深拷贝与对象操作)
- [媒体流播放](#媒体流播放)
- [模板渲染与组件构建](#模板渲染与组件构建)
- [可观察对象 Observable](#可观察对象-observable)
- [队列结构 Queue](#队列结构-queue)
- [DOM 创建工具](#dom-创建工具)
---
## 初始化
```js
var bricks = window.bricks || {};
```
确保 `bricks` 全局命名空间存在,避免覆盖已有变量。
---
## 核心配置
### `bricks.bug`
- **类型**: `boolean | string`
- **默认值**: `false`
- **说明**: 控制是否开启调试模式。
- `false`: 不输出任何调试信息。
- `true`: 在控制台打印调用栈和参数。
- `'server'`: 将日志发送到服务器 `/debug` 接口。
---
## 时间与格式化
### `bricks.timeDiff(startTime)`
计算当前时间与指定时间之间的差值,并格式化为 `HH:mm:ss.SSS` 字符串。
#### 参数
| 参数名 | 类型 | 说明 |
|----------|------------------|------------------------|
| startTime | `Date \| number` | 开始时间(毫秒或 Date 对象) |
#### 返回值
- **类型**: `string`
- **格式**: `"HH:mm:ss.SSS"`
#### 示例
```js
const start = new Date() - 3661000; // 1小时1分钟1秒前
console.log(bricks.timeDiff(start)); // "01:01:01.000"
```
---
## DOM 与观察者
### `bricks.resize_observer`
使用 `ResizeObserver` 监听元素尺寸变化。
#### 功能
- 自动触发绑定了 `bricks_widget` 的组件的 `element_resize` 事件。
- 传递 `contentRect` 作为事件参数。
#### 使用方式
```js
widget.dom_element.bricks_widget = widget;
bricks.resize_observer.observe(widget.dom_element);
```
---
### `bricks.dom_on_off_observer`
使用 `MutationObserver` 监听 DOM 添加/移除事件。
#### 触发事件
| 事件名 | 触发条件 |
|---------|------------------|
| `domon` | 元素被添加到 DOM |
| `domoff` | 元素从 DOM 移除(包括后代)|
#### 配置
- 监听 `document.body`
- 启用 `childList``subtree`,深度遍历所有子节点。
#### 注意事项
- 只处理元素节点Node Type === 1
- 支持嵌套组件自动解绑
---
## URL 与资源管理
### `addParamsToUrl(url, params, widget)`
向 URL 添加查询参数,支持相对路径。
#### 参数
| 参数 | 类型 | 说明 |
|--------|--------|----------------------|
| url | string | 原始 URL相对或绝对 |
| params | object | 要添加的键值对参数 |
| widget | any | (未使用,保留字段) |
#### 返回值
- **类型**: `string`
- 包含新参数的完整 URL
#### 示例
```js
addParamsToUrl('/api/data', { id: 123 });
// => "http://current-domain/api/data?id=123"
```
---
### `bricks.absurl(url, widget)`
生成绝对 URL基于 `widget.baseURI``bricks.Body.baseURI`
#### 规则
- 若 URL 已是 `http://``https://`,直接返回。
- 若以 `/` 开头,则基于根路径拼接。
- 否则基于当前脚本所在目录补全。
#### 示例
```js
bricks.absurl('style.css'); // => "https://example.com/path/to/style.css"
```
---
### `bricks.path`
- **类型**: `string`
- 当前执行脚本所在的目录路径(末尾不带 `/`
`currentScriptPath()` 函数设置。
---
### `bricks_resource(name)`
根据 `bricks.path` 生成资源路径。
#### 示例
```js
bricks_resource('img/logo.png');
// => "https://example.com/scripts/img/logo.png"
```
---
## 数据处理与类型判断
### `isString(value)`
判断是否为字符串类型。
#### 返回值
- `true` if `typeof value === 'string'` or `value instanceof String`
---
### `formdata2object(formdata)`
`FormData` 转换为普通 JavaScript 对象。
#### 示例
```js
const fd = new FormData();
fd.append('name', 'Alice');
formdata2object(fd); // { name: "Alice" }
```
---
### `inputdata2dic(data)`
提取 `FormData` 所有字段,对 `prompt` 字段进行特殊字符转义。
#### 特性
- 自动调用 `bricks.escapeSpecialChars(x)` 处理 `prompt` 字段
- 异常捕获:若失败则原样返回输入数据
---
### `bricks.map(data_source, mapping, need_others)`
按映射规则重命名对象属性。
#### 参数
| 参数 | 说明 |
|------------|----------------------------------|
| data_source | 源对象 |
| mapping | 键映射表 `{旧名: 新名}` |
| need_others | 是否保留未映射的原始字段 |
#### 示例
```js
bricks.map({ a: 1, b: 2 }, { a: 'x' }, true);
// => { x: 1, b: 2 }
```
---
### `bricks.delete_null_values(obj)`
删除对象中所有值为 `null` 的属性。
#### 示例
```js
bricks.delete_null_values({ a: null, b: 2 }); // { b: 2 }
```
---
### `bricks.is_empty(obj)`
判断对象是否为空(`null``{}`)。
#### 示例
```js
bricks.is_empty(null); // true
bricks.is_empty({}); // true
bricks.is_empty({ a: 1 }); // false
```
---
## 颜色解析
### `parseRGB(colorStr)`
解析 `rgb(r, g, b)` 格式的颜色字符串。
#### 成功返回
```js
{ r: 255, g: 100, b: 50 }
```
#### 失败返回
- `null`
---
### `parseRGBA(colorStr)`
解析 `rgba(r, g, b, a?)` 格式颜色字符串,`a` 可选,默认 `1`
#### 示例
```js
parseRGBA('rgba(255, 0, 0, 0.8)'); // { r: 255, g: 0, b: 0, a: 0.8 }
parseRGBA('rgb(255, 0, 0)'); // { r: 255, g: 0, b: 0, a: 1 }
```
---
## 异步与流式响应
### `streamResponseJson(response, onJson)`
逐行解析 NDJSON 流式响应并回调处理每个 JSON 对象。
#### 参数
| 参数 | 类型 | 说明 |
|----------|----------|--------------------------|
| response | Response | fetch 返回的响应对象 |
| onJson | Function | 每解析一行有效的 JSON 后调用该函数 |
#### 特性
- 支持 UTF-8 解码
- 缓冲未完成的行至下次读取
- 自动忽略空行和解析错误
#### 示例
```js
fetch('/stream').then(res => streamResponseJson(res, json => {
console.log('Received:', json);
}));
```
---
## Blob 与 Base64 转换
### `base64_to_url(base64, mimeType)`
将 Base64 字符串转换为 Blob URL。
#### 参数
| 参数 | 默认值 | 说明 |
|-----------|--------------|------------------|
| base64 | required | Base64 编码的数据 |
| mimeType | `"audio/wav"` | MIME 类型 |
#### 返回值
- `string`: `blob:` URL
#### 示例
```js
const audioUrl = base64_to_url(base64Data);
new Audio(audioUrl).play();
```
---
### `blobToBase64(blob)`
异步将 Blob 转换为 Data URL包含 Base64 编码)。
#### 返回值
- `Promise<string>`: 如 `"data:audio/wav;base64,..."`
#### 示例
```js
const dataUrl = await blobToBase64(blob);
```
---
## 表单与对象操作
### `bricks.formdata_copy(fd)`
克隆 `FormData` 实例。
#### 返回值
- 新的 `FormData` 实例,内容与原实例一致。
---
### `objcopy(obj)`
通过 `JSON.stringify + JSON.parse` 实现浅层深拷贝。
#### 局限性
- 不支持函数、undefined、Symbol、循环引用
---
### `objget(obj, key, defval)`
安全获取对象属性,类似 Python 的 `.get()` 方法。
#### 示例
```js
objget({ a: 1 }, 'b', 'default'); // "default"
```
---
### `bricks.obj_fmtstr(obj, fmt)`
模板字符串替换,支持 `${key}``${key:}` 语法。
#### 支持语法
| 语法 | 效果 |
|--------------|-----------------------------|
| `${name}` | 替换为 `obj.name` |
| `${name:}` | 替换为 `"name:value"` 形式 |
| `${name=}` | 同上(兼容旧写法) |
| 不存在的 key | 替换为空字符串 |
#### 示例
```js
bricks.obj_fmtstr({ name: "Tom" }, "Hello ${name}!");
// => "Hello Tom!"
```
---
## 调试系统
### `bricks.debug(...args)`
统一调试输出接口。
#### 行为逻辑
- 如果 `bricks.bug === false` → 无输出
- 如果 `bricks.bug === true` → 输出到控制台 + 调用栈
- 如果 `bricks.bug === 'server'` → 延迟 0.1s 发送到 `/debug` 接口
#### 示例
```js
bricks.bug = true;
bricks.debug('Test message', { x: 1 });
// [调用位置] Test message {x: 1}
```
---
### `bricks.serverdebug(message)`
将消息 POST 到 `/debug` 接口(需配合后端接收)。
#### 内部使用
- 通过 `bricks.HttpJson().post()` 发送
- 仅当 `bricks.bug === 'server'` 时启用
---
## 设备检测
### `bricks.is_mobile()`
判断是否为移动设备。
#### 判断依据
1. UserAgent 包含常见移动设备关键词
2. 存在触摸事件支持(`ontouchstart`, `maxTouchPoints`
3. 屏幕尺寸 ≤ 768×1024
#### 返回值
- `true` if mobile device
---
## UI 定位与布局
### `archor_at(archor)`
计算锚点坐标百分比。
#### 支持锚点
| 值 | 含义 |
|-----|------------|
| tl | 左上 |
| tc | 顶部居中 |
| tr | 右上 |
| cl | 中左 |
| cc | 居中(默认) |
| cr | 中右 |
| bl | 左下 |
| bc | 底部居中 |
| br | 右下 |
#### 返回值
```js
{ x: "50%", y: "50%", left: "50%", top: "50%" }
```
---
### `archorize(ele, archor)`
将元素定位到指定锚点,并应用 CSS transform 居中。
#### 效果
- 设置 `position: absolute`
- 设置 `top`, `left`
- 使用 `transform: translateY(-y) translateX(-x)` 精确居中
#### 示例
```js
archorize(menuEl, 'tr'); // 右上角弹出菜单
```
---
### `bricks.relocate_by_eventpos(event, widget)`
根据鼠标点击位置智能调整组件位置,避免溢出视口。
#### 策略
- X轴左侧或右侧展开避开边缘
- Y轴上方或下方展开
- 留出 `bricks.app.charsize` 边距
#### 应用场景
- 上下文菜单
- 提示框浮动定位
---
## 扩展方法Array
### `Array.prototype.insert(index, ...items)`
在指定索引插入多个元素。
#### 示例
```js
[1, 2].insert(1, 'a', 'b'); // [1, 'a', 'b', 2]
```
---
### `Array.prototype.remove(item)`
移除数组中第一个匹配项。
#### 示例
```js
[1, 2, 3].remove(2); // [1, 3]
```
---
### `removeArrayItems(array, itemsToRemove)`
返回过滤后的数组,排除指定元素。
#### 示例
```js
removeArrayItems([1,2,3], [2]); // [1,3]
```
---
## Cookie 操作
### `setCookie(name, value, days)`
设置 cookie支持过期时间。
#### 参数
| 参数 | 说明 |
|------|------------------------|
| name | 名称 |
| value| 值 |
| days | 过期天数可选null 表示会话 cookie |
---
### `getCookie(name)`
获取指定名称的 cookie 值。
#### 返回值
- 字符串或 `null`(未找到)
---
### `eraseCookie(name)`
清除指定 cookie。
---
## 高度同步
### `set_max_height(w1, w2)`
使两个组件的高度保持一致(取较大者)。
#### 用途
- 并排显示的面板保持视觉对齐
#### 调用方式
```js
w1.set_height(h); // 假设 set_height 是组件方法
```
---
## 深拷贝与对象操作
### `bricks.extend(d, s)`
递归合并源对象 `s` 到目标对象 `d`
#### 特性
- 仅复制自有属性
- 遇到同名对象时递归合并
- 基础类型直接覆盖
#### 示例
```js
bricks.extend({ a: { x: 1 } }, { a: { y: 2 }, b: 3 });
// => { a: { x: 1, y: 2 }, b: 3 }
```
---
## 媒体流播放
### `bricks.playResponseAudio(response, target)`
播放 `fetch` 返回的音频响应。
#### 参数
| 参数 | 类型 | 说明 |
|---------|---------------------|---------------------------|
| response | Response | HTTP 响应对象 |
| target | string \| JsWidget | 组件 ID 或描述对象 |
#### 流程
1. 检查状态码是否为 200
2. 转换为 Blob 并创建 Object URL
3. 设置 `widget.set_url(url)` 并调用 `.play()`
---
### `bricks.set_stream_source(target, url, params)`
流式加载音频(适用于大文件或实时流)。
#### 使用技术
- `MediaSource API`
- 分块加载 `audio/wav` 数据
#### 流程
1. 创建 `MediaSource`
2. 创建 `SourceBuffer`
3. 通过 `fetch` 流式读取并 `appendBuffer`
#### 限制
- 需浏览器支持 MSEMedia Source Extensions
---
## 模板渲染与组件构建
### `bricks.widgetBuildWithData(desc_tmpl, from_widget, data)`
使用模板和数据动态创建组件。
#### 参数
| 参数 | 说明 |
|------------|----------------------------|
| desc_tmpl | 包含 `${}` 占位符的 JSON 模板字符串 |
| from_widget| 父组件(可选) |
| data | 数据源对象 |
#### 流程
1. 使用 `bricks.obj_fmtstr` 替换模板变量
2. `JSON.parse` 得到组件描述
3. 调用 `widgetBuild` 创建组件
4. 绑定 `row_data = data`
---
## 可观察对象 Observable
### `bricks.Observable`
简单的发布-订阅模型属性包装器。
#### 构造函数
```js
new bricks.Observable(owner, name, initialValue)
```
#### 方法
| 方法 | 说明 |
|------|---------------------------------|
| `set(v)` | 设置值,若改变则触发 `owner.dispatch(name, v)` |
| `get()` | 获取当前值(注意:内部应为 `this.value` |
> ⚠️ 注意:当前代码中 `get()` 返回的是 `this.v`,应修正为 `this.value`
---
## 队列结构 Queue
### `bricks.Queue`
标准 FIFO 队列实现。
#### 方法
| 方法 | 说明 |
|--------------|------------------------------|
| `enqueue(e)` | 入队 |
| `dequeue()` | 出队(移除并返回首元素) |
| `peek()` | 查看首元素(不移除) |
| `isEmpty()` | 判断是否为空 |
| `size()` | 获取长度 |
| `clear()` | 清空 |
| `print()` | 控制台输出数组内容 |
| `done()` | 标记队列结束(自定义状态) |
| `is_done()` | 查询是否已结束 |
#### 示例
```js
const q = new bricks.Queue();
q.enqueue(1);
q.enqueue(2);
console.log(q.dequeue()); // 1
```
---
## DOM 创建工具
### `bricks.dom_create(tag, opts)`
创建带有类名和 ID 的 DOM 元素。
#### 参数
| 参数 | 说明 |
|------|------------------------|
| tag | 标签名(如 `'div'` |
| opts | 选项:`css`(字符串)、`id` |
#### 示例
```js
bricks.dom_create('div', { css: 'menu active', id: 'main-menu' });
```
> ❌ Bug: `css.split(' ')``css` 应为 `opts.css`
✅ 正确实现应为:
```js
if (opts.css) {
const arr = opts.css.split(' ');
arr.forEach(c => e.classList.add(c));
}
```
---
### `bricks.element_from_html(html)`
从 HTML 字符串创建 DOM 元素(仅根元素)。
#### 示例
```js
const el = bricks.element_from_html('<div class="box">Content</div>');
document.body.appendChild(el);
```
> ❌ 注意:`outerHTML` 不会修改自身标签,此方法可能无效。
✅ 推荐改用:
```js
const div = document.createElement('div');
div.innerHTML = html;
return div.firstElementChild;
```
---
## 工具函数
### `convert2int(s)`
从字符串中提取首个整数。
#### 示例
```js
convert2int("width: 100px"); // 100
```
---
### `schedule_once(f, t)`
延迟执行函数(单位:秒)。
#### 示例
```js
schedule_once(() => console.log('After 0.5s'), 0.5);
```
---
### `schedule_interval(f, t)`
模拟 `setInterval`,递归调用 `setTimeout`
> ⚠️ 存在 bug内部 `mf.bind(func, t)` 使用错误,会导致上下文丢失。
建议重构为更稳定的方式。
---
### `querySelectorAllShadows(selector, el)`
递归查找包括 Shadow DOM 内的所有匹配元素。
#### 用途
- 跨 Shadow Boundary 查询组件
#### 示例
```js
querySelectorAllShadows('.btn', document.body);
```
---
## 总结
**Bricks.js** 提供了一套完整的前端开发辅助工具集,涵盖:
✅ DOM 操作
✅ 事件监听Resize / Mutation
✅ 数据转换与校验
✅ 模板引擎
✅ 异步流处理
✅ 音频播放
✅ 调试与日志
✅ 移动适配
✅ 组件通信机制
适合用于构建模块化、响应式的 Web 应用或微前端组件系统。
---
> ✅ **建议改进项**
> - 修复 `obj_fmtstr``Observable.get()` 中的变量名错误
> - 优化 `schedule_interval` 的递归绑定问题
> - 替换 `element_from_html` 的实现方式
> - 文档补充各组件依赖关系图
---
📄 *文档版本v1.0*
📅 *最后更新2025年4月5日*

268
docs/cn/vadtext.md Normal file
View File

@ -0,0 +1,268 @@
# `VadText` 技术文档
```markdown
# bricks.VadText 模块技术文档
## 概述
`bricks.VadText` 是一个基于 Web Audio 和语音活动检测VAD的交互式语音识别组件用于实时采集用户语音、转换为 WAV 格式并发送至后端 ASR自动语音识别服务最终将识别结果展示在界面上。该组件封装了音频录制、编码、网络请求和 UI 控件逻辑。
该模块依赖于全局对象 `bricks`,并使用了 VADVoice Activity Detection库进行语音检测。
---
## 1. 工具函数:`audioBufferToWav`
### 功能说明
将多通道的 `Float32Array[]` 音频数据(标准 Web Audio API 的 `AudioBuffer` 数据格式)转换为标准的 WAV 容器格式的 `ArrayBuffer`
### 函数签名
```js
function audioBufferToWav(channelBuffers, sampleRate)
```
### 参数
| 参数名 | 类型 | 描述 |
|----------------|----------------------|------|
| `channelBuffers` | `Float32Array[]` | 包含每个声道音频数据的数组,例如 `[leftChannel, rightChannel]`。单声道时为 `[monoData]`。 |
| `sampleRate` | `number` | 音频采样率Hz如 16000、44100 等。 |
### 返回值
- `{ArrayBuffer}`:符合 RIFF/WAV 标准的二进制缓冲区,可用于生成 Blob 或 Base64 编码。
### 实现细节
- 输出为 **16-bit PCM**、小端字节序little-endian
- 支持任意声道数(单声道/立体声等)。
- 自动填充 WAV 头部信息44 字节标准头):
- RIFF 标识符与长度
- fmt 块PCM 格式、声道数、采样率、比特率等
- data 块:实际音频样本
#### 关键步骤
1. 计算总样本数:`channelBuffers[0].length * channelBuffers.length`
2. 创建大小合适的 `ArrayBuffer`44 字节头部 + 每样本 2 字节)
3. 写入 WAV 文件头字段(使用 `DataView` 和辅助函数 `writeString`
4. 将浮点样本(范围 [-1, 1])转换为 16 位整数 PCM
```js
s < 0 ? s * 0x8000 : s * 0x7FFF
```
> ⚠️ 注意:此函数假设所有通道具有相同长度。
---
## 2. 主类:`bricks.VadText`
继承自 `bricks.VBox`,提供垂直布局容器功能,并集成语音输入与文本输出界面。
### 构造函数
```js
new bricks.VadText(opts)
```
#### 参数 `opts`
| 属性名 | 类型 | 必需 | 默认值 | 描述 |
|--------|------|-------|--------|------|
| `name` | string | 否 | `'asr_text'` | 表单字段名称,用于 `getValue()` 输出 |
| `height` | string | 否 | `'100%'` | 组件高度(会被强制设置) |
| 其他属性 | any | 否 | - | 传递给父类 `VBox` |
#### 内部初始化内容
- **按钮 (`this.button`)**:点击切换录音状态,图标为 `speak.svg`
- **音频播放器 (`this.audio`)**:回放已录制的语音片段,样式为 `'filler'`
- **水平布局 (`hbox`)**:包含按钮和音频播放器
- **填充分隔符 (`this.filler`)**:容纳文本显示区域
- **文本控件 (`this.text_w`)**:显示 ASR 识别结果,支持换行
- **事件绑定**
- 按钮点击 → `toggle_status()`
- 触发 `'audio_ready'` 事件 → `handle_audio()`
---
## 3. 核心方法
### `toggle_status()`
切换录音启停状态:
- 若正在录音(`this.vad` 存在),则调用 `stop()`
- 否则调用 `start()`
---
### `start()`
启动语音识别流程。
#### 流程
1. 更新按钮文本为 “stop”
2. 使用 `schedule_once` 延迟执行 `_start()` 以避免同步阻塞
---
### `async _start()`
异步初始化麦克风 VAD 实例。
#### 步骤
1. 如果已有全局 `bricks.vad` 实例,先停止它(保证唯一性)
2. 创建新的 `vad.MicVAD` 实例:
- 配置 `onSpeechEnd` 回调:当检测到语音结束时触发
- 回调中派发 `'audio_ready'` 事件并处理音频
3. 启动 VAD 监听
4. 设置全局引用 `bricks.vad = this`
5. 清空历史文本 `this.text = ''`
> ✅ 提示:`MicVAD.new()` 应返回 Promise确保异步加载完成。
---
### `stop()`
停止当前录音。
#### 流程
1. 更新按钮文本为 “start”
2. 延迟调用 `_stop()`
---
### `async _stop()`
真正执行停止操作:
1. 暂停 VAD 实例
2. 清理 `this.vad``bricks.vad`
3. 如果已有识别文本,派发 `'changed'` 事件通知外部值变更
---
### `async handle_audio(audio)`
处理由 VAD 捕获的音频数据(`Float32Array` 单通道音频)。
#### 步骤
1. 调用 `audioBufferToWav([audio], 16000)` 生成 WAV 二进制
2. 使用 `arrayBufferToBase64()` 转为 Base64 字符串
3. 设置 `this.audio` 播放源为 Data URL`data:audio/wav;base64,...`
4. 发送 HTTP 请求到 ASR 接口:
- 方法POST
- 参数:
- `model`: 使用的模型名(来自 `this.model`
- `audio`: Base64 编码的音频
5. 成功响应:
- 追加 `rj.content``this.text`
- 更新文本显示
6. 失败响应:
- 弹出错误窗口(`bricks.Error`
---
### `arrayBufferToBase64(buffer)`
`ArrayBuffer` 转换为 Base64 字符串。
#### 实现原理
1. 使用 `Uint8Array` 遍历字节
2. 转换为字符串(`String.fromCharCode`
3. 调用 `btoa()` 进行 Base64 编码
> ❗ 性能提示:大音频文件下可能影响性能,建议改用 `FileReader``Buffer.from(buffer).toString('base64')`Node.js 环境)
---
### `getValue()`
获取当前表单值,供外部收集数据使用。
#### 返回格式
```js
{
[this.name]: "累积识别文本"
}
```
> ⚠️ 当前实现有缺陷:未返回对象!应修改为:
```js
getValue(){
let d = {};
d[this.name] = this.text;
return d; // 缺少 return
}
```
---
## 4. 注册组件
```js
bricks.Factory.register('VadText', bricks.VadText);
```
允许通过工厂模式动态创建该组件,例如:
```js
bricks.Factory.create('VadText', { model: 'whisper-small', url: '/asr' })
```
---
## 5. 依赖与配置要求
### 外部依赖
- `vad.MicVAD`:语音活动检测模块(需提前加载)
- `bricks.Button`, `bricks.AudioPlayer`, `bricks.HBox`, `bricks.VBox`, `bricks.Filler`, `bricks.Text`, `bricks.HttpJson`, `bricks.Error`UI 组件库
- `bricks_resource()`:资源路径解析函数(如 SVG 图标)
- `schedule_once(fn, delay)`:延迟执行工具函数
### 配置项(实例属性)
| 属性 | 类型 | 说明 |
|------|------|------|
| `url` | string | ASR 服务接口地址 |
| `model` | string | 指定使用的语音识别模型 |
> 示例初始化:
> ```js
> new bricks.VadText({
> name: 'user_speech',
> url: '/api/asr',
> model: 'deepseek-asr'
> })
> ```
---
## 6. 事件系统
| 事件名 | 触发时机 | 携带数据 |
|--------|----------|---------|
| `audio_ready` | VAD 检测到语音结束 | `Float32Array` 音频数据 |
| `changed` | 录音停止且存在识别文本 | `getValue()` 结果对象(需修复返回问题) |
---
## 7. 已知问题与改进建议
| 问题 | 描述 | 建议修复 |
|------|------|----------|
| `getValue()` 缺失返回值 | 当前方法无 `return` 语句 | 添加 `return d;` |
| Base64 编码效率低 | 大音频下循环拼接字符串性能差 | 改用 `Blob` + `FileReader` 或现代编码方式 |
| 固定采样率 16000 | 不灵活 | 可从配置或 VAD 获取真实采样率 |
| 错误处理仅弹窗 | 缺少重试机制 | 增加重试按钮或自动重连 |
| 全局状态污染 | `bricks.vad` 为共享状态 | 可考虑设计为单例管理器 |
---
## 8. 使用示例
```js
var vadWidget = new bricks.VadText({
name: 'transcript',
url: '/api/speech-to-text',
model: 'base'
});
vadWidget.bind('changed', function(data) {
console.log("识别结果:", data.transcript);
});
document.body.appendChild(vadWidget.dom);
```
---
## 9. 许可与版权
© 2025 Bricks Framework. All rights reserved.
```
---
**文档版本**1.0
📌 **最后更新**2025年4月5日

292
docs/cn/video.md Normal file
View File

@ -0,0 +1,292 @@
# Bricks Video 模块技术文档
> 基于 `video.js` 实现的视频播放组件及 IPTV 集成控件
---
## 概述
本模块提供了两个核心类:
- **`bricks.Video`**:基于 [Video.js](https://videojs.com) 的视频播放组件,封装了常见播放控制逻辑和事件处理。
- **`bricks.Iptv`**IPTV 专用容器组件,集成频道信息展示与播放状态上报功能。
该模块适用于 Web 端流媒体(如 HLS、MP4播放场景支持自动播放、全屏、错误监听以及播放成功/失败回调上报。
---
## 依赖说明
- **[Video.js](https://videojs.com)**:必须在全局环境中加载 Video.js 库。
- `window.bricks`:基础框架对象,提供布局系统 (`Layout`, `VBox`)、HTTP 工具 (`HttpJson`, `HttpText`) 和事件调度机制。
- `schedule_once(fn, delay)`:延迟执行函数(单位:秒),用于异步初始化。
---
## 1. `bricks.Video`
继承自 `bricks.Layout`,封装 Video.js 播放器实例。
### 构造函数
```js
new bricks.Video(opts)
```
#### 参数 `opts`
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `url` | String | 是 | 视频资源 URL |
| `type` | String | 否 | MIME 类型(如 `'video/mp4'`),若未提供则自动推断 |
| `autoplay` | Boolean | 否 | 是否自动播放,默认 `false` |
| `fullscreen` | Boolean | 否 | 是否进入全屏模式,默认不启用 |
#### 示例
```js
const video = new bricks.Video({
url: 'https://example.com/stream.m3u8',
autoplay: true,
fullscreen: true
});
```
---
### 样式设置
构造时自动应用以下 CSS 类和样式:
```js
this.set_csses('video-js', 'vjs-big-play-centered', 'vjs-fluid');
this.set_style('object-fit', 'contain');
this.set_style('width', '100%');
this.set_style('height', '100%');
```
> ⚠️ 注意:需确保引入 Video.js 的 CSS 文件以正确渲染 UI。
---
### 事件系统
通过 `dispatch()` 触发以下自定义事件:
| 事件名 | 触发条件 | 回调参数格式 |
|------------|--------------------|------------------------------|
| `play_ok` | 视频开始播放 | `{ src: 当前视频URL }` |
| `play_end` | 视频播放结束 | `{ src: 当前视频URL }` |
| `play_failed` | 播放出错(网络/解码等) | `{ src: 当前视频URL }` |
可通过 `bind(event, handler)` 监听这些事件。
---
### 方法列表
#### `create()`
创建原生 `<video>` 元素并赋值给 `this.dom_element`,启用播放控件。
#### `create_player()`
初始化 Video.js 播放器实例,并绑定事件监听器。
##### 播放器配置项
```js
{
controls: true,
autoplay: opts.autoplay || false,
preload: "auto",
muted: true,
crossorigin: 'anonymous',
nativeTextTracks: false
}
```
> 🔊 **静音策略**初始静音1 秒后调用 `unmuted()` 解除静音并设音量为 100%。
#### `disable_captions()`
禁用所有字幕轨道text tracks防止默认字幕弹出。
#### `set_source(url, vtype)`
动态更换视频源。
- `url`: 新视频地址
- `vtype`: MIME 类型(可选)
内部会更新 `cur_url``cur_vtype`,并调用 `player.src()` 切换源。
#### `guess_type(url)`
根据文件扩展名推测 MIME 类型:
| 扩展名 | 类型 |
|-------|------|
| `.m3u8` | `application/x-mpegURL` |
| `.mp4` | `video/mp4` |
| `.avi` | `video/avi` |
| `.webm` | `video/webm` |
> 默认返回 `application/x-mpegURL`HLS 流)
#### `play()`
手动触发播放(调用 `player.play()`)。
#### `unmuted()`
解除静音并设置音量为最大值1.0)。延迟执行以绕过浏览器自动静音限制。
---
## 2. `bricks.Iptv`
继承自 `bricks.VBox`,用于构建完整的 IPTV 播放界面。
### 构造函数
```js
new bricks.Iptv(opts)
```
#### 支持的选项字段
| 字段名 | 类型 | 必填 | 描述 |
|--------|------|------|------|
| `iptv_data_url` | String | 是 | 获取频道数据的接口地址 |
| `playok_url` | String | 否 | 播放成功后上报的 URL |
| `playfailed_url` | String | 否 | 播放失败后上报的 URL |
> 💡 所有 URL 接口均使用 GET 请求传参。
---
### 内部结构
- 上方:`bricks.Text` 显示当前频道名称
- 下方:`bricks.Video` 实例进行播放
---
### 核心方法
#### `build_subwidgets()`
异步加载 IPTV 频道数据并初始化子组件:
1. 调用 `HttpJson().httpcall(iptv_data_url)` 获取频道信息
2. 缓存 `deviceid`(通过 `bricks.deviceid('iptv')`
3. 创建 `Video``Text` 组件并添加到布局中
4. 绑定播放事件回调
##### 返回数据示例user_data
```json
{
"id": "ch_001",
"tv_name": "CCTV-1 综合频道",
"url": "https://live.example.com/cctv1.m3u8"
}
```
#### `report_play_ok()`
当收到 `play_ok` 事件时调用,向 `playok_url` 发送设备和频道 ID 上报。
##### 请求参数
```json
{
"deviceid": "设备唯一标识",
"channelid": "当前频道ID"
}
```
#### `report_play_failed()`
播放失败时调用,向 `playfailed_url` 上报异常。
参数同上。
#### `setValue(data)`
外部更新频道内容的方法。
- 更新显示标题
- 更换播放地址
```js
iptv_widget.setValue({
id: 'ch_002',
tv_name: '湖南卫视',
url: 'https://live.example.com/hunantv.m3u8'
});
```
---
## 使用示例
### 注册组件(已内置)
```js
bricks.Factory.register('Video', bricks.Video);
bricks.Factory.register('Iptv', bricks.Iptv);
```
### 实例化 IPTV 播放器
```js
const iptv = new bricks.Iptv({
iptv_data_url: '/api/iptv/channel/current',
playok_url: '/api/report/playok',
playfailed_url: '/api/report/playfail'
});
// 添加到页面
document.body.appendChild(iptv.get_dom());
```
---
## 日志输出
模块在关键节点输出日志至控制台,便于调试:
- `build_subwidgets called`
- `this.user_data = ...`
- `report playok ok / failed`
- 错误提示等
---
## 注意事项
1. **跨域问题**:建议服务器配置 CORS 头部,或使用代理避免跨域限制。
2. **自动播放限制**:现代浏览器禁止无用户交互下的有声音频自动播放,因此采用“先静音播放 → 延迟取消静音”策略。
3. **HLS 支持**`.m3u8` 依赖浏览器对 MSE 的支持或 Video.js 的 hls.js 插件。
4. **延迟初始化**:使用 `schedule_once(..., 0.1)` 避免 DOM 渲染未完成导致的问题。
---
## 版本兼容性
- 浏览器Chrome / Firefox / Safari / Edge现代版本
- Video.js 版本≥7.x 或 ≥8.x推荐最新版
- 不支持 IE11 及以下
---
## 开发维护
- 作者Bricks Framework Team
- 官网参考:[https://videojs.com](https://videojs.com)
---
✅ **建议生产环境压缩代码并开启 Source Map 以便调试。**

398
docs/cn/videoplayer.md Normal file
View File

@ -0,0 +1,398 @@
# `bricks.VideoPlayer``bricks.Iptv` 技术文档
---
## 概述
本文档介绍基于 `bricks` 框架实现的两个核心类:
- **`bricks.VideoPlayer`**一个功能完整的自定义视频播放器组件支持多种视频格式MP4、HLS `.m3u8`、DASH `.mpd`),并提供播放控制、音量调节、倍速播放、音轨切换和全屏等功能。
- **`bricks.Iptv`**:一个用于 IPTV 频道播放的高级组件,集成 `VideoPlayer`,可动态加载频道数据,并上报播放状态。
该组件依赖外部库:
- [hls.js](https://github.com/video-dev/hls.js) 用于播放 HLS 流(`.m3u8`
- [dash.js](https://github.com/Dash-Industry-Forum/dash.js) 用于播放 DASH 流(`.mpd`
### 外部依赖引入建议
```html
<!-- 在页面中引入 -->
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script src="https://cdn.dashjs.org/latest/dash.all.min.js"></script>
```
---
## 1. `bricks.VideoPlayer`
### 类定义
```js
bricks.VideoPlayer = class extends bricks.VBox
```
继承自 `bricks.VBox`,表示这是一个容器型 UI 组件,内部包含视频元素及控制栏。
---
### 构造函数:`constructor(opts)`
#### 参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `opts.url` | `String` | 是 | 视频源地址,支持 `.mp4`, `.m3u8`, `.mpd` 或普通流媒体 URL |
| `opts.autoplay` | `Boolean` | 否 | 是否自动播放,默认为 `false` |
#### 示例
```js
const player = new bricks.VideoPlayer({
url: 'https://example.com/stream.m3u8',
autoplay: true
});
```
---
### DOM 结构
创建如下 HTML 结构:
```html
<div class="video-container">
<video class="video-element"></video>
<div class="controls">
<div class="progress-container">
<input type="range" class="progress-bar" value="0" step="0.0001" />
</div>
<div class="controls-bottom">
<button class="play-pause"></button>
<div class="volume-container">
<button class="mute">🔊</button>
<input type="range" class="volume" min="0" max="1" step="0.01" value="1" />
</div>
<span class="time">00:00 / 00:00</span>
<div class="speed-container">
<select class="playback-speed">
<option value="0.5">0.5x</option>
<option value="0.75">0.75x</option>
<option value="1" selected>1x</option>
<option value="1.25">1.25x</option>
<option value="1.5">1.5x</option>
<option value="2">2x</option>
</select>
</div>
<div class="audio-tracks">
<select class="audio-track-select"></select>
</div>
<button class="fullscreen"></button>
</div>
</div>
</div>
```
---
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `this.video` | `HTMLVideoElement` | 内部 `<video>` 元素引用 |
| `this.hls` | `Hls``null` | hls.js 实例,仅当播放 `.m3u8` 时创建 |
| `this.dashPlayer` | `dashjs.MediaPlayer``null` | dash.js 播放器实例,仅当播放 `.mpd` 时创建 |
| `this.controls` | `Element` | 控制栏 DOM 引用 |
| `this.playPauseBtn` | `Element` | 播放/暂停按钮 |
| `this.muteBtn` | `Element` | 静音按钮 |
| `this.volumeInput` | `Element` | 音量滑块 |
| `this.progressBar` | `Element` | 进度条 |
| `this.timeDisplay` | `Element` | 时间显示文本 |
| `this.speedSelect` | `Element` | 倍速选择下拉框 |
| `this.audioTrackSelect` | `Element` | 音轨选择下拉框 |
| `this.fullscreenBtn` | `Element` | 全屏按钮 |
---
### 方法
#### `init()`
初始化播放器,在 DOM 加载完成后调用。负责加载视频、绑定事件、更新 UI 并根据 `autoplay` 设置开始播放。
#### `destroy()`
销毁当前播放器实例,释放资源:
- 销毁 Hls 或 Dash 播放器
- 清空 video 的 `src`
- 防止内存泄漏
#### `loadVideo(src)`
动态加载指定视频源。
##### 支持类型:
| 格式 | 判断方式 | 使用技术 |
|------|----------|---------|
| `.m3u8` 或含 `m3u8` 字符串 | `endsWith('.m3u8') || includes('m3u8')` | `hls.js` |
| `.mpd` 或含 `mpd` 字符串 | `endsWith('.mpd') || includes('mpd')` | `dash.js` |
| 其他(如 `.mp4` | 默认情况 | 原生 `<video>` |
> ⚠️ 调用前会先执行 `destroy()`,确保旧播放器被清理。
#### `onLoaded()`
视频元信息加载完成后的回调,用于刷新 UI 和音轨列表。
#### `bindEvents()`
绑定所有用户交互事件和视频事件,包括:
| 事件目标 | 事件类型 | 功能 |
|--------|--------|------|
| `.play-pause` | `click` | 切换播放/暂停 |
| `.mute` | `click` | 切换静音 |
| `.volume` | `input` | 调节音量 |
| `.progress-bar` | `input` | 拖动进度条跳转时间 |
| `.playback-speed` | `change` | 更改播放速度 |
| `.audio-track-select` | `change` | 切换音轨 |
| `.fullscreen` | `click` | 进入/退出全屏 |
| `video` | `play/pause` | 更新播放按钮图标 |
| `video` | `timeupdate` | 更新进度条和时间显示 |
| `video` | `durationchange` | 更新总时长 |
| `video` | `volumechange` | 同步静音按钮状态 |
| `video` | `loadedmetadata` | 加载音轨信息 |
| `video` | `seeking` | 更新进度条位置 |
#### `updateUI()`
统一刷新所有控件状态。
#### `updatePlayPauseUI()`
根据播放状态更新播放按钮图标:
- 暂停 → `▶`
- 播放中 → `❚❚`
#### `updateMuteUI()`
根据是否静音或音量为 0 来更新静音按钮图标:
- 静音 → `🔇`
- 非静音 → `🔊`
#### `updateProgress()`
更新进度条和时间显示:
- 显示当前时间和总时间(格式化为 `mm:ss``hh:mm:ss`
- 进度条值为 `(currentTime / duration)`
#### `updateAudioTracks()`
`video.audioTracks` 中读取可用音轨,并填充到下拉菜单中。默认选中已启用的轨道。
若无音轨,则显示“无音轨”选项且禁用。
#### `formatTime(seconds)``String`
将秒数转换为可读时间字符串。
##### 示例输出:
- `65``"1:05"`
- `3665``"1:01:05"`
---
### 控制行为特性
- **自动隐藏控制栏**点击任意位置显示控制栏4 秒后自动隐藏。
- `show_controls()`:显示控制栏
- `hide_controls()`:隐藏控制栏
- **全屏切换**:兼容 Chrome/Firefox/Safari/Edge 的全屏 API。
- 图标在全屏状态下变为返回窗口图标SVG 箭头)
- **响应式设计建议**CSS 类名可用于样式定制(见下文)
---
## 2. `bricks.Iptv`
### 类定义
```js
bricks.Iptv = class extends bricks.VBox
```
专用于播放 IPTV 频道的组件,自动获取频道信息并使用 `VideoPlayer` 播放。
---
### 构造函数:`constructor(opts)`
#### 参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `iptv_data_url` | `String` | 是 | 获取频道信息的接口地址 |
| `playok_url` | `String` | 否 | 上报“播放成功”的接口 |
| `playfailed_url` | `String` | 否 | 上报“播放失败”的接口 |
> 接口应返回 JSON 数据,至少包含字段:`url`, `tv_name`, `id`
#### 示例
```js
const iptv = new bricks.Iptv({
iptv_data_url: '/api/channel?id=123',
playok_url: '/api/report/playok',
playfailed_url: '/api/report/playfailed'
});
```
---
### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `this.user_data` | `Object` | 存储从服务器获取的频道数据 |
| `this.deviceid` | `String` | 设备唯一标识(通过 `bricks.deviceid('iptv')` 生成) |
| `this.video` | `bricks.VideoPlayer` | 内嵌的视频播放器实例 |
| `this.title_w` | `bricks.Text` | 显示频道名称的文本组件 |
---
### 方法
#### `build_subwidgets()`
异步方法,首次构建子组件:
1. 若未加载 `user_data`,则通过 HTTP 请求获取
2. 创建 `VideoPlayer` 播放器并传入 `user_data.url`
3. 添加标题和播放器到界面
4. 绑定播放成功/失败事件以进行状态上报
#### `report_play_ok()``Promise<void>`
播放开始后触发,向 `playok_url` 发送设备 ID 和频道 ID。
#### `report_play_failed()``Promise<void>`
播放出错时触发,上报失败记录。
> 注意:这两个方法仅在对应 URL 存在时才发送请求。
#### `setValue(data)`
外部更新频道的方法。可用于切换频道而不重新创建组件。
##### 参数
- `data`: 包含新频道信息的对象,结构同 `user_data`
##### 示例
```js
iptv.setValue({
id: 'cctv5',
tv_name: 'CCTV-5 体育频道',
url: 'https://live.example.com/cctv5.m3u8'
});
```
---
## 事件系统Event Binding
`bricks` 框架支持事件绑定机制:
| 自定义事件 | 触发条件 | 用途 |
|-----------|----------|------|
| `domon` | 组件挂载到 DOM 后 | 执行 `init()` 初始化 |
| `domoff` | 组件从 DOM 卸载前 | 执行 `destroy()` 回收资源 |
| `click` | 用户点击组件区域 | 显示控制栏 |
| `play_ok` | 视频开始播放(由 `VideoPlayer` 抛出) | 上报播放成功 |
| `play_failed` | 播放出错(由 `VideoPlayer` 抛出) | 上报播放失败 |
> 注:`play_ok``play_failed` 需结合业务逻辑手动抛出(当前代码中暂未体现具体抛出点,可能需进一步扩展)
---
## 注册组件Factory Registration
以下语句将组件注册至 `bricks.Factory`,便于通过配置或模板动态创建:
```js
bricks.Factory.register('Iptv', bricks.Iptv);
bricks.Factory.register('VideoPlayer', bricks.VideoPlayer);
bricks.Factory.register('Video', bricks.VideoPlayer); // 别名支持
```
这意味着你可以这样使用:
```js
bricks.Factory.create('VideoPlayer', { url: '...' });
bricks.Factory.create('Iptv', { iptv_data_url: '...' });
```
---
## CSS 类名参考(供样式开发)
| 类名 | 用途 |
|------|------|
| `.video-container` | 播放器最外层容器 |
| `.video-element` | `<video>` 元素 |
| `.controls` | 控制栏整体 |
| `.progress-container`, `.progress-bar` | 进度条容器与滑块 |
| `.controls-bottom` | 底部按钮行 |
| `.play-pause`, `.mute`, `.fullscreen` | 各功能按钮 |
| `.volume`, `.playback-speed`, `.audio-track-select` | 输入控件 |
| `.time` | 时间显示区域 |
> 建议添加 CSS 以美化外观例如半透明背景、hover 效果等。
---
## 使用示例
### 示例 1直接播放视频
```js
const player = new bricks.VideoPlayer({
url: 'https://example.com/video.mp4',
autoplay: true
});
document.body.appendChild(player.dom_element);
```
### 示例 2播放 HLS 直播流
```js
const player = new bricks.VideoPlayer({
url: 'https://live.example.com/stream.m3u8',
autoplay: true
});
container.add_widget(player);
```
### 示例 3使用 IPTV 组件
```js
const iptv = new bricks.Iptv({
iptv_data_url: '/api/get_channel?id=101',
playok_url: '/log/play_start',
playfailed_url: '/log/play_error'
});
document.getElementById('app').appendChild(iptv.dom_element);
```
---
## 已知限制与注意事项
1. **浏览器兼容性**
- HLS 播放依赖 `hls.js`,不支持 iOS Safari 原生 HLS但通常仍可回退
- DASH 需要 MSEMedia Source Extensions支持
2. **跨域问题**
- 视频源必须允许 CORS否则无法加载元数据或音轨
3. **移动端体验**
- 自动隐藏控制栏的时间40帧 ≈ 0.67秒)可能偏短,建议调整
- 移动端建议禁用鼠标 hover 行为,改为点击切换显示
4. **错误处理待完善**
- 当前未监听 `error` 事件来触发 `play_failed`,建议补充:
```js
this.video.addEventListener('error', () => {
this.fire('play_failed'); // 触发事件
});
```
- 可增加重试机制或备用源切换逻辑
5. **性能优化**
- 多次切换视频源时频繁重建播放器,可考虑缓存策略优化
---
## 总结
`bricks.VideoPlayer` 是一个轻量级、多功能、跨平台的视频播放组件,适用于点播与直播场景;
`bricks.Iptv` 在其基础上封装了频道管理和状态上报能力,适合构建智能电视或 Web 端 IPTV 应用。
两者结合可快速搭建现代化流媒体播放界面,具备良好的扩展性和维护性。
---
> ✅ 文档版本v1.0
> 📅 最后更新2025-04-05

176
docs/cn/views.md Normal file
View File

@ -0,0 +1,176 @@
以下是为提供的代码编写的 **Markdown 格式技术文档**,清晰地描述了代码的功能、结构、类与函数说明,并对 `uitype` 类型进行了归纳。
---
# 技术文档UI 类型视图系统View System
## 概述
本模块实现了一个基于 JavaScript 的轻量级 UI 视图渲染系统,用于根据不同的字段类型(`uitype`)生成对应的显示组件。系统支持多种数据类型的可视化展示,如字符串、密码、数字、邮箱等,并通过注册机制将特定 `uitype` 映射到对应的视图类。
该系统适用于表单字段、数据展示界面或低代码平台中的动态 UI 渲染场景。
---
## 支持的 uitype 类型
以下为系统预定义并可扩展的字段类型(`uitype`),用于控制如何渲染数据:
| uitype | 说明 |
|--------------|------|
| `str` | 普通字符串,直接显示文本内容 |
| `password` | 密码字段,显示为 `****` 隐藏实际值 |
| `int` | 整数类型(暂未实现具体视图) |
| `float` | 浮点数类型(暂未实现具体视图) |
| `tel` | 电话号码(暂未实现具体视图) |
| `email` | 邮箱地址(暂未实现具体视图) |
| `file` | 文件上传/链接(暂未实现具体视图) |
| `'check` | 布尔检查框(可能为拼写错误,应为 `check``checkbox`|
| `checkbox` | 复选框(布尔值)|
| `date` | 日期类型(暂未实现具体视图) |
| `text` | 多行文本(暂未实现具体视图) |
| `code` | 代码块显示(暂未实现具体视图) |
| `video` | 视频媒体(暂未实现具体视图) |
| `audio` | 音频媒体(暂未实现具体视图) |
> 注:当前仅实现了 `str``password` 两种类型的视图组件。
---
## 核心 API 说明
### `ViewStr(desc) → Widget`
创建一个用于显示字符串字段的文本组件。
#### 参数
- `desc` (Object): 字段描述对象,包含以下属性:
- `value` (string): 要显示的字符串值。
- `row_data` (optional, any): 关联的原始行数据(可用于后续交互)。
#### 返回值
- `Widget`: 一个 `Text` 类型的 UI 组件,左对齐显示文本。
#### 示例
```js
var widget = ViewStr({ value: "Hello World", row_data: { id: 1 } });
// 渲染出显示 "Hello World" 的文本组件
```
#### 内部逻辑
- 使用 `Text({ text: ..., halign: 'left' })` 创建文本控件。
- 若 `desc.row_data` 存在,则将其附加到返回组件的 `data` 属性上,便于后续操作。
> ⚠️ 注意:当前版本中 `w.desc_object = desc;``ViewStr` 中设置的是 `desc_object`,而在 `ViewPassword` 中是 `data`,建议统一命名以避免混淆。
---
### `ViewPassword(desc) → Widget`
创建一个用于显示密码字段的安全文本组件,隐藏真实值。
#### 参数
- `desc` (Object): 字段描述对象,包含以下属性:
- `value` (string): 实际密码值(不会显示)。
- `row_data` (optional, any): 关联的原始行数据(可用于点击后查看明文或其他行为)。
#### 返回值
- `Widget`: 一个显示 `"****"``Text` 组件,左对齐。
#### 示例
```js
var widget = ViewPassword({ value: "secret123", row_data: userRecord });
// 显示为 "****",保护隐私
```
#### 内部逻辑
- 文本固定显示为 `"****"`,防止泄露密码。
- 若存在 `row_data`,则挂载至组件 `data` 属性,可用于事件回调。
---
### `uitypesdef.setViewKlass(uitype, viewClass)`
注册指定 `uitype` 对应的视图构造函数。
#### 示例用法
```js
uitypesdef.setViewKlass('str', ViewStr);
```
`str` 类型字段的视图类设置为 `ViewStr`
> 此方法属于外部依赖 `uitypesdef` 对象,需确保其已全局可用。
---
## 类定义
### `class ViewType extends JsWidget`
所有视图类的基类,继承自 `JsWidget`
#### 构造函数
```js
constructor(opts)
```
调用父类构造函数,初始化基础组件选项。
> 当前无额外逻辑,作为扩展基类使用。
---
### `class ViewStr extends ViewType`
`str` 类型字段的具体视图类(目前为空实现,功能由工厂函数完成)。
> ⚠️ 当前存在不一致:`ViewStr` 同时以函数和类的形式出现,可能导致命名冲突或维护困难。
#### 建议改进
- 移除 `ViewStr` 函数,完全使用类构造;
- 或重命名函数为 `createViewStr`,保持类名唯一性。
---
## 设计问题与改进建议
| 问题 | 描述 | 建议 |
|------|------|-------|
| 1. 工厂函数与类同名 | `ViewStr` 既是函数又是类,易造成混淆 | 区分命名,例如函数改为 `createViewStr()` |
| 2. 属性命名不一致 | `ViewStr` 设置 `desc_object``ViewPassword` 设置 `data` | 统一为 `data``sourceDesc` |
| 3. `'check` 类型异常 | 类型名以单引号开头,可能是笔误 | 应修正为 `check``switch` |
| 4. 缺乏事件处理 | 所有视图均为静态显示 | 可添加点击显示密码等功能 |
| 5. 未实现多数 uitype | 列出 13 种类型但仅实现 2 种 | 应逐步补全或标记为“待实现” |
---
## 使用示例
```js
// 显示用户名
const nameField = { value: "Alice", row_data: userData };
const nameLabel = ViewStr(nameField);
// 显示密码(隐藏)
const pwdField = { value: "p@ssw0rd", row_data: userData };
const pwdLabel = ViewPassword(pwdField);
// 注册到系统
uitypesdef.setViewKlass('str', ViewStr);
uitypesdef.setViewKlass('password', ViewPassword);
```
---
## 总结
本模块构建了一个灵活的 UI 视图映射框架,允许根据不同字段类型动态渲染合适的显示组件。尽管目前仅实现了部分功能,但其设计具备良好的扩展性,适合进一步发展为完整的低代码前端渲染引擎。
---
> ✅ **状态**:原型阶段,核心机制可行,需优化命名一致性与完整性。
> 📦 **适用场景**:表单预览、后台管理系统、配置面板等需要动态 UI 渲染的场合。
---
*文档版本1.0*
*最后更新2025-04-05*

90
docs/cn/vision.md Normal file
View File

@ -0,0 +1,90 @@
# Bricks 框架技术文档
## 概述
`bricks.Vision``bricks` 前端框架中的一个类,用于构建可视化相关的用户界面组件。该类继承自 `bricks.Layout`,扩展了布局功能以支持视觉化展示需求。
---
## 全局命名空间初始化
```javascript
bricks = window.bricks || {}
```
### 说明
- **目的**:确保 `bricks` 全局命名空间的存在。
- **行为**
- 如果 `window.bricks` 已存在,则使用现有对象。
- 否则,创建一个新的空对象 `{}` 并赋值给 `window.bricks`
- **作用**:防止命名冲突,并为后续类和模块的注册提供统一的全局入口。
---
## bricks.Vision 类定义
```javascript
bricks.Vision = class extends bricks.Layout {
}
```
### 继承关系
- **父类**`bricks.Layout`
- **语义**`Vision` 类在标准布局能力的基础上进行扩展,适用于需要图形化、数据可视化或多媒体展示的场景。
### 当前状态
- 该类目前为空实现(占位类)。
- 可作为未来添加以下功能的基础:
- 图表容器管理
- 可视化组件渲染
- 动画与交互逻辑
- 响应式视觉布局
---
## 使用示例(待扩展)
```javascript
// 示例:实例化 Vision 组件
const visionPanel = new bricks.Vision({
// 配置项(未来可扩展)
});
```
> ⚠️ 注意:当前版本尚未实现具体方法或属性,仅提供结构骨架。
---
## 设计意图
| 特性 | 描述 |
|--------------|------|
| 模块化 | 通过 `bricks` 命名空间组织代码,避免全局污染 |
| 可扩展性 | 使用 ES6 Class 语法便于继承与多态 |
| 职责分离 | `Vision` 专注视觉呈现,`Layout` 处理基础布局 |
---
## 后续开发建议
1. 添加构造函数参数解析(如 `options` 对象)
2. 实现生命周期钩子(如 `render`, `update`, `destroy`
3. 集成图表库(如 D3.js、ECharts 等)支持
4. 支持响应式设计与主题定制
---
## 版本信息
- **Bricks Framework v0.1.0**
- **模块**Vision可视化模块
- **状态**:草案 / 初始化阶段
---
> 📝 文档更新日期2025-04-05
> 更多信息请参考 [Bricks 官方文档](#)(链接待定)

254
docs/cn/websocket.md Normal file
View File

@ -0,0 +1,254 @@
# `bricks.WebSocket` 技术文档
> 基于 WebSocket 的通信组件,继承自 `bricks.VBox`用于在前端与后端之间建立双向实时通信通道。支持文本、Base64 编码的音视频数据等消息类型的发送与接收。
---
## 概述
`bricks.WebSocket` 是一个封装了原生 WebSocket 功能的类,提供了更便捷的事件处理机制和数据封装方法。它允许开发者通过简单的配置连接到指定的 WebSocket 服务并支持会话认证session以及多种类型的消息收发。
该类通过 `bricks.Factory.register` 注册为可复用组件,适用于 bricks UI 框架体系。
---
## 继承关系
- **父类**: `bricks.VBox`
- **子类**: `bricks.WebSocket`
---
## 构造函数
```js
new bricks.WebSocket(options)
```
### 参数
| 参数 | 类型 | 必填 | 描述 |
|------|------|------|------|
| `options.ws_url` | `String` | ✅ | WebSocket 服务器地址(如:`ws://localhost:8080/ws` |
| `options.with_session` | `Boolean` | ❌ | 是否携带当前应用会话信息进行连接,默认为 `false` |
### 示例
```js
var ws = new bricks.WebSocket({
ws_url: 'ws://example.com/ws',
with_session: true
});
```
> 当 `with_session: true` 时,系统将调用 `bricks.app.get_session()` 获取会话标识并作为协议参数传递给 WebSocket 构造函数(注意:代码中存在拼写错误 `sessopn` 应为 `session`)。
---
## 事件回调
`bricks.WebSocket` 支持以下事件监听,可通过 `on(event, handler)` 方法绑定:
| 事件名 | 触发时机 | 回调参数 |
|--------|----------|-----------|
| `onopen` | WebSocket 连接成功打开时 | 无 |
| `onclose` | WebSocket 连接关闭时 | 无 |
| `onerror` | WebSocket 发生错误时 | 错误对象 |
| `ontext` | 接收到类型为 `text` 的消息时 | 文本数据 |
| `onbase64audio` | 接收到 Base64 编码的音频数据时 | Base64 字符串 |
| `onbase64video` | 接收到 Base64 编码的视频数据时 | Base64 字符串 |
| `on{type}` | 接收到任意自定义类型 `{type}` 的消息时 | 数据体 |
> 所有事件均通过 `this.dispatch(eventname, data)` 触发,可使用标准事件监听机制订阅。
---
## 方法说明
### `on_open(e)`
WebSocket 连接打开时的内部处理函数。
- 触发 `onopen` 事件
- 控制台输出 `"open"`
### `on_close(e)`
WebSocket 连接关闭时的内部处理函数。
- 触发 `onclose` 事件
- 控制台输出 `"close"`
### `on_error(e)`
WebSocket 发生错误时的内部处理函数。
- 触发 `onerror` 事件
- **⚠️ Bug 提示**:日志中 `console.log(error)` 应为 `console.log(e)`,否则会抛出未定义变量异常。
### `on_message(e)`
处理接收到的消息。
#### 流程:
1. 解析 `e.data` 为 JSON 对象,格式应为:
```json
{
"type": "text",
"data": "Hello World"
}
```
2. 根据 `type` 动态生成事件名(如 `type: "text"` → 事件 `ontext`
3. 使用 `this.dispatch(eventname, d.data)` 派发事件
> 支持扩展任意消息类型,只需确保服务端发送的数据结构符合规范。
---
### `send_text(text)`
发送纯文本消息。
#### 参数
- `text` (`String`):要发送的文本内容
#### 示例
```js
ws.send_text("Hello from client");
```
#### 发送格式
```json
{ "type": "text", "data": "Hello from client" }
```
---
### `send_base64_video(b64video)`
发送 Base64 编码的视频数据。
#### 参数
- `b64video` (`String`)Base64 编码的视频字符串(不含前缀如 `data:video/mp4;base64,`
#### 示例
```js
ws.send_base64_video(videoBase64Data);
```
#### 发送格式
```json
{ "type": "base64video", "data": "...base64..." }
```
---
### `send_base64_audio(b64audio)`
发送 Base64 编码的音频数据。
#### 参数
- `b64audio` (`String`)Base64 编码的音频字符串
#### 示例
```js
ws.send_base64_audio(audioBase64Data);
```
#### 发送格式
```json
{ "type": "base64audio", "data": "...base64..." }
```
---
### `send_typedata(type, data)`
发送指定类型的消息(通用接口)。
#### 参数
- `type` (`String`):消息类型(如 `"custom"`
- `data` (`Any`):任意可序列化数据
#### 返回值
- `undefined`(但调用了 `send(d)`
> **⚠️ Bug 提示**:方法体内 `return send(d);` 应为 `return this.send(d);`,否则 `send` 函数无法正确调用。
---
### `send(d)`
底层消息发送方法,负责序列化并发送数据。
#### 参数
- `d` (`Object`):消息对象,格式如下:
```js
{
type: String,
data: Any
}
```
#### 行为
1. 使用 `JSON.stringify(d)` 序列化对象
2. 调用 `this.ws.send(s)` 发送字符串
---
## 已知问题 / 注意事项
| 问题 | 说明 | 建议修复 |
|------|------|---------|
| `sessopn` 拼写错误 | `constructor``sessopn` 应为 `session` | 更正为 `this.ws = new WebSocket(this.ws_url, session);` |
| `console.log(error)` 错误引用 | `on_error` 方法中变量名错误 | 改为 `console.log(e);` |
| `send_typedata` 调用错误 | `send(d)` 应为 `this.send(d)` | 修正为 `return this.send(d);` |
| 缺少连接状态管理 | 未暴露 `readyState` 或重连机制 | 可增加 `isConnected()` 方法 |
---
## 使用示例
```js
// 创建 WebSocket 实例
var ws = new bricks.WebSocket({
ws_url: 'ws://localhost:3000/ws',
with_session: true
});
// 绑定事件
ws.on('onopen', function() {
console.log('Connected!');
ws.send_text('Client ready');
});
ws.on('ontext', function(data) {
console.log('Received text:', data);
});
ws.on('onbase64video', function(videoData) {
document.getElementById('video').src = 'data:video/webm;base64,' + videoData;
});
```
---
## 注册为工厂组件
```js
bricks.Factory.register('WebSocket', bricks.WebSocket);
```
允许通过工厂模式创建实例:
```js
var instance = bricks.Factory.create('WebSocket', { ws_url: '...' });
```
---
## 总结
`bricks.WebSocket` 是一个轻量级、易于集成的 WebSocket 封装组件,适合用于实时通信场景(如聊天、音视频流推送、远程控制等)。尽管存在少量代码缺陷,但整体设计清晰,具备良好的扩展性和事件驱动特性。
建议在实际使用前修复上述指出的问题以保证稳定性。

240
docs/cn/webspeech.md Normal file
View File

@ -0,0 +1,240 @@
# WebTTS 与 WebASR 模块技术文档
---
## 简介
`bricks.WebTTS``bricks.WebASR` 是基于浏览器原生 API 实现的语音合成Text-to-Speech, TTS和语音识别Automatic Speech Recognition, ASR组件。它们扩展自 `bricks.VBox`,可用于在 Web 应用中集成语音输入与输出功能。
这些模块依赖于现代浏览器提供的 `SpeechSynthesis``SpeechRecognition` API适用于支持这些特性的主流浏览器。
---
## 1. `bricks.WebTTS` - 文本转语音组件
### 类定义
```js
class bricks.WebTTS extends bricks.VBox
```
### 构造函数
```js
constructor(opts)
```
- **参数**
- `opts` (Object): 传递给父类 `VBox` 的配置选项。
- **说明**:调用父类构造函数初始化组件。
### 方法:`speak(text)`
触发文本到语音的合成功能。
#### 参数
| 参数名 | 类型 | 描述 |
|--------|--------|--------------------|
| text | String | 要朗读的文本内容 |
#### 功能描述
- 检查当前浏览器是否支持 `speechSynthesis` API。
- 创建一个 `SpeechSynthesisUtterance` 对象,并设置以下属性:
- `lang`: 使用 `bricks.app.lang` 设置语言。
- `pitch`: 音调设为 1正常
- `rate`: 语速设为 1正常
- 注册事件回调:
- `onstart`: 语音开始播放时输出日志。
- `onend`: 语音结束时输出日志。
- `onerror`: 出错时输出错误信息。
- 将语音任务加入系统队列并播放。
#### 示例代码
```js
const tts = new bricks.WebTTS();
tts.speak("你好,欢迎使用语音合成功能!");
```
#### 浏览器兼容性提示
若浏览器不支持 `speechSynthesis`,将在控制台打印错误:
```text
浏览器不支持SpeechSynthesis
```
> ⚠️ 注意:该功能在部分旧版或移动浏览器上可能受限,需确保运行环境支持。
---
## 2. `bricks.WebASR` - 语音识别组件
### 类定义
```js
class bricks.WebASR extends bricks.VBox
```
### 构造函数
```js
constructor(opts)
```
- **参数**
- `opts` (Object): 传递给父类 `VBox` 的配置选项。
- **内部初始化**
- `this.recognition` 初始化为 `None`(注意:应为 `null``undefined`,此处可能存在拼写/语法错误)
> ❌ Bug 提示:`None` 在 JavaScript 中未定义,正确写法应为 `null`
建议修正为:
```js
this.recognition = null;
```
### 方法:`start_recording()`
启动麦克风录音并进行实时语音识别。
#### 功能描述
- 检测浏览器是否支持 `SpeechRecognition` 接口。
- 若支持,则创建 `SpeechRecognition` 实例,并配置如下:
- `lang`: 使用 `bricks.app.lang` 设置识别语言。
- `onresult`: 当识别结果返回时,提取首个备选文本(`event.results[0][0].transcript`),并通过 `dispatch` 触发 `asr_result` 自定义事件。
- `onend`: 识别结束时输出日志。
- `onerror`: 发生错误时输出错误类型。
- 启动识别服务。
#### 示例代码
```js
const asr = new bricks.WebASR();
asr.start_recording();
// 监听识别结果
asr.on('asr_result', function(data) {
console.log('用户说:', data.content);
});
```
#### 控制台输出示例
```text
识别结果: 今天天气真好
```
#### 兼容性处理
如果浏览器不支持 `SpeechRecognition`,将输出:
```text
browser has not SpeechRecognition
```
> ⚠️ 注意Chrome 及基于 Chromium 的浏览器支持较好Safari 和 Firefox 支持有限或需要前缀。
---
### 方法:`stop_recording()`
停止正在进行的语音识别。
#### 功能描述
调用底层 `SpeechRecognition.stop()` 方法终止录音和识别过程。
#### 示例
```js
asr.stop_recording();
```
> ✅ 建议配合 UI 控件(如“按住说话”按钮)使用,在释放时调用此方法。
---
## 3. 工厂注册
这两个组件通过 `bricks.Factory` 进行注册,便于动态创建实例。
```js
bricks.Factory.register('WebTTS', bricks.WebTTS);
bricks.Factory.register('WebASR', bricks.WebASR);
```
### 用途
允许通过字符串名称来创建组件实例,例如(假设有工厂创建方法):
```js
let tts = bricks.Factory.create('WebTTS');
let asr = bricks.Factory.create('WebASR');
```
这提高了系统的可扩展性和解耦程度。
---
## 使用前提条件
### 1. 浏览器支持要求
| 特性 | 推荐浏览器 |
|------|------------|
| `SpeechSynthesis` | Chrome、Edge、Firefox、Safari部分支持 |
| `SpeechRecognition` | 主要支持 Chrome需 HTTPS 或 localhost |
> 🔒 安全限制:大多数浏览器要求页面运行在 **HTTPS** 或本地开发环境(`localhost`)下才能启用麦克风权限。
### 2. 语言设置依赖
组件使用全局变量:
```js
bricks.app.lang
```
请确保在应用启动时正确设置该值,例如:
```js
bricks.app.lang = 'zh-CN'; // 中文普通话
// 或
bricks.app.lang = 'en-US'; // 英语(美国)
```
常见语言代码参考:
- `zh-CN`: 中文(普通话)
- `en-US`: 英语(美式)
- `ja-JP`: 日语
- `ko-KR`: 韩语
---
## 已知问题与改进建议
| 问题 | 说明 | 建议修复 |
|------|------|---------|
| `None` 错误 | `this.reognition = None;` 是无效语法 | 改为 `this.recognition = null;` |
| 拼写错误 | `reognition``recognition` | 修正所有相关拼写 |
| 缺少权限请求处理 | 未显式请求麦克风权限 | 可增加 `navigator.mediaDevices.getUserMedia` 预检 |
| 事件作用域问题 | `onresult``this` 指向可能丢失 | 使用箭头函数或 `.bind(this)` |
### 推荐修复后的 `start_recording` 片段
```js
this.recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
this.dispatch('asr_result', { content: transcript });
console.log('识别结果:', transcript);
};
```
---
## 总结
| 组件 | 功能 | 核心 API | 是否需要网络 | 是否需要麦克风 |
|------------|------------------|----------------------------|--------------|----------------|
| `WebTTS` | 文本转语音 | `SpeechSynthesis` | 否 | 否 |
| `WebASR` | 语音转文本 | `SpeechRecognition` | 是(云端) | 是 |
✅ 适用场景:
- 教育类应用中的发音朗读
- 智能助手对话界面
- 无障碍访问工具
- 语音指令控制系统
🚫 不适用场景:
- 无网络环境下离线高精度 ASR
- 低延迟工业级语音处理
---
## 参考链接
- [MDN: SpeechSynthesis](https://developer.mozilla.org/zh-CN/docs/Web/API/SpeechSynthesis)
- [MDN: SpeechRecognition](https://developer.mozilla.org/zh-CN/docs/Web/API/SpeechRecognition)
- [Google Chrome 语音识别限制说明](https://www.chromium.org/developers/design-documents/media/capture)
---
📝 **维护建议**:定期检查浏览器对 Web Speech API 的支持情况,并考虑降级方案(如接入第三方 SDK

499
docs/cn/widget.md Normal file
View File

@ -0,0 +1,499 @@
以下是为提供的 JavaScript 代码编写的 **Markdown 格式技术文档**,涵盖了核心类结构、继承关系、属性、方法和使用说明。
---
# Bricks UI 组件库技术文档
`bricks.JsWidget` 是 Bricks 框架中的基础 UI 组件类,提供通用的 DOM 管理、样式设置、事件绑定、弹窗支持等功能。其他文本类组件(如 `Text`, `Title`, `Tooltip` 等)均继承自该基类或其子类。
---
## 目录
- [1. 基础结构](#1-基础结构)
- [2. `JsWidget` 类](#2-jswidget-类)
- [构造函数](#构造函数)
- [实例属性](#实例属性)
- [公共方法](#公共方法)
- [DOM 操作](#dom-操作)
- [样式与 CSS](#样式与-css)
- [事件处理](#事件处理)
- [尺寸与布局](#尺寸与布局)
- [弹窗功能](#弹窗功能)
- [全屏控制](#全屏控制)
- [显示/隐藏控制](#显示隐藏控制)
- [ID 与属性管理](#id-与属性管理)
- [3. `TextBase` 类](#3-textbase-类)
- [4. 文本组件系列](#4-文本组件系列)
- [`Text`](#text)
- [`KeyinText`](#keyintext)
- [`Title1` ~ `Title6`](#title1--title6)
- [5. Tooltip 组件](#5-tooltip-组件)
- [6. 工厂注册](#6-工厂注册)
- [7. 使用示例](#7-使用示例)
---
## 1. 基础结构
```js
var bricks = window.bricks || {};
```
确保 `bricks` 全局命名空间存在,并在此基础上定义各类组件。
所有组件以 ES6 Class 形式实现,支持继承和模块化扩展。
---
## 2. `JsWidget`
> 基础 UI 组件类,封装了 DOM 创建、样式设置、事件监听、动态尺寸调整等通用能力。
### 构造函数
```js
constructor(options)
```
#### 参数:
| 参数名 | 类型 | 说明 |
|-------|------|------|
| `options` | Object | 配置选项对象,可选 |
#### 支持的配置项:
| 属性 | 类型 | 说明 |
|------|------|------|
| `baseURI` | String | 基础资源路径 |
| `css` | String | 添加到元素的 CSS 类名(空格分隔) |
| `csses` | String | 同 `css`,兼容旧写法 |
| `tip` | String | 鼠标悬停提示文字(自动绑定 tooltip |
| `popup` | Object | 弹窗配置对象 |
| &nbsp;&nbsp;`.popup_event` | String | 触发弹窗的事件类型(如 `'click'` |
| &nbsp;&nbsp;`.popup_desc` | WidgetDesc | 弹窗内容描述(用于构建内部组件) |
| &nbsp;&nbsp;`.popupwindow` | Boolean | 是否使用独立窗口模式弹窗 |
| &nbsp;&nbsp;`.options` | Object | 传递给 Popup/PoupWindow 的参数 |
| `bgimage` | String (URL) | 背景图片 URL |
| `width`, `height` | String/Number | 宽高(支持像素或字符串如 `'100%'` |
| `x`, `y` | Number | X/Y 偏移(暂未实际定位) |
| `dynsize` | Boolean | 启用字符单位动态缩放 |
| `cwidth`, `cheight` | Number | 字符宽度/高度单位(乘以 `charsize` 得到真实 px |
| `cfontsize` | Number | 字体大小(相对于字符单位) |
| `color`, `bgcolor` | String | 文字颜色 / 背景色(`bgcolor` 映射为 `backgroundColor` |
| `align`, `textAlign` | String | 对齐方式 |
| `margin`, `padding` 等 | Any | 支持多种标准 CSS 属性直接传入 |
> ⚠️ 注意:`opts_set_style()` 方法会将匹配的 `options` 键自动映射到 DOM 样式上。
---
### 实例属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `dom_element` | HTMLElement | 关联的 DOM 元素(默认为 `<div>` |
| `id` | String | 自动生成的唯一 ID通过 `bricks.uuid()` |
| `baseURI` | String | 当前组件的基础 URI |
| `opts` | Object | 传入的原始配置对象 |
| `parent` | JsWidget | 父级组件(尚未在代码中赋值) |
| `_container` | Boolean | 是否为容器组件(默认 `false` |
| `sizable_elements` | Array | 参与字体缩放的子元素列表 |
| `user_data` | Any | 用户自定义数据存储 |
| `popup_widget` | Popup \| PopupWindow | 当前打开的弹窗实例(延迟创建) |
---
### 公共方法
#### DOM 操作
##### `.create()`
创建一个 `<div>` 作为根元素并赋值给 `this.dom_element`
##### `.is_in_dom() → Boolean`
检查当前组件是否已插入文档树中。
##### `.showRectage() → DOMRect`
返回组件的边界矩形(调用 `getBoundingClientRect()`)。
---
#### 样式与 CSS
##### `.set_style(key, value)`
设置单个 CSS 样式属性。
```js
widget.set_style('color', 'red');
```
##### `.set_css(css, remove_flg)`
添加或移除 CSS 类名(支持多个类名空格分隔)。
```js
widget.set_css('btn primary'); // 添加类
widget.set_css('hidden', true); // 移除类
```
##### `.set_csses(csses, remove_flg)`
`.set_css()`,保留向后兼容。
##### `.unset_css(css)`
移除指定 CSS 类。
##### `.set_cssObject(cssobj)`
批量设置内联样式对象(通过 `bricks.extend` 扩展)。
##### `.opts_set_style()`
根据 `options` 中的键值自动设置对应样式,支持别名映射(如 `bgcolor → backgroundColor`),并处理 `cwidth`/`cheight` 字符单位转换。
##### `.charsize_sizing()`
响应全局 `charsize` 变化的回调函数,重新计算基于字符单位的宽高和字体大小。
> 自动绑定于 `dynsize: true` 时。
##### `.set_bg_image(url)`
设置背景图,应用以下样式:
- `background-size: cover`
- `background-position: center`
- `background-repeat: no-repeat`
---
#### 事件处理
##### `.bind(eventname, handler)`
绑定 DOM 事件。
```js
widget.bind('click', () => console.log('clicked'));
```
##### `.unbind(eventname, handler)`
解绑事件。
##### `.dispatch(eventname, params)`
派发自定义事件,携带 `params` 数据。
```js
widget.dispatch('changed', { data: 'new value' });
```
> 注:`params` 会挂载在事件对象的 `.params` 属性上。
---
#### 尺寸与布局
##### `.get_width() → Number`
返回客户端宽度(`clientWidth`)。
##### `.get_height() → Number`
返回客户端高度(`clientHeight`)。
##### `.set_width(width)`
设置宽度,若传入数字则自动加 `'px'`
##### `.set_height(height)`
同上,设置高度。
##### `.h_center()`, `.h_left()`, `.h_right()`
水平对齐:
- `h_center`: `margin: auto`
- `h_left`: `marginLeft: 0`
- `h_right`: `marginRight: 0`
##### `.ht_center()`, `.ht_left()`, `.ht_right()`
文本对齐:
- 设置 `textAlign` 样式。
---
#### 弹窗功能
##### `.popup_action() → Promise<void>`
异步处理弹窗显示/关闭逻辑:
- 若已有弹窗,则销毁;
- 否则根据 `popup.popupwindow` 决定创建 `PopupWindow``Popup`
- 使用 `bricks.widgetBuild(popup_desc, parent, userData)` 动态构建内容;
- 显示并定位弹窗。
##### `.destroy_popup()`
销毁当前弹窗实例,清空引用。
> 绑定在弹窗的 `'dismissed'` 事件上。
> 💡 提示:`popup_desc` 应符合框架的组件描述格式。
---
#### 全屏控制
##### `.enter_fullscreen()`
请求进入全屏模式,兼容各浏览器 APIFullscreen API
##### `.exit_fullscreen()`
退出全屏模式(调用 `document.exitFullscreen()`)。
---
#### 显示/隐藏控制
##### `.show()`
显示组件(`display = ''`)。
##### `.hide()`
隐藏组件(`display = 'none'`)。
##### `.toggle_hide()`
切换显隐状态。
##### `.is_hide() → Boolean`
判断是否处于隐藏状态。
---
#### ID 与属性管理
##### `.set_id(id)`
设置组件 ID并同步到 DOM。
##### `.set_baseURI(uri)`
更新 `baseURI`,若为容器则递归通知子组件更新。
##### `.set_attribute(attr, value)`
设置 DOM 属性。
##### `.get_attribute(attr) → String`
获取 DOM 属性(⚠️ 当前实现有误,缺少 `return`)。
> ❌ 修正建议:
```js
get_attribute(attr) {
return this.dom_element.getAttribute(attr);
}
```
##### `.selected(flag)`
添加或移除 `selected` CSS 类。
---
#### 辅助方法
##### `.observable(name, value)`
创建一个可观察对象(绑定到当前组件),用于响应式数据管理。
##### `.disabled(flag)`
启用/禁用交互(通过 `pointerEvents: none/auto` 控制)。
##### `.getUserData() → Any`
获取用户数据。
##### `.setUserData(v)`
设置用户数据。
---
## 3. `TextBase`
> 文本组件基类,继承自 `JsWidget`支持国际化i18n、文本渲染、字体缩放。
### 构造函数
```js
constructor(options)
```
#### 特有配置项:
| 属性 | 类型 | 说明 |
|------|------|------|
| `text` | String | 初始文本内容 |
| `otext` | String | 原始文本(用于 i18n 查找) |
| `i18n` | Boolean | 是否启用国际化 |
| `rate` | Number | 字体缩放系数(默认 1 |
| `halign`, `valign` | String | 水平/垂直对齐(未在代码中使用) |
| `color`, `bgtcolor` | String | 文字/背景色(后者未使用) |
### 方法
##### `.set_attrs()`
初始化文本内容:
- 优先使用 `text`
- 若提供 `otext``i18n`,则通过 `I18n._()` 转换
- 设置 `innerHTML`
##### `.set_text(text)`
更新文本内容(同时更新 `this.text` 和 DOM
##### `.set_otext(otxt)`
更新原始文本,并重新国际化渲染。
##### `.set_i18n_text()`
触发国际化文本刷新(通常由语言变更事件驱动)。
---
## 4. 文本组件系列
### `Text`
> 普通文本组件,字体大小为 `1 × charsize`
```js
new bricks.Text({ text: "Hello" })
```
- 继承 `TextBase`
- 默认 `cfontsize = 1`
- 自动调用 `charsize_sizing()`
---
### `KeyinText`
> 可输入文本组件,模拟键盘输入行为。
#### 功能:
- 监听全局 `keydown` 事件(绑定至 `bricks.app`
- 支持:
- 输入单字符
- Backspace 删除末尾字符
- Delete 清空内容
- 每次变更后派发 `changed` 事件,携带 `{ name: text }` 数据
- 默认 `name = 'data'`
#### 方法:
- `dispatch_changed()`:封装事件派发逻辑
> 📝 示例用途:简易输入框、密码输入、命令行模拟。
---
### `Title1` ~ `Title6`
> 六级标题组件,字体加粗,字号逐级递减。
| 组件 | `cfontsize` |
|------|------------|
| `Title1` | 1.96 |
| `Title2` | 1.80 |
| `Title3` | 1.64 |
| `Title4` | 1.48 |
| `Title5` | 1.32 |
| `Title6` | 1.16 |
均设置 `fontWeight: bold`,并自动适配字符尺寸。
---
## 5. Tooltip 组件
> 工具提示组件,继承自 `Text`,预设样式。
### 构造选项
- `rate: 0.8`:较小字体
- `tip: null`:避免递归绑定提示
- 添加 `modal` CSS 类
- 最小宽度 `90px`
- 自动隐藏任务调度器(`auto_task`
### 方法
##### `.show(otext, event)`
显示提示框:
- 设置文本
- 设为可见且高 `zIndex`
- 定位到鼠标事件位置(`bricks.relocate_by_eventpos`
- 启动 6 秒后自动隐藏(可取消)
##### `.hide()`
隐藏提示框,清除定时任务。
> ✅ 支持防抖和重复触发清理。
---
## 6. 工厂注册
所有组件通过 `bricks.Factory.register` 注册,便于动态创建:
```js
bricks.Factory.register('Tooltip', bricks.Tooltip);
bricks.Factory.register('Text', bricks.Text);
// ... 其他组件
```
支持通过名称字符串动态实例化组件。
---
## 7. 使用示例
### 创建普通文本
```js
const text = new bricks.Text({
text: 'Welcome!',
css: 'text-primary bold',
width: 200,
tip: 'This is a welcome message'
});
document.body.appendChild(text.dom_element);
```
### 创建可输入文本
```js
const input = new bricks.KeyinText({
name: 'username',
cwidth: 20
});
input.bind('changed', e => {
console.log('Input changed:', e.params);
});
```
### 创建标题
```js
const title = new bricks.Title1({
otext: 'Main Title',
i18n: true
});
```
### 创建带弹窗的按钮(假设)
```js
const btn = new bricks.Text({
text: 'Click Me',
popup: {
popup_event: 'click',
popup_desc: { type: 'Panel', children: [...] },
popupwindow: false
}
});
```
### 设置背景图
```js
widget.set_bg_image('/images/bg.jpg');
```
---
## 总结
`bricks.JsWidget` 提供了一个灵活、可扩展的前端组件基类,具备以下优势:
✅ 模块化设计
✅ 支持动态尺寸(字符单位)
✅ 内建弹窗、tooltip、国际化支持
✅ 易于继承和定制
✅ 与 Bricks 框架深度集成event bus, factory, resize observer
适用于构建响应式、多语言、高交互性的 Web 应用界面。
---
> 📚 *文档版本v1.0*
> © 2025 Bricks Framework Team

View File

@ -1,100 +1,207 @@
# 控件列表
Bricks内置许多的显示控件所有显示控件都继承自JsWidget容器控件Layout就继承自JsWidget其他的容器HBox VBox继承自Layout
# bricks控件
bricks内置许多的显示控件所有显示控件都继承自JsWidget容器控件Layout就继承自JsWidget其他的容器HBox VBox继承自Layout
# 控件继承树
## 基础控件
* [Form](form.md):输入表单控件
自动根据options中的fields定义构造表单目前支持的数据类型有
** 'str' 对应的控件为: bricks.UiStr
** 'hide' 对应的控件为: bricks.UiHide
** 'tel' 对应的控件为: bricks.UiTel
** 'date' 对应的控件为: bricks.UiDate
** 'int' 对应的控件为: bricks.UiInt
** 'float' 对应的控件为: bricks.UiFloat
** 'check' 对应的控件为: bricks.UiCheck
** 'checkbox' 对应的控件为: bricks.UiCheckBox
** 'email' 对应的控件为: bricks.UiEmail
** 'file' 对应的控件为: bricks.UiFile
** 'image' 对应的控件为: bricks.UiImage
** 'code' 对应的控件为: bricks.UiCode
** 'text' 对应的控件为: bricks.UiText
** 'password' 对应的控件为: bricks.UiPassword
** 'audio' 对应的控件为: bricks.UiAudio
** 'video' 对应的控件为: bricks.UiVideo
上述控件都在[输入定义](inout.js)
* [Accordion](accordion.md) bricks.Accordion
手风琴控件,支持多个标题,内容组成的控件,内容和展开和折叠
* [AudioPlayer](audio.md) bricks.AudioPlayer
音频播放控件
# 控件清单
* [Accordion](widgets/accordion.md):可折叠控件
* [AudioPlayer](widgets/audioplayer.md):音频播放器
* [AudioRecorder](widgets/audiorecorder.md)
* [SttClient](widgets/sttclient.md)
* [Button](widgets/button.md)
* [Conform](widgets/conform.md)
* [DataGrid](widgets/datagrid.md)
* [DataRow](widgets/datarow.md)
* [DynamicAccordion](widgets/dynamicaccordion.md)
* [DynamicColumn](widgets/dynamiccolums.md)
* [IconBar](widgets/iconbar.md)
* [IconTextBar](widgets/icontextbar.md)
* [FloatIconBar](widgets/floaticonbar.md)
* [FloatIconTextBar](widgets/floaticontextbar.wd)
* [Form](widgets/form.md)
* [Html](widgets/html.md)
* [Iframe](widgets/iframe.md)
* [Image](widgets/image.md)
* [Icon](widgets/icon.md)
* [BlankIcon](widgets/blankicon)
* [HBox](widgets/hbox.md)
* [FHBox](widgets/fhbox.md)
* [VBox](widgets/vbox.md)
* [FVBox](widgets/fvbox.md)
* [Filler](widgets/filler.md)
* [LlmDialog](widgets/llmdialog.md)
* [MarkdownViewer](widgets/markdownviewer.md)
* [MdWidget](widgets/mdwidget.md)
* [Menu](widgets/menu.md)
* [Message](widgets/menu.md)
* [Error](widgets/menu.md)
* [PopupForm](widgets/menu.md)
* [MiniForm](widgets/menu.md)
* [Modal](widgets/menu.md)
* [ModalForm](widgets/menu.md)
* [MultipleStateImage](widgets/menu.md)
* [MultipleStateIcon](widgets/menu.md)
* [Running](widgets/menu.md)
* [VScrollPanel](widgets/menu.md)
* [HScrollPanel](widgets/menu.md)
* [TabPanel](widgets/menu.md)
* [Tabular](widgets/menu.md)
* [Toolbar](widgets/menu.md)
* [Tree](widgets/menu.md)
* [EditableTree](widgets/menu.md)
* [Video](widgets/menu.md)
* [WebSocket](widgets/menu.md)
* [Tooltip](widgets/menu.md)
* [Text](widgets/menu.md)
* [Title1](widgets/menu.md)
* [Title2](widgets/menu.md)
* [Title3](widgets/menu.md)
* [Title4](widgets/menu.md)
* [Title5](widgets/menu.md)
* [Title6](widgets/menu.md)
* [Wterm](widgets/menu.md)
* [ChartBar](bar.md) bricks.ChartBar
将后台数据显示为条形图表
* [Button](button.md) bricks.Button
按钮控件
* [JsWidget](widgets/jswidget.md) 祖先控件,对应于<div>元素
* [Button](widgets/button.md):按钮控件
* [DataGrid](widgets/datagrid.md):数据表格控件
* [Form](widgets/form.md):输入表单控件
* [Image](widgets/image.md):图片控件
* [Icon](widgets/icon.md):图标控件
* [BlankIcon](widgets/blackicon.js):空白图标控件
* [HBox](widgets/hbox.md):横向布局器
* [VBox](widgets/vbox.md):纵向布局器
* [HFiller](widgets/hfiller.md):横向填充控件
* [VFiller](widgets/vfiller.md):纵向填充控件
* [MarkdownViewer](widgets/markdonwviewer.md)markdown文档显示控件
* [Menu](widgets/menu.md):菜单控件
* [Message](widgets/message.md):消息控件
* [Error](widgets/error.md):错误信息控件
* [PopupForm](widgets/popupform.md):弹出表单控件
* [MiniForm](widgets/meniform.md):多选单一输入表单
* [Modal](widgets/modal.md):独占控件
* [ModalForm](widgets/modalform.md):独占表单
* [MultipleStateImage](widgets/multiplestateimage.md):多状态图像显示控件
* [ScrollPanel](widgets/scrollpanel.md):滚动面板
* [VScrollPanel](widgets/vscrollpanel.md):纵向滚动面板
* [HScrollPanel](widgets/hscrollpanel.md):横向滚动面板
* [TabPanel][widgets/tabpanel.md):标签面板控件
* [Toolbar](widgets/toolbar.md):工具条控件
* [Tree](widgets/tree.md):树状显示控件
* [EditableTree](widgets/editabletree.md):可编辑树状显示控件
* [VideoPlayer](widgets/videoplayer.md):视频播放器控件
* [Text](widgets/text.md):正文控件
* [Title1](widgets/title1.md)标题1控件
* [Title2](widgets/title2.md)标题2控件
* [Title3](widgets/title3.md)标题3控件
* [Title4](widgets/title4.md)标题4控件
* [Title5](widgets/title5.md)标题5控件
* [Title6](widgets/title6.md)标题6控件
* [Cols](cols.md) bricks.Cols
列式排列控件,可动态填满父控件的宽度
* [Conform](conform.md) bricks.Conform
确认控件,弹出窗口显示内容,并要求用户确认
#
* [Countdown](countdown.md) bricks.Countdown
时间倒计时控件,显示从还剩下的时间
* [TimePassed](countdown.md) bricks.TimePassed
时间消耗控件,显示从开始计时开始所消耗的时间
* [DataGrid](datagrid.md) bricks.DataGrid
数据表格控件
* [DataRow](datarow.md) bricks.DataRow
数据行控件
* [DataViewer](dataviewer.md) bricks.DataViewer
数据显示控件DynamicColumn控件的后代控件
* [DOCXviewer](docxviewer.md) bricks.DOCXviewer
docx文件显示控件
* [EXCELviewer](docxviewer.md) bricks.EXCELviewer
excel文件显示控件
* [PDFviewer](accordion.md) bricks.PDFviewer
pdf显示控件
* [DynamicAccordion](dynamicaccordion.md) bricks.DynamicAccordion
动态手风琴控件
* [IconBar](floaticonbar.md) bricks.IconBar
图标条控件
* [IconTextBar](floaticonbar.md) bricks.IconTextBar
图标文本条控件
* [FloatIconBar](floaticonbar.md) bricks.FloatIconBar
浮动图标条,平时显示一个标识图标,点击此标识图标后显示图标条
* [FloatIconTextBar](floaticonbar.md) bricks.FloatIconTextBar
浮动图标正文条,平时显示一个标识图标,点击此图标后显示图标正文条
* [Html](html.md) bricks.Html
HTML控件直接显示html内容
* [IconbarPage](iconbarpage.md) bricks.IconbarPage
图标条页控件,显示图标条,不同的图标点击后显示图标对应的内容
* [NewWindow](iframe.md) bricks.NewWindow
新浏览器页签或窗口控件浏览器创建新的窗口或页签显示url的内容
* [Iframe](iframe.md) bricks.Iframe
Iframe控件用于显示外部网站内容
* [Image](image.md) bricks.Image
图像控件
* [StatedIcon](image.md) bricks.StatedIcon
多站台图标,点击后状态改变,支持多个状态,并发出状态改变事件
* [Icon](image.md) bricks.Icon
图标控件支持多种图像格式url
* [BlankIcon](image.md) bricks.BlankIcon
空白图标占位控件
* [ChartLine](line.md) bricks.ChartLine
* [LlmIO](llm.md) bricks.LlmIO
* [LlmOut](llm.md) bricks.LlmOut
* [MarkdownViewer](markdownviewer.md) bricks.MarkdownViewer
* [MdWidget](markdownviewer.md) bricks.MdWidget
* [Menu](menu.md) bricks.Menu
* [Message](message.md) bricks.Message
* [Error](message.md) bricks.Error
* [MultipleStateImage](multiple_state_image.md) bricks.MultipleStateImage
多状态图像控件
* [PeriodDays](period.md) bricks.PeriodDays
日期期间控件,自动计算时间段的起始日期
* [ChartPie](pie.md) bricks.ChartPie
饼图控件基于echrts
* [ProgressBar](progressbar.md) bricks.ProgressBar
进度条控件
* [SysCamera](recorder.md) bricks.SysCamera
照相控件,可拍摄照片
* [WidgetRecorder](recorder.md) bricks.WidgetRecorder
控件视频录制控件,可录制浏览器播放的视频
* [SysAudioRecorder](recorder.md) bricks.SysAudioRecorder
浏览器音频录制控件,用来录制音频
* [SysVideoRecorder](recorder.md) bricks.SysVideoRecorder
浏览器视频录制控件,用来录制视频
* [Running](running.md) bricks.Running
运行图标控件modal模式显示正在运行相关控件不可操作需要在完成 任务后dismiss它
* [Splitter](splitter.md) bricks.Splitter
分割器控件,显示水平或垂直分割线
* [Svg](svg.md) bricks.Svg
Svg图标控件
* [StatedSvg](svg.md) bricks.StatedSvg
带状态的svg图标控件不同状态显示不同的图标
* [MultipleStateIcon](svg.md) bricks.MultipleStateIcon
多状态图标控件
* [TabPanel](tab.md) bricks.TabPanel
页签控件
* [Tabular](tabular.md) bricks.Tabular
数据列表形式的数据维护控件,支持数据的显示,增加,修改和删除
[xls2ddl](https://git.opencomputing.cn/yumoqing/xls2ddl)工具能根据数据表结构自动生成数据Tabular控件以及相关的数据维护dspy
* [Toolbar](toolbar.md) bricks.Toolbar
工具条控件
* [Tree](tree.md) bricks.Tree
树形控件
* [VadText](vadtext.md) bricks.VadText
自动捕获语音并将捕获的语音发送给服务器
* [VideoPlayer](videoplayer.md) bricks.VideoPlayer
视频播放控件支持浏览器支持的视频格式外还支持m3u8流媒体和Dash流媒体,
bricks已在3parties目录中包含了所依赖的hls和dash包
* [Video](videoplayer.md) bricks.VideoPlayer
视频播放控件同VideoPlayer
* [WebSocket](websocket.md) bricks.WebSocket
支持websocket
* [WebTTS](webspeech.js.md) bricks.WebTTS
为完成控件,浏览器内置文本转语音能力
* [WebASR](webspeech.js.md) bricks.WebASR
未完成控件,浏览器内部的语音识别能力
* [Tooltip](widget.md) bricks.Tooltip
Tooltip控件不直接创建而是在控件中添加“tip":"提示字符串“属性为控件添加Tooltip控件
* [Text](widget.md) bricks.Text
文本控件
* [Title1](widget.md) bricks.Title1
第一号标题
* [Title2](widget.md) bricks.Title2
第二号标题
* [Title3](widget.md) bricks.Title3
第三号标题
* [Title4](widget.md) bricks.Title4
第四号标题
* [Title5](widget.md) bricks.Title5
第五号标题
* [Title6](widget.md) bricks.Title6
第六号标题
* [Wterm](wterm.md) bricks.Wterm
xterm.js在bricks中的实现
## 容器类控件
* [VScrollPanel](accordion.md) bricks.VScrollPanel
垂直滚动容器,需要设置固定的高度或占满全部父容器高度
* [HScrollPanel](accordion.md) bricks.HScrollPanel
水平滚动容器,需要设置固定的宽度或占满全部父容器宽度
* [Popup](accordion.md) bricks.Popup
弹出容器,置于当前全部控件最上面
* [PopupWindow](accordion.md) bricks.PopupWindow
弹出窗口,置于当前全部控件最上面
* [HBox](accordion.md) bricks.HBox
水平扩展容器,全部子控件水平排放
* [VBox](accordion.md) bricks.VBox
垂直扩展容器,全部子控件垂直排放
* [Filler](accordion.md) bricks.Filler
占满父容器剩余控件如果父容器有多个Filler控件则平均分配剩余控件Filler容器下可添加子控件
* [DynamicColumn](dynamiccolumn.md) bricks.DynamicColumn
子控件需要设置固定宽度,动态从左到右,从上到下排列子控件
* [ResponsableBox](layout.md) bricks.ResponsableBox
自适应容器,当宽度大则水平排列子控件,而高度大时则水平排列子控件, 并能根据宽高变化自动改变。
* [Modal](modal.md) bricks.Modal
modal容器

427
docs/cn/wsllm.md Normal file
View File

@ -0,0 +1,427 @@
# `bricks.LlmIO` 技术文档
> 本模块实现了一个基于 Web 的多模型 LLM大语言模型交互界面组件支持流式/同步响应、用户输入、语音输出、反馈评分等功能。适用于构建类 ChatGPT 的对话系统。
---
## 目录
- [概述](#概述)
- [核心类结构](#核心类结构)
- [类详解](#类详解)
- [`bricks.LlmIO`](#bricksllmio)
- [`bricks.LlmModel`](#bricksllmmodel)
- [`bricks.ModelOutput`](#bricksmodeloutput)
- [`bricks.RoleOutput`](#bricksroleoutput)
- [数据格式说明](#数据格式说明)
- [事件与回调机制](#事件与回调机制)
- [扩展功能](#扩展功能)
- [使用示例](#使用示例)
- [依赖与注册](#依赖与注册)
---
## 概述
`bricks.LlmIO` 是一个复合型 UI 控件,用于集成多个 LLM 模型并统一管理用户输入和模型输出。它提供了以下能力:
- 支持添加多个 LLM 模型;
- 用户通过表单提交输入;
- 支持 **流式 (stream)**、**同步 (sync)** 和 **异步 (async)** 响应模式;
- 输出内容可动态渲染(支持模板化视图);
- 支持 TTS文本转语音流式播放
- 可对模型输出进行满意度评分(点赞/踩);
- 自动维护会话历史(可选);
该组件基于 `bricks.js` 框架构建,采用面向对象设计,高度可配置和可扩展。
---
## 核心类结构
| 类名 | 功能 |
|------|------|
| `bricks.LlmIO` | 主容器,管理所有模型实例和输入/输出区域 |
| `bricks.LlmModel` | 单个 LLM 模型的封装,处理请求发送与消息管理 |
| `bricks.ModelOutput` | 显示单条模型输出结果,支持富内容渲染与反馈控件 |
| `bricks.RoleOutput` *(内部基类)* | 统一用户与模型输出样式布局的基础类 |
---
## 类详解
### `bricks.LlmIO`
主控件,继承自 `bricks.VBox`,作为整个对话系统的容器。
#### 构造参数 (`opts`)
```js
{
ws_url: String, // WebSocket 地址(预留未使用)
user_icon: String, // 用户头像图标 URL
list_models_url: String, // 获取可用模型列表的 API 接口
input_fields: Array, // 输入表单字段定义(用于 bricks.Form
input_view: Object, // 定义如何展示用户输入的内容widget 描述)
output_view: Object, // 默认全局输出视图模板(可被模型覆盖)
models: Array<LlmModelOpts>, // 初始加载的模型配置数组
tts_url: String, // TTS 接口地址(启用语音时使用)
estimate_url: String, // 提交反馈评分的接口地址
msg_css: String // 用户消息的 CSS 类名(默认 'user_msg'
}
```
#### 属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `ws` | `bricks.WebSocket` | 预留 WebSocket 实例(当前未实际使用) |
| `llmmodels` | `Array<bricks.LlmModel>` | 已添加的模型实例列表 |
| `title_w` | `bricks.HBox` | 顶部显示模型标签的容器 |
| `o_w` | `bricks.Filler` | 输出内容滚动区域 |
| `i_w`, `nm_w` | `bricks.Svg` | “输入” 和 “新增模型” 图标按钮 |
| `textvoice` | Boolean | 是否启用文本语音合成功能 |
#### 方法
##### `constructor(opts)`
初始化主界面,创建标题栏、输出区、底部操作按钮,并加载初始模型。
##### `show_added_model(m)`
将给定模型配置 `m` 实例化为 `LlmModel` 并加入界面。
- 若启用了 `textvoice`,自动注入 TTS 配置。
- 调用 `render_title()` 创建模型标签并插入标题栏。
##### `open_search_models(event)`
弹出模型选择弹窗(`PopupWindow`),从服务器拉取可选模型列表。
- 使用 `bricks.Cols` 展示模型卡片列表;
- 点击任一模型触发 `add_new_model()`
- 弹窗点击后自动关闭。
##### `add_new_model(event)`
接收来自 `Cols` 的选中模型数据,将其加入 `this.models` 数组,并调用 `show_added_model()` 渲染到界面上。
##### `open_input_widget(event)`
打开输入表单弹窗,允许用户输入内容。
- 使用 `bricks.Form` 构建表单;
- 表单字段由 `input_fields` 配置;
- 提交后触发 `handle_input()`
##### `handle_input(event)`
处理用户输入数据:
1. 调用 `show_input(params)` 显示用户输入;
2. 遍历所有 `LlmModel` 实例,若其接受 `'userinput'` 来源,则调用 `model_inputed(params)` 发起请求。
> 使用 `schedule_once` 延迟执行以避免阻塞 UI。
##### `show_input(params)`
将用户输入数据根据 `input_view` 模板渲染成可视化组件,并显示在输出区域左侧(带用户图标)。
- 支持富文本或结构化数据显示;
- 添加 CSS 类 `user_msg` 或自定义类名。
---
### `bricks.LlmModel`
表示一个具体的 LLM 模型实例,负责请求发送、消息管理和响应解析。
#### 构造参数 (`opts`)
```js
{
icon: String,
model: String, // 模型名称
url: String, // 请求后端 API 地址
output_view: Object|String, // 输出渲染模板widget 描述)
params: Object, // 固定请求参数
user_message_format: Object, // 用户消息格式模板
system_message_format: Object, // 系统消息格式模板
llm_message_format: Object, // 模型回复消息格式模板
use_session: Boolean, // 是否保持会话上下文
input_from: String, // 接收输入来源标识(如 'userinput'
textvoice: Boolean, // 是否启用 TTS
tts_url: String, // TTS 流地址
response_mode: 'stream'|'sync'|'async' // 响应模式
}
```
#### 属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `llmio` | `bricks.LlmIO` | 所属父容器 |
| `messages` | Array | 当前会话的历史消息(遵循 OpenAI 类似格式) |
| `resp_data` | Object | 最终完整响应数据(用于 sync 模式拼接) |
#### 方法
##### `constructor(llmio, opts)`
初始化模型实例,设置默认消息格式。
##### `render_title()`
生成模型标题栏组件HBox包含图标和点击事件绑定未来可用于配置面板
##### `inputdata2uploaddata(data)`
将原始输入数据转换为适合发送给后端的格式:
- 支持 `FormData` 或普通对象;
- 自动追加 `model`, `modelinstanceid`, `modeltypeid`, `messages` 等字段;
- 若定义了 `user_message_format`,则按模板格式化用户消息并推入 `messages`
##### `model_inputed(data)`
主入口方法:当收到用户输入时触发。
根据 `response_mode` 分支处理:
| 模式 | 处理方式 |
|------|---------|
| `stream` / `async` | 使用 `HttpResponseStream` 分块接收流式响应,逐段更新输出 |
| `sync` | 使用 `HttpJson` 一次性获取完整响应,并整体更新 |
> 在流式模式下,每收到一段数据都会调用 `chunk_response()` 更新 UI。
##### `is_accept_source(source)`
判断是否接受指定来源的输入(目前仅支持 `'userinput'`)。
##### `llm_msg_format()`
返回模型回复消息的标准格式,默认为:
```json
{ "role": "assistant", "content": "${content}" }
```
##### `chunk_response(mout, line)`
处理流式响应中的每一个数据块:
- 解析 JSON
- 过滤空内容;
- 转义特殊字符;
- 调用 `mout.update_data(d)` 实时更新输出;
- 累积内容至 `this.resp_data`
##### `chunk_ended()`
流式响应结束后,将最终累积的内容按 `llm_msg_format` 格式化并压入 `messages` 数组,完成上下文保存。
---
### `bricks.ModelOutput`
继承自 `RoleOutput`,专门用于显示 **LLM 模型的输出内容**
#### 构造参数 (`opts`)
```js
{
icon: String,
model: String,
estimate_url: String, // 提交评分的接口地址
output_view: Object|String, // 输出内容渲染模板
textvoice: Boolean,
tts_url: String
}
```
#### 属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `img` | `bricks.Svg` | 模型图标 |
| `filler` | `bricks.VBox` | 内容填充区域 |
| `run` | `bricks.BaseRunning` | 加载动画(请求中状态) |
| `estimate_w` | `bricks.HBox` | 满意度评价组件(点赞/踩) |
| `upstreaming` | `bricks.UpStreaming` | TTS 流上传器(语音播报) |
| `logid` | String | 日志 ID用于反馈关联 |
#### 方法
##### `constructor(opts)`
初始化输出框:
- 创建头部模型信息行(图标 + 名称);
- 创建内容区域(含加载动画);
- 若是 LLM 角色,创建左右留白;
- 调用 `build_estimate_widgets()` 创建评价控件(如果配置了 `estimate_url`)。
##### `build_estimate_widgets()`
构建“结果满意吗?”评价组件:
- 包含文字提示、“赞”和“踩”SVG图标
- 绑定点击事件到 `estimate_llm(val)`
- 初始隐藏,待有 `logid` 时再显示。
##### `estimate_llm(val, event)`
提交用户评分:
- 构造 `urlwidget` 请求描述;
- 参数包括 `logid` 和评分值1=满意,-1=不满意);
- 动态创建 widget 发起请求;
- 提交后禁用按钮防止重复提交。
##### `update_data(data)`
更新输出内容:
- 移除加载动画;
- 启动 TTS 流(如有配置);
- 使用 `output_view` 模板结合 `data` 数据构建 UI 组件;
- 将组件添加到 `filler` 中;
- 若存在 `logid`,显示评价组件。
> 支持 `output_view` 为字符串JSON 字符串)或对象。
##### `finish()`
结束 TTS 流传输。
---
### `bricks.RoleOutput`
> ⚠️ 注意:代码中存在语法错误 —— `defautl_icon` 应为 `default_icon`
`ModelOutput` 的基类,也用于用户输出(但未导出独立类)。统一管理角色输出的布局结构。
#### 共享特性
- 左侧图标(用户 or LLM
- 内容区域居中对齐;
- 左右两侧空白图标占位(实现对话气泡错位效果);
- 支持动态内容更新。
---
## 数据格式说明
### `output_view` 结构
用于定义模型输出的 UI 渲染模板,是一个标准的 `bricks.widget` 描述对象:
```js
{
widgettype: "Text",
options: {
text: "${content}",
wrap: true
}
}
```
支持变量插值(`${xxx}`),由 `bricks.apply_data(template, data)` 替换。
### `input_view` 结构
同上,用于渲染用户输入内容。
### 消息格式(`messages` 数组项)
```js
{
role: 'user' | 'system' | 'assistant',
content: String
}
```
可通过 `user_message_format``llm_message_format` 自定义格式。
---
## 事件与回调机制
| 事件名 | 触发条件 | 示例 |
|--------|----------|------|
| `record_click` | Cols 中某条记录被点击 | 添加新模型 |
| `submit` | Form 提交 | 处理用户输入 |
| `click` | SVG 按钮点击 | 打开输入窗、添加模型 |
| 自定义绑定 | 如 `likew.bind('click', ...)` | 提交评分 |
---
## 扩展功能
### ✅ 流式响应支持
通过 `HttpResponseStream` 实现 SSE 或分块传输解码,实时更新输出。
### ✅ 文本转语音TTS
利用 `UpStreaming` 组件边生成边播放音频流,提升交互体验。
### ✅ 用户反馈评分
提供“点赞/踩”按钮,收集用户对模型输出质量的反馈。
### ✅ 多模型共存
支持同时运行多个模型,各自独立维护上下文。
### ✅ 模板化渲染
使用 `apply_data()` 实现数据驱动的 UI 渲染,灵活支持 Markdown、代码块等复杂内容。
---
## 使用示例
```js
var llmio = new bricks.LlmIO({
user_icon: '/icons/user.png',
list_models_url: '/api/models/list',
input_fields: [
{ name: 'prompt', type: 'textarea', label: '请输入问题' }
],
input_view: {
widgettype: 'Text',
options: { text: '${prompt}', css: 'user-input' }
},
estimate_url: '/api/feedback/rate',
tts_url: '/api/tts/stream',
models: [{
model: 'gpt-3.5-turbo',
icon: '/icons/gpt.svg',
url: '/api/llm/chat',
output_view: {
widgettype: 'Markdown',
options: { source: '${content}' }
},
response_mode: 'stream',
input_from: 'userinput',
use_session: true
}]
});
document.body.appendChild(llmio.dom);
```
---
## 依赖与注册
```js
bricks.Factory.register('LlmIO', bricks.LlmIO);
```
确保该组件可通过工厂方式动态创建:
```js
bricks.widgetBuild({ widgettype: 'LlmIO', options: {...} });
```
---
## 已知问题与改进建议
| 问题 | 建议修复 |
|------|---------|
| `defautl_icon` 拼写错误 → 应为 `default_icon` | 修正拼写 |
| `opts``model_inputed` 中引用错误(应为 `this.opts` | 修改为 `this.opts.use_session` |
| `ws` 成员未实际使用 | 删除或补充 WebSocket 支持逻辑 |
| `handle_input` 中注释掉 `await` 可能导致并发问题 | 建议保留 `await` 或明确使用异步调度 |
---
## 版本信息
- 编写日期2025年4月
- 框架版本:`bricks.js` (假设)
- 作者Auto-generated from source code
---
✅ **文档完**

347
docs/cn/wterm.md Normal file
View File

@ -0,0 +1,347 @@
# `bricks.Wterm` 技术文档
> 基于 xterm.js 的终端组件,通过 WebSocket 与后端交互,支持自动连接、心跳保活、终端尺寸自适应等功能。
---
## 概述
`bricks.Wterm` 是一个基于 [xterm.js](https://xtermjs.org/) 的前端终端控件类,继承自 `bricks.JsWidget`。它用于在网页中嵌入一个可交互的终端界面,并通过 WebSocket 连接到后端服务进行数据通信。
该组件主要功能包括:
- 终端渲染(使用 xterm.js
- WebSocket 通信管理
- 自动发送终端尺寸
- 心跳机制保活连接
- 键盘输入转发
- DOM 生命周期绑定(如页面显示/隐藏时自动处理连接)
---
## 依赖
- **xterm.js**:核心终端库
- **FitAddon for xterm.js**:用于自适应容器大小
- `bricks.js` 框架运行环境
- 全局函数 `schedule_once(fn, delay)`:用于延迟执行任务(类似 `setTimeout` 封装)
---
## 类定义
```js
class bricks.Wterm extends bricks.JsWidget
```
---
## 构造函数
### `constructor(opts)`
初始化终端组件。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `opts.ws_url` | `String` | WebSocket 服务器地址(必需) |
| `opts.ping_timeout` | `Number` | 心跳间隔时间(秒),默认为 `19` 秒 |
#### 示例
```js
const term = new bricks.Wterm({
ws_url: 'ws://localhost:8080/ws',
ping_timeout: 20
});
```
#### 内部逻辑
1. 调用父类构造函数。
2. 初始化 `socket``null`
3. 设置心跳超时时间。
4. 使用 `schedule_once` 延迟调用 `open()` 方法(避免同步阻塞)。
5. 绑定事件:
- `'domon'` → 当元素可见时触发 `send_term_size`
- `'domoff'` → 当元素不可见时触发 `destroy`
---
## 核心方法
### `async open()`
建立终端和 WebSocket 连接。
#### 流程说明
1. 创建 `Terminal` 实例并应用配置(包括宽高百分比等)。
2. 将终端挂载到 `this.dom_element`(由父类提供)。
3. 创建 WebSocket 连接至 `opts.ws_url`
4. 加载 `FitAddon` 插件以实现自适应布局。
5. 调整字体大小(依赖全局 `bricks.app.charsize`)。
6. 设置 WebSocket 回调:
- `onmessage`:接收服务器数据并写入终端
- `onclose`:清理心跳任务
- `onopen`:连接成功后发送初始尺寸并启动心跳
7. 监听键盘输入,转发至服务端。
8. 监听终端尺寸变化,自动同步。
9. 主动发送一次终端尺寸并聚焦。
#### 接收消息类型(来自服务端)
| 类型 | 数据结构 | 动作 |
|------|----------|------|
| `"heartbeat"` | `{ data: { type: "heartbeat" } }` | 打印日志“connection alive” |
| `"data"` | `{ data: { type: "data", data: "..." } }` | 调用 `term.write(...)` 显示内容 |
| 其他 | 任意 | 打印原始消息内容 |
#### 发送消息类型(至服务端)
| 类型 | 数据格式 | 触发条件 |
|------|---------|----------|
| `"input"` | `{ type: "input", data: key }` | 用户按键输入 |
| `"resize"` | `{ type: "resize", width, height, rows, cols }` | 终端或窗口尺寸改变 |
| `"heartbeat"` | `{ type: "heartbeat" }` | 定期发送心跳 |
---
### `send_data(d)`
向服务器发送用户输入的数据。
#### 参数
| 参数 | 类型 | 说明 |
|------|------|------|
| `d` | `String` | 键盘输入字符 |
#### 示例
```js
this.send_data('hello');
```
#### 实现
```js
this.socket.send(JSON.stringify({ type: "input", data: d }));
```
---
### `send_term_size()`
向服务器发送当前终端的尺寸信息。
#### 发送内容
```json
{
"type": "resize",
"width": <容器宽度>,
"height": <容器高度>,
"rows": <行数>,
"cols": <列数>
}
```
#### 注意事项
- 若 WebSocket 尚未就绪(`readyState !== 1`),会捕获异常并打印 `'ws not ready'`
- 在 `open()` 中被调用两次:首次连接 + 终端初始化后。
---
### `send_heartbeat()`
发送心跳包以维持连接活跃。
#### 发送内容
```json
{ "type": "heartbeat" }
```
---
### `heartbeat()`
周期性执行的心跳函数。
#### 行为逻辑
1. 检查 WebSocket 是否处于打开状态(`readyState === 1`)。
2. 如果是,则调用 `send_heartbeat()`
3. 使用 `schedule_once` 调度下一次心跳(间隔为 `this.ping_timeout` 秒)。
> ⚠️ 此方法采用递归调度方式实现定时循环。
---
### `charsize_sizing()`
根据全局字体设置调整终端字号。
#### 依赖
```js
var cs = bricks.app.charsize; // 假设为数字(例如 14
```
#### 实现
```js
this.term.setOption('fontSize', cs);
```
---
### `term_resize()`
响应外部容器尺寸变化的回调函数。
#### 功能
调用 `fitAddon.fit()` 让终端自适应新尺寸。
> 注释掉 `this.send_term_size()` 可能是为了防止重复发送(已在 xterm 的 `onResize` 中处理)。
---
## 资源释放与销毁
### `close_websocket()`
关闭并清理 WebSocket 连接。
#### 清理项
- 调用 `close(1000, 'close now')`
- 解除所有事件监听器
- 将 `this.socket` 设为 `null`
#### 异常处理
捕获任何错误并重置 `socket` 引用,确保不会残留无效引用。
---
### `close_terminal()`
关闭并销毁 xterm 终端实例。
#### 清理项
- 调用 `fitAddon.dispose()`
- 调用 `term.dispose()`
- 将 `this.term` 设为 `null`
---
### `destroy()`
完整销毁组件资源(响应 `'domoff'` 事件)。
#### 执行步骤
1. 打印调试日志
2. 取消心跳任务(如果存在)
3. 解绑 `element_resize` 事件
4. 关闭 WebSocket如有
5. 销毁终端实例(如有)
#### 日志输出示例
```text
------domoff event, destory this widget
---1--domoff event, destory this widget
---2--domoff event, destory this widget
---3--domoff event, destory this widget
```
---
## 事件绑定
| 事件名 | 触发时机 | 处理函数 |
|--------|---------|---------|
| `'domon'` | DOM 元素变为可见 | `send_term_size()` |
| `'domoff'` | DOM 元素隐藏或移除 | `destroy()` |
| `'element_resize'` | 容器尺寸变化 | `term_resize()` |
> 使用 `this.bind(event, handler)``this.unbind(...)` 管理事件。
---
## 静态注册
```js
bricks.Factory.register('Wterm', bricks.Wterm);
```
允许通过工厂模式创建实例:
```js
bricks.Factory.create('Wterm', options);
```
---
## 使用示例
```html
<div id="terminal-container" style="width:100%; height:500px;"></div>
<script>
// 假设已加载 xterm.js 和 FitAddon
const terminalWidget = new bricks.Wterm({
ws_url: 'ws://your-terminal-server/ws/session-id',
ping_timeout: 20
});
// 挂载到 DOM
terminalWidget.render('#terminal-container');
</script>
```
---
## 注意事项
1. **必须引入 xterm.js 和 FitAddon**
```html
<script src="https://unpkg.com/xterm@5.x.x/lib/xterm.js"></script>
<script src="https://unpkg.com/xterm-addon-fit@0.x.x/lib/FitAddon.js"></script>
```
2. `schedule_once(fn, delay)` 需要全局可用(通常由框架提供)。
3. 字体大小依赖 `bricks.app.charsize`,请确保其存在。
4. WebSocket 服务需支持以下协议字段:
- `type: "input"` / `"resize"` / `"heartbeat"`
- 并能返回 `type: "data"``"heartbeat"` 消息。
---
## 版本兼容性
| 库名 | 推荐版本 |
|------|----------|
| xterm.js | `>= 4.0.0 < 6.0.0` |
| xterm-addon-fit | 对应版本 |
---
## 总结
`bricks.Wterm` 是一个功能完整的 Web 终端组件,适用于远程 shell、在线 IDE、调试工具等场景。其设计模块化、生命周期清晰结合 bricks 框架可轻松集成进复杂前端系统。
---
**建议扩展方向**
- 支持主题切换
- 添加复制粘贴快捷键拦截
- 增加连接失败重试机制
- 支持二进制数据传输(如图像)