基于Vue实现可以拖拽排序的树形表格

栏目: JavaScript · 发布时间: 6年前

内容简介:项目需要一个可以拖拽排序的树形表格,github上搜了一下,并为找到,大部分都不支持拖拽,所以自己实现了一个简单的组件,已开源数据源(lists)配置lists 配置示例

项目需要一个可以拖拽 排序 的树形表格,github上搜了一下,并为找到,大部分都不支持拖拽,所以自己实现了一个简单的组件,已开源 源代码在这里 ,并发布到npm上,如果有类似需求可以试一下,chrome上没有任何问题

效果图如下:

基于Vue实现可以拖拽排序的树形表格
drag-tree-table

使用方式

npm i drag-tree-table --save-dev
复制代码
import dragTreeTable from 'drag-tree-table'
复制代码
<dragTreeTable :data="treeData" :onDrag="onTreeDataChange"></dragTreeTable>
复制代码
// state
treeData: {
  lists: [],
  columns: []
}
// methods
onTreeDataChange(list) {
  this.treeData.lists = list
},
复制代码

数据源(lists)配置

参数 可选值 描述
id String 唯一标志
parent_id String 父节点ID
order Number 排序,0开始,onDrag后order会重置
name String 默认显示内容
open Boolean true展开,false收起
lists Array 子节点

lists 配置示例

[
 {
   "id":40,
   "parent_id":0,
   "order":0,
   "name":"动物类",
   "uri":"/masd/ds",
   "open":true,
   "lists":[]
 },{
   "id":5,
   "parent_id":0,
   "order":1,
   "name":"昆虫类",
   "uri":"/masd/ds",
   "open":true,
   "lists":[
     {
       "id":12,
       "parent_id":5,
       "open":true,
       "order":0,
       "name":"蚂蚁",
       "uri":"/masd/ds",
       "lists":[]
     }
   ]
 },
 {
   "id":19,
   "parent_id":0,
   "order":2,
   "name":"植物类",
   "uri":"/masd/ds",
   "open":true,
   "lists":[]
 }
]
复制代码

列(columns)配置

参数 可选值 描述
type 'selection', 'actions' selection会显示折叠图标,actions指操作栏
title String 表格标题
field String 单元格内容取值使用
width Number 单元格宽度
align left,center,right 单元格对齐方式,默认局左对齐
formatter Function 自定义单元格显示内容,参数为当前行数据

columns 配置示例

[
 {
   type: 'selection',
   title: '菜单名称',
   field: 'name',
   width: 200,
   align: 'center',
   formatter: (item) => {
     return '<a>'+item.name+'</a>'
   }
 },
 {
   title: '链接',
   field: 'url',
   width: 200,
   align: 'center'
 },
 {
   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>'
       }
     }
   ]
 },
]
复制代码

实现原理

组件结构

dragTreeTable.vue是入口组件,定义整体结构
row是递归组件(核心组件)
clolmn单元格,内容承载
space控制缩进
复制代码

核心点

1. 核心组件row,通过组件递归自己实现树形结构,并非用JSX方式
2. 通过v-html传入函数,返回自定义html,实现单元格内容自定义
3. 通过H5的新特性draggable,实现拖拽,拖拽的同时需要匹配对应的行,并区分上/中/下,分别对应上移/下移/中间插入,并高亮,drop时对数组进行重新排列,生成新的数组
复制代码

递归调用写法如下

<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>
复制代码

动态匹配实现

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) {
              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
          }
        }
      }
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

伟大创意的诞生

伟大创意的诞生

史蒂文·约翰逊 (Steven Johnson) / 盛杨燕 / 浙江人民出版社 / 2014-8-1 / CNY 52.90

 大家都认得出好创意。印刷机、铅笔、抽水马桶、电池、互联网、GPS、Google——这些都是绝妙的创意。然而,它们是如何产生的?被喻为“科技界的达尔文”的作者,在《伟大创意的诞生》一书中,提供了深具启示意义以及有论证实据的答案。  作者史蒂文•约翰逊以富有感染力、包罗万象的风格游历于多重领域,从神经生物学、都市研究,到网络文化,找出了独特创新背后的7大关键模式,深入人类600年重要发明的......一起来看看 《伟大创意的诞生》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具