低代码平台表单设计系统技术分析(实战三)

第三篇:拖拽功能与布局系统

前两篇我们分析了低代码平台表单设计系统的整体架构和组件体系,这一篇将深入探讨拖拽功能与布局系统的实现

1. 拖拽功能实现

该低代码平台使用 vuedraggable 库实现组件的拖拽功能,主要包括两个场景:

1.1 从左侧组件库拖拽到画布

ini 复制代码
<draggable
  :list="formDefine"
  :group="{ name: 'widget', pull: pullFuction, put: false }"
  item-key="id"
  :sort="false"
  @start="dragStart"
>
  <template #item="{ element }">
    <div class="item" @click="addComponent(element)" fill="currentColor">
      <span v-html="icons[element.type]" class="item-icon"></span>
      <span>{{ element.title }}</span>
    </div>
  </template>
</draggable>

核心实现:

  • 使用 vuedraggable 组件包装左侧组件列表
  • 配置 group 属性,设置拖拽组名为 "widget"
  • pull 函数控制拖拽行为,支持克隆模式
  • dragStart 事件处理拖拽开始时的逻辑
  • addComponent 方法处理点击添加组件的逻辑

1.2 画布内组件的拖拽排序

ini 复制代码
<draggable
  :list="props.formData.list"
  group="widget"
  item-key="id"
  @add="handleAdd"
>
  <template #item="{ element }">
    <FormDesignView
      @on-widget-select="widgetSelect(element)"
      :item="element"
      :chosenItem="currentItem"
      :formData="formData"
    ></FormDesignView>
  </template>
</draggable>

核心实现:

  • 使用 vuedraggable 包装画布内的组件列表
  • 配置 group 属性为 "widget",与左侧组件库保持一致
  • @add 事件处理组件添加到画布的逻辑
  • item-key 使用组件的 id 确保正确的DOM更新

1.3 拖拽逻辑处理

scss 复制代码
// 拖拽开始处理
const dragStart = (e) => {
  currentDragItem.value = formDefine[e.oldDraggableIndex]
  const currentType = currentDragItem.value.type
  currentTemplate(currentType)
  triggerScroll()
}

// 处理添加组件
const handleAdd = ({ newIndex }) => {
  const itemId = props.formData.list[newIndex].type + '_' + new Date().getTime()
  props.formData.list[newIndex] = {
    ...JSON.parse(JSON.stringify(props.formData.list[newIndex])),
    itemId,
    grid: props.formData.config?.colSpan || 24
  }
  currentItem.value = props.formData.list[newIndex]
}

拖拽处理特点:

  • 拖拽时克隆组件,而非移动原始组件
  • 为新添加的组件生成唯一的 id
  • 应用当前表单的布局配置
  • 自动选中新添加的组件
  • 触发右侧配置面板的更新

2. 布局系统设计

2.1 布局配置选项

表单布局通过 FormConfig 组件进行配置,支持多种布局方式:

ini 复制代码
<el-form-item label="表单布局" :label-position="itemLabelPosition">
  <el-select v-model="config.colSpan">
    <el-option
      v-for="item in layOutOptions"
      :key="item.key"
      :label="item.name"
      :value="item.colSpan"
    />
  </el-select>
</el-form-item>

布局选项:

vbnet 复制代码
const layOutOptions = [
  {
    key: 'single',
    name: '单列',
    colSpan: 24
  },
  {
    key: 'double',
    name: '双列',
    colSpan: 12
  },
  {
   ....省略
  }
]

2.2 标签对齐方式

支持三种标签对齐方式:

ini 复制代码
<el-form-item label="标签对齐方式" :label-position="itemLabelPosition">
  <el-radio-group
    v-model="config.labelPosition"
    aria-label="label position"
    @change="handleLabelPositionChange"
  >
    <el-radio-button value="left">左侧</el-radio-button>
    <el-radio-button value="right">右侧</el-radio-button>
    <el-radio-button value="top">顶部</el-radio-button>
  </el-radio-group>
</el-form-item>

2.3 标签宽度配置

ini 复制代码
<el-form-item label="标签宽度" :label-position="itemLabelPosition">
  <el-input-number
    v-model="config.labelWidth"
    :min="60"
    :max="500"
  >
    <template #suffix>
      <span>px</span>
    </template>
  </el-input-number>
</el-form-item>

2.4 组件级布局控制

每个组件可以单独设置宽度:

ini 复制代码
<el-form-item label="字段宽度" v-if="specialShow">
  <el-radio-group v-model="item.grid" class="field-width-wrapper">
    <el-radio-button :value="6" label="1/4" />
 
    <-- 省略-->

    <el-radio-button :value="24" label="整行" />
  </el-radio-group>
</el-form-item>

3. 布局渲染实现

3.1 响应式布局

使用 Element Plus 的栅格系统实现响应式布局:

xml 复制代码
<el-col :span="fixGridOptions.includes(item.type) ? 24 : finalGrid">
  <!-- 组件内容 -->
</el-col>

布局计算逻辑:

scss 复制代码
const { finalGrid, fixGridOptions } = useFormData()

// 监听组件属性和表单属性的布局变化
watch(
  () => props.formData.config?.colSpan,
  (newVal) => {
    finalGrid.value = newVal
    props.item.grid = newVal
  }
)

3.2 固定宽度组件

某些组件(如多标签页、分割线等)需要固定宽度:

arduino 复制代码
const fixGridOptions = [
  FORM_TYPE.MULTI_TAB,
  FORM_TYPE.SEPARATOR,
  // 其他需要固定宽度的组件
]

4. 拖拽与布局的交互

4.1 拖拽时的布局应用

当组件被拖拽到画布时,会自动应用当前表单的布局设置:

javascript 复制代码
const handleAdd = ({ newIndex }) => {
  // ...
  props.formData.list[newIndex] = {
    ...JSON.parse(JSON.stringify(props.formData.list[newIndex])),
    itemId,
    // 应用当前表单配置的布局
    grid: props.formData.config?.colSpan || 24
  }
  // ...
}

4.2 布局变更的实时响应

当表单布局发生变化时,所有组件会自动更新:

ini 复制代码
watch(
  () => props.formData.config?.colSpan,
  (newVal) => {
    finalGrid.value = newVal
    props.item.grid = newVal
  }
)

4.3 组件宽度的独立控制

组件可以覆盖表单的默认布局,设置自己的宽度:

javascript 复制代码
watch(
  () => props.item.grid,
  (newVal) => {
    finalGrid.value = newVal
  }
)

5. 技术亮点

  • 流畅的拖拽体验 :使用 vuedraggable 实现平滑的拖拽效果
  • 智能的布局应用 :拖拽时自动应用表单布局设置
  • 灵活的布局选项 :支持多种布局方式和标签对齐方式
  • 组件级布局控制 :每个组件可以单独设置宽度
  • 响应式设计 :基于 Element Plus 的栅格系统
  • 实时布局更新 :布局变更实时反映到所有组件
  • 固定宽度组件 :某些组件自动使用固定宽度

这种拖拽与布局系统的设计,大大简化了表单设计过程。用户可以通过直观的拖拽操作和灵活的布局配置,快速创建出表单。并且还有预览功能,直接在预览界面就可实时看到表单布局和试用数据填报。

下一篇预告 :《组件属性配置系统》,将详细分析组件属性配置的实现机制和设计思路。

相关推荐
牛奶2 小时前
ts随笔:面向对象与高级类型
前端·面试·typescript
牛奶2 小时前
React 基础理论 & API 使用
前端·react.js·面试
大漠_w3cpluscom2 小时前
别再死记CSS属性了!真正能让你少走半年弯路的,是这套思维
前端
兆子龙2 小时前
用 React + Remotion 做视频:入门与 AI 驱动生成
前端·架构
SuperEugene2 小时前
从 Vue2 到 Vue3:语法差异与迁移时最容易懵的点
前端·vue.js·面试
鼓浪屿2 小时前
vue3:组件中,v-model的区别(新版)
前端
Leon3 小时前
新手引导 intro.js 的使用
前端·javascript·vue.js
Zeros3 小时前
Claude Code 使用心得 - 从尝鲜到日常的进阶之路
前端
我是何平3 小时前
js中,什么是线性查找?
前端