项目场景:在货位总览区域,手指滑动选择货位
主要用到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>
最终效果:
整体代码:
<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>