已添加1个文件
已修改9个文件
1495 ■■■■■ 文件已修改
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 445 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/components/Detail.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/components/GranulationForm.vue 939 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/components/ProductionRecordForm.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrder/index.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vite.config.js 18 ●●●● 补丁 | 查看 | 原始文档 | 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
@@ -36,21 +36,36 @@
                  <div
                    class="step-title"
                    :class="{ selected: idx === selectedIndex }"
                    @click="selectProcess(idx)"
                    @click.stop="selectProcess(idx)"
                  >
                    {{ `${idx + 1}. ${p.processName || "-"}` }}
                  </div>
                </template>
                <template #description>
                  <div class="step-panel">
                    <div v-if="idx === active" class="current-progress">
                  <div
                    class="step-panel"
                    :class="{
                      'step-panel-selected': idx === selectedIndex,
                      'step-panel-current': idx === active,
                    }"
                    @click="selectProcess(idx)"
                  >
                    <span
                      v-if="p?.status"
                      class="step-status-badge"
                      :class="`step-status-badge-${p.status}`"
                    >
                      {{ statusTagText(p.status) }}
                    </span>
                    <div v-if="p.status !== 'wait'" class="current-progress">
                      <div class="current-progress-head">
                        <span class="current-progress-title">当前工序进度</span>
                        <!-- <span class="current-progress-value">{{ currentProcessPercentage }}%</span> -->
                        <span class="current-progress-title">工序进度</span>
                        <!-- <span class="current-progress-value">{{ processPercentage(p) }}%</span> -->
                      </div>
                      <el-progress
                        :percentage="currentProcessPercentage"
                        :status="currentProcessPercentage >= 100 ? 'success' : ''"
                        :percentage="processPercentage(p)"
                        :color="progressColor(processPercentage(p))"
                        :status="processPercentage(p) >= 100 ? 'success' : ''"
                        :stroke-width="10"
                      />
                    </div>
@@ -99,30 +114,55 @@
            </div>
            <div v-if="!selectedProcess" class="right-empty">
              ç‚¹å‡»å·¦ä¾§æŸä¸ªå·¥åºï¼Œå³ä¾§å±•示该工序的报工明细。
              æš‚无工序数据。
            </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 />
                <el-table-column label="报工时间" prop="reportTime" min-width="160" show-overflow-tooltip />
                <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="dealResult" 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)">
                      ç”Ÿäº§è®°å½•
                    </el-button>
                  </template>
                </el-table-column>
              </el-table>
            </div>
          </div>
        </div>
      </div>
    </el-card>
    <el-dialog
      v-model="reportRecordDialogVisible"
      title="报工生产记录"
      width="680px"
      destroy-on-close
    >
      <div class="report-record-placeholder">
        <div>报工单号:{{ currentReportRow?.reportNo || "-" }}</div>
        <div>工序:{{ selectedProcess?.processName || "-" }}</div>
        <div class="placeholder-tip">弹框内容待定(后续补充详细生产记录)。</div>
      </div>
      <template #footer>
        <el-button @click="reportRecordDialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { computed, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
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();
@@ -134,89 +174,162 @@
  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 selectedIndex = ref(null);
const normalizeStatus = (statusText, completionStatus, inputQty, outputQty) => {
  const s = statusText === null || statusText === undefined ? "" : String(statusText).trim();
  // æŒ‰æŽ¥å£å®žé™…三种状态:已生成 / ç”Ÿäº§ä¸­ / å¾…生产
  if (s.includes("生产中")) return "process";
  if (s.includes("待生产")) return "wait";
  if (s.includes("已生产")) return "success";
  // å…œåº•:仍按 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);
const selectProcess = (idx) => {
  selectedIndex.value = idx;
};
watch(
  () => (processes.value || []).length,
  (len) => {
    if (!len) return;
    if (selectedIndex.value >= len) selectedIndex.value = len - 1;
    if (selectedIndex.value < 0) selectedIndex.value = 0;
  }
);
const selectedProcess = computed(() => {
  if (selectedIndex.value === null || selectedIndex.value === undefined) return null;
  return (processes.value || [])[selectedIndex.value] || null;
  const list = processes.value || [];
  if (!list.length) return null;
  const raw = selectedIndex.value;
  const idx =
    raw === null || raw === undefined
      ? 0
      : Math.min(Math.max(0, raw), list.length - 1);
  return list[idx] || null;
});
// æ¨¡æ‹ŸæŠ¥å·¥ä¿¡æ¯ï¼ˆåŽç»­ç”¨æŽ¥å£æ›¿æ¢ï¼‰
const mockReports = computed(() => {
  const p = selectedProcess.value;
  if (!p) return [];
  const code = p.processCode || "GX";
  return [
    {
      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: "收尾",
    },
  ];
});
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,
    dealResult: item?.dealResult ?? item?.dealResultText ?? 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;
  currentReportRow.value = row;
  reportRecordDialogVisible.value = true;
};
const reportRecordDialogVisible = ref(false);
const currentReportRow = ref(null);
const clampPercentage = (val) => {
  const n = Number(val);
@@ -226,30 +339,62 @@
  return Math.round(n);
};
const statusTagText = (status) => {
  if (status === "success") return "已生产";
  if (status === "process") return "生产中";
  if (status === "wait") return "待生产";
  return String(status ?? "");
};
// el-steps: active ä¸ºå½“前进行中的步骤下标(模拟)
const active = computed(() => {
  const list = processes.value || [];
  const idx = list.findIndex((p) => p.status === "process");
  return idx >= 0 ? idx : 0;
  // æ¿€æ´»çŠ¶æ€ä¸ºâ€œå¾…ç”Ÿäº§â€çš„ä¸Šä¸€æ¡
  const firstWaitIdx = list.findIndex((p) => p?.status === "wait");
  if (firstWaitIdx > 0) return firstWaitIdx - 1;
  // å¦‚果没有待生产:
  // - ä¸”最后一条是生产中:激活最后一条
  // - å¦åˆ™ï¼šæ²¡æœ‰æ¿€æ´»æ ·å¼
  const lastIdx = list.length - 1;
  if (lastIdx >= 0 && list[lastIdx]?.status === "process") return lastIdx;
  return null;
});
const currentProcess = computed(() => {
  const list = processes.value || [];
  return list[active.value] || null;
});
// å½“前工序进度:用产出/投入估算(UI å…ˆè·‘通,后续按真实规则替换)
const currentProcessPercentage = computed(() => {
  const p = currentProcess.value;
// å·¥åºè¿›åº¦ï¼šç”¨äº§å‡º/投入估算(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;
  return clampPercentage((output / input) * 100);
});
};
// 30/50/80/100 åˆ†æ®µé¢œè‰²ï¼šçº¢/橙/蓝/绿
const progressColor = (percentage) => {
  const p = clampPercentage(percentage);
  if (p < 30) return "#f56c6c";
  if (p < 50) return "#e6a23c";
  if (p < 80) return "#409eff";
  return "#67c23a";
};
// ä¸è‰¯çŽ‡ï¼šä¸è‰¯æ•°é‡ / äº§å‡ºæ•°é‡ï¼ˆå…ˆæŒ‰æ­¤å£å¾„,后续对接接口可调整)
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%";
@@ -327,13 +472,15 @@
    align-items: center;
    padding: 2px 6px;
    border-radius: 6px;
    transition: background-color 0.15s ease;
    transition: background-color 0.2s ease, color 0.2s ease, transform 0.2s ease;
    &:hover {
      background: #f5f7fa;
      transform: translateX(2px);
    }
    &.selected {
      background: rgba(64, 158, 255, 0.12);
      background: rgba(64, 158, 255, 0.18);
      color: #409eff;
      font-weight: 700;
    }
  }
@@ -371,6 +518,8 @@
  }
  .step-panel {
    position: relative;
    cursor: pointer;
    background: #f6f8fb;
    border: 1px solid #ebeef5;
    border-radius: 10px;
@@ -378,6 +527,93 @@
    width: 100%;
    max-width: none;
    box-sizing: border-box;
    transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease,
      background 0.25s ease;
    &:hover {
      border-color: #c6e2ff;
      box-shadow: 0 4px 14px rgba(64, 158, 255, 0.12);
      transform: translateY(-1px);
    }
  }
  /* è¢«é€‰ä¸­ï¼šæ²¿ç”¨åŽŸã€Œé«˜äº®ã€è¯­ä¹‰å¹¶åŠ å‘¼å¸åŠ¨ç”» */
  .step-panel-selected {
    background: #ecf5ff;
    border-color: #409eff;
    animation: step-panel-selected-pulse 2.2s ease-in-out infinite;
    &:hover {
      transform: translateY(-1px);
    }
  }
  @keyframes step-panel-selected-pulse {
    0%,
    100% {
      box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.22);
    }
    50% {
      box-shadow: 0 0 0 6px rgba(64, 158, 255, 0.12);
    }
  }
  /* å½“前生产中的工序:橙色主题,与选中区分;若同时选中则以选中为主,仅保留角标 */
  .step-panel-current:not(.step-panel-selected) {
    background: #fdf6ec;
    border-color: #e6a23c;
    border-left: 4px solid #e6a23c;
    padding-left: 9px;
    &:hover {
      box-shadow: 0 4px 14px rgba(230, 162, 60, 0.18);
    }
  }
  .step-current-badge {
    position: absolute;
    top: 8px;
    right: 10px;
    z-index: 1;
    font-size: 11px;
    font-weight: 600;
    color: #b88230;
    background: rgba(230, 162, 60, 0.18);
    border: 1px solid rgba(230, 162, 60, 0.45);
    border-radius: 4px;
    padding: 2px 8px;
    line-height: 1.2;
    pointer-events: none;
  }
  .step-status-badge {
    position: absolute;
    top: 8px;
    right: 10px;
    z-index: 1;
    font-size: 11px;
    font-weight: 600;
    border-radius: 4px;
    padding: 2px 8px;
    line-height: 1.2;
    pointer-events: none;
    white-space: nowrap;
    border: 1px solid transparent;
  }
  .step-status-badge-success {
    color: #67c23a;
    background: rgba(103, 194, 58, 0.14);
    border-color: rgba(103, 194, 58, 0.35);
  }
  .step-status-badge-process {
    color: #b88230;
    background: rgba(230, 162, 60, 0.18);
    border-color: rgba(230, 162, 60, 0.45);
  }
  .step-status-badge-wait {
    color: #909399;
    background: rgba(144, 147, 153, 0.12);
    border-color: rgba(144, 147, 153, 0.35);
  }
  .right-panel {
@@ -439,6 +675,17 @@
    }
  }
  .report-record-placeholder {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 120px;
    color: #303133;
    .placeholder-tip {
      color: #909399;
    }
  }
  .step-meta {
    display: flex;
    gap: 16px;
src/views/productionManagement/productionReporting/components/Detail.vue
@@ -1,5 +1,5 @@
<script setup lang="ts">
import {computed, onMounted} from "vue";
import {computed, onMounted, ref} from "vue";
defineOptions({
  name: "ReportingDetail"
@@ -23,10 +23,19 @@
  set: (value: boolean) => emits("update:isShow", value),
});
const otherData = ref([])
const otherData = ref({})
const otherDataEntries = computed(() => {
  return Object.entries(otherData.value || {})
      .filter(([key]) => key !== "rows")
      .map(([key, value]) => ({key, value}));
});
const init = () => {
  if (props.row.otherData !== null && props.row.otherData !== '') {
    otherData.value = JSON.parse(props.row.otherData);
    try {
      otherData.value = JSON.parse(props.row.otherData);
    } catch {
      otherData.value = {};
    }
  }
};
@@ -52,7 +61,15 @@
      <el-descriptions-item label="单位">{{ row.scrapQty || '-' }}</el-descriptions-item>
      <el-descriptions-item label="创建时间">{{ row.createTime || '-' }}</el-descriptions-item>
      <el-descriptions-item
          v-for="item in otherData"
          v-for="item in otherDataEntries"
          :key="item.key"
          :label="item.key"
      >
        {{ item.value || '-' }}
      </el-descriptions-item>
      <el-descriptions-item
          v-for="item in (otherData.rows || [])"
          :key="item.parameterItem"
          :label="`${item.parameterItem}`"
      >
        {{ item.value || '-' }}
src/views/productionManagement/productionReporting/index.vue
@@ -148,6 +148,12 @@
        :isEdit="false"
        :row="currentReportRowData"
        @refreshData="getList"/>
    <GranulationForm
        v-if="granulationFormVisible"
        v-model:isShow="granulationFormVisible"
        :isEdit="false"
        :row="currentReportRowData"
        @refreshData="getList"/>
    <Detail
        v-if="showDetail"
        v-model:isShow="showDetail"
@@ -167,9 +173,10 @@
import {productionProductMainListPage} from "@/api/productionManagement/productionProductMain.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import InputModal from "@/views/productionManagement/productionReporting/Input.vue";
import VoltageSortingForm from "@/views/productionManagement/workOrder/components/VoltageSortingForm.vue";
const VoltageSortingForm = defineAsyncComponent(() => import("@/views/productionManagement/workOrder/components/VoltageSortingForm.vue"));
const CopperPrintingForm = defineAsyncComponent(() => import("@/views/productionManagement/workOrder/components/CopperPrintingForm.vue"));
const GranulationForm = defineAsyncComponent(() => import("@/views/productionManagement/workOrder/components/GranulationForm.vue"));
const Detail = defineAsyncComponent(() => import("@/views/productionManagement/productionReporting/components/Detail.vue"));
@@ -430,6 +437,7 @@
const isShowInput = ref(false);
const copperPrintingFormVisible = ref(false);
const voltageSortingFormVisible = ref(false);
const granulationFormVisible = ref(false);
const showDetail = ref(false);
const isShowingId = ref(0);
const showInput = row => {
@@ -457,6 +465,8 @@
    copperPrintingFormVisible.value = true;
  } else if (row.process === '电压分选') {
    voltageSortingFormVisible.value = true;
  } else if (row.process === '造粒') {
    granulationFormVisible.value = true;
  } else {
    showDetail.value = true;
  }
src/views/productionManagement/workOrder/components/GranulationForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,939 @@
<script setup lang="ts">
import {computed, onMounted, reactive, ref} from "vue";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import {ElMessage} from "element-plus";
import {addProductMain} from "@/api/productionManagement/workOrder.js";
defineOptions({
  name: "GranulationForm"
});
const props = defineProps({
  isShow: {
    type: Boolean,
    required: true
  },
  isEdit: {
    type: Boolean,
    default: false
  },
  row: {
    type: Object,
    default: () => ({}),
  }
});
const emits = defineEmits(["update:isShow", "refreshData"]);
const visible = computed({
  get: () => props.isShow,
  set: (value: boolean) => emits("update:isShow", value),
});
const formData = reactive({
  productProcessRouteItemId: undefined,
  workOrderId: undefined,
  planQuantity: undefined,
  reportWork: undefined,
  productMainId: undefined,
  quantity: undefined, // äº§å‡ºæ•°é‡
  userId: undefined, // ä½œä¸šå‘˜
  userName: undefined, // ä½œä¸šå‘˜
  otherData: {
    whiteMaterialNumber: undefined, // ç™½æ–™æ–™å·
    ingredientDateTime: undefined, // é…æ–™æ—¥æœŸæ—¶é—´
    weight: undefined, // é‡é‡
    stirredMillIncludesMixing: {
      oneBucketStartTime: undefined, // 1桶开始时间
      oneBucketEndTime: undefined, // 1桶结束时间
      twoBucketStartTime: undefined, // 2桶开始时间
      twoBucketEndTime: undefined, // 2桶结束时间
      fiveBucketStartTime: undefined, // 5桶开始时间
      fiveBucketEndTime: undefined, // 5桶结束时间
      powderWeight: undefined, // ç²‰æ–™é‡é‡
      waterWeight: undefined, // æ°´é‡é‡
      stirStartTime: undefined, //  æ…拌开始时间
      stirEndTime: undefined, //  æ…拌结束时间
      stirDispersantWeight: undefined, //  æ…拌分散剂重量
      userId: undefined,
      userName: undefined,
      confirmId: undefined,
      confirmName: undefined,
    },
    granulation: {
      output: undefined, // äº§å‡ºæ€»äº§é‡
      qualifiedMaterialWeight: undefined, // åˆæ ¼æ–™é‡é‡
      wallMaterial: undefined, // å£æ–™
      wasteMaterials: undefined, // å°¾æ–™
    },
    burningMaterial: {
      startTime: undefined, // å¼€å§‹æ—¶é—´
      endTime: undefined, // ç»“束时间
      furnaceNumber: undefined, // ç‚‰å·
      highestTemperatureZone: undefined, // æœ€é«˜æ¸©åŒº
      calcinationRate: undefined, // ç……烧速度
      calcinationWeight: undefined, // ç……烧重量
      userId: undefined,
      userName: undefined,
      confirmId: undefined,
      confirmName: undefined,
    },
    powder: {
      excess: undefined, // ç²‰ç¢Žæ–™æ”¾æ–™æ²¡è¶…过料斗限位线
    },
    burntBlockBatchNumber: undefined, // çƒ§å—料批次号
    burntBlockBatchNumberDate: undefined, // çƒ§å—料日期
    stirredMill: {
      threeBucketStartTime: undefined, // 3桶开始时间
      threeBucketEndTime: undefined, // 3桶结束时间
      threeBucketMixingPowderWeight: undefined, // 3桶加粉料重量
      fourBucketStartTime: undefined, // 4桶开始时间
      fourBucketEndTime: undefined, // 4桶结束时间
      fourBucketMixingPowderWeight: undefined, // 4桶加粉料重量
      sixBucketStartTime: undefined, // 6桶开始时间
      sixBucketEndTime: undefined, // 6桶结束时间
      sixBucketMixingPowderWeight: undefined, // 6桶加粉料重量
      userId: undefined,
      userName: undefined,
      confirmId: undefined,
      confirmName: undefined,
    },
    stir: {
      startTime: undefined,
      endTime: undefined,
      weightRatio: undefined, // é‡é‡æ¯”(水/料)
      pva: undefined, // PVA粘合剂重量
      dispersantWeight: undefined, // åˆ†æ•£å‰‚重量
      releaseAgentWeight: undefined, // è„±æ¨¡å‰‚重量
    },
    granulationB: {
      startTime: undefined,
      endTime: undefined,
      atomizerRotationalSpeed: undefined, // é›¾åŒ–器转速
      importSpeed: undefined, // è¿›å£é€Ÿåº¦
      outSpeed: undefined, // å‡ºå£é€Ÿåº¦
      qualifiedWeight: undefined, // åˆæ ¼é‡é‡
      wallMaterial: undefined, // å£æ–™
      wasteMaterials: undefined, // å°¾æ–™
      userId: undefined,
      userName: undefined,
      confirmId: undefined,
      confirmName: undefined,
    },
    remark: undefined, // å¤‡æ³¨
  }
})
const userOptions = ref([]);
const getUserList = () => {
  userListNoPageByTenantId()
      .then(res => {
        if (res.code === 200) {
          userOptions.value = res.data || [];
        }
      })
      .catch(err => {
        console.error("获取用户列表失败", err);
      });
};
// ç”¨æˆ·é€‰æ‹©å˜åŒ–æ—¶æ›´æ–° userName
const handleUserChange = (userId: any, reportType: string) => {
  if (userId) {
    const selectedUser = userOptions.value.find(user => user.userId === userId);
    switch (reportType) {
      case 'stirredMillIncludesMixingUserId':
        formData.otherData.stirredMillIncludesMixing.userName = selectedUser.userName;
        break;
      case 'stirredMillIncludesMixingConfirmId':
        formData.otherData.stirredMillIncludesMixing.confirmName = selectedUser.userName;
        break;
      case 'burningMaterialUserId':
        formData.otherData.burningMaterial.userName = selectedUser.userName;
        break;
      case 'burningMaterialConfirmId':
        formData.otherData.stirredMillIncludesMixing.confirmName = selectedUser.userName;
        break;
      case 'stirredMillUserId':
        formData.otherData.stirredMill.userName = selectedUser.userName;
        break;
      case 'stirredMillConfirmId':
        formData.otherData.stirredMill.confirmName = selectedUser.userName;
        break;
      case 'granulationBUserId':
        formData.otherData.granulationB.userName = selectedUser.userName;
        break;
      case 'granulationBConfirmId':
        formData.otherData.granulationB.confirmName = selectedUser.userName;
        break;
    }
  } else {
    switch (reportType) {
      case 'stirredMillIncludesMixingUserId':
        formData.otherData.stirredMillIncludesMixing.userName = "";
        break;
      case 'stirredMillIncludesMixingConfirmId':
        formData.otherData.stirredMillIncludesMixing.confirmName = "";
        break;
      case 'burningMaterialUserId':
        formData.otherData.burningMaterial.userName = "";
        break;
      case 'burningMaterialConfirmId':
        formData.otherData.stirredMillIncludesMixing.confirmName = "";
        break;
      case 'stirredMillUserId':
        formData.otherData.stirredMill.userName = "";
        break;
      case 'stirredMillConfirmId':
        formData.otherData.stirredMill.confirmName = "";
        break;
      case 'granulationBUserId':
        formData.otherData.granulationB.userName = "";
        break;
      case 'granulationBConfirmId':
        formData.otherData.granulationB.confirmName = "";
        break;
    }
  }
};
const handleReport = () => {
  if (!formData.otherData.stirredMillIncludesMixing.userId && !formData.otherData.stirredMill.userId && !formData.otherData.burningMaterial.userId && !formData.otherData.granulationB.userId) {
    ElMessage.error('请选择作业员')
    return;
  }
  if (!formData.quantity || formData.quantity <= 0) {
    ElMessage.error('请输入生产数量')
    return;
  }
  formData.userId = formData.otherData.stirredMillIncludesMixing.userId || formData.otherData.stirredMill.userId || formData.otherData.burningMaterial.userId || formData.otherData.granulationB.userId;
  const otherData = JSON.stringify(formData.otherData);
  const submitData = {
    ...formData,
    otherData: otherData
  };
  addProductMain(submitData).then(res => {
    if (res.code === 200) {
      ElMessage({
        message: '报工成功',
        type: 'success',
      })
      emits("refreshData")
      visible.value = false;
    } else {
      ElMessage.error('报工失败')
    }
  });
};
const initData = () => {
  if (!props.isEdit) {
    formData.otherData = JSON.parse(props.row.otherData || '{}');
    formData.quantity = props.row.quantity;
  } else {
    const row = props.row;
    formData.planQuantity = row.planQuantity
    formData.productProcessRouteItemId = row.productProcessRouteItemId
    formData.workOrderId = row.id
    formData.reportWork = row.reportWork
    formData.productMainId = row.productMainId
  }
}
const displayValue = (value: any) => {
  return value === undefined || value === null || value === "" ? "-" : value;
};
onMounted(() => {
  getUserList();
  initData()
})
</script>
<template>
  <el-dialog v-model="visible"
             :title="isEdit? '造粒报工' : '详情'"
             width="90%">
    <el-form :model="formData">
      <table class="report-table">
        <tbody>
        <tr>
          <td class="label" colspan="2">白料料号:</td>
          <td colspan="3">
            <el-input v-if="props.isEdit" v-model="formData.otherData.whiteMaterialNumber" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.whiteMaterialNumber) }}</span>
          </td>
          <td class="label" colspan="2">配料日期:</td>
          <td colspan="5">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.ingredientDateTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.ingredientDateTime) }}</span>
          </td>
          <td class="label" colspan="2">重量:</td>
          <td colspan="2">
            <el-input v-if="props.isEdit" v-model="formData.otherData.weight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.weight) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="2" rowspan="5">搅拌磨(含搅拌):</td>
          <td class="label" colspan="7">搅拌磨</td>
          <td class="label" colspan="5" rowspan="2">搅拌桶搅拌</td>
          <td class="label"  colspan="2" rowspan="3">
            <span>作业员:</span>
            <el-select v-model="formData.otherData.stirredMillIncludesMixing.userId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择作业员"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'stirredMillIncludesMixingUserId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.userName || formData.otherData.stirredMillIncludesMixing.userId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="2"></td>
          <td class="label" colspan="1">1号桶:</td>
          <td class="label" colspan="2">2号桶:</td>
          <td class="label" colspan="2">5号桶:</td>
        </tr>
        <tr>
          <td class="label" colspan="2">开始时间:</td>
          <td colspan="1">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.oneBucketStartTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.oneBucketStartTime) }}</span>
          </td>
          <td colspan="2">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.twoBucketStartTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.twoBucketStartTime) }}</span>
          </td>
          <td colspan="2">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.fiveBucketStartTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.fiveBucketStartTime) }}</span>
          </td>
            <td class="label" colspan="5">
              <span>开始时间:</span>
              <el-date-picker
                  v-if="props.isEdit"
                  v-model="formData.otherData.stirredMillIncludesMixing.stirStartTime"
                  type="datetime"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  format="YYYY-MM-DD HH:mm:ss"
                  placeholder="请选择"
                  style="width: 100%"
              />
              <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.stirStartTime) }}</span>
            </td>
        </tr>
        <tr>
          <td class="label" colspan="2">结束时间:</td>
          <td colspan="1">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.oneBucketEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.oneBucketEndTime) }}</span>
          </td>
          <td colspan="2">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.twoBucketEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.twoBucketEndTime) }}</span>
          </td>
          <td colspan="2">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.fiveBucketEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.fiveBucketEndTime) }}</span>
          </td>
          <td colspan="5">
            <span>结束时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMillIncludesMixing.stirEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.stirEndTime) }}</span>
          </td>
          <td class="label"  colspan="2" rowspan="3">
            <span>确认人:</span>
            <el-select v-model="formData.otherData.stirredMillIncludesMixing.confirmId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择确认人"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'stirredMillIncludesMixingConfirmId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.confirmName || formData.otherData.stirredMillIncludesMixing.confirmId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="2">粉料重量:</td>
          <td colspan="1">
            <el-input v-if="props.isEdit" v-model="formData.otherData.stirredMillIncludesMixing.powderWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.powderWeight) }}</span>
            <span>KG</span>
          </td>
          <td class="label" colspan="2">水重量:</td>
          <td colspan="2">
            <el-input v-if="props.isEdit" v-model="formData.otherData.stirredMillIncludesMixing.waterWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.waterWeight) }}</span>
            <span>KG</span>
          </td>
          <td class="label" colspan="2">分散剂重量:</td>
          <td colspan="3">
            <el-input v-if="props.isEdit" v-model="formData.otherData.stirredMillIncludesMixing.stirDispersantWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMillIncludesMixing.stirDispersantWeight) }}</span>
            <span>g</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="2">造粒:</td>
          <td class="label" colspan="2">产出总重量:</td>
          <td colspan="1">
            <el-input v-if="props.isEdit" v-model="formData.otherData.granulation.output" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulation.output) }}</span>
            <span>KG</span>
          </td>
          <td class="label" colspan="2">合格料重量:</td>
          <td colspan="2">
            <el-input v-if="props.isEdit" v-model="formData.otherData.granulation.qualifiedMaterialWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulation.qualifiedMaterialWeight) }}</span>
            <span>KG</span>
          </td>
          <td class="label" colspan="2">
            å£æ–™ï¼š
            <el-input v-if="props.isEdit" v-model="formData.otherData.granulation.wallMaterial" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulation.wallMaterial) }}</span>
          </td>
          <td colspan="3">
            å°¾æ–™ï¼š
            <el-input v-if="props.isEdit" v-model="formData.otherData.granulation.wasteMaterials" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulation.wasteMaterials) }}</span>
            <span>KG</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="2" rowspan="3">烧料:</td>
          <td class="label"  colspan="7">
            <span>开始时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.burningMaterial.startTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.startTime) }}</span>
          </td>
          <td class="label"  colspan="5">
            <span>结束时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.burningMaterial.endTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.endTime) }}</span>
          </td>
          <td class="label"  colspan="2" rowspan="2">
            <span>作业员:</span>
            <el-select v-model="formData.otherData.burningMaterial.userId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择作业员"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'burningMaterialUserId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.userName || formData.otherData.burningMaterial.userId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="7">
            <span>炉号:</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.burningMaterial.furnaceNumber" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.furnaceNumber) }}</span>
          </td>
          <td class="label"  colspan="5">
            <span>最高温度区:</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.burningMaterial.highestTemperatureZone" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.highestTemperatureZone) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="7">
            <span>煅烧速度:</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.burningMaterial.calcinationRate" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.calcinationRate) }}</span>
            <span>min/板</span>
          </td>
          <td class="label"  colspan="5">
            <span>煅烧后重量:</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.burningMaterial.calcinationWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.calcinationWeight) }}</span>
            <span>KG</span>
          </td>
          <td class="label"  colspan="2" rowspan="2">
            <span>确认人:</span>
            <el-select v-model="formData.otherData.burningMaterial.confirmId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择确认人"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'burningMaterialConfirmId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burningMaterial.confirmName || formData.otherData.burningMaterial.confirmId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="2">粉料</td>
          <td class="label"  colspan="12">
            <span>粉碎料放料没超过料斗限位线:</span>
            <el-checkbox-group v-if="props.isEdit" v-model="formData.otherData.powder.excess">
              <el-checkbox label="是" value="是" />
              <el-checkbox label="否" value="否" />
            </el-checkbox-group>
            <span v-else class="view-value">{{ displayValue(formData.otherData.powder.excess) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="9">
            <span>烧块料批号:</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.burntBlockBatchNumber" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burntBlockBatchNumber) }}</span>
          </td>
          <td class="label"  colspan="2">日期:</td>
          <td class="label"  colspan="3">
            <el-input v-if="props.isEdit" v-model="formData.otherData.burntBlockBatchNumberDate" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.burntBlockBatchNumberDate) }}</span>
          </td>
          <td class="label"  colspan="2" rowspan="4">
            <span>作业员:</span>
            <el-select v-model="formData.otherData.stirredMill.userId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择作业员"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'stirredMillUserId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.userName || formData.otherData.stirredMill.userId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="2" rowspan="4">搅拌磨</td>
          <td class="label"  colspan="2"></td>
          <td class="label"  colspan="3">3号桶</td>
          <td class="label"  colspan="4">4号桶</td>
          <td class="label"  colspan="3">6号桶</td>
        </tr>
        <tr>
          <td class="label"  colspan="2">搅拌粉料重量:</td>
          <td class="label"  colspan="3">
            <el-input v-if="props.isEdit" v-model="formData.otherData.stirredMill.threeBucketMixingPowderWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.threeBucketMixingPowderWeight) }}</span>
          </td>
          <td class="label"  colspan="4">
            <el-input v-if="props.isEdit" v-model="formData.otherData.stirredMill.fourBucketMixingPowderWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.fourBucketMixingPowderWeight) }}</span>
          </td>
          <td class="label"  colspan="3">
            <el-input v-if="props.isEdit" v-model="formData.otherData.stirredMill.sixBucketMixingPowderWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.sixBucketMixingPowderWeight) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="2">开始时间:</td>
          <td class="label"  colspan="3">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMill.threeBucketStartTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.threeBucketStartTime) }}</span>
          </td>
          <td class="label"  colspan="4">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMill.fourBucketStartTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.fourBucketStartTime) }}</span>
          </td>
          <td class="label"  colspan="3">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMill.sixBucketStartTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.sixBucketStartTime) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="2">结束时间:</td>
          <td class="label"  colspan="3">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMill.threeBucketEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.threeBucketEndTime) }}</span>
          </td>
          <td class="label"  colspan="4">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMill.fourBucketEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.fourBucketEndTime) }}</span>
          </td>
          <td class="label"  colspan="3">
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stirredMill.sixBucketEndTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.sixBucketEndTime) }}</span>
          </td>
          <td class="label"  colspan="2" rowspan="3">
            <span>确认人:</span>
            <el-select v-model="formData.otherData.stirredMill.confirmId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择确认人"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'stirredMillConfirmId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stirredMill.confirmName || formData.otherData.stirredMill.confirmId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="2" rowspan="2">搅拌</td>
          <td class="label"  colspan="4">
            <span>开始时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stir.startTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stir.startTime) }}</span>
          </td>
          <td class="label"  colspan="4">
            <span>结束时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.stir.endTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.stir.endTime) }}</span>
          </td>
          <td class="label" colspan="4">
            <span>重量比(水/料):</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.stir.weightRatio" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stir.weightRatio) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="4">
            <span>PVA粘合剂重量 ï¼š</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.stir.pva" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stir.pva) }}</span>
          </td>
          <td class="label" colspan="4">
            <span>分散剂重量 ï¼š</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.stir.dispersantWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stir.dispersantWeight) }}</span>
          </td>
          <td class="label" colspan="4">
            <span>脱模剂重量 ï¼š</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.stir.releaseAgentWeight" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.stir.releaseAgentWeight) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label"  colspan="2" rowspan="4">造粒</td>
          <td class="label"  colspan="6">
            <span>开始时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.granulationB.startTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.startTime) }}</span>
          </td>
          <td class="label"  colspan="6">
            <span>结束时间:</span>
            <el-date-picker
                v-if="props.isEdit"
                v-model="formData.otherData.granulationB.endTime"
                type="datetime"
                value-format="YYYY-MM-DD HH:mm:ss"
                format="YYYY-MM-DD HH:mm:ss"
                placeholder="请选择"
                style="width: 100%"
            />
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.endTime) }}</span>
          </td>
          <td class="label"  colspan="2" rowspan="2">
            <span>作业员:</span>
            <el-select v-model="formData.otherData.granulationB.userId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择作业员"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'granulationBUserId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.userName || formData.otherData.granulationB.userId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="4">
            <span>雾化器转速 ï¼š</span>
            <el-input  v-if="props.isEdit" v-model="formData.otherData.granulationB.atomizerRotationalSpeed" placeholder="请输入">
              <template #append>r/min</template>
            </el-input>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.atomizerRotationalSpeed) }}</span>
          </td>
          <td class="label" colspan="4">
            <span>进口速度 ï¼š</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.granulationB.importSpeed" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.importSpeed) }}</span>
          </td>
          <td class="label" colspan="4">
            <span>出口速度 ï¼š</span>
            <el-input v-if="props.isEdit" v-model="formData.otherData.granulationB.outSpeed" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.outSpeed) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="6">
            <span>产出总数 ï¼š</span>
            <el-input  v-if="props.isEdit" v-model="formData.quantity" placeholder="请输入">
              <template #append>KG</template>
            </el-input>
            <span v-else class="view-value">{{ displayValue(formData.quantity) }}</span>
          </td>
          <td class="label" colspan="6">
            <span>合格重量 ï¼š</span>
            <el-input  v-if="props.isEdit" v-model="formData.otherData.granulationB.qualifiedWeight" placeholder="请输入">
              <template #append>KG</template>
            </el-input>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.qualifiedWeight) }}</span>
          </td>
          <td class="label"  colspan="2" rowspan="2">
            <span>确认人:</span>
            <el-select v-model="formData.otherData.granulationB.confirmId"
                       v-if="props.isEdit"
                       style="width: 100%"
                       placeholder="请选择确认人"
                       clearable
                       filterable
                       @change="handleUserChange($event, 'stirredMillConfirmId')">
              <el-option v-for="user in userOptions"
                         :key="user.userId"
                         :label="user.userName"
                         :value="user.userId"/>
            </el-select>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.confirmName || formData.otherData.granulationB.confirmId) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="6">
            <span>尾料 ï¼š</span>
            <el-input  v-if="props.isEdit" v-model="formData.otherData.granulationB.wasteMaterials" placeholder="请输入">
              <template #append>KG</template>
            </el-input>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.wasteMaterials) }}</span>
          </td>
          <td class="label" colspan="6">
            <span>壁料 ï¼š</span>
            <el-input  v-if="props.isEdit" v-model="formData.otherData.granulationB.wallMaterial" placeholder="请输入">
              <template #append>KG</template>
            </el-input>
            <span v-else class="view-value">{{ displayValue(formData.otherData.granulationB.wallMaterial) }}</span>
          </td>
        </tr>
        <tr>
          <td class="label" colspan="16">
            <span>备注 ï¼š</span>
            <el-input  v-if="props.isEdit" v-model="formData.otherData.remark" placeholder="请输入"/>
            <span v-else class="view-value">{{ displayValue(formData.otherData.remark) }}</span>
          </td>
        </tr>
        </tbody>
      </table>
    </el-form>
    <template #footer>
        <span class="dialog-footer">
          <el-button v-if="props.isEdit" type="primary"
                     @click="handleReport">确定</el-button>
          <el-button @click="visible = false">{{ props.isEdit ? "取消" : "关闭" }}</el-button>
        </span>
    </template>
  </el-dialog>
</template>
<style scoped>
.report-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 13px;
}
.report-table td {
  border: 1px solid #dcdfe6;
  padding: 6px;
  vertical-align: middle;
}
.report-table .label {
  width: 90px;
  background: #f5f7fa;
  text-align: center;
  font-weight: 500;
}
.report-table .tip {
  text-align: center;
  font-weight: 500;
  line-height: 1.4;
  background: #fafafa;
}
.view-value {
  display: inline-block;
  min-height: 32px;
  line-height: 32px;
}
</style>
src/views/productionManagement/workOrder/components/ProductionRecordForm.vue
@@ -12,6 +12,10 @@
    default() {
      return [];
    }
  },
  labelWidth: {
    type: Number,
    default: 120
  }
});
@@ -79,7 +83,7 @@
</script>
<template>
  <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px">
  <el-form ref="formRef" :model="formData" :rules="rules" :label-width="`${labelWidth}px`">
    <el-form-item
        v-for="(item, index) in formData.list"
        :key="item.id"
src/views/productionManagement/workOrder/index.vue
@@ -234,6 +234,12 @@
        :isEdit="true"
        :row="currentReportRowData"
        @refreshData="getList"/>
    <GranulationForm
        v-if="granulationFormVisible"
        v-model:isShow="granulationFormVisible"
        :isEdit="true"
        :row="currentReportRowData"
        @refreshData="getList"/>
  </div>
</template>
@@ -261,6 +267,7 @@
const CopperPrintingForm = defineAsyncComponent(() => import("./components/CopperPrintingForm.vue"));
const VoltageSortingForm = defineAsyncComponent(() => import("./components/VoltageSortingForm.vue"));
const ProductionRecordForm = defineAsyncComponent(() => import("./components/ProductionRecordForm.vue"));
const GranulationForm = defineAsyncComponent(() => import("./components/GranulationForm.vue"));
const tableColumn = ref([
  {
    label: "优先级",
@@ -388,6 +395,7 @@
const editDialogVisible = ref(false);
const copperPrintingFormVisible = ref(false);
const voltageSortingFormVisible = ref(false);
const granulationFormVisible = ref(false);
const transferCardVisible = ref(false);
const transferCardData = ref([]);
const transferCardQrUrl = ref("");
@@ -406,7 +414,9 @@
  productProcessRouteItemId: "",
  userId: "",
  productMainId: null,
  otherData: {}
  otherData: {
    rows: [],
  }
});
const productionRecordFormRef = ref();
@@ -663,6 +673,10 @@
    voltageSortingFormVisible.value = true
    return;
  }
  if (row.processName === '造粒') {
    granulationFormVisible.value = true
    return;
  }
  processParamList.value = await getProcessParamList(row)
  reportForm.planQuantity = row.planQuantity;
  reportForm.quantity = row.quantity !== undefined && row.quantity !== null ? row.quantity : null;
@@ -695,7 +709,7 @@
const handleReport = async () => {
  const data = await productionRecordFormRef.value.submitData()
  reportForm.otherData = JSON.stringify(data || {});
  reportForm.otherData.rows = JSON.stringify(data || {});
  reportFormRef.value?.validate((valid) => {
    if (!valid) {
      return false;
vite.config.js
@@ -7,16 +7,16 @@
  const env = loadEnv(mode, process.cwd());
  const { VITE_APP_ENV } = env;
  const baseUrl =
      env.VITE_APP_ENV === "development"
          ? "http://1.15.17.182:9009"
          : env.VITE_BASE_API;
    env.VITE_APP_ENV === "development"
      ? "http://1.15.17.182:9009"
      : env.VITE_BASE_API;
  const javaUrl =
      env.VITE_APP_ENV === "development"
          ? "http://1.15.17.182:9009"
          : env.VITE_JAVA_API;
    env.VITE_APP_ENV === "development"
      ? "http://1.15.17.182:9008"
      : env.VITE_JAVA_API;
  return {
    define:{
      __BASE_API__: JSON.stringify(javaUrl)
    define: {
      __BASE_API__: JSON.stringify(javaUrl),
    },
    // éƒ¨ç½²ç”Ÿäº§çŽ¯å¢ƒå’Œå¼€å‘çŽ¯å¢ƒä¸‹çš„URL。
    // é»˜è®¤æƒ…况下,vite ä¼šå‡è®¾ä½ çš„应用是被部署在一个域名的根路径上
@@ -51,7 +51,7 @@
    },
    // vite ç›¸å…³é…ç½®
    server: {
      port: 80,
      port: 8001,
      host: true,
      open: true,
      proxy: {