亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

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

2019-11-19 12:41:05
字體:
來源:轉載
供稿:網友

因業務需求,需要一個樹形表格,并且支持拖拽排序,任意未知插入,github搜了下,真不到合適的,大部分樹形表格都沒有拖拽功能,所以決定自己實現一個。這里分享一下實現過程,項目源代碼請看github,插件已打包封裝好,發布到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在表格拖拽時觸發,返回新的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觸發時調用,該方法會重新生成一個新的排完序的數據,然后返回父組件

下面是所有實現代碼

<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支持傳入函數,這樣可以實現自定義展示,渲染數據時需要判斷是否有子節點,有的畫遞歸調用本身,并傳入子節點數據

結構如下

<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>       </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上運行穩定,因為用H5的dragable,所以兼容會有點問題,后續會修改拖拽的實現方式,手動實現拖拽

總結

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
在线看欧美日韩| 91美女片黄在线观| 国产成+人+综合+亚洲欧美丁香花| 91av在线视频观看| 韩日精品中文字幕| 久久91亚洲人成电影网站| 亚洲综合视频1区| 精品国偷自产在线| 97在线精品国自产拍中文| 欧美激情一级欧美精品| 韩曰欧美视频免费观看| 色综合亚洲精品激情狠狠| 亚洲一区二区免费在线| 精品自在线视频| 久久亚洲欧美日韩精品专区| 日韩精品视频在线观看免费| 精品偷拍一区二区三区在线看| 91在线无精精品一区二区| 日韩欧美极品在线观看| 亚洲xxxx视频| 久久精品一区中文字幕| 另类少妇人与禽zozz0性伦| 亚洲激情成人网| 成年人精品视频| 久久精品国产综合| 亚洲福利影片在线| 亚洲天堂av在线免费| 7777免费精品视频| 国产亚洲精品激情久久| 色偷偷av亚洲男人的天堂| 91精品久久久久| 亚洲黄在线观看| 亚洲最大成人在线| 国内揄拍国内精品少妇国语| 国产成人av网址| 国产成人午夜视频网址| 国产精品欧美亚洲777777| 91国产精品91| 日韩免费在线播放| 91成人福利在线| 国产成人精品国内自产拍免费看| 91国内精品久久| 中文字幕亚洲欧美日韩2019| 欧美精品videossex88| 久久影视免费观看| 国产精品爽爽爽爽爽爽在线观看| 亚洲国产精品va在线观看黑人| 国产精品福利网站| 亚洲黄页网在线观看| 日韩欧美在线中文字幕| 精品毛片网大全| 亚洲女性裸体视频| 爽爽爽爽爽爽爽成人免费观看| 亚洲人成电影网站色| 日韩av成人在线| 91av在线免费观看视频| 欧美电影免费在线观看| 奇米影视亚洲狠狠色| 国产精品日韩av| 色偷偷偷综合中文字幕;dd| 国产精品极品尤物在线观看| xxxxx91麻豆| 欧美激情精品久久久久久免费印度| 国产精品入口夜色视频大尺度| 久久久久久一区二区三区| 91成人性视频| 欧美性资源免费| 91在线精品视频| 98午夜经典影视| 色偷偷噜噜噜亚洲男人的天堂| 久久99视频精品| 欧美亚洲激情在线| 91中文字幕在线观看| 日本久久久a级免费| 91日韩在线播放| 日韩av电影院| 久久九九全国免费精品观看| 国产精品夫妻激情| 亚洲视频一区二区三区| 亚洲欧美国产一本综合首页| 日本韩国欧美精品大片卡二| 成人动漫网站在线观看| 亚洲第一区在线观看| 深夜福利亚洲导航| 久久影视电视剧凤归四时歌| 国产精品吊钟奶在线| 中文字幕视频一区二区在线有码| www.日本久久久久com.| 精品国产一区二区三区久久狼黑人| 亚洲国产日韩欧美在线动漫| 日韩av大片免费看| 韩日欧美一区二区| 日韩欧美在线观看| 欧美激情精品久久久久久蜜臀| 欧美性xxxxhd| 国产最新精品视频| 午夜精品一区二区三区在线| 久久九九有精品国产23| 成人字幕网zmw| 97色伦亚洲国产| 国产精品网红福利| 午夜剧场成人观在线视频免费观看| 亚洲成人激情视频| 精品精品国产国产自在线| 欧美国产日韩视频| 精品国产成人av| 亚洲国产黄色片| 国产精品久久色| 国产一区二区三区直播精品电影| 97成人精品视频在线观看| 欧美大片免费观看| 日韩黄色在线免费观看| 在线性视频日韩欧美| 亚洲r级在线观看| 久久99精品视频一区97| 在线日韩欧美视频| 成人亲热视频网站| 日韩精品极品在线观看| 国产一区二区在线免费视频| 91精品视频大全| 欧美老肥婆性猛交视频| 日韩精品免费观看| 亚洲一区二区三区777| 成人午夜激情网| 欧美性极品xxxx做受| 亚洲第一网站男人都懂| 日韩精品在线看| 在线不卡国产精品| 精品久久久香蕉免费精品视频| 黄色一区二区在线观看| 亚洲人成亚洲人成在线观看| 日韩欧美aaa| 亚洲精品美女网站| 国产一区二区三区四区福利| 在线电影av不卡网址| 国产成人精品日本亚洲| 亚洲性日韩精品一区二区| 91免费电影网站| 中文字幕精品在线| 在线看福利67194| 国产高清在线不卡| 国产精品美女www爽爽爽视频| 中文字幕国内精品| 亚洲伦理中文字幕| 亚洲国产精品成人精品| 精品动漫一区二区三区| 欧美日韩国产综合新一区| 亚洲成色www8888| 98精品国产高清在线xxxx天堂| 亚洲精品久久久久中文字幕二区| 欧美久久精品一级黑人c片| 尤物tv国产一区| 久久久国产精彩视频美女艺术照福利| 中文字幕欧美日韩va免费视频| 国产午夜精品全部视频播放| 北条麻妃一区二区在线观看| 911国产网站尤物在线观看| 北条麻妃在线一区二区| 欧美性猛交xxxx乱大交极品| 国产精品va在线播放| 日韩女在线观看| www欧美xxxx| 少妇高潮 亚洲精品| 韩国精品美女www爽爽爽视频|