最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Vue實現可以拖拽的樹形表格實例詳解

 更新時間:2018年10月18日 08:32:11   作者:北風吹雪  
因業(yè)務需求,需要一個樹形表格,并且支持拖拽排序,任意未知插入,github搜了下,真不到合適的,大部分樹形表格都沒有拖拽功能,所以決定自己實現一個。這里分享一下實現過程,需要的朋友可以參考下

因業(yè)務需求,需要一個樹形表格,并且支持拖拽排序,任意未知插入,github搜了下,真不到合適的,大部分樹形表格都沒有拖拽功能,所以決定自己實現一個。這里分享一下實現過程,項目源代碼請看github,插件已打包封裝好,發(fā)布到npm上 

本博文會分為兩部分,第一部分為使用方式,第二部分為實現方式

安裝方式

npm i drag-tree-table --save-dev

使用方式

import dragTreeTable from 'drag-tree-table'

 模版寫法

<dragTreeTable :data="treeData" :onDrag="onTreeDataChange"></dragTreeTable> 

data參數示例

{
 lists: [
 {
 "id":40,
 "parent_id":0,
 "order":0,
 "name":"動物類",
 "open":true,
 "lists":[]
 },{
 "id":5,
 "parent_id":0,
 "order":1,
 "name":"昆蟲類",
 "open":true,
 "lists":[
  {
  "id":12,
  "parent_id":5,
  "open":true,
  "order":0,
  "name":"螞蟻",
  "lists":[]
  }
 ]
 },
 {
 "id":19,
 "parent_id":0,
 "order":2,
 "name":"植物類",
 "open":true,
 "lists":[]
 }
 ],
 columns: [
 {
 type: 'selection',
 title: '名稱',
 field: 'name',
 width: 200,
 align: 'center',
 formatter: (item) => {
  return '<a>'+item.name+'</a>'
 }
 },
 {
 title: '操作',
 type: 'action',
 width: 350,
 align: 'center',
 actions: [
  {
  text: '查看角色',
  onclick: this.onDetail,
  formatter: (item) => {
   return '<i>查看角色</i>'
  }
  },
  {
  text: '編輯',
  onclick: this.onEdit,
  formatter: (item) => {
   return '<i>編輯</i>'
  }
  }
 ]
 },
 ]
} 

 onDrag在表格拖拽時觸發(fā),返回新的list

onTreeDataChange(lists) {
 this.treeData.lists = lists
} 

到這里組件的使用方式已經介紹完畢

實現

•遞歸生成樹姓結構(非JSX方式實現)
•實現拖拽排序(借助H5的dragable屬性)
•單元格內容自定義展示

組件拆分-共分為四個組件

  dragTreeTable.vue是入口組件,定義整體結構

  row是遞歸組件(核心組件)

  clolmn單元格,內容承載

  space控制縮進

看一下dragTreeTable的結構

<template>
 <div class="drag-tree-table">
  <div class="drag-tree-table-header">
   <column
   v-for="(item, index) in data.columns"
   :width="item.width"
   :key="index" >
   {{item.title}}
   </column>
  </div>
  <div class="drag-tree-table-body" @dragover="draging" @dragend="drop">
   <row depth="0" :columns="data.columns"
   :model="item" v-for="(item, index) in data.lists" :key="index">
  </row>
  </div>
 </div>
</template> 

看起來分原生table很像,dragTreeTable主要定義了tree的框架,并實現拖拽邏輯

filter函數用來匹配當前鼠標懸浮在哪個行內,并分為三部分,上中下,并對當前匹配的行進行高亮

resetTreeData當drop觸發(fā)時調用,該方法會重新生成一個新的排完序的數據,然后返回父組件

下面是所有實現代碼

<script>
 import row from './row.vue'
 import column from './column.vue'
 import space from './space.vue'
 document.body.ondrop = function (event) {
 event.preventDefault();
 event.stopPropagation();
 }
 export default {
 name: "dragTreeTable",
 components: {
  row,
  column,
  space
 },
 props: {
  data: Object,
  onDrag: Function
 },
 data() {
  return {
  treeData: [],
  dragX: 0,
  dragY: 0,
  dragId: '',
  targetId: '',
  whereInsert: ''
  }
 },
 methods: {
  getElementLeft(element) {
  var actualLeft = element.offsetLeft;
  var current = element.offsetParent;
  while (current !== null){
   actualLeft += current.offsetLeft;
   current = current.offsetParent;
  }
  return actualLeft
  },
  getElementTop(element) {
  var actualTop = element.offsetTop;
  var current = element.offsetParent;
  while (current !== null) {
   actualTop += current.offsetTop;
   current = current.offsetParent;
  }
  return actualTop
  },
  draging(e) {
  if (e.pageX == this.dragX && e.pageY == this.dragY) return
  this.dragX = e.pageX
  this.dragY = e.pageY
  this.filter(e.pageX, e.pageY)
  },
  drop(event) {
  this.clearHoverStatus()
  this.resetTreeData()
  },
  filter(x,y) {
  var rows = document.querySelectorAll('.tree-row')
  this.targetId = undefined
  for(let i=0; i < rows.length; i++) {
   const row = rows[i]
   const rx = this.getElementLeft(row);
   const ry = this.getElementTop(row);
   const rw = row.clientWidth;
   const rh = row.clientHeight;
   if (x > rx && x < (rx + rw) && y > ry && y < (ry + rh)) {
   const diffY = y - ry
   const hoverBlock = row.children[row.children.length - 1]
   hoverBlock.style.display = 'block'
   const targetId = row.getAttribute('tree-id')
   if (targetId == window.dragId){
    this.targetId = undefined
    return
   }
   this.targetId = targetId
   let whereInsert = ''
   var rowHeight = document.getElementsByClassName('tree-row')[0].clientHeight
   if (diffY/rowHeight > 3/4) {
    console.log(111, hoverBlock.children[2].style)
    if (hoverBlock.children[2].style.opacity !== '0.5') {
    this.clearHoverStatus()
    hoverBlock.children[2].style.opacity = 0.5
    }
    whereInsert = 'bottom'
   } else if (diffY/rowHeight > 1/4) {
    if (hoverBlock.children[1].style.opacity !== '0.5') {
    this.clearHoverStatus()
    hoverBlock.children[1].style.opacity = 0.5
    }
    whereInsert = 'center'
   } else {
    if (hoverBlock.children[0].style.opacity !== '0.5') {
    this.clearHoverStatus()
    hoverBlock.children[0].style.opacity = 0.5
    }
    whereInsert = 'top'
   }
   this.whereInsert = whereInsert
   }
  }
  },
  clearHoverStatus() {
  var rows = document.querySelectorAll('.tree-row')
  for(let i=0; i < rows.length; i++) {
   const row = rows[i]
   const hoverBlock = row.children[row.children.length - 1]
   hoverBlock.style.display = 'none'
   hoverBlock.children[0].style.opacity = 0.1
   hoverBlock.children[1].style.opacity = 0.1
   hoverBlock.children[2].style.opacity = 0.1
  }
  },
  resetTreeData() {
  if (this.targetId === undefined) return 
  const newList = []
  const curList = this.data.lists
  const _this = this
  function pushData(curList, needPushList) {
   for( let i = 0; i < curList.length; i++) {
   const item = curList[i]
   var obj = _this.deepClone(item)
   obj.lists = []
   if (_this.targetId == item.id) {
    const curDragItem = _this.getCurDragItem(_this.data.lists, window.dragId)
    if (_this.whereInsert === 'top') {
    curDragItem.parent_id = item.parent_id
    needPushList.push(curDragItem)
    needPushList.push(obj)
    } else if (_this.whereInsert === 'center'){
    curDragItem.parent_id = item.id
    obj.lists.push(curDragItem)
    needPushList.push(obj)
    } else {
    curDragItem.parent_id = item.parent_id
    needPushList.push(obj)
    needPushList.push(curDragItem)
    }
   } else {
    if (window.dragId != item.id)
    needPushList.push(obj)
   }
   if (item.lists && item.lists.length) {
    pushData(item.lists, obj.lists)
   }
   }
  }
  pushData(curList, newList)
  this.onDrag(newList)
  },
  deepClone (aObject) {
  if (!aObject) {
   return aObject;
  }
  var bObject, v, k;
  bObject = Array.isArray(aObject) ? [] : {};
  for (k in aObject) {
   v = aObject[k];
   bObject[k] = (typeof v === "object") ? this.deepClone(v) : v;
  }
  return bObject;
  },
  getCurDragItem(lists, id) {
  var curItem = null
  var _this = this
  function getchild(curList) {
   for( let i = 0; i < curList.length; i++) {
   var item = curList[i]
   if (item.id == id) {
    curItem = JSON.parse(JSON.stringify(item))
    break
   } else if (item.lists && item.lists.length) {
    getchild(item.lists)
   }
   }
  }
  getchild(lists)
  return curItem;
  }
 }
 }
</script>

row組件核心在于遞歸,并注冊拖拽事件,v-html支持傳入函數,這樣可以實現自定義展示,渲染數據時需要判斷是否有子節(jié)點,有的畫遞歸調用本身,并傳入子節(jié)點數據

結構如下

<template>
  <div class="tree-block" draggable="true" @dragstart="dragstart($event)"
   @dragend="dragend($event)">
   <div class="tree-row" 
    @click="toggle" 
    :tree-id="model.id"
    :tree-p-id="model.parent_id"> 
    <column
     v-for="(subItem, subIndex) in columns"
     v-bind:class="'align-' + subItem.align"
     :field="subItem.field"
     :width="subItem.width"
     :key="subIndex">
     <span v-if="subItem.type === 'selection'">
      <space :depth="depth"/>
      <span v-if = "model.lists && model.lists.length" class="zip-icon" v-bind:class="[model.open ? 'arrow-bottom' : 'arrow-right']">
      </span>
      <span v-else class="zip-icon arrow-transparent">
      </span>
      <span v-if="subItem.formatter" v-html="subItem.formatter(model)"></span>
      <span v-else v-html="model[subItem.field]"></span>

     </span>
     <span v-else-if="subItem.type === 'action'">
      <a class="action-item"
       v-for="(acItem, acIndex) in subItem.actions"
       :key="acIndex"
       type="text" size="small" 
       @click.stop.prevent="acItem.onclick(model)">
       <i :class="acItem.icon" v-html="acItem.formatter(model)"></i>&nbsp;
      </a>
     </span>
     <span v-else-if="subItem.type === 'icon'">
       {{model[subItem.field]}}
     </span>
     <span v-else>
      {{model[subItem.field]}}
     </span>
    </column>
    <div class="hover-model" style="display: none">
     <div class="hover-block prev-block">
      <i class="el-icon-caret-top"></i>
     </div>
     <div class="hover-block center-block">
      <i class="el-icon-caret-right"></i>
     </div>
     <div class="hover-block next-block">
      <i class="el-icon-caret-bottom"></i>
     </div>
    </div>
   </div>
   <row 
    v-show="model.open"
    v-for="(item, index) in model.lists" 
    :model="item"
    :columns="columns"
    :key="index" 
    :depth="depth * 1 + 1"
    v-if="isFolder">
   </row>
  </div>
  
 </template>
 <script>
 import column from './column.vue'
 import space from './space.vue'
 export default {
  name: 'row',
  props: ['model','depth','columns'],
  data() {
   return {
    open: false,
    visibility: 'visible'
   }
  },
  components: {
   column,
   space
  },
  computed: {
   isFolder() {
    return this.model.lists && this.model.lists.length
   }
  },
  methods: {
   toggle() {
    if(this.isFolder) {
     this.model.open = !this.model.open
    }
   },
   dragstart(e) {
    e.dataTransfer.setData('Text', this.id);
    window.dragId = e.target.children[0].getAttribute('tree-id')
    e.target.style.opacity = 0.2
   },
   dragend(e) {
    e.target.style.opacity = 1;
    
   }
  }
 }

clolmn和space比較簡單,這里就不過多闡述

上面就是整個實現過程,組件在chrome上運行穩(wěn)定,因為用H5的dragable,所以兼容會有點問題,后續(xù)會修改拖拽的實現方式,手動實現拖拽

總結

以上所述是小編給大家介紹的基于Vue實現可以拖拽的樹形表格實例詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!

相關文章

  • Vue.js實現價格計算器功能

    Vue.js實現價格計算器功能

    這篇文章主要為大家詳細介紹了Vue.js實現價格計算器功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Vue實現淘寶購物車三級選中功能詳解

    Vue實現淘寶購物車三級選中功能詳解

    這篇文章主要介紹了通過Vue實現淘寶購物車中三級選中的功能,文中的實現過程講解詳細,對我們學習Vue有一定的幫助,感興趣的可以了解一下
    2022-01-01
  • vue?data中的return使用方法示例

    vue?data中的return使用方法示例

    當一個組件被定義,data必須聲明為返回一個初始數據對象的函數,因為組件可能被用來創(chuàng)建多個實例,下面這篇文章主要給大家介紹了關于vue?data中return使用,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • Vue-Router的使用方法

    Vue-Router的使用方法

    這篇文章主要介紹了Vue-Router的使用方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • vue中項目如何提交form格式數據的表單

    vue中項目如何提交form格式數據的表單

    這篇文章主要介紹了vue中項目如何提交form格式數據的表單,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 在Vue中使用Echarts可視化庫的完整步驟記錄

    在Vue中使用Echarts可視化庫的完整步驟記錄

    這篇文章主要給大家介紹了關于在Vue中使用Echarts可視化庫的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • 淺談vue使用axios的回調函數中this不指向vue實例,為undefined

    淺談vue使用axios的回調函數中this不指向vue實例,為undefined

    這篇文章主要介紹了淺談vue使用axios的回調函數中this不指向vue實例,為undefined,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 基于elementUI實現圖片預覽組件的示例代碼

    基于elementUI實現圖片預覽組件的示例代碼

    這篇文章主要介紹了基于elementUI實現圖片預覽組件的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • vue?圖片路徑?“@/assets“?報錯問題及解決

    vue?圖片路徑?“@/assets“?報錯問題及解決

    這篇文章主要介紹了vue?圖片路徑?“@/assets“?報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue面包屑組件的封裝方法

    vue面包屑組件的封裝方法

    這篇文章主要為大家詳細介紹了vue面包屑組件的封裝方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評論

滦南县| 孟州市| 定南县| 社会| 庆云县| 偏关县| 金华市| 永登县| 溆浦县| 通河县| 台南县| 黔西县| 银川市| 鹤岗市| 通山县| 西华县| 老河口市| 乌恰县| 新疆| 石狮市| 临邑县| 和顺县| 彭泽县| 桑植县| 黑山县| 千阳县| 信丰县| 香河县| 特克斯县| 大冶市| 连山| 义乌市| 黄冈市| 巴马| 徐州市| 赤水市| 中牟县| 阜阳市| 宣汉县| 陕西省| 德令哈市|