2026-05-07 15:59:27 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="app-container">
|
|
|
|
|
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="80px">
|
|
|
|
|
|
<el-form-item label="单号" prop="orderCode">
|
|
|
|
|
|
<el-input v-model="queryParams.orderCode" placeholder="请输入单据编号" clearable @keyup.enter.native="handleQuery" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="业务类型" prop="bizType">
|
|
|
|
|
|
<el-input v-model="queryParams.bizType" placeholder="请输入业务类型" clearable />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="责任人" prop="responsibleName">
|
|
|
|
|
|
<el-input v-model="queryParams.responsibleName" placeholder="请输入责任人" clearable />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" icon="Search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
|
|
|
<el-button 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="primary" plain icon="Plus" size="mini" @click="handleAdd">新增</el-button>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="1.5">
|
|
|
|
|
|
<el-button type="danger" plain icon="RefreshLeft" size="mini" :disabled="single" @click="handleRevoke()">撤回</el-button>
|
|
|
|
|
|
</el-col>
|
2026-05-08 16:46:36 +08:00
|
|
|
|
<el-col :span="1.5">
|
|
|
|
|
|
<el-button type="warning" plain icon="Download" size="mini" @click="handleExport">导出</el-button>
|
|
|
|
|
|
</el-col>
|
2026-05-09 14:09:52 +08:00
|
|
|
|
<el-col :span="1.5">
|
2026-05-11 17:59:12 +08:00
|
|
|
|
<el-button type="info" plain size="mini" @click="openFlow">统计/导出</el-button>
|
2026-05-09 14:09:52 +08:00
|
|
|
|
</el-col>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
|
|
|
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
|
|
|
<el-table-column label="单据编号" align="center" prop="orderCode" min-width="160" />
|
2026-05-09 14:09:52 +08:00
|
|
|
|
<el-table-column label="创建时间" align="center" prop="createTime" min-width="160" />
|
2026-05-07 15:59:27 +08:00
|
|
|
|
<el-table-column label="物料" align="center" min-width="220">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tooltip v-if="scope.row.materialNames && String(scope.row.materialNames).length > 16" effect="dark" placement="top">
|
|
|
|
|
|
<template #content>
|
|
|
|
|
|
<div style="max-width: 420px; white-space: normal; word-break: break-all;">
|
|
|
|
|
|
{{ scope.row.materialNames }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<span>{{ String(scope.row.materialNames).slice(0, 16) + '…' }}</span>
|
|
|
|
|
|
</el-tooltip>
|
|
|
|
|
|
<span v-else>{{ scope.row.materialNames || '-' }}</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="业务类型" align="center" prop="bizType" min-width="120" />
|
|
|
|
|
|
<el-table-column label="责任人" align="center" prop="responsibleName" min-width="120" />
|
2026-05-08 16:46:36 +08:00
|
|
|
|
<el-table-column label="状态" align="center" width="90">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag v-if="scope.row.revokeFlag === '1'" type="danger">已撤回</el-tag>
|
|
|
|
|
|
<el-tag v-else type="info">正常</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="220">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-button size="mini" type="text" icon="Document" @click="showDetail(scope.row)">明细</el-button>
|
2026-05-08 16:46:36 +08:00
|
|
|
|
<el-button size="mini" type="text" icon="RefreshLeft" :disabled="scope.row.revokeFlag === '1'" @click="handleRevoke(scope.row)">撤回</el-button>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<pagination
|
|
|
|
|
|
v-show="total > 0"
|
|
|
|
|
|
:total="total"
|
|
|
|
|
|
v-model:page="queryParams.pageNum"
|
|
|
|
|
|
v-model:limit="queryParams.pageSize"
|
|
|
|
|
|
@pagination="getList"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<el-dialog :title="editTitle" v-model="editOpen" width="1100px" top="5vh" append-to-body>
|
|
|
|
|
|
<el-form ref="editFormRef" :model="editForm" :rules="editRules" label-width="90px">
|
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="类型">
|
|
|
|
|
|
<el-tag type="warning">出库</el-tag>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="业务类型" prop="bizType">
|
|
|
|
|
|
<el-input v-model="editForm.bizType" placeholder="请输入业务类型" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="8">
|
|
|
|
|
|
<el-form-item label="责任人" prop="responsibleName">
|
|
|
|
|
|
<el-input v-model="editForm.responsibleName" placeholder="请输入责任人" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="24">
|
|
|
|
|
|
<el-form-item label="备注" prop="remark">
|
|
|
|
|
|
<el-input v-model="editForm.remark" placeholder="请输入备注" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<div style="display:flex;gap:8px;align-items:center;margin-bottom:10px;">
|
|
|
|
|
|
<el-button type="primary" plain icon="Plus" size="mini" @click="addDetailRow">新增明细行</el-button>
|
|
|
|
|
|
<RawSelector v-model:materialIds="batchMaterialIds" :multiple="true" :allowAdd="false" @multi-change="handleBatchPicked">
|
|
|
|
|
|
<template #trigger>
|
|
|
|
|
|
<el-button type="primary" plain size="mini">批量选择原料</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</RawSelector>
|
|
|
|
|
|
<el-button type="danger" plain icon="Delete" size="mini" :disabled="detailSelection.length === 0" @click="removeDetailRows">删除选中</el-button>
|
|
|
|
|
|
<div style="margin-left:auto;">
|
|
|
|
|
|
<span>合计数量:{{ totalQtyText }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table :data="editDetails" border style="width: 100%" @selection-change="onDetailSelectionChange">
|
|
|
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
|
|
|
<el-table-column label="行号" width="70" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
{{ scope.row.lineNo }}
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="物料类型" width="100" align="center">
|
|
|
|
|
|
<template #default>
|
|
|
|
|
|
原料
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="原料" min-width="260" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<RawSelector v-model="scope.row.itemId" :allowAdd="false" @change="onMaterialPicked(scope.row, $event)" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="名称快照" min-width="180" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.itemName" placeholder="可选" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="数量" min-width="120" align="center">
|
|
|
|
|
|
<template #default="scope">
|
2026-05-08 16:46:36 +08:00
|
|
|
|
<el-input v-model="scope.row.quantity" placeholder="请输入数量" />
|
2026-05-07 15:59:27 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="单位" width="90" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.unit" placeholder="单位" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="批次号" min-width="120" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.batchNo" placeholder="批次号" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="备注" min-width="140" align="center">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-input v-model="scope.row.remark" placeholder="备注" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button :loading="buttonLoading" type="primary" @click="submitEdit">出库</el-button>
|
|
|
|
|
|
<el-button @click="editOpen = false">取消</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<el-dialog title="单据明细" v-model="detailOpen" width="1100px" append-to-body>
|
|
|
|
|
|
<template v-if="detailData && detailData.order">
|
|
|
|
|
|
<el-descriptions :title="'单号:' + (detailData.order.orderCode || '-')" :column="2" border>
|
|
|
|
|
|
<el-descriptions-item label="类型">出库</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="业务类型">{{ detailData.order.bizType }}</el-descriptions-item>
|
|
|
|
|
|
<el-descriptions-item label="责任人">{{ detailData.order.responsibleName }}</el-descriptions-item>
|
2026-05-08 16:46:36 +08:00
|
|
|
|
<el-descriptions-item label="撤回人">{{ detailData.order.revokeBy || '-' }}</el-descriptions-item>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
</el-descriptions>
|
2026-05-09 14:09:52 +08:00
|
|
|
|
<div style="margin: 12px 0;">
|
|
|
|
|
|
<el-table :data="operationRows(detailData.order)" border style="width: 100%">
|
|
|
|
|
|
<el-table-column label="操作" prop="action" width="120" align="center" />
|
|
|
|
|
|
<el-table-column label="操作人" prop="user" width="140" align="center" />
|
|
|
|
|
|
<el-table-column label="操作时间" prop="time" min-width="180" align="center" />
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</div>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
<div style="margin: 12px 0; text-align: right;">
|
2026-05-08 16:46:36 +08:00
|
|
|
|
<el-button type="danger" size="small" :disabled="detailData.order.revokeFlag === '1'" @click="handleRevoke(detailData.order)">撤回</el-button>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<el-table :data="detailData.details || []" border style="width: 100%">
|
|
|
|
|
|
<el-table-column label="行号" prop="lineNo" width="70" align="center" />
|
|
|
|
|
|
<el-table-column label="原料ID" prop="itemId" width="120" align="center" />
|
|
|
|
|
|
<el-table-column label="名称快照" prop="itemName" min-width="160" align="center" />
|
|
|
|
|
|
<el-table-column label="数量" prop="quantity" width="120" align="center" />
|
|
|
|
|
|
<el-table-column label="单位" prop="unit" width="90" align="center" />
|
|
|
|
|
|
<el-table-column label="批次号" prop="batchNo" min-width="120" align="center" />
|
|
|
|
|
|
<el-table-column label="备注" prop="remark" min-width="140" align="center" />
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<div style="height:200px;line-height:200px;text-align:center;">未获取到单据数据</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<el-button @click="detailOpen = false">关闭</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
2026-05-09 14:09:52 +08:00
|
|
|
|
|
|
|
|
|
|
<el-dialog title="物料出入库统计" v-model="flowOpen" width="980px" top="6vh" append-to-body>
|
|
|
|
|
|
<el-form :inline="true" size="small" label-width="90px">
|
|
|
|
|
|
<el-form-item label="物料">
|
|
|
|
|
|
<RawSelector v-model="flowForm.itemId" :allowAdd="false" @change="onFlowMaterialChange" />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item label="时间范围">
|
|
|
|
|
|
<el-date-picker
|
|
|
|
|
|
v-model="flowForm.timeRange"
|
|
|
|
|
|
type="datetimerange"
|
|
|
|
|
|
range-separator="至"
|
|
|
|
|
|
start-placeholder="开始时间"
|
|
|
|
|
|
end-placeholder="结束时间"
|
|
|
|
|
|
value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
|
format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
|
style="width: 360px"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
<el-form-item>
|
|
|
|
|
|
<el-button type="primary" :loading="flowLoading" @click="fetchFlow">查询</el-button>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-loading="flowLoading" style="margin-top: 10px;">
|
|
|
|
|
|
<div ref="flowPrintContent">
|
|
|
|
|
|
<div style="font-size: 16px; font-weight: 600; text-align: center; margin-bottom: 8px;">
|
|
|
|
|
|
物料出入库统计
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div style="margin-bottom: 10px;">
|
|
|
|
|
|
<span>物料:</span><span>{{ flowItemName || '-' }}</span>
|
|
|
|
|
|
<span style="margin-left: 18px;">时间:</span>
|
|
|
|
|
|
<span>{{ (flowForm.timeRange && flowForm.timeRange[0]) || '-' }}</span>
|
|
|
|
|
|
<span> 至 </span>
|
|
|
|
|
|
<span>{{ (flowForm.timeRange && flowForm.timeRange[1]) || '-' }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<el-row :gutter="10" style="margin-bottom: 10px;">
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
|
<div>确认入库</div>
|
|
|
|
|
|
<div style="font-size: 18px; font-weight: 600;">{{ flowResult ? flowResult.confirmInQty : '-' }}</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
|
<div>出库</div>
|
|
|
|
|
|
<div style="font-size: 18px; font-weight: 600;">{{ flowResult ? flowResult.outQty : '-' }}</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
|
<div>撤回入库</div>
|
|
|
|
|
|
<div style="font-size: 18px; font-weight: 600;">{{ flowResult ? flowResult.revokeInQty : '-' }}</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
|
<div>撤回出库</div>
|
|
|
|
|
|
<div style="font-size: 18px; font-weight: 600;">{{ flowResult ? flowResult.revokeOutQty : '-' }}</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
<el-row :gutter="10" style="margin-bottom: 10px;">
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-card shadow="never">
|
|
|
|
|
|
<div>净变动</div>
|
|
|
|
|
|
<div style="font-size: 18px; font-weight: 600;">{{ flowResult ? flowResult.netQty : '-' }}</div>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table :data="(flowResult && flowResult.rows) || []" border style="width: 100%;">
|
|
|
|
|
|
<el-table-column label="时间" prop="time" min-width="170" />
|
|
|
|
|
|
<el-table-column label="动作" prop="action" width="100" />
|
|
|
|
|
|
<el-table-column label="单号" prop="orderCode" min-width="160" />
|
|
|
|
|
|
<el-table-column label="数量变化" prop="qtyChange" width="120" />
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
2026-05-11 17:59:12 +08:00
|
|
|
|
<el-button :disabled="!flowResult" @click="exportFlow">导出Excel</el-button>
|
2026-05-09 14:09:52 +08:00
|
|
|
|
<el-button @click="flowOpen = false">关闭</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
2026-05-07 15:59:27 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import RawSelector from '@/components/RawSelector/index.vue'
|
|
|
|
|
|
import {
|
|
|
|
|
|
listStockIoOrder,
|
|
|
|
|
|
getStockIoOrderWithDetail,
|
|
|
|
|
|
addStockIoOrderWithDetail,
|
2026-05-09 14:09:52 +08:00
|
|
|
|
revokeStockIoOrder,
|
|
|
|
|
|
getMaterialFlow
|
2026-05-07 15:59:27 +08:00
|
|
|
|
} from '@/api/wms/stockIoOrder'
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'StockIoOrderOut',
|
|
|
|
|
|
components: { RawSelector },
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
loading: true,
|
|
|
|
|
|
buttonLoading: false,
|
|
|
|
|
|
ids: [],
|
|
|
|
|
|
rows: [],
|
|
|
|
|
|
single: true,
|
|
|
|
|
|
showSearch: true,
|
|
|
|
|
|
total: 0,
|
|
|
|
|
|
list: [],
|
|
|
|
|
|
queryParams: {
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 20,
|
|
|
|
|
|
orderCode: undefined,
|
|
|
|
|
|
ioType: 'O',
|
|
|
|
|
|
bizType: undefined,
|
|
|
|
|
|
responsibleName: undefined
|
|
|
|
|
|
},
|
|
|
|
|
|
editOpen: false,
|
|
|
|
|
|
editTitle: '',
|
|
|
|
|
|
editForm: {},
|
|
|
|
|
|
editDetails: [],
|
|
|
|
|
|
batchMaterialIds: [],
|
|
|
|
|
|
detailSelection: [],
|
|
|
|
|
|
editRules: {
|
|
|
|
|
|
bizType: [{ required: true, message: '业务类型不能为空', trigger: 'blur' }]
|
|
|
|
|
|
},
|
|
|
|
|
|
detailOpen: false,
|
2026-05-09 14:09:52 +08:00
|
|
|
|
detailData: null,
|
|
|
|
|
|
flowOpen: false,
|
|
|
|
|
|
flowLoading: false,
|
|
|
|
|
|
flowForm: {
|
|
|
|
|
|
itemId: undefined,
|
|
|
|
|
|
timeRange: []
|
|
|
|
|
|
},
|
|
|
|
|
|
flowItemName: '',
|
|
|
|
|
|
flowResult: null
|
2026-05-07 15:59:27 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
totalQtyText() {
|
|
|
|
|
|
const total = (this.editDetails || []).reduce((sum, r) => sum + this.toNumber(r.quantity), 0)
|
|
|
|
|
|
return String(total)
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
created() {
|
|
|
|
|
|
this.getList()
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
toNumber(val) {
|
|
|
|
|
|
const n = Number(val)
|
|
|
|
|
|
return Number.isFinite(n) ? n : 0
|
|
|
|
|
|
},
|
2026-05-09 14:09:52 +08:00
|
|
|
|
operationRows(order) {
|
|
|
|
|
|
const rows = []
|
|
|
|
|
|
if (!order) return rows
|
|
|
|
|
|
rows.push({
|
|
|
|
|
|
action: '创建单据',
|
|
|
|
|
|
user: order.createBy || '-',
|
|
|
|
|
|
time: order.createTime || '-'
|
|
|
|
|
|
})
|
|
|
|
|
|
if (order.executeTime) {
|
|
|
|
|
|
rows.push({
|
|
|
|
|
|
action: '出库',
|
|
|
|
|
|
user: order.executeBy || '-',
|
|
|
|
|
|
time: order.executeTime || '-'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
if (order.revokeTime) {
|
|
|
|
|
|
rows.push({
|
|
|
|
|
|
action: '撤回',
|
|
|
|
|
|
user: order.revokeBy || '-',
|
|
|
|
|
|
time: order.revokeTime || '-'
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
return rows
|
|
|
|
|
|
},
|
2026-05-07 15:59:27 +08:00
|
|
|
|
getList() {
|
|
|
|
|
|
this.loading = true
|
|
|
|
|
|
listStockIoOrder(this.queryParams)
|
|
|
|
|
|
.then((res) => {
|
|
|
|
|
|
this.list = res.rows || []
|
|
|
|
|
|
this.total = res.total || 0
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
this.loading = false
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
handleQuery() {
|
|
|
|
|
|
this.queryParams.pageNum = 1
|
|
|
|
|
|
this.getList()
|
|
|
|
|
|
},
|
|
|
|
|
|
resetQuery() {
|
|
|
|
|
|
this.resetForm('queryForm')
|
|
|
|
|
|
this.queryParams.ioType = 'O'
|
|
|
|
|
|
this.handleQuery()
|
|
|
|
|
|
},
|
|
|
|
|
|
handleSelectionChange(selection) {
|
|
|
|
|
|
this.rows = selection
|
|
|
|
|
|
this.ids = selection.map((r) => r.orderId)
|
|
|
|
|
|
this.single = selection.length !== 1
|
|
|
|
|
|
},
|
2026-05-08 16:46:36 +08:00
|
|
|
|
handleExport() {
|
|
|
|
|
|
this.download(
|
|
|
|
|
|
'/gear/stockIoOrder/export',
|
|
|
|
|
|
{
|
|
|
|
|
|
...this.queryParams
|
|
|
|
|
|
},
|
|
|
|
|
|
`stockIoOrder_out_${new Date().getTime()}.xlsx`
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
2026-05-07 15:59:27 +08:00
|
|
|
|
resetEdit() {
|
|
|
|
|
|
this.editForm = {
|
|
|
|
|
|
ioType: 'O',
|
|
|
|
|
|
bizType: undefined,
|
|
|
|
|
|
responsibleName: undefined,
|
|
|
|
|
|
remark: undefined
|
|
|
|
|
|
}
|
|
|
|
|
|
this.editDetails = []
|
|
|
|
|
|
this.detailSelection = []
|
|
|
|
|
|
this.resetForm('editFormRef')
|
|
|
|
|
|
},
|
|
|
|
|
|
handleAdd() {
|
|
|
|
|
|
this.resetEdit()
|
|
|
|
|
|
this.editTitle = '新增出库单据'
|
|
|
|
|
|
this.editOpen = true
|
|
|
|
|
|
this.addDetailRow()
|
|
|
|
|
|
},
|
|
|
|
|
|
showDetail(row) {
|
|
|
|
|
|
this.detailOpen = true
|
|
|
|
|
|
this.detailData = null
|
|
|
|
|
|
getStockIoOrderWithDetail(row.orderId).then((res) => {
|
|
|
|
|
|
this.detailData = res.data
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
handleRevoke(row) {
|
|
|
|
|
|
const target = row || (this.rows && this.rows[0])
|
|
|
|
|
|
if (!target || !target.orderId) return
|
|
|
|
|
|
this.$modal
|
2026-05-08 16:46:36 +08:00
|
|
|
|
.confirm('确认撤回该出库单据?撤回后将标记为已撤回。')
|
2026-05-07 15:59:27 +08:00
|
|
|
|
.then(() => {
|
|
|
|
|
|
this.buttonLoading = true
|
2026-05-08 16:46:36 +08:00
|
|
|
|
return revokeStockIoOrder(target.orderId, '')
|
2026-05-07 15:59:27 +08:00
|
|
|
|
})
|
2026-05-08 16:46:36 +08:00
|
|
|
|
.then(() => {
|
|
|
|
|
|
this.$modal.msgSuccess('撤回成功')
|
2026-05-07 15:59:27 +08:00
|
|
|
|
this.getList()
|
2026-05-08 16:46:36 +08:00
|
|
|
|
if (this.detailOpen) {
|
|
|
|
|
|
this.detailOpen = false
|
|
|
|
|
|
}
|
2026-05-07 15:59:27 +08:00
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
this.buttonLoading = false
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
2026-05-09 14:09:52 +08:00
|
|
|
|
openFlow() {
|
|
|
|
|
|
this.flowOpen = true
|
|
|
|
|
|
this.flowResult = null
|
|
|
|
|
|
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
|
|
|
|
|
const end = new Date()
|
|
|
|
|
|
const start = new Date(end.getTime() - 7 * 24 * 60 * 60 * 1000)
|
|
|
|
|
|
this.flowForm.timeRange = [this.formatDateTime(start), this.formatDateTime(end)]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
formatDateTime(d) {
|
|
|
|
|
|
const pad = (n) => (n < 10 ? '0' + n : String(n))
|
|
|
|
|
|
const yyyy = d.getFullYear()
|
|
|
|
|
|
const MM = pad(d.getMonth() + 1)
|
|
|
|
|
|
const dd = pad(d.getDate())
|
|
|
|
|
|
const HH = pad(d.getHours())
|
|
|
|
|
|
const mm = pad(d.getMinutes())
|
|
|
|
|
|
const ss = pad(d.getSeconds())
|
|
|
|
|
|
return `${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}`
|
|
|
|
|
|
},
|
|
|
|
|
|
onFlowMaterialChange(material) {
|
|
|
|
|
|
this.flowItemName = material && material.materialName ? material.materialName : ''
|
|
|
|
|
|
},
|
|
|
|
|
|
fetchFlow() {
|
|
|
|
|
|
if (!this.flowForm.itemId) {
|
|
|
|
|
|
this.$modal.msgError('请选择物料')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
|
|
|
|
|
this.$modal.msgError('请选择时间范围')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.flowLoading = true
|
|
|
|
|
|
this.flowResult = null
|
|
|
|
|
|
getMaterialFlow({
|
|
|
|
|
|
itemId: this.flowForm.itemId,
|
|
|
|
|
|
startTime: this.flowForm.timeRange[0],
|
|
|
|
|
|
endTime: this.flowForm.timeRange[1]
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((res) => {
|
|
|
|
|
|
this.flowResult = res.data || null
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
this.flowLoading = false
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
2026-05-11 17:59:12 +08:00
|
|
|
|
exportFlow() {
|
|
|
|
|
|
if (!this.flowForm.itemId) {
|
|
|
|
|
|
this.$modal.msgError('请选择物料')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!this.flowForm.timeRange || this.flowForm.timeRange.length !== 2) {
|
|
|
|
|
|
this.$modal.msgError('请选择时间范围')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.download(
|
|
|
|
|
|
'/gear/stockIoOrder/materialFlow/export',
|
|
|
|
|
|
{
|
|
|
|
|
|
itemId: this.flowForm.itemId,
|
|
|
|
|
|
startTime: this.flowForm.timeRange[0],
|
|
|
|
|
|
endTime: this.flowForm.timeRange[1]
|
|
|
|
|
|
},
|
|
|
|
|
|
`material_flow_${this.flowForm.itemId}_${new Date().getTime()}.xlsx`
|
|
|
|
|
|
)
|
2026-05-09 14:09:52 +08:00
|
|
|
|
},
|
2026-05-07 15:59:27 +08:00
|
|
|
|
submitEdit() {
|
|
|
|
|
|
this.$refs.editFormRef.validate((valid) => {
|
|
|
|
|
|
if (!valid) return
|
|
|
|
|
|
if (!this.editDetails || this.editDetails.length === 0) {
|
|
|
|
|
|
this.$modal.msgError('请先添加明细')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const check = this.validateDetails()
|
|
|
|
|
|
if (!check.ok) {
|
|
|
|
|
|
this.$modal.msgError(check.message)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const payload = this.buildPayload()
|
|
|
|
|
|
if (payload.orderId) {
|
|
|
|
|
|
this.$modal.msgError('已出库单据不支持修改,请撤回后重新出库')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!payload.details || payload.details.length === 0) {
|
|
|
|
|
|
this.$modal.msgError('请至少填写一条明细:选择原料并输入数量')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.buttonLoading = true
|
|
|
|
|
|
addStockIoOrderWithDetail(payload)
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
this.$modal.msgSuccess('出库成功')
|
|
|
|
|
|
this.editOpen = false
|
|
|
|
|
|
this.getList()
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
this.buttonLoading = false
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
validateDetails() {
|
|
|
|
|
|
for (const d of this.editDetails || []) {
|
|
|
|
|
|
if (!d) continue
|
|
|
|
|
|
const hasAny =
|
|
|
|
|
|
(d.itemId != null && String(d.itemId) !== '') ||
|
|
|
|
|
|
(d.quantity != null && String(d.quantity) !== '') ||
|
|
|
|
|
|
(d.batchNo != null && String(d.batchNo) !== '') ||
|
|
|
|
|
|
(d.remark != null && String(d.remark) !== '') ||
|
|
|
|
|
|
(d.itemName != null && String(d.itemName) !== '')
|
|
|
|
|
|
if (!hasAny) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
const lineNo = d.lineNo != null ? d.lineNo : '-'
|
|
|
|
|
|
if (d.itemId == null || String(d.itemId) === '') {
|
|
|
|
|
|
return { ok: false, message: `第${lineNo}行请选择原料` }
|
|
|
|
|
|
}
|
|
|
|
|
|
const qty = this.toNumber(d.quantity)
|
|
|
|
|
|
if (qty <= 0) {
|
|
|
|
|
|
return { ok: false, message: `第${lineNo}行请输入数量` }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return { ok: true, message: '' }
|
|
|
|
|
|
},
|
|
|
|
|
|
buildPayload() {
|
|
|
|
|
|
const details = (this.editDetails || [])
|
|
|
|
|
|
.filter((d) => d && d.itemId != null && String(d.itemId) !== '' && this.toNumber(d.quantity) > 0)
|
|
|
|
|
|
.map((d, idx) => {
|
|
|
|
|
|
const lineNo = d.lineNo != null ? d.lineNo : idx + 1
|
|
|
|
|
|
const quantity = this.toNumber(d.quantity)
|
|
|
|
|
|
return {
|
|
|
|
|
|
detailId: d.detailId,
|
|
|
|
|
|
lineNo,
|
|
|
|
|
|
itemType: 'material',
|
|
|
|
|
|
itemId: d.itemId,
|
2026-05-08 16:46:36 +08:00
|
|
|
|
materialTypeSnapshot: d._materialType,
|
2026-05-07 15:59:27 +08:00
|
|
|
|
itemName: d.itemName,
|
|
|
|
|
|
quantity: quantity,
|
|
|
|
|
|
unit: d.unit,
|
|
|
|
|
|
batchNo: d.batchNo,
|
|
|
|
|
|
remark: d.remark
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const totalQty = details.reduce((sum, d) => sum + this.toNumber(d.quantity), 0)
|
|
|
|
|
|
return Object.assign({}, this.editForm, { ioType: 'O', totalQty, details })
|
|
|
|
|
|
},
|
|
|
|
|
|
addDetailRow() {
|
|
|
|
|
|
const nextNo = (this.editDetails || []).length + 1
|
|
|
|
|
|
this.editDetails.push({
|
|
|
|
|
|
detailId: undefined,
|
|
|
|
|
|
lineNo: nextNo,
|
|
|
|
|
|
itemType: 'material',
|
|
|
|
|
|
itemId: undefined,
|
|
|
|
|
|
itemName: undefined,
|
|
|
|
|
|
quantity: undefined,
|
|
|
|
|
|
unit: undefined,
|
|
|
|
|
|
batchNo: undefined,
|
|
|
|
|
|
remark: undefined
|
|
|
|
|
|
})
|
|
|
|
|
|
this.rebuildLineNo()
|
|
|
|
|
|
},
|
|
|
|
|
|
onDetailSelectionChange(rows) {
|
|
|
|
|
|
this.detailSelection = rows || []
|
|
|
|
|
|
},
|
|
|
|
|
|
removeDetailRows() {
|
|
|
|
|
|
const ids = new Set((this.detailSelection || []).map((r) => r.lineNo))
|
|
|
|
|
|
this.editDetails = (this.editDetails || []).filter((r) => !ids.has(r.lineNo))
|
|
|
|
|
|
this.detailSelection = []
|
|
|
|
|
|
if (this.editDetails.length === 0) {
|
|
|
|
|
|
this.addDetailRow()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.rebuildLineNo()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
rebuildLineNo() {
|
|
|
|
|
|
let i = 1
|
|
|
|
|
|
this.editDetails = (this.editDetails || []).map((r) => Object.assign({}, r, { lineNo: i++ }))
|
|
|
|
|
|
},
|
|
|
|
|
|
handleBatchPicked(materials) {
|
|
|
|
|
|
const rows = materials || []
|
|
|
|
|
|
if (rows.length === 0) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
let startIndex = 0
|
|
|
|
|
|
if (this.editDetails.length === 1) {
|
|
|
|
|
|
const r = this.editDetails[0]
|
|
|
|
|
|
const empty =
|
|
|
|
|
|
(r.itemId == null || String(r.itemId) === '') &&
|
2026-05-08 16:46:36 +08:00
|
|
|
|
(r.quantity == null || String(r.quantity) === '')
|
2026-05-07 15:59:27 +08:00
|
|
|
|
if (empty) {
|
|
|
|
|
|
r.itemId = rows[0].materialId
|
|
|
|
|
|
this.onMaterialPicked(r, rows[0])
|
|
|
|
|
|
startIndex = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for (let i = startIndex; i < rows.length; i++) {
|
|
|
|
|
|
this.addDetailRow()
|
|
|
|
|
|
const r = this.editDetails[this.editDetails.length - 1]
|
|
|
|
|
|
r.itemId = rows[i].materialId
|
|
|
|
|
|
this.onMaterialPicked(r, rows[i])
|
|
|
|
|
|
}
|
|
|
|
|
|
this.batchMaterialIds = []
|
|
|
|
|
|
},
|
|
|
|
|
|
onMaterialPicked(row, material) {
|
|
|
|
|
|
if (material && material.materialName) {
|
|
|
|
|
|
row.itemName = material.materialName
|
2026-05-08 16:46:36 +08:00
|
|
|
|
if (material.materialType != null) {
|
|
|
|
|
|
row._materialType = material.materialType
|
|
|
|
|
|
}
|
2026-05-07 15:59:27 +08:00
|
|
|
|
if (!row.unit && material.unit) {
|
|
|
|
|
|
row.unit = material.unit
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|