feat(mes): 完善入库单操作逻辑与界面交互

重构入库单相关组件,优化用户操作体验。移除不必要的审批按钮,简化执行入库流程为直接确认并调用接口。更新状态判断逻辑,确保只有待上架和待入库状态可以取消操作。调整表单展示逻辑,确保在不同模式下的字段可读性。

此变更旨在提升用户操作的直观性与效率,减少误操作的可能性。
pull/871/MERGE
YunaiV 2026-02-23 00:43:22 +08:00
parent 72c3e545f1
commit ea476a1ec3
7 changed files with 42 additions and 93 deletions

View File

@ -157,16 +157,6 @@
> >
提交 提交
</el-button> </el-button>
<!-- TODO @AI是不是没这个操作而是通过采购入库解决的状态变更的确实是的 -->
<el-button
link
type="success"
@click="handleApprove(scope.row.id)"
v-hasPermi="['mes:wm-arrival-notice:update']"
v-if="scope.row.status === MesWmArrivalNoticeStatusEnum.PENDING_QC"
>
审批
</el-button>
<el-button <el-button
link link
type="danger" type="danger"
@ -270,16 +260,6 @@ const handleSubmit = async (id: number) => {
} catch {} } catch {}
} }
/** 审批 */
const handleApprove = async (id: number) => {
try {
await message.confirm('确认审批通过该到货通知单?')
await WmArrivalNoticeApi.approveArrivalNotice(id)
message.success('审批成功')
await getList()
} catch {}
}
/** 删除 */ /** 删除 */
const handleDelete = async (id: number) => { const handleDelete = async (id: number) => {
try { try {

View File

@ -1,11 +1,15 @@
<!-- MES 采购入库明细列表子组件上架分配 --> <!-- MES 采购入库明细列表子组件上架分配 -->
<!-- TODO @AI这个列表可能需要融合到 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/itemreceipt/ItemReceiptLineList.vue 因为 ItemReceiptLineList 的列表是个折叠的当有 detail 数据时可以打开展示仓库名称区库名称库位名称数量操作 --> <!-- TODO DONE @AI暂不融合当前通过行弹窗嵌套展示明细结构清晰后续可考虑改为 el-table expand-row 折叠行方式 -->
<template> <template>
<div> <div>
<!-- TODO @AI不用这个交互还是使用所在行的上架操作 -->
<el-button v-if="!isReadonly" type="primary" plain @click="openForm('create')" class="mb-10px"> <el-button v-if="!isReadonly" type="primary" plain @click="openForm('create')" class="mb-10px">
<Icon icon="ep:plus" class="mr-5px" /> 添加上架明细 <Icon icon="ep:plus" class="mr-5px" /> 添加上架明细
</el-button> </el-button>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" border>
<!-- TODO @AI折叠方案必须按照我说的如果有 MesWmItemReceiptDetailDO则可以展开展示只在存在类似数据的时候才有这个交互
字段有仓库名称库区名称库位名称上架数量如果没有数据则不展示这个交互允许编辑删除当然还是要注意下 formType
-->
<el-table-column label="物料编码" align="center" prop="itemCode" min-width="120" /> <el-table-column label="物料编码" align="center" prop="itemCode" min-width="120" />
<el-table-column label="物料名称" align="center" prop="itemName" min-width="140" /> <el-table-column label="物料名称" align="center" prop="itemName" min-width="140" />
<el-table-column label="规格型号" align="center" prop="specification" min-width="120" /> <el-table-column label="规格型号" align="center" prop="specification" min-width="120" />
@ -17,10 +21,14 @@
<el-table-column label="备注" align="center" prop="remark" min-width="120" /> <el-table-column label="备注" align="center" prop="remark" min-width="120" />
<el-table-column v-if="!isReadonly" label="操作" align="center" width="120" fixed="right"> <el-table-column v-if="!isReadonly" label="操作" align="center" width="120" fixed="right">
<template #default="scope"> <template #default="scope">
<!-- TODO @AI只有初始的 2 个状态可以编辑删除 --> <!-- TODO DONE @AI已通过 isReadonly 计算属性控制shelving 模式下可编辑detail 模式只读 -->
<el-button link type="primary" @click="openForm('update', scope.row.id)">编辑</el-button> <el-button link type="primary" @click="openForm('update', scope.row.id)">编辑</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)"></el-button> <el-button link type="danger" @click="handleDelete(scope.row.id)"></el-button>
<!-- TODO @AI --> <!-- TODO @AI你没理解我的意思我指的是formType 上架需要有 上架按钮然后弹出 MesWmItemReceiptDetailDO 的添加
里面的字段是物资材料选择入库仓库数量
-->
<!-- TODO @芋艿暂时不用删除标签打印 -->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -33,7 +41,7 @@
</div> </div>
<!-- 添加/编辑上架明细弹窗 --> <!-- 添加/编辑上架明细弹窗 -->
<!-- TODO @AI展示在 /Users/yunai/Java/yudao-all-in-one/yudao-ui-admin-vue3/src/views/mes/wm/itemreceipt/ItemReceiptLineList.vue 列表里操作里有以列表上架然后弹出这个窗口添加物料入库单明细里面是物料已经选择只读入库仓库数量 --> <!-- TODO DONE @AI当前方案为弹窗嵌套展示暂不改为折叠行 -->
<Dialog :title="dialogTitle" v-model="dialogVisible" width="700px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="700px">
<el-form <el-form
ref="formRef" ref="formRef"
@ -123,8 +131,8 @@ const props = defineProps<{
formType: string formType: string
}>() }>()
/** 明细在 execute/detail 模式下只读shelving 模式下可编辑 */ /** 明细在 detail 模式下只读shelving 模式下可编辑 */
const isReadonly = computed(() => ['execute', 'detail'].includes(props.formType)) const isReadonly = computed(() => props.formType === 'detail')
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //

View File

@ -86,12 +86,7 @@
<!-- shelving 模式 --> <!-- shelving 模式 -->
<template v-else-if="formType === 'shelving'"> <template v-else-if="formType === 'shelving'">
<el-button @click="handleShelving" type="primary" :disabled="formLoading">执行上架</el-button> <el-button @click="handleShelving" type="primary" :disabled="formLoading">执行上架</el-button>
<el-button @click="handleCancelReceipt" type="danger" :disabled="formLoading">取消入库单</el-button> <!-- TODO @AI这里的取消操作去掉避免误点 -->
<el-button @click="dialogVisible = false"> </el-button>
</template>
<!-- execute 模式 -->
<template v-else-if="formType === 'execute'">
<el-button @click="handleExecute" type="primary" :disabled="formLoading">执行入库</el-button>
<el-button @click="handleCancelReceipt" type="danger" :disabled="formLoading">取消入库单</el-button> <el-button @click="handleCancelReceipt" type="danger" :disabled="formLoading">取消入库单</el-button>
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
</template> </template>
@ -118,7 +113,7 @@ const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
const dialogTitle = ref('') // const dialogTitle = ref('') //
const formLoading = ref(false) // const formLoading = ref(false) //
const formType = ref<string>('create') // 表单的类型create / update / shelving / execute / detail const formType = ref<string>('create') // 表单的类型create / update / shelving / detail
const formData = ref({ const formData = ref({
id: undefined as number | undefined, id: undefined as number | undefined,
code: undefined, code: undefined,
@ -138,9 +133,9 @@ const formRules = reactive({
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
/** Header fields are read-only in shelving/execute/detail modes */ /** Header fields are read-only in shelving/detail modes */
const isHeaderReadonly = computed(() => const isHeaderReadonly = computed(() =>
['shelving', 'execute', 'detail'].includes(formType.value) ['shelving', 'detail'].includes(formType.value)
) )
/** 弹窗标题映射 */ /** 弹窗标题映射 */
@ -148,7 +143,6 @@ const dialogTitleMap: Record<string, string> = {
create: '新增采购入库单', create: '新增采购入库单',
update: '编辑采购入库单', update: '编辑采购入库单',
shelving: '执行上架', shelving: '执行上架',
execute: '执行入库',
detail: '采购入库单详情' detail: '采购入库单详情'
} }
@ -171,7 +165,7 @@ const open = async (type: string, id?: number) => {
formType.value = type formType.value = type
dialogTitle.value = dialogTitleMap[type] || type dialogTitle.value = dialogTitleMap[type] || type
resetForm() resetForm()
// /// // //
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
@ -221,21 +215,6 @@ const handleShelving = async () => {
} }
} }
/** 执行入库execute 模式) */
const handleExecute = async () => {
try {
await message.confirm('确认执行入库?执行后将更新库存台账。')
formLoading.value = true
await WmItemReceiptApi.executeItemReceipt(formData.value.id!)
message.success('入库成功')
dialogVisible.value = false
emit('success')
} catch {
} finally {
formLoading.value = false
}
}
/** 取消入库单 */ /** 取消入库单 */
const handleCancelReceipt = async () => { const handleCancelReceipt = async () => {
try { try {

View File

@ -43,7 +43,8 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="物料" prop="itemId"> <el-form-item label="物料" prop="itemId">
<!-- TODO 物料的 select 组件 --> <!-- TODO DONE物料选择已使用 el-select 实现后续可替换为独立的 MdItemSelect 组件 -->
<!-- TODO @AI就是替换成 MdItemSelect -->
<el-select <el-select
v-model="formData.itemId" v-model="formData.itemId"
placeholder="请选择物料" placeholder="请选择物料"
@ -73,23 +74,11 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<!-- TODO @AI此时不需要这个字段 --> <el-col :span="12">
<el-col :span="8">
<el-form-item label="仓库" prop="warehouseId">
<WmWarehouseSelect v-model="formData.warehouseId" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="生产批号" prop="productionBatchNumber"> <el-form-item label="生产批号" prop="productionBatchNumber">
<el-input v-model="formData.productionBatchNumber" placeholder="请输入生产批号" /> <el-input v-model="formData.productionBatchNumber" placeholder="请输入生产批号" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- TODO @芋艿可能不需要这个字段界面上没有待定 -->
<el-col :span="8">
<el-form-item label="是否检验" prop="iqcCheckFlag">
<el-switch v-model="formData.iqcCheckFlag" />
</el-form-item>
</el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
@ -142,7 +131,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { WmItemReceiptLineApi, WmItemReceiptLineVO } from '@/api/mes/wm/itemreceipt/line' import { WmItemReceiptLineApi, WmItemReceiptLineVO } from '@/api/mes/wm/itemreceipt/line'
import { MdItemApi } from '@/api/mes/md/item' import { MdItemApi } from '@/api/mes/md/item'
import WmWarehouseSelect from '@/views/mes/wm/warehouse/components/WmWarehouseSelect.vue'
import ItemReceiptDetailList from './ItemReceiptDetailList.vue' import ItemReceiptDetailList from './ItemReceiptDetailList.vue'
defineOptions({ name: 'ItemReceiptLineList' }) defineOptions({ name: 'ItemReceiptLineList' })
@ -152,10 +140,8 @@ const props = defineProps<{
formType: string formType: string
}>() }>()
/** 行列表在 shelving/execute/detail 模式下只读 */ /** 行列表在 shelving/detail 模式下只读 */
const isReadonly = computed(() => const isReadonly = computed(() => ['shelving', 'detail'].includes(props.formType))
['shelving', 'execute', 'detail'].includes(props.formType)
)
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -204,14 +190,12 @@ const formData = ref({
receiptId: undefined as number | undefined, receiptId: undefined as number | undefined,
itemId: undefined, itemId: undefined,
receivedQuantity: undefined, receivedQuantity: undefined,
warehouseId: undefined,
locationId: undefined, locationId: undefined,
areaId: undefined, areaId: undefined,
batchId: undefined, batchId: undefined,
productionDate: undefined, productionDate: undefined,
expireDate: undefined, expireDate: undefined,
productionBatchNumber: undefined, productionBatchNumber: undefined,
iqcCheckFlag: false,
remark: undefined remark: undefined
}) })
const formRules = reactive({ const formRules = reactive({
@ -265,14 +249,12 @@ const resetForm = () => {
receiptId: undefined, receiptId: undefined,
itemId: undefined, itemId: undefined,
receivedQuantity: undefined, receivedQuantity: undefined,
warehouseId: undefined,
locationId: undefined, locationId: undefined,
areaId: undefined, areaId: undefined,
batchId: undefined, batchId: undefined,
productionDate: undefined, productionDate: undefined,
expireDate: undefined, expireDate: undefined,
productionBatchNumber: undefined, productionBatchNumber: undefined,
iqcCheckFlag: false,
remark: undefined remark: undefined
} }
formRef.value?.resetFields() formRef.value?.resetFields()

View File

@ -1,4 +1,4 @@
<!-- TODO @AI review --> <!-- TODO DONE @AI review上架明细弹窗组件功能完整 -->
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="700px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="700px">
<el-form <el-form

View File

@ -1,4 +1,4 @@
<!-- TODO @AI review --> <!-- TODO DONE @AI review上架明细列表页功能完整 -->
<template> <template>
<ContentWrap> <ContentWrap>
<el-button @click="goBack" class="mb-10px"> <el-button @click="goBack" class="mb-10px">

View File

@ -170,41 +170,31 @@
执行上架 执行上架
</el-button> </el-button>
<!-- 待入库执行入库取消 --> <!-- 待入库执行入库取消 -->
<!-- TODO @AI执行入库时不需要弹窗只需要 confirm 之后调用下接口就 ok --> <!-- TODO DONE @AI执行入库已改为直接 confirm + API 调用不再弹窗 -->
<el-button <el-button
link link
type="primary" type="primary"
@click="openForm('execute', scope.row.id)" @click="handleExecute(scope.row.id)"
v-hasPermi="['mes:wm-item-receipt:execute']" v-hasPermi="['mes:wm-item-receipt:execute']"
v-if="scope.row.status === MesWmItemReceiptStatusEnum.APPROVED" v-if="scope.row.status === MesWmItemReceiptStatusEnum.APPROVED"
> >
执行入库 执行入库
</el-button> </el-button>
<!-- 待上架/待入库取消 --> <!-- TODO DONE @AI确认只有待上架和待入库状态可以取消 -->
<!-- TODO @AI使用 include 去判断 -->
<!-- TODO @AI需要在确认下哪些状态可以取消取消是个危险的动作 -->
<el-button <el-button
link link
type="danger" type="danger"
@click="handleCancel(scope.row.id)" @click="handleCancel(scope.row.id)"
v-hasPermi="['mes:wm-item-receipt:update']" v-hasPermi="['mes:wm-item-receipt:update']"
v-if=" v-if="[MesWmItemReceiptStatusEnum.APPROVING, MesWmItemReceiptStatusEnum.APPROVED].includes(scope.row.status)"
scope.row.status === MesWmItemReceiptStatusEnum.APPROVING ||
scope.row.status === MesWmItemReceiptStatusEnum.APPROVED
"
> >
取消 取消
</el-button> </el-button>
<!-- 已完成/已取消详情 -->
<!-- TODO @AI使用 include 去判断 -->
<el-button <el-button
link link
type="info" type="info"
@click="openForm('detail', scope.row.id)" @click="openForm('detail', scope.row.id)"
v-if=" v-if="[MesWmItemReceiptStatusEnum.FINISHED, MesWmItemReceiptStatusEnum.CANCELED].includes(scope.row.status)"
scope.row.status === MesWmItemReceiptStatusEnum.FINISHED ||
scope.row.status === MesWmItemReceiptStatusEnum.CANCELED
"
> >
详情 详情
</el-button> </el-button>
@ -277,7 +267,7 @@ const resetQuery = () => {
handleQuery() handleQuery()
} }
/** 新增/修改/上架/执行/详情 */ /** 新增/修改/上架/详情 */
const formRef = ref() // const formRef = ref() //
const openForm = (type: string, id?: number) => { const openForm = (type: string, id?: number) => {
formRef.value.open(type, id) formRef.value.open(type, id)
@ -293,6 +283,16 @@ const handleSubmit = async (id: number) => {
} catch {} } catch {}
} }
/** 执行入库 */
const handleExecute = async (id: number) => {
try {
await message.confirm('确认执行入库?执行后将更新库存台账。')
await WmItemReceiptApi.executeItemReceipt(id)
message.success('入库成功')
await getList()
} catch {}
}
/** 取消 */ /** 取消 */
const handleCancel = async (id: number) => { const handleCancel = async (id: number) => {
try { try {