低代码研学笔记之 VForm(四)

原创
2024/12/12 09:47
阅读数 60

上一篇分析了设计器的页面布局,今天更近一步研学表单设计器核心:组件、容器实现原理

低代码研学笔记之 VForm(四)  WidgetPanel 左边栏的可拖拽组件

低代码研学笔记之 VForm(五)  VFormWidget 设计器容器组件

低代码研学笔记之 VForm(六)   SettingPanel 组件属性和表单属性设计组件 

低代码研学笔记之 VForm(七)    ToolbarPanel 设计器顶部的工具栏组件

基础知识储备:

vue框架、elementui框架、vuedraggable插件。

学习关键字

vuedraggable、components、props、inject

Vue.Draggable是一款基于Sortable.js实现的vue拖拽插件。支持移动设备、拖拽和选择文本、智能滚动,可以在不同列表间拖拽、不依赖jQuery为基础、vue 2过渡动画兼容、支持撤销操作,总之是一款非常优秀的vue拖拽组件。本篇将介绍如何搭建环境及简单的例子,使用起来特别简单对被拖拽元素也没有CSS样式的特殊要求。

components: vue组件引用

props: 父级组件给子级组件传参定义

inject: 一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。

组件布局分析

<template>
  <el-scrollbar class="side-scroll-bar" :style="{height: scrollerHeight}">
    <div class="panel-container">
    <!--  el标签页布局,划分组件库和模板库-->
    <el-tabs v-model="firstTab" class="no-bottom-margin indent-left-margin">
      <!-- 组件库-->
      <el-tab-pane name="componentLib">
        <span slot="label"><i class="el-icon-set-up"></i> {{i18nt('designer.componentLib')}}</span>
        <!-- el布局组件手风琴-->
        <el-collapse v-model="activeNames" class="widget-collapse">
          <el-collapse-item name="1" :title="i18nt('designer.containerTitle')">
            <!--            初始化容器组件-->
            <draggable tag="ul" :list="containers" :group="{name: 'dragGroup', pull: 'clone', put: false}"
                       :clone="handleContainerWidgetClone" ghost-class="ghost" :sort="false"
                       :move="checkContainerMove" @end="onContainerDragEnd">
              <li v-for="(ctn, index) in containers" :key="index" class="container-widget-item" :title="ctn.displayName"
                  @dblclick="addContainerByDbClick(ctn)">
                <span><svg-icon :icon-class="ctn.icon" class-name="color-svg-icon" />{{i18n2t(`designer.widgetLabel.${ctn.type}`, `extension.widgetLabel.${ctn.type}`)}}</span>
              </li>
            </draggable>
          </el-collapse-item>

          <!--          初始化基础表单元素组件,input, selet、radio等组件-->
          <el-collapse-item name="2" :title="i18nt('designer.basicFieldTitle')">
            <draggable tag="ul" :list="basicFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
                       :move="checkFieldMove"
                       :clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
              <li v-for="(fld, index) in basicFields" :key="index" class="field-widget-item" :title="fld.displayName"
                  @dblclick="addFieldByDbClick(fld)">
                <span><svg-icon :icon-class="fld.icon" class-name="color-svg-icon" />{{i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)}}</span>
              </li>
            </draggable>
          </el-collapse-item>

          <!--          初始化高级表单组件,如上传、图片展示等组件-->
          <el-collapse-item name="3" :title="i18nt('designer.advancedFieldTitle')">
            <draggable tag="ul" :list="advancedFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
                       :move="checkFieldMove"
                       :clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
              <li v-for="(fld, index) in advancedFields" :key="index" class="field-widget-item" :title="fld.displayName"
                  @dblclick="addFieldByDbClick(fld)">
                <span><svg-icon :icon-class="fld.icon" class-name="color-svg-icon" />{{i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)}}</span>
              </li>
            </draggable>
          </el-collapse-item>

          <!-- 初始化用户自定义组件 -->
          <el-collapse-item name="4" :title="i18nt('designer.customFieldTitle')">
            <draggable tag="ul" :list="customFields" :group="{name: 'dragGroup', pull: 'clone', put: false}"
                       :move="checkFieldMove"
                       :clone="handleFieldWidgetClone" ghost-class="ghost" :sort="false">
              <li v-for="(fld, index) in customFields" :key="index" class="field-widget-item" :title="fld.displayName"
                  @dblclick="addFieldByDbClick(fld)">
                <span>
                  <svg-icon :icon-class="fld.icon" class-name="color-svg-icon" />{{i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)}}</span>
              </li>
            </draggable>
          </el-collapse-item>
        </el-collapse>
      </el-tab-pane>

      <!-- 模板库-->
      <el-tab-pane v-if="showFormTemplates()" name="formLib" style="padding: 8px">
        <span slot="label"><i class="el-icon-c-scale-to-original"></i> {{i18nt('designer.formLib')}}</span>

        <template v-for="(ft, idx) in formTemplates">
          <el-card :key="idx" :bord-style="{ padding: '0' }" shadow="hover" class="ft-card">
            <el-popover placement="right" trigger="hover">
              <img slot="reference" :src="ft.imgUrl" style="width: 200px">
              <img :src="ft.imgUrl" style="height: 600px;width: 720px">
            </el-popover>
            <div class="bottom clear-fix">
              <span class="ft-title">#{{idx+1}} {{ft.title}}</span>
              <el-button type="text" class="right-button" @click="loadFormTemplate(ft.jsonUrl)">
                {{i18nt('designer.hint.loadFormTemplate')}}</el-button>
            </div>
          </el-card>
        </template>
      </el-tab-pane>

    </el-tabs>

    </div>
  </el-scrollbar>
</template>

  依赖库分析

vue拖拽组件库;容器、表单组件、自定义组件初始化定义;新增窗口缩放处理方法;多语言库;异步请求处理库;Svg图标库;

<script>
  import Draggable from 'vuedraggable'
  //组件定义的核心配置文件
  import {containers, basicFields, advancedFields, customFields} from "./widgetsConfig"
  import {formTemplates} from './templatesConfig'
  import {addWindowResizeHandler} from "@/utils/util"
  import i18n from "@/utils/i18n"
  import axios from "axios"
  import SvgIcon from '@/components/svg-icon'
</script>

组件定义的核心配置文件,为什么是核心呢?因为左边栏的组件列表均是基于此配置数据来渲染的,与数据相对应的还有element-ui组件;vant-ui组件等;
import {containers, basicFields, advancedFields, customFields} from "./widgetsConfig"

src/components/form-designer/widget-panel/widgetsConfig.js

[
  {
    type: 'grid',          //组件类型,宫格布局
    category: 'container', //组件类别,容器
    icon: 'grid',          //图标
    cols: [],              //列
    options: {             // >>> 组件属性设置,是要复制属性应用到新组件的。
      name: '',            //组件名称,即变量名
      hidden: false,       //是否隐藏
      gutter: 12,          //栅格间隔
      colHeight: null,     //栅格列统一高度属性,用于解决栅格列设置响应式布局浮动后被挂住的问题!!
      customClass: '',     //自定义css类名
    }
  },
  ...此处省略5个
]

表单设计器属性和方法定义

export default {
    name: "FieldPanel",
    mixins: [i18n],
    components: {
      Draggable,
      SvgIcon,
    },
    props: {
      designer: Object,
    },
    inject: ['getBannedWidgets', 'getDesignerConfig'],
    data() {
      return {
        designerConfig: this.getDesignerConfig(),

        firstTab: 'componentLib',

        scrollerHeight: 0,

        activeNames: ['1', '2', '3', '4'],

        containers,
        basicFields,
        advancedFields,
        customFields,

        formTemplates: formTemplates,
        // ftImages: [
        //   {imgUrl: ftImg1},
        //   {imgUrl: ftImg2},
        //   {imgUrl: ftImg3},
        //   {imgUrl: ftImg4},
        //   {imgUrl: ftImg5},
        //   {imgUrl: ftImg6},
        //   {imgUrl: ftImg7},
        //   {imgUrl: ftImg8},
        // ]
      }
    },
    computed: {
      //
    },
    mounted() {
      this.loadWidgets()

      this.scrollerHeight = window.innerHeight - 56 + 'px'
      addWindowResizeHandler(() => {
        this.$nextTick(() => {
          this.scrollerHeight = window.innerHeight - 56 + 'px'
          //console.log(this.scrollerHeight)
        })
      })
    },
    methods: {
       //此处省略10个方法
       /**
       * 初始化加载组件
       */
      loadWidgets() {
        this.containers = this.containers.map(con => {
          return {
            ...con,
            displayName: this.i18n2t(`designer.widgetLabel.${con.type}`, `extension.widgetLabel.${con.type}`)
          }
        }).filter(con => {
          return !con.internal && !this.isBanned(con.type)
        })

        this.basicFields = this.basicFields.map(fld => {
          return {
            ...fld,
            displayName: this.i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)
          }
        }).filter(fld => {
          return !this.isBanned(fld.type)
        })

        this.advancedFields = this.advancedFields.map(fld => {
          return {
            ...fld,
            displayName: this.i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)
          }
        }).filter(fld => {
          return !this.isBanned(fld.type)
        })

        this.customFields = this.customFields.map(fld => {
          return {
            ...fld,
            displayName: this.i18n2t(`designer.widgetLabel.${fld.type}`, `extension.widgetLabel.${fld.type}`)
          }
        }).filter(fld => {
          return !this.isBanned(fld.type)
        })
      },
    }
}

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部