zhangwencui
4 天以前 108a4fb5ce13ed06596f7e125e59632e76aafa58
src/views/productionPlan/productionPlan/index.vue
@@ -50,6 +50,7 @@
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                height="calc(100vh - 350px)"
                :tableLoading="tableLoading"
                :isSelection="true"
                :selectable="isSelectable"
@@ -63,12 +64,12 @@
               width="500px">
      <el-form :model="mergeForm"
               label-width="120px">
        <el-form-item label="生产计划号">
          <el-input v-model="mergeForm.productionPlanNo"
        <el-form-item label="序列号">
          <el-input v-model="mergeForm.serialNo"
                    disabled />
        </el-form-item>
        <el-form-item label="生产计划数量">
          <el-input-number v-model="mergeForm.totalManufactureQuantity"
          <el-input-number v-model="mergeForm.totalquantity"
                           :min="1"
                           :step="1"
                           style="width: 100%" />
@@ -93,17 +94,83 @@
      <el-table :data="categorySummary"
                border
                style="width: 100%">
        <el-table-column prop="productCategory"
        <el-table-column prop="materialCategory"
                         label="产品类别"
                         align="center"
                         width="150" />
        <el-table-column prop="totalManufactureQuantity"
        <el-table-column prop="totalquantity"
                         label="总制造数量"
                         align="center" />
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showCategorySummaryDialog = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>
    <!-- 追踪进度弹窗 -->
    <el-dialog v-model="showTrackProgressDialog"
               :title="`追踪进度 - ${trackProgressForm.serialNo || ''}`"
               width="600px">
      <el-form :model="trackProgressForm"
               label-width="120px">
        <el-form-item label="序列号">
          <el-input v-model="trackProgressForm.serialNo"
                    disabled />
        </el-form-item>
        <el-form-item label="当前状态">
          <el-select v-model="trackProgressForm.currentStatus"
                     placeholder="请选择状态">
            <el-option label="待处理"
                       value="pending" />
            <el-option label="进行中"
                       value="processing" />
            <el-option label="已完成"
                       value="completed" />
          </el-select>
        </el-form-item>
        <el-form-item label="完成进度">
          <el-progress :percentage="trackProgressForm.completionRate"
                       :status="trackProgressForm.completionRate === 100 ? 'success' : ''" />
        </el-form-item>
        <el-form-item label="进度详情">
          <el-table :data="trackProgressForm.progressDetails"
                    border
                    style="width: 100%">
            <el-table-column prop="step"
                             label="步骤"
                             align="center"
                             width="100" />
            <el-table-column prop="status"
                             label="状态"
                             align="center"
                             width="100">
              <template #default="scope">
                <el-tag :type="scope.row.status === 'completed' ? 'success' : scope.row.status === 'processing' ? 'warning' : 'info'">
                  {{ scope.row.status === 'completed' ? '已完成' : scope.row.status === 'processing' ? '进行中' : '待开始' }}
                </el-tag>
              </template>
            </el-table-column>
            <el-table-column prop="startTime"
                             label="开始时间"
                             align="center"
                             width="180" />
            <el-table-column prop="endTime"
                             label="结束时间"
                             align="center"
                             width="180" />
          </el-table>
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="trackProgressForm.remark"
                    type="textarea" />
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="showTrackProgressDialog = false">关闭</el-button>
          <el-button type="primary"
                     @click="handleUpdateProgress">更新进度</el-button>
        </span>
      </template>
    </el-dialog>
@@ -114,7 +181,7 @@
  import { onMounted, ref } from "vue";
  import { ElMessage } from "element-plus";
  import dayjs from "dayjs";
  import { productOrderListPage } from "@/api/productionManagement/productionOrder.js";
  import { productionPlanListPage } from "@/api/productionPlan/productionPlan.js";
  import PIMTable from "./components/PIMTable.vue";
  const tableColumn = ref([
@@ -139,8 +206,8 @@
      width: "120px",
    },
    {
      label: "生产计划号",
      prop: "productionPlanNo",
      label: "序列号",
      prop: "serialNo",
      width: "140px",
    },
    {
@@ -155,7 +222,7 @@
    },
    {
      label: "产品类别",
      prop: "productCategory",
      prop: "materialCategory",
      width: "100px",
    },
    {
@@ -171,7 +238,7 @@
    },
    {
      label: "制造数量",
      prop: "manufactureQuantity",
      prop: "quantity",
      width: "100px",
      align: "right",
    },
@@ -237,8 +304,8 @@
          clickFun: row => {
            // 单独下发操作
            // 设置表单数据
            mergeForm.productionPlanNo = row.productionPlanNo;
            mergeForm.totalManufactureQuantity = row.manufactureQuantity;
            mergeForm.serialNo = row.serialNo;
            mergeForm.totalquantity = row.quantity;
            mergeForm.remark = "";
            // 打开弹窗
@@ -249,8 +316,7 @@
          name: "追踪进度",
          type: "text",
          clickFun: row => {
            // 追踪进度操作
            ElMessage.warning("追踪进度功能待开发");
            handleTrackProgress(row);
          },
        },
      ],
@@ -274,17 +340,118 @@
  const isShowNewModal = ref(false);
  // 合并下发表单数据
  const mergeForm = reactive({
    productionPlanNo: "",
    totalManufactureQuantity: 0,
    serialNo: "",
    totalquantity: 0,
    remark: "",
  });
  // 追踪进度弹窗控制
  const showTrackProgressDialog = ref(false);
  // 追踪进度表单数据
  const trackProgressForm = reactive({
    serialNo: "",
    currentStatus: "",
    completionRate: 0,
    progressDetails: [],
    remark: "",
  });
  // 处理追踪进度按钮点击
  const handleTrackProgress = row => {
    // 设置表单数据
    trackProgressForm.serialNo = row.serialNo;
    trackProgressForm.currentStatus = row.status;
    // 生成模拟进度数据
    trackProgressForm.progressDetails = generateProgressDetails(row.status);
    // 计算完成率
    trackProgressForm.completionRate = calculateCompletionRate(
      trackProgressForm.progressDetails
    );
    trackProgressForm.remark = "";
    // 打开弹窗
    showTrackProgressDialog.value = true;
  };
  // 生成模拟进度详情数据
  const generateProgressDetails = status => {
    const details = [
      {
        step: "计划确认",
        status: "completed",
        startTime: "2026-03-01 09:00:00",
        endTime: "2026-03-01 10:00:00",
      },
      {
        step: "物料准备",
        status:
          status === "completed"
            ? "completed"
            : status === "processing"
            ? "completed"
            : "pending",
        startTime:
          status === "completed" || status === "processing"
            ? "2026-03-01 10:30:00"
            : "",
        endTime:
          status === "completed" || status === "processing"
            ? "2026-03-02 16:00:00"
            : "",
      },
      {
        step: "生产加工",
        status:
          status === "completed"
            ? "completed"
            : status === "processing"
            ? "processing"
            : "pending",
        startTime:
          status === "completed" || status === "processing"
            ? "2026-03-03 08:00:00"
            : "",
        endTime: status === "completed" ? "2026-03-08 17:00:00" : "",
      },
      {
        step: "质量检验",
        status: status === "completed" ? "completed" : "pending",
        startTime: status === "completed" ? "2026-03-09 09:00:00" : "",
        endTime: status === "completed" ? "2026-03-09 15:00:00" : "",
      },
      {
        step: "入库",
        status: status === "completed" ? "completed" : "pending",
        startTime: status === "completed" ? "2026-03-10 10:00:00" : "",
        endTime: status === "completed" ? "2026-03-10 11:00:00" : "",
      },
    ];
    return details;
  };
  // 计算完成率
  const calculateCompletionRate = details => {
    const completedSteps = details.filter(
      step => step.status === "completed"
    ).length;
    return Math.round((completedSteps / details.length) * 100);
  };
  // 处理进度更新
  const handleUpdateProgress = () => {
    // 这里可以添加更新进度的逻辑
    ElMessage.success("进度更新成功");
    showTrackProgressDialog.value = false;
  };
  const data = reactive({
    searchForm: {
      customerName: "",
      salesContractNo: "",
      projectName: "",
      productCategory: "",
      materialCategory: "",
      specificationModel: "",
    },
  });
@@ -307,14 +474,14 @@
    // 遍历表格数据,按产品类别汇总
    tableData.value.forEach(row => {
      const category = row.productCategory;
      const category = row.materialCategory;
      if (!summary[category]) {
        summary[category] = {
          productCategory: category,
          totalManufactureQuantity: 0,
          materialCategory: category,
          totalquantity: 0,
        };
      }
      summary[category].totalManufactureQuantity += row.manufactureQuantity;
      summary[category].totalquantity += row.quantity;
    });
    // 转换为数组格式
@@ -326,164 +493,164 @@
    // 构造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    tableData.value = [
      {
        id: 1,
        source: "销售订单",
        status: "待处理",
        auditStatus: "已审核",
        orderNo: "SO20260301001",
        productionPlanNo: "PP20260301001",
        partNo: "P001",
        partName: "零件A",
        productCategory: "类别1",
        processFileNo: "PF20260301001",
        salesQuantity: 100,
        manufactureQuantity: 105,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-15",
        commitmentDate: "2026-03-10",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 2,
        source: "销售订单",
        status: "待处理",
        auditStatus: "已审核",
        orderNo: "SO20260301002",
        productionPlanNo: "PP20260301001",
        partNo: "P002",
        partName: "零件B",
        productCategory: "类别1",
        processFileNo: "PF20260301002",
        salesQuantity: 200,
        manufactureQuantity: 210,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-15",
        commitmentDate: "2026-03-10",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 3,
        source: "销售订单",
        status: "进行中",
        auditStatus: "已审核",
        orderNo: "SO20260301003",
        productionPlanNo: "PP20260301002",
        partNo: "P003",
        partName: "零件C",
        productCategory: "类别2",
        processFileNo: "PF20260301003",
        salesQuantity: 150,
        manufactureQuantity: 155,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-20",
        commitmentDate: "2026-03-15",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 4,
        source: "销售订单",
        status: "进行中",
        auditStatus: "已审核",
        orderNo: "SO20260301004",
        productionPlanNo: "PP20260301002",
        partNo: "P004",
        partName: "零件D",
        productCategory: "类别2",
        processFileNo: "PF20260301004",
        salesQuantity: 300,
        manufactureQuantity: 315,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-20",
        commitmentDate: "2026-03-15",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
      {
        id: 5,
        source: "销售订单",
        status: "已完成",
        auditStatus: "已审核",
        orderNo: "SO20260301005",
        productionPlanNo: "PP20260301003",
        partNo: "P005",
        partName: "零件E",
        productCategory: "类别3",
        processFileNo: "PF20260301005",
        salesQuantity: 250,
        manufactureQuantity: 260,
        partUnit: "个",
        mainPlanDemandDate: "2026-03-10",
        commitmentDate: "2026-03-05",
        manufactureProperty: "常规",
        remark: "",
        updateTime: "2026-03-01",
        updateBy: "admin",
        createTime: "2026-03-01",
        createBy: "admin",
      },
    ];
    tableLoading.value = false;
    page.total = tableData.value.length;
    // 计算产品类别汇总统计
    calculateCategorySummary();
    // productOrderListPage(params)
    //   .then(res => {
    //     tableLoading.value = false;
    // tableData.value = [
    //   {
    //     id: 1,
    //     source: "销售订单",
    //     status: "待处理",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301001",
    //     serialNo: "PP20260301001",
    //     partNo: "P001",
    //     partName: "零件A",
    //     materialCategory: "类别1",
    //     processFileNo: "PF20260301001",
    //     salesQuantity: 100,
    //     quantity: 105,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-15",
    //     commitmentDate: "2026-03-10",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 2,
    //     source: "销售订单",
    //     status: "待处理",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301002",
    //     serialNo: "PP20260301001",
    //     partNo: "P002",
    //     partName: "零件B",
    //     materialCategory: "类别1",
    //     processFileNo: "PF20260301002",
    //     salesQuantity: 200,
    //     quantity: 210,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-15",
    //     commitmentDate: "2026-03-10",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 3,
    //     source: "销售订单",
    //     status: "进行中",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301003",
    //     serialNo: "PP20260301002",
    //     partNo: "P003",
    //     partName: "零件C",
    //     materialCategory: "类别2",
    //     processFileNo: "PF20260301003",
    //     salesQuantity: 150,
    //     quantity: 155,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-20",
    //     commitmentDate: "2026-03-15",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 4,
    //     source: "销售订单",
    //     status: "进行中",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301004",
    //     serialNo: "PP20260301002",
    //     partNo: "P004",
    //     partName: "零件D",
    //     materialCategory: "类别2",
    //     processFileNo: "PF20260301004",
    //     salesQuantity: 300,
    //     quantity: 315,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-20",
    //     commitmentDate: "2026-03-15",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    //   {
    //     id: 5,
    //     source: "销售订单",
    //     status: "已完成",
    //     auditStatus: "已审核",
    //     orderNo: "SO20260301005",
    //     serialNo: "PP20260301003",
    //     partNo: "P005",
    //     partName: "零件E",
    //     materialCategory: "类别3",
    //     processFileNo: "PF20260301005",
    //     salesQuantity: 250,
    //     quantity: 260,
    //     partUnit: "个",
    //     mainPlanDemandDate: "2026-03-10",
    //     commitmentDate: "2026-03-05",
    //     manufactureProperty: "常规",
    //     remark: "",
    //     updateTime: "2026-03-01",
    //     updateBy: "admin",
    //     createTime: "2026-03-01",
    //     createBy: "admin",
    //   },
    // ];
    // tableLoading.value = false;
    // page.total = tableData.value.length;
    // // 计算产品类别汇总统计
    // calculateCategorySummary();
    productionPlanListPage(params)
      .then(res => {
        tableLoading.value = false;
    //     tableData.value = res.data.records;
    //     page.total = res.data.total;
    //     // 计算产品类别汇总统计
    //     calculateCategorySummary();
    //   })
    //   .catch(() => {
    //     tableLoading.value = false;
    //   });
        tableData.value = res.data.records;
        page.total = res.data.total;
        // 计算产品类别汇总统计
        calculateCategorySummary();
      })
      .catch(() => {
        tableLoading.value = false;
      });
  };
  // 选中的生产计划号
  const selectedProductionPlanNo = ref("");
  // 选中的序列号
  const selectedserialNo = ref("");
  // 表格选择数据
  const handleSelectionChange = selection => {
    selectedRows.value = selection;
    // 如果有选中的行,记录第一个选中行的生产计划号
    // 如果有选中的行,记录第一个选中行的序列号
    if (selection.length > 0) {
      selectedProductionPlanNo.value = selection[0].productionPlanNo;
      selectedserialNo.value = selection[0].serialNo;
    } else {
      // 如果没有选中的行,清空生产计划号
      selectedProductionPlanNo.value = "";
      // 如果没有选中的行,清空序列号
      selectedserialNo.value = "";
    }
  };
  // 判断行是否可选择
  const isSelectable = row => {
    // 如果没有选中的行,所有行都可选择
    if (!selectedProductionPlanNo.value) {
    if (!selectedserialNo.value) {
      return true;
    }
    // 如果有选中的行,只有生产计划号相同的行才可选择
    return row.productionPlanNo === selectedProductionPlanNo.value;
    // 如果有选中的行,只有序列号相同的行才可选择
    return row.serialNo === selectedserialNo.value;
  };
  // 处理合并下发按钮点击
@@ -495,12 +662,12 @@
    // 计算总制造数量
    const totalQuantity = selectedRows.value.reduce((sum, row) => {
      return sum + row.manufactureQuantity;
      return sum + row.quantity;
    }, 0);
    // 设置表单数据
    mergeForm.productionPlanNo = selectedProductionPlanNo.value;
    mergeForm.totalManufactureQuantity = totalQuantity;
    mergeForm.serialNo = selectedserialNo.value;
    mergeForm.totalquantity = totalQuantity;
    mergeForm.remark = "";
    // 打开弹窗
@@ -521,56 +688,212 @@
</script>
<style scoped lang="scss">
  .app-container {
    padding: 24px;
    background-color: #f0f2f5;
    min-height: calc(100vh - 48px);
  }
  .search_form {
    align-items: start;
  }
  .summary-section {
    margin-bottom: 16px;
  }
  .horizontal-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    padding: 10px 0;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 24px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    transition: all 0.3s ease;
    &:hover {
      box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
    }
  }
  .summary-item {
    flex: 1;
    min-width: 120px;
    text-align: center;
    padding: 10px;
    background-color: #f5f7fa;
  .table_list {
    // margin-bottom: 24px;
    background-color: #ffffff;
    border-radius: 6px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
    overflow: hidden;
    height: calc(100vh - 250px);
  }
  :deep(.el-table) {
    border: none;
    border-radius: 6px;
    overflow: hidden;
    .el-table__header-wrapper {
      background-color: #fafafa;
      th {
        background-color: #fafafa;
        font-weight: 600;
        color: #303133;
        border-bottom: 1px solid #ebeef5;
        padding: 14px 0;
      }
    }
    .el-table__body-wrapper {
      tr {
        transition: all 0.3s ease;
        &:hover {
          background-color: #f5f7fa;
        }
        td {
          border-bottom: 1px solid #ebeef5;
          padding: 12px 0;
        }
      }
      tr.current-row {
        background-color: #ecf5ff;
      }
    }
    .el-table__empty-block {
      padding: 40px 0;
    }
  }
  .pagination-container {
    display: flex;
    justify-content: flex-end;
    padding: 16px 20px;
    background-color: #ffffff;
    border-top: 1px solid #ebeef5;
    border-radius: 0 0 12px 12px;
  }
  :deep(.el-button) {
    transition: all 0.3s ease;
    &:hover {
      transform: translateY(-1px);
    }
  }
  :deep(.el-dialog) {
    border-radius: 6px;
    overflow: hidden;
    .el-dialog__header {
      background-color: #fafafa;
      border-bottom: 1px solid #ebeef5;
      padding: 20px 24px;
      .el-dialog__title {
        font-size: 16px;
        font-weight: 600;
        color: #303133;
      }
    }
    .el-dialog__body {
      padding: 24px;
    }
    .el-dialog__footer {
      padding: 16px 24px;
      border-top: 1px solid #ebeef5;
      background-color: #fafafa;
    }
  }
  :deep(.el-form) {
    .el-form-item {
      margin-bottom: 20px;
      .el-form-item__label {
        font-weight: 500;
        color: #303133;
      }
      .el-input,
      .el-select,
      .el-date-picker,
      .el-input-number {
        width: 100%;
        // .el-input__inner {
        //   border-radius: 6px;
        //   border: 1px solid #dcdfe6;
        //   transition: all 0.3s ease;
        //   &:focus {
        //     border-color: #409eff;
        //     box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
        //   }
        // }
      }
    }
  }
  :deep(.el-tag) {
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
    padding: 2px 8px;
    font-size: 12px;
  }
  .summary-label {
    font-size: 14px;
    color: #606266;
    margin-bottom: 5px;
  @media (max-width: 768px) {
    .app-container {
      padding: 16px;
    }
    .search_form {
      flex-direction: column;
      align-items: flex-start;
      gap: 12px;
      .el-form {
        width: 100%;
        .el-form-item {
          width: 100%;
        }
      }
      > div {
        width: 100%;
        display: flex;
        gap: 12px;
        .el-button {
          flex: 1;
        }
      }
    }
    :deep(.el-table) {
      th,
      td {
        padding: 10px 0;
        font-size: 12px;
      }
    }
    :deep(.el-dialog) {
      width: 90% !important;
      margin: 20px auto !important;
    }
  }
  .consumption-value {
    font-weight: bold;
    color: #409eff;
  }
  .summary-value {
    font-size: 18px;
    font-weight: 600;
    color: #303133;
  .consumption-unit {
    font-size: 12px;
    color: #909399;
    margin-left: 4px;
  }
  ::v-deep .yellow {
    background-color: #faf0de;
  }
  ::v-deep .pink {
    background-color: #fae1de;
  }
  ::v-deep .red {
    background-color: #f80202;
  }
  ::v-deep .purple {
    background-color: #f4defa;
  .search_form {
    :deep(.el-form-item) {
      margin-bottom: 0px !important;
    }
  }
</style>