Files
FTC25009/ruoyi-ui/src/views/out/job/index.vue

464 lines
16 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="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="任务号" prop="taskNo">
<el-input
v-model="queryParams.taskNo"
placeholder="请输入任务号"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="任务状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择任务状态" clearable>
<el-option
v-for="dict in dict.type.mydo_job_status"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="任务类型" prop="inOrOut">
<el-select v-model="queryParams.inOrOut" placeholder="请选择任务类型" clearable>
<el-option
v-for="dict in dict.type.out_job_type"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="执行阶段" prop="executionPhase">
<el-select v-model="queryParams.executionPhase" placeholder="请选择执行阶段" clearable>
<el-option
v-for="dict in dict.type.mydo_job_execution_phase"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="托盘码" prop="trayCode">
<el-input
v-model="queryParams.trayCode"
placeholder="请输入托盘码"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="仓库名称" prop="warehouseName">
<el-input
v-model="queryParams.warehouseName"
placeholder="请输入仓库名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="巷道ID" prop="roadwayId">
<el-input
v-model="queryParams.roadwayId"
placeholder="请输入巷道ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货位ID" prop="cargoSpaceId">
<el-input
v-model="queryParams.cargoSpaceId"
placeholder="请输入货位ID"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="货位名称" prop="cargoSpaceName">
<el-input
v-model="queryParams.cargoSpaceName"
placeholder="请输入货位名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-close"
size="mini"
:disabled="single"
@click="handleCancel"
v-hasPermi="['out:job:cancel']"
>手动作废</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-check"
size="mini"
:disabled="single"
@click="handleComplete"
v-hasPermi="['out:job:complete']"
>手动完成</el-button>
</el-col>
<el-col :span="1.5">
<el-tooltip content="手动下发 WCS" placement="top">
<span>
<el-button
type="primary"
plain
icon="el-icon-s-promotion"
size="mini"
:disabled="single || !canPushWcs(selectedRows[0])"
@click="handlePushWcs"
v-hasPermi="['out:job:pushWcs']"
>手动下发 WCS</el-button>
</span>
</el-tooltip>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="jobList" @selection-change="handleSelectionChange" @row-click="handleRowClick" highlight-current-row>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="任务ID" align="center" prop="jobId" />
<el-table-column label="任务状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :options="dict.type.mydo_job_status" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="任务号" align="center" prop="taskNo" />
<el-table-column label="任务类型" align="center" prop="inOrOut">
<template slot-scope="scope">
<dict-tag :options="dict.type.out_job_type" :value="scope.row.inOrOut"/>
</template>
</el-table-column>
<el-table-column label="执行阶段" align="center" prop="executionPhase">
<template slot-scope="scope">
<dict-tag :options="dict.type.mydo_job_execution_phase" :value="scope.row.executionPhase"/>
</template>
</el-table-column>
<el-table-column label="托盘码" align="center" prop="trayCode" />
<el-table-column label="仓库名称" align="center" prop="warehouseName" />
<el-table-column label="巷道ID" align="center" prop="roadwayId" />
<el-table-column label="货位ID" align="center" prop="cargoSpaceId" />
<el-table-column label="货位名称" align="center" prop="cargoSpaceName" />
<el-table-column label="出口" align="center" prop="exitId" />
<!-- <el-table-column label="高度" align="center" prop="height" />-->
<!-- <el-table-column label="重量" align="center" prop="weight" />-->
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="WCS状态" align="center" prop="dispatchStatus" width="112">
<template slot-scope="scope">
<el-tag :type="dispatchStatusType(scope.row.dispatchStatus)" size="mini" effect="plain">
{{ dispatchStatusLabel(scope.row.dispatchStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="自动重试" align="center" prop="autoRetryCount" width="88">
<template slot-scope="scope">{{ scope.row.autoRetryCount || 0 }}</template>
</el-table-column>
<el-table-column label="最后下发错误" align="left" prop="lastDispatchError" min-width="160">
<template slot-scope="scope">
<el-tooltip
v-if="scope.row.lastDispatchError"
:content="scope.row.lastDispatchError"
placement="top"
effect="dark"
>
<span class="dispatch-error">{{ scope.row.lastDispatchError }}</span>
</el-tooltip>
<span v-else class="dispatch-empty">-</span>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="finishTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.finishTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 子作业详情 -->
<div class="detail-panel" style="margin-top: 20px;">
<div class="detail-header" style="margin-bottom: 10px; display: flex; align-items: center; gap: 8px;">
<span style="font-size: 14px; font-weight: 600; color: #303133;">子作业详情</span>
<el-tag v-if="currentJob" type="primary" size="small" style="margin-left: 4px;">
主任务:{{ currentJob.taskNo || currentJob.jobId }}
</el-tag>
<span v-else style="font-size: 12px; color: #909399; margin-left: 4px;">(请点击上方主作业行查看对应子作业数据)</span>
</div>
<el-table
v-loading="detailLoading"
:data="jobDetailList"
border
stripe
size="small"
style="width: 100%;"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column label="子任务ID" align="center" prop="jobDetailId" width="180" />
<el-table-column label="物料编码" align="center" prop="productId" width="120" />
<el-table-column label="物料名称" align="center" prop="productName" min-width="150" show-overflow-tooltip />
<el-table-column label="批次" align="center" prop="lotNo" width="120" />
<el-table-column label="子单编号" align="center" prop="inOrOutDId" width="160" />
<!-- <el-table-column label="入库数量" align="center" prop="inQuantity" width="100" />-->
<el-table-column label="库存数量" align="center" prop="stockQuantity" width="100" />
<el-table-column label="出库数量" align="center" prop="outQuantity" width="100" />
<!-- <el-table-column label="客户名称" align="center" prop="customerName" width="120" />-->
<el-table-column label="供应商名称" align="center" prop="supplierName" width="120" />
<!-- <el-table-column label="入库时间" align="center" prop="inDate" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.inDate, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
</template>
</el-table-column>-->
<template slot="empty">
<div style="padding: 30px 0; color: #909399; font-size: 13px;">
<i class="el-icon-document" style="font-size: 28px; display: block; margin-bottom: 8px;"></i>
{{ currentJob ? '暂无子作业数据' : '请先选择上方主作业行' }}
</div>
</template>
</el-table>
<pagination
v-show="detailTotal > 0"
:total="detailTotal"
:page.sync="detailQueryParams.pageNum"
:limit.sync="detailQueryParams.pageSize"
@pagination="getJobDetailList"
style="margin-top: 0;"
/>
</div>
</div>
</template>
<script>
import { listJob, cancelJob, completeJob, pushJobToWcs } from "@/api/out/job"
import { listJobdetail } from "@/api/out/jobdetail"
export default {
name: "Job",
dicts: ['mydo_job_execution_phase', 'mydo_job_status', 'out_job_type'],
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 当前选中行
selectedRows: [],
// 非单个禁用
single: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 作业信息表格数据
jobList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
status: null,
taskNo: null,
inOrOut: null,
executionPhase: null,
trayCode: null,
warehouseName: null,
roadwayId: null,
cargoSpaceId: null,
cargoSpaceName: null
},
// ---- 子作业相关 ----
// 当前选中主作业行
currentJob: null,
// 子作业加载状态
detailLoading: false,
// 子作业总条数
detailTotal: 0,
// 子作业列表数据
jobDetailList: [],
// 子作业查询参数
detailQueryParams: {
pageNum: 1,
pageSize: 10,
jobId: null,
status: null
}
}
},
created() {
this.getList()
},
methods: {
/** 查询作业信息列表 */
getList() {
this.loading = true
listJob(this.queryParams).then(response => {
this.jobList = response.rows
this.total = response.total
this.loading = false
}).catch(() => {
this.loading = false
})
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
this.resetDetailQuery()
},
resetDetailQuery() {
this.jobDetailList = []
this.detailQueryParams.jobId = null
this.detailQueryParams.status = null
this.detailTotal = 0
this.currentJob = null
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.jobId)
this.selectedRows = selection
this.single = selection.length !== 1
},
/** 手动作废按钮操作 */
handleCancel() {
const jobId = this.ids[0]
this.$modal.confirm('是否确认作废选中的作业任务?').then(() => {
return cancelJob(jobId)
}).then(() => {
this.$modal.msgSuccess("作废成功")
this.getList()
}).catch(() => {})
},
/** 手动完成按钮操作 */
handleComplete() {
const jobId = this.ids[0]
this.$modal.confirm('是否确认手动完成选中的作业任务?').then(() => {
return completeJob(jobId)
}).then(() => {
this.$modal.msgSuccess("操作成功")
this.getList()
}).catch(() => {})
},
/** 推送 WCS 按钮操作 */
handlePushWcs() {
const row = this.selectedRows[0]
if (!this.canPushWcs(row)) {
this.$modal.msgWarning('当前作业状态不允许手动下发 WCS')
return
}
const autoRetries = Number(row.autoRetryCount || 0)
const manualRetries = Number(row.manualRetryCount || 0)
const lastError = row.lastDispatchError || '无'
const message = `确认手动下发作业 ${row.jobId} 到 WCS?自动重试 ${autoRetries} 次,人工下发 ${manualRetries} 次;最后错误:${lastError}`
this.$modal.confirm(message).then(() => {
return pushJobToWcs(row.jobId)
}).then(response => {
const resultMessage = response.msg || 'WCS手动下发请求已处理'
if (resultMessage.includes('下发成功')) {
this.$modal.msgSuccess(resultMessage)
} else {
this.$modal.msgWarning(resultMessage)
}
this.getList()
}).catch(() => {})
},
canPushWcs(row) {
if (!row || String(row.status) !== '1') {
return false
}
return ['PENDING', 'RETRY_WAIT', 'EXHAUSTED', 'BLOCKED']
.includes(this.normalizeDispatchStatus(row.dispatchStatus))
},
normalizeDispatchStatus(status) {
return status || 'PENDING'
},
dispatchStatusLabel(status) {
const labels = {
PENDING: '待下发',
SENDING: '下发中',
BLOCKED: '通道阻挡',
RETRY_WAIT: '等待重试',
SUCCESS: '已下发',
EXHAUSTED: '重试耗尽'
}
return labels[this.normalizeDispatchStatus(status)] || status || '待下发'
},
dispatchStatusType(status) {
const types = {
PENDING: 'info',
SENDING: 'warning',
BLOCKED: 'warning',
RETRY_WAIT: 'warning',
SUCCESS: 'success',
EXHAUSTED: 'danger'
}
return types[this.normalizeDispatchStatus(status)] || 'info'
},
/** 点击主作业行 —— 联动刷新子作业 */
handleRowClick(row) {
this.currentJob = row
this.detailQueryParams.jobId = row.jobId
this.detailQueryParams.status = row.status
this.detailQueryParams.pageNum = 1
this.getJobDetailList()
},
/** 查询子作业列表 */
getJobDetailList() {
if (!this.detailQueryParams.jobId) return
this.detailLoading = true
listJobdetail(this.detailQueryParams).then(response => {
this.jobDetailList = response.rows || []
this.detailTotal = response.total || 0
this.detailLoading = false
}).catch(() => {
this.detailLoading = false
})
}
}
}
</script>
<style scoped>
.dispatch-error {
display: inline-block;
max-width: 100%;
overflow: hidden;
color: #f56c6c;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
.dispatch-empty {
color: #c0c4cc;
}
</style>