基于vue的vuedraggable + 双击div(contenteditable)实现可编辑,删除,可拖拽(ui组件为elementui)

基于vue的vuedraggable + 双击div(contenteditable)实现可编辑,删除,可拖拽(ui组件为elementui)

<template>
<!-- 可配置化菜单demo -->
  <div class="optionC">
    <el-tabs v-model="activeName" @tab-click="handleClick" type="border-card">
      <el-tab-pane :label="n.name" :name="n.activeName" v-for="(n, m) in syllable" :key="n.activeName">
        <div v-for="(items , index) in n.children" :key="index + 'b'" style="display: flex;flex-direction: column;">
          <span>{{items.title}}</span>
            <draggable
                class="syllable_ul"
                v-model="items.children"
                animation="300"
                group="site"
                :options="{group:'title', animation:150}"
                :no-transition-on-drag="true"
                @change="change"
                @start="start"
                @end="end"
                :disabled="disabled"
                :move="move"
                @add="add1"
              >
                    <!-- <transition-group type="transition"> -->
                      <div
                        @input="changeText(g.tip,g.id+index+'f',g.id+index+'s')"
                        contenteditable="false"
                        @blur="blur(g.id+index+'f', g.tip, g.id+index+'s')"
                        :ref="g.id+index+'f'"
                        @dblclick="dblclick(g.id+index+'f', g.id+idx, g, g.id+index+'s')"
                        class="syllable_li"
                        v-for="(g , idx) in items.children"
                        :key="g.id+idx+'f'"
                        >
                          <!-- 要用标签包裹 不然光标有问题 -->
                          <span :key="g.id+index+'s'" style="overflow: hidden;width: 80px;text-align: center;" :ref="g.id+index+'s'">{{g.tip}}</span>
                          <i :ref="g.id+idx" :key="idx+'a'" @click="deleteCard(g,n.id)" class="el-icon-circle-close i_content"></i>
                        <!-- <el-input :key="idx+'a'"></el-input> -->
                      </div>
                  <!-- </transition-group> -->
              </draggable>
              <div>
                <el-button>新增
                </el-button>
              </div>
        </div>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
  components: {
    draggable
  },
  data() {
    return {
      activeName: 'first',
      disabled: false,
      edit: true,
      contenteditable: false,
      editData: '', // 双击编辑完后的值
      curEdit: '',
      curEditDiv: '',
      changeTex: '',
      alertext: false, // 小于一个字符不让删除
      syllable:[
        {
          name: '总部机构',
          id: 'a01',
          activeName: 'first',
          children: [
            {
              title: '线上经营',
              id: 'b01',
              children: [
                {
                  tip: '预约客追踪',
                  id: 'c01' // 拖拽后的顺序
                },
                {
                  tip: '预约宝追踪',
                  id: 'c02'
                },
                {
                  tip: 'c',
                  id: 'c03'
                }
              ]
            },
            {
              title: '数据中心',
              id: 'b_02',
              children: [
                {
                  tip: '数据一号',
                  id: 'c04'
                },
                {
                  tip: '数据二号',
                  id: 'c05'
                },
                {
                  tip: '8',
                  id: 'c06'
                }
              ]
            },
          ]
        },
        {
          name: '营业区',
          id: 'a02',
          activeName: 'second',
          children: [
            {
              title: '线上经营',
              id: 'a001',
              children: [
                {
                  tip: 'yyqa',
                  id: 'c00e1'
                },
                {
                  tip: 'yyqb',
                  id: 'c00e2'
                },
                {
                  tip: 'yyqc',
                  id: 'c00e3'
                }
              ]
            },
            {
              title: '数据中心',
              id: 'b002',
              children: [
                {
                  tip: 'yyq1',
                  id: 'c004e'
                },
                {
                  tip: 'yyq2',
                  id: 'c005e'
                },
                {
                  tip: 'yyq3',
                  id: 'c006e'
                }
              ]
            },
          ]
        }
      ],
      drag: false,
    }
  },
  methods: {
    changeText(e,el,sp) {
      // 去掉空格 重新定位光标
      if(this.curEditDiv.innerText.indexOf(' ') > -1) {
        this.curEditDiv.innerText = this.curEditDiv.innerText.trim();
        this.localRange(el, sp);
      }
    },
    // 定位光标
    localRange(el, sp) {
      console.log(this.$refs[sp])
      if(window.getSelection) {
        this.$refs[el][0].focus();
        let ranges = window.getSelection();//创建range
        ranges.selectAllChildren(this.$refs[sp][0]);
        ranges.collapseToEnd();//光标移至最后
      }else if (document.selection) { //ie10 9 8 7 6 5
          var range = document.selection.createRange(); //创建选择对象
          //var range = document.body.createTextRange();
          range.moveToElementText(this.$refs[sp][0]); //range定位到obj
          range.collapse(false); //光标移至最后
          range.select();
        }
    },

    blur(el, text, sp) {
      if(this.curEditDiv.innerText.trim() === '') {
        // alert('不能为空');
        this.curEditDiv.innerText = text.substr(0,1);
        this.localRange(el, sp);
      }
      this.$refs[el][0].setAttribute("contenteditable", false);
      let result = [];
      this.replaceAndSearch(this.syllable, this.curEdit.id, this.curEditDiv.innerText, result);

    },
    // 双击触发编辑
    // g是当前要编辑的对象
    dblclick(elInpul, i, g, sp) {
      this.curEdit = g;
      let el = this.$refs[elInpul][0];
      this.curEditDiv = this.$refs[sp][0];
      let oldHtml = this.$refs[elInpul][0].innerText;
      this.$refs[elInpul][0].setAttribute("contenteditable", true);
      this.localRange(elInpul, sp);
    },
    deleteCard(a,id) {
      this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
      this.deleteNode(this.syllable, a.id);
      this.$message({
          type: 'success',
          message: '删除成功!'
        });
      }).catch(() => {
        this.$message({
          type: 'info',
          message: '已取消删除'
        });
      });
    },
    // 根据id查询/替换节点
    replaceAndSearch(list, id, tip, result) {
      let items = null, hasFound=false;
       list.forEach(item => {
        if(item.id === id) {
          // debugger
          item.tip = tip;
          result.push(item);
          hasFound = true;
        } else if(item.children && item.children.length){
            this.replaceAndSearch(item.children, id, tip ,result);
          }

      });
    },
    // 递归删除节点
    deleteNode(data, id) {
      let newDatas = data.filter(el => el.id !== id)
      newDatas.forEach(el => el.children && (el.children = this.deleteNode(el.children, id)));
      return newDatas
    },
    handleClick(tab, event) {
      // console.log(tab, event);
    },
     //evt里面有两个值,一个evt.added 和evt.removed  可以分别知道移动元素的ID和删除元素的ID
    change(evt) {
      // console.log(evt , 'change...')
    },
    //start ,end ,add,update, sort, remove 得到的都差不多
    start(evt) {
      this.drag = true
      // console.log(evt , 'start...')
    },
    end(evt) {
      this.drag = false
      evt.item //可以知道拖动的本身
      evt.to    // 可以知道拖动的目标列表
      evt.from  // 可以知道之前的列表
      evt.oldIndex  // 可以知道拖动前的位置
      evt.newIndex  // 可以知道拖动后的位置
      console.log('end', this.syllable);
    },
    move(evt, originalEvent) {
      // console.log(evt , 'move')
      // console.log(originalEvent) //鼠标位置
    },
    //拖拽完成事件
    add1(e) {
      console.log('add1',e, this.syllable);
    },
    sort(e) {
      console.log('sort',e);
    }
  },
}
</script>
<style lang="scss" scoped>
.optionC {
  width: 600px;
  height: 300px;
  position: absolute;
  left: 50%;
  top: 50%;
  margin-top: -150px;
  margin-left: -300px;
}
.syllable_ul {
  height: 100%;
}
.syllable_li {
  width: 80px;
  height: 30px;
  background:#e1e4e8;
  margin-bottom: 10px;
  float: left;
  list-style:none; /* 将默认的列表符号去掉 */
  padding:0; /* 将默认的内边距去掉 */
  margin-right:10px; /* 将默认的外边距去掉 */
  border-radius: 8px;
  margin-top: 5px;
  // text-align: center;
  font-size: 12px;
  white-space: nowrap;
  display: flex;
  justify-content: center;
  align-items: center;
  &:focus{
    outline:none;
    // border-bottom: 0.01rem solid #80af57;
  }
  position: relative;
}
.sortable-ghost {
  opacity: 0.4;
  background-color: #e1e4e8;
}
.i_content {
  position: absolute;
    color: white;
    margin-left: 75px;
    margin-bottom: 30px;
    font-size: 16px;
    border-radius: 10px;
    background: antiquewhite;
    cursor: pointer;
    left: 0;
    top: -7px;
}
// .input-style {
//   width: 80px;
//   height: 30px !important;
//   margin-bottom: 0px !important;
// }
// .syllable_li:empty::before{
//      content: attr(placeholder);
//      font-size: 14px;
//      color: #CCC;
//      line-height: 21px;
//      padding-top: 10px;
//  }
//  div[contenteditable="true"]:empty:before{
// 		    content: attr(placeholder);
// 		    color: #BAB3AF;
// 		    /*padding: 10px 0;*/
// 		    -webkit-tap-highlight-color:transparent;
// 		    -webkit-user-modify:read-write;
// 		    outline:none;
// 		    border:none;
// 		}


</style>

(如果有样式问题,请自行修改)
这里有几个问题需要注意下:
1. draggable标签下嵌入 transition-group 标签时拖拽会有闪动,更多draggable官网有提供。
2.
在div设置contenteditable内容下的标签里面设置overflow的问题,我上面用的span(换成其他也行),这里我写了个样式overflow: hidden,这个会影响到div处于编辑下光标会出现问题。随后我设置了width: 80px;给了宽度消除bug。具体的可以看代码(以上仅供参考)

你可能感兴趣的:(javascript,css3,es6,html5,vue.js)