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 } ] }
相关文章推荐
- element-ui表格合并span-method的实现方法
- elementUI table表格动态合并的示例代码
- CakePHP中使用Paginator组件(Helper)实现表格的翻页及排序
- iOS 使用UILocalizedIndexedCollation实现区域索引标题(Section Indexed Title)即拼音排序
- vue+element UI实现表格中动态添加开关控制按钮
- vuedraggable+element ui实现页面控件拖拽排序效果
- Vue.js实现可排序的表格组件功能示例
- element-ui表格多选实现翻页选择记忆功能
- element-ui表格使用前台排序,当table部分数据为空null时,解决排序不起作用、排序错乱和排序不整齐的问题
- C#自定义网格组件(DataGridView)实现数据分组、排序
- vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能
- [置顶] element-ui 表格实现单元格可编辑的方法
- Vue2.0+ElementUI+PageHelper实现的表格分页
- element-ui 表格实现单元格可编辑的示例
- 使用element UI el-upload组件实现视频文件上传及上传进度显示方法总结
- element ui table(表格)实现点击一行展开功能
- 列排序jQuery+Ajax实现表格数据不同列标题排序
- elementUI el-table表格列排序的两种方法
- element UI table表格组件使用[超级完整功能]
- Vue整合Element-UI的分页组件实现分页