food/src/components/ControllerTabs.vue
tk 0149d4be59 【类 型】:style 没影响
【原  因】:
【过  程】:
【影  响】:

# 类型 包含:
# feat:新功能(feature)
# fix:修补bug
# docs:文档(documentation)
# style: 格式(不影响代码运行的变动)
# refactor:重构(即不是新增功能,也不是修改bug的代码变动)
# test:增加测试
# chore:构建过程或辅助工具的变动
2024-07-26 23:33:46 +08:00

871 lines
34 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="w-100 h-100 mainBox">
<!-- 弹出框 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="320px" top="30vh">
<!-- 起飞设置弹出框 -->
<template v-if="dialogItem == 'takeoffBox'">
<el-slider class="w-100" v-model="takeoffValue" :show-tooltip="false" show-input :min="1" :max="100">
</el-slider>
<span slot="footer" class="dialog-footer">
<el-button size="medium" @click="dialogVisible = false">关闭</el-button>
<el-button size="medium" type="primary"
@click="publishFun(`{setPlaneState:{bit:6,state:1,count:1,param:[${takeoffValue}]}`); speakText('确认起飞')">确认起飞</el-button>
</span>
</template>
<!-- 摄像头弹出框 -->
<template v-if="dialogItem == 'cameraBox'">
<div class="slider-container">
<div class="m-l-10 m-r-10">俯</div>
<el-slider @change="releaseCameraSlider('pitch')" @input="setCamera('pitch')" v-model="pitchValue"
:show-tooltip="false" class="w-80"></el-slider>
<div class="m-l-10 m-r-10">仰</div>
</div>
<div class="slider-container">
<div class="m-l-10 m-r-10">左</div>
<el-slider @change="releaseCameraSlider('yaw')" @input="setCamera('yaw')" v-model="yawValue"
:show-tooltip="false" class="w-80"></el-slider>
<div class="m-l-10 m-r-10">右</div>
</div>
<div class="slider-container">
<div class="m-l-10 m-r-10">大</div>
<el-slider @change="releaseCameraSlider('scale')" @input="setCamera('scale')" v-model="scaleValue"
:show-tooltip="false" class="w-80"></el-slider>
<div class="m-l-10 m-r-10">小</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button size="medium" @click="dialogVisible = false">关闭</el-button>
</span>
</template>
</el-dialog>
<!-- 底边 tab控件组 -->
<div class="flex column mr mac tabContainer p-l-10 p-r-10">
<!-- tab控件组 内容组 -->
<div class="tabContent" :class="{ 'active': activeIndex !== null }">
<!-- 订单任务 -->
<div v-if="activeIndex === 0" class="tabContentBox">
<!-- 标题 -->
<div class="clearB m-b-15 fb f-s-16 contentTit">
<i class="iconfont icon-dingdanguanli f-s-22 m-r-5"></i>
<span>送餐任务</span>
</div>
<!-- 内容 -->
<el-form label-position="left" ref="questForm" :model="questForm" label-width="80px">
<el-form-item label="订单选择" v-if="!executeOrder">
<el-select v-model="questForm.id" :filterable="isMobile" placeholder="请选择,也可输入搜索"
:disabled="executeOrder">
<el-option v-for="item in questList" :key="item.id" :label="item.id" :value="item.id"
:class="isWaring(item) ? 'danger-color' : ''">
<span class="l">{{ item.id }}</span>
<span class="l m-l-5">{{ item.receiver }}</span>
<span class="l m-l-5">{{ item.receive_site_name }}</span>
</el-option>
</el-select>
</el-form-item>
<template v-else>
<el-form-item label="订单ID:">
{{ executeOrder.id }}
</el-form-item>
<el-form-item label="收货站点:">
{{ executeOrder.receive_site_name }}
</el-form-item>
<el-form-item label="联系电话:">
{{ executeOrder.tel }}
</el-form-item>
</template>
<el-form-item label="飞机操作">
<el-button-group>
<el-button size="mini" class="f-s-14" v-if="plane.planeState.state & 1" type="primary"
icon="f-s-14 iconfont icon-chakanzhihangrizhi" @click="checkQuest">
<font class="m-l-5">上传航点</font>
</el-button>
<el-button size="mini" class="f-s-14" v-else-if="plane.planeState.state & 2" key="wirteBut" type="info"
:loading="true" disabled>
<font class="m-l-5">航点写入中···</font>
</el-button>
<el-button size="mini" class="f-s-14"
v-else-if="plane.planeState.state & 4 && !(plane.planeState.state & 16)" type="warning"
icon="f-s-14 iconfont icon-jiesuo"
@click="publishFun('{setPlaneState:{bit:3,state:1,count:2,param:[1,0]}}'); speakText('解锁飞机')">
<font class="m-l-5">解锁飞机</font>
</el-button>
<el-button size="mini" class="f-s-14"
v-else-if="plane.planeState.state & 16 && !(plane.planeState.state & 1) && !(plane.planeState.state & 2)"
type="success" icon="f-s-14 iconfont icon-yangshi_icon_tongyong_departure"
@click="publishFun('{setPlaneState:{bit:5,state:1}'); speakText('准备起飞,执行送餐任务')">
<font class="m-l-5">执行任务</font>
</el-button>
</el-button-group>
</el-form-item>
<el-form-item label="任务操作">
<el-button-group>
<el-button size="mini" class="f-s-14" type="danger" icon="iconfont icon-meiyoudingdan-01" key="celBUt"
@click="reQuest">
<font class="m-l-5">取消任务</font>
</el-button>
<el-button size="mini" class="f-s-14" type="success" icon="iconfont icon-qiandai" key="bingBut"
@click="overQuest">
<font class="m-l-5">已送达</font>
</el-button>
</el-button-group>
</el-form-item>
</el-form>
</div>
<!-- 飞机操作 -->
<div v-else-if="activeIndex === 1" class="tabContentBox">
<!-- 标题 -->
<div class="clearB m-b-15 fb f-s-16 contentTit">
<i class="iconfont icon-youxishoubing f-s-22 m-r-5"></i>
<span>飞机控制</span>
</div>
<div class="butIconBox gap10 flex">
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{setPlaneState:{bit:3,state:1,count:2,param:[1,0]}}'); speakText('解锁飞机')">
<i class="iconfont icon-jiesuo f-s-24"></i>
<div class="m-t-5">解锁</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="confirmation('飞机加锁,螺旋桨将停转,请谨慎操作!', '加锁操作', '{setPlaneState:{bit:3,state:0,count:2,param:[0,21196]}}'); speakText('加锁,请注意安全')">
<i class=" iconfont icon-suoding f-s-24"></i>
<div class="m-t-5">加锁</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="dialogVisible = true; dialogTitle = '起飞高度(米)设置'; dialogItem = 'takeoffBox'; speakText('设置起飞高度')">
<i class="iconfont icon-yangshi_icon_tongyong_departure f-s-24"></i>
<div class="m-t-5">起飞</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{setPlaneState:{bit:7,state:1}'); speakText('悬停')">
<i class="iconfont icon-fengzheng1 f-s-24"></i>
<div class="m-t-5">悬停</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon" @click="speakText('继续执行航线')">
<i class="iconfont icon-duandianxufei f-s-24"></i>
<div class="m-t-5">复航</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{setPlaneState:{bit:9,state:1}'); speakText('返航')">
<i class="iconfont icon-yijianfanhang f-s-24"></i>
<div class="m-t-5">返航</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{setPlaneState:{bit:5,state:1}'); speakText('降落')">
<i class="iconfont icon-yangshi_icon_tongyong_arriving f-s-24"></i>
<div class="m-t-5">降落</div>
</el-button>
</div>
</div>
<!-- 附加模组操作 -->
<div v-else-if="activeIndex === 2" class="tabContentBox">
<!-- 标题 -->
<div class="clearB m-b-15 fb f-s-16 contentTit">
<i class="iconfont icon-mianxingdiaogou f-s-22 m-r-5"></i>
<span>挂载仓控制</span>
</div>
<div class="butIconBox m-b-15 gap10 flex">
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{hookConteroller:4}'); speakText('重置重量传感器')">
<i class="iconfont icon-zhongliang f-s-24"></i>
<div class="m-t-5">归零</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{hookConteroller:0}'); speakText('收钩')">
<i class="iconfont icon-xiangshang f-s-24"></i>
<div class="m-t-5">收钩</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{hookConteroller:3}'); speakText('继续放钩')">
<i class="iconfont icon-qiyong f-s-24"></i>
<div class="m-t-5">继续</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{hookConteroller:2}'); speakText('暂停放钩')">
<i class="iconfont icon-xuanting-zanting f-s-24"></i>
<div class="m-t-5">暂停</div>
</el-button>
</div>
<!-- 标题 -->
<div class="clearB m-b-15 fb f-s-16 contentTit">
<i class="iconfont icon-shipinjiankong f-s-22 m-r-5"></i>
<span>摄像头控制</span>
</div>
<div class="butIconBox m-b-15 gap10 flex">
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{cameraController:{item:0,val:0}}'); speakText('摄像头一键回中')">
<i class="iconfont icon-icon-rotation f-s-24"></i>
<div class="m-t-5">回中</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{cameraController:{item:2,val:0,yaw:0,pitch:-50}}'); speakText('摄像头一键俯瞰')">
<i class="iconfont icon-down f-s-24"></i>
<div class="m-t-5">俯瞰</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="dialogVisible = true; dialogTitle = '摄像头控制'; dialogItem = 'cameraBox'; speakText('手动调整摄像头')">
<i class="iconfont icon-chukong f-s-24"></i>
<div class="m-t-5">手动</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="dialogVisible = true; dialogTitle = '摄像头控制'; dialogItem = 'cameraBox'; speakText('调整镜头焦距')">
<i class="iconfont icon-fangda f-s-24"></i>
<div class="m-t-5">焦距</div>
</el-button>
</div>
<!-- 标题 -->
<div class="clearB m-b-15 fb f-s-16 contentTit">
<i class="iconfont icon-tongzhi f-s-22 m-r-5"></i>
<span>喇叭控制</span>
</div>
<div class="butIconBox gap10 flex">
<el-button size="medium" type="primary" class="flex1 butIcon">
<i class="iconfont icon-icon-test f-s-24"></i>
<div class="m-t-5">喊话</div>
</el-button>
</div>
</div>
<!-- 飞机调试 -->
<div v-else-if="activeIndex === 3" class="tabContentBox">
<!-- 标题 -->
<div class="clearB m-b-15 fb f-s-16 contentTit">
<i class="iconfont icon-banshou_Line f-s-22 m-r-5"></i>
<span>飞机调试</span>
</div>
<div class="butIconBox m-b-15 gap10 flex">
<el-button size="medium" type="primary" class="flex1 butIcon"
@click="publishFun('{bit:11,state:1}'); speakText('校准磁罗盘')">
<i class="iconfont icon-zhinanzhen f-s-24"></i>
<div class="m-t-5">磁罗盘</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon" @click="speakText('校准加速度计')">
<i class="iconfont icon-zuobiaozhoupeizhixiang f-s-24"></i>
<div class="m-t-5">加速度计</div>
</el-button>
<el-button size="medium" type="primary" class="flex1 butIcon" @click="speakText('写入参数')">
<i class="iconfont icon-canshupeizhi f-s-24"></i>
<div class="m-t-5">写入参数</div>
</el-button>
</div>
</div>
</div>
<!-- tab控件组 按钮组 -->
<div class="flex gap10 m-b-10 taButGroup">
<div class="flex1 h-100 taBut flex column mac mc animation" :class="activeIndex === index ? 'taButBG' : ''"
v-for="(item, index) in controlItems" :key="index" @click="toggleContent(index, item.voice)">
<i :class="item.icon" class="iconfont f-s-35 no-select"></i>
<div class="m-t-15 fb f-s-18 no-select">{{ item.title }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { questAss } from '@/utils/api/table'
import mqtt from '@/utils/mqtt'
import { speakText } from '@/utils/index'
export default {
name: 'TabController',
data () {
return {
dialogTitle: '', // 弹出框 标题
dialogItem: '', // 弹出框 项目类型判断
dialogVisible: false, // 弹出框 显隐
pitchValue: 50, // 摄像头控制滑动条 俯仰值
yawValue: 50, // 摄像头控制滑动条 旋转值
scaleValue: 50, // 摄像头控制滑动条 焦距值
takeoffValue: 2, // 起飞高度
controlItems: [// 菜单
{ title: '任务', icon: 'icon-songcanfuwu', voice: '设置送餐任务' },
{ title: '控制', icon: 'icon-youxishoubing', voice: '控制飞机' },
{ title: '扩展', icon: 'icon-linghuokuozhan', voice: '控制扩展组件' },
{ title: '调试', icon: 'icon-banshou_Line', voice: '调试飞机' }
],
activeIndex: null, // 当前选中的菜单
tabIsOpen: false, // 判断tab 是否弹出
questForm: { // 送餐任务表单
id: ''
}
}
},
props: {
plane: {
typeof: 'Object',
deep: true
}
},
computed: {
/**
* @description: 终端平台是否是pc
*/
isMobile () {
return this.$store.state.app.isMobile
},
/**
* @description: 已付款 已接单 未发起退款 订单列表
*/
questList () {
const plane = this.plane
return plane ? this.$store.state.paidOrderList.filter((item) => item.shop_id === plane.shop_id && item.shipment_status === '已接单' && item.refund_status !== '申请中') : []
},
/**
* @description: 已发货 订单列表
*/
ShippedList () {
const plane = this.plane
return plane ? this.$store.state.paidOrderList.filter((item) => item.shop_id === plane.shop_id && item.shipment_status === '已发货') : []
},
/**
* @description: 当前选框 中的订单
*/
currentOrder () {
if (this.questForm && this.questForm.id !== undefined) {
return this.questList.find((item) => item.id === this.questForm.id) || null
}
return null
},
/**
* @description: 正在执行的订单 没有未null 代表飞机空闲
*/
executeOrder () {
const plane = this.plane
if (plane) {
const order = this.ShippedList.find((item) => item.by_plane_id === plane.id)
return order && Object.keys(order).length === 0 ? null : order || null
}
return null
},
/**
* @description: 航线列表
*/
routeList () {
return this.$store.state.routeList
},
/**
* @description: 获取站点列表
*/
siteList () {
return this.$store.state.siteList
}
},
methods: {
questAss,
speakText,
/**
* @description: 摄像头 滑动条松开
* @param {string} item 项目
*/
releaseCameraSlider (item) {
if (item === 'pitch') {
this.pitchValue = 50
this.publishFun('{cameraController:{item:2,val:0,yaw:0,pitch:0}}')
} else if (item === 'yaw') {
this.yawValue = 50
this.publishFun('{cameraController:{item:2,val:0,yaw:0,pitch:0}}')
} else if (item === 'scale') {
this.publishFun('{cameraController:{item:1,val:0}}')
this.scaleValue = 50
}
},
/**
* @description: 摄像头 滑动条滚动 发送设置命令
* @param {*} item 项目
*/
setCamera (item) {
if (item === 'pitch') { // 设置pitch轴
const pitchV = (this.pitchValue - 50) * 2
this.publishFun(`{cameraController:{item:2,yaw:0,pitch:${pitchV}}`)
} else if (item === 'yaw') { // 设置yaw轴
const yawV = (this.yawValue - 50) * 2
this.publishFun(`{cameraController:{item:2,yaw:${yawV},pitch:0}`)
} else if (item === 'scale') {
if (this.scaleValue < 50) { // 焦距放大
this.publishFun('{cameraController:{item:1,val:1}}')
} else if (this.scaleValue > 50) {
this.publishFun('{cameraController:{item:1,val:255}}')
}
}
},
/**
* @description: 菜单切换 PS:UI
* @param {*} index 序号
* @param {*} voice 播放声音的文本
*/
toggleContent (index, voice) {
this.activeIndex = this.activeIndex === index ? null : index
if (this.tabIsOpen) {
if (index !== this.activeIndex) {
this.tabIsOpen = false
this.$emit('mapXOffset', 0, -200)
} else {
this.speakText(voice)
}
} else {
this.tabIsOpen = true
this.$emit('mapXOffset', 0, 200)
this.speakText(voice)
}
},
/**
* @description: 发布 mqtt 信息
* @param {*} jsonData {'item':val} // item: questAss飞行航点任务 setPlaneState 设置飞机状态 resetState设置飞机初始状态 chan1油门通道1 chan2油门通道2 chan3油门通道3 chan4油门通道4 hookConteroller钩子控制 cameraController云台相机控制
*/
publishFun (jsonData) {
if (this.plane) {
mqtt.publishFun(`cmd/${this.plane.macadd}`, jsonData)
} else {
this.$message.warning('与飞机通信未接通,请稍后')
}
},
/**
* @description: 执行任务前 先检测订单是否 合法 例如:订单重量会不会超出飞机载重上限
*/
checkQuest () {
let checkOrder // 提交要检查的任务
// 有正在执行的 检查正在执行的 否则检查当前选中的
if (this.executeOrder) {
checkOrder = this.executeOrder
} else {
checkOrder = this.currentOrder
}
// 检查是否选则了 订单
if (this.questForm.id === '' && !checkOrder) {
this.$message.error('未选择订单任务!')
return
}
// 退款状态检查
if (checkOrder.refund_status === '申请中') {
this.$message.error('此订单已被申请退款或者订单已经被取消!')
return
}
// 判断站点是否已经绑定站点 未绑定 中断操作
if (checkOrder.bind_route === null) {
this.$message.error('此站点,未绑定任务航点')
return
}
/* 如果是正在执行订单 直接调用重新提交 */
if (this.executeOrder) {
this.continueQuest()
return
}
/* 综合检查 这部分针对 新提交订单 */
// 飞机载重 上限检查
const weightCheck = new Promise((resolve, reject) => {
if (Number(checkOrder.total_weight) >= Number(this.plane.weight_max)) {
this.$confirm('此订单总重超出本飞机的载重上限', '检测订单', {
confirmButtonText: '继续',
cancelButtonText: '放弃',
type: 'warning'
})
.then(() => {
resolve()
})
.catch(action => {
this.$message({
type: 'info',
message: '取消提交订单'
})
reject(new Error('Weight check failed'))
})
} else {
resolve()
}
})
// 检查站点是否有飞机正在执行
const runningCheck = new Promise((resolve, reject) => {
if ((checkOrder.runing ?? '').split(',').some(item => item !== '')) {
this.$confirm('此订单的目标站点,已经有飞机正在执行任务。请注意安全!', '检测订单', {
confirmButtonText: '继续',
cancelButtonText: '放弃',
type: 'warning'
})
.then(() => {
resolve()
})
.catch(action => {
this.$message({
type: 'info',
message: '取消提交订单'
})
reject(new Error('Running check failed'))
})
} else {
resolve()
}
})
// 选择订单检查 执行订单任务
Promise.all([weightCheck, runningCheck])
.then(() => {
this.runQuest()
})
.catch((error) => {
console.log(error.message)
})
},
/**
* @description: 正在执行的任务 重新上传航线
*/
continueQuest () {
/* 执行写在这里 */
let routeData // 航线数据内容
try {
/* 航线选择 */
const bindRoute = (this.executeOrder.bind_route ?? '').split(',')
const runing = (this.executeOrder.runing ?? '').split(',')
const matchingIndex = runing
.map((route, index) => route === this.executeOrder.by_plane_id ? index : -1)
.filter(index => index !== -1)// 找到注册飞机与绑定航线对应索引
routeData = this.routeList.find(element => element.id === bindRoute[matchingIndex]).route_data
routeData = JSON.parse(routeData)// 反序列化
// 处理声音航点 航点里面的表达式 如$food_sn$ 正则替换成订单对应的字段
this.executeOrder.telTail = this.executeOrder.tel.substr(-4)// 手动加一个手机尾号telTail字段 从 tel字段截取后四位
routeData.questAss.tasks.forEach((x, index) => {
if (x.sound) {
const str = this.voiceRouteParse(this.executeOrder, x.sound)
routeData.questAss.tasks[index].sound = str// 重新写入声音航点
}
})
routeData = JSON.stringify(routeData)// 重新序列化
// 发送航点信息主题
this.publishFun(routeData)
} catch (error) {
this.$message.error('操作失败,航线异常')
}
},
/**
* @description: 选择订单的 执行订单任务 ps:改变订单状态 站点runing状态 上传航线
*/
async runQuest () {
/* 插入日志 */
this.$store.dispatch('fetchLog', { content: `${this.plane.name} 开始执行 订单ID${this.currentOrder.id}、叫餐号:${this.currentOrder.food_sn}号。` })
/* 执行写在这里 */
let routeData // 航线数据内容
let newRuning // 执行飞机注册后的 running字段信息
try {
/* 站点正在执行任务runing 注册 */
const runing = (this.currentOrder.runing ?? '').split(',')
let foundEmpty = false
let matchingIndex // 记录执行飞机注册的索引 此索引对应要使用的航线的索引
runing.some((item, index, arr) => {
if (item === '') {
arr[index] = this.plane.id
foundEmpty = true
matchingIndex = index
return true // 找到空位后退出循环
}
})
newRuning = runing.join(',')
if (!foundEmpty) {
this.$message({
type: 'warning',
message: '此站点所有航线均被占用,等航线空闲再试!'
})
return // 退出外层函数
}
/* 航线选择 */
const bindRoute = (this.currentOrder.bind_route ?? '').split(',')
routeData = this.routeList.find(element => element.id === bindRoute[matchingIndex]).route_data
routeData = JSON.parse(routeData)// 反序列化
// 处理声音航点 航点里面的表达式 如$food_sn$ 正则替换成订单对应的字段
this.currentOrder.telTail = this.currentOrder.tel.substr(-4)// 手动加一个手机尾号telTail字段 从 tel字段截取后四位
routeData.questAss.tasks.forEach((x, index) => {
if (x.sound) {
const str = this.voiceRouteParse(this.currentOrder, x.sound)
routeData.questAss.tasks[index].sound = str// 重新写入声音航点
}
})
routeData = JSON.stringify(routeData)// 重新序列化
} catch (error) {
this.$message.error('操作失败,航线异常')
return
}
let res = await this.$store.dispatch('fetchLockSite', { id: this.currentOrder.receive_site_id, shop_id: this.plane.shop_id, runing: newRuning })// 航线注册飞机 锁定送餐点
if (res.data.status === 1) {
res = await this.questAss(this.currentOrder.id, ['shipment_status', 'by_plane_id'], ['已发货', this.plane.id])// 订单改为发货状态和执行订 并更新订单列表
if (res.data.status === 1) {
this.publishFun(routeData)// 发送航点信息主题
}
}
},
/**
* @description: 匹配声音航点字符串 比如$food_sn$ food_sn匹配成 送餐订单里面的对应字段
* @param {*} questItem 送餐的订单 ps:从数据库里拿的 订单信息对象
* @param {*} voiceRouteStr 声音航点的字符串
*/
voiceRouteParse (questItem, voiceRouteStr) {
// 定义正则表达式
const regex = /\$(.*?)\$/g
// 使用replace()方法进行匹配和替换
const replacedStr = voiceRouteStr.replace(regex, (match, p1) => {
// match表示匹配到的字符串p1表示捕获组中的内容
return questItem[p1] // 用捕获组的内容替换匹配到的字符串
})
// 输出替换后的字符串
return replacedStr // 输出 "请45号电话为13301115846的先生取餐"
},
/**
* @description: 确认操作
* @param {*}msg 提示的话语内容
* @param {*}tit 提示框的标题
* @param {*}instruct 需要发送的mqtt指令内容
*/
confirmation (msg, tit, instruct) {
this.$confirm(msg, tit, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.publishFun(instruct)
}).catch(() => {
this.$message.info('取消操作!')
})
},
/**
* @description: 取消任务
*/
reQuest () {
if (!this.executeOrder) { // 只有飞机锁定状态 才向下执行 "取消"操作
this.$message.warning('当前没有执行任务')
return
}
this.$confirm('取消已发货状态,并使飞机复位?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
/* 关联当前订单的站点 搜寻注册的飞机 并预设newRuning 用于注销runing字段航线 */
// 分割字符串成数组
const runingArray = this.executeOrder.runing.split(',')
// 遍历数组并替换相等的值
for (let i = 0; i < runingArray.length; i++) {
if (runingArray[i] === this.executeOrder.by_plane_id) {
runingArray[i] = ''
}
}
// 将数组重新组合成字符串
const newRuning = runingArray.join(',')
/* 确认 把订单从发货退回到已接单 站点runing字段飞机航线注销 重置飞机状态 */
this.$store.dispatch('fetchLockSite', { id: this.executeOrder.receive_site_id, shop_id: this.plane.shop_id, runing: newRuning }).then(res => { // 退回到已接单 并更新订单列表
if (res.data.status === 1) {
this.questAss(this.executeOrder.id, ['shipment_status', 'by_plane_id'], ['已接单', 'null']).then(res => { // 注销航线
if (res.data.status === 1) {
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
}
})
}
})
}).catch(() => {
this.$message.info('取消操作!')
})
},
/**
* @description: 已送达任务
*/
overQuest () {
if (!this.executeOrder) { // 只有飞机锁定状态 才向量下执行 "已送达"操作
this.$message.warning('当前没有执行任务')
return
}
this.$confirm('确认订单已送达?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
/* 关联当前订单的站点 搜寻注册的飞机 并预设newRuning 用于注销runing字段航线 */
// 分割字符串成数组
const runingArray = this.executeOrder.runing.split(',')
// 遍历数组并替换相等的值
for (let i = 0; i < runingArray.length; i++) {
if (runingArray[i] === this.executeOrder.by_plane_id) {
runingArray[i] = ''
}
}
// 将数组重新组合成字符串
const newRuning = runingArray.join(',')
/* 确认 把订单从发货退回到已接单 站点runing字段飞机航线注销 重置飞机状态 */
this.$store.dispatch('fetchLockSite', { id: this.executeOrder.receive_site_id, shop_id: this.plane.shop_id, runing: newRuning }).then(res => { // 订单改为已完成状态
if (res.data.status === 1) {
this.questAss(this.executeOrder.id, 'shipment_status', '已送达').then(res => { // 注销航线
if (res.data.status === 1) {
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
}
})
}
})
}).catch(() => {
this.$message.info('取消操作!')
})
},
/**
* @description: 在地图上绘制航线
*/
makeRouteForMap () {
let bindRoute
let routeData
let found = false
this.siteList.some(item => {
const runing = (item.runing ?? '').split(',')
const index = runing.indexOf(this.plane.id.toString())
if (index !== -1) {
found = true
// 获取航点信息
if (item.bind_route !== null) { // 判断站点是否已经绑定站点
bindRoute = (item.bind_route ?? '').split(',')
this.$store.dispatch('fetchRouteList').then(res => { // 只能异步拿 虽然效率低一些
routeData = res.find(element => element.id === bindRoute[index]).route_data
this.$emit('makeRoute', JSON.parse(routeData))
})
} else {
this.$message.error('此站点,未绑定任务航点')
}
return true // 找到匹配项后退出循环
}
return false // 继续循环
})
if (!found) {
this.$message.error('未找到匹配的站点')
}
},
/**
* @description: 判断 已接订单下拉框 的任务 有疑问的 坐标集 class样式显示成红色
* @param {*} item 对应下拉框的任务
*/
isWaring (item) {
const isOverWaight = Number(item.total_weight) >= Number(this.plane.weight_max)// 是否超重
const isQuestIng = (item.runing ?? '').split(',').some(i => i !== '') // 是否有飞机正在执行
return isOverWaight || isQuestIng
}
},
mounted () {
// 初始化
if (this.executeOrder) { // 有正在执行单点
this.makeRouteForMap()// 绘制地图
} else { // 没有执行订单
if (this.plane) {
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
}
}
},
watch: {
executeOrder (val) {
if (val) { // 如果当前飞机正在执行任务 把航线绘制到地图上
this.makeRouteForMap()
} else { // 如果没有执行任务 把地图上航线清除
this.$emit('clearRoute')
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
}
},
questList (val) {
/* 任务列表更新时 判断还有没有当前选择的id 没有就清空 */
const found = val.some(item => item.id === this.questForm.id)
if (!found) {
this.questForm.id = ''
}
}
},
destroyed () {
}
}
</script>
<style lang="scss" scoped>
@import "@/styles/theme.scss";
.danger-color {
color: $danger-color;
font-weight: bold;
}
.mainBox {
position: absolute;
}
.tabContainer {
width: 100%;
height: 100%;
}
.tabContent {
z-index: 90;
position: relative;
width: 100%;
border-radius: 10px;
max-width: 470px;
background-color: rgba(255, 255, 255, 0.8);
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);
top: -50px;
opacity: 0;
transition: top 0.5s ease, opacity 1s ease;
}
.tabContent.active {
top: -10px;
opacity: 1;
}
.tabContentBox {
padding: 20px;
}
.contentTit i {
vertical-align: middle;
}
.butIconBox {
flex-wrap: wrap;
/* 允许换行 */
justify-content: flex-start;
/* 主轴对齐方式 */
align-content: space-around;
/* 多行在侧轴上的对齐方式 */
}
.butIcon {
border-radius: 10px;
text-align: center;
border: none;
margin-left: 0px !important;
}
.taButGroup {
position: relative;
width: 100%;
max-width: 470px;
height: 105px;
cursor: pointer;
z-index: 90;
}
@media (max-width: 480px) {
.taButGroup {
height: calc((100vw - 50px)/4);
}
.tabContainer {
width: 100vw;
}
}
.taBut {
color: $maintext-color;
background-color: rgba(255, 255, 255, 0.5);
border-radius: 10px;
padding: 5px;
text-align: center;
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.2);
}
.taButBG {
color: $graylight-color;
background-color: $brand-color;
}
.gap10 {
gap: 10px;
}
</style>