food/src/components/ControllerTabs.vue
tk 5ca0c40754 【类 型】:fix 飞机超重提示标签bug
【原  因】:飞机载重上线 和 货物总重量比较 字符串比较 出错
【过  程】:转成number类型
【影  响】:
2024-08-08 17:57:06 +08:00

970 lines
37 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 w-100 flex mse mac">
<font>俯</font>
<el-slider @change="releaseCameraSlider('pitch')" @input="setCamera('pitch')" v-model="pitchValue"
:show-tooltip="false" class="w-80"></el-slider>
<font>仰</font>
</div>
<div class="slider-container w-100 flex mse mac m-t-10">
<font>左</font>
<el-slider @change="releaseCameraSlider('yaw')" @input="setCamera('yaw')" v-model="yawValue"
:show-tooltip="false" class="w-80"></el-slider>
<font>右</font>
</div>
<div class="slider-container w-100 flex mse mac m-t-10">
<font>大</font>
<el-slider @change="releaseCameraSlider('scale')" @input="setCamera('scale')" v-model="scaleValue"
:show-tooltip="false" class="w-80"></el-slider>
<font>小</font>
</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 v-if="waringTags.length > 0" label="提示">
<el-tag v-for="(tag, index) in waringTags" :key="index" class="m-r-5" type="warning">{{ tag }}</el-tag>
</el-form-item>
<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="Number(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="Number(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="Number(plane.planeState.state) === 4" 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="Number(plane.planeState.state) === 12" type="info"
:loading="true" disabled>
<font class="m-l-5">解锁中...</font>
</el-button>
<el-button size="mini" class="f-s-14" v-else-if="Number(plane.planeState.state) === 20" 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 size="mini" class="f-s-14" v-else-if="isShipped" type="info" :loading="true" disabled>
<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-cuowu" 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:8,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 { lockSite } 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: ''
},
waringTags: []// 任务栏 提示标签 有退款 超重 等
}
},
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: 是否已经发货
*/
isShipped () {
if (this.executeOrder) {
return this.executeOrder.shipment_status === '已发货'
}
return false
},
/**
* @description: 航线列表
*/
routeList () {
return this.$store.state.routeList
},
/**
* @description: 获取站点列表
*/
siteList () {
return this.$store.state.siteList
}
},
methods: {
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(() => {
this.$message({
type: 'info',
message: '取消提交订单'
})
reject(new Error('Weight check failed')) // 用户取消操作
})
} else {
resolve() // 如果重量没有超出限制,直接通过检查
}
})
// 检查站点是否有飞机正在执行
const runningCheck = weightCheck.then(() => {
return new Promise((resolve, reject) => {
if ((checkOrder.runing ?? '').split(',').some(item => item !== '')) {
// 如果站点已经有飞机在执行任务,显示确认弹窗
this.$confirm('此订单的目标站点,已经有飞机正在执行任务。请注意安全!', '检测订单', {
confirmButtonText: '继续',
cancelButtonText: '放弃',
type: 'warning'
})
.then(() => {
resolve() // 用户选择继续
})
.catch(() => {
this.$message({
type: 'info',
message: '取消提交订单'
})
reject(new Error('Running check failed')) // 用户取消操作
})
} else {
resolve() // 如果没有飞机正在执行任务,直接通过检查
}
})
})
// 选择订单检查并执行任务
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
}
// 站点表 和 订单表 同时修改 没问题 向飞机提交航点
const res = await lockSite({
site_id: this.currentOrder.receive_site_id,
shop_id: this.plane.shop_id,
runing: newRuning,
order_id: 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: 取消任务
*/
async reQuest () {
if (!this.executeOrder) { // 只有飞机锁定状态 才向下执行 "取消"操作
this.$message.warning('当前没有执行任务')
return
}
try {
const confirmation = await this.$confirm('确认货物未送达,取消已发货状态,并使飞机复位?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if (confirmation) {
// 分割字符串成数组
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(',')
// 站点表 和 订单表 同时修改 没问题 让飞机状态复位
const res = await lockSite({
site_id: this.executeOrder.receive_site_id,
shop_id: this.plane.shop_id,
runing: newRuning,
order_id: this.executeOrder.id,
shipment_status: '已接单',
by_plane_id: 'null'
})
if (res.data.status === 1) {
this.publishFun('{"resetState":1}') // 发送设置飞机状态主题 状态设为闲置
}
}
} catch (error) {
this.$message.info('取消操作!')
}
},
/**
* @description: 已送达任务
*/
async overQuest () {
if (!this.executeOrder) {
this.$message.warning('当前没有执行任务')
return
}
try {
const confirmation = await this.$confirm('确认货物已送达?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if (confirmation) {
// 分割字符串成数组
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(',')
// 站点表 和 订单表 同时修改 没问题 让飞机状态复位
const res = await lockSite({
site_id: this.executeOrder.receive_site_id,
shop_id: this.plane.shop_id,
runing: newRuning,
order_id: this.executeOrder.id,
shipment_status: '已送达',
by_plane_id: this.plane.id
})
if (res.data.status === 1) {
this.publishFun('{"resetState":1}') // 发送设置飞机状态主题 状态设为闲置
}
}
} catch (error) {
console.error('Confirmation dialog error:', error)
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
},
/**
* 设置执行订单 提示标签
* @param order 执行订单
*/
setExecuteOrderTag (order) {
const aIndex = this.waringTags.indexOf('已申请退款')
const bIndex = this.waringTags.indexOf('已退款')
const cIndex = this.waringTags.indexOf('超出飞机载重')
const dIndex = this.waringTags.indexOf('站点多架执行')
// 退款提示
if (order.refund_status === '申请中') {
if (aIndex === -1) {
this.waringTags.push('已申请退款')
}
} else if (order.refund_status === '已同意') {
if (bIndex === -1) {
this.waringTags.push('已退款')
}
} else {
if (aIndex !== -1) {
this.waringTags.splice(aIndex, 1)
}
if (bIndex !== -1) {
this.waringTags.splice(bIndex, 1)
}
}
// 超出飞机载重 提示
if (Number(this.plane.weight_max) < Number(order.total_weight)) {
if (cIndex === -1) {
this.waringTags.push('超出飞机载重')
}
} else {
if (cIndex !== -1) {
this.waringTags.splice(cIndex, 1)
}
}
// 站点多飞机执行 提示
const splitArray = order.runing.split(',')// 分割执行任务的飞机s
const nonEmptyArray = splitArray.filter(item => item.trim() !== '')// 过滤掉空值
if (nonEmptyArray.length > 1) { // 不止一架
this.waringTags.push('站点多架执行')
} else {
if (dIndex !== -1) {
this.waringTags.splice(dIndex, 1)
}
}
}
},
mounted () {
// 初始化
if (this.executeOrder) { // 有正在执行单点
// 设置提示标签
this.setExecuteOrderTag(this.executeOrder)
// 绘制地图
this.makeRouteForMap()
} else { // 没有执行订单
if (this.plane) {
this.publishFun('{"resetState":1}')// 发送设置飞机状态主题 状态设为闲置
}
}
},
watch: {
executeOrder (val) {
if (val) {
/* 如果当前飞机正在执行任务 把航线绘制到地图上 */
this.makeRouteForMap()
/* 检查添加提示标签 */
this.setExecuteOrderTag(val)
} 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>