robot-admin-web/src/views/generate/TaskGenerateView.vue

1088 lines
44 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="page-container">
<el-card shadow="never" :class="['gen-page-card', { 'm-gen-card': isMobile }]">
<template #header>
<span class="card-title">任务生成</span>
</template>
<el-row :gutter="isMobile ? 0 : 20" class="gen-layout">
<el-col :xs="24" :sm="24" :md="8" :lg="7" class="gen-nav-col">
<nav class="gen-type-nav" aria-label="任务类型">
<button
v-for="opt in taskTypeNavItems"
:key="opt.key"
type="button"
:class="['gen-type-nav__item', { 'is-active': panelKey === opt.key }]"
@click="selectPanel(opt.key)"
>
{{ opt.label }}
</button>
</nav>
</el-col>
<el-col :xs="24" :sm="24" :md="16" :lg="17" class="gen-panel-col">
<div class="gen-panel-surface">
<header class="gen-panel-head">
<h2 class="gen-panel-head__title">{{ currentTaskLabel }}</h2>
</header>
<div class="gen-panel-body">
<!-- 入库 -->
<div v-show="isPanel('inbound')" class="gen-form-wrap">
<el-form :model="inboundForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan">
<el-form-item label="仓库编码" required>
<el-input v-model="inboundForm.warehouse" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="容器编码" required>
<el-input v-model="inboundForm.container" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="目标类型">
<el-select v-model="inboundForm.targetType" clearable placeholder="请选择" style="width: 100%">
<el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="目标编码">
<el-input v-model="inboundForm.targetCode" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="站点编号">
<el-input v-model="inboundForm.stationCode" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="业务追踪号">
<el-input v-model="inboundForm.businessNo" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="SKU">
<el-input v-model="inboundForm.sku" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="优先级">
<el-input-number v-model="inboundForm.priority" :min="0" :controls="false" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="货架方向">
<el-input v-model="inboundForm.shelfDirection" placeholder="AB面" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleInbound" :style="isMobile ? 'width:100%' : ''">提交入库</el-button>
</el-form-item>
</el-form>
</div>
<!-- 出库(等待) -->
<div v-show="isPanel('outboundWait')" class="gen-form-wrap">
<el-form :model="outboundWaitForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="outboundWaitForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="outboundWaitForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="outboundWaitForm.targetCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标点位"><el-input v-model="outboundWaitForm.targetPoint" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="outboundWaitForm.businessNo" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="优先级"><el-input-number v-model="outboundWaitForm.priority" :min="0" :controls="false" style="width: 100%" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="货架方向"><el-input v-model="outboundWaitForm.shelfDirection" placeholder="AB面" /></el-form-item></el-col>
<el-col :span="24">
<el-form-item label="容器编码" required>
<el-input v-model="outboundWaitForm.containerBatch" type="textarea" :rows="6" placeholder="支持批量输入,每行一个或使用逗号、空格分隔" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleOutboundBatch('wait')" :style="isMobile ? 'width:100%' : ''">提交出库(等待)</el-button>
</el-form-item>
</el-form>
</div>
<!-- 拣货出库(新模版) -->
<div v-show="isPanel('outboundWaitNew')" class="gen-form-wrap">
<el-form :model="outboundWaitNewForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="outboundWaitNewForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="outboundWaitNewForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="outboundWaitNewForm.targetCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标点位"><el-input v-model="outboundWaitNewForm.targetPoint" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="outboundWaitNewForm.businessNo" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="优先级"><el-input-number v-model="outboundWaitNewForm.priority" :min="0" :controls="false" style="width: 100%" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="货架方向"><el-input v-model="outboundWaitNewForm.shelfDirection" placeholder="AB面" /></el-form-item></el-col>
<el-col :span="24">
<el-form-item label="容器编码" required>
<el-input v-model="outboundWaitNewForm.containerBatch" type="textarea" :rows="6" placeholder="支持批量输入,每行一个或使用逗号、空格分隔" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleOutboundBatch('waitNew')" :style="isMobile ? 'width:100%' : ''">提交拣货出库(新模版)</el-button>
</el-form-item>
</el-form>
</div>
<!-- 出库(不等待) -->
<div v-show="isPanel('outboundNoWait')" class="gen-form-wrap">
<el-form :model="outboundNoWaitForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="outboundNoWaitForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="outboundNoWaitForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="outboundNoWaitForm.targetCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标点位"><el-input v-model="outboundNoWaitForm.targetPoint" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="outboundNoWaitForm.businessNo" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="优先级"><el-input-number v-model="outboundNoWaitForm.priority" :min="0" :controls="false" style="width: 100%" /></el-form-item></el-col>
<el-col :span="24">
<el-form-item label="容器编码" required>
<el-input v-model="outboundNoWaitForm.containerBatch" type="textarea" :rows="6" placeholder="支持批量输入,每行一个或使用逗号、空格分隔" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleOutboundBatch('noWait')" :style="isMobile ? 'width:100%' : ''">提交出库(不等待)</el-button>
</el-form-item>
</el-form>
</div>
<!-- 直出模式(新模版) -->
<div v-show="isPanel('outboundNoWaitNew')" class="gen-form-wrap">
<el-form :model="outboundNoWaitNewForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="outboundNoWaitNewForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="outboundNoWaitNewForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="outboundNoWaitNewForm.targetCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标点位"><el-input v-model="outboundNoWaitNewForm.targetPoint" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="outboundNoWaitNewForm.businessNo" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="优先级"><el-input-number v-model="outboundNoWaitNewForm.priority" :min="0" :controls="false" style="width: 100%" /></el-form-item></el-col>
<el-col :span="24">
<el-form-item label="容器编码" required>
<el-input v-model="outboundNoWaitNewForm.containerBatch" type="textarea" :rows="6" placeholder="支持批量输入,每行一个或使用逗号、空格分隔" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleOutboundBatch('noWaitNew')" :style="isMobile ? 'width:100%' : ''">提交直出模式(新模版)</el-button>
</el-form-item>
</el-form>
</div>
<!-- 移库 -->
<div v-show="isPanel('move')" class="gen-form-wrap">
<el-form :model="moveForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="moveForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="moveForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="moveForm.targetCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="指定楼层"><el-input-number v-model="moveForm.targetLevel" :min="1" :max="6" :controls="false" clearable placeholder="选填" style="width: 100%" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="moveForm.businessNo" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="优先级"><el-input-number v-model="moveForm.priority" :min="0" :controls="false" style="width: 100%" /></el-form-item></el-col>
<el-col :span="24">
<el-form-item label="容器编码" required>
<el-input v-model="moveForm.containerBatch" type="textarea" :rows="6" placeholder="支持批量输入,每行一个或使用逗号、空格分隔" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleMoveBatch" :style="isMobile ? 'width:100%' : ''">提交移库</el-button>
</el-form-item>
</el-form>
</div>
<!-- 批量移库(新模版) -->
<div v-show="isPanel('moveNew')" class="gen-form-wrap">
<el-form :model="moveNewForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="moveNewForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="moveNewForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="moveNewForm.targetCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="指定楼层"><el-input-number v-model="moveNewForm.targetLevel" :min="1" :max="6" :controls="false" clearable placeholder="选填" style="width: 100%" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="moveNewForm.businessNo" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="优先级"><el-input-number v-model="moveNewForm.priority" :min="0" :controls="false" style="width: 100%" /></el-form-item></el-col>
<el-col :span="24">
<el-form-item label="容器编码" required>
<el-input v-model="moveNewForm.containerBatch" type="textarea" :rows="6" placeholder="支持批量输入,每行一个或使用逗号、空格分隔" />
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleMoveBatchNew" :style="isMobile ? 'width:100%' : ''">提交批量移库(新模版)</el-button>
</el-form-item>
</el-form>
</div>
<!-- 释放信号 -->
<div v-show="isPanel('release')" class="gen-form-wrap">
<el-form :model="releaseForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="任务链编号" required><el-input v-model="releaseForm.taskChainCode" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="releaseForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="容器编码"><el-input v-model="releaseForm.container" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标类型"><el-select v-model="releaseForm.targetType" clearable placeholder="请选择" style="width: 100%"><el-option v-for="t in targetTypes" :key="t.value" :label="t.label" :value="t.value" /></el-select></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="目标编码"><el-input v-model="releaseForm.targetCode" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleRelease" :style="isMobile ? 'width:100%' : ''">发送释放信号</el-button>
</el-form-item>
</el-form>
</div>
<!-- 绑定站点 -->
<div v-show="isPanel('bindStation')" class="gen-form-wrap">
<el-form :model="bindForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="bindForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="容器编码" required><el-input v-model="bindForm.container" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="站点编码" required><el-input v-model="bindForm.station" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleBindStation" :style="isMobile ? 'width:100%' : ''">绑定站点</el-button>
</el-form-item>
</el-form>
</div>
<!-- 解绑站点 -->
<div v-show="isPanel('releaseStation')" class="gen-form-wrap">
<el-form :model="releaseStationForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="releaseStationForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="容器编码" required><el-input v-model="releaseStationForm.container" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="站点编码" required><el-input v-model="releaseStationForm.station" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button type="warning" :loading="loading" @click="handleReleaseStation" :style="isMobile ? 'width:100%' : ''">解绑站点</el-button>
</el-form-item>
</el-form>
</div>
<!-- 取消任务(按仓库+容器,后台任务生成接口) -->
<div v-show="isPanel('cancel')" class="gen-form-wrap">
<el-form :model="cancelForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="cancelForm.warehouse" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="容器编码" required><el-input v-model="cancelForm.container" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="取消原因"><el-input v-model="cancelForm.reason" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="业务追踪号"><el-input v-model="cancelForm.businessNo" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button type="danger" :loading="loading" @click="handleCancel" :style="isMobile ? 'width:100%' : ''">取消任务</el-button>
</el-form-item>
</el-form>
</div>
<!-- 业务取消(按业务跟踪号) -->
<div v-show="isPanel('businessCancel')" class="gen-form-wrap">
<el-form :model="businessCancelForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="业务跟踪号" required><el-input v-model="businessCancelForm.businessNo" placeholder="与任务链业务追踪号一致" /></el-form-item></el-col>
<el-col :span="colSpan"><el-form-item label="取消原因"><el-input v-model="businessCancelForm.reason" type="textarea" :rows="2" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button type="danger" :loading="loading" @click="handleBusinessCancel" :style="isMobile ? 'width:100%' : ''">业务取消任务</el-button>
</el-form-item>
</el-form>
</div>
<!-- 入库异常处理 -->
<div v-show="isPanel('inboundException')" class="gen-form-wrap">
<el-form :model="inboundExceptionForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="托盘码" required><el-input v-model="inboundExceptionForm.palletCode" /></el-form-item></el-col>
<el-col :span="colSpan">
<el-form-item label="出库站点" required>
<el-select v-model="inboundExceptionForm.outboundStation" placeholder="请选择" style="width: 100%">
<el-option v-for="s in outboundStationOptions" :key="s" :label="s" :value="s" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="colSpan">
<el-form-item label="提升机" required>
<el-select v-model="inboundExceptionForm.elevatorId" placeholder="请选择" style="width: 100%">
<el-option v-for="e in elevatorOptions" :key="e.id" :label="e.label" :value="e.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="colSpan"><el-form-item label="仓库编码" required><el-input v-model="inboundExceptionForm.warehouse" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button type="primary" :loading="loading" @click="handleInboundException" :style="isMobile ? 'width:100%' : ''">出库</el-button>
<el-button type="warning" :loading="loading" @click="handleInboundExceptionUnbind" :style="isMobile ? 'width:100%' : ''">解绑</el-button>
</el-form-item>
</el-form>
</div>
<!-- 任务加急 -->
<div v-show="isPanel('expedite')" class="gen-form-wrap">
<el-form :model="expediteForm" :label-width="formLabelWidth" :label-position="labelPos" class="gen-form">
<el-row :gutter="gutter">
<el-col :span="colSpan"><el-form-item label="业务跟踪号" required><el-input v-model="expediteForm.businessNo" placeholder="与任务链业务追踪号一致" /></el-form-item></el-col>
</el-row>
<el-form-item>
<el-button :loading="loading" @click="handleExpedite(60)" :style="isMobile ? 'width:100%' : ''">正常</el-button>
<el-button type="warning" :loading="loading" @click="handleExpedite(500)" :style="isMobile ? 'width:100%' : ''">加急</el-button>
<el-button type="danger" :loading="loading" @click="handleExpedite(1000)" :style="isMobile ? 'width:100%' : ''">非常紧急</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</el-col>
</el-row>
</el-card>
<!-- 结果展示 -->
<el-card v-if="resultVisible" shadow="hover" class="result-card">
<template #header>
<div class="result-header">
<span>执行结果</span>
<el-button text type="info" size="small" @click="resultVisible = false">关闭</el-button>
</div>
</template>
<el-alert :title="resultTitle" :type="resultType" :closable="false" show-icon>
<pre v-if="resultDetail" class="result-detail">{{ resultDetail }}</pre>
</el-alert>
</el-card>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'TaskGenerateView' })
import {
generateBindStation,
generateCancel,
generateExpedite,
generateInbound,
generateInboundException,
generateMoveBatch,
generateMoveBatchNew,
generateOutboundNoWait,
generateOutboundNoWaitNew,
generateOutboundWait,
generateOutboundWaitNew,
generateRelease,
generateReleaseStation,
type BindStationReq,
type CancelReq,
type InboundReq,
type MoveReq,
type OutboundReq,
type ReleaseReq,
type ReleaseStationReq,
} from '@/api/generate'
import { cancelTaskByBusinessNo } from '@/api/task'
import { useDevice } from '@/composables/useDevice'
import { ElMessage, ElMessageBox } from 'element-plus'
import { computed, reactive, ref } from 'vue'
const { isMobile } = useDevice()
const colSpan = computed(() => (isMobile.value ? 24 : 12))
const gutter = computed(() => (isMobile.value ? 0 : 20))
const formLabelWidth = computed(() => (isMobile.value ? '' : '120px'))
const labelPos = computed<'top' | 'right'>(() => (isMobile.value ? 'top' : 'right'))
/** 当前任务类型(扁平列表,无分组) */
const taskTypeNavItems: { key: string; label: string }[] = [
{ key: 'inbound', label: '入库' },
{ key: 'outboundWait', label: '出库(等待)' },
{ key: 'outboundWaitNew', label: '拣货出库(新模版)' },
{ key: 'outboundNoWait', label: '出库(不等待)' },
{ key: 'outboundNoWaitNew', label: '直出模式(新模版)' },
{ key: 'move', label: '移库' },
{ key: 'moveNew', label: '批量移库(新模版)' },
{ key: 'release', label: '释放信号' },
{ key: 'bindStation', label: '绑定站点' },
{ key: 'releaseStation', label: '解绑站点' },
{ key: 'cancel', label: '取消任务(仓+容器)' },
{ key: 'businessCancel', label: '业务取消' },
{ key: 'inboundException', label: '入库异常处理' },
{ key: 'expedite', label: '任务加急' },
]
const panelKey = ref('inbound')
const currentTaskLabel = computed(
() => taskTypeNavItems.find((i) => i.key === panelKey.value)?.label ?? '任务生成',
)
function selectPanel(key: string) {
panelKey.value = key
}
function isPanel(name: string) {
return panelKey.value === name
}
const loading = ref(false)
const resultVisible = ref(false)
const resultTitle = ref('')
const resultType = ref<'success' | 'error'>('success')
const resultDetail = ref('')
const targetTypes = [
{ value: 'AREA', label: '库区' },
{ value: 'LOCATION', label: '库位' },
{ value: 'STATION', label: '站点' },
{ value: 'CONTAINER', label: '容器' },
]
const inboundForm = reactive<InboundReq>({ warehouse: '', container: '' })
const outboundWaitForm = reactive({
warehouse: '', containerBatch: '',
targetType: undefined as string | undefined, targetCode: '', targetPoint: '', businessNo: '', priority: undefined as number | undefined, shelfDirection: '',
})
const outboundNoWaitForm = reactive({
warehouse: '', containerBatch: '',
targetType: undefined as string | undefined, targetCode: '', targetPoint: '', businessNo: '', priority: undefined as number | undefined,
})
const outboundWaitNewForm = reactive({
warehouse: 'WH01', containerBatch: '',
targetType: 'STATION', targetCode: 'B4', targetPoint: '0217280SS0198585', businessNo: 'JH00001', priority: 50, shelfDirection: '',
})
const outboundNoWaitNewForm = reactive({
warehouse: '', containerBatch: '',
targetType: undefined as string | undefined, targetCode: '', targetPoint: '', businessNo: '', priority: undefined as number | undefined,
})
const moveForm = reactive({
warehouse: '', containerBatch: '',
targetType: undefined as string | undefined, targetCode: '', targetLevel: undefined as number | undefined, businessNo: '', priority: undefined as number | undefined,
})
const moveNewForm = reactive({
warehouse: '', containerBatch: '',
targetType: undefined as string | undefined, targetCode: '', targetLevel: undefined as number | undefined, businessNo: '', priority: undefined as number | undefined,
})
const releaseForm = reactive<ReleaseReq>({ warehouse: '', container: '', taskChainCode: '' })
const bindForm = reactive<BindStationReq>({ warehouse: '', container: '', station: '' })
const releaseStationForm = reactive<ReleaseStationReq>({ warehouse: '', container: '', station: '' })
const cancelForm = reactive<CancelReq>({ warehouse: '', container: '' })
const businessCancelForm = reactive({ businessNo: '', reason: '' })
/** 入库异常处理:出库站点与提升机点位映射(与后台 ASRSConsts 保持一致) */
const outboundStationOptions = ['A3', 'A4']
const elevatorOptions = [
{ id: 1, label: '提升机1号', point: '0211520SS0225040' },
{ id: 2, label: '提升机2号', point: '0228800SS0225040' },
]
const inboundExceptionForm = reactive({
palletCode: '',
outboundStation: 'A3',
elevatorId: 1 as number,
warehouse: 'WH01',
})
const expediteForm = reactive({ businessNo: '' })
function showResult(title: string, type: 'success' | 'error', detail?: unknown) {
resultTitle.value = title
resultType.value = type
resultDetail.value = detail != null ? JSON.stringify(detail, null, 2) : ''
resultVisible.value = true
}
async function handleInbound() {
if (!inboundForm.warehouse || !inboundForm.container) {
return ElMessage.warning('请填写仓库编码和容器编码')
}
loading.value = true
try {
const res = await generateInbound({ ...inboundForm })
showResult(`入库任务链已创建: ${res.data}`, 'success')
ElMessage.success('入库任务链已创建')
} catch (e) {
showResult('入库失败', 'error', e)
} finally {
loading.value = false
}
}
function parseContainerBatch(text: string): string[] {
if (!text?.trim()) return []
return text
.split(/[\n,\s]+/)
.map(s => s.trim())
.filter(Boolean)
}
async function handleOutboundBatch(mode: 'wait' | 'noWait' | 'waitNew' | 'noWaitNew') {
const formMap = { wait: outboundWaitForm, noWait: outboundNoWaitForm, waitNew: outboundWaitNewForm, noWaitNew: outboundNoWaitNewForm }
const form = formMap[mode]
if (!form.warehouse) {
return ElMessage.warning('请填写仓库编码')
}
const containers = parseContainerBatch(form.containerBatch)
if (containers.length === 0) {
return ElMessage.warning('请填写至少一个容器编码')
}
const base = {
warehouse: form.warehouse,
targetType: form.targetType,
targetCode: form.targetCode,
targetPoint: form.targetPoint,
businessNo: form.businessNo,
priority: form.priority,
}
const shelfDirection = mode === 'wait' ? outboundWaitForm.shelfDirection : mode === 'waitNew' ? outboundWaitNewForm.shelfDirection : undefined
const list: OutboundReq[] = containers.map(container =>
shelfDirection !== undefined ? { ...base, container, shelfDirection } : { ...base, container },
)
loading.value = true
try {
const fnMap = { wait: generateOutboundWait, noWait: generateOutboundNoWait, waitNew: generateOutboundWaitNew, noWaitNew: generateOutboundNoWaitNew }
const fn = fnMap[mode]
const res = await fn(list)
showResult('出库任务链已创建', 'success', res.data)
ElMessage.success(`已创建 ${containers.length} 条出库任务链`)
} catch (e) {
showResult('出库失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleMoveBatch() {
if (!moveForm.warehouse) {
return ElMessage.warning('请填写仓库编码')
}
const containers = parseContainerBatch(moveForm.containerBatch)
if (containers.length === 0) {
return ElMessage.warning('请填写至少一个容器编码')
}
const list: MoveReq[] = containers.map(container => ({
warehouse: moveForm.warehouse,
container,
targetType: moveForm.targetType,
targetCode: moveForm.targetCode,
targetLevel: moveForm.targetLevel,
businessNo: moveForm.businessNo,
priority: moveForm.priority,
}))
loading.value = true
try {
const res = await generateMoveBatch(list)
showResult('移库任务链已创建', 'success', res.data)
ElMessage.success(`已创建 ${containers.length} 条移库任务链`)
} catch (e) {
showResult('移库失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleMoveBatchNew() {
if (!moveNewForm.warehouse) {
return ElMessage.warning('请填写仓库编码')
}
const containers = parseContainerBatch(moveNewForm.containerBatch)
if (containers.length === 0) {
return ElMessage.warning('请填写至少一个容器编码')
}
const list: MoveReq[] = containers.map(container => ({
warehouse: moveNewForm.warehouse,
container,
targetType: moveNewForm.targetType,
targetCode: moveNewForm.targetCode,
targetLevel: moveNewForm.targetLevel,
businessNo: moveNewForm.businessNo,
priority: moveNewForm.priority,
}))
loading.value = true
try {
const res = await generateMoveBatchNew(list)
showResult('批量移库(新模版)任务链已创建', 'success', res.data)
ElMessage.success(`已创建 ${containers.length} 条批量移库(新模版)任务链`)
} catch (e) {
showResult('批量移库(新模版)失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleRelease() {
if (!releaseForm.taskChainCode || !releaseForm.warehouse) {
return ElMessage.warning('请填写任务链编号和仓库编码')
}
loading.value = true
try {
await generateRelease({ ...releaseForm })
showResult('释放信号已发送', 'success')
ElMessage.success('释放信号已发送')
} catch (e) {
showResult('释放失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleBindStation() {
if (!bindForm.warehouse || !bindForm.container || !bindForm.station) {
return ElMessage.warning('请填写所有必填项')
}
loading.value = true
try {
await generateBindStation({ ...bindForm })
showResult('站点绑定成功', 'success')
ElMessage.success('站点绑定成功')
} catch (e) {
showResult('绑定失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleReleaseStation() {
if (!releaseStationForm.warehouse || !releaseStationForm.container || !releaseStationForm.station) {
return ElMessage.warning('请填写所有必填项')
}
loading.value = true
try {
await generateReleaseStation({ ...releaseStationForm })
showResult('站点解绑成功', 'success')
ElMessage.success('站点解绑成功')
} catch (e) {
showResult('解绑失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleCancel() {
if (!cancelForm.warehouse || !cancelForm.container) {
return ElMessage.warning('请填写仓库编码和容器编码')
}
loading.value = true
try {
const res = await generateCancel({ ...cancelForm })
showResult('任务取消成功', 'success', res.data)
ElMessage.success('任务取消成功')
} catch (e) {
showResult('取消失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleInboundException() {
if (!inboundExceptionForm.palletCode) {
return ElMessage.warning('请输入托盘码')
}
if (!inboundExceptionForm.outboundStation) {
return ElMessage.warning('请选择出库站点')
}
const elevator = elevatorOptions.find(e => e.id === inboundExceptionForm.elevatorId)
if (!elevator) {
return ElMessage.warning('请选择提升机')
}
loading.value = true
try {
await generateInboundException({
palletCode: inboundExceptionForm.palletCode,
outboundStation: inboundExceptionForm.outboundStation,
elevatorId: elevator.id,
elevatorPoint: elevator.point,
})
showResult('入库异常出库已提交', 'success')
ElMessage.success('入库异常出库已提交')
} catch (e) {
showResult('入库异常出库失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleInboundExceptionUnbind() {
if (!inboundExceptionForm.warehouse || !inboundExceptionForm.palletCode || !inboundExceptionForm.outboundStation) {
return ElMessage.warning('请填写仓库编码、托盘码和出库站点')
}
loading.value = true
try {
await generateReleaseStation({
warehouse: inboundExceptionForm.warehouse,
container: inboundExceptionForm.palletCode,
station: inboundExceptionForm.outboundStation,
})
showResult('解绑成功', 'success')
ElMessage.success('解绑成功')
} catch (e) {
showResult('解绑失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleExpedite(priority: number) {
const businessNo = expediteForm.businessNo?.trim()
if (!businessNo) {
return ElMessage.warning('请填写业务跟踪号')
}
const label = priority >= 1000 ? '非常紧急' : priority >= 500 ? '加急' : '正常'
try {
await ElMessageBox.confirm(
`确认将业务跟踪号「${businessNo}」的任务链设为${label}(优先级 ${priority}`,
'任务加急确认',
{ type: 'warning', confirmButtonText: '确认', cancelButtonText: '取消' },
)
} catch {
return
}
loading.value = true
try {
await generateExpedite({ businessNo, priority })
showResult(`任务已${label}(优先级 ${priority}`, 'success')
ElMessage.success(`任务已${label}`)
} catch (e) {
showResult('任务加急失败', 'error', e)
} finally {
loading.value = false
}
}
async function handleBusinessCancel() {
if (!businessCancelForm.businessNo?.trim()) {
return ElMessage.warning('请填写业务跟踪号')
}
loading.value = true
try {
const res = await cancelTaskByBusinessNo(
businessCancelForm.businessNo.trim(),
businessCancelForm.reason?.trim() || undefined,
)
showResult('业务取消已提交', 'success', res.data)
ElMessage.success('业务取消已提交')
} catch (e) {
showResult('业务取消失败', 'error', e)
} finally {
loading.value = false
}
}
</script>
<style scoped>
.page-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.gen-page-card {
border-radius: 12px;
border: 1px solid var(--el-border-color-lighter);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
}
.gen-page-card :deep(.el-card__header) {
border-bottom: 1px solid var(--el-border-color-lighter);
padding: 16px 20px;
}
.gen-page-card :deep(.el-card__body) {
padding: 20px 20px 24px;
}
.card-title {
font-weight: 600;
font-size: 17px;
letter-spacing: 0.02em;
color: var(--el-text-color-primary);
}
.result-card {
margin-top: 0;
}
.result-header {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 600;
}
.result-detail {
margin: 8px 0 0;
white-space: pre-wrap;
word-break: break-all;
font-size: 13px;
color: #606266;
}
.outbound-item {
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 16px 16px 0;
margin-bottom: 12px;
}
.outbound-item-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.outbound-item-title {
font-weight: 600;
font-size: 14px;
color: #303133;
}
.outbound-actions {
display: flex;
gap: 12px;
padding: 8px 0 16px;
}
/* ===== 移动端 ===== */
.m-gen-card :deep(.el-card__body) {
padding: 12px;
}
.m-gen-card :deep(.el-card__header) {
padding: 12px;
}
.gen-layout {
align-items: stretch;
}
.gen-nav-col {
margin-bottom: 0;
}
.gen-panel-col {
min-width: 0;
}
/* 扁平类型列表 */
.gen-type-nav {
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px;
overflow-y: auto;
background: linear-gradient(165deg, #f8f9fc 0%, #f2f4f8 100%);
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 12px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
/* 桌面:左右列同高,侧栏与表单区纵向铺满 */
@media (min-width: 768px) {
.gen-layout {
min-height: min(72vh, 580px);
}
.gen-nav-col {
display: flex;
flex-direction: column;
min-height: 0;
}
.gen-type-nav {
flex: 1;
min-height: 0;
}
.gen-panel-col {
display: flex;
flex-direction: column;
min-height: 0;
}
.gen-panel-surface {
flex: 1;
min-height: 0;
}
}
.gen-type-nav__item {
display: block;
width: 100%;
margin: 0;
padding: 11px 14px;
border: none;
border-radius: 9px;
text-align: left;
font-size: 14px;
line-height: 1.4;
color: var(--el-text-color-regular);
background: transparent;
cursor: pointer;
transition:
background 0.15s ease,
color 0.15s ease,
box-shadow 0.15s ease;
}
.gen-type-nav__item:hover {
color: var(--el-text-color-primary);
background: rgba(255, 255, 255, 0.85);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.gen-type-nav__item.is-active {
font-weight: 600;
color: var(--el-color-primary);
background: #fff;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.08),
0 0 0 1px var(--el-color-primary-light-7);
}
.gen-type-nav__item:focus-visible {
outline: 2px solid var(--el-color-primary-light-5);
outline-offset: 2px;
}
/* 右侧表单区 */
.gen-panel-surface {
display: flex;
flex-direction: column;
border-radius: 12px;
border: 1px solid var(--el-border-color-lighter);
background: var(--el-bg-color);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.gen-panel-head {
flex-shrink: 0;
padding: 16px 22px 14px;
border-bottom: 1px solid var(--el-border-color-lighter);
background: linear-gradient(180deg, var(--el-fill-color-blank) 0%, var(--el-bg-color) 100%);
}
.gen-panel-head__title {
margin: 0;
font-size: 16px;
font-weight: 600;
letter-spacing: 0.02em;
color: var(--el-text-color-primary);
}
.gen-panel-body {
flex: 1;
padding: 16px 20px 22px;
overflow: auto;
background: var(--el-fill-color-lighter);
}
/* 单块表单:内层卡片 + 统一控件样式 */
.gen-form-wrap {
min-height: 80px;
padding: 22px 24px 24px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-extra-light);
border-radius: 10px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.gen-form-wrap :deep(.gen-form) {
padding: 0;
max-width: 960px;
}
.gen-form-wrap :deep(.el-form-item) {
margin-bottom: 18px;
}
.gen-form-wrap :deep(.el-form-item__label) {
font-weight: 500;
color: var(--el-text-color-regular);
line-height: 1.4;
}
.gen-form-wrap :deep(.el-input__wrapper) {
border-radius: 8px;
transition: box-shadow 0.15s ease;
}
.gen-form-wrap :deep(.el-select .el-input__wrapper) {
border-radius: 8px;
}
.gen-form-wrap :deep(.el-textarea__inner) {
border-radius: 8px;
padding: 10px 12px;
line-height: 1.5;
}
.gen-form-wrap :deep(.el-input-number .el-input__wrapper) {
border-radius: 8px;
}
/* 提交区:与字段区分隔 */
.gen-form-wrap :deep(.el-form-item:last-child) {
margin-bottom: 0;
margin-top: 4px;
padding-top: 20px;
border-top: 1px dashed var(--el-border-color-lighter);
}
.gen-form-wrap :deep(.el-form-item:last-child .el-form-item__content) {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.gen-form-wrap :deep(.el-form-item:last-child .el-button) {
min-width: 120px;
border-radius: 8px;
padding: 10px 20px;
}
@media (max-width: 767px) {
.gen-nav-col {
margin-bottom: 14px;
}
.gen-type-nav {
/* 条目较多时需留出可视高度,避免「移库」以下选项被裁切误以为未上线 */
max-height: min(58vh, 440px);
flex-direction: row;
flex-wrap: wrap;
gap: 8px;
padding: 10px;
}
.gen-type-nav__item {
width: auto;
flex: 1 1 calc(50% - 4px);
min-width: calc(50% - 4px);
text-align: center;
font-size: 13px;
padding: 10px 8px;
}
.gen-panel-surface {
min-height: 0;
}
.gen-panel-head {
padding: 14px 16px 12px;
}
.gen-panel-body {
padding: 12px 12px 16px;
}
.gen-form-wrap {
padding: 16px 14px 18px;
border-radius: 8px;
}
.gen-form-wrap :deep(.el-form-item) {
margin-bottom: 16px;
}
.gen-form-wrap :deep(.el-form-item:last-child .el-button) {
min-width: 0;
width: 100%;
}
}
</style>