huangxiaoqiang
2025-12-25 090f169d69a69c901d4a48f2264274ef7f862a80
Merge branch 'htq20251215' of http://115.159.85.185:8098/r/ZhongRui/ALDbanyunxiangmu into htq20251215
已添加12个文件
已修改7个文件
2372 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/config/buttons.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/inbound/extend/OrderStockTake.vue 868 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/inbound/takeStockOrder.js 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/inbound/takeStockOrderDetail.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/stock/stockView.js 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/router/viewGird.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/inbound/takeStockOrder.vue 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/inbound/takeStockOrderDetail.vue 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Inbound/CompleteStockTakeDTO.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IInboundService/ITakeStockOrderDetailService.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IInboundService/ITakeStockOrderService.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/TakeStockOrderDetailService.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/TakeStockOrderService.cs 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Inbound/Dt_TakeStockOrderDetail.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_DictionaryService.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/TakeStockOrderController.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/TakeStockOrderDetailController.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/config/buttons.js
@@ -275,7 +275,24 @@
    type: 'primary',
    onClick: function () {
    }
}
},{
    name: "选 å®š åº“ å­˜ ç›˜ ç‚¹",
    icon: '',
    class: '',
    value: 'SelectStockTake',
    type: 'warning',
    onClick: function () {
    }
},
,{
    name: "单 æ® ç›˜ ç‚¹ æ“ ä½œ",
    icon: '',
    class: '',
    value: 'OrderStockTake',
    type: 'warning',
    onClick: function () {
    }
},
]
export default buttons
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/OrderStockTake.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,868 @@
<template>
  <div>
    <vol-box
      v-model="showStockTakeBox"
      :lazy="true"
      :width="isMobile ? '95%' : '70%'"
      :padding="24"
      title="库存盘点操作"
      class="custom-vol-box"
    >
      <div class="stock-take-container">
        <div class="receipt-info">
          <el-tag type="primary" size="small" class="receipt-tag">
            å½“前盘点单据号:<span class="receipt-no">{{ orderNo }}</span>
          </el-tag>
        </div>
        <!-- æ ¸å¿ƒè¾“入表单 -->
        <el-form
          :model="formData"
          ref="formRef"
          label-width="100px"
          class="stock-take-form"
          @submit.prevent
        >
          <!-- æ–™ç®±å·è¾“入框 -->
          <el-form-item
            label="料箱号:"
            name="boxNo"
            :rules="[
              {
                required: true,
                message: '请扫描或输入料箱号',
                trigger: 'blur',
              },
              { validator: validateBoxNo, trigger: 'blur' },
            ]"
            class="form-item"
          >
            <el-input
              ref="boxNoInputRef"
              v-model="formData.boxNo"
              placeholder="请使用扫码枪扫描料箱号,或手动输入"
              clearable
              @keydown.enter="debouncedHandleBoxNoScan"
              @blur="handleBoxNoBlur"
              :disabled="loading"
              class="custom-input"
              :class="{ 'has-value': formData.boxNo.trim() }"
            >
              <template #append>
                <el-button
                  icon="Search"
                  type="primary"
                  size="small"
                  @click="handleBoxNoScan"
                  :disabled="!formData.boxNo.trim() || loading"
                  class="input-btn"
                ></el-button>
              </template>
            </el-input>
          </el-form-item>
          <!-- æ¡ç è¾“入框 -->
          <el-form-item
            label="盘点条码:"
            name="barcode"
            :rules="[
              { required: true, message: '请扫描或输入条码', trigger: 'blur' },
              { validator: validateBarcode, trigger: 'blur' },
            ]"
            class="form-item"
          >
            <el-input
              ref="barcodeInputRef"
              v-model="formData.barcode"
              placeholder="请使用扫码枪扫描条码,或手动输入"
              clearable
              @keydown.enter="debouncedHandleBarcodeScan"
              :disabled="!formData.boxNo.trim() || loading"
              class="custom-input"
              :class="{ 'has-value': formData.barcode.trim() }"
            >
              <template #append>
                <el-button
                  icon="Search"
                  type="primary"
                  size="small"
                  @click="handleBarcodeScan"
                  :disabled="
                    !formData.boxNo.trim() ||
                    !formData.barcode.trim() ||
                    loading
                  "
                  class="input-btn"
                ></el-button>
              </template>
            </el-input>
          </el-form-item>
          <!-- åº“存数量框(只读) -->
          <el-form-item
            label="库存数量:"
            name="stockQuantity"
            class="form-item"
          >
            <el-input
              v-model="formData.stockQuantity"
              placeholder="扫描条码后自动填充"
              readonly
              class="custom-input custom-readonly-input"
              :class="{ 'has-value': formData.stockQuantity }"
            ></el-input>
          </el-form-item>
          <!-- å®žé™…盘点数量 -->
          <el-form-item
            label="实际盘点数量:"
            name="actualQuantity"
            :rules="[
              {
                required: true,
                message: '请输入实际盘点数量',
                trigger: 'blur',
              },
              { type: 'number', message: '请输入有效的数字', trigger: 'blur' },
              { validator: validateActualQuantity, trigger: 'blur' },
            ]"
            class="form-item"
          >
            <el-input
              v-model.number="formData.actualQuantity"
              placeholder="请输入实际盘点数量(大于0)"
              type="number"
              clearable
              @keydown.enter="handleStockTakeComplete"
              :disabled="!formData.stockQuantity || loading"
              class="custom-input"
              :class="{ 'has-value': formData.actualQuantity }"
            ></el-input>
          </el-form-item>
        </el-form>
        <!-- æ“ä½œæŒ‰é’®åŒºåŸŸ -->
        <div class="action-buttons">
          <el-button
            type="info"
            size="small"
            @click="handleBoxReturn"
            :disabled="!formData.boxNo.trim() || loading"
            class="return-btn"
          >
            <Return /> æ–™ç®±å›žåº“
          </el-button>
          <el-button
            type="primary"
            size="small"
            @click="handleStockTakeComplete"
            :disabled="
              loading ||
              !formData.boxNo.trim() ||
              !formData.barcode.trim() ||
              !formData.stockQuantity ||
              !formData.actualQuantity
            "
            class="complete-btn"
          >
            <Check /> ç›˜ç‚¹å®Œæˆ
          </el-button>
          <el-button
            type="text"
            size="small"
            @click="handleCancel"
            :disabled="loading"
            class="cancel-btn"
          >
            å–消
          </el-button>
        </div>
      </div>
    </vol-box>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick, watch, defineEmits, computed } from "vue";
import { ElMessage, ElTag, ElMessageBox } from "element-plus";
import { Search, Check, Return } from "@element-plus/icons-vue";
import VolBox from "@/components/basic/VolBox.vue";
import http from "@/api/http";
// å“åº”式变量 - å•据号
const orderNo = ref("");
// æš´éœ²äº‹ä»¶
const emit = defineEmits(["close", "refresh", "box-returned"]);
// å“åº”式数据
const showStockTakeBox = ref(false);
const formData = reactive({
  boxNo: "", // æ–™ç®±å·
  barcode: "", // ç›˜ç‚¹æ¡ç 
  stockQuantity: "", // åº“存数量(只读)
  actualQuantity: "", // å®žé™…盘点数量
});
const loading = ref(false);
const formRef = ref(null);
// æ¨¡æ¿å¼•用
const boxNoInputRef = ref(null);
const barcodeInputRef = ref(null);
// æ£€æµ‹æ˜¯å¦ä¸ºç§»åŠ¨ç«¯
const isMobile = computed(() => {
  return window.innerWidth < 768;
});
// ç»„件挂载时聚焦到料箱号输入框
onMounted(() => {
  nextTick(() => {
    boxNoInputRef.value?.focus();
  });
  // ç›‘听窗口大小变化
  window.addEventListener("resize", () => {});
});
// ç›‘听料箱号变化,清空后续相关字段
watch(
  () => formData.boxNo,
  (newVal) => {
    if (!newVal.trim()) {
      formData.barcode = "";
      formData.stockQuantity = "";
      formData.actualQuantity = "";
    }
  },
  { immediate: true }
);
// é˜²æŠ–函数
const debounce = (fn, delay = 100) => {
  let timer = null;
  return (...args) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
};
// æ‰“开弹窗并接收单据号
const open = (receiptNo) => {
  showStockTakeBox.value = true;
  orderNo.value = receiptNo;
  // é‡ç½®è¡¨å•
  formData.boxNo = "";
  formData.barcode = "";
  formData.stockQuantity = "";
  formData.actualQuantity = "";
  nextTick(() => {
    boxNoInputRef.value?.focus();
  });
};
// å…³é—­å¼¹çª—
const handleCancel = (e) => {
  e.stopPropagation();
  e.preventDefault();
  showStockTakeBox.value = false;
  emit("close");
  orderNo.value = "";
};
// æ–™ç®±å·éªŒè¯ï¼ˆä¼˜åŒ–:有值时仅验证长度,无值时触发必填)
const validateBoxNo = (rule, value, callback) => {
  // æœ‰å€¼æ—¶éªŒè¯é•¿åº¦
  if (value && value.trim().length < 3) {
    callback(new Error("料箱号长度不能少于3位"));
  }
  // æ— å€¼æ—¶ç”±required规则处理,这里不重复提示
  else {
    callback();
  }
};
// æ¡ç éªŒè¯ï¼ˆä¼˜åŒ–:有值时仅验证长度)
const validateBarcode = (rule, value, callback) => {
  if (value && value.trim().length < 6) {
    callback(new Error("条码长度不能少于6位"));
  } else {
    callback();
  }
};
// å®žé™…盘点数量验证(优化:有值时验证合法性)
const validateActualQuantity = (rule, value, callback) => {
  if (value === null || value === undefined) {
    callback(new Error("请输入实际盘点数量"));
  } else if (value <= 0) {
    callback(new Error("实际盘点数量必须大于0"));
  } else if (!Number.isInteger(value)) {
    callback(new Error("实际盘点数量必须是整数"));
  } else {
    callback();
  }
};
// æ–™ç®±å·å¤±ç„¦å¤„理
const handleBoxNoBlur = () => {
  if (formData.boxNo.trim() && boxNoInputRef.value?.input) {
    boxNoInputRef.value.input.select();
  }
};
// æ–™ç®±å·æ‰«æéªŒè¯ï¼ˆåŽç«¯æŽ¥å£ï¼‰
const validateBoxNoApi = async (boxNo) => {
  try {
    const res = await http.post(
      `/api/TakeStockOrder/ValidateBoxNo?orderNo=${encodeURIComponent(
        orderNo.value
      )}&boxNo=${encodeURIComponent(boxNo)}`,
      "验证料箱号中..."
    );
    if (!res.status) {
      throw new Error(res.message || "料箱号验证失败");
    }
    return res.data;
  } catch (error) {
    throw error;
  }
};
// æ–™ç®±å·æ‰«æå¤„理
const handleBoxNoScan = async () => {
  if (!formRef.value) return;
  // æ‰‹åŠ¨è§¦å‘éªŒè¯ï¼ˆä»…éªŒè¯å½“å‰å­—æ®µï¼‰
  const valid = await formRef.value.validateField("boxNo");
  if (valid !== true) return;
  const boxNo = formData.boxNo.trim();
  if (!boxNo) return;
  try {
    loading.value = true;
    await validateBoxNoApi(boxNo);
    ElMessage.success(`料箱号 ${boxNo} éªŒè¯é€šè¿‡`);
    nextTick(() => {
      barcodeInputRef.value?.focus();
    });
  } catch (error) {
    ElMessage.error(error.message);
    nextTick(() => {
      if (boxNoInputRef.value?.input) {
        boxNoInputRef.value.input.select();
      }
    });
  } finally {
    loading.value = false;
  }
};
// æ¡ç æ‰«æéªŒè¯ï¼ˆåŽç«¯æŽ¥å£ï¼‰
const validateBarcodeApi = async (boxNo, barcode) => {
  try {
    const res = await http.post(
      `/api/TakeStockOrder/ValidateBarcode?boxNo=${encodeURIComponent(
        boxNo
      )}&barcode=${encodeURIComponent(barcode)}`,
      "验证条码中..."
    );
    if (!res.status) {
      throw new Error(res.message || "条码验证失败");
    }
    if (
      res.data?.stockQuantity === undefined ||
      res.data?.stockQuantity === null
    ) {
      throw new Error("未查询到该条码的库存数量");
    }
    return res.data;
  } catch (error) {
    throw error;
  }
};
// æ¡ç æ‰«æå¤„理
const handleBarcodeScan = async () => {
  if (!formRef.value) return;
  const valid = await formRef.value.validateField("barcode");
  if (valid !== true) return;
  const boxNo = formData.boxNo.trim();
  const barcode = formData.barcode.trim();
  if (!boxNo || !barcode) return;
  try {
    loading.value = true;
    const result = await validateBarcodeApi(boxNo, barcode);
    formData.stockQuantity = result.stockQuantity;
    ElMessage.success(
      `条码 ${barcode} éªŒè¯é€šè¿‡ï¼Œåº“存数量:${result.stockQuantity}`
    );
    nextTick(() => {
      const actualQuantityInput = document.querySelector(
        ".form-item:last-child .el-input__inner"
      );
      actualQuantityInput?.focus();
    });
  } catch (error) {
    ElMessage.error(error.message);
    nextTick(() => {
      if (barcodeInputRef.value?.input) {
        barcodeInputRef.value.input.select();
      }
    });
  } finally {
    loading.value = false;
  }
};
// ç›˜ç‚¹å®Œæˆæäº¤
const handleStockTakeComplete = async () => {
  if (!formRef.value) return;
  // å…¨è¡¨å•验证
  const valid = await formRef.value.validate();
  if (!valid) return;
  const { boxNo, barcode, actualQuantity, stockQuantity } = formData;
  const receiptNo = orderNo.value;
  try {
    loading.value = true;
    const res = await http.post(
      "/api/TakeStockOrder/CompleteStockTake",
      {
        orderNo: receiptNo,
        boxNo,
        barcode,
        actualQuantity,
        stockQuantity,
      },
      {
        loadingText: "提交盘点数据中...",
      }
    );
    if (res.status) {
      ElMessage.success("盘点完成,提交成功!");
      formData.barcode = "";
      formData.stockQuantity = "";
      formData.actualQuantity = "";
      nextTick(() => {
        barcodeInputRef.value?.focus();
      });
      emit("refresh");
    } else {
      throw new Error(res.message || "盘点提交失败");
    }
  } catch (error) {
    ElMessage.error(error.message || "网络异常,提交失败");
  } finally {
    loading.value = false;
  }
};
// æ–™ç®±å›žåº“功能
const handleBoxReturn = async () => {
  const boxNo = formData.boxNo.trim();
  if (!boxNo) {
    ElMessage.warning("请先输入或扫描料箱号");
    return;
  }
  try {
    await ElMessageBox.confirm(`确定将料箱【${boxNo}】回库吗?`, "回库确认", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "info",
      center: true,
      confirmButtonClass: "el-button--primary",
      cancelButtonClass: "el-button--text",
    });
    loading.value = true;
    const res = await http.post(
      `/api/TakeStockOrder/ReturnBox?boxNo=${encodeURIComponent(boxNo)}&orderNo=${encodeURIComponent(
        orderNo.value
      )}`,
      "料箱回库中..."
    );
    if (res.status) {
      ElMessage.success(`料箱【${boxNo}】回库成功!`);
      formData.boxNo = "";
      formData.barcode = "";
      formData.stockQuantity = "";
      formData.actualQuantity = "";
      nextTick(() => {
        boxNoInputRef.value?.focus();
      });
      emit("box-returned", boxNo);
    } else {
      throw new Error(res.message || "料箱回库失败");
    }
  } catch (error) {
    if (error.name !== "Cancel") {
      ElMessage.error(error.message);
    }
  } finally {
    loading.value = false;
  }
};
// å¸¦é˜²æŠ–的扫描处理
const debouncedHandleBoxNoScan = debounce(async (e) => {
  e.stopPropagation();
  e.preventDefault();
  await handleBoxNoScan();
}, 100);
const debouncedHandleBarcodeScan = debounce(async (e) => {
  e.stopPropagation();
  e.preventDefault();
  await handleBarcodeScan();
}, 100);
// æš´éœ²æ–¹æ³•
defineExpose({
  open,
});
</script>
<style scoped>
/* ä¸»å®¹å™¨æ ·å¼ - ä¼˜åŒ–间距和响应式 */
.stock-take-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 8px 0;
}
/* å•据信息样式 - ä¼˜åŒ–视觉层级 */
.receipt-info {
  margin-bottom: 4px;
}
.receipt-tag {
  padding: 8px 16px;
  font-size: 14px;
  background-color: #e8f4f8 !important;
  border-color: #409eff !important;
  color: #1989fa !important;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
}
.receipt-no {
  font-weight: 600;
  color: #1989fa;
  margin-left: 6px;
}
/* è¡¨å•样式 - ä¼˜åŒ–阴影和内边距 */
.stock-take-form {
  width: 100%;
  background-color: #ffffff;
  padding: 20px;
  border-radius: 12px;
  box-shadow: 0 2px 12px rgba(64, 158, 255, 0.06);
  border: 1px solid #f0f8fb;
}
.form-item {
  margin-bottom: 16px;
}
/* è¾“入框核心样式 - ä¼˜åŒ–状态显示 */
.custom-input {
  width: 100%;
  position: relative;
}
/* æœ‰å€¼æ—¶éšè—å ä½ç¬¦ + ä¼˜åŒ–边框 */
.custom-input.has-value :deep(.el-input__inner) {
  --el-input-placeholder-color: transparent; /* éšè—å ä½ç¬¦ */
  border-color: #8cc5ff; /* æµ…蓝边框,区分无值状态 */
  background-color: #ffffff;
}
.custom-input :deep(.el-input__inner) {
  border-radius: 8px;
  border-color: #e5f0fa;
  transition: all 0.2s ease;
  height: 42px;
  line-height: 42px;
  font-size: 14px;
  background-color: #f8fbff;
  padding: 0 12px;
}
/* èšç„¦æ ·å¼ä¼˜åŒ– */
.custom-input :deep(.el-input__inner:focus) {
  border-color: #409eff;
  box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.1);
  background-color: #ffffff;
  outline: none;
}
/* æ¸…除按钮样式优化 */
.custom-input :deep(.el-input__clear) {
  color: #91c9f7;
  width: 18px;
  height: 18px;
}
.custom-input :deep(.el-input__clear:hover) {
  color: #409eff;
}
/* åªè¯»è¾“入框样式 - å¢žå¼ºåŒºåˆ†åº¦ */
.custom-readonly-input :deep(.el-input__inner) {
  background-color: #f0f8fb;
  color: #1989fa;
  font-weight: 500;
  cursor: default;
  border-color: #d1e7fd;
  padding-right: 12px;
}
/* è¾“入框按钮样式 - ä¼˜åŒ–尺寸和hover效果 */
.input-btn {
  border-radius: 0 8px 8px 0 !important;
  height: 42px;
  width: 48px;
  background-color: #409eff;
  border-color: #409eff;
  transition: all 0.2s ease;
}
.input-btn:hover {
  background-color: #1989fa !important;
  border-color: #1989fa !important;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
  transform: translateY(-1px);
}
/* è¡¨å•验证错误样式 - å…³é”®ï¼šæœ‰å€¼æ—¶ä¸æ˜¾ç¤ºé”™è¯¯è¾¹æ¡† */
.form-item :deep(.el-form-item__error) {
  font-size: 12px;
  color: #f56c6c;
  margin-top: 4px;
  /* ä»…在输入框为空且验证失败时显示 */
  display: none;
}
/* åªæœ‰è¾“入框为空 + éªŒè¯å¤±è´¥æ—¶æ˜¾ç¤ºé”™è¯¯æç¤º */
.form-item:deep(.el-form-item--error) .custom-input:not(.has-value) + .el-form-item__error {
  display: block;
}
/* æœ‰å€¼æ—¶å³ä½¿éªŒè¯å¤±è´¥ï¼Œä¹Ÿä¸æ˜¾ç¤ºé”™è¯¯è¾¹æ¡† */
.custom-input.has-value :deep(.el-input__inner.el-input__inner--error) {
  border-color: #8cc5ff;
  box-shadow: none;
}
/* æ“ä½œæŒ‰é’®åŒºåŸŸ - ä¼˜åŒ–间距和响应式 */
.action-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 20px;
  flex-wrap: wrap;
}
/* å›žåº“按钮样式 - ä¼˜åŒ–交互 */
.return-btn {
  border-radius: 8px;
  padding: 9px 20px;
  font-size: 14px;
  font-weight: 500;
  background-color: #e8f4f8;
  border-color: #409eff;
  color: #1989fa;
  transition: all 0.2s ease;
}
.return-btn:hover {
  background-color: #d1e7fd !important;
  border-color: #1989fa !important;
  color: #1989fa !important;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.15);
}
.return-btn:disabled {
  background-color: #f0f8fb !important;
  border-color: #b3d8ff !important;
  color: #91c9f7 !important;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
/* ç›˜ç‚¹å®ŒæˆæŒ‰é’®æ ·å¼ - ä¼˜åŒ–交互 */
.complete-btn {
  border-radius: 8px;
  padding: 9px 20px;
  font-size: 14px;
  font-weight: 500;
  background-color: #409eff;
  border-color: #409eff;
  transition: all 0.2s ease;
}
.complete-btn:hover {
  background-color: #1989fa !important;
  border-color: #1989fa !important;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
.complete-btn:disabled {
  background-color: #a0cfff !important;
  border-color: #a0cfff !important;
  color: #ffffff !important;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}
/* å–消按钮样式 - ä¼˜åŒ–点击区域 */
.cancel-btn {
  color: #666666;
  font-size: 14px;
  transition: all 0.2s ease;
  padding: 9px 20px;
  border-radius: 8px;
  min-width: 80px;
}
.cancel-btn:hover {
  color: #1989fa !important;
  background-color: #f0f8fb !important;
}
/* å¼¹çª—整体样式 - ä¼˜åŒ–圆角和阴影 */
.custom-vol-box {
  border-radius: 16px !important;
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(64, 158, 255, 0.12);
}
.custom-vol-box :deep(.el-dialog__header) {
  background-color: #e8f4f8;
  border-bottom: 1px solid #d1e7fd;
  padding: 16px 24px;
}
.custom-vol-box :deep(.el-dialog__title) {
  color: #1989fa;
  font-size: 18px;
  font-weight: 600;
}
.custom-vol-box :deep(.el-dialog__body) {
  padding: 16px 24px;
}
/* å“åº”式适配 - å°å±å¹•优化 */
@media (max-width: 768px) {
  .stock-take-form {
    padding: 16px;
  }
  .form-item {
    margin-bottom: 12px;
  }
  .action-buttons {
    justify-content: center;
  }
  .custom-vol-box :deep(.el-dialog__header) {
    padding: 12px 16px;
  }
  .custom-vol-box :deep(.el-dialog__body) {
    padding: 12px 16px;
  }
}
</style>
<style>
/* å…¨å±€è¡¥å……样式 - ç»Ÿä¸€ä¸»é¢˜ */
.el-tag--primary {
  --el-tag-bg-color: #e8f4f8;
  --el-tag-border-color: #409eff;
  --el-tag-text-color: #1989fa;
}
/* å ä½ç¬¦æ ·å¼ - ç»Ÿä¸€é¢œè‰² */
.el-input__inner::-webkit-input-placeholder {
  color: #b3d8ff;
  font-size: 13px;
}
.el-input__inner::-moz-placeholder {
  color: #b3d8ff;
  font-size: 13px;
}
.el-input__inner:-ms-input-placeholder {
  color: #b3d8ff;
  font-size: 13px;
}
.el-input__inner::placeholder {
  color: #b3d8ff;
  font-size: 13px;
}
/* ä¿¡æ¯æŒ‰é’®å…¨å±€æ ·å¼ */
.el-button--info {
  --el-button-bg-color: #e8f4f8;
  --el-button-border-color: #409eff;
  --el-button-text-color: #1989fa;
  --el-button-hover-bg-color: #d1e7fd;
  --el-button-hover-border-color: #1989fa;
  --el-button-hover-text-color: #1989fa;
}
/* ç¡®è®¤å¼¹çª—样式优化 */
.el-message-box {
  border-radius: 12px !important;
  box-shadow: 0 4px 20px rgba(64, 158, 255, 0.15);
}
.el-message-box__header {
  border-bottom: 1px solid #f0f8fb;
  padding: 12px 20px;
}
.el-message-box__title {
  color: #1989fa;
  font-size: 16px;
  font-weight: 600;
}
.el-message-box__content {
  color: #666666;
  font-size: 14px;
  padding: 16px 20px;
}
.el-message-box__btns {
  padding: 12px 20px;
  border-top: 1px solid #f0f8fb;
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/takeStockOrder.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,83 @@
// è‡ªå®šä¹‰æ‰©å±•业务代码
import gridBody from "./extend/OrderStockTake.vue";
let extension = {
  components: {
    // æŸ¥è¯¢ç•Œé¢æ‰©å±•组件:将自定义弹窗注册为 gridBody ç»„ä»¶
    gridHeader: '',
    gridBody: gridBody, // å¯¹åº”你的盘点弹窗组件
    gridFooter: '',
    // æ–°å»º/编辑弹出框扩展组件(此处不用,留空)
    modelHeader: '',
    modelBody: '',
    modelFooter: ''
  },
  tableAction: '', // æ— éœ€æŒ‡å®šè¡¨åï¼ˆé»˜è®¤é€‚用当前页面表格)
  buttons: { view: [], box: [], detail: [] }, // æ‰©å±•按钮(如需额外添加可在此配置)
  methods: {
    onInit() {
      // æ‰¾åˆ° value ä¸º "OrderStockTake" çš„æŒ‰é’®ï¼ˆéœ€åœ¨æ¡†æž¶ä¸­æå‰é…ç½®è¯¥æŒ‰é’®ï¼‰
      let OrderStockTakeBtn = this.buttons.find(x => x.value === 'OrderStockTake');
      if (OrderStockTakeBtn) {
        // é‡å†™æŒ‰é’®ç‚¹å‡»äº‹ä»¶
        OrderStockTakeBtn.onClick = function () {
          // 1. èŽ·å–è¡¨æ ¼é€‰ä¸­è¡Œæ•°æ®
          let rows = this.$refs.table.getSelected();
          if (rows.length === 0) return this.$error("请选择一条盘点单据数据!");
          if (rows.length > 1) return this.$error("只能选择一条盘点单据数据!");
          const selectedReceiptNo = rows[0].orderNo;
          if (!selectedReceiptNo) return this.$error("选中的单据缺少有效的单据号!");
          // 3. è°ƒç”¨è‡ªå®šä¹‰å¼¹çª—çš„ open æ–¹æ³•,并传递单据号(核心:给弹窗传参)
          this.$refs.gridBody.open(selectedReceiptNo);
        };
      }
      // 4. ç›‘听自定义弹窗的事件(同步主页面数据)
      this.$nextTick(() => {
        const stockTakeComp = this.$refs.gridBody;
        if (stockTakeComp) {
          // ç›‘听弹窗的 "refresh" äº‹ä»¶ï¼ˆç›˜ç‚¹å®ŒæˆåŽåˆ·æ–°ä¸»é¡µé¢ï¼‰
          stockTakeComp.$on('refresh', () => {
            this.$refs.table.reload(); // åˆ·æ–°è¡¨æ ¼æ•°æ®
          });
          // ç›‘听弹窗的 "box-returned" äº‹ä»¶ï¼ˆæ–™ç®±å›žåº“后可按需处理)
          stockTakeComp.$on('box-returned', (boxNo) => {
            this.$success(`料箱【${boxNo}】回库成功,表格将刷新!`);
            this.$refs.table.reload(); // å›žåº“后也刷新表格(可选,根据业务需求)
          });
        }
      });
    },
    onInited() {
      // æ¡†æž¶åˆå§‹åŒ–完成后执行(如需配置明细表可在此操作)
    },
    searchBefore(param) {
      // æŸ¥è¯¢å‰æ‹¦æˆªï¼šå¯æ·»åŠ é¢å¤–æŸ¥è¯¢æ¡ä»¶ï¼ˆå¦‚æƒé™è¿‡æ»¤ï¼‰
      return true;
    },
    searchAfter(result) {
      // æŸ¥è¯¢åŽæ•°æ®å¤„理(如格式化日期、状态显示)
      return true;
    },
    addBefore(formData) {
      // æ–°å»ºä¿å­˜å‰å¤„理(此处不用)
      return true;
    },
    updateBefore(formData) {
      // ç¼–辑保存前处理(此处不用)
      return true;
    },
    rowClick({ row, column, event }) {
      // å•击行选中当前行(优化用户体验)
      this.$refs.table.$refs.table.toggleRowSelection(row);
    },
    modelOpenAfter(row) {
      // æ–°å»º/编辑弹窗打开后处理(此处不用)
    }
  }
};
export default extension;
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/takeStockOrderDetail.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
//此js文件是用来自定义扩展业务代码,可以扩展一些自定义页面或者重新配置生成的代码
let extension = {
    components: {
      //查询界面扩展组件
      gridHeader: '',
      gridBody: '',
      gridFooter: '',
      //新建、编辑弹出框扩展组件
      modelHeader: '',
      modelBody: '',
      modelFooter: ''
    },
    tableAction: '', //指定某张表的权限(这里填写表名,默认不用填写)
    buttons: { view: [], box: [], detail: [] }, //扩展的按钮
    methods: {
       //下面这些方法可以保留也可以删除
      onInit() {
      },
      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/stockView.js
@@ -1,4 +1,5 @@
import { createVNode, render, h, reactive } from 'vue';
import { ElDialog, ElForm, ElFormItem, ElSelect, ElOption, ElButton, ElMessage, ElLoading } from 'element-plus';
let extension = {
  components: {
    //查询界面扩展组件
@@ -156,6 +157,175 @@
          }
        }
      })
      let SelectTake = this.buttons.find(x => x.value == 'SelectStockTake');
if (SelectTake) {
  // æ”¹ä¸ºç®­å¤´å‡½æ•°ç¡®ä¿this指向Vue组件实例
  SelectTake.onClick = async () => {
    // èŽ·å–é€‰ä¸­æ•°æ®ï¼ˆä¸ŽåŽŸé€»è¾‘ä¸€è‡´ï¼Œç¡®ä¿æ˜¯æ•°ç»„æ ¼å¼ï¼‰
    let stockViews = this.$refs.table.getSelected();
    // æ•°æ®æ ¡éªŒï¼šè‡³å°‘选择一条数据
    if (stockViews.length === 0) return ElMessage.error("请选择需要操作的数据!");
    // ç«™å°é€‰é¡¹ï¼ˆå¯æ ¹æ®å®žé™…业务调整选项值)
    const stationOptions = [
      { label: "站台2", value: "2-1" },
      { label: "站台3", value: "3-1" },
      { label: "站台4", value: "4-1" },
    ];
    // åˆ›å»ºå¼¹çª—挂载节点
    const mountNode = document.createElement("div");
    document.body.appendChild(mountNode);
    // è¡¨å•数据(绑定选中的站台)
    const formData = reactive({
      outStation: stationOptions[0].value, // é»˜è®¤é€‰ä¸­ç¬¬ä¸€ä¸ªç«™å°
    });
    // åˆ›å»ºå¼¹çª—VNode
    const vnode = createVNode(
      ElDialog,
      {
        title: "库存操作 - é€‰æ‹©å‡ºåº“站台",
        width: "500px",
        modelValue: true,
        appendToBody: true,
        "onUpdate:modelValue": (isVisible) => {
          if (!isVisible) {
            render(null, mountNode);
            document.body.removeChild(mountNode);
          }
        },
        style: {
          padding: "20px 0",
          borderRadius: "8px",
        },
      },
      {
        default: () =>
          h(
            ElForm,
            {
              model: formData,
              rules: {
                outStation: [
                  { required: true, message: "请选择出库站台", trigger: "change" },
                ],
              },
              ref: "stockTakeForm", // è¡¨å•ref标识
              labelWidth: "100px",
              style: {
                padding: "0 30px",
              },
            },
            [
              // ç«™å°é€‰æ‹©ä¸‹æ‹‰æ¡†
              h(ElFormItem, {
                label: "出库站台",
                prop: "outStation",
                style: { marginBottom: "24px" },
              }, [
                h(ElSelect, {
                  placeholder: "请选择出库站台",
                  modelValue: formData.outStation,
                  "onUpdate:modelValue": (val) => {
                    formData.outStation = val;
                  },
                  style: {
                    width: "100%",
                    height: "40px",
                    borderRadius: "4px",
                    borderColor: "#dcdfe6",
                  },
                }, stationOptions.map((station) =>
                  h(ElOption, { label: station.label, value: station.value })
                )),
              ]),
              // åº•部按钮区域
              h("div", {
                style: {
                  textAlign: "right",
                  marginTop: "8px",
                  paddingRight: "4px",
                },
              }, [
                // å–消按钮
                h(ElButton, {
                  type: "text",
                  onClick: () => {
                    render(null, mountNode);
                    document.body.removeChild(mountNode);
                    ElMessage.info("取消库存操作");
                  },
                  style: { marginRight: "8px", color: "#606266" },
                }, "取消"),
                // ç¡®å®šæŒ‰é’®
                h(ElButton, {
                  type: "primary",
                  onClick: async () => {
                    // ç¡®ä¿è¡¨å•ref已挂载
                    await this.$nextTick();
                    const formRef = vnode.component.refs.stockTakeForm;
                    if (!formRef) {
                      ElMessage.error("表单初始化失败,请重试");
                      return;
                    }
                    // è¡¨å•验证
                    try {
                      await formRef.validate();
                    } catch (err) {
                      return;
                    }
                    // æž„造请求参数:
                    // 1. è¯·æ±‚体:直接传递stockViews数组(对应后端List<StockViewDTO>)
                    // 2. outStation:通过查询参数传递(因后端未指定[FromBody]接收该参数)
                    const requestBody = stockViews; // ç›´æŽ¥ä¼ é€’选中数组(确保数组内对象结构与StockViewDTO一致)
                    const outStation = formData.outStation;
                    // æŽ¥å£è¯·æ±‚:Post方式,请求体传stockViews数组,outStation拼到URL查询参数
                    try {
                      // æ‹¼æŽ¥æŸ¥è¯¢å‚数(如果outStation有特殊字符,可使用encodeURIComponent处理)
                      const url = `api/Task/TakeOutbound?outStation=${encodeURIComponent(outStation)}`;
                      const x = await this.http.post(
                        url, // å¸¦æŸ¥è¯¢å‚æ•°çš„URL
                        requestBody, // è¯·æ±‚体:stockViews数组
                        "数据处理中"
                      );
                      if (!x.status) {
                        ElMessage.error(x.message || "库存操作失败");
                        return;
                      }
                      ElMessage.success("操作成功");
                      this.refresh(); // åŽŸåˆ·æ–°é€»è¾‘ä¿ç•™
                    } catch (error) {
                      console.error("库存接口请求失败:", error);
                      ElMessage.error("请求失败,请稍后重试");
                    } finally {
                      // æ— è®ºæˆåŠŸå¤±è´¥ï¼Œå…³é—­å¼¹çª—
                      render(null, mountNode);
                      document.body.removeChild(mountNode);
                    }
                  },
                  style: { borderRadius: "4px", padding: "8px 20px" },
                }, "确定操作"),
              ]),
            ]),
      }
    );
    // ç»‘定app上下文(确保弹窗内组件正常工作)
    vnode.appContext = this.$.appContext;
    render(vnode, mountNode);
  };
}
    },
    onInited() {
      //框架初始化配置后
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/router/viewGird.js
@@ -239,6 +239,14 @@
    component: () => import('@/views/system/Log.vue'),
    meta: {
    }
  }, {
    path: '/takeStockOrder',
    name: 'takeStockOrder',
    component: () => import('@/views/inbound/takeStockOrder.vue')
  }, {
    path: '/takeStockOrderDetail',
    name: 'takeStockOrderDetail',
    component: () => import('@/views/inbound/takeStockOrderDetail.vue')
  }
]
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/takeStockOrder.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,291 @@
<template>
    <!-- <div>
      <assemble-dist></assemble-dist>
    </div> -->
    <view-grid
      ref="grid"
      :columns="columns"
      :detail="detail"
      :editFormFields="editFormFields"
      :editFormOptions="editFormOptions"
      :searchFormFields="searchFormFields"
      :searchFormOptions="searchFormOptions"
      :table="table"
      :extend="extend"
    >
    </view-grid>
  </template>
<script>
  import extend from "@/extension/inbound/takeStockOrder.js";
  import { ref, defineComponent } from "vue";
  // import AssembleDistVue from '../charts/AssembleDist.vue';
  export default defineComponent({
    // components: {
    //   'assemble-dist': AssembleDistVue,
    // },
    setup() {
      //使用组件AssembleDist
      const table = ref({
        key: "id",
        footer: "Foots",
        cnName: "盘点单",
        name: "takeStockOrder",
        url: "/TakeStockOrder/",
        sortName: "id",
      });
      const searchFormFields = ref({
        orderNo:""
      });
      const searchFormOptions = ref([
        [
        { title: "单据编号", field: "orderNo", type: "like" },
        ]
      ]);
      const editFormFields = ref({
        orderNo:"",
        warehouseId: "",
        remark:""
      });
      const editFormOptions = ref([
      [
        {
          field: "orderNo",
          title: "单据编号",
          type: "string",
          readonly: "true",
        },
        {
          title: "仓库",
          field: "warehouseId",
          type: "select",
          dataKey: "warehouses",
          data: [],
          required: true,
        },
        {
          field: "remark",
          title: "备注",
          type: "string",
        }
      ]
      ]);
      const columns = ref([
        {
          field: "id",
          title: "Id",
          type: "int",
          width: 90,
          hidden: true,
          readonly: true,
          require: true,
          align: "left",
        },
        {
          field: "orderNo",
          title: "盘点单号",
          type: "string",
          width: 150,
          align: "left",
        },
        {
          field: "takeStockStatus",
          title: "盘点状态",
          type: "string",
          width: 120,
          align: "left",
          bind: { key: "takeStockStatusEnum", data: [] },
        },
        {
          field: "allPalletCode",
          title: "单据盘点托盘",
          type: "string",
          width: 600,
          align: "left",
        },
        {
          field: "creater",
          title: "创建人",
          type: "string",
          width: 90,
          align: "left",
        },
        {
          field: "createDate",
          title: "创建时间",
          type: "datetime",
          width: 160,
          align: "left",
        },
        {
          field: "modifier",
          title: "修改人",
          type: "string",
          width: 100,
          align: "left",
        },
        {
          field: "modifyDate",
          title: "修改时间",
          type: "datetime",
          width: 160,
          align: "left",
        },
        {
          field: "remark",
          title: "出库站台",
          type: "string",
          width: 100,
          align: "left",
        },
      ]);
      const detail = ref({
        cnName: "盘点明细",
        table: "Dt_TakeStockOrderDetail",
        columns: [
          {
            field: "id",
            title: "Id",
            type: "int",
            width: 90,
            hidden: true,
            readonly: true,
            require: true,
            align: "left",
          },
          {
            field: "takeStockId",
            title: "盘点单主键",
            type: "string",
            width: 90,
            align: "left",
            hidden: true,
          },
          {
            field: "takePalletCode",
            title: "盘点托盘",
            type: "string",
            width: 150,
            align: "left",
            required: true,
          },
          {
            field: "takeDetalStatus",
            title: "盘点明细状态",
            type: "int",
            width: 130,
            align: "left",
            bind: { key: "takeStockDetailStatusEnum", data: [] },
          },
          {
            field: "materielCode",
            title: "物料编码",
            type: "string",
            width: 110,
            align: "left",
            required: true,
          },
          {
            field: "materielName",
            title: "物料名称",
            type: "string",
            width: 120,
            align: "left",
            required: true,
          },
          {
            field: "batchNo",
            title: "批次号",
            type: "string",
            width: 130,
            align: "left",
            required: true,
          },
          {
            field: "materielSpec",
            title: "物料名称",
            type: "string",
            width: 120,
            align: "left",
            required: true,
          },
          {
            field: "sysQty",
            title: "账面数量",
            type: "decimal",
            width: 90,
            align: "left",
            required: true,
            edit: { type: "decimal" }
          },
          {
            field: "qty",
            title: "实盘数量",
            type: "decimal",
            width: 90,
            align: "left",
            required: true,
            edit: { type: "decimal" }
          },
          {
            field: "unit",
            title: "单位",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "creater",
            title: "创建人",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "createDate",
            title: "创建时间",
            type: "datetime",
            width: 160,
            align: "left",
          },
          {
            field: "modifier",
            title: "修改人",
            type: "string",
            width: 100,
            align: "left",
          },
          {
            field: "modifyDate",
            title: "修改时间",
            type: "datetime",
            width: 160,
            align: "left",
          },
          {
            field: "remark",
            title: "备注",
            type: "string",
            width: 100,
            align: "left",
          },
        ],
        sortName: "id",
        key: "id",
      });
      return {
        table,
        extend,
        editFormFields,
        editFormOptions,
        searchFormFields,
        searchFormOptions,
        columns,
        detail,
      };
    },
  });
  </script>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/takeStockOrderDetail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,254 @@
<template>
    <!-- <div>
      <assemble-dist></assemble-dist>
    </div> -->
    <view-grid
      ref="grid"
      :columns="columns"
      :detail="detail"
      :editFormFields="editFormFields"
      :editFormOptions="editFormOptions"
      :searchFormFields="searchFormFields"
      :searchFormOptions="searchFormOptions"
      :table="table"
      :extend="extend"
    >
    </view-grid>
  </template>
<script>
  import extend from "@/extension/inbound/takeStockOrderDetail.js";
  import { ref, defineComponent } from "vue";
  // import AssembleDistVue from '../charts/AssembleDist.vue';
  export default defineComponent({
    // components: {
    //   'assemble-dist': AssembleDistVue,
    // },
    setup() {
      //使用组件AssembleDist
      const table = ref({
        key: "id",
        footer: "Foots",
        cnName: "盘点单",
        name: "TakeStockOrderDetail",
        url: "/TakeStockOrderDetail/",
        sortName: "id",
      });
      const searchFormFields = ref({
        orderNo:""
      });
      const searchFormOptions = ref([
        [
        { title: "盘点单据", field: "takeStockNo", type: "like" },
        { title: "盘点料箱", field: "takePalletCode", type: "like" },
        { title: "物料编号", field: "materielCode", type: "like" },
        { title: "批次号", field: "batchNo", type: "like" },
        ],
        [
        { title: "条码", field: "barcode", type: "like" },
        { title: "仓库", field: "warehouseCode", type: "like" },
        { title: "厂区", field: "factoryArea", type: "like" },
        { title: "供应商编号", field: "supplyCode", type: "like" },
        ],
        [
        { title: "创建人", field: "creater", type: "like" },
        { title: "创建时间", field: "createDate", type: "datetime" },
        ]
      ]);
      const editFormFields = ref({
        orderNo:"",
        warehouseId: "",
        remark:""
      });
      const editFormOptions = ref([
      [
        {
          field: "orderNo",
          title: "单据编号",
          type: "string",
          readonly: "true",
        },
        {
          title: "仓库",
          field: "warehouseId",
          type: "select",
          dataKey: "warehouses",
          data: [],
          required: true,
        },
        {
          field: "remark",
          title: "备注",
          type: "string",
        }
      ]
      ]);
      const columns = ref([
        {
            field: "id",
            title: "Id",
            type: "int",
            width: 90,
            hidden: true,
            readonly: true,
            require: true,
            align: "left",
          },
          {
            field: "takeStockId",
            title: "盘点单主键",
            type: "string",
            width: 90,
            align: "left",
            hidden: true,
          },
          {
            field: "takeStockNo",
            title: "盘点单据",
            type: "string",
            width: 145,
            align: "left",
          },
          {
            field: "takePalletCode",
            title: "盘点料箱",
            type: "string",
            width: 110,
            align: "left",
            required: true,
          },
          {
            field: "materielCode",
            title: "物料编码",
            type: "string",
            width: 130,
            align: "left",
            required: true,
          },
          {
            field: "materielName",
            title: "物料名称",
            type: "string",
            width: 120,
            align: "left",
            required: true,
          },
          {
            field: "batchNo",
            title: "批次号",
            type: "string",
            width: 130,
            align: "left",
            required: true,
          },
          {
            field: "barcode",
            title: "条码",
            type: "string",
            width: 120,
            align: "left",
            required: true,
          },
          {
            field: "sysQty",
            title: "账面数量",
            type: "decimal",
            width: 90,
            align: "left",
            required: true,
            edit: { type: "decimal" }
          },
          {
            field: "qty",
            title: "实盘数量",
            type: "decimal",
            width: 90,
            align: "left",
            required: true,
            edit: { type: "decimal" }
          },
          {
            field: "unit",
            title: "单位",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "takeDetalStatus",
            title: "盘点明细状态",
            type: "int",
            width: 130,
            align: "left",
            bind: { key: "takeStockDetailStatusEnum", data: [] },
          },
          {
            field: "warehouseCode",
            title: "仓库编号",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "factoryArea",
            title: "厂区",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "supplyCode",
            title: "供应商编号",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "creater",
            title: "创建人",
            type: "string",
            width: 90,
            align: "left",
          },
          {
            field: "createDate",
            title: "创建时间",
            type: "datetime",
            width: 160,
            align: "left",
          },
          {
            field: "remark",
            title: "备注",
            type: "string",
            width: 100,
            align: "left",
          },
      ]);
      const detail = ref({
        cnName: "盘点明细",
        table: "Dt_TakeStockOrderDetail",
        columns: [
        ],
        sortName: "id",
        key: "id",
      });
      return {
        table,
        extend,
        editFormFields,
        editFormOptions,
        searchFormFields,
        searchFormOptions,
        columns,
        detail,
      };
    },
  });
  </script>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Inbound/CompleteStockTakeDTO.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.Attributes;
namespace WIDESEA_DTO.Inbound
{
    public class CompleteStockTakeDTO
    {
        public string orderNo { get; set; }
        public string boxNo { get; set; }
        public string barcode { get; set; }
        public decimal actualQuantity { get; set; }
        public decimal stockQuantity { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IInboundService/ITakeStockOrderDetailService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.BaseServices;
using WIDESEA_Model.Models;
namespace WIDESEA_IInboundService
{
    public interface ITakeStockOrderDetailService:IService<Dt_TakeStockOrderDetail>
    {
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IInboundService/ITakeStockOrderService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core;
using WIDESEA_Core.BaseServices;
using WIDESEA_DTO.Inbound;
using WIDESEA_Model.Models;
namespace WIDESEA_IInboundService
{
    public interface ITakeStockOrderService : IService<Dt_TakeStockOrder>
    {
        WebResponseContent ValidateBoxNo(string orderNo, string boxNo);
        WebResponseContent ValidateBarcode(string boxNo, string barcode);
        WebResponseContent CompleteStockTake([FromBody] CompleteStockTakeDTO completeStockTakeDTO);
        WebResponseContent ReturnBox(string orderNo, string boxNo);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/TakeStockOrderDetailService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_IInboundService;
using WIDESEA_Model.Models;
namespace WIDESEA_InboundService
{
    public class TakeStockOrderDetailService : ServiceBase<Dt_TakeStockOrderDetail, IRepository<Dt_TakeStockOrderDetail>>, ITakeStockOrderDetailService
    {
        public IRepository<Dt_TakeStockOrderDetail> Repository => BaseDal;
        public TakeStockOrderDetailService(IRepository<Dt_TakeStockOrderDetail> BaseDal) : base(BaseDal)
        {
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/TakeStockOrderService.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core;
using WIDESEA_IInboundService;
using WIDESEA_Model.Models;
using WIDESEA_Common.OrderEnum;
using WIDESEA_Core.Helper;
using Microsoft.AspNetCore.Mvc.RazorPages;
using WIDESEA_Core.Enums;
using SqlSugar;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Common.StockEnum;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.ReturnMES;
using MailKit.Search;
using static System.Collections.Specialized.BitVector32;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_IBasicService;
namespace WIDESEA_InboundService
{
    public class TakeStockOrderService : ServiceBase<Dt_TakeStockOrder, IRepository<Dt_TakeStockOrder>>, ITakeStockOrderService
    {
        public IRepository<Dt_TakeStockOrder> Repository => BaseDal;
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        private readonly IRepository<Dt_TakeStockOrder> _takeStockOrder;
        private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
        private readonly IRepository<Dt_TakeStockOrderDetail> _takeStockOrderDetail;
        private readonly IRepository<Dt_Task> _taskRepository;
        private readonly ILocationInfoService _locationInfoService;
        public TakeStockOrderService(IRepository<Dt_TakeStockOrder> BaseDal, IUnitOfWorkManage unitOfWorkManage,IRepository<Dt_TakeStockOrder> takeStockOrder,IRepository<Dt_StockInfo> stockInfoRepository,IRepository<Dt_TakeStockOrderDetail> takeStockOrderDetail,IRepository<Dt_Task> taskRepository,ILocationInfoService locationInfoService) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _takeStockOrder = takeStockOrder;
            _stockInfoRepository = stockInfoRepository;
            _takeStockOrderDetail = takeStockOrderDetail;
            _taskRepository = taskRepository;
            _locationInfoService = locationInfoService;
        }
        public WebResponseContent ValidateBoxNo(string orderNo, string boxNo)
        {
            try
            {
                Dt_StockInfo stockInfo = _stockInfoRepository.QueryFirst(x => x.PalletCode == boxNo);
                if (stockInfo == null) {
                    return WebResponseContent.Instance.Error("未找到该托盘库存");
                }
                if(stockInfo.StockStatus != StockStatusEmun.盘点出库完成.ObjToInt())
                {
                    return WebResponseContent.Instance.Error("该托盘处于非盘点状态,请检查盘点任务");
                }
                Dt_TakeStockOrder takeStockOrder = _takeStockOrder.QueryFirst(x=>x.OrderNo == orderNo);
                if (takeStockOrder == null)
                {
                    return WebResponseContent.Instance.Error("未找到该盘点单据");
                }
                if (takeStockOrder.AllPalletCode.Contains(","))
                {
                    var remarkValues = takeStockOrder.AllPalletCode
                        .Split(',')
                        .Select(s => s.Trim())
                        .Where(s => !string.IsNullOrWhiteSpace(s))
                        .ToList();
                    if (remarkValues.Count == 0)
                    {
                        return WebResponseContent.Instance.Error("盘点单箱号仅包含逗号,无有效箱号数据");
                    }
                    bool isMatch = remarkValues.Any(val => val.Equals(boxNo, StringComparison.OrdinalIgnoreCase));
                    if (!isMatch)
                    {
                        return WebResponseContent.Instance.Error($"箱号【{boxNo}】未在盘点单箱号【{takeStockOrder.Remark}】中找到匹配项");
                    }
                }
                else
                {
                    bool isMatch = takeStockOrder.Remark.Trim().Equals(boxNo, StringComparison.OrdinalIgnoreCase);
                    if (!isMatch)
                    {
                        return WebResponseContent.Instance.Error($"箱号【{boxNo}】与盘点单箱号【{takeStockOrder.Remark}】不匹配");
                    }
                }
                return WebResponseContent.Instance.OK();
            }
            catch(Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent ValidateBarcode(string boxNo, string barcode)
        {
            try
            {
                Dt_StockInfo stockInfo = _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletCode == boxNo).Includes(x => x.Details).First();
                if(stockInfo == null)
                {
                    return WebResponseContent.Instance.Error("未找到该料箱库存信息");
                }
                if(stockInfo.Details == null)
                {
                    return WebResponseContent.Instance.Error("未找到该料箱库存");
                }
                List<string> barcodes = stockInfo.Details.Select(x => x.Barcode).ToList();
                bool isMatch = barcodes.Any(val => val.Equals(barcode, StringComparison.OrdinalIgnoreCase));
                if (!isMatch)
                {
                    return WebResponseContent.Instance.Error($"条码【{barcode}】未在【{boxNo}】料箱条码中找到匹配项");
                }
                var stockDetail = stockInfo.Details.FirstOrDefault(x=>x.Barcode == barcode);
                var resultData = new { stockQuantity = stockDetail.StockQuantity };
                return WebResponseContent.Instance.OK(data:resultData);
            }
            catch(Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent CompleteStockTake([FromBody] CompleteStockTakeDTO completeStockTakeDTO)
        {
            try
            {
                Dt_TakeStockOrder takeStockOrder = _takeStockOrder.QueryFirst(x=>x.OrderNo == completeStockTakeDTO.orderNo);
                if (takeStockOrder == null)
                {
                    return WebResponseContent.Instance.Error("未找到该盘点单据");
                }
                Dt_StockInfo stockInfo = _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletCode == completeStockTakeDTO.boxNo).Includes(x=>x.Details).First();
                if(stockInfo == null)
                {
                    return WebResponseContent.Instance.Error("未找到该托盘库存");
                }
                Dt_StockInfoDetail stockInfoDetail = stockInfo.Details.FirstOrDefault(x => x.Barcode == completeStockTakeDTO.barcode);
                if(stockInfoDetail == null)
                {
                    return WebResponseContent.Instance.Error("条码库存数据未找到匹配数据");
                }
                Dt_TakeStockOrderDetail takeStockOrderDetail = new Dt_TakeStockOrderDetail()
                {
                    TakeStockId = takeStockOrder.Id,
                    MaterielCode = stockInfoDetail.MaterielCode,
                    MaterielName = stockInfoDetail.MaterielName??"",
                    BatchNo = stockInfoDetail.BatchNo,
                    TakePalletCode = completeStockTakeDTO.boxNo,
                    TakeDetalStatus = TakeStockDetailStatusEnum.盘点完成.ObjToInt(),
                    Unit = stockInfoDetail.Unit,
                    SysQty = completeStockTakeDTO.stockQuantity,
                    Qty =completeStockTakeDTO.actualQuantity,
                    Remark = completeStockTakeDTO.stockQuantity-completeStockTakeDTO.actualQuantity>=0 ?"盘亏":"盘盈",
                    barcode = completeStockTakeDTO.barcode,
                    WarehouseCode = stockInfoDetail.WarehouseCode??"",
                    FactoryArea = stockInfoDetail.FactoryArea,
                    SupplyCode = stockInfoDetail.SupplyCode??"",
                    TakeStockNo = takeStockOrder.OrderNo,
                };
                _unitOfWorkManage.BeginTran();
                _takeStockOrderDetail.AddData(takeStockOrderDetail);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK();
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent ReturnBox(string orderNo, string boxNo)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                Dt_TakeStockOrder takeStockOrder = _takeStockOrder.QueryFirst(x=>x.OrderNo == orderNo);
                if(takeStockOrder == null)
                {
                    return content.Error("未找到该盘点单据");
                }
                var stock = _stockInfoRepository.QueryFirst(x => x.PalletCode == boxNo);
                if (stock == null)
                {
                    return content.Error($"未找到托盘{boxNo}库存信息不允许回库");
                }
                var task = _taskRepository.QueryFirst(x => x.PalletCode == boxNo);
                if (task != null)
                {
                    return content.Error($"托盘{boxNo}存在任务回库失败!");
                }
                if(stock.StockStatus != StockStatusEmun.盘点出库完成.ObjToInt())
                {
                    return content.Error("该托盘状态不对,不允许盘点入库");
                }
                stock.StockStatus = StockStatusEmun.入库确认.ObjToInt();
                takeStockOrder.TakeStockStatus = TakeStockStatusEnum.盘点完成.ObjToInt();
                // åˆ†é…æ–°è´§ä½
                var newLocation = _locationInfoService.AssignLocation(stock.LocationType);
                var newTask = new Dt_Task()
                {
                    CurrentAddress = takeStockOrder.Remark,
                    Grade = 0,
                    PalletCode = boxNo,
                    NextAddress = "",
                    OrderNo = takeStockOrder.OrderNo,
                    Roadway = newLocation.RoadwayNo,
                    SourceAddress = takeStockOrder.Remark,
                    TargetAddress = newLocation.LocationCode,
                    TaskStatus = (int)TaskStatusEnum.New,
                    TaskType = TaskTypeEnum.InInventory.ObjToInt(),
                    PalletType = stock.PalletType,
                    WarehouseId = stock.WarehouseId
                };
                _unitOfWorkManage.BeginTran();
                _stockInfoRepository.UpdateData(stock);
                _takeStockOrder.UpdateData(takeStockOrder);
                _taskRepository.AddData(newTask);
                _unitOfWorkManage.CommitTran();
                //var moveResult = await _eSSApiService.MoveContainerAsync(new MoveContainerRequest
                //{
                //    slotCode = movestations[station],
                //    containerCode = palletCode
                //});
                return content.OK();
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return content.Error(ex.Message);
            }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Inbound/Dt_TakeStockOrderDetail.cs
@@ -1,4 +1,5 @@
using SqlSugar;
using Magicodes.ExporterAndImporter.Core;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -18,108 +19,126 @@
        /// ä¸»é”®
        /// </summary>
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "主键")]
        [ExporterHeader(DisplayName = "主键")]
        public int Id { get; set; }
        /// <summary>
        /// ç›˜ç‚¹å•主键
        /// </summary>
        [SugarColumn(IsNullable = false, ColumnDescription = "盘点单主键")]
        [ExporterHeader(DisplayName = "盘点单主键")]
        public int TakeStockId { get; set; }
        /// <summary>
        /// ç‰©æ–™ç¼–码
        /// </summary>
        [SugarColumn(IsNullable = false, Length = 50, ColumnDescription = "物料编码")]
        [ExporterHeader(DisplayName = "物料编码")]
        public string MaterielCode { get; set; }
        /// <summary>
        /// ç‰©æ–™åç§°
        /// </summary>
        [SugarColumn(IsNullable = false, Length = 200, ColumnDescription = "物料名称")]
        [ExporterHeader(DisplayName = "物料名称")]
        public string MaterielName { get; set; }
        /// <summary>
        /// æ‰¹æ¬¡å·
        /// </summary>
        [SugarColumn(IsNullable = false, Length = 50, ColumnDescription = "批次号")]
        [ExporterHeader(DisplayName = "批次号")]
        public string BatchNo { get; set; }
        /// <summary>
        /// ç‰©æ–™è§„æ ¼
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 200, ColumnDescription = "物料规格")]
        [ExporterHeader(DisplayName = "物料规格")]
        public string MaterielSpec { get; set; }
        /// <summary>
        /// åŽŸä»“ä½
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 50, ColumnDescription = "原仓位")]
        [ExporterHeader(DisplayName = "原仓位")]
        public string LocationCode { get; set; }
        /// <summary>
        /// ç›˜ç‚¹æ‰˜ç›˜
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 50, ColumnDescription = "盘点托盘")]
        [ExporterHeader(DisplayName = "盘点托盘")]
        public string TakePalletCode { get; set; }
        /// <summary>
        /// ç›˜ç‚¹æ˜Žç»†çŠ¶æ€
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "盘点明细状态")]
        [ExporterHeader(DisplayName = "盘点明细状态")]
        public int TakeDetalStatus { get; set; }
        /// <summary>
        /// å•位
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 50, ColumnDescription = "单位")]
        [ExporterHeader(DisplayName = "单位")]
        public string Unit { get; set; }
        /// <summary>
        /// è´¦é¢æ•°é‡
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "账面数量")]
        [ExporterHeader(DisplayName = "账面数量")]
        public decimal SysQty { get; set; }
        /// <summary>
        /// å®žç›˜æ•°é‡
        /// </summary>
        [SugarColumn(IsNullable = true, ColumnDescription = "实盘数量")]
        [ExporterHeader(DisplayName = "实盘数量")]
        public decimal Qty { get; set; }
        /// <summary>
        /// å¤‡æ³¨
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 500, ColumnDescription = "备注")]
        [ExporterHeader(DisplayName = "备注")]
        public string Remark { get; set; }
        /// <summary>
        /// æ¡ç 
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 50, ColumnDescription = "条码")]
        [ExporterHeader(DisplayName = "条码")]
        public string barcode { get; set; }
        /// <summary>
        /// ç‰©æ–™ä»“库
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 20, ColumnDescription = "物料仓库")]
        [ExporterHeader(DisplayName = "物料仓库")]
        public string WarehouseCode { get; set; }
        /// <summary>
        /// åŽ‚åŒº
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 10, ColumnDescription = "厂区")]
        [ExporterHeader(DisplayName = "厂区")]
        public string FactoryArea { get; set; }
        /// <summary>
        /// ä¾›åº”商编号
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 20, ColumnDescription = "供应商编号")]
        [ExporterHeader(DisplayName = "供应商编号")]
        public string SupplyCode { get; set; }
        /// <summary>
        /// ç›˜ç‚¹å•单据
        /// </summary>
        [SugarColumn(IsNullable = true, Length = 50, ColumnDescription = "盘点单单据")]
        [ExporterHeader(DisplayName = "盘点单单据")]
        public string TakeStockNo { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_SystemService/Sys_DictionaryService.cs
@@ -295,6 +295,54 @@
                            result = new VueDictionaryDTO { DicNo = key, Config = "", Data = data };
                        }
                        break;
                    case "takeStockStatusEnum":
                        {
                            List<object> data = new List<object>();
                            Type type = typeof(TakeStockStatusEnum);
                            List<int> enums = Enum.GetValues(typeof(TakeStockStatusEnum)).Cast<int>().ToList();
                            int index = 0;
                            foreach (var item in enums)
                            {
                                FieldInfo? fieldInfo = typeof(TakeStockStatusEnum).GetField(((TakeStockStatusEnum)item).ToString());
                                DescriptionAttribute? description = fieldInfo.GetCustomAttribute<DescriptionAttribute>();
                                if (description != null)
                                {
                                    data.Add(new { key = item.ToString(), value = description.Description });
                                }
                                else
                                {
                                    data.Add(new { key = item.ToString(), value = item.ToString() });
                                }
                                index++;
                            }
                            result = new VueDictionaryDTO { DicNo = key, Config = "", Data = data };
                        }
                        break;
                    case "takeStockDetailStatusEnum":
                        {
                            List<object> data = new List<object>();
                            Type type = typeof(TakeStockDetailStatusEnum);
                            List<int> enums = Enum.GetValues(typeof(TakeStockDetailStatusEnum)).Cast<int>().ToList();
                            int index = 0;
                            foreach (var item in enums)
                            {
                                FieldInfo? fieldInfo = typeof(TakeStockDetailStatusEnum).GetField(((TakeStockDetailStatusEnum)item).ToString());
                                DescriptionAttribute? description = fieldInfo.GetCustomAttribute<DescriptionAttribute>();
                                if (description != null)
                                {
                                    data.Add(new { key = item.ToString(), value = description.Description });
                                }
                                else
                                {
                                    data.Add(new { key = item.ToString(), value = item.ToString() });
                                }
                                index++;
                            }
                            result = new VueDictionaryDTO { DicNo = key, Config = "", Data = data };
                        }
                        break;
                }
                return result;
            }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -94,6 +94,7 @@
        private readonly IRepository<Dt_AllocateMaterialInfo_Hty> _allocateMaterialInfo_Hty;
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IBasicService _basicService;
        private readonly IRepository<Dt_TakeStockOrder> _takeStockOrder;
        public IRepository<Dt_Task> Repository => BaseDal;
        private Dictionary<string, SqlSugar.OrderByType> _taskOrderBy = new()
@@ -113,7 +114,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) : 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) : base(BaseDal)
        {
            _mapper = mapper;
            _unitOfWorkManage = unitOfWorkManage;
@@ -141,6 +142,7 @@
            _allocateMaterialInfo_Hty = allocateMaterialInfo_Hty;
            _httpClientHelper = httpClientHelper;
            _basicService = basicService;
            _takeStockOrder = takeStockOrder;
        }
        public async Task TaskStatusChange(string taskNum, TaskStatusEnum taskStatusEnum)
@@ -1850,5 +1852,134 @@
            return TaskCompleted(TaskNum);
        }
        /// <summary>
        /// ç›˜ç‚¹å‡ºåº“完成
        /// </summary>
        public WebResponseContent OutInventoryTaskCompleted(Dt_Task task)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                task = BaseDal.QueryFirst(x => x.TaskNum == task.TaskNum);
                if (task == null)
                {
                    return WebResponseContent.Instance.Error("未找到任务信息");
                }
                var SourceAddress = task.SourceAddress;
                Dt_StockInfo stockInfo = _stockService.StockInfoService.Repository.QueryFirst(x=>x.PalletCode == task.PalletCode);
                Dt_LocationInfo locationInfo = _basicService.LocationInfoService.Repository.QueryFirst(x => x.LocationCode == SourceAddress);
                if (stockInfo == null) return WebResponseContent.Instance.Error("未找到库存信息");
                if (locationInfo == null) return WebResponseContent.Instance.Error("未找到货位信息");
                //获取对应盘点单
                Dt_TakeStockOrder takeStockOrder = _takeStockOrder.Db.Queryable<Dt_TakeStockOrder>().Where(x => x.OrderNo == task.OrderNo).Includes(x => x.Details).First();
                if (takeStockOrder == null)
                {
                    return content.Error($"盘点单{task.OrderNo}不存在");
                }
                if (takeStockOrder.TakeStockStatus != TakeStockStatusEnum.盘点中.ObjToInt())
                {
                    return content.Error($"盘点单{task.OrderNo}盘点已完成或未开始");
                }
                if (stockInfo.StockStatus != StockStatusEmun.出库锁定.ObjToInt())
                {
                    return content.Error($"{stockInfo.PalletCode}库存状态不正确");
                }
                if (locationInfo.LocationStatus != LocationStatusEnum.Lock.ObjToInt())
                {
                    return content.Error($"{locationInfo.LocationCode}货位状态不正确");
                }
                stockInfo.StockStatus = StockStatusEmun.盘点出库完成.ObjToInt();
                stockInfo.LocationCode = "";
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                int beforeStatus = locationInfo.LocationStatus;
                locationInfo.LocationStatus = LocationStatusEnum.Free.ObjToInt();
                _unitOfWorkManage.BeginTran();
                //货位变动记录
                _basicService.LocationInfoService.UpdateData(locationInfo);
                _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(locationInfo, beforeStatus, LocationStatusEnum.Free.ObjToInt(),  takeStockOrder?.OrderNo ?? "", task.TaskNum);
                _stockRepository.UpdateData(stockInfo);
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                _unitOfWorkManage.CommitTran();
                content.OK();
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                content.Error(ex.Message);
            }
            return content;
        }
        /// <summary>
        /// ç›˜ç‚¹å…¥åº“完成
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> InInventoryTaskCompleted(Dt_Task task)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == task.TargetAddress);
                if (locationInfo == null)
                {
                    return content.Error($"未找到对应的终点货位信息");
                }
                Dt_StockInfo stockInfo = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletCode == task.PalletCode && x.WarehouseId == task.WarehouseId).First();
                if (stockInfo == null)
                {
                    return WebResponseContent.Instance.Error($"未找到托盘对应的组盘信息");
                }
                if (!string.IsNullOrEmpty(stockInfo.LocationCode))
                {
                    return WebResponseContent.Instance.Error($"该托盘已绑定货位");
                }
                if (locationInfo.LocationStatus == LocationStatusEnum.InStock.ObjToInt())
                {
                    return WebResponseContent.Instance.Error($"货位状态不正确");
                }
                var beforelocationStatus = locationInfo.LocationStatus;
                locationInfo.LocationStatus = LocationStatusEnum.InStock.ObjToInt();
                stockInfo.LocationCode = locationInfo.LocationCode;
                stockInfo.PalletCode = task.PalletCode;
                stockInfo.LocationCode = task.TargetAddress;
                stockInfo.StockStatus = StockStatusEmun.入库完成.ObjToInt();
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                _unitOfWorkManage.BeginTran();
                _locationInfoService.Repository.UpdateData(locationInfo);
                _stockRepository.UpdateData(stockInfo);
                var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.人工删除);
                _unitOfWorkManage.CommitTran();
                if (!result)
                {
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                try
                {
                    _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforelocationStatus, StockChangeType.Inbound.ObjToInt(), "", task.TaskNum);
                }
                catch (Exception ex)
                {
                    _logger.LogInformation($"InEmptyTaskCompleted AddLocationStatusChangeRecord : {ex.Message} ");
                }
                return content;
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
            }
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -393,7 +393,7 @@
                            TargetAddress = outStation,
                            TaskStatus = TaskStatusEnum.New.ObjToInt(),
                            TaskType = taskType.ObjToInt(),
                            // TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
                            //TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
                            PalletType = stockInfo.PalletType,
                            WarehouseId = stockInfo.WarehouseId,
                        };
@@ -1127,7 +1127,7 @@
                }
                //获取货位
                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
                List<Dt_LocationInfo> locationInfos = (List<Dt_LocationInfo>)_locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode));
                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));
@@ -1138,7 +1138,9 @@
                    WarehouseId = stockInfos.FirstOrDefault().WarehouseId,
                    TakeStockStatus = TakeStockStatusEnum.盘点中.ObjToInt(),
                    OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.PDCodeRule)),
                    Details = new List<Dt_TakeStockOrderDetail>()
                    AllPalletCode = string.Join(",", stockInfos.Select(item => item.PalletCode).Where(palletCode => !string.IsNullOrEmpty(palletCode))),
                    Remark = outStation
                };
                foreach (var item in stockInfos)
                {
@@ -1172,7 +1174,7 @@
                _unitOfWorkManage.BeginTran();
                //更新库存状态
                _stockRepository.UpdateData(stockInfos);
                BaseDal.Db.InsertNav(takeStockOrder);
                _takeStockOrder.AddData(takeStockOrder);
                //新建任务
                BaseDal.AddData(tasks);
                _locationInfoService.UpdateData(locationInfos);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/TakeStockOrderController.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core;
using WIDESEA_Core.BaseController;
using WIDESEA_DTO.Inbound;
using WIDESEA_IInboundService;
using WIDESEA_Model.Models;
namespace WIDESEA_WMSServer.Controllers.Inbound
{
    /// <summary>
    /// ç›˜ç‚¹å•
    /// </summary>
    [Route("api/TakeStockOrder")]
    [ApiController]
    public class TakeStockOrderController : ApiBaseController<ITakeStockOrderService, Dt_TakeStockOrder>
    {
        public TakeStockOrderController(ITakeStockOrderService service) : base(service)
        {
        }
        /// <summary>
        /// æ–™ç®±éªŒè¯
        /// </summary>
        [HttpPost,HttpGet, Route("ValidateBoxNo"), AllowAnonymous]
        public WebResponseContent ValidateBoxNo(string orderNo , string boxNo)
        {
            return Service.ValidateBoxNo(orderNo,boxNo);
        }
        /// <summary>
        /// æ¡ç éªŒè¯
        /// </summary>
        [HttpPost, HttpGet, Route("ValidateBarcode"), AllowAnonymous]
        public WebResponseContent ValidateBarcode( string boxNo,string barcode)
        {
            return Service.ValidateBarcode(boxNo, barcode);
        }
        /// <summary>
        /// ç›˜ç‚¹å®Œæˆè®°å½•
        /// </summary>
        [HttpPost, HttpGet, Route("CompleteStockTake"), AllowAnonymous]
        public WebResponseContent CompleteStockTake([FromBody] CompleteStockTakeDTO completeStockTakeDTO)
        {
            return Service.CompleteStockTake(completeStockTakeDTO);
        }
        /// <summary>
        /// ç›˜ç‚¹æ–™ç®±å›žåº“
        /// </summary>
        [HttpPost, HttpGet, Route("ReturnBox"), AllowAnonymous]
        public WebResponseContent ReturnBox(string orderNo, string boxNo)
        {
            return Service.ReturnBox(orderNo, boxNo);
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/TakeStockOrderDetailController.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core.BaseController;
using WIDESEA_IInboundService;
using WIDESEA_Model.Models;
namespace WIDESEA_WMSServer.Controllers.Inbound
{
    [Route("api/TakeStockOrderDetail")]
    [ApiController]
    public class TakeStockOrderDetailController : ApiBaseController<ITakeStockOrderDetailService, Dt_TakeStockOrderDetail>
    {
        public TakeStockOrderDetailController(ITakeStockOrderDetailService service) : base(service)
        {
        }
    }
}