1
647556386
11 小时以前 2bdda9b2bedb8f084e364e92627f19e1938dceca
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/StockSelect.vue
@@ -1,9 +1,10 @@
<template>
  <div>
    <!-- åº“存指定弹窗 -->
    <vol-box
      v-model="showDetialBox"
      :lazy="true"
      width="60%"
      width="80%"
      :padding="15"
      title="指定库存"
    >
@@ -11,39 +12,27 @@
        <el-alert :closable="false" style="width: 100%">
          <el-row>
            <el-col :span="16">
              <span class="less-style">单据编号:{{ mainOrderNo }}</span>
              <el-divider direction="vertical"></el-divider>
              <span class="less-style">物料名称: {{ row.materielName }} </span>
              <el-divider direction="vertical"></el-divider>
              <span class="less-style">物料编号: {{ row.materielCode }} </span>
              <el-divider direction="vertical"></el-divider>
              <span class="less-style"
                >需求数量: {{ row.orderQuantity }}
              <span class="less-style">
                éœ€æ±‚数量: {{ requireQuantity }}
              </span>
              <el-divider direction="vertical"></el-divider>
              <span :class="selectionClass"
                >已选数量: {{ selectionSum }}
              <span :class="selectionClass">
                å·²é€‰æ•°é‡ï¼š {{ selectionSum }}
              </span>
            </el-col>
            <el-col :span="8">
              <el-link
                type="primary"
                size="small"
                style="float: right; height: 20px"
                @click="getData(false)"
                >{{ kcname }}</el-link
              >
              <el-link
                type="primary"
                size="small"
                style="float: right; height: 20px; margin-right: 10px"
                @click="getData"
                >刷新</el-link
              >
              <el-link
                type="primary"
                size="small"
                style="float: right; height: 20px; margin-right: 10px"
                @click="revokeAssign"
                >撤销分配</el-link
              >
            </el-col>
          </el-row>
@@ -58,8 +47,9 @@
          @row-click="handleRowClick"
          height="500px"
          @selection-change="handleSelectionChange"
          @select="selectRowChange"
          @select-all="selectAllChange"
        >
          >
          <el-table-column type="selection" width="55"> </el-table-column>
          <el-table-column
            label="序号"
@@ -76,138 +66,259 @@
            :width="item.width"
            align="center"
          >
            <template #default="scoped" v-if="item.type == 'icon'">
              <el-tooltip
                class="item"
                effect="dark"
                :content="item.title"
                placement="bottom"
                ><el-button
                  type="text"
                  @click="tableButtonClick(scoped.row, item)"
                  ><i :class="item.icon" style="font-size: 22px"></i></el-button
              ></el-tooltip>
            <template #default="scoped">
              <div v-if="item.type == 'icon'">
                <el-tooltip
                  class="item"
                  effect="dark"
                  :content="item.title"
                  placement="bottom"
                  ><el-button
                    type="text"
                    @click="tableButtonClick(scoped.row, item)"
                    ><i :class="item.icon" style="font-size: 22px"></i></el-button
                ></el-tooltip>
              </div>
              <div v-else-if="item.type == 'tag'">
                <el-tag size="small">
                  {{ getDictionary(scoped.row, item) }}
                </el-tag>
              </div>
              <div v-else>
                {{ scoped.row[item.prop] }}
              </div>
            </template>
          </el-table-column>
        </el-table>
      </div>
      <template #footer>
        <el-button type="primary" size="small" @click="outbound"
        <el-button type="primary" size="small" @click="openStationDialog"
          >直接出库</el-button
        >
        <el-button type="primary" size="small" @click="lockStock"
          >锁定库存</el-button
        >
        <el-button type="danger" size="small" @click="showDetialBox = false"
          >关闭</el-button
        >
      </template>
    </vol-box>
    <!-- ç«™å°é€‰æ‹©å¼¹çª— -->
    <el-dialog
      v-model="showStationDialog"
      title="选择出库站台"
      width="500px"
      :close-on-click-modal="false"
    >
      <div class="station-dialog-content">
        <el-form :model="stationForm" label-width="100px">
          <el-form-item label="选择站台:" required>
            <el-select
              v-model="stationForm.selectedPlatform"
              placeholder="请选择出库站台"
              style="width: 100%"
            >
              <el-option
                v-for="item in stations"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              ></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="出库数量:">
            <el-input
              v-model="selectionSum"
              readonly
              style="width: 100%"
            ></el-input>
          </el-form-item>
          <el-form-item label="物料信息:">
            <div style="font-size: 12px; color: #666; line-height: 1.5">
              <div>物料编号:{{ row.materielCode }}</div>
              <div>物料名称:{{ row.materielName }}</div>
            </div>
          </el-form-item>
        </el-form>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showStationDialog = false">取消</el-button>
          <el-button type="primary" @click="confirmOutbound">确认出库</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import VolBox from "@/components/basic/VolBox.vue";
import { fa } from "element-plus/es/locales.mjs";
import { stationManager } from "@/../src/uitils/stationManager";
export default {
  components: { VolBox },
  data() {
    return {
      row: null,
      kcname: "",
      mainOrderNo: "",
      pkcx: false,
      showDetialBox: false,
      showStationDialog: false,
      tableData: [],
      dictionaryList: null,
      tableColumns: [
        { prop: "materielCode", title: "物料编号", type: "string", width: 150 },
        { prop: "materielName", title: "物料名称", type: "string", width: 150 },
        { prop: "palletCode", title: "托盘编号", type: "string", width: 150 },
        { prop: "barcode", title: "条码", type: "string", width: 150 },
        { prop: "locationCode", title: "货位编号", type: "string", width: 180 },
        { prop: "useableQuantity", title: "可用数量", type: "string" },
        { prop: "supplyCode", title: "供应商", type: "string" },
        { prop: "batchNo", title: "批次号", type: "string" },
        {
          prop: "materielCode",
          title: "物料编号",
          type: "string",
          width: 150,
          prop: "stockStatus",
          title: "库存明细状态",
          type: "tag",
          width: 90,
          bindKey: "stockStatusEmun",
        },
        {
          prop: "materielName",
          title: "物料名称",
          type: "string",
          width: 150,
        },
        {
          prop: "palletCode",
          title: "托盘编号",
          type: "string",
          width: 150,
        },
        {
          prop: "locationCode",
          title: "货位编号",
          type: "string",
          width: 180,
        },
        {
          prop: "useableQuantity",
          title: "可用数量",
          type: "string",
        },
        { prop: "stockId", title: "库存主键", type: "string" },
        { prop: "orderDetailId", title: "单据明细主键", type: "string" },
      ],
      selection: [],
      selectionSum: 0,
      selectionClass: "less-style",
      originalQuantity: 0,
      stations: [
        { label: "站台2", value: "2-1" },
        { label: "站台3", value: "3-1" },
      ],
      stationForm: {
        selectedPlatform: "",
      },
      isProcessingSelect: false,
    };
  },
  computed: {
    requireQuantity() {
      if (!this.row) return 0;
      return this.row.orderQuantity - this.row.lockQuantity - (this.row.moveQty || 0);
    },
    isFull() {
      return this.selectionSum >= this.requireQuantity && this.requireQuantity > 0;
    }
  },
  methods: {
    open(row) {
      console.log(row);
    open(row, orderNo) {
      this.row = row;
      this.mainOrderNo = orderNo;
      this.showDetialBox = true;
      this.originalQuantity = this.row.lockQuantity;
      this.selectionSum = this.row.lockQuantity;
      this.originalQuantity = 0;
      this.selectionSum = 0;
      this.selection = [];
      this.getDictionaryData();
      this.getData();
      if (this.selectionSum == this.row.orderQuantity) {
        this.selectionClass = "equle-style";
      } else if (this.selectionSum < this.row.orderQuantity) {
        this.selectionClass = "less-style";
      } else {
        this.selectionClass = "more-style";
      }
      this.updateSelectionStyle();
    },
    lockStock() {
    getDictionaryData() {
      if (this.dictionaryList) {
        return;
      }
      var param = [];
      this.tableColumns.forEach((x) => {
        if (x.type == "tag" && x.bindKey != "") {
          param.push(x.bindKey);
        }
      });
      this.http
        .post(
          "api/OutboundOrderDetail/LockOutboundStock?id=" + this.row.id,
          this.selection,
          "数据处理中"
        )
        .post("api/Sys_Dictionary/GetVueDictionary", param, "查询中")
        .then((x) => {
          if (!x.status) return this.$message.error(x.message);
          this.$message.success("操作成功");
          this.showDetialBox = false;
          this.$emit("parentCall", ($vue) => {
            $vue.getData();
          });
          if (x.length > 0) {
            this.dictionaryList = x;
          }
        });
    },
    outbound() {
    getDictionary(row, column) {
      if (this.dictionaryList) {
        var item = this.dictionaryList.find((x) => x.dicNo == column.bindKey);
        if (item) {
          var dicItem = item.data.find((x) => x.key == row[column.prop]);
          if (dicItem) {
            return dicItem.value;
          } else {
            return row[column.prop];
          }
        }
      }
      return row[column.prop];
    },
    openStationDialog() {
      if (this.selection.length <= 0) {
        return this.$message.error("请勾选");
        return this.$message.error("请先勾选要出库的库存");
      }
      let url = this.pkcx
        ? "api/Task/GeneratePKOutboundTask?orderDetailId="
        : "api/Task/GenerateOutboundTask?orderDetailId=";
      this.http
        .post(url + this.row.id, this.selection, "数据处理中")
        .then((x) => {
          if (!x.status) return this.$message.error(x.message);
          this.$message.success("操作成功");
          this.showDetialBox = false;
          this.$emit("parentCall", ($vue) => {
            $vue.getData();
          });
        });
      const cachedStation = stationManager.getStation();
      this.stationForm.selectedPlatform = cachedStation || "";
      this.showStationDialog = true;
    },
    async confirmOutbound() {
      if (!this.stationForm.selectedPlatform) {
        return this.$message.error("请选择出库站台");
      }
      const requestParams = {
        detailIds: [this.row.id],
        outboundTargetLocation: this.stationForm.selectedPlatform,
        outboundQuantity: this.selectionSum,
        operator: this.getCurrentOperator(),
        orderNo: this.mainOrderNo,
        stockDetailIds: this.selection.map((item) => item.stockId),
      };
      try {
        const x = await this.http.post(
          "api/Outbound/ProcessPickingOutbound",
          requestParams,
          "数据处理中"
        );
        if (!x.status) {
          return this.$message.error(x.message);
        }
        this.$message.success("出库任务创建成功");
        this.showStationDialog = false;
        this.showDetialBox = false;
        this.$emit("parentCall", ($vue) => {
          $vue.getData();
        });
      } catch (error) {
        this.$message.error(error.message || "出库处理失败");
      }
    },
    getCurrentOperator() {
      try {
        const userInfo = JSON.parse(localStorage.getItem("userInfo") || "{}");
        if (userInfo.userName) {
          return userInfo.userName;
        }
        if (this.$store && this.$store.state.user) {
          return this.$store.state.user.userName;
        }
      } catch (error) {
        console.error("获取操作人信息失败:", error);
      }
      return "admin";
    },
    getData(a) {
      if (!a) this.pkcx = !this.pkcx;
      let url = this.pkcx
        ? "api/StockInfo/GetPKStockSelectViews?materielCode="
        ? "api/StockInfo/GetStockSelectViews?materielCode="
        : "api/StockInfo/GetStockSelectViews?materielCode=";
      this.kcname = this.pkcx ? "立库库存" : "平库库存";
      this.http
@@ -218,101 +329,111 @@
        )
        .then((x) => {
          this.tableData = x;
          this.clearSelection();
        });
    },
    revokeAssign() {
      console.log(this.row);
      this.http
        .post(
          "api/OutboundOrderDetail/RevokeLockOutboundStock?id=" + this.row.id,
          null,
          "数据处理中"
        )
        .then((x) => {
          if (!x.status) return this.$message.error(x.message);
          this.$message.success("操作成功");
          this.showDetialBox = false;
          this.$emit("parentCall", ($vue) => {
            $vue.getData();
          });
        });
    // ç‚¹å‡»è¡Œå‹¾é€‰ - æœ€ç»ˆä¿®å¤ç‰ˆ
    handleRowClick(row) {
      // åˆ¤æ–­å½“前行是否已选中
      const isSelected = this.selection.some(s => s.stockId === row.stockId);
      // å·²é€‰ä¸­ â†’ å…è®¸å–消勾选
      if (isSelected) {
        this.$refs.singleTable.toggleRowSelection(row);
        return;
      }
      // æœªé€‰ä¸­ + å·²æ»¡ â†’ ç¦æ­¢æ–°å¢žï¼ŒæŠ¥é”™
      if (this.isFull) {
        this.$message.error(`已达到需求数量 ${this.requireQuantity},无法再勾选库存!`);
        return;
      }
      // æ­£å¸¸å‹¾é€‰
      this.$refs.singleTable.toggleRowSelection(row);
    },
    handleSelectionChange(val) {
      this.selection = val;
      this.selectionSum =
        val.reduce(
          (accumulator, currentValue) =>
            accumulator + currentValue["useableQuantity"],
          0
        ) + this.originalQuantity;
      if (this.selectionSum == this.row.orderQuantity) {
        this.selectionClass = "equle-style";
      } else if (this.selectionSum < this.row.orderQuantity) {
        this.selectionClass = "less-style";
      } else {
        this.selectionClass = "more-style";
    // å‹¾é€‰æ¡†é€‰æ‹© - æœ€ç»ˆä¿®å¤ç‰ˆ
    selectRowChange(selection, row) {
      if (this.isProcessingSelect) return;
      const isAdding = selection.some(x => x.stockId === row.stockId);
      // æ–°å¢žå‹¾é€‰ + å·²æ»¡ â†’ æ‹¦æˆª
      if (isAdding && this.isFull) {
        this.isProcessingSelect = true;
        this.$message.error(`已达到需求数量 ${this.requireQuantity},无法再勾选!`);
        this.$nextTick(() => {
          this.$refs.singleTable.toggleRowSelection(row, false);
          this.isProcessingSelect = false;
        });
      }
    },
    toggleSelection(rows) {
      if (rows) {
        rows.forEach((row) => {
          this.$refs.singleTable.toggleRowSelection(row);
        });
      } else {
    // å…¨é€‰ - æœ€ç»ˆä¿®å¤ç‰ˆ
    selectAllChange(selection) {
      if (this.isProcessingSelect) return;
      const allTotal = this.tableData.reduce((sum, item) => sum + (item.useableQuantity || 0), 0);
      const need = this.requireQuantity;
      if (allTotal > need && selection.length > 0) {
        this.isProcessingSelect = true;
        this.$message.error(`全选数量超出需求,已清空选择!`);
        this.$refs.singleTable.clearSelection();
        this.selection = [];
        this.selectionSum = 0;
        this.updateSelectionStyle();
        this.$nextTick(() => {
          this.isProcessingSelect = false;
        });
      }
    },
    // é€‰æ‹©è®¡ç®—
    handleSelectionChange(val) {
      if (this.isProcessingSelect) return;
      let total = val.reduce((s, i) => s + (i.useableQuantity || 0), 0);
      const need = this.requireQuantity;
      this.selectionSum = total >= need ? need : total;
      this.selection = val;
      this.updateSelectionStyle();
    },
    updateSelectionStyle() {
      if (this.isFull) {
        this.selectionClass = "equle-style";
      } else {
        this.selectionClass = "less-style";
      }
    },
    clearSelection() {
      this.$refs.singleTable.clearSelection();
    },
    handleRowClick(row) {
      this.$refs.singleTable.toggleRowSelection(row);
      this.selection = [];
      this.selectionSum = 0;
      this.updateSelectionStyle();
    },
  },
};
</script>
<style scoped>
.less-style {
  color: black;
}
.equle-style {
  color: green;
}
.more-style {
  color: red;
}
</style>
<style>
.text-button:hover {
  background-color: #f0f9eb !important;
}
.el-table .warning-row {
  background: oldlace;
}
.box-table .el-table tbody tr:hover > td {
  background-color: #d8e0d4 !important;
  /* color: #ffffff; */
}
.box-table .el-table tbody tr.current-row > td {
  background-color: #f0f9eb !important;
  /* color: #ffffff; */
}
.el-table .success-row {
  background: #f0f9eb;
}
.box-table .el-table {
  border: 1px solid #ebeef5;
}
.box-head .el-alert__content {
  width: 100%;
}
.less-style { color: black; }
.equle-style { color: green; }
.more-style { color: red; }
.station-dialog-content { padding: 10px 0; }
.dialog-footer { display: flex; justify-content: flex-end; gap: 10px; }
.text-button:hover { background-color: #f0f9eb !important; }
.el-table .warning-row { background: oldlace; }
.box-table .el-table tbody tr:hover > td { background-color: #d8e0d4 !important; }
.box-table .el-table tbody tr.current-row > td { background-color: #f0f9eb !important; }
.el-table .success-row { background: #f0f9eb; }
.box-table .el-table { border: 1px solid #ebeef5; }
.box-head .el-alert__content { width: 100%; }
</style>