2 changed files with 845 additions and 0 deletions
@ -0,0 +1,27 @@ |
|||||
|
import { createAPI } from "@/utils/httpRequest.js"; |
||||
|
|
||||
|
// 获取AGV运输任务列表
|
||||
|
export const getTransportTaskList = data => createAPI(`agvTask/getTransportTaskList`, 'POST', data) |
||||
|
|
||||
|
// 获取运输任务明细列表
|
||||
|
export const getTransportTaskDetails = data => createAPI(`agvTask/getTransportTaskDetails`, 'POST', data) |
||||
|
|
||||
|
// ==================== TUSK集成接口 ====================
|
||||
|
|
||||
|
// 调整任务优先级
|
||||
|
export const adjustPriority = data => createAPI(`agvTask/adjustPriority`, 'POST', data) |
||||
|
|
||||
|
// 变更任务位置
|
||||
|
export const changeLocation = data => createAPI(`agvTask/changeLocation`, 'POST', data) |
||||
|
|
||||
|
// 取消TUSK任务
|
||||
|
export const cancelTuskTask = data => createAPI(`agvTask/cancelTuskTask`, 'POST', data) |
||||
|
|
||||
|
// 获取AGV实时状态
|
||||
|
export const getAgvStatus = data => createAPI(`agvTask/getAgvStatus`, 'POST', data) |
||||
|
|
||||
|
// 获取AGV告警信息
|
||||
|
export const getAgvAlarms = data => createAPI(`agvTask/getAgvAlarms`, 'POST', data) |
||||
|
|
||||
|
// 测试TUSK连接
|
||||
|
export const testTuskConnection = data => createAPI(`agvTask/testTuskConnection`, 'POST', data) |
||||
@ -0,0 +1,818 @@ |
|||||
|
<template> |
||||
|
<div class="mod-config"> |
||||
|
<el-row> |
||||
|
<el-col :span="24"> |
||||
|
<el-button @click="getDataList()" type="primary">查询</el-button> |
||||
|
<el-button @click="showAgvMonitor()" type="success">AGV监控</el-button> |
||||
|
<el-button @click="testTuskConnection()" type="info">测试连接</el-button> |
||||
|
<download-excel |
||||
|
:fields="fields()" |
||||
|
:data="exportData" |
||||
|
type="xls" |
||||
|
:name="exportName" |
||||
|
:header="exportHeader" |
||||
|
:footer="exportFooter" |
||||
|
:defaultValue="exportDefaultValue" |
||||
|
:fetch="createExportData" |
||||
|
:before-generate="startDownload" |
||||
|
:before-finish="finishDownload" |
||||
|
worksheet="导出信息" |
||||
|
class="el-button el-button--primary el-button--medium"> |
||||
|
导出 |
||||
|
</download-excel> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- 查询条件 --> |
||||
|
<el-row> |
||||
|
<el-col :span="24"> |
||||
|
<el-form :inline="true" label-position="top"> |
||||
|
<el-form-item label="任务单号"> |
||||
|
<el-input style="width: 150px;" v-model="queryHeaderData.taskNo" placeholder="请输入任务单号"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="来源类型"> |
||||
|
<el-select v-model="queryHeaderData.sourceType" placeholder="请选择" style="width: 150px;"> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option label="领料" value="ISSUE"></el-option> |
||||
|
<el-option label="入库" value="INBOUND"></el-option> |
||||
|
<el-option label="出库" value="OUTBOUND"></el-option> |
||||
|
<el-option label="移库" value="TRANSFER"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="来源单号"> |
||||
|
<el-input style="width: 150px;" v-model="queryHeaderData.sourceBillNo" placeholder="请输入来源单号"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="物料编码"> |
||||
|
<el-input style="width: 150px;" v-model="queryHeaderData.partNo" placeholder="请输入物料编码"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="AGV编码"> |
||||
|
<el-input style="width: 150px;" v-model="queryHeaderData.agvCode" placeholder="请输入AGV编码"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="任务状态"> |
||||
|
<el-select v-model="queryHeaderData.status" placeholder="请选择" style="width: 150px;"> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option label="已创建" value="CREATED"></el-option> |
||||
|
<el-option label="已下发WCS" value="SENT_TO_WCS"></el-option> |
||||
|
<el-option label="执行中" value="EXECUTING"></el-option> |
||||
|
<el-option label="失败" value="FAILED"></el-option> |
||||
|
<el-option label="已取消" value="CANCELED"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- 数据表格 --> |
||||
|
<el-row> |
||||
|
<el-col :span="24"> |
||||
|
<el-table |
||||
|
:height="height" |
||||
|
:data="dataList" |
||||
|
border |
||||
|
v-loading="dataListLoading" |
||||
|
style="width: 100%;" |
||||
|
row-key="id" |
||||
|
@expand-change="handleExpandChange"> |
||||
|
|
||||
|
<!-- 展开行 --> |
||||
|
<el-table-column type="expand"> |
||||
|
<template slot-scope="props"> |
||||
|
<div > |
||||
|
<el-table |
||||
|
:data="props.row.details" |
||||
|
border |
||||
|
style="width: 100%;" |
||||
|
class="detail-table"> |
||||
|
<el-table-column prop="seqNo" label="序号" width="80" align="center"></el-table-column> |
||||
|
<el-table-column prop="actionType" label="动作类型" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="fromLocation" label="起始位置" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="toLocation" label="目标位置" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="agvCode" label="AGV编码" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="status" label="状态" width="100" align="center"> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="startTime" label="开始时间" width="160" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.startTime | dateFormat }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="completeTime" label="完成时间" width="160" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.completeTime | dateFormat }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="comment" label="备注" min-width="150"></el-table-column> |
||||
|
<el-table-column prop="errorMsg" label="错误信息" min-width="200"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.errorMsg" style="color: red;">{{ scope.row.errorMsg }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<el-table-column prop="taskNo" label="任务单号" width="180" align="center"></el-table-column> |
||||
|
<el-table-column prop="sourceType" label="来源类型" width="100" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ getSourceTypeText(scope.row.sourceType) }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="sourceBillNo" label="来源单号" width="150" align="center"></el-table-column> |
||||
|
<el-table-column prop="partNo" label="物料编码" width="150" align="center"></el-table-column> |
||||
|
<el-table-column prop="qty" label="数量" width="100" align="center"></el-table-column> |
||||
|
<el-table-column prop="batchNo" label="批次号" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="serialNo" label="标签号" width="150" align="center"></el-table-column> |
||||
|
<el-table-column prop="fromLocation" label="起始库位" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="toLocation" label="目标库位" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="palletId" label="托盘ID" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="agvCode" label="AGV编码" width="120" align="center"></el-table-column> |
||||
|
<el-table-column prop="priority" label="优先级" width="80" align="center"></el-table-column> |
||||
|
<el-table-column prop="status" label="状态" width="120" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag :type="getStatusType(scope.row.status)">{{ getStatusText(scope.row.status) }}</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="wmsSendTime" label="WMS发送时间" width="160" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.wmsSendTime | dateFormat }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="startTime" label="开始时间" width="160" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.startTime | dateFormat }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="completeTime" label="完成时间" width="160" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.completeTime | dateFormat }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="errorMsg" label="错误信息" min-width="200"> |
||||
|
<template slot-scope="scope"> |
||||
|
<span v-if="scope.row.errorMsg" style="color: red;">{{ scope.row.errorMsg }}</span> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="createdTime" label="创建时间" width="160" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
{{ scope.row.createdTime | dateFormat }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
|
||||
|
<!-- TUSK操作列 --> |
||||
|
<el-table-column label="操作" width="150" align="center" fixed="right"> |
||||
|
<template slot-scope="scope"> |
||||
|
<a size="mini" type="primary" @click="adjustPriority(scope.row)" |
||||
|
>调整优先级</a> |
||||
|
<a size="mini" type="warning" @click="changeLocation(scope.row)" |
||||
|
>变更位置</a> |
||||
|
<a size="mini" type="danger" style="color: red" @click="cancelTuskTask(scope.row)" |
||||
|
>取消</a> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页 --> |
||||
|
<el-pagination |
||||
|
@size-change="sizeChangeHandle" |
||||
|
@current-change="currentChangeHandle" |
||||
|
:current-page="queryHeaderData.page" |
||||
|
:page-sizes="[10, 20, 50, 100]" |
||||
|
:page-size="queryHeaderData.size" |
||||
|
:total="queryHeaderData.totalCount" |
||||
|
layout="total, sizes, prev, pager, next, jumper"> |
||||
|
</el-pagination> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- 调整优先级对话框 --> |
||||
|
<el-dialog title="调整任务优先级" :visible.sync="priorityDialogVisible" width="240px" :close-on-click-modal="false"> |
||||
|
<el-form :model="priorityForm" label-width="100px" :inline="true" label-position="top"> |
||||
|
<el-form-item label="任务ID"> |
||||
|
<el-input v-model="priorityForm.taskId" disabled style="width: 100%"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="当前优先级"> |
||||
|
<el-input v-model="priorityForm.currentPriority" disabled style="width: 100%"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="新优先级" required> |
||||
|
<el-input v-model="priorityForm.newPriority" style="width: 100%"></el-input> |
||||
|
<div style="font-size: 12px; color: #999; margin-top: 5px;">优先级范围:1-9,数字越大优先级越高</div> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer" style="margin-top: 20px"> |
||||
|
<el-button @click="priorityDialogVisible = false">取消</el-button> |
||||
|
<el-button type="primary" @click="confirmAdjustPriority" :loading="priorityLoading">确定</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
|
||||
|
<!-- 变更位置对话框 --> |
||||
|
<el-dialog title="变更任务位置" :visible.sync="locationDialogVisible" width="180px" :close-on-click-modal="false"> |
||||
|
<el-form :model="locationForm" label-width="100px" :inline="true" label-position="top"> |
||||
|
<el-form-item label="任务ID"> |
||||
|
<el-input v-model="locationForm.taskId" disabled></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="当前起点"> |
||||
|
<el-input v-model="locationForm.currentFromLocation" disabled></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="当前终点"> |
||||
|
<el-input v-model="locationForm.currentToLocation" disabled></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="新终点" required> |
||||
|
<el-input v-model="locationForm.newToLocation" placeholder="请输入新的终点位置"></el-input> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button @click="locationDialogVisible = false">取消</el-button> |
||||
|
<el-button type="primary" @click="confirmChangeLocation" :loading="locationLoading">确定</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
|
||||
|
<!-- AGV监控对话框 --> |
||||
|
<el-dialog title="AGV实时监控" :visible.sync="agvMonitorVisible" width="56%" :close-on-click-modal="false"> |
||||
|
<div> |
||||
|
<!-- 状态统计 --> |
||||
|
<el-row :gutter="20" style="margin-bottom: 20px;"> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="stat-card"> |
||||
|
<div class="stat-content"> |
||||
|
<span class="stat-number">{{ agvStats.online }}</span> |
||||
|
<span class="stat-label">在线AGV</span> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="stat-card"> |
||||
|
<div class="stat-content"> |
||||
|
<span class="stat-number">{{ agvStats.running }}</span> |
||||
|
<span class="stat-label">运行中</span> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="stat-card"> |
||||
|
<div class="stat-content"> |
||||
|
<span class="stat-number">{{ agvStats.idle }}</span> |
||||
|
<span class="stat-label">空闲</span> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
<el-col :span="6"> |
||||
|
<el-card class="stat-card error"> |
||||
|
<div class="stat-content"> |
||||
|
<span class="stat-number">{{ agvStats.error }}</span> |
||||
|
<span class="stat-label">异常</span> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
|
||||
|
<!-- AGV状态表格 --> |
||||
|
<el-table :data="agvList" border style="width: 100%;" max-height="400"> |
||||
|
<el-table-column prop="id" label="AGV编号" width="100" align="center"></el-table-column> |
||||
|
<el-table-column prop="soc" label="电量" width="120" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-progress :percentage="scope.row.soc" :color="getBatteryColor(scope.row.soc)"></el-progress> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="agvStat" label="状态" width="120" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag :type="getAgvStatusType(scope.row.agvStat)"> |
||||
|
{{ getAgvStatusText(scope.row.agvStat) }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="cargo" label="载货状态" width="100" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag :type="scope.row.cargo ? 'success' : 'info'"> |
||||
|
{{ scope.row.cargo ? '有货' : '空载' }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="offline" label="连接状态" width="100" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag :type="scope.row.offline ? 'danger' : 'success'"> |
||||
|
{{ scope.row.offline ? '离线' : '在线' }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column label="位置信息" width="200" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<div>X: {{ scope.row.x }}mm</div> |
||||
|
<div>Y: {{ scope.row.y }}mm</div> |
||||
|
<div>角度: {{ (scope.row.theta / 1000).toFixed(1) }}°</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="offPlat" label="控制模式" width="100" align="center"> |
||||
|
<template slot-scope="scope"> |
||||
|
<el-tag :type="scope.row.offPlat ? 'warning' : 'primary'"> |
||||
|
{{ scope.row.offPlat ? '手动' : '自动' }} |
||||
|
</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
</div> |
||||
|
<div slot="footer" class="dialog-footer"> |
||||
|
<el-button @click="refreshAgvStatus" type="primary">刷新</el-button> |
||||
|
<el-button @click="agvMonitorVisible = false">关闭</el-button> |
||||
|
</div> |
||||
|
</el-dialog> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import { |
||||
|
getTransportTaskList, |
||||
|
getTransportTaskDetails, |
||||
|
adjustPriority as adjustPriorityApi, |
||||
|
changeLocation as changeLocationApi, |
||||
|
cancelTuskTask as cancelTuskTaskApi, |
||||
|
getAgvStatus, |
||||
|
testTuskConnection as testTuskConnectionApi |
||||
|
} from '@/api/automatedWarehouse/agvTask.js' |
||||
|
import { |
||||
|
userFavoriteList, |
||||
|
saveUserFavorite, |
||||
|
removeUserFavorite, |
||||
|
} from '@/api/userFavorite.js' |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
queryHeaderData: { |
||||
|
site: this.$store.state.user.site, |
||||
|
taskNo: '', |
||||
|
sourceType: '', |
||||
|
sourceBillNo: '', |
||||
|
partNo: '', |
||||
|
agvCode: '', |
||||
|
status: '', |
||||
|
page: 1, |
||||
|
size: 10, |
||||
|
totalCount: 0 |
||||
|
}, |
||||
|
// table高度 |
||||
|
height: 450, |
||||
|
// 是否收藏 |
||||
|
favorite: false, |
||||
|
// 数据集 |
||||
|
dataList: [], |
||||
|
dataListLoading: false, |
||||
|
// 导出相关 |
||||
|
exportData: [], |
||||
|
exportName: "AGV任务管理", |
||||
|
exportHeader: ["AGV任务管理"], |
||||
|
exportFooter: [], |
||||
|
exportDefaultValue: "", |
||||
|
|
||||
|
// TUSK集成相关 |
||||
|
agvMonitorVisible: false, |
||||
|
agvList: [], |
||||
|
agvStats: { |
||||
|
online: 0, |
||||
|
running: 0, |
||||
|
idle: 0, |
||||
|
error: 0 |
||||
|
}, |
||||
|
// 调整优先级对话框 |
||||
|
priorityDialogVisible: false, |
||||
|
priorityLoading: false, |
||||
|
priorityForm: { |
||||
|
taskId: '', |
||||
|
currentPriority: '', |
||||
|
newPriority: '' |
||||
|
}, |
||||
|
// 变更位置对话框 |
||||
|
locationDialogVisible: false, |
||||
|
locationLoading: false, |
||||
|
locationForm: { |
||||
|
taskId: '', |
||||
|
currentFromLocation: '', |
||||
|
currentToLocation: '', |
||||
|
newFromLocation: '', |
||||
|
newToLocation: '' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.$nextTick(() => { |
||||
|
this.height = window.innerHeight - 220; |
||||
|
}) |
||||
|
}, |
||||
|
activated() { |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
filters: { |
||||
|
dateFormat(value) { |
||||
|
if (!value) return '' |
||||
|
const date = new Date(value) |
||||
|
return date.toLocaleString('zh-CN', { |
||||
|
year: 'numeric', |
||||
|
month: '2-digit', |
||||
|
day: '2-digit', |
||||
|
hour: '2-digit', |
||||
|
minute: '2-digit', |
||||
|
second: '2-digit' |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取数据列表 |
||||
|
getDataList() { |
||||
|
this.dataListLoading = true |
||||
|
getTransportTaskList(this.queryHeaderData).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
// 保存当前展开的行的明细数据 |
||||
|
const expandedDetails = {}; |
||||
|
this.dataList.forEach(item => { |
||||
|
if (item.details) { |
||||
|
expandedDetails[item.id] = item.details; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 更新数据列表 |
||||
|
this.dataList = data.page.list || []; |
||||
|
|
||||
|
// 恢复展开行的明细数据 |
||||
|
this.dataList.forEach(item => { |
||||
|
if (expandedDetails[item.id]) { |
||||
|
this.$set(item, 'details', expandedDetails[item.id]); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
this.queryHeaderData.page = data.page.currPage |
||||
|
this.queryHeaderData.size = data.page.pageSize |
||||
|
this.queryHeaderData.totalCount = data.page.totalCount |
||||
|
} |
||||
|
this.dataListLoading = false |
||||
|
}).catch(error => { |
||||
|
console.error('获取数据失败:', error) |
||||
|
this.$message.error('获取数据失败') |
||||
|
this.dataListLoading = false |
||||
|
}) |
||||
|
}, |
||||
|
|
||||
|
// 处理展开行变化 |
||||
|
handleExpandChange(row, expandedRows) { |
||||
|
const isExpanded = expandedRows.some(r => r.id === row.id); |
||||
|
if (isExpanded) { |
||||
|
// 展开时加载明细数据 |
||||
|
if (!row.details || row.details.length === 0) { |
||||
|
getTransportTaskDetails(row.taskNo).then(({data}) => { |
||||
|
if (data && data.code === 0) { |
||||
|
// 使用Vue.set确保响应式更新 |
||||
|
this.$set(row, 'details', data.details || []) |
||||
|
// 强制更新表格 |
||||
|
this.$forceUpdate() |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
console.error('获取任务明细失败:', error) |
||||
|
this.$message.error('获取任务明细失败') |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// 获取状态文本 |
||||
|
getStatusText(status) { |
||||
|
const statusMap = { |
||||
|
'CREATED': '已创建', |
||||
|
'SENT_TO_WCS': '已下发WCS', |
||||
|
'EXECUTING': '执行中', |
||||
|
'COMPLETED': '已完成', |
||||
|
'FAILED': '失败', |
||||
|
'CANCELED': '已取消' |
||||
|
} |
||||
|
return statusMap[status] || status |
||||
|
}, |
||||
|
|
||||
|
// 获取状态标签类型 |
||||
|
getStatusType(status) { |
||||
|
const typeMap = { |
||||
|
'CREATED': 'info', |
||||
|
'SENT_TO_WCS': 'warning', |
||||
|
'EXECUTING': 'primary', |
||||
|
'COMPLETED': 'success', |
||||
|
'FAILED': 'danger', |
||||
|
'CANCELED': 'info' |
||||
|
} |
||||
|
return typeMap[status] || 'info' |
||||
|
}, |
||||
|
|
||||
|
// 获取来源类型文本 |
||||
|
getSourceTypeText(sourceType) { |
||||
|
const typeMap = { |
||||
|
'ISSUE': '领料', |
||||
|
'INBOUND': '入库', |
||||
|
'OUTBOUND': '出库', |
||||
|
'TRANSFER': '移库' |
||||
|
} |
||||
|
return typeMap[sourceType] || sourceType |
||||
|
}, |
||||
|
|
||||
|
// 每页数 |
||||
|
sizeChangeHandle(val) { |
||||
|
this.queryHeaderData.size = val |
||||
|
this.queryHeaderData.page = 1 |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
|
||||
|
// 当前页 |
||||
|
currentChangeHandle(val) { |
||||
|
this.queryHeaderData.page = val |
||||
|
this.getDataList() |
||||
|
}, |
||||
|
|
||||
|
|
||||
|
// 导出相关方法 |
||||
|
fields() { |
||||
|
return { |
||||
|
"任务单号": "taskNo", |
||||
|
"来源类型": "sourceType", |
||||
|
"来源单号": "sourceBillNo", |
||||
|
"物料编码": "partNo", |
||||
|
"数量": "qty", |
||||
|
"批次号": "batchNo", |
||||
|
"标签号": "serialNo", |
||||
|
"起始库位": "fromLocation", |
||||
|
"目标库位": "toLocation", |
||||
|
"托盘ID": "palletId", |
||||
|
"AGV编码": "agvCode", |
||||
|
"优先级": "priority", |
||||
|
"状态": "status", |
||||
|
"创建时间": "createdTime" |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
createExportData() { |
||||
|
return this.dataList; |
||||
|
}, |
||||
|
|
||||
|
startDownload() { |
||||
|
// 导出开始 |
||||
|
}, |
||||
|
|
||||
|
finishDownload() { |
||||
|
// 导出完成 |
||||
|
}, |
||||
|
|
||||
|
// ==================== TUSK集成方法 ==================== |
||||
|
|
||||
|
// 调整任务优先级 |
||||
|
adjustPriority(row) { |
||||
|
this.priorityForm.taskId = row.taskNo; |
||||
|
this.priorityForm.currentPriority = row.priority || '未设置'; |
||||
|
this.priorityForm.newPriority = row.priority || 5; |
||||
|
this.priorityDialogVisible = true; |
||||
|
}, |
||||
|
|
||||
|
confirmAdjustPriority() { |
||||
|
if (!this.priorityForm.newPriority || this.priorityForm.newPriority < 1 || this.priorityForm.newPriority > 9) { |
||||
|
this.$message.error('请输入有效的优先级(1-9)'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.priorityLoading = true; |
||||
|
const params = { |
||||
|
taskId: this.priorityForm.taskId, |
||||
|
priority: this.priorityForm.newPriority |
||||
|
}; |
||||
|
|
||||
|
adjustPriorityApi(params).then(({data}) => { |
||||
|
this.priorityLoading = false; |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('优先级调整成功'); |
||||
|
this.priorityDialogVisible = false; |
||||
|
this.getDataList(); |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '优先级调整失败'); |
||||
|
} |
||||
|
}).catch(() => { |
||||
|
this.priorityLoading = false; |
||||
|
this.$message.error('优先级调整异常'); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 变更任务位置 |
||||
|
changeLocation(row) { |
||||
|
this.locationForm.taskId = row.taskNo; |
||||
|
this.locationForm.currentFromLocation = row.fromLocation || '未设置'; |
||||
|
this.locationForm.currentToLocation = row.toLocation || '未设置'; |
||||
|
this.locationForm.newFromLocation = row.fromLocation || ''; |
||||
|
this.locationForm.newToLocation = ''; |
||||
|
this.locationDialogVisible = true; |
||||
|
}, |
||||
|
|
||||
|
confirmChangeLocation() { |
||||
|
if (!this.locationForm.newFromLocation || !this.locationForm.newToLocation) { |
||||
|
this.$message.error('请输入新的起点和终点位置'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.locationLoading = true; |
||||
|
const params = { |
||||
|
taskId: this.locationForm.taskId, |
||||
|
fromLocation: this.locationForm.newFromLocation, |
||||
|
toLocation: this.locationForm.newToLocation |
||||
|
}; |
||||
|
|
||||
|
changeLocationApi(params).then(({data}) => { |
||||
|
this.locationLoading = false; |
||||
|
if (data && data.code === 0) { |
||||
|
this.$message.success('位置变更成功'); |
||||
|
this.locationDialogVisible = false; |
||||
|
this.getDataList(); |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '位置变更失败'); |
||||
|
} |
||||
|
}).catch(() => { |
||||
|
this.locationLoading = false; |
||||
|
this.$message.error('位置变更异常'); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 取消TUSK任务 |
||||
|
cancelTuskTask(row) { |
||||
|
this.$confirm('确定要取消此TUSK任务吗?取消后可能无法恢复!', '警告', { |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning' |
||||
|
}).then(() => { |
||||
|
const params = { taskNo: row.taskNo }; |
||||
|
|
||||
|
cancelTuskTaskApi(params).then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.$message.success(data.msg || 'TUSK任务取消成功'); |
||||
|
this.getDataList(); // 刷新列表 |
||||
|
} else { |
||||
|
this.$message.error(data.msg || 'TUSK任务取消失败'); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.$message.error('取消TUSK任务异常:' + error.message); |
||||
|
}); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 判断任务是否可以调整优先级 |
||||
|
canAdjustPriority(status) { |
||||
|
// 可以调整优先级的状态:已创建、已下发、等待中、执行中 |
||||
|
return ['CREATED', 'SENT_TO_WCS', 'WAITING', 'IN_PROGRESS', 'EXECUTING'].includes(status); |
||||
|
}, |
||||
|
|
||||
|
// 判断任务是否可以取消 |
||||
|
canCancelTask(status) { |
||||
|
return ['CREATED', 'SENT_TO_WCS', 'EXECUTING', 'WAITING', 'IN_PROGRESS'].includes(status); |
||||
|
}, |
||||
|
|
||||
|
// 判断任务是否可以变更位置 |
||||
|
canChangeLocation(status) { |
||||
|
// 可以变更位置的状态:已创建、等待中(未开始执行的任务) |
||||
|
return ['CREATED', 'WAITING'].includes(status); |
||||
|
}, |
||||
|
|
||||
|
// 显示AGV监控 |
||||
|
showAgvMonitor() { |
||||
|
this.agvMonitorVisible = true; |
||||
|
this.refreshAgvStatus(); |
||||
|
}, |
||||
|
|
||||
|
// 刷新AGV状态 |
||||
|
refreshAgvStatus() { |
||||
|
getAgvStatus().then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.agvList = data.agvList || []; |
||||
|
this.calculateAgvStats(); |
||||
|
} else { |
||||
|
this.$message.error(data.msg || '获取AGV状态失败'); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.$message.error('获取AGV状态异常:' + error.message); |
||||
|
}); |
||||
|
}, |
||||
|
|
||||
|
// 计算AGV统计信息 |
||||
|
calculateAgvStats() { |
||||
|
this.agvStats = { |
||||
|
online: this.agvList.filter(agv => !agv.offline).length, |
||||
|
running: this.agvList.filter(agv => agv.agvStat >= 1 && agv.agvStat <= 12).length, |
||||
|
idle: this.agvList.filter(agv => agv.agvStat === 0).length, |
||||
|
error: this.agvList.filter(agv => agv.agvStat >= 128).length |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
// 获取AGV状态文本 |
||||
|
getAgvStatusText(status) { |
||||
|
const statusMap = { |
||||
|
0: '空闲', |
||||
|
1: '运行中', |
||||
|
2: '直线运动中', |
||||
|
3: '旋转中', |
||||
|
13: '充电中', |
||||
|
23: '机器人暂停', |
||||
|
128: '异常状态', |
||||
|
129: '急停按钮触发', |
||||
|
130: '碰撞告警触发', |
||||
|
131: '告警' |
||||
|
}; |
||||
|
return statusMap[status] || `未知状态(${status})`; |
||||
|
}, |
||||
|
|
||||
|
// 获取AGV状态标签类型 |
||||
|
getAgvStatusType(status) { |
||||
|
if (status === 0) return 'info'; // 空闲 |
||||
|
if (status >= 1 && status <= 12) return 'primary'; // 运行中 |
||||
|
if (status === 13) return 'success'; // 充电中 |
||||
|
if (status >= 128) return 'danger'; // 异常状态 |
||||
|
return 'info'; |
||||
|
}, |
||||
|
|
||||
|
// 获取电池颜色 |
||||
|
getBatteryColor(soc) { |
||||
|
if (soc >= 60) return '#67c23a'; // 绿色 |
||||
|
if (soc >= 30) return '#e6a23c'; // 橙色 |
||||
|
return '#f56c6c'; // 红色 |
||||
|
}, |
||||
|
|
||||
|
// 测试TUSK连接 |
||||
|
testTuskConnection() { |
||||
|
this.$message.success('正在测试TUSK连接...'); |
||||
|
|
||||
|
testTuskConnectionApi().then(({data}) => { |
||||
|
if (data.code === 0) { |
||||
|
this.$message.success(data.msg || 'TUSK连接正常'); |
||||
|
} else { |
||||
|
this.$message.error(data.msg || 'TUSK连接失败'); |
||||
|
} |
||||
|
}).catch(error => { |
||||
|
this.$message.error('测试TUSK连接异常:' + error.message); |
||||
|
}); |
||||
|
} |
||||
|
}, |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
.sl-svg { |
||||
|
overflow: hidden; |
||||
|
float: right; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.el-table .el-table__expanded-cell { |
||||
|
padding: 20px 50px; |
||||
|
background-color: #f5f7fa; |
||||
|
} |
||||
|
|
||||
|
.el-table .el-table__expanded-cell h4 { |
||||
|
margin-bottom: 15px; |
||||
|
color: #409EFF; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 明细表格表头样式 - 设置淡一些的颜色 */ |
||||
|
.detail-table >>> .el-table__header-wrapper .el-table__header { |
||||
|
background-color: #fafbfc; |
||||
|
} |
||||
|
|
||||
|
.detail-table >>> .el-table__header-wrapper th { |
||||
|
background-color: #fafbfc !important; |
||||
|
color: #909399; |
||||
|
font-weight: normal; |
||||
|
} |
||||
|
|
||||
|
/* AGV监控样式 */ |
||||
|
.stat-card { |
||||
|
text-align: center; |
||||
|
cursor: pointer; |
||||
|
transition: all 0.3s; |
||||
|
} |
||||
|
|
||||
|
.stat-card:hover { |
||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15); |
||||
|
} |
||||
|
|
||||
|
.stat-card.error { |
||||
|
border-color: #f56c6c; |
||||
|
} |
||||
|
|
||||
|
.stat-content { |
||||
|
padding: 20px; |
||||
|
} |
||||
|
|
||||
|
.stat-number { |
||||
|
display: block; |
||||
|
font-size: 32px; |
||||
|
font-weight: bold; |
||||
|
color: #409EFF; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.stat-card.error .stat-number { |
||||
|
color: #f56c6c; |
||||
|
} |
||||
|
|
||||
|
.stat-label { |
||||
|
display: block; |
||||
|
font-size: 14px; |
||||
|
color: #909399; |
||||
|
} |
||||
|
</style> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue