国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > 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實現可以拖拽的樹形表格實例詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 汽车| 新平| 文安县| 九台市| 靖西县| 北京市| 忻城县| 庐江县| 孝昌县| 吉隆县| 阿尔山市| 寿光市| 新邵县| 东明县| 栾川县| 霞浦县| 建阳市| 嘉善县| 丰宁| 汝州市| 潼南县| 西华县| 西贡区| 宁波市| 沁阳市| 陆河县| 泌阳县| 温州市| 曲沃县| 繁昌县| 罗源县| 永胜县| 平顶山市| 南平市| 昌平区| 南充市| 麻江县| 乌鲁木齐市| 达日县| 类乌齐县| 体育|