您的位置:首页 > 产品设计 > UI/UE

element-ui表格拖曳标题分组合并排序组件的实现

2019-02-20 15:37 671 查看
效果预览

拖曳功能参考 https://github.com/qgh810/dnd + https://github.com/hilongjw/vue-dragging

数组排序使用插件 array-sort 

思路:

      通过el-table的header自定义,搭配拖动插件,使其可被拖动,拖动完成时触发的事件排序并改写每个单元格的的rowspan

  • 根据以上安装依赖,以及引入
  • 创建组件GroupTable,置于根目录components

  • 创建标题拖入区域,使用拖曳组件
[code]<div class="g-table" v-if="hackReset"> <!-- hackReset用于强制刷新表格组件 -->

<!-- 标题拖曳进入处 -->
<droppable
class="drop-box"
v-if="option.useDrag" <!-- 通过父组件传入的参数控制是否需要拖曳功能 -->
@dragstart="onDragStart"
@dragend="onDragEnd"
@drop="onDrop"
@dragenter="onDragEnter"
@dragover="onDragOver"
@dragleave="onDragLeave">

<!--空白时提示,有标题拖入后消失-->
<span v-show="groupHeaders.length === 0" class="droppable-placeholder">拖入列名</span>

<!-- 拖入的并非是原本的dom,而是在拖入事件触发时,根据拖入标题dom附带的信息在groupHeaders中push一个标签,delete操作也是相对于groupHeaders -->
<!-- 使用v-dragging使得groupHeaders内的元素可被拖动排序 -->
<div class="droppable-tag" v-for="(item, index) in groupHeaders" :key="index"
v-dragging="{ item: item, list: groupHeaders, group: groupId}"><!--groupId需要全局唯一,代码中使用当前时间控制-->
<span class="droppable-tag-span">{{item.label}}</span>
<el-button type="danger" size="mini" icon="el-icon-delete"
@click="deleteTag(index)"></el-button>
</div>
</droppable>
<el-table/><!--见下文-->
</div>

droppable组件的配置沿用文档中的,在method中配置使标题保持唯一

[code]onDrop (params) {
// console.log('监听到被拖动元素放下')
if (this.groupHeaders.filter(item => item.label === params.data.label).length === 0) {
this.groupHeaders.push(params.data)
}
},
  •  el-table表格组件配置
[code]<el-table
ref="elTable"
:data="tableData"
:highlight-current-row="option.select || false"
:border="option.border"
:height="height || option.height"
:style="{ width: parseInt(option.width)+'px' }"
:stripe="option.stripe"
:span-method="cellMethod"
@current-change="handleCurrentChange"

>
<!-- 表格的 header 内容是由父组件传入的 -->
<el-table-column v-for="(col, index) in header" :key="index"
:prop="col.prop"
:label="col.label"
:width="col.width"
:min-width="col.minWidth"
:sortable="!option.useDrag ? col.sortable: false"
>

<!-- 原本使用的 header-render,看到vue的推荐提示后改成 slot header -->
<!-- header 模板 -->
<template slot="header">
<draggable v-if="col.dragGroup && option.useDrag"
dragstart='onHeaderDragStart' dragsend='onHeaderDragEnd'
class='draggable-header' :data="col">
<i class="el-icon-rank"></i>
<span>{{col.label}}</span>
</draggable>
<span v-else>{{col.label}}</span>
<!-- 通过父组件配置,判断是否是需要拖动的标题 -->

</template>
<template slot-scope="scope">
<!-- 单元格模板 -->
<cell :scope="scope" :col="col" :label="scope.row[col.prop]">
</cell>
</template>
</el-table-column>
</el-table>
  • js部分
[code]import Draggable from './draggable'
import Droppable from './droppable'

export default {
name: 'GTable',
components: {
Draggable,
Droppable,
cell: {
props: {
label: {
default: () => {
return ''
},
type: String
},
scope: {
default: () => {
return {}
},
type: Object
},
col: {
default: () => {
return null
},
type: Object
}
},
render: function (h) {
// 如果未定义render,则直接展示数据,否则调用父节点定义的render
return this.col.render ? this.col.render(h, this.scope) : h('span', this.label)
}
}
},
props: {
height: {
default: () => {
return 0
},
type: Number
},
data: {
default: () => {
return []
},
type: Array
},
header: {
default: () => {
return []
},
type: Array
},
option: {
default: () => {
return {}
},
type: Object
},
pagination: {
default: () => {
return {}
},
type: Object
}
},
data () {
return {
// tableHeader: this.header,
groupHeaders: [],
groupId: '',
headArr: [],
hackReset: true,
hoverOrderArr: []
}
},
created () {
this.groupId = (new Date()).valueOf
},
watch: {
/* header(val, oldVal) {
this.tableHeader = val
}, */
groupHeaders (val, oldVal) {
// console.log(1)
this.resetGroup()
this.headArr = []
for (const head of val) {
this.headArr.push(head.prop)
}
}/* ,
header(val, oldVal) {

} */
},
computed: {
tableData () {
// console.log(this.groupHeaders.length)
let old_data = this.data
if (this.option.filter) {
old_data.filter(this.option.filter)
}
const new_data = (this.pagination && this.pagination.status === true) ? JSON.parse(JSON.stringify(old_data.slice((this.pagination.currentPage - 1) * this.pagination.pageSize, this.pagination.currentPage * this.pagination.pageSize))) : old_data
if (this.groupHeaders.length > 0) {
this.resetHack()
return this.setSpan(new_data)
} else {
return new_data
}
}
},
methods: {
handleCurrentChange (row) {
// console.log(row)
if (this.option.select) {
this.$emit('change-chose-row', row)
}
},
currentChange (page) {
this.pagination.currentPage = page
if (this.pagination.currentChange) {
this.pagination.currentChange()
}
},
sizeChange (pageSize) {
this.pagination.pageSize = pageSize
},
resetHack () {
this.hackReset = false
this.$nextTick(() => {
this.hackReset = true
})
},
onDrop (params) {
// console.log('监听到被拖动元素放下')
if (this.groupHeaders.filter(item => item.label === params.data.label).length === 0) {
this.groupHeaders.push(params.data)
}
},
cellMethod ({ row, column, rowIndex, columnIndex }) {
return row._span ? (row._span[column.property] || {
rowspan: 1,
colspan: 1
}) : {
rowspan: 1,
colspan: 1
}
},
deleteTag (index) {
this.groupHeaders.splice(index, 1)
},
resetGroup () {
if (this.groupHeaders.length > 0) {
for (const head of this.header) {
head._index = 0
}
let index = 1
for (const tag of this.groupHeaders) {
for (const head of this.header) {
// console.log(tag.prop)
if ((tag.prop === head.prop) && !head._index) {
head._index = index
index++
}
}
}
for (const head of this.header) {
if (!head._index) {
head._index = index
index++
}
}

this.header.sort((a, b) => a._index > b._index)
// this.resetData()
}
},
groupData (array, f) {
const groups = {}
array.forEach(function (o) {
const group = JSON.stringify(f(o))
groups[group] = groups[group] || []
groups[group].push(o)
})
return Object.keys(groups).map(function (group) {
return groups[group]
})
},
cleanChose () {
this.$refs.elTable.setCurrentRow()
},
setSpan (data) {
const temp_arr = []
// const reverse_head_arr = this.headArr.reverse()
const new_data = this.$arraySort(data, this.headArr)
// console.log(new_data)
for (const prop of this.headArr) {
temp_arr.push(prop)
this.groupData(new_data, function (item) {
const arr = []
temp_arr.forEach(head => {
arr.push(item[head])
})
return arr
}).forEach(item => {
item.forEach((item1, i) => {
if (!item1._span) {
item1._span = {}
}
item1._span[pro
1e92c
p] = {
count: item.length
}
})
})
let count = 0
new_data.forEach((item, index) => {
if (count === 0) {
count = item._span[prop].count
item._span[prop].rowspan = item._span[prop].count
item._span[prop].colspan = 1
} else {
item._span[prop].count = count
item._span[prop].rowspan = 0
item._span[prop].colspan = 0
}
// item._span[prop].rowspan = 1
// item._span[prop].colspan = 1
count--
})
}

return new_data
},
show (something) {
console.log(something)
}
}
}
  • 父组件调用
[code]<g-table :data="table.data" :header="table.header" :option="table.option"
:height="fullHeight-730"  ref="gTable"></g-table>
[code]table: {
option: {
border: true,
select: true,
useDrag: true // 是否需要拖动
},
header: [
{
prop: 'date',
label: '日期',
width: '155',
dragGroup: true // 是否需要拖动
}, {
prop: 'name',
label: '姓名',
minWidth: '80',
dragGroup: true
}, {
prop: 'province',
label: '省份',
minWidth: '80',
dragGroup: true
}, {
label: '操作',
minWidth: '100',
render: (createElement, scope) => { // 定义该列单元格的 render, jsx
return (
<el-button size='mini' onClick={() => this.show(scope)}>显示</el-button>
)
}
}
],
data: [
{
date: '2016-05-01',
name: '王小虎1',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-02',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-03',
name: '王小虎',
province: '上海1',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-02',
name: '王小虎',
province: '上海1',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-02',
name: '王小虎',
province: '上海1',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-02',
name: '王小虎',
province: '上海1',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎1',
province: '上海2',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-03',
name: '王小虎2',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎2',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎1',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎1',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
name: '王小虎1',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
zip: 200333
}
]
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: