zhangwencui
7 小时以前 2b2bc285c0e1798305353e83b47776156e81007d
生产报工模块修改
已添加1个文件
已修改2个文件
已删除5个文件
3027 ■■■■ 文件已修改
src/api/productionManagement/productProcessRoute.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/Input.vue 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/Output.vue 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/components/ReportingDialog.vue 588 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/components/formDia.vue 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index2.vue 420 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/reportingDialog.vue 1589 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productProcessRoute.js
@@ -83,4 +83,11 @@
    url: `/productionOrderRouteItemParam/delete/${id}`,
    method: "delete",
  });
}
// ç”Ÿäº§æŠ¥å·¥-新增
export function productionRecordAdd(id) {
  return request({
    url: "/productionRecord/add/" + id,
    method: "get",
  });
}
src/views/productionManagement/productionReporting/Input.vue
ÎļþÒÑɾ³ý
src/views/productionManagement/productionReporting/Output.vue
ÎļþÒÑɾ³ý
src/views/productionManagement/productionReporting/components/ReportingDialog.vue
ÎļþÒÑɾ³ý
src/views/productionManagement/productionReporting/components/formDia.vue
ÎļþÒÑɾ³ý
src/views/productionManagement/productionReporting/index.vue
@@ -57,14 +57,12 @@
        </template>
      </PIMTable>
    </div>
    <ReportingDialog v-model:visible="dialogVisible"
                     :data="form"
                     @completed="handleQuery" />
  </div>
</template>
<script setup>
  import { onMounted, ref, reactive, getCurrentInstance } from "vue";
  import { useRouter } from "vue-router";
  import { ElMessage, ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import {
@@ -74,8 +72,8 @@
    productionReportDelete,
  } from "@/api/productionManagement/productionReporting.js";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import ReportingDialog from "./components/ReportingDialog.vue";
  const router = useRouter();
  const { proxy } = getCurrentInstance();
  const tableColumn = ref([
@@ -320,7 +318,6 @@
    },
  ];
  const dialogVisible = ref(false);
  const form = reactive({
    id: undefined,
    orderId: "",
@@ -387,7 +384,10 @@
      processId: "",
      params: {},
    });
    dialogVisible.value = true;
    router.push({
      path: "/productionManagement/ReportingDialog",
      query: { data: JSON.stringify(form) },
    });
  };
  const handleEdit = row => {
@@ -407,7 +407,10 @@
      processId: row.processId || "",
      params: row.params || {},
    });
    dialogVisible.value = true;
    router.push({
      path: "/productionManagement/ReportingDialog",
      query: { data: JSON.stringify(form) },
    });
  };
  const handleDetail = row => {
src/views/productionManagement/productionReporting/index2.vue
ÎļþÒÑɾ³ý
src/views/productionManagement/productionReporting/reportingDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1589 @@
<template>
  <div class="reporting-page">
    <!-- é¡µé¢å¤´éƒ¨ -->
    <PageHeader content="生产工单报工">
    </PageHeader>
    <!-- æ­¥éª¤æŒ‡ç¤ºå™¨ -->
    <div class="step-indicator">
      <div v-for="(step, index) in steps"
           :key="index"
           class="step-item"
           :class="{
          'active': index === activeStep,
          'completed': index < activeStep
        }">
        <div class="step-circle">
          <span v-if="index < activeStep"
                class="step-check">
            <el-icon>
              <Check />
            </el-icon>
          </span>
          <span v-else
                class="step-number">{{ index + 1 }}</span>
        </div>
        <div class="step-content">
          <div class="step-title">{{ step.title }}</div>
          <div class="step-description">{{ step.description }}</div>
        </div>
        <div class="step-line"
             v-if="index < steps.length - 1"></div>
      </div>
    </div>
    <!-- é¡µé¢å†…容 -->
    <div class="page-content">
      <!-- ç¬¬ä¸€æ­¥ï¼šé€‰æ‹©ç”Ÿäº§è®¢å• -->
      <div v-if="activeStep === 0"
           class="step-panel">
        <div class="panel-header">
          <div>
            <h3 class="panel-title">选择生产订单</h3>
            <p class="panel-subtitle">请从以下列表中选择需要报工的生产订单</p>
          </div>
          <div class="header-actions">
            <el-button @click="activeStep--"
                       v-if="activeStep > 0"
                       :disabled="isSubmitting">
              <el-icon>
                <ArrowLeft />
              </el-icon> ä¸Šä¸€æ­¥
            </el-button>
            <el-button type="primary"
                       @click="handleNextStep"
                       v-if="activeStep < 3"
                       :disabled="isSubmitting">
              ä¸‹ä¸€æ­¥ <el-icon>
                <ArrowRight />
              </el-icon>
            </el-button>
          </div>
        </div>
        <el-form :model="form"
                 ref="formRef"
                 class="form-container">
          <el-form-item label="生产订单"
                        prop="orderId"
                        required>
            <el-select v-model="orderId"
                       placeholder="请选择生产订单"
                       clearable
                       filterable
                       class="form-select"
                       :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"
           class="step-panel">
        <div class="panel-header">
          <div>
            <h3 class="panel-title">填写基础信息</h3>
            <p class="panel-subtitle">请填写报工的基本信息</p>
          </div>
          <div class="header-actions">
            <el-button @click="activeStep--"
                       v-if="activeStep > 0"
                       :disabled="isSubmitting">
              <el-icon>
                <ArrowLeft />
              </el-icon> ä¸Šä¸€æ­¥
            </el-button>
            <el-button type="primary"
                       @click="handleNextStep"
                       v-if="activeStep < 3"
                       :disabled="isSubmitting">
              ä¸‹ä¸€æ­¥ <el-icon>
                <ArrowRight />
              </el-icon>
            </el-button>
          </div>
        </div>
        <el-form :model="form"
                 :rules="rules"
                 ref="formRef"
                 class="form-container">
          <div class="form-grid">
            <el-form-item label="生产订单号"
                          prop="npsNo"
                          class="form-item">
              <el-input disabled
                        v-model="form.npsNo"
                        class="form-input" />
            </el-form-item>
            <el-form-item label="班组"
                          prop="teamName"
                          required
                          class="form-item">
              <el-select v-model="form.teamName"
                         placeholder="请选择班组"
                         class="form-select">
                <el-option label="白班"
                           value="白班" />
                <el-option label="夜班"
                           value="夜班" />
              </el-select>
            </el-form-item>
            <el-form-item label="产品编码"
                          prop="materialCode"
                          class="form-item">
              <el-input disabled
                        v-model="form.materialCode"
                        class="form-input" />
            </el-form-item>
            <el-form-item label="产品名称"
                          prop="productName"
                          class="form-item">
              <el-input disabled
                        v-model="form.productName"
                        class="form-input" />
            </el-form-item>
            <el-form-item label="规格"
                          prop="specification"
                          class="form-item">
              <el-input disabled
                        v-model="form.specification"
                        class="form-input" />
            </el-form-item>
            <el-form-item label="创建人"
                          prop="createBy"
                          required
                          class="form-item">
              <el-select v-model="form.createBy"
                         placeholder="请选择创建人"
                         class="form-select"
                         :loading="userLoading">
                <el-option v-for="user in userList"
                           :key="user.id"
                           :label="user.nickName || user.userName"
                           :value="user.nickName || user.userName" />
              </el-select>
            </el-form-item>
            <el-form-item label="创建时间"
                          prop="createTime"
                          class="form-item">
              <el-date-picker disabled
                              v-model="form.createTime"
                              type="datetime"
                              placeholder="请选择创建时间"
                              class="form-input" />
            </el-form-item>
          </div>
        </el-form>
      </div>
      <!-- ç¬¬ä¸‰æ­¥ï¼šæŸ¥çœ‹å·¥åºå‚æ•° -->
      <div v-else-if="activeStep === 2"
           class="step-panel process-panel">
        <div class="panel-header">
          <div>
            <h3 class="panel-title">工序参数管理</h3>
            <p class="panel-subtitle">请查看并填写各工序的参数信息</p>
          </div>
          <div class="header-actions">
            <el-button @click="activeStep--"
                       v-if="activeStep > 0"
                       :disabled="isSubmitting">
              <el-icon>
                <ArrowLeft />
              </el-icon> ä¸Šä¸€æ­¥
            </el-button>
            <el-button type="primary"
                       @click="handleNextStep"
                       v-if="activeStep < 3"
                       :disabled="isSubmitting">
              ä¸‹ä¸€æ­¥ <el-icon>
                <ArrowRight />
              </el-icon>
            </el-button>
          </div>
        </div>
        <div class="process-container">
          <!-- å·¦ä¾§å·¥åºå¯¼èˆª -->
          <div class="process-nav">
            <div v-for="process in processList"
                 :key="process.processId"
                 class="process-nav-item"
                 :class="{ 'active': activeProcessId === process.processId + '' }"
                 @click="handleProcessClick(process.processId)">
              <span class="process-name">{{ process.processName }}</span>
              <span class="process-badge"
                    v-if="getProcessInfo(parseInt(process.processId)).postPersonnel">
                {{ getProcessInfo(parseInt(process.processId)).postPersonnel }}
              </span>
            </div>
          </div>
          <!-- å³ä¾§å·¥åºå†…容 -->
          <div class="process-content">
            <div v-if="activeProcessId"
                 class="process-details">
              <!-- å›ºå®šå‚æ•° -->
              <div class="param-section">
                <div class="section-header">
                  <h4 class="section-title">工序基本信息</h4>
                </div>
                <div class="param-form">
                  <el-form :label-position="'top'">
                    <div class="form-grid">
                      <el-form-item label="岗位人员"
                                    class="form-item">
                        <el-select v-model="getProcessInfo(parseInt(activeProcessId)).postPersonnel"
                                   placeholder="请选择岗位人员"
                                   class="form-select"
                                   :loading="userLoading">
                          <el-option v-for="user in userList"
                                     :key="user.id"
                                     :label="user.nickName || user.userName"
                                     :value="user.nickName || user.userName" />
                        </el-select>
                      </el-form-item>
                      <el-form-item label="设备异常情况"
                                    class="form-item">
                        <el-input v-model="getProcessInfo(parseInt(activeProcessId)).equipmentAbnormality"
                                  placeholder="请输入设备异常情况"
                                  type="textarea"
                                  :rows="2"
                                  class="form-textarea" />
                      </el-form-item>
                      <el-form-item label="当班设备处置"
                                    class="form-item">
                        <el-input v-model="getProcessInfo(parseInt(activeProcessId)).equipmentHandling"
                                  placeholder="请输入当班设备处置"
                                  type="textarea"
                                  :rows="2"
                                  class="form-textarea" />
                      </el-form-item>
                      <el-form-item label="工艺人员交待"
                                    class="form-item">
                        <el-input v-model="getProcessInfo(parseInt(activeProcessId)).processInstructions"
                                  placeholder="请输入工艺人员交待"
                                  type="textarea"
                                  :rows="2"
                                  class="form-textarea" />
                      </el-form-item>
                      <el-form-item label="上传文件"
                                    class="form-item"
                                    :span="24">
                        <el-upload class="upload-demo upload-block"
                                   action="#"
                                   :on-preview="handlePreview"
                                   :on-remove="handleRemove"
                                   :file-list="getProcessInfo(parseInt(activeProcessId)).files || []"
                                   :auto-upload="false"
                                   :accept="'.jpg,.png'"
                                   :max-size="500000"
                                   :on-change="handleFileChange">
                          <el-button type="primary"
                                     :icon="Upload">点击上传</el-button>
                          <template #tip>
                            <div class="el-upload__tip">
                              åªèƒ½ä¸Šä¼ jpg/png文件,且不超过500kb
                            </div>
                          </template>
                        </el-upload>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </div>
              <!-- BOM信息 -->
              <div class="param-section"
                   v-if="getProcessStructures(parseInt(activeProcessId)).length > 0">
                <div class="section-header">
                  <h4 class="section-title">BOM信息</h4>
                </div>
                <div class="param-form">
                  <el-form :label-position="'top'">
                    <div class="form-grid">
                      <el-form-item v-for="item in getProcessStructures(parseInt(activeProcessId))"
                                    :key="item.id"
                                    :label="`${item.productName} ${item.model}`"
                                    class="form-item">
                        <div class="consumable-input-group">
                          <el-input-number v-model="getProcessInfo(parseInt(activeProcessId)).consumables[item.id]"
                                           :min="0"
                                           :model-value="getConsumableValue(parseInt(activeProcessId), item.id)"
                                           @change="val => getProcessInfo(parseInt(activeProcessId)).consumables[item.id] = val"
                                           class="consumable-input" />
                          <span class="consumable-unit">{{ item.unit }}</span>
                        </div>
                      </el-form-item>
                    </div>
                  </el-form>
                </div>
              </div>
              <!-- å‚数组列表 -->
              <div class="param-section">
                <div class="section-header">
                  <h4 class="section-title">参数组管理</h4>
                  <div class="section-actions">
                    <el-switch v-model="useTableView"
                               active-text="表格视图"
                               inactive-text="卡片视图"
                               inline-prompt />
                    <el-button type="primary"
                               @click="addParamGroup(parseInt(activeProcessId))"
                               :icon="Plus">
                      æ–°å¢žå‚数组
                    </el-button>
                  </div>
                </div>
                <!-- å¡ç‰‡è§†å›¾ -->
                <div v-if="!useTableView"
                     class="param-cards">
                  <div v-for="(group, index) in form.paramGroups[activeProcessId] || []"
                       :key="index"
                       class="param-card">
                    <div class="card-header">
                      <span class="card-title">参数组 {{ index + 1 }}</span>
                      <el-button type="danger"
                                 size="small"
                                 @click="removeParamGroup(parseInt(activeProcessId), index)"
                                 v-if="(form.paramGroups[activeProcessId] || []).length > 1"
                                 circle>
                        <el-icon>
                          <Delete />
                        </el-icon>
                      </el-button>
                    </div>
                    <div class="card-body">
                      <div class="param-grid">
                        <el-form-item v-for="param in params"
                                      :key="param.id"
                                      :label="param.paramName"
                                      :label-width="120"
                                      class="param-item">
                          <template v-if="param.paramType == '1'">
                            <!-- æ•°å­—类型 -->
                            <div class="param-input-group">
                              <el-input-number v-model="form.paramGroups[activeProcessId][index][param.id]"
                                               controls-position="right"
                                               :precision="getPrecision(param.paramFormat)"
                                               class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
                                {{ param.unit }}
                              </span>
                            </div>
                          </template>
                          <template v-else-if="param.paramType == '2'">
                            <!-- æ–‡æœ¬ç±»åž‹ -->
                            <div class="param-input-group">
                              <el-input v-model="form.paramGroups[activeProcessId][index][param.id]"
                                        class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
                                {{ param.unit }}
                              </span>
                            </div>
                          </template>
                          <template v-else-if="param.paramType == '3'">
                            <!-- å­—典类型 -->
                            <div class="param-input-group">
                              <el-select v-model="form.paramGroups[activeProcessId][index][param.id]"
                                         placeholder="请选择"
                                         class="param-select"
                                         style="width: 100%">
                                <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 != '/'"
                                    class="param-unit">
                                {{ param.unit }}
                              </span>
                            </div>
                          </template>
                          <template v-else-if="param.paramType == '4'">
                            <!-- æ—¥æœŸç±»åž‹ -->
                            <div class="param-input-group">
                              <el-date-picker :value-format="param.paramFormat"
                                              :format="param.paramFormat"
                                              :type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'"
                                              v-model="form.paramGroups[activeProcessId][index][param.id]"
                                              class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
                                {{ param.unit }}
                              </span>
                            </div>
                          </template>
                          <template v-else>
                            <!-- å…¶ä»–类型 -->
                            <div class="param-input-group">
                              <el-input v-model="form.paramGroups[activeProcessId][index][param.id]"
                                        class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
                                {{ param.unit }}
                              </span>
                            </div>
                          </template>
                        </el-form-item>
                      </div>
                    </div>
                  </div>
                </div>
                <!-- è¡¨æ ¼è§†å›¾ -->
                <div v-else
                     class="param-table">
                  <el-table :data="form.paramGroups[activeProcessId] || []"
                            style="width: 100%"
                            class="table-view">
                    <!-- æ“ä½œåˆ— -->
                    <el-table-column label="操作"
                                     width="100"
                                     fixed>
                      <template #default="{ $index }">
                        <el-button type="danger"
                                   size="small"
                                   @click="removeParamGroup(parseInt(activeProcessId), $index)"
                                   circle>
                          <el-icon>
                            <Delete />
                          </el-icon>
                        </el-button>
                      </template>
                    </el-table-column>
                    <!-- å‚数列 -->
                    <el-table-column v-for="param in params"
                                     :key="param.id"
                                     :label="param.paramName"
                                     min-width="200">
                      <template #default="{ row }">
                        <template v-if="param.paramType == '1'">
                          <!-- æ•°å­—类型 -->
                          <el-input-number v-model="row[param.id]"
                                           controls-position="right"
                                           :precision="getPrecision(param.paramFormat)"
                                           class="table-input" />
                        </template>
                        <template v-else-if="param.paramType == '2'">
                          <!-- æ–‡æœ¬ç±»åž‹ -->
                          <el-input v-model="row[param.id]"
                                    class="table-input" />
                        </template>
                        <template v-else-if="param.paramType == '3'">
                          <!-- å­—典类型 -->
                          <el-select v-model="row[param.id]"
                                     placeholder="请选择"
                                     class="table-select">
                            <el-option v-for="option in dictOptions[param.paramFormat] || []"
                                       :key="option.dictValue"
                                       :label="option.dictLabel"
                                       :value="option.dictValue" />
                          </el-select>
                        </template>
                        <template v-else-if="param.paramType == '4'">
                          <!-- æ—¥æœŸç±»åž‹ -->
                          <el-date-picker :value-format="param.paramFormat"
                                          :format="param.paramFormat"
                                          width="100%"
                                          :type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'"
                                          v-model="row[param.id]"
                                          class="table-input table-select" />
                        </template>
                        <template v-else>
                          <!-- å…¶ä»–类型 -->
                          <el-input v-model="row[param.id]"
                                    class="table-input" />
                        </template>
                      </template>
                    </el-table-column>
                  </el-table>
                </div>
                <!-- æ–°å¢žå‚数组按钮 -->
                <!-- <div class="param-actions">
                  <el-button type="primary"
                             @click="addParamGroup(parseInt(activeProcessId))"
                             :icon="Plus">
                    æ–°å¢žå‚数组
                  </el-button>
                </div> -->
              </div>
            </div>
            <div v-else
                 class="empty-process">
              <el-empty description="请选择一个工序"
                        :image-size="120" />
            </div>
          </div>
        </div>
      </div>
      <!-- ç¬¬å››æ­¥ï¼šå¡«å†™äº§é‡ä¿¡æ¯ -->
      <div v-else-if="activeStep === 3"
           class="step-panel">
        <div class="panel-header">
          <div>
            <h3 class="panel-title">填写产量信息</h3>
            <p class="panel-subtitle">请填写本次报工的产量数据</p>
          </div>
          <div class="header-actions">
            <el-button @click="activeStep--"
                       v-if="activeStep > 0"
                       :disabled="isSubmitting">
              <el-icon>
                <ArrowLeft />
              </el-icon> ä¸Šä¸€æ­¥
            </el-button>
            <el-button type="primary"
                       @click="handleSubmit"
                       v-if="activeStep === 3"
                       :loading="isSubmitting">
              <el-icon v-if="!isSubmitting">
                <Check />
              </el-icon>
              <el-icon v-else>
                <Loading />
              </el-icon>
              {{ isSubmitting ? '提交中...' : '确认提交' }}
            </el-button>
          </div>
        </div>
        <el-form :model="form"
                 :rules="rules"
                 ref="formRef"
                 :label-position="'top'"
                 class="form-container">
          <div class="form-grid1">
            <el-form-item label="产出方量"
                          prop="outputVolume"
                          required
                          class="form-item">
              <div class="volume-input-group">
                <el-input-number v-model="form.outputVolume"
                                 :min="0"
                                 :precision="2"
                                 class="volume-input" />
                <span class="volume-unit">方</span>
              </div>
            </el-form-item>
            <el-form-item label="不合格方量"
                          prop="unqualifiedVolume"
                          required
                          class="form-item">
              <div class="volume-input-group">
                <el-input-number v-model="form.unqualifiedVolume"
                                 :min="0"
                                 :precision="2"
                                 class="volume-input" />
                <span class="volume-unit">方</span>
              </div>
            </el-form-item>
            <el-form-item label="完成方量"
                          prop="completedVolume"
                          required
                          class="form-item">
              <div class="volume-input-group">
                <el-input-number v-model="form.completedVolume"
                                 :min="0"
                                 :precision="2"
                                 class="volume-input" />
                <span class="volume-unit">方</span>
              </div>
            </el-form-item>
          </div>
        </el-form>
      </div>
    </div>
    <!-- åº•部按钮 -->
  </div>
</template>
<script setup>
  import { ref, reactive, computed, watch, onMounted } from "vue";
  import { ElMessage, ElEmpty } from "element-plus";
  import { useRouter, useRoute } from "vue-router";
  import { getDicts } from "@/api/system/dict/data";
  import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
  import { productionRecordAdd } from "@/api/productionManagement/productProcessRoute.js";
  import { userListNoPage } from "@/api/system/user.js";
  import { getInfo } from "@/api/login.js";
  import {
    Check,
    Close,
    Delete,
    Plus,
    ArrowLeft,
    ArrowRight,
    Loading,
    Upload,
  } from "@element-plus/icons-vue";
  const router = useRouter();
  // ä»Žè·¯ç”±å‚数获取数据
  const route = useRoute();
  const data = route.query.data ? JSON.parse(route.query.data) : {};
  const dialogTitle = computed(() => (data.id ? "编辑报工" : "新增报工"));
  const formRef = ref(null);
  const isSubmitting = ref(false);
  const orderLoading = ref(false);
  const processLoading = ref(false);
  const activeStep = ref(0);
  const steps = [
    { title: "选择生产订单", description: "选择需要报工的生产订单" },
    { title: "填写基础信息", description: "填写报工的基本信息" },
    { title: "查看工序参数", description: "填写各工序的参数信息" },
    { title: "填写产量信息", description: "填写本次报工的产量数据" },
  ];
  // è®¡ç®—当前工序的参数组数量
  const paramGroupCount = computed(() => {
    if (!activeProcessId.value) return 0;
    return (form.paramGroups[activeProcessId.value] || []).length;
  });
  const orderId = ref(data.orderId || "");
  const processId = ref(data.processId || "");
  const activeProcessId = ref("");
  const orderList = ref([]);
  const processList = ref([]);
  const params = ref([]);
  const dictOptions = ref({});
  const userList = ref([]);
  const userLoading = ref(false);
  const useTableView = ref(false); // æŽ§åˆ¶æ˜¯å¦ä½¿ç”¨è¡¨æ ¼è§†å›¾
  const form = reactive({
    id: data.id || undefined,
    orderId: data.orderId || "",
    npsNo: data.npsNo || "",
    teamName: data.teamName || "",
    materialCode: data.materialCode || "",
    productName: data.productName || "",
    specification: data.specification || "",
    outputVolume: data.outputVolume || 0,
    unqualifiedVolume: data.unqualifiedVolume || 0,
    completedVolume: data.completedVolume || 0,
    createBy: data.createBy || "当前登录人",
    createTime: data.createTime || new Date(),
    paramGroups: data.paramGroups || {}, // å­˜å‚¨æ¯ä¸ªå·¥åºçš„参数组
    processInfo: data.processInfo || {}, // å­˜å‚¨æ¯ä¸ªå·¥åºçš„基本信息
  });
  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;
    // è°ƒç”¨æ–°çš„æŽ¥å£
    productionRecordAdd(orderId)
      .then(res => {
        if (res.code === 200) {
          const data = res.data;
          // æå–工序列表
          processList.value = data.productionOrderRouteItemVos || [];
          // å­˜å‚¨å·¥åºç»“构数据
          form.processStructures = {};
          processList.value.forEach(process => {
            form.processStructures[process.processId] =
              process.orderStructureVos || [];
          });
          // å¦‚果有工序,默认选择第一个
          if (processList.value.length > 0) {
            const firstProcess = processList.value[0];
            activeProcessId.value = firstProcess.processId + "";
            processId.value = firstProcess.processId;
            form.processId = firstProcess.processId;
            // åŠ è½½ç¬¬ä¸€ä¸ªå·¥åºçš„å‚æ•°
            loadParams(firstProcess.processId, orderId);
          }
        }
      })
      .finally(() => {
        processLoading.value = false;
      });
  };
  // å¤„理工序导航点击
  const handleProcessClick = selectedProcessId => {
    activeProcessId.value = selectedProcessId + "";
    processId.value = selectedProcessId;
    form.processId = selectedProcessId;
    // åŠ è½½å‚æ•°åˆ—è¡¨
    loadParams(selectedProcessId, form.orderId);
  };
  // èŽ·å–å·¥åºåŸºæœ¬ä¿¡æ¯ï¼Œä¸å­˜åœ¨åˆ™åˆå§‹åŒ–
  const getProcessInfo = processId => {
    if (!form.processInfo) {
      form.processInfo = {};
    }
    if (!form.processInfo[processId]) {
      form.processInfo[processId] = {
        postPersonnel: "",
        equipmentAbnormality: "",
        equipmentHandling: "",
        processInstructions: "",
        files: [],
        consumables: {},
      };
    }
    return form.processInfo[processId];
  };
  // èŽ·å–å·¥åºç»“æž„æ•°æ®ï¼ˆBOM列表)
  const getProcessStructures = processId => {
    return form.processStructures && form.processStructures[processId]
      ? form.processStructures[processId]
      : [];
  };
  // èŽ·å–æ¶ˆè€—å“æ•°é‡ï¼Œé»˜è®¤ä¸º0
  const getConsumableValue = (processId, itemId) => {
    const processInfo = getProcessInfo(processId);
    if (!processInfo.consumables[itemId]) {
      processInfo.consumables[itemId] = 0;
    }
    return processInfo.consumables[itemId];
  };
  // å¤„理文件预览
  const handlePreview = file => {
    // æ£€æŸ¥æ˜¯å¦æ˜¯å›¾ç‰‡æ–‡ä»¶
    if (file.raw && file.raw.type.startsWith("image/")) {
      // åˆ›å»ºå›¾ç‰‡é¢„览
      const imageUrl = URL.createObjectURL(file.raw);
      const image = new Image();
      image.src = imageUrl;
      // åˆ›å»ºé¢„览容器
      const previewContainer = document.createElement("div");
      previewContainer.style.position = "fixed";
      previewContainer.style.top = "0";
      previewContainer.style.left = "0";
      previewContainer.style.width = "100%";
      previewContainer.style.height = "100%";
      previewContainer.style.backgroundColor = "rgba(0, 0, 0, 0.8)";
      previewContainer.style.display = "flex";
      previewContainer.style.alignItems = "center";
      previewContainer.style.justifyContent = "center";
      previewContainer.style.zIndex = "9999";
      previewContainer.style.cursor = "pointer";
      // æ·»åŠ å›¾ç‰‡
      previewContainer.appendChild(image);
      image.style.maxWidth = "90%";
      image.style.maxHeight = "90%";
      // æ·»åŠ å…³é—­åŠŸèƒ½
      previewContainer.addEventListener("click", () => {
        URL.revokeObjectURL(imageUrl);
        document.body.removeChild(previewContainer);
      });
      // æ·»åŠ åˆ°æ–‡æ¡£
      document.body.appendChild(previewContainer);
    }
  };
  // å¤„理文件删除
  const handleRemove = (file, fileList) => {
    const processId = parseInt(activeProcessId.value);
    if (processId) {
      const processInfo = getProcessInfo(processId);
      processInfo.files = fileList;
    }
  };
  // å¤„理文件变更
  const handleFileChange = (file, fileList) => {
    const processId = parseInt(activeProcessId.value);
    if (processId) {
      const processInfo = getProcessInfo(processId);
      processInfo.files = fileList;
    }
  };
  // èŽ·å–å­—å…¸æ•°æ®
  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) => {
    // ä»Žå·²åŠ è½½çš„å·¥åºæ•°æ®ä¸­èŽ·å–å‚æ•°åˆ—è¡¨
    const process = processList.value.find(
      p => p.processId === parseInt(processId)
    );
    if (process) {
      params.value = process.orderRouteItemParaVos || [];
      // åˆå§‹åŒ–参数组
      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) {
            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) {
        isSubmitting.value = true;
        // è¿™é‡Œå¯ä»¥è°ƒç”¨API进行提交
        setTimeout(() => {
          ElMessage.success(data.id ? "修改成功" : "新增成功");
          router.back();
          isSubmitting.value = false;
        }, 1000);
      }
    });
  };
  // å¤„理取消
  const handleCancel = () => {
    router.back();
  };
  // æ–°å¢žå‚数组
  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 loadUsers = () => {
    userLoading.value = true;
    userListNoPage()
      .then(res => {
        userList.value = res.data || [];
      })
      .finally(() => {
        userLoading.value = false;
      });
  };
  // èŽ·å–å½“å‰ç™»å½•äººä¿¡æ¯
  const getCurrentUser = async () => {
    try {
      const res = await getInfo();
      if (res && res.user) {
        form.createBy = res.user.nickName || res.user.userName;
      }
    } catch (error) {
      console.error("获取当前登录人信息失败:", error);
    }
  };
  // åˆå§‹åŒ–
  const init = () => {
    // æ— è®ºæ–°å¢žè¿˜æ˜¯ç¼–辑,都加载订单列表和用户列表
    loadOrders();
    loadUsers();
    getCurrentUser();
    if (data.id) {
      // ç¼–辑时设置表单数据
      Object.assign(form, data);
      // è®¾ç½®orderId
      orderId.value = data.orderId || "";
      // å¦‚果有订单ID,加载工序和参数
      if (data.orderId) {
        // æ¨¡æ‹Ÿé€‰æ‹©è®¢å•的操作,触发数据加载
        setTimeout(() => {
          handleOrderChange(data.orderId);
        }, 100);
      }
    } else {
      // æ–°å¢žæ—¶è®¾ç½®é»˜è®¤å€¼
      form.createTime = new Date();
    }
    // å§‹ç»ˆä»Žç¬¬ä¸€æ­¥å¼€å§‹
    activeStep.value = 0;
  };
  // é¡µé¢åŠ è½½æ—¶åˆå§‹åŒ–
  onMounted(() => {
    init();
  });
</script>
<style scoped>
  /* é¡µé¢å®¹å™¨ */
  .reporting-page {
    min-height: 100vh;
    padding: 24px;
    background-color: #f0f2f5;
  }
  /* é¡µé¢å¤´éƒ¨ */
  .page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 32px;
    padding-bottom: 16px;
    border-bottom: 1px solid #e8e8e8;
  }
  .page-title {
    margin: 0;
    font-size: 24px;
    font-weight: 600;
    color: #1f2329;
  }
  .header-actions {
    display: flex;
    gap: 12px;
  }
  /* æ­¥éª¤æŒ‡ç¤ºå™¨ */
  .step-indicator {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 32px;
    padding: 0 16px;
    padding-top: 16px;
  }
  .step-item {
    display: flex;
    align-items: center;
    flex: 1;
    position: relative;
  }
  .step-circle {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background-color: #f0f0f0;
    color: #999;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    font-weight: 600;
    z-index: 2;
    transition: all 0.3s ease;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }
  .step-item.active .step-circle {
    background-color: #1890ff;
    color: white;
    box-shadow: 0 0 0 8px rgba(24, 144, 255, 0.1);
  }
  .step-item.completed .step-circle {
    background-color: #52c41a;
    color: white;
    box-shadow: 0 0 0 8px rgba(82, 196, 26, 0.1);
  }
  .step-check {
    font-size: 20px;
  }
  .step-content {
    margin-left: 16px;
    flex: 1;
  }
  .step-title {
    font-size: 14px;
    font-weight: 600;
    color: #666;
    margin-bottom: 4px;
    transition: color 0.3s ease;
  }
  .step-item.active .step-title {
    color: #1890ff;
  }
  .step-item.completed .step-title {
    color: #52c41a;
  }
  .step-description {
    font-size: 12px;
    color: #999;
  }
  .step-line {
    position: absolute;
    top: 20px;
    left: 50%;
    right: -50%;
    height: 2px;
    background-color: #e8e8e8;
    z-index: 1;
    transition: all 0.3s ease;
  }
  .step-item.completed .step-line {
    background-color: #52c41a;
  }
  /* é¡µé¢å†…容 */
  .page-content {
    background-color: white;
    border-radius: 12px;
    padding: 0;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    margin-bottom: 32px;
    overflow: hidden;
  }
  /* æ­¥éª¤é¢æ¿ */
  .step-panel {
    padding: 32px;
  }
  .panel-header {
    margin-bottom: 24px;
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
  }
  .panel-title {
    font-size: 18px;
    font-weight: 600;
    color: #1f2329;
    margin: 0 0 8px 0;
  }
  .panel-subtitle {
    font-size: 14px;
    color: #666;
    margin: 0;
  }
  .header-actions {
    display: flex;
    gap: 12px;
    align-items: center;
  }
  /* è¡¨å•容器 */
  .form-container {
    width: 100%;
  }
  /* ä¸Šä¼ ç»„件样式 */
  .upload-block {
    display: block;
  }
  .upload-block .el-upload__tip {
    margin-top: 8px;
  }
  /* BOM输入组样式 */
  .consumable-input-group {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .consumable-input {
    flex: 1;
  }
  .consumable-unit {
    font-size: 14px;
    color: #666;
    white-space: nowrap;
  }
  .form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 20px;
  }
  .form-grid1 {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
    gap: 20px;
  }
  .form-item {
    margin-bottom: 0;
  }
  .form-select,
  .form-input {
    width: 100%;
  }
  .form-textarea {
    width: 100%;
    resize: vertical;
  }
  /* äº§é‡è¾“入组 */
  .volume-input-group {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .volume-input {
    flex: 1;
    width: 100%;
  }
  .volume-unit {
    font-size: 14px;
    color: #666;
    white-space: nowrap;
  }
  /* å·¥åºå®¹å™¨ */
  .process-container {
    display: flex;
    gap: 24px;
    min-height: 500px;
  }
  /* å·¥åºå¯¼èˆª */
  .process-nav {
    width: 200px;
    background-color: #fafafa;
    border-radius: 8px;
    padding: 16px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
    overflow-y: auto;
  }
  .process-nav-item {
    padding: 12px 16px;
    margin-bottom: 8px;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.3s ease;
    font-size: 14px;
    font-weight: 500;
    color: #666;
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  .process-nav-item:hover {
    background-color: #e6f7ff;
    color: #1890ff;
  }
  .process-nav-item.active {
    background-color: #1890ff;
    color: white;
    box-shadow: 0 2px 8px rgba(24, 144, 255, 0.3);
  }
  .process-badge {
    font-size: 12px;
    background-color: rgba(255, 255, 255, 0.2);
    padding: 2px 8px;
    border-radius: 10px;
  }
  /* å·¥åºå†…容 */
  .process-content {
    flex: 1;
    overflow-y: auto;
  }
  .empty-process {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 400px;
  }
  /* å‚数部分 */
  .param-section {
    margin-bottom: 32px;
    padding: 24px;
    background-color: #fafafa;
    border-radius: 8px;
  }
  .section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
  }
  .section-title {
    font-size: 16px;
    font-weight: 600;
    color: #1f2329;
    margin: 0;
  }
  .section-actions {
    display: flex;
    gap: 8px;
  }
  /* å‚数表单 */
  .param-form {
    background-color: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  }
  /* å‚数卡片 */
  .param-cards {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }
  .param-card {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    overflow: hidden;
    transition: all 0.3s ease;
  }
  .param-card:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    transform: translateY(-2px);
  }
  .card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px 20px;
    background-color: #fafafa;
    border-bottom: 1px solid #e8e8e8;
  }
  .card-title {
    font-size: 14px;
    font-weight: 600;
    color: #1f2329;
  }
  .card-body {
    padding: 20px;
  }
  /* å‚数网格 */
  .param-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
    gap: 16px;
  }
  .param-item {
    margin-bottom: 0;
  }
  .param-input-group {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
  }
  .param-input,
  .param-select {
    flex: 1;
  }
  .param-unit {
    font-size: 14px;
    color: #666;
    white-space: nowrap;
  }
  /* è¡¨æ ¼è§†å›¾ */
  .param-table {
    margin: 16px 0;
  }
  .table-view {
    border-radius: 8px;
    overflow: hidden;
  }
  .table-view th {
    background-color: #fafafa;
    font-weight: 600;
  }
  .table-input,
  .table-select {
    width: 100%;
  }
  /* å‚数操作 */
  .param-actions {
    margin-top: 16px;
    display: flex;
    justify-content: flex-end;
  }
  /* é¡µé¢åº•部 */
  .page-footer {
    display: flex;
    justify-content: center;
    padding: 24px;
    background-color: white;
    border-top: 1px solid #e8e8e8;
    border-radius: 0 0 12px 12px;
  }
  .footer-actions {
    display: flex;
    gap: 12px;
  }
  /* å“åº”式设计 */
  @media (max-width: 1024px) {
    .form-grid {
      grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    }
    .param-grid {
      grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    }
  }
  @media (max-width: 768px) {
    .reporting-page {
      padding: 16px;
    }
    .step-indicator {
      flex-direction: column;
      align-items: flex-start;
      gap: 16px;
      padding: 0;
    }
    .step-item {
      flex-direction: row;
      width: 100%;
    }
    .step-line {
      display: none;
    }
    .step-panel {
      padding: 20px;
    }
    .form-grid {
      grid-template-columns: 1fr;
    }
    .process-container {
      flex-direction: column;
    }
    .process-nav {
      width: 100%;
      margin-bottom: 16px;
    }
    .param-grid {
      grid-template-columns: 1fr;
    }
    .footer-actions {
      flex-direction: column;
      width: 100%;
    }
    .footer-actions button {
      width: 100%;
    }
  }
  /* æ»šåŠ¨æ¡æ ·å¼ */
  .process-nav::-webkit-scrollbar,
  .process-content::-webkit-scrollbar {
    width: 6px;
    height: 6px;
  }
  .process-nav::-webkit-scrollbar-track,
  .process-content::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 3px;
  }
  .process-nav::-webkit-scrollbar-thumb,
  .process-content::-webkit-scrollbar-thumb {
    background: #c1c1c1;
    border-radius: 3px;
  }
  .process-nav::-webkit-scrollbar-thumb:hover,
  .process-content::-webkit-scrollbar-thumb:hover {
    background: #a8a8a8;
  }
  /* åŠ¨ç”»æ•ˆæžœ */
  @keyframes fadeIn {
    from {
      opacity: 0;
      transform: translateY(10px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
  .step-panel {
    animation: fadeIn 0.3s ease;
  }
  /* åŠ è½½çŠ¶æ€ */
  .loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(255, 255, 255, 0.8);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
  }
</style>