如何使用ngx-datatable创建带分页的行列分隔式摄像头马赛克墙
ngx-datatable 摄像头马赛克网格实现方案
核心逻辑
你需要实现的效果本质是打破ngx-datatable“一行对应一个业务对象、一列对应一个数据字段”的默认渲染规则,转为每行固定排列N个摄像头卡片、列与列之间明确分隔、自带分页能力的网格效果,核心实现路径:
- 先把一维存储的摄像头原始数据,按预设的每行列数做切片分组,生成对齐的网格行数据(不足列数的位置补空占位)
- 用ngx-datatable的自定义单元格模板,逐列渲染对应位置的摄像头卡片
- 复用ngx-datatable原生的分页能力,基于分组后的网格行数做分页计算
现有方案的问题定位
- 方案1缺陷:将每个摄像头单独作为一行数据,导致单元格位置错位,无法实现每行固定列数的马赛克排列。比如你给出的示例中第一个摄像头绑定
column1、第二个绑定column2,会渲染为第一行仅第一列有内容、第二行仅第二列有内容,行列完全错乱 - 方案2缺陷:硬编码合并摄像头到固定行结构,没有通用的分组适配逻辑,无法应对摄像头数量动态变化的场景,且未处理不足列数的空占位,也没有和ngx-datatable的列渲染规则对齐
具体实现代码
1. 组件TS核心逻辑
先定义全局网格配置,完成原始数据到网格行的转换,对接分页参数:
// 固定每行展示的摄像头列数,可根据需求调整为3/4列 readonly GRID_COLUMN_COUNT = 2; // 每页展示的网格行数,根据摄像头单页承载量调整 readonly PAGE_SIZE = 2; // 完整的网格行缓存(避免分页时重复计算) private fullGridRows = []; // 传给datatable的当前页行数据 gridRows = []; // 分页参数 totalRows = 0; currentPageOffset = 0; // 你的原始摄像头数据 rawCameraList = [ { name: "Pet shop", id: "1", video: 'https://xxx' }, { name: "E-shop", id: "2", video: 'https://xxx' }, { name: "My house", id: "3", url: 'https://xxx' }, { name: "Garage", id: "4", url: 'https://xxx' }, { name: "Front door", id: "5", url: 'https://xxx' } // 测试奇数数据的占位场景 ]; ngOnInit() { this.transformToGridRows(); // 初始化加载第一页数据 this.updatePageData(); } // 核心:把一维摄像头列表转为对齐的网格行 private transformToGridRows() { this.fullGridRows = []; // 按列数切片分组 for (let i = 0; i < this.rawCameraList.length; i += this.GRID_COLUMN_COUNT) { const currentRowCameras = this.rawCameraList.slice(i, i + this.GRID_COLUMN_COUNT); // 不足列数的补null占位,保证行列对齐 while (currentRowCameras.length < this.GRID_COLUMN_COUNT) { currentRowCameras.push(null); } // 转为datatable可识别的行结构,key对应网格列位置 this.fullGridRows.push({ col0: currentRowCameras[0], col1: currentRowCameras[1] }); } // 同步总数据量给分页组件 this.totalRows = this.fullGridRows.length; } // 分页切换时更新当前页的行数据 onPageChange(event) { this.currentPageOffset = event.offset; this.updatePageData(); } private updatePageData() { const start = this.currentPageOffset * this.PAGE_SIZE; const end = start + this.PAGE_SIZE; this.gridRows = this.fullGridRows.slice(start, end); }
2. 页面模板配置
直接在HTML中定义自定义列模板,渲染摄像头卡片,开启行列分隔和分页:
<ngx-datatable [rows]="gridRows" [columnMode]="'force'" [headerHeight]="0" <!-- 不需要表头可直接设为0隐藏 --> [rowHeight]="220" <!-- 匹配摄像头卡片的高度 --> [limit]="PAGE_SIZE" [count]="totalRows" [offset]="currentPageOffset" (page)="onPageChange($event)" [verticalSeparator]="true" <!-- 开启列之间的分隔线 --> [horizontalSeparator]="true" <!-- 开启行之间的分隔线 --> > <!-- 第一列摄像头模板 --> <ngx-datatable-column> <ng-template ngx-datatable-cell-template let-row="row"> <div *ngIf="row.col0" class="camera-card"> <div class="camera-title">{{ row.col0.name }}</div> <div class="camera-stream"> <!-- 这里替换为你的实际视频播放组件/标签 --> 视频地址:{{ row.col0.video || row.col0.url }} </div> </div> <div *ngIf="!row.col0" class="empty-cell"></div> </ng-template> </ngx-datatable-column> <!-- 第二列摄像头模板 --> <ngx-datatable-column> <ng-template ngx-datatable-cell-template let-row="row"> <div *ngIf="row.col1" class="camera-card"> <div class="camera-title">{{ row.col1.name }}</div> <div class="camera-stream"> 视频地址:{{ row.col1.video || row.col1.url }} </div> </div> <div *ngIf="!row.col1" class="empty-cell"></div> </ng-template> </ngx-datatable-column> </ngx-datatable>
3. 样式实现(强化行列分隔效果)
/* 给datatable整体加外边框,强化网格边界 */ .ngx-datatable { border: 1px solid #e5e7eb; border-radius: 4px; } /* 摄像头卡片样式 */ .camera-card { width: 100%; height: 100%; background: #fff; display: flex; flex-direction: column; } .camera-title { height: 36px; padding: 0 12px; line-height: 36px; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e7eb; background: #f9fafb; } .camera-stream { flex: 1; display: flex; align-items: center; justify-content: center; color: #9ca3af; background: #111827; } /* 空单元格占位 */ .empty-cell { width: 100%; height: 100%; background: #f3f4f6; }
通用适配说明
- 如果需要调整为每行3/4列的网格效果,仅需修改
GRID_COLUMN_COUNT的值,对应增加<ngx-datatable-column>模板、在transformToGridRows方法里补充col2/col3的映射即可,无需调整核心逻辑 - 分页能力完全复用ngx-datatable原生能力,支持页码切换、每页数量调整等内置功能,无需额外开发分页组件
- 摄像头数量为奇数时,自动在最后一行补空单元格占位,不会出现单元格错位、边框断开的问题
内容的提问来源于stack exchange,提问作者Edson Junior
相关产品推荐
相关产品推荐

