# `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