bugfix
This commit is contained in:
parent
ebdb090e98
commit
53265bfa1d
205
docs/cn/accordion.md
Normal file
205
docs/cn/accordion.md
Normal 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
226
docs/cn/aggrid.md
Normal 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
220
docs/cn/asr.md
Normal 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
404
docs/cn/audio.md
Normal 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
211
docs/cn/bar.md
Normal 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
276
docs/cn/binstreaming.md
Normal 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
|
||||||
@ -1,135 +1,498 @@
|
|||||||
# widgetBuild
|
# Bricks.js 技术文档
|
||||||
函数用于创建bricks框架的控件实例。
|
|
||||||
|
> **Bricks.js** 是一个基于 JavaScript 的前端组件化框架,用于构建可扩展、模块化的 Web 应用程序。它支持动态组件加载、事件绑定、数据实时获取、弹窗管理、国际化(i18n)、媒体流控制等功能。
|
||||||
## 语法
|
|
||||||
var w = widgetBuild(desc, widget)
|
---
|
||||||
|
|
||||||
## 参数说明
|
## 目录
|
||||||
### desc
|
|
||||||
desc是一个对象类型数据,创建控件参数,必须有"widgettype", "options"属性, 可选属性有"subwidegets","binds"
|
- [1. 概述](#1-概述)
|
||||||
|
- [2. 核心对象 `bricks`](#2-核心对象-bricks)
|
||||||
### widget
|
- [3. 绑定动作(Bind Actions)](#3-绑定动作bind-actions)
|
||||||
widget是一个控件实例,用于解析desc中出现的url
|
- [通用属性](#通用属性)
|
||||||
|
- [各类型绑定的特定属性](#各类型绑定的特定属性)
|
||||||
## 返回值
|
- [4. 工具函数](#4-工具函数)
|
||||||
* null 出错,可能1)widgettype类型未注册或不存在;2)json文件格式不对
|
- [`uuid()`](#uuid)
|
||||||
* 新创建的控件实体
|
- [`deviceid(appname)`](#deviceidappname)
|
||||||
|
- [`str2data(s, d)`](#str2datas-d)
|
||||||
## 例子
|
- [`apply_data(desc, data)`](#apply_datadesc-data)
|
||||||
tree.json
|
- [5. 组件构建系统](#5-组件构建系统)
|
||||||
```
|
- [`widgetBuild(desc, widget, data)`](#widgetbuilddesc-widget-data)
|
||||||
{
|
- [`buildBind(w, desc)`](#buildbindw-desc)
|
||||||
"widgettype":"Tree",
|
- [`buildEventBind(from_widget, widget, event, desc)`](#buildeventbindfrom_widget-widget-event-desc)
|
||||||
"options":{
|
- [`universal_handler(from_widget, widget, desc, event)`](#universal_handlerfrom_widget-widget-desc-event)
|
||||||
"idField":"id",
|
- [6. 事件处理器生成器](#6-事件处理器生成器)
|
||||||
"textField":"text",
|
- [`buildEventHandler(w, desc, event)`](#buildeventhandlerw-desc-event)
|
||||||
"data":[
|
- [`getRealtimeData(w, desc)`](#getrealtimedataw-desc)
|
||||||
{
|
- [7. 各类 Handler 构造函数](#7-各类-handler-构造函数)
|
||||||
"id":1,
|
- [`buildNewWindowHandler`](#buildnewwindowhandler)
|
||||||
"text":"node1",
|
- [`buildUrlwidgetHandler`](#buildurlwidgethandler)
|
||||||
"is_leaf":false,
|
- [`buildBricksHandler`](#buildbrickshandler)
|
||||||
"children":[
|
- [`buildRegisterFunctionHandler`](#buildregisterfunctionhandler)
|
||||||
{
|
- [`buildMethodHandler`](#buildmethodhandler)
|
||||||
"id":11,
|
- [`buildScriptHandler`](#buildscripthandler)
|
||||||
"text":"node11",
|
- [`buildDispatchEventHandler`](#builddispatcheventhandler)
|
||||||
"is_leaf":false,
|
- [8. 辅助函数](#8-辅助函数)
|
||||||
"children":[
|
- [`getWidgetById(id, from_widget)`](#getwidgetbyidid-from_widget)
|
||||||
{
|
- [9. App 类 (`bricks.App`)](#9-app-类-bricksapp)
|
||||||
"id":112,
|
- [构造函数参数 `opts`](#构造函数参数-opts)
|
||||||
"text":"node.12",
|
- [主要方法](#主要方法)
|
||||||
"is_leaf":false,
|
|
||||||
"children":[
|
---
|
||||||
{
|
|
||||||
"id":1112,
|
## 1. 概述
|
||||||
"text":"node1112",
|
|
||||||
"is_leaf":true
|
`bricks` 是一个全局命名空间对象,封装了整个应用的核心逻辑:
|
||||||
},
|
|
||||||
{
|
```js
|
||||||
"id":1113,
|
var bricks = window.bricks || {};
|
||||||
"text":"node113",
|
bricks.app = null;
|
||||||
"is_leaf":true
|
```
|
||||||
}
|
|
||||||
]
|
所有功能均挂载在 `bricks` 对象下,包括:
|
||||||
},
|
- 组件工厂(Factory)
|
||||||
{
|
- 弹窗管理(Popup / PopupWindow)
|
||||||
"id":113,
|
- HTTP 请求客户端(HttpJson)
|
||||||
"text":"node113",
|
- 国际化支持(I18n)
|
||||||
"is_leaf":true
|
- 设备标识与会话管理
|
||||||
}
|
- 动态组件构建与事件绑定机制
|
||||||
]
|
|
||||||
},
|
---
|
||||||
{
|
|
||||||
"id":12,
|
## 2. 核心对象 `bricks`
|
||||||
"text":"node12",
|
|
||||||
"is_leaf":true
|
| 属性/方法 | 类型 | 描述 |
|
||||||
},
|
|------------------|-----------|------|
|
||||||
{
|
| `app` | App 实例 | 当前应用主实例 |
|
||||||
"id":13,
|
| `Body` | DOM 元素包装 | 页面 body 封装 |
|
||||||
"text":"node13",
|
| `bug` / `debug` | Boolean | 是否开启调试模式 |
|
||||||
"is_leaf":true
|
| `Factory` | Class | 组件注册与创建工厂 |
|
||||||
}
|
| `RF` | RegisterFunction | 注册函数管理器 |
|
||||||
]
|
|
||||||
},
|
---
|
||||||
{
|
|
||||||
"id":2,
|
## 3. 绑定动作(Bind Actions)
|
||||||
"text":"node2",
|
|
||||||
"is_leaf":false,
|
通过 `desc.binds` 数组定义用户交互行为,每个绑定描述符包含以下结构。
|
||||||
"children":[
|
|
||||||
{
|
### 通用属性
|
||||||
"id":21,
|
|
||||||
"text":"node21",
|
所有绑定动作都具备以下字段:
|
||||||
"is_leaf":true
|
|
||||||
},
|
| 字段名 | 类型 | 必填 | 描述 |
|
||||||
{
|
|------------------|----------|------|------|
|
||||||
"id":22,
|
| `actiontype` | String | ✅ | 动作类型:`bricks`, `urlwidget`, `method`, `script`, `registerfunction`, `event`, `newwindow` |
|
||||||
"text":"node22",
|
| `wid` | String | ✅ | 触发事件的组件 ID |
|
||||||
"is_leaf":true
|
| `event` | String | ✅ | 监听的事件名称,如 `'click'` |
|
||||||
},
|
| `target` | String 或 Widget | ✅ | 执行目标组件或特殊值(如 `'Popup'`) |
|
||||||
{
|
| `datawidget` | String | ❌ | 获取运行时数据的源组件 ID |
|
||||||
"id":23,
|
| `datamethod` | String | ❌ | 数据获取方法,默认 `'getValue'` |
|
||||||
"text":"node23",
|
| `dataparams` | Object | ❌ | 调用 `datamethod` 的参数 |
|
||||||
"is_leaf":true
|
| `datascript` | String | ❌ | 自定义脚本获取数据 |
|
||||||
}
|
| `rtdata` | Object | ❌ | 静态运行时数据 |
|
||||||
]
|
| `conform` | Object | ❌ | 确认对话框配置,在执行前弹出确认窗口 |
|
||||||
},
|
|
||||||
{
|
---
|
||||||
"id":3,
|
|
||||||
"text":"node3",
|
### 各类型绑定的特定属性
|
||||||
"is_leaf":true
|
|
||||||
}
|
#### `urlwidget` action
|
||||||
]
|
|
||||||
}
|
从远程 URL 加载组件描述并渲染。
|
||||||
}
|
|
||||||
```
|
| 属性 | 类型 | 描述 |
|
||||||
tree.html
|
|--------|---------|------|
|
||||||
```
|
| `mode` | String | `'replace'`, `'insert'`, `'append'`,默认 `'replace'` |
|
||||||
<html>
|
| `options.method` | String | HTTP 方法,默认 `'GET'` |
|
||||||
<head>
|
| `options.params` | Object | 请求参数 |
|
||||||
<link rel="stylesheet" href="/bricks/css/bricks.css" />
|
| `options.url` | String | 远程组件 JSON 地址 |
|
||||||
</head>
|
|
||||||
<body>
|
#### `bricks` action
|
||||||
<script src="/bricks/bricks.js"></script>
|
|
||||||
<script>
|
本地创建 Bricks 组件。
|
||||||
const opts =
|
|
||||||
{
|
| 属性 | 类型 | 描述 |
|
||||||
"widget": {
|
|--------|---------|------|
|
||||||
"widgettype":"urlwidget",
|
| `mode` | String | 同上 |
|
||||||
"options":{
|
| `options.widgettype` | String | 组件类型,必须已注册 |
|
||||||
"url":"tree.json"
|
| `options.*` | Any | 组件初始化选项 |
|
||||||
}
|
|
||||||
}
|
#### `method` action
|
||||||
}
|
|
||||||
|
调用目标组件的方法。
|
||||||
;
|
|
||||||
const app = new BricksApp(opts);
|
| 属性 | 类型 | 描述 |
|
||||||
app.run();
|
|---------|----------|------|
|
||||||
</script>
|
| `method` | String | 方法名 |
|
||||||
</body>
|
| `params` | Object | 方法参数(kwargs) |
|
||||||
</html>
|
|
||||||
|
#### `script` action
|
||||||
```
|
|
||||||
这是一个树形控件, 运行[这里](https://github.com/bricks/examples/tree.html)
|
执行内联脚本。
|
||||||
更多的例子请看
|
|
||||||
[bricks控件例子](https://github.com/yumoqing/bricks/examples)
|
| 属性 | 类型 | 描述 |
|
||||||
|
|---------|----------|------|
|
||||||
|
| `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)`
|
||||||
|
|
||||||
|
模板字符串解析函数,支持变量插值和类型转换。
|
||||||
|
|
||||||
|
**语法格式:**
|
||||||
|
|
||||||
|
```
|
||||||
|
'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日
|
||||||
@ -60,4 +60,3 @@ bricks采用控件这一概念来描述web GUI的显示部件,每个控件均
|
|||||||
ui文件可以直接调试,如在服务器根目录下的test目录下有一个hello.ui文件,
|
ui文件可以直接调试,如在服务器根目录下的test目录下有一个hello.ui文件,
|
||||||
就可以在浏览器中用url:https://sername/test/hello.ui调试
|
就可以在浏览器中用url:https://sername/test/hello.ui调试
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
254
docs/cn/button.md
Normal file
254
docs/cn/button.md
Normal 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
355
docs/cn/camera.md
Normal 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 URL(base64 编码的 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
323
docs/cn/cols.md
Normal 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
125
docs/cn/compoments.list
Normal 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
249
docs/cn/conform.md
Normal 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
314
docs/cn/continueaudio.md
Normal 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
284
docs/cn/countdown.md
Normal 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
88
docs/cn/css.md
Normal 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
434
docs/cn/datagrid.md
Normal 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
311
docs/cn/datarow.md
Normal 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
502
docs/cn/dataviewer.md
Normal 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
262
docs/cn/docxviewer.md
Normal 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
399
docs/cn/dynamicaccordion.md
Normal 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 (可选) | 启用编辑模式的相关配置 |
|
||||||
|
| `.add_icon`, `.update_icon`, `.delete_icon` | String | 图标路径或资源引用 |
|
||||||
|
| `.form_cheight` | Number | 表单高度系数 |
|
||||||
|
| `.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
173
docs/cn/dynamiccolumn.md
Normal 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
301
docs/cn/echartsext.md
Normal 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
162
docs/cn/factory.md
Normal 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
321
docs/cn/floaticonbar.md
Normal 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
353
docs/cn/form.md
Normal 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
283
docs/cn/gobang.md
Normal 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 | 横向坐标(列),取值范围 `1–15` |
|
||||||
|
| `p_y` | Number | 纵向坐标(行),取值范围 `1–15` |
|
||||||
|
|
||||||
|
> 注:坐标系以左上角为 `(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
123
docs/cn/html.md
Normal 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
283
docs/cn/i18n.md
Normal 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
218
docs/cn/iconbarpage.md
Normal 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
184
docs/cn/iframe.md
Normal 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
350
docs/cn/image.md
Normal 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()` 方法时,若图像来自跨域服务器且未开启 CORS,Canvas 将被污染导致无法导出数据。
|
||||||
|
2. **异步加载**:`base64()` 应在图像 `load` 事件完成后调用,否则结果不可靠。
|
||||||
|
3. **内存泄漏风险**:频繁调用 `base64()` 会创建大量临时 Canvas,建议缓存或节流。
|
||||||
|
4. **默认图像防循环**:确保 `default_url` 图像本身有效,避免 `onerror` 死循环。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本信息
|
||||||
|
|
||||||
|
- 框架:`bricks.js`
|
||||||
|
- 模块:`image.js`
|
||||||
|
- 最后更新:2025年4月5日
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ **建议使用场景**:
|
||||||
|
- 图标按钮、状态指示器、用户头像、动态图像预览等需要灵活控制图像行为的 UI 元素。
|
||||||
@ -27,7 +27,7 @@ bricks的开发理念是:应用开发可分为底层控件的开发以及操
|
|||||||
|
|
||||||
通过这样分工,让精通底层开发的人员专心开发底层控件,而精通操控控件的人员专心搭建应用,从而提高开发效率和开发质量,希望大家参与进来一起做。
|
通过这样分工,让精通底层开发的人员专心开发底层控件,而精通操控控件的人员专心搭建应用,从而提高开发效率和开发质量,希望大家参与进来一起做。
|
||||||
|
|
||||||
关于控件更多的信息,请看[控件](widgettypes.md)
|
关于控件更多的信息,请看[控件](widgets.md)
|
||||||
|
|
||||||
## 依赖
|
## 依赖
|
||||||
如果要使用Markdown,需要引用marked模块,
|
如果要使用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
621
docs/cn/input.md
Normal 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
391
docs/cn/jsoncall.md
Normal 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
126
docs/cn/keypress.md
Normal 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
359
docs/cn/layout.md
Normal 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
203
docs/cn/line.md
Normal 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
414
docs/cn/llm.md
Normal 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
366
docs/cn/llm_dialog.md
Normal 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()` |
|
||||||
|
| 📏 布局兼容 | 使用 Flexbox(HBox/VBox),确保父级容器设置正确尺寸 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 总结
|
||||||
|
|
||||||
|
本模块提供了完整的前端 LLM 聊天对话解决方案,具备以下特性:
|
||||||
|
|
||||||
|
✅ 多模型支持
|
||||||
|
✅ 流式 & 同步响应
|
||||||
|
✅ Markdown 渲染
|
||||||
|
✅ 自动滚动与加载动画
|
||||||
|
✅ 可扩展模板机制
|
||||||
|
|
||||||
|
适合集成进低代码平台、AI 助手界面、多模型对比测试工具等场景。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> 文档版本:1.0
|
||||||
|
> 最后更新:2025年4月5日
|
||||||
297
docs/cn/llmout.md
Normal file
297
docs/cn/llmout.md
Normal 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 中添加图片语法:`` |
|
||||||
|
| 其他 | 使用代码块包裹内容:`\`\`\`\nvalue\n\`\`\`` |
|
||||||
|
|
||||||
|
> **注意**:字段标签优先使用 `f.label`,若无则使用 `f.name`
|
||||||
|
|
||||||
|
#### 流程步骤
|
||||||
|
1. 初始化空字符串 `mdtext`
|
||||||
|
2. 遍历所有输入字段,拼接 Markdown 内容或创建媒体组件
|
||||||
|
3. 清除当前所有子组件(`clear_widgets()`)
|
||||||
|
4. 创建新的 `MdWidget` 显示 Markdown 内容
|
||||||
|
5. 添加音视频组件(如有)
|
||||||
|
|
||||||
|
#### 示例输出 Markdown
|
||||||
|
```markdown
|
||||||
|
* 用户提问
|
||||||
|
```
|
||||||
|
这是用户的输入文本
|
||||||
|
```
|
||||||
|
|
||||||
|
* 示例图片
|
||||||
|

|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 属性
|
||||||
|
| 属性 | 类型 | 描述 |
|
||||||
|
|------|------|------|
|
||||||
|
| `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
250
docs/cn/markdown_viewer.md
Normal 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
367
docs/cn/menu.md
Normal 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
218
docs/cn/message.md
Normal 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
314
docs/cn/miniform.md
Normal 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<Object> | ✅ | - | 字段定义数组,每个字段包含以下子属性 |
|
||||||
|
|
||||||
|
##### `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
295
docs/cn/modal.md
Normal 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日*
|
||||||
170
docs/cn/multiple_state_image.md
Normal file
170
docs/cn/multiple_state_image.md
Normal 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
117
docs/cn/myoperator.md
Normal 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
164
docs/cn/myvad.md
Normal 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 Web(VAD 依赖) -->
|
||||||
|
<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
317
docs/cn/page_data_loader.md
Normal 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
272
docs/cn/paging.md
Normal 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
322
docs/cn/period.md
Normal 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
257
docs/cn/pie.md
Normal 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
502
docs/cn/popup.md
Normal 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
150
docs/cn/progressbar.md
Normal 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
343
docs/cn/qaframe.md
Normal 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
410
docs/cn/recorder.md
Normal 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
183
docs/cn/registerfunction.md
Normal 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
435
docs/cn/rtc.md
Normal 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
198
docs/cn/running.md
Normal 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
293
docs/cn/scroll.md
Normal 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
139
docs/cn/splitter.md
Normal 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
240
docs/cn/streaming_audio.md
Normal 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
350
docs/cn/svg.md
Normal 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
334
docs/cn/tab.md
Normal 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
304
docs/cn/tabular.md
Normal 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
368
docs/cn/toolbar.md
Normal 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<Object> | `[]` | 工具项描述数组,每项包含图标、名称、标签等信息 |
|
||||||
|
| `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<Widget> | 存储所有已创建的工具按钮实例 |
|
||||||
|
| `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
356
docs/cn/tree.md
Normal 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
106
docs/cn/tree_choose.md
Normal 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
274
docs/cn/uitype.md
Normal 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
428
docs/cn/uitypesdef.md
Normal 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
787
docs/cn/utils.md
Normal 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`
|
||||||
|
|
||||||
|
#### 限制
|
||||||
|
- 需浏览器支持 MSE(Media 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
268
docs/cn/vadtext.md
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
# `VadText` 技术文档
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# bricks.VadText 模块技术文档
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
`bricks.VadText` 是一个基于 Web Audio 和语音活动检测(VAD)的交互式语音识别组件,用于实时采集用户语音、转换为 WAV 格式并发送至后端 ASR(自动语音识别)服务,最终将识别结果展示在界面上。该组件封装了音频录制、编码、网络请求和 UI 控件逻辑。
|
||||||
|
|
||||||
|
该模块依赖于全局对象 `bricks`,并使用了 VAD(Voice 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
292
docs/cn/video.md
Normal 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
398
docs/cn/videoplayer.md
Normal 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 需要 MSE(Media 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
176
docs/cn/views.md
Normal 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
90
docs/cn/vision.md
Normal 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
254
docs/cn/websocket.md
Normal 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
240
docs/cn/webspeech.md
Normal 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
499
docs/cn/widget.md
Normal 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 | 弹窗配置对象 |
|
||||||
|
| `.popup_event` | String | 触发弹窗的事件类型(如 `'click'`) |
|
||||||
|
| `.popup_desc` | WidgetDesc | 弹窗内容描述(用于构建内部组件) |
|
||||||
|
| `.popupwindow` | Boolean | 是否使用独立窗口模式弹窗 |
|
||||||
|
| `.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()`
|
||||||
|
请求进入全屏模式,兼容各浏览器 API(Fullscreen 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
|
||||||
@ -1,100 +1,207 @@
|
|||||||
# 控件列表
|
# bricks控件
|
||||||
Bricks内置许多的显示控件,所有显示控件都继承自JsWidget,容器控件Layout几就继承自JsWidget,其他的容器HBox, VBox继承自Layout
|
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
|
||||||
|
音频播放控件
|
||||||
|
|
||||||
# 控件清单
|
* [ChartBar](bar.md) bricks.ChartBar
|
||||||
* [Accordion](widgets/accordion.md):可折叠控件
|
将后台数据显示为条形图表
|
||||||
* [AudioPlayer](widgets/audioplayer.md):音频播放器
|
* [Button](button.md) bricks.Button
|
||||||
* [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)
|
|
||||||
|
|
||||||
* [JsWidget](widgets/jswidget.md): 祖先控件,对应于<div>元素
|
* [Cols](cols.md) bricks.Cols
|
||||||
* [Button](widgets/button.md):按钮控件
|
列式排列控件,可动态填满父控件的宽度
|
||||||
* [DataGrid](widgets/datagrid.md):数据表格控件
|
* [Conform](conform.md) bricks.Conform
|
||||||
* [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控件
|
|
||||||
|
|
||||||
#
|
* [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
427
docs/cn/wsllm.md
Normal 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
347
docs/cn/wterm.md
Normal 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 框架可轻松集成进复杂前端系统。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ **建议扩展方向**:
|
||||||
|
- 支持主题切换
|
||||||
|
- 添加复制粘贴快捷键拦截
|
||||||
|
- 增加连接失败重试机制
|
||||||
|
- 支持二进制数据传输(如图像)
|
||||||
Loading…
x
Reference in New Issue
Block a user