二次封装Element-UI 的 Table 组件,添加列拖拽功能

二次封装Element-UI 的 Table 组件,添加列拖拽功能,根据https://www.cnblogs.com/wisewrong/p/8820508.html改编,

1.继承Element-UI 的 Table 组件,所有属性方法可以继承使用

2.修正当出现水平滚动条或有固定列并滚动后,拖动列时出现的虚线位置不正确

3.改善当需要调整列大小时,鼠标经过表对列边缘难以出现调整标志(因为修改后的表头大小自动填满了,都变成是拖动列标志)

demo:

https://output.jsbin.com/xotidip/1

效果:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8" />
		<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
		<meta content="yes" name="apple-mobile-web-app-capable">
		<meta content="black" name="apple-mobile-web-app-status-bar-style">
		<title>header Dragend 拖动列改变位置或调整大小</title>
		<link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.5.4/theme-chalk/index.css">
		<link rel="stylesheet" href="table_drag.css">
	</head>
	<body>
		<div id="app" class="text-center" style="margin-top: 0px">
			<template>
				<div class="app-container">
					<w-table v-loading="listLoading" :data="tableData" :header="tableHeader" element-loading-text="Loading" border fit
					 highlight-current-row>
						<el-table-column slot="fixed" :key="'fix'" fixed prop="date" label="日期" width="150">
						</el-table-column>
						<el-table-column :key="'fix2'" slot="fixed" fixed="right" label="操作" width="100">
							<template slot-scope="scope">
								<el-button type="text" size="small">编辑</el-button>
							</template>
						</el-table-column>
					</w-table>
				</div>
			</template>
		</div>
	</body>

	<script src="https://cdn.bootcss.com/vue/2.5.18/vue.min.js"></script>
	<script src="https://cdn.bootcss.com/element-ui/2.5.4/index.js"></script>

	<script type="text/javascript">
		Vue.component("wTable", {
			props: {
				data: {
					default: function() {
						return []
					},
					type: Array
				},
				header: {
					default: function() {
						return []
					},
					type: Array
				},
			},
			data() {
				return {
					tableHeader: this.header,
					dragState: {
						start: -9, // 起始元素的 index
						end: -9, // 移动鼠标时所覆盖的元素 index
						dragging: false, // 是否正在拖动
						direction: undefined // 拖动方向
					},
					// 记录左边的固定列的列数
					fixedCount: -1,
					formThead: this.header
				}
			},
			mounted() {
				const that = this;
				that.$nextTick(() => {

				});
			},
			watch: {
				header(val, oldVal) {
					this.formThead = val
				},
			},
			methods: {
				// 当更列位置变化后,有的列没有指定宽度或最小宽度,交换后,发现列表头的siz会自动为span的宽度
				// 所以事后恢复为列位置变化前的那个宽度
				fixMinWidth() {
					const that = this;
					let item = null,
						colList = null,
						fixCount = that.fixedCount;

					if (fixCount < 0) {
						fixCount = this.getFixedCount()
					}
					for (let i = 0; i < that.formThead.length; i++) {
						item = that.formThead[i];
						if (!("minWidth" in item)) {
							if (!colList) {
								let colgroup = this.$el.querySelector(".el-table__header-wrapper").querySelector("colgroup");
								colList = colgroup.children;
							}
							currentCol = colList[i + fixCount];
							item.minWidth = +currentCol.width;
						}
					}
				},
				// 计算左边的固定列,有多少列
				getFixedCount() {
					//el-table__fixed
					const that = this;
					let fixedTable = that.$el.querySelector(".el-table__fixed");
					if (fixedTable) {
						// 查找所有不包含类.is-hidden的th
						let thList = fixedTable.querySelector(".el-table__fixed-header-wrapper").querySelectorAll("th:not(.is-hidden)");
						that.fixedCount = thList.length;
					} else {
						that.fixedCount = 0;
					}
					return that.fixedCount;
				},
				//table.$emit('header-dragend', column.width, startLeft - startColumnLeft, column, event);
				//headerDragend(tNewWidth, tOldWidth, tColumn, tEvent) {},
				// 按下鼠标开始拖动
				handleMouseDown(e, column, tIdx) {
					const that = this;
					tIdx += that.getFixedCount();
					that.dragState.dragging = true
					that.dragState.start = tIdx

					that.fixMinWidth();
					// 给拖动时的虚拟容器添加宽高,因为virtual是v-if ="dragState.dragging"的,所以没有那么展现
					that.$nextTick(() => {
						const table = that.$el,
							virtual = table.getElementsByClassName('virtual'),
							scrollLeft = table.querySelector(".el-table__body-wrapper").scrollLeft;

						for (const item of virtual) {
							item.style.height = table.clientHeight - 1 + 'px'
							item.style.width = item.parentElement.parentElement.clientWidth + 'px'
							if (scrollLeft > 0) {
								//这里减10,是猜的,因为css 里面.el-table th .virtual的marginLeft也是-10
								item.style.marginLeft = -scrollLeft - 10 + "px"
							}

						}
					});


					document.addEventListener('mouseup', that.handleMouseUp)

				},

				// 鼠标放开结束拖动
				handleMouseUp() {
					if (this.dragState.direction != undefined) {
						this.dragColumn(this.dragState)
					}

					// 初始化拖动状态
					this.dragState = {
						start: -9,
						end: -9,
						dragging: false,
						direction: undefined
					}
					document.removeEventListener('mouseup', this.handleMouseUp)
				},

				// 拖动中
				handleMouseMove(e, column, tIdx) {
					if (this.dragState.dragging) {
						tIdx += this.getFixedCount();
						// 记录起始列
						const index = tIdx
						if (index - this.dragState.start !== 0) {
							this.dragState.direction = index - this.dragState.start < 0 ? 'left' : 'right' // 判断拖动方向
							this.dragState.end = index
						} else {
							this.dragState.direction = undefined
						}
					} else {
						return false
					}
				},

				// 拖动易位
				dragColumn({
					start,
					end,
					direction
				}) {
					// fixCount,左边的固定列个数
					let fixCount = this.fixedCount,
						item = null;

					if (fixCount < 0) {
						fixCount = this.getFixedCount()
					}

					start = start - fixCount;
					end = end - fixCount;

					const tempData = []
					//const left = direction === 'left'
					const left = (direction === 'left') ? -1 : 1;
					const min = (left <= 0) ? end : start - 1
					const max = (left <= 0) ? start + 1 : end

					let colList = null,
						currentIndex = null,
						currentCol = null;
					for (let i = 0; i < this.formThead.length; i++) {
						if (i === end) {
							currentIndex = start;
							item = this.formThead[start];
						} else if (i > min && i < max) {
							currentIndex = i + left;
							item = this.formThead[currentIndex];
						} else {
							currentIndex = i;
							item = this.formThead[i];
						}
						/*
												if (!("minWidth" in item)) {
													if (!colList) {
														let colgroup = this.$el.querySelector(".el-table__header-wrapper").querySelector("colgroup");
														colList = colgroup.children;
													}
													currentCol = colList[currentIndex + fixCount];
													item.minWidth = currentCol.width;
												}
												*/
						tempData.push(item)
					}
					this.formThead = tempData
				},
				headerCellClassName({
					column,
					columnIndex
				}) {
					const active = columnIndex === this.dragState.end ? `darg_active_${this.dragState.direction}` : ''
					const start = columnIndex === this.dragState.start ? `darg_start` : ''
					return `${active} ${start}`
				},
				cellClassName({
					column,
					columnIndex
				}) {
					return (columnIndex === this.dragState.start ? `darg_start` : '')
				}
			},


			template: `
				<el-table
					v-bind:class="{'w-table_moving':dragState.dragging}"
					v-bind="$attrs" 
					v-on="$listeners"					
					:data="data"
					:cell-class-name="cellClassName"
					:header-cell-class-name="headerCellClassName">
				  <slot name="fixed"></slot>
				  <el-table-column
					v-for="(col, index) in formThead"
					:key="index"
					:prop="col.prop"
					:label="col.label"
					:width="col.minWidth"
					:min-width="col.minWidth"
					:type="col.type"
					:header-align="col.headerAlign"
					:column-key="(index).toString()"					
					show-overflow-tooltip>
					
					<template slot="header" slot-scope="scope">
						<div class="thead-cell" 
						@mousedown="handleMouseDown($event, col,index)"
						@mousemove="handleMouseMove($event, col,index)"
						>
							<a>{{col.label}}</a>
							<span class="virtual" v-if="dragState.dragging"></span>
						</div>					
					</template>
					<template slot-scope="scope">
					  {{ scope.row[col.prop] }}
					</template>
				  </el-table-column>

				  <slot name="handled"/>
				</el-table>
			  
			`,
		})


		new Vue({
			el: "#app",
			data() {
				return {
					tableData: [{
						date: '2016-05-03',
						name: '王小虎',
						province: '上海',
						city: '普陀区',
						address: '上海市普陀区金沙江路 1518 弄',
						zip: 200333
					}, {
						date: '2016-05-02',
						name: '王小虎',
						province: '上海',
						city: '普陀区',
						address: '上海市普陀区金沙江路 1518 弄',
						zip: 200333
					}, {
						date: '2016-05-04',
						name: '王小虎',
						province: '上海',
						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-06',
						name: '王小虎',
						province: '上海',
						city: '普陀区',
						address: '上海市普陀区金沙江路 1518 弄',
						zip: 200333
					}],


					listLoading: false,

					tableHeader: [{
						prop: 'name',
						label: '姓名',
						sortable: true,
						sortMethod: this.handleNameSort
					}, {
						prop: 'province',
						label: '省份',
						minWidth: '120'
					}, {
						prop: 'city',
						label: '市区',

					}, {
						prop: 'address',
						label: '地区',
						minWidth: '150'
					}, {
						prop: 'zip',
						label: '邮编',
						minWidth: '120'
					}],
				}
			}
		});
	</script>
</html>

css:

.el-table .darg_start {
	background-color: #f3f3f3;
}

.el-table th {
	padding: 0;
}

.el-table th .virtual {
	position: fixed;
	display: block;
	width: 0;
	height: 0;
	margin-left: -10px;
	background: none;
	border: none;
}
/*竖线虚线*/
.el-table .el-table__header-wrapper th.darg_active_left .virtual {	
	border-left: 2px dotted #666;
	border-color: #2196F3;
	z-index: 99;
}

.el-table th.darg_active_right .virtual {	
	border-right: 2px dotted #666;
	z-index: 99;
}

.el-table .thead-cell {
	padding: 0;	
	display: inline-flex;
	flex-direction: column;
	align-items: left;
	cursor: pointer;
	overflow: initial;
	
}
/*
.el-table .thead-cell:before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	bottom: 0;
	right: 0;
}
*/
.w-table_moving.el-table th .thead-cell {
	cursor: move !important;
}

.w-table_moving .el-table__fixed, .w-table_moving .el-table__fixed-right{
	cursor: not-allowed;
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值