企业工作台模块化布局方案

需求

  • 可自由拖动模块的位置、设置模块的宽度
  • 可添加/移除模块
  • 每一行的模块高度相等
  • 部分模块有最大和最小宽度限制
  • 当前用户无配置时使用默认模块布局

选型

使用 react-grid-layout 管理模块布局。

实现方案

栅格化布局

将内容区域宽度划分为 12 格,高度以 100px 为一格,模块的宽高以格为单位,假设内容区宽度为 1080px,那么 4x4 尺寸的模块在 react-grid-layout 中的效果就是 360px x 400px。

行内元素等高

由于 react-grid-layout 中的布局是基于绝对定位(position: absolute)和变换(trasnform: translate(x, y))实现的,无法使用 block 和 flex 布局特性,若基于它实现行内模块等高的效果的话会比较复杂。

为了简化 react-grid-layout 相关代码的复杂度,目前可行的解决方法是将页面内容布局模式分为普通和编辑:

  • 只读模式: 读取模块化配置,根据各个模块的几何属性(x、y、w、h)进行排版然后渲染到页面中,利用 flex 布局实现行内模块等高特性
  • 编辑模式: 转换模块化配置然后交给 react-grid-layout 处理

这两种模式的布局代码可以划分到 DashboardFlowLayout 和 DashboardGridLayout 组件中,在切换到编辑模式时,获取 DashboardFlowLayout 组件中所有模块的实际高度并传给 DashboardGridLayout 组件,每当模块布局变化时,DashboardGridLayout 组件根据模块的实际高度重新排版,然后将行内模块最大高度作为行高并应用到所有模块。

排版的效果与浏览器渲染机制中的 reflow (回流) 相似,为了方便理解和引用相关资料,在实际代码中实现排版这一操作的函数已命名为 reflow,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function reflow(elements, maxWidth) {
let row = null;
const rows = [];

elements
.slice()
.sort((a, b) => (a.y * maxWidth + a.x) - (b.y * maxWidth + b.x))
.forEach((el) => {
if (!row || el.y >= row.y + row.height || row.width + el.width > maxWidth) {
row = {
y: row ? row.y + row.height : 0,
width: el.width,
height: el.height,
children: [el]
};
rows.push(row);
return;
}
row.height = Math.max(el.y - row.y + el.height, row.height);
row.children.push(el);
});

return rows;
}

只读模式下的布局还原

编辑模式下的布局是基于 react-grid-layout 实现的,能够用 xy 灵活的控制模块位置,那么在基于 flex 布局的只读模式下如何还原模块的位置?

较为稳妥的做法是用空 div 元素填充模块之间的空白,空白 div 的宽度等于当前模块的 x 与上个模块的 x + width 的差值,即:width = el.x - (prevEl.x + prevEl.width)

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×