heshaofeng
9 天以前 b6a40a2d8fdcffb3accfc7e424c0726a87a59ddf
提交
已添加2个文件
已修改10个文件
1077 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/inbound/inboundOrder.js 847 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/BaseServices/ServiceBase.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/Helper/UtilConvert.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Stock/StockDetailDtO.cs 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockDetailByMaterielService.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoDetailService.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockViewService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/inboundOrder.js
@@ -1,23 +1,24 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
import http from '@/api/http.js'
import { h,createVNode, render,reactive,ref  } from 'vue';
import { ElDialog , ElForm, ElFormItem, ElInput, ElButton, ElMessage ,ElSelect ,ElOption } from 'element-plus'; // å¼•å…¥ElMessage,解决提示无反应
import { h, createVNode, render, reactive, ref } from 'vue';
import { ElDialog, ElForm, ElFormItem, ElInput, ElButton, ElMessage, ElSelect, ElOption } from 'element-plus'; // å¼•å…¥ElMessage,解决提示无反应
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [
       {
  components: {
    //查询界面扩展组件
    gridHeader: '',
    gridBody: '',
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
  buttons: {
    view: [
      {
        name: '组盘',
        type: 'primary',
        value: '组盘',
@@ -39,417 +40,443 @@
          }
          const targetRow = selectedRows[0];
          this.$emit('openPalletDialog', targetRow.inboundOrderNo);
        }
      },
      {
                name: '撤销组盘',
                type: 'primary',
                value: '撤销组盘',
                onClick: function () {
                    console.log('撤销组盘按钮被点击');
                    const mountNode = document.createElement('div');
                    document.body.appendChild(mountNode);
        name: '撤销组盘',
        type: 'primary',
        value: '撤销组盘',
        onClick: function () {
          console.log('撤销组盘按钮被点击');
          const mountNode = document.createElement('div');
          document.body.appendChild(mountNode);
                    // å“åº”式表单数据:托盘号(必填)
                    const formData = reactive({
                        palletCode: '' // æ‰˜ç›˜å·è¾“入框
                    });
          // å“åº”式表单数据:托盘号(必填)
          const formData = reactive({
            palletCode: '' // æ‰˜ç›˜å·è¾“入框
          });
                    // æäº¤è¡¨å•的统一逻辑
                    const submitForm = async () => {
                        const formRef = vnode.component.refs.cancelPalletForm;
                        try {
                            // æ‰§è¡Œè¡¨å•校验(托盘号必填)
                            await formRef.validate();
                        } catch (err) {
                            ElMessage.warning('请输入有效的托盘号');
                            return;
                        }
          // æäº¤è¡¨å•的统一逻辑
          const submitForm = async () => {
            const formRef = vnode.component.refs.cancelPalletForm;
            try {
              // æ‰§è¡Œè¡¨å•校验(托盘号必填)
              await formRef.validate();
            } catch (err) {
              ElMessage.warning('请输入有效的托盘号');
              return;
            }
                        // å‘起撤销组盘请求
                        try {
                            //console.log('发起撤销组盘请求,托盘号:', formData.palletCode.trim());
                            const response = await http.post('/api/InboundOrder/UndoPalletGroup?palletCode='+formData.palletCode.trim());
            // å‘起撤销组盘请求
            try {
              //console.log('发起撤销组盘请求,托盘号:', formData.palletCode.trim());
              const response = await http.post('/api/InboundOrder/UndoPalletGroup?palletCode=' + formData.palletCode.trim());
                            const { status, message, data } = response;
                            if (status) {
                                ElMessage.success(`撤销组盘成功,托盘号:${formData.palletCode.trim()}`);
                                this.refresh(); // æˆåŠŸåŽåˆ·æ–°åˆ—è¡¨
                                // å…³é—­å¯¹è¯æ¡†
                                render(null, mountNode);
                                document.body.removeChild(mountNode);
                            } else {
                                console.log('撤销组盘失败,后端提示:', message);
                                ElMessage.error(message || data?.message || '撤销组盘失败');
                                selectPalletCodeInput(); // é€‰ä¸­è¾“入框方便重新输入
                            }
                        } catch (error) {
                            console.error('撤销组盘请求异常:', error);
                            ElMessage.error('网络异常或接口错误,请稍后重试');
                            selectPalletCodeInput();
                        }
                    };
                    // é€‰ä¸­è¾“入框文本(方便重新输入)
                    const selectPalletCodeInput = () => {
                        setTimeout(() => {
                            const inputRef = vnode.component.refs.palletCodeInput;
                            if (inputRef) {
                                const targetInput = inputRef.$el?.querySelector('input') || inputRef;
                                targetInput?.focus();
                                targetInput?.select();
                            }
                        }, 100);
                    };
              const { status, message, data } = response;
              if (status) {
                ElMessage.success(`撤销组盘成功,托盘号:${formData.palletCode.trim()}`);
                this.refresh(); // æˆåŠŸåŽåˆ·æ–°åˆ—è¡¨
                // å…³é—­å¯¹è¯æ¡†
                render(null, mountNode);
                document.body.removeChild(mountNode);
              } else {
                console.log('撤销组盘失败,后端提示:', message);
                ElMessage.error(message || data?.message || '撤销组盘失败');
                selectPalletCodeInput(); // é€‰ä¸­è¾“入框方便重新输入
              }
            } catch (error) {
              console.error('撤销组盘请求异常:', error);
              ElMessage.error('网络异常或接口错误,请稍后重试');
              selectPalletCodeInput();
            }
          };
                    // åˆ›å»ºå¯¹è¯æ¡†VNode
                    const vnode = createVNode(ElDialog, {
                        title: '撤销组盘',
                        width: '400px',
                        modelValue: true,
                        appendToBody: true,
                        onOpened: () => {
                            // å¯¹è¯æ¡†æ‰“开后自动聚焦输入框
                            setTimeout(() => {
                                const inputRef = vnode.component.refs.palletCodeInput;
                                inputRef?.focus();
                            }, 100);
                        },
                        'onUpdate:modelValue': (isVisible) => {
                            if (!isVisible) {
                                render(null, mountNode);
                                document.body.removeChild(mountNode);
                            }
                        }
                    }, {
                        default: () => h(ElForm, {
                            model: formData,
                            rules: {
                                palletCode: [
                                    { required: true, message: '请输入托盘号', trigger: ['blur', 'enter'] },
                                    { min: 1, max: 50, message: '托盘号长度不能超过50个字符', trigger: ['blur', 'input'] }
                                ]
                            },
                            ref: 'cancelPalletForm'
                        }, [
                            // æ‰˜ç›˜å·è¾“入项
                            h(ElFormItem, { label: '托盘号', prop: 'palletCode', required: true }, [
                                h(ElInput, {
                                    type: 'text',
                                    modelValue: formData.palletCode,
                                    'onUpdate:modelValue': (val) => {
                                        formData.palletCode = val;
                                    },
                                    ref: 'palletCodeInput',
                                    placeholder: '扫码输入或手动输入托盘号',
                                    maxLength: 50,
                                    // ç›‘听回车事件(扫码枪默认会发送回车)
                                    onKeydown: (e) => {
                                        if (e.key === 'Enter') {
                                            e.preventDefault();
                                            submitForm();
                                        }
                                    }
                                })
                            ]),
                            // åº•部按钮区
                            h('div', { style: { textAlign: 'right', marginTop: '16px' } }, [
                                h(ElButton, {
                                    type: 'text',
                                    onClick: () => {
                                        render(null, mountNode);
                                        document.body.removeChild(mountNode);
                                        ElMessage.info('取消撤销组盘');
                                    }
                                }, '取消'),
                                h(ElButton, {
                                    type: 'primary',
                                    onClick: submitForm.bind(this) // ç»‘定this上下文
                                }, '确认撤销')
                            ])
                        ])
                    });
          // é€‰ä¸­è¾“入框文本(方便重新输入)
          const selectPalletCodeInput = () => {
            setTimeout(() => {
              const inputRef = vnode.component.refs.palletCodeInput;
              if (inputRef) {
                const targetInput = inputRef.$el?.querySelector('input') || inputRef;
                targetInput?.focus();
                targetInput?.select();
              }
            }, 100);
          };
                    vnode.appContext = this.$.appContext;
                    render(vnode, mountNode);
                }
          // åˆ›å»ºå¯¹è¯æ¡†VNode
          const vnode = createVNode(ElDialog, {
            title: '撤销组盘',
            width: '400px',
            modelValue: true,
            appendToBody: true,
            onOpened: () => {
              // å¯¹è¯æ¡†æ‰“开后自动聚焦输入框
              setTimeout(() => {
                const inputRef = vnode.component.refs.palletCodeInput;
                inputRef?.focus();
              }, 100);
            },
      {
  name: '分批入库',
  type: 'primary',
  value: '分批入库',
  onClick: async function () {
    console.log('分批入库按钮被点击,开始校验');
    const selectedRows = this.$refs.table.getSelected();
    // æ ¡éªŒ1:是否选中行(至少选择一条)
    if (selectedRows.length === 0) {
      console.log('校验不通过:未选中任何单据');
      ElMessage.warning('请选择至少一条单据');
      return;
    }
    // æ”¶é›†æ‰€æœ‰é€‰ä¸­å•据的编号(过滤无单据号的异常行)
    const inboundOrderNos = selectedRows
      .filter(row => row.inboundOrderNo)
      .map(row => row.inboundOrderNo);
    // æ ¡éªŒ2:是否有有效单据号
    if (inboundOrderNos.length === 0) {
      console.log('校验不通过:选中单据无有效编号');
      ElMessage.warning('选中的单据中无有效编号,请重新选择');
      return;
    }
    try {
      console.log('发起分批入库请求,参数:', { inboundOrderNos});
      const response = await http.post('/api/InboundOrder/BatchOrderFeedbackToMes', {
        orderNos: inboundOrderNos,
        inout:1
      });
      const { status, message, data } = response;
      if (status) {
        console.log('分批入库成功,后端返回:', data);
        ElMessage.success(`分批入库成功!共处理${inboundOrderNos.length}条单据`);
        this.refresh(); // å…¥åº“成功后刷新列表(复用原有逻辑)
      } else {
        console.log('分批入库失败,后端提示:', message);
        ElMessage.error(message || data?.message || '分批入库失败');
      }
    } catch (error) {
      console.error('分批入库请求异常:', error);
      ElMessage.error('网络异常或接口错误,请稍后重试');
    }
  }
},
     {
  name: '空托盘入库',
  type: 'primary',
  value: '空托盘入库',
  onClick: function () {
    const mountNode = document.createElement('div');
    document.body.appendChild(mountNode);
    // å“åº”式表单数据:料箱码(必填,扫码枪/手动输入)
    const formData = reactive({
      boxCode: '',
      warehouseCode:''
    });
    const warehouses = ref([]);
    const isLoadingWarehouses = ref(false);
    const getWarehouseList = async () => {
      isLoadingWarehouses.value = true;
      try {
        const { data, status } = await http.post('/api/LocationInfo/GetLocationTypes');
        if (status && Array.isArray(data)) {
          // æ ¼å¼åŒ–仓库选项:适配ElSelect的label-value格式
          warehouses.value = data.map(item => ({
            label: item.locationTypeDesc,
            value: item.locationType
          }));
        } else {
          ElMessage.error('获取区域列表失败');
          warehouses.value = [];
        }
      } catch (err) {
        ElMessage.error('区域数据请求异常,请稍后重试');
        warehouses.value = [];
      } finally {
        isLoadingWarehouses.value = false;
      }
    };
    // æäº¤è¡¨å•的统一逻辑(供回车触发和按钮点击共用)
    const submitForm = async () => {
      const formRef = vnode.component.refs.batchInForm;
      try {
        // æ‰§è¡Œè¡¨å•校验(料箱码必填)
        await formRef.validate();
      } catch (err) {
        ElMessage.warning('请输入有效的料箱码');
        return;
      }
      http.post('/api/InboundOrder/EmptyMaterielGroup', {
        palletCode: formData.boxCode.trim(),
        warehouseCode:formData.warehouseCode
      }).then(({ data, status, message }) => {
        if (status) {
          ElMessage.success(`入库成功,料箱码:${formData.boxCode.trim()}`);
          this.refresh();
          formData.boxCode = '';
          setTimeout(() => {
            const inputRef = vnode.component.refs.boxCodeInput;
            inputRef?.focus();
          }, 100);
        } else {
          ElMessage.error(message || data?.message || '入库失败');
          selectBoxCodeInput();
        }
      }).catch(() => {
        ElMessage.error('请求失败,请稍后重试');
        selectBoxCodeInput();
      });
    };
    const selectBoxCodeInput = () => {
      setTimeout(() => {
        const inputRef = vnode.component.refs.boxCodeInput;
        if (inputRef) {
          const targetInput = inputRef.$el?.querySelector('input') || inputRef;
          targetInput?.focus();
          targetInput?.select();
        }
  }, 100);
}
    const vnode = createVNode(ElDialog, {
      title: '空托盘入库',
      width: '400px',
      modelValue: true,
      appendToBody: true,
      onOpened: async () => {
        await getWarehouseList();
        const inputRef = vnode.component.refs.boxCodeInput;
        inputRef?.focus();
      },
      'onUpdate:modelValue': (isVisible) => {
        if (!isVisible) {
          render(null, mountNode);
          document.body.removeChild(mountNode);
        }
      }
    }, {
      default: () => h(ElForm, {
        model: formData,
        rules: {
          boxCode: [
            { required: true, message: '请输入料箱码', trigger: ['blur', 'enter'] }
          ],
          warehouseCode:[
            { required: true, message: '请选择区域', trigger: ['change', 'blur'] }
          ]
        },
        ref: 'batchInForm'
      }, [
        //仓库数据
        h(ElFormItem, { label: '区域', prop: 'warehouseCode', required: true }, [
          h(ElSelect, {
            modelValue: formData.warehouseCode,
            'onUpdate:modelValue': (val) => {
              formData.warehouseCode = val;
            },
            placeholder: '请选择入库区域',
            filterable: true, // æ”¯æŒæœç´¢ä»“库
            loading: isLoadingWarehouses.value, // åŠ è½½çŠ¶æ€
            style: { width: '100%' }
          }, [
            // æ¸²æŸ“仓库下拉选项
            warehouses.value.map(item => h(ElOption, {
              label: item.label,
              value: item.value
            }))
          ])
        ]),
        // æ–™ç®±ç è¾“入项(支持聚焦、回车提交)
        h(ElFormItem, { label: '料箱码', prop: 'boxCode', required: true }, [
          h(ElInput, {
            type: 'text',
            modelValue: formData.boxCode,
            'onUpdate:modelValue': (val) => {
              formData.boxCode = val;
            },
            ref: 'boxCodeInput',
            placeholder: '扫码输入或手动输入料箱码',
            // ç›‘听回车事件(扫码枪默认会发送回车)
            onKeydown: (e) => {
              if (e.key === 'Enter') {
                e.preventDefault();
                submitForm();
            'onUpdate:modelValue': (isVisible) => {
              if (!isVisible) {
                render(null, mountNode);
                document.body.removeChild(mountNode);
              }
            }
          })
        ]),
        // åº•部按钮区
        h('div', { style: { textAlign: 'right', marginTop: '16px' } }, [
          h(ElButton, {
            type: 'text',
            onClick: () => {
              render(null, mountNode);
              document.body.removeChild(mountNode);
              ElMessage.info('取消入库任务');
          }, {
            default: () => h(ElForm, {
              model: formData,
              rules: {
                palletCode: [
                  { required: true, message: '请输入托盘号', trigger: ['blur', 'enter'] },
                  { min: 1, max: 50, message: '托盘号长度不能超过50个字符', trigger: ['blur', 'input'] }
                ]
              },
              ref: 'cancelPalletForm'
            }, [
              // æ‰˜ç›˜å·è¾“入项
              h(ElFormItem, { label: '托盘号', prop: 'palletCode', required: true }, [
                h(ElInput, {
                  type: 'text',
                  modelValue: formData.palletCode,
                  'onUpdate:modelValue': (val) => {
                    formData.palletCode = val;
                  },
                  ref: 'palletCodeInput',
                  placeholder: '扫码输入或手动输入托盘号',
                  maxLength: 50,
                  // ç›‘听回车事件(扫码枪默认会发送回车)
                  onKeydown: (e) => {
                    if (e.key === 'Enter') {
                      e.preventDefault();
                      submitForm();
                    }
                  }
                })
              ]),
              // åº•部按钮区
              h('div', { style: { textAlign: 'right', marginTop: '16px' } }, [
                h(ElButton, {
                  type: 'text',
                  onClick: () => {
                    render(null, mountNode);
                    document.body.removeChild(mountNode);
                    ElMessage.info('取消撤销组盘');
                  }
                }, '取消'),
                h(ElButton, {
                  type: 'primary',
                  onClick: submitForm.bind(this) // ç»‘定this上下文
                }, '确认撤销')
              ])
            ])
          });
          vnode.appContext = this.$.appContext;
          render(vnode, mountNode);
        }
      },
      {
        name: '分批入库',
        type: 'primary',
        value: '分批入库',
        onClick: async function () {
          console.log('分批入库按钮被点击,开始校验');
          const selectedRows = this.$refs.table.getSelected();
          // æ ¡éªŒ1:是否选中行(至少选择一条)
          if (selectedRows.length === 0) {
            console.log('校验不通过:未选中任何单据');
            ElMessage.warning('请选择至少一条单据');
            return;
          }
          // æ”¶é›†æ‰€æœ‰é€‰ä¸­å•据的编号(过滤无单据号的异常行)
          const inboundOrderNos = selectedRows
            .filter(row => row.inboundOrderNo)
            .map(row => row.inboundOrderNo);
          // æ ¡éªŒ2:是否有有效单据号
          if (inboundOrderNos.length === 0) {
            console.log('校验不通过:选中单据无有效编号');
            ElMessage.warning('选中的单据中无有效编号,请重新选择');
            return;
          }
          try {
            console.log('发起分批入库请求,参数:', { inboundOrderNos });
            const response = await http.post('/api/InboundOrder/BatchOrderFeedbackToMes', {
              orderNos: inboundOrderNos,
              inout: 1
            });
            const { status, message, data } = response;
            if (status) {
              console.log('分批入库成功,后端返回:', data);
              ElMessage.success(`分批入库成功!共处理${inboundOrderNos.length}条单据`);
              this.refresh(); // å…¥åº“成功后刷新列表(复用原有逻辑)
            } else {
              console.log('分批入库失败,后端提示:', message);
              ElMessage.error(message || data?.message || '分批入库失败');
            }
          }, '取消'),
          h(ElButton, {
            type: 'primary',
            onClick: submitForm
          }, '确定')
        ])
      ])
    });
          } catch (error) {
            console.error('分批入库请求异常:', error);
            ElMessage.error('网络异常或接口错误,请稍后重试');
          }
        }
      },
      {
        name: '空托盘入库',
        type: 'primary',
        value: '空托盘入库',
    vnode.appContext = this.$.appContext;
    render(vnode, mountNode);
  }
}
    ], box: [], detail: [] },
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
      // this.searchFormFields.orderType=[0]
         let wheres = [{
            'name': 'orderType',
            'value': '0',
            'displayType': 'text'}];
        onClick: function () {
          const mountNode = document.createElement('div');
          document.body.appendChild(mountNode);
          param.wheres.push(...wheres);
        return true;
      },
      searchAfter(result) {
        //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
        return true;
      },
      addBefore(formData) {
        //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
        return true;
      },
      updateBefore(formData) {
        //编辑保存前formData为对象,包括明细表、删除行的Id
        return true;
      },
      rowClick({ row, column, event }) {
        //查询界面点击行事件
        this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
      },
      modelOpenAfter(row) {
        //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
        //(1)判断是编辑还是新建操作: this.currentAction=='Add';
        //(2)给弹出框设置默认值
        //(3)this.editFormFields.字段='xxx';
        //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
        //看不懂就把输出看:console.log(this.editFormOptions)
          // å“åº”式表单数据:料箱码(必填,扫码枪/手动输入)
          const formData = reactive({
            boxCode: '',
            warehouseCode: ''
          });
          const warehouses = ref([]);
          const isLoadingWarehouses = ref(false);
          const getWarehouseList = async () => {
            isLoadingWarehouses.value = true;
            try {
              const { data, status } = await http.post('/api/LocationInfo/GetLocationTypes');
              if (status && Array.isArray(data)) {
                // æ ¼å¼åŒ–仓库选项:适配ElSelect的label-value格式
                warehouses.value = data.map(item => ({
                  label: item.locationTypeDesc,
                  value: item.locationType
                }));
              } else {
                ElMessage.error('获取区域列表失败');
                warehouses.value = [];
              }
            } catch (err) {
              ElMessage.error('区域数据请求异常,请稍后重试');
              warehouses.value = [];
            } finally {
              isLoadingWarehouses.value = false;
            }
          };
          // æäº¤è¡¨å•的统一逻辑(供回车触发和按钮点击共用)
          const submitForm = async () => {
            const formRef = vnode.component.refs.batchInForm;
            try {
              // æ‰§è¡Œè¡¨å•校验(料箱码必填)
              await formRef.validate();
            } catch (err) {
              ElMessage.warning('请输入有效的料箱码');
              return;
            }
            http.post('/api/InboundOrder/EmptyMaterielGroup', {
              palletCode: formData.boxCode.trim(),
              warehouseCode: formData.warehouseCode
            }).then(({ data, status, message }) => {
              if (status) {
                ElMessage.success(`入库成功,料箱码:${formData.boxCode.trim()}`);
                this.refresh();
                formData.boxCode = '';
                setTimeout(() => {
                  const inputRef = vnode.component.refs.boxCodeInput;
                  inputRef?.focus();
                }, 100);
              } else {
                ElMessage.error(message || data?.message || '入库失败');
                selectBoxCodeInput();
              }
            }).catch(() => {
              ElMessage.error('请求失败,请稍后重试');
              selectBoxCodeInput();
            });
          };
          const selectBoxCodeInput = () => {
            setTimeout(() => {
              const inputRef = vnode.component.refs.boxCodeInput;
              if (inputRef) {
                const targetInput = inputRef.$el?.querySelector('input') || inputRef;
                targetInput?.focus();
                targetInput?.select();
              }
            }, 100);
          }
          const vnode = createVNode(ElDialog, {
            title: '空托盘入库',
            width: '400px',
            modelValue: true,
            appendToBody: true,
            onOpened: async () => {
              await getWarehouseList();
              const inputRef = vnode.component.refs.boxCodeInput;
              inputRef?.focus();
            },
            'onUpdate:modelValue': (isVisible) => {
              if (!isVisible) {
                render(null, mountNode);
                document.body.removeChild(mountNode);
              }
            }
          }, {
            default: () => h(ElForm, {
              model: formData,
              rules: {
                boxCode: [
                  { required: true, message: '请输入料箱码', trigger: ['blur', 'enter'] }
                ],
                warehouseCode: [
                  { required: true, message: '请选择区域', trigger: ['change', 'blur'] }
                ]
              },
              ref: 'batchInForm'
            }, [
              //仓库数据
              h(ElFormItem, { label: '区域', prop: 'warehouseCode', required: true }, [
                h(ElSelect, {
                  modelValue: formData.warehouseCode,
                  'onUpdate:modelValue': (val) => {
                    formData.warehouseCode = val;
                  },
                  placeholder: '请选择入库区域',
                  filterable: true, // æ”¯æŒæœç´¢ä»“库
                  loading: isLoadingWarehouses.value, // åŠ è½½çŠ¶æ€
                  style: { width: '100%' }
                }, [
                  // æ¸²æŸ“仓库下拉选项
                  warehouses.value.map(item => h(ElOption, {
                    label: item.label,
                    value: item.value
                  }))
                ])
              ]),
              // æ–™ç®±ç è¾“入项(支持聚焦、回车提交)
              h(ElFormItem, { label: '料箱码', prop: 'boxCode', required: true }, [
                h(ElInput, {
                  type: 'text',
                  modelValue: formData.boxCode,
                  'onUpdate:modelValue': (val) => {
                    formData.boxCode = val;
                  },
                  ref: 'boxCodeInput',
                  placeholder: '扫码输入或手动输入料箱码',
                  // ç›‘听回车事件(扫码枪默认会发送回车)
                  onKeydown: (e) => {
                    if (e.key === 'Enter') {
                      e.preventDefault();
                      submitForm();
                    }
                  }
                })
              ]),
              // åº•部按钮区
              h('div', { style: { textAlign: 'right', marginTop: '16px' } }, [
                h(ElButton, {
                  type: 'text',
                  onClick: () => {
                    render(null, mountNode);
                    document.body.removeChild(mountNode);
                    ElMessage.info('取消入库任务');
                  }
                }, '取消'),
                h(ElButton, {
                  type: 'primary',
                  onClick: submitForm
                }, '确定')
              ])
            ])
          });
          vnode.appContext = this.$.appContext;
          render(vnode, mountNode);
        }
      }
    ], box: [], detail: []
  },
  methods: {
    //下面这些方法可以保留也可以删除
    onInit() {
      this.columns.forEach(column => {
      if (column.field === 'orderStatistics') {
        column.formatter = (row) => {
          // æ ¡éªŒdetails是否存在且有数据
          if (row.details && row.details.length > 0) {
            //按materielCode分组统计orderQuantity总和
            const materielSumMap = row.details.reduce((acc, item) => {
              const materielCode = item.materielCode || '未知物料';
              const quantity = Number(item.orderQuantity) || 0;
              acc[materielCode] = (acc[materielCode] || 0) + quantity;
              return acc;
            }, {});
            //每个物料项生成独立div,跨行显示
            const displayItems = Object.entries(materielSumMap).map(([code, total]) => {
              return `<div style="line-height: 1.5; white-space: normal;">${code}:${total}个</div>`;
            });
            const displayContent = displayItems.join('');
            return `<div style="color: #F56C6C; white-space: normal; word-break: break-all;">${displayContent}</div>`;
          } else {
            return '<span style="color: #F56C6C">无入库明细</span>';
          }
        };
      }
    });
    },
    onInited() {
      //框架初始化配置后
      //如果要配置明细表,在此方法操作
      //this.detailOptions.columns.forEach(column=>{ });
    },
    searchBefore(param) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      // this.searchFormFields.orderType=[0]
      let wheres = [{
        'name': 'orderType',
        'value': '0',
        'displayType': 'text'
      }];
      param.wheres.push(...wheres);
      return true;
    },
    searchAfter(result) {
      //查询后,result返回的查询数据,可以在显示到表格前处理表格的值
      return true;
    },
    addBefore(formData) {
      //新建保存前formData为对象,包括明细表,可以给给表单设置值,自己输出看formData的值
      return true;
    },
    updateBefore(formData) {
      //编辑保存前formData为对象,包括明细表、删除行的Id
      return true;
    },
    rowClick({ row, column, event }) {
      //查询界面点击行事件
      this.$refs.table.$refs.table.toggleRowSelection(row); //单击行时选中当前行;
    },
    modelOpenAfter(row) {
      //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
      //(1)判断是编辑还是新建操作: this.currentAction=='Add';
      //(2)给弹出框设置默认值
      //(3)this.editFormFields.字段='xxx';
      //如果需要给下拉框设置默认值,请遍历this.editFormOptions找到字段配置对应data属性的key值
      //看不懂就把输出看:console.log(this.editFormOptions)
    }
  };
  export default extension;
  }
};
export default extension;
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js
@@ -383,17 +383,6 @@
    searchBefore(param) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      let wheres = [{
        'name': 'orderType',
        'value': '0',
        'displayType': 'text'
      }];
      param.wheres.push(...wheres);
      return true;
      return true;
    },
    searchAfter(result) {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue
@@ -79,7 +79,7 @@
    const searchFormFields = ref({
      inboundOrderNo: "",
      upperOrderNo: "",
      orderType: "0",
      orderType: "",
      orderStatus: "",
      createType: "",
      creater: "",
@@ -169,6 +169,13 @@
        bind: { key: "inboundState", data: [] },
      },
      {
        field: "orderStatistics",
        title: "单据物料统计",
        type: "string",
        width: 230,
        align: "left",
      },
      {
        field: "createType",
        title: "创建方式",
        type: "string",
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/BaseServices/ServiceBase.cs
@@ -223,7 +223,7 @@
                        if (string.IsNullOrEmpty(where))
                            where += $"{searchParametersList[i].Name} >= '{searchParametersList[i].Value}'";
                        else
                            where += $" and {searchParametersList[i].Name} {searchParametersList[i].DisplayType.GetLinqCondition()} '{searchParametersList[i].Value}'";
                            where += $" and {searchParametersList[i].Name} >= '{searchParametersList[i].Value}'";
                    }
                    else if (searchParametersList[i].DisplayType.GetLinqCondition() == LinqExpressionType.LessThanOrEqual)
                    {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/Helper/UtilConvert.cs
@@ -958,10 +958,10 @@
                case HtmlElementType.Contains:
                    linqExpression = LinqExpressionType.In;
                    break;
                case HtmlElementType.ThanOrEqual:
                case HtmlElementType.thanorequal:
                    linqExpression = LinqExpressionType.ThanOrEqual;
                    break;
                case HtmlElementType.LessOrequal:
                case HtmlElementType.lessorequal:
                    linqExpression = LinqExpressionType.LessThanOrEqual;
                    break;
                case HtmlElementType.GT:
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Stock/StockDetailDtO.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEA_DTO.Stock
{
    public class StockDetailDtO
    {
        public int Id { get; set; }
        public int StockId { get; set; }
        public string MaterielCode { get; set; }
        public string MaterielName { get; set; }
        public string OrderNo { get; set; }
        public string BatchNo { get; set; }
        public string ProductionDate { get; set; }
        public string EffectiveDate { get; set; }
        public string SerialNumber { get; set; }
        public decimal StockQuantity { get; set; }
        public decimal OutboundQuantity { get; set; }
        public int Status { get; set; }
        public string Unit { get; set; }
        public string InboundOrderRowNo { get; set; }
        public string? SupplyCode { get; set; }
        public string FactoryArea { get; set; }
        public string? WarehouseCode { get; set; }
        public string? Barcode { get; set; }
        public string? BusinessType { get; set; }
        public decimal BarcodeQty { get; set; }
        public string BarcodeUnit { get; set; }
        public DateTime? ValidDate { get; set; }
        public string Remark { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs
@@ -689,5 +689,41 @@
            return WebResponseContent.Instance.OK("托盘撤销成功");
        }
        public override PageGridData<Dt_InboundOrder> GetPageData(PageDataOptions options)
        {
            string wheres = ValidatePageOptions(options);
            //获取排序字段
            Dictionary<string, SqlSugar.OrderByType> orderbyDic = GetPageDataSort(options, TProperties);
            List<OrderByModel> orderByModels = new List<OrderByModel>();
            foreach (var item in orderbyDic)
            {
                OrderByModel orderByModel = new()
                {
                    FieldName = item.Key,
                    OrderByType = item.Value
                };
                orderByModels.Add(orderByModel);
            }
            int totalCount = 0;
            List<SearchParameters> searchParametersList = new List<SearchParameters>();
            if (!string.IsNullOrEmpty(options.Wheres))
            {
                try
                {
                    searchParametersList = options.Wheres.DeserializeObject<List<SearchParameters>>();
                    options.Filter = searchParametersList;
                }
                catch { }
            }
            var data = BaseDal.Db.Queryable<Dt_InboundOrder>()
                .WhereIF(!wheres.IsNullOrEmpty(), wheres)
                .OrderBy(orderByModels).Includes(x=>x.Details)
                .ToPageList(options.Page, options.Rows, ref totalCount);
            return new PageGridData<Dt_InboundOrder>(totalCount, data);
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
@@ -36,6 +37,7 @@
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Check;
using WIDESEA_Model.Models.Outbound;
namespace WIDESEA_OutboundService
{
@@ -66,6 +68,7 @@
        private readonly IReCheckOrderService _reCheckOrderService;
        private readonly ITask_HtyService _task_HtyService;
        private readonly ILogger<OutboundPickingService> _logger;
        private readonly IRepository<Dt_InterfaceLog> _interfaceLog;
        private Dictionary<string, string> stations = new Dictionary<string, string>
        {
@@ -84,7 +87,7 @@
        public OutboundPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IReCheckOrderService reCheckOrderService, ITask_HtyService task_HtyService) : base(BaseDal)
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IReCheckOrderService reCheckOrderService, ITask_HtyService task_HtyService,IRepository<Dt_InterfaceLog> interfaceLog) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -106,6 +109,7 @@
            _warehouseAreaRepository = warehouseAreaRepository;
            _reCheckOrderService = reCheckOrderService;
            _task_HtyService = task_HtyService;
            _interfaceLog = interfaceLog;
        }
@@ -2851,10 +2855,11 @@
                var result1 = await _invokeMESService.FeedbackInbound(infeedmodel);
                if (result1 != null && result1.code == 200)
                {
                    var orderIds = inboundOrderDetails.Select(x => x.Id).Distinct().ToList();
                    _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                    .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                    _inboundOrderDetailService.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                    .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
                    .Where(it => orderIds.Contains(it.Id)).ExecuteCommand();
                }
                //出库回传MES
@@ -2873,7 +2878,7 @@
                    status = outboundOrder.OrderStatus,
                    details = new List<FeedbackOutboundDetailsModel>()
                };
                foreach (var detail in outboundOrder.Details)
                foreach (var detail in outboundOrderDetails)
                {
                    // èŽ·å–è¯¥æ˜Žç»†å¯¹åº”çš„æ¡ç ä¿¡æ¯ï¼ˆä»Žé”å®šè®°å½•ï¼‰
                    var detailLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
@@ -2903,18 +2908,37 @@
                          }).ToList();
                    outfeedmodel.details.AddRange(groupdata);
                }
                //存储回传参数,保证异常手动回传
                Dt_InterfaceLog interfaceLog = new Dt_InterfaceLog
                {
                    OrderNo = outboundOrder.UpperOrderNo,
                    DocumentNo = documentNo,
                    OrderType = "虚拟出入库",
                    Content = outfeedmodel.ToJson(),
                    ReturnToMESStatus = 0,
                    IsDeleted = false
                };
                _interfaceLog.AddData(interfaceLog);
                var result = await _invokeMESService.FeedbackOutbound(outfeedmodel);
                if (result != null && result.code == 200)
                {
                    var orderIds = outboundOrderDetails.Select(x => x.Id).Distinct().ToList();
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
                        .Where(x => x.OrderId == outboundOrder.Id)
                        .Where(x => orderIds.Contains(x.Id))
                        .ExecuteCommandAsync();
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
                        .Where(x => x.Id == outboundOrder.Id)
                        .ExecuteCommandAsync();
                    await _interfaceLog.Db.Updateable<Dt_InterfaceLog>()
                        .SetColumns(x => x.ReturnToMESStatus == 1)
                        .Where(x=>x.DocumentNo == documentNo)
                        .ExecuteCommandAsync();
                }
                return WebResponseContent.Instance.OK();
            }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockDetailByMaterielService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEA_StockService
{
    internal class StockDetailByMaterielService
    {
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoDetailService.cs
@@ -72,6 +72,42 @@
            return new PageGridData<StockInfoDetailWithPalletDto> { Rows = dtoList, Total = lists.Total, Summary = lists.Summary  };
        }
        public PageGridData<StockDetailDtO> GetPageDataByMateriel(PageDataOptions options)
        {
            PageGridData<Dt_StockInfoDetail> lists = base.GetPageData(options);
            List<StockDetailDtO> dtoList = lists.Rows.GroupBy(detail => new { detail.MaterielCode, detail.BatchNo, detail.SupplyCode }).Select(group =>
            {
                var firstItem = group.First();
                return new StockDetailDtO
                {
                    Id = firstItem.Id,
                    StockId = firstItem.StockId,
                    MaterielCode = group.Key.MaterielCode,
                    MaterielName = firstItem.MaterielName,
                    OrderNo = firstItem.OrderNo,
                    BatchNo = group.Key.BatchNo,
                    ProductionDate = firstItem.ProductionDate,
                    EffectiveDate = firstItem.EffectiveDate,
                    SerialNumber = firstItem.SerialNumber,
                    // æ ¸å¿ƒï¼šå¯¹å½“前分组的StockQuantity求和
                    StockQuantity = group.Sum(item => item.StockQuantity),
                    OutboundQuantity = firstItem.OutboundQuantity,
                    Status = firstItem.Status,
                    Unit = firstItem.Unit,
                    InboundOrderRowNo = firstItem.InboundOrderRowNo,
                    SupplyCode = group.Key.SupplyCode,
                    WarehouseCode = firstItem.WarehouseCode,
                    Barcode = firstItem.Barcode,
                    BusinessType = firstItem.BusinessType,
                    Remark = firstItem.Remark
                };
            }).ToList();
            return new PageGridData<StockDetailDtO> { Rows = dtoList, Total = lists.Total, Summary = lists.Summary };
        }
    }
 }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockViewService.cs
@@ -42,7 +42,7 @@
            //}
            int totalCount = 0;
            ISugarQueryable<Dt_StockInfo> sugarQueryable1 = _dbBase.Queryable<Dt_StockInfo>();
            ISugarQueryable<Dt_StockInfo> sugarQueryable1 = _dbBase.Queryable<Dt_StockInfo>().Includes(x=>x.Details);
            ISugarQueryable<Dt_LocationInfo> sugarQueryable = _dbBase.Queryable<Dt_LocationInfo>();
            ISugarQueryable<Dt_StockInfoDetail> sugarQueryable2 = _dbBase.Queryable<Dt_StockInfoDetail>();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs
@@ -1,10 +1,12 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core;
using WIDESEA_Core.BaseController;
using WIDESEA_DTO.Stock;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
using WIDESEA_StockService;
namespace WIDESEA_WMSServer.Controllers.Stock
{
@@ -28,5 +30,11 @@
            return Json(result);
        }
        [HttpPost, Route("GetPageDataByMateriel"),AllowAnonymous]
        public PageGridData<StockDetailDtO> GetPageDataByMateriel([FromBody] PageDataOptions options)
        {
            return Service.GetPageDataByMateriel(options);
        }
    }
}