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