zhangwencui
11 小时以前 8d48fa86c9096f6bac90e83ed779e5a5b62b0fc7
生产报工模块
已添加1个文件
已修改5个文件
1068 ■■■■ 文件已修改
src/api/productionManagement/productProcessRoute.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionReporting.js 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/parameterMaintenance/index.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/detailDialog.vue 486 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/reportingDialog.vue 219 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productProcessRoute.js
@@ -98,4 +98,12 @@
    method: "post",
    data: data,
  });
}
// ç”Ÿäº§æŠ¥å·¥-编辑
export function productionRecordEditSubmit(data) {
  return request({
    url: "/productionRecord/edit",
    method: "post",
    data: data,
  });
}
src/api/productionManagement/productionReporting.js
@@ -33,11 +33,28 @@
    data: query,
  });
}
// ç”Ÿäº§æŠ¥å·¥-删除
export function productionReportDelete(query) {
// ç”Ÿäº§æŠ¥å·¥-分页查询
export function productionReportListPage(query) {
  return request({
    url: "/productionProductMain/delete",
    method: "delete",
    data: query,
    url: "/productionProductMain/listPage",
    method: "get",
    params: query,
  });
}
// ç”Ÿäº§æŠ¥å·¥-详情
export function productionReportDetail(id) {
  return request({
    url: "/productionRecord/detail/" + id,
    method: "get",
  });
}
// ç”Ÿäº§æŠ¥å·¥-删除
export function productionReportDelete(id) {
  return request({
    url: `/productionRecord/`+id,
    method: "delete",
  });
}
src/views/basicData/parameterMaintenance/index.vue
@@ -86,6 +86,17 @@
                      prop="paramFormat">
          <el-input v-model="formData.paramFormat"
                    placeholder="请输入取值格式" />
          <!-- <el-select v-model="formData.paramFormat"
                     placeholder="请选择取值模式">
            <el-option label="#.00000"
                       value="#.00000" />
            <el-option label="#.0000"
                       value="#.0000" />
            <el-option label="#.000"
                       value="#.000" />
            <el-option label="#.00"
                       value="#.00" />
          </el-select> -->
        </el-form-item>
        <el-form-item label="下拉字典"
                      v-else-if="formData.paramType == '3'"
@@ -348,7 +359,7 @@
  // const isProductTypeEdit = ref(false);
  const handleParamTypeChange = () => {
    if (formData.paramType === "1") {
      formData.paramFormat = "#.0000";
      formData.paramFormat = "#.00000";
    } else if (formData.paramType === "4") {
      formData.paramFormat = "YYYY-MM-DD HH:mm:ss";
    } else {
@@ -497,6 +508,7 @@
      row.valueMode !== undefined ? String(row.valueMode) : "1";
    formData.unit = row.unit || "";
    formData.remark = row.remark || "";
    formData.paramFormat = row.paramFormat || "";
    dialogVisible.value = true;
  };
src/views/productionManagement/productionReporting/detailDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,486 @@
<template>
  <el-dialog v-model="dialogVisible"
             :title="dialogTitle"
             width="1000px"
             :close-on-click-modal="false"
             custom-class="custom-dialog">
    <div class="detail-container">
      <!-- åŸºç¡€ä¿¡æ¯ -->
      <div class="detail-section">
        <h3 class="section-title">基础信息</h3>
        <el-descriptions :column="3"
                         border>
          <el-descriptions-item label="生产订单号">{{ detailData.npsNo || '-' }}</el-descriptions-item>
          <el-descriptions-item label="班组">{{ detailData.schedule || '-' }}</el-descriptions-item>
          <el-descriptions-item label="岗位人员">{{ detailData.postName || '-' }}</el-descriptions-item>
          <el-descriptions-item label="产品编码">{{ detailData.materialCode || '-' }}</el-descriptions-item>
          <el-descriptions-item label="产品名称">{{ detailData.productName || '-' }}</el-descriptions-item>
          <el-descriptions-item label="规格">{{ detailData.model || '-' }}</el-descriptions-item>
          <el-descriptions-item label="合格数量">{{ detailData.qualifiedQuantity || 0 }} æ–¹</el-descriptions-item>
          <el-descriptions-item label="不合格数量">{{ detailData.unqualifiedQuantity || 0 }} æ–¹</el-descriptions-item>
          <el-descriptions-item label="总数量">{{ detailData.quantity || 0 }} æ–¹</el-descriptions-item>
          <el-descriptions-item label="报工时间">{{ formatTime(detailData.reportingTime) }}</el-descriptions-item>
          <el-descriptions-item label="创建时间">{{ formatTime(detailData.createTime) }}</el-descriptions-item>
          <el-descriptions-item label="更新时间">{{ formatTime(detailData.updateTime) }}</el-descriptions-item>
        </el-descriptions>
      </div>
      <!-- å·¥åºä¿¡æ¯ -->
      <div class="detail-section"
           v-if="detailData.productionProductRouteItemDtoList && detailData.productionProductRouteItemDtoList.length > 0">
        <h3 class="section-title">工序信息</h3>
        <div v-for="(process, index) in detailData.productionProductRouteItemDtoList"
             :key="process.id"
             class="process-item">
          <div class="process-header">
            <h4 class="process-title">工序 {{ index + 1 }}</h4>
            <div class="process-info">
              <span class="process-label">岗位人员:{{ process.postName || '-' }}</span>
              <span class="process-label">工序ID:{{ process.processId || '-' }}</span>
            </div>
          </div>
          <!-- å·¥åºåŸºæœ¬ä¿¡æ¯ -->
          <div class="process-details">
            <el-descriptions :column="2"
                             border>
              <el-descriptions-item label="设备异常情况">{{ process.equipmentMalfunction || '-' }}</el-descriptions-item>
              <el-descriptions-item label="当班设备处置">{{ process.equipmentDisposal || '-' }}</el-descriptions-item>
              <el-descriptions-item label="工艺人员交待"
                                    :span="2">{{ process.processExplained || '-' }}</el-descriptions-item>
            </el-descriptions>
          </div>
          <!-- å·¥åºå‚æ•° -->
          <div v-if="process.productionProductRouteItemParamDtoList && process.productionProductRouteItemParamDtoList.length > 0">
            <!-- BOM信息 -->
            <div class="param-section"
                 v-if="getBomList(process.productionProductRouteItemParamDtoList).length > 0">
              <h5 class="param-title">BOM信息</h5>
              <el-table :data="getBomList(process.productionProductRouteItemParamDtoList)"
                        style="width: 100%"
                        size="small">
                <el-table-column prop="paramName"
                                 label="参数名称"
                                 min-width="120"></el-table-column>
                <el-table-column prop="productValue"
                                 label="产品值"
                                 min-width="100"></el-table-column>
                <el-table-column prop="unit"
                                 label="单位"
                                 width="80"></el-table-column>
              </el-table>
            </div>
            <!-- å‚数信息 -->
            <div class="param-section"
                 v-if="getParamList(process.productionProductRouteItemParamDtoList).length > 0">
              <h5 class="param-title">参数信息</h5>
              <div v-for="(group, sort) in getParamGroups(process.productionProductRouteItemParamDtoList)"
                   :key="sort"
                   class="param-group">
                <div class="group-header">
                  <span class="group-title">参数组 {{ sort }}</span>
                </div>
                <div class="param-grid">
                  <div v-for="param in group"
                       :key="param.id"
                       class="param-item">
                    <span class="param-label">{{ param.paramName || '-' }}:</span>
                    <span class="param-value">{{ param.paramValue || '-' }}</span>
                    <span v-if="param.unit && param.unit !== '/'"
                          class="param-unit">{{ param.unit }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <!-- ä¸Šä¼ æ–‡ä»¶ -->
          <div class="file-section"
               v-if="process.fileList && process.fileList.length > 0">
            <h5 class="file-title">上传文件</h5>
            <div class="file-grid">
              <div v-for="file in process.fileList"
                   :key="file.id"
                   class="file-item">
                <img v-if="file.fileUrl"
                     :src="baseUrl + file.fileUrl"
                     :alt="file.fileName"
                     class="file-image"
                     @click="handleFilePreview(file)" />
                <div class="file-info">
                  <span class="file-name">{{ file.fileName || '-' }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="dialogVisible = false">关闭</el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup>
  import { ref, computed, watch } from "vue";
  import dayjs from "dayjs";
  const baseUrl = import.meta.env.VITE_APP_BASE_API;
  const props = defineProps({
    visible: {
      type: Boolean,
      default: false,
    },
    data: {
      type: Object,
      default: () => ({}),
    },
  });
  const emit = defineEmits(["update:visible"]);
  const dialogVisible = computed({
    get: () => props.visible,
    set: value => emit("update:visible", value),
  });
  const dialogTitle = computed(() => "生产报工详情");
  const detailData = ref(props.data);
  // æ ¼å¼åŒ–æ—¶é—´
  const formatTime = time => {
    return time ? dayjs(time).format("YYYY-MM-DD HH:mm:ss") : "-";
  };
  // æ ¼å¼åŒ–文件列表
  const formatFileList = fileList => {
    return fileList.map(file => ({
      name: file.fileName,
      url: file.fileUrl,
      size: file.fileSize,
    }));
  };
  // å¤„理文件预览
  const handleFilePreview = file => {
    // è¿™é‡Œå¯ä»¥å®žçŽ°æ–‡ä»¶é¢„è§ˆé€»è¾‘
    console.log("预览文件:", file);
  };
  // èŽ·å–BOM列表
  const getBomList = paramList => {
    return paramList.filter(item => item.bomId);
  };
  // èŽ·å–å‚æ•°åˆ—è¡¨
  const getParamList = paramList => {
    return paramList.filter(item => !item.bomId);
  };
  // æŒ‰sourceSort分组参数
  const getParamGroups = paramList => {
    const params = getParamList(paramList);
    const groups = {};
    params.forEach(param => {
      const sort = param.sourceSort || 1;
      if (!groups[sort]) {
        groups[sort] = [];
      }
      groups[sort].push(param);
    });
    return groups;
  };
  // ç›‘听数据变化
  watch(
    () => props.data,
    newData => {
      detailData.value = newData;
    },
    { deep: true }
  );
</script>
<style scoped>
  .detail-container {
    max-height: 600px;
    overflow-y: auto;
    padding: 0 16px;
  }
  .detail-section {
    margin-bottom: 28px;
    background-color: #ffffff;
    border-radius: 8px;
    padding: 20px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
  }
  .section-title {
    font-size: 16px;
    font-weight: 600;
    margin-bottom: 16px;
    color: #1a1a1a;
    border-bottom: 2px solid #409eff;
    padding-bottom: 10px;
  }
  .process-item {
    margin-bottom: 24px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 8px;
    border: 1px solid #ebeef5;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  }
  .process-header {
    margin-bottom: 20px;
    padding-bottom: 12px;
    border-bottom: 1px solid #f0f2f5;
  }
  .process-title {
    font-size: 15px;
    font-weight: 600;
    margin-bottom: 12px;
    color: #1a1a1a;
    display: flex;
    align-items: center;
  }
  .process-title::before {
    content: "";
    display: inline-block;
    width: 4px;
    height: 16px;
    background-color: #409eff;
    margin-right: 8px;
    border-radius: 2px;
  }
  .process-info {
    display: flex;
    gap: 20px;
    font-size: 13px;
    color: #606266;
  }
  .process-label {
    padding: 4px 12px;
    background-color: #ecf5ff;
    border-radius: 4px;
    color: #409eff;
    font-weight: 500;
  }
  .process-details {
    margin-bottom: 20px;
  }
  .param-section {
    margin-bottom: 20px;
    background-color: #f9f9f9;
    border-radius: 6px;
    padding: 16px;
    border: 1px solid #f0f2f5;
  }
  .param-title {
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 14px;
    color: #1a1a1a;
    padding-bottom: 8px;
    border-bottom: 1px solid #e8e8e8;
  }
  .file-section {
    margin-top: 20px;
    background-color: #f9f9f9;
    border-radius: 6px;
    padding: 16px;
    border: 1px solid #f0f2f5;
  }
  .file-title {
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 14px;
    color: #1a1a1a;
    padding-bottom: 8px;
    border-bottom: 1px solid #e8e8e8;
  }
  .file-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 16px;
  }
  .file-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: #ffffff;
    border: 1px solid #e8e8e8;
    border-radius: 6px;
    padding: 10px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
  }
  .file-item:hover {
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    border-color: #409eff;
    transform: translateY(-2px);
  }
  .file-image {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border-radius: 4px;
    cursor: pointer;
    margin-bottom: 8px;
  }
  .file-info {
    width: 100%;
    text-align: center;
  }
  .file-name {
    font-size: 12px;
    color: #606266;
    word-break: break-all;
    line-height: 1.4;
  }
  .param-group {
    margin-bottom: 16px;
    padding: 14px;
    background-color: #ffffff;
    border-radius: 6px;
    border: 1px solid #e8e8e8;
  }
  .group-header {
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid #f0f2f5;
  }
  .group-title {
    font-size: 14px;
    font-weight: 600;
    color: #303133;
  }
  .param-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
  }
  .param-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    border-bottom: 1px solid #f5f7fa;
  }
  .param-item:last-child {
    border-bottom: none;
  }
  .param-label {
    font-size: 13px;
    color: #606266;
    min-width: 100px;
    font-weight: 500;
  }
  .param-value {
    font-size: 13px;
    color: #1a1a1a;
    font-weight: 600;
    flex: 1;
  }
  .param-unit {
    font-size: 12px;
    color: #909399;
    background-color: #f0f2f5;
    padding: 2px 6px;
    border-radius: 3px;
  }
  .dialog-footer {
    text-align: center;
    padding: 20px;
    border-top: 1px solid #ebeef5;
  }
  .dialog-footer .el-button {
    min-width: 100px;
    padding: 8px 20px;
  }
  /* è‡ªå®šä¹‰å¯¹è¯æ¡†æ ·å¼ */
  :deep(.custom-dialog) {
    border-radius: 12px;
    overflow: hidden;
  }
  :deep(.custom-dialog .el-dialog__header) {
    background-color: #f5f7fa;
    padding: 20px;
    border-bottom: 1px solid #ebeef5;
  }
  :deep(.custom-dialog .el-dialog__title) {
    font-size: 18px;
    font-weight: 600;
    color: #1a1a1a;
  }
  :deep(.custom-dialog .el-dialog__body) {
    padding: 20px;
  }
  /* è¡¨æ ¼æ ·å¼ä¼˜åŒ– */
  :deep(.el-table) {
    border-radius: 6px;
    overflow: hidden;
  }
  :deep(.el-table th) {
    background-color: #f5f7fa;
    font-weight: 600;
    color: #303133;
  }
  :deep(.el-table tr:hover > td) {
    background-color: #ecf5ff !important;
  }
  /* æè¿°åˆ—表样式优化 */
  :deep(.el-descriptions) {
    border-radius: 6px;
    overflow: hidden;
  }
  :deep(.el-descriptions__label) {
    font-weight: 500;
    color: #606266;
  }
  :deep(.el-descriptions__content) {
    color: #1a1a1a;
    font-weight: 500;
  }
</style>
src/views/productionManagement/productionReporting/index.vue
@@ -4,14 +4,14 @@
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="生产订单号:">
          <el-input v-model="searchForm.orderNo"
          <el-input v-model="searchForm.npsNo"
                    placeholder="请输入"
                    clearable
                    style="width: 160px;"
                    @keyup.enter="handleQuery" />
        </el-form-item>
        <el-form-item label="班组:">
          <el-select v-model="searchForm.teamName"
          <el-select v-model="searchForm.schedule"
                     placeholder="请选择"
                     clearable
                     style="width: 160px;"
@@ -21,7 +21,7 @@
            <el-option label="夜班"
                       value="夜班" />
          </el-select>
          <!-- <el-input v-model="searchForm.teamName"
          <!-- <el-input v-model="searchForm.schedule"
                    placeholder="请输入""
                    @keyup.enter="handleQuery" /> -->
        </el-form-item>
@@ -54,17 +54,20 @@
                :isSelection="false"
                @selection-change="handleSelectionChange"
                @pagination="pagination">
        <template #outputVolume="{ row }">
          <span style="font-weight: bold;color: #409eff;">{{ row.outputVolume }}</span><span style="margin-left: 5px;color: #909399;">方</span>
        <template #totalQuantity="{ row }">
          <span style="font-weight: bold;color: #409eff;">{{ row.totalQuantity }}</span><span style="margin-left: 5px;color: #909399;">方</span>
        </template>
        <template #unqualifiedVolume="{ row }">
          <span style="font-weight: bold;color: #b43434;">{{ row.unqualifiedVolume }}</span><span style="margin-left: 5px;color: #909399;">方</span>
        <template #scrapQty="{ row }">
          <span style="font-weight: bold;color: #b43434;">{{ row.scrapQty }}</span><span style="margin-left: 5px;color: #909399;">方</span>
        </template>
        <template #completedVolume="{ row }">
          <span style="font-weight: bold;color: #28e431;">{{ row.completedVolume }}</span><span style="margin-left: 5px;color: #909399;">方</span>
        <template #quantity="{ row }">
          <span style="font-weight: bold;color: #28e431;">{{ row.quantity }}</span><span style="margin-left: 5px;color: #909399;">方</span>
        </template>
      </PIMTable>
    </div>
    <!-- è¯¦æƒ…弹窗 -->
    <detail-dialog v-model:visible="detailDialogVisible"
                   :data="detailData" />
  </div>
</template>
@@ -74,12 +77,12 @@
  import { ElMessage, ElMessageBox } from "element-plus";
  import dayjs from "dayjs";
  import {
    workListPage,
    productionReport,
    productionReportUpdate,
    productionReportDelete,
    productionReportDetail,
    productionReportListPage,
  } from "@/api/productionManagement/productionReporting.js";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import DetailDialog from "./detailDialog.vue";
  const router = useRouter();
  const { proxy } = getCurrentInstance();
@@ -87,11 +90,12 @@
  const tableColumn = ref([
    {
      label: "生产订单号",
      prop: "orderNo",
      prop: "npsNo",
      width: "150px",
    },
    {
      label: "班组",
      prop: "teamName",
      prop: "schedule",
      width: "120px",
      dataType: "tag",
      formatType: params => {
@@ -110,37 +114,37 @@
    },
    {
      label: "规格",
      prop: "specification",
      width: "120px",
      className: "specification-cell",
      prop: "productModelName",
      width: "150px",
      className: "productModelName-cell",
    },
    {
      label: "产出方量",
      prop: "outputVolume",
      width: "120px",
      prop: "totalQuantity",
      width: "100px",
      align: "right",
      dataType: "slot",
      slot: "outputVolume",
      slot: "totalQuantity",
    },
    {
      label: "不合格方量",
      prop: "unqualifiedVolume",
      width: "120px",
      prop: "scrapQty",
      width: "100px",
      align: "right",
      dataType: "slot",
      slot: "unqualifiedVolume",
      slot: "scrapQty",
    },
    {
      label: "完成方量",
      prop: "completedVolume",
      width: "120px",
      prop: "quantity",
      width: "100px",
      align: "right",
      dataType: "slot",
      slot: "completedVolume",
      slot: "quantity",
    },
    {
      label: "创建人",
      prop: "createBy",
      prop: "postName",
      width: "120px",
      dataType: "tag",
    },
@@ -190,170 +194,41 @@
  });
  const searchForm = reactive({
    orderNo: "",
    teamName: "",
    npsNo: "",
    schedule: "",
    productName: "",
  });
  const mockData = [
    {
      id: 1,
      orderNo: "PO202401001",
      teamName: "白班",
      materialCode: "PC001",
      productName: "标准砌块",
      specification: "600×240×200",
      outputVolume: 120.5,
      unqualifiedVolume: 2.3,
      completedVolume: 118.2,
      createBy: "张三",
      createTime: "2024-01-15 08:30:00",
    },
    {
      id: 2,
      orderNo: "PO202401002",
      teamName: "夜班",
      materialCode: "PC002",
      productName: "标准砌块",
      specification: "600×240×200",
      outputVolume: 150.8,
      unqualifiedVolume: 1.5,
      completedVolume: 149.3,
      createBy: "李四",
      createTime: "2024-01-15 09:15:00",
    },
    {
      id: 3,
      orderNo: "PO202401003",
      teamName: "白班",
      materialCode: "PC003",
      productName: "加气砌块",
      specification: "600×240×250",
      outputVolume: 95.2,
      unqualifiedVolume: 0.8,
      completedVolume: 94.4,
      createBy: "王五",
      createTime: "2024-01-15 10:00:00",
    },
    {
      id: 4,
      orderNo: "PO202401004",
      teamName: "白班",
      materialCode: "PC004",
      productName: "标准砌块",
      specification: "600×240×200",
      outputVolume: 180.6,
      unqualifiedVolume: 3.2,
      completedVolume: 177.4,
      createBy: "赵六",
      createTime: "2024-01-15 14:20:00",
    },
    {
      id: 5,
      orderNo: "PO202401005",
      teamName: "夜班",
      materialCode: "PC005",
      productName: "加气砌块",
      specification: "600×240×250",
      outputVolume: 110.3,
      unqualifiedVolume: 1.1,
      completedVolume: 109.2,
      createBy: "孙七",
      createTime: "2024-01-15 15:45:00",
    },
    {
      id: 6,
      orderNo: "PO202401006",
      teamName: "白班",
      materialCode: "PC006",
      productName: "标准砌块",
      specification: "600×240×200",
      outputVolume: 135.7,
      unqualifiedVolume: 2.5,
      completedVolume: 133.2,
      createBy: "周八",
      createTime: "2024-01-16 08:00:00",
    },
    {
      id: 7,
      orderNo: "PO202401007",
      teamName: "白班",
      materialCode: "PC007",
      productName: "加气砌块",
      specification: "600×240×250",
      outputVolume: 88.4,
      unqualifiedVolume: 0.6,
      completedVolume: 87.8,
      createBy: "吴九",
      createTime: "2024-01-16 09:30:00",
    },
    {
      id: 8,
      orderNo: "PO202401008",
      teamName: "夜班",
      materialCode: "PC008",
      productName: "标准砌块",
      specification: "600×240×200",
      outputVolume: 165.2,
      unqualifiedVolume: 2.8,
      completedVolume: 162.4,
      createBy: "郑十",
      createTime: "2024-01-16 11:00:00",
    },
    {
      id: 9,
      orderNo: "PO202401009",
      teamName: "白班",
      materialCode: "PC009",
      productName: "加气砌块",
      specification: "600×240×250",
      outputVolume: 102.5,
      unqualifiedVolume: 1.3,
      completedVolume: 101.2,
      createBy: "钱十一",
      createTime: "2024-01-16 13:15:00",
    },
    {
      id: 10,
      orderNo: "PO202401010",
      teamName: "白班",
      materialCode: "PC010",
      productName: "标准砌块",
      specification: "600×240×200",
      outputVolume: 142.8,
      unqualifiedVolume: 2.1,
      completedVolume: 140.7,
      createBy: "刘十二",
      createTime: "2024-01-16 15:00:00",
    },
  ];
  const form = reactive({
    id: undefined,
    orderId: "",
    orderNo: "",
    teamName: "",
    npsNo: "",
    schedule: "",
    materialCode: "",
    productName: "",
    specification: "",
    outputVolume: 0,
    unqualifiedVolume: 0,
    completedVolume: 0,
    productModelName: "",
    totalQuantity: 0,
    scrapQty: 0,
    quantity: 0,
    processId: "",
    params: {},
  });
  const selectedRows = ref([]);
  const detailDialogVisible = ref(false);
  const detailData = ref({});
  const getList = () => {
    tableLoading.value = true;
    setTimeout(() => {
    productionReportListPage({
      current: page.current,
      size: page.size,
      ...searchForm,
    }).then(res => {
      tableData.value = res.data.records;
      page.total = res.data.total;
      tableLoading.value = false;
      const start = (page.current - 1) * page.size;
      const end = start + page.size;
      tableData.value = mockData.slice(start, end);
      page.total = mockData.length;
    }, 500);
    });
  };
  const handleQuery = () => {
@@ -362,8 +237,8 @@
  };
  const handleReset = () => {
    searchForm.orderNo = "";
    searchForm.teamName = "";
    searchForm.npsNo = "";
    searchForm.schedule = "";
    searchForm.productName = "";
    page.current = 1;
    getList();
@@ -381,16 +256,17 @@
  const handleAdd = () => {
    Object.assign(form, {
      type: "add",
      id: undefined,
      orderId: "",
      orderNo: "",
      teamName: "",
      npsNo: "",
      schedule: "",
      materialCode: "",
      productName: "",
      specification: "",
      outputVolume: 0,
      unqualifiedVolume: 0,
      completedVolume: 0,
      productModelName: "",
      totalQuantity: 0,
      scrapQty: 0,
      quantity: 0,
      processId: "",
      params: {},
    });
@@ -401,30 +277,58 @@
  };
  const handleEdit = row => {
    Object.assign(form, {
      id: row.id,
      orderId: row.orderId || "",
      orderNo: row.orderNo,
      teamName: row.teamName,
      materialCode: row.materialCode,
      productName: row.productName,
      specification: row.specification,
      outputVolume: row.outputVolume,
      unqualifiedVolume: row.unqualifiedVolume,
      completedVolume: row.completedVolume,
      createBy: row.createBy || "",
      createTime: row.createTime || new Date(),
      processId: row.processId || "",
      params: row.params || {},
    });
    router.push({
      path: "/productionManagement/ReportingDialog",
      query: { data: JSON.stringify(form) },
    });
    // è°ƒç”¨è¯¦æƒ…接口获取完整数据
    productionReportDetail(row.id)
      .then(res => {
        if (res.code === 200) {
          const detailData = res.data;
          // æž„建编辑表单数据
          const editForm = {
            id: row.id,
            type: "edit",
            orderId: detailData.productOrderId || "",
            npsNo: detailData.npsNo || "",
            schedule: detailData.schedule || "",
            materialCode: detailData.materialCode || "",
            productName: detailData.productName || "",
            productModelName: detailData.model || "",
            totalQuantity: detailData.quantity || 0,
            scrapQty: detailData.unqualifiedQuantity || 0,
            quantity: detailData.qualifiedQuantity || 0,
            createBy: detailData.postName || "",
            createTime: detailData.createTime || new Date(),
            processId: "",
            params: {},
            // ä¼ é€’工序信息
            productionProductRouteItemDtoList:
              detailData.productionProductRouteItemDtoList || [],
          };
          router.push({
            path: "/productionManagement/ReportingDialog",
            query: { data: JSON.stringify(editForm) },
          });
        } else {
          ElMessage.error("获取详情失败");
        }
      })
      .catch(() => {
        ElMessage.error("获取详情失败");
      });
  };
  const handleDetail = row => {
    ElMessage.info("详情功能待实现");
    productionReportDetail(row.id)
      .then(res => {
        if (res.code === 200) {
          detailData.value = res.data;
          detailDialogVisible.value = true;
        } else {
          ElMessage.error("获取详情失败");
        }
      })
      .catch(() => {
        ElMessage.error("获取详情失败");
      });
  };
  const handleDelete = row => {
@@ -434,7 +338,7 @@
      type: "warning",
    })
      .then(() => {
        productionReportDelete({ id: row.id })
        productionReportDelete(row.id)
          .then(() => {
            ElMessage.success("删除成功");
            getList();
@@ -552,7 +456,7 @@
  }
</style>
<style lang="scss">
  .specification-cell {
  .productModelName-cell {
    color: #7a7d81;
    font-style: italic;
  }
src/views/productionManagement/productionReporting/reportingDialog.vue
@@ -305,10 +305,10 @@
                                    :label="`${item.productName} ${item.model}`"
                                    class="form-item">
                        <div class="consumable-input-group">
                          <el-input-number v-model="getProcessInfo(parseInt(activeProcessId)).consumables[item.id]"
                          <el-input-number v-model="getProcessInfo(parseInt(activeProcessId)).consumables[item.bomId]"
                                           :min="0"
                                           :model-value="getConsumableValue(parseInt(activeProcessId), item.id)"
                                           @change="val => getProcessInfo(parseInt(activeProcessId)).consumables[item.id] = val"
                                           :model-value="getConsumableValue(parseInt(activeProcessId), item.bomId)"
                                           @change="val => getProcessInfo(parseInt(activeProcessId)).consumables[item.bomId] = val"
                                           class="consumable-input" />
                          <span class="consumable-unit">{{ item.unit }}</span>
                        </div>
@@ -353,7 +353,7 @@
                    </div>
                    <div class="card-body">
                      <div class="param-grid">
                        <el-form-item v-for="param in params"
                        <el-form-item v-for="param in params[activeProcessId] || []"
                                      :key="param.id"
                                      :label="param.paramName"
                                      :label-width="120"
@@ -362,9 +362,10 @@
                          <template v-if="param.paramType == '1'">
                            <!-- æ•°å­—类型 -->
                            <div class="param-input-group">
                              <!-- :precision="getPrecision(param.paramFormat)" -->
                              <el-input-number v-model="form.paramGroups[activeProcessId][index][param.id]"
                                               controls-position="right"
                                               :precision="getPrecision(param.paramFormat)"
                                               :key="param.id"
                                               class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
@@ -376,6 +377,7 @@
                            <!-- æ–‡æœ¬ç±»åž‹ -->
                            <div class="param-input-group">
                              <el-input v-model="form.paramGroups[activeProcessId][index][param.id]"
                                        :key="param.id"
                                        class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
@@ -388,6 +390,7 @@
                            <div class="param-input-group">
                              <el-select v-model="form.paramGroups[activeProcessId][index][param.id]"
                                         placeholder="请选择"
                                         :key="param.id"
                                         class="param-select"
                                         style="width: 100%">
                                <el-option v-for="option in dictOptions[param.paramFormat] || []"
@@ -406,6 +409,7 @@
                            <div class="param-input-group">
                              <el-date-picker :value-format="param.paramFormat"
                                              :format="param.paramFormat"
                                              :key="param.id"
                                              :type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'"
                                              v-model="form.paramGroups[activeProcessId][index][param.id]"
                                              class="param-input" />
@@ -419,6 +423,7 @@
                            <!-- å…¶ä»–类型 -->
                            <div class="param-input-group">
                              <el-input v-model="form.paramGroups[activeProcessId][index][param.id]"
                                        :key="param.id"
                                        class="param-input" />
                              <span v-if="param.unit && param.unit != '/'"
                                    class="param-unit">
@@ -453,7 +458,7 @@
                      </template>
                    </el-table-column>
                    <!-- å‚数列 -->
                    <el-table-column v-for="param in params"
                    <el-table-column v-for="param in params[activeProcessId] || []"
                                     :key="param.id"
                                     :min-width="200">
                      <template #header>
@@ -462,20 +467,23 @@
                      <template #default="{ row }">
                        <template v-if="param.paramType == '1'">
                          <!-- æ•°å­—类型 -->
                          <!-- :precision="getPrecision(param.paramFormat)" -->
                          <el-input-number v-model="row[param.id]"
                                           controls-position="right"
                                           :precision="getPrecision(param.paramFormat)"
                                           :key="param.id"
                                           class="table-input" />
                        </template>
                        <template v-else-if="param.paramType == '2'">
                          <!-- æ–‡æœ¬ç±»åž‹ -->
                          <el-input v-model="row[param.id]"
                                    :key="param.id"
                                    class="table-input" />
                        </template>
                        <template v-else-if="param.paramType == '3'">
                          <!-- å­—典类型 -->
                          <el-select v-model="row[param.id]"
                                     placeholder="请选择"
                                     :key="param.id"
                                     class="table-select">
                            <el-option v-for="option in dictOptions[param.paramFormat] || []"
                                       :key="option.dictValue"
@@ -487,6 +495,7 @@
                          <!-- æ—¥æœŸç±»åž‹ -->
                          <el-date-picker :value-format="param.paramFormat"
                                          :format="param.paramFormat"
                                          :key="param.id"
                                          width="100%"
                                          :type="param.paramFormat=='YYYY-MM-DD'?'date':'datetime'"
                                          v-model="row[param.id]"
@@ -610,6 +619,7 @@
  import {
    productionRecordAdd,
    productionRecordAddSubmit,
    productionRecordEditSubmit,
  } from "@/api/productionManagement/productProcessRoute.js";
  import { userListNoPage } from "@/api/system/user.js";
  import { getInfo } from "@/api/login.js";
@@ -623,7 +633,9 @@
  const route = useRoute();
  const data = route.query.data ? JSON.parse(route.query.data) : {};
  const dialogTitle = computed(() => (data.id ? "编辑报工" : "新增报工"));
  const dialogTitle = computed(() =>
    form.type === "edit" ? "编辑报工" : "新增报工"
  );
  const formRef = ref(null);
  const isSubmitting = ref(false);
@@ -656,20 +668,23 @@
  const useTableView = ref(false); // æŽ§åˆ¶æ˜¯å¦ä½¿ç”¨è¡¨æ ¼è§†å›¾
  const form = reactive({
    type: data.type || "add",
    id: data.id || undefined,
    orderId: data.orderId || "",
    orderId: data.productOrderId || "",
    npsNo: data.npsNo || "",
    teamName: data.teamName || "白班",
    teamName: data.schedule || 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 || "当前登录人",
    specification: data.productModelName || "",
    outputVolume: data.totalQuantity || data.outputVolume || 0,
    unqualifiedVolume: data.scrapQty || data.unqualifiedQuantity || 0,
    completedVolume: data.quantity || data.completedVolume || 0,
    createBy: data.createBy || data.postName || "当前登录人",
    createTime: data.createTime || new Date(),
    paramGroups: data.paramGroups || {}, // å­˜å‚¨æ¯ä¸ªå·¥åºçš„参数组
    processInfo: data.processInfo || {}, // å­˜å‚¨æ¯ä¸ªå·¥åºçš„基本信息
    productionProductRouteItemDtoList:
      data.productionProductRouteItemDtoList || [], // å·¥åºä¿¡æ¯
  });
  const rules = {
@@ -783,6 +798,7 @@
        processExplained: "",
        files: [],
        consumables: {},
        delFileIds: [], // å­˜å‚¨è¦åˆ é™¤çš„æ–‡ä»¶id
      };
    }
    return form.processInfo[processId];
@@ -796,12 +812,12 @@
  };
  // èŽ·å–æ¶ˆè€—å“æ•°é‡ï¼Œé»˜è®¤ä¸º0
  const getConsumableValue = (processId, itemId) => {
  const getConsumableValue = (processId, bomId) => {
    const processInfo = getProcessInfo(processId);
    if (!processInfo.consumables[itemId]) {
      processInfo.consumables[itemId] = 0;
    if (!processInfo.consumables[bomId]) {
      processInfo.consumables[bomId] = 0;
    }
    return processInfo.consumables[itemId];
    return processInfo.consumables[bomId];
  };
  // å¤„理文件预览
@@ -848,13 +864,16 @@
    const processId = parseInt(activeProcessId.value);
    if (processId) {
      const processInfo = getProcessInfo(processId);
      // è®°å½•被删除的文件id(只有编辑模式下的现有文件才需要记录)
      if (file.uid && !file.tempId) {
        processInfo.delFileIds.push(file.uid);
      }
      processInfo.files = fileList;
    }
  };
  // å¤„理文件变更
  const handleFileChange = async (file, fileList) => {
    console.log(file, fileList);
    const formData = new FormData();
    formData.append("file", file.raw);
@@ -867,7 +886,6 @@
        Authorization: `Bearer ${getToken()}`,
      },
    });
    console.log(uploadRes);
    if (uploadRes.code === 200) {
      const tempId = uploadRes.data.tempId;
      // å°†tempId存储到file对象中
@@ -905,16 +923,79 @@
      p => p.processId === parseInt(processId)
    );
    if (process) {
      params.value = process.orderRouteItemParaVos || [];
      params.value[processId] = process.orderRouteItemParaVos || [];
      // åˆå§‹åŒ–参数组
      if (!form.paramGroups[processId]) {
        form.paramGroups[processId] = [];
      }
      // æ£€æŸ¥æ˜¯å¦æœ‰ç¼–辑数据
      if (
        form.productionProductRouteItemDtoList &&
        form.productionProductRouteItemDtoList.length > 0
      ) {
        const editProcess = form.productionProductRouteItemDtoList.find(
          p => p.processId === parseInt(processId)
        );
        if (editProcess && editProcess.productionProductRouteItemParamDtoList) {
          // æŒ‰sourceSort分组参数
          const paramGroups = {};
          editProcess.productionProductRouteItemParamDtoList.forEach(param => {
            if (!param.bomId) {
              // åªå¤„理非BOM参数
              const sort = param.sourceSort || 1;
              if (!paramGroups[sort]) {
                paramGroups[sort] = {};
              }
              paramGroups[sort][param.orderItemParamId] = param.paramValue || "";
              // å¦‚果是字典类型参数,获取字典数据
              if (param.paramType == "3" && param.paramFormat) {
                getDictOptions(param.paramFormat);
              }
            }
          });
          // è½¬æ¢ä¸ºæ•°ç»„
          form.paramGroups[processId] = Object.values(paramGroups);
          // åˆå§‹åŒ–工序基本信息
          if (editProcess) {
            const processInfo = getProcessInfo(parseInt(processId));
            processInfo.postName = editProcess.postName || "";
            processInfo.equipmentMalfunction =
              editProcess.equipmentMalfunction || "";
            processInfo.equipmentDisposal = editProcess.equipmentDisposal || "";
            processInfo.id = editProcess.id || "";
            processInfo.processExplained = editProcess.processExplained || "";
            // å¤„理文件
            if (editProcess.fileList) {
              processInfo.files = editProcess.fileList.map(file => ({
                name: file.fileName,
                url: file.fileUrl,
                uid: file.id,
              }));
            }
            // å¤„理BOM信息
            if (editProcess.productionProductRouteItemParamDtoList) {
              editProcess.productionProductRouteItemParamDtoList.forEach(
                param => {
                  if (param.bomId) {
                    // ä½¿ç”¨bomId作为key,因为getProcessStructures返回的item.id是bomId
                    processInfo.consumables[param.bomId] =
                      param.productValue || 0;
                  }
                }
              );
            }
          }
        }
      }
      // å¦‚果没有参数组,添加一个默认参数组
      if (form.paramGroups[processId].length === 0) {
        const defaultGroup = {};
        for (const param of params.value) {
        for (const param of params.value[processId]) {
          defaultGroup[param.id] = param.standardValue || "";
          // å¦‚果是字典类型参数,获取字典数据
          if (param.paramType == "3" && param.paramFormat) {
@@ -962,7 +1043,6 @@
        // æž„建请求参数
        const order = orderList.value.find(item => item.id === form.orderId);
        console.log(order, "order");
        const submitParams = {
          productOrderId: form.orderId,
          productId: order ? order.productId : null,
@@ -976,7 +1056,6 @@
            const processInfo = getProcessInfo(process.processId);
            const paramGroups = form.paramGroups[process.processId] || [];
            const productionProductRouteItemParamDtoList = [];
            // æ·»åŠ å‚æ•°ç»„
            paramGroups.forEach((group, index) => {
              Object.entries(group).forEach(([paramId, value]) => {
@@ -990,16 +1069,18 @@
                    )
                  : null;
                if (param) {
                  console.log(param, "param");
                  productionProductRouteItemParamDtoList.push({
                    id: parseInt(paramId),
                    standardValue: param.standardValue,
                    minValue: param.minValue,
                    maxValue: param.maxValue,
                    // standardValue: param.standardValue,
                    // minValue: param.minValue,
                    // maxValue: param.maxValue,
                    productId: param.productId,
                    productValue: value,
                    paramValue: value,
                    // productValue: value,
                    sourceSort: index + 1,
                    unit: param.unit,
                    isRequired: param.isRequired,
                    // isRequired: param.isRequired,
                  });
                }
              });
@@ -1007,11 +1088,10 @@
            // æ·»åŠ BOM信息
            const structures = getProcessStructures(process.processId);
            console.log(structures, "structures");
            structures.forEach(structure => {
              const consumableValue = getConsumableValue(
                process.processId,
                structure.id
                structure.bomId
              );
              if (consumableValue > 0) {
                productionProductRouteItemParamDtoList.push({
@@ -1023,35 +1103,66 @@
                });
              }
            });
            const fileIds = [];
            processInfo.files.forEach(file => {
              if (file.tempId) {
                fileIds.push(file.tempId);
              }
            });
            console.log(processInfo, "processInfo");
            return {
              postName: processInfo.postName,
              id: processInfo.id,
              equipmentMalfunction: processInfo.equipmentMalfunction,
              equipmentDisposal: processInfo.equipmentDisposal,
              processExplained: processInfo.processExplained,
              processId: process.processId,
              delFileIds: [...(processInfo.delFileIds || [])],
              productionProductRouteItemParamDtoList,
              files: processInfo.files.map(file => file.tempId || file.uid),
              // files: processInfo.files.map(file => file.tempId || file.uid),
              files: fileIds,
            };
          }),
        };
        console.log(submitParams, "submitParams");
        isSubmitting.value = false;
        // è°ƒç”¨API进行提交
        productionRecordAddSubmit(submitParams)
          .then(res => {
            if (res.code === 200) {
              ElMessage.success(data.id ? "修改成功" : "新增成功");
              router.back();
            } else {
              ElMessage.error(res.msg || "提交失败");
            }
          })
          .catch(error => {
            ElMessage.error("提交失败,请稍后重试");
            console.error("提交错误:", error);
          })
          .finally(() => {
            isSubmitting.value = false;
          });
        if (form.type === "edit") {
          submitParams.productMainId = form.id;
          productionRecordEditSubmit(submitParams)
            .then(res => {
              if (res.code === 200) {
                ElMessage.success(form.type === "edit" ? "修改成功" : "新增成功");
                router.back();
              } else {
                ElMessage.error(res.msg || "提交失败");
              }
            })
            .catch(error => {
              ElMessage.error("提交失败,请稍后重试");
              console.error("提交错误:", error);
            })
            .finally(() => {
              isSubmitting.value = false;
            });
        } else {
          productionRecordAddSubmit(submitParams)
            .then(res => {
              if (res.code === 200) {
                ElMessage.success(form.type === "edit" ? "修改成功" : "新增成功");
                router.back();
              } else {
                ElMessage.error(res.msg || "提交失败");
              }
            })
            .catch(error => {
              ElMessage.error("提交失败,请稍后重试");
              console.error("提交错误:", error);
            })
            .finally(() => {
              isSubmitting.value = false;
            });
        }
      }
    });
  };
@@ -1119,16 +1230,16 @@
    loadUsers();
    getCurrentUser();
    if (data.id) {
    if (form.type === "edit") {
      // ç¼–辑时设置表单数据
      Object.assign(form, data);
      // è®¾ç½®orderId
      orderId.value = data.orderId || "";
      orderId.value = form.orderId || "";
      // å¦‚果有订单ID,加载工序和参数
      if (data.orderId) {
      if (form.orderId) {
        // æ¨¡æ‹Ÿé€‰æ‹©è®¢å•的操作,触发数据加载
        setTimeout(() => {
          handleOrderChange(data.orderId);
          handleOrderChange(form.orderId);
        }, 100);
      }
    } else {