heshaofeng
2026-02-06 cefe93f0197d675b19fe68d6758aabb010c3fbb0
添加计算库存总和功能
已添加1个文件
已修改6个文件
312 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/config/buttons.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/extend/CalculateStock.vue 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/stockInfoDetailByMaterielSum.js 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/outPicking.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IStockService/IStockDetailByMaterielService.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockDetailByMaterielService.cs 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockDetailByMaterielController.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/config/buttons.js
@@ -347,6 +347,14 @@
    type: 'warning',
    onClick: function () {
    }
},{
    name: "计算库存总和",
    icon: '',
    class: '',
    value: 'CalculateStock',
    type: 'warning',
    onClick: function () {
    }
}
]
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/extend/CalculateStock.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,250 @@
<template>
    <vol-box v-model="show" title="物料库存查询" :width="800" :height="600">
        <template #content>
            <el-form ref="form" :model="form" :rules="rules" label-width="90px">
                <!-- ä»“库号输入框 -->
                <el-form-item label="仓库号:" prop="warehouseCode">
                    <el-input
                        v-model="form.warehouseCode"
                        placeholder="请输入仓库号(如:5053)"
                        clearable
                        @input="handleWarehouseInput"
                        @keyup.enter="focusMaterielInput"
                        ref="warehouseCodeInput"
                        :disabled="loading"
                    />
                </el-form-item>
                <!-- ç‰©æ–™æ¡ç è¾“入框 -->
                <el-form-item label="物料条码:" prop="materielBarcode">
                    <el-input
                        v-model="form.materielBarcode"
                        placeholder="请扫描/输入物料条码(如:100401-01211)"
                        @keyup.enter="getStockTotal"
                        clearable
                        @paste="handlePaste"
                        @input="handleInput"
                        ref="materielCodeInput"
                        :disabled="loading"
                    />
                    <!-- åº“存查询加载状态 -->
                    <el-icon class="ml-2" v-if="loading"><loading /></el-icon>
                </el-form-item>
                <!-- åº“存总和显示区域 -->
                <el-form-item label="库存总和:" prop="totalStockQuantity">
                    <el-input
                        v-model="form.totalStockQuantity"
                        placeholder="请输入仓库号和物料条码查询"
                        disabled
                        prefix-icon="el-icon-s-data"
                    />
                    <!-- æ— æ•°æ®æç¤º -->
                    <span v-if="form.totalStockQuantity === 0 && !loading && form.materielBarcode && form.warehouseCode" class="text-gray-500 ml-2">
                        è¯¥ç‰©æ–™åœ¨å½“前仓库暂无库存
                    </span>
                </el-form-item>
            </el-form>
        </template>
        <template #footer>
            <div class="dialog-footer">
                <el-button @click="resetForm">重置</el-button>
                <el-button @click="show = false">关闭</el-button>
            </div>
        </template>
    </vol-box>
</template>
<script>
import VolBox from '@/components/basic/VolBox.vue'
import { Loading } from '@element-plus/icons-vue'
export default {
    components: { VolBox, Loading },
    props: {
        value: { type: Boolean, default: false }
    },
    data() {
        // ä»“库号验证规则
        const validateWarehouseCode = (rule, value, callback) => {
            if (!value) {
                return callback(new Error('请输入仓库号'));
            }
            callback();
        };
        // ç‰©æ–™æ¡ç éªŒè¯è§„则
        const validateMaterielBarcode = (rule, value, callback) => {
            if (!value) {
                return callback(new Error('请输入物料条码'));
            }
            callback();
        };
        return {
            show: false,
            loading: false, // åº“存查询加载状态
            form: {
                warehouseCode: '', // ä»“库号
                materielBarcode: '', // ç‰©æ–™æ¡ç 
                totalStockQuantity: 0 // åº“存总和
            },
            // è¡¨å•验证规则
            rules: {
                warehouseCode: [
                    { validator: validateWarehouseCode, trigger: ['blur', 'change'] }
                ],
                materielBarcode: [
                    { validator: validateMaterielBarcode, trigger: ['blur', 'change'] }
                ]
            }
        }
    },
    methods: {
        // æ‰“开弹窗初始化
        open() {
            this.show = true
            this.$nextTick(() => {
                this.focusWarehouseInput()
            })
        },
        async getStockTotal() {
            // è¡¨å•验证
            try {
                await this.$refs.form.validate();
            } catch (error) {
                if (!this.form.warehouseCode) {
                    this.focusWarehouseInput();
                } else {
                    this.focusAndSelectInput();
                }
                return;
            }
            this.loading = true;
            try {
                const res = await this.http.post('/api/StockDetailByMateriel/CalculateStock?warehouseCode=' + this.form.warehouseCode.trim() + '&materielCode=' + this.form.materielBarcode.trim());
                if (res.status && res.code === 0) {
                    this.form.totalStockQuantity = Number(res.data) || 0;
                    this.$message.success('库存查询成功');
                } else {
                    this.form.totalStockQuantity = 0;
                    this.$message.error(res.message || '库存查询失败:接口返回异常');
                }
            } catch (error) {
                this.form.totalStockQuantity = 0;
                const errorMsg = error.response
                    ? `接口错误:${error.response.status} - ${error.response.data?.message || '未知错误'}`
                    : `网络异常:${error.message}`;
                this.$message.error(`库存查询失败:${errorMsg}`);
            } finally {
                this.loading = false;
            }
        },
        // ä»“库号输入过滤
        handleWarehouseInput(value) {
            this.form.warehouseCode = value.replace(/[^a-zA-Z0-9]/g, '').toUpperCase();
            this.$nextTick(() => {
                this.$refs.form.validateField('warehouseCode');
            });
        },
        // ç‰©æ–™æ¡ç è¾“入过滤
        handleInput(value) {
            this.form.materielBarcode = value.replace(/[^-a-zA-Z0-9]/g, '');
            this.$nextTick(() => {
                this.$refs.form.validateField('materielBarcode');
            });
        },
        // ç²˜è´´ç‰©æ–™æ¡ç è‡ªåŠ¨æŸ¥è¯¢
        handlePaste(e) {
            const clipboardData = e.clipboardData || window.clipboardData;
            const pastedText = clipboardData.getData('text');
            const cleanedText = pastedText.replace(/[^-a-zA-Z0-9]/g, '');
            if (cleanedText) {
                this.form.materielBarcode = cleanedText;
                setTimeout(() => {
                    this.getStockTotal();
                }, 50);
            }
            e.preventDefault();
        },
        // ä»“库号回车聚焦物料条码
        focusMaterielInput() {
            this.$nextTick(() => {
                const inputRef = this.$refs.materielCodeInput;
                if (inputRef) {
                    const inputEl = inputRef.$el ? inputRef.$el.querySelector('input') : inputRef;
                    inputEl?.focus();
                }
            });
        },
        // èšç„¦ä»“库号输入框
        focusWarehouseInput() {
            this.$nextTick(() => {
                const inputRef = this.$refs.warehouseCodeInput;
                if (inputRef) {
                    const inputEl = inputRef.$el ? inputRef.$el.querySelector('input') : inputRef;
                    inputEl?.focus();
                }
            });
        },
        // èšç„¦å¹¶é€‰ä¸­ç‰©æ–™æ¡ç è¾“入框
        focusAndSelectInput() {
            this.$nextTick(() => {
                setTimeout(() => {
                    const inputRef = this.$refs.materielCodeInput;
                    if (inputRef) {
                        const inputEl = inputRef.$el ? inputRef.$el.querySelector('input') : inputRef;
                        if (inputEl) {
                            inputEl.focus();
                            inputEl.select();
                        }
                    }
                }, 100);
            });
        },
        // é‡ç½®è¡¨å•
        resetForm() {
            this.form = {
                warehouseCode: '',
                materielBarcode: '',
                totalStockQuantity: 0
            };
            this.$refs.form.clearValidate();
            this.focusWarehouseInput();
        }
    },
    watch: {
        show(val) {
            if (val) {
                this.$nextTick(() => {
                    this.focusWarehouseInput()
                })
            } else {
                this.resetForm();
            }
        }
    }
}
</script>
<style scoped>
.dialog-footer {
    text-align: right;
}
.text-gray-500 {
    color: #909399;
    font-size: 12px;
}
.ml-2 {
    margin-left: 8px;
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/stockInfoDetailByMaterielSum.js
@@ -1,10 +1,10 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
import gridHeader from "./extend/CalculateStock.vue";
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridHeader: gridHeader,
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
@@ -17,7 +17,14 @@
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {  
        console.log(this)
        var EmptyTrayInboundBtn = this.buttons.find(
        (x) => x.value == "CalculateStock"
      );
      if (EmptyTrayInboundBtn != null) {
        EmptyTrayInboundBtn.onClick = () => {
          this.$refs.gridHeader.open();
        };
      }
      },
      onInited() {
        //框架初始化配置后
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -9,6 +9,8 @@
                    <i class="el-icon-document"></i>
                    <span class="order-label">订单号:</span>
                    <span class="order-value">{{ orderNo }}</span>
                    <span class="order-label" style="margin-left: 20px;">产线名称:</span>
                    <span class="order-value">{{ orderInfo?.departmentName || '' }}</span>
                </div>
                <div class="order-status">
                    <el-tag v-if="orderInfo" :type="getStatusType(orderInfo.orderStatus)" size="medium"
@@ -656,7 +658,6 @@
            this.pickedTotal = this.pickedCount
        },
        // ============== å¾®è°ƒï¼šæ–°å¢žèŽ·å–è®¢å•å·é€»è¾‘ï¼Œå…¶ä½™ä¸å˜ ==============
        handlePalletScan(flag = true) {
            const palletCode = this.scanForm.palletCode.trim();
            if (!palletCode) {
@@ -673,7 +674,6 @@
                this.loadPalletData(flag);
            });
        },
        // ============== å¾®è°ƒç»“束 ==============
        handleMaterialScan() {
            if (!this.scanForm.palletCode) {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IStockService/IStockDetailByMaterielService.cs
@@ -14,5 +14,7 @@
    public interface IStockDetailByMaterielService : IDependency
    {
        PageGridData<StockDetailByMateriel> GetPageGridData(PageDataOptions options);
        WebResponseContent CalculateStock(string warehouseCode, string materielCode);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockDetailByMaterielService.cs
@@ -18,11 +18,13 @@
    {
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        private readonly SqlSugarClient _dbBase;
        private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository;
        public StockDetailByMaterielService(IUnitOfWorkManage unitOfWorkManage)
        public StockDetailByMaterielService(IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _dbBase = unitOfWorkManage.GetDbClient();
            _stockInfoDetailRepository = stockInfoDetailRepository;
        }
@@ -146,7 +148,12 @@
                    }
                }
                decimal totalStockQuantity = 0;
                if (groupedData.Count > 0)
                {
                    totalStockQuantity = groupedData.Values.Sum(x => x.StockQuantity);
                }
                materielnfoStatistics = groupedData.Values.ToList();
                int startIndex = (options.Page - 1) * options.Rows;
                int endIndex = Math.Min(startIndex + options.Rows, materielnfoStatistics.Count);
@@ -160,12 +167,25 @@
                }
                int count = groupedData.Count;
                return new PageGridData<StockDetailByMateriel>(count, materielnfoStatistics);
                return new PageGridData<StockDetailByMateriel>(count, materielnfoStatistics)
                {
                    TotalStockQuantity = totalStockQuantity
                };
            }
            catch (Exception ex)
            {
                return new PageGridData<StockDetailByMateriel>();
            }
        }
        public WebResponseContent CalculateStock(string warehouseCode,string materielCode)
        {
           var calculateStock =_stockInfoDetailRepository.QueryData(x => x.WarehouseCode == warehouseCode && x.MaterielCode == materielCode).Sum(x => x.StockQuantity);
           if(calculateStock == 0)
            {
                return WebResponseContent.Instance.Error("未找到库存");
            }
            return WebResponseContent.Instance.OK(data: calculateStock);
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockDetailByMaterielController.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization;
using Autofac.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core;
using WIDESEA_DTO.Stock;
@@ -22,5 +23,11 @@
        {
            return _stockDetailByMaterielService.GetPageGridData(options);
        }
        [HttpPost, Route("CalculateStock"), AllowAnonymous]
        public WebResponseContent CalculateStock(string warehouseCode,string materielCode)
        {
            return _stockDetailByMaterielService.CalculateStock(warehouseCode,materielCode);
        }
    }
}