draggable 组件


1.新建一个draggable文件夹
2.文件夹内的目录结构

3.此下是每个文件中的内容 首先是index.vue文件

<template>
  <div>
    <el-container class="container-main">
      <!-- 左侧 -->
      <el-aside width="350px" class="main-right">
        <el-header class="header-title">常用组件</el-header>
        <draggable
          :list="CommonUserComponents"
          v-bind="{
            group: {
              name: 'people',
              pull: 'clone',
              put: false
            },
            sort: false,
            ghostClass: 'ghostClass'
          }"
          :move="onMove"
          class="draggable-container"
        >
          <el-tag
            v-for="(item, index) in CommonUserComponents"
            :key="index"
            effect="dark"
            style="width: 100%"
          >
            <i :class="item.icon"></i> {{ item.name }}
          </el-tag>
        </draggable>
      </el-aside>
      <!-- 中间 -->
      <el-container>
        <el-header class="main-bottom header-padding">
          <!-- 清空按钮 -->
          <el-tag
            effect="plain"
            type="danger"
            class="header-tags"
            @click="clearWidetConfit"
            ><i class="el-icon-delete"></i><span>清空</span>
          </el-tag>
          <!-- 导入json -->
          <el-tag
            effect="plain"
            type=""
            class="header-tags"
            @click="setFormJson"
            ><i class="el-icon-connection"></i><span>导入JSON</span>
          </el-tag>
          <!-- 生成JSON -->
          <el-tag
            type=""
            effect="plain"
            class="header-tags"
            @click="saveWidetConfit"
            ><i class="el-icon-document"></i><span>生成JSON</span>
          </el-tag>
          <!-- 表单预览 -->
          <el-tag effect="plain" type="" class="header-tags" @click="showDialog"
            ><i class="el-icon-help"></i><span>表单预览</span>
          </el-tag>
          <!-- 示例DEMO -->
          <el-tag type="" @click="hrefDemo" effect="plain" class="header-tags"
            ><i class="el-icon-star-off"></i><span>示例DEMO</span>
          </el-tag>
        </el-header>
        <el-main class="box-padding">
          <div class="widget-form-container">
            <!-- 表单展示组件,传递当亲拖拽的表单信息 -->
            <!-- sync修饰符表示子父组件传递的参数是双向绑定的 -->
            <widgeform
              ref="configFormData"
              :configFormData="configFormData"
              :select.sync="widgetFormSelect"
            ></widgeform>
            <div class="biaoge"></div>
          </div>
        </el-main>
      </el-container>
      <!-- 右侧 -->
      <el-aside width="350px" class="main-left">
        <el-tabs v-model="activeName" style="margin-top: -4px">
          <el-tab-pane label="组件设置" name="first">
            <widgetconfig :eleItem.sync="widgetFormSelect"></widgetconfig>
          </el-tab-pane>
          <el-tab-pane label="表单属性" name="second">
            <formconfig :formdata="configFormData.config"></formconfig>
          </el-tab-pane>
        </el-tabs>
      </el-aside>
    </el-container>
    <!-- 预览弹出框 -->
    <el-dialog
      title="表单预览"
      :visible.sync="dialogVisible"
      width="70%"
      top="5vh"
    >
      <dialogform ref="dialogForm"></dialogform>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="getDialogForm">获取数据</el-button>
      </span>
    </el-dialog>
    <!-- 生成json框 -->
    <el-dialog :title="dialogTitle" :visible.sync="jsonDialog" top="5vh">
      <div v-if="!jsonTextareaReadonly">
        JSON格式如下,直接复制生成的json覆盖此处代码点击确定即可
      </div>
      <div style="max-height: 400px; overflow-y: scroll">
        <el-input
          type="textarea"
          autosize
          :readonly="jsonTextareaReadonly"
          v-model="jsonmsg"
        >
        </el-input>
      </div>
      <span slot="footer" class="dialog-footer" v-if="!jsonTextareaReadonly">
        <el-button @click="jsonDialog = false">取 消</el-button>
        <el-button type="primary" @click="setJsonBtn">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import draggable from 'vuedraggable';
import widgeform from './widgetForm';
import widgetconfig from './widgetConfig';
import dialogform from './dialogForm';
import formconfig from './formConfig';
import { copy as util_Copy } from './util/clone';
import { CommonUserComponents } from './componentsConfig';
export default {
  components: {
    draggable: draggable,
    widgeform: widgeform,
    widgetconfig: widgetconfig,
    dialogform: dialogform,
    formconfig: formconfig
  },
  data() {
    return {
      // 自定义表单的json数据
      CommonUserComponents,
      // 拖拽展示的数据
      configFormData: {
        list: [],
        config: {
          labelWidth: 100,
          labelPosition: 'right',
          size: 'small'
        }
      },
      widgetFormSelect: {
        key: null,
        options: {
          placeholder: null
        }
      },
      // 预览弹框是否显示
      dialogVisible: false,
      // 生成json弹框
      jsonDialog: false,
      // 生成的json语句
      jsonmsg: '',
      // 弹框标题
      dialogTitle: '生成JSON',
      // 右侧头部标签栏
      activeName: 'first',
      // 弹出文本域是否可编辑,默认不可编辑
      jsonTextareaReadonly: true
    };
  },
  methods: {
    // 是否可以被拖拽
    onMove() {
      return true;
    },
    // 保存方法
    saveWidetConfit() {
      this.dialogTitle = '生成JSON';
      this.jsonTextareaReadonly = true;
      this.jsonDialog = true;
      this.jsonmsg = JSON.stringify(this.configFormData, null, 1);
    },
    // 清空方法
    clearWidetConfit() {
      this.configFormData = {
        list: [],
        config: {
          labelWidth: 100,
          labelPosition: 'right',
          size: 'small'
        }
      };
      this.widgetFormSelect = {
        key: null,
        options: {
          placeholder: null
        }
      };
    },
    // 点击预览方法
    showDialog() {
      if (this.configFormData.list.length === 0) {
        this.$message('请先拖拽组件');
        return;
      }
      // 弹出预览框
      this.dialogVisible = true;
      // 异步调用弹框的方法
      setTimeout(() => {
        // 异步调用弹框中的方法传递表单数据
        this.$refs.dialogForm.setFormVal(util_Copy(this.configFormData));
      });
    },
    // 读取预览表单的数据
    getDialogForm() {
      // 读取表单生成器中的getFormVal方法获取表单填写的数据
      this.$refs.dialogForm.getFormVal().then((data) => {
        this.dialogTitle = '表单数据';
        this.jsonTextareaReadonly = true;
        this.jsonDialog = true;
        this.jsonmsg = JSON.stringify(data, null, 1);
      });
    },
    // 点击示例DEMO按钮
    hrefDemo() {
      this.$router.push({ path: '/demo' });
    },
    // 导入json方法
    setFormJson() {
      this.dialogTitle = '导入JSON';
      this.jsonTextareaReadonly = false;
      this.jsonDialog = true;
      this.jsonmsg = JSON.stringify(
        {
          list: [],
          config: {
            labelWidth: 100,
            labelPosition: 'right',
            size: 'small'
          }
        },
        null,
        1
      );
    },
    // 确定导入json
    setJsonBtn() {
      try {
        this.initJson(this.jsonmsg);
        this.jsonDialog = false;
      } catch (error) {
        this.$message('导入JSON有误');
      }
    },
    // 初始化json,接收表单json数据,根据json数据自动渲染表单页面
    initJson(strJson) {
      this.configFormData = JSON.parse(strJson);
      this.widgetFormSelect = this.configFormData.list[0];
    }
  }
};
</script>
<style>
.biaoge {
  margin: 10px;
  border: 1px dashed #999;
  height: 30vh;
  /* padding-right: 5px; */
}
.container-main {
  height: 100vh;
}
.header-title {
  width: 100%;
  height: 35px !important;
  line-height: 25px !important;
  padding: 0px !important;
  border-bottom: 1px solid #999999;
}
.main-right {
  border-right: 1px solid #999;
  padding: 10px;
}
.draggable-container {
  cursor: move;
  margin-top: 10px;
  /* 分栏布局 */
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 25px;
  gap: 20px;
}

.main-bottom {
  border-bottom: 1px solid #999;
  height: 45px !important;
  line-height: 45px;
}
.main-left {
  border-left: 1px solid #999;
  padding: 10px;
}
.header-padding {
  position: relative;
  padding: 0px !important;
  text-align: right;
}
.box-padding {
  padding: 0px !important;
  position: relative;
  overflow-y: auto;
}
.widget-form-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.ghostClass {
  border: 2px solid #409eff !important;
}
.header-tags {
  margin-right: 10px;
  cursor: pointer;
}
.header-tags:nth-child(1) {
  position: absolute;
  left: 10px;
  top: 8px;
}
</style>
  1. 下面是补全index.vue里面引用的各个组件文件
    1)widgetForm.vue
<template>
  <div class="main-form-box">
    <!-- 拖拽为空时显示 -->
    <div class="null-form" v-if="configFormData.list.length === 0">
      <i class="el-icon-files null-icon"></i>
      <div>将左侧组件拽到此处</div>
    </div>
    <el-form
      :size="configFormData.config.size"
      :label-position="configFormData.config.labelPosition"
      :label-width="configFormData.config.labelWidth + 'px'"
    >
      <!-- 拖拽展示组件 -->
      <draggable
        v-model="configFormData.list"
        v-bind="{ group: 'people', animation: 200 }"
        @add="handleWidgetAdd"
        class="container-form"
        handle=".movetag"
      >
        <!-- 循环展示拖拽的组件 -->
        <div
          v-for="(item, index) in configFormData.list"
          :key="index"
          class="formitem-style"
        >
          <!-- 栅格布局 -->
          <template v-if="item.type === 'grid'">
            <el-row
              v-if="item && item.key"
              type="flex"
              :gutter="item.options.gutter"
              :justify="item.options.justify"
              :align="item.options.align"
              @click.native="handleSelectWidget(index)"
              class="widget-col"
              :class="{ active: selectWidget.key === item.key }"
            >
              <el-col
                v-for="(col, colIndex) in item.columns"
                :key="colIndex"
                :span="col.span ? col.span : 0"
              >
                <draggable
                  v-model="col.list"
                  :no-transition-on-drag="false"
                  v-bind="{
                    group: 'people',
                    animation: 200,
                    handle: '.drag-widget'
                  }"
                  class="widget-col-list"
                  @add="handleWidgetColAdd($event, item, colIndex)"
                  handle=".drag-widget"
                >
                  <template v-for="(el, i) in col.list">
                    <widgetformitem
                      :key="i"
                      v-if="el && el.key"
                      :select.sync="selectWidget"
                      :eleItem="el"
                      :eleIndex="i"
                      :listdata="col"
                    ></widgetformitem>
                  </template>
                </draggable>
              </el-col>
              <!-- 删除 -->
              <div
                class="widget-col-action"
                v-if="selectWidget.key === item.key"
              >
                <el-tag
                  class="grid-tag"
                  effect="dark"
                  @click="removeWidgetElement(index)"
                >
                  <i class="el-icon-delete"></i
                ></el-tag>
                <el-tag effect="dark" class="grid-tag drag-widget">
                  <i class="el-icon-rank"></i>
                </el-tag>
              </div>
            </el-row>
          </template>
          <template v-else>
            <widgetformitem
              v-if="item && item.key"
              :select.sync="selectWidget"
              :eleItem="item"
              :eleIndex="index"
              :listdata="configFormData"
            ></widgetformitem>
          </template>
        </div>
      </draggable>
    </el-form>
  </div>
</template>
<script>
import draggable from 'vuedraggable';
// 每个表单数据
import widgetformitem from './widgetFormItem';
import { copy as util_Copy } from './util/clone';
export default {
  components: {
    draggable: draggable,
    widgetformitem: widgetformitem
  },
  // 接收拖拽时传递过来的数据
  props: ['configFormData', 'select'],
  data() {
    return {
      // 拖拽的表单
      selectWidget: this.select
    };
  },
  methods: {
    // 拖拽完成事件
    handleWidgetAdd(e) {
      // 获取当前拖拽的元素在放置栏中的下标
      const newIndex = e.newIndex;
      // 设置元素唯一key值
      const key = `${Date.parse(new Date())}_${Math.ceil(
        Math.random() * 99999
      )}`;
      // 拖拽完成后往当前元素的options添加remoteFunc,options平级添加key,model
      this.$set(this.configFormData.list, newIndex, {
        // 解构语法解构当前数据达到深拷贝效果,改变数据不会影响原有数据
        ...this.configFormData.list[newIndex],
        // 同名属性覆盖原有属性
        options: {
          ...this.configFormData.list[newIndex].options,
          remoteFunc: 'func_' + key
        },
        // 新增属性key
        key,
        // 新增属性model
        model: this.configFormData.list[newIndex].type + '_' + key,
        rules: []
      });

      // 深拷贝数据,使重复组件之前相互不受影响
      this.$set(
        this.configFormData.list,
        newIndex,
        util_Copy(this.configFormData.list[newIndex])
      );
      // 获取拖拽的表单数据,子父组件数据双向绑定实时改变右侧配置
      this.selectWidget = this.configFormData.list[newIndex];
    },
    // 点击栅格布局组件触发
    handleSelectWidget(index) {
      this.selectWidget = this.configFormData.list[index];
    },
    // 栅格布局删除组件方法
    removeWidgetElement(index) {
      if (this.configFormData.list.length - 1 === index) {
        if (index === 0) {
          this.selectWidget = {
            key: null,
            options: {
              placeholder: null
            }
          };
          console.log(this.eleItem);
        } else {
          this.selectWidget = this.configFormData.list[index - 1];
        }
      } else {
        this.selectWidget = this.configFormData.list[index + 1];
      }
      this.$nextTick(() => {
        this.configFormData.list.splice(index, 1);
      });
    },
    // 栅格布局拖拽完成事件
    handleWidgetColAdd($event, row, colIndex) {
      const newIndex = $event.newIndex;
      const oldIndex = $event.oldIndex;
      const item = $event.item;
      // 设置元素唯一key值
      const key = `${Date.parse(new Date())}_${Math.ceil(
        Math.random() * 99999
      )}`;
      // 拖拽完成后往当前元素的options添加remoteFunc,options平级添加key,model
      this.$set(row.columns[colIndex].list, newIndex, {
        // 解构语法解构当前数据达到深拷贝效果,改变数据不会影响原有数据
        ...row.columns[colIndex].list[newIndex],
        // 同名属性覆盖原有属性
        options: {
          ...row.columns[colIndex].list[newIndex].options,
          remoteFunc: 'func_' + key
        },
        // 新增属性key
        key,
        // 新增属性model
        model: row.columns[colIndex].list[newIndex].type + '_' + key,
        rules: []
      });

      // 深拷贝数据,使重复组件之前相互不受影响
      this.$set(
        row.columns[colIndex].list,
        newIndex,
        util_Copy(row.columns[colIndex].list[newIndex])
      );
      // 获取拖拽的表单数据,子父组件数据双向绑定实时改变右侧配置
      this.selectWidget = row.columns[colIndex].list[newIndex];
    }
  },
  watch: {
    select(itemdata) {
      this.selectWidget = itemdata;
    },
    selectWidget: {
      handler(data) {
        this.$emit('update:select', data);
      }
    }
  }
};
</script>

<style scoped lang="scss">
.main-form-box {
  position: relative;
}
.null-form {
  position: absolute;
  width: 100%;
  height: 100%;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr 1fr;
  justify-items: center;
  color: #999999;
}
.null-icon {
  display: grid;
  align-items: end;
  font-size: 93px;
}

.container-form {
  margin: 10px;
  border: 1px dashed #999;
  height: 30vh;
  padding-right: 5px;
}
.formitem-style {
  margin: 5px;
  border: 1px dashed #999;
}
.view-operations {
  display: none;
}
.widget-view-grid {
  margin-bottom: 0px !important;
  height: 30px;
}
.widget-col {
  padding-bottom: 0;
  padding: 5px;
  background-color: rgba(253, 246, 236, 0.3);
  &.active {
    outline: 2px solid #e6a23c;
  }
  &:hove {
    background: #fdf6ec;
    outline: 1px solid #e6a23c;
    outline-offset: 0px;
  }
  &.active {
    outline: 2px solid #e6a23c;
    border: 1px solid #e6a23c;
    outline-offset: 0;
  }
  .el-col {
    min-height: 50px;
  }
  .widget-col-action {
    position: absolute;
    bottom: 0;
    right: 0;
    display: flex;
    cursor: pointer;
  }
  .grid-tag {
    background: #e6a23c;
    border: 1px solid #e6a23c;
  }
}
.widget-col-list {
  min-height: 50px;
  border: 1px dashed #ccc;
  background: #fff;
}
</style>

2)widgetConfig.vue

<template>
  <div>
    <div v-if="eleItem && eleItem.key">
      <el-form size="mini" label-position="top">
        <!-- 表单名称 -->
        <el-form-item label="标题" class="form-borderbottom">
          <el-input
            v-model="eleItem.name"
            :disabled="eleItem.options.nameDisabled"
          ></el-input>
        </el-form-item>
        <!-- 表单绑定的model -->
        <el-form-item label="数据绑定字段" class="form-borderbottom">
          <el-input v-model="eleItem.model"></el-input>
        </el-form-item>
        <!-- 设置表单宽度 -->
        <template v-if="eleItem.options.hasOwnProperty('width')">
          <el-form-item label="表单宽度" class="form-borderbottom">
            <el-input v-model="eleItem.options.width"></el-input>
          </el-form-item>
        </template>

        <!-- 表单提示语句 -->
        <template v-if="eleItem.options.placeholder">
          <el-form-item label="提示语句" class="form-borderbottom">
            <el-input v-model="eleItem.options.placeholder"></el-input>
          </el-form-item>
        </template>
        <!-- 多行文本 -->
        <template v-if="eleItem.type === 'textarea'">
          <el-form-item label="行数" class="form-borderbottom">
            <el-input
              type="number"
              min="1"
              v-model="eleItem.options.rows"
            ></el-input>
          </el-form-item>
        </template>
        <!-- 输入框 -->
        <template v-if="eleItem.type === 'input'">
          <!-- 是否是密码 -->
          <el-form-item label="密码框" class="form-borderbottom">
            <el-switch
              v-model="eleItem.options.password"
              active-text="是"
              inactive-text="否"
            >
            </el-switch>
          </el-form-item>
        </template>
        <!-- 多选框组 -->
        <template v-if="eleItem.type === 'checkbox'">
          <!-- 最大选择个数 -->
          <el-form-item label="最大选择个数" class="form-borderbottom">
            <el-input
              v-model="eleItem.options.max"
              placeholder="请输入最大选择个数"
              type="number"
            >
            </el-input>
          </el-form-item>
        </template>
        <!-- 计数器 -->
        <template v-if="eleItem.type === 'inputNumber'">
          <!-- 最小值 -->
          <el-form-item label="最小值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.min"
              placeholder="请输入最小值"
              type="number"
            >
            </el-input>
          </el-form-item>
          <!-- 最大值 -->
          <el-form-item label="最大值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.max"
              placeholder="请输入最大值"
              type="number"
            >
            </el-input>
          </el-form-item>
          <!-- 控制按钮位置 -->
          <el-form-item label="控制按钮位置" class="form-borderbottom">
            <el-radio-group v-model="eleItem.options.position">
              <el-radio label="">默认</el-radio>
              <el-radio label="right">右侧</el-radio>
            </el-radio-group>
          </el-form-item>
        </template>
        <!-- 下拉框 -->
        <template v-if="eleItem.type === 'select'">
          <!-- 是否可多选 -->
          <el-form-item label="是否可多选" class="form-borderbottom">
            <el-switch
              v-model="eleItem.options.multiple"
              active-text="是"
              inactive-text="否"
            >
            </el-switch>
          </el-form-item>
        </template>
        <!-- 滑块 -->
        <template v-if="eleItem.type === 'slider'">
          <!-- 默认值 -->
          <el-form-item label="默认值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.defaultValue"
              type="number"
              placeholder="请输入默认值"
            >
            </el-input>
          </el-form-item>
          <!-- 最小值 -->
          <el-form-item label="最小值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.min"
              placeholder="请输入最小值"
              type="number"
            >
            </el-input>
          </el-form-item>
          <!-- 最大值 -->
          <el-form-item label="最大值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.max"
              placeholder="请输入最大值"
              type="number"
            >
            </el-input>
          </el-form-item>
        </template>

        <!-- 评分 -->
        <template v-if="eleItem.type === 'rate'">
          <!-- 默认值 -->
          <el-form-item label="默认值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.defaultValue"
              type="number"
              placeholder="请输入默认值"
            >
            </el-input>
          </el-form-item>
          <!-- 最大值 -->
          <el-form-item label="最大值" class="form-borderbottom">
            <el-input
              v-model.number="eleItem.options.max"
              placeholder="请输入最大值"
              type="number"
            >
            </el-input>
          </el-form-item>
          <!-- 辅助文字 -->
          <el-form-item label="辅助文字" class="form-borderbottom">
            <el-switch
              v-model="eleItem.options.text"
              active-text="是"
              inactive-text="否"
            >
            </el-switch>
          </el-form-item>
        </template>

        <!-- 日期选择器 -->
        <template v-if="eleItem.type === 'datePicker'">
          <!-- 可选时间 -->
          <el-form-item label="时间类型" class="form-borderbottom">
            <el-radio-group
              @change="changeDateType"
              v-model="eleItem.options.type"
              class="date-type-group"
            >
              <el-radio label="date">只选择日期</el-radio>
              <el-radio label="datetime">选择日期和时间</el-radio>
              <el-radio label="daterange">选择日期范围</el-radio>
            </el-radio-group>
          </el-form-item>
        </template>

        <!-- 文字 -->
        <template v-if="eleItem.type === 'text'">
          <!-- 编辑文字 -->
          <el-form-item label="编辑文字" class="form-borderbottom">
            <el-input
              v-model="eleItem.options.defaultValue"
              placeholder="请输入文字"
              type="textarea"
              autosize
            >
            </el-input>
          </el-form-item>
          <!-- 位置 -->
          <el-form-item label="位置" class="form-borderbottom">
            <el-radio-group v-model="eleItem.options.position">
              <el-radio label="">靠左对齐</el-radio>
              <el-radio label="center">居中对齐</el-radio>
              <el-radio label="end" style="margin-top: 10px">靠右对齐</el-radio>
            </el-radio-group>
          </el-form-item>
          <!-- 字体颜色 -->
          <el-form-item label="字体颜色" class="form-borderbottom">
            <el-color-picker
              @active-change="changeTextColor"
              v-model="eleItem.options.color"
            ></el-color-picker>
          </el-form-item>
          <!-- 字体大小 -->
          <el-form-item label="字体大小" class="form-borderbottom">
            <el-input-number
              v-model="eleItem.options.size"
              :min="1"
              label="字体大小"
            ></el-input-number>
          </el-form-item>
          <!-- 字体粗细 -->
          <el-form-item label="字体粗细" class="form-borderbottom">
            <el-radio-group v-model="eleItem.options.weight">
              <el-radio label="">默认</el-radio>
              <el-radio label="700">加粗</el-radio>
            </el-radio-group>
          </el-form-item>
          <!-- 字体倾斜 -->
          <el-form-item label="字体倾斜" class="form-borderbottom">
            <el-radio-group v-model="eleItem.options.style">
              <el-radio label="">默认</el-radio>
              <el-radio label="italic">倾斜</el-radio>
            </el-radio-group>
          </el-form-item>
          <!-- 首行缩进 -->
          <el-form-item label="首行缩进" class="form-borderbottom">
            <el-input-number
              :min="0"
              v-model="eleItem.options.indent"
              label="首行缩进"
            ></el-input-number>
          </el-form-item>
          <!-- 字体缩进 -->
          <el-form-item label="字体缩进" class="form-borderbottom">
            <el-input-number
              :min="0"
              v-model="eleItem.options.leftPadding"
              label="字体缩进"
            ></el-input-number>
          </el-form-item>
          <!-- 字体行间距 -->
          <el-form-item label="字体行间距" class="form-borderbottom">
            <el-input-number
              :min="0"
              v-model="eleItem.options.lineHeight"
              label="字体行间距"
            ></el-input-number>
          </el-form-item>
        </template>
        <!-- 分割线 -->
        <template v-if="eleItem.type === 'divider'">
          <!-- 分割线文案 -->
          <el-form-item label="分割线文案" class="form-borderbottom">
            <el-input
              v-model="eleItem.options.defaultValue"
              placeholder="分割线文案"
            ></el-input>
          </el-form-item>
          <!-- 文案位置 -->
          <el-form-item
            label="文案位置"
            class="form-borderbottom"
            v-if="eleItem.options.defaultValue"
          >
            <el-radio-group v-model="eleItem.options.position">
              <el-radio label="left">靠左对齐</el-radio>
              <el-radio label="center">居中对齐</el-radio>
              <el-radio label="right" style="margin-top: 10px"
                >靠右对齐</el-radio
              >
            </el-radio-group>
          </el-form-item>
        </template>

        <!-- 多组件共享配置:单选框和多选框和下拉框 -->
        <template
          v-if="
            eleItem.type === 'radio' ||
            eleItem.type === 'checkbox' ||
            eleItem.type === 'select'
          "
        >
          <el-form-item label="是否显示标签" class="form-borderbottom">
            <el-switch
              v-model="eleItem.options.isTag"
              active-text="是"
              inactive-text="否"
            >
            </el-switch>
          </el-form-item>
          <el-form-item label="选项组" class="form-borderbottom">
            <draggable
              v-model="eleItem.options.valueData"
              animation="300"
              class="dragg-ui"
              handle=".i-success"
              ghostClass="ghost"
            >
              <li
                v-for="(item, index) in eleItem.options.valueData"
                :key="index"
              >
                <div class="value-label-input">
                  <!-- 设置显示的值 -->
                  <el-input
                    v-model="item.value"
                    :class="{ value_input: !eleItem.options.isTag }"
                  ></el-input>
                  <!-- 设置绑定的值 -->
                  <el-input
                    v-if="eleItem.options.isTag"
                    v-model="item.label"
                  ></el-input>
                </div>
                <i class="el-icon-rank i-success"></i>
                <i
                  class="el-icon-delete i-danger"
                  @click="removeEleItemValueData(index)"
                ></i>
              </li>
            </draggable>
            <el-button type="text config-addbtn" @click="addEleItemValueData"
              >添加选项</el-button
            >
            <el-button type="text config-addbtn" @click="addMultipleValueData"
              >批量添加</el-button
            >
          </el-form-item>
        </template>

        <!-- 组件共享配置 : 是否添加边框 -->
        <template
          v-if="eleItem.type === 'radio' || eleItem.type === 'checkbox'"
        >
          <!-- 布局方法 -->
          <el-form-item label="布局方法" class="form-borderbottom">
            <el-radio-group v-model="eleItem.options.inline">
              <el-radio label="inline-block">行内</el-radio>
              <el-radio label="block">块级</el-radio>
            </el-radio-group>
          </el-form-item>
        </template>

        <!-- 组件公共配置项目 : 最大输入长度 -->
        <template v-if="eleItem.options.hasOwnProperty('maxlength')">
          <el-form-item label="最大输入长度" class="form-borderbottom">
            <el-input
              type="number"
              placeholder="请输入最大输入长度"
              v-model="eleItem.options.maxlength"
            >
            </el-input>
          </el-form-item>
        </template>
        <!-- 组件公共配置项目 :是否必填 -->
        <template v-if="eleItem.options.hasOwnProperty('required')">
          <el-form-item label="是否必填" class="form-borderbottom">
            <el-switch
              v-model="eleItem.options.required"
              active-text="是"
              inactive-text="否"
            >
            </el-switch>
          </el-form-item>
          <!-- 错误提示语句 -->
          <el-form-item
            v-if="eleItem.options.required"
            label="自定义错误提示语句"
            class="form-borderbottom"
          >
            <el-input
              v-model="eleItem.options.requiredMsg"
              placeholder="自定义错误提示语句"
            >
            </el-input>
          </el-form-item>
        </template>
        <!-- 组件公共配置项目 : 是否禁用 -->
        <el-form-item
          label="是否禁用"
          class="form-borderbottom"
          v-if="eleItem.type !== 'text' && eleItem.type !== 'divider'"
        >
          <el-switch
            v-model="eleItem.options.disabled"
            active-text="是"
            inactive-text="否"
          >
          </el-switch>
        </el-form-item>
        <!-- 组件公共配置项目 : 是否可清空 -->
        <template v-if="eleItem.options.hasOwnProperty('clearable')">
          <el-form-item label="是否可清空" class="form-borderbottom">
            <el-switch
              v-model="eleItem.options.clearable"
              active-text="是"
              inactive-text="否"
            >
            </el-switch>
          </el-form-item>
        </template>
      </el-form>
    </div>

    <!-- 批量添加选项弹框 -->
    <el-dialog title="批量添加选项" :visible.sync="addMultipleStatus" top="5vh">
      <div class="addHeaderTitle">
        每行代表一个选项,可以添加多个选项<span>(已自动过滤重复项)</span>。
      </div>
      <el-input
        type="textarea"
        autosize
        :autosize="{ minRows: 3, maxRows: 17 }"
        placeholder="请输入内容"
        v-model="eleItemValueData"
      >
      </el-input>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addMultipleStatus = false">取 消</el-button>
        <el-button type="primary" @click="saveAddMultiple">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import draggable from "vuedraggable";
export default {
  components: {
    draggable: draggable,
  },
  props: ["eleItem"],
  data() {
    return {
      // 批量添加弹框
      addMultipleStatus: false,
      eleItemValueData: "",
    };
  },
  methods: {
    // 删除选项
    removeEleItemValueData(index) {
      this.eleItem.options.valueData.splice(index, 1);
    },
    // 添加选项
    addEleItemValueData() {
      // 随机生成4位数防止报key重复错误
      let random = Math.floor(Math.random() * 9999);
      this.eleItem.options.valueData.push({
        value: "新选项" + random,
        label: "新选项" + random,
      });
    },
    // 弹出批量添加选项框方法
    addMultipleValueData() {
      this.addMultipleStatus = true;
      this.eleItemValueData = "";
    },
    // 确认批量添加选项方法
    saveAddMultiple() {
      if (!this.eleItemValueData) {
        this.$message({
          message: "请输入内容",
          type: "warning",
        });
        return;
      }
      let reg = /(\s*)(.+)(\s*)/g;
      let values = [];
      this.eleItemValueData.replace(reg, (...args) => {
        values.push(args[2]);
      });
      values = [...new Set(values)];
      values.forEach((item) => {
        this.eleItem.options.valueData.push({
          value: item,
          label: item,
        });
      });
      this.addMultipleStatus = false;
    },
    // 改变日期选择器的类型触发
    changeDateType(e) {
      this.eleItem.options.valueFormat =
        e === "datetime" ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd";
    },
    // 改变文字颜色是实时改变页面中的文字颜色
    changeTextColor(e) {
      this.eleItem.options.color = e;
    },
  },
  watch: {
    eleItem: {
      handler(data) {
        // 不需要展示标题的组件集合
        let noShowNameData = ["text", "divider"];        
        if (noShowNameData.some((item) => item === data.type)) {
          data.name = "";
        }
        this.$emit("update:eleItem", data);
      },
    },
  },
};
</script>

<style scoped>
.date-type-group {
  display: grid;
  grid-template-columns: 1fr;
  gap: 10px;
}
.value-label-input {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 5px;
}
.value_input {
  grid-column-start: span 2;
}
.el-form-item--mini > .el-form-item__label {
  padding: 0px !important;
}
.form-borderbottom {
  margin-bottom: 15px !important;
  padding-bottom: 8px;
  border-bottom: 1px solid #e1e1e1;
}
form-item--mini .el-form-item__label {
  margin-bottom: -10px !important;
  line-height: 28px !important;
}

li {
  list-style-type: none;
}
.dragg-ui {
  display: grid;
  grid-auto-columns: 1fr;
  gap: 5px;
}
.dragg-ui > li {
  display: grid;
  grid-template-columns: 5fr 1fr 1fr;
  justify-items: right;
  align-items: center;
}
.i-success {
  padding: 4px;
  border-radius: 50%;
  color: rgb(255, 255, 255);
  background-color: rgb(64, 158, 255);
  border-color: rgb(64, 158, 255);
}
.i-danger {
  padding: 4px;
  border-radius: 50%;
  color: rgb(255, 255, 255);
  background-color: rgb(245, 108, 108);
  border-color: rgb(245, 108, 108);
}
.ghost {
  height: 28px;
  border: 1px dashed #0f79e2;
  background-color: #add3f8;
}
.ghost > * {
  display: none !important;
}
.config-addbtn {
  text-align: left !important;
}

.config-title {
  width: 100%;
  height: 35px !important;
  line-height: 25px !important;
  padding: 0px !important;
  border-bottom: 1px solid #999999;
}
.addHeaderTitle {
  margin-bottom: 10px;
}
.addHeaderTitle > span {
  color: #999999;
  font-size: 10px;
}
</style>

3)dialogForm.vue

<template>
  <div v-if="eleList">
    <el-form
      :size="eleList.config.size"
      :label-position="eleList.config.labelPosition"
      :label-width="eleList.config.labelWidth + 'px'"
    >
      <!-- 循环展示拖拽的组件 -->
      <div
        v-for="(item, index) in eleList.list"
        :key="index"
        class="formitem-style"
      >
        <el-form-item :label="item.name" :required="item.options.required">
          <dialogformitems
            :eleItem="item"
            :eleConfig="eleList.config"
          ></dialogformitems>
        </el-form-item>
      </div>
    </el-form>
  </div>
</template>

<script>
import dialogformitems from "./dialogFormItems";
export default {
  components: {
    dialogformitems: dialogformitems,
  },
  data() {
    return {
      eleList: null,
      formsVal: {},
    };
  },
  methods: {
    // 获取到页面数据
    setFormVal(data) {
      this.eleList = data;
    },
    // 表单验证方法
    valueCheck() {
      const promises = [];
      for (const key of this.eleList.list) {
        if (key.options.required) {
          if (key.options.defaultValue instanceof Array) {
            if (key.options.defaultValue.length === 0) {
              if (key.options.requiredMsg) {
                promises.push(Promise.reject(key.options.requiredMsg));
              } else {
                promises.push(Promise.reject(`${key.name}必填`));
              }
            }
          } else {
            if (!key.options.defaultValue) {
              if (key.options.requiredMsg) {
                promises.push(Promise.reject(key.options.requiredMsg));
              } else {
                promises.push(Promise.reject(`${key.name}必填`));
              }
            }
          }
        } else {
          promises.push(Promise.resolve());
        }
      }
      return promises;
    },
    getFormVal() {
      return new Promise((resolve, reject) => {
        Promise.all(this.valueCheck())
          .then((res) => {
            // 读取数据前先清空表单数据
            this.formsVal = {};
            // 循环读取表单数据
            for (const key of this.eleList.list) {
              Object.assign(this.formsVal, {
                [key.model]: key.options.defaultValue,
              });
            }
            resolve(this.formsVal);
          })
          .catch((err) => {
            this.$message.error(err);
            reject();
          });
      });
    },
  },
};
</script>

4)formConfig.vue

<template>
  <div>
    <el-form label-position="top" size="mini">
      <el-form-item label="标签对齐方式" class="form-borderbottom">
        <el-radio-group v-model="formdata.labelPosition">
          <div class="positon-group">
            <el-radio label="right">右对齐(默认值)</el-radio>
            <el-radio label="top">顶部对齐</el-radio>
            <el-radio label="left">左对齐</el-radio>
          </div>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="组件尺寸" class="form-borderbottom">
        <el-radio-group v-model="formdata.size">
          <div class="positon-group">
            <el-radio label="medium">大</el-radio>
            <el-radio label="small">中(默认值)</el-radio>
            <el-radio label="mini">小</el-radio>
          </div>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="表单字段宽度(单位:px)" class="form-borderbottom">
        <el-input
          v-model.number="formdata.labelWidth"
          type="number"
          min="10"
        ></el-input>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  props: ["formdata"],
};
</script>

<style scoped>
.form-borderbottom {
  margin-bottom: 15px !important;
  padding-bottom: 8px;
  border-bottom: 1px solid #e1e1e1;
}
.positon-group {
  display: grid !important;
  grid-template-columns: 1fr;
  gap: 10px;
}
</style>

5)componentsConfig.js

export const CommonUserComponents = [{
    type: "input",
    name: "单行文本",
    icon: "el-icon-share",
    options: {
        width: "100%",
        placeholder: "请输入单行文本",
        defaultValue: "",
        required: false,
        clearable: false,
        maxlength: ""
    }
},
{
    type: "textarea",
    name: "多行行文本",
    icon: "el-icon-share",
    options: {
        width: "100%",
        rows: 2,
        placeholder: "请输入多行行文本",
        defaultValue: "",
        required: false,
        maxlength: ""
    }
},
{
    type: "radio",
    name: "单选框组",
    icon: "el-icon-share",
    options: {
        inline: "inline-block",
        valueData: [{
            value: '选项1',
            label: '选项1'
        },
        {
            value: '选项2',
            label: '选项2'
        }
        ],
        defaultValue: "",
        required: false
    }
},
{
    type: "checkbox",
    name: "多选框组",
    icon: "el-icon-share",
    options: {
        inline: "inline-block",
        valueData: [{
            value: '选项1',
            label: '选项1'
        },
        {
            value: '选项2',
            label: '选项2'
        }
        ],
        defaultValue: [],
        required: false
    }
},
{
    type: "inputNumber",
    name: "计数器",
    icon: "el-icon-share",
    options: {
        defaultValue: 0,
        min: 0,
        max: 100,
        required: false,
        position: ""
    }
},
{
    type: "select",
    name: "下拉框",
    icon: "el-icon-share",
    options: {
        width: "100%",
        required: false,
        defaultValue: "",
        clearable: false,
        valueData: [{
            value: '选项1',
            label: '选项1'
        },
        {
            value: '选项2',
            label: '选项2'
        }
        ],
    }
},
{

    type: "slider",
    name: "滑块",
    icon: "el-icon-share",
    options: {
        required: false,
        defaultValue: 0,
        min: 0,
        max: 100
    }
},
{
    type: "rate",
    name: "评分",
    icon: "el-icon-share",
    options: {
        required: false,
        defaultValue: 0,
        max: 5
    }
},
{
    type: "timePicker",
    name: "时间选择器",
    icon: "el-icon-share",
    options: {
        width: "100%",
        required: false,
        defaultValue: "",
        clearable: false,
    }
},
{
    type: "datePicker",
    name: "日期选择器",
    icon: "el-icon-share",
    options: {
        width: "100%",
        required: false,
        clearable: false,
        defaultValue: "",
        type: "date",
        valueFormat: "yyyy-MM-dd"
    }
},
{
    type: "text",
    name: "文字",
    icon: "el-icon-share",
    options: {
        nameDisabled: true,
        defaultValue: "默认文字",
        position: "",
        lineHeight: 25,
        size: 14,
        leftPadding: 0,
        color: "#000000",
        weight: '',
        style: '',
        indent: 0
    }
},
{
    type: "divider",
    name: "分割线",
    icon: "el-icon-share",
    options: {
        nameDisabled: true,
        defaultValue: "",
        position: 'center'
    }
},
// {
//     type: "grid",
//     name: "网格布局",
//     icon: "el-icon-share",
//     columns: [{
//         span: 12,
//         list: []
//     },
//     {
//         span: 12,
//         list: []
//     }
//     ],
//     options: {
//         gutter: 0,
//         justify: "start",
//         align: 'top'
//     }
// }
]
  1. 文件夹内新建一个util文件夹 并新建一个clone.js文件
export const copy = (obj) => {
    // 先判断拷贝目标是对象还是数组,用instanceof判断原型是Array还是Object
    let res = obj instanceof Array ? [] : {};
    for (const [key, value] of Object.entries(obj)) {
        res[key] = typeof value === "object" ? copy(value) : value;
    }
    return res
    // return JSON.parse(JSON.stringify(obj));
}

此上index.vue里面的文件就补全了 但是引用的文件里还有音容的其他文件
1)dialogForm.vue里面引用的dialogFormItems.vue

<template>
  <div>
    <!-- 单行文本 -->
    <template v-if="eleItem.type === 'input'">
      <el-input
        v-model="eleItem.options.defaultValue"
        :placeholder="eleItem.options.placeholder"
        :style="{ width: eleItem.options.width }"
        :disabled="eleItem.options.disabled"
        :clearable="eleItem.options.clearable"
        :show-password="eleItem.options.password"
        :maxlength="eleItem.options.maxlength"
        show-word-limit
      ></el-input>
    </template>
    <!-- 多行文本 -->
    <template v-if="eleItem.type === 'textarea'">
      <el-input
        type="textarea"
        :style="{ width: eleItem.options.width }"
        :rows="eleItem.options.rows"
        v-model="eleItem.options.defaultValue"
        :placeholder="eleItem.options.placeholder"
        :disabled="eleItem.options.disabled"
        :maxlength="eleItem.options.maxlength"
        show-word-limit
      >
      </el-input>
    </template>
    <!-- 单选 -->
    <template v-if="eleItem.type === 'radio'">
      <el-radio-group v-model="eleItem.options.defaultValue">
        <el-radio
          v-for="(item, index) in eleItem.options.valueData"
          :key="index"
          :label="item.label"
          :style="{ display: eleItem.options.inline }"
          :class="{ radioLineHeight: !eleItem.options.border }"
          :disabled="eleItem.options.disabled"
          :border="eleItem.options.border"
          >{{ item.value }}</el-radio
        >
      </el-radio-group>
    </template>
    <!-- 多选 -->
    <template v-if="eleItem.type === 'checkbox'">
      <el-checkbox-group
        v-model="eleItem.options.defaultValue"
        :max="eleItem.options.max"
      >
        <el-checkbox
          v-for="(item, index) in eleItem.options.valueData"
          :key="index"
          :label="item.label"
          :style="{ display: eleItem.options.inline }"
          :disabled="eleItem.options.disabled"
          :border="eleItem.options.border"
          >{{ item.value }}</el-checkbox
        >
      </el-checkbox-group>
    </template>
    <!-- 计数器 -->
    <template v-if="eleItem.type === 'inputNumber'">
      <el-input-number
        v-model="eleItem.options.defaultValue"
        :min="eleItem.options.min"
        :max="eleItem.options.max"
        :controls-position="eleItem.options.position"
        :disabled="eleItem.options.disabled"
      ></el-input-number>
    </template>
    <!-- 下拉框 -->
    <template v-if="eleItem.type === 'select'">
      <el-select
        v-model="eleItem.options.defaultValue"
        placeholder="请选择一个选项"
        :style="{ width: eleItem.options.width }"
        :multiple="eleItem.options.multiple"
        :clearable="eleItem.options.clearable"
        :disabled="eleItem.options.disabled"
      >
        <el-option
          v-for="item in eleItem.options.valueData"
          :key="item.label"
          :label="item.value"
          :value="item.label"
        >
        </el-option>
      </el-select>
    </template>
    <!-- 滑块 -->
    <template v-if="eleItem.type === 'slider'">
      <el-slider
        v-model="eleItem.options.defaultValue"
        :disabled="eleItem.options.disabled"
        :min="eleItem.options.min"
        :max="eleItem.options.max"
      ></el-slider>
    </template>
    <!-- 评分  -->
    <template v-if="eleItem.type === 'rate'">
      <el-rate
        v-model="eleItem.options.defaultValue"
        :max="eleItem.options.max"
        :disabled="eleItem.options.disabled"
        :show-text="eleItem.options.text"
        style="margin-top: 6px"
      ></el-rate>
    </template>
    <!-- 时间选择器 -->
    <template v-if="eleItem.type === 'timePicker'">
      <el-time-picker
        :style="{ width: eleItem.options.width }"
        v-model="eleItem.options.defaultValue"
        placeholder="选择时间"
        :disabled="eleItem.options.disabled"
        :clearable="eleItem.options.clearable"
        :default-value="new Date()"
        value-format="HH:mm:ss"
        format="HH:mm:ss"
      >
      </el-time-picker>
    </template>
    <!-- 日期选择器 -->
    <template v-if="eleItem.type === 'datePicker'">
      <el-date-picker
        v-model="eleItem.options.defaultValue"
        placeholder="选择日期"
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        :value-format="eleItem.options.valueFormat"
        :default-value="new Date()"
        :style="{ width: eleItem.options.width }"
        :type="eleItem.options.type"
        :disabled="eleItem.options.disabled"
        :clearable="eleItem.options.clearable"
      >
      </el-date-picker>
    </template>
    <!-- 文字 -->
    <template v-if="eleItem.type === 'text'">
      <div
        :class="{ 'form-text': eleConfig.labelPosition !== 'top' }"
        :style="{
          display: 'grid',
          'justify-content': eleItem.options.position,
          'font-size': eleItem.options.size + 'px',
          color: eleItem.options.color,
          'line-height': eleItem.options.lineHeight + 'px',
          'font-weight': eleItem.options.weight,
        }"
      >
        <div
          :style="{
            'padding-left': eleItem.options.leftPadding + 'px',
            'font-style': eleItem.options.style,
            'text-indent': eleItem.options.indent + 'em',
          }"
        >
          {{ eleItem.options.defaultValue }}
        </div>
      </div>
    </template>
    <!-- 分割线 -->
    <template v-if="eleItem.type === 'divider'">
      <div :class="{ 'form-text': eleConfig.labelPosition !== 'top' }">
        <el-divider :content-position="eleItem.options.position">
          {{ eleItem.options.defaultValue }}
        </el-divider>
      </div>
    </template>
  </div>
</template>
<script>
export default {
  props: ["eleItem", "eleConfig"],
};
</script>
<style scoped>
.radioLineHeight {
  line-height: 32px;
}
.form-text {
  margin-left: -100px;
  font-style: unset;
}
</style>

2)widgetForm.vue引用的 widgetFormItem.vue

<template>
  <el-form-item
    v-if="eleItem && eleItem.key"
    :label="eleItem.name"
    class="widget-view"
    :class="{ active: selectWidget.key === eleItem.key }"
    @click.native.stop="handleSelectWidget()"
    :required="eleItem.options.required"
  >
    <!-- 单行文本 -->
    <template v-if="eleItem.type === 'input'">
      <el-input
        v-model="eleItem.options.defaultValue"
        :style="{ width: eleItem.options.width }"
        :placeholder="eleItem.options.placeholder"
        :disabled="eleItem.options.disabled"
        :show-password="eleItem.options.password"
        :clearable="eleItem.options.clearable"
        :maxlength="eleItem.options.maxlength"
        show-word-limit
        readonly
      ></el-input>
    </template>
    <!-- 多行文本 -->
    <template v-if="eleItem.type === 'textarea'">
      <el-input
        type="textarea"
        readonly
        :style="{ width: eleItem.options.width }"
        :rows="eleItem.options.rows"
        v-model="eleItem.options.defaultValue"
        :placeholder="eleItem.options.placeholder"
        :disabled="eleItem.options.disabled"
        :maxlength="eleItem.options.maxlength"
        show-word-limit
      >
      </el-input>
    </template>
    <!-- 单选 -->
    <template v-if="eleItem.type === 'radio'">
      <el-radio-group v-model="eleItem.options.defaultValue">
        <el-radio
          v-for="(item, index) in eleItem.options.valueData"
          :key="index"
          :label="item.label"
          :style="{ display: eleItem.options.inline }"
          :class="{ radioLineHeight: !eleItem.options.border }"
          :disabled="eleItem.options.disabled"
          :border="eleItem.options.border"
          >{{ item.value }}</el-radio
        >
      </el-radio-group>
    </template>
    <!-- 多选 -->
    <template v-if="eleItem.type === 'checkbox'">
      <el-checkbox-group
        v-model="eleItem.options.defaultValue"
        :max="eleItem.options.max"
      >
        <el-checkbox
          v-for="(item, index) in eleItem.options.valueData"
          :key="index"
          :label="item.value"
          :style="{ display: eleItem.options.inline }"
          :disabled="eleItem.options.disabled"
          :border="eleItem.options.border"
          >{{ item.value }}</el-checkbox
        >
      </el-checkbox-group>
    </template>
    <!-- 计数器 -->
    <template v-if="eleItem.type === 'inputNumber'">
      <el-input-number
        v-model="eleItem.options.defaultValue"
        :min="eleItem.options.min"
        :max="eleItem.options.max"
        :controls-position="eleItem.options.position"
        :disabled="eleItem.options.disabled"
      ></el-input-number>
    </template>
    <!-- 下拉框 -->
    <template v-if="eleItem.type === 'select'">
      <el-select
        v-model="eleItem.options.defaultValue"
        placeholder="请选择一个选项"
        :style="{ width: eleItem.options.width }"
        :multiple="eleItem.options.multiple"
        :clearable="eleItem.options.clearable"
        :disabled="eleItem.options.disabled"
      >
        <el-option
          v-for="item in eleItem.options.valueData"
          :key="item.value"
          :label="item.label"
          :value="item.value"
        >
        </el-option>
      </el-select>
    </template>
    <!-- 滑块 -->
    <template v-if="eleItem.type === 'slider'">
      <el-slider
        v-model="eleItem.options.defaultValue"
        :disabled="eleItem.options.disabled"
        :min="eleItem.options.min"
        :max="eleItem.options.max"
      ></el-slider>
    </template>
    <!-- 评分  -->
    <template v-if="eleItem.type === 'rate'">
      <el-rate
        v-model="eleItem.options.defaultValue"
        :max="eleItem.options.max"
        :disabled="eleItem.options.disabled"
        :show-text="eleItem.options.text"
        style="margin-top: 6px"
      ></el-rate>
    </template>
    <!-- 时间选择器 -->
    <template v-if="eleItem.type === 'timePicker'">
      <el-time-picker
        :style="{ width: eleItem.options.width }"
        v-model="eleItem.options.defaultValue"
        placeholder="选择时间"
        :disabled="eleItem.options.disabled"
        :clearable="eleItem.options.clearable"
        :default-value="new Date()"
        value-format="HH:mm:ss"
        format="HH:mm:ss"
      >
      </el-time-picker>
    </template>
    <!-- 日期选择器 -->
    <template v-if="eleItem.type === 'datePicker'">
      <el-date-picker
        v-model="eleItem.options.defaultValue"
        placeholder="选择日期"
        range-separator="至"
        start-placeholder="开始日期"
        end-placeholder="结束日期"
        :value-format="eleItem.options.valueFormat"
        :default-value="new Date()"
        :style="{ width: eleItem.options.width }"
        :type="eleItem.options.type"
        :disabled="eleItem.options.disabled"
        :clearable="eleItem.options.clearable"
      >
      </el-date-picker>
    </template>
    <!-- 文字 -->
    <template v-if="eleItem.type === 'text'">
      <div
        :class="{ 'form-text': listdata.config.labelPosition !== 'top' }"
        :style="{
          display: 'grid',
          'justify-content': eleItem.options.position,
          'font-size': eleItem.options.size + 'px',
          color: eleItem.options.color,
          'line-height': eleItem.options.lineHeight + 'px',
          'font-weight': eleItem.options.weight,
        }"
      >
        <div
          :style="{
            'padding-left': eleItem.options.leftPadding + 'px',
            'font-style': eleItem.options.style,
            'text-indent': eleItem.options.indent + 'em',
          }"
        >
          {{ eleItem.options.defaultValue }}
        </div>
      </div>
    </template>
    <!-- 分割线 -->
    <template v-if="eleItem.type === 'divider'">
      <div :class="{ 'form-text': listdata.config.labelPosition !== 'top' }">
        <el-divider :content-position="eleItem.options.position">
          {{ eleItem.options.defaultValue }}
        </el-divider>
      </div>
    </template>

    <!-- 组件操作按钮 -->
    <div class="view-operations">
      <!-- 移动 -->
      <div>
        <el-tag effect="dark" class="movetag">
          <i class="el-icon-rank"></i>
        </el-tag>
      </div>
      <!-- 删除 -->
      <div>
        <el-tag effect="dark" @click="removeWidgetElement()">
          <i class="el-icon-delete"></i
        ></el-tag>
      </div>
    </div>
  </el-form-item>
</template>
<script>
import draggable from "vuedraggable";

export default {
  components: {
    draggable: draggable,
  },
  props: ["select", "eleItem", "eleIndex", "listdata"],
  data() {
    return {
      selectWidget: this.select,
    };
  },
  methods: {
    // 点击不同的组件触发
    handleSelectWidget() {
      this.selectWidget = this.eleItem;
    },
    // 删除组件方法
    removeWidgetElement() {
      const index = this.eleIndex;
      if (this.listdata.list.length - 1 === index) {
        if (index === 0) {
          this.selectWidget = {
            key: null,
            options: {
              placeholder: null,
            },
          };
          console.log(this.eleItem);
        } else {
          this.selectWidget = this.listdata.list[index - 1];
        }
      } else {
        this.selectWidget = this.listdata.list[index + 1];
      }
      this.$nextTick(() => {
        this.listdata.list.splice(index, 1);
      });
    },
  },
  watch: {
    select(itemdata) {
      this.selectWidget = itemdata;
    },
    selectWidget: {
      handler(data) {
        this.$emit("update:select", data);
      },
    },
  },
};
</script>
<style scoped>
.view-operations {
  display: none;
}
.widget-view {
  margin-bottom: 0px !important;
}

.active {
  border: 2px solid #409eff !important;
}
.active .view-operations {
  position: absolute;
  bottom: -5px;
  right: 0;
  display: flex;
  cursor: pointer;
}
.grid-template {
  margin-left: -100px !important;
}
.grid-template > .columns {
  border: 2px solid #9999;
  height: 50px;
}
.radioLineHeight {
  line-height: 32px;
}
.form-text {
  margin-left: -100px;
}
</style>

posted @ 2025-02-26 15:24  刘酸酸sour  阅读(70)  评论(0)    收藏  举报