From 384fb98c2d78e3ef2d37af24eb9da56a4e8be8da Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期一, 23 三月 2026 11:17:56 +0800
Subject: [PATCH] Merge branch 'dev_银川_中盛建材' of http://114.132.189.42:9002/r/product-inventory-management into dev_银川_中盛建材

---
 src/views/productionManagement/productionReporting/components/ReportingDialog.vue |  588 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 588 insertions(+), 0 deletions(-)

diff --git a/src/views/productionManagement/productionReporting/components/ReportingDialog.vue b/src/views/productionManagement/productionReporting/components/ReportingDialog.vue
new file mode 100644
index 0000000..8eff508
--- /dev/null
+++ b/src/views/productionManagement/productionReporting/components/ReportingDialog.vue
@@ -0,0 +1,588 @@
+<template>
+  <el-dialog v-model="localVisible"
+             :title="dialogTitle"
+             width="800px"
+             @close="handleClose">
+    <!-- 姝ラ鏉� -->
+    <el-steps :active="activeStep"
+              finish-status="success">
+      <el-step title="閫夋嫨鐢熶骇璁㈠崟" />
+      <el-step title="濉啓鍩虹淇℃伅" />
+      <el-step title="鏌ョ湅宸ュ簭鍙傛暟" />
+      <el-step title="濉啓浜ч噺淇℃伅" />
+    </el-steps>
+    <!-- 绗竴姝ワ細閫夋嫨鐢熶骇璁㈠崟 -->
+    <div v-if="activeStep === 0">
+      <el-form :model="form"
+               ref="formRef"
+               label-width="120px">
+        <el-form-item label="鐢熶骇璁㈠崟"
+                      prop="orderId"
+                      required>
+          <el-select v-model="orderId"
+                     placeholder="璇烽�夋嫨鐢熶骇璁㈠崟"
+                     clearable
+                     filterable
+                     style="width: 100%"
+                     :loading="orderLoading"
+                     @change="handleOrderChange">
+            <el-option v-for="order in orderList"
+                       :key="order.id"
+                       :label="`${order.npsNo} - ${order.productName} ${order.model}`"
+                       :value="order.id" />
+          </el-select>
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 绗簩姝ワ細濉啓鍩虹淇℃伅 -->
+    <div v-else-if="activeStep === 1">
+      <el-form :model="form"
+               :rules="rules"
+               ref="formRef"
+               label-width="120px">
+        <el-form-item label="鐢熶骇璁㈠崟鍙�"
+                      prop="npsNo">
+          <el-input disabled
+                    v-model="form.npsNo" />
+        </el-form-item>
+        <el-form-item label="鐝粍"
+                      prop="teamName"
+                      required>
+          <el-select v-model="form.teamName"
+                     placeholder="璇烽�夋嫨鐝粍"
+                     style="width: 100%">
+            <el-option label="鐧界彮"
+                       value="鐧界彮" />
+            <el-option label="澶滅彮"
+                       value="澶滅彮" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="浜у搧缂栫爜"
+                      prop="materialCode">
+          <el-input disabled
+                    v-model="form.materialCode" />
+        </el-form-item>
+        <el-form-item label="浜у搧鍚嶇О"
+                      prop="productName">
+          <el-input disabled
+                    v-model="form.productName" />
+        </el-form-item>
+        <el-form-item label="瑙勬牸"
+                      prop="specification">
+          <el-input disabled
+                    v-model="form.specification" />
+        </el-form-item>
+        <el-form-item label="鍒涘缓浜�"
+                      prop="createBy"
+                      required>
+          <el-input v-model="form.createBy"
+                    placeholder="璇疯緭鍏ュ垱寤轰汉" />
+        </el-form-item>
+        <el-form-item label="鍒涘缓鏃堕棿"
+                      prop="createTime">
+          <el-date-picker disabled
+                          v-model="form.createTime"
+                          type="datetime"
+                          placeholder="璇烽�夋嫨鍒涘缓鏃堕棿"
+                          style="width: 100%" />
+        </el-form-item>
+      </el-form>
+    </div>
+    <!-- 绗笁姝ワ細鏌ョ湅宸ュ簭鍙傛暟 -->
+    <div v-else-if="activeStep === 2">
+      <!-- 宸ュ簭Tab椤� -->
+      <el-tabs v-model="activeProcessId"
+               @tab-click="handleTabClick">
+        <el-tab-pane v-for="process in processList"
+                     :key="process.id"
+                     :label="process.processName"
+                     :name="process.id + ''">
+          <div>
+            <!-- 鍙傛暟缁勫垪琛� -->
+            <div v-for="(group, groupIndex) in form.paramGroups[process.id] || []"
+                 :key="groupIndex"
+                 class="param-group">
+              <div class="group-header">
+                <span>鍙傛暟缁� {{ groupIndex + 1 }}</span>
+                <el-button type="danger"
+                           size="small"
+                           @click="removeParamGroup(process.id, groupIndex)"
+                           v-if="(form.paramGroups[process.id] || []).length > 1">
+                  鍒犻櫎
+                </el-button>
+              </div>
+              <el-form :model="form"
+                       ref="formRef"
+                       label-width="120px">
+                <!-- 鍔ㄦ�佸弬鏁� -->
+                <el-form-item v-for="param in params"
+                              :key="param.id"
+                              :label="param.paramName">
+                  <template v-if="param.paramType == '1'">
+                    <!-- 鏁板瓧绫诲瀷 -->
+                    <div style="display: flex; align-items: center; gap: 8px;">
+                      <el-input-number v-model="group[param.id]"
+                                       controls-position="right"
+                                       :precision="getPrecision(param.paramFormat)"
+                                       style="flex: 1" />
+                      <span v-if="param.unit && param.unit != '/'">
+                        {{ param.unit }}
+                      </span>
+                    </div>
+                  </template>
+                  <template v-else-if="param.paramType == '2'">
+                    <!-- 鏂囨湰绫诲瀷 -->
+                    <div style="display: flex; align-items: center; gap: 8px;">
+                      <el-input v-model="group[param.id]"
+                                style="flex: 1" />
+                      <span v-if="param.unit && param.unit != '/'">
+                        {{ param.unit }}
+                      </span>
+                    </div>
+                  </template>
+                  <template v-else-if="param.paramType == '3'">
+                    <!-- 瀛楀吀绫诲瀷 -->
+                    <div style="display: flex; align-items: center; gap: 8px;">
+                      <el-select v-model="group[param.id]"
+                                 placeholder="璇烽�夋嫨"
+                                 style="flex: 1;width: 150px">
+                        <el-option v-for="option in dictOptions[param.paramFormat] || []"
+                                   :key="option.dictValue"
+                                   :label="option.dictLabel"
+                                   :value="option.dictValue" />
+                      </el-select>
+                      <span v-if="param.unit && param.unit != '/'">
+                        {{ param.unit }}
+                      </span>
+                    </div>
+                  </template>
+                  <template v-else-if="param.paramType == '4'">
+                    <!-- 鏃ユ湡绫诲瀷 -->
+                    <div style="display: flex; align-items: center; gap: 8px;">
+                      <el-date-picker :value-format="param.paramFormat"
+                                      :format="param.paramFormat"
+                                      :type="param.paramFormat=='YYYY-MM-DD'?'daterange':'datetimerange'"
+                                      v-model="group[param.id]"
+                                      style="flex: 1" />
+                      <span v-if="param.unit && param.unit != '/'">
+                        {{ param.unit }}
+                      </span>
+                    </div>
+                  </template>
+                  <template v-else>
+                    <!-- 鍏朵粬绫诲瀷 -->
+                    <div style="display: flex; align-items: center; gap: 8px;">
+                      <el-input v-model="group[param.id]"
+                                style="flex: 1" />
+                      <span v-if="param.unit && param.unit != '/'">
+                        {{ param.unit }}
+                      </span>
+                    </div>
+                  </template>
+                </el-form-item>
+              </el-form>
+            </div>
+            <!-- 鏂板鍙傛暟缁勬寜閽� -->
+            <el-button type="primary"
+                       size="small"
+                       @click="addParamGroup(process.id)">
+              鏂板鍙傛暟缁�
+            </el-button>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </div>
+    <!-- 绗洓姝ワ細濉啓浜ч噺淇℃伅 -->
+    <div v-else-if="activeStep === 3">
+      <el-form :model="form"
+               :rules="rules"
+               ref="formRef"
+               label-width="120px">
+        <el-form-item label="浜у嚭鏂归噺"
+                      prop="outputVolume"
+                      required>
+          <el-input-number v-model="form.outputVolume"
+                           :min="0"
+                           :precision="2"
+                           style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="涓嶅悎鏍兼柟閲�"
+                      prop="unqualifiedVolume"
+                      required>
+          <el-input-number v-model="form.unqualifiedVolume"
+                           :min="0"
+                           :precision="2"
+                           style="width: 100%" />
+        </el-form-item>
+        <el-form-item label="瀹屾垚鏂归噺"
+                      prop="completedVolume"
+                      required>
+          <el-input-number v-model="form.completedVolume"
+                           :min="0"
+                           :precision="2"
+                           style="width: 100%" />
+        </el-form-item>
+      </el-form>
+    </div>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="handleClose">鍙� 娑�</el-button>
+        <el-button type="primary"
+                   v-if="activeStep > 0"
+                   @click="activeStep--">涓婁竴姝�</el-button>
+        <el-button type="primary"
+                   v-if="activeStep < 3"
+                   @click="handleNextStep">涓嬩竴姝�</el-button>
+        <el-button type="primary"
+                   v-if="activeStep === 3"
+                   :loading="submitLoading"
+                   @click="handleSubmit">纭� 璁�</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { ref, reactive, computed, watch } from "vue";
+  import { ElMessage } from "element-plus";
+  import { getDicts } from "@/api/system/dict/data";
+  import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
+  import {
+    findProductProcessRouteItemList,
+    findProcessParamListOrder,
+  } from "@/api/productionManagement/productProcessRoute.js";
+
+  const props = defineProps({
+    visible: {
+      type: Boolean,
+      default: false,
+    },
+    data: {
+      type: Object,
+      default: () => ({}),
+    },
+  });
+
+  const emit = defineEmits(["update:visible", "completed"]);
+
+  const dialogTitle = computed(() => (props.data.id ? "缂栬緫鎶ュ伐" : "鏂板鎶ュ伐"));
+
+  const formRef = ref(null);
+  const submitLoading = ref(false);
+  const orderLoading = ref(false);
+  const processLoading = ref(false);
+  const localVisible = ref(props.visible);
+  const activeStep = ref(0);
+
+  const orderId = ref(props.data.orderId || "");
+  const processId = ref(props.data.processId || "");
+  const activeProcessId = ref("");
+  const orderList = ref([]);
+  const processList = ref([]);
+  const params = ref([]);
+  const dictOptions = ref({});
+
+  const form = reactive({
+    id: props.data.id || undefined,
+    orderId: props.data.orderId || "",
+    npsNo: props.data.npsNo || "",
+    teamName: props.data.teamName || "",
+    materialCode: props.data.materialCode || "",
+    productName: props.data.productName || "",
+    specification: props.data.specification || "",
+    outputVolume: props.data.outputVolume || 0,
+    unqualifiedVolume: props.data.unqualifiedVolume || 0,
+    completedVolume: props.data.completedVolume || 0,
+    createBy: props.data.createBy || "褰撳墠鐧诲綍浜�",
+    createTime: props.data.createTime || new Date(),
+    paramGroups: props.data.paramGroups || {}, // 瀛樺偍姣忎釜宸ュ簭鐨勫弬鏁扮粍
+  });
+
+  const rules = {
+    teamName: [{ required: true, message: "璇烽�夋嫨鐝粍", trigger: "blur" }],
+    outputVolume: [
+      { required: true, message: "璇疯緭鍏ヤ骇鍑烘柟閲�", trigger: "blur" },
+    ],
+    unqualifiedVolume: [
+      { required: true, message: "璇疯緭鍏ヤ笉鍚堟牸鏂归噺", trigger: "blur" },
+    ],
+    completedVolume: [
+      { required: true, message: "璇疯緭鍏ュ畬鎴愭柟閲�", trigger: "blur" },
+    ],
+    createBy: [{ required: true, message: "璇疯緭鍏ュ垱寤轰汉", trigger: "blur" }],
+  };
+
+  // 鍔犺浇鐢熶骇璁㈠崟鍒楄〃
+  const loadOrders = () => {
+    orderLoading.value = true;
+    productOrderListPage({ pageNum: 1, pageSize: 100 })
+      .then(res => {
+        orderList.value = res.data.records || [];
+      })
+      .finally(() => {
+        orderLoading.value = false;
+      });
+  };
+
+  // 澶勭悊鐢熶骇璁㈠崟閫夋嫨
+  const handleOrderChange = val => {
+    if (val) {
+      const order = orderList.value.find(item => item.id === val);
+      if (order) {
+        form.orderId = val;
+        form.npsNo = order.npsNo;
+        form.materialCode = order.materialCode;
+        form.productName = order.productName;
+        form.specification = order.model;
+      }
+      // 鍔犺浇宸ュ簭鍒楄〃
+      loadProcesses(val);
+    } else {
+      form.orderId = "";
+      form.npsNo = "";
+      form.materialCode = "";
+      form.productName = "";
+      form.specification = "";
+      processId.value = "";
+      activeProcessId.value = "";
+      processList.value = [];
+      params.value = [];
+      form.params = {};
+    }
+  };
+
+  // 鍔犺浇宸ュ簭鍒楄〃
+  const loadProcesses = orderId => {
+    processLoading.value = true;
+    findProductProcessRouteItemList({ orderId })
+      .then(res => {
+        processList.value = res.data || [];
+        // 濡傛灉鏈夊伐搴忥紝榛樿閫夋嫨绗竴涓�
+        if (processList.value.length > 0) {
+          const firstProcess = processList.value[0];
+          activeProcessId.value = firstProcess.id + "";
+          processId.value = firstProcess.id;
+          form.processId = firstProcess.id;
+          // 鍔犺浇绗竴涓伐搴忕殑鍙傛暟
+          loadParams(firstProcess.id, orderId);
+        }
+      })
+      .finally(() => {
+        processLoading.value = false;
+      });
+  };
+
+  // 澶勭悊tab椤电偣鍑�
+  const handleTabClick = tab => {
+    const selectedProcessId = parseInt(tab.paneName);
+    processId.value = selectedProcessId;
+    form.processId = selectedProcessId;
+    // 鍔犺浇鍙傛暟鍒楄〃
+    loadParams(selectedProcessId, form.orderId);
+  };
+
+  // 鑾峰彇瀛楀吀鏁版嵁
+  const getDictOptions = async dictType => {
+    if (!dictType) return [];
+    if (dictOptions.value[dictType]) return dictOptions.value[dictType];
+
+    try {
+      const res = await getDicts(dictType);
+      if (res.code === 200) {
+        dictOptions.value[dictType] = res.data;
+        return res.data;
+      }
+      return [];
+    } catch (error) {
+      console.error("鑾峰彇瀛楀吀鏁版嵁澶辫触:", error);
+      return [];
+    }
+  };
+
+  // 鍔犺浇鍙傛暟鍒楄〃
+  const loadParams = (processId, orderId) => {
+    findProcessParamListOrder({ orderId, routeItemId: processId }).then(
+      async res => {
+        params.value = res.data || [];
+        // 鍒濆鍖栧弬鏁扮粍
+        if (!form.paramGroups[processId]) {
+          form.paramGroups[processId] = [];
+        }
+        // 濡傛灉娌℃湁鍙傛暟缁勶紝娣诲姞涓�涓粯璁ゅ弬鏁扮粍
+        if (form.paramGroups[processId].length === 0) {
+          const defaultGroup = {};
+          for (const param of params.value) {
+            defaultGroup[param.id] = param.standardValue || "";
+            // 濡傛灉鏄瓧鍏哥被鍨嬪弬鏁帮紝鑾峰彇瀛楀吀鏁版嵁
+            if (param.paramType == "3" && param.paramFormat) {
+              await getDictOptions(param.paramFormat);
+            }
+          }
+          form.paramGroups[processId].push(defaultGroup);
+        }
+      }
+    );
+  };
+
+  // 鑾峰彇灏忔暟绮惧害
+  const getPrecision = format => {
+    if (!format) return 2;
+    const match = format.match(/\.(\d+)/);
+    return match ? parseInt(match[1].length) : 2;
+  };
+
+  // 澶勭悊涓嬩竴姝�
+  const handleNextStep = () => {
+    if (activeStep.value === 0) {
+      // 绗竴姝ワ細楠岃瘉鐢熶骇璁㈠崟閫夋嫨
+      if (!orderId.value) {
+        ElMessage.error("璇烽�夋嫨鐢熶骇璁㈠崟");
+        return;
+      }
+      activeStep.value = 1;
+    } else if (activeStep.value === 1) {
+      // 绗簩姝ワ細楠岃瘉鍩虹淇℃伅
+      formRef.value.validate(valid => {
+        if (valid) {
+          activeStep.value = 2;
+        }
+      });
+    } else if (activeStep.value === 2) {
+      // 绗笁姝ワ細鐩存帴杩涘叆绗洓姝�
+      activeStep.value = 3;
+    }
+  };
+
+  // 澶勭悊鎻愪氦
+  const handleSubmit = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        submitLoading.value = true;
+        // 杩欓噷鍙互璋冪敤API杩涜鎻愪氦
+        setTimeout(() => {
+          ElMessage.success(props.data.id ? "淇敼鎴愬姛" : "鏂板鎴愬姛");
+          localVisible.value = false;
+          emit("completed");
+          submitLoading.value = false;
+        }, 1000);
+      }
+    });
+  };
+
+  // 澶勭悊鍏抽棴
+  const handleClose = () => {
+    // 閲嶇疆鍒板垵濮嬬姸鎬�
+    activeStep.value = 0;
+    orderId.value = "";
+    processId.value = "";
+    activeProcessId.value = "";
+    processList.value = [];
+    params.value = [];
+    Object.assign(form, {
+      id: undefined,
+      orderId: "",
+      npsNo: "",
+      teamName: "",
+      materialCode: "",
+      productName: "",
+      specification: "",
+      outputVolume: 0,
+      unqualifiedVolume: 0,
+      completedVolume: 0,
+      createBy: "褰撳墠鐧诲綍浜�",
+      createTime: new Date(),
+      params: {},
+    });
+    localVisible.value = false;
+  };
+
+  // 鏂板鍙傛暟缁�
+  const addParamGroup = processId => {
+    if (!form.paramGroups[processId]) {
+      form.paramGroups[processId] = [];
+    }
+    // 鍒涘缓涓�涓柊鐨勫弬鏁扮粍锛屼娇鐢ㄩ粯璁ゅ��
+    const newGroup = {};
+    params.value.forEach(param => {
+      newGroup[param.id] = param.standardValue || "";
+    });
+    form.paramGroups[processId].push(newGroup);
+  };
+
+  // 鍒犻櫎鍙傛暟缁�
+  const removeParamGroup = (processId, index) => {
+    if (form.paramGroups[processId] && form.paramGroups[processId].length > 1) {
+      form.paramGroups[processId].splice(index, 1);
+    }
+  };
+
+  // 鍒濆鍖�
+  const init = () => {
+    // 鏃犺鏂板杩樻槸缂栬緫锛岄兘鍔犺浇璁㈠崟鍒楄〃
+    loadOrders();
+
+    if (props.data.id) {
+      // 缂栬緫鏃惰缃〃鍗曟暟鎹�
+      Object.assign(form, props.data);
+      // 璁剧疆orderId
+      orderId.value = props.data.orderId || "";
+      // 濡傛灉鏈夎鍗旾D锛屽姞杞藉伐搴忓拰鍙傛暟
+      if (props.data.orderId) {
+        // 妯℃嫙閫夋嫨璁㈠崟鐨勬搷浣滐紝瑙﹀彂鏁版嵁鍔犺浇
+        setTimeout(() => {
+          handleOrderChange(props.data.orderId);
+        }, 100);
+      }
+    } else {
+      // 鏂板鏃惰缃粯璁ゅ��
+      form.createBy = "褰撳墠鐧诲綍浜�";
+      form.createTime = new Date();
+    }
+    // 濮嬬粓浠庣涓�姝ュ紑濮�
+    activeStep.value = 0;
+  };
+
+  // 鐩戝惉props.visible鍙樺寲
+  watch(
+    () => props.visible,
+    newVal => {
+      localVisible.value = newVal;
+      if (newVal) {
+        init();
+      }
+    }
+  );
+
+  // 鐩戝惉localVisible鍙樺寲
+  watch(localVisible, newVal => {
+    emit("update:visible", newVal);
+  });
+</script>
+
+<style scoped>
+  .dialog-footer {
+    text-align: right;
+  }
+
+  .param-group {
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    padding: 16px;
+    margin-bottom: 16px;
+    background-color: #f9f9f9;
+  }
+
+  .group-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+    padding-bottom: 8px;
+    border-bottom: 1px solid #e4e7ed;
+  }
+
+  .group-header span {
+    font-weight: bold;
+    font-size: 14px;
+  }
+</style>
\ No newline at end of file

--
Gitblit v1.9.3