这是大佬的博客 https://blog.csdn.net/zjiang1994/article/details/79809687
里面对vue.draggable的一些方法和属性,进行了详解,非常的详细,比官网的还要详细哦!给大佬点赞!!!
这是vue.draggable的官网 https://www.itxst.com/vue-draggable/jirneq6b.html
虽然官网说的不是很详细,但是有一些例子还不错

在这里插入图片描述
如图,想要实现这样的效果,左侧列表有一堆框框,要把左侧的列表拖拽到右侧列表,然后就去了官网

在这里插入图片描述
官网列子 未拖拽之前是这样的
在这里插入图片描述

拖拽后是这样的
在这里插入图片描述
首先用的这个方法,多个列表之间进行拖拽,但是用了这个方法后,发现左侧的一些项到右侧去了,但是同时左侧的一些项也消失了,若是想在右侧列表存在两个单选框就无法实现,所以换了个方法,使用了 clone拷贝

未拖拽前是这样的
在这里插入图片描述
拖拽后是这样的(有个点需要注意下,从左侧向右侧进行拖拽时,记得从右侧的一列数据上面拖进去,不要从下面往上面拖,否则拖不进去,而且右侧列表需要保留一列数据,否则左侧数据也会拖不过来
在这里插入图片描述
可以发现 ,初步实现了效果
接下来就是一些比较细的注意事项了
在这里插入图片描述
可以发现,左侧可以向右侧拖拽,但是同时右侧也可以往左侧拖拽,这会导致左侧列表混乱起来,这和我的需求就不符了,我是想着左侧的列表项是固定的,这样就只是单纯的把左侧的一些项复制到右侧
然后就去看了大佬的博客
在这里插入图片描述
可以看到 pull:‘clone’时可以进行克隆,所以 pull:''时,就相当于禁止克隆了
以下是代码截图

在这里插入图片描述

但是又有一个问题 我只想 右侧列表实现内部拖拽,左侧列表禁止内部拖拽,现在是两边都可以进行拖拽 ,这无疑又导致了左侧列表发生了混乱
然后又去仔细看了下大佬的博客,有这么段话

在这里插入图片描述
发现了这个 sort 属性,sort:true时,可以进行内部拖拽,sort:false时,禁止拖拽

在这里插入图片描述
ok,这下舒服了

监测右侧列表内部拖拽后的下标
在这里插入图片描述
在这里插入图片描述
以下是目前的全部代码

<template>
  <div>
    <div class="body_top">
      <el-row :gutter="20">
        <el-col :span="24">
          <el-card shadow="always">
            <div style="background-color: #fff">
              <el-form :inline="true"
                >
                <el-form-item label="模块标识:" prop="molds">
                    <el-input  v-model="top_form.molds" controls-position="right"    placeholder="请输入模块标识" class="ele-fluid ele-text-left"/>
                </el-form-item>                                                  
                <el-form-item label="模块名称:" prop="name">
                    <el-input  v-model="top_form.name" controls-position="right"  placeholder="请输入模块名称" class="ele-fluid ele-text-left"/>
                </el-form-item>
                <el-form-item label="状态码:" prop="state">
                    <el-input  v-model="top_form.state" controls-position="right"  placeholder="请输入状态码" class="ele-fluid ele-text-left"/>
                </el-form-item>
                <el-form-item>
                  <el-button @click="showEdit=false">取消</el-button>
                  <el-button type="primary" @click="formSave">保存</el-button>
                </el-form-item>
              </el-form>
            </div></el-card>
          </el-col>
        </el-row>
    </div>
    <div class="ele-body">
      <el-row :gutter="20">
        <el-col :span="7">
          <el-card shadow="never">
            <div class="col">
              <el-row>
                <div class="base_title">基础字段</div>
                <el-divider></el-divider>
                <draggable v-model="arr1" @end="end1" :options="{ group: { name: 'tt', pull: 'clone' },sort: false,}" animation="300" style="width: 100%; min-height: 330px">
                  <!-- <transition-group> -->
                    <div v-for="item in arr1" :key="item.id">
                      <div class="base_style">
                        <span class="el-icon-circle-check icon"></span>
                        {{ item.name }}
                      </div>
                    </div>
                  <!-- </transition-group> -->
                </draggable>
              </el-row>
              <el-row>
                <div class="base_title remark_margin">特殊字段</div>
                <el-divider></el-divider>
                <draggable v-model="arr2" @end="end1" :options="{ group: { name: 'tt', pull: 'clone' }, sort: false,}" animation="300" style="width: 100%; min-height: 330px">
                  <!-- <transition-group> -->
                    <div v-for="item in arr2" :key="item.id">
                      <div class="base_style">
                        <span class="el-icon-circle-check icon"></span>
                        {{ item.name }}
                      </div>
                    </div>
                  <!-- </transition-group> -->
                </draggable>
              </el-row>
            </div>
          </el-card>
        </el-col>
        <el-col :span="10">
          <el-card shadow="never">
            <div class="col">
              <draggable v-model="arr3" :options="{ group: { name: 'tt', pull: '' }, sort: true,}" animation="300" :move="onMove" style="width: 100%; min-height: 330px">
                <!-- <transition-group> -->
                  <div v-for="(item, index) in arr3" :key="index">
                    <div v-if="item.type == 'text'" class="base_margin" @click="showMenu(item)">
                      <div class="static_wrap">{{ item.name }} <div class="static_border"></div></div>
                      <div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div>
                    </div>
                    <div v-if="item.type == 'textarea'" class="base_margin" @click="showMenu(item)">
                      <div class="static_wrap"> {{ item.name }}<div class="static_border static_textarea"><span class="static_bias">//</span></div></div>
                      <div> <el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div>
                    </div>
                    <div v-if="item.type == 'select'" class="base_margin" @click="showMenu(item)">
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border static_textarea">
                          <span class="el-icon-arrow-down static_select"></span>
                        </div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'number'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border"></div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'radio'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="circle_wrap">
                        {{ item.name }}
                        <span class="static_circle"></span>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'checkbox'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="circle_wrap">
                        {{ item.name }}
                        <span class="static_react"></span>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'datetime'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        <span>
                          {{ item.name }}
                        </span>
                        <div class="static_border static_textarea">
                          <span class="el-icon-time static_time"></span>
                        </div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'daterange'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border static_textarea">
                          <span class="el-icon-date static_time"></span>
                        </div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'place'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border"></div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'image'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="image_wrap">
                        {{ item.name }}
                        <span
                          style="font-size: 40px; line-height: 60px"
                          class="el-icon-picture"
                        ></span>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'file'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="file_wrap">
                        {{ item.name }}
                        <div class="static_file">点击上传</div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'tel'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border"></div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'button'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="file_wrap">
                        {{ item.name }}
                        <div class="static_file"></div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'signature'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border"></div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'content'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border"></div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div
                      v-if="item.type == 'city'"
                      class="base_margin"
                      @click="showMenu(item)"
                    >
                      <div class="static_wrap">
                        {{ item.name }}
                        <div class="static_border static_textarea">
                          <span class="el-icon-arrow-down static_select"></span>
                        </div>
                      </div>
                      <div>
                        <el-button type="text" @click="deleteDomain(index)"
                          ><i class="el-icon-delete"></i>
                        </el-button>
                      </div>
                    </div>
                    <div v-if="item.type == 'example'" class="base_margin" style="visibility:hidden;">
                      <div class="static_wrap">
                        <span style="color: red">{{ item.name }}</span>
                        <div class="static_border"></div>
                      </div>
                      <div></div>
                    </div>
                  </div>
                <!-- </transition-group> -->
              </draggable></div></el-card
        ></el-col>
        <el-col :span="7"
          ><el-card shadow="never">
            <div class="col">
              <div
                style="
                  text-align: center;
                  margin-bottom: 10px;
                  font-weight: bold;
                  color: rgb(2, 167, 240);
                "
              >
                字段属性
              </div>
              <el-divider></el-divider>
              <div v-if="isShow">
                <el-form
                  :model="controlForm"
                  ref="controlForm"
                  label-width="100px;"
                >
                  <div class="remark_margin">控件说明</div>
                  <div class="remark_margin">
                    此控件 {{ controlForm.remark }}
                  </div>
                  <div class="remark_margin">大小:</div>
                  <div
                    style="
                      display: flex;
                      justify-content: space-around;
                      margin-bottom: 10px;
                    "
                  >
                    <div>
                      
                      <el-input
                        class="base_input"
                        v-model="controlForm.height"
                      ></el-input>
                    </div>
                    <div>
                      
                      <el-input
                        class="base_input"
                        v-model="controlForm.width"
                      ></el-input>
                    </div>
                  </div>
                  <el-form-item label="标题:"
                    ><el-input
                      v-model="controlForm.label"
                      style="width: calc(100% - 20px)"
                    ></el-input
                  ></el-form-item>
                  <el-form-item
                    ><el-checkbox-group v-model="controlForm.checkList">
                      <el-checkbox label="1">显示标题</el-checkbox>
                      <el-checkbox label="2"
                        >换行</el-checkbox
                      ></el-checkbox-group
                    ></el-form-item
                  >
                  <el-form-item label="提示文字:"
                    ><el-input
                      v-model="controlForm.placeholder"
                      style="width: calc(100% - 20px)"
                    ></el-input
                  ></el-form-item>
                  <el-form-item label="默认值:"
                    ><el-select v-model="controlForm.default">
                      <el-option label="自定义" :value="1" />
                      <el-option label="关联系统表" :value="2" /> </el-select
                  ></el-form-item>
                  <el-form-item label="选格式:"
                    ><el-select v-model="controlForm.format">
                      <el-option label="文本" :value="1" />
                      <el-option label="手机号" :value="2" />
                      <el-option label="邮箱" :value="3" />
                      <el-option label="身份证号" :value="4" />
                      <el-option label="邮政编码" :value="5" /> </el-select
                  ></el-form-item>
                  <div style="margin-bottom: 20px">校验:</div>
                  <el-form-item>
                    <el-checkbox-group v-model="controlForm.checked">
                      <el-checkbox label="1">必填</el-checkbox
                      ><el-checkbox label="2">不允许重复</el-checkbox
                      ><el-checkbox label="3"
                        >脱敏</el-checkbox
                      ></el-checkbox-group
                    >
                  </el-form-item>
                  <div style="margin-bottom: 20px">字段权限:</div>
                  <el-form-item>
                    <el-checkbox-group v-model="controlForm.force">
                      <el-checkbox label="3">指定人员可见</el-checkbox>
                      <el-checkbox label="1">可见</el-checkbox
                      ><el-checkbox label="2">可编辑</el-checkbox
                      ><el-checkbox label="4">指定人员可编辑</el-checkbox
                      ><el-checkbox label="5">导入</el-checkbox
                      ><el-checkbox label="6"
                        >导出</el-checkbox
                      ></el-checkbox-group
                    >
                  </el-form-item>
                </el-form>
                <div
                  slot="footer"
                  style="display: flex; justify-content: center"
                >
                  <el-button icon="el-icon-close">重置</el-button>
                  <el-button type="primary" @click="fieldSave">保存</el-button>
                </div>
              </div>
            </div></el-card
          ></el-col
        >
      </el-row>
    </div>
  </div>
</template>
<script>
//导入draggable组件
import draggable from "vuedraggable";
export default {
  //注册draggable组件
  name:'oa_template_add',
  components: {
    draggable,
  },
  data() {
    return {
      //基本字段
      arr1: [
        { id: 1, icon: "", name: "单行文本框", type: "text" },
        { id: 2, icon: "", name: "多行文本框", type: "textarea" },
        { id: 3, icon: "", name: "下拉列表框", type: "select" },
        { id: 4, icon: "", name: "数字文本框", type: "number" },
        { id: 5, icon: "", name: "单选按钮组", type: "radio" },
        { id: 6, icon: "", name: "复选框组", type: "checkbox" },
        { id: 7, icon: "", name: "日期时间框", type: "datetime" },
        { id: 8, icon: "", name: "日期区间", type: "daterange" },
      ],
      //特殊字段
      arr2: [
        { id: 9, icon: "", name: "地址框", type: "place" },
        { id: 10, icon: "", name: "图片", type: "image" },
        { id: 12, icon: "", name: "附件", type: "file" },
        { id: 13, icon: "", name: "手机号", type: "tel" },
        { id: 14, icon: "", name: "按钮", type: "button" },
        { id: 16, icon: "", name: "手写签名", type: "signature" },
        { id: 15, icon: "", name: "内容联动", type: "content" },
        { id: 17, icon: "", name: "城市级联", type: "city" },
      ],
      // arr3: [{ id: 100, icon: "", name: "示例框", type: "example" }],
      arr3: [],
      
      controlForm: {
        width: "",
        height: "",
        label: "",
        checkList: [],
        checked: [],
        force: [],
        placeholder: "",
        default: "",
        format: "",
        type: "",
        remark: "",
        id: "",
      },
      //顶部表单
      top_form: {
        name: "",
        molds:"",
        state:"",
        field: [],
      },
      formArr: [],
      isShow: false, //字段属性是否显示
    };
  },
  methods: {
    //左侧拖动结束时的事件
    end1() {
      let arr5 = [];
      //对数组进行重新赋值,使其id等于下标
      this.arr3.forEach((d, index) => {
        arr5.push({
          id: index,
          name: d.name,
          type: d.type,
        });
      });
      this.arr3 = arr5;
      // console.log(this.arr3);
    },
    //内部拖拽时触发的方法
    onMove(e) {
      //e.draggedContext.index为拖拽前的下标,e.draggedContext.futureIndex为拖拽后的下标
      // console.log(e);
      // console.log(e.draggedContext.index, e.draggedContext.futureIndex);
    },
    //删除按钮
    deleteDomain(index) {
      this.arr3.splice(index, 1);
    },
    //字段表单的显示与隐藏
    showMenu(e) {
      this.isShow = true;
      // console.log(e);
      this.controlForm.remark = e.name;
      this.controlForm.id = e.id;
      this.controlForm.type = e.type;
    },
    //提交字段表单
    fieldSave() {
      this.formArr.push(this.controlForm)
      // console.log(this.controlForm);
      // let newArr = this.top_form.field;
      // newArr.push(this.controlForm);
      // if (newArr.length == 0) {
      //   newArr.push(this.controlForm);
      // } else {
      //   for (var i = 0; i < newArr.length; i++) {
      //     if (newArr[i].id == this.controlForm.id) {
      //       newArr[i] = this.controlForm;
      //       this.$message.warning("警告");
      //     } else {
      //       newArr.push(this.controlForm);
      //       this.$message.success("添加");
      //     }
      //   }
      // }
      // console.log(this.top_form);
    },
    //提交form表单
    formSave() {
      this.top_form.field=this.formArr;
      console.log(this.top_form);
    },
  },
};
</script>
<style scoped>
.col {
  background-color: #fff;
  height: 750px;
  overflow: scroll;
}
.base_title {
  font-size: 18px;
  width: 80px;
  height: 20px;
  line-height: 20px;
  text-align: center;
  color: #000;
  font-weight: bold;
  margin-bottom: 10px;
}
.base_style {
  width: 135px;
  height: 35px;
  border: 1px solid #999;
  line-height: 35px;
  text-align: center;
  float: left;
  margin-left: 10px;
  margin-top: 10px;
  margin-right: 10px;
  margin-bottom: 10px;
  box-sizing: border-box;
  border-radius: 5px;
  text-align: left;
  cursor: pointer;
}
.icon {
  color: rgb(86, 169, 251);
  margin-right: 5px;
  margin-left: 10px;
}
.base_input {
  width: 160px;
}
.base_margin {
  margin-top: 10px;
  margin-bottom: 10px;
  background-color: rgba(235, 248, 251, 1);
  height: 80px;
  line-height: 60px;
  border-radius: 10px;
  padding: 10px;
  box-sizing: border-box;
  display: flex;
  justify-content: space-between;
  cursor: pointer;
}
.remark_margin {
  margin-top: 10px;
  margin-bottom: 10px;
}
.body_top {
  padding: 15px 15px 0 15px;
}
/* 中间静态框css样式 */
.static_wrap {
  width: 300px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.static_border {
  width: 200px;
  height: 36px;
  background-color: #fff;
  border-radius: 5px;
  border: 1px solid #c0c4cc;
  cursor: pointer;
}
.static_textarea {
  position: relative;
}
.static_bias {
  position: absolute;
  right: 2px;
  top: 0;
  font-size: 12px;
}
.static_select {
  position: absolute;
  right: 5px;
  top: 10px;
}
.circle_wrap {
  width: 110px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.static_circle {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid #c0c4cc;
  cursor: pointer;
}
.static_react {
  width: 10px;
  height: 10px;
  border: 1px solid #c0c4cc;
  cursor: pointer;
}
.static_time {
  position: absolute;
  left: 10px;
  top: 10px;
}
.image_wrap {
  width: 135px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
}
.file_wrap {
  width: 180px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.static_file {
  width: 80px;
  height: 40px;
  background-color: rgb(86, 169, 251);
  text-align: center;
  line-height: 40px;
  color: #fff;
  border-radius: 5px;
  font-size: 12px;
}
</style>

还有一点 拖拽 group 里的 name 记得改成 一致 的,否则拖拽不过去
在这里插入图片描述

<----------------------------------------->
2021.10.29更
之前提到过,右侧列表数据为空时,左侧的标签拖拽不到右侧列表,所以当时就想着给右侧列表加个默认的数据,并且给这个数据背景颜色设为空白,但是这会有很多问题,比如说必须从默认的数据上面拖,才能拖拽到右侧,而且原地会有个空白区,用户体验不好,今日碰巧看到了大佬的博客,解决了!!感谢感谢

这是之前的效果图,可以看到第一次拖拽的时候拖不过去,需要从右侧隐藏的那个示例框上面拖才行,而且拖过去了也会有空白,
在这里插入图片描述
这是现在的图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这是大佬的博客地址,说的很详细哦 vue.draggable多列表间拖拽列表数据为空时,拖拽失效bug解决

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐