uniapp监听触摸事件,实现手指滑动多选元素

959 阅读1分钟

项目场景:在货位总览区域,手指滑动选择货位

主要用到Uniapp中监听触摸事件:

  • @touchstart :触摸开始;
  • @touchmove:手指滑动的过程;
  • @touchend:触摸结束,手指离开屏幕。

实现思路:

1.在货位dom元素渲染完成后,获取每个元素的位置,并给元素绑定位置信息,给货位元素的父级区域绑定监听触摸事件

// 获取每个货位元素的位置
getElClient(){
    for (var i = 0; i < this.locationList.length; i++) {
        let child = this.locationList[i]
        for (var j = 0; j < child.length; j++) {
            let view=uni.createSelectorQuery().select('#'+child[j].LocationId);
            let obj = child[j]
            view.boundingClientRect(data=>{
                obj.clientX = data.left
                obj.clientY = data.top
            }).exec();
        }
    }
},

2.给货位元素的父级区域绑定监听触摸事件

// 绑定触摸监听事件
<view @touchstart="touchStart" @touchend="touchEnd" @touchmove="touchMove">
    <view class="row" v-for="(item,index) in locationList" :key="index">
        <view v-for="cItem in item" :key="cItem.LocationId" class="color_con color_box" :id="cItem.LocationId">
        </view>
    </view>
</view>

3.监听触摸、滑动事件,记录手指滑动的初始位置和滑动位置

data() {
    return {
        startClient: {X:"",Y:''},
        touchClient: {X:"",Y:''}
    }
},
methods:{
    // 手指触摸事件
    touchStart(e){
        this.touchClient.X = '';
        this.touchClient.Y = '';
        this.startClient.X = e.changedTouches[0].clientX
        this.startClient.Y = e.changedTouches[0].clientY
    },
    touchMove(event) {  //@touchmove触摸移动
        let touch = event.touches[0];  //滑动过程中,手指滑动的坐标信息 返回的是Objcet对象
        this.touchClient.X = touch.clientX
        this.touchClient.Y = touch.clientY
    }
}

4.通过对比滑动位置和货位元素的位置,改变货位元素的选中状态

<view @touchstart="touchStart" @touchend="touchEnd" @touchmove="touchMove">
    <view class="row" v-for="(item,index) in locationList" :key="index">
        <view v-for="cItem in item" :key="cItem.LocationId" class="color_con color_box" :id="cItem.LocationId"
        :class="['color'+cItem.Status,
        {'location_box_checked':
        (cItem.clientX+30>=startClient.X&&cItem.clientY<=touchClient.Y&&cItem.clientY+30>=touchClient.Y&&cItem.clientX<=touchClient.X&&startClient.X)||
        (cItem.clientY>=startClient.Y&&cItem.clientY<=touchClient.Y&&cItem.clientX<=touchClient.X&&startClient.X)||
        (cItem.clientX+30>=startClient.X&&cItem.clientY+30>=startClient.Y&&cItem.clientY+30<=touchClient.Y&&startClient.X)
        }]">
        </view>
    </view>
</view>

<style lang="scss" scoped>
.location_box_checked {
    border: 1px solid blue;
}
</style>

最终效果:

滑动多选.gif

整体代码:

<template>
	<view class="container_wrap setting_container">
		<view class="location_wrap">
			<view class="state_color_con">
				<view class="box" v-for="item in stateList" :key="item.Code">
					<view class="color_con color_box" :class="'color'+item.Code"></view>
					<text>{{item.Value}}</text>
				</view>
			</view>
			<view class="location_con">
				<view @touchstart="touchStart" @touchend="touchEnd" @touchmove="touchMove">
					<view class="row" v-for="(item,index) in locationList" :key="index">
						<view v-for="cItem in item" :key="cItem.LocationId" class="color_con color_box" :id="cItem.LocationId"
							:class="['color'+cItem.Status,
							{'location_box_checked':
							(cItem.clientX+30>=startClient.X&&cItem.clientY<=touchClient.Y&&cItem.clientY+30>=touchClient.Y&&cItem.clientX<=touchClient.X&&startClient.X)||
							(cItem.clientY>=startClient.Y&&cItem.clientY<=touchClient.Y&&cItem.clientX<=touchClient.X&&startClient.X)||
							(cItem.clientX+30>=startClient.X&&cItem.clientY+30>=startClient.Y&&cItem.clientY+30<=touchClient.Y&&startClient.X)||
							currentObj.LocationId==cItem.LocationId
							}]"
							@click="clickLoction(cItem)"
						>
						</view>
					</view>
				</view>
			</view>
			<text class="tip">已选中:{{checkedIds.length}}托</text>
			
			<!-- <view>start: {{startClient.X}},{{startClient.Y}}</view> -->
			<!-- <view>touch: {{touchClient.X}},{{touchClient.Y}}</view> -->
		</view>
		<u-form ref="uForm" :model="form" class="setting_form">
			<u-form-item label="质检结果" prop="status" labelWidth="160" >
				<u-input v-model="form.status" placeholder="请选择质检结果" type="select" @click="stateShow=true"></u-input>
			</u-form-item>
		</u-form>
		<u-button class="primary_button" @click="submit" shape='circle' :disabled="saveDisabled">确认录入</u-button>
		<u-modal v-model="confirmShow" title="确认录入" show-cancel-button @confirm="modalConfirm" cancel="confirmShow = false">
			<view class="slot-content form-modal-cont">
				<view>质检结果:{{form.status}}</view>
			</view>
		</u-modal>
		<u-select :default-value="defaultValue" value-name="Code" label-name="Value" :mode="mode" v-model="stateShow" :list="stateList" @confirm="confirmPicker"></u-select>
	</view>
</template>

<script>
	import request from '@/utils/tools/request.js'
	import { GetReceivingStroages, QualityResulting } from '@/api/linBo/storage.js'
	import { getSysEnumData } from '@/api/sysEnumData.js'
	export default {
		data() {
			return {
				serveIp: '',
				servePort: '',
				form:{
					status:''
				},
				defaultValue: [0],
				mode: 'single-column',
				stateShow: false,
				locationList:[],
				locationObj:{},
				rules: {
					status: [
						{
							required: true, 
							message: '请选择质检结果', 
							trigger: ['blur'],
						}
					],
				},
				confirmShow:false,
				confirmCon:'',
				saveDisabled:false,
				stateList: [],
				stateObj: {},
				startClient: {X:"",Y:''},
				touchClient: {X:"",Y:''},
				currentObj: {},
				checkedIds: []
			}
		},
		onLoad: function () {
			try {
				this.serveIp = uni.getStorageSync('ServeIp')||'';
				this.servePort=uni.getStorageSync('ServePort')||''
				this.getStateList()
				this.GetLocationsListFn() 
			} catch (e) {
				uni.showToast({
					icon:'fail',
					title:'异常,原因:'+e.message
				});
			}
		},
		onReady() {
			this.$refs.uForm.setRules(this.rules);
		},
		methods:{
			getStateList(){
				getSysEnumData({EnumName:'QualityStatus'}).then(res => {
					const { Data } = res
					this.stateList = Data
				})
			},
			GetLocationsListFn(message){
				GetReceivingStroages().then(res => {
					const { Data } = res
					let arrA = [],Row = 0;
					for (var i = 0; i < Data.length; i++) {
						Data[i].clientX=''
						Data[i].clientY=''
						if(Row!=Data[i].Row){
							arrA.push([Data[i]])
							Row = Data[i].Row
						}else{
							arrA[arrA.length-1].push(Data[i])
						}
					}
					this.locationList= arrA
					this.$nextTick(()=>{
						this.getElClient()
					})
					if(message){
						uni.showToast({
							icon:'success',
							title:message
						});
					}
				}).catch((err) => {
				    // if(err.Message){
				    // 	uni.showModal({
				    // 		title: 'error',
				    // 		content: err.Message,
				    // 		showCancel:false
				    // 	});
				    // }
				})
			},
			submit() {
				this.$refs.uForm.validate(valid => {
					if (valid) {
						this.confirmShow = true
					} else {
						console.log('验证失败');
					}
				});
			},
			modalConfirm(){
				QualityResulting(this.form).then(res => {
					if(res.Success){
						this.form={
							locationId:''
						}
						this.GetLocationsListFn('操作成功')
					}else{
						uni.showToast({
							icon:'error',
							title:'操作失败'
						});
					}
				}).catch((err) => {
				    if(err.Message){
				    	uni.showModal({
				    		title: 'error',
				    		content: err.Message,
				    		showCancel:false
				    	});
				    }
				})
			},
			confirmPicker(obj){
				this.form.status = obj[0].label
				this.stateObj = obj[0]
			},
			// 手指触摸事件
			touchStart(e){
				this.currentObj = {}
				this.touchClient.X = '';
				this.touchClient.Y = '';
				this.startClient.X = e.changedTouches[0].clientX
				this.startClient.Y = e.changedTouches[0].clientY
			},
			touchEnd(e){
				const query = uni.createSelectorQuery().in(this);     //这样写就只会选择本页面组件的类名box的
				query.selectAll('.location_box_checked').boundingClientRect(data => {   //回调函数,data中存储的是这些元素节点(每个节点的信息存为一个对象)的位置信息
				  this.checkedIds = []
				  data.forEach(obj=>{
					const { id } = obj
					this.checkedIds.push(id)
				  })
				}).exec();
			},
			touchMove(event) {  //@touchmove触摸移动
				let touch = event.touches[0];  //滑动过程中,手指滑动的坐标信息 返回的是Objcet对象
				this.touchClient.X = touch.clientX
				this.touchClient.Y = touch.clientY
			},
			// 获取每个货位元素的位置
			getElClient(){
				for (var i = 0; i < this.locationList.length; i++) {
					let child = this.locationList[i]
					for (var j = 0; j < child.length; j++) {
						let view=uni.createSelectorQuery().select('#'+child[j].LocationId);
						let obj = child[j]
						view.boundingClientRect(data=>{
							obj.clientX = data.left
							obj.clientY = data.top
						}).exec();
					}
				}
			},
			clickLoction(item){
				this.touchClient.X = '';
				this.touchClient.Y = '';
				this.startClient.X = '';
				this.startClient.Y = '';
				this.currentObj = item
				this.checkedIds = [item.LocationId]
			}
		}
	}
</script>

<style lang="scss" scoped>
	.setting_container {
		padding: 20rpx 0rpx;
		box-sizing: border-box;
		background-color: #f8f8f8;
		height: 100%;
		.location_wrap {
			height: calc(100% - 110rpx - 160rpx);
			background: #eef4fb;
			padding-top: 14rpx;
			.state_color_con {
				text-align: center;
				height: 70rpx;
				line-height: 70rpx;
				.box {
					display: inline-block;
					margin-right: 30rpx;
					.color_con {
						width: 30rpx;
						height: 28rpx;
						border-radius: 6rpx;
						display: inline-block;
						margin-right: 12rpx;
						position: relative;
						top: 4rpx;
						background: #c7c7c7;
					}
				}
				.box:last-child{
					margin-right: 0;
				}
			}
			.location_con {
				width: 100%;
				height: calc(100% - 140rpx);
				overflow: auto;
				padding: 20rpx 0 20rpx 20rpx;
				.color_con {
					width: 60rpx;
					height: 60rpx;
					border-radius: 10rpx;
					display: inline-block;
					margin-right: 12rpx;
					background: #c7c7c7;
				}
				.location_box_checked {
					border: 1px solid blue;
				}
			}
			.color_con.color_box.color0{
				background: #c7c7c7;
			}
			.color_con.color_box.color1{
				background: #c70303;
			}
			.color_con.color_box.color2{
				background: #4af19c;
			}
			.color_con.color_box.color3{
				background: #fbcd4b;
			}
			.tip {
				display: inline-block;
				width: 100%;
				text-align: center;
				font-size: 34rpx;
			}
		}
		.setting_form{
			background-color: #fff;
			padding: 0 30rpx;
			border-radius: 6px;
			height: 110rpx;
			/deep/ .u-form-item{
				color: #606266;
			}
			/deep/ .u-form-item--left__content__label{
				display: inline-block;
				text-align-last: justify;
				text-align: justify;
				margin-right: 8rpx;
			}
		}	
	}
	.form-modal-cont{
		padding: 24rpx 40rpx;
		uni-view{
			min-height: 64rpx;
			line-height: 64rpx;
		}
	}
	.primary_button{
		margin-top: 30rpx !important;
	}
</style>