heshaofeng
23 小时以前 673b5a596f611099eaacc310f6e7def0e022daca
添加盘点模板和胶箱台账功能
已添加10个文件
已修改14个文件
38765 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/config/buttons.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/package-lock.json 21101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/inbound/extend/BatchOutboundByExcel.vue 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/EmptyTrayOutbound.vue 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/Dt_PlasticContainerLedger.js 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/extend/BatchOutboundByExcel.vue 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/extend/GlueLineLedgerSummary.vue 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/stockInfo.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/stockView.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/router/viewGird.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/stock/Dt_PlasticContainerLedger.vue 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/yarn.lock 16059 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Stock/StockViewDTO.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IStockService/IPlasticContainerLedgerService.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Allocate/Stuedent.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_PlasticContainerLedger.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_StockInfo.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/PlasticContainerLedgerService.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/PlasticContainerLedgerController.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/config/buttons.js
@@ -372,6 +372,22 @@
    type: 'danger',
    onClick: function () {
    }
},{
    name: "盘点模板导入",
    icon: '',
    class: '',
    value: 'BatchOutboundByExcel',
    type: 'danger',
    onClick: function () {
    }
},{
    name: "胶线台账",
    icon: '',
    class: '',
    value: 'GlueLineLedgerSummary',
    type: 'warning',
    onClick: function () {
    }
}
]
ÏîÄ¿´úÂë/WIDESEA_WMSClient/package-lock.json
ÎļþÌ«´ó
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/BatchOutboundByExcel.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,182 @@
<template>
    <!-- é®ç½©å±‚ -->
    <div class="mask-layer" v-if="loading"></div>
    <vol-box v-model="show" title="批量Excel盘点出库" :width="800" :height="500">
        <template #content>
            <el-form ref="form" :model="form" label-width="90px" class="mt-10">
                <!-- å‡ºåº“区域(和你直接出库完全一样) -->
                <el-form-item label="出库区域:">
                    <el-select v-model="station" placeholder="请选择出库区域" style="width: 100%">
                        <el-option
                            v-for="item in stations"
                            :key="item.key"
                            :label="item.label"
                            :value="item.value">
                        </el-option>
                    </el-select>
                </el-form-item>
                <!-- Excel上传 -->
                <el-form-item label="Excel文件:" prop="file">
                    <el-upload
                        ref="upload"
                        :auto-upload="false"
                        :limit="1"
                        accept=".xlsx"
                        :file-list="fileList"
                        @change="handleFileChange"
                    >
                        <el-button type="primary" :disabled="loading">选择Excel文件</el-button>
                        <template #tip>
                            <div class="text-gray-500 text-xs mt-1">
                                ä»…支持 .xlsx<br>
                                ç¬¬ä¸€åˆ—:仓库号  ç¬¬äºŒåˆ—:物料编码
                            </div>
                        </template>
                    </el-upload>
                </el-form-item>
                <!-- ç»“æžœ -->
                <el-form-item label="处理结果:">
                    <div class="result-box p-3 border rounded bg-gray-50 h-32 overflow-auto">
                        <div v-if="resultText" class="text-sm whitespace-pre-line">{{ resultText }}</div>
                        <div v-else class="text-gray-400 text-sm">请选择文件并执行批量出库</div>
                    </div>
                </el-form-item>
            </el-form>
        </template>
        <template #footer>
            <div class="dialog-footer">
                <el-button size="small" plain @click="resetForm">重置</el-button>
                <el-button size="small" type="danger" plain @click="batchImport" :loading="loading">
                    <i class="el-icon-check"></i> ç¡®è®¤æ‰¹é‡å‡ºåº“
                </el-button>
                <el-button size="small" type="primary" plain @click="show = false">
                    <i class="el-icon-close"></i> å…³é—­
                </el-button>
            </div>
        </template>
    </vol-box>
</template>
<script>
import VolBox from '@/components/basic/VolBox.vue'
import { stationManager } from "@/../src/uitils/stationManager";
export default {
    components: { VolBox },
    props: {
        value: { type: Boolean, default: false }
    },
    data() {
        return {
            show: false,
            loading: false,
            station: stationManager.getStation(), // ä»Žæœ¬åœ°ç¼“存获取站台
            stations: [
                { label: "站台2", value: "2-1" },
                { label: "站台3", value: "3-1" },
            ],
            fileList: [],
            resultText: '',
            form: {}
        }
    },
    methods: {
        open() {
            this.show = true
            this.station = stationManager.getStation() // æ¯æ¬¡æ‰“开重新获取
            this.resetForm()
        },
        handleFileChange(file) {
            this.fileList = [file.raw]
        },
        async batchImport() {
            // éªŒè¯ç«™å°
            if (!this.station) {
                this.$message.warning("请选择出库区域")
                return
            }
            if (this.fileList.length === 0) {
                this.$message.warning("请选择Excel文件")
                return
            }
            this.loading = true
            this.resultText = "正在处理..."
            try {
                const formData = new FormData()
                formData.append('file', this.fileList[0])
                formData.append('outStation', this.station) // æäº¤ç«™å°ï¼
                const res = await this.http.post(
                    '/api/Task/BatchOutboundByExcel',
                    formData,
                    { headers: { 'Content-Type': 'multipart/form-data' } }
                )
                if (res.status) {
                    const d = res.data
                    this.resultText = `总条数:${d.总条数}\n成功:${d.成功条数}\n失败:${d.失败条数}`
                    if (d.失败明细?.length) this.resultText += `\n\n失败:\n${d.失败明细.join('\n')}`
                    this.$message.success("批量完成")
                } else {
                    this.resultText = "失败:" + res.message
                    this.$message.error(res.message)
                }
            } catch (err) {
                this.resultText = "异常:" + err.message
                this.$message.error("批量失败:" + err.message)
            } finally {
                this.loading = false
            }
        },
        resetForm() {
            this.fileList = []
            this.resultText = ''
            this.$refs.upload?.clearFiles()
        }
    },
    watch: {
        show(val) {
            if (!val) this.resetForm()
        }
    }
}
</script>
<style scoped>
.dialog-footer { text-align: right; }
.text-gray-500 { color: #909399; font-size: 12px; }
.result-box { white-space: pre-line; line-height: 1.5; }
/* é®ç½©å±‚ å’Œä½ é¡µé¢å®Œå…¨ä¸€æ · */
.mask-layer {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(0,0,0,0.5);
    z-index: 9999;
    display: flex;
    justify-content: center;
    align-items: center;
}
.mask-layer::after {
    content: "";
    width: 40px;
    height: 40px;
    border: 4px solid #fff;
    border-top: 4px solid #409eff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/EmptyTrayOutbound.vue
@@ -25,10 +25,33 @@
          </el-input-number>
        </el-form-item>
      </el-form>
      <!-- ===================== æ–°å¢žï¼šä¾›åº”商编码(实时搜索下拉) ===================== -->
      <el-form ref="form" :model="form" label-width="90px">
        <el-form-item label="供应商编码:">
          <el-select
            v-model="supplierCode"
            filterable
            remote
            reserve-keyword
            placeholder="请输入/选择供应商编码"
            :remote-method="remoteSearchSupplier"
            style="width: 100%"
          >
            <el-option
              v-for="item in supplierList"
              :key="item"
              :label="item"
              :value="item"
            />
          </el-select>
        </el-form-item>
      </el-form>
      <!-- ======================================================================== -->
    </template>
    <template #footer>
      <div>
        <!-- æ·»åŠ loading状态和disabled属性 -->
        <el-button 
          type="danger" 
          size="small" 
@@ -62,8 +85,11 @@
      show: false,
      locationTypes: [],
      locationType: "",
      // æ–°å¢žæäº¤çŠ¶æ€æ ‡è¯†
      isSubmitting: false
      isSubmitting: false,
      // ================= æ–°å¢ž =================
      supplierCode: '',       // ä¾›åº”商编码绑定
      supplierList: []        // ä¸‹æ‹‰åˆ—表
      // =======================================
    }
  },
  methods: {
@@ -72,17 +98,19 @@
      this.getData();
    },
    submit() {
      // 1. éªŒè¯å¿…填项
      if (!this.locationType) {
        this.$message.warning('请选择出库区域');
        return;
      }
      
      // 2. è®¾ç½®æäº¤çŠ¶æ€ä¸ºtrue,禁用按钮
      this.isSubmitting = true;
      
      this.$emit('parentCall', ($vue) => {
        this.http.post(`/api/Task/PalletOutboundTask?num=${this.num}&locationType=${this.locationType}`, {}, '数据处理中...')
        // ===================== æ”¹é€ æŽ¥å£ï¼šå¸¦ä¸Š supplierCode =====================
        this.http.post(
          `/api/Task/PalletOutboundTask?num=${this.num}&locationType=${this.locationType}&SupplierCode=${this.supplierCode || ''}`,
          {}, '数据处理中...')
        // ====================================================================
          .then((x) => {
            if (!x.status) {
              this.$message.error(x.message)
@@ -93,12 +121,10 @@
            }
          })
          .catch((error) => {
            // æ•获请求异常,提示用户
            this.$message.error('请求失败,请稍后重试');
            console.error('提交失败:', error);
          })
          .finally(() => {
            // 3. è¯·æ±‚完成(成功/失败)后重置提交状态
            this.isSubmitting = false;
          });
      })
@@ -109,6 +135,21 @@
          this.locationTypes = x.data;
        })
    },
    remoteSearchSupplier(keyword) {
      if (!keyword) {
        this.supplierList = [];
        return;
      }
      // è°ƒç”¨åŽç«¯å®žæ—¶æœç´¢æŽ¥å£
      this.http.get(`/api/Task/SearchSupplierCode?keyword=${keyword}`)
        .then(res => {
          if (res.status) {
            this.supplierList = res.data || [];
          }
        })
    }
  }
}
</script>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/Dt_PlasticContainerLedger.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
import gridHeader from "./extend/GlueLineLedgerSummary.vue";
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: gridHeader,
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
        this.summary = true;
        var GlueLineLedgerSummaryBtn = this.buttons.find(
                (x) => x.value == "GlueLineLedgerSummary"
              );
              if (GlueLineLedgerSummaryBtn != null) {
                GlueLineLedgerSummaryBtn.onClick = () => {
                  this.$refs.gridHeader.open();
                };
              }
      },
      onInited() {
        //框架初始化配置后
        //如果要配置明细表,在此方法操作
        //this.detailOptions.columns.forEach(column=>{ });
      },
      searchBefore(param) {
        //界面查询前,可以给param.wheres添加查询参数
        //返回false,则不会执行查询
        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;
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/extend/BatchOutboundByExcel.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,182 @@
<template>
    <!-- é®ç½©å±‚ -->
    <div class="mask-layer" v-if="loading"></div>
    <vol-box v-model="show" title="批量Excel盘点出库" :width="800" :height="500">
        <template #content>
            <el-form ref="form" :model="form" label-width="90px" class="mt-10">
                <!-- å‡ºåº“区域(和你直接出库完全一样) -->
                <el-form-item label="出库区域:">
                    <el-select v-model="station" placeholder="请选择出库区域" style="width: 100%">
                        <el-option
                            v-for="item in stations"
                            :key="item.key"
                            :label="item.label"
                            :value="item.value">
                        </el-option>
                    </el-select>
                </el-form-item>
                <!-- Excel上传 -->
                <el-form-item label="Excel文件:" prop="file">
                    <el-upload
                        ref="upload"
                        :auto-upload="false"
                        :limit="1"
                        accept=".xlsx"
                        :file-list="fileList"
                        @change="handleFileChange"
                    >
                        <el-button type="primary" :disabled="loading">选择Excel文件</el-button>
                        <template #tip>
                            <div class="text-gray-500 text-xs mt-1">
                                ä»…支持 .xlsx<br>
                                ç¬¬å…«åˆ—:仓库号  ç¬¬äºŒåˆ—:物料编码
                            </div>
                        </template>
                    </el-upload>
                </el-form-item>
                <!-- ç»“æžœ -->
                <el-form-item label="处理结果:">
                    <div class="result-box p-3 border rounded bg-gray-50 h-32 overflow-auto">
                        <div v-if="resultText" class="text-sm whitespace-pre-line">{{ resultText }}</div>
                        <div v-else class="text-gray-400 text-sm">请选择文件并执行批量出库</div>
                    </div>
                </el-form-item>
            </el-form>
        </template>
        <template #footer>
            <div class="dialog-footer">
                <el-button size="small" plain @click="resetForm">重置</el-button>
                <el-button size="small" type="danger" plain @click="batchImport" :loading="loading">
                    <i class="el-icon-check"></i> ç¡®è®¤æ‰¹é‡å‡ºåº“
                </el-button>
                <el-button size="small" type="primary" plain @click="show = false">
                    <i class="el-icon-close"></i> å…³é—­
                </el-button>
            </div>
        </template>
    </vol-box>
</template>
<script>
import VolBox from '@/components/basic/VolBox.vue'
import { stationManager } from "@/../src/uitils/stationManager";
export default {
    components: { VolBox },
    props: {
        value: { type: Boolean, default: false }
    },
    data() {
        return {
            show: false,
            loading: false,
            station: stationManager.getStation(), // ä»Žæœ¬åœ°ç¼“存获取站台
            stations: [
                { label: "站台2", value: "2-1" },
                { label: "站台3", value: "3-1" },
            ],
            fileList: [],
            resultText: '',
            form: {}
        }
    },
    methods: {
        open() {
            this.show = true
            this.station = stationManager.getStation() // æ¯æ¬¡æ‰“开重新获取
            this.resetForm()
        },
        handleFileChange(file) {
            this.fileList = [file.raw]
        },
        async batchImport() {
            // éªŒè¯ç«™å°
            if (!this.station) {
                this.$message.warning("请选择出库区域")
                return
            }
            if (this.fileList.length === 0) {
                this.$message.warning("请选择Excel文件")
                return
            }
            this.loading = true
            this.resultText = "正在处理..."
            try {
                const formData = new FormData()
                formData.append('file', this.fileList[0])
                formData.append('outStation', this.station) // æäº¤ç«™å°ï¼
                const res = await this.http.post(
                    '/api/Task/BatchOutboundByExcel',
                    formData,
                    { headers: { 'Content-Type': 'multipart/form-data' } }
                )
                if (res.status) {
                    const d = res.data
                    this.resultText = `满足仓库号和料号总条数:${d.total}\n成功出库总条数:${d.success}\n失败:${d.fail}`
                    if (d.failMsg?.length) this.resultText += `\n\n失败:\n${d.failMsg.join('\n')}`
                    this.$message.success("批量完成")
                } else {
                    this.resultText = "失败:" + res.message
                    this.$message.error(res.message)
                }
            } catch (err) {
                this.resultText = "异常:" + err.message
                this.$message.error("批量失败:" + err.message)
            } finally {
                this.loading = false
            }
        },
        resetForm() {
            this.fileList = []
            this.resultText = ''
            this.$refs.upload?.clearFiles()
        }
    },
    watch: {
        show(val) {
            if (!val) this.resetForm()
        }
    }
}
</script>
<style scoped>
.dialog-footer { text-align: right; }
.text-gray-500 { color: #909399; font-size: 12px; }
.result-box { white-space: pre-line; line-height: 1.5; }
/* é®ç½©å±‚ å’Œä½ é¡µé¢å®Œå…¨ä¸€æ · */
.mask-layer {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(0,0,0,0.5);
    z-index: 9999;
    display: flex;
    justify-content: center;
    align-items: center;
}
.mask-layer::after {
    content: "";
    width: 40px;
    height: 40px;
    border: 4px solid #fff;
    border-top: 4px solid #409eff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/extend/GlueLineLedgerSummary.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,247 @@
<template>
    <vol-box v-model="show" title="胶线台账汇总" :width="1200" :height="750">
        <template #content>
            <el-form ref="form" :model="form" :rules="rules" label-width="100px" class="form-container">
                <div style="display: flex;flex-wrap:wrap;align-items: center;gap: 10px;margin-bottom:10px;">
                    <el-form-item label="供应商编码:" prop="supplyCode" style="margin-bottom: 0;flex:1;">
                        <el-input
                            v-model="form.supplyCode"
                            placeholder="请输入供应商编码"
                            clearable
                            @keyup.enter="getSummary"
                            ref="supplyInput"
                            :disabled="loading"
                        />
                    </el-form-item>
                    <!-- ðŸ”¥ ä¿®å¤åŽçš„æ—¥æœŸé€‰æ‹©ï¼Œå’Œä½ æˆªå›¾æ ·å¼å®Œå…¨ä¸€è‡´ -->
                    <el-form-item label="借出日期:" style="margin-bottom: 0;">
                        <div style="display: flex; align-items: center; gap: 4px;">
                            <el-date-picker
                                v-model="form.startDate"
                                type="date"
                                placeholder="yyyy-MM-dd"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 140px;"
                                :disabled="loading"
                            />
                            <span>至</span>
                            <el-date-picker
                                v-model="form.endDate"
                                type="date"
                                placeholder="yyyy-MM-dd"
                                format="YYYY-MM-DD"
                                value-format="YYYY-MM-DD"
                                style="width: 140px;"
                                :disabled="loading"
                            />
                        </div>
                    </el-form-item>
                    <el-form-item label="状态:" style="margin-bottom: 0;width:180px;">
                        <el-select v-model="form.status" placeholder="全部" clearable :disabled="loading">
                            <el-option label="全部" value="" />
                            <el-option label="全部归还" value="全部归还" />
                            <el-option label="部分归还" value="部分归还" />
                            <el-option label="未归还" value="未归还" />
                        </el-select>
                    </el-form-item>
                    <el-form-item style="margin-bottom: 0;">
                        <el-button type="primary" @click="getSummary" :loading="loading">
                            æŸ¥è¯¢æ±‡æ€»
                        </el-button>
                        <el-button @click="resetForm">重置</el-button>
                    </el-form-item>
                </div>
            </el-form>
            <div class="table-container">
                <el-table
                    v-loading="loading"
                    :data="tableData"
                    border
                    stripe
                    style="width:100%;"
                    show-summary
                    :summary-method="getSummaries"
                >
                    <el-table-column label="供应商编码" prop="supplyCode" width="160" align="center" />
                    <el-table-column label="借出日期" prop="borrowTime" width="180" align="center" />
                    <el-table-column label="借出数量" prop="unreturnedQuantity" width="130" align="center" />
                    <el-table-column label="已归还数量" prop="returnedQty" width="130" align="center" />
                    <el-table-column label="待归还数量" prop="pendingReturnQty" width="130" align="center" />
                    <el-table-column label="状态" prop="status" min-width="120"/>
                </el-table>
                <div v-if="!loading && tableData.length === 0" class="text-center py-10">
                    æš‚无数据
                </div>
            </div>
        </template>
        <template #footer>
            <div class="dialog-footer text-right">
                <el-button type="success" icon="el-icon-download" @click="exportExcel">
                    å¯¼å‡ºExcel
                </el-button>
                <el-button @click="show = false">关闭</el-button>
            </div>
        </template>
    </vol-box>
</template>
<script>
import VolBox from '@/components/basic/VolBox.vue'
export default {
    components: { VolBox },
    props: {
        value: { type: Boolean, default: false }
    },
    data() {
        const validateSupply = (rule, value, callback) => {
            if (!value) return callback(new Error('请输入供应商编码'))
            callback()
        }
        return {
            show: false,
            loading: false,
            form: {
                supplyCode: '',
                startDate: '',
                endDate: '',
                status: ''
            },
            tableData: [],
            rules: {
                supplyCode: [{ validator: validateSupply, trigger: 'blur' }]
            }
        }
    },
    methods: {
        open() {
            this.show = true
            this.$nextTick(() => this.$refs.supplyInput.focus())
        },
        setRowStatus(row) {
            const pending = Number(row.pendingReturnQty || 0)
            if (pending === 0) {
                row.status = '全部归还'
            } else if (row.returnedQty > 0) {
                row.status = '部分归还'
            } else {
                row.status = '未归还'
            }
        },
        getSummaries(param) {
            const sums = ['合计', '', 0, 0, 0, '']
            this.tableData.forEach(item => {
                sums[2] += Number(item.unreturnedQuantity || 0)
                sums[3] += Number(item.returnedQty || 0)
                sums[4] += Number(item.pendingReturnQty || 0)
            })
            return sums
        },
        exportExcel() {
    if (this.tableData.length === 0) {
        this.$message.warning('暂无数据可导出')
        return
    }
    // è®¡ç®—合计
    let totalUnreturn = 0
    let totalReturn = 0
    let totalPending = 0
    this.tableData.forEach(item => {
        totalUnreturn += Number(item.unreturnedQuantity || 0)
        totalReturn += Number(item.returnedQty || 0)
        totalPending += Number(item.pendingReturnQty || 0)
    })
    // è¡¨å¤´
    const headers = '供应商编码,借出日期,借出数量,已归还数量,待归还数量,状态\n'
    let csv = headers
    // æ‹¼æŽ¥æ•°æ®
    this.tableData.forEach(item => {
        csv += `${item.supplyCode},${item.borrowTime},${item.unreturnedQuantity},${item.returnedQty},${item.pendingReturnQty},${item.status}\n`
    })
    // æ‹¼æŽ¥åˆè®¡è¡Œ
    csv += `合计,,${totalUnreturn},${totalReturn},${totalPending},\n`
    // ä¸‹è½½
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = '胶线台账汇总.csv'
    link.click()
    this.$message.success('导出成功')
},
        async getSummary() {
            await this.$refs.form.validate()
            let supplyCode = this.form.supplyCode.trim()
            this.loading = true
            try {
                const res = await this.http.post(
                    "/api/PlasticContainerLedger/GlueLineLedgerSummary?supplyCode=" + supplyCode
                )
                if (res.status && res.code === 0) {
                    let list = res.data || []
                    list.forEach(item => this.setRowStatus(item))
                    // æ—¥æœŸç­›é€‰
                    if (this.form.startDate && this.form.endDate) {
                        list = list.filter(item => {
                            return item.borrowTime >= this.form.startDate && item.borrowTime <= this.form.endDate
                        })
                    } else if (this.form.startDate) {
                        list = list.filter(item => item.borrowTime >= this.form.startDate)
                    } else if (this.form.endDate) {
                        list = list.filter(item => item.borrowTime <= this.form.endDate)
                    }
                    if (this.form.status) {
                        list = list.filter(item => item.status === this.form.status)
                    }
                    this.tableData = list
                    this.$message.success("查询成功")
                }
            } catch (error) {
                this.$message.error("请求失败")
            } finally {
                this.loading = false
            }
        },
        resetForm() {
            this.form = {
                supplyCode: '',
                startDate: '',
                endDate: '',
                status: ''
            }
            this.tableData = []
            this.$refs.form.clearValidate()
        }
    },
    watch: {
        value(val) { this.show = val },
        show(val) { if (!val) setTimeout(() => this.resetForm(), 200) }
    }
}
</script>
<style scoped>
.table-container { height: 450px; }
.py-10 { text-align: center; padding: 30px 0; color: #999; }
.dialog-footer { text-align: right; padding: 10px 0; }
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/stockInfo.js
@@ -1,6 +1,5 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
@@ -17,7 +16,6 @@
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {  
      },
      onInited() {
        //框架初始化配置后
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/stock/stockView.js
@@ -1,11 +1,12 @@
import { createVNode, render, h, reactive } from 'vue';
import { ElDialog, ElForm, ElFormItem, ElSelect, ElOption, ElButton, ElMessage, ElLoading } from 'element-plus';
import gridHeader from './extend/CrossAreaRelocationDialog.vue'
import gridBody from './extend/BatchOutboundByExcel.vue'
let extension = {
  components: {
    //查询界面扩展组件
    gridHeader: gridHeader,
    gridBody: '',
    gridBody: gridBody,
    gridFooter: '',
    //新建、编辑弹出框扩展组件
    modelHeader: '',
@@ -17,6 +18,13 @@
  methods: {
    //下面这些方法可以保留也可以删除
    onInit() {
      let BatchOutboundByExcelBtn = this.buttons.find(x => x.value === 'BatchOutboundByExcel');
      if (BatchOutboundByExcelBtn) {
        // é‡å†™æŒ‰é’®ç‚¹å‡»äº‹ä»¶
        BatchOutboundByExcelBtn.onClick = function () {
          this.$refs.gridBody.open();
        };
      }
      // let InOrder = this.buttons.find(x => x.value == 'StockOutbound');
      // if (InOrder) {
      //   InOrder.onClick = function () {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/router/viewGird.js
@@ -257,7 +257,11 @@
  name: 'recheckOutPicking', 
  component: () => import('@/views/outbound/recheckOutPicking.vue'),
  meta: { title: '拣选确认', keepAlive: false }
}
}, {
    path: '/PlasticContainerLedger',
    name: 'Dt_PlasticContainerLedger',
    component: () => import('@/views/stock/Dt_PlasticContainerLedger.vue')
  }
]
export default viewgird   
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/inboundOrder.vue
@@ -264,7 +264,7 @@
          type: "select",
          width: 120,
          align: "left",
          edit: { type: "" },
          required: true,
        },
        {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/stock/Dt_PlasticContainerLedger.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
<template>
  <view-grid
    ref="grid"
    :columns="columns"
    :editFormFields="editFormFields"
    :editFormOptions="editFormOptions"
    :searchFormFields="searchFormFields"
    :searchFormOptions="searchFormOptions"
    :table="table"
    :extend="extend">
  </view-grid>
</template>
<script>
import extend from "@/extension/stock/Dt_PlasticContainerLedger.js";
import { ref, defineComponent } from "vue";
export default defineComponent({
  setup() {
    const table = ref({
      key: "id",
      footer: "Foots",
      cnName: "胶箱台账",
      name: "plasticContainerLedger",
      url: "/PlasticContainerLedger/",
      sortName: "createDate",
    });
    // ç¼–辑字段
    const editFormFields = ref({
      supplyCode: "",
      palletCode: "",
      remark: "",
    });
    const editFormOptions = ref([
      [
        { field: "supplyCode", title: "供应商编号", type: "string" },
        { field: "palletCode", title: "托盘编号", type: "string" },
      ],
      [
        { field: "remark", title: "备注", type: "textarea", span: 24 }
      ]
    ]);
    // æœç´¢æ¡ä»¶
    const searchFormFields = ref({
      supplyCode: "",
      palletCode: "",
      createDate: "",
    });
    const searchFormOptions = ref([
      [
        { title: "供应商编号", field: "supplyCode", type: "like" },
        { title: "托盘编号", field: "palletCode", type: "like" },
        { title: "借出时间", field: "createDate", type: "datetime" },
      ]
    ]);
    // è¡¨æ ¼åˆ— å®Œå…¨å¯¹åº” Dt_PlasticContainerLedger
    const columns = ref([
      { field: "id", title: "ID", type: "int", width: 70, align: "left", hidden: true },
      { field: "supplyCode", title: "供应商编号", type: "string", width: 140, align: "left" },
      { field: "palletCode", title: "托盘编号", type: "string", width: 150, align: "left" },
      { field: "remark", title: "备注", type: "string", width: 220, align: "left" },
      { field: "creater", title: "创建人", type: "string", width: 100, align: "left" },
      { field: "createDate", title: "借出时间", type: "datetime", width: 180, align: "left" },
    ]);
    return {
      table,
      extend,
      editFormFields,
      editFormOptions,
      searchFormFields,
      searchFormOptions,
      columns
    };
  },
});
</script>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/yarn.lock
ÎļþÌ«´ó
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Stock/StockViewDTO.cs
@@ -136,4 +136,17 @@
        [Navigate(NavigateType.OneToMany, nameof(Dt_StockInfoDetail.StockId), nameof(StockId))]
        public List<Dt_StockInfoDetail> Details { get; set; }
    }
    public class ExcelInventoryOutboundDto
    {
        /// <summary>
        /// å­˜å‚¨åœ°ç‚¹
        /// </summary>
        public string WarehouseCode { get; set; }
        /// <summary>
        /// æ–™å·
        /// </summary>
        public string MaterialCode { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IStockService/IPlasticContainerLedgerService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_DTO.Stock;
using WIDESEA_Model.Models;
namespace WIDESEA_IStockService
{
    public interface IPlasticContainerLedgerService : IService<Dt_PlasticContainerLedger>
    {
        IRepository<Dt_PlasticContainerLedger> Repository { get; }
        WebResponseContent GlueLineLedgerSummary(string supplyCode);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -15,6 +15,7 @@
 *----------------------------------------------------------------*/
#endregion << ç‰ˆ æœ¬ æ³¨ é‡Š >>
using Microsoft.AspNetCore.Http;
using SqlSugar;
using System;
using System.Collections.Generic;
@@ -45,7 +46,7 @@
        Task<WebResponseContent> RequestInboundTask(string palletCode, string stationCode);
        Task<WebResponseContent> PalletOutboundTask(int num, int locationType);
        Task<WebResponseContent> PalletOutboundTask(int num, int locationType,string? supplierCode = null);
        Task<WebResponseContent> TaskCompleted(string taskNum);
@@ -83,5 +84,9 @@
        Task<WebResponseContent> AreaOutbound(List<StockViewDTO> stockViews);
        Task<WebResponseContent> CrossAreaOutbound(List<StockViewDTO> stockViews, int targetLocationType);
        Task<WebResponseContent> BatchOutboundByExcel(IFormFile file, string outStation);
        Task<WebResponseContent> SearchSupplierCode(string keyword);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Allocate/Stuedent.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEA_Model.Models.Allocate
{
    [SugarTable(nameof(Stuedent), "调拨单")]
    public class Stuedent
    {
        /// <summary>
        /// ä¸»é”®ID(自增)
        /// </summary>
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "主键")]
        public int Id { get; set; }
        /// <summary>
        /// ä»“库ID
        /// </summary>
        [SugarColumn(IsNullable = false, ColumnDescription = "仓库ID")]
        public int WarehouseId { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_PlasticContainerLedger.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.DB.Models;
namespace WIDESEA_Model.Models
{
    [SugarTable(nameof(Dt_PlasticContainerLedger), "胶箱台账")]
    public class Dt_PlasticContainerLedger : BaseEntity
    {
        /// <summary>
        /// ä¸»é”®
        /// </summary>
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "主键")]
        public int Id { get; set; }
        /// <summary>
        /// ä¾›åº”商编号
        /// </summary>
        [SugarColumn(IsNullable = false, Length = 50, ColumnDescription = "供应商编号")]
        public string SupplyCode { get; set; }
        [SugarColumn(IsNullable = true, ColumnDescription = "备注")]
        public string Remark { get; set; }
        [SugarColumn(IsNullable = false, ColumnDescription = "托盘编号")]
        public string PalletCode { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_StockInfo.cs
@@ -59,6 +59,18 @@
        public string Remark { get; set; }
        /// <summary>
        /// å€Ÿå‡ºä¾›åº”商
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "借出供应商")]
        public string SupplyCode { get; set; }
        /// <summary>
        /// å€Ÿå‡ºæ—¶é—´
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "借出时间")]
        public DateTime BorrowTime { get; set; }
        /// <summary>
        /// åº“存明细
        /// </summary>
        [Navigate(NavigateType.OneToMany, nameof(Dt_StockInfoDetail.StockId), nameof(Id))]
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/PlasticContainerLedgerService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
using WIDESEA_Core.Utilities;
using WIDESEA_DTO.Stock;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
namespace WIDESEA_StockService
{
    public class PlasticContainerLedgerService : ServiceBase<Dt_PlasticContainerLedger, IRepository<Dt_PlasticContainerLedger>>, IPlasticContainerLedgerService
    {
        private readonly IRepository<Dt_StockInfo> _stockRepository;
        public PlasticContainerLedgerService(IRepository<Dt_PlasticContainerLedger> BaseDal, IRepository<Dt_StockInfo> stockRepository) : base(BaseDal)
        {
            _stockRepository = stockRepository;
        }
        public IRepository<Dt_PlasticContainerLedger> Repository => BaseDal;
        public WebResponseContent GlueLineLedgerSummary(string supplyCode)
        {
            supplyCode = supplyCode?.Trim() ?? "";
            var now = DateTime.Now;
            var borrowGroup = BaseDal.Db.Queryable<Dt_PlasticContainerLedger>()
                .Where(x => x.SupplyCode == supplyCode)
                .GroupBy(x => new { x.SupplyCode, x.CreateDate.Date })
                .Select(x => new
                {
                    x.SupplyCode,
                    CreateDate = x.CreateDate.Date,
                    BorrowQty = SqlFunc.AggregateCount(x.Id)
                })
                .ToList();
            var returnedGroup = _stockRepository.Db.Queryable<Dt_StockInfo>()
                .Where(x => x.SupplyCode == supplyCode && !string.IsNullOrEmpty(x.Remark))
                .GroupBy(x => new { x.SupplyCode, x.BorrowTime.Date })
                .Select(x => new
                {
                    x.SupplyCode,
                    CreateDate = x.BorrowTime.Date,
                    ReturnedQty = SqlFunc.AggregateCount(x.Id)
                })
                .ToList();
            var result = new List<GlueLineLedgerSummaryDTO>();
            foreach (var item in borrowGroup)
            {
                result.Add(new GlueLineLedgerSummaryDTO
                {
                    SupplyCode = item.SupplyCode,
                    BorrowTime = item.CreateDate.ToString("yyyy-MM-dd"),
                    UnreturnedQuantity = item.BorrowQty,
                    ReturnedQty = 0,
                    PendingReturnQty = item.BorrowQty
                });
            }
            foreach (var item in returnedGroup)
            {
                result.Add(new GlueLineLedgerSummaryDTO
                {
                    SupplyCode = item.SupplyCode,
                    BorrowTime = item.CreateDate.ToString("yyyy-MM-dd"),
                    UnreturnedQuantity = item.ReturnedQty,
                    ReturnedQty = item.ReturnedQty,
                    PendingReturnQty = 0
                });
            }
            var filterResult = result
                .Where(x =>
                {
                    DateTime borrowTime = DateTime.Parse(x.BorrowTime);
                    if (x.PendingReturnQty > 0)
                        return true;
                    if (borrowTime >= now.AddMonths(-1))
                        return true;
                    return false;
                })
                .ToList();
            return WebResponseContent.Instance.OK(data: filterResult);
        }
        public class GlueLineLedgerSummaryDTO{
           public string SupplyCode { get; set; }
           public int UnreturnedQuantity { get; set; }
           public int ReturnedQty { get; set; }
           public int PendingReturnQty { get; set; }
           public string BorrowTime { get; set; }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -100,6 +100,7 @@
        public readonly IRepository<Dt_LocationType> _locationTypeRepository;
        public readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository;
        private readonly IRepository<Dt_OutStockLockInfo> _outboundLockInfoRepository;
        private readonly IRepository<Dt_PlasticContainerLedger> _plasticContainerLedger;
        public IRepository<Dt_Task> Repository => BaseDal;
        private Dictionary<string, SqlSugar.OrderByType> _taskOrderBy = new()
@@ -119,7 +120,7 @@
        public List<int> TaskOutboundTypes => typeof(TaskTypeEnum).GetEnumIndexList();
        public TaskService(IRepository<Dt_Task> BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockRepository, ILocationInfoService locationInfoService, IInboundOrderService inboundOrderService, ILocationStatusChangeRecordService locationStatusChangeRecordService, IESSApiService eSSApiService, ILogger<TaskService> logger, IStockService stockService, IRecordService recordService, IInboundOrderDetailService inboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IInvokeMESService invokeMESService, IOutStockLockInfoService outStockLockInfoService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, IRepository<Dt_ReCheckOrder> reCheckOrderRepository, IRepository<Dt_AllocateOrderDetail> allocateOrderDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository, IMaterialUnitService materialUnitService, ITask_HtyService task_HtyService, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfo, IRepository<Dt_AllocateMaterialInfo_Hty> allocateMaterialInfo_Hty, HttpClientHelper httpClientHelper, IBasicService basicService,IRepository<Dt_TakeStockOrder> takeStockOrder, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository) : base(BaseDal)
        public TaskService(IRepository<Dt_Task> BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockRepository, ILocationInfoService locationInfoService, IInboundOrderService inboundOrderService, ILocationStatusChangeRecordService locationStatusChangeRecordService, IESSApiService eSSApiService, ILogger<TaskService> logger, IStockService stockService, IRecordService recordService, IInboundOrderDetailService inboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IInvokeMESService invokeMESService, IOutStockLockInfoService outStockLockInfoService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, IRepository<Dt_ReCheckOrder> reCheckOrderRepository, IRepository<Dt_AllocateOrderDetail> allocateOrderDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository, IMaterialUnitService materialUnitService, ITask_HtyService task_HtyService, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfo, IRepository<Dt_AllocateMaterialInfo_Hty> allocateMaterialInfo_Hty, HttpClientHelper httpClientHelper, IBasicService basicService,IRepository<Dt_TakeStockOrder> takeStockOrder, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository, IRepository<Dt_PlasticContainerLedger> plasticContainerLedger) : base(BaseDal)
        {
            _mapper = mapper;
            _unitOfWorkManage = unitOfWorkManage;
@@ -151,6 +152,7 @@
            _locationTypeRepository = locationTypeRepository;
            _warehouseAreaRepository = warehouseAreaRepository;
            _outboundLockInfoRepository = outboundLockInfoRepository;
            _plasticContainerLedger = plasticContainerLedger;
        }
        public async Task TaskStatusChange(string taskNum, TaskStatusEnum taskStatusEnum)
@@ -192,6 +194,54 @@
                }
                _logger.LogInformation($"TaskService TaskCompleted: {JsonConvert.SerializeObject(task)} , {task.TaskType} ");
                var taskType = (TaskTypeEnum)task.TaskType;
                bool isInboundTask = taskType is
                    TaskTypeEnum.Inbound
                    or TaskTypeEnum.InEmpty
                    or TaskTypeEnum.InInventory
                    or TaskTypeEnum.InPick
                    or TaskTypeEnum.InQuality
                    or TaskTypeEnum.ProductionReturn
                    or TaskTypeEnum.MesMatReturn
                    or TaskTypeEnum.InAllocate
                    or TaskTypeEnum.AllocateInWarehouse;
                if (isInboundTask && !string.IsNullOrEmpty(task.PalletCode))
                {
                    string palletCode = task.PalletCode;
                    // 1. å¼‚步查询托盘对应的借出台账(正确写法)
                    var ledger = await _plasticContainerLedger.Db.Queryable<Dt_PlasticContainerLedger>()
                        .FirstAsync(x => x.PalletCode == palletCode);
                    if (ledger != null)
                    {
                        // 2. åˆ é™¤å°è´¦ï¼ˆå·²å½’还)
                        await _plasticContainerLedger.DeleteDataAsync(ledger);
                        // 3. æŸ¥è¯¢åº“å­˜
                        var stock = await _stockService.StockInfoService.Repository
                            .QueryFirstAsync(x => x.PalletCode == palletCode);
                        if (stock != null)
                        {
                            // 4. æ‹¼æŽ¥å¤‡æ³¨
                            string borrowTime = ledger.CreateDate.ToString("yyyy-MM-dd HH:mm:ss");
                            string returnTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                            string remark = $"{borrowTime} å€Ÿå‡ºï¼Œ{returnTime} è¿˜å›ž";
                            stock.Remark = string.IsNullOrEmpty(stock.Remark)
                                ? remark
                                : $"{stock.Remark};{remark}";
                            stock.SupplyCode = ledger.SupplyCode;
                            stock.BorrowTime = ledger.CreateDate;
                            await _stockService.StockInfoService.Repository.UpdateDataAsync(stock);
                        }
                    }
                }
                MethodInfo? methodInfo = GetType().GetMethod(((TaskTypeEnum)task.TaskType) + "TaskCompleted");
                if (methodInfo != null)
                {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -1,6 +1,8 @@
using MailKit.Search;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OfficeOpenXml;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using SqlSugar;
using System;
@@ -31,6 +33,7 @@
using WIDESEA_Model.Models.Check;
using WIDESEA_Model.Models.Outbound;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
@@ -40,18 +43,19 @@
        /// </summary>
        /// <param name="inTask"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType)
        // æ–¹æ³•增加了 string? SupplierCode å¯é€‰å‚æ•°
        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType, string? supplierCode = null)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                Dictionary<string, SqlSugar.OrderByType> orderByDict = new Dictionary<string, SqlSugar.OrderByType>()
                {
                     { nameof(Dt_LocationInfo.Layer), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Row), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Column), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Depth), SqlSugar.OrderByType.Desc },
                };
        {
            { nameof(Dt_LocationInfo.Layer), SqlSugar.OrderByType.Asc },
            { nameof(Dt_LocationInfo.Row), SqlSugar.OrderByType.Asc },
            { nameof(Dt_LocationInfo.Column), SqlSugar.OrderByType.Asc },
            { nameof(Dt_LocationInfo.Depth), SqlSugar.OrderByType.Desc },
        };
                var query = _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt()
@@ -59,7 +63,7 @@
                    .WhereIF(locationType != 0, x => x.LocationType == locationType)
                    .LeftJoin<Dt_LocationInfo>((s, l) => s.LocationCode == l.LocationCode);
                if (query.Count() == 0)
                if (!await query.AnyAsync())
                {
                    return WebResponseContent.Instance.Error("未找到空托盘库存");
                }
@@ -80,17 +84,34 @@
                    }
                    else
                    {
                        query = query.OrderBy(sortSql);
                        query.OrderBy(sortSql);
                    }
                }
                var stockInfos = await query.Take(num).ToListAsync();
                _unitOfWorkManage.BeginTran();
                Dt_PlasticContainerLedger? todayLedger = null;
                var today = DateTime.Now.Date;
                var tomorrow = today.AddDays(1);
                if (!string.IsNullOrWhiteSpace(supplierCode))
                {
                    todayLedger = await _stockRepository.Db.Queryable<Dt_PlasticContainerLedger>()
                        .Where(x => x.SupplyCode == supplierCode)
                        .Where(x => x.CreateDate >= today && x.CreateDate < tomorrow)
                        .FirstAsync();
                }
                string allTaskNums = string.Empty;
                foreach (var stockInfo in stockInfos)
                {
                    Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
                    Dt_LocationInfo locationInfo = await _locationInfoService.Repository.QueryFirstAsync(x => x.LocationCode == stockInfo.LocationCode);
                    if (locationInfo == null)
                    {
                        _unitOfWorkManage.RollbackTran();
                        return WebResponseContent.Instance.Error("未找到空托盘库存对应的货位信息");
                    }
@@ -107,35 +128,78 @@
                        TaskType = TaskTypeEnum.OutEmpty.ObjToInt(),
                        WarehouseId = stockInfo.WarehouseId,
                        PalletType = stockInfo.PalletType
                    };
                    int beforeStatus = locationInfo.LocationStatus;
                    _unitOfWorkManage.BeginTran();
                    stockInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt();
                    locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                    int taskId = BaseDal.AddData(task);
                    int taskId = await BaseDal.AddDataAsync(task);
                    task.TaskId = taskId;
                    allTaskNums += task.TaskNum + ",";
                    stockInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt();
                    _stockService.StockInfoService.UpdateData(stockInfo);
                    int beforeStatus = locationInfo.LocationStatus;
                    locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                    _locationInfoService.UpdateData(locationInfo);
                    _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum);
                    _unitOfWorkManage.CommitTran();
                    _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(
                       locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum);
                }
                if (!string.IsNullOrWhiteSpace(supplierCode))
                {
                    var ledgerList = new List<Dt_PlasticContainerLedger>();
                    foreach (var stock in stockInfos)
                    {
                        ledgerList.Add(new Dt_PlasticContainerLedger
                        {
                            SupplyCode = supplierCode,
                            PalletCode = stock.PalletCode,
                            CreateDate = DateTime.Now,
                            Creater = App.User?.ToString()
                        });
                    }
                    _plasticContainerLedger.AddData(ledgerList);
                }
                _unitOfWorkManage.CommitTran();
                return content.OK("空托出库成功!");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        /// <summary>
        /// å®žæ—¶æœç´¢ä¾›åº”商编码(支持输入 r è‡ªåŠ¨åŒ¹é…ï¼Œä¸‹æ‹‰æ¡†ç”¨ï¼‰
        /// </summary>
        /// <param name="keyword">输入的关键词(如 r)</param>
        /// <returns>匹配的供应商编码列表</returns>
        public async Task<WebResponseContent> SearchSupplierCode(string keyword)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(keyword))
                    return WebResponseContent.Instance.OK("没有匹配到该供应商编号");
                var list = await _stockRepository.Db.Queryable<Dt_SupplierInfo>()
                    .Where(x => x.SupplierShortName.StartsWith(keyword))
                    .OrderBy(x => x.SupplierShortName)
                    .Take(20)
                    .Select(x => x.SupplierShortName)
                    .ToListAsync();
                return WebResponseContent.Instance.OK(data:list);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        /// <summary>
        /// å‡ºåº“任务数据处理
@@ -1126,127 +1190,196 @@
        /// <returns></returns>
        public async Task<WebResponseContent> TakeOutbound(List<StockViewDTO> stockViews, string outStation)
        {
            WebResponseContent content = new WebResponseContent();
            var content = new WebResponseContent();
            var errorMessages = new List<string>();
            try
            {
                var allFactoryAreas = stockViews.SelectMany(sv => sv.Details)
                                                .Select(x => x.FactoryArea)
                                                .GroupBy(x => x)
                                                .ToList();
                if (allFactoryAreas.Count >= 2)
                if (stockViews == null || !stockViews.Any())
                {
                    return content.Error($"请选择同一厂区区域的库存进行盘点,当前涉及{allFactoryAreas.Count}个不同的厂区");
                    content.Error("未获取到库存数据");
                    content.Data = new { total = 0, success = 0, fail = 0, failMsg = errorMessages };
                    return content;
                }
                List<int> ids = stockViews.Select(x => x.StockId).ToList();
                //获取库存
                List<Dt_StockInfo> stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => ids.Contains(x.Id)).Includes(x => x.Details).ToList();
                if (stockInfos.Count != stockViews.Count)
                var ids = stockViews.Select(x => x.StockId).ToList();
                var stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Where(x => ids.Contains(x.Id))
                    .Includes(x => x.Details)
                    .ToList();
                var locCodes = stockInfos.Select(x => x.LocationCode).ToList();
                var locationInfos = _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                    .Where(x => locCodes.Contains(x.LocationCode))
                    .ToList();
                var finalSuccessStocks = new List<Dt_StockInfo>();
                var finalSuccessLocations = new List<Dt_LocationInfo>();
                foreach (var stock in stockInfos)
                {
                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).ToList().Contains(x.PalletCode));
                    return content.Error($"未找到{stockViewDTO?.PalletCode}库存");
                }
                //获取货位
                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
                List<Dt_LocationInfo> locationInfos =_locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode)).ToList();
                if (stockInfos.Count != locationInfos.Count)
                {
                    string? locStr = locStrs.FirstOrDefault(x => !locationInfos.Select(x => x.LocationCode).ToList().Contains(x));
                    return content.Error($"未找到{locStr}货位数据");
                }
                Dt_TakeStockOrder takeStockOrder = new Dt_TakeStockOrder()
                {
                    WarehouseId = stockInfos.FirstOrDefault().WarehouseId,
                    TakeStockStatus = TakeStockStatusEnum.盘点中.ObjToInt(),
                    OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.PDCodeRule)),
                    AllPalletCode = string.Join(",", stockInfos.Select(item => item.PalletCode).Where(palletCode => !string.IsNullOrEmpty(palletCode))),
                    Remark = outStation
                };
                foreach (var item in stockInfos)
                {
                    if (item.Details.Count <= 0)
                    try
                    {
                        return content.Error($"未找到{item.PalletCode}库存明细数据");
                        if (stock.Details == null || !stock.Details.Any())
                        {
                            errorMessages.Add($"托盘【{stock.PalletCode}】:无库存明细");
                            continue;
                        }
                        var loc = locationInfos.FirstOrDefault(x => x.LocationCode == stock.LocationCode);
                        if (loc == null)
                        {
                            errorMessages.Add($"托盘【{stock.PalletCode}】:库位不存在");
                            continue;
                        }
                        if (loc.EnableStatus != EnableStatusEnum.Normal.ObjToInt() ||
                            loc.LocationStatus != LocationStatusEnum.InStock.ObjToInt() ||
                            stock.StockStatus != StockStatusEmun.入库完成.ObjToInt())
                        {
                            errorMessages.Add($"托盘【{stock.PalletCode}】:状态不允许出库");
                            continue;
                        }
                        finalSuccessStocks.Add(stock);
                        finalSuccessLocations.Add(loc);
                    }
                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
                    if (locationInfo == null || (locationInfo.EnableStatus == EnableStatusEnum.Disable.ObjToInt() || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt()) || locationInfo.LocationStatus != LocationStatusEnum.InStock.ObjToInt() || item.StockStatus != StockStatusEmun.入库完成.ObjToInt())
                    catch (Exception ex)
                    {
                        return content.Error($"{item.PalletCode}货位或库存状态不满足出库条件");
                    }
                        errorMessages.Add($"托盘【{stock.PalletCode}】异常:{ex.Message}");
                        continue;
                    }
                }
                List<Dt_Task> tasks = GetTasks(stockInfos, TaskTypeEnum.OutInventory,outStation);
                if (tasks == null || tasks.Count <= 0)
                if (finalSuccessStocks.Any())
                {
                    return content.Error($"生成任务失败");
                    var takeStockOrder = new Dt_TakeStockOrder
                    {
                        WarehouseId = finalSuccessStocks.First().WarehouseId,
                        TakeStockStatus = TakeStockStatusEnum.盘点中.ObjToInt(),
                        OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.PDCodeRule)),
                        AllPalletCode = string.Join(",", finalSuccessStocks.Select(x => x.PalletCode)),
                        Remark = outStation
                    };
                    var tasks = GetTasks(finalSuccessStocks, TaskTypeEnum.OutInventory, outStation);
                    if (tasks == null || tasks.Count <= 0)
                    {
                        errorMessages.Add("任务生成失败");
                    }
                    else
                    {
                        finalSuccessStocks.ForEach(x => x.StockStatus = StockStatusEmun.盘点出库锁定.ObjToInt());
                        finalSuccessLocations.ForEach(x => x.LocationStatus = LocationStatusEnum.Lock.ObjToInt());
                        tasks.ForEach(x => x.OrderNo = takeStockOrder.OrderNo);
                        _unitOfWorkManage.BeginTran();
                        _stockRepository.UpdateData(finalSuccessStocks);
                        _takeStockOrder.AddData(takeStockOrder);
                        BaseDal.AddData(tasks);
                        _locationInfoService.UpdateData(finalSuccessLocations);
                        _unitOfWorkManage.CommitTran();
                    }
                }
                stockInfos.ForEach(x =>
                {
                    x.StockStatus = StockStatusEmun.盘点出库锁定.ObjToInt();
                });
                tasks.ForEach(x =>
                {
                    x.OrderNo = takeStockOrder.OrderNo;
                });
                locationInfos.ForEach(x =>
                {
                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                });
                _unitOfWorkManage.BeginTran();
                //更新库存状态
                _stockRepository.UpdateData(stockInfos);
                _takeStockOrder.AddData(takeStockOrder);
                //新建任务
                BaseDal.AddData(tasks);
                _locationInfoService.UpdateData(locationInfos);
                _unitOfWorkManage.CommitTran();
                content.OK();
                //TaskModel esstask = new TaskModel()
                //{
                //    taskType = "carry",
                //    taskGroupCode = "",
                //    groupPriority = 0,
                //    tasks = new List<TasksType>()
                //};
                //foreach (var task in tasks)
                //{
                //    esstask.
                //       tasks.Add(new TasksType
                //       {
                //           taskCode = task.TaskNum.ToString(),
                //           taskPriority = 0,
                //           taskDescribe = new TaskDescribeType
                //           {
                //               containerCode = task.PalletCode,
                //               containerType = "CT_KUBOT_STANDARD",
                //               fromLocationCode = task.SourceAddress ?? "",
                //               toStationCode = "",
                //               toLocationCode = task.TargetAddress,
                //               deadline = 0,
                //               storageTag = ""
                //           }
                //       }
                //   );
                //}
                //var result = await _eSSApiService.CreateTaskAsync(esstask);
                //_logger.LogInformation("创建任务PalletOutboundTask è¿”回:  " + result);
                //if (result)
                //{
                //    return WebResponseContent.Instance.OK();
                //}
                //else
                //{
                //    return WebResponseContent.Instance.Error("下发机器人任务失败!");
                //}
                //content.OK();
                content.OK($"处理完成:成功【{finalSuccessStocks.Count}】条,失败【{errorMessages.Count}】条");
                content.Data = new
                {
                    total = stockInfos.Count,
                    success = finalSuccessStocks.Count,
                    fail = errorMessages.Count,
                    failMsg = errorMessages
                };
                return content;
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
                content.Error($"系统异常:{ex.Message}");
                content.Data = new
                {
                    total = stockViews?.Count ?? 0,
                    success = 0,
                    fail = 1,
                    failMsg = new List<string> { ex.Message }
                };
                return content;
            }
            return content;
        }
        public async Task<WebResponseContent> BatchOutboundByExcel(IFormFile file, string outStation)
        {
            WebResponseContent content = new WebResponseContent();
            List<StockViewDTO> successStockViews = new List<StockViewDTO>();
            List<string> noStockMaterialCodes = new List<string>();
            try
            {
                if (file == null || file.Length == 0)
                    return content.Error("上传失败:请选择需要导入的Excel文件");
                var ext = Path.GetExtension(file.FileName).ToLower();
                if (ext != ".xls" && ext != ".xlsx")
                    return content.Error("上传失败:仅支持 .xls å’Œ .xlsx æ ¼å¼çš„Excel文件,请重新上传");
                try
                {
                    using var stream = file.OpenReadStream();
                    var excelList = ReadExcel(stream);
                    if (!excelList.Any())
                        return content.Error("导入失败:Excel文件中未读取到任何有效数据");
                    foreach (var item in excelList)
                    {
                        var matchedStocks = _stockRepository.Db.Queryable<Dt_StockInfo>()
                            .Includes(x => x.Details)
                            .Where(x =>
                                x.Details.Any(d =>
                                    d.WarehouseCode == item.WarehouseCode
                                    && d.MaterielCode == item.MaterialCode
                                )
                            )
                            .ToList();
                        if (matchedStocks.Any())
                        {
                            successStockViews.AddRange(matchedStocks.Select(s => new StockViewDTO
                            {
                                StockId = s.Id
                            }));
                        }
                        else
                        {
                            noStockMaterialCodes.Add(item.MaterialCode);
                        }
                    }
                    // æ‰§è¡Œå‡ºåº“
                    if (successStockViews.Any())
                    {
                        var result = await TakeOutbound(successStockViews, outStation);
                        return result;
                    }
                    // æ— ä»»ä½•可出库数据
                    var msg = "未查询到任何可出库库存";
                    if (noStockMaterialCodes.Any())
                    {
                        msg += $",无库存料号:{string.Join("、", noStockMaterialCodes)}";
                    }
                    return content.Error(msg);
                }
                catch (Exception ex)
                {
                    return content.Error($"Excel解析失败:{ex.Message}");
                }
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return content.Error($"批量出库失败:系统异常,{ex.Message}");
            }
        }
        /// <summary>
        /// å•据生成方法
@@ -1584,6 +1717,38 @@
                }
            }
        }
        public static List<ExcelInventoryOutboundDto> ReadExcel(Stream stream)
        {
            var list = new List<ExcelInventoryOutboundDto>();
            using (var package = new ExcelPackage(stream))
            {
                var sheet = package.Workbook.Worksheets.FirstOrDefault();
                if (sheet == null) return list;
                // æ­£ç¡®èŽ·å–è¡Œæ•°
                int rowCount = sheet.Dimension?.End.Row ?? 0;
                // ç¬¬2行开始读
                for (int row = 2; row <= rowCount; row++)
                {
                    string location = sheet.Cells[row, 8].Value?.ToString()?.Trim() ?? "";
                    string materialCode = sheet.Cells[row, 2].Value?.ToString()?.Trim() ?? "";
                    if (string.IsNullOrEmpty(location) || string.IsNullOrEmpty(materialCode))
                        continue;
                    list.Add(new ExcelInventoryOutboundDto
                    {
                        WarehouseCode = location,
                        MaterialCode = materialCode
                    });
                }
            }
            return list;
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/PlasticContainerLedgerController.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core;
using WIDESEA_Core.BaseController;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
using WIDESEA_StockService;
namespace WIDESEA_WMSServer.Controllers.Stock
{
    [ApiController]
    [Route("api/[controller]")]
    [Authorize]
    public class PlasticContainerLedgerController : ApiBaseController<IPlasticContainerLedgerService,Dt_PlasticContainerLedger>
    {
        private readonly IPlasticContainerLedgerService _service;
        public PlasticContainerLedgerController(IPlasticContainerLedgerService service) : base(service)
        {
            _service = service;
        }
        [HttpPost, Route("GlueLineLedgerSummary"), AllowAnonymous]
        public WebResponseContent GlueLineLedgerSummary(string supplyCode)
        {
            return _service.GlueLineLedgerSummary(supplyCode);
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -1,4 +1,5 @@
using MailKit.Search;
using Magicodes.ExporterAndImporter.Excel.Utility;
using MailKit.Search;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@@ -57,11 +58,21 @@
        }
        [HttpPost, Route("PalletOutboundTask"), AllowAnonymous, MethodParamsValidate]
        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType)
        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType, string? supplierCode = null)
        {
            return  await Service.PalletOutboundTask(num, locationType);
            return  await Service.PalletOutboundTask(num, locationType, supplierCode);
        }
        /// <summary>
        /// ä¾›åº”商实时搜索
        /// </summary>
        /// <param name="keyword"></param>
        /// <returns></returns>
        [HttpGet("SearchSupplierCode")]
        public async Task<WebResponseContent> SearchSupplierCode(string keyword)
        {
            return await Service.SearchSupplierCode(keyword);
        }
        /// <summary>
        /// ç”Ÿæˆå‡ºåº“任务
@@ -151,5 +162,11 @@
        {
            return await Service.CrossAreaOutbound(stockViews,targetLocationType);
        }
        [HttpPost("BatchOutboundByExcel")]
        public async Task<WebResponseContent> BatchOutboundByExcel(IFormFile file, string outStation)
        {
            return await Service.BatchOutboundByExcel(file, outStation);
        }
    }
}