快点阅读免费版
65.01MB · 2025-09-27
在前两篇博客 动态表单(一)、动态表单(二) 中,我们深入探讨了动态表单的基础实现和表单项的动态控制。本文将在此基础上,为动态表单增加分栏布局能力,让表单呈现更加专业的视觉效果,提升用户体验。
实现分栏布局的核心在于数据结构的巧妙转换——将表单项的一维数组转换为二维数组。这个二维数组的每个子数组代表表单的一行,子数组内的元素则代表该行中的各个表单项。这种转换使得我们可以利用Element Plus的栅格系统来实现灵活布局。
我们在表单项配置中引入两个新属性:
group
- 定义表单项所属的分栏(默认值为1),1表示左栏,2表示右栏span
- 定义表单项需要合并的列数(默认值为1),大于1时表示需要跨栏考虑到只需处理单栏和双栏情况,我们采用双指针算法来高效构建布局二维数组。这种方法的时间复杂度为O(n),在处理大规模表单项时仍能保持高效性能。
算法执行步骤如下:
group
属性分为左栏(group=1)和右栏(group=2)双指针处理是整个算法的核心环节,其执行逻辑可以通过以下步骤详细说明:
指针初始化与遍历 我们使用两个指针i和j分别指向左栏数组和右栏数组的当前位置。这两个指针同步移动,但移动规则取决于当前处理项的特性。
处理逻辑决策树
左栏项需要跨栏合并(span ≥ 2)
左栏项不需要合并(span = 1或未设置)
特殊情况处理
可视化执行过程 可以将双指针的处理过程想象成两个并行的传送带,每个表单项就像传送带上的物品。算法负责决定哪些物品应该放在同一个包装盒(行)中:
这种机制确保了:
通过这种精细的控制逻辑,双指针算法能够高效地将一维表单项数组转换为适合分栏显示的二维布局数组,为后续的界面渲染提供结构化数据。
const left = currentDisplayFormItems.value.filter(
(item) => !item.group || item.group === 1
);
const right = currentDisplayFormItems.value.filter((item) => item.group >= 2);
if (!right.length) {
return left.map((item) => [item]);
}
const array = [];
let i = 0,
j = 0;
for (; i < left.length && j < right.length;) {
if (!left[i].span || left[i].span === 1) {
if (right[j]) {
array.push([left[i], right[j]]);
i++;
j++;
} else {
array.push([left[i]]);
i++;
}
} else if (left[i].span >= 2) {
array.push([left[i]]);
i++;
}
}
if (i === left.length && j < right.length) {
for (; j < right.length; j++) {
array.push([right[j]]);
}
} else if (j === right.length && i < left.length) {
for (; i < left.length; i++) {
array.push([left[i]]);
}
}
return array;
<el-form class="dynamic-form" :readonly="!极狐isEdit" :model="currentModel" :key="formKey">
<el-row :gutter="24" v-for="(item, index) of displayedFormItemMatrix" :key="index">
<el-col :span="24 / (item.length ?? 1)" v-for="col of item" :key="col.prop">
<el-form-item :prop="col.prop" :label="col.label">
<DynamicFormItem :is-edit="isEdit" :data="data" v-model:model="currentModel" :item="col">
</DynamicFormItem>
</el-form-item>
</el-col>
</el-row>
</el-form>
假设我们有以下表单项配置:
const formItems = [
{prop: "name", label: "姓名", span: 2}, // 需要合并栏
{prop: "age", label: "年龄"}, // 普通项
{prop: "street", label: "街道", group: 2}, // 右栏项
{prop: "city", label: "城市", group: 2} // 右栏项
];
步骤1:初始化
步骤2:第一轮处理(i=0, j=0)
步骤3:第二轮处理(i=1, j=0)
步骤4:处理剩余项
初始状态:
左栏: [姓名(span=2), 年龄]
右栏: [街道, 城市]
指针: i=0, j=0
结果: []
处理过程:
1. 处理左栏[0]: 姓名需要合并栏 → 单独成行
结果: [[姓名]]
指针: i=1, j=0
2. 处理左栏[1]: 年龄不需要合并栏 → 与右栏[0]组合
结果: [[姓名], [年龄, 街道]]
指针: i=2, j=1
3. 左栏处理完毕,处理右栏剩余项
结果: [[姓名], [年龄, 街道], [城市]]
html代码
<my-dynamic-form :is-edit="true" :data="newModel" v-model:model="newModel" :form-items="newFormItems"
@update:model="changeFn"></my-dynamic-form>
我们通过三种典型场景验证分栏功能的正确性:
当所有表单项均未设置分组时,表单保持原有的单栏布局,所有表单项垂直排列,占满整个宽度。这种布局适合内容较少或需要突出单个表单项的场景。
测试代码
const newFormItems = reactive([
{
prop: "name",
label: "姓名",
// span: 2,
edit: {
type: "text",
},
},
{
prop: "partition",
label: "分区",
edit: {
type: "select",
options: [
{ value: "one", label: "分区一" },
{ value: "two", label: "分区二" },
],
},
},
{
prop: "street",
label: "街道",
edit: {
type: "select",
options: [
{ value: "one", label: "街道一" },
{ value: "two", label: "街道二" },
],
},
},
{
prop: "age",
label: "年龄",
edit: {
type: "number",
},
},
{
prop: "address",
label: "详细地址",
edit: {
type: "textarea",
},
},
]);
效果
通过为部分表单项设置group: 2
,实现标准的双栏布局,表单项按左右两栏排列。这种布局有效利用水平空间,适合中等复杂度的表单。
测试代码
const newFormItems = reactive([
{
prop: "name",
label: "姓名",
edit: {
type: "text",
},
},
{
prop: "partition",
label: "分区",
edit: {
type: "select",
options: [
{ value: "one", label: "分区一" },
{ value: "two", label: "分区二" },
],
},
},
{
prop: "street",
label: "街道",
group: 2,
edit: {
type: "select",
options: [
{ value: "one", label: "街道一" },
{ value: "two", label: "街道二" },
],
},
},
{
prop: "age",
label: "年龄",
edit: {
type: "number",
},
},
{
prop: "address",
label: "详细地址",
edit: {
type: "textarea",
},
},
]);
效果
通过span
属性实现表单项的跨栏合并,特定表单项可以横跨两栏显示。这种布局适合需要突出重要信息或处理长文本输入的场景。
测试代码
const newFormItems = reactive([
{
prop: "name",
label: "姓名",
span: 2,
edit: {
type: "text",
},
},
{
prop: "partition",
label: "分区",
edit: {
type: "select",
options: [
{ value: "one", label: "分区一" },
{ value: "two", label: "分区二" },
],
},
},
{
prop: "street",
label: "街道",
group: 2,
edit: {
type: "select",
options: [
{ value: "one", label: "街道一" },
{ value: "two", label: "街道二" },
],
},
},
{
prop: "age",
label: "年龄",
edit: {
type: "number",
},
},
{
prop: "address",
label: "详细地址",
span: 2,
edit: {
type: "textarea",
},
},
]);
效果
通过本文介绍的双指针算法,我们成功为动态表单添加了灵活的分栏布局能力。这种实现方式具有以下优势:
这种分栏设计方案不仅提升了表单的视觉效果,也为用户提供了更加清晰的信息组织结构,极大提升了表单填写体验。双指针算法的应用展示了如何通过简洁的代码实现复杂的布局逻辑,是前端开发中算法思维的典型应用。
希望本文对您实现动态表单的分栏布局有所帮助。如有任何问题或建议,欢迎在评论区交流讨论。
高通第六代骁龙 8 至尊版芯片踪迹曝光:代号 SM8950,小米 18 系列手机首发
【保姆级教程-从0开始开发MCP服务器】二、使用ClaudeCode连接第一个MCP服务器