vxe-table 鼠标滑动选择多行,鼠标区域选中批量操作[2]

前几天写了一个关于 vxe-table 鼠标滑动选择多行 的博客,在项目上线的过程中,发现这个功能还是有点bug,在经过我对vxe-table pro版本 的演示后vxe-table PRO,认真调试后,终于解决了bug,并且这个功能和pro版本可以说是几乎一模一样。注意:我是说这个滑动选择的功能,不是说是pro版本的所有功能,哈哈哈。

我VUE用的是2.x版本.

如果是VUE是3版本,请参考下面这篇博客。

Vue3 vxe-table 手写鼠标区域选中-CSDN博客

下面是官网的pro版本示例

这是我的示例,是不是感觉一样呢。demo 看在线演示。

 接下来我来展示我的实现方法。

先说下思路吧:

1.鼠标滑动时显示的那个框,实际上是一个 div,所以需要定义div。

2.用户进行滑动操作,肯定需要给表格添加鼠标移动监听事件。

3.用户从什么时候位置开始移动鼠标,什么时候结束移动鼠标。需要记录那个框的开始位置和结束位置。

4.最后在用户鼠标移动结束后,显示这个框就行了。

开始实现:

1.先来一个最基础的vxe-grid表格,相信大家都清楚吧。

<template>
	<div>
<!-- 注意这里的ref名称,后续都会用到的 -->
		<vxe-grid ref='xGrid' v-bind="gridOptions" height="400px">
			
		</vxe-grid>
	</div>
</template>

<script>
	export default {
		data() {
			return{
				gridOptions:{
					//行配置,这里的行高一定需要指定
					"row-config":{isCurrent:true,height:35},
					//左上角按钮
					toolbarConfig:{
						perfect: true,
						enabled:true,
						size:"mini",
						buttons:[
							{
								code:'getcellselctdata',type:"text",name:'获取选中数据'
							}
						],
					},
					//列配置 (使用列拖拽功能,必须配置useKey为true)
					"column-config":{resizable:true,useKey:true},
					//边框
					border:"full",
					//斑马纹
					stripe:true,
					//列信息
					columns:[
						{width:70,field:"id",title:"#",align:"left",fixed:"left"},
						{width:100,field:"name",title:"姓名",align:"left",fixed:"left"},
						{width:100,field:"age",title:"年龄",align:"left",fixed:"left"},
						{width:100,field:"sex",title:"性别",align:"left"},
						{width:100,field:"job",title:"岗位",align:"left"},
						{width:270,field:"address",title:"地址",align:"left"},
					],
					//数据
					data:[
						{id:1,name:"张三",age:30,sex:"男",job:"前端",address:"中国xxxxxxxxxx"},
						{id:2,name:"李四",age:30,sex:"男",job:"后端",address:"中国xxxxxxxxxx"},
						{id:3,name:"王五",age:30,sex:"女",job:"运维",address:"中国xxxxxxxxxx"},
						{id:4,name:"赵六",age:30,sex:"男",job:"美工",address:"中国xxxxxxxxxx"},
						{id:5,name:"老八",age:30,sex:"男",job:"项目经理",address:"中国xxxxxxxxxx"},
						{id:6,name:"桀桀",age:30,sex:"女",job:"售后",address:"中国xxxxxxxxxx"},
					],
					//这里一定要指定true,否则row-config的height没用
					'show-overflow':true,
					//行配置,这里的行高一定需要指定
					"row-config":{isCurrent:true,height:35,isHover:true},
				},
			}
		},
		mounted() {
		},
		methods: {
		}
	}
</script>

<style scoped>

</style>

显示的效果是这样的(下图),并且滑动选中是浏览器默认的选中效果。

 2.清除浏览器的默认选中行为:

.vxe-grid{
		-webkit-user-select: none; /* Safari */
		-moz-user-select: none; /* Firefox */
		-ms-user-select: none; /* Internet Explorer/Edge */
		user-select: none;
	}

加上这个css代码,这样在鼠标选中时就不会出现刚刚那个蓝色的背景色了,这一步很重要哦。

需要全局的也可以写到app.vue中。

3.定义div。也就是我所说的框。并且在官网示例中也是这样做的,大家可以放心。

我这里是定义了两个框。分为非fixed和fixed区域的框。

<!-- 注意这里的ref名称,后续都会用到的 -->
<!-- 正常区域的框 -->
		<div class="vxe-table--cell-area" ref="cellarea">
			<span class="vxe-table--cell-main-area"></span>
			<span class="vxe-table--cell-active-area"></span>
		</div>
<!-- 注意这里的ref名称,后续都会用到的 -->
		<!-- 左边fixed区域的框,右边fixed没有尝试过。。。 -->
		<div class="vxe-table--cell-area" ref="fixedcellarea">
			<span class="vxe-table--cell-main-area"></span>
			<span class="vxe-table--cell-active-area"></span>
		</div>

4.给表格添加监听(用户鼠标操作),记录用户鼠标开始操作的位置和结束的位置。

在data中定义所需的变量,记录用户鼠标滑动开始位置和结束位置。

isSelecting: false, // 是否正在进行选择操作
				selectionStart:{rowIndex:-1,cellIndex:-1}, // 选择操作起始单元格位置
				selectionEnd:{rowIndex:-1,cellIndex:-1}, // 选择操作结束单元格位置

 然后给表格添加事件监听(这里的代码不做过多的解释,大家可以看看注释):

methods里面是11个方法,在mounted里面调用addListener方法就欧克了。

 接下来给出这11个方法的代码,有兴趣的可以自己的需求进行修改。

//返回table的ref名称
			getTablexGrid(){
				return this.$refs.xGrid;
			},
			//添加事件
			addListener(){
				//添加多选列
				this.$nextTick(()=>{
					let tbody=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper table tbody");
					if(tbody){
						tbody.addEventListener("mousedown",this.tbodymousedown);
						tbody.addEventListener("mouseup",this.tbodymouseup);
						tbody.addEventListener("mousemove",this.tbodymousemove);
					}
					let bodyWrapper=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper");
					if(bodyWrapper){
						//注意这里的ref名称,这里是非fixed区域的框的名称
						bodyWrapper.appendChild(this.$refs.cellarea);
					}
					
					setTimeout(()=>{
						let fixedtbody=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper table tbody");
						if(fixedtbody){
							fixedtbody.addEventListener("mousedown",this.tbodymousedown);
							fixedtbody.addEventListener("mouseup",this.tbodymouseup);
							fixedtbody.addEventListener("mousemove",this.tbodymousemove);
						}
						
						let fixedBodyWrapper=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper");
						if(fixedBodyWrapper){
							//注意这里的ref名称,这里是fixed区域的框的名称
							fixedBodyWrapper.appendChild(this.$refs.fixedcellarea);
						}
					})
				})
			},
			//鼠标按下事件
			tbodymousedown(event){
				//左键
				if(event.button === 0){
					// 记录选择操作起始位置
					this.selectionStart = this.getCellPosition(event.target);
					this.isSelecting = true;
				}
			},
			//鼠标移动事件
			tbodymousemove(event){
				//左键
				if(event.button === 0){
					if (!this.isSelecting) return;
					var x = event.clientX;
					// 记录选择操作结束位置
					this.selectionEnd = this.getCellPosition(event.target);
					//设置样式
					this.setselectedCellArea();
					//持续向右滚动
					var x = event.clientX;
					var table=this.getTablexGrid().$el.querySelector(".vxe-table--body-wrapper table");
					if(table){
						let tableRect=table.parentElement.getBoundingClientRect();
						if (x > tableRect.right - 20){
							table.parentElement.scrollLeft+=20;
						}
					}
				}
			},
			//鼠标按键结束事件
			tbodymouseup(event) {
				//左键
				if(event.button === 0){
					this.isSelecting = false;
				}
			},
			// 获取单元格位置
			getCellPosition(cell) {
				try{
					while(cell.tagName !== 'TD'){
						cell = cell.parentElement;
					}
					// const rowIndex = cell.parentElement.rowIndex;
					// const cellIndex = cell.cellIndex;
					let visibleColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
					const cellIndex = visibleColumn.findIndex((col)=>{
						return col.id==cell.getAttribute("colid");
					})
					let visibleData=this.getTablexGrid().getTableData()["visibleData"];
					const rowIndex =visibleData.findIndex((row)=>{
						return row._X_ROW_KEY==cell.parentElement.getAttribute("rowid");
					})
					return { rowIndex, cellIndex };
				}catch(e){
					return { rowIndex:-1,cellIndex:-1};
				}
			},
			//设置框打开
			setselectedCellArea(){
				let startRowIndex=this.selectionStart["rowIndex"];
				let endRowIndex=this.selectionEnd["rowIndex"];
				let startColumnIndex=this.selectionStart["cellIndex"];
				let endColumnIndex=this.selectionEnd["cellIndex"];
				let {width,height,left,top}=this.getAreaBoxPostion();
				// .vxe-table--fixed-wrapper .vxe-table--body-wrapper
				// .vxe-table--main-wrapper .vxe-table--body-wrapper
				var activeElement=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area");
				var mainElement=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area");
				var fixedActiveElement=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area");
				var fixedMainElement=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area");
				//获取固定列宽度fixed--hidden
				var fixedWidth=0;
				let flexDiv=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-left-wrapper");
				if(flexDiv){
					fixedWidth=flexDiv.offsetWidth;
				}
				var elements=[activeElement,mainElement,fixedActiveElement,fixedMainElement];
				elements.forEach((element)=>{
					if(element){
						element.style.width=`${width}px`;
						element.style.height=`${height}px`;
						element.style.top=`${top}px`;
						element.style.left=`${left}px`;
						element.style.display="block";
					}
				})
				this.openAreaBox();
				this.selectionStart={"cellIndex":startColumnIndex,"rowIndex":startRowIndex};
				this.selectionEnd={"cellIndex":endColumnIndex,"rowIndex":endRowIndex};
			},
			//根据开始位置和结束位置的索引计算框的width,height,left,top
			getAreaBoxPostion(){
				let startRowIndex=this.selectionStart["rowIndex"];
				let endRowIndex=this.selectionEnd["rowIndex"];
				let startColumnIndex=this.selectionStart["cellIndex"];
				let endColumnIndex=this.selectionEnd["cellIndex"];
				let visibleColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
				let visibleData=this.getTablexGrid().getTableData()["visibleData"];
				if(startColumnIndex<0||endColumnIndex<0||startRowIndex<0||endRowIndex<0)return;
				var maxColumnIndex=visibleColumn.length-1;
				var maxRowIndex=visibleData.length-1;
				if(endColumnIndex>maxColumnIndex){
					endColumnIndex=maxColumnIndex;
				}
				if(endRowIndex>maxRowIndex){
					endRowIndex=maxRowIndex;
				}
				// height width left top display
				// cellarea
				let width=0,height=0,left=0,top=0;
				let fixedwidth=0,fixedheight=0,fixedleft=0,fixedtop=0;
				visibleColumn.forEach((col,index)=>{
					if(index<startColumnIndex){
						left+=this.getTablexGrid().getColumnWidth(col);
					}
					if(index<=endColumnIndex&&startColumnIndex<=index){
						width+=this.getTablexGrid().getColumnWidth(col);
					}
				})
				height=(endRowIndex-startRowIndex+1)*this.gridOptions["row-config"]["height"];
				if(height<=0||width<=0){
					this.destroyAreaBox();
					return;
				}
				top=startRowIndex*this.gridOptions["row-config"]["height"];
				return {width,height,left,top};
			},
			//显示范围框
			openAreaBox(){
				var element=this.$refs.xGrid.$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="block";
				}
				element=this.$refs.xGrid.$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="block";
				}
			},
			//关闭范围框
			closeAreaBox(){
				var element=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
				element=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
			},
			//销毁范围框
			destroyAreaBox(){
				this.selectionStart={"rowIndex":-1,"cellIndex":-1};
				this.selectionEnd={"rowIndex":-1,"cellIndex":-1};
				var element=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
				element=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
			},

文章到这,就实现了功能哦。

5.但是需要在加一个功能,用户点击表格单元格时需要关闭原先的框,显示用户点击单元格时的新框。这个可以说的不是很清楚,但是在你们使用时就会发现这个问题的。

 方法:

//表格单元格点击事件
			tableCellClick(e){
				let {row,column}=e;
				if(!this.isSelecting){
					if(!this.lastActive||this.lastActive["rowid"]!=row["_X_ROW_KEY"]||this.lastActive["colid"]!=column["id"]){
						this.selectionStart = this.getCellPosition(e.$event.target);
						this.selectionEnd =this.selectionStart;
						//设置样式
						this.setselectedCellArea();
						this.lastActive={"rowid":"","colid":""};
					}
				}
			},

6.那如何获取选中区域的数据呢?相信这是大家比较关心的问题吧。

						console.log("是否正在进行滑动选中操作:",this.isSelecting);
						//左上角坐标
						console.log("单元格起始位置:索引:",this.selectionStart);
						//右下角坐标
						console.log("单元格结束位置:索引:",this.selectionEnd);
						
						//这里需要是visibleData
						let tableData=this.getTablexGrid().getTableData()["visibleData"];
						let rowStart=this.selectionStart.rowIndex;
						let rowEnd=this.selectionEnd.rowIndex;
						let selectRows=tableData.filter((col,index)=>{
							return rowStart<=index&&rowEnd>=index;
						})
						console.log("鼠标选中行:",JSON.stringify(selectRows));
						
						//这里需要是visibleColumn
						let colStart=this.selectionStart.cellIndex;
						let colEnd=this.selectionEnd.cellIndex;
						let tableColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
						let selectCols=tableColumn.filter((col,index)=>{
							return colStart<=index&&colEnd>=index;
						})
						console.log("鼠标选中列:",JSON.stringify(selectCols));

下面给出整个示例的完整代码:

<template>
	<div style="width: 600px;">
		<!-- 正常区域的框 -->
		<div class="vxe-table--cell-area" ref="cellarea">
			<span class="vxe-table--cell-main-area"></span>
			<span class="vxe-table--cell-active-area"></span>
		</div>
		<!-- 左边fixed区域的框,右边fixed没有尝试过。。。 -->
		<div class="vxe-table--cell-area" ref="fixedcellarea">
			<span class="vxe-table--cell-main-area"></span>
			<span class="vxe-table--cell-active-area"></span>
		</div>
		
		<vxe-grid ref='xGrid' v-bind="gridOptions" height="400px"
			@cell-click="tableCellClick"
			@keydown="tableKeydown"
			@toolbar-button-click="toolbarButtonClickEvent">
			
		</vxe-grid>
	</div>
</template>

<script>
	import XEClipboard from 'xe-clipboard';
	export default {
		data() {
			return{
				gridOptions:{
					"keyboard-config":{
						isArrow: true,
					},
					//左上角按钮
					toolbarConfig:{
						perfect: true,
						enabled:true,
						size:"mini",
						buttons:[
							{
								code:'getcellselctdata',type:"text",name:'获取选中数据(结果看控制台)'
							}
						],
					},
					//列配置 (使用列拖拽功能,必须配置useKey为true)
					"column-config":{resizable:true,useKey:true},
					//边框
					border:"full",
					//斑马纹
					stripe:true,
					//列信息
					columns:[
						{width:70,field:"id",title:"#",align:"left",fixed:"left"},
						{width:100,field:"name",title:"姓名",align:"left",fixed:"left"},
						{width:100,field:"age",title:"年龄",align:"left",fixed:"left"},
						{width:100,field:"sex",title:"性别",align:"left"},
						{width:100,field:"job",title:"岗位",align:"left"},
						{width:270,field:"address",title:"地址",align:"left"},
					],
					//数据
					data:[
						{id:1,name:"张三",age:30,sex:"男",job:"前端",address:"中国xxxxxxxxxx"},
						{id:2,name:"李四",age:30,sex:"男",job:"后端",address:"中国xxxxxxxxxx"},
						{id:3,name:"王五",age:30,sex:"女",job:"运维",address:"中国xxxxxxxxxx"},
						{id:4,name:"赵六",age:30,sex:"男",job:"美工",address:"中国xxxxxxxxxx"},
						{id:5,name:"老八",age:30,sex:"男",job:"项目经理",address:"中国xxxxxxxxxx"},
						{id:6,name:"桀桀",age:30,sex:"女",job:"售后",address:"中国xxxxxxxxxx"},
					],
					//这里一定要指定true,否则row-config的height没用
					'show-overflow':true,
					//行配置,这里的行高一定需要指定
					"row-config":{isCurrent:true,height:35,isHover:true},
				},
				//鼠标滑动选中
				isSelecting: false, // 是否正在进行选择操作
				selectionStart:{rowIndex:-1,cellIndex:-1}, // 选择操作起始单元格位置
				selectionEnd:{rowIndex:-1,cellIndex:-1}, // 选择操作结束单元格位置
			}
		},
		mounted() {
			this.addListener();
		},
		methods: {
			//返回table的ref名称
			getTablexGrid(){
				return this.$refs.xGrid;
			},
			//添加事件
			addListener(){
				//添加多选列
				this.$nextTick(()=>{
					let tbody=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper table tbody");
					if(tbody){
						tbody.addEventListener("mousedown",this.tbodymousedown);
						tbody.addEventListener("mouseup",this.tbodymouseup);
						tbody.addEventListener("mousemove",this.tbodymousemove);
						tbody.addEventListener("paste",this.tbodykeydown);
					}
					let bodyWrapper=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper");
					if(bodyWrapper){
						//注意这里的ref名称,这里是非fixed区域的框的名称
						bodyWrapper.appendChild(this.$refs.cellarea);
					}
					
					setTimeout(()=>{
						let fixedtbody=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper table tbody");
						if(fixedtbody){
							fixedtbody.addEventListener("mousedown",this.tbodymousedown);
							fixedtbody.addEventListener("mouseup",this.tbodymouseup);
							fixedtbody.addEventListener("mousemove",this.tbodymousemove);
							fixedtbody.addEventListener("paste",this.tbodykeydown);
						}
						
						let fixedBodyWrapper=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper");
						if(fixedBodyWrapper){
							//注意这里的ref名称,这里是fixed区域的框的名称
							fixedBodyWrapper.appendChild(this.$refs.fixedcellarea);
						}
					})
				})
			},
			//鼠标按下事件
			tbodymousedown(event){
				//左键
				if(event.button === 0){
					// 记录选择操作起始位置
					this.selectionStart = this.getCellPosition(event.target);
					this.isSelecting = true;
				}
			},
			//鼠标移动事件
			tbodymousemove(event){
				//左键
				if(event.button === 0){
					if (!this.isSelecting) return;
					var x = event.clientX;
					// 记录选择操作结束位置
					this.selectionEnd = this.getCellPosition(event.target);
					//设置样式
					this.setselectedCellArea();
					//持续向右滚动
					var x = event.clientX;
					var table=this.getTablexGrid().$el.querySelector(".vxe-table--body-wrapper table");
					if(table){
						let tableRect=table.parentElement.getBoundingClientRect();
						if (x > tableRect.right - 20){
							table.parentElement.scrollLeft+=20;
						}
					}
				}
			},
			//鼠标按键结束事件
			tbodymouseup(event) {
				//左键
				if(event.button === 0){
					this.isSelecting = false;
				}
			},
			// 获取单元格位置
			getCellPosition(cell) {
				try{
					while(cell.tagName !== 'TD'){
						cell = cell.parentElement;
					}
					// const rowIndex = cell.parentElement.rowIndex;
					// const cellIndex = cell.cellIndex;
					let visibleColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
					const cellIndex = visibleColumn.findIndex((col)=>{
						return col.id==cell.getAttribute("colid");
					})
					let visibleData=this.getTablexGrid().getTableData()["visibleData"];
					const rowIndex =visibleData.findIndex((row)=>{
						return row._X_ROW_KEY==cell.parentElement.getAttribute("rowid");
					})
					return { rowIndex, cellIndex };
				}catch(e){
					return { rowIndex:-1,cellIndex:-1};
				}
			},
			//设置框打开
			setselectedCellArea(){
				try{
					let startRowIndex=this.selectionStart["rowIndex"];
					let endRowIndex=this.selectionEnd["rowIndex"];
					let startColumnIndex=this.selectionStart["cellIndex"];
					let endColumnIndex=this.selectionEnd["cellIndex"];
					let {width,height,left,top}=this.getAreaBoxPostion();
					// .vxe-table--fixed-wrapper .vxe-table--body-wrapper
					// .vxe-table--main-wrapper .vxe-table--body-wrapper
					var activeElement=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area");
					var mainElement=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area");
					var fixedActiveElement=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-active-area");
					var fixedMainElement=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-main-area");
					//获取固定列宽度fixed--hidden
					var fixedWidth=0;
					let flexDiv=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-left-wrapper");
					if(flexDiv){
						fixedWidth=flexDiv.offsetWidth;
					}
					var elements=[activeElement,mainElement,fixedActiveElement,fixedMainElement];
					elements.forEach((element)=>{
						if(element){
							element.style.width=`${width}px`;
							element.style.height=`${height}px`;
							element.style.top=`${top}px`;
							element.style.left=`${left}px`;
							element.style.display="block";
						}
					})
					this.openAreaBox();
					this.selectionStart={"cellIndex":startColumnIndex,"rowIndex":startRowIndex};
					this.selectionEnd={"cellIndex":endColumnIndex,"rowIndex":endRowIndex};
				}catch(e){
				}
			},
			//根据开始位置和结束位置的索引计算框的width,height,left,top
			getAreaBoxPostion(){
				let startRowIndex=this.selectionStart["rowIndex"];
				let endRowIndex=this.selectionEnd["rowIndex"];
				let startColumnIndex=this.selectionStart["cellIndex"];
				let endColumnIndex=this.selectionEnd["cellIndex"];
				let visibleColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
				let visibleData=this.getTablexGrid().getTableData()["visibleData"];
				if(startColumnIndex<0||endColumnIndex<0||startRowIndex<0||endRowIndex<0)return;
				var maxColumnIndex=visibleColumn.length-1;
				var maxRowIndex=visibleData.length-1;
				if(endColumnIndex>maxColumnIndex){
					endColumnIndex=maxColumnIndex;
				}
				if(endRowIndex>maxRowIndex){
					endRowIndex=maxRowIndex;
				}
				// height width left top display
				// cellarea
				let width=0,height=0,left=0,top=0;
				let fixedwidth=0,fixedheight=0,fixedleft=0,fixedtop=0;
				visibleColumn.forEach((col,index)=>{
					if(index<startColumnIndex){
						left+=this.getTablexGrid().getColumnWidth(col);
					}
					if(index<=endColumnIndex&&startColumnIndex<=index){
						width+=this.getTablexGrid().getColumnWidth(col);
					}
				})
				height=(endRowIndex-startRowIndex+1)*this.gridOptions["row-config"]["height"];
				if(height<=0||width<=0){
					this.destroyAreaBox();
					return;
				}
				top=startRowIndex*this.gridOptions["row-config"]["height"];
				return {width,height,left,top};
			},
			//显示范围框
			openAreaBox(){
				var element=this.$refs.xGrid.$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="block";
				}
				element=this.$refs.xGrid.$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="block";
				}
			},
			//关闭范围框
			closeAreaBox(){
				var element=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
				element=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="block";
				}
			},
			//销毁范围框
			destroyAreaBox(){
				this.selectionStart={"rowIndex":-1,"cellIndex":-1};
				this.selectionEnd={"rowIndex":-1,"cellIndex":-1};
				var element=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
				element=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-wrapper .vxe-table--body-wrapper .vxe-table--cell-area");
				if(element){
					element.style.display="none";
				}
			},
			//表格单元格点击事件
			tableCellClick(e){
				let {row,column}=e;
				if(!this.isSelecting){
					if(!this.lastActive||this.lastActive["rowid"]!=row["_X_ROW_KEY"]||this.lastActive["colid"]!=column["id"]){
						this.selectionStart = this.getCellPosition(e.$event.target);
						this.selectionEnd =this.selectionStart;
						//设置样式
						this.setselectedCellArea();
						this.lastActive={"rowid":"","colid":""};
					}
				}
			},
			
			toolbarButtonClickEvent({code,button}){
				switch(code){
					case "getcellselctdata":
						//我给大家打印处理:
						console.log("是否正在进行滑动选中操作:",this.isSelecting);
						//左上角坐标
						console.log("单元格起始位置:索引:",this.selectionStart);
						//右下角坐标
						console.log("单元格结束位置:索引:",this.selectionEnd);
						
						//这里需要是visibleData
						let tableData=this.getTablexGrid().getTableData()["visibleData"];
						let rowStart=this.selectionStart.rowIndex;
						let rowEnd=this.selectionEnd.rowIndex;
						let selectRows=tableData.filter((col,index)=>{
							return rowStart<=index&&rowEnd>=index;
						})
						console.log("鼠标选中行:",JSON.stringify(selectRows));
						
						//这里需要是visibleColumn
						let colStart=this.selectionStart.cellIndex;
						let colEnd=this.selectionEnd.cellIndex;
						let tableColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
						let selectCols=tableColumn.filter((col,index)=>{
							return colStart<=index&&colEnd>=index;
						})
						console.log("鼠标选中列:",JSON.stringify(selectCols));
						break;
				}
			},
			tableKeydown({$event}){
				this.tbodykeydown($event);
			},
			tbodykeydown(event){
				let tablexgrid=this.getTablexGrid();
				let tableColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
				let tableData=this.getTablexGrid().getTableData()["visibleData"];
				let startRowIndex=this.selectionStart["rowIndex"];
				let endRowIndex=this.selectionEnd["rowIndex"];
				let startColumnIndex=this.selectionStart["cellIndex"];
				let endColumnIndex=this.selectionEnd["cellIndex"];
				var maxColumnIndex=tableColumn.length-1;
				var maxRowIndex=tableData.length-1;
				var minColumnIndex=0;
				var minRowIndex=0;
				
				if (event.ctrlKey && event.keyCode === 67) {
					//ctrl+c 复制
					this.contextMenuClickEvent({menu:{code:"cellCopy"}});
					event.preventDefault();
				}else if(event.ctrlKey&&event.keyCode === 86){
					//ctrl+v 粘贴
					this.contextMenuClickEvent({menu:{code:"cellPaste"}});
					event.preventDefault();
				}else if(event.ctrlKey && event.key === 'd'){
					//ctrl+d
					this.contextMenuClickEvent({menu:{code:"cellLineCopy"}});
					event.preventDefault();
				}else if(event.key === 'Delete'){
					//delete
					this.contextMenuClickEvent({menu:{code:"cellDelete"}});
					event.preventDefault();
				}else if(event.ctrlKey && event.key === 'x'){
					//ctrl+x
					this.contextMenuClickEvent({menu:{code:"cellCut"}});
					event.preventDefault();
				}else if(event.ctrlKey && event.key === 'z'){
					//ctrl+z
					this.contextMenuClickEvent({menu:{code:"cellSortValue"}});
					event.preventDefault();
				}else if(event.keyCode===39){
					//右键 向右
					if(endColumnIndex+1<=maxColumnIndex){
						var currentRow=tablexgrid.getCurrentRecord();
						const rowIndex =tableData.findIndex((row)=>{
							return row._X_ROW_KEY==currentRow._X_ROW_KEY;
						})
						this.selectionStart={"cellIndex":endColumnIndex+1,"rowIndex":rowIndex};
						this.selectionEnd={"cellIndex":endColumnIndex+1,"rowIndex":rowIndex};
					}
					this.setselectedCellArea();
					this.tableScrollMove("right");
					event.preventDefault();
				}else if(event.keyCode===37){
					//左键 向左
					if(startColumnIndex-1>=minColumnIndex){
						var currentRow=tablexgrid.getCurrentRecord();
						const rowIndex =tableData.findIndex((row)=>{
							return row._X_ROW_KEY==currentRow._X_ROW_KEY;
						})
						this.selectionStart={"cellIndex":startColumnIndex-1,"rowIndex":rowIndex};
						this.selectionEnd={"cellIndex":startColumnIndex-1,"rowIndex":rowIndex};
					}
					this.setselectedCellArea();
					this.tableScrollMove("left");
					event.preventDefault();
				}else if(event.keyCode===38){
					//向上
					var currentRow=tablexgrid.getCurrentRecord();
					const rowIndex =tableData.findIndex((row)=>{
						return row._X_ROW_KEY==currentRow._X_ROW_KEY;
					})
					if(rowIndex-1>=minRowIndex){
						this.selectionStart={"cellIndex":startColumnIndex,"rowIndex":rowIndex-1};
						this.selectionEnd={"cellIndex":startColumnIndex,"rowIndex":rowIndex-1};
					}
					this.setselectedCellArea();
					//判断是否小于最小行的个数
					event.preventDefault();
				}else if(event.keyCode===40){
					//向下
					var currentRow=tablexgrid.getCurrentRecord();
					const rowIndex =tableData.findIndex((row)=>{
						return row._X_ROW_KEY==currentRow._X_ROW_KEY;
					})
					if(rowIndex+1<=maxRowIndex){
						this.selectionStart={"cellIndex":startColumnIndex,"rowIndex":rowIndex+1};
						this.selectionEnd={"cellIndex":startColumnIndex,"rowIndex":rowIndex+1};
					}
					this.setselectedCellArea();
					event.preventDefault();
				}
			},
			//控制滚动条,元素隐藏时滚动条自动滚动到显示的位置
			//move:快捷键左右时有效,其他无效
			tableScrollMove(move){
				let tableColumn=this.getTablexGrid().getTableColumn()["visibleColumn"];
				let tableData=this.getTablexGrid().getTableData()["visibleData"];
				let startRowIndex=this.selectionStart["rowIndex"];
				let endRowIndex=this.selectionEnd["rowIndex"];
				let startColumnIndex=this.selectionStart["cellIndex"];
				let endColumnIndex=this.selectionEnd["cellIndex"];
				
				let fixedWidth=0;
				
				//获取固定列宽度fixed--hidden
				let flexDiv=this.getTablexGrid().$el.querySelector(".vxe-table--fixed-left-wrapper");
				if(flexDiv){
					fixedWidth=flexDiv.offsetWidth;
				}
				//获取td
				var td;
				var tbody=this.getTablexGrid().$el.querySelector(".vxe-table--main-wrapper table tbody");
				if(tbody&&move=="left"){
					var column=tableColumn[startColumnIndex];
					td=tbody.querySelector(`td[colid="${column.id}"]`);
				}else if(tbody&&move=="right"){
					var column=tableColumn[endColumnIndex];
					td=tbody.querySelector(`td[colid="${column.id}"]`);
				}
				if(td){
				//判断是否隐藏
					var tdRect=td.getBoundingClientRect();
					var table=this.getTablexGrid().$el.querySelector(".vxe-table--body-wrapper table");
					if(table){
						let tableRect=table.parentElement.getBoundingClientRect();
						//需要减去左边固定列的宽度
						if (tdRect.top < tableRect.top ||tdRect.bottom > tableRect.bottom ||
						    tdRect.left < tableRect.left+fixedWidth ||tdRect.right > tableRect.right){
							if(move=="right"){
								table.parentElement.scrollLeft+=(tdRect.right-tableRect.right);
							}else if(move=="left"){
								table.parentElement.scrollLeft+=(tdRect.left-tableRect.left-fixedWidth);
							}
						  } else {
						    // console.log("该 td 元素未隐藏");
						  }
					}
				}
			},
			//
			contextMenuClickEvent({menu,row,column,rowIndex,columnIndex,$event}){
				let startRowIndex=this.selectionStart["rowIndex"];
				let endRowIndex=this.selectionEnd["rowIndex"];
				let startColumnIndex=this.selectionStart["cellIndex"];
				let endColumnIndex=this.selectionEnd["cellIndex"];
				var tableColumn=JSON.parse(JSON.stringify(this.getTablexGrid().getTableColumn()["visibleColumn"]));
				var tableData=this.getTablexGrid().getTableData()["visibleData"];
				switch(menu.code){
					//复制
					case "cellCopy":
						let enterStr="\r\n";
						let spaceStr="\t";
						let data=[];
						for(var i=startRowIndex;i<=endRowIndex;i++){
							let value=[];
							for(var j=startColumnIndex;j<=endColumnIndex;j++){
								value.push(tableData[i][tableColumn[j].field]);
							}
							data.push(value);
						}
						let finalStr=data.map((value)=>{
							return value.join(spaceStr);
						}).join(enterStr);
						this.copyValue(finalStr);
						break;
					//粘贴
					case "cellPaste":
						navigator.clipboard.readText().then((text)=>{
							if(text){
								//去除首尾换行
								text=text.replace(/^\r\n+|\r\n+$/g, '');
								var snsArr=text.split(/\r\n+/);
								var tArr=snsArr.map((value)=>{
									return value.split("\t");
								})
								for(var i=0;i<tArr.length;i++){
									let line=tArr[i];
									if(startRowIndex+i>tableData.length-1)break;
									let row=tableData[startRowIndex+i];
									for(var j=0;j<line.length;j++){
										if(startColumnIndex+j>tableColumn.length)break;
										let column=tableColumn[startColumnIndex+j];
										row[column.field]=line[j];
									}
								}
							}
						})
						break;
					//delete清除
					case "cellDelete":
						for(var i=startRowIndex;i<=endRowIndex;i++){
							if(i>tableData.length-1)break;
							for(var j=startColumnIndex;j<=endColumnIndex;j++){
								if(j>tableColumn.length-1)break;
								let row=tableData[i];
								let column=tableColumn[j];
								this.getTablexGrid().clearData(row,column.property);
							}
						}
						break;
					case "cellLineCopy":
						//第一行的值不变,后面的行等于第一行的值
						var firstRow=tableData[startRowIndex];
						for(var i=startRowIndex+1;i<=endRowIndex;i++){
							if(i>tableData.length-1)break;
							for(var j=startColumnIndex;j<=endColumnIndex;j++){
								if(j>tableColumn.length-1)break;
								tableData[i][tableColumn[j].field]=firstRow[tableColumn[j].field];
							}
						}
						break;
					case "cellCut":
						//剪切
						this.contextMenuClickEvent({menu:{code:"cellCopy"}});
						this.contextMenuClickEvent({menu:{code:"cellDelete"}});
						break;
					case "cellSortValue":
						//自增
						var firstRow=tableData[startRowIndex];
						for(var i=startRowIndex+1;i<=endRowIndex;i++){
							if(i>tableData.length-1)break;
							for(var j=startColumnIndex;j<=endColumnIndex;j++){
								if(j>tableColumn.length-1)break;
								let value=firstRow[tableColumn[j].field];
								if(!value)break;
								if(!isNaN(value)){
									tableData[i][tableColumn[j].field]=parseFloat(value)+(i-startRowIndex);
								}else{
									//最后一个字符
									let lastChar=value[value.length-1];
									//去除最后一个字符
									let nvalChar=value.slice(0, -1);
									if(/[a-zA-Z]/.test(lastChar)){
										let result =this.generateAlphabetChars(lastChar,i-startRowIndex+1);
										tableData[i][tableColumn[j].field]=nvalChar+result;
									}
								}
							}
						}
						break;
				}
			},
			//数据自增的工具方法
			generateAlphabetChars(c, shift){
				/**
				 * 将一个字符按照指定的偏移量进行移位
				 * @param {string} c 需要移位的字符
				 * @param {number} shift 移位的偏移量
				 * @returns {string} 移位后的字符
				 */
				// 将字符转换为 ASCII 码
				var asciiCode = c.charCodeAt(0);
				// 计算移位后的 ASCII 码
				var shiftedAsciiCode = asciiCode + shift;
				let flag = false;
				if (shiftedAsciiCode > 122) {
					shiftedAsciiCode -= 26;
				} else if (shiftedAsciiCode < 97) {
					shiftedAsciiCode += 26;
				}
				// 将 ASCII 码转换为字符
				const shiftedChar = String.fromCharCode(shiftedAsciiCode);
				return asciiCode + shift > 122 ? 'a' + shiftedChar: shiftedChar;
			},
			//复制的工具方法
			copyValue(value){
				if (XEClipboard.copy(value)) {
					this.$VXETable.modal.message({content: '已复制到剪贴板!',status:'success',duration:400});
				}
			},
		}
	}
</script>

<style scoped>
	.vxe-grid{
		-webkit-user-select: none; /* Safari */
		-moz-user-select: none; /* Firefox */
		-ms-user-select: none; /* Internet Explorer/Edge */
		user-select: none;
	}
</style>

  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
vxe-table中嵌套vxe-table时,内层的vxe-table可以通过作用域插槽(scoped slot)来获取外层vxe-table的row数据。具体步骤如下: 1. 在外层vxe-table的列定义中,使用作用域插槽(scoped slot)来传递row数据给内层vxe-table。例如,可以在外层vxe-table的列定义中添加一个自定义列,并使用作用域插槽将row数据传递给内层vxe-table,如下所示: ```html <vxe-table :data="outerData"> <vxe-column type="index" width="60"></vxe-column> <vxe-column field="name" title="Name"></vxe-column> <vxe-column title="Nested Table"> <template #default="{ row }"> <vxe-table :data="row.nestedData"> <!-- 内层vxe-table的列定义 --> </vxe-table> </template> </vxe-column> </vxe-table> ``` 2. 在内层vxe-table的列定义中,可以通过作用域插槽(scoped slot)的方式获取外层vxe-table传递的row数据。例如,可以在内层vxe-table的列定义中使用作用域插槽来访问外层vxe-table的row数据,如下所示: ```html <vxe-table :data="nestedData"> <vxe-column field="nestedField1" title="Nested Field 1"></vxe-column> <vxe-column field="nestedField2" title="Nested Field 2"></vxe-column> <vxe-column title="Outer Row Data"> <template #default="{ $table }"> <!-- 使用 $table.$scoped.row 获取外层vxe-table的row数据 --> <span>{{ $table.$scoped.row }}</span> </template> </vxe-column> </vxe-table> ``` 通过以上步骤,内层的vxe-table就可以获取到外层vxe-table的row数据,并进行相应的操作

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值