spring
2 天以前 3ffdb76baf74089912a23c1f8f8112d5c8c1063b
fix: 生产详情接口联调90%
已修改4个文件
249 ■■■■■ 文件已修改
src/api/productionManagement/productionProductMain.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/workOrder.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/procurementManagement/procurementLedger/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/Detail/index.vue 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionProductMain.js
@@ -9,3 +9,11 @@
        params: query,
    });
}
// 根据工序工单 id 查询报工信息
export function getByProductWorkOrderId(productWorkOrderId) {
    return request({
        url: `/productionProductMain/getByProductWorkOrderId/${productWorkOrderId}`,
        method: "get",
    });
}
src/api/productionManagement/workOrder.js
@@ -8,6 +8,14 @@
  });
}
// 根据生产订单 id 查询工序工单列表
export function getByProductOrderId(productOrderId) {
  return request({
    url: `/productWorkOrder/getByProductOrderId/${productOrderId}`,
    method: "get",
  });
}
export function updateProductWorkOrder(data) {
  return request({
    url: "/productWorkOrder/updateProductWorkOrder",
src/views/procurementManagement/procurementLedger/index.vue
@@ -1474,6 +1474,22 @@
    if (type === "edit") {
      // 复制行数据
      productForm.value = { ...row };
      // el-radio-group 的 value 是布尔 true/false
      // 后端/表格数据可能是 0/1 或字符串,需做一次归一化,避免不回显/提交默认“否”
      const normalizeIsChecked = (val) => {
        if (val === true) return true;
        if (val === false) return false;
        if (val === 1 || val === "1") return true;
        if (val === 0 || val === "0") return false;
        if (typeof val === "string") {
          const s = val.trim().toLowerCase();
          if (["是", "yes", "true", "y"].includes(s)) return true;
          if (["否", "no", "false", "n"].includes(s)) return false;
        }
        return !!val;
      };
      productForm.value.isChecked = normalizeIsChecked(row?.isChecked);
      
      // 如果是从模板加载的数据,可能没有 productId 和 productModelId
      // 需要根据 productCategory 和 specificationModel 来查找对应的 ID
src/views/productionManagement/productionOrder/Detail/index.vue
@@ -112,7 +112,7 @@
            </div>
            <div v-else class="right-content">
              <el-table :data="mockReports" border height="420">
              <el-table :data="reports" border height="420" v-loading="reportLoading">
                <el-table-column label="序号" type="index" width="60" align="center" />
                <el-table-column label="报工单号" prop="reportNo" min-width="140" show-overflow-tooltip />
                <el-table-column label="报工人员" prop="reportUser" min-width="120" show-overflow-tooltip />
@@ -120,7 +120,7 @@
                <el-table-column label="产出数量" prop="outputQty" min-width="110" />
                <el-table-column label="合格数量" prop="qualifiedQty" min-width="110" />
                <el-table-column label="不良数量" prop="badQty" min-width="110" />
                <el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip />
                <el-table-column label="不合格处理" prop="remark" min-width="160" show-overflow-tooltip />
                <el-table-column label="操作" width="150" fixed="right">
                  <template #default="{ row }">
                    <el-button type="primary" link @click="viewReportRecord(row)">
@@ -153,8 +153,10 @@
</template>
<script setup>
import { computed, ref, watch } from "vue";
import { computed, onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import { getByProductOrderId } from "@/api/productionManagement/workOrder.js";
import { getByProductWorkOrderId } from "@/api/productionManagement/productionProductMain.js";
const route = useRoute();
@@ -166,45 +168,72 @@
  specificationModel: route.query.specificationModel,
}));
// 模拟工序数据(后续用接口替换)
const processes = computed(() => [
  {
    processCode: "GX-001",
    processName: "备料",
    inputQty: 1000,
    outputQty: 980,
    qualifiedQty: 970,
    badQty: 10,
    status: "success",
  },
  {
    processCode: "GX-002",
    processName: "成型",
    inputQty: 980,
    outputQty: 960,
    qualifiedQty: 948,
    badQty: 12,
    status: "process",
  },
  {
    processCode: "GX-003",
    processName: "烘干",
    inputQty: 960,
    outputQty: 950,
    qualifiedQty: 948,
    badQty: 2,
    status: "wait",
  },
  {
    processCode: "GX-004",
    processName: "包装入库",
    inputQty: 950,
    outputQty: 920,
    qualifiedQty: 918,
    badQty: 2,
    status: "wait",
  },
]);
// 工序数据(接口替换)
const processes = ref([]);
const normalizeStatus = (statusText, completionStatus, inputQty, outputQty) => {
  const s = statusText === null || statusText === undefined ? "" : String(statusText).trim();
  const lower = s.toLowerCase();
  // 常见中文状态兜底:进行中/已完成/等待
  if (s.includes("进行") || lower.includes("process") || lower.includes("doing") || lower.includes("running")) return "process";
  if (s.includes("完成") || s.includes("已完") || lower.includes("success") || lower.includes("done") || lower.includes("completed")) return "success";
  if (s.includes("待") || s.includes("未开始") || lower.includes("wait") || lower.includes("pending") || lower.includes("not_start")) return "wait";
  // 用 completionStatus 做兜底(一般为 0~100)
  const cs = Number(completionStatus);
  if (Number.isFinite(cs)) {
    if (cs >= 100) return "success";
    if (cs > 0) return "process";
    return "wait";
  }
  // 最后再用数量兜底
  if (Number.isFinite(inputQty) && inputQty > 0 && Number.isFinite(outputQty) && outputQty >= inputQty) return "success";
  if (Number.isFinite(outputQty) && outputQty > 0) return "process";
  return "wait";
};
const normalizeProcess = (item) => {
  // 字段以接口约定为准(你给的截图字段映射)
  // 工序:completionStatus/statusText/processNo/scrapRate/planQuantity/completeQuantity/completeQty/scrapQty
  const inputQty = Number(item?.planQuantity ?? item?.inputQty ?? 0);
  const outputQty = Number(item?.completeQuantity ?? item?.outputQty ?? 0);
  const qualifiedQty = Number(item?.completeQty ?? item?.qualifiedQty ?? item?.goodQty ?? 0);
  const badQty = Number(item?.scrapQty ?? item?.badQty ?? item?.defectQty ?? 0);
  const completionStatus = Number(item?.completionStatus ?? 0);
  const scrapRate = Number(item?.scrapRate ?? NaN);
  const status = normalizeStatus(item?.statusText ?? item?.status ?? item?.workStatus ?? item?.processStatus ?? item?.state, completionStatus, inputQty, outputQty);
  return {
    processCode: item?.processNo ?? item?.processCode ?? item?.processWorkOrderCode ?? "",
    processName: item?.processName ?? item?.processWorkOrderName ?? item?.processNo ?? "",
    productWorkOrderId: item?.productWorkOrderId ?? item?.workOrderId ?? item?.id ?? null,
    inputQty: Number.isFinite(inputQty) ? inputQty : 0,
    outputQty: Number.isFinite(outputQty) ? outputQty : 0,
    qualifiedQty: Math.max(0, Number.isFinite(qualifiedQty) ? qualifiedQty : 0),
    badQty: Math.max(0, Number.isFinite(badQty) ? badQty : 0),
    completionStatus: Number.isFinite(completionStatus) ? completionStatus : 0,
    scrapRate: Number.isFinite(scrapRate) ? scrapRate : null,
    status,
  };
};
onMounted(async () => {
  const productOrderId = header.value?.orderId;
  if (!productOrderId) return;
  try {
    const res = await getByProductOrderId(productOrderId);
    const payload = res?.data;
    const list = Array.isArray(payload) ? payload : payload?.records || payload?.data || [];
    processes.value = list.map((it) => normalizeProcess(it));
  } catch (e) {
    console.error("获取工序工单列表失败:", e);
    processes.value = [];
  }
});
// 默认选中第一道序(接口数据就绪后仍可从 0 开始)
const selectedIndex = ref(0);
@@ -233,42 +262,60 @@
  return list[idx] || null;
});
// 模拟报工信息(后续用接口替换)
const mockReports = computed(() => {
  const p = selectedProcess.value;
  if (!p) return [];
  const code = p.processCode || "GX";
  const reports = [
    {
      reportNo: `${code}-BG-0001`,
      reportUser: "张三",
      reportTime: "2026-03-14 09:20",
      outputQty: Math.floor((p.outputQty ?? 0) * 0.4),
      badQty: Math.floor((p.badQty ?? 0) * 0.4),
      remark: "正常报工",
    },
    {
      reportNo: `${code}-BG-0002`,
      reportUser: "李四",
      reportTime: "2026-03-14 13:45",
      outputQty: Math.floor((p.outputQty ?? 0) * 0.35),
      badQty: Math.floor((p.badQty ?? 0) * 0.35),
      remark: "设备调试后恢复",
    },
    {
      reportNo: `${code}-BG-0003`,
      reportUser: "王五",
      reportTime: "2026-03-14 17:10",
      outputQty: Math.max(0, (p.outputQty ?? 0) - Math.floor((p.outputQty ?? 0) * 0.75)),
      badQty: Math.max(0, (p.badQty ?? 0) - Math.floor((p.badQty ?? 0) * 0.75)),
      remark: "收尾",
    },
  ];
  return reports.map((item) => ({
    ...item,
    qualifiedQty: Math.max(0, Number(item.outputQty ?? 0) - Number(item.badQty ?? 0)),
  }));
});
const reports = ref([]);
const reportLoading = ref(false);
const normalizeReport = (item) => {
  // 报工记录:productNo/userName/createTime/quantity/qualifiedQty/scrapQty
  const outputQty = Number(item?.quantity ?? item?.outputQty ?? 0);
  const qualifiedQty = Number(item?.qualifiedQty ?? item?.completeQty ?? item?.goodQty ?? 0);
  const badQty = Number(item?.scrapQty ?? item?.badQty ?? 0);
  return {
    reportNo: item?.productNo ?? item?.reportNo ?? item?.productionReportNo ?? item?.id ?? "",
    reportUser: item?.userName ?? item?.reportUser ?? item?.createdByName ?? item?.reportUserName ?? "",
    reportTime: item?.createTime ?? item?.reportTime ?? item?.createdAt ?? item?.reportDate ?? "",
    outputQty: Number.isFinite(outputQty) ? outputQty : 0,
    qualifiedQty: Math.max(0, Number.isFinite(qualifiedQty) ? qualifiedQty : 0),
    badQty: Number.isFinite(badQty) ? Math.max(0, badQty) : 0,
    remark: item?.remark ?? item?.remarkText ?? item?.description ?? "",
  };
};
const fetchReportsForProcess = async (p) => {
  if (!p) {
    reports.value = [];
    return;
  }
  const productWorkOrderId = p.productWorkOrderId ?? p.id ?? p.workOrderId ?? null;
  if (!productWorkOrderId) {
    reports.value = [];
    return;
  }
  reportLoading.value = true;
  try {
    const res = await getByProductWorkOrderId(productWorkOrderId);
    const payload = res?.data;
    const list = Array.isArray(payload)
      ? payload
      : payload?.records || payload?.data || payload?.list || [];
    reports.value = list.map((it) => normalizeReport(it));
  } catch (e) {
    console.error("获取报工信息失败:", e);
    reports.value = [];
  } finally {
    reportLoading.value = false;
  }
};
watch(
  () => selectedProcess.value,
  (val) => {
    fetchReportsForProcess(val);
  },
  { immediate: true }
);
const viewReportRecord = (row) => {
  if (!row?.reportNo) return;
@@ -297,6 +344,11 @@
// 工序进度:用产出/投入估算(UI 先跑通,后续按真实规则替换)
const processPercentage = (p) => {
  if (!p) return 0;
  // 优先使用接口字段 completionStatus(你给的截图“工序进度”)
  const cs = Number(p?.completionStatus ?? NaN);
  if (Number.isFinite(cs)) return clampPercentage(cs);
  // 兜底:用产出/投入估算
  const input = Number(p.inputQty ?? 0);
  const output = Number(p.outputQty ?? 0);
  if (!Number.isFinite(input) || input <= 0) return 0;
@@ -314,6 +366,15 @@
// 不良率:不良数量 / 产出数量(先按此口径,后续对接接口可调整)
const defectRateText = (p) => {
  // 优先使用接口字段 scrapRate(你给的截图“不良率”)
  const scrapRate = Number(p?.scrapRate ?? NaN);
  if (Number.isFinite(scrapRate)) {
    // 有些接口 scrapRate 可能是 0~1 或 0~100,这里做一个简单判断
    const percent = scrapRate <= 1 ? scrapRate * 100 : scrapRate;
    return `${percent.toFixed(2)}%`;
  }
  // 兜底:不良数量 / 产出数量
  const bad = Number(p?.badQty ?? 0);
  const output = Number(p?.outputQty ?? 0);
  if (!Number.isFinite(bad) || bad <= 0) return "0%";