Commit e0229562 by 李君

巡店流程

1 parent 141be65a
<template>
<div class="fullPage touchmove">
<livePlayer :params="paramObj"></livePlayer>
<div class="tour-btns" v-if="paramObj.type=='titem'">
<van-button @click="navBackTour" type="primary">返回上一页</van-button>
<van-button @click="pictureBtn" type="primary">确认并截图</van-button>
<div v-if="picUrl">
<!-- <img mode="widthFix" style="position:relative;width:100vw;height: 50vwpx;" :src="picUrl"/> -->
<canvas id="painter" ref="captureCanvas"></canvas>
<van-cell :title="templateNameDefault" @click="show = true" is-link />
<van-popup v-model:show="show" position="bottom">
<van-picker :columns="templateNameList" @confirm="onConfirm" @cancel="onCancel" />
</van-popup>
<div class="itemList" id="itemList">
<van-checkbox-group v-model="checked">
<van-cell-group inset>
<van-cell v-for="(item, index) in templateItemList" clickable center :key="index" :title="'['+item.score+']'+item.name" :label="item.intro" @click="toggle(index)">
<template #right-icon>
<van-checkbox :name="item.id" :ref="el => checkboxRefs[index] = el" @click.stop />
</template>
</van-cell>
</van-cell-group>
</van-checkbox-group>
</div>
<div class="formBox">
<van-field name="radio" label="是否需要整改">
<template #input>
<van-radio-group v-model="isRectify" direction="horizontal">
<van-radio name="1">需要</van-radio>
<van-radio name="2">不需要</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field
v-show="isRectify==1"
v-model="rectificationUserName"
is-link
readonly
name="picker"
label="整改人"
placeholder="点击选择整改人"
@click="showRectification = true"
/>
<van-popup v-model:show="showRectification" position="bottom">
<van-picker
:columns="userNameList"
@confirm="onUserConfirm"
@cancel="showRectification = false"
/>
</van-popup>
<van-cell-group inset>
<van-field v-model="message" rows="2" autosize label="备注" type="textarea" placeholder="请输入备注" />
</van-cell-group>
<van-button round block type="primary" @click="submitContent">提交</van-button>
</div>
</div>
<div class="tour-btns" v-else-if="paramObj.type=='view'">
<van-button block @click="navBack" type="primary">查看其他监控点</van-button>
</div>
<div class="tour-btns" v-else>
<van-button @click="navBack" type="primary">查看其他监控点</van-button>
<van-button @click="pictureBtn" type="primary">开始巡检</van-button>
<div v-else>
<livePlayer ref="livePlayerRef" :params="paramObj"></livePlayer>
<div class="tour-btns" v-if="paramObj.type=='titem'">
<van-button @click="navBackTour" type="primary">返回上一页</van-button>
<van-button @click="pictureBtn" type="primary">确认并截图</van-button>
</div>
<div class="tour-btns" v-else-if="paramObj.type=='view'">
<van-button block @click="navBack" type="primary">查看其他监控点</van-button>
</div>
<div class="tour-btns1" v-else>
<div class="box">
<span @click="navBack">{{decodeURIComponent(paramObj.name)}}
<van-icon class="icon" name="play" />
</span>
</div>
<div class="box box1">
<span v-if="bookmark" @click="delCollectGate">
<van-icon class="iconMargin" color="#F7B500" name="star" />收藏
</span>
<span v-else @click="collectGate">
<van-icon class="iconMargin" name="star-o" />收藏
</span>
<span @click="pictureBtn">
<van-icon class="iconMargin" name="photograph" />巡检
</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted, getCurrentInstance } from 'vue';
import { Toast } from 'vant';
import livePlayer from '@/components/livePlayer.vue';
//import vconsole from 'vconsole';
if (process.env.NODE_ENV !== 'production') {
//new vconsole();
}
/*获取 Url 参数 S*/
import parse from 'url-param-parser';
const paramObj = parse(window.location.href).search || {}
/********************************/
document.title = decodeURIComponent(paramObj.name);
localStorage.setItem('atoken', paramObj.atoken);
localStorage.setItem('lang', 'zh_CN');
/*获取 Url 参数 E*/
import tourApi from '@/api';
//var data = reactive(v);
const navBack = () => {
wx.miniProgram.redirectTo({
url: `/pages/tour/gate/index?type=${paramObj.type}`
});
};
const navBackTour = ()=>{
wx.miniProgram.navigateBack();
}
const pictureBtn = () => {
Toast.loading({
duration: 0,
message: '视频截图中···',
forbidClick: true,
});
tourApi.getCapture({id:paramObj.channelid}).then(res=>{
if(res.status!=200){
return Toast.fail(res.data.msg);
import {
reactive,
ref,
onMounted,
getCurrentInstance
} from 'vue';
import {
Toast
} from 'vant';
import livePlayer from '@/components/livePlayer.vue';
import shopTour from '@/components/shopTour.vue';
//import vconsole from 'vconsole';
if (process.env.NODE_ENV !== 'production') {
//new vconsole();
}
/*获取 Url 参数 S*/
import parse from 'url-param-parser';
let url = 'https://store.keliuyun.com/video/?userId=8840&atoken=16e4bf63-a3cd-420c-895a-a22617939989&type=undefined&id=42&channelid=122&platform=2&name=全景球机&mallId=9216&bookmark=true&accountId=337'
const paramObj = parse(url).search || {}
// const paramObj = parse(window.location.href).search || {}
/********************************/
document.title = decodeURIComponent(paramObj.name);
window.localStorage.setItem('atoken', paramObj.atoken);
window.localStorage.setItem('lang', 'zh_CN');
/*获取 Url 参数 E*/
import tourApi from '@/api';
//var data = reactive(v);
const dataURLtoFile = (dataurl, filename, filetype)=> {
var arr = dataurl.split(","),
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {
type: filetype
});
}
const navBack = () => {
wx.miniProgram.redirectTo({
url: `/pages/tour/gate/index?type=${paramObj.type}&atoken=${paramObj.atoken}&userId=${paramObj.userId}&id=${paramObj.id}&channelid=${paramObj.channelid}&platform=${paramObj.platform}&mallId=${paramObj.mallId}&name=${paramObj.name}`
});
};
const navBackTour = () => {
wx.miniProgram.navigateBack();
}
const bookmark = ref(paramObj.bookmark)
const collectGate = () => {
tourApi.setBookmark({
userId: paramObj.userId,
patrolGateIds: [paramObj.id]
}).then(res => {
if (res.data.code == 200) {
bookmark.value = true;
return Toast.success('收藏成功');
}
})
}
const delCollectGate = () => {
tourApi.delBookmark({
userId: paramObj.userId,
patrolGateIds: [paramObj.id]
}).then(res => {
if (res.data.code == 200) {
bookmark.value = false;
return Toast.success('取消收藏成功');
}
})
}
const show = ref(false);
const showPopup = () => {
show.value = true;
};
const checked = ref([]);
const onConfirm = (value, index) => {
templateNameDefault.value = value;
checked.value = []
if (index > 0) {
getPatrolTemplateOne(templateList.value[index].id)
} else {
templateItemList.value = []
associateData.value.forEach(item => {
templateItemList.value.push(...item.sopProjects)
})
}
let picUrl = res.data.data;
Toast.clear();
if(paramObj.type!='titem'){
wx.miniProgram.redirectTo({
url: `/pages/tour/index/index?action=capture&picUrl=${picUrl}&id=${paramObj.id}&channelid=${paramObj.channelid}&mallId=${paramObj.mallId}&title=${paramObj.name}`
});
show.value = false;
};
const onCancel = () => {
show.value = false;
}
const checkboxRefs = ref([]);
const toggle = (index) => {
checkboxRefs.value[index].toggle();
}
const message = ref();
const isRectify = ref("1")
const showRectification = ref(false)
const rectificationUserName = ref('')
const onUserConfirm = (value) => {
showRectification.value = false;
rectificationUserName.value = value
};
const submitContent = () => {
let base64Str = dataURLtoFile(captureCanvas.value.toDataURL('image/jpeg'), new Date().getTime(), 'image/jpeg')
let formData = new FormData();
formData.append('accountId', paramObj.accountId)
formData.append('mallId', paramObj.mallId)
formData.append('gateId', paramObj.id)
formData.append('patrolType', 1)
let sops = []
if(checked.value && checked.value.length>0){
checked.value.forEach(item=>{
let sopItem = templateItemList.value.find(childItem => item==childItem.id);
sops.push({
"id": sopItem.id,
"status": 0,
"score": sopItem.score
})
})
formData.append('sops', JSON.stringify(sops))
}else{
/***********巡店详情截图****************/
wx.miniProgram.navigateTo({
url: `/pages/tour/titem/index?action=capture&picUrl=${picUrl}&id=${paramObj.tid}`
});
return Toast.fail('请选择检查项');
}
if (message.value) {
formData.append('remark', message.value)
}
if(isRectify.value==1){
if(rectificationUserName.value){
let user = userList.value.find(item => item.name==rectificationUserName.value);
formData.append('handler', user.id)
}else{
return Toast.fail('请选择整改人');
}
}
})
};
onMounted(() => {
formData.append('pic', base64Str)
tourApi.confirmPatrolRecord(formData, {
headers: {
'Content-type': 'application/x-www-form-urlencoded;charset=utf-8' //multipart/form-data; charset=utf-8
}
}).then(res=>{
let result = res.data;
if (result.code == '200'){
Toast.success('提交成功');
setTimeout(()=>{
message.value = '';
rectificationUserName.value = '';
checked.value = [];
let context = captureCanvas.value.getContext("2d")
context.clearRect(0, 0, window.innerWidth, window.innerWidth/1.78)
picUrl.value = '';
console.log(livePlayerRef.value.playerEz)
livePlayerRef.value.playerEz.play();
},1000)
}else{
return Toast.fail(result.msg);
}
})
}
const userList = ref([]);
const userNameList = ref([]);
const getUserList = () => {
userList.value = []
userNameList.value = []
tourApi.getUsers({
mallId: paramObj.mallId
}).then(data => {
let result = data.data
if (result.data && result.data.length) {
result.data.forEach(item => {
item.name = item.realName + '(' + item.loginName + ')';
userNameList.value.push(item.realName + '(' + item.loginName + ')')
})
}
userList.value = result.data
})
}
const livePlayerRef = ref()
const picUrl = ref('')
const captureCanvas = ref('')
// 获取检查项
const templateList = ref([]);
const templateNameList = ref([]);
const templateNameDefault = ref();
const tempalteId = ref()
// 单个模板检查项
const templateItemList = ref([]);
const getPatrolTemplateList = () => {
tourApi.getPatrolTemplateListFun({
accountId: paramObj.accountId,
pageSize: 999,
}).then(res => {
let result = res.data;
if (result.code == 200) {
templateList.value = result.data.list;
if (templateList.value.length) {
tempalteId.value = templateList.value[0].id
getPatrolTemplateOne(templateList.value[0].id)
templateNameDefault.value = templateList.value[0].name;
templateList.value.unshift({
name: '全部',
id: ''
})
templateNameList.value = []
templateList.value.forEach(item => {
templateNameList.value.push(item.name)
})
}
}
})
}
// 获取单个模板检查项
const getPatrolTemplateOne = (val) => {
tourApi.getPatrolTemplateOne({
id: val,
}).then(res => {
let result = res.data;
if (result.code == 200) {
templateItemList.value = result.data.sopProjects || [];
}
})
}
// 获取关联的SOP
const associateData = ref([])
const getAssociateList = (record) => {
associateData.value = [];
tourApi.getPatrolSopTypeTree({
accountId: paramObj.accountId,
system: 0
}).then(data => {
let result = data.data;
if (result.code == '200') {
let tableData = result.data
tableData.forEach((item, index) => {
if (item.sopProjects.length > 0) {
item.key = 1000 + item.id
let sopProjects = JSON.parse(JSON.stringify(item.sopProjects))
item.sops = sopProjects
item.len = sopProjects.length
item.choiceNum = 0
associateData.value.push(item)
}
})
}
})
}
const pictureBtn = () => {
getPatrolTemplateList()
getAssociateList()
getUserList()
Toast.loading({
duration: 0,
message: '视频截图中···',
forbidClick: true,
});
tourApi.getCapture({id:paramObj.channelid}).then(res=>{
if(res.status!=200){
return Toast.fail(res.data.msg);
}
picUrl.value = res.data.data;
Toast.clear();
if(paramObj.type!='titem'){
// picUrl.value = 'https://store.keliuyun.com/images/patrol/capture/20230825/ecd2a635-84af-4968-bc30-1af130460d12.jpg'
livePlayerRef.value.playerEz.stop();
let image = new Image()
image.src = 'https://store.keliuyun.com/images/'+ picUrl.value;
image.style.width = window.innerWidth + 'px';
let heightC = window.innerWidth / 1.78
image.style.height = heightC + 'px'
image.crossOrigin = 'Anonymous'
image.onload = () => {
captureCanvas.value.width = window.innerWidth;
captureCanvas.value.height = heightC;
let ctx = captureCanvas.value.getContext("2d");
ctx.drawImage(image, 0, 0, window.innerWidth, heightC);
capture()
document.getElementById('itemList').style.height = (window.innerHeight - heightC - 250) + 'px'
}
// this.$refs.livePlayerRef.value.playerEz.stop();
// wx.miniProgram.redirectTo({
// url: `/pages/tour/index/index?action=capture&picUrl=${picUrl}&id=${paramObj.id}&channelid=${paramObj.channelid}&mallId=${paramObj.mallId}&title=${paramObj.name}&atoken=${paramObj.atoken}&userId=${paramObj.userId}`
// });
}else{
/***********巡店详情截图****************/
wx.miniProgram.navigateTo({
url: `/pages/tour/titemDetail/index?action=capture&picUrl=${picUrl}&id=${paramObj.tid}`
});
}
})
};
const locHistory = ref([]);
const lastLoc = ref();
const isMouseDown = ref(false);
const windowToCanvas = function(x, y) {
var bbox = captureCanvas.value.getBoundingClientRect(); //获取canvas的位置信息
return {
x: Math.round(x - bbox.left),
y: Math.round(y - bbox.top)
} //返回当前鼠标相对于canvas的位置
}
const capture = function() {
// 绘制
var context = captureCanvas.value.getContext("2d");
captureCanvas.value.addEventListener('touchstart', function(e) {
e.preventDefault();
isMouseDown.value = true;
lastLoc.value = windowToCanvas(e.touches[0].clientX, e.touches[0].clientY)
locHistory.value.push({
x: lastLoc.value.x,
y: lastLoc.value.y,
})
console.log(locHistory.value)
})
captureCanvas.value.addEventListener('touchmove', function(e) {
e.preventDefault();
if (isMouseDown.value) {
moveStroke({
x: e.touches[0].clientX,
y: e.touches[0].clientY
}, context)
}
})
captureCanvas.value.addEventListener('touchend', function(e) {
locHistory.value.push({
x: lastLoc.value.x,
y: lastLoc.value.y,
})
isMouseDown.value = false;
})
}
const calcDistance = function(loc1, loc2) {
return Math.sqrt((loc1.x - loc2.x) * (loc1.x - loc2.x) + (loc1.y - loc2.y) * (loc1.y - loc2.y)) //通过起始结束坐标x,y值计算路程长度
}
const moveStroke = function(point, context) {
var curLoc = windowToCanvas(point.x, point.y)
var curTimestamp = new Date().getTime();
var s = calcDistance(curLoc, lastLoc.value)
var lineWidth = 3
//draw
context.beginPath()
context.moveTo(lastLoc.value.x, lastLoc.value.y)
context.lineTo(curLoc.x, curLoc.y)
})
locHistory.value.push({
x: curLoc.x,
y: curLoc.y,
})
context.lineWidth = 3
context.strokeStyle = '#FF0000';
context.lineCap = "round"
context.linJoin = "round"
context.stroke();
//每次过程结束时,将结束值赋给初始值,一直延续
lastLoc.value = curLoc
}
onMounted(() => {})
</script>
<style scoped lang="less">
.tour-btns{
display: flex;
justify-content: space-between;
padding:20px 2vw 0;
--van-button-border-radius:60px;
:deep(.van-button){
margin-bottom: 20px;
border-radius:60px;
width:47vw;
&.van-button--block{
width:100%;
}
}
}
</style>
\ No newline at end of file
.tour-btns {
display: flex;
justify-content: space-between;
padding: 20px 2vw 0;
--van-button-border-radius: 60px;
:deep(.van-button) {
margin-bottom: 20px;
border-radius: 60px;
width: 47vw;
&.van-button--block {
width: 100%;
}
}
}
.tour-btns1 {
display: flex;
background: #E6EDFF;
height: 15vw;
line-height: 15vw;
padding: 0 20px;
.box {
display: inline-block;
flex: 1;
}
.box1 {
text-align: right;
span {
margin-left: 20px;
}
}
.icon {
transform: rotate(90deg);
}
}
.iconMargin {
margin-right: 10px;
}
.itemList {
overflow: auto;
height: 50%;
}
:deep(.van-cell-group--inset) {
margin: 0;
}
</style>
......@@ -10,5 +10,40 @@ const tourApi = {
getCapture(params,config) {
return req('get', `/patrol/patrolDeviceChannel/capture/${params.id}`, params, config)
},
// 提交巡店记录
submitPatrolRecord(params,config) {
return req('post', `/patrol/patrolRecord`, params, config)
},
setBookmark(params,config) {
return req('post', `/patrol/patrolGate/bookmark`, params, config)
},
delBookmark(params,config) {
return req('post', `/patrol/patrolGate/unbookmark`, params, config)
},
// 获取监控点
getPatrolGateList(params,config) {
return req('get', `/patrol/patrolGate/list`, params, config)
},
uploadScreenshot(params,config){
return req('post', `/patrol/b-patrol-screenshot`, params, config)
},
// 巡店模板
getPatrolTemplateListFun(params,config){
return req('get', `/patrol/b-patrol-template/list`, params, config)
},
// 查询单个模板
getPatrolTemplateOne(params,config){
return req('get', `/patrol/b-patrol-template/${params.id}`, params, config)
},
getPatrolSopTypeTree(params,config) {
return req('get', `/patrol/patrolSopType/tree`, params, config)
},
getUsers(params,config) {
return req('get', `/patrol/s-user/mall/${params.mallId}`)
},
//提交巡店记录
confirmPatrolRecord(params,config) {
return req('post', `/patrol/patrolRecord`, params, config)
},
}
export default tourApi;
\ No newline at end of file
......@@ -16,7 +16,7 @@
import { defineComponent,reactive,toRefs} from 'vue'
import { Toast} from 'vant';
import EZUIKit from 'ezuikit-js'
import SLPlayer from '@/static/js/slplayer'
import SLPlayer from '@/static/js/slplayer';
import tourApi from '@/api';
export default defineComponent({
props: {
......@@ -83,6 +83,7 @@ export default defineComponent({
if (this.platform == 1) {
// 清除萤石云视频
if (this.playerEz) {
this.playerEz.stop()
let canvasVideo = document.getElementById('my-video')
canvasVideo.innerHTML = ''
}
......@@ -103,23 +104,23 @@ export default defineComponent({
this.player.stop();
}
if (this.playerEz) {
let canvasVideo = document.getElementById('my-video')
canvasVideo.innerHTML = ''
this.playerEz.stop().then(()=>{
this.playerEz.play(this.palyUrl);
});
}else{
// 开始萤石云视频
this.playerEz = new EZUIKit.EZUIKitPlayer({
id: "my-video", // 视频容器ID
accessToken: atoken,
url: this.palyUrl,
template: 'mobileLive', //simple - 极简版;standard-标准版;security - 安防版(预览回放);voice-语音版mobileLive-手机
autoplay: true,
width: window.innerWidth,
height:window.innerWidth*1080/1920,
});
}
// 开始萤石云视频
this.playerEz = new EZUIKit.EZUIKitPlayer({
id: "my-video", // 视频容器ID
accessToken: atoken,
url: this.palyUrl,
template: 'mobileLive', //simple - 极简版;standard-标准版;security - 安防版(预览回放);voice-语音版;
autoplay: true,
//footer: ['hd', 'fullScreen'],
width: window.innerWidth,
height:window.innerWidth*1080/1920,
});
Toast.clear();
}
},
loadTourVideo() {
Toast.loading({
......@@ -146,9 +147,13 @@ export default defineComponent({
created() {
this.loadTourVideo();
},
destroyed(){
if (this.player) {
this.player.stop();
}
if (this.playerEz) {
this.playerEz.stop()
}
},
setup(props, context) {
const params = reactive(props.params);
......@@ -171,7 +176,7 @@ export default defineComponent({
background: #000;
}
}
:deep(.head-message),:deep(.live-ptz-title){
:deep(.head-message),:deep(.live-ptz-title),:deep(.mobile-ez-ptz-container){
display: none!important;
}
:deep(.mobile-ez-ptz-container){
......
<template>
<div class="tour-btns1">
<div class="box">
<span @click="navBack">{{decodeURIComponent(paramObj.name)}}<van-icon class="icon" name="play" /></span>
</div>
<div class="box box1">
<span v-if="bookmark" @click="delCollectGate">
<van-icon class="iconMargin" color="#F7B500" name="star" />收藏
</span>
<span v-else @click="collectGate">
<van-icon class="iconMargin" name="star-o" />收藏
</span>
<span @click="pictureBtn">
<van-icon class="iconMargin" name="photograph" />开始巡检
</span>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted, getCurrentInstance } from 'vue';
import { Toast } from 'vant';
import tourApi from '@/api';
const navBack = () => {
wx.miniProgram.redirectTo({
url: `/pages/tour/gate/index?type=${paramObj.type}&atoken=${paramObj.atoken}&userId=${paramObj.userId}&id=${paramObj.id}&channelid=${paramObj.channelid}&platform=${paramObj.platform}&mallId=${paramObj.mallId}&name=${paramObj.name}`
});
};
const bookmark = ref(paramObj.bookmark)
const collectGate= ()=>{
tourApi.setBookmark({
userId:paramObj.userId,
patrolGateIds:[paramObj.id]
}).then(res=>{
if(res.data.code==200){
bookmark.value = true;
return Toast.success('收藏成功');
}
})
}
const delCollectGate= ()=>{
tourApi.delBookmark({
userId:paramObj.userId,
patrolGateIds:[paramObj.id]
}).then(res=>{
if(res.data.code==200){
bookmark.value = false;
return Toast.success('取消收藏成功');
}
})
}
const pictureBtn = () => {
Toast.loading({
duration: 0,
message: '视频截图中···',
forbidClick: true,
});
// tourApi.getCapture({id:paramObj.channelid}).then(res=>{
// if(res.status!=200){
// return Toast.fail(res.data.msg);
// }
// let picUrl = res.data.data;
// Toast.clear();
// if(paramObj.type!='titem'){
// wx.miniProgram.redirectTo({
// url: `/pages/tour/index/index?action=capture&picUrl=${picUrl}&id=${paramObj.id}&channelid=${paramObj.channelid}&mallId=${paramObj.mallId}&title=${paramObj.name}&atoken=${paramObj.atoken}&userId=${paramObj.userId}`
// });
// }else{
// /***********巡店详情截图****************/
// wx.miniProgram.navigateTo({
// url: `/pages/tour/titemDetail/index?action=capture&picUrl=${picUrl}&id=${paramObj.tid}`
// });
// }
// })
};
</script>
<style scoped lang="less">
.tour-btns1{
display: flex;
background: #E6EDFF;
height: 15vw;
line-height: 15vw;
padding: 0 20px;
.box{
display: inline-block;
flex: 1;
}
.box1{
text-align: right;
span{
margin-left: 20px;
}
}
.icon{
transform: rotate(90deg);
}
}
</style>
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!