已添加53个文件
已复制12个文件
已重命名47个文件
已删除16个文件
已修改84个文件
12568 ■■■■ 文件已修改
Code/.omc/state/agent-replay-9007b9ea-1eb6-4d24-8fe7-2c3a949eac88.jsonl 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/checkpoints/checkpoint-2026-04-12T13-52-50-252Z.json 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/idle-notif-cooldown.json 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/last-tool-error.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/mission-state.json 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/subagent-tracking.json 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/.server-stopped 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/button-visibility.html 230 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/error-handling.html 223 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/operations-column.html 203 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/operations-layout-v2.html 347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/status-mapping.html 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.superpowers/brainstorm/2159-1775998341/waiting.html 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/CLAUDE.md 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/.omc/state/idle-notif-cooldown.json 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Tenant.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/robotTask.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Tenant.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/robotTask.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/taskHty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Client/vite.config.mjs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/BaseRepository/UnitOfWorks/IUnitOfWorkManage.cs 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/BaseRepository/UnitOfWorks/UnitOfWorkManage.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/ReceiveTaskResultDto.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/UpdateTaskDto.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/ITaskService.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/TaskController.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/OutboundTaskFlowService.cs 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/RelocationTaskFlowService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/RobotTaskFlowService.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ManualInbound/ManualInboundTaskHandler.cs 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/package.json 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/pnpm-lock.yaml 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/api/mes.js 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/components/MesConfirmDialog.vue 273 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/customerInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/locationInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/materielCodeInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/materielInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/palletCodeInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/roadwayInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/supplierInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/userInfo.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/userInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/basic/warehouse.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrder.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrderResult.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrderResult.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail_Hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder_Hty.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder_Hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrder.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrder.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrderDetail.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrderDetail.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrder.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrderDetail.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrderDetail.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/mesOutboundOrder.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outStockLockInfo.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrderDetail.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrderDetail_Hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder_Hty.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder_Hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/record/locationStatusChangeRecord.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/record/stockQuantityChangeRecord.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stock.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockChat.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo.jsx 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail_Hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo_Hty.js 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo_Hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockView.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Dictionary.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_DictionaryList.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Log.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Role.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Role1.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Tenant.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_User.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/system/Sys_Department.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/addManualTask.vue 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/gridBodyExtension.vue 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.jsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task_hty.jsx 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/customerInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/locationInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/materielCodeInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/materielInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/palletCodeInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/supplierInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/userInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/basic/warehouse.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/check/checkOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/check/checkOrderResult.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrderDetail_Hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrder_Hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/purchaseOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/purchaseOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/receiveOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/inbound/receiveOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/outbound/mesOutboundOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outStockLockInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrderDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrderDetail_Hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrder_Hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/record/locationStatusChangeRecord.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/record/stockQuantityChangeRecord.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stock.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail_Hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfo_Hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockView.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Dictionary.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_DictionaryList.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Log.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Role.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Role1.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Tenant.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_User.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/system/Sys_Department.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/taskinfo/task.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/taskinfo/task_hty.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/vite.config.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/vite.config.mjs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/Database/Scripts/20260412_MesApiLog.sql 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/BaseRepository/UnitOfWorks/IUnitOfWorkManage.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/BaseRepository/UnitOfWorks/UnitOfWorkManage.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/BindContainerRequestDto.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/ContainerNgReportRequestDto.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/InboundInContainerRequestDto.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesApiLogDto.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/OutboundInContainerRequestDto.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/UnbindContainerRequestDto.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/ReceiveTaskResultDto.cs 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/UpdateTaskDto.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_IMesService/WIDESEA_IMesService.csproj 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_MesService/WIDESEA_MesService.csproj 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs 881 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_AutoOutbound.cs 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs 328 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Relocation.cs 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Robot.cs 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_TaskStatus.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Tray.cs 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/docs/superpowers/plans/2026-04-11-manual-task-creation-plan.md 434 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/docs/superpowers/plans/2026-04-12-manual-dispatch-tasks-to-wcs-plan.md 723 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/docs/superpowers/plans/2026-04-12-mes-integration-plan.md 1933 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/docs/superpowers/specs/2026-04-11-manual-task-creation-design.md 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/docs/superpowers/specs/2026-04-12-manual-dispatch-tasks-to-wcs-design.md 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/docs/superpowers/specs/2026-04-12-mes-integration-design.md 700 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1001/config.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1002/config.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/测试工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/protocol-templates.json 124 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/agent-replay-9007b9ea-1eb6-4d24-8fe7-2c3a949eac88.jsonl
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
{"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:brainstorming"}
{"t":0,"agent":"a9501da","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a9501da","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":8663}
{"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:writing-plans"}
{"t":0,"agent":"abb2829","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"abb2829","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":12576}
{"t":0,"agent":"system","event":"skill_invoked","skill_name":"superpowers:subagent-driven-development"}
{"t":0,"agent":"ab6a2d9","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"ab6a2d9","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":55725}
{"t":0,"agent":"a3a7fd0","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a3a7fd0","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":11968}
{"t":0,"agent":"a0ed568","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a0ed568","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":84239}
{"t":0,"agent":"a32ad6a","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a32ad6a","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":155707}
{"t":0,"agent":"a4dd492","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a4dd492","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":17301}
{"t":0,"agent":"aefc20e","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"aefc20e","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":180634}
{"t":0,"agent":"a18fadb","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a18fadb","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":92939}
{"t":0,"agent":"a379d3a","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a379d3a","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":19656}
{"t":0,"agent":"a7985a3","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a7985a3","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":79712}
{"t":0,"agent":"ab97ed1","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"ab97ed1","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":57941}
{"t":0,"agent":"ad231d3","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"ad231d3","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":119172}
{"t":0,"agent":"a7e494a","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a7e494a","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":53504}
{"t":0,"agent":"aba5116","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"aba5116","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":94295}
{"t":0,"agent":"a8e5fbf","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a8e5fbf","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":192007}
{"t":0,"agent":"a9a1cc3","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a9a1cc3","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":29527}
{"t":0,"agent":"a581cc0","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a581cc0","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":35311}
{"t":0,"agent":"a6536cf","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a6536cf","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":154828}
{"t":0,"agent":"ab287b4","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"ab287b4","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":22379}
{"t":0,"agent":"a3bba61","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a3bba61","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":106152}
{"t":0,"agent":"a0a843a","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a0a843a","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":30220}
{"t":0,"agent":"afd8eac","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"afd8eac","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":167934}
{"t":0,"agent":"aa4ea81","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"aa4ea81","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":28896}
{"t":0,"agent":"a522225","agent_type":"superpowers:code-reviewer","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a522225","agent_type":"superpowers:code-reviewer","event":"agent_stop","success":true,"duration_ms":35164}
{"t":0,"agent":"ac7c987","agent_type":"unknown","event":"agent_stop","success":true}
{"t":0,"agent":"a405c70","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a405c70","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":59509}
{"t":0,"agent":"a0ff377","agent_type":"general-purpose","event":"agent_start","parent_mode":"none"}
{"t":0,"agent":"a0ff377","agent_type":"general-purpose","event":"agent_stop","success":true,"duration_ms":7226}
Code/.omc/state/checkpoints/checkpoint-2026-04-12T13-52-50-252Z.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
{
  "created_at": "2026-04-12T13:52:50.251Z",
  "trigger": "auto",
  "active_modes": {},
  "todo_summary": {
    "pending": 0,
    "in_progress": 0,
    "completed": 0
  },
  "wisdom_exported": false,
  "background_jobs": {
    "active": [],
    "recent": [],
    "stats": null
  }
}
Code/.omc/state/idle-notif-cooldown.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
{
  "lastSentAt": "2026-04-12T12:35:52.350Z"
}
Code/.omc/state/last-tool-error.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
{
  "tool_name": "Read",
  "tool_input_preview": "{\"file_path\":\"D:\\\\Git\\\\ShanMeiXinNengYuan\\\\Code\\\\WMS\\\\WIDESEA_WMSServer\\\\WIDESEA_WMSServer\\\\Controllers\\\\BatteryCellController.cs\"}",
  "error": "File does not exist. Note: your current working directory is D:\\Git\\ShanMeiXinNengYuan\\Code.",
  "timestamp": "2026-04-12T13:48:48.578Z",
  "retry_count": 1
}
Code/.omc/state/mission-state.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,329 @@
{
  "updatedAt": "2026-04-12T13:54:45.455Z",
  "missions": [
    {
      "id": "session:9007b9ea-1eb6-4d24-8fe7-2c3a949eac88:none",
      "source": "session",
      "name": "none",
      "objective": "Session mission",
      "createdAt": "2026-04-12T13:05:45.505Z",
      "updatedAt": "2026-04-12T13:54:45.455Z",
      "status": "done",
      "workerCount": 27,
      "taskCounts": {
        "total": 27,
        "pending": 0,
        "blocked": 0,
        "inProgress": 0,
        "completed": 27,
        "failed": 0
      },
      "agents": [
        {
          "name": "general-purpose:a9501da",
          "role": "general-purpose",
          "ownership": "a9501da36be20eb24",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:53:13.149Z"
        },
        {
          "name": "general-purpose:abb2829",
          "role": "general-purpose",
          "ownership": "abb28297f69440cf9",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:10:58.257Z"
        },
        {
          "name": "general-purpose:ab6a2d9",
          "role": "general-purpose",
          "ownership": "ab6a2d9ed6f459274",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:13:25.065Z"
        },
        {
          "name": "general-purpose:a3a7fd0",
          "role": "general-purpose",
          "ownership": "a3a7fd0c0829e5f0c",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:13:48.330Z"
        },
        {
          "name": "superpowers:code-reviewer:a0ed568",
          "role": "superpowers:code-reviewer",
          "ownership": "a0ed568f9e32bd5b0",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:17:47.626Z"
        },
        {
          "name": "general-purpose:a32ad6a",
          "role": "general-purpose",
          "ownership": "a32ad6a96659ec279",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:24:23.170Z"
        },
        {
          "name": "general-purpose:a4dd492",
          "role": "general-purpose",
          "ownership": "a4dd4923f28eeaea9",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:24:47.505Z"
        },
        {
          "name": "superpowers:code-reviewer:aefc20e",
          "role": "superpowers:code-reviewer",
          "ownership": "aefc20e8f3f9438f3",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:27:54.713Z"
        },
        {
          "name": "general-purpose:a18fadb",
          "role": "general-purpose",
          "ownership": "a18fadb164b0dfdb8",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:29:34.186Z"
        },
        {
          "name": "superpowers:code-reviewer:a379d3a",
          "role": "superpowers:code-reviewer",
          "ownership": "a379d3a8646888a66",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:29:58.098Z"
        },
        {
          "name": "general-purpose:a7985a3",
          "role": "general-purpose",
          "ownership": "a7985a337688f4971",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:31:36.395Z"
        },
        {
          "name": "general-purpose:ab97ed1",
          "role": "general-purpose",
          "ownership": "ab97ed1012975ec79",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:32:42.067Z"
        },
        {
          "name": "general-purpose:ad231d3",
          "role": "general-purpose",
          "ownership": "ad231d3220b6ba05e",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:34:46.714Z"
        },
        {
          "name": "general-purpose:a7e494a",
          "role": "general-purpose",
          "ownership": "a7e494aa6cfed7748",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:35:43.945Z"
        },
        {
          "name": "superpowers:code-reviewer:aba5116",
          "role": "superpowers:code-reviewer",
          "ownership": "aba5116eaa0ef6b17",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:37:25.682Z"
        },
        {
          "name": "general-purpose:a8e5fbf",
          "role": "general-purpose",
          "ownership": "a8e5fbfad6a90c9c9",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:41:43.878Z"
        },
        {
          "name": "general-purpose:a9a1cc3",
          "role": "general-purpose",
          "ownership": "a9a1cc30ce01dd110",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:42:18.517Z"
        },
        {
          "name": "superpowers:code-reviewer:a581cc0",
          "role": "superpowers:code-reviewer",
          "ownership": "a581cc05f79eed2ab",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:42:59.753Z"
        },
        {
          "name": "general-purpose:a6536cf",
          "role": "general-purpose",
          "ownership": "a6536cf10ec91573d",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:45:40.542Z"
        },
        {
          "name": "superpowers:code-reviewer:ab287b4",
          "role": "superpowers:code-reviewer",
          "ownership": "ab287b483b80dd1ab",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:46:06.732Z"
        },
        {
          "name": "general-purpose:a3bba61",
          "role": "general-purpose",
          "ownership": "a3bba6185e3ada31c",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:48:07.136Z"
        },
        {
          "name": "general-purpose:a0a843a",
          "role": "general-purpose",
          "ownership": "a0a843ae77e400e58",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:48:41.191Z"
        },
        {
          "name": "general-purpose:afd8eac",
          "role": "general-purpose",
          "ownership": "afd8eacdfadd73310",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:51:34.998Z"
        },
        {
          "name": "general-purpose:aa4ea81",
          "role": "general-purpose",
          "ownership": "aa4ea817d9e391ac6",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:52:08.592Z"
        },
        {
          "name": "superpowers:code-reviewer:a522225",
          "role": "superpowers:code-reviewer",
          "ownership": "a5222258aed5f5f3d",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:52:49.666Z"
        },
        {
          "name": "general-purpose:a405c70",
          "role": "general-purpose",
          "ownership": "a405c7090be2886ee",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:54:30.286Z"
        },
        {
          "name": "general-purpose:a0ff377",
          "role": "general-purpose",
          "ownership": "a0ff37729a29ea04f",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-12T13:54:45.455Z"
        }
      ],
      "timeline": [
        {
          "id": "session-start:a405c7090be2886ee:2026-04-12T13:53:30.777Z",
          "at": "2026-04-12T13:53:30.777Z",
          "kind": "update",
          "agent": "general-purpose:a405c70",
          "detail": "started general-purpose:a405c70",
          "sourceKey": "session-start:a405c7090be2886ee"
        },
        {
          "id": "session-stop:a405c7090be2886ee:2026-04-12T13:54:30.286Z",
          "at": "2026-04-12T13:54:30.286Z",
          "kind": "completion",
          "agent": "general-purpose:a405c70",
          "detail": "completed",
          "sourceKey": "session-stop:a405c7090be2886ee"
        },
        {
          "id": "session-start:a0ff37729a29ea04f:2026-04-12T13:54:38.229Z",
          "at": "2026-04-12T13:54:38.229Z",
          "kind": "update",
          "agent": "general-purpose:a0ff377",
          "detail": "started general-purpose:a0ff377",
          "sourceKey": "session-start:a0ff37729a29ea04f"
        },
        {
          "id": "session-stop:a0ff37729a29ea04f:2026-04-12T13:54:45.455Z",
          "at": "2026-04-12T13:54:45.455Z",
          "kind": "completion",
          "agent": "general-purpose:a0ff377",
          "detail": "completed",
          "sourceKey": "session-stop:a0ff37729a29ea04f"
        }
      ]
    }
  ]
}
Code/.omc/state/subagent-tracking.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,251 @@
{
  "agents": [
    {
      "agent_id": "a9501da36be20eb24",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:05:45.505Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:05:54.168Z",
      "duration_ms": 8663
    },
    {
      "agent_id": "abb28297f69440cf9",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:10:45.681Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:10:58.257Z",
      "duration_ms": 12576
    },
    {
      "agent_id": "ab6a2d9ed6f459274",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:12:29.340Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:13:25.065Z",
      "duration_ms": 55725
    },
    {
      "agent_id": "a3a7fd0c0829e5f0c",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:13:36.362Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:13:48.330Z",
      "duration_ms": 11968
    },
    {
      "agent_id": "a0ed568f9e32bd5b0",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:16:23.387Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:17:47.626Z",
      "duration_ms": 84239
    },
    {
      "agent_id": "a32ad6a96659ec279",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:21:47.463Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:24:23.170Z",
      "duration_ms": 155707
    },
    {
      "agent_id": "a4dd4923f28eeaea9",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:24:30.204Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:24:47.505Z",
      "duration_ms": 17301
    },
    {
      "agent_id": "aefc20e8f3f9438f3",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:24:54.079Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:27:54.713Z",
      "duration_ms": 180634
    },
    {
      "agent_id": "a18fadb164b0dfdb8",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:28:01.247Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:29:34.186Z",
      "duration_ms": 92939
    },
    {
      "agent_id": "a379d3a8646888a66",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:29:38.442Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:29:58.098Z",
      "duration_ms": 19656
    },
    {
      "agent_id": "a7985a337688f4971",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:30:16.683Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:31:36.395Z",
      "duration_ms": 79712
    },
    {
      "agent_id": "ab97ed1012975ec79",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:31:44.126Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:32:42.067Z",
      "duration_ms": 57941
    },
    {
      "agent_id": "ad231d3220b6ba05e",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:32:47.542Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:34:46.714Z",
      "duration_ms": 119172
    },
    {
      "agent_id": "a7e494aa6cfed7748",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:34:50.441Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:35:43.945Z",
      "duration_ms": 53504
    },
    {
      "agent_id": "aba5116eaa0ef6b17",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:35:51.387Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:37:25.682Z",
      "duration_ms": 94295
    },
    {
      "agent_id": "a8e5fbfad6a90c9c9",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:38:31.871Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:41:43.878Z",
      "duration_ms": 192007
    },
    {
      "agent_id": "a9a1cc30ce01dd110",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:41:48.990Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:42:18.517Z",
      "duration_ms": 29527
    },
    {
      "agent_id": "a581cc05f79eed2ab",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:42:24.442Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:42:59.753Z",
      "duration_ms": 35311
    },
    {
      "agent_id": "a6536cf10ec91573d",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:43:05.714Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:45:40.542Z",
      "duration_ms": 154828
    },
    {
      "agent_id": "ab287b483b80dd1ab",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:45:44.353Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:46:06.732Z",
      "duration_ms": 22379
    },
    {
      "agent_id": "a3bba6185e3ada31c",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:46:20.984Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:48:07.136Z",
      "duration_ms": 106152
    },
    {
      "agent_id": "a0a843ae77e400e58",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:48:10.971Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:48:41.191Z",
      "duration_ms": 30220
    },
    {
      "agent_id": "afd8eacdfadd73310",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:48:47.064Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:51:34.998Z",
      "duration_ms": 167934
    },
    {
      "agent_id": "aa4ea817d9e391ac6",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:51:39.696Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:52:08.592Z",
      "duration_ms": 28896
    },
    {
      "agent_id": "a5222258aed5f5f3d",
      "agent_type": "superpowers:code-reviewer",
      "started_at": "2026-04-12T13:52:14.502Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:52:49.666Z",
      "duration_ms": 35164
    },
    {
      "agent_id": "a405c7090be2886ee",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:53:30.777Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:54:30.286Z",
      "duration_ms": 59509
    },
    {
      "agent_id": "a0ff37729a29ea04f",
      "agent_type": "general-purpose",
      "started_at": "2026-04-12T13:54:38.229Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-12T13:54:45.455Z",
      "duration_ms": 7226
    }
  ],
  "total_spawned": 27,
  "total_completed": 27,
  "total_failed": 0,
  "last_updated": "2026-04-12T13:54:45.571Z"
}
Code/.superpowers/brainstorm/2159-1775998341/.server-stopped
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
{"reason":"idle timeout","timestamp":1776000741677}
Code/.superpowers/brainstorm/2159-1775998341/button-visibility.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,230 @@
<h2>按钮显示与状态控制</h2>
<p class="subtitle">根据库存状态控制操作按钮的显示</p>
<div class="section">
  <h3>场景分析</h3>
  <p class="subtitle">不同状态的库存可能允许或禁止某些操作</p>
  <div class="mockup">
    <div class="mockup-header">库存状态枚举</div>
    <div class="mockup-body">
      <div class="status-list">
        <div class="status-item">
          <span class="status-badge status-badge-idle">待入库</span>
          <div class="status-desc">托盘已创建但尚未完成入库</div>
        </div>
        <div class="status-item">
          <span class="status-badge status-badge-instock">在库</span>
          <div class="status-desc">托盘正常在库中</div>
        </div>
        <div class="status-item">
          <span class="status-badge status-badge-outbound">出库中</span>
          <div class="status-desc">托盘正在出库流程中</div>
        </div>
        <div class="status-item">
          <span class="status-badge status-badge-lock">锁定</span>
          <div class="status-desc">托盘被锁定,不可操作</div>
        </div>
      </div>
    </div>
  </div>
</div>
<div class="section">
  <h3>按钮显示策略选择</h3>
  <p class="subtitle">不同策略的用户体验和实现复杂度</p>
  <div class="options">
    <div class="option" data-choice="always" onclick="toggleSelect(this)">
      <div class="letter">A</div>
      <div class="content">
        <h3>始终显示</h3>
        <p>所有操作按钮始终显示,点击后在弹窗中校验状态</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>实现简单</li><li>用户清楚有哪些功能</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>可能产生无效点击</li><li>体验较差</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="dynamic" onclick="toggleSelect(this)">
      <div class="letter">B</div>
      <div class="content">
        <h3>动态显示</h3>
        <p>根据库存状态动态显示/隐藏按钮</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>界面清晰</li><li>减少误操作</li><li>体验更好</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>需要维护状态映射</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="disable" onclick="toggleSelect(this)">
      <div class="letter">C</div>
      <div class="content">
        <h3>显示并禁用</h3>
        <p>所有按钮都显示,但不可用的显示为禁用状态</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>用户知道有此功能</li><li>清楚为何不可用</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>界面可能拥挤</li></ul></div>
        </div>
      </div>
    </div>
  </div>
</div>
<div class="section">
  <h3>示例:动态显示效果</h3>
  <p class="subtitle">不同状态下的按钮显示情况</p>
  <div class="mockup">
    <div class="mockup-header">预览:不同状态的按钮显示</div>
    <div class="mockup-body">
      <table class="mock-table">
        <thead>
          <tr>
            <th>托盘编号</th>
            <th>库存状态</th>
            <th>操作按钮</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>P001</td>
            <td><span class="status-badge status-badge-idle">待入库</span></td>
            <td>
              <button class="mock-btn-inline mock-btn-primary">进站</button>
              <button class="mock-btn-inline mock-btn-disabled">出站</button>
            </td>
          </tr>
          <tr>
            <td>P002</td>
            <td><span class="status-badge status-badge-instock">在库</span></td>
            <td>
              <button class="mock-btn-inline mock-btn-disabled">进站</button>
              <button class="mock-btn-inline mock-btn-success">出站</button>
            </td>
          </tr>
          <tr>
            <td>P003</td>
            <td><span class="status-badge status-badge-lock">锁定</span></td>
            <td>
              <span class="text-muted">暂无可执行操作</span>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
<style>
.status-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.status-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  background: #fafafa;
  border-radius: 6px;
}
.status-badge {
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
}
.status-badge-idle {
  background: #e3f2fd;
  color: #1976d2;
}
.status-badge-instock {
  background: #e8f5e9;
  color: #388e3c;
}
.status-badge-outbound {
  background: #fff3e0;
  color: #f57c00;
}
.status-badge-lock {
  background: #ffebee;
  color: #d32f2f;
}
.status-desc {
  font-size: 13px;
  color: #606266;
}
.mock-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.mock-table th, .mock-table td {
  border: 1px solid #e0e0e0;
  padding: 10px 12px;
  text-align: left;
}
.mock-table th {
  background: #f5f5f5;
  font-weight: 600;
}
.mock-table tbody tr:nth-child(even) {
  background: #fafafa;
}
.mock-btn-inline {
  padding: 6px 14px;
  font-size: 13px;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
  background: white;
  cursor: pointer;
  margin-right: 8px;
}
.mock-btn-primary {
  background: #1f5eff;
  color: white;
  border-color: #1f5eff;
}
.mock-btn-success {
  background: #67c23a;
  color: white;
  border-color: #67c23a;
}
.mock-btn-disabled {
  background: #f5f5f5;
  color: #c0c4cc;
  border-color: #e4e7ed;
  cursor: not-allowed;
}
.text-muted {
  color: #909399;
  font-size: 13px;
}
.pros-cons {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}
.pros, .cons {
  flex: 1;
  font-size: 13px;
}
.pros h4 {
  color: #4caf50;
  margin: 0 0 6px 0;
}
.cons h4 {
  color: #f56c6c;
  margin: 0 0 6px 0;
}
.pros ul, .cons ul {
  margin: 0;
  padding-left: 16px;
}
.pros li, .cons li {
  margin-bottom: 4px;
}
</style>
Code/.superpowers/brainstorm/2159-1775998341/error-handling.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,223 @@
<h2>错误处理与反馈</h2>
<p class="subtitle">MES接口调用失败时的处理方式</p>
<div class="section">
  <h3>场景分析</h3>
  <p class="subtitle">MES接口可能因网络、MES系统异常、参数错误等原因失败</p>
  <div class="mockup">
    <div class="mockup-header">常见错误类型</div>
    <div class="mockup-body">
      <div class="error-list">
        <div class="error-item">
          <span class="error-icon error-icon-network">📡</span>
          <div class="error-content">
            <div class="error-title">网络超时</div>
            <div class="error-desc">MES服务器无响应或连接超时</div>
          </div>
        </div>
        <div class="error-item">
          <span class="error-icon error-icon-server">⚠️</span>
          <div class="error-content">
            <div class="error-title">MES业务错误</div>
            <div class="error-desc">托盘不存在、电芯已绑定等业务校验失败</div>
          </div>
        </div>
        <div class="error-item">
          <span class="error-icon error-icon-auth">🔒</span>
          <div class="error-content">
            <div class="error-title">认证失败</div>
            <div class="error-desc">MES接口认证信息过期或无效</div>
          </div>
        </div>
        <div class="error-item">
          <span class="error-icon error-icon-unknown">❓</span>
          <div class="error-content">
            <div class="error-title">未知错误</div>
            <div class="error-desc">MES系统返回异常或未预期的响应</div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
<div class="section">
  <h3>错误处理方式选择</h3>
  <p class="subtitle">选择适合您业务的处理策略</p>
  <div class="options">
    <div class="option" data-choice="simple" onclick="toggleSelect(this)">
      <div class="letter">A</div>
      <div class="content">
        <h3>简单提示</h3>
        <p>调用失败后弹出错误提示,用户自行决定是否重试</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>实现简单</li><li>用户完全控制</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>需要手动重试</li><li>容易遗漏</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="auto-retry" onclick="toggleSelect(this)">
      <div class="letter">B</div>
      <div class="content">
        <h3>自动重试</h3>
        <p>失败后自动重试指定次数(如3次),仍失败则提示用户</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>处理临时网络问题</li><li>提高成功率</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>可能重复执行业务</li><li>增加响应时间</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="queue" onclick="toggleSelect(this)">
      <div class="letter">C</div>
      <div class="content">
        <h3>异步队列</h3>
        <p>失败后加入重试队列,后台定时任务自动重试</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>不阻塞用户操作</li><li>可追溯重试记录</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>实现复杂</li><li>需要额外监控</li></ul></div>
        </div>
      </div>
    </div>
  </div>
</div>
<div class="section">
  <h3>错误提示展示</h3>
  <p class="subtitle">用户看到的错误信息样式</p>
  <div class="mockup">
    <div class="mockup-header">预览:错误提示样式</div>
    <div class="mockup-body">
      <div class="error-toast error-toast-error">
        <span class="error-toast-icon">✕</span>
        <div class="error-toast-content">
          <div class="error-toast-title">操作失败</div>
          <div class="error-toast-message">MES服务器连接超时,请检查网络后重试</div>
        </div>
        <button class="error-toast-retry">重试</button>
      </div>
      <div style="height: 12px;"></div>
      <div class="error-toast error-toast-warning">
        <span class="error-toast-icon">⚠</span>
        <div class="error-toast-content">
          <div class="error-toast-title">业务校验失败</div>
          <div class="error-toast-message">托盘 P001 åœ¨MES中不存在,请先在MES中创建</div>
        </div>
      </div>
    </div>
  </div>
</div>
<style>
.error-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.error-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px;
  background: #fafafa;
  border-radius: 6px;
  border: 1px solid #e0e0e0;
}
.error-icon {
  font-size: 24px;
  flex-shrink: 0;
}
.error-content {
  flex: 1;
}
.error-title {
  font-weight: 600;
  font-size: 14px;
  color: #303133;
  margin-bottom: 4px;
}
.error-desc {
  font-size: 13px;
  color: #606266;
}
.pros-cons {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}
.pros, .cons {
  flex: 1;
  font-size: 13px;
}
.pros h4 {
  color: #4caf50;
  margin: 0 0 6px 0;
}
.cons h4 {
  color: #f56c6c;
  margin: 0 0 6px 0;
}
.pros ul, .cons ul {
  margin: 0;
  padding-left: 16px;
}
.pros li, .cons li {
  margin-bottom: 4px;
}
.error-toast {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.error-toast-error {
  background: #fef0f0;
  border: 1px solid #fde2e2;
}
.error-toast-warning {
  background: #fdf6ec;
  border: 1px solid #f5dab1;
}
.error-toast-icon {
  font-size: 20px;
  flex-shrink: 0;
}
.error-toast-error .error-toast-icon {
  color: #f56c6c;
}
.error-toast-warning .error-toast-icon {
  color: #e6a23c;
}
.error-toast-content {
  flex: 1;
}
.error-toast-title {
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 4px;
}
.error-toast-error .error-toast-title {
  color: #f56c6c;
}
.error-toast-warning .error-toast-title {
  color: #e6a23c;
}
.error-toast-message {
  font-size: 13px;
  color: #606266;
}
.error-toast-retry {
  padding: 6px 16px;
  background: #1f5eff;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 13px;
  cursor: pointer;
  flex-shrink: 0;
}
</style>
Code/.superpowers/brainstorm/2159-1775998341/operations-column.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,203 @@
<h2>库存页面操作列设计</h2>
<p class="subtitle">为库存信息和库存明细页面添加MES接口操作按钮</p>
<div class="section">
  <h3>1. åº“存信息页面 - æ‰˜ç›˜çº§åˆ«æ“ä½œ</h3>
  <p class="subtitle">当前页面显示托盘编号、货位编号等信息,需要添加进站/出站操作</p>
  <div class="mockup">
    <div class="mockup-header">预览:库存信息表格</div>
    <div class="mockup-body">
      <table class="mock-table">
        <thead>
          <tr>
            <th>托盘编号</th>
            <th>货位编号</th>
            <th>仓库</th>
            <th>创建人</th>
            <th class="op-col">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>P001</td>
            <td>A01-01-01</td>
            <td>一号仓</td>
            <td>张三</td>
            <td class="op-col">
              <button class="mock-button mock-btn-sm">进站</button>
              <button class="mock-button mock-btn-sm mock-btn-primary">出站</button>
            </td>
          </tr>
          <tr>
            <td>P002</td>
            <td>A01-01-02</td>
            <td>一号仓</td>
            <td>李四</td>
            <td class="op-col">
              <button class="mock-button mock-btn-sm">进站</button>
              <button class="mock-button mock-btn-sm mock-btn-primary">出站</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
<div class="section">
  <h3>2. åº“存明细页面 - ç”µèŠ¯çº§åˆ«æ“ä½œ</h3>
  <p class="subtitle">当前页面显示物料编号、电芯码等信息,需要添加绑定/解绑/NG上报操作</p>
  <div class="mockup">
    <div class="mockup-header">预览:库存明细表格</div>
    <div class="mockup-body">
      <table class="mock-table">
        <thead>
          <tr>
            <th>物料编号</th>
            <th>物料名称</th>
            <th>电芯码</th>
            <th>库存数量</th>
            <th>状态</th>
            <th class="op-col">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>M001</td>
            <td>三元锂电池</td>
            <td>SN20240101001</td>
            <td>100</td>
            <td><span class="mock-tag mock-tag-success">正常</span></td>
            <td class="op-col">
              <button class="mock-button mock-btn-sm mock-btn-primary">绑定</button>
              <button class="mock-button mock-btn-sm">解绑</button>
              <button class="mock-button mock-btn-sm mock-btn-danger">NG上报</button>
            </td>
          </tr>
          <tr>
            <td>M001</td>
            <td>三元锂电池</td>
            <td>SN20240101002</td>
            <td>100</td>
            <td><span class="mock-tag mock-tag-warning">异常</span></td>
            <td class="op-col">
              <button class="mock-button mock-btn-sm mock-btn-primary">绑定</button>
              <button class="mock-button mock-btn-sm">解绑</button>
              <button class="mock-button mock-btn-sm mock-btn-danger">NG上报</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
<div class="section">
  <h3>按钮操作确认问题</h3>
  <p class="subtitle">MES接口调用可能失败或需要额外参数</p>
  <div class="options">
    <div class="option" data-choice="direct" onclick="toggleSelect(this)">
      <div class="letter">A</div>
      <div class="content">
        <h3>直接调用</h3>
        <p>点击按钮直接调用MES接口,成功/失败后弹出提示</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>操作简单快捷</li><li>适合高频操作</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>无法输入额外参数</li><li>错误后难以重试</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="dialog" onclick="toggleSelect(this)">
      <div class="letter">B</div>
      <div class="content">
        <h3>弹窗确认</h3>
        <p>点击后弹出确认对话框,显示参数并可修改,确认后调用</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>可预览/修改参数</li><li>防止误操作</li><li>显示详细错误信息</li></ul></div>
          <div class="cons"><h4>缺点</h4><ul><li>操作步骤多</li><li>影响效率</li></ul></div>
        </div>
      </div>
    </div>
  </div>
</div>
<style>
.mock-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.mock-table th, .mock-table td {
  border: 1px solid #e0e0e0;
  padding: 10px 12px;
  text-align: left;
}
.mock-table th {
  background: #f5f5f5;
  font-weight: 600;
  color: #333;
}
.mock-table .op-col {
  width: 180px;
  text-align: center;
}
.mock-table tbody tr:nth-child(even) {
  background: #fafafa;
}
.mock-btn-sm {
  padding: 5px 12px;
  font-size: 12px;
  margin: 0 3px;
}
.mock-btn-primary {
  background: #1f5eff;
  color: white;
  border: none;
}
.mock-btn-danger {
  background: #f56c6c;
  color: white;
  border: none;
}
.mock-tag {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 4px;
  font-size: 12px;
}
.mock-tag-success {
  background: #e1f5e1;
  color: #4caf50;
}
.mock-tag-warning {
  background: #fff3e0;
  color: #ff9800;
}
.pros-cons {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}
.pros, .cons {
  flex: 1;
  font-size: 13px;
}
.pros h4 {
  color: #4caf50;
  margin: 0 0 6px 0;
}
.cons h4 {
  color: #f56c6c;
  margin: 0 0 6px 0;
}
.pros ul, .cons ul {
  margin: 0;
  padding-left: 16px;
}
.pros li, .cons li {
  margin-bottom: 4px;
}
</style>
Code/.superpowers/brainstorm/2159-1775998341/operations-layout-v2.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,347 @@
<h2>操作列布局优化</h2>
<p class="subtitle">按钮在同一排显示,设置固定宽度防止界面变形</p>
<div class="section">
  <h3>1. åº“存信息页面 - æ‰˜ç›˜çº§åˆ«æ“ä½œ</h3>
  <p class="subtitle">操作列固定宽度,按钮横向排列</p>
  <div class="mockup">
    <div class="mockup-header">预览:库存信息表格</div>
    <div class="mockup-body">
      <table class="mock-table">
        <thead>
          <tr>
            <th style="width: 120px;">托盘编号</th>
            <th style="width: 150px;">货位编号</th>
            <th style="width: 100px;">仓库</th>
            <th style="width: 90px;">创建人</th>
            <th style="width: 200px;">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>P001</td>
            <td>A01-01-01</td>
            <td>一号仓</td>
            <td>张三</td>
            <td>
              <button class="mock-btn-inline mock-btn-primary">进站</button>
              <button class="mock-btn-inline mock-btn-success">出站</button>
            </td>
          </tr>
          <tr>
            <td>P002</td>
            <td>A01-01-02</td>
            <td>一号仓</td>
            <td>李四</td>
            <td>
              <button class="mock-btn-inline mock-btn-primary">进站</button>
              <button class="mock-btn-inline mock-btn-success">出站</button>
            </td>
          </tr>
          <tr>
            <td>P003</td>
            <td>B01-01-01</td>
            <td>二号仓</td>
            <td>王五</td>
            <td>
              <button class="mock-btn-inline mock-btn-primary">进站</button>
              <button class="mock-btn-inline mock-btn-success">出站</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
<div class="section">
  <h3>2. åº“存明细页面 - ç”µèŠ¯çº§åˆ«æ“ä½œ</h3>
  <p class="subtitle">三个按钮横向排列,操作列固定宽度</p>
  <div class="mockup">
    <div class="mockup-header">预览:库存明细表格</div>
    <div class="mockup-body">
      <table class="mock-table">
        <thead>
          <tr>
            <th style="width: 100px;">物料编号</th>
            <th style="width: 140px;">物料名称</th>
            <th style="width: 150px;">电芯码</th>
            <th style="width: 90px;">数量</th>
            <th style="width: 80px;">状态</th>
            <th style="width: 280px;">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>M001</td>
            <td>三元锂电池</td>
            <td>SN20240101001</td>
            <td>100</td>
            <td><span class="mock-tag mock-tag-success">正常</span></td>
            <td>
              <button class="mock-btn-inline mock-btn-primary">绑定</button>
              <button class="mock-btn-inline mock-btn-warning">解绑</button>
              <button class="mock-btn-inline mock-btn-danger">NG上报</button>
            </td>
          </tr>
          <tr>
            <td>M001</td>
            <td>三元锂电池</td>
            <td>SN20240101002</td>
            <td>100</td>
            <td><span class="mock-tag mock-tag-warning">异常</span></td>
            <td>
              <button class="mock-btn-inline mock-btn-primary">绑定</button>
              <button class="mock-btn-inline mock-btn-warning">解绑</button>
              <button class="mock-btn-inline mock-btn-danger">NG上报</button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
<div class="section">
  <h3>弹窗确认交互流程</h3>
  <p class="subtitle">点击按钮 â†’ å¼¹å‡ºç¡®è®¤å¯¹è¯æ¡† â†’ ç¡®è®¤åŽè°ƒç”¨MES接口</p>
  <div class="mockup">
    <div class="mockup-header">预览:确认对话框示例</div>
    <div class="mockup-body">
      <div class="mock-dialog">
        <div class="mock-dialog-header">
          <span class="mock-dialog-title">确认操作</span>
          <span class="mock-dialog-close">✕</span>
        </div>
        <div class="mock-dialog-body">
          <p class="mock-dialog-text">您即将执行 <strong>托盘进站</strong> æ“ä½œ</p>
          <div class="mock-dialog-info">
            <div class="mock-info-row">
              <span class="mock-info-label">托盘码:</span>
              <span class="mock-info-value">P001</span>
            </div>
            <div class="mock-info-row">
              <span class="mock-info-label">设备编码:</span>
              <span class="mock-info-value">WCS_001</span>
            </div>
            <div class="mock-info-row">
              <span class="mock-info-label">资源编码:</span>
              <span class="mock-info-value">RESOURCE_001</span>
            </div>
          </div>
        </div>
        <div class="mock-dialog-footer">
          <button class="mock-btn-inline mock-btn-default">取消</button>
          <button class="mock-btn-inline mock-btn-primary">确认执行</button>
        </div>
      </div>
    </div>
  </div>
</div>
<div class="section">
  <h3>下一个问题</h3>
  <p class="subtitle">关于MES接口调用的参数获取方式</p>
  <div class="options">
    <div class="option" data-choice="config" onclick="toggleSelect(this)">
      <div class="letter">A</div>
      <div class="content">
        <h3>系统配置</h3>
        <p>设备编码、资源编码等参数从系统配置中读取,用户无需输入</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>操作简单</li><li>减少人为错误</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="input" onclick="toggleSelect(this)">
      <div class="letter">B</div>
      <div class="content">
        <h3>弹窗输入</h3>
        <p>在确认对话框中显示参数输入框,允许用户修改默认值</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>灵活性高</li><li>适应不同场景</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="hybrid" onclick="toggleSelect(this)">
      <div class="letter">C</div>
      <div class="content">
        <h3>混合模式</h3>
        <p>关键参数从配置读取,可选参数在弹窗中提供输入</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>平衡简便性和灵活性</li><li>常见操作快速,特殊场景可调整</li></ul></div>
        </div>
      </div>
    </div>
  </div>
</div>
<style>
.mock-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  table-layout: fixed;
}
.mock-table th, .mock-table td {
  border: 1px solid #e0e0e0;
  padding: 10px 12px;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mock-table th {
  background: #f5f5f5;
  font-weight: 600;
  color: #333;
}
.mock-table tbody tr:nth-child(even) {
  background: #fafafa;
}
.mock-table tbody tr:hover {
  background: #f0f7ff;
}
.mock-btn-inline {
  padding: 6px 14px;
  font-size: 13px;
  border: 1px solid #dcdfe6;
  background: white;
  border-radius: 4px;
  cursor: pointer;
  margin-right: 8px;
  transition: all 0.2s;
}
.mock-btn-inline:last-child {
  margin-right: 0;
}
.mock-btn-inline:hover {
  opacity: 0.85;
}
.mock-btn-primary {
  background: #1f5eff;
  color: white;
  border-color: #1f5eff;
}
.mock-btn-success {
  background: #67c23a;
  color: white;
  border-color: #67c23a;
}
.mock-btn-warning {
  background: #e6a23c;
  color: white;
  border-color: #e6a23c;
}
.mock-btn-danger {
  background: #f56c6c;
  color: white;
  border-color: #f56c6c;
}
.mock-btn-default {
  background: white;
  color: #606266;
  border-color: #dcdfe6;
}
.mock-tag {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 4px;
  font-size: 12px;
}
.mock-tag-success {
  background: #e1f5e1;
  color: #4caf50;
}
.mock-tag-warning {
  background: #fff3e0;
  color: #ff9800;
}
.mock-dialog {
  width: 420px;
  margin: 20px auto;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  background: white;
}
.mock-dialog-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  border-bottom: 1px solid #e0e0e0;
  background: #f8fafc;
}
.mock-dialog-title {
  font-weight: 600;
  font-size: 16px;
  color: #303133;
}
.mock-dialog-close {
  cursor: pointer;
  color: #909399;
  font-size: 18px;
}
.mock-dialog-body {
  padding: 20px;
}
.mock-dialog-text {
  margin: 0 0 16px 0;
  font-size: 14px;
  color: #606266;
}
.mock-dialog-info {
  background: #f8fafc;
  border-radius: 6px;
  padding: 12px 16px;
}
.mock-info-row {
  display: flex;
  margin-bottom: 10px;
  font-size: 13px;
}
.mock-info-row:last-child {
  margin-bottom: 0;
}
.mock-info-label {
  color: #909399;
  width: 80px;
}
.mock-info-value {
  color: #303133;
  font-weight: 500;
}
.mock-dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  padding: 16px 20px;
  border-top: 1px solid #e0e0e0;
  background: #f8fafc;
}
.pros-cons {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}
.pros, .cons {
  flex: 1;
  font-size: 13px;
}
.pros h4 {
  color: #4caf50;
  margin: 0 0 6px 0;
}
.pros ul {
  margin: 0;
  padding-left: 16px;
}
.pros li {
  margin-bottom: 4px;
}
</style>
Code/.superpowers/brainstorm/2159-1775998341/status-mapping.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,213 @@
<h2>状态与操作映射规则</h2>
<p class="subtitle">定义哪些库存状态下允许执行哪些MES操作</p>
<div class="section">
  <h3>库存信息页面 - æ‰˜ç›˜çº§åˆ«</h3>
  <p class="subtitle">进站/出站操作的触发条件</p>
  <div class="mockup">
    <div class="mockup-header">状态映射表(请在浏览器中选择)</div>
    <div class="mockup-body">
      <table class="mapping-table">
        <thead>
          <tr>
            <th>库存状态</th>
            <th>进站操作</th>
            <th>出站操作</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><span class="status-tag status-idle">待入库</span></td>
            <td><input type="checkbox" checked> å…è®¸</td>
            <td><input type="checkbox"> ç¦æ­¢</td>
          </tr>
          <tr>
            <td><span class="status-tag status-instock">在库</span></td>
            <td><input type="checkbox"> ç¦æ­¢</td>
            <td><input type="checkbox" checked> å…è®¸</td>
          </tr>
          <tr>
            <td><span class="status-tag status-outbound">出库中</span></td>
            <td><input type="checkbox"> ç¦æ­¢</td>
            <td><input type="checkbox" checked> å…è®¸</td>
          </tr>
          <tr>
            <td><span class="status-tag status-lock">锁定</span></td>
            <td><input type="checkbox"> ç¦æ­¢</td>
            <td><input type="checkbox"> ç¦æ­¢</td>
          </tr>
        </tbody>
      </table>
      <p class="hint">提示:以上是默认建议,请在终端中告诉我需要调整的地方</p>
    </div>
  </div>
</div>
<div class="section">
  <h3>库存明细页面 - ç”µèŠ¯çº§åˆ«</h3>
  <p class="subtitle">绑定/解绑/NG上报操作的触发条件</p>
  <div class="mockup">
    <div class="mockup-header">状态映射表(请在浏览器中选择)</div>
    <div class="mockup-body">
      <table class="mapping-table">
        <thead>
          <tr>
            <th>电芯状态</th>
            <th>绑定操作</th>
            <th>解绑操作</th>
            <th>NG上报</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td><span class="status-tag status-normal">正常</span></td>
            <td><input type="checkbox" checked> å…è®¸</td>
            <td><input type="checkbox" checked> å…è®¸</td>
            <td><input type="checkbox" checked> å…è®¸</td>
          </tr>
          <tr>
            <td><span class="status-tag status-exception">异常</span></td>
            <td><input type="checkbox" checked> å…è®¸</td>
            <td><input type="checkbox" checked> å…è®¸</td>
            <td><input type="checkbox" checked> å…è®¸</td>
          </tr>
          <tr>
            <td><span class="status-tag status-locked">已锁定</span></td>
            <td><input type="checkbox"> ç¦æ­¢</td>
            <td><input type="checkbox"> ç¦æ­¢</td>
            <td><input type="checkbox"> ç¦æ­¢</td>
          </tr>
        </tbody>
      </table>
      <p class="hint">提示:以上是默认建议,请在终端中告诉我需要调整的地方</p>
    </div>
  </div>
</div>
<div class="section">
  <h3>下一个问题</h3>
  <p class="subtitle">是否需要记录MES接口调用日志?</p>
  <div class="options">
    <div class="option" data-choice="log-full" onclick="toggleSelect(this)">
      <div class="letter">A</div>
      <div class="content">
        <h3>完整日志</h3>
        <p>记录每次MES接口调用的完整信息(请求、响应、耗时、结果)</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>便于问题排查</li><li>支持审计追溯</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="log-simple" onclick="toggleSelect(this)">
      <div class="letter">B</div>
      <div class="content">
        <h3>简单日志</h3>
        <p>仅记录调用成功/失败状态和错误信息</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>存储占用小</li><li>满足基本需求</li></ul></div>
        </div>
      </div>
    </div>
    <div class="option" data-choice="log-none" onclick="toggleSelect(this)">
      <div class="letter">C</div>
      <div class="content">
        <h3>不记录</h3>
        <p>不单独记录日志,仅依赖系统日志和MES系统日志</p>
        <div class="pros-cons">
          <div class="pros"><h4>优点</h4><ul><li>无额外存储</li><li>实现简单</li></ul></div>
        </div>
      </div>
    </div>
  </div>
</div>
<style>
.mapping-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.mapping-table th, .mapping-table td {
  border: 1px solid #e0e0e0;
  padding: 10px 12px;
  text-align: center;
}
.mapping-table th {
  background: #f5f5f5;
  font-weight: 600;
  color: #303133;
}
.mapping-table tbody tr:nth-child(even) {
  background: #fafafa;
}
.mapping-table input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
}
.status-tag {
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
}
.status-idle {
  background: #e3f2fd;
  color: #1976d2;
}
.status-instock {
  background: #e8f5e9;
  color: #388e3c;
}
.status-outbound {
  background: #fff3e0;
  color: #f57c00;
}
.status-lock {
  background: #ffebee;
  color: #d32f2f;
}
.status-normal {
  background: #e8f5e9;
  color: #388e3c;
}
.status-exception {
  background: #fff3e0;
  color: #f57c00;
}
.status-locked {
  background: #ffebee;
  color: #d32f2f;
}
.hint {
  margin: 12px 0 0 0;
  padding: 10px 12px;
  background: #f0f7ff;
  border-left: 3px solid #1f5eff;
  font-size: 12px;
  color: #606266;
}
.pros-cons {
  display: flex;
  gap: 16px;
  margin-top: 12px;
}
.pros, .cons {
  flex: 1;
  font-size: 13px;
}
.pros h4 {
  color: #4caf50;
  margin: 0 0 6px 0;
}
.pros ul {
  margin: 0;
  padding-left: 16px;
}
.pros li {
  margin-bottom: 4px;
}
</style>
Code/.superpowers/brainstorm/2159-1775998341/waiting.html
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
<div style="display:flex;align-items:center;justify-content:center;min-height:60vh">
  <p class="subtitle">正在生成设计方案...</p>
</div>
Code/CLAUDE.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,132 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## é¡¹ç›®æ¦‚è¿°
**WIDESEA** æ˜¯ä¸€ä¸ªå·¥ä¸šä»“储自动化系统,包含两个核心子系统:
- **WCS**(仓库控制系统):直接控制堆垛机、输送线、机器人、穿梭车等物理设备,与 PLC é€šä¿¡
- **WMS**(仓库管理系统):管理库存、入库、出库任务、盘点等业务逻辑
两个系统通过 HTTP API äº¤äº’,WMS ä¾èµ– WCS å®Œæˆè®¾å¤‡è°ƒåº¦ã€‚
## æž„建命令
```bash
# WCS åŽç«¯ï¼ˆç«¯å£ 9292)
dotnet build WCS/WIDESEAWCS_Server/WIDESEAWCS_Server.sln
dotnet run --project WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/WIDESEAWCS_Server.csproj
# WMS åŽç«¯ï¼ˆç«¯å£ 9291)
dotnet build WMS/WIDESEA_WMSServer/WIDESEA_WMSServer.sln
dotnet run --project WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj
# WCS å‰ç«¯
cd WCS/WIDESEAWCS_Client && npm run serve
# WMS å‰ç«¯
cd WMS/WIDESEA_WMSClient && npm run serve
# S7 æ¨¡æ‹Ÿå™¨
cd æµ‹è¯•工具/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web && npm run dev
```
## æž¶æž„概览
```
┌─────────────────────────────────────────────────────────────┐
│                        WCS (端口 9292)                       â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”‚
│  â”‚   Quartz    â”‚  â”‚  Communicator â”‚  â”‚   Redis L1+L2      â”‚  â”‚
│  â”‚   Jobs      â”‚→ â”‚  (PLC协议)    â”‚  â”‚   Cache            â”‚  â”‚
│  â””─────────────┘  â””──────────────┘  â””────────────────────┘  â”‚
│         â†“                                                    â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”     â”‚
│  â”‚  TcpSocketServer (端口 2000) / WebSocket (端口 9296) â”‚     â”‚
│  â””─────────────────────────────────────────────────────┘     â”‚
└─────────────────────────────────────────────────────────────┘
                              â†‘ HTTP API
                              â†“
┌─────────────────────────────────────────────────────────────┐
│                        WMS (端口 9291)                       â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”‚
│  â”‚   Tasks     â”‚  â”‚    Stock     â”‚  â”‚    Inbound/        â”‚  â”‚
│  â”‚   Service   â”‚  â”‚    Service   â”‚  â”‚    Outbound        â”‚  â”‚
│  â””─────────────┘  â””──────────────┘  â””────────────────────┘  â”‚
└─────────────────────────────────────────────────────────────┘
```
## WCS é¡¹ç›®ç»“æž„
| é¡¹ç›® | ç”¨é€” |
|------|------|
| `WIDESEAWCS_Server` | ä¸» API å…¥å£ï¼ˆç«¯å£ 9292) |
| `WIDESEAWCS_Core` | æ¡†æž¶ï¼ˆæ•°æ®åº“、缓存、DI、日志) |
| `WIDESEAWCS_Common` | å¸¸é‡ã€æžšä¸¾ã€Redis é”® |
| `WIDESEAWCS_Model` | å®žä½“模型 |
| `WIDESEAWCS_DTO` | æ•°æ®ä¼ è¾“对象 |
| `WIDESEAWCS_Communicator` | å·¥ä¸šåè®®ï¼ˆModbus、S7、Omron ç­‰ï¼‰ |
| `WIDESEAWCS_Tasks` | åŽå°è®¾å¤‡æŽ§åˆ¶ä½œä¸š |
| `WIDESEAWCS_QuartzJob` | Quartz.NET è°ƒåº¦ |
**详细 WCS è§„范**:见 `WCS/WIDESEAWCS_Server/CLAUDE.md`
## WMS é¡¹ç›®ç»“æž„
| é¡¹ç›® | ç”¨é€” |
|------|------|
| `WIDESEA_WMSServer` | ä¸» API å…¥å£ï¼ˆç«¯å£ 9291) |
| `WIDESEA_Core` | æ¡†æž¶å·¥å…· |
| `WIDESEA_Common` | å¸¸é‡ã€æžšä¸¾ |
| `WIDESEA_Model` | å®žä½“模型 |
| `WIDESEA_*Service` | ä¸šåŠ¡é€»è¾‘æœåŠ¡ |
## å…³é”®è®¾è®¡
- **ORM**:SqlSugar(支持多数据库)
- **依赖注入**:Autofac,通过 `AutofacModuleRegister` æ³¨å†Œ
- **缓存**:Redis L1(内存)+ L2(Redis)混合缓存
- **调度**:Quartz.NET
- **实时通信**:SignalR WebSocket
- **设备协议**:存储在数据库 `Dt_DeviceProtocol` è¡¨ï¼Œéžé…ç½®æ–‡ä»¶
## æŠ€æœ¯æ ˆ
- **后端**:.NET 6/8,ASP.NET Core,C# 12
- **前端**:Vue 3,Element Plus,Vite,Pinia
- **数据库**:SQL Server
- **缓存**:Redis
## æ³¨é‡Šä¸Žæ–‡æ¡£ (强制)
- **XML æ–‡æ¡£æ³¨é‡Š**: æ‰€æœ‰ `public` ç±»ã€æŽ¥å£ã€æ–¹æ³•、属性**必须**包含 XML æ–‡æ¡£æ³¨é‡Š (`/// <summary>...</summary>`),解释其用途、参数和返回值。
- **行内注释**: å¯¹äºŽå¤æ‚的业务逻辑、算法实现或非直观的代码块,**必须**添加 `//` è¡Œå†…注释解释“为什么这么做”。
- **TODO æ ‡è®°**: å¦‚果代码未完成或有临时方案,必须使用 `// TODO: è¯´æ˜Ž` æ ‡è®°ã€‚
## é€šç”¨è§„范
- **异步编程**: æ‰€æœ‰ I/O æ“ä½œå¿…须使用 `async/await`。库代码请使用 `ConfigureAwait(false)`。
- **命名**:
  - æŽ¥å£ä»¥ "I" å¼€å¤´ (例如: `IUserService`)。
  - ç±»åã€æ–¹æ³•名使用 **PascalCase**。
  - ç§æœ‰å­—段、局部变量使用 **camelCase**。
- **命名空间**: ä½¿ç”¨ **文件作用域命名空间** (`namespace MyApp.Api;`)。
- å½“需要对两个或以上的数据库执行修改/添加操作并保证事务一致性时,请使用工作单元管理器(`IUnitOfWorkManage`)的事务方法:
  - **同步场景**:调用 `BeginTran(Func<WebResponseContent> func)`,传入同步委托。
  - **异步场景**:调用 `BeginTranAsync(Func<Task<WebResponseContent>> funcAsync)`,传入异步委托。
  **注意**:`IUnitOfWorkManage` éœ€é€šè¿‡ä¾èµ–注入(DI)获取。该方法会自动管理事务的提交与回滚,只需确保所有数据库操作在传入的委托内完成。
## ðŸš« ä¸¥ç¦äº‹é¡¹
- **严禁** ç”Ÿæˆæ²¡æœ‰æ³¨é‡Šçš„代码 (尤其是公共方法)。
- **严禁** ä½¿ç”¨ `Task.Result` æˆ– `Task.Wait()`。
- **严禁** åœ¨å¼‚步上下文中使用 `.ToList()` (必须用 `.ToListAsync()`)。
- **严禁** ç›´æŽ¥æš´éœ²å®žä½“ (Entity),必须使用 DTO。
- **严禁** æ•获 `Exception` è€Œä¸è®°å½•日志。
Code/WCS/WIDESEAWCS_Client/.omc/state/idle-notif-cooldown.json
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
{
  "lastSentAt": "2026-04-12T15:31:49.457Z"
}
Code/WCS/WIDESEAWCS_Client/package.json
@@ -25,6 +25,7 @@
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "@vitejs/plugin-vue-jsx": "^5.1.5",
    "@vue/compiler-sfc": "^3.0.0",
    "eslint": "^8.50.0",
    "less": "^4.1.1",
Code/WCS/WIDESEAWCS_Client/src/extension/basicinfo/router.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceInfo.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.js
ÎļþÒÑɾ³ý
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.jsx
copy from Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js copy to Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocolDetail.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.js
ÎļþÒÑɾ³ý
Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.jsx
copy from Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/deviceProtocol.js copy to Code/WCS/WIDESEAWCS_Client/src/extension/quartzJob/dispatchInfo.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Dictionary.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_DictionaryList.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Log.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Role1.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_Tenant.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/Sys_User.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/system/system/Sys_Department.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/robotTask.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/task.jsx
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.js
ÎļþÒÑɾ³ý
Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.jsx
copy from Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/robotTask.js copy to Code/WCS/WIDESEAWCS_Client/src/extension/taskinfo/taskHty.jsx
Code/WCS/WIDESEAWCS_Client/src/views/basicinfo/router.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/basicinfo/router.js";
import extend from "@/extension/basicinfo/router.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceInfo.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/quartzJob/deviceInfo.js";
import extend from "@/extension/quartzJob/deviceInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocol.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
  <script>
import extend from "@/extension/quartzJob/deviceProtocol.js";
import extend from "@/extension/quartzJob/deviceProtocol.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/deviceProtocolDetail.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
  <script>
import extend from "@/extension/quartzJob/deviceProtocolDetail.js";
import extend from "@/extension/quartzJob/deviceProtocolDetail.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/quartzJob/dispatchInfo.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
  <script>
import extend from "@/extension/quartzJob/dispatchInfo.js";
import extend from "@/extension/quartzJob/dispatchInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Dictionary.vue
@@ -19,7 +19,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Dictionary.js";
import extend from "@/extension/system/Sys_Dictionary.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_DictionaryList.vue
@@ -19,7 +19,7 @@
</template>
<script>
    import extend from "@/extension/system/Sys_DictionaryList.js";
    import extend from "@/extension/system/Sys_DictionaryList.jsx";
    var vueParam = {
        data() {
            return {
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Log.vue
@@ -19,7 +19,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Log.js";
import extend from "@/extension/system/Sys_Log.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role.vue
@@ -19,7 +19,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Role.js";
import extend from "@/extension/system/Sys_Role.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Role1.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/system/Sys_Role1.js";
    import extend from "@/extension/system/Sys_Role1.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_Tenant.vue
@@ -13,7 +13,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Tenant.js";
import extend from "@/extension/system/Sys_Tenant.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
Code/WCS/WIDESEAWCS_Client/src/views/system/Sys_User.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_User.js";
import extend from "@/extension/system/Sys_User.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/system/system/Sys_Department.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/system/system/Sys_Department.js";
    import extend from "@/extension/system/system/Sys_Department.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/robotTask.vue
@@ -14,7 +14,7 @@
</template>
<script>
import extend from "@/extension/taskinfo/robotTask.js";
import extend from "@/extension/taskinfo/robotTask.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/task.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
  <script>
import extend from "@/extension/taskinfo/task.js";
import extend from "@/extension/taskinfo/task.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WCS/WIDESEAWCS_Client/src/views/taskinfo/taskHty.vue
@@ -13,7 +13,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/taskinfo/taskHty.js";
import extend from "@/extension/taskinfo/taskHty.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
Code/WCS/WIDESEAWCS_Client/vite.config.mjs
ÎļþÃû´Ó Code/WCS/WIDESEAWCS_Client/vite.config.js ÐÞ¸Ä
@@ -1,11 +1,12 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { fileURLToPath, URL } from 'node:url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
export default defineConfig({
  plugins: [vue()],
  plugins: [vue(), vueJsx()],
  
  server: {
    host: '0.0.0.0',
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/BaseRepository/UnitOfWorks/IUnitOfWorkManage.cs
@@ -1,10 +1,5 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEAWCS_Core.BaseRepository
{
@@ -15,6 +10,7 @@
        /// </summary>
        /// <returns>SqlSugarClient数据库客户端对象</returns>
        SqlSugarClient GetDbClient();
        int TranCount { get; }
        /// <summary>
@@ -27,28 +23,40 @@
        /// å¼€å§‹ä¸€ä¸ªäº‹åŠ¡
        /// </summary>
        void BeginTran();
        /// <summary>
        /// å¼€å§‹ä¸€ä¸ªäº‹åŠ¡
        /// </summary>
        /// <param name="method">触发事务的方法信息</param>
        void BeginTran(MethodInfo method);
        /// <summary>
        /// å¼€å§‹ä¸€ä¸ªäº‹åŠ¡ï¼Œå¹¶æ‰§è¡Œæä¾›çš„å‡½æ•°
        /// </summary>
        /// <param name="func">委托方法</param>
        /// <returns></returns>
        WebResponseContent BeginTran(Func<WebResponseContent> func);
        /// <summary>
        /// æäº¤å½“前事务
        /// </summary>
        void CommitTran();
        /// <summary>
        /// æäº¤äº‹åŠ¡
        /// </summary>
        /// <param name="method">触发提交事务的方法信息</param>
        void CommitTran(MethodInfo method);
        /// <summary>
        /// å›žæ»šå½“前事务
        /// </summary>
        void RollbackTran();
        /// <summary>
        /// å›žæ»šå½“前事务
        /// </summary>
        /// <param name="method">触发回滚的方法信息</param>
        void RollbackTran(MethodInfo method);
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Core/BaseRepository/UnitOfWorks/UnitOfWorkManage.cs
@@ -1,13 +1,7 @@
using Microsoft.Extensions.Logging;
using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WIDESEAWCS_Core.Helper;
namespace WIDESEAWCS_Core.BaseRepository
@@ -16,13 +10,16 @@
    {
        // å®šä¹‰æ—¥å¿—记录器
        private readonly ILogger<UnitOfWorkManage> _logger;
        // å®šä¹‰SqlSugarClient
        private readonly ISqlSugarClient _sqlSugarClient;
        // å®šä¹‰äº‹åŠ¡è®¡æ•°å™¨
        private int _tranCount { get; set; }
        // å®šä¹‰äº‹åŠ¡è®¡æ•°å™¨çš„åªè¯»å±žæ€§
        public int TranCount => _tranCount;
        // å®šä¹‰äº‹åŠ¡æ ˆ
        public readonly ConcurrentStack<string> TranStack = new();
@@ -44,7 +41,6 @@
            return _sqlSugarClient as SqlSugarClient;
        }
        // åˆ›å»ºUnitOfWork
        public UnitOfWork CreateUnitOfWork()
        {
@@ -56,7 +52,7 @@
            uow.Db.Open();
            uow.Tenant.BeginTran();
            _logger.LogDebug("UnitOfWork Begin");
            return uow;
        }
@@ -101,7 +97,7 @@
                    }
                    return content;
                }
                catch(Exception ex)
                catch (Exception ex)
                {
                    RollbackTran();
                    return WebResponseContent.Instance.Error(ex.Message);
@@ -141,7 +137,6 @@
                {
                    spinner.SpinOnce();
                }
                if (result == method.GetFullName())
                {
@@ -209,4 +204,4 @@
            }
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/ReceiveTaskResultDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
namespace WIDESEAWCS_DTO.TaskInfo
{
    /// <summary>
    /// WCS接收WMS任务的结果Dto
    /// </summary>
    public class ReceiveTaskResultDto
    {
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        public bool Success { get; set; }
        /// <summary>
        /// æ¶ˆæ¯
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        /// æ–°å»ºçš„任务数量
        /// </summary>
        public int CreatedCount { get; set; }
        /// <summary>
        /// é‡å¤/已存在的任务列表(只包含任务号和托盘号)
        /// </summary>
        public List<DuplicateTaskDto> DuplicateTasks { get; set; }
    }
    /// <summary>
    /// é‡å¤ä»»åŠ¡ä¿¡æ¯Dto
    /// </summary>
    public class DuplicateTaskDto
    {
        /// <summary>
        /// ä»»åŠ¡å·
        /// </summary>
        public int TaskNum { get; set; }
        /// <summary>
        /// æ‰˜ç›˜å·
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// ä»»åŠ¡ç±»åž‹
        /// </summary>
        public int TaskType { get; set; }
        /// <summary>
        /// å½“前状态
        /// </summary>
        public int TaskStatus { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_DTO/TaskInfo/UpdateTaskDto.cs
@@ -20,5 +20,16 @@
        /// newStatus - æ–°çŠ¶æ€ï¼Œå¿…å¡«é¡¹ï¼Œç”¨äºŽæŒ‡å®šä»»åŠ¡çš„æ–°çŠ¶æ€
        /// </summary>
        public int NewStatus { get; set; }
        /// <summary>
        /// NextAddress - ä¸‹ä¸€åœ°å€ï¼Œå¿…填项,用于指定任务的下一位置
        /// </summary>
        public string? NextAddress { get; set; }
        /// <summary>
        /// currentAddress - å½“前地址,必填项,用于指定任务的当前位置
        /// </summary>
        public string? CurrentAddress { get; set; }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_ITaskInfoService/ITaskService.cs
@@ -61,6 +61,20 @@
        WebResponseContent ReceiveWMSTask([NotNull] List<WMSTaskDTO> taskDTOs);
        /// <summary>
        /// æŽ¥æ”¶WMS手动创建的任务,创建WCS任务
        /// </summary>
        /// <param name="taskDTOs">WMS任务对象集合</param>
        /// <returns>返回处理结果</returns>
        WebResponseContent ReceiveManualTask([NotNull] List<WMSTaskDTO> taskDTOs);
        /// <summary>
        /// æŸ¥è¯¢æŒ‡å®šèµ·ç‚¹åœ°å€çš„æ–°å»ºæ‰‹åŠ¨å…¥åº“ä»»åŠ¡
        /// </summary>
        /// <param name="sourceAddress">起点地址</param>
        /// <returns>任务列表</returns>
        Dt_Task QueryManualInboundTask(string sourceAddress);
        /// <summary>
        /// æ ¹æ®æ‰˜ç›˜å·ã€èµ·å§‹åœ°å€å‘WMS请求任务
        /// </summary>
        /// <param name="palletCode">托盘号</param>
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/TaskController.cs
@@ -25,6 +25,12 @@
            return Service.ReceiveWMSTask(taskDTOs);
        }
        [HttpPost, Route("ReceiveManualTask"), AllowAnonymous]
        public WebResponseContent ReceiveManualTask([FromBody] List<WMSTaskDTO> taskDTOs)
        {
            return Service.ReceiveManualTask(taskDTOs);
        }
        [HttpPost, HttpGet(), Route("UpdateTaskExceptionMessage")]
        public WebResponseContent UpdateTaskExceptionMessage(int taskNum, string message)
        {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs
@@ -137,7 +137,7 @@
        {
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.UpdateTaskByStatus),
                new UpdateTaskDto { Id = task.TaskNum, NewStatus = task.TaskStatus }.ToJson());
                new UpdateTaskDto { Id = task.TaskNum, NewStatus = task.TaskStatus, NextAddress = task.NextAddress, CurrentAddress = task.CurrentAddress }.ToJson());
            if (!result.IsSuccess || !result.Data.Status)
                return WebResponseContent.Instance.Error($"调用WMS接口更新任务状态失败,任务号:【{task.TaskNum}】,错误信息:【{result.Data?.Message}】");
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/OutboundTaskFlowService.cs
@@ -1,18 +1,18 @@
using Newtonsoft.Json;
using Autofac;
using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
using WIDESEA_Core;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.BaseRepository;
using WIDESEAWCS_Core.Enums;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO;
using WIDESEAWCS_DTO.Stock;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.Models;
using WIDESEAWCS_QuartzJob.Service;
@@ -27,8 +27,8 @@
        private readonly IRouterService _routerService;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IRobotTaskService _robotTaskService;
        private readonly ITaskService _taskService;
        private readonly ITaskRepository _taskRepository;
        private readonly IComponentContext _componentContext;
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        /// <summary>
        /// åˆå§‹åŒ–出库任务流程服务。
@@ -36,16 +36,20 @@
        /// <param name="routerService">路由服务。</param>
        /// <param name="httpClientHelper">WMS接口调用帮助类。</param>
        /// <param name="robotTaskService">机械手任务服务。</param>
        /// <param name="taskService">任务服务(用于接收入库任务)。</param>
        /// <param name="taskRepository">任务仓储(用于删除出库任务)。</param>
        public OutboundTaskFlowService(IRouterService routerService, HttpClientHelper httpClientHelper, IRobotTaskService robotTaskService, ITaskService taskService, ITaskRepository taskRepository)
        /// <param name="componentContext">Autofac组件上下文(用于延迟解析ITaskService以避免循环依赖)。</param>
        public OutboundTaskFlowService(IRouterService routerService, HttpClientHelper httpClientHelper, IRobotTaskService robotTaskService, IComponentContext componentContext, IUnitOfWorkManage unitOfWorkManage)
        {
            _routerService = routerService;
            _httpClientHelper = httpClientHelper;
            _robotTaskService = robotTaskService;
            _taskService = taskService;
            _taskRepository = taskRepository;
            _componentContext = componentContext;
            _unitOfWorkManage = unitOfWorkManage;
        }
        /// <summary>
        /// å»¶è¿Ÿè§£æžITaskService以避免循环依赖
        /// </summary>
        private ITaskService TaskService => _componentContext.Resolve<ITaskService>();
        /// <summary>
        /// æŽ¥æ”¶WMS任务时初始化出库任务。
@@ -148,15 +152,23 @@
                if (inboundTaskDto != null)
                {
                    // å…ˆåˆ é™¤æœ¬åœ°å‡ºåº“任务,避免托盘号唯一键冲突
                    _taskRepository.DeleteAndMoveIntoHty(task, OperateTypeEnum.自动完成);
                    // è°ƒç”¨ReceiveWMSTask创建本地入库任务
                    var receiveResult = _taskService.ReceiveWMSTask(new List<WMSTaskDTO> { inboundTaskDto });
                    if (!receiveResult.Status)
                    _unitOfWorkManage.BeginTran(() =>
                    {
                        return content.Error($"创建本地入库任务失败: {receiveResult.Message}");
                    }
                        // å…ˆåˆ é™¤æœ¬åœ°å‡ºåº“任务,避免托盘号唯一键冲突
                        bool isDeleted = TaskService.Repository.DeleteAndMoveIntoHty(task, OperateTypeEnum.自动完成);
                        if (!isDeleted)
                        {
                            return content.Error($"删除本地出库任务失败,任务号:【{task.TaskNum}】,托盘号:【{task.PalletCode}】");
                        }
                        // è°ƒç”¨ReceiveWMSTask创建本地入库任务
                        var receiveResult = TaskService.ReceiveWMSTask(new List<WMSTaskDTO> { inboundTaskDto });
                        if (!receiveResult.Status)
                        {
                            return content.Error($"创建本地入库任务失败: {receiveResult.Message}");
                        }
                        return content.OK("创建本地入库任务成功");
                    });
                }
            }
@@ -213,7 +225,7 @@
        {
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.UpdateTaskByStatus),
                new UpdateTaskDto { Id = task.TaskNum, NewStatus = task.TaskStatus }.ToJson());
                new UpdateTaskDto { Id = task.TaskNum, NewStatus = task.TaskStatus, NextAddress = task.NextAddress, CurrentAddress = task.CurrentAddress }.ToJson());
            if (!result.IsSuccess || !result.Data.Status)
                return WebResponseContent.Instance.Error($"调用WMS接口更新任务状态失败,任务号:【{task.TaskNum}】,错误信息:【{result.Data?.Message}】");
@@ -238,4 +250,4 @@
            return WebResponseContent.Instance.OK();
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/RelocationTaskFlowService.cs
@@ -102,7 +102,7 @@
        {
            var result = _httpClientHelper.Post<WebResponseContent>(
                nameof(ConfigKey.UpdateTaskByStatus),
                new UpdateTaskDto { Id = task.TaskNum, NewStatus = task.TaskStatus }.ToJson());
                new UpdateTaskDto { Id = task.TaskNum, NewStatus = task.TaskStatus, NextAddress = task.NextAddress, CurrentAddress = task.CurrentAddress }.ToJson());
            if (!result.IsSuccess || !result.Data.Status)
                return WebResponseContent.Instance.Error($"调用WMS接口更新任务状态失败,任务号:【{task.TaskNum}】,错误信息:【{result.Data?.Message}】");
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/RobotTaskFlowService.cs
@@ -1,7 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
@@ -53,4 +52,4 @@
            return WebResponseContent.Instance.OK();
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
@@ -644,6 +644,74 @@
        {
            return BaseDal.QueryFirst(x => x.TaskNum == taskNum);
        }
        /// <summary>
        /// æŽ¥æ”¶WMS手动创建的任务,创建WCS任务
        /// </summary>
        /// <param name="taskDTOs">WMS任务对象集合</param>
        /// <returns>返回处理结果</returns>
        public WebResponseContent ReceiveManualTask([NotNull] List<WMSTaskDTO> taskDTOs)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                // æ”¶é›†é‡å¤ä»»åŠ¡çš„ä¿¡æ¯
                var duplicateTasks = new List<DuplicateTaskDto>();
                // å€’序遍历,安全删除并收集被移除的项
                for (int i = taskDTOs.Count - 1; i >= 0; i--)
                {
                    var item = taskDTOs[i];
                    var exists = BaseDal.QueryFirst(x => x.TaskNum == item.TaskNum || x.PalletCode == item.PalletCode);
                    if (exists != null)
                    {
                        duplicateTasks.Add(new DuplicateTaskDto
                        {
                            TaskNum = exists.TaskNum,
                            PalletCode = exists.PalletCode,
                            TaskType = exists.TaskType,
                            TaskStatus = exists.TaskStatus
                        });
                        taskDTOs.RemoveAt(i);
                    }
                }
                // è°ƒç”¨ ReceiveWMSTask åˆ›å»º WCS ä»»åŠ¡
                content = ReceiveWMSTask(taskDTOs);
                // å¦‚果有重复任务,修改返回结果
                if (duplicateTasks.Count > 0 && content.Status)
                {
                    var result = new ReceiveTaskResultDto
                    {
                        Success = true,
                        Message = content.Message + $",其中{duplicateTasks.Count}个任务在WCS中已存在",
                        CreatedCount = taskDTOs.Count,
                        DuplicateTasks = duplicateTasks
                    };
                    content.Data = result;
                }
                return content;
            }
            catch (Exception ex)
            {
                content = WebResponseContent.Instance.Error($"手动任务接收错误,错误信息:{ex.Message}");
                return content;
            }
        }
        /// <summary>
        /// æŸ¥è¯¢æŒ‡å®šèµ·ç‚¹åœ°å€çš„æ–°å»ºæ‰‹åŠ¨å…¥åº“ä»»åŠ¡
        /// </summary>
        /// <param name="sourceAddress">起点地址</param>
        /// <returns>任务实体</returns>
        public Dt_Task QueryManualInboundTask(string sourceAddress)
        {
            return BaseDal.QueryFirst(x =>
                x.TaskType == (int)TaskInboundTypeEnum.Inbound &&
                x.TaskStatus == (int)TaskInStatusEnum.InNew &&
                x.SourceAddress == sourceAddress);
        }
    }
    public enum ConveyorLineDBNameNew
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -14,6 +14,7 @@
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.Service;
using ManualInboundTaskHandler = WIDESEAWCS_Tasks.ConveyorLineNewJob.ManualInbound.ManualInboundTaskHandler;
namespace WIDESEAWCS_Tasks
{
@@ -212,6 +213,22 @@
                                // å¦‚æžœ WCS_ACK ä¸º 1,先清除(表示处理过上一次请求)
                                if (command.WCS_ACK == 1)
                                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)0, childDeviceCode);
                                // å¤„理手动入库任务(起点为线体点位的任务)
                                try
                                {
                                    var task = _taskService.QueryManualInboundTask(childDeviceCode);
                                    if (task != null)
                                    {
                                        var handler = new ManualInboundTaskHandler(_taskService);
                                        handler.WriteTaskToPlc(conveyorLine, childDeviceCode, task);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    _logger.LogError(ex, "处理手动入库任务异常");
                                    QuartzLogger.Error($"处理手动入库任务异常: {ex.Message}", "CommonConveyorLineNewJob", ex);
                                }
                                continue;
                            }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -210,14 +210,13 @@
                // æ›´æ–°ä»»åŠ¡çŠ¶æ€åˆ°ä¸‹ä¸€é˜¶æ®µï¼ˆé€šå¸¸æ˜¯å®Œæˆï¼‰
                if (_taskService.UpdateTaskStatusToNext(task).Status)
                {
                    // å›žå¤ ACK ç¡®è®¤
                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                    _logger.LogInformation("ConveyorLineInFinish:入库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
                    QuartzLogger.Info($"入库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
                }
                // å›žå¤ ACK ç¡®è®¤
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                _logger.LogInformation("ConveyorLineInFinish:入库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", task.TaskNum, childDeviceCode);
                QuartzLogger.Info($"入库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode);
            }
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ManualInbound/ManualInboundTaskHandler.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.ConveyorLine;
namespace WIDESEAWCS_Tasks.ConveyorLineNewJob.ManualInbound
{
    /// <summary>
    /// æ‰‹åŠ¨å…¥åº“ä»»åŠ¡å¤„ç†å™¨
    /// </summary>
    /// <remarks>
    /// è´Ÿè´£å¤„理手动创建的入库任务,当PLC请求入库时,根据childDeviceCode查找任务并写入PLC。
    /// </remarks>
    public class ManualInboundTaskHandler
    {
        /// <summary>
        /// ä»»åŠ¡æœåŠ¡
        /// </summary>
        private readonly ITaskService _taskService;
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="taskService">任务服务</param>
        public ManualInboundTaskHandler(ITaskService taskService)
        {
            _taskService = taskService;
        }
        /// <summary>
        /// å†™å…¥æ‰‹åŠ¨å…¥åº“ä»»åŠ¡åˆ°PLC
        /// </summary>
        /// <param name="conveyorLine">输送线设备对象</param>
        /// <param name="childDeviceCode">子设备编码</param>
        /// <param name="task">任务实体</param>
        public void WriteTaskToPlc(CommonConveyorLine conveyorLine, string childDeviceCode, Dt_Task task)
        {
            if (conveyorLine == null || string.IsNullOrEmpty(childDeviceCode) || task == null)
            {
                QuartzLogger.Error("ManualInboundTaskHandler.WriteTaskToPlc: å‚数为空", "ManualInbound");
                return;
            }
            try
            {
                // å†™å…¥ä»»åŠ¡å·
                conveyorLine.SetValue(ConveyorLineDBNameNew.TaskNo, (short)task.TaskNum, childDeviceCode);
                // å†™å…¥èµ·å§‹åœ°å€
                conveyorLine.SetValue(ConveyorLineDBNameNew.Source, short.Parse(task.SourceAddress ?? "0"), childDeviceCode);
                // å†™å…¥ç›®æ ‡åœ°å€
                conveyorLine.SetValue(ConveyorLineDBNameNew.Target, short.Parse(task.NextAddress ?? "0"), childDeviceCode);
                // æ›´æ–°ä»»åŠ¡çŠ¶æ€åˆ°ä¸‹ä¸€é˜¶æ®µ
                var updateResult = _taskService.UpdateTaskStatusToNext(task);
                if (!updateResult.Status)
                {
                    QuartzLogger.Error($"ManualInboundTaskHandler: æ›´æ–°ä»»åŠ¡çŠ¶æ€å¤±è´¥ï¼Œä»»åŠ¡å·ã€{task.TaskNum}】,错误信息:{updateResult.Message}", conveyorLine.DeviceCode);
                    return;
                }
                // å†™å…¥ACK标志
                conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                QuartzLogger.Info($"ManualInboundTaskHandler: æ‰‹åŠ¨ä»»åŠ¡å†™å…¥PLC成功,任务号【{task.TaskNum}】,源地址【{task.SourceAddress}】", conveyorLine.DeviceCode);
            }
            catch (Exception ex)
            {
                QuartzLogger.Error($"ManualInboundTaskHandler: å†™å…¥è¾“送线任务异常,错误信息:{ex.Message}", "ManualInbound");
            }
        }
    }
}
Code/WMS/WIDESEA_WMSClient/package.json
@@ -31,6 +31,7 @@
  "devDependencies": {
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@vitejs/plugin-vue": "^4.0.0",
    "@vitejs/plugin-vue-jsx": "^5.1.5",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/test-utils": "^2.0.0-0",
    "chai": "^4.1.2",
Code/WMS/WIDESEA_WMSClient/pnpm-lock.yaml
@@ -66,6 +66,9 @@
      '@vitejs/plugin-vue':
        specifier: ^4.0.0
        version: 4.6.2(vite@5.4.21(@types/node@25.5.0)(less@4.6.4)(sass@1.98.0)(stylus@0.54.8)(terser@5.46.1))(vue@3.5.30)
      '@vitejs/plugin-vue-jsx':
        specifier: ^5.1.5
        version: 5.1.5(vite@5.4.21(@types/node@25.5.0)(less@4.6.4)(sass@1.98.0)(stylus@0.54.8)(terser@5.46.1))(vue@3.5.30)
      '@vue/compiler-sfc':
        specifier: ^3.0.0
        version: 3.5.30
@@ -115,12 +118,26 @@
    resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-annotate-as-pure@7.27.3':
    resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-compilation-targets@7.28.6':
    resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-create-class-features-plugin@7.28.6':
    resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==}
    engines: {node: '>=6.9.0'}
    peerDependencies:
      '@babel/core': ^7.0.0
  '@babel/helper-globals@7.28.0':
    resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-member-expression-to-functions@7.28.5':
    resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-module-imports@7.28.6':
@@ -133,8 +150,22 @@
    peerDependencies:
      '@babel/core': ^7.0.0
  '@babel/helper-optimise-call-expression@7.27.1':
    resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-plugin-utils@7.28.6':
    resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-replace-supers@7.28.6':
    resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==}
    engines: {node: '>=6.9.0'}
    peerDependencies:
      '@babel/core': ^7.0.0
  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
    resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
    engines: {node: '>=6.9.0'}
  '@babel/helper-string-parser@7.27.1':
@@ -160,6 +191,24 @@
  '@babel/plugin-syntax-dynamic-import@7.8.3':
    resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
    peerDependencies:
      '@babel/core': ^7.0.0-0
  '@babel/plugin-syntax-jsx@7.28.6':
    resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==}
    engines: {node: '>=6.9.0'}
    peerDependencies:
      '@babel/core': ^7.0.0-0
  '@babel/plugin-syntax-typescript@7.28.6':
    resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==}
    engines: {node: '>=6.9.0'}
    peerDependencies:
      '@babel/core': ^7.0.0-0
  '@babel/plugin-transform-typescript@7.28.6':
    resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==}
    engines: {node: '>=6.9.0'}
    peerDependencies:
      '@babel/core': ^7.0.0-0
@@ -452,6 +501,9 @@
    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
    engines: {node: '>=14'}
  '@rolldown/pluginutils@1.0.0-rc.15':
    resolution: {integrity: sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==}
  '@rollup/rollup-android-arm-eabi@4.60.0':
    resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==}
    cpu: [arm]
@@ -617,12 +669,35 @@
  '@types/web-bluetooth@0.0.20':
    resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
  '@vitejs/plugin-vue-jsx@5.1.5':
    resolution: {integrity: sha512-jIAsvHOEtWpslLOI2MeElGFxH7M8pM83BU/Tor4RLyiwH0FM4nUW3xdvbw20EeU9wc5IspQwMq225K3CMnJEpA==}
    engines: {node: ^20.19.0 || >=22.12.0}
    peerDependencies:
      vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
      vue: ^3.0.0
  '@vitejs/plugin-vue@4.6.2':
    resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==}
    engines: {node: ^14.18.0 || >=16.0.0}
    peerDependencies:
      vite: ^4.0.0 || ^5.0.0
      vue: ^3.2.25
  '@vue/babel-helper-vue-transform-on@2.0.1':
    resolution: {integrity: sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==}
  '@vue/babel-plugin-jsx@2.0.1':
    resolution: {integrity: sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==}
    peerDependencies:
      '@babel/core': ^7.0.0-0
    peerDependenciesMeta:
      '@babel/core':
        optional: true
  '@vue/babel-plugin-resolve-type@2.0.1':
    resolution: {integrity: sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==}
    peerDependencies:
      '@babel/core': ^7.0.0-0
  '@vue/compiler-core@3.5.30':
    resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==}
@@ -2087,6 +2162,10 @@
      '@jridgewell/trace-mapping': 0.3.31
      jsesc: 3.1.0
  '@babel/helper-annotate-as-pure@7.27.3':
    dependencies:
      '@babel/types': 7.29.0
  '@babel/helper-compilation-targets@7.28.6':
    dependencies:
      '@babel/compat-data': 7.29.0
@@ -2095,7 +2174,27 @@
      lru-cache: 5.1.1
      semver: 6.3.1
  '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)':
    dependencies:
      '@babel/core': 7.29.0
      '@babel/helper-annotate-as-pure': 7.27.3
      '@babel/helper-member-expression-to-functions': 7.28.5
      '@babel/helper-optimise-call-expression': 7.27.1
      '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0)
      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
      '@babel/traverse': 7.29.0
      semver: 6.3.1
    transitivePeerDependencies:
      - supports-color
  '@babel/helper-globals@7.28.0': {}
  '@babel/helper-member-expression-to-functions@7.28.5':
    dependencies:
      '@babel/traverse': 7.29.0
      '@babel/types': 7.29.0
    transitivePeerDependencies:
      - supports-color
  '@babel/helper-module-imports@7.28.6':
    dependencies:
@@ -2113,7 +2212,27 @@
    transitivePeerDependencies:
      - supports-color
  '@babel/helper-optimise-call-expression@7.27.1':
    dependencies:
      '@babel/types': 7.29.0
  '@babel/helper-plugin-utils@7.28.6': {}
  '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)':
    dependencies:
      '@babel/core': 7.29.0
      '@babel/helper-member-expression-to-functions': 7.28.5
      '@babel/helper-optimise-call-expression': 7.27.1
      '@babel/traverse': 7.29.0
    transitivePeerDependencies:
      - supports-color
  '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
    dependencies:
      '@babel/traverse': 7.29.0
      '@babel/types': 7.29.0
    transitivePeerDependencies:
      - supports-color
  '@babel/helper-string-parser@7.27.1': {}
@@ -2134,6 +2253,27 @@
    dependencies:
      '@babel/core': 7.29.0
      '@babel/helper-plugin-utils': 7.28.6
  '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)':
    dependencies:
      '@babel/core': 7.29.0
      '@babel/helper-plugin-utils': 7.28.6
  '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)':
    dependencies:
      '@babel/core': 7.29.0
      '@babel/helper-plugin-utils': 7.28.6
  '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)':
    dependencies:
      '@babel/core': 7.29.0
      '@babel/helper-annotate-as-pure': 7.27.3
      '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0)
      '@babel/helper-plugin-utils': 7.28.6
      '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
      '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
    transitivePeerDependencies:
      - supports-color
  '@babel/template@7.28.6':
    dependencies:
@@ -2355,6 +2495,8 @@
  '@pkgjs/parseargs@0.11.0':
    optional: true
  '@rolldown/pluginutils@1.0.0-rc.15': {}
  '@rollup/rollup-android-arm-eabi@4.60.0':
    optional: true
@@ -2458,11 +2600,52 @@
  '@types/web-bluetooth@0.0.20': {}
  '@vitejs/plugin-vue-jsx@5.1.5(vite@5.4.21(@types/node@25.5.0)(less@4.6.4)(sass@1.98.0)(stylus@0.54.8)(terser@5.46.1))(vue@3.5.30)':
    dependencies:
      '@babel/core': 7.29.0
      '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
      '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0)
      '@rolldown/pluginutils': 1.0.0-rc.15
      '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.0)
      vite: 5.4.21(@types/node@25.5.0)(less@4.6.4)(sass@1.98.0)(stylus@0.54.8)(terser@5.46.1)
      vue: 3.5.30
    transitivePeerDependencies:
      - supports-color
  '@vitejs/plugin-vue@4.6.2(vite@5.4.21(@types/node@25.5.0)(less@4.6.4)(sass@1.98.0)(stylus@0.54.8)(terser@5.46.1))(vue@3.5.30)':
    dependencies:
      vite: 5.4.21(@types/node@25.5.0)(less@4.6.4)(sass@1.98.0)(stylus@0.54.8)(terser@5.46.1)
      vue: 3.5.30
  '@vue/babel-helper-vue-transform-on@2.0.1': {}
  '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.0)':
    dependencies:
      '@babel/helper-module-imports': 7.28.6
      '@babel/helper-plugin-utils': 7.28.6
      '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
      '@babel/template': 7.28.6
      '@babel/traverse': 7.29.0
      '@babel/types': 7.29.0
      '@vue/babel-helper-vue-transform-on': 2.0.1
      '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.29.0)
      '@vue/shared': 3.5.30
    optionalDependencies:
      '@babel/core': 7.29.0
    transitivePeerDependencies:
      - supports-color
  '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.29.0)':
    dependencies:
      '@babel/code-frame': 7.29.0
      '@babel/core': 7.29.0
      '@babel/helper-module-imports': 7.28.6
      '@babel/helper-plugin-utils': 7.28.6
      '@babel/parser': 7.29.2
      '@vue/compiler-sfc': 3.5.30
    transitivePeerDependencies:
      - supports-color
  '@vue/compiler-core@3.5.30':
    dependencies:
      '@babel/parser': 7.29.2
Code/WMS/WIDESEA_WMSClient/src/api/mes.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
/**
 * MES接口API模块
 * æä¾›ä¸ŽMES系统的交互接口,包括托盘进出站、电芯绑定解绑、NG上报等功能
 */
import http from '@/api/http.js';
const baseURL = '/api';
// åº“存信息相关MES接口
export const stockInfoMesApi = {
  /**
   * æ‰˜ç›˜è¿›ç«™
   * @param {Object} data - è¯·æ±‚数据 { palletCode, stockId }
   * @returns {Promise}
   */
  inboundInContainer(data) {
    return http.post(`${baseURL}/StockInfo/inboundInContainer`, data, false, {
      headers: { 'Content-Type': 'application/json' }
    });
  },
  /**
   * æ‰˜ç›˜å‡ºç«™
   * @param {Object} data - è¯·æ±‚数据 { palletCode, stockId, paramList }
   * @returns {Promise}
   */
  outboundInContainer(data) {
    return http.post(`${baseURL}/StockInfo/outboundInContainer`, data, false, {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};
// åº“存明细相关MES接口
export const stockDetailMesApi = {
  /**
   * æ‰˜ç›˜ç”µèŠ¯ç»‘å®š
   * @param {Object} data - è¯·æ±‚数据 { palletCode, sfcList, location, operationType }
   * @returns {Promise}
   */
  bindContainer(data) {
    return http.post(`${baseURL}/StockInfoDetail/bindContainer`, data, false, {
      headers: { 'Content-Type': 'application/json' }
    });
  },
  /**
   * æ‰˜ç›˜ç”µèŠ¯è§£ç»‘
   * @param {Object} data - è¯·æ±‚数据 { palletCode, sfcList }
   * @returns {Promise}
   */
  unbindContainer(data) {
    return http.post(`${baseURL}/StockInfoDetail/unbindContainer`, data, false, {
      headers: { 'Content-Type': 'application/json' }
    });
  },
  /**
   * æ‰˜ç›˜NG电芯上报
   * @param {Object} data - è¯·æ±‚数据 { palletCode, ngSfcList }
   * @returns {Promise}
   */
  containerNgReport(data) {
    return http.post(`${baseURL}/StockInfoDetail/containerNgReport`, data, false, {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};
export default {
  stockInfo: stockInfoMesApi,
  stockDetail: stockDetailMesApi
};
Code/WMS/WIDESEA_WMSClient/src/components/MesConfirmDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,273 @@
<template>
  <el-dialog
    v-model="visible"
    :title="dialogTitle"
    width="500px"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="mes-confirm-content">
      <p class="operation-text">{{ operationText }}</p>
      <div class="info-section">
        <div class="info-row" v-for="(item, index) in displayInfo" :key="index">
          <span class="info-label">{{ item.label }}:</span>
          <span class="info-value">{{ item.value }}</span>
        </div>
      </div>
      <div v-if="errorMessage" class="error-message">
        <el-icon><Warning /></el-icon>
        <span>{{ errorMessage }}</span>
      </div>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="handleClose">取消</el-button>
        <el-button
          type="primary"
          :loading="loading"
          @click="handleConfirm"
        >
          ç¡®è®¤æ‰§è¡Œ
        </el-button>
      </span>
    </template>
  </el-dialog>
</template>
<script>
import { defineComponent, ref, computed, watch } from 'vue';
import { Warning } from '@element-plus/icons-vue';
/**
 * MES确认对话框组件
 * ç”¨äºŽåœ¨æ‰§è¡ŒMES操作(进站/出站/绑定/解绑/NG上报)前显示确认信息
 */
export default defineComponent({
  name: 'MesConfirmDialog',
  components: {
    Warning
  },
  props: {
    modelValue: {
      type: Boolean,
      default: false
    },
    /**
     * æ“ä½œç±»åž‹: inbound(进站) | outbound(出站) | bind(绑定) | unbind(解绑) | ngReport(NG上报)
     */
    operationType: {
      type: String,
      required: true
    },
    /**
     * æ‰˜ç›˜ç 
     */
    palletCode: {
      type: String,
      required: true
    },
    /**
     * åº“存信息对象(用于进站/出站操作)
     */
    stockInfo: {
      type: Object,
      default: null
    },
    /**
     * åº“存明细信息对象(用于绑定/解绑/NG上报操作)
     */
    detailInfo: {
      type: Object,
      default: null
    }
  },
  emits: ['update:modelValue', 'confirm'],
  setup(props, { emit }) {
    const visible = ref(false);
    const loading = ref(false);
    const errorMessage = ref('');
    // ç›‘听modelValue变化,同步到visible
    watch(
      () => props.modelValue,
      (newVal) => {
        visible.value = newVal;
        // å¯¹è¯æ¡†æ‰“开时重置错误信息
        if (newVal) {
          errorMessage.value = '';
        }
      },
      { immediate: true }
    );
    // ç›‘听visible变化,同步到modelValue
    watch(visible, (newVal) => {
      emit('update:modelValue', newVal);
    });
    /**
     * æ“ä½œç±»åž‹é…ç½®æ˜ å°„
     */
    const operationConfig = {
      inbound: { title: '托盘进站', text: '您即将执行托盘进站操作' },
      outbound: { title: '托盘出站', text: '您即将执行托盘出站操作' },
      bind: { title: '电芯绑定', text: '您即将执行电芯绑定操作' },
      unbind: { title: '电芯解绑', text: '您即将执行电芯解绑操作' },
      ngReport: { title: 'NG上报', text: '您即将执行NG电芯上报操作' }
    };
    /**
     * å¯¹è¯æ¡†æ ‡é¢˜
     */
    const dialogTitle = computed(() => {
      return operationConfig[props.operationType]?.title || '确认操作';
    });
    /**
     * æ“ä½œæç¤ºæ–‡æœ¬
     */
    const operationText = computed(() => {
      return operationConfig[props.operationType]?.text || '';
    });
    /**
     * æ˜¾ç¤ºçš„信息列表
     */
    const displayInfo = computed(() => {
      const info = [
        { label: '托盘码', value: props.palletCode || '-' }
      ];
      // å¦‚果有明细信息,显示电芯数量
      if (props.detailInfo) {
        info.push({
          label: '电芯数量',
          value: props.detailInfo.sfcCount !== undefined ? props.detailInfo.sfcCount : '-'
        });
      }
      // å¦‚果有库存信息,可以显示库位等信息
      if (props.stockInfo) {
        info.push({
          label: '库位',
          value: props.stockInfo.location || '-'
        });
      }
      return info;
    });
    /**
     * å…³é—­å¯¹è¯æ¡†
     */
    const handleClose = () => {
      visible.value = false;
      errorMessage.value = '';
      loading.value = false;
    };
    /**
     * ç¡®è®¤æ‰§è¡Œæ“ä½œ
     * è§¦å‘confirm事件,传递操作回调和错误处理函数
     */
    const handleConfirm = () => {
      loading.value = true;
      errorMessage.value = '';
      // è§¦å‘confirm事件,传递操作参数和回调函数
      emit('confirm', {
        operationType: props.operationType,
        palletCode: props.palletCode,
        stockInfo: props.stockInfo,
        detailInfo: props.detailInfo,
        onSuccess: () => {
          // æ“ä½œæˆåŠŸï¼šå…³é—­å¯¹è¯æ¡†å¹¶é‡ç½®çŠ¶æ€
          visible.value = false;
          loading.value = false;
          errorMessage.value = '';
        },
        onError: (error) => {
          // æ“ä½œå¤±è´¥ï¼šæ˜¾ç¤ºé”™è¯¯ä¿¡æ¯å¹¶ä¿æŒå¯¹è¯æ¡†æ‰“å¼€
          errorMessage.value = error || '操作失败,请重试';
          loading.value = false;
        }
      });
    };
    return {
      visible,
      loading,
      errorMessage,
      dialogTitle,
      operationText,
      displayInfo,
      handleClose,
      handleConfirm
    };
  }
});
</script>
<style lang="less" scoped>
.mes-confirm-content {
  padding: 10px 0;
}
.operation-text {
  font-size: 14px;
  color: #303133;
  margin-bottom: 20px;
  font-weight: 500;
}
.info-section {
  background: #f8fafc;
  border-radius: 8px;
  padding: 16px;
  margin-bottom: 16px;
}
.info-row {
  display: flex;
  margin-bottom: 12px;
  font-size: 14px;
  &:last-child {
    margin-bottom: 0;
  }
}
.info-label {
  color: #909399;
  width: 80px;
  flex-shrink: 0;
}
.info-value {
  color: #303133;
  font-weight: 500;
}
.error-message {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px;
  background: #fef0f0;
  border: 1px solid #fde2e2;
  border-radius: 6px;
  color: #f56c6c;
  font-size: 14px;
  .el-icon {
    font-size: 18px;
  }
}
</style>
Code/WMS/WIDESEA_WMSClient/src/extension/basic/customerInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/locationInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/materielCodeInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/materielInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/palletCodeInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/roadwayInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/supplierInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/userInfo.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/basic/userInfo.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/basic/supplierInfo.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/basic/userInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/basic/warehouse.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrder.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrderResult.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrderResult.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrder.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/check/checkOrderResult.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail_Hty.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder_Hty.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder_Hty.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail_Hty.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder_Hty.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrder.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrder.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrder.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrder.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrderDetail.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrderDetail.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail_Hty.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/inbound/purchaseOrderDetail.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrder.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrderDetail.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrderDetail.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/inbound/inboundOrderDetail_Hty.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/inbound/receiveOrderDetail.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/mesOutboundOrder.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outStockLockInfo.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrderDetail.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrderDetail_Hty.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder_Hty.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder_Hty.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrderDetail_Hty.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/outbound/outboundOrder_Hty.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/record/locationStatusChangeRecord.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/record/stockQuantityChangeRecord.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stock.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockChat.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo.jsx
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: '',
    gridBody: '',
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '',
  buttons: { view: [], box: [], detail: [] },
  methods: {
    onInit() {
      // æ·»åŠ MES操作列
      this.columns.push({
        title: '操作',
        field: '操作',
        align: 'center',
        width: 200,
        fixed: 'right',
        render: (h, { row, column, index }) => {
          return (
            <div>
              <el-button
                type="primary"
                size="small"
                onClick={($e) => { this.handleInbound(row); }}
              >进站</el-button>
              <el-button
                type="success"
                size="small"
                style="margin-left: 8px"
                onClick={($e) => { this.handleOutbound(row); }}
              >出站</el-button>
            </div>
          );
        }
      });
    },
    // æ‰˜ç›˜è¿›ç«™æ“ä½œ
    async handleInbound(row) {
      try {
        await this.$confirm(`确认执行托盘进站操作?\n托盘编号:${row.palletCode}`, "进站确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfo/inboundInContainer", {
          palletCode: row.palletCode,
          stockId: row.id
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "托盘进站成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "托盘进站失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    // æ‰˜ç›˜å‡ºç«™æ“ä½œ
    async handleOutbound(row) {
      try {
        await this.$confirm(`确认执行托盘出站操作?\n托盘编号:${row.palletCode}`, "出站确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfo/outboundInContainer", {
          palletCode: row.palletCode,
          stockId: row.id
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "托盘出站成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "托盘出站失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    onInited() {
      // æ¡†æž¶åˆå§‹åŒ–配置后
    },
    searchBefore(param) {
      return true;
    },
    searchAfter(result) {
      return true;
    },
    addBefore(formData) {
      return true;
    },
    updateBefore(formData) {
      return true;
    },
    rowClick({ row, column, event }) {
      this.$refs.table.$refs.table.toggleRowSelection(row);
    },
    modelOpenAfter(row) {
      // ç‚¹å‡»ç¼–辑、新建按钮弹出框后
    }
  }
};
export default extension;
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail.jsx
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,170 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: '',
    gridBody: '',
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '',
  buttons: { view: [], box: [], detail: [] },
  methods: {
    onInit() {
      // æ·»åŠ MES操作列
      this.columns.push({
        title: '操作',
        field: '操作',
        align: 'center',
        width: 280,
        fixed: 'right',
        render: (h, { row, column, index }) => {
          // é”å®šçŠ¶æ€ä¸æ˜¾ç¤ºæŒ‰é’®
          // çŠ¶æ€: 7=出库锁定, 9=移库锁定, 99=组盘撤销, 199=入库撤销
          const lockedStatuses = [7, 9, 99, 199];
          if (lockedStatuses.includes(row.status)) {
            return <span style="color: #909399">暂无可执行操作</span>;
          }
          return (
            <div>
              <el-button
                type="primary"
                size="small"
                onClick={($e) => { this.handleBind(row); }}
              >绑定</el-button>
              <el-button
                type="warning"
                size="small"
                style="margin-left: 6px"
                onClick={($e) => { this.handleUnbind(row); }}
              >解绑</el-button>
              <el-button
                type="danger"
                size="small"
                style="margin-left: 6px"
                onClick={($e) => { this.handleNgReport(row); }}
              >NG上报</el-button>
            </div>
          );
        }
      });
    },
    // æ‰˜ç›˜ç”µèŠ¯ç»‘å®šæ“ä½œ
    async handleBind(row) {
      try {
        await this.$confirm(`确认执行电芯绑定操作?\n电芯码:${row.serialNumber}`, "绑定确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfoDetail/bindContainer", {
          palletCode: row.palletCode || "P001",
          sfcList: [row.serialNumber],
          location: row.location || "",
          operationType: 1
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "电芯绑定成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "电芯绑定失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    // æ‰˜ç›˜ç”µèŠ¯è§£ç»‘æ“ä½œ
    async handleUnbind(row) {
      try {
        await this.$confirm(`确认执行电芯解绑操作?\n电芯码:${row.serialNumber}`, "解绑确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfoDetail/unbindContainer", {
          palletCode: row.palletCode || "P001",
          sfcList: [row.serialNumber]
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "电芯解绑成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "电芯解绑失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    // æ‰˜ç›˜NG电芯上报操作
    async handleNgReport(row) {
      try {
        await this.$confirm(`确认执行NG电芯上报操作?\n电芯码:${row.serialNumber}`, "NG上报确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfoDetail/containerNgReport", {
          palletCode: row.palletCode || "P001",
          ngSfcList: [{
            sfc: row.serialNumber,
            ngCode: "NG001",
            ngEquipmentCode: "WCS_001",
            ngResourceCode: "RESOURCE_001"
          }]
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "NG上报成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "NG上报失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    onInited() {
      // æ¡†æž¶åˆå§‹åŒ–配置后
    },
    searchBefore(param) {
      return true;
    },
    searchAfter(result) {
      return true;
    },
    addBefore(formData) {
      return true;
    },
    updateBefore(formData) {
      return true;
    },
    rowClick({ row, column, event }) {
      this.$refs.table.$refs.table.toggleRowSelection(row);
    },
    modelOpenAfter(row) {
      // ç‚¹å‡»ç¼–辑、新建按钮弹出框后
    }
  }
};
export default extension;
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail_Hty.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo_Hty.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo_Hty.jsx
copy from Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfoDetail_Hty.js copy to Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo_Hty.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockView.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Dictionary.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_DictionaryList.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Log.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Role.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Role1.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_Tenant.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/Sys_User.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/system/system/Sys_Department.jsx
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/addManualTask.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
<template>
  <div>
    <vol-box
      v-model="showBox"
      :lazy="true"
      width="500px"
      :padding="15"
      title="手动创建任务"
    >
      <el-form :model="formData" ref="form" label-width="100px">
        <el-form-item label="任务类型" prop="taskType" required>
          <el-select v-model="formData.taskType" placeholder="请选择任务类型">
            <el-option label="入库" value="入库"></el-option>
            <el-option label="出库" value="出库"></el-option>
            <el-option label="移库" value="移库"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="起点地址" prop="sourceAddress" required>
          <el-input v-model="formData.sourceAddress" placeholder="请输入起点地址"></el-input>
        </el-form-item>
        <el-form-item label="终点地址" prop="targetAddress" required>
          <el-input v-model="formData.targetAddress" placeholder="请输入终点地址"></el-input>
        </el-form-item>
        <el-form-item label="条码" prop="barcode" required>
          <el-input v-model="formData.barcode" placeholder="请输入条码"></el-input>
        </el-form-item>
        <el-form-item label="仓库ID" prop="warehouseId" required>
          <el-input v-model="formData.warehouseId" placeholder="请输入仓库ID"></el-input>
        </el-form-item>
        <el-form-item label="优先级">
          <el-input v-model="formData.grade" readonly></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" size="small" @click="submit">确定</el-button>
        <el-button type="danger" size="small" @click="showBox = false">关闭</el-button>
      </template>
    </vol-box>
  </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
export default {
  components: { VolBox },
  data() {
    return {
      showBox: false,
      formData: {
        taskType: "",
        sourceAddress: "",
        targetAddress: "",
        barcode: "",
        warehouseId: "",
        grade: 1,
      },
    };
  },
  methods: {
    open() {
      this.showBox = true;
      this.resetForm();
    },
    resetForm() {
      this.formData = {
        taskType: "",
        sourceAddress: "",
        targetAddress: "",
        barcode: "",
        warehouseId: "",
        grade: 1,
      };
    },
    submit() {
      if (!this.formData.taskType) return this.$message.error("请选择任务类型");
      if (!this.formData.sourceAddress) return this.$message.error("请输入起点地址");
      if (!this.formData.targetAddress) return this.$message.error("请输入终点地址");
      if (!this.formData.barcode) return this.$message.error("请输入条码");
      if (!this.formData.warehouseId) return this.$message.error("请输入仓库ID");
      this.http
        .post("/api/Task/CreateManualTask", this.formData, "数据处理中...")
        .then((res) => {
          if (!res.status) return this.$message.error(res.message);
          this.$message.success("任务创建成功");
          this.showBox = false;
          this.$emit("parentCall", ($vue) => {
            $vue.refresh();
          });
        });
    },
  },
};
</script>
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/gridBodyExtension.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,329 @@
<template>
  <div>
    <!-- æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡å¼¹çª— -->
    <vol-box v-model="showManualCreate" :lazy="true" width="500px" :padding="15" title="手动创建任务">
      <el-form :model="manualFormData" ref="form" label-width="100px">
        <el-form-item label="任务类型" prop="taskType" required>
          <el-select v-model="manualFormData.taskType" placeholder="请选择任务类型">
            <el-option label="入库" value="入库"></el-option>
            <el-option label="出库" value="出库"></el-option>
            <el-option label="移库" value="移库"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="起点地址" prop="sourceAddress" required>
          <el-input v-model="manualFormData.sourceAddress" placeholder="请输入起点地址"></el-input>
        </el-form-item>
        <el-form-item label="终点地址" prop="targetAddress" required>
          <el-input v-model="manualFormData.targetAddress" placeholder="请输入终点地址"></el-input>
        </el-form-item>
        <el-form-item label="条码" prop="barcode" required>
          <el-input v-model="manualFormData.barcode" placeholder="请输入条码"></el-input>
        </el-form-item>
        <el-form-item label="仓库ID" prop="warehouseId" required>
          <el-input v-model="manualFormData.warehouseId" placeholder="请输入仓库ID"></el-input>
        </el-form-item>
        <el-form-item label="优先级">
          <el-input v-model="manualFormData.grade" readonly></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button type="primary" size="small" @click="submitManualCreate">确定</el-button>
        <el-button type="danger" size="small" @click="showManualCreate = false">关闭</el-button>
      </template>
    </vol-box>
    <!-- æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡å¼¹çª— -->
    <vol-box v-model="showDispatch" :lazy="true" width="900px" :padding="15" title="手动下发任务到 WCS">
      <div v-if="dispatchRows.length > 0" class="dispatch-info">
        å·²é€‰ä»»åŠ¡æ•°: <span class="count">{{ dispatchRows.length }}</span> ä¸ª
      </div>
      <el-table :data="dispatchTableData" border style="width: 100%; margin-top: 10px" max-height="400">
        <el-table-column prop="taskNum" label="任务号" width="120"></el-table-column>
        <el-table-column prop="palletCode" label="托盘号" width="140">
          <template v-slot="{ row }">
            <el-input v-if="row && row.editable" v-model="row.palletCode" size="small" placeholder="请输入"></el-input>
            <span v-else>{{ row ? row.palletCode : '' }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="sourceAddress" label="起点地址" width="160">
          <template v-slot="{ row }">
            <el-input v-if="row && row.editable" v-model="row.sourceAddress" size="small" placeholder="请输入"></el-input>
            <span v-else>{{ row ? row.sourceAddress : '' }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="targetAddress" label="终点地址" width="160">
          <template v-slot="{ row }">
            <el-input v-if="row && row.editable" v-model="row.targetAddress" size="small" placeholder="请输入"></el-input>
            <span v-else>{{ row ? row.targetAddress : '' }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="grade" label="优先级" width="100">
          <template v-slot="{ row }">
            <el-input-number v-if="row && row.editable" v-model="row.grade" :min="1" :max="99" size="small" style="width: 80px"></el-input-number>
            <span v-else>{{ row ? row.grade : '' }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="statusName" label="状态" width="120">
          <template v-slot="{ row }">
            <span :class="{ 'status-error': row && !row.editable }">{{ row ? row.statusName : '' }}</span>
          </template>
        </el-table-column>
      </el-table>
      <div v-if="dispatchFailResults && dispatchFailResults.length > 0" class="fail-results">
        <div class="fail-title">下发失败任务:</div>
        <el-table :data="dispatchFailResults" border style="width: 100%; margin-top: 10px" max-height="200">
          <el-table-column prop="taskNum" label="任务号" width="120"></el-table-column>
          <el-table-column prop="errorMessage" label="失败原因"></el-table-column>
        </el-table>
      </div>
      <template #footer>
        <el-button type="primary" size="small" @click="submitDispatch" :loading="dispatchLoading">确认下发</el-button>
        <el-button type="danger" size="small" @click="showDispatch = false">取消</el-button>
      </template>
    </vol-box>
  </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
export default {
  components: { VolBox },
  emits: ["parentCall"],
  data() {
    return {
      // æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡
      showManualCreate: false,
      manualFormData: {
        taskType: "",
        sourceAddress: "",
        targetAddress: "",
        barcode: "",
        warehouseId: "",
        grade: 1,
      },
      // æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡
      showDispatch: false,
      dispatchLoading: false,
      dispatchRows: [],
      dispatchTableData: [],
      dispatchFailResults: [],
      dispatchOriginalData: [],
    };
  },
  methods: {
    // æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡ - æ‰“å¼€
    open() {
      this.showManualCreate = true;
      this.resetManualForm();
    },
    resetManualForm() {
      this.manualFormData = {
        taskType: "",
        sourceAddress: "",
        targetAddress: "",
        barcode: "",
        warehouseId: "",
        grade: 1,
      };
    },
    submitManualCreate() {
      if (!this.manualFormData.taskType) return this.$message.error("请选择任务类型");
      if (!this.manualFormData.sourceAddress) return this.$message.error("请输入起点地址");
      if (!this.manualFormData.targetAddress) return this.$message.error("请输入终点地址");
      if (!this.manualFormData.barcode) return this.$message.error("请输入条码");
      if (!this.manualFormData.warehouseId) return this.$message.error("请输入仓库ID");
      this.http
        .post("/api/Task/CreateManualTask", this.manualFormData, "数据处理中...")
        .then((res) => {
          if (!res.status) return this.$message.error(res.message);
          this.$message.success("任务创建成功");
          this.showManualCreate = false;
          this.$emit("parentCall", ($vue) => { $vue.refresh(); });
        });
    },
    // æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡ - æ‰“å¼€
    openDispatch(rows) {
      console.log('openDispatch called with rows:', rows);
      console.log('rows type:', typeof rows, Array.isArray(rows));
      // ç¡®ä¿æ˜¯æ•°ç»„
      if (!rows) {
        this.dispatchRows = [];
      } else if (Array.isArray(rows)) {
        this.dispatchRows = rows;
      } else {
        // å¦‚果是单个对象,包装成数组
        this.dispatchRows = [rows];
      }
      console.log('dispatchRows length:', this.dispatchRows.length);
      this.dispatchFailResults = [];
      // å…ˆåˆå§‹åŒ–数据,再显示弹窗
      this.initDispatchTableData();
      this.showDispatch = true;
    },
    initDispatchTableData() {
      console.log('initDispatchTableData dispatchRows:', this.dispatchRows);
      console.log('dispatchRows isArray:', Array.isArray(this.dispatchRows), 'length:', this.dispatchRows?.length);
      // ç¡®ä¿ dispatchRows æ˜¯æ•°ç»„
      if (!this.dispatchRows || !Array.isArray(this.dispatchRows)) {
        console.warn('dispatchRows ä¸æ˜¯æœ‰æ•ˆæ•°ç»„,重置为空数组');
        this.dispatchTableData = [];
        return;
      }
      const statusNames = {
        'inbound_200': '入库新单',
        'outbound_100': '出库新单',
        'relocation_300': '移库新单',
        'outbound_110': '堆垛机出库执行中',
        'outbound_115': '堆垛机出库完成',
        'inbound_220': '输送线入库执行中',
        'inbound_230': '堆垛机入库执行中',
        'inbound_290': '入库任务完成',
        'outbound_120': '输送线出库执行中',
        'outbound_125': '输送线出库完成',
        'outbound_200': '出库任务完成',
        'relocation_310': '堆垛机移库执行中'
      };
      const result = [];
      const original = [];
      let filteredCount = 0; // è®°å½•被过滤的任务数量
      for (const row of this.dispatchRows) {
        console.log('Processing row:', row);
        // Handle possible field name differences (camelCase vs PascalCase)
        const taskId = row.taskId ?? row.Id ?? row.id ?? 0;
        const taskType = row.taskType ?? row.TaskType ?? row.task_type ?? 0;
        const taskStatus = row.taskStatus ?? row.TaskStatus ?? row.task_status ?? 0;
        console.log('taskId:', taskId, 'taskType:', taskType, 'taskStatus:', taskStatus);
        const statusKey = this.getStatusKey(taskType, taskStatus);
        console.log('statusKey:', statusKey);
        const statusName = statusNames[statusKey] || `状态${taskStatus}`;
        const editable = this.isEditable(taskType, taskStatus);
        console.log('editable:', editable, '根据 taskType:', taskType, 'taskStatus:', taskStatus);
        // éžæ–°å»ºçŠ¶æ€çš„ä»»åŠ¡ä¸æ˜¾ç¤ºåœ¨å¼¹çª—ä¸­
        if (!editable) {
          console.log(`任务${taskId}状态[${statusName}]不是新建,已过滤`);
          filteredCount++;
          continue;
        }
        const item = {
          taskId: taskId,
          taskNum: row.taskNum ?? row.TaskNum ?? 0,
          sourceAddress: row.sourceAddress || row.SourceAddress || '',
          targetAddress: row.targetAddress || row.TargetAddress || '',
          grade: row.grade ?? row.Grade ?? 1,
          statusName: statusName,
          palletCode: row.palletCode || row.PalletCode || '',
          editable: editable,
          taskType: taskType,
          taskStatus: taskStatus
        };
        result.push(item);
        // ä¿å­˜åŽŸå§‹æ•°æ®çš„å‰¯æœ¬
        original.push({ ...item });
      }
      this.dispatchTableData = result;
      this.dispatchOriginalData = original;
      console.log('dispatchTableData result:', this.dispatchTableData);
      console.log('dispatchOriginalData:', this.dispatchOriginalData);
      // å¦‚果有被过滤的任务,显示提示
      if (filteredCount > 0) {
        this.$message.warning(`选中的${this.dispatchRows.length}个任务中有${filteredCount}个非新建状态任务已过滤`);
      }
    },
    getStatusKey(taskType, taskStatus) {
      if (taskType === 200) return `inbound_${taskStatus}`;
      if (taskType === 100) return `outbound_${taskStatus}`;
      if (taskType === 300) return `relocation_${taskStatus}`;
      return `other_${taskStatus}`;
    },
    isEditable(taskType, taskStatus) {
      if (taskType === 200 && taskStatus === 200) return true;
      if (taskType === 100 && taskStatus === 100) return true;
      if (taskType === 300 && taskStatus === 300) return true;
      return false;
    },
    submitDispatch() {
      if (this.dispatchTableData.length === 0) return this.$message.error("请先选择任务");
      const dispatchData = this.dispatchTableData.map(row => ({
        taskId: row.taskId,
        palletCode: row.palletCode,
        sourceAddress: row.sourceAddress,
        targetAddress: row.targetAddress,
        grade: row.grade
      }));
      console.log('Dispatching data:', dispatchData);
      this.dispatchLoading = true;
      this.http
        .post("/api/Task/DispatchTasksToWCS", dispatchData, "数据处理中...")
        .then((res) => {
          this.dispatchLoading = false;
          if (!res.status) {
            this.$message.error(res.message);
            if (res.data && res.data.failResults) {
              this.dispatchFailResults = res.data.failResults;
            }
            return;
          }
          if (res.data && res.data.failCount === 0) {
            this.$message.success(res.message);
            this.showDispatch = false;
            this.$emit("parentCall", ($vue) => { $vue.refresh(); });
            return;
          }
          if (res.data && res.data.failResults) {
            this.dispatchFailResults = res.data.failResults;
          }
          if (res.data && res.data.failCount > 0 && res.data.successCount > 0) {
            this.$message.warning(res.message);
          } else {
            this.$message.error(res.message);
          }
        })
        .catch(() => {
          this.dispatchLoading = false;
        });
    }
  }
};
</script>
<style scoped>
.dispatch-info {
  font-size: 14px;
  color: #606266;
  margin-bottom: 10px;
}
.dispatch-info .count {
  color: #409eff;
  font-weight: bold;
}
.status-error {
  color: #f56c6c;
}
.fail-results {
  margin-top: 15px;
  padding: 10px;
  background: #fef0f0;
  border-radius: 4px;
}
.fail-title {
  font-size: 14px;
  color: #f56c6c;
  margin-bottom: 5px;
}
</style>
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.jsx
ÎļþÃû´Ó Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js ÐÞ¸Ä
@@ -1,22 +1,46 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
import gridBodyExtension from './extend/gridBodyExtension.vue'
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridBody: gridBodyExtension,
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
      modelFooter: '',
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      onInit() {
        //添加"手动创建任务"按钮
        this.buttons.push({
          name: '手动创建任务',
          icon: 'el-icon-plus',
          type: 'primary',
          value: 'ManualCreateTask',
          onClick: () => {
            this.$refs.gridBody.open();
          }
        });
        //添加"手动下发"按钮
        this.buttons.push({
          name: '手动下发',
          icon: 'el-icon-s-promotion',
          type: 'primary',
          value: 'DispatchTasksToWCS',
          onClick: () => {
            let rows = this.$refs.table.getSelected();
            if (rows.length == 0) return this.$error("请先选择任务");
            this.$refs.gridBody.openDispatch(rows);
          }
        });
        let TaskHandCancelBtn = this.buttons.find(x => x.value == 'TaskHandCancel');
      if (TaskHandCancelBtn) {
        TaskHandCancelBtn.onClick = function () {
@@ -93,4 +117,3 @@
    }
  };
  export default extension;
Code/WMS/WIDESEA_WMSClient/src/extension/taskinfo/task_hty.jsx
Code/WMS/WIDESEA_WMSClient/src/views/basic/customerInfo.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/basic/customerInfo.js";
    import extend from "@/extension/basic/customerInfo.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/locationInfo.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/basic/locationInfo.js";
import extend from "@/extension/basic/locationInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/materielCodeInfo.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
      <script>
import extend from "@/extension/basic/materielCodeInfo.js";
import extend from "@/extension/basic/materielCodeInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/materielInfo.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/basic/materielInfo.js";
import extend from "@/extension/basic/materielInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/palletCodeInfo.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
      <script>
import extend from "@/extension/basic/palletCodeInfo.js";
import extend from "@/extension/basic/palletCodeInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/supplierInfo.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/basic/supplierInfo.js";
    import extend from "@/extension/basic/supplierInfo.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/userInfo.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/basic/userInfo.js";
    import extend from "@/extension/basic/userInfo.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/basic/warehouse.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/basic/warehouse.js";
import extend from "@/extension/basic/warehouse.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/check/checkOrder.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/check/checkOrder.js";
    import extend from "@/extension/check/checkOrder.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/check/checkOrderResult.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/check/checkOrderResult.js";
    import extend from "@/extension/check/checkOrderResult.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/inbound/inboundOrder.js";
import extend from "@/extension/inbound/inboundOrder.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrderDetail.vue
@@ -14,7 +14,7 @@
    </view-grid>
  </template>
    <script>
  import extend from "@/extension/inbound/inboundOrderDetail.js";
  import extend from "@/extension/inbound/inboundOrderDetail.jsx";
  import { ref, defineComponent } from "vue";
  export default defineComponent({
    setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrderDetail_Hty.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/inbound/inboundOrderDetail_Hty.js";
    import extend from "@/extension/inbound/inboundOrderDetail_Hty.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/inboundOrder_Hty.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/inbound/inboundOrder_Hty.js";
    import extend from "@/extension/inbound/inboundOrder_Hty.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/purchaseOrder.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/inbound/purchaseOrder.js";
import extend from "@/extension/inbound/purchaseOrder.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/purchaseOrderDetail.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/inbound/purchaseOrderDetail.js";
    import extend from "@/extension/inbound/purchaseOrderDetail.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/receiveOrder.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
      <script>
import extend from "@/extension/inbound/receiveOrder.js";
import extend from "@/extension/inbound/receiveOrder.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/inbound/receiveOrderDetail.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/inbound/receiveOrderDetail.js";
    import extend from "@/extension/inbound/receiveOrderDetail.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/outbound/mesOutboundOrder.vue
@@ -14,7 +14,7 @@
    </view-grid>
  </template>
    <script>
  import extend from "@/extension/outbound/mesOutboundOrder.js";
  import extend from "@/extension/outbound/mesOutboundOrder.jsx";
  import { ref, defineComponent } from "vue";
  export default defineComponent({
    setup() {
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outStockLockInfo.vue
@@ -13,7 +13,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/outbound/outStockLockInfo.js";
import extend from "@/extension/outbound/outStockLockInfo.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrder.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/outbound/outboundOrder.js";
import extend from "@/extension/outbound/outboundOrder.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrderDetail.vue
@@ -14,7 +14,7 @@
    </view-grid>
  </template>
    <script>
  import extend from "@/extension/outbound/outboundOrderDetail.js";
  import extend from "@/extension/outbound/outboundOrderDetail.jsx";
  import { ref, defineComponent } from "vue";
  export default defineComponent({
    setup() {
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrderDetail_Hty.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/outbound/outboundOrderDetail_Hty.js";
    import extend from "@/extension/outbound/outboundOrderDetail_Hty.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/outbound/outboundOrder_Hty.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/outbound/outboundOrder_Hty.js";
    import extend from "@/extension/outbound/outboundOrder_Hty.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/record/locationStatusChangeRecord.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/record/locationStatusChangeRecord.js";
import extend from "@/extension/record/locationStatusChangeRecord.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/record/stockQuantityChangeRecord.vue
@@ -14,7 +14,7 @@
    </view-grid>
  </template>
    <script>
  import extend from "@/extension/record/stockQuantityChangeRecord.js";
  import extend from "@/extension/record/stockQuantityChangeRecord.jsx";
  import { ref, defineComponent } from "vue";
  export default defineComponent({
    setup() {
Code/WMS/WIDESEA_WMSClient/src/views/stock/stock.vue
@@ -13,7 +13,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/stock/stock.js";
import extend from "@/extension/stock/stock.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue
@@ -15,7 +15,7 @@
</template>
<script>
import extend from "@/extension/stock/stockInfo.js";
import extend from "@/extension/stock/stockInfo.jsx";
import {
  defineComponent,
  getCurrentInstance,
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail.vue
@@ -5,7 +5,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/stock/stockInfoDetail.js";
import extend from "@/extension/stock/stockInfoDetail.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail_Hty.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/stock/stockInfoDetail_Hty.js";
    import extend from "@/extension/stock/stockInfoDetail_Hty.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockInfo_Hty.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/stock/stockInfo_Hty.js";
    import extend from "@/extension/stock/stockInfo_Hty.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/stock/stockView.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/stock/stockView.js";
import extend from "@/extension/stock/stockView.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Dictionary.vue
@@ -19,7 +19,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Dictionary.js";
import extend from "@/extension/system/Sys_Dictionary.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_DictionaryList.vue
@@ -19,7 +19,7 @@
</template>
<script>
    import extend from "@/extension/system/Sys_DictionaryList.js";
    import extend from "@/extension/system/Sys_DictionaryList.jsx";
    var vueParam = {
        data() {
            return {
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Log.vue
@@ -19,7 +19,7 @@
  </view-grid>
</template>
  <script>
import extend from "@/extension/system/Sys_Log.js";
import extend from "@/extension/system/Sys_Log.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Role.vue
@@ -19,7 +19,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Role.js";
import extend from "@/extension/system/Sys_Role.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Role1.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/system/Sys_Role1.js";
    import extend from "@/extension/system/Sys_Role1.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_Tenant.vue
@@ -13,7 +13,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_Tenant.js";
import extend from "@/extension/system/Sys_Tenant.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
Code/WMS/WIDESEA_WMSClient/src/views/system/Sys_User.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
<script>
import extend from "@/extension/system/Sys_User.js";
import extend from "@/extension/system/Sys_User.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/system/system/Sys_Department.vue
@@ -17,7 +17,7 @@
    </view-grid>
</template>
<script>
    import extend from "@/extension/system/system/Sys_Department.js";
    import extend from "@/extension/system/system/Sys_Department.jsx";
    import { ref, defineComponent } from "vue";
    export default defineComponent({
        setup() {
Code/WMS/WIDESEA_WMSClient/src/views/taskinfo/task.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/taskinfo/task.js";
import extend from "@/extension/taskinfo/task.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/src/views/taskinfo/task_hty.vue
@@ -14,7 +14,7 @@
  </view-grid>
</template>
    <script>
import extend from "@/extension/taskinfo/task_hty.js";
import extend from "@/extension/taskinfo/task_hty.jsx";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
Code/WMS/WIDESEA_WMSClient/vite.config.js
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSClient/vite.config.mjs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { resolve, dirname } from 'path'
import { fileURLToPath } from 'url'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), vueJsx()],
  resolve: {
    alias: {
      '@': resolve(dirname(fileURLToPath(import.meta.url)), 'src')
    }
  },
  server: {
    host: '0.0.0.0',
    port: 8080,
    open: false
  },
  build: {
    sourcemap: false
  }
})
Code/WMS/WIDESEA_WMSServer/Database/Scripts/20260412_MesApiLog.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
-- =============================================
-- WMS MES接口调用日志表
-- åˆ›å»ºæ—¥æœŸ: 2026-04-12
-- æè¿°: è®°å½•所有MES接口的调用日志,包括请求、响应、耗时、成功/失败状态等信息
-- =============================================
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Dt_MesApiLog')
BEGIN
    CREATE TABLE Dt_MesApiLog (
        Id BIGINT PRIMARY KEY IDENTITY(1,1),
        ApiType NVARCHAR(50) NOT NULL,           -- æŽ¥å£ç±»åž‹
        RequestJson NVARCHAR(MAX) NULL,          -- è¯·æ±‚JSON
        ResponseJson NVARCHAR(MAX) NULL,         -- å“åº”JSON
        IsSuccess BIT NOT NULL DEFAULT 0,        -- æ˜¯å¦æˆåŠŸ
        ErrorMessage NVARCHAR(500) NULL,         -- é”™è¯¯ä¿¡æ¯
        ElapsedMs INT NOT NULL DEFAULT 0,        -- è€—æ—¶(毫秒)
        CreateDate DATETIME NOT NULL,            -- åˆ›å»ºæ—¶é—´
        Creator NVARCHAR(50) NULL,               -- åˆ›å»ºäºº
        ModifyDate DATETIME NULL,                -- ä¿®æ”¹æ—¶é—´
        Modifier NVARCHAR(50) NULL               -- ä¿®æ”¹äºº
    );
    -- åˆ›å»ºç´¢å¼•
    CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType);
    CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate);
    CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess);
    PRINT 'MES接口日志表 Dt_MesApiLog åˆ›å»ºæˆåŠŸ';
END
ELSE
BEGIN
    PRINT 'MES接口日志表 Dt_MesApiLog å·²å­˜åœ¨';
END
GO
-- =============================================
-- æ’å…¥MES系统配置
-- =============================================
-- æ£€æŸ¥å¹¶æ’å…¥MES设备编码配置
IF NOT EXISTS (SELECT * FROM Dt_SystemConfig WHERE ConfigKey = 'MES_EquipmentCode')
BEGIN
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_EquipmentCode', 'WCS_001', 'MES设备编码', GETDATE(), 'System');
    PRINT 'MES设备编码配置插入成功';
END
ELSE
BEGIN
    PRINT 'MES设备编码配置已存在';
END
GO
-- æ£€æŸ¥å¹¶æ’å…¥MES资源编码配置
IF NOT EXISTS (SELECT * FROM Dt_SystemConfig WHERE ConfigKey = 'MES_ResourceCode')
BEGIN
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_ResourceCode', 'RESOURCE_001', 'MES资源编码', GETDATE(), 'System');
    PRINT 'MES资源编码配置插入成功';
END
ELSE
BEGIN
    PRINT 'MES资源编码配置已存在';
END
GO
-- æ£€æŸ¥å¹¶æ’å…¥MES接口地址配置
IF NOT EXISTS (SELECT * FROM Dt_SystemConfig WHERE ConfigKey = 'MES_ApiBaseUrl')
BEGIN
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_ApiBaseUrl', 'http://mes-server/api', 'MES接口地址', GETDATE(), 'System');
    PRINT 'MES接口地址配置插入成功';
END
ELSE
BEGIN
    PRINT 'MES接口地址配置已存在';
END
GO
-- æ£€æŸ¥å¹¶æ’å…¥MES接口超时时间配置
IF NOT EXISTS (SELECT * FROM Dt_SystemConfig WHERE ConfigKey = 'MES_TimeoutSeconds')
BEGIN
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_TimeoutSeconds', '30', 'MES接口超时时间(秒)', GETDATE(), 'System');
    PRINT 'MES接口超时时间配置插入成功';
END
ELSE
BEGIN
    PRINT 'MES接口超时时间配置已存在';
END
GO
PRINT 'MES接口日志表和系统配置脚本执行完成';
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/BaseRepository/UnitOfWorks/IUnitOfWorkManage.cs
@@ -17,6 +17,8 @@
        void BeginTran();
        void BeginTran(MethodInfo method);
        WebResponseContent BeginTran(Func<WebResponseContent> func);
        Task<WebResponseContent> BeginTranAsync(Func<Task<WebResponseContent>> funcAsync);
        void CommitTran();
        void CommitTran(MethodInfo method);
        void RollbackTran();
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/BaseRepository/UnitOfWorks/UnitOfWorkManage.cs
@@ -100,6 +100,32 @@
            }
        }
        private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);
        public async Task<WebResponseContent> BeginTranAsync(Func<Task<WebResponseContent>> funcAsync)
        {
            await _asyncLock.WaitAsync();
            try
            {
                BeginTran();   // å‡è®¾è¿™æ˜¯åŒæ­¥æ–¹æ³•,启动事务
                WebResponseContent content = await funcAsync();
                if (content.Status)
                    CommitTran();   // åŒæ­¥æäº¤
                else
                    RollbackTran(); // åŒæ­¥å›žæ»š
                return content;
            }
            catch (Exception ex)
            {
                RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
            finally
            {
                _asyncLock.Release();
            }
        }
        public void CommitTran()
        {
            lock (this)
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/BindContainerRequestDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
using System.Collections.Generic;
namespace WIDESEA_DTO.MES
{
    /// <summary>
    /// æ‰˜ç›˜ç”µèŠ¯ç»‘å®šè¯·æ±‚DTO
    /// </summary>
    public class BindContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// ç”µèŠ¯ç åˆ—è¡¨
        /// </summary>
        public List<string> SfcList { get; set; }
        /// <summary>
        /// ä½ç½®ä¿¡æ¯
        /// </summary>
        public string Location { get; set; }
        /// <summary>
        /// æ“ä½œç±»åž‹ï¼š0-默认 1-进站 2-出站 3-进出站
        /// </summary>
        public int OperationType { get; set; } = 1;
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/ContainerNgReportRequestDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace WIDESEA_DTO.MES
{
    /// <summary>
    /// æ‰˜ç›˜NG电芯上报请求DTO
    /// </summary>
    public class ContainerNgReportRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// NG电芯列表
        /// </summary>
        public List<NgSfcItemDto> NgSfcList { get; set; }
        /// <summary>
        /// NG电芯项DTO
        /// </summary>
        public class NgSfcItemDto
        {
            /// <summary>
            /// äº§å“æ¡ç 
            /// </summary>
            public string Sfc { get; set; }
            /// <summary>
            /// NG代码
            /// </summary>
            public string NgCode { get; set; }
            /// <summary>
            /// NG设备
            /// </summary>
            public string NgEquipmentCode { get; set; }
            /// <summary>
            /// NG资源
            /// </summary>
            public string NgResourceCode { get; set; }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/InboundInContainerRequestDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
namespace WIDESEA_DTO.MES
{
    /// <summary>
    /// æ‰˜ç›˜è¿›ç«™è¯·æ±‚DTO
    /// </summary>
    public class InboundInContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// åº“å­˜ID
        /// </summary>
        public long StockId { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesApiLogDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
namespace WIDESEA_DTO.MES
{
    /// <summary>
    /// MES接口日志DTO
    /// </summary>
    public class MesApiLogDto
    {
        /// <summary>
        /// æŽ¥å£ç±»åž‹
        /// </summary>
        public string ApiType { get; set; }
        /// <summary>
        /// è¯·æ±‚JSON
        /// </summary>
        public string RequestJson { get; set; }
        /// <summary>
        /// å“åº”JSON
        /// </summary>
        public string ResponseJson { get; set; }
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        public bool IsSuccess { get; set; }
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯
        /// </summary>
        public string ErrorMessage { get; set; }
        /// <summary>
        /// è€—时(毫秒)
        /// </summary>
        public int ElapsedMs { get; set; }
        /// <summary>
        /// åˆ›å»ºäºº
        /// </summary>
        public string Creator { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/OutboundInContainerRequestDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace WIDESEA_DTO.MES
{
    /// <summary>
    /// æ‰˜ç›˜å‡ºç«™è¯·æ±‚DTO
    /// </summary>
    public class OutboundInContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// åº“å­˜ID
        /// </summary>
        public long StockId { get; set; }
        /// <summary>
        /// äº§å“å‚数列表(可选)
        /// </summary>
        public List<ParamItemDto> ParamList { get; set; }
        /// <summary>
        /// å‚数项DTO
        /// </summary>
        public class ParamItemDto
        {
            /// <summary>
            /// å‚数编码
            /// </summary>
            public string ParamCode { get; set; }
            /// <summary>
            /// å‚数值
            /// </summary>
            public string ParamValue { get; set; }
            /// <summary>
            /// é‡‡é›†æ—¶é—´
            /// </summary>
            public string CollectionTime { get; set; }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/UnbindContainerRequestDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace WIDESEA_DTO.MES
{
    /// <summary>
    /// æ‰˜ç›˜ç”µèŠ¯è§£ç»‘è¯·æ±‚DTO
    /// </summary>
    public class UnbindContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// ç”µèŠ¯ç åˆ—è¡¨
        /// </summary>
        public List<string> SfcList { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
using System.Text.Json.Serialization;
using WIDESEA_Common.TaskEnum;
namespace WIDESEA_DTO.Task
{
    /// <summary>
    /// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡Dto
    /// </summary>
    public class CreateManualTaskDto
    {
        /// <summary>
        /// ä»»åŠ¡ç±»åž‹ï¼šå…¥åº“/出库/移库
        /// </summary>
        [JsonPropertyName("taskType")]
        public string TaskType { get; set; }
        /// <summary>
        /// èµ·ç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("sourceAddress")]
        public string SourceAddress { get; set; }
        /// <summary>
        /// ç»ˆç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("targetAddress")]
        public string TargetAddress { get; set; }
        /// <summary>
        /// æ¡ç 
        /// </summary>
        [JsonPropertyName("barcode")]
        public string Barcode { get; set; }
        /// <summary>
        /// ä»“库ID
        /// </summary>
        [JsonPropertyName("warehouseId")]
        public int WarehouseId { get; set; }
        /// <summary>
        /// ä¼˜å…ˆçº§ï¼Œé»˜è®¤1
        /// </summary>
        [JsonPropertyName("grade")]
        public int Grade { get; set; } = 1;
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
using System.Text.Json.Serialization;
namespace WIDESEA_DTO.Task
{
    /// <summary>
    /// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡Dto
    /// </summary>
    public class DispatchTaskDto
    {
        /// <summary>
        /// ä»»åŠ¡ID
        /// </summary>
        [JsonPropertyName("taskId")]
        public long TaskId { get; set; }
        /// <summary>
        /// æ‰˜ç›˜å·
        /// </summary>
        [JsonPropertyName("palletCode")]
        public string PalletCode { get; set; }
        /// <summary>
        /// èµ·ç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("sourceAddress")]
        public string SourceAddress { get; set; }
        /// <summary>
        /// ç»ˆç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("targetAddress")]
        public string TargetAddress { get; set; }
        /// <summary>
        /// ä¼˜å…ˆçº§
        /// </summary>
        [JsonPropertyName("grade")]
        public int Grade { get; set; }
    }
    /// <summary>
    /// ä»»åŠ¡ä¸‹å‘ç»“æžœDto
    /// </summary>
    public class DispatchTaskResultDto
    {
        /// <summary>
        /// ä»»åŠ¡ID
        /// </summary>
        [JsonPropertyName("taskId")]
        public long TaskId { get; set; }
        /// <summary>
        /// ä»»åŠ¡å·
        /// </summary>
        [JsonPropertyName("taskNum")]
        public int TaskNum { get; set; }
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        [JsonPropertyName("success")]
        public bool Success { get; set; }
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯
        /// </summary>
        [JsonPropertyName("errorMessage")]
        public string ErrorMessage { get; set; }
    }
    /// <summary>
    /// æ‰¹é‡ä¸‹å‘结果Dto
    /// </summary>
    public class DispatchResultDto
    {
        /// <summary>
        /// æˆåŠŸæ•°é‡
        /// </summary>
        [JsonPropertyName("successCount")]
        public int SuccessCount { get; set; }
        /// <summary>
        /// å¤±è´¥æ•°é‡
        /// </summary>
        [JsonPropertyName("failCount")]
        public int FailCount { get; set; }
        /// <summary>
        /// å¤±è´¥ä»»åŠ¡åˆ—è¡¨
        /// </summary>
        [JsonPropertyName("failResults")]
        public List<DispatchTaskResultDto> FailResults { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/ReceiveTaskResultDto.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
namespace WIDESEA_DTO.Task
{
    /// <summary>
    /// WCS接收WMS任务的结果Dto
    /// </summary>
    public class ReceiveTaskResultDto
    {
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        public bool Success { get; set; }
        /// <summary>
        /// æ¶ˆæ¯
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        /// æ–°å»ºçš„任务数量
        /// </summary>
        public int CreatedCount { get; set; }
        /// <summary>
        /// é‡å¤/已存在的任务列表
        /// </summary>
        public List<DuplicateTaskDto> DuplicateTasks { get; set; }
    }
    /// <summary>
    /// é‡å¤ä»»åŠ¡ä¿¡æ¯Dto
    /// </summary>
    public class DuplicateTaskDto
    {
        /// <summary>
        /// ä»»åŠ¡å·
        /// </summary>
        public int TaskNum { get; set; }
        /// <summary>
        /// æ‰˜ç›˜å·
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// ä»»åŠ¡ç±»åž‹
        /// </summary>
        public int TaskType { get; set; }
        /// <summary>
        /// å½“前状态
        /// </summary>
        public int TaskStatus { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/UpdateTaskDto.cs
@@ -19,5 +19,15 @@
        /// </summary>
        [Required(ErrorMessage = "新状态不能为空")]
        public int NewStatus { get; set; }
        /// <summary>
        /// NextAddress - ä¸‹ä¸€åœ°å€ï¼Œå¿…填项,用于指定任务的下一位置
        /// </summary>
        public string? NextAddress { get; set; }
        /// <summary>
        /// currentAddress - å½“前地址,必填项,用于指定任务的当前位置
        /// </summary>
        public string? CurrentAddress { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEA_DTO.MES;
namespace WIDESEA_IMesService
{
    /// <summary>
    /// MES接口日志服务接口
    /// </summary>
    public interface IMesLogService : IDependency
    {
        /// <summary>
        /// è®°å½•MES接口调用日志
        /// </summary>
        /// <param name="log">日志DTO</param>
        /// <returns>是否记录成功</returns>
        Task<bool> LogAsync(MesApiLogDto log);
        /// <summary>
        /// èŽ·å–æœ€è¿‘çš„MES接口调用记录
        /// </summary>
        /// <param name="apiType">接口类型</param>
        /// <param name="count">记录数量</param>
        /// <returns>日志列表</returns>
        Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50);
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_IMesService/WIDESEA_IMesService.csproj
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\WIDESEA_DTO\WIDESEA_DTO.csproj" />
    <ProjectReference Include="..\WIDESEA_Core\WIDESEA_Core.csproj" />
  </ItemGroup>
</Project>
Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -103,7 +103,7 @@
        /// <param name="taskId"></param>
        /// <param name="newStatus"></param>
        /// <returns></returns>
        Task<WebResponseContent> UpdateTaskByStatusAsync(int taskId, int newStatus);
        Task<WebResponseContent> UpdateTaskByStatusAsync(UpdateTaskDto taskDto);
        /// <summary>
        /// å †åž›æœºå–放货完成后物流通知化成分容柜完成信号
@@ -160,6 +160,20 @@
        /// <returns></returns>
        Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock);
        /// <summary>
        /// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡
        /// </summary>
        /// <param name="dto">手动创建任务参数</param>
        /// <returns></returns>
        Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto);
        /// <summary>
        /// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ°WCS
        /// </summary>
        /// <param name="dtos">下发任务参数列表</param>
        /// <returns>批量下发结果</returns>
        Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos);
        #region æžå·åº“任务模块
        /// <summary>
Code/WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SqlSugar;
using WIDESEA_Core;
using WIDESEA_DTO.MES;
using WIDESEA_IMesService;
using WIDESEA_Model.Models.Mes;
namespace WIDESEA_MesService
{
    /// <summary>
    /// MES接口日志服务实现
    /// </summary>
    public class MesLogService : IMesLogService
    {
        private readonly ISqlSugarClient _db;
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="db">数据库客户端</param>
        public MesLogService(ISqlSugarClient db)
        {
            _db = db;
        }
        /// <summary>
        /// è®°å½•MES接口调用日志
        /// </summary>
        /// <param name="log">日志DTO</param>
        /// <returns>是否记录成功</returns>
        public async Task<bool> LogAsync(MesApiLogDto log)
        {
            try
            {
                var entity = new Dt_MesApiLog
                {
                    ApiType = log.ApiType,
                    RequestJson = log.RequestJson,
                    ResponseJson = log.ResponseJson,
                    IsSuccess = log.IsSuccess,
                    ErrorMessage = log.ErrorMessage,
                    ElapsedMs = log.ElapsedMs,
                    CreateDate = DateTime.Now,
                    Creator = log.Creator
                };
                var result = await _db.Insertable(entity).ExecuteCommandAsync();
                return result > 0;
            }
            catch (Exception ex)
            {
                // è®°å½•日志失败不影响主流程
                Console.WriteLine($"记录MES日志失败: {ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–æœ€è¿‘çš„MES接口调用记录
        /// </summary>
        /// <param name="apiType">接口类型</param>
        /// <param name="count">记录数量</param>
        /// <returns>日志列表</returns>
        public async Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50)
        {
            try
            {
                var entities = await _db.Queryable<Dt_MesApiLog>()
                    .Where(x => x.ApiType == apiType)
                    .OrderByDescending(x => x.CreateDate)
                    .Take(count)
                    .ToListAsync();
                return entities.Select(e => new MesApiLogDto
                {
                    ApiType = e.ApiType,
                    RequestJson = e.RequestJson,
                    ResponseJson = e.ResponseJson,
                    IsSuccess = e.IsSuccess,
                    ErrorMessage = e.ErrorMessage,
                    ElapsedMs = e.ElapsedMs,
                    Creator = e.Creator
                }).ToList();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取MES日志失败: {ex.Message}");
                return new List<MesApiLogDto>();
            }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_MesService/WIDESEA_MesService.csproj
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\WIDESEA_IMesService\WIDESEA_IMesService.csproj" />
    <ProjectReference Include="..\WIDESEA_Core\WIDESEA_Core.csproj" />
    <ProjectReference Include="..\WIDESEA_Model\WIDESEA_Model.csproj" />
    <ProjectReference Include="..\WIDESEA_DTO\WIDESEA_DTO.csproj" />
  </ItemGroup>
</Project>
Code/WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
using SqlSugar;
using System;
namespace WIDESEA_Model.Models.Mes
{
    /// <summary>
    /// MES接口调用日志实体
    /// </summary>
    [SugarTable("Dt_MesApiLog")]
    public class Dt_MesApiLog
    {
        /// <summary>
        /// ä¸»é”®ID
        /// </summary>
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public long Id { get; set; }
        /// <summary>
        /// æŽ¥å£ç±»åž‹ï¼šInboundInContainer, OutboundInContainer, BindContainer, UnbindContainer, ContainerNgReport
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = false)]
        public string ApiType { get; set; }
        /// <summary>
        /// è¯·æ±‚JSON
        /// </summary>
        [SugarColumn(ColumnDataType = "nvarchar(max)", IsNullable = true)]
        public string RequestJson { get; set; }
        /// <summary>
        /// å“åº”JSON
        /// </summary>
        [SugarColumn(ColumnDataType = "nvarchar(max)", IsNullable = true)]
        public string ResponseJson { get; set; }
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        [SugarColumn(IsNullable = false)]
        public bool IsSuccess { get; set; }
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯
        /// </summary>
        [SugarColumn(Length = 500, IsNullable = true)]
        public string ErrorMessage { get; set; }
        /// <summary>
        /// è€—时(毫秒)
        /// </summary>
        [SugarColumn(IsNullable = false)]
        public int ElapsedMs { get; set; }
        /// <summary>
        /// åˆ›å»ºæ—¶é—´
        /// </summary>
        [SugarColumn(IsNullable = false)]
        public DateTime CreateDate { get; set; }
        /// <summary>
        /// åˆ›å»ºäºº
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true)]
        public string Creator { get; set; }
        /// <summary>
        /// ä¿®æ”¹æ—¶é—´
        /// </summary>
        public DateTime? ModifyDate { get; set; }
        /// <summary>
        /// ä¿®æ”¹äºº
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true)]
        public string Modifier { get; set; }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
ÎļþÒÑɾ³ý
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_AutoOutbound.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,175 @@
using Microsoft.Extensions.Configuration;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region è‡ªåŠ¨å‡ºåº“ä»»åŠ¡
        /// <summary>
        /// è‡ªåŠ¨åˆ›å»ºå‡ºåº“ä»»åŠ¡ - æŸ¥è¯¢åˆ°æœŸåº“存并创建任务
        /// </summary>
        public async Task<WebResponseContent> CreateAutoOutboundTasksAsync()
        {
            try
            {
                // 1. æŸ¥è¯¢åˆ°æœŸåº“å­˜
                var expiredStocks = await _stockInfoService.Repository
                    .QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now
                        && s.StockStatus == StockStatusEmun.入库完成.GetHashCode());
                if (expiredStocks == null || !expiredStocks.Any())
                {
                    return WebResponseContent.Instance.OK("无到期库存需要处理");
                }
                // è¿‡æ»¤æœ‰ä½ç½®ä¸”位置有库存的记录
                expiredStocks = expiredStocks
                    .Where(s => s.LocationDetails != null
                        && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode())
                    .ToList();
                if (!expiredStocks.Any())
                {
                    return WebResponseContent.Instance.OK("无符合条件的到期库存");
                }
                // 2. æ£€æŸ¥å·²å­˜åœ¨çš„任务
                var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList();
                var existingTasks = await Repository.QueryDataAsync(t =>
                    palletCodes.Contains(t.PalletCode)
                    && (t.TaskStatus == TaskStatusEnum.New.GetHashCode()
                        || t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode()
                        || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()));
                var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet();
                // 3. ç­›é€‰éœ€è¦å¤„理的库存
                var stocksToProcess = expiredStocks
                    .Where(s => !processedPallets.Contains(s.PalletCode))
                    .ToList();
                if (!stocksToProcess.Any())
                {
                    return WebResponseContent.Instance.OK("所有到期库存已存在任务");
                }
                // 4. èŽ·å–é…ç½®çš„ç›®æ ‡åœ°å€æ˜ å°„
                var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses")
                    .Get<Dictionary<string, List<string>>>()
                    ?? new Dictionary<string, List<string>>();
                // 5. æ‰¹é‡åˆ›å»ºä»»åŠ¡
                var taskList = new List<Dt_Task>();
                foreach (var stock in stocksToProcess)
                {
                    // æ ¹æ®å··é“确定目标地址(优先根据 Remark ç¡®å®šï¼ŒRemark ä¸ºç©ºåˆ™æ ¹æ®å··é“配置)
                    var targetAddress = DetermineTargetAddressByRemark(
                        stock.Remark ?? "",
                        stock.LocationDetails?.RoadwayNo ?? "",
                        targetAddressMap);
                    var task = new Dt_Task
                    {
                        WarehouseId = stock.WarehouseId,
                        PalletCode = stock.PalletCode,
                        PalletType = stock.PalletType,
                        SourceAddress = stock.LocationCode,
                        CurrentAddress = stock.LocationCode,
                        NextAddress = targetAddress,
                        TargetAddress = targetAddress,
                        Roadway = stock.LocationDetails?.RoadwayNo ?? "",
                        TaskType = TaskTypeEnum.Outbound.GetHashCode(),
                        TaskStatus = TaskStatusEnum.New.GetHashCode(),
                        Grade = 1,
                        TaskNum = await BaseDal.GetTaskNo(),
                        Creater = "system_auto"
                    };
                    taskList.Add(task);
                }
                var transactionResult = await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    var addResult = await BaseDal.AddDataAsync(taskList) > 0;
                    if (!addResult)
                    {
                        return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} ä¸ªä»»åŠ¡");
                    }
                    // ä»»åŠ¡åˆ›å»ºæˆåŠŸåŽï¼ŒåŒæ­¥é”å®šåº“å­˜å’Œè´§ä½çŠ¶æ€ï¼Œé¿å…é‡å¤åˆ†é…
                    var stocksToUpdate = stocksToProcess
                        .Select(s =>
                        {
                            s.StockStatus = StockStatusEmun.出库锁定.GetHashCode();
                            return s;
                        })
                        .ToList();
                    var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate);
                    if (!updateStockResult)
                    {
                        return WebResponseContent.Instance.Error($"任务创建成功,但库存状态更新失败,共 {stocksToUpdate.Count} æ¡");
                    }
                    var locationsToUpdate = stocksToProcess
                        .Where(s => s.LocationDetails != null)
                        .GroupBy(s => s.LocationDetails.Id)
                        .Select(g =>
                        {
                            var location = g.First().LocationDetails;
                            location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
                            return location;
                        })
                        .ToList();
                    if (locationsToUpdate.Any())
                    {
                        var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate);
                        if (!updateLocationResult)
                        {
                            return WebResponseContent.Instance.Error($"任务创建成功,但货位状态更新失败,共 {locationsToUpdate.Count} æ¡");
                        }
                    }
                    return WebResponseContent.Instance.OK();
                });
                if (!transactionResult.Status)
                {
                    return transactionResult;
                }
                // 6. é€šçŸ¥ WCS(异步,不影响主流程)
                _ = Task.Run(() =>
                {
                    try
                    {
                        var wmstaskDtos = _mapper.Map<List<WMSTaskDTO>>(taskList);
                        _httpClientHelper.Post<WebResponseContent>(
                            "http://localhost:9292/api/Task/ReceiveTask",
                            wmstaskDtos.ToJson());
                    }
                    catch (Exception ex)
                    {
                        // WCS é€šçŸ¥å¤±è´¥ä¸å½±å“ä»»åŠ¡åˆ›å»ºï¼Œè®°å½•æ—¥å¿—å³å¯
                        Console.WriteLine($"WCS æ‰¹é‡é€šçŸ¥å¤±è´¥ï¼Œä»»åŠ¡æ•°é‡: {taskList.Count}, é”™è¯¯: {ex.Message}");
                    }
                });
                return WebResponseContent.Instance.OK($"成功创建 {taskList.Count} ä¸ªå‡ºåº“任务", taskList.Count);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"自动创建出库任务失败: {ex.Message}");
            }
        }
        #endregion è‡ªåŠ¨å‡ºåº“ä»»åŠ¡
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,210 @@
using WIDESEA_Common.Constants;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Common.WareHouseEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region å…¥åº“任务
        /// <summary>
        /// åˆ›å»ºä»»åŠ¡ï¼ˆç»„ç›˜å…¥åº“ä»»åŠ¡ã€ç©ºæ‰˜ç›˜å›žåº“ä»»åŠ¡ï¼‰
        /// </summary>
        public async Task<WebResponseContent> CreateTaskInboundAsync(CreateTaskDto taskDto)
        {
            try
            {
                WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
                if (content.Status)
                {
                    return content;
                }
                if (string.IsNullOrWhiteSpace(taskDto.PalletCode) ||
                    string.IsNullOrWhiteSpace(taskDto.Roadway))
                {
                    return WebResponseContent.Instance.Error("无效的任务详情");
                }
                if (taskDto.TaskType != TaskTypeEnum.Inbound && taskDto.TaskType != TaskTypeEnum.InEmpty)
                {
                    return WebResponseContent.Instance.Error("无效的任务详情");
                }
                // ä½¿ç”¨ switch è¡¨è¾¾å¼æ˜ å°„任务类型
                int taskInboundType = taskDto.TaskType switch
                {
                    TaskTypeEnum.Inbound => TaskInboundTypeEnum.Inbound.GetHashCode(),
                    TaskTypeEnum.InEmpty => TaskInboundTypeEnum.InEmpty.GetHashCode(),
                    _ => 0 // ç†è®ºä¸Šä¸ä¼šèµ°åˆ°è¿™é‡Œï¼Œå› ä¸ºå·²ç»éªŒè¯è¿‡äº†
                };
                var task = new Dt_Task
                {
                    TaskNum = await BaseDal.GetTaskNo(),
                    PalletCode = taskDto.PalletCode,
                    PalletType = taskDto.PalletType,
                    Roadway = taskDto.Roadway,
                    TaskType = taskInboundType,
                    TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
                    SourceAddress = taskDto.SourceAddress,
                    TargetAddress = taskDto.TargetAddress,
                    CurrentAddress = taskDto.SourceAddress,
                    NextAddress = taskDto.TargetAddress,
                    WarehouseId = taskDto.WarehouseId,
                    Grade = 1,
                    Creater = "system"
                };
                var result = await Repository.AddDataAsync(task) > 0;
                if (!result) return WebResponseContent.Instance.Error("任务创建失败");
                var wmstaskDto = _mapper.Map<WMSTaskDTO>(task);
                return WebResponseContent.Instance.OK("任务创建成功", wmstaskDto);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"任务创建失败: {ex.Message}");
            }
        }
        /// <summary>
        /// èŽ·å–å¯å…¥åº“è´§ä½
        /// </summary>
        public async Task<WebResponseContent> GetTasksLocationAsync(CreateTaskDto taskDto)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
                if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
                var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway);
                if (locationInfo == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
                    task.CurrentAddress = task.SourceAddress;
                    task.NextAddress = locationInfo.LocationCode;
                    task.TargetAddress = locationInfo.LocationCode;
                    task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode();
                    var updateTaskResult = await BaseDal.UpdateDataAsync(task);
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
                    if (!updateTaskResult || !updateLocationResult)
                    {
                        return WebResponseContent.Instance.Error("任务更新失败");
                    }
                    return WebResponseContent.Instance.OK("任务更新成功", locationInfo.LocationCode);
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"获取任务失败: {ex.Message}");
            }
        }
        /// <summary>
        /// å…¥åº“任务完成:添加库存,修改货位状态,删除任务数据,添加历史任务数据
        /// </summary>
        public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
                if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
                if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                // åˆ¤æ–­æ˜¯ä¸æ˜¯æžå·åº“任务
                if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
                {
                    return await CompleteAgvInboundTaskAsync(taskDto);
                }
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    WebResponseContent content = new WebResponseContent();
                    stockInfo.LocationCode = location.LocationCode;
                    stockInfo.LocationId = location.Id;
                    SetOutboundDateByRoadway(task, stockInfo);
                    stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateLocationResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("任务完成失败");
                    // è°ƒç”¨MES托盘进站
                    //var inboundRequest = new InboundInContainerRequest
                    //{
                    //    EquipmentCode = "STK-GROUP-001",
                    //    ResourceCode = "STK-GROUP-001",
                    //    LocalTime = DateTime.Now,
                    //    ContainerCode = taskDto.PalletCode
                    //};
                    //var inboundResult = _mesService.InboundInContainer(inboundRequest);
                    //if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess)
                    //{
                    //    return content.Error($"任务完成失败:MES进站失败: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "未知错误"}");
                    //}
                    return await CompleteTaskAsync(task, "入库完成");
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ ¹æ®å··é“类型设置库存的出库时间和备注
        /// </summary>
        /// <param name="task">任务信息</param>
        /// <param name="stockInfo">库存信息</param>
        private void SetOutboundDateByRoadway(Dt_Task task, Dt_StockInfo stockInfo)
        {
            var now = DateTime.Now;
            if (task.Roadway.Contains("GW"))
            {
                stockInfo.OutboundDate = string.IsNullOrEmpty(stockInfo.Remark)
                    ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST)
                    : stockInfo.Remark == StockRemarkConstants.GW1
                        ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_SECOND)
                        : now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST);
                stockInfo.Remark = string.IsNullOrEmpty(stockInfo.Remark)
                    ? StockRemarkConstants.GW1
                    : stockInfo.Remark == StockRemarkConstants.GW1
                        ? StockRemarkConstants.GW2
                        : stockInfo.Remark;
            }
            else if (task.Roadway.Contains("CW"))
            {
                stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_CW1);
                if (stockInfo.Remark == StockRemarkConstants.GW2)
                    stockInfo.Remark = StockRemarkConstants.CW1;
            }
            else
            {
                stockInfo.OutboundDate = now;
            }
        }
        #endregion å…¥åº“任务
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Manual.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,328 @@
using System.ComponentModel;
using System.Reflection;
using Newtonsoft.Json;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region æ‰‹åŠ¨ä»»åŠ¡
        /// <summary>
        /// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡
        /// </summary>
        /// <param name="dto">手动创建任务参数</param>
        /// <returns></returns>
        public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto)
        {
            try
            {
                int taskType;
                int taskStatus;
                switch (dto.TaskType)
                {
                    case "入库":
                        taskType = TaskInboundTypeEnum.Inbound.GetHashCode();
                        taskStatus = TaskInStatusEnum.InNew.GetHashCode();
                        break;
                    case "出库":
                        taskType = TaskOutboundTypeEnum.Outbound.GetHashCode();
                        taskStatus = TaskOutStatusEnum.OutNew.GetHashCode();
                        break;
                    case "移库":
                        taskType = TaskRelocationTypeEnum.Relocation.GetHashCode();
                        taskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode();
                        break;
                    default:
                        return WebResponseContent.Instance.Error($"不支持的任务类型: {dto.TaskType}");
                }
                int taskNum = await BaseDal.GetTaskNo();
                var task = new Dt_Task
                {
                    TaskNum = taskNum,
                    PalletCode = dto.Barcode,
                    SourceAddress = dto.SourceAddress,
                    TargetAddress = dto.TargetAddress,
                    TaskType = taskType,
                    TaskStatus = taskStatus,
                    Grade = dto.Grade,
                    Roadway = dto.TargetAddress,
                    WarehouseId = dto.WarehouseId,
                    CurrentAddress = dto.SourceAddress,
                    NextAddress = dto.TargetAddress,
                    Creater = "manual",
                    CreateDate = DateTime.Now,
                    ModifyDate = DateTime.Now
                };
                var wmsTaskDtos = new List<WMSTaskDTO>()
                {
                    new()
                    {
                        TaskNum = task.TaskNum,
                        PalletCode = task.PalletCode,
                        SourceAddress = task.SourceAddress,
                        TargetAddress = task.TargetAddress,
                        TaskType = task.TaskType,
                        Roadway = task.Roadway,
                        TaskStatus = task.TaskStatus,
                        WarehouseId = task.WarehouseId
                    }
                };
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    // ä¿å­˜åˆ°æ•°æ®åº“并同步发送给WCS
                    var result = await BaseDal.AddDataAsync(task) > 0;
                    if (!result)
                        return WebResponseContent.Instance.Error("创建任务失败");
                    var wcsResult = _httpClientHelper.Post<WebResponseContent>(
                        "http://localhost:9292/api/Task/ReceiveManualTask",
                        wmsTaskDtos.ToJson());
                    if (!wcsResult.IsSuccess || !wcsResult.Data.Status)
                        return WebResponseContent.Instance.Error($"任务已创建但发送给WCS失败: {wcsResult.Data?.Message}");
                    return WebResponseContent.Instance.OK($"手动创建任务成功,任务号: {taskNum}");
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"手动创建任务异常: {ex.Message}");
            }
        }
        /// <summary>
        /// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ°WCS(批量处理)
        /// </summary>
        /// <param name="dtos">下发任务参数列表</param>
        /// <returns>批量下发结果</returns>
        public async Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)
        {
            try
            {
                if (dtos == null || !dtos.Any())
                    return WebResponseContent.Instance.Error("请选择要下发的任务");
                var resultDto = new DispatchResultDto
                {
                    SuccessCount = 0,
                    FailCount = 0,
                    FailResults = new List<DispatchTaskResultDto>()
                };
                // ç¬¬ä¸€æ­¥ï¼šæŸ¥è¯¢å¹¶æ ¡éªŒæ‰€æœ‰ä»»åŠ¡çŠ¶æ€ï¼Œæ”¶é›†æœ‰æ•ˆä»»åŠ¡
                var validTasks = new List<(DispatchTaskDto Dto, Dt_Task Task)>();
                foreach (var dto in dtos)
                {
                    var task = await BaseDal.QueryFirstAsync(t => t.TaskId == dto.TaskId);
                    if (task == null)
                    {
                        resultDto.FailResults.Add(new DispatchTaskResultDto
                        {
                            TaskId = dto.TaskId,
                            TaskNum = 0,
                            Success = false,
                            ErrorMessage = "任务不存在"
                        });
                        resultDto.FailCount++;
                        continue;
                    }
                    // æ ¡éªŒä»»åŠ¡çŠ¶æ€ï¼šä»…å…¥åº“æ–°å•/出库新单/移库新单可下发
                    bool canDispatch = false;
                    if (task.TaskType == TaskInboundTypeEnum.Inbound.GetHashCode()
                        && task.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())
                        canDispatch = true;
                    else if (task.TaskType == TaskOutboundTypeEnum.Outbound.GetHashCode()
                        && task.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode())
                        canDispatch = true;
                    else if (task.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode()
                        && task.TaskStatus == TaskRelocationStatusEnum.RelocationNew.GetHashCode())
                        canDispatch = true;
                    if (!canDispatch)
                    {
                        var statusName = GetTaskStatusName(task.TaskType, task.TaskStatus);
                        resultDto.FailResults.Add(new DispatchTaskResultDto
                        {
                            TaskId = dto.TaskId,
                            TaskNum = task.TaskNum,
                            Success = false,
                            ErrorMessage = $"任务状态[{statusName}]不允许下发"
                        });
                        resultDto.FailCount++;
                        continue;
                    }
                    validTasks.Add((dto, task));
                }
                // å¦‚果全部校验失败,直接返回
                if (validTasks.Count == 0)
                    return WebResponseContent.Instance.Error($"下发失败,共{resultDto.FailCount}个任务");
                // ç¬¬äºŒæ­¥ï¼šæž„造所有WMSTaskDTO,一次性调用WCS
                var wmsTaskDtos = validTasks.Select(vt => new WMSTaskDTO
                {
                    Id = vt.Task.TaskId,
                    TaskNum = vt.Task.TaskNum,
                    PalletCode = vt.Dto.PalletCode,
                    SourceAddress = vt.Dto.SourceAddress,
                    TargetAddress = vt.Dto.TargetAddress,
                    CurrentAddress = vt.Dto.SourceAddress,
                    NextAddress = vt.Dto.TargetAddress,
                    TaskType = vt.Task.TaskType,
                    TaskStatus = vt.Task.TaskStatus,
                    Roadway = vt.Task.Roadway,
                    Grade = vt.Dto.Grade,
                    WarehouseId = vt.Task.WarehouseId,
                    PalletType = vt.Task.PalletType
                }).ToList();
                // ä¸€æ¬¡æ€§è°ƒç”¨WCS批量接口
                var wcsResult = _httpClientHelper.Post<WebResponseContent>(
                    "http://localhost:9292/api/Task/ReceiveManualTask",
                    wmsTaskDtos.ToJson());
                if (wcsResult == null || !wcsResult.IsSuccess)
                {
                    // WCS调用失败,所有任务都算失败
                    // å°è¯•从WCS响应中解析错误详情
                    string errorDetail = "";
                    if (wcsResult?.Data != null)
                    {
                        // å°è¯•å°†Data转换为错误信息
                        try
                        {
                            var errorData = wcsResult.Data.ToString();
                            errorDetail = $"WCS错误: {errorData}";
                        }
                        catch
                        {
                            errorDetail = wcsResult?.ErrorMessage ?? "WCS响应异常";
                        }
                    }
                    else
                    {
                        errorDetail = wcsResult?.ErrorMessage ?? "WCS响应异常";
                    }
                    foreach (var vt in validTasks)
                    {
                        resultDto.FailResults.Add(new DispatchTaskResultDto
                        {
                            TaskId = vt.Dto.TaskId,
                            TaskNum = vt.Task.TaskNum,
                            Success = false,
                            ErrorMessage = $"{errorDetail} (任务号:{vt.Task.TaskNum}, æ‰˜ç›˜:{vt.Dto.PalletCode})"
                        });
                        resultDto.FailCount++;
                    }
                    resultDto.SuccessCount = 0;
                    return WebResponseContent.Instance.Error($"WCS批量下发失败,共{resultDto.FailCount}个任务");
                }
                // WCS调用成功,解析返回的结构化数据
                ReceiveTaskResultDto wcsResultData = null;
                try
                {
                    if (wcsResult.Data?.Data != null)
                    {
                        var jsonStr = wcsResult.Data.Data.ToString();
                        if (!string.IsNullOrEmpty(jsonStr) && jsonStr.Contains("duplicateTasks"))
                        {
                            wcsResultData = JsonConvert.DeserializeObject<ReceiveTaskResultDto>(jsonStr);
                        }
                    }
                }
                catch (Exception ex)
                {
                    // è§£æžWCS返回数据失败,记录日志但继续处理
                    Console.WriteLine($"解析WCS返回数据异常: {ex.Message}");
                }
                // å¦‚果有重复任务,记录到失败结果中
                if (wcsResultData?.DuplicateTasks != null && wcsResultData.DuplicateTasks.Count > 0)
                {
                    foreach (var dup in wcsResultData.DuplicateTasks)
                    {
                        var statusName = GetTaskStatusName(dup.TaskType, dup.TaskStatus);
                        resultDto.FailResults.Add(new DispatchTaskResultDto
                        {
                            TaskId = 0, // é‡å¤ä»»åŠ¡å¯èƒ½æ²¡æœ‰WMS的TaskId
                            TaskNum = dup.TaskNum,
                            Success = false,
                            ErrorMessage = $"WCS中已存在该任务(托盘:{dup.PalletCode}, çŠ¶æ€:{statusName})"
                        });
                        resultDto.FailCount++;
                    }
                }
                // ç¬¬ä¸‰æ­¥ï¼šWCS调用成功后,批量更新DB
                foreach (var vt in validTasks)
                {
                    vt.Task.PalletCode = vt.Dto.PalletCode;
                    vt.Task.SourceAddress = vt.Dto.SourceAddress;
                    vt.Task.TargetAddress = vt.Dto.TargetAddress;
                    vt.Task.CurrentAddress = vt.Dto.SourceAddress;
                    vt.Task.NextAddress = vt.Dto.TargetAddress;
                    vt.Task.Grade = vt.Dto.Grade;
                    vt.Task.Dispatchertime = DateTime.Now;
                }
                // æ‰¹é‡æ›´æ–°DB
                var tasksToUpdate = validTasks.Select(vt => vt.Task).ToList();
                await BaseDal.UpdateDataAsync(tasksToUpdate);
                resultDto.SuccessCount = validTasks.Count - (wcsResultData?.DuplicateTasks?.Count ?? 0);
                if (resultDto.FailCount == 0)
                    return WebResponseContent.Instance.OK($"成功下发{resultDto.SuccessCount}个任务", resultDto);
                var errorResponse = WebResponseContent.Instance.Error($"部分下发成功{resultDto.SuccessCount}个,失败{resultDto.FailCount}个");
                errorResponse.Data = resultDto;
                return errorResponse;
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"下发任务异常: {ex.Message}");
            }
        }
        /// <summary>
        /// èŽ·å–ä»»åŠ¡çŠ¶æ€åç§°
        /// </summary>
        /// <param name="taskType">任务类型</param>
        /// <param name="taskStatus">任务状态</param>
        /// <returns>状态的中文描述</returns>
        private string GetTaskStatusName(int taskType, int taskStatus)
        {
            FieldInfo? fieldInfo = taskType switch
            {
                _ when taskType == TaskInboundTypeEnum.Inbound.GetHashCode() => typeof(TaskInStatusEnum).GetField(((TaskInStatusEnum)taskStatus).ToString()),
                _ when taskType == TaskOutboundTypeEnum.Outbound.GetHashCode() => typeof(TaskOutStatusEnum).GetField(((TaskOutStatusEnum)taskStatus).ToString()),
                _ when taskType == TaskRelocationTypeEnum.Relocation.GetHashCode() => typeof(TaskRelocationStatusEnum).GetField(((TaskRelocationStatusEnum)taskStatus).ToString()),
                _ => null
            };
            var descAttr = fieldInfo?.GetCustomAttributes(typeof(DescriptionAttribute), false)
                .FirstOrDefault() as DescriptionAttribute;
            return descAttr?.Description ?? taskStatus.ToString();
        }
        #endregion æ‰‹åŠ¨ä»»åŠ¡
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
using WIDESEA_Common.Constants;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Common.WareHouseEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region å‡ºåº“任务
        /// <summary>
        /// æ ¹æ®æŒ‡å®šçš„任务详情异步创建新的出库任务
        /// </summary>
        public async Task<WebResponseContent> CreateTaskOutboundAsync(CreateTaskDto taskDto)
        {
            try
            {
                var stockResult = await _stockInfoService.GetStockInfoAsync(taskDto.WarehouseId);
                if (stockResult == null || !stockResult.Any())
                    return WebResponseContent.Instance.Error("未找到库存信息");
                var taskList = stockResult.Select(item => new Dt_Task
                {
                    WarehouseId = item.WarehouseId,
                    PalletCode = item.PalletCode,
                    PalletType = item.PalletType,
                    SourceAddress = item.LocationCode,
                    TargetAddress = taskDto.TargetAddress,
                    Roadway = item.LocationDetails.RoadwayNo,
                    TaskType = TaskTypeEnum.Outbound.GetHashCode(),
                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
                    Grade = 1,
                    TaskNum = 0,
                    CurrentAddress = item.LocationCode,
                    NextAddress = taskDto.TargetAddress,
                    Creater = "system",
                }).ToList();
                var result = await BaseDal.AddDataAsync(taskList) > 0;
                var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null;
                return WebResponseContent.Instance.OK(result ? "任务创建成功" : "任务创建失败", wmstaskDto ?? new object());
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"任务创建失败: {ex.Message}");
            }
        }
        /// <summary>
        /// å‡ºåº“任务完成 ï¼šä¿®æ”¹åº“存,修改货位状态,删除任务数据,添加历史任务数据
        /// </summary>
        public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
                if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
                if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                // åˆ¤æ–­æ˜¯ä¸æ˜¯æžå·åº“任务
                if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
                {
                    OutTaskCompleteDto outTaskCompleteDto = new OutTaskCompleteDto()
                    {
                        TaskId = task.OrderNo ?? string.Empty,
                        DevId = task.TargetAddress ?? string.Empty,
                        ReqTime = DateTime.Now.ToString()
                    };
                    return await OutTaskComplete(outTaskCompleteDto);
                }
                WebResponseContent content = new WebResponseContent();
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    stockInfo.LocationId = 0;
                    stockInfo.LocationCode = string.Empty;
                    stockInfo.OutboundDate = DateTime.Now;
                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateLocationResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("任务完成失败");
                    // é«˜æ¸©2号出库到CWSC1时,自动创建入库任务到常温1号巷道
                    WMSTaskDTO? inboundTaskDto = null;
                    if (task.TargetAddress == TaskAddressConstants.GW2_ADDRESS)
                    {
                        var inboundTask = new Dt_Task
                        {
                            TaskNum = await BaseDal.GetTaskNo(),
                            PalletCode = task.PalletCode,
                            PalletType = task.PalletType,
                            Roadway = "CW1",
                            TaskType = TaskInboundTypeEnum.Inbound.GetHashCode(),
                            TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
                            SourceAddress = task.TargetAddress,
                            TargetAddress = task.TargetAddress,
                            CurrentAddress = task.TargetAddress,
                            NextAddress = task.TargetAddress,
                            WarehouseId = (int)WarehouseEnum.CW1,
                            Grade = 1,
                            Creater = "system_auto"
                        };
                        await Repository.AddDataAsync(inboundTask);
                        inboundTaskDto = _mapper.Map<WMSTaskDTO>(inboundTask);
                    }
                    var completeResult = await CompleteTaskAsync(task, "出库完成");
                    if (!completeResult.Status)
                        return completeResult;
                    // è¿”回入库任务信息(如果有)
                    if (inboundTaskDto != null)
                    {
                        return content.OK("出库完成,已创建入库任务", inboundTaskDto);
                    }
                    return content.OK("出库完成");
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
            }
        }
        #endregion å‡ºåº“任务
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Relocation.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Task;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region ç§»åº“任务
        /// <summary>
        /// ç§»åº“任务完成:修改库存位置与状态,修改源/目标货位状态,删除任务数据
        /// </summary>
        public async Task<WebResponseContent> RelocationFinishTaskAsync(CreateTaskDto taskDto)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s =>
                    s.PalletCode == taskDto.PalletCode &&
                    s.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode());
                if (task == null) return WebResponseContent.Instance.Error("未找到对应的移库任务");
                var sourceLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
                if (sourceLocation == null) return WebResponseContent.Instance.Error("未找到移库源货位");
                var targetLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
                if (targetLocation == null) return WebResponseContent.Instance.Error("未找到移库目标货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    stockInfo.LocationCode = targetLocation.LocationCode;
                    stockInfo.LocationId = targetLocation.Id;
                    stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
                    sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                    targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                    var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation);
                    var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateSourceResult || !updateTargetResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("移库任务完成失败");
                    return await CompleteTaskAsync(task, "移库完成");
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
            }
        }
        #endregion ç§»åº“任务
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Robot.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Stock;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region æœºæ¢°æ‰‹ä»»åŠ¡
        /// <summary>
        /// åˆ›å»ºæœºæ¢°æ‰‹ç»„盘任务
        /// </summary>
        public async Task<WebResponseContent> CreateRobotGroupPalletTaskAsync(StockDTO stock)
        {
            return await CreateRobotPalletTaskAsync(
                stock,
                "组盘",
                RobotTaskTypeEnum.GroupPallet,
                s => string.IsNullOrWhiteSpace(s.TargetPalletNo) ? s.SourcePalletNo : s.TargetPalletNo,
                requireStockWithoutLocation: false);
        }
        /// <summary>
        /// åˆ›å»ºæœºæ¢°æ‰‹æ¢ç›˜ä»»åŠ¡
        /// </summary>
        public async Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock)
        {
            return await CreateRobotPalletTaskAsync(
                stock,
                "换盘",
                RobotTaskTypeEnum.ChangePallet,
                s => s.SourcePalletNo,
                requireStockWithoutLocation: true,
                stockPalletCodeSelector: s => s.SourcePalletNo);
        }
        /// <summary>
        /// åˆ›å»ºæœºæ¢°æ‰‹æ‹†ç›˜ä»»åŠ¡
        /// </summary>
        public async Task<WebResponseContent> CreateRobotSplitPalletTaskAsync(StockDTO stock)
        {
            return await CreateRobotPalletTaskAsync(
                stock,
                "拆盘",
                RobotTaskTypeEnum.SplitPallet,
                s => s.SourcePalletNo,
                requireStockWithoutLocation: true,
                stockPalletCodeSelector: s => s.SourcePalletNo);
        }
        /// <summary>
        /// åˆ›å»ºæœºæ¢°æ‰‹ä»»åŠ¡çš„é€šç”¨æ–¹æ³•
        /// </summary>
        /// <param name="stock">库存信息</param>
        /// <param name="taskName">任务名称(组盘/换盘/拆盘)</param>
        /// <param name="taskType">机械手任务类型</param>
        /// <param name="palletCodeSelector">托盘号选择器</param>
        /// <param name="requireStockWithoutLocation">是否要求库存未绑定货位</param>
        /// <param name="stockPalletCodeSelector">库存查询用的托盘号选择器</param>
        /// <returns>创建结果</returns>
        private async Task<WebResponseContent> CreateRobotPalletTaskAsync(
            StockDTO stock,
            string taskName,
            RobotTaskTypeEnum taskType,
            Func<StockDTO, string?> palletCodeSelector,
            bool requireStockWithoutLocation,
            Func<StockDTO, string?>? stockPalletCodeSelector = null)
        {
            try
            {
                if (stock == null)
                    return WebResponseContent.Instance.Error("任务参数不能为空");
                var palletCode = palletCodeSelector(stock)?.Trim();
                if (string.IsNullOrWhiteSpace(palletCode))
                    return WebResponseContent.Instance.Error("托盘号不能为空");
                var sourceLineNo = stock.SourceLineNo?.Trim();
                var targetLineNo = stock.TargetLineNo?.Trim();
                if (string.IsNullOrWhiteSpace(sourceLineNo) || string.IsNullOrWhiteSpace(targetLineNo))
                    return WebResponseContent.Instance.Error("来源线体编号和目标线体编号不能为空");
                var existingTask = await BaseDal.QueryFirstAsync(t =>
                    t.PalletCode == palletCode &&
                    (t.TaskStatus == TaskRobotStatusEnum.RobotNew.GetHashCode()
                     || t.TaskStatus == TaskRobotStatusEnum.RobotExecuting.GetHashCode()
                     || t.TaskStatus == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()
                     || t.TaskStatus == TaskRobotStatusEnum.RobotPutFinish.GetHashCode()
                     || t.TaskStatus == TaskRobotStatusEnum.RobotPending.GetHashCode()));
                if (existingTask != null)
                    return WebResponseContent.Instance.Error($"托盘[{palletCode}]已存在未完成任务");
                Dt_StockInfo? stockInfo = null;
                if (requireStockWithoutLocation)
                {
                    var stockPalletCode = (stockPalletCodeSelector ?? palletCodeSelector).Invoke(stock)?.Trim();
                    if (string.IsNullOrWhiteSpace(stockPalletCode))
                        return WebResponseContent.Instance.Error("源托盘号不能为空");
                    stockInfo = await _stockInfoService.GetStockInfoAsync(stockPalletCode);
                    if (stockInfo == null)
                        return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存不存在");
                    if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode))
                        return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存已绑定货位,不能创建机械手{taskName}任务");
                }
                var section = App.Configuration.GetSection("RobotTaskAddressRules").GetSection(targetLineNo).GetChildren().Select(c => c.Value).ToArray();
                if (section.Length < 2)
                    return WebResponseContent.Instance.Error($"未找到线体[{targetLineNo}]的地址配置");
                var task = new Dt_Task
                {
                    TaskNum = await BaseDal.GetTaskNo(),
                    PalletCode = palletCode,
                    PalletType = stockInfo?.PalletType ?? 0,
                    Roadway = stock.Roadway ?? string.Empty,
                    TaskType = taskType.GetHashCode(),
                    TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(),
                    SourceAddress = section[0]!,
                    TargetAddress = section[1]!,
                    CurrentAddress = targetLineNo,
                    NextAddress = targetLineNo,
                    WarehouseId = stockInfo?.WarehouseId ?? 1,
                    Grade = 1,
                    Remark = $"机械手{taskName}",
                    Creater = "system"
                };
                var result = await Repository.AddDataAsync(task) > 0;
                if (!result)
                    return WebResponseContent.Instance.Error($"机械手{taskName}任务创建失败");
                var wmstaskDto = _mapper.Map<WMSTaskDTO>(task) ?? new WMSTaskDTO();
                return WebResponseContent.Instance.OK($"机械手{taskName}任务创建成功", wmstaskDto);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"机械手{taskName}任务创建失败: {ex.Message}");
            }
        }
        #endregion æœºæ¢°æ‰‹ä»»åŠ¡
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_TaskStatus.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Task;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region ä»»åŠ¡çŠ¶æ€ç®¡ç†
        /// <summary>
        /// ä¿®æ”¹ä»»åŠ¡çŠ¶æ€ï¼ˆæ ¹æ®ä»»åŠ¡ID修改为指定状态)
        /// </summary>
        /// <param name="taskDto">任务状态更新参数</param>
        /// <returns>修改结果</returns>
        public async Task<WebResponseContent> UpdateTaskByStatusAsync(UpdateTaskDto taskDto)
        {
            try
            {
                var taskInfo = await BaseDal.QueryFirstAsync(s => s.TaskNum == taskDto.Id);
                if (taskInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的任务");
                taskInfo.TaskStatus = taskDto.NewStatus;
                taskInfo.NextAddress = taskDto.NextAddress;
                taskInfo.CurrentAddress = taskDto.CurrentAddress;
                await BaseDal.UpdateDataAsync(taskInfo);
                return WebResponseContent.Instance.OK("修改成功", taskInfo);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"修改失败: {ex.Message}");
            }
        }
        #endregion ä»»åŠ¡çŠ¶æ€ç®¡ç†
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Tray.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,197 @@
using Mapster;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region ç©ºæ‰˜ç›˜ä»»åŠ¡
        /// <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å…¥åº“任务
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto)
        {
            try
            {
                WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
                if (content.Status)
                {
                    return content;
                }
                return WebResponseContent.Instance.OK("查询成功");
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ç©ºæ‰˜ç›˜å…¥åº“完成
        /// </summary>
        public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
                if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
                if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    stockInfo.LocationCode = location.LocationCode;
                    stockInfo.LocationId = location.Id;
                    stockInfo.StockStatus = StockStatusEmun.空托盘库存.GetHashCode();
                    location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateLocationResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("任务完成失败");
                    var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "空托盘入库完成");
                    if (!saveTaskHistoryResult.Status)
                        return saveTaskHistoryResult;
                    var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "空托盘入库完成");
                    if (!saveStockHistoryResult.Status)
                        return saveStockHistoryResult;
                    var deleteResult = await BaseDal.DeleteDataAsync(task);
                    if (!deleteResult) return WebResponseContent.Instance.Error("任务完成失败");
                    return WebResponseContent.Instance.OK("任务完成");
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
            }
        }
        /// <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å‡ºåº“任务
        /// </summary>
        /// <param name="taskDto"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto)
        {
            try
            {
                var dt_Task = await BaseDal.QueryFirstAsync(x => x.TargetAddress == taskDto.TargetAddress);
                if (dt_Task != null)
                {
                    var taskDTO = dt_Task.Adapt<WMSTaskDTO>();
                    return WebResponseContent.Instance.OK("任务创建成功", taskDTO);
                }
                var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空托盘库存.GetHashCode());
                if (stockInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的库存信息");
                var task = new Dt_Task()
                {
                    WarehouseId = stockInfo.LocationDetails.WarehouseId,
                    PalletCode = stockInfo.PalletCode,
                    PalletType = stockInfo.PalletType,
                    SourceAddress = stockInfo.LocationCode,
                    CurrentAddress = stockInfo.LocationCode,
                    NextAddress = taskDto.TargetAddress,
                    TargetAddress = taskDto.TargetAddress,
                    Roadway = stockInfo.LocationDetails.RoadwayNo,
                    TaskType = TaskOutboundTypeEnum.OutEmpty.GetHashCode(),
                    TaskStatus = TaskStatusEnum.New.GetHashCode(),
                    Grade = 1,
                    TaskNum = await BaseDal.GetTaskNo(),
                    Creater = "system",
                };
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationId);
                    locationInfo.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
                    if (!updateLocationResult)
                        return WebResponseContent.Instance.Error("任务创建失败");
                    var taskDtos = task.Adapt<WMSTaskDTO>();
                    var addResult = await BaseDal.AddDataAsync(task) > 0;
                    if (!addResult)
                        return WebResponseContent.Instance.Error("任务创建失败");
                    return WebResponseContent.Instance.OK("任务创建成功", taskDtos);
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ç©ºæ‰˜ç›˜å‡ºåº“完成
        /// </summary>
        public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
                if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
                var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
                if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
                var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
                return await _unitOfWorkManage.BeginTranAsync(async () =>
                {
                    stockInfo.LocationId = 0;
                    stockInfo.LocationCode = string.Empty;
                    stockInfo.StockStatus = StockStatusEmun.出库完成.GetHashCode();
                    location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
                    var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
                    var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
                    if (!updateLocationResult || !updateStockResult)
                        return WebResponseContent.Instance.Error("任务完成失败");
                    var saveTaskHistoryResult = await SaveTaskHistoryAsync(task, "空托盘出库完成");
                    if (!saveTaskHistoryResult.Status)
                        return saveTaskHistoryResult;
                    var saveStockHistoryResult = await SaveStockHistoryAsync(stockInfo, "空托盘出库完成");
                    if (!saveStockHistoryResult.Status)
                        return saveStockHistoryResult;
                    var deleteResult = await BaseDal.DeleteDataAsync(task);
                    if (!deleteResult) return WebResponseContent.Instance.Error("任务完成失败");
                    return WebResponseContent.Instance.OK("任务完成");
                });
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
            }
        }
        #endregion ç©ºæ‰˜ç›˜ä»»åŠ¡
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs
@@ -4,8 +4,14 @@
using WIDESEA_Core;
using WIDESEA_Core.BaseController;
using WIDESEA_DTO.Stock;
using WIDESEA_DTO.MES;
using WIDESEA_IStockService;
using WIDESEA_IBasicService;
using WIDESEA_IMesService;
using WIDESEA_ISystemService;
using WIDESEA_Model.Models;
using WIDESEA_Common.StockEnum;
using System.Diagnostics;
namespace WIDESEA_WMSServer.Controllers.Stock
{
@@ -16,8 +22,19 @@
    [ApiController]
    public class StockInfoController : ApiBaseController<IStockInfoService, Dt_StockInfo>
    {
        public StockInfoController(IStockInfoService service) : base(service)
        private readonly IMesLogService _mesLogService;
        private readonly IMesService _mesService;
        private readonly ISys_DictionaryService _sysDictionaryService;
        public StockInfoController(
            IStockInfoService service,
            IMesLogService mesLogService,
            IMesService mesService,
            ISys_DictionaryService sysDictionaryService) : base(service)
        {
            _mesLogService = mesLogService;
            _mesService = mesService;
            _sysDictionaryService = sysDictionaryService;
        }
        /// <summary>
@@ -31,5 +48,250 @@
            var result = await Service.Get3DLayoutAsync(warehouseId);
            return WebResponseContent.Instance.OK(data: result);
        }
        /// <summary>
        /// æ‰˜ç›˜è¿›ç«™ - è°ƒç”¨MES接口
        /// </summary>
        /// <param name="dto">进站请求参数</param>
        /// <returns>操作结果</returns>
        [HttpPost("inboundInContainer")]
        public async Task<WebResponseContent> InboundInContainer([FromBody] InboundInContainerRequestDto dto)
        {
            var response = new WebResponseContent();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                // 1. å‚数验证
                if (string.IsNullOrWhiteSpace(dto.PalletCode))
                {
                    return response.Error("托盘编号不能为空");
                }
                // 2. æŸ¥è¯¢åº“存信息 - ä½¿ç”¨å•条记录查询方法提高效率
                var stockInfo = await Service.Repository.QueryDataNavFirstAsync(x => x.Id == dto.StockId);
                if (stockInfo == null)
                {
                    return response.Error("库存信息不存在");
                }
                // 3. éªŒè¯åº“存状态(仅"入库完成"状态允许进站)
                if (stockInfo.StockStatus != StockStatusEmun.入库完成.GetHashCode())
                {
                    return response.Error($"当前库存状态不允许进站操作,当前状态:{stockInfo.StockStatus}");
                }
                // 4. èŽ·å–ç³»ç»Ÿé…ç½® - ç›´æŽ¥ä»Žæ•°æ®åº“查询
                var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" });
                string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001");
                string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001");
                // 5. æž„造MES请求
                var mesRequest = new InboundInContainerRequest
                {
                    EquipmentCode = equipmentCode,
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainerCode = dto.PalletCode
                };
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 6. è°ƒç”¨MES接口(同步方法)
                var mesResult = _mesService.InboundInContainer(mesRequest);
                stopwatch.Stop();
                // 7. è®°å½•日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "InboundInContainer",
                    RequestJson = requestJson,
                    ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
                    IsSuccess = mesResult.IsSuccess,
                    ErrorMessage = mesResult.ErrorMessage,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                // 8. è¿”回结果
                if (mesResult.IsSuccess)
                {
                    return response.OK("托盘进站成功");
                }
                else
                {
                    return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
                }
            }
            catch (System.Exception ex)
            {
                stopwatch.Stop();
                // è®°å½•错误日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "InboundInContainer",
                    IsSuccess = false,
                    ErrorMessage = ex.Message,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                return response.Error($"托盘进站失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ‰˜ç›˜å‡ºç«™ - è°ƒç”¨MES接口
        /// </summary>
        /// <param name="dto">出站请求参数</param>
        /// <returns>操作结果</returns>
        [HttpPost("outboundInContainer")]
        public async Task<WebResponseContent> OutboundInContainer([FromBody] OutboundInContainerRequestDto dto)
        {
            var response = new WebResponseContent();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                // 1. å‚数验证
                if (string.IsNullOrWhiteSpace(dto.PalletCode))
                {
                    return response.Error("托盘编号不能为空");
                }
                // 2. æŸ¥è¯¢åº“存信息 - ä½¿ç”¨å•条记录查询方法提高效率
                var stockInfo = await Service.Repository.QueryDataNavFirstAsync(x => x.Id == dto.StockId);
                if (stockInfo == null)
                {
                    return response.Error("库存信息不存在");
                }
                // 3. éªŒè¯åº“存状态("出库锁定"或"出库完成"状态允许出站)
                var allowedStatuses = new[]
                {
                    StockStatusEmun.出库锁定.GetHashCode(),
                    StockStatusEmun.出库完成.GetHashCode()
                };
                if (!allowedStatuses.Contains(stockInfo.StockStatus))
                {
                    return response.Error($"当前库存状态不允许出站操作,当前状态:{stockInfo.StockStatus}");
                }
                // 4. èŽ·å–ç³»ç»Ÿé…ç½®
                var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" });
                string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001");
                string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001");
                // 5. æž„造MES请求
                var mesRequest = new OutboundInContainerRequest
                {
                    EquipmentCode = equipmentCode,
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainerCode = dto.PalletCode,
                    ParamList = dto.ParamList?.Select(p => new ParamItem
                    {
                        ParamCode = p.ParamCode,
                        ParamValue = p.ParamValue,
                        CollectionTime = DateTime.TryParse(p.CollectionTime, out var ct) ? ct : DateTime.Now
                    }).ToList()
                };
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 6. è°ƒç”¨MES接口(同步方法)
                var mesResult = _mesService.OutboundInContainer(mesRequest);
                stopwatch.Stop();
                // 7. è®°å½•日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "OutboundInContainer",
                    RequestJson = requestJson,
                    ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
                    IsSuccess = mesResult.IsSuccess,
                    ErrorMessage = mesResult.ErrorMessage,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                // 8. è¿”回结果
                if (mesResult.IsSuccess)
                {
                    return response.OK("托盘出站成功");
                }
                else
                {
                    return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
                }
            }
            catch (System.Exception ex)
            {
                stopwatch.Stop();
                // è®°å½•错误日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "OutboundInContainer",
                    IsSuccess = false,
                    ErrorMessage = ex.Message,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                return response.Error($"托盘出站失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ä»Žé…ç½®å­—典中获取配置值
        /// </summary>
        /// <param name="configs">配置字典列表</param>
        /// <param name="key">配置键</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns>配置值</returns>
        private string GetConfigValue(System.Collections.Generic.List<WIDESEA_DTO.System.VueDictionaryDTO> configs, string key, string defaultValue = "")
        {
            if (configs != null)
            {
                var config = configs.FirstOrDefault(c => c.DicNo == key);
                if (config != null && config.Data != null)
                {
                    // Data是dynamic类型,尝试获取第一个元素的value属性
                    try
                    {
                        // ä½¿ç”¨dynamic来访问匿名类型的属性
                        dynamic data = config.Data;
                        if (data != null)
                        {
                            // data可能是IEnumerable或者单个对象
                            var enumerable = data as System.Collections.IEnumerable;
                            if (enumerable != null)
                            {
                                foreach (var item in enumerable)
                                {
                                    // èŽ·å–ç¬¬ä¸€ä¸ªå…ƒç´ 
                                    dynamic firstItem = item;
                                    var value = firstItem.value;
                                    return value?.ToString() ?? defaultValue;
                                }
                            }
                        }
                    }
                    catch
                    {
                        // å¦‚果无法获取,返回默认值
                        return defaultValue;
                    }
                }
            }
            return defaultValue;
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs
@@ -1,8 +1,14 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core;
using WIDESEA_Core.BaseController;
using WIDESEA_DTO.MES;
using WIDESEA_IStockService;
using WIDESEA_IBasicService;
using WIDESEA_IMesService;
using WIDESEA_ISystemService;
using WIDESEA_Model.Models;
using System.Diagnostics;
namespace WIDESEA_WMSServer.Controllers.Stock
{
@@ -13,8 +19,355 @@
    [ApiController]
    public class StockInfoDetailController : ApiBaseController<IStockInfoDetailService, Dt_StockInfoDetail>
    {
        public StockInfoDetailController(IStockInfoDetailService service) : base(service)
        private readonly IMesLogService _mesLogService;
        private readonly IMesService _mesService;
        private readonly ISys_DictionaryService _sysDictionaryService;
        public StockInfoDetailController(
            IStockInfoDetailService service,
            IMesLogService mesLogService,
            IMesService mesService,
            ISys_DictionaryService sysDictionaryService) : base(service)
        {
            _mesLogService = mesLogService;
            _mesService = mesService;
            _sysDictionaryService = sysDictionaryService;
        }
        /// <summary>
        /// æ‰˜ç›˜ç”µèŠ¯ç»‘å®š - è°ƒç”¨MES接口
        /// </summary>
        /// <param name="dto">绑定请求参数</param>
        /// <returns>操作结果</returns>
        [HttpPost("bindContainer")]
        public async Task<WebResponseContent> BindContainer([FromBody] BindContainerRequestDto dto)
        {
            var response = new WebResponseContent();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                // 1. å‚数验证
                if (string.IsNullOrWhiteSpace(dto.PalletCode))
                {
                    return response.Error("托盘编号不能为空");
                }
                if (dto.SfcList == null || !dto.SfcList.Any())
                {
                    return response.Error("电芯码列表不能为空");
                }
                // 2. éªŒè¯ç”µèŠ¯çŠ¶æ€ï¼ˆéž'已锁定'状态允许绑定)
                var stockDetails = await Service.Repository.QueryDataAsync(x => dto.SfcList.Contains(x.SerialNumber));
                if (stockDetails != null && stockDetails.Any(d => d.Status == 99))
                {
                    return response.Error("当前库存明细包含已锁定状态,不允许执行绑定操作");
                }
                // 3. èŽ·å–ç³»ç»Ÿé…ç½®
                var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" });
                string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001");
                string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001");
                // 3. æž„造MES请求 - å°†ç”µèŠ¯åˆ—è¡¨è½¬æ¢ä¸ºContainerSfcItem格式
                var mesRequest = new BindContainerRequest
                {
                    EquipmentCode = equipmentCode,
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainerCode = dto.PalletCode,
                    ContainerSfcList = dto.SfcList.Select(sfc => new ContainerSfcItem
                    {
                        Sfc = sfc,
                        Location = dto.Location ?? ""
                    }).ToList(),
                    OperationType = dto.OperationType
                };
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 4. è°ƒç”¨MES接口(同步方法)
                var mesResult = _mesService.BindContainer(mesRequest);
                stopwatch.Stop();
                // 5. è®°å½•日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "BindContainer",
                    RequestJson = requestJson,
                    ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
                    IsSuccess = mesResult.IsSuccess,
                    ErrorMessage = mesResult.ErrorMessage,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                // 6. è¿”回结果
                if (mesResult.IsSuccess)
                {
                    return response.OK("托盘电芯绑定成功");
                }
                else
                {
                    return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
                }
            }
            catch (System.Exception ex)
            {
                stopwatch.Stop();
                // è®°å½•错误日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "BindContainer",
                    IsSuccess = false,
                    ErrorMessage = ex.Message,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                return response.Error($"托盘电芯绑定失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ‰˜ç›˜ç”µèŠ¯è§£ç»‘ - è°ƒç”¨MES接口
        /// </summary>
        /// <param name="dto">解绑请求参数</param>
        /// <returns>操作结果</returns>
        [HttpPost("unbindContainer")]
        public async Task<WebResponseContent> UnbindContainer([FromBody] UnbindContainerRequestDto dto)
        {
            var response = new WebResponseContent();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                // 1. å‚数验证
                if (string.IsNullOrWhiteSpace(dto.PalletCode))
                {
                    return response.Error("托盘编号不能为空");
                }
                if (dto.SfcList == null || !dto.SfcList.Any())
                {
                    return response.Error("电芯码列表不能为空");
                }
                // 2. éªŒè¯ç”µèŠ¯çŠ¶æ€ï¼ˆéž'已锁定'状态允许解绑)
                var stockDetails = await Service.Repository.QueryDataAsync(x => dto.SfcList.Contains(x.SerialNumber));
                if (stockDetails != null && stockDetails.Any(d => d.Status == 99))
                {
                    return response.Error("当前库存明细包含已锁定状态,不允许执行解绑操作");
                }
                // 3. èŽ·å–ç³»ç»Ÿé…ç½®
                var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" });
                string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001");
                string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001");
                // 3. æž„造MES请求
                var mesRequest = new UnBindContainerRequest
                {
                    EquipmentCode = equipmentCode,
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainCode = dto.PalletCode,
                    SfcList = dto.SfcList
                };
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 4. è°ƒç”¨MES接口(同步方法)
                var mesResult = _mesService.UnBindContainer(mesRequest);
                stopwatch.Stop();
                // 5. è®°å½•日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "UnbindContainer",
                    RequestJson = requestJson,
                    ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
                    IsSuccess = mesResult.IsSuccess,
                    ErrorMessage = mesResult.ErrorMessage,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                // 6. è¿”回结果
                if (mesResult.IsSuccess)
                {
                    return response.OK("托盘电芯解绑成功");
                }
                else
                {
                    return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
                }
            }
            catch (System.Exception ex)
            {
                stopwatch.Stop();
                // è®°å½•错误日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "UnbindContainer",
                    IsSuccess = false,
                    ErrorMessage = ex.Message,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                return response.Error($"托盘电芯解绑失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ‰˜ç›˜NG电芯上报 - è°ƒç”¨MES接口
        /// </summary>
        /// <param name="dto">NG上报请求参数</param>
        /// <returns>操作结果</returns>
        [HttpPost("containerNgReport")]
        public async Task<WebResponseContent> ContainerNgReport([FromBody] ContainerNgReportRequestDto dto)
        {
            var response = new WebResponseContent();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                // 1. å‚数验证
                if (string.IsNullOrWhiteSpace(dto.PalletCode))
                {
                    return response.Error("托盘编号不能为空");
                }
                if (dto.NgSfcList == null || !dto.NgSfcList.Any())
                {
                    return response.Error("NG电芯列表不能为空");
                }
                // 2. éªŒè¯ç”µèŠ¯çŠ¶æ€ï¼ˆéž'已锁定'状态允许NG上报)
                var sfcList = dto.NgSfcList.Select(x => x.Sfc).ToList();
                var stockDetails = await Service.Repository.QueryDataAsync(x => sfcList.Contains(x.SerialNumber));
                if (stockDetails != null && stockDetails.Any(d => d.Status == 99))
                {
                    return response.Error("当前库存明细包含已锁定状态,不允许执行NG上报操作");
                }
                // 3. èŽ·å–ç³»ç»Ÿé…ç½®
                var configs = _sysDictionaryService.GetVueDictionary(new[] { "MES_EquipmentCode", "MES_ResourceCode" });
                string equipmentCode = GetConfigValue(configs, "MES_EquipmentCode", "WCS_001");
                string resourceCode = GetConfigValue(configs, "MES_ResourceCode", "RESOURCE_001");
                // 3. æž„造MES请求 - å°†DTO格式转换为MES请求格式
                var mesRequest = new ContainerNgReportRequest
                {
                    EquipmentCode = equipmentCode,
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainerCode = dto.PalletCode,
                    NgSfcList = dto.NgSfcList.Select(ng => new NgSfcItem
                    {
                        Sfc = ng.Sfc,
                        NgCode = ng.NgCode,
                        NgEquipmentCode = ng.NgEquipmentCode,
                        NgResourceCode = ng.NgResourceCode
                    }).ToList()
                };
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 4. è°ƒç”¨MES接口(同步方法)
                var mesResult = _mesService.ContainerNgReport(mesRequest);
                stopwatch.Stop();
                // 5. è®°å½•日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "ContainerNgReport",
                    RequestJson = requestJson,
                    ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
                    IsSuccess = mesResult.IsSuccess,
                    ErrorMessage = mesResult.ErrorMessage,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                // 6. è¿”回结果
                if (mesResult.IsSuccess)
                {
                    return response.OK("NG电芯上报成功");
                }
                else
                {
                    return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
                }
            }
            catch (System.Exception ex)
            {
                stopwatch.Stop();
                // è®°å½•错误日志
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    ApiType = "ContainerNgReport",
                    IsSuccess = false,
                    ErrorMessage = ex.Message,
                    ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                    Creator = App.User.UserName
                });
                return response.Error($"NG电芯上报失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ä»Žé…ç½®å­—典中获取配置值
        /// </summary>
        /// <param name="configs">配置字典列表</param>
        /// <param name="key">配置键</param>
        /// <param name="defaultValue">默认值</param>
        /// <returns>配置值</returns>
        private string GetConfigValue(System.Collections.Generic.List<WIDESEA_DTO.System.VueDictionaryDTO> configs, string key, string defaultValue = "")
        {
            if (configs != null)
            {
                var config = configs.FirstOrDefault(c => c.DicNo == key);
                if (config != null && config.Data != null)
                {
                    // Data是dynamic类型,尝试获取第一个元素的value属性
                    try
                    {
                        // ä½¿ç”¨dynamic来访问匿名类型的属性
                        dynamic data = config.Data;
                        if (data != null)
                        {
                            // data可能是IEnumerable或者单个对象
                            var enumerable = data as System.Collections.IEnumerable;
                            if (enumerable != null)
                            {
                                foreach (var item in enumerable)
                                {
                                    // èŽ·å–ç¬¬ä¸€ä¸ªå…ƒç´ 
                                    dynamic firstItem = item;
                                    var value = firstItem.value;
                                    return value?.ToString() ?? defaultValue;
                                }
                            }
                        }
                    }
                    catch
                    {
                        // å¦‚果无法获取,返回默认值
                        return defaultValue;
                    }
                }
            }
            return defaultValue;
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -47,6 +47,28 @@
        }
        /// <summary>
        /// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡
        /// </summary>
        /// <param name="dto">手动创建任务参数</param>
        /// <returns></returns>
        [HttpGet, HttpPost, Route("CreateManualTask"), AllowAnonymous]
        public async Task<WebResponseContent?> CreateManualTaskAsync([FromBody] CreateManualTaskDto dto)
        {
            return await Service.CreateManualTaskAsync(dto);
        }
        /// <summary>
        /// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ°WCS
        /// </summary>
        /// <param name="dtos">下发任务参数列表</param>
        /// <returns>批量下发结果</returns>
        [HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous]
        public async Task<WebResponseContent?> DispatchTasksToWCSAsync([FromBody] List<DispatchTaskDto> dtos)
        {
            return await Service.DispatchTasksToWCSAsync(dtos);
        }
        /// <summary>
        /// èŽ·å–å¯å…¥åº“è´§ä½
        /// </summary>
        /// <param name="taskDto"></param>
@@ -131,7 +153,7 @@
        [HttpGet, HttpPost, Route("UpdateTaskByStatus"), AllowAnonymous]
        public async Task<WebResponseContent?> UpdateTaskByStatusAsync([FromBody] UpdateTaskDto taskDto)
        {
            return await Service.UpdateTaskByStatusAsync(taskDto.Id, taskDto.NewStatus);
            return await Service.UpdateTaskByStatusAsync(taskDto);
        }
        /// <summary>
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj
@@ -59,6 +59,7 @@
      <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" />
      <ProjectReference Include="..\WIDESEA_CheckService\WIDESEA_CheckService.csproj" />
      <ProjectReference Include="..\WIDESEA_InboundService\WIDESEA_InboundService.csproj" />
      <ProjectReference Include="..\WIDESEA_MesService\WIDESEA_MesService.csproj" />
      <ProjectReference Include="..\WIDESEA_OutboundService\WIDESEA_OutboundService.csproj" />
      <ProjectReference Include="..\WIDESEA_RecordService\WIDESEA_RecordService.csproj" />
      <ProjectReference Include="..\WIDESEA_StockService\WIDESEA_StockService.csproj" />
Code/docs/superpowers/plans/2026-04-11-manual-task-creation-plan.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,434 @@
# æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡åŠŸèƒ½å®žæ–½è®¡åˆ’
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** åœ¨WMS界面上添加手动创建任务功能,支持入库/出库/移库三种类型,任务发送至WCS后,WCS判断起点为线体点位(11068/11010/11001)时写入输送线任务。
**Architecture:** WMS前端Vue页面 â†’ WMS后端API â†’ WCS ReceiveTask â†’ WCS FlowService判断并写入输送线PLC
**Tech Stack:** .NET 6 (WMS/WCS Backend), Vue3 (WMS Frontend), MapsterMapper, SqlSugar
---
## æ–‡ä»¶ç»“æž„
| æ–‡ä»¶ | èŒè´£ |
|------|------|
| `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | æ·»åŠ æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡æŒ‰é’®å’Œå¤„ç†é€»è¾‘ |
| `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs` | **新建** - æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡è¯·æ±‚DTO |
| `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` | æ·»åŠ  CreateManualTask æŽ¥å£ |
| `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs` | æ·»åŠ  CreateManualTaskAsync æ–¹æ³• |
| `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs` | ReceiveWMSTask å·²æœ‰åˆ†å‘逻辑,确认入口正确 |
| `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs` | æ·»åŠ çº¿ä½“ç‚¹ä½åˆ¤æ–­å’Œè¾“é€çº¿ä»»åŠ¡å†™å…¥é€»è¾‘ |
---
## WMS å‰ç«¯
### Task 1: æ·»åŠ æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡æŒ‰é’®
**文件:**
- Modify: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js:16`
- [ ] **Step 1: åœ¨ buttons.box æ·»åŠ æ‰‹åŠ¨åˆ›å»ºæŒ‰é’®**
在 `task.js` çš„ `buttons: { view: [], box: [], detail: [] }` ä¸­æ·»åŠ ï¼š
```javascript
buttons: {
  view: [],
  box: [
    {
      value: 'CreateManualTask',
      label: '手动创建任务',
      onClick: function () {
        // å¼¹å‡ºæ‰‹åŠ¨åˆ›å»ºä»»åŠ¡å¯¹è¯æ¡†
        this.$refs.grid.openModel('Add');
      }
    }
  ],
  detail: []
},
```
- [ ] **Step 2: åœ¨ modelFooter æ·»åŠ è‡ªå®šä¹‰å¼¹çª—**
在 `components.modelFooter` æ·»åŠ  Vue æ¨¡æ¿ï¼ˆå¦‚果框架支持自定义弹窗内容),或使用框架内置的 `openModel` é…åˆ `addBefore` å¤„理。
> **注意:** å…·ä½“的弹窗实现取决于当前前端框架的扩展机制。若当前页面不支持自定义字段,则需要在 `task.vue` ä¸­æ·»åŠ æ–°çš„é¡µé¢ç»„ä»¶ï¼Œæˆ–é€šè¿‡ `modelBody` æ‰©å±•自定义表单。
- [ ] **Step 3: æäº¤**
```bash
git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
git commit -m "feat(WMS): æ·»åŠ æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡æŒ‰é’®"
```
---
## WMS åŽç«¯
### Task 2: åˆ›å»º DTO
**文件:**
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs`
- [ ] **Step 1: ç¼–写 CreateManualTaskDto**
```csharp
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using WIDESEA_Common.TaskEnum;
namespace WIDESEA_DTO.Task
{
    /// <summary>
    /// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡è¯·æ±‚DTO
    /// </summary>
    public class CreateManualTaskDto
    {
        /// <summary>
        /// ä»»åŠ¡ç±»åž‹ï¼š1=入库, 2=出库, 3=移库
        /// </summary>
        [JsonPropertyName("taskType")]
        [Required(ErrorMessage = "任务类型不能为空")]
        public TaskTypeEnum TaskType { get; set; }
        /// <summary>
        /// èµ·ç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("sourceAddress")]
        [Required(ErrorMessage = "起点地址不能为空")]
        public string SourceAddress { get; set; }
        /// <summary>
        /// ç»ˆç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("targetAddress")]
        [Required(ErrorMessage = "终点地址不能为空")]
        public string TargetAddress { get; set; }
        /// <summary>
        /// æ¡ç 
        /// </summary>
        [JsonPropertyName("barcode")]
        [Required(ErrorMessage = "条码不能为空")]
        public string Barcode { get; set; }
        /// <summary>
        /// ä»“库ID
        /// </summary>
        [JsonPropertyName("warehouseId")]
        [Required(ErrorMessage = "仓库ID不能为空")]
        public int WarehouseId { get; set; }
        /// <summary>
        /// ä¼˜å…ˆçº§ï¼Œé»˜è®¤1
        /// </summary>
        [JsonPropertyName("grade")]
        public int Grade { get; set; } = 1;
    }
}
```
- [ ] **Step 2: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/CreateManualTaskDto.cs
git commit -m "feat(WMS): æ–°å¢žCreateManualTaskDto"
```
---
### Task 3: æ·»åŠ  Controller æŽ¥å£
**文件:**
- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs`
- [ ] **Step 1: æ·»åŠ  CreateManualTask æŽ¥å£**
在 `TaskController` ç±»ä¸­æ·»åŠ ï¼š
```csharp
/// <summary>
/// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡
/// </summary>
/// <param name="dto">手动创建任务参数</param>
/// <returns></returns>
[HttpGet, HttpPost, Route("CreateManualTask"), AllowAnonymous]
public async Task<WebResponseContent?> CreateManualTaskAsync([FromBody] CreateManualTaskDto dto)
{
    return await Service.CreateManualTaskAsync(dto);
}
```
- [ ] **Step 2: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
git commit -m "feat(WMS): æ·»åŠ æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡æŽ¥å£ CreateManualTask"
```
---
### Task 4: æ·»åŠ  TaskService æ–¹æ³•
**文件:**
- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs`
首先查看现有 `TaskService.cs` ç»“构,确认方法签名格式。
- [ ] **Step 1: æ·»åŠ  CreateManualTaskAsync æ–¹æ³•**
在 `TaskService` ç±»ä¸­æ·»åŠ ï¼ˆå‚è€ƒ `CreateAutoOutboundTasksAsync` çš„æ¨¡å¼ï¼‰ï¼š
```csharp
/// <summary>
/// æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡
/// </summary>
/// <param name="dto">手动创建任务参数</param>
/// <returns></returns>
public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto)
{
    try
    {
        // 1. ç”Ÿæˆä»»åŠ¡å·
        int taskNum = await BaseDal.GetTaskNo();
        // 2. æ ¹æ®ä»»åŠ¡ç±»åž‹ç¡®å®šçŠ¶æ€å€¼
        int taskStatus = dto.TaskType switch
        {
            TaskTypeEnum.Inbound => TaskInStatusEnum.InNew.GetHashCode(),    // 200
            TaskTypeEnum.Outbound => TaskOutStatusEnum.OutNew.GetHashCode(),  // 100
            TaskTypeEnum.Relocation => TaskRelocationStatusEnum.New.GetHashCode(), // 300
            _ => TaskStatusEnum.New.GetHashCode()
        };
        // 3. æž„建任务实体
        var task = new Dt_Task
        {
            TaskNum = taskNum,
            PalletCode = dto.Barcode,
            SourceAddress = dto.SourceAddress,
            TargetAddress = dto.TargetAddress,
            TaskType = dto.TaskType.GetHashCode(),
            TaskStatus = taskStatus,
            Grade = dto.Grade,
            WarehouseId = dto.WarehouseId,
            Creater = "manual",
            CreateDate = DateTime.Now,
            ModifyDate = DateTime.Now
        };
        // 4. ä¿å­˜åˆ°æ•°æ®åº“
        var result = await BaseDal.Add(task);
        if (!result)
            return WebResponseContent.Instance.Error("创建任务失败");
        // 5. å‘送任务到WCS
        var wmsTaskDto = new WMSTaskDTO
        {
            TaskNum = task.TaskNum,
            PalletCode = task.PalletCode,
            SourceAddress = task.SourceAddress,
            TargetAddress = task.TargetAddress,
            TaskType = task.TaskType,
            TaskStatus = task.TaskStatus,
            WarehouseId = task.WarehouseId ?? 0
        };
        var wcsResult = await _httpClientHelper.PostAsync<WebResponseContent>(
            "http://localhost:9292/api/Task/ReceiveTask",
            wmsTaskDto.ToJson());
        if (!wcsResult.IsSuccess || !wcsResult.Data.Status)
            return WebResponseContent.Instance.Error($"任务已创建但发送给WCS失败: {wcsResult.Data?.Message}");
        return WebResponseContent.Instance.OK($"手动创建任务成功,任务号: {taskNum}");
    }
    catch (Exception ex)
    {
        return WebResponseContent.Instance.Error($"手动创建任务异常: {ex.Message}");
    }
}
```
> **注意:** éœ€è¦ç¡®è®¤ `TaskRelocationStatusEnum` æ˜¯å¦å­˜åœ¨ï¼Œå¦‚不存在则使用 `TaskStatusEnum.New`。
- [ ] **Step 2: æ·»åŠ å¿…è¦çš„ using**
```csharp
using WIDESEA_DTO.Task;
using WIDESEA_Common.TaskEnum;
using WIDESEAWCS_DTO;
```
- [ ] **Step 3: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
git commit -m "feat(WMS): æ·»åŠ æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡ CreateManualTaskAsync æ–¹æ³•"
```
---
## WCS åŽç«¯
### Task 5: ç¡®è®¤ ReceiveWMSTask å…¥å£
**文件:**
- Modify: `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs`
- [ ] **Step 1: ç¡®è®¤ ReceiveWMSTask æ–¹æ³•存在**
确认 `ReceiveWMSTask` æŽ¥æ”¶ `WMSTaskDTO` åˆ—表后路由到对应 FlowService。
```csharp
public WebResponseContent ReceiveWMSTask([NotNull] List<WMSTaskDTO> taskDTOs)
{
    // éåŽ†ä»»åŠ¡ï¼Œæ ¹æ® TaskType åˆ†å‘到不同 FlowService
    foreach (var item in taskDTOs)
    {
        // æ ¹æ® item.TaskType åˆ¤æ–­è·¯ç”±åˆ° Inbound/Outbound/Relocation
    }
}
```
- [ ] **Step 2: æäº¤**
```bash
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs
git commit -m "feat(WCS): ç¡®è®¤ ReceiveWMSTask å…¥å£æ­£ç¡®"
```
---
### Task 6: åœ¨ InboundTaskFlowService æ·»åŠ çº¿ä½“ç‚¹ä½å¤„ç†
**文件:**
- Modify: `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs`
首先查看现有的 `InitializeOnReceive` æ–¹æ³•完整实现。
- [ ] **Step 1: æ·»åŠ çº¿ä½“ç‚¹ä½å¸¸é‡**
在类中添加:
```csharp
/// <summary>
/// çº¿ä½“入库点位
/// </summary>
private static readonly string[] LINE_IN_POINTS = { "11068", "11010", "11001" };
```
- [ ] **Step 2: ä¿®æ”¹ InitializeOnReceive æ–¹æ³•**
在 `InitializeOnReceive` æ–¹æ³•中,判断如果 `SourceAddress` æ˜¯çº¿ä½“点位,则写入输送线任务:
```csharp
public void InitializeOnReceive([NotNull] Dt_Task task, [NotNull] WMSTaskDTO source)
{
    // å…ˆæ‰§è¡ŒåŽŸæœ‰çš„è·¯ç”±é€»è¾‘
    Dt_Router routers = _routerService.QueryNextRoute(source.SourceAddress);
    if (routers.IsNullOrEmpty())
    {
        return;
    }
    task.TaskStatus = (int)TaskInStatusEnum.InNew;
    task.CurrentAddress = source.SourceAddress;
    task.NextAddress = routers.ChildPosi;
    // å¦‚果起点是线体点位(11068/11010/11001),直接写入输送线任务
    if (LINE_IN_POINTS.Contains(source.SourceAddress))
    {
        WriteConveyorLineTask(task, source.SourceAddress);
    }
}
/// <summary>
/// å†™å…¥è¾“送线任务到PLC
/// </summary>
private void WriteConveyorLineTask(Dt_Task task, string sourceAddress)
{
    // 1. é€šè¿‡ Storage.Devices æŸ¥æ‰¾å¯¹åº”的输送线设备
    IDevice? device = Storage.Devices
        .FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));
    if (device == null)
    {
        // è®°å½•日志:未找到对应的输送线设备
        QuartzLogger.Error($"手动创建任务:未找到源地址【{sourceAddress}】对应的输送线设备", "InboundTaskFlowService");
        return;
    }
    // 2. è½¬æ¢ä¸º CommonConveyorLine ç±»åž‹
    if (device is not CommonConveyorLine conveyorLine)
    {
        QuartzLogger.Error($"设备【{device.DeviceCode}】不是输送线类型", "InboundTaskFlowService");
        return;
    }
    // 3. èŽ·å–å­è®¾å¤‡ç¼–ç 
    string? childDeviceCode = device.DeviceProDTOs
        .FirstOrDefault(d => d.DeviceChildCode == sourceAddress)?.DeviceChildCode;
    if (string.IsNullOrEmpty(childDeviceCode))
    {
        QuartzLogger.Error($"源地址【{sourceAddress}】未找到对应的子设备编码", "InboundTaskFlowService");
        return;
    }
    // 4. æž„造 ConveyorLineTaskCommandNew
    ConveyorLineTaskCommandNew command = new ConveyorLineTaskCommandNew
    {
        TaskNo = (short)task.TaskNum,
        Source = short.Parse(sourceAddress),
        Target = short.Parse(task.TargetAddress ?? "0"),
        Barcode = task.PalletCode ?? string.Empty,
        WCS_STB = 1,  // WCS已发送标志
        WCS_ACK = 0,
        PLC_STB = 0,
        PLC_ACK = 0
    };
    // 5. å†™å…¥PLC
    bool success = conveyorLine.SendCommand(command, childDeviceCode);
    if (success)
    {
        QuartzLogger.Info($"手动创建入库任务已写入输送线:任务号【{task.TaskNum}】,源地址【{sourceAddress}】", conveyorLine.DeviceCode);
    }
    else
    {
        QuartzLogger.Error($"手动创建入库任务写入输送线失败:任务号【{task.TaskNum}】,源地址【{sourceAddress}】", conveyorLine.DeviceCode);
    }
}
```
- [ ] **Step 3: æ·»åŠ å¿…è¦çš„ using**
```csharp
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_QuartzJob.ConveyorLine;
using WIDESEAWCS_Tasks;
using WIDESEAWCS_Core.LogHelper;
```
- [ ] **Step 4: æäº¤**
```bash
git add WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs
git commit -m "feat(WCS): å…¥åº“任务线体点位时写入输送线任务"
```
---
## éªŒè¯æ¸…单
- [ ] WMS å‰ç«¯ï¼šç‚¹å‡»"手动创建任务"按钮弹出对话框
- [ ] WMS å‰ç«¯ï¼šå¡«å†™è¡¨å•后提交,返回成功
- [ ] WMS åŽç«¯ï¼šåˆ›å»ºä»»åŠ¡å†™å…¥æ•°æ®åº“
- [ ] WMS åŽç«¯ï¼šä»»åŠ¡æˆåŠŸå‘é€ç»™ WCS
- [ ] WCS åŽç«¯ï¼šReceiveTask æ”¶åˆ°ä»»åŠ¡
- [ ] WCS åŽç«¯ï¼šèµ·ç‚¹ä¸ºçº¿ä½“点位时,任务写入 PLC æˆåŠŸ
- [ ] WCS åŽç«¯ï¼šèµ·ç‚¹ä¸ºæ™®é€šç‚¹ä½æ—¶ï¼Œèµ°åŽŸæœ‰è·¯ç”±é€»è¾‘
Code/docs/superpowers/plans/2026-04-12-manual-dispatch-tasks-to-wcs-plan.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,723 @@
# æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ° WCS åŠŸèƒ½å®žæ–½è®¡åˆ’
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** åœ¨ WMS å‰ç«¯ä»»åŠ¡ç®¡ç†é¡µé¢æ·»åŠ å·¥å…·æ æŒ‰é’®ï¼Œæ”¯æŒé€‰ä¸­ä¸€ä¸ªæˆ–å¤šä¸ªä»»åŠ¡åŽï¼Œç¼–è¾‘åœ°å€ï¼ˆèµ·ç‚¹/终点)和优先级后手动下发到 WCS。
**Architecture:**
- å‰ç«¯ï¼šæ–°å¢ž `dispatchTasksToWCS.vue` å¼¹çª—组件,复用 `task.js` æ‰©å±•机制添加工具栏按钮
- åŽç«¯ï¼šæ–°å¢ž `DispatchTasksToWCSAsync` æ–¹æ³•,复用 `ReceiveManualTask` æŽ¥å£ä¸‹å‘到 WCS
**Tech Stack:** Vue 3 + Element Plus(前端),ASP.NET Core + SqlSugar(后端)
---
## æ–‡ä»¶ç»“æž„
| æ”¹åŠ¨ç±»åž‹ | æ–‡ä»¶è·¯å¾„ |
|----------|----------|
| æ–°å¢ž | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` |
| ä¿®æ”¹ | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` |
| æ–°å¢ž | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` |
| ä¿®æ”¹ | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` |
| ä¿®æ”¹ | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` |
---
## Task 1: åŽç«¯ - æ–°å¢ž DispatchTaskDto
**文件:**
- åˆ›å»º: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs`
- [ ] **创建 DispatchTaskDto.cs**
```csharp
using System.Text.Json.Serialization;
namespace WIDESEA_DTO.Task
{
    /// <summary>
    /// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡Dto
    /// </summary>
    public class DispatchTaskDto
    {
        /// <summary>
        /// ä»»åŠ¡ID
        /// </summary>
        [JsonPropertyName("taskId")]
        public long TaskId { get; set; }
        /// <summary>
        /// èµ·ç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("sourceAddress")]
        public string SourceAddress { get; set; }
        /// <summary>
        /// ç»ˆç‚¹åœ°å€
        /// </summary>
        [JsonPropertyName("targetAddress")]
        public string TargetAddress { get; set; }
        /// <summary>
        /// ä¼˜å…ˆçº§
        /// </summary>
        [JsonPropertyName("grade")]
        public int Grade { get; set; }
    }
    /// <summary>
    /// ä»»åŠ¡ä¸‹å‘ç»“æžœDto
    /// </summary>
    public class DispatchTaskResultDto
    {
        /// <summary>
        /// ä»»åŠ¡ID
        /// </summary>
        [JsonPropertyName("taskId")]
        public long TaskId { get; set; }
        /// <summary>
        /// ä»»åŠ¡å·
        /// </summary>
        [JsonPropertyName("taskNum")]
        public int TaskNum { get; set; }
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        [JsonPropertyName("success")]
        public bool Success { get; set; }
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯
        /// </summary>
        [JsonPropertyName("errorMessage")]
        public string ErrorMessage { get; set; }
    }
    /// <summary>
    /// æ‰¹é‡ä¸‹å‘结果Dto
    /// </summary>
    public class DispatchResultDto
    {
        /// <summary>
        /// æˆåŠŸæ•°é‡
        /// </summary>
        [JsonPropertyName("successCount")]
        public int SuccessCount { get; set; }
        /// <summary>
        /// å¤±è´¥æ•°é‡
        /// </summary>
        [JsonPropertyName("failCount")]
        public int FailCount { get; set; }
        /// <summary>
        /// å¤±è´¥ä»»åŠ¡åˆ—è¡¨
        /// </summary>
        [JsonPropertyName("failResults")]
        public List<DispatchTaskResultDto> FailResults { get; set; }
    }
}
```
- [ ] **Commit**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs
git commit -m "feat(WMS): æ–°å¢žæ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡Dto
- DispatchTaskDto: ä¸‹å‘请求参数
- DispatchTaskResultDto: å•个任务下发结果
- DispatchResultDto: æ‰¹é‡ä¸‹å‘结果
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 2: åŽç«¯ - TaskService æ–°å¢ž DispatchTasksToWCSAsync
**文件:**
- ä¿®æ”¹: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs`
**前置了解:**
- ä»»åŠ¡çŠ¶æ€æžšä¸¾ï¼š`TaskInStatusEnum.InNew`(入库新单)、`TaskOutStatusEnum.OutNew`(出库新单)、`TaskRelocationStatusEnum.RelocationNew`(移库新单)
- å·²æœ‰ WCS è°ƒç”¨æ–¹å¼ï¼š`_httpClientHelper.Post<WebResponseContent>("http://localhost:9292/api/Task/ReceiveManualTask", json)`
- å·²æœ‰äº‹åŠ¡å¤„ç†ï¼š`await _unitOfWorkManage.BeginTranAsync(async () => { ... })`
- å·²æœ‰ `WMSTaskDTO` ç”¨äºŽç»™ WCS å‘送任务
**在 `TaskService_WCS.cs` æœ«å°¾ `#endregion WCS逻辑处理` ä¹‹å‰æ·»åŠ ï¼š**
```csharp
/// <summary>
/// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ°WCS
/// </summary>
/// <param name="dtos">下发任务参数列表</param>
/// <returns>批量下发结果</returns>
public async Task<WebResponseContent> DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)
{
    try
    {
        if (dtos == null || !dtos.Any())
            return WebResponseContent.Instance.Error("请选择要下发的任务");
        var resultDto = new DispatchResultDto
        {
            SuccessCount = 0,
            FailCount = 0,
            FailResults = new List<DispatchTaskResultDto>()
        };
        foreach (var dto in dtos)
        {
            var task = await BaseDal.QueryFirstAsync(t => t.TaskId == dto.TaskId);
            if (task == null)
            {
                resultDto.FailResults.Add(new DispatchTaskResultDto
                {
                    TaskId = dto.TaskId,
                    TaskNum = 0,
                    Success = false,
                    ErrorMessage = "任务不存在"
                });
                resultDto.FailCount++;
                continue;
            }
            // æ ¡éªŒä»»åŠ¡çŠ¶æ€ï¼šä»…å…¥åº“æ–°å•/出库新单/移库新单可下发
            bool canDispatch = false;
            if (task.TaskType == TaskInboundTypeEnum.Inbound.GetHashCode()
                && task.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())
                canDispatch = true;
            else if (task.TaskType == TaskOutboundTypeEnum.Outbound.GetHashCode()
                && task.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode())
                canDispatch = true;
            else if (task.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode()
                && task.TaskStatus == TaskRelocationStatusEnum.RelocationNew.GetHashCode())
                canDispatch = true;
            if (!canDispatch)
            {
                var statusName = GetTaskStatusName(task.TaskType, task.TaskStatus);
                resultDto.FailResults.Add(new DispatchTaskResultDto
                {
                    TaskId = dto.TaskId,
                    TaskNum = task.TaskNum,
                    Success = false,
                    ErrorMessage = $"任务状态[{statusName}]不允许下发"
                });
                resultDto.FailCount++;
                continue;
            }
            // æ›´æ–°ä»»åŠ¡çš„åœ°å€å’Œä¼˜å…ˆçº§
            task.SourceAddress = dto.SourceAddress;
            task.TargetAddress = dto.TargetAddress;
            task.CurrentAddress = dto.SourceAddress;
            task.NextAddress = dto.TargetAddress;
            task.Grade = dto.Grade;
            task.Dispatchertime = DateTime.Now;
            await BaseDal.UpdateDataAsync(task);
            // æž„造WMSTaskDTO发送给WCS
            var wmsTaskDto = new WMSTaskDTO
            {
                Id = task.TaskId,
                TaskNum = task.TaskNum,
                PalletCode = task.PalletCode,
                SourceAddress = task.SourceAddress,
                TargetAddress = task.TargetAddress,
                CurrentAddress = task.CurrentAddress,
                NextAddress = task.NextAddress,
                TaskType = task.TaskType,
                TaskStatus = task.TaskStatus,
                Roadway = task.Roadway,
                Grade = task.Grade,
                WarehouseId = task.WarehouseId,
                PalletType = task.PalletType
            };
            var wcsResult = _httpClientHelper.Post<WebResponseContent>(
                "http://localhost:9292/api/Task/ReceiveManualTask",
                new List<WMSTaskDTO> { wmsTaskDto }.ToJson());
            if (wcsResult != null && wcsResult.IsSuccess)
            {
                resultDto.SuccessCount++;
            }
            else
            {
                resultDto.FailResults.Add(new DispatchTaskResultDto
                {
                    TaskId = dto.TaskId,
                    TaskNum = task.TaskNum,
                    Success = false,
                    ErrorMessage = wcsResult?.Message ?? "WCS响应异常"
                });
                resultDto.FailCount++;
            }
        }
        if (resultDto.FailCount == 0)
            return WebResponseContent.Instance.OK($"成功下发{resultDto.SuccessCount}个任务", resultDto);
        if (resultDto.SuccessCount == 0)
            return WebResponseContent.Instance.Error($"下发失败,共{resultDto.FailCount}个任务", resultDto);
        return WebResponseContent.Instance.Error($"部分下发成功{resultDto.SuccessCount}个,失败{resultDto.FailCount}个", resultDto);
    }
    catch (Exception ex)
    {
        return WebResponseContent.Instance.Error($"下发任务异常: {ex.Message}");
    }
}
/// <summary>
/// èŽ·å–ä»»åŠ¡çŠ¶æ€åç§°
/// </summary>
private string GetTaskStatusName(int taskType, int taskStatus)
{
    if (taskType == TaskInboundTypeEnum.Inbound.GetHashCode())
        return ((TaskInStatusEnum)taskStatus).ToString();
    if (taskType == TaskOutboundTypeEnum.Outbound.GetHashCode())
        return ((TaskOutStatusEnum)taskStatus).ToString();
    if (taskType == TaskRelocationTypeEnum.Relocation.GetHashCode())
        return ((TaskRelocationStatusEnum)taskStatus).ToString();
    return taskStatus.ToString();
}
```
**注意:** éœ€è¦åœ¨æ–‡ä»¶é¡¶éƒ¨ç¡®è®¤å·²æœ‰ä»¥ä¸‹ using:
```csharp
using WIDESEA_DTO.Task;
> **状态同步说明**:WMS ä¸‹å‘成功后,WMS ç«¯ä»»åŠ¡çŠ¶æ€ä¿æŒ"New"不变,不切换到执行中状态。这是现有 `CreateManualTaskAsync` çš„设计模式(因为 WCS ä¾§æœ‰ç‹¬ç«‹çš„任务生命周期),不跟进此 PR æ”¹å˜ã€‚如需 WMS ç«¯ä¹ŸåŒæ­¥çŠ¶æ€ï¼Œéœ€å¦è¡Œè¯„ä¼° WCS→WMS çš„状态回调机制。
using WIDESEA_Model.Models;
```
- [ ] **Commit**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs
git commit -m "feat(WMS): TaskService新增DispatchTasksToWCSAsync方法
- æ ¡éªŒä»»åŠ¡çŠ¶æ€ï¼Œä»…å…¥åº“æ–°å•/出库新单/移库新单可下发
- æ›´æ–°ä»»åŠ¡åœ°å€å’Œä¼˜å…ˆçº§åŽè°ƒç”¨WCS ReceiveManualTask接口
- è¿”回批量下发结果(成功/失败列表及原因)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 3: åŽç«¯ - TaskController æ–°å¢žä¸‹å‘接口
**文件:**
- ä¿®æ”¹: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs`
**在 Controller ä¸­æ·»åŠ ä»¥ä¸‹æ–¹æ³•ï¼ˆæ”¾åœ¨å…¶ä»–æ–¹æ³•é™„è¿‘ï¼Œå»ºè®®åœ¨ CreateManualTaskAsync ä¹‹åŽï¼‰ï¼š**
```csharp
/// <summary>
/// æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ°WCS
/// </summary>
/// <param name="dtos">下发任务参数列表</param>
/// <returns>批量下发结果</returns>
[HttpGet, HttpPost, Route("DispatchTasksToWCS"), AllowAnonymous]
public async Task<WebResponseContent?> DispatchTasksToWCSAsync([FromBody] List<DispatchTaskDto> dtos)
{
    return await Service.DispatchTasksToWCSAsync(dtos);
}
```
- [ ] **Commit**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
git commit -m "feat(WMS): TaskController新增DispatchTasksToWCS接口
POST /api/TaskInfo/DispatchTasksToWCS
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 4: å‰ç«¯ - æ–°å¢ž dispatchTasksToWCS.vue å¼¹çª—组件
**文件:**
- åˆ›å»º: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue`
**参考现有 addManualTask.vue çš„æ¨¡å¼ï¼Œä½¿ç”¨ el-table å®žçŽ°æ‰¹é‡ç¼–è¾‘ï¼š**
```vue
<template>
  <div>
    <vol-box v-model="showBox" :lazy="true" width="900px" :padding="15" title="手动下发任务到 WCS">
      <div v-if="selectedRows.length > 0" class="dispatch-info">
        å·²é€‰ä»»åŠ¡æ•°: <span class="count">{{ selectedRows.length }}</span> ä¸ª
      </div>
      <el-table :data="tableData" border style="width: 100%; margin-top: 10px" max-height="400">
        <el-table-column prop="taskNum" label="任务号" width="120"></el-table-column>
        <el-table-column prop="sourceAddress" label="起点地址" width="160">
          <template slot-scope="scope">
            <el-input
              v-if="scope.row.editable"
              v-model="scope.row.sourceAddress"
              size="small"
              placeholder="请输入"
            ></el-input>
            <span v-else>{{ scope.row.sourceAddress }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="targetAddress" label="终点地址" width="160">
          <template slot-scope="scope">
            <el-input
              v-if="scope.row.editable"
              v-model="scope.row.targetAddress"
              size="small"
              placeholder="请输入"
            ></el-input>
            <span v-else>{{ scope.row.targetAddress }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="grade" label="优先级" width="100">
          <template slot-scope="scope">
            <el-input-number
              v-if="scope.row.editable"
              v-model="scope.row.grade"
              :min="1"
              :max="99"
              size="small"
              style="width: 80px"
            ></el-input-number>
            <span v-else>{{ scope.row.grade }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="statusName" label="状态" width="120">
          <template slot-scope="scope">
            <span :class="{ 'status-error': !scope.row.editable }">{{ scope.row.statusName }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="palletCode" label="托盘号"></el-table-column>
      </el-table>
      <!-- å¤±è´¥ç»“æžœ -->
      <div v-if="failResults.length > 0" class="fail-results">
        <div class="fail-title">下发失败任务:</div>
        <el-table :data="failResults" border style="width: 100%; margin-top: 10px" max-height="200">
          <el-table-column prop="taskNum" label="任务号" width="120"></el-table-column>
          <el-table-column prop="errorMessage" label="失败原因"></el-table-column>
        </el-table>
      </div>
      <template #footer>
        <el-button type="primary" size="small" @click="submit" :loading="loading">确认下发</el-button>
        <el-button type="danger" size="small" @click="showBox = false">关闭</el-button>
      </template>
    </vol-box>
  </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
export default {
  components: { VolBox },
  data() {
    return {
      showBox: false,
      loading: false,
      selectedRows: [],
      tableData: [],
      failResults: [],
      // å¯ä¸‹å‘状态的任务类型和状态值(需与后端 TaskTypeEnum æžšä¸¾ä¸€è‡´ï¼‰
      // Inbound=200, Outbound=100, Relocation=300
      // çŠ¶æ€: InNew=200, OutNew=100, RelocationNew=300
      dispatchableStatuses: {
        inbound: { taskType: 200, status: 200 },    // å…¥åº“新单
        outbound: { taskType: 100, status: 100 },  // å‡ºåº“新单
        relocation: { taskType: 300, status: 300 } // ç§»åº“新单
      }
    };
  },
  methods: {
    open(rows) {
      this.showBox = true;
      this.selectedRows = rows || [];
      this.failResults = [];
      this.initTableData();
    },
    initTableData() {
      // ä»»åŠ¡çŠ¶æ€åç§°æ˜ å°„ï¼ˆéœ€ä¸ŽåŽç«¯æžšä¸¾ä¸€è‡´ï¼‰
      // taskType: Inbound=200, Outbound=100, Relocation=300
      // status: InNew=200, OutNew=100, RelocationNew=300, SC_OutExecuting=110, etc.
      const statusNames = {
        'inbound_200': '入库新单',
        'outbound_100': '出库新单',
        'relocation_300': '移库新单',
        'outbound_110': '堆垛机出库执行中',
        'outbound_115': '堆垛机出库完成',
        'inbound_220': '输送线入库执行中',
        'inbound_230': '堆垛机入库执行中',
        'inbound_290': '入库任务完成',
        'outbound_120': '输送线出库执行中',
        'outbound_125': '输送线出库完成',
        'outbound_200': '出库任务完成',
        'relocation_310': '堆垛机移库执行中'
      };
      this.tableData = this.selectedRows.map(row => {
        const taskType = row.taskType || 0;
        const taskStatus = row.taskStatus || 0;
        const statusKey = this.getStatusKey(taskType, taskStatus);
        const statusName = statusNames[statusKey] || `状态${taskStatus}`;
        // åˆ¤æ–­æ˜¯å¦å¯ç¼–辑:仅入库新单/出库新单/移库新单可编辑
        const editable = this.isEditable(taskType, taskStatus);
        return {
          taskId: row.taskId,
          taskNum: row.taskNum,
          sourceAddress: row.sourceAddress || '',
          targetAddress: row.targetAddress || '',
          grade: row.grade || 1,
          statusName: statusName,
          palletCode: row.palletCode || '',
          editable: editable,
          taskType: taskType,
          taskStatus: taskStatus
        };
      });
    },
    getStatusKey(taskType, taskStatus) {
      if (taskType === 200) return `inbound_${taskStatus}`;
      if (taskType === 100) return `outbound_${taskStatus}`;
      if (taskType === 300) return `relocation_${taskStatus}`;
      return `other_${taskStatus}`;
    },
    isEditable(taskType, taskStatus) {
      if (taskType === 200 && taskStatus === 200) return true;  // å…¥åº“新单
      if (taskType === 100 && taskStatus === 100) return true;  // å‡ºåº“新单
      if (taskType === 300 && taskStatus === 300) return true;  // ç§»åº“新单
      return false;
    },
    submit() {
      if (this.tableData.length === 0) return this.$message.error("请先选择任务");
      const dispatchData = this.tableData.map(row => ({
        taskId: row.taskId,
        sourceAddress: row.sourceAddress,
        targetAddress: row.targetAddress,
        grade: row.grade
      }));
      this.loading = true;
      this.http
        .post("/api/TaskInfo/DispatchTasksToWCS", dispatchData, "数据处理中...")
        .then((res) => {
          this.loading = false;
          if (!res.status) {
            this.$message.error(res.message);
            // æ˜¾ç¤ºå¤±è´¥åˆ—表
            if (res.data && res.data.failResults) {
              this.failResults = res.data.failResults;
            }
            return;
          }
          // å…¨éƒ¨æˆåŠŸ
          if (res.data && res.data.failCount === 0) {
            this.$message.success(res.message);
            this.showBox = false;
            this.$emit("parentCall", ($vue) => {
              $vue.refresh();
            });
            return;
          }
          // éƒ¨åˆ†æˆåŠŸæˆ–å…¨éƒ¨å¤±è´¥
          if (res.data && res.data.failResults) {
            this.failResults = res.data.failResults;
          }
          if (res.data && res.data.failCount > 0 && res.data.successCount > 0) {
            this.$message.warning(res.message);
          } else {
            this.$message.error(res.message);
          }
        })
        .catch(() => {
          this.loading = false;
        });
    }
  }
};
</script>
<style scoped>
.dispatch-info {
  font-size: 14px;
  color: #606266;
  margin-bottom: 10px;
}
.dispatch-info .count {
  color: #409eff;
  font-weight: bold;
}
.status-error {
  color: #f56c6c;
}
.fail-results {
  margin-top: 15px;
  padding: 10px;
  background: #fef0f0;
  border-radius: 4px;
}
.fail-title {
  font-size: 14px;
  color: #f56c6c;
  margin-bottom: 5px;
}
</style>
```
- [ ] **Commit**
```bash
git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue
git commit -m "feat(WMS): æ–°å¢ždispatchTasksToWCS.vue批量下发弹窗组件
- å·¥å…·æ æŒ‰é’®è§¦å‘弹窗
- è¡¨æ ¼å±•示选中任务,可编辑地址和优先级
- éžå¯ä¸‹å‘状态任务行标红且不可编辑
- æ˜¾ç¤ºä¸‹å‘失败任务列表
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 5: å‰ç«¯ - task.js æ·»åŠ å·¥å…·æ æŒ‰é’®
**文件:**
- ä¿®æ”¹: `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js`
**需要修改两处:**
1. åœ¨ `components` éƒ¨åˆ†å¼•入弹窗组件:
```javascript
import addManualTask from './extend/addManualTask.vue'
import dispatchTasksToWCS from './extend/dispatchTasksToWCS.vue'  // æ–°å¢ž
```
2. åœ¨ `components` å¯¹è±¡ä¸­æ³¨å†Œï¼š
```javascript
components: {
  gridBody: addManualTask,
  dispatchBody: dispatchTasksToWCS,  // æ–°å¢ž
},
```
3. åœ¨ `onInit()` æ–¹æ³•中添加手动下发按钮(在手动创建任务按钮之后):
```javascript
// æ·»åŠ "手动下发"按钮
this.buttons.push({
  name: '手动下发',
  icon: 'el-icon-s-promotion',
  type: 'primary',
  value: 'DispatchTasksToWCS',
  onClick: () => {
    let rows = this.$refs.table.getSelected();
    if (rows.length == 0) return this.$error("请先选择任务");
    this.$refs.dispatchBody.open(rows);
  }
});
```
**完整修改后的 task.js å…³é”®éƒ¨åˆ†ï¼š**
```javascript
import addManualTask from './extend/addManualTask.vue'
import dispatchTasksToWCS from './extend/dispatchTasksToWCS.vue'
let extension = {
    components: {
      gridBody: addManualTask,
      dispatchBody: dispatchTasksToWCS,
    },
    buttons: { view: [], box: [], detail: [] },
    methods: {
      onInit() {
        //添加"手动创建任务"按钮
        this.buttons.push({
          name: '手动创建任务',
          icon: 'el-icon-plus',
          type: 'primary',
          value: 'ManualCreateTask',
          onClick: () => {
            this.$refs.gridBody.open();
          }
        });
        // æ·»åŠ "手动下发"按钮
        this.buttons.push({
          name: '手动下发',
          icon: 'el-icon-s-promotion',
          type: 'primary',
          value: 'DispatchTasksToWCS',
          onClick: () => {
            let rows = this.$refs.table.getSelected();
            if (rows.length == 0) return this.$error("请先选择任务");
            this.$refs.dispatchBody.open(rows);
          }
        });
        // ... å…¶ä½™çŽ°æœ‰æŒ‰é’®é€»è¾‘ä¿æŒä¸å˜
      },
      // ... å…¶ä½™æ–¹æ³•保持不变
    }
};
export default extension;
```
- [ ] **Commit**
```bash
git add WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js
git commit -m "feat(WMS): task.js添加工具栏'手动下发'按钮
- å¼•å…¥dispatchTasksToWCS.vue组件
- ç‚¹å‡»æŒ‰é’®èŽ·å–é€‰ä¸­ä»»åŠ¡å¹¶æ‰“å¼€ä¸‹å‘å¼¹çª—
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 6: éªŒè¯æž„建
- [ ] **WMS åŽç«¯æž„建**
```bash
cd D:/Git/ShanMeiXinNengYuan/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer
dotnet build
```
- [ ] **WMS å‰ç«¯æž„建**
```bash
cd D:/Git/ShanMeiXinNengYuan/Code/WMS/WIDESEA_WMSClient
npm run serve
```
---
## éªŒæ”¶æ ‡å‡†
- [ ] å·¥å…·æ æ·»åŠ "手动下发"按钮,未选中任务时点击提示"请先选择任务"
- [ ] å¼¹çª—正确显示所有选中任务
- [ ] ä»…入库新单/出库新单/移库新单状态的任务可编辑地址和优先级
- [ ] å…¶ä»–状态任务行标红,地址/优先级不可编辑
- [ ] ç¡®è®¤ä¸‹å‘后调用后端接口
- [ ] æˆåŠŸæ—¶å¼¹çª—å…³é—­å¹¶åˆ·æ–°åˆ—è¡¨ï¼›å¤±è´¥æ—¶æ˜¾ç¤ºå¤±è´¥ä»»åŠ¡åŠåŽŸå› 
- [ ] æ‰¹é‡ä¸‹å‘时正确统计成功/失败数量
Code/docs/superpowers/plans/2026-04-12-mes-integration-plan.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1933 @@
# WMS库存页面MES接口集成实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** ä¸ºWMS库存信息页面和库存明细页面添加操作列,调用MES系统的进站/出站/绑定/解绑/NG上报接口
**Architecture:** å‰ç«¯åœ¨åº“存表格中添加操作列,点击后弹出确认对话框,确认后调用后端API;后端接收请求,调用MES服务,记录完整日志,返回结果
**Tech Stack:** Vue 3, Element Plus, .NET 8, SqlSugar ORM, SQL Server, HttpClient
---
## æ–‡ä»¶ç»“æž„
### å‰ç«¯æ–‡ä»¶
```
WMS/WIDESEA_WMSClient/src/
├── views/stock/
│   â”œâ”€â”€ stockInfo.vue                         # ä¿®æ”¹ï¼šæ·»åŠ æ“ä½œåˆ—
│   â””── stockInfoDetail.vue                   # ä¿®æ”¹ï¼šæ·»åŠ æ“ä½œåˆ—
├── components/
│   â””── MesConfirmDialog.vue                  # æ–°å¢žï¼šMES确认对话框
└── api/
    â””── mes.js                                # æ–°å¢žï¼šMES API调用
```
### åŽç«¯æ–‡ä»¶
```
WMS/WIDESEA_WMSServer/
├── Controllers/
│   â””── Stock/
│       â”œâ”€â”€ StockInfoController.cs            # ä¿®æ”¹ï¼šæ·»åŠ è¿›ç«™/出站接口
│       â””── StockInfoDetailController.cs      # ä¿®æ”¹ï¼šæ·»åŠ ç»‘å®š/解绑/NG上报接口
├── Services/
│   â””── Mes/
│       â”œâ”€â”€ IMesLogService.cs                 # æ–°å¢žï¼šæ—¥å¿—服务接口
│       â””── MesLogService.cs                  # æ–°å¢žï¼šæ—¥å¿—服务实现
├── DTO/
│   â”œâ”€â”€ Mes/
│   â”‚   â”œâ”€â”€ MesApiLogDto.cs                   # æ–°å¢žï¼šæ—¥å¿—DTO
│   â”‚   â”œâ”€â”€ InboundInContainerRequestDto.cs   # æ–°å¢žï¼šè¿›ç«™è¯·æ±‚DTO
│   â”‚   â”œâ”€â”€ OutboundInContainerRequestDto.cs  # æ–°å¢žï¼šå‡ºç«™è¯·æ±‚DTO
│   â”‚   â”œâ”€â”€ BindContainerRequestDto.cs        # æ–°å¢žï¼šç»‘定请求DTO
│   â”‚   â”œâ”€â”€ UnbindContainerRequestDto.cs      # æ–°å¢žï¼šè§£ç»‘请求DTO
│   â”‚   â””── ContainerNgReportRequestDto.cs    # æ–°å¢žï¼šNG上报请求DTO
│   â””── Models/
│       â””── Mes/
│           â””── Dt_MesApiLog.cs                # æ–°å¢žï¼šæ—¥å¿—实体
└── Database/
    â””── Scripts/
        â””── 20260412_MesApiLog.sql             # æ–°å¢žï¼šæ—¥å¿—表创建脚本
```
---
## Task 1: åˆ›å»ºæ•°æ®åº“表
**Files:**
- Create: `WMS/WIDESEA_WMSServer/Database/Scripts/20260412_MesApiLog.sql`
- [ ] **Step 1: åˆ›å»ºMES接口日志表SQL脚本**
```sql
-- =============================================
-- WMS MES接口调用日志表
-- =============================================
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Dt_MesApiLog')
BEGIN
    CREATE TABLE Dt_MesApiLog (
        Id BIGINT PRIMARY KEY IDENTITY(1,1),
        ApiType NVARCHAR(50) NOT NULL,           -- æŽ¥å£ç±»åž‹
        RequestJson NVARCHAR(MAX) NULL,          -- è¯·æ±‚JSON
        ResponseJson NVARCHAR(MAX) NULL,         -- å“åº”JSON
        IsSuccess BIT NOT NULL DEFAULT 0,        -- æ˜¯å¦æˆåŠŸ
        ErrorMessage NVARCHAR(500) NULL,         -- é”™è¯¯ä¿¡æ¯
        ElapsedMs INT NOT NULL DEFAULT 0,        -- è€—æ—¶(毫秒)
        CreateDate DATETIME NOT NULL,            -- åˆ›å»ºæ—¶é—´
        Creator NVARCHAR(50) NULL,               -- åˆ›å»ºäºº
        ModifyDate DATETIME NULL,                -- ä¿®æ”¹æ—¶é—´
        Modifier NVARCHAR(50) NULL               -- ä¿®æ”¹äºº
    );
    -- åˆ›å»ºç´¢å¼•
    CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType);
    CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate);
    CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess);
    PRINT 'MES接口日志表 Dt_MesApiLog åˆ›å»ºæˆåŠŸ';
END
ELSE
BEGIN
    PRINT 'MES接口日志表 Dt_MesApiLog å·²å­˜åœ¨';
END
-- æ’å…¥MES系统配置
IF NOT EXISTS (SELECT * FROM Dt_SystemConfig WHERE ConfigKey = 'MES_EquipmentCode')
BEGIN
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_EquipmentCode', 'WCS_001', 'MES设备编码', GETDATE(), 'System');
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_ResourceCode', 'RESOURCE_001', 'MES资源编码', GETDATE(), 'System');
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_ApiBaseUrl', 'http://mes-server/api', 'MES接口地址', GETDATE(), 'System');
    INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
    VALUES ('MES_TimeoutSeconds', '30', 'MES接口超时时间(秒)', GETDATE(), 'System');
    PRINT 'MES系统配置插入成功';
END
```
- [ ] **Step 2: æ‰§è¡ŒSQL脚本创建表**
Run: åœ¨SQL Server Management Studio中执行该脚本
Expected: è¡¨åˆ›å»ºæˆåŠŸï¼Œé…ç½®æ’å…¥æˆåŠŸ
- [ ] **Step 3: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/Database/Scripts/20260412_MesApiLog.sql
git commit -m "feat(MES): æ·»åŠ MES接口日志表和系统配置
- åˆ›å»º Dt_MesApiLog è¡¨è®°å½•接口调用日志
- æ·»åŠ MES相关系统配置项(设备编码、资源编码、接口地址等)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 2: åˆ›å»ºåŽç«¯å®žä½“å’ŒDTO
**Files:**
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/MesApiLogDto.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/InboundInContainerRequestDto.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/OutboundInContainerRequestDto.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/BindContainerRequestDto.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/UnbindContainerRequestDto.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/ContainerNgReportRequestDto.cs`
- [ ] **Step 1: åˆ›å»ºMES日志实体**
```csharp
using SqlSugar;
using System;
namespace WIDESEA_Model.Models.Mes
{
    /// <summary>
    /// MES接口调用日志实体
    /// </summary>
    [SugarTable("Dt_MesApiLog")]
    public class Dt_MesApiLog
    {
        /// <summary>
        /// ä¸»é”®ID
        /// </summary>
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public long Id { get; set; }
        /// <summary>
        /// æŽ¥å£ç±»åž‹ï¼šInboundInContainer, OutboundInContainer, BindContainer, UnbindContainer, ContainerNgReport
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = false)]
        public string ApiType { get; set; }
        /// <summary>
        /// è¯·æ±‚JSON
        /// </summary>
        [SugarColumn(ColumnDataType = "nvarchar(max)", IsNullable = true)]
        public string RequestJson { get; set; }
        /// <summary>
        /// å“åº”JSON
        /// </summary>
        [SugarColumn(ColumnDataType = "nvarchar(max)", IsNullable = true)]
        public string ResponseJson { get; set; }
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        [SugarColumn(IsNullable = false)]
        public bool IsSuccess { get; set; }
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯
        /// </summary>
        [SugarColumn(Length = 500, IsNullable = true)]
        public string ErrorMessage { get; set; }
        /// <summary>
        /// è€—时(毫秒)
        /// </summary>
        [SugarColumn(IsNullable = false)]
        public int ElapsedMs { get; set; }
        /// <summary>
        /// åˆ›å»ºæ—¶é—´
        /// </summary>
        [SugarColumn(IsNullable = false)]
        public DateTime CreateDate { get; set; }
        /// <summary>
        /// åˆ›å»ºäºº
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true)]
        public string Creator { get; set; }
        /// <summary>
        /// ä¿®æ”¹æ—¶é—´
        /// </summary>
        public DateTime? ModifyDate { get; set; }
        /// <summary>
        /// ä¿®æ”¹äºº
        /// </summary>
        [SugarColumn(Length = 50, IsNullable = true)]
        public string Modifier { get; set; }
    }
}
```
- [ ] **Step 2: åˆ›å»ºMES日志DTO**
```csharp
namespace WIDESEA_DTO.Mes
{
    /// <summary>
    /// MES接口日志DTO
    /// </summary>
    public class MesApiLogDto
    {
        /// <summary>
        /// æŽ¥å£ç±»åž‹
        /// </summary>
        public string ApiType { get; set; }
        /// <summary>
        /// è¯·æ±‚JSON
        /// </summary>
        public string RequestJson { get; set; }
        /// <summary>
        /// å“åº”JSON
        /// </summary>
        public string ResponseJson { get; set; }
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        public bool IsSuccess { get; set; }
        /// <summary>
        /// é”™è¯¯ä¿¡æ¯
        /// </summary>
        public string ErrorMessage { get; set; }
        /// <summary>
        /// è€—时(毫秒)
        /// </summary>
        public int ElapsedMs { get; set; }
        /// <summary>
        /// åˆ›å»ºäºº
        /// </summary>
        public string Creator { get; set; }
    }
}
```
- [ ] **Step 3: åˆ›å»ºè¿›ç«™è¯·æ±‚DTO**
```csharp
namespace WIDESEA_DTO.Mes
{
    /// <summary>
    /// æ‰˜ç›˜è¿›ç«™è¯·æ±‚DTO
    /// </summary>
    public class InboundInContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// åº“å­˜ID
        /// </summary>
        public long StockId { get; set; }
    }
}
```
- [ ] **Step 4: åˆ›å»ºå‡ºç«™è¯·æ±‚DTO**
```csharp
using System.Collections.Generic;
namespace WIDESEA_DTO.Mes
{
    /// <summary>
    /// æ‰˜ç›˜å‡ºç«™è¯·æ±‚DTO
    /// </summary>
    public class OutboundInContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// åº“å­˜ID
        /// </summary>
        public long StockId { get; set; }
        /// <summary>
        /// äº§å“å‚数列表(可选)
        /// </summary>
        public List<ParamItemDto> ParamList { get; set; }
        /// <summary>
        /// å‚数项DTO
        /// </summary>
        public class ParamItemDto
        {
            /// <summary>
            /// å‚数编码
            /// </summary>
            public string ParamCode { get; set; }
            /// <summary>
            /// å‚数值
            /// </summary>
            public string ParamValue { get; set; }
            /// <summary>
            /// é‡‡é›†æ—¶é—´
            /// </summary>
            public string CollectionTime { get; set; }
        }
    }
}
```
- [ ] **Step 5: åˆ›å»ºç»‘定请求DTO**
```csharp
using System.Collections.Generic;
namespace WIDESEA_DTO.Mes
{
    /// <summary>
    /// æ‰˜ç›˜ç”µèŠ¯ç»‘å®šè¯·æ±‚DTO
    /// </summary>
    public class BindContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// ç”µèŠ¯ç åˆ—è¡¨
        /// </summary>
        public List<string> SfcList { get; set; }
        /// <summary>
        /// ä½ç½®ä¿¡æ¯
        /// </summary>
        public string Location { get; set; }
        /// <summary>
        /// æ“ä½œç±»åž‹ï¼š0-默认 1-进站 2-出站 3-进出站
        /// </summary>
        public int OperationType { get; set; } = 1;
    }
}
```
- [ ] **Step 6: åˆ›å»ºè§£ç»‘请求DTO**
```csharp
using System.Collections.Generic;
namespace WIDESEA_DTO.Mes
{
    /// <summary>
    /// æ‰˜ç›˜ç”µèŠ¯è§£ç»‘è¯·æ±‚DTO
    /// </summary>
    public class UnbindContainerRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// ç”µèŠ¯ç åˆ—è¡¨
        /// </summary>
        public List<string> SfcList { get; set; }
    }
}
```
- [ ] **Step 7: åˆ›å»ºNG上报请求DTO**
```csharp
using System.Collections.Generic;
namespace WIDESEA_DTO.Mes
{
    /// <summary>
    /// æ‰˜ç›˜NG电芯上报请求DTO
    /// </summary>
    public class ContainerNgReportRequestDto
    {
        /// <summary>
        /// æ‰˜ç›˜ç¼–号
        /// </summary>
        public string PalletCode { get; set; }
        /// <summary>
        /// NG电芯列表
        /// </summary>
        public List<NgSfcItemDto> NgSfcList { get; set; }
        /// <summary>
        /// NG电芯项DTO
        /// </summary>
        public class NgSfcItemDto
        {
            /// <summary>
            /// äº§å“æ¡ç 
            /// </summary>
            public string Sfc { get; set; }
            /// <summary>
            /// NG代码
            /// </summary>
            public string NgCode { get; set; }
            /// <summary>
            /// NG设备
            /// </summary>
            public string NgEquipmentCode { get; set; }
            /// <summary>
            /// NG资源
            /// </summary>
            public string NgResourceCode { get; set; }
        }
    }
}
```
- [ ] **Step 8: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs
git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/*.cs
git commit -m "feat(MES): æ·»åŠ MES接口相关实体和DTO
- æ·»åŠ  Dt_MesApiLog æ—¥å¿—实体
- æ·»åŠ  MesApiLogDto åŠå„接口请求DTO
- æ”¯æŒè¿›ç«™ã€å‡ºç«™ã€ç»‘定、解绑、NG上报接口
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 3: åˆ›å»ºMES日志服务
**Files:**
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs`
- Create: `WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs`
- [ ] **Step 1: åˆ›å»ºMES日志服务接口**
```csharp
using System.Collections.Generic;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEA_DTO.Mes;
namespace WIDESEA_IMesService
{
    /// <summary>
    /// MES接口日志服务接口
    /// </summary>
    public interface IMesLogService : IDependency
    {
        /// <summary>
        /// è®°å½•MES接口调用日志
        /// </summary>
        /// <param name="log">日志DTO</param>
        /// <returns>是否记录成功</returns>
        Task<bool> LogAsync(MesApiLogDto log);
        /// <summary>
        /// èŽ·å–æœ€è¿‘çš„MES接口调用记录
        /// </summary>
        /// <param name="apiType">接口类型</param>
        /// <param name="count">记录数量</param>
        /// <returns>日志列表</returns>
        Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50);
    }
}
```
- [ ] **Step 2: åˆ›å»ºMES日志服务实现**
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEA_DTO.Mes;
using WIDESEA_IMesService;
using WIDESEA_Model.Models.Mes;
namespace WIDESEA_MesService
{
    /// <summary>
    /// MES接口日志服务实现
    /// </summary>
    public class MesLogService : IMesLogService
    {
        private readonly ISqlSugarClient _db;
        /// <summary>
        /// æž„造函数
        /// </summary>
        public MesLogService(ISqlSugarClient db)
        {
            _db = db;
        }
        /// <summary>
        /// è®°å½•MES接口调用日志
        /// </summary>
        public async Task<bool> LogAsync(MesApiLogDto log)
        {
            try
            {
                var entity = new Dt_MesApiLog
                {
                    ApiType = log.ApiType,
                    RequestJson = log.RequestJson,
                    ResponseJson = log.ResponseJson,
                    IsSuccess = log.IsSuccess,
                    ErrorMessage = log.ErrorMessage,
                    ElapsedMs = log.ElapsedMs,
                    CreateDate = DateTime.Now,
                    Creator = log.Creator
                };
                var result = await _db.Insertable(entity).ExecuteCommandAsync();
                return result > 0;
            }
            catch (Exception ex)
            {
                // è®°å½•日志失败不影响主流程
                Console.WriteLine($"记录MES日志失败: {ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–æœ€è¿‘çš„MES接口调用记录
        /// </summary>
        public async Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50)
        {
            try
            {
                var entities = await _db.Queryable<Dt_MesApiLog>()
                    .Where(x => x.ApiType == apiType)
                    .OrderByDescending(x => x.CreateDate)
                    .Take(count)
                    .ToListAsync();
                return entities.Select(e => new MesApiLogDto
                {
                    ApiType = e.ApiType,
                    RequestJson = e.RequestJson,
                    ResponseJson = e.ResponseJson,
                    IsSuccess = e.IsSuccess,
                    ErrorMessage = e.ErrorMessage,
                    ElapsedMs = e.ElapsedMs,
                    Creator = e.Creator
                }).ToList();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"获取MES日志失败: {ex.Message}");
                return new List<MesApiLogDto>();
            }
        }
    }
}
```
- [ ] **Step 3: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs
git add WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs
git commit -m "feat(MES): æ·»åŠ MES日志服务
- å®žçް IMesLogService æŽ¥å£
- æ”¯æŒè®°å½•和查询MES接口调用日志
- å¼‚常处理不影响主流程
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 4: æ‰©å±•StockInfoController添加进站/出站接口
**Files:**
- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs`
- [ ] **Step 1: åœ¨StockInfoController中添加MES接口方法**
在现有StockInfoController类中添加以下方法:
```csharp
/// <summary>
/// MES日志服务
/// </summary>
private readonly IMesLogService _mesLogService;
/// <summary>
/// MES服务(已在项目中定义)
/// </summary>
private readonly IMesService _mesService;
/// <summary>
/// ç³»ç»Ÿé…ç½®æœåŠ¡
/// </summary>
private readonly ISystemConfigService _configService;
// åœ¨æž„造函数中注入这些服务(如果尚未注入)
```
添加进站接口:
```csharp
/// <summary>
/// æ‰˜ç›˜è¿›ç«™ - è°ƒç”¨MES接口
/// </summary>
/// <param name="dto">进站请求DTO</param>
/// <returns>操作结果</returns>
[HttpPost("inboundInContainer")]
[Permission("MES_INBOUND")]
public async Task<WebResponseContent> InboundInContainer([FromBody] InboundInContainerRequestDto dto)
{
    var response = new WebResponseContent();
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    try
    {
        // 1. å‚数验证
        if (string.IsNullOrWhiteSpace(dto.PalletCode))
        {
            return response.Error("托盘编号不能为空");
        }
        // 2. æŸ¥è¯¢åº“存信息
        var stockInfo = await _service.FindAsIQueryable(x => x.Id == dto.StockId)
            .FirstAsync();
        if (stockInfo == null)
        {
            return response.Error("库存信息不存在");
        }
        // 3. éªŒè¯åº“存状态(仅"待入库"状态允许进站)
        if (stockInfo.Status != 0) // å‡è®¾0=待入库
        {
            return response.Error($"当前库存状态不允许进站操作");
        }
        // 4. èŽ·å–ç³»ç»Ÿé…ç½®
        var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
        var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
        if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
        {
            return response.Error("MES系统配置不完整,请联系管理员");
        }
        // 5. æž„造MES请求
        var mesRequest = new InboundInContainerRequest
        {
            EquipmentCode = equipmentCode,
            ResourceCode = resourceCode,
            LocalTime = DateTime.Now,
            ContainerCode = dto.PalletCode
        };
        string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
        // 6. è°ƒç”¨MES接口
        var mesResult = await _mesService.InboundInContainer(mesRequest);
        stopwatch.Stop();
        // 7. è®°å½•日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "InboundInContainer",
            RequestJson = requestJson,
            ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
            IsSuccess = mesResult.Success,
            ErrorMessage = mesResult.ErrorMessage,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        // 8. è¿”回结果
        if (mesResult.Success)
        {
            return response.OK("托盘进站成功");
        }
        else
        {
            return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
        }
    }
    catch (System.Exception ex)
    {
        stopwatch.Stop();
        // è®°å½•错误日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "InboundInContainer",
            IsSuccess = false,
            ErrorMessage = ex.Message,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        return response.Error($"托盘进站失败: {ex.Message}");
    }
}
```
添加出站接口:
```csharp
/// <summary>
/// æ‰˜ç›˜å‡ºç«™ - è°ƒç”¨MES接口
/// </summary>
/// <param name="dto">出站请求DTO</param>
/// <returns>操作结果</returns>
[HttpPost("outboundInContainer")]
[Permission("MES_OUTBOUND")]
public async Task<WebResponseContent> OutboundInContainer([FromBody] OutboundInContainerRequestDto dto)
{
    var response = new WebResponseContent();
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    try
    {
        // 1. å‚数验证
        if (string.IsNullOrWhiteSpace(dto.PalletCode))
        {
            return response.Error("托盘编号不能为空");
        }
        // 2. æŸ¥è¯¢åº“存信息
        var stockInfo = await _service.FindAsIQueryable(x => x.Id == dto.StockId)
            .FirstAsync();
        if (stockInfo == null)
        {
            return response.Error("库存信息不存在");
        }
        // 3. éªŒè¯åº“存状态("在库"或"出库中"状态允许出站)
        if (stockInfo.Status != 1 && stockInfo.Status != 2) // å‡è®¾1=在库, 2=出库中
        {
            return response.Error($"当前库存状态不允许出站操作");
        }
        // 4. èŽ·å–ç³»ç»Ÿé…ç½®
        var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
        var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
        if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
        {
            return response.Error("MES系统配置不完整,请联系管理员");
        }
        // 5. æž„造MES请求
        var mesRequest = new OutboundInContainerRequest
        {
            EquipmentCode = equipmentCode,
            ResourceCode = resourceCode,
            LocalTime = DateTime.Now,
            ContainerCode = dto.PalletCode,
            ParamList = dto.ParamList?.Select(p => new ParamItem
            {
                ParamCode = p.ParamCode,
                ParamValue = p.ParamValue,
                CollectionTime = DateTime.TryParse(p.CollectionTime, out var ct) ? ct : DateTime.Now
            }).ToList()
        };
        string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
        // 6. è°ƒç”¨MES接口
        var mesResult = await _mesService.OutboundInContainer(mesRequest);
        stopwatch.Stop();
        // 7. è®°å½•日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "OutboundInContainer",
            RequestJson = requestJson,
            ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
            IsSuccess = mesResult.Success,
            ErrorMessage = mesResult.ErrorMessage,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        // 8. è¿”回结果
        if (mesResult.Success)
        {
            return response.OK("托盘出站成功");
        }
        else
        {
            return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
        }
    }
    catch (System.Exception ex)
    {
        stopwatch.Stop();
        // è®°å½•错误日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "OutboundInContainer",
            IsSuccess = false,
            ErrorMessage = ex.Message,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        return response.Error($"托盘出站失败: {ex.Message}");
    }
}
```
- [ ] **Step 2: æ·»åŠ å¿…è¦çš„using语句**
在文件顶部添加:
```csharp
using WIDESEA_DTO.MES;
using WIDESEA_DTO.Mes;
using WIDESEA_IMesService;
```
- [ ] **Step 3: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs
git commit -m "feat(MES): åº“存信息页面添加进站/出站接口
- POST /api/StockInfo/inboundInContainer æ‰˜ç›˜è¿›ç«™
- POST /api/StockInfo/outboundInContainer æ‰˜ç›˜å‡ºç«™
- æ·»åŠ åº“å­˜çŠ¶æ€æ ¡éªŒ
- è®°å½•完整调用日志
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 5: æ‰©å±•StockInfoDetailController添加绑定/解绑/NG上报接口
**Files:**
- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs`
- [ ] **Step 1: åœ¨StockInfoDetailController中添加MES接口方法**
在现有StockInfoDetailController类中添加依赖注入和接口方法:
```csharp
/// <summary>
/// MES日志服务
/// </summary>
private readonly IMesLogService _mesLogService;
/// <summary>
/// MES服务
/// </summary>
private readonly IMesService _mesService;
/// <summary>
/// ç³»ç»Ÿé…ç½®æœåŠ¡
/// </summary>
private readonly ISystemConfigService _configService;
```
添加绑定接口:
```csharp
/// <summary>
/// æ‰˜ç›˜ç”µèŠ¯ç»‘å®š - è°ƒç”¨MES接口
/// </summary>
/// <param name="dto">绑定请求DTO</param>
/// <returns>操作结果</returns>
[HttpPost("bindContainer")]
[Permission("MES_BIND")]
public async Task<WebResponseContent> BindContainer([FromBody] BindContainerRequestDto dto)
{
    var response = new WebResponseContent();
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    try
    {
        // 1. å‚数验证
        if (string.IsNullOrWhiteSpace(dto.PalletCode))
        {
            return response.Error("托盘编号不能为空");
        }
        if (dto.SfcList == null || !dto.SfcList.Any())
        {
            return response.Error("电芯码列表不能为空");
        }
        // 2. èŽ·å–ç³»ç»Ÿé…ç½®
        var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
        var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
        if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
        {
            return response.Error("MES系统配置不完整,请联系管理员");
        }
        // 3. æž„造MES请求
        var mesRequest = new BindContainerRequest
        {
            EquipmentCode = equipmentCode,
            ResourceCode = resourceCode,
            LocalTime = DateTime.Now,
            ContainerCode = dto.PalletCode,
            ContainerSfcList = dto.SfcList.Select(sfc => new ContainerSfcItem
            {
                Sfc = sfc,
                Location = dto.Location ?? ""
            }).ToList(),
            OperationType = dto.OperationType
        };
        string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
        // 4. è°ƒç”¨MES接口
        var mesResult = await _mesService.BindContainer(mesRequest);
        stopwatch.Stop();
        // 5. è®°å½•日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "BindContainer",
            RequestJson = requestJson,
            ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
            IsSuccess = mesResult.Success,
            ErrorMessage = mesResult.ErrorMessage,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        // 6. è¿”回结果
        if (mesResult.Success)
        {
            return response.OK($"成功绑定 {dto.SfcList.Count} ä¸ªç”µèН");
        }
        else
        {
            return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
        }
    }
    catch (System.Exception ex)
    {
        stopwatch.Stop();
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "BindContainer",
            IsSuccess = false,
            ErrorMessage = ex.Message,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        return response.Error($"电芯绑定失败: {ex.Message}");
    }
}
```
添加解绑接口:
```csharp
/// <summary>
/// æ‰˜ç›˜ç”µèŠ¯è§£ç»‘ - è°ƒç”¨MES接口
/// </summary>
/// <param name="dto">解绑请求DTO</param>
/// <returns>操作结果</returns>
[HttpPost("unbindContainer")]
[Permission("MES_UNBIND")]
public async Task<WebResponseContent> UnbindContainer([FromBody] UnbindContainerRequestDto dto)
{
    var response = new WebResponseContent();
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    try
    {
        // 1. å‚数验证
        if (string.IsNullOrWhiteSpace(dto.PalletCode))
        {
            return response.Error("托盘编号不能为空");
        }
        if (dto.SfcList == null || !dto.SfcList.Any())
        {
            return response.Error("电芯码列表不能为空");
        }
        // 2. èŽ·å–ç³»ç»Ÿé…ç½®
        var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
        var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
        if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
        {
            return response.Error("MES系统配置不完整,请联系管理员");
        }
        // 3. æž„造MES请求
        var mesRequest = new UnBindContainerRequest
        {
            EquipmentCode = equipmentCode,
            ResourceCode = resourceCode,
            LocalTime = DateTime.Now,
            ContainCode = dto.PalletCode,
            SfcList = dto.SfcList
        };
        string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
        // 4. è°ƒç”¨MES接口
        var mesResult = await _mesService.UnBindContainer(mesRequest);
        stopwatch.Stop();
        // 5. è®°å½•日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "UnbindContainer",
            RequestJson = requestJson,
            ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
            IsSuccess = mesResult.Success,
            ErrorMessage = mesResult.ErrorMessage,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        // 6. è¿”回结果
        if (mesResult.Success)
        {
            return response.OK($"成功解绑 {dto.SfcList.Count} ä¸ªç”µèН");
        }
        else
        {
            return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
        }
    }
    catch (System.Exception ex)
    {
        stopwatch.Stop();
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "UnbindContainer",
            IsSuccess = false,
            ErrorMessage = ex.Message,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        return response.Error($"电芯解绑失败: {ex.Message}");
    }
}
```
添加NG上报接口:
```csharp
/// <summary>
/// æ‰˜ç›˜NG电芯上报 - è°ƒç”¨MES接口
/// </summary>
/// <param name="dto">NG上报请求DTO</param>
/// <returns>操作结果</returns>
[HttpPost("containerNgReport")]
[Permission("MES_NG_REPORT")]
public async Task<WebResponseContent> ContainerNgReport([FromBody] ContainerNgReportRequestDto dto)
{
    var response = new WebResponseContent();
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    try
    {
        // 1. å‚数验证
        if (string.IsNullOrWhiteSpace(dto.PalletCode))
        {
            return response.Error("托盘编号不能为空");
        }
        if (dto.NgSfcList == null || !dto.NgSfcList.Any())
        {
            return response.Error("NG电芯列表不能为空");
        }
        // 2. èŽ·å–ç³»ç»Ÿé…ç½®
        var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
        var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
        if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
        {
            return response.Error("MES系统配置不完整,请联系管理员");
        }
        // 3. æž„造MES请求
        var mesRequest = new ContainerNgReportRequest
        {
            EquipmentCode = equipmentCode,
            ResourceCode = resourceCode,
            LocalTime = DateTime.Now,
            ContainerCode = dto.PalletCode,
            NgSfcList = dto.NgSfcList.Select(ng => new NgSfcItem
            {
                Sfc = ng.Sfc,
                NgCode = ng.NgCode,
                NgEquipmentCode = ng.NgEquipmentCode,
                NgResourceCode = ng.NgResourceCode
            }).ToList()
        };
        string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
        // 4. è°ƒç”¨MES接口
        var mesResult = await _mesService.ContainerNgReport(mesRequest);
        stopwatch.Stop();
        // 5. è®°å½•日志
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "ContainerNgReport",
            RequestJson = requestJson,
            ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
            IsSuccess = mesResult.Success,
            ErrorMessage = mesResult.ErrorMessage,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        // 6. è¿”回结果
        if (mesResult.Success)
        {
            return response.OK($"成功上报 {dto.NgSfcList.Count} ä¸ªNG电芯");
        }
        else
        {
            return response.Error($"MES接口调用失败: {mesResult.ErrorMessage}");
        }
    }
    catch (System.Exception ex)
    {
        stopwatch.Stop();
        await _mesLogService.LogAsync(new MesApiLogDto
        {
            ApiType = "ContainerNgReport",
            IsSuccess = false,
            ErrorMessage = ex.Message,
            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
            Creator = UserContext.Current.UserName
        });
        return response.Error($"NG上报失败: {ex.Message}");
    }
}
```
- [ ] **Step 2: æ·»åŠ å¿…è¦çš„using语句**
在文件顶部添加:
```csharp
using WIDESEA_DTO.MES;
using WIDESEA_DTO.Mes;
using WIDESEA_IMesService;
```
- [ ] **Step 3: æäº¤**
```bash
git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs
git commit -m "feat(MES): åº“存明细页面添加绑定/解绑/NG上报接口
- POST /api/StockInfoDetail/bindContainer æ‰˜ç›˜ç”µèŠ¯ç»‘å®š
- POST /api/StockInfoDetail/unbindContainer æ‰˜ç›˜ç”µèŠ¯è§£ç»‘
- POST /api/StockInfoDetail/containerNgReport NG电芯上报
- è®°å½•完整调用日志
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 6: åˆ›å»ºå‰ç«¯MES API调用模块
**Files:**
- Create: `WMS/WIDESEA_WMSClient/src/api/mes.js`
- [ ] **Step 1: åˆ›å»ºMES API模块**
```javascript
/**
 * MES接口API模块
 */
import axios from 'axios';
const baseURL = '/api';
// åº“存信息相关MES接口
export const stockInfoMesApi = {
  /**
   * æ‰˜ç›˜è¿›ç«™
   * @param {Object} data - è¯·æ±‚数据 { palletCode, stockId }
   * @returns {Promise}
   */
  inboundInContainer(data) {
    return axios.post(`${baseURL}/StockInfo/inboundInContainer`, data);
  },
  /**
   * æ‰˜ç›˜å‡ºç«™
   * @param {Object} data - è¯·æ±‚数据 { palletCode, stockId, paramList }
   * @returns {Promise}
   */
  outboundInContainer(data) {
    return axios.post(`${baseURL}/StockInfo/outboundInContainer`, data);
  }
};
// åº“存明细相关MES接口
export const stockDetailMesApi = {
  /**
   * æ‰˜ç›˜ç”µèŠ¯ç»‘å®š
   * @param {Object} data - è¯·æ±‚数据 { palletCode, sfcList, location, operationType }
   * @returns {Promise}
   */
  bindContainer(data) {
    return axios.post(`${baseURL}/StockInfoDetail/bindContainer`, data);
  },
  /**
   * æ‰˜ç›˜ç”µèŠ¯è§£ç»‘
   * @param {Object} data - è¯·æ±‚数据 { palletCode, sfcList }
   * @returns {Promise}
   */
  unbindContainer(data) {
    return axios.post(`${baseURL}/StockInfoDetail/unbindContainer`, data);
  },
  /**
   * æ‰˜ç›˜NG电芯上报
   * @param {Object} data - è¯·æ±‚数据 { palletCode, ngSfcList }
   * @returns {Promise}
   */
  containerNgReport(data) {
    return axios.post(`${baseURL}/StockInfoDetail/containerNgReport`, data);
  }
};
export default {
  stockInfo: stockInfoMesApi,
  stockDetail: stockDetailMesApi
};
```
- [ ] **Step 2: æäº¤**
```bash
git add WMS/WIDESEA_WMSClient/src/api/mes.js
git commit -m "feat(MES): æ·»åŠ å‰ç«¯MES API调用模块
- å°è£…库存信息进站/出站接口
- å°è£…库存明细绑定/解绑/NG上报接口
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 7: åˆ›å»ºMES确认对话框组件
**Files:**
- Create: `WMS/WIDESEA_WMSClient/src/components/MesConfirmDialog.vue`
- [ ] **Step 1: åˆ›å»ºç¡®è®¤å¯¹è¯æ¡†ç»„ä»¶**
```vue
<template>
  <el-dialog
    v-model="visible"
    :title="dialogTitle"
    width="500px"
    :close-on-click-modal="false"
    @close="handleClose"
  >
    <div class="mes-confirm-content">
      <p class="operation-text">{{ operationText }}</p>
      <div class="info-section">
        <div class="info-row" v-for="(item, index) in displayInfo" :key="index">
          <span class="info-label">{{ item.label }}:</span>
          <span class="info-value">{{ item.value }}</span>
        </div>
      </div>
      <div v-if="errorMessage" class="error-message">
        <el-icon><Warning /></el-icon>
        <span>{{ errorMessage }}</span>
      </div>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="handleClose">取消</el-button>
        <el-button
          type="primary"
          :loading="loading"
          @click="handleConfirm"
        >
          ç¡®è®¤æ‰§è¡Œ
        </el-button>
      </span>
    </template>
  </el-dialog>
</template>
<script>
import { ref, computed } from 'vue';
import { Warning } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
export default {
  name: 'MesConfirmDialog',
  components: {
    Warning
  },
  props: {
    modelValue: {
      type: Boolean,
      default: false
    },
    operationType: {
      type: String,
      required: true
      },
    palletCode: {
      type: String,
      required: true
    },
    stockInfo: {
      type: Object,
      default: null
    },
    detailInfo: {
      type: Object,
      default: null
    }
  },
  emits: ['update:modelValue', 'confirm'],
  setup(props, { emit }) {
    const visible = computed({
      get: () => props.modelValue,
      set: (val) => emit('update:modelValue', val)
    });
    const loading = ref(false);
    const errorMessage = ref('');
    const operationConfig = {
      inbound: { title: '托盘进站', text: '您即将执行托盘进站操作' },
      outbound: { title: '托盘出站', text: '您即将执行托盘出站操作' },
      bind: { title: '电芯绑定', text: '您即将执行电芯绑定操作' },
      unbind: { title: '电芯解绑', text: '您即将执行电芯解绑操作' },
      ngReport: { title: 'NG上报', text: '您即将执行NG电芯上报操作' }
    };
    const dialogTitle = computed(() => {
      return operationConfig[props.operationType]?.title || '确认操作';
    });
    const operationText = computed(() => {
      return operationConfig[props.operationType]?.text || '';
    });
    const displayInfo = computed(() => {
      const info = [
        { label: '托盘码', value: props.palletCode }
      ];
      if (props.detailInfo) {
        info.push({ label: '电芯数量', value: props.detailInfo.sfcCount || '-' });
      }
      return info;
    });
    const handleClose = () => {
      visible.value = false;
      errorMessage.value = '';
    };
    const handleConfirm = async () => {
      loading.value = true;
      errorMessage.value = '';
      try {
        emit('confirm', {
          operationType: props.operationType,
          palletCode: props.palletCode,
          stockInfo: props.stockInfo,
          detailInfo: props.detailInfo,
          onSuccess: () => {
            visible.value = false;
            loading.value = false;
          },
          onError: (error) => {
            errorMessage.value = error;
            loading.value = false;
          }
        });
      } catch (error) {
        errorMessage.value = error.message || '操作失败';
        loading.value = false;
      }
    };
    return {
      visible,
      loading,
      errorMessage,
      dialogTitle,
      operationText,
      displayInfo,
      handleClose,
      handleConfirm
    };
  }
};
</script>
<style scoped>
.mes-confirm-content {
  padding: 10px 0;
}
.operation-text {
  font-size: 14px;
  color: #303133;
  margin-bottom: 20px;
  font-weight: 500;
}
.info-section {
  background: #f8fafc;
  border-radius: 8px;
  padding: 16px;
  margin-bottom: 16px;
}
.info-row {
  display: flex;
  margin-bottom: 12px;
  font-size: 14px;
}
.info-row:last-child {
  margin-bottom: 0;
}
.info-label {
  color: #909399;
  width: 80px;
  flex-shrink: 0;
}
.info-value {
  color: #303133;
  font-weight: 500;
}
.error-message {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px;
  background: #fef0f0;
  border: 1px solid #fde2e2;
  border-radius: 6px;
  color: #f56c6c;
  font-size: 14px;
}
.error-message .el-icon {
  font-size: 18px;
}
</style>
```
- [ ] **Step 2: æäº¤**
```bash
git add WMS/WIDESEA_WMSClient/src/components/MesConfirmDialog.vue
git commit -m "feat(MES): æ·»åŠ MES操作确认对话框组件
- æ”¯æŒå¤šç§æ“ä½œç±»åž‹ï¼ˆè¿›ç«™ã€å‡ºç«™ã€ç»‘定、解绑、NG上报)
- æ˜¾ç¤ºå…³é”®ä¿¡æ¯ï¼ˆæ‰˜ç›˜ç ã€ç”µèŠ¯æ•°é‡ç­‰ï¼‰
- é”™è¯¯çŠ¶æ€å±•ç¤º
- ç¡®è®¤/取消操作
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 8: ä¿®æ”¹stockInfo.vue添加操作列
**Files:**
- Modify: `WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue`
- [ ] **Step 1: åœ¨script中添加组件引入和方法**
在现有的script setup中添加:
```javascript
import MesConfirmDialog from '@/components/MesConfirmDialog.vue';
import { stockInfoMesApi } from '@/api/mes';
import { ElMessage, ElMessageBox } from 'element-plus';
import { ref } from 'vue';
// MES对话框状态
const mesDialogVisible = ref(false);
const currentOperationType = ref('');
const currentStockRow = ref(null);
const mesLoading = ref(false);
// æ‰“å¼€MES确认对话框
const openMesDialog = (operationType, row) => {
  currentOperationType.value = operationType;
  currentStockRow.value = row;
  mesDialogVisible.value = true;
};
// å¤„理MES确认
const handleMesConfirm = async ({ onSuccess, onError }) => {
  const row = currentStockRow.value;
  const operationType = currentOperationType.value;
  try {
    let result;
    switch (operationType) {
      case 'inbound':
        result = await stockInfoMesApi.inboundInContainer({
          palletCode: row.palletCode,
          stockId: row.id
        });
        break;
      case 'outbound':
        result = await stockInfoMesApi.outboundInContainer({
          palletCode: row.palletCode,
          stockId: row.id
        });
        break;
    }
    if (result.data.status) {
      ElMessage.success(result.data.message || '操作成功');
      onSuccess();
      // åˆ·æ–°åˆ—表
      proxy.$refs.grid.load();
    } else {
      onError(result.data.message || '操作失败');
    }
  } catch (error) {
    onError(error.response?.data?.message || error.message || '网络错误,请稍后重试');
  }
};
// æ£€æŸ¥æŒ‰é’®æ˜¯å¦åº”该显示
const shouldShowButton = (buttonType, row) => {
  const status = row.status; // 0=待入库, 1=在库, 2=出库中, 3=锁定
  switch (buttonType) {
    case 'inbound':
      return status === 0; // ä»…待入库显示进站
    case 'outbound':
      return status === 1 || status === 2; // åœ¨åº“或出库中显示出站
    default:
      return false;
  }
};
```
- [ ] **Step 2: åœ¨columns中添加操作列**
在现有的columns ref定义中添加操作列(在最后添加):
```javascript
const columns = ref([
  // ... çŽ°æœ‰åˆ—å®šä¹‰ ...
  {
    field: "actions",
    title: "操作",
    width: 200,
    fixed: "right",
    align: "center",
    formatter: (row) => {
      const buttons = [];
      if (shouldShowButton('inbound', row)) {
        buttons.push(
          `<el-button type="primary" size="small" onclick="window.handleInbound(${row.id})">进站</el-button>`
        );
      }
      if (shouldShowButton('outbound', row)) {
        buttons.push(
          `<el-button type="success" size="small" onclick="window.handleOutbound(${row.id})">出站</el-button>`
        );
      }
      return buttons.join(' ');
    }
  }
]);
```
- [ ] **Step 3: åœ¨æ¨¡æ¿ä¸­æ·»åŠ å¯¹è¯æ¡†ç»„ä»¶**
在template中的view-grid标签后添加:
```vue
<template>
  <view-grid ... >
  </view-grid>
  <!-- MES确认对话框 -->
  <MesConfirmDialog
    v-model="mesDialogVisible"
    :operation-type="currentOperationType"
    :pallet-code="currentStockRow?.palletCode"
    :stock-info="currentStockRow"
    @confirm="handleMesConfirm"
  />
</template>
```
- [ ] **Step 4: æ·»åŠ å…¨å±€æ–¹æ³•ç”¨äºŽHTML字符串中的onclick**
在setup的最后添加:
```javascript
// æŒ‚载到window供HTML字符串中的onclick使用
window.handleInbound = (id) => {
  const row = proxy.$refs.grid.tableData.find(item => item.id === id);
  if (row) openMesDialog('inbound', row);
};
window.handleOutbound = (id) => {
  const row = proxy.$refs.grid.tableData.find(item => item.id === id);
  if (row) openMesDialog('outbound', row);
};
```
- [ ] **Step 5: æäº¤**
```bash
git add WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue
git commit -m "feat(MES): åº“存信息页面添加进站/出站操作列
- æ·»åŠ æ“ä½œåˆ—ï¼Œæ ¹æ®åº“å­˜çŠ¶æ€åŠ¨æ€æ˜¾ç¤ºæŒ‰é’®
- å¾…入库状态显示进站按钮
- åœ¨åº“/出库中状态显示出站按钮
- é›†æˆMES确认对话框
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## Task 9: ä¿®æ”¹stockInfoDetail.vue添加操作列
**Files:**
- Modify: `WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail.vue`
- [ ] **Step 1: åœ¨script中添加组件引入和方法**
在现有的script setup中添加:
```javascript
import MesConfirmDialog from '@/components/MesConfirmDialog.vue';
import { stockDetailMesApi } from '@/api/mes';
import { ElMessage } from 'element-plus';
import { ref } from 'vue';
// MES对话框状态
const mesDialogVisible = ref(false);
const currentOperationType = ref('');
const currentDetailRow = ref(null);
const mesLoading = ref(false);
// æ‰“å¼€MES确认对话框
const openMesDialog = (operationType, row) => {
  currentOperationType.value = operationType;
  currentDetailRow.value = row;
  mesDialogVisible.value = true;
};
// å¤„理MES确认
const handleMesConfirm = async ({ onSuccess, onError }) => {
  const row = currentDetailRow.value;
  const operationType = currentOperationType.value;
  try {
    let result;
    switch (operationType) {
      case 'bind':
        result = await stockDetailMesApi.bindContainer({
          palletCode: row.palletCode || 'P001', // éœ€è¦ä»Žåº“存信息获取
          sfcList: [row.serialNumber],
          location: row.location || '',
          operationType: 1
        });
        break;
      case 'unbind':
        result = await stockDetailMesApi.unbindContainer({
          palletCode: row.palletCode || 'P001',
          sfcList: [row.serialNumber]
        });
        break;
      case 'ngReport':
        result = await stockDetailMesApi.containerNgReport({
          palletCode: row.palletCode || 'P001',
          ngSfcList: [{
            sfc: row.serialNumber,
            ngCode: 'NG001',
            ngEquipmentCode: 'WCS_001',
            ngResourceCode: 'RESOURCE_001'
          }]
        });
        break;
    }
    if (result.data.status) {
      ElMessage.success(result.data.message || '操作成功');
      onSuccess();
      proxy.$refs.grid.load();
    } else {
      onError(result.data.message || '操作失败');
    }
  } catch (error) {
    onError(error.response?.data?.message || error.message || '网络错误,请稍后重试');
  }
};
// æ£€æŸ¥æŒ‰é’®æ˜¯å¦åº”该显示
const shouldShowButton = (row) => {
  const status = row.status; // 1=正常, 2=异常, 99=已锁定
  return status !== 99; // éž"已锁定"状态显示所有按钮
};
```
- [ ] **Step 2: åœ¨columns中添加操作列**
在现有的columns ref定义中添加操作列(在最后添加):
```javascript
const columns = ref([
  // ... çŽ°æœ‰åˆ—å®šä¹‰ ...
  {
    field: "actions",
    title: "操作",
    width: 280,
    fixed: "right",
    align: "center",
    formatter: (row) => {
      if (!shouldShowButton(row)) {
        return '<span class="text-muted">暂无可执行操作</span>';
      }
      return `
        <el-button type="primary" size="small" onclick="window.handleBind(${row.id})">绑定</el-button>
        <el-button type="warning" size="small" onclick="window.handleUnbind(${row.id})">解绑</el-button>
        <el-button type="danger" size="small" onclick="window.handleNgReport(${row.id})">NG上报</el-button>
      `;
    }
  }
]);
```
- [ ] **Step 3: åœ¨æ¨¡æ¿ä¸­æ·»åŠ å¯¹è¯æ¡†ç»„ä»¶**
在template中的view-grid标签后添加:
```vue
<template>
  <view-grid ... >
  </view-grid>
  <!-- MES确认对话框 -->
  <MesConfirmDialog
    v-model="mesDialogVisible"
    :operation-type="currentOperationType"
    :pallet-code="currentDetailRow?.palletCode"
    :detail-info="{ sfcCount: 1 }"
    @confirm="handleMesConfirm"
  />
</template>
```
- [ ] **Step 4: æ·»åŠ å…¨å±€æ–¹æ³•**
在setup的最后添加:
```javascript
window.handleBind = (id) => {
  const row = proxy.$refs.grid.tableData.find(item => item.id === id);
  if (row) openMesDialog('bind', row);
};
window.handleUnbind = (id) => {
  const row = proxy.$refs.grid.tableData.find(item => item.id === id);
  if (row) openMesDialog('unbind', row);
};
window.handleNgReport = (id) => {
  const row = proxy.$refs.grid.tableData.find(item => item.id === id);
  if (row) openMesDialog('ngReport', row);
};
```
- [ ] **Step 5: æäº¤**
```bash
git add WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail.vue
git commit -m "feat(MES): åº“存明细页面添加绑定/解绑/NG上报操作列
- æ·»åŠ æ“ä½œåˆ—ï¼Œæ ¹æ®ç”µèŠ¯çŠ¶æ€åŠ¨æ€æ˜¾ç¤ºæŒ‰é’®
- éžé”å®šçŠ¶æ€æ˜¾ç¤ºç»‘å®šã€è§£ç»‘ã€NG上报按钮
- é›†æˆMES确认对话框
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
```
---
## æµ‹è¯•检查清单
完成以上所有任务后,进行以下测试:
### åŽç«¯æµ‹è¯•
- [ ] éªŒè¯æ•°æ®åº“表 Dt_MesApiLog å·²åˆ›å»º
- [ ] éªŒè¯ç³»ç»Ÿé…ç½®å·²æ’å…¥
- [ ] æµ‹è¯•进站接口(Postman或Swagger)
- [ ] æµ‹è¯•出站接口
- [ ] æµ‹è¯•绑定接口
- [ ] æµ‹è¯•解绑接口
- [ ] æµ‹è¯•NG上报接口
- [ ] éªŒè¯æ—¥å¿—记录正确
### å‰ç«¯æµ‹è¯•
- [ ] åº“存信息页面操作列正常显示
- [ ] æŒ‰é’®æ ¹æ®çŠ¶æ€æ­£ç¡®æ˜¾ç¤º/隐藏
- [ ] ç‚¹å‡»æŒ‰é’®å¼¹å‡ºç¡®è®¤å¯¹è¯æ¡†
- [ ] ç¡®è®¤åŽæˆåŠŸè°ƒç”¨æŽ¥å£
- [ ] æˆåŠŸåŽåˆ·æ–°åˆ—è¡¨
- [ ] å¤±è´¥åŽæ˜¾ç¤ºé”™è¯¯æç¤º
- [ ] åº“存明细页面操作列正常显示
- [ ] æ‰€æœ‰æŒ‰é’®åŠŸèƒ½æ­£å¸¸
### é›†æˆæµ‹è¯•
- [ ] å®Œæ•´æµç¨‹æµ‹è¯•(点击→确认→执行→结果)
- [ ] ç½‘络异常处理
- [ ] MES服务异常处理
- [ ] æƒé™æŽ§åˆ¶æµ‹è¯•
---
## å¤‡æ³¨
1. **状态值映射**:代码中使用的状态值(0=待入库, 1=在库等)需要根据实际枚举值调整
2. **托盘码获取**:库存明细页面中需要通过stockId关联获取托盘码
3. **权限配置**:需要在系统中添加MES相关权限项
4. **MES接口地址**:在系统配置中设置正确的MES接口地址
5. **错误处理**:根据实际MES返回的错误码调整提示信息
Code/docs/superpowers/specs/2026-04-11-manual-task-creation-design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,151 @@
# æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡åŠŸèƒ½è®¾è®¡
## 1. éœ€æ±‚概述
在 WMS ç•Œé¢ä¸Šæ·»åŠ æ‰‹åŠ¨åˆ›å»ºä»»åŠ¡åŠŸèƒ½ã€‚ç”¨æˆ·è¾“å…¥èµ·ç‚¹ã€ç»ˆç‚¹ã€æ¡ç ã€ä»“åº“ID,系统自动生成任务号等字段,发送给 WCS。WCS æ ¹æ®èµ·ç‚¹æ˜¯å¦ä¸ºçº¿ä½“点位(11068/11010/11001)判断是否需要写入输送线任务。
## 2. æ•´ä½“流程
```
用户(WMS界面)
    â†“ è¾“入:任务类型/起点/终点/条码/仓库ID
WMS后端
    â†“ è°ƒç”¨ BaseDal.GetTaskNo() ç”Ÿæˆ TaskNum
    â†“ æ ¹æ®ä»»åŠ¡ç±»åž‹è®¾ç½®çŠ¶æ€
    â†“ è°ƒç”¨ WCS ReceiveTask
WCS后端
    â†“ åˆ¤æ–­èµ·ç‚¹æ˜¯å¦ä¸ºçº¿ä½“点位(11068/11010/11001)
    â†“ æ˜¯ â†’ æŸ¥ Storage.Devices èŽ·å–è¾“é€çº¿å®žä¾‹
    â†“ å†™å…¥è¾“送线任务(起点/终点/任务号等)
    â†“ è¿”回成功
```
## 3. WMS å‰ç«¯æ”¹åЍ
### 3.1 ä»»åŠ¡é¡µé¢ (`task.vue`)
在现有任务管理页面添加**手动创建任务**按钮,弹出对话框。
### 3.2 å¯¹è¯æ¡†å­—段
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| ä»»åŠ¡ç±»åž‹ | ä¸‹æ‹‰æ¡† | å…¥åº“ / å‡ºåº“ / ç§»åº“ |
| èµ·ç‚¹åœ°å€ | è¾“入框 | ç”¨æˆ·è¾“入,如 11068 |
| ç»ˆç‚¹åœ°å€ | è¾“入框 | ç”¨æˆ·è¾“å…¥ |
| æ¡ç  | è¾“入框 | ç”¨æˆ·è¾“å…¥ |
| ä»“库ID | è¾“入框 | ç”¨æˆ·è¾“å…¥ |
| ä¼˜å…ˆçº§ | åªè¯» | é»˜è®¤å€¼ 1 |
## 4. WMS åŽç«¯æ”¹åЍ
### 4.1 æ–°å¢žæŽ¥å£
**Controller:** `TaskController.cs`
```
POST /api/Task/CreateManualTask
```
### 4.2 Service æ–¹æ³•
**TaskService.cs** æ–°å¢ž `CreateManualTaskAsync` æ–¹æ³•:
```csharp
public async Task<WebResponseContent> CreateManualTaskAsync(CreateManualTaskDto dto)
{
    // 1. è°ƒç”¨ BaseDal.GetTaskNo() ç”Ÿæˆ TaskNum
    // 2. æ ¹æ®ä»»åŠ¡ç±»åž‹è®¾ç½®çŠ¶æ€
    //    - å…¥åº“ â†’ InNew (200)
    //    - å‡ºåº“ â†’ OutNew (100)
    //    - ç§»åº“ â†’ RelocationNew (300)
    // 3. æž„建 Dt_Task å®žä½“
    // 4. è°ƒç”¨ WCS ReceiveTask (POST /api/Task/ReceiveTask)
    // 5. è¿”回结果
}
```
### 4.3 DTO å®šä¹‰
```csharp
public class CreateManualTaskDto
{
    public int TaskType { get; set; }      // 1=入库, 2=出库, 3=移库
    public string SourceAddress { get; set; }
    public string TargetAddress { get; set; }
    public string Barcode { get; set; }
    public int WarehouseId { get; set; }
    public int Grade { get; set; } = 1;    // é»˜è®¤ä¼˜å…ˆçº§1
}
```
## 5. WCS åŽç«¯æ”¹åЍ
### 5.1 å·²æœ‰é€»è¾‘复用
`ReceiveWMSTask` æ–¹æ³•已支持接收 WMS ä»»åŠ¡å¹¶åˆ†å‘åˆ°å¯¹åº” FlowService:
- å…¥åº“任务 â†’ `InboundTaskFlowService.InitializeOnReceive()`
- å‡ºåº“任务 â†’ `OutboundTaskFlowService.InitializeOnReceive()`
- ç§»åº“任务 â†’ `RelocationTaskFlowService.InitializeOnReceive()`
### 5.2 çº¿ä½“点位判断
在 `InboundTaskFlowService.InitializeOnReceive()` ä¸­ï¼Œåˆ¤æ–­ `SourceAddress` æ˜¯å¦ä¸ºçº¿ä½“点位(11068/11010/11001)。这三个点位均为**入库线体点位**。
### 5.3 èŽ·å–è¾“é€çº¿å®žä¾‹
```csharp
var conveyorLine = Storage.Devices
    .FirstOrDefault(x => x.DeviceProDTOs.Any(d => d.DeviceChildCode == sourceAddress));
```
### 5.4 æ–°å¢žï¼šå†™å…¥è¾“送线任务
**此逻辑为新增**,目前 FlowService ä¸­ä¸å­˜åœ¨å†™å…¥è¾“送线的逻辑,需要在判断为线体点位后新增:
1. èŽ·å–è¾“é€çº¿å®žä¾‹æˆåŠŸåŽï¼Œè¯»å–æºçº¿ä½“å·
2. æž„造 `ConveyorLineTaskCommandNew` å¯¹è±¡
3. è°ƒç”¨ `conveyorLine.WriteCustomer(sourceLineNo, command)` å†™å…¥ PLC
写入字段:
- `TaskNo` = WCS åˆ†é…çš„任务号
- `Source` = èµ·ç‚¹åœ°å€
- `Target` = ç»ˆç‚¹åœ°å€
- `Barcode` = æ¡ç 
- `WCS_STB` = 1 (标记WCS已发送)
### 5.5 é‡å¤ä»»åŠ¡å¤„ç†
`ReceiveWMSTask` ä¸­å·²æœ‰é‡å¤ä»»åŠ¡æ£€æŸ¥é€»è¾‘ï¼ˆæ ¹æ® TaskNum æˆ– PalletCode),手动创建时如遇重复返回错误。
## 6. ä»»åŠ¡çŠ¶æ€æžšä¸¾
| ç±»åž‹ | æ–°å»ºçŠ¶æ€ | çŠ¶æ€å€¼ |
|------|----------|--------|
| å…¥åº“ | InNew | 200 |
| å‡ºåº“ | OutNew | 100 |
| ç§»åº“ | RelocationNew | 300 |
## 7. é”™è¯¯å¤„理
- ä»»åŠ¡å·èŽ·å–å¤±è´¥ â†’ è¿”回错误
- WCS æŸ¥æ‰¾è¾“送线实例失败 â†’ è¿”回错误给 WMS
- å†™å…¥ PLC å¤±è´¥ â†’ è¿”回错误,WMS ä»»åŠ¡çŠ¶æ€ä¿æŒæ–°å»º
- WCS è¿”回重复任务 â†’ è¿”回错误给 WMS
## 8. æ¶‰åŠçš„æºæ–‡ä»¶
### WMS å‰ç«¯
- `WMS/WIDESEA_WMSClient/src/views/taskinfo/task.vue`
- `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js`
### WMS åŽç«¯
- `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs`
- `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs`
### WCS åŽç«¯
- `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/TaskService.cs`
- `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/InboundTaskFlowService.cs`
- `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/OutboundTaskFlowService.cs`
- `WCS/WIDESEAWCS_Server/WIDESEAWCS_TaskInfoService/Flows/RelocationTaskFlowService.cs`
- `WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs` (参考写入模式)
- `WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLine/ConveyorLineTaskCommandNew.cs` (任务命令结构)
Code/docs/superpowers/specs/2026-04-12-manual-dispatch-tasks-to-wcs-design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,154 @@
# æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ° WCS åŠŸèƒ½è®¾è®¡
**日期**: 2026-04-12
**状态**: å·²æ‰¹å‡†
---
## 1. åŠŸèƒ½æ¦‚è¿°
在 WMS å‰ç«¯ä»»åŠ¡ç®¡ç†é¡µé¢æ·»åŠ å·¥å…·æ æŒ‰é’®ï¼Œæ”¯æŒä»Žä»»åŠ¡åˆ—è¡¨ä¸­é€‰æ‹©ä¸€ä¸ªæˆ–å¤šä¸ªä»»åŠ¡ï¼Œæ‰‹åŠ¨ç¼–è¾‘åœ°å€ï¼ˆèµ·ç‚¹/终点)和优先级后,下发到 WCS ç³»ç»Ÿæ‰§è¡Œã€‚
**使用场景**:当任务因设备故障、网络问题等原因未能正常下发时,操作员可手动重新下发。
---
## 2. å‰ç«¯æ”¹åЍ
### 2.1 æ–°å¢žæ–‡ä»¶
| æ–‡ä»¶ | ç”¨é€” |
|------|------|
| `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | æ‰¹é‡ä¸‹å‘编辑弹窗组件 |
### 2.2 ä¿®æ”¹æ–‡ä»¶
| æ–‡ä»¶ | æ”¹åЍ |
|------|------|
| `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | æ·»åŠ å·¥å…·æ "手动下发"按钮 |
### 2.3 å¼¹çª—交互
**触发方式**:工具栏点击"手动下发"按钮
**前置条件**:至少选中一项任务
**弹窗内容**:
- é¡¶éƒ¨æ˜¾ç¤º"已选任务数: N ä¸ª"
- è¡¨æ ¼å±•示所有选中任务,列:任务号、起点地址、终点地址、优先级、状态
- åœ°å€å­—段:`el-input` æˆ– `el-select`(取决于是否有标准地址字典)
- ä¼˜å…ˆçº§ï¼š`el-input-number`,范围 1-99
- çŠ¶æ€åˆ—ï¼šä»…å±•ç¤ºï¼Œä¸å¯ç¼–è¾‘
**按钮**:取消、确认下发
**下发后**:
- æˆåŠŸï¼šå¼¹çª—å…³é—­ï¼Œåˆ·æ–°ä»»åŠ¡åˆ—è¡¨
- å¤±è´¥ï¼šå¼¹çª—内显示失败任务列表(任务号 + å¤±è´¥åŽŸå› ï¼‰
---
## 3. åŽç«¯æ”¹åЍ
### 3.1 æ–°å¢ž DTO
**文件**: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs`
```csharp
public class DispatchTaskDto
{
    public long TaskId { get; set; }
    public string SourceAddress { get; set; }
    public string TargetAddress { get; set; }
    public int Priority { get; set; }
}
```
### 3.2 æ–°å¢žæŽ¥å£
**Controller**: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs`
```
POST /api/TaskInfo/DispatchTasksToWCS
```
**Service**: `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs`
新增方法 `DispatchTasksToWCSAsync(List<DispatchTaskDto> dtos)`
### 3.3 ä¸šåŠ¡é€»è¾‘
```
1. æ ¡éªŒé€‰ä¸­ä»»åŠ¡æ•°é‡ > 0
2. æ ¡éªŒä»»åŠ¡çŠ¶æ€ï¼Œä»…å…è®¸ä»¥ä¸‹çŠ¶æ€çš„ä»»åŠ¡ä¸‹å‘ï¼š
   - å…¥åº“:InNew(入库新单)
   - å‡ºåº“:OutNew(出库新单)
   - ç§»åº“:RelocationNew(移库新单)
3. æ›´æ–°ä»»åŠ¡çš„åœ°å€ï¼ˆSourceAddress、TargetAddress)和优先级(Grade)
4. è°ƒç”¨ WCS æŽ¥å£ï¼šPOST http://localhost:9292/api/Task/ReceiveManualTask
5. æ±‡æ€»æ¯æ¡ä»»åŠ¡çš„ä¸‹å‘ç»“æžœï¼ˆæˆåŠŸ/失败及原因)
6. è¿”回给前端
```
### 3.4 WCS æŽ¥å£å¤ç”¨
**已有接口**: `POST /api/Task/ReceiveManualTask`(commit b032763)
WMS è°ƒç”¨è¯¥æŽ¥å£ï¼Œå°†ä»»åŠ¡æ•°æ®ä»¥ `WMSTaskDTO` æ ¼å¼å‘送。
---
## 4. é”™è¯¯å¤„理
| åœºæ™¯ | å‰ç«¯å¤„理 | åŽç«¯å¤„理 |
|------|----------|----------|
| æœªé€‰ä¸­ä»»åŠ¡ | æŒ‰é’®ç½®ç°ï¼›ç‚¹å‡»æç¤º"请先选择任务" | è¿”回错误提示 |
| ä»»åŠ¡çŠ¶æ€ä¸å¯ä¸‹å‘ | è¡Œå†…状态列标红,地址/优先级置灰不可编辑 | æ ¡éªŒå¹¶è¿”回不可下发任务列表 |
| WCS è°ƒç”¨è¶…æ—¶ | è¯¥ä»»åŠ¡æ ‡è®°"下发超时" | æ ‡è®°ä»»åŠ¡ï¼Œä¸‹å‘ç»“æžœè¿”å›žå‰ç«¯ |
| WCS è¿”回失败 | æ˜¾ç¤º WCS é”™è¯¯åŽŸå› ï¼ˆå¦‚"设备忙") | é€ä¼  WCS é”™è¯¯ä¿¡æ¯ |
| å…¨éƒ¨æˆåŠŸ | å¼¹çª—关闭,刷新列表 | è¿”回成功 |
---
## 5. æ¶‰åŠçš„代码文件
| å±‚级 | æ–‡ä»¶è·¯å¾„ | æ”¹åŠ¨ç±»åž‹ |
|------|----------|----------|
| å‰ç«¯ | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/task.js` | ä¿®æ”¹ |
| å‰ç«¯ | `WMS/WIDESEA_WMSClient/src/extension/taskinfo/extend/dispatchTasksToWCS.vue` | æ–°å¢ž |
| å‰ç«¯ | `WMS/WIDESEA_WMSClient/src/api/` | æ–°å¢ž API æ–¹æ³• |
| åŽç«¯ | `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Task/DispatchTaskDto.cs` | æ–°å¢ž |
| åŽç«¯ | `WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_WCS.cs` | ä¿®æ”¹ |
| åŽç«¯ | `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs` | ä¿®æ”¹ |
| åŽç«¯ | `WMS/WIDESEA_WMSServer/WIDESEA_Core/Helper/HTTP/HttpClientHelper.cs` | å¤ç”¨ |
---
## 6. UI ç¤ºæ„å›¾
```
┌─────────────────────────────────────────────────────────────┐
│  æ‰‹åŠ¨ä¸‹å‘ä»»åŠ¡åˆ° WCS                                    [X]  â”‚
├─────────────────────────────────────────────────────────────┤
│  å·²é€‰ä»»åŠ¡æ•°: 5 ä¸ª                                          â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”   â”‚
│  â”‚ ä»»åŠ¡å· â”‚ èµ·ç‚¹åœ°å€     â”‚ ç»ˆç‚¹åœ°å€    â”‚ ä¼˜å…ˆçº§ â”‚ çŠ¶æ€  â”‚   â”‚
│  â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤   â”‚
│  â”‚ T001   â”‚ [PLC-A01  ] â”‚ [Rack-05] â”‚ [ 5 ] â”‚ å…¥åº“新单│   â”‚
│  â”‚ T002   â”‚ [PLC-A02  ] â”‚ [Rack-06] â”‚ [ 3 ] â”‚ å…¥åº“新单│   â”‚
│  â””─────────────────────────────────────────────────────┘   â”‚
├─────────────────────────────────────────────────────────────┤
│                                    [取消]  [确认下发]       â”‚
└─────────────────────────────────────────────────────────────┘
```
---
## 7. éªŒæ”¶æ ‡å‡†
- [ ] å·¥å…·æ æ·»åŠ "手动下发"按钮,未选中任务时按钮可用但点击提示选择
- [ ] å¼¹çª—正确显示所有选中任务
- [ ] ä»…入库新单/出库新单/移库新单状态的任务可编辑地址和优先级
- [ ] ç¡®è®¤ä¸‹å‘后调用后端接口
- [ ] æˆåŠŸ/失败结果正确反馈给用户
- [ ] æˆåŠŸåŽåˆ·æ–°ä»»åŠ¡åˆ—è¡¨
Code/docs/superpowers/specs/2026-04-12-mes-integration-design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,700 @@
# WMS库存页面MES接口集成设计文档
**日期**: 2026-04-12
**项目**: WIDESEA WMS
**作者**: Claude Code
---
## ä¸€ã€éœ€æ±‚概述
为WMS库存信息页面和库存明细页面添加操作列,调用MES系统的出站/入站/绑定/解绑/NG上报接口,实现WMS与MES的数据同步。
### åŠŸèƒ½èŒƒå›´
**库存信息页面(stockInfo.vue)- æ‰˜ç›˜çº§åˆ«ï¼š**
- æ‰˜ç›˜è¿›ç«™ï¼ˆè°ƒç”¨MES InboundInContainer接口)
- æ‰˜ç›˜å‡ºç«™ï¼ˆè°ƒç”¨MES OutboundInContainer接口)
**库存明细页面(stockInfoDetail.vue)- ç”µèŠ¯çº§åˆ«ï¼š**
- æ‰˜ç›˜ç”µèŠ¯ç»‘å®šï¼ˆè°ƒç”¨MES BindContainer接口)
- æ‰˜ç›˜ç”µèŠ¯è§£ç»‘ï¼ˆè°ƒç”¨MES UnBindContainer接口)
- æ‰˜ç›˜NG电芯上报(调用MES ContainerNgReport接口)
---
## äºŒã€æž¶æž„设计
### 2.1 ç³»ç»Ÿæž¶æž„图
```
┌─────────────────────────────────────────────────────────────────┐
│                         å‰ç«¯ (Vue 3)                           â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â” â”‚
│  â”‚  stockInfo.vue   â”‚  â”‚stockInfoDetail   â”‚  â”‚ MesDialog.vue  â”‚ â”‚
│  â”‚  (库存信息)       â”‚  â”‚  .vue(库存明细)   â”‚  â”‚ (确认对话框)    â”‚ â”‚
│  â”‚  + æ“ä½œåˆ—        â”‚  â”‚  + æ“ä½œåˆ—         â”‚  â”‚                â”‚ â”‚
│  â””────────┬─────────┘  â””────────┬─────────┘  â””────────┬───────┘ â”‚
│           â”‚                     â”‚                      â”‚         â”‚
│           â””─────────────────────┴──────────────────────┘         â”‚
│                              â”‚                                   â”‚
└──────────────────────────────┼───────────────────────────────────┘
                               â”‚ HTTP API
┌──────────────────────────────┼───────────────────────────────────┐
│                               â–¼                    åŽç«¯ (.NET)   â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â” â”‚
│  â”‚ StockController  â”‚→ â”‚  MesController   â”‚  â”‚ MesLogService  â”‚ â”‚
│  â”‚                  â”‚  â”‚                  â”‚  â”‚                â”‚ â”‚
│  â””──────────────────┘  â””────────┬─────────┘  â””────────────────┘ â”‚
│                                 â”‚                                  â”‚
│                                 â–¼                                  â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â” â”‚
│  â”‚   IMesService    â”‚→ â”‚   MesService     â”‚  â”‚Dt_MesApiLog    â”‚ â”‚
│  â”‚   (接口定义)      â”‚  â”‚   (HTTP调用)     â”‚  â”‚  (日志表)       â”‚ â”‚
│  â””──────────────────┘  â””──────────────────┘  â””────────────────┘ â”‚
└─────────────────────────────────────────────────────────────────┘
                               â”‚
                               â–¼
                        â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
                        â”‚  MES系统     â”‚
                        â”‚  (外部接口)   â”‚
                        â””──────────────┘
```
### 2.2 æŠ€æœ¯æ ˆ
- **前端**: Vue 3, Element Plus, Pinia
- **后端**: .NET 8, ASP.NET Core, SqlSugar ORM
- **数据库**: SQL Server
- **HTTP客户端**: HttpClient (后端调用MES)
---
## ä¸‰ã€å‰ç«¯è®¾è®¡
### 3.1 åº“存信息页面 (stockInfo.vue)
#### æ“ä½œåˆ—设计
**表格列配置:**
```javascript
{
  field: "actions",
  title: "操作",
  width: 200,
  fixed: "right",
  align: "center",
  formatter: (row) => renderActions(row)
}
```
**按钮定义:**
- è¿›ç«™æŒ‰é’®ï¼šè°ƒç”¨æ‰˜ç›˜è¿›ç«™æŽ¥å£
- å‡ºç«™æŒ‰é’®ï¼šè°ƒç”¨æ‰˜ç›˜å‡ºç«™æŽ¥å£
**按钮显示规则:**
| åº“存状态 | è¿›ç«™ | å‡ºç«™ |
|---------|:----:|:----:|
| å¾…入库 (0) | âœ“ | âœ— |
| åœ¨åº“ (1) | âœ— | âœ“ |
| å‡ºåº“中 (2) | âœ— | âœ“ |
| é”å®š (3) | âœ— | âœ— |
### 3.2 åº“存明细页面 (stockInfoDetail.vue)
#### æ“ä½œåˆ—设计
**表格列配置:**
```javascript
{
  field: "actions",
  title: "操作",
  width: 280,
  fixed: "right",
  align: "center",
  formatter: (row) => renderActions(row)
}
```
**按钮定义:**
- ç»‘定按钮:调用托盘电芯绑定接口
- è§£ç»‘按钮:调用托盘电芯解绑接口
- NG上报按钮:调用托盘NG电芯上报接口
**按钮显示规则:**
| ç”µèŠ¯çŠ¶æ€ | ç»‘定 | è§£ç»‘ | NG上报 |
|---------|:----:|:----:|:------:|
| æ­£å¸¸ (1) | âœ“ | âœ“ | âœ“ |
| å¼‚常 (2) | âœ“ | âœ“ | âœ“ |
| å·²é”å®š (99) | âœ— | âœ— | âœ— |
### 3.3 ç¡®è®¤å¯¹è¯æ¡†ç»„ä»¶ (MesConfirmDialog.vue)
**组件职责:**
- æ˜¾ç¤ºå³å°†æ‰§è¡Œçš„æ“ä½œä¿¡æ¯
- å±•示关键参数(托盘码、设备编码等)
- æä¾›ç¡®è®¤/取消按钮
- æ˜¾ç¤ºè°ƒç”¨ç»“果(成功/失败)
**Props定义:**
```typescript
interface MesConfirmDialogProps {
  visible: boolean;
  operationType: 'inbound' | 'outbound' | 'bind' | 'unbind' | 'ngReport';
  palletCode: string;
  stockInfo?: any;
  detailInfo?: any;
}
```
### 3.4 äº¤äº’流程
```
用户点击操作按钮
    â”‚
    â–¼
检查登录状态 â”€â”€â”€â”€â”€â”€â†’ æœªç™»å½• â”€â”€â”€â”€â†’ æç¤º"请先登录"
    â”‚
    â–¼ å·²ç™»å½•
检查操作权限 â”€â”€â”€â”€â”€â”€â†’ æ— æƒé™ â”€â”€â”€â”€â†’ æç¤º"无权限执行此操作"
    â”‚
    â–¼ æœ‰æƒé™
弹出确认对话框
    â”‚
    â–¼
用户点击"确认执行"
    â”‚
    â–¼
显示loading状态
    â”‚
    â–¼
调用后端API
    â”‚
    â”œâ”€â†’ æˆåŠŸ â”€â”€â”€â”€â†’ æ˜¾ç¤ºæˆåŠŸæç¤º â”€â”€â”€â”€â†’ åˆ·æ–°åˆ—表
    â”‚
    â””─→ å¤±è´¥ â”€â”€â”€â”€â†’ æ˜¾ç¤ºé”™è¯¯æç¤º + "重试"按钮
```
---
## å››ã€åŽç«¯è®¾è®¡
### 4.1 API接口设计
#### 4.1.1 åº“存信息相关接口
**托盘进站**
```csharp
/// <summary>
/// æ‰˜ç›˜è¿›ç«™ - è°ƒç”¨MES接口
/// </summary>
[HttpPost("inboundInContainer")]
public async Task<WebResponseContent> InboundInContainer([FromBody] InboundInContainerRequestDto dto)
```
**请求DTO:**
```csharp
public class InboundInContainerRequestDto
{
    /// <summary>
    /// æ‰˜ç›˜ç¼–号
    /// </summary>
    public string PalletCode { get; set; }
    /// <summary>
    /// åº“å­˜ID
    /// </summary>
    public long StockId { get; set; }
}
```
**托盘出站**
```csharp
/// <summary>
/// æ‰˜ç›˜å‡ºç«™ - è°ƒç”¨MES接口
/// </summary>
[HttpPost("outboundInContainer")]
public async Task<WebResponseContent> OutboundInContainer([FromBody] OutboundInContainerRequestDto dto)
```
**请求DTO:**
```csharp
public class OutboundInContainerRequestDto
{
    /// <summary>
    /// æ‰˜ç›˜ç¼–号
    /// </summary>
    public string PalletCode { get; set; }
    /// <summary>
    /// åº“å­˜ID
    /// </summary>
    public long StockId { get; set; }
    /// <summary>
    /// äº§å“å‚数列表(可选)
    /// </summary>
    public List<ParamItemDto> ParamList { get; set; }
}
```
#### 4.1.2 åº“存明细相关接口
**托盘电芯绑定**
```csharp
/// <summary>
/// æ‰˜ç›˜ç”µèŠ¯ç»‘å®š - è°ƒç”¨MES接口
/// </summary>
[HttpPost("bindContainer")]
public async Task<WebResponseContent> BindContainer([FromBody] BindContainerRequestDto dto)
```
**请求DTO:**
```csharp
public class BindContainerRequestDto
{
    /// <summary>
    /// æ‰˜ç›˜ç¼–号
    /// </summary>
    public string PalletCode { get; set; }
    /// <summary>
    /// ç”µèŠ¯ç åˆ—è¡¨
    /// </summary>
    public List<string> SfcList { get; set; }
    /// <summary>
    /// ä½ç½®ä¿¡æ¯
    /// </summary>
    public string Location { get; set; }
    /// <summary>
    /// æ“ä½œç±»åž‹ï¼š0-默认 1-进站 2-出站 3-进出站
    /// </summary>
    public int OperationType { get; set; } = 1;
}
```
**托盘电芯解绑**
```csharp
/// <summary>
/// æ‰˜ç›˜ç”µèŠ¯è§£ç»‘ - è°ƒç”¨MES接口
/// </summary>
[HttpPost("unbindContainer")]
public async Task<WebResponseContent> UnbindContainer([FromBody] UnbindContainerRequestDto dto)
```
**请求DTO:**
```csharp
public class UnbindContainerRequestDto
{
    /// <summary>
    /// æ‰˜ç›˜ç¼–号
    /// </summary>
    public string PalletCode { get; set; }
    /// <summary>
    /// ç”µèŠ¯ç åˆ—è¡¨
    /// </summary>
    public List<string> SfcList { get; set; }
}
```
**托盘NG电芯上报**
```csharp
/// <summary>
/// æ‰˜ç›˜NG电芯上报 - è°ƒç”¨MES接口
/// </summary>
[HttpPost("containerNgReport")]
public async Task<WebResponseContent> ContainerNgReport([FromBody] ContainerNgReportRequestDto dto)
```
**请求DTO:**
```csharp
public class ContainerNgReportRequestDto
{
    /// <summary>
    /// æ‰˜ç›˜ç¼–号
    /// </summary>
    public string PalletCode { get; set; }
    /// <summary>
    /// NG电芯列表
    /// </summary>
    public List<NgSfcItemDto> NgSfcList { get; set; }
}
public class NgSfcItemDto
{
    /// <summary>
    /// äº§å“æ¡ç 
    /// </summary>
    public string Sfc { get; set; }
    /// <summary>
    /// NG代码
    /// </summary>
    public string NgCode { get; set; }
    /// <summary>
    /// NG设备
    /// </summary>
    public string NgEquipmentCode { get; set; }
    /// <summary>
    /// NG资源
    /// </summary>
    public string NgResourceCode { get; set; }
}
```
### 4.2 æ•°æ®åº“设计
#### 4.2.1 MES接口调用日志表
```sql
CREATE TABLE Dt_MesApiLog (
    Id BIGINT PRIMARY KEY IDENTITY(1,1),
    ApiType NVARCHAR(50) NOT NULL,           -- æŽ¥å£ç±»åž‹
    RequestJson NVARCHAR(MAX) NULL,          -- è¯·æ±‚JSON
    ResponseJson NVARCHAR(MAX) NULL,         -- å“åº”JSON
    IsSuccess BIT NOT NULL DEFAULT 0,        -- æ˜¯å¦æˆåŠŸ
    ErrorMessage NVARCHAR(500) NULL,         -- é”™è¯¯ä¿¡æ¯
    ElapsedMs INT NOT NULL DEFAULT 0,        -- è€—æ—¶(毫秒)
    CreateDate DATETIME NOT NULL,            -- åˆ›å»ºæ—¶é—´
    Creator NVARCHAR(50) NULL,               -- åˆ›å»ºäºº
    INDEX IX_MesApiLog_ApiType (ApiType),
    INDEX IX_MesApiLog_CreateDate (CreateDate)
);
```
**字段说明:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| Id | bigint | ä¸»é”® |
| ApiType | string(50) | æŽ¥å£ç±»åž‹ï¼šInboundInContainer, OutboundInContainer, BindContainer, UnbindContainer, ContainerNgReport |
| RequestJson | string(MAX) | MES接口请求JSON |
| ResponseJson | string(MAX) | MES接口响应JSON |
| IsSuccess | bool | è°ƒç”¨æ˜¯å¦æˆåŠŸ |
| ErrorMessage | string(500) | å¤±è´¥æ—¶çš„错误信息 |
| ElapsedMs | int | æŽ¥å£è°ƒç”¨è€—时(毫秒) |
| CreateDate | datetime | åˆ›å»ºæ—¶é—´ |
| Creator | string(50) | åˆ›å»ºäºº |
#### 4.2.2 ç³»ç»Ÿé…ç½®è¡¨æ‰©å±•
```sql
-- åœ¨ Dt_SystemConfig è¡¨ä¸­æ–°å¢žMES配置项
INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate)
VALUES
('MES_EquipmentCode', 'WCS_001', 'MES设备编码', GETDATE()),
('MES_ResourceCode', 'RESOURCE_001', 'MES资源编码', GETDATE()),
('MES_ApiBaseUrl', 'http://mes-server/api', 'MES接口地址', GETDATE()),
('MES_TimeoutSeconds', '30', 'MES接口超时时间(秒)', GETDATE());
```
### 4.3 æœåŠ¡å±‚è®¾è®¡
#### 4.3.1 MES日志服务接口
```csharp
/// <summary>
/// MES接口日志服务接口
/// </summary>
public interface IMesLogService : IDependency
{
    /// <summary>
    /// è®°å½•MES接口调用日志
    /// </summary>
    Task<bool> LogAsync(MesApiLogDto log);
    /// <summary>
    /// èŽ·å–æœ€è¿‘çš„MES接口调用记录
    /// </summary>
    Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50);
}
```
#### 4.3.2 MES日志服务实现
```csharp
/// <summary>
/// MES接口日志服务实现
/// </summary>
public class MesLogService : IMesLogService
{
    private readonly ISqlSugarClient _db;
    public async Task<bool> LogAsync(MesApiLogDto log)
    {
        var entity = new Dt_MesApiLog
        {
            ApiType = log.ApiType,
            RequestJson = log.RequestJson,
            ResponseJson = log.ResponseJson,
            IsSuccess = log.IsSuccess,
            ErrorMessage = log.ErrorMessage,
            ElapsedMs = log.ElapsedMs,
            CreateDate = DateTime.Now,
            Creator = log.Creator
        };
        return await _db.Insertable(entity).ExecuteCommandAsync() > 0;
    }
    public async Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50)
    {
        return await _db.Queryable<Dt_MesApiLog>()
            .Where(x => x.ApiType == apiType)
            .OrderByDescending(x => x.CreateDate)
            .Take(count)
            .ToListAsync();
    }
}
```
---
## äº”、接口调用流程
### 5.1 æ‰˜ç›˜è¿›ç«™æµç¨‹
```
用户点击"进站"按钮
    â”‚
    â–¼
前端弹出确认对话框(显示托盘码、设备编码等)
    â”‚
    â–¼
用户确认
    â”‚
    â–¼
前端调用 POST /api/StockInfo/inboundInContainer
    â”‚
    â–¼
后端 StockInfoController.InboundInContainer()
    â”‚
    â”œâ”€â†’ 1. éªŒè¯åº“存状态(仅"待入库"状态允许进站)
    â”‚
    â”œâ”€â†’ 2. èŽ·å–ç³»ç»Ÿé…ç½®ï¼ˆè®¾å¤‡ç¼–ç ã€èµ„æºç¼–ç ï¼‰
    â”‚
    â”œâ”€â†’ 3. æž„造MES请求对象
    â”‚
    â”œâ”€â†’ 4. è°ƒç”¨ IMesService.InboundInContainer()
    â”‚   â”‚
    â”‚   â”œâ”€â†’ è®°å½•开始时间
    â”‚   â”œâ”€â†’ å‘送HTTP请求到MES
    â”‚   â”œâ”€â†’ è®°å½•响应、耗时
    â”‚   â””─→ ä¿å­˜æ—¥å¿—到 Dt_MesApiLog
    â”‚
    â”œâ”€â†’ 5. è¿”回结果给前端
    â”‚
    â””─→ å‰ç«¯æ˜¾ç¤ºæˆåŠŸ/失败提示
```
### 5.2 æ‰˜ç›˜ç”µèŠ¯ç»‘å®šæµç¨‹
```
用户点击"绑定"按钮
    â”‚
    â–¼
前端弹出确认对话框(显示托盘码、电芯码列表)
    â”‚
    â–¼
用户确认
    â”‚
    â–¼
前端调用 POST /api/StockInfoDetail/bindContainer
    â”‚
    â–¼
后端 StockInfoDetailController.BindContainer()
    â”‚
    â”œâ”€â†’ 1. éªŒè¯ç”µèŠ¯çŠ¶æ€ï¼ˆéž"已锁定"状态允许绑定)
    â”‚
    â”œâ”€â†’ 2. èŽ·å–ç³»ç»Ÿé…ç½®
    â”‚
    â”œâ”€â†’ 3. æž„造MES请求对象(包含电芯码列表)
    â”‚
    â”œâ”€â†’ 4. è°ƒç”¨ IMesService.BindContainer()
    â”‚   â”‚
    â”‚   â”œâ”€â†’ è®°å½•开始时间
    â”‚   â”œâ”€â†’ å‘送HTTP请求到MES
    â”‚   â”œâ”€â†’ è®°å½•响应、耗时
    â”‚   â””─→ ä¿å­˜æ—¥å¿—到 Dt_MesApiLog
    â”‚
    â”œâ”€â†’ 5. è¿”回结果给前端
    â”‚
    â””─→ å‰ç«¯æ˜¾ç¤ºæˆåŠŸ/失败提示
```
---
## å…­ã€é”™è¯¯å¤„理
### 6.1 é”™è¯¯ç±»åž‹
| é”™è¯¯ç±»åž‹ | è¯´æ˜Ž | å¤„理方式 |
|---------|------|---------|
| ç½‘络超时 | MES服务器无响应或连接超时 | æ˜¾ç¤ºé”™è¯¯æç¤ºï¼Œæä¾›"重试"按钮 |
| ä¸šåŠ¡æ ¡éªŒå¤±è´¥ | æ‰˜ç›˜ä¸å­˜åœ¨ã€ç”µèŠ¯å·²ç»‘å®šç­‰ | æ˜¾ç¤ºMES返回的错误信息 |
| è®¤è¯å¤±è´¥ | MES接口认证信息过期 | æ˜¾ç¤º"MES认证失败,请联系管理员" |
| å‚数错误 | è¯·æ±‚参数不完整或格式错误 | æ˜¾ç¤º"参数错误:{具体错误}" |
| æœªçŸ¥é”™è¯¯ | MES系统返回异常响应 | æ˜¾ç¤º"MES系统异常,请稍后重试" |
### 6.2 å‰ç«¯é”™è¯¯æç¤º
```javascript
// æˆåŠŸæç¤º
ElMessage.success('托盘进站成功');
// å¤±è´¥æç¤º
ElMessage.error({
  message: 'MES服务器连接超时,请检查网络后重试',
  showRetry: true,
  onRetry: () => retryOperation()
});
```
### 6.3 åŽç«¯é”™è¯¯æ—¥å¿—
```csharp
try
{
    // è°ƒç”¨MES接口
}
catch (HttpRequestException ex)
{
    // ç½‘络异常
    await _mesLogService.LogAsync(new MesApiLogDto
    {
        ApiType = "InboundInContainer",
        IsSuccess = false,
        ErrorMessage = $"网络异常: {ex.Message}",
        ElapsedMs = elapsedMs
    });
    return ResponseContent.Error("MES服务器连接失败,请检查网络");
}
catch (Exception ex)
{
    // å…¶ä»–异常
    _logger.LogError(ex, "调用MES接口异常");
    return ResponseContent.Error($"MES接口调用失败: {ex.Message}");
}
```
---
## ä¸ƒã€æƒé™æŽ§åˆ¶
### 7.1 åŠŸèƒ½æƒé™å®šä¹‰
建议在系统中新增以下功能权限:
| æƒé™ä»£ç  | æƒé™åç§° | è¯´æ˜Ž |
|---------|---------|------|
| MES_INBOUND | MES进站操作 | å…è®¸æ‰§è¡Œæ‰˜ç›˜è¿›ç«™æ“ä½œ |
| MES_OUTBOUND | MES出站操作 | å…è®¸æ‰§è¡Œæ‰˜ç›˜å‡ºç«™æ“ä½œ |
| MES_BIND | MES绑定操作 | å…è®¸æ‰§è¡Œç”µèŠ¯ç»‘å®šæ“ä½œ |
| MES_UNBIND | MES解绑操作 | å…è®¸æ‰§è¡Œç”µèŠ¯è§£ç»‘æ“ä½œ |
| MES_NG_REPORT | MES NG上报 | å…è®¸æ‰§è¡ŒNG上报操作 |
### 7.2 æƒé™éªŒè¯
```csharp
[HttpPost("inboundInContainer")]
[Permission("MES_INBOUND")]
public async Task<WebResponseContent> InboundInContainer([FromBody] InboundInContainerRequestDto dto)
{
    // æŽ¥å£å®žçް
}
```
---
## å…«ã€æ–‡ä»¶å˜æ›´æ¸…单
### 8.1 å‰ç«¯æ–‡ä»¶
```
WMS/WIDESEA_WMSClient/src/
├── views/stock/
│   â”œâ”€â”€ stockInfo.vue                         # ä¿®æ”¹ï¼šæ·»åŠ æ“ä½œåˆ—
│   â””── stockInfoDetail.vue                   # ä¿®æ”¹ï¼šæ·»åŠ æ“ä½œåˆ—
├── components/
│   â””── MesConfirmDialog.vue                  # æ–°å¢žï¼šMES确认对话框
└── api/
    â””── mes.js                                # æ–°å¢žï¼šMES API调用
```
### 8.2 åŽç«¯æ–‡ä»¶
```
WMS/WIDESEA_WMSServer/
├── Controllers/
│   â””── Stock/
│       â”œâ”€â”€ StockInfoController.cs            # ä¿®æ”¹ï¼šæ·»åŠ è¿›ç«™/出站接口
│       â””── StockInfoDetailController.cs      # ä¿®æ”¹ï¼šæ·»åŠ ç»‘å®š/解绑/NG上报接口
├── Services/
│   â””── Mes/
│       â”œâ”€â”€ IMesLogService.cs                 # æ–°å¢žï¼šæ—¥å¿—服务接口
│       â””── MesLogService.cs                  # æ–°å¢žï¼šæ—¥å¿—服务实现
└── DTO/
    â”œâ”€â”€ Mes/
    â”‚   â”œâ”€â”€ MesApiLogDto.cs                   # æ–°å¢žï¼šæ—¥å¿—DTO
    â”‚   â”œâ”€â”€ InboundInContainerRequestDto.cs   # æ–°å¢žï¼šè¿›ç«™è¯·æ±‚DTO
    â”‚   â”œâ”€â”€ OutboundInContainerRequestDto.cs  # æ–°å¢žï¼šå‡ºç«™è¯·æ±‚DTO
    â”‚   â”œâ”€â”€ BindContainerRequestDto.cs        # æ–°å¢žï¼šç»‘定请求DTO
    â”‚   â”œâ”€â”€ UnbindContainerRequestDto.cs      # æ–°å¢žï¼šè§£ç»‘请求DTO
    â”‚   â””── ContainerNgReportRequestDto.cs    # æ–°å¢žï¼šNG上报请求DTO
    â””── Models/
        â””── Mes/
            â””── Dt_MesApiLog.cs                # æ–°å¢žï¼šæ—¥å¿—实体
```
### 8.3 æ•°æ®åº“文件
```
Database/
└── Scripts/
    â””── 20260412_MesApiLog.sql                # æ–°å¢žï¼šæ—¥å¿—表创建脚本
```
---
## ä¹ã€æµ‹è¯•要点
### 9.1 åŠŸèƒ½æµ‹è¯•
| æµ‹è¯•场景 | é¢„期结果 |
|---------|---------|
| å¾…入库状态托盘点击进站 | å¼¹å‡ºç¡®è®¤å¯¹è¯æ¡†ï¼Œç¡®è®¤åŽæˆåŠŸè°ƒç”¨ |
| åœ¨åº“状态托盘点击进站 | è¿›ç«™æŒ‰é’®ä¸æ˜¾ç¤º |
| ç½‘络中断时点击操作 | æ˜¾ç¤ºç½‘络错误提示,提供重试按钮 |
| MES返回业务错误 | æ˜¾ç¤ºMES返回的具体错误信息 |
| ç‚¹å‡»æ“ä½œåŽåˆ·æ–°é¡µé¢ | æ“ä½œè®°å½•已保存到日志表 |
### 9.2 æ€§èƒ½æµ‹è¯•
| æŒ‡æ ‡ | ç›®æ ‡å€¼ |
|------|--------|
| MES接口响应时间 | < 2秒 |
| å‰ç«¯æŒ‰é’®ç‚¹å‡»å“åº” | < 100ms |
| æ—¥å¿—写入耗时 | < 50ms |
---
## åã€åŽç»­ä¼˜åŒ–建议
1. **批量操作**:支持选中多个托盘/电芯批量调用MES接口
2. **自动重试**:对于网络超时等临时故障,自动重试3次
3. **接口监控**:增加MES接口调用的实时监控和告警
4. **参数配置化**:将超时时间、重试次数等参数做成可配置项
5. **操作审计**:增加操作审计日志,记录谁在什么时间执行了什么操作
---
**文档版本**: 1.0
**最后更新**: 2026-04-12
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1001/config.json
@@ -2,7 +2,7 @@
  "id": "1001",
  "name": "A\u533A_\u4E00\u6CE8\u8F93\u9001\u7EBF",
  "plcType": "S71500",
  "port": 105,
  "port": 103,
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-1001",
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/instance-1002/config.json
@@ -2,7 +2,7 @@
  "id": "1002",
  "name": "A\u533A_\u6362\u76D8\u8F93\u9001\u7EBF",
  "plcType": "S71500",
  "port": 108,
  "port": 104,
  "activationKey": "",
  "autoStart": false,
  "protocolTemplateId": "protocol-1002",
Code/²âÊÔ¹¤¾ß/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Server/Data/protocol-templates.json
@@ -705,7 +705,7 @@
        "dbNumber": 50,
        "offset": 18,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 22,
        "direction": 2
      },
@@ -849,7 +849,7 @@
        "dbNumber": 50,
        "offset": 578,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 22,
        "direction": 2
      },
@@ -993,7 +993,7 @@
        "dbNumber": 50,
        "offset": 1378,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 22,
        "direction": 2
      },
@@ -1137,8 +1137,8 @@
        "dbNumber": 50,
        "offset": 1618,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "dataType": 0,
        "length": 20,
        "direction": 2
      }
    ]
@@ -1367,14 +1367,14 @@
        "dbNumber": 50,
        "offset": 18,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "dataType": 0,
        "length": 20,
        "direction": 2
      },
      {
        "fieldKey": "11010_TaskNo",
        "dbNumber": 50,
        "offset": 400,
        "offset": 360,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1383,7 +1383,7 @@
      {
        "fieldKey": "11010_Source",
        "dbNumber": 50,
        "offset": 402,
        "offset": 362,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1392,7 +1392,7 @@
      {
        "fieldKey": "11010_Target",
        "dbNumber": 50,
        "offset": 404,
        "offset": 364,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1401,7 +1401,7 @@
      {
        "fieldKey": "11010_BoxType",
        "dbNumber": 50,
        "offset": 406,
        "offset": 366,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1410,7 +1410,7 @@
      {
        "fieldKey": "11010_CV_State",
        "dbNumber": 50,
        "offset": 407,
        "offset": 367,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1419,7 +1419,7 @@
      {
        "fieldKey": "11010_CV_ERRCode",
        "dbNumber": 50,
        "offset": 408,
        "offset": 368,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1428,7 +1428,7 @@
      {
        "fieldKey": "11010_WCS_STB",
        "dbNumber": 50,
        "offset": 409,
        "offset": 369,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1437,7 +1437,7 @@
      {
        "fieldKey": "11010_WCS_ACK",
        "dbNumber": 50,
        "offset": 410,
        "offset": 370,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1446,7 +1446,7 @@
      {
        "fieldKey": "11010_PLC_STB",
        "dbNumber": 50,
        "offset": 411,
        "offset": 371,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1455,7 +1455,7 @@
      {
        "fieldKey": "11010_PLC_ACK",
        "dbNumber": 50,
        "offset": 412,
        "offset": 372,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1464,7 +1464,7 @@
      {
        "fieldKey": "11010_PLC_REQ",
        "dbNumber": 50,
        "offset": 413,
        "offset": 373,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1473,7 +1473,7 @@
      {
        "fieldKey": "11010_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 414,
        "offset": 374,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1482,7 +1482,7 @@
      {
        "fieldKey": "11010_WCS_Special",
        "dbNumber": 50,
        "offset": 415,
        "offset": 375,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1491,7 +1491,7 @@
      {
        "fieldKey": "11010_Equ_Auto",
        "dbNumber": 50,
        "offset": 416,
        "offset": 376,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1500,7 +1500,7 @@
      {
        "fieldKey": "11010_Last_pallet",
        "dbNumber": 50,
        "offset": 417,
        "offset": 377,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1509,16 +1509,16 @@
      {
        "fieldKey": "11010_Barcode",
        "dbNumber": 50,
        "offset": 418,
        "offset": 378,
        "bit": 1,
        "dataType": 3,
        "length": 22,
        "dataType": 0,
        "length": 20,
        "direction": 2
      },
      {
        "fieldKey": "11020_TaskNo",
        "dbNumber": 50,
        "offset": 800,
        "offset": 760,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1527,7 +1527,7 @@
      {
        "fieldKey": "11020_Source",
        "dbNumber": 50,
        "offset": 802,
        "offset": 762,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1536,7 +1536,7 @@
      {
        "fieldKey": "11020_Target",
        "dbNumber": 50,
        "offset": 804,
        "offset": 764,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1545,7 +1545,7 @@
      {
        "fieldKey": "11020_BoxType",
        "dbNumber": 50,
        "offset": 806,
        "offset": 766,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1554,7 +1554,7 @@
      {
        "fieldKey": "11020_CV_State",
        "dbNumber": 50,
        "offset": 807,
        "offset": 767,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1563,7 +1563,7 @@
      {
        "fieldKey": "11020_CV_ERRCode",
        "dbNumber": 50,
        "offset": 808,
        "offset": 768,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1572,7 +1572,7 @@
      {
        "fieldKey": "11020_WCS_STB",
        "dbNumber": 50,
        "offset": 809,
        "offset": 769,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1581,7 +1581,7 @@
      {
        "fieldKey": "11020_WCS_ACK",
        "dbNumber": 50,
        "offset": 810,
        "offset": 770,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1590,7 +1590,7 @@
      {
        "fieldKey": "11020_PLC_STB",
        "dbNumber": 50,
        "offset": 811,
        "offset": 771,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1599,7 +1599,7 @@
      {
        "fieldKey": "11020_PLC_ACK",
        "dbNumber": 50,
        "offset": 812,
        "offset": 772,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1608,7 +1608,7 @@
      {
        "fieldKey": "11020_PLC_REQ",
        "dbNumber": 50,
        "offset": 813,
        "offset": 773,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1617,7 +1617,7 @@
      {
        "fieldKey": "11020_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 814,
        "offset": 774,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1626,7 +1626,7 @@
      {
        "fieldKey": "11020_WCS_Special",
        "dbNumber": 50,
        "offset": 815,
        "offset": 775,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1635,7 +1635,7 @@
      {
        "fieldKey": "11020_Equ_Auto",
        "dbNumber": 50,
        "offset": 816,
        "offset": 776,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1644,7 +1644,7 @@
      {
        "fieldKey": "11020_Last_pallet",
        "dbNumber": 50,
        "offset": 817,
        "offset": 777,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1653,16 +1653,16 @@
      {
        "fieldKey": "11020_Barcode",
        "dbNumber": 50,
        "offset": 818,
        "offset": 778,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 22,
        "direction": 2
      },
      {
        "fieldKey": "11026_TaskNo",
        "dbNumber": 50,
        "offset": 1040,
        "offset": 1000,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1671,7 +1671,7 @@
      {
        "fieldKey": "11026_Source",
        "dbNumber": 50,
        "offset": 1042,
        "offset": 1002,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1680,7 +1680,7 @@
      {
        "fieldKey": "11026_Target",
        "dbNumber": 50,
        "offset": 1044,
        "offset": 1004,
        "bit": 1,
        "dataType": 1,
        "length": 1,
@@ -1689,7 +1689,7 @@
      {
        "fieldKey": "11026_BoxType",
        "dbNumber": 50,
        "offset": 1046,
        "offset": 1006,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1698,7 +1698,7 @@
      {
        "fieldKey": "11026_CV_State",
        "dbNumber": 50,
        "offset": 1047,
        "offset": 1007,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1707,7 +1707,7 @@
      {
        "fieldKey": "11026_CV_ERRCode",
        "dbNumber": 50,
        "offset": 1048,
        "offset": 1008,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1716,7 +1716,7 @@
      {
        "fieldKey": "11026_WCS_STB",
        "dbNumber": 50,
        "offset": 1049,
        "offset": 1009,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1725,7 +1725,7 @@
      {
        "fieldKey": "11026_WCS_ACK",
        "dbNumber": 50,
        "offset": 1050,
        "offset": 1010,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1734,7 +1734,7 @@
      {
        "fieldKey": "11026_PLC_STB",
        "dbNumber": 50,
        "offset": 1051,
        "offset": 1011,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1743,7 +1743,7 @@
      {
        "fieldKey": "11026_PLC_ACK",
        "dbNumber": 50,
        "offset": 1052,
        "offset": 1012,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1752,7 +1752,7 @@
      {
        "fieldKey": "11026_PLC_REQ",
        "dbNumber": 50,
        "offset": 1053,
        "offset": 1013,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1761,7 +1761,7 @@
      {
        "fieldKey": "11026_WCS_ERRCode",
        "dbNumber": 50,
        "offset": 1054,
        "offset": 1014,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1770,7 +1770,7 @@
      {
        "fieldKey": "11026_WCS_Special",
        "dbNumber": 50,
        "offset": 1055,
        "offset": 1015,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1779,7 +1779,7 @@
      {
        "fieldKey": "11026_Equ_Auto",
        "dbNumber": 50,
        "offset": 1056,
        "offset": 1016,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1788,7 +1788,7 @@
      {
        "fieldKey": "11026_Last_pallet",
        "dbNumber": 50,
        "offset": 1057,
        "offset": 1017,
        "bit": 1,
        "dataType": 0,
        "length": 1,
@@ -1797,9 +1797,9 @@
      {
        "fieldKey": "11026_Barcode",
        "dbNumber": 50,
        "offset": 1058,
        "offset": 1018,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 22,
        "direction": 2
      }
@@ -1950,7 +1950,7 @@
        "dbNumber": 50,
        "offset": 18,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 20,
        "direction": 2
      },
@@ -2094,7 +2094,7 @@
        "dbNumber": 50,
        "offset": 338,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 20,
        "direction": 2
      },
@@ -2238,7 +2238,7 @@
        "dbNumber": 50,
        "offset": 618,
        "bit": 1,
        "dataType": 3,
        "dataType": 0,
        "length": 22,
        "direction": 2
      }