zhangwencui
9 小时以前 ed36047f6ce0b91dad25efc10c8a0e83dd533a68
src/views/productionManagement/productionOrder/index.vue
@@ -3,6 +3,14 @@
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="订单号:">
          <el-input v-model="searchForm.npsNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="产品名称:">
          <el-input v-model="searchForm.productName"
                    placeholder="请输入"
@@ -10,6 +18,29 @@
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery" />
        </el-form-item>
        <el-form-item label="产品类型:">
          <el-select v-model="searchForm.strength"
                     style="width: 200px;"
                     placeholder="请选择产品类型"
                     clearable
                     @change="handleQuery">
            <el-option v-for="option in productTypeOptions2"
                       :key="option.dictLabel"
                       :label="option.dictLabel"
                       :value="option.dictLabel" />
          </el-select>
        </el-form-item>
        <el-form-item label="创建时间:">
          <el-date-picker v-model="createTime"
                          type="daterange"
                          range-separator="至"
                          start-placeholder="开始日期"
                          value-format="YYYY-MM-DD"
                          format="YYYY-MM-DD"
                          end-placeholder="结束日期"
                          style="width: 300px;"
                          @change="handleQuery" />
        </el-form-item>
        <el-form-item label="规格:">
          <el-input v-model="searchForm.model"
@@ -35,13 +66,13 @@
                     @click="handleQuery">搜索</el-button>
          <el-button type="primary"
                     @click="handleReset">重置</el-button>
          <el-button type="danger"
                     @click="handleDelete">退回</el-button>
          <el-button @click="handleOut">导出</el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="danger"
                   @click="handleDelete">退回</el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
      <!-- <div style="width:350px;text-align:right;">
      </div> -->
    </div>
    <div class="table_list">
      <PIMTable rowKey="id"
@@ -58,6 +89,12 @@
          <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
        <template #quantity="{ row }">
          {{ row.quantity || '-' }}<span style="color:rgb(63, 95, 211)"> 块</span>
        </template>
        <template #completeQuantity="{ row }">
          {{ row.completeQuantity || '-' }}<span style="color:rgb(42, 169, 146)"> 方</span>
        </template>
      </PIMTable>
    </div>
@@ -89,6 +126,127 @@
    <new-product-order v-if="isShowNewModal"
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery" />
    <!-- 来源数据弹窗 -->
    <el-dialog v-model="sourceDataDialogVisible"
               title="来源数据"
               width="1000px">
      <div class="applyno-summary1">
        <div class="summary-item">
          <span class="summary-label">产品名称:</span>
          <span class="summary-value">
            <el-tag type="primary">{{ sourceRowData.productName || '-' }}</el-tag>
          </span>
        </div>
        <div class="summary-item">
          <span class="summary-label">产品规格:</span>
          <span class="summary-value">{{ sourceRowData.model || '-' }}</span>
        </div>
        <div class="summary-item">
          <span class="summary-label">物料编码:</span>
          <span class="summary-value">{{ sourceRowData.materialCode || '-' }}</span>
        </div>
        <div class="summary-item">
          <span class="summary-label">强度:</span>
          <span class="summary-value">{{ sourceRowData.strength || '-' }}</span>
        </div>
      </div>
      <div class="source-data-container">
        <!-- 左侧applyNo列表 -->
        <div class="applyno-list">
          <div class="list-header">申请单列表</div>
          <div class="list-body">
            <div v-for="(item, index) in sourceTableData"
                 :key="item.applyNo || index"
                 class="applyno-item"
                 :class="{ active: selectedApplyNo === item.applyNo }"
                 @click="selectApplyNo(item)">
              <div class="applyno-text">{{ item.applyNo }}</div>
              <div class="applyno-info">{{ item.customerName }}</div>
            </div>
          </div>
        </div>
        <!-- 右侧详细信息 -->
        <div class="detail-info">
          <div v-if="selectedSourceData && selectedSourceData.items && selectedSourceData.items.length > 0">
            <div v-for="item in selectedSourceData.items"
                 :key="item.id"
                 class="source-data-card">
              <!-- <div class="card-header">
                <div class="data-source-tag">
                </div>
                <div class="card-title">产品明细</div>
              </div> -->
              <div class="card-body">
                <div class="info-grid">
                  <div class="info-item">
                    <div class="info-label">数据来源</div>
                    <div class="info-value">
                      <el-tag :type="item.dataSourceType == 1 ? 'primary' : 'warning'">
                        {{ item.dataSourceType == 1 ? '钉钉同步' : '手动新增' }}
                      </el-tag>
                    </div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">块数</div>
                    <div class="info-value">{{ item.quantity || '-' }}<span style="color:rgb(63, 95, 211)"> 块</span></div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">方数</div>
                    <div class="info-value">{{ item.volume || '-' }}<span style="color:rgba(27, 104, 90, 0.76)"> 方</span></div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">下发状态</div>
                    <div class="info-value">
                      <el-tag :type="{
                        0: 'warning',
                        1: 'primary',
                        2: 'info'
                      }[item.status] || 'info'">
                        {{ item.status == 0 ? '待下发' : item.status == 1 ? '部分下发' : '已下发' }}
                      </el-tag>
                    </div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">已下发方数</div>
                    <div class="info-value">{{ item.assignedQuantity ? `${item.assignedQuantity}` : 0 }}<span style="color:rgba(214, 134, 22, 0.76)"> 方</span></div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">尺寸</div>
                    <div class="info-value">{{ item.length || '-' }}mm × {{ item.width || '-' }}mm × {{ item.height || '-' }}mm</div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">计划开始日期</div>
                    <div class="info-value">{{ item.startDate ? dayjs(item.startDate).format('YYYY-MM-DD') : '' }}</div>
                  </div>
                  <div class="info-item">
                    <div class="info-label">计划结束日期</div>
                    <div class="info-value">{{ item.endDate ? dayjs(item.endDate).format('YYYY-MM-DD') : '' }}</div>
                  </div>
                  <!-- <div class="info-item">
                    <div class="info-label">强度</div>
                    <div class="info-value">{{ item.strength || '' }}</div>
                  </div> -->
                </div>
                <div class="remarks-section">
                  <div class="info-item full-width">
                    <div class="info-label">备注 1</div>
                    <div class="info-value">{{ item.remarkOne || '-' }}</div>
                  </div>
                  <div class="info-item full-width">
                    <div class="info-label">备注 2</div>
                    <div class="info-value">{{ item.remarkTwo || '-' }}</div>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div v-else
               class="empty-state">
            <el-empty :description="selectedSourceData ? '该申请单暂无数据' : '请选择一个申请单'" />
          </div>
        </div>
      </div>
    </el-dialog>
  </div>
</template>
@@ -104,6 +262,8 @@
    bindingRoute,
    listProcessBom,
    delProductOrder,
    revokeProductOrder,
    getProductOrderSource,
  } from "@/api/productionManagement/productionOrder.js";
  import { listPage } from "@/api/productionManagement/processRoute.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
@@ -117,6 +277,11 @@
  const router = useRouter();
  const isShowNewModal = ref(false);
  const sourceDataDialogVisible = ref(false);
  const sourceTableData = ref([]);
  const selectedApplyNo = ref("");
  const selectedSourceData = ref(null);
  const sourceRowData = ref(null);
  const tableColumn = ref([
    {
@@ -153,11 +318,18 @@
      label: "产品名称",
      prop: "productName",
      width: "120px",
      dataType: "tag",
    },
    {
      label: "规格",
      prop: "model",
      width: "120px",
    },
    {
      label: "强度",
      prop: "strength",
      width: "120px",
      dataType: "tag",
    },
    {
      label: "物料编码",
@@ -168,14 +340,23 @@
      label: "工艺路线编号",
      prop: "processRouteCode",
      width: "200px",
      className: "status-cell",
    },
    {
      label: "需求数量",
      prop: "quantity",
      dataType: "slot",
      align: "right",
      slot: "quantity",
      width: 120,
    },
    {
      label: "完成数量",
      prop: "completeQuantity",
      dataType: "slot",
      align: "right",
      slot: "completeQuantity",
      width: 120,
    },
    {
      dataType: "slot",
@@ -200,6 +381,12 @@
      label: "交付日期",
      prop: "planCompleteTime",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
      width: 120,
    },
    {
      label: "创建时间",
      prop: "createTime",
      formatData: val => (val ? dayjs(val).format("YYYY-MM-DD HH:mm:ss") : ""),
      width: 120,
    },
@@ -259,6 +446,156 @@
  });
  const selectedRows = ref([]);
  // 来源数据弹窗相关
  const sourceTableColumn = ref([
    {
      label: "数据来源",
      width: "100px",
      prop: "dataSourceType",
      dataType: "tag",
      formatType: params => {
        const typeMap = {
          2: "warning",
          1: "primary",
        };
        return typeMap[params] || "info";
      },
      formatData: cell => (cell == 1 ? "钉钉同步" : "手动新增"),
    },
    {
      label: "申请单编号",
      prop: "applyNo",
      width: "150px",
    },
    {
      label: "客户名称",
      prop: "customerName",
      width: "150px",
    },
    {
      label: "产品名称",
      prop: "productName",
      width: "200px",
      dataType: "tag",
      formatType: params => {
        return "primary";
      },
    },
    {
      label: "产品规格",
      prop: "model",
      width: "150px",
      className: "spec-cell",
    },
    {
      label: "物料编码",
      prop: "materialCode",
      width: "150px",
    },
    {
      label: "块数",
      prop: "quantity",
      align: "right",
      dataType: "slot",
      slot: "quantity",
    },
    {
      label: "方数",
      prop: "volume",
      width: "150px",
      align: "right",
      dataType: "slot",
      slot: "volume",
      className: "volume-cell",
    },
    {
      label: "下发状态",
      prop: "status",
      width: "150px",
      className: "status-cell",
      dataType: "tag",
      formatType: params => {
        const typeMap = {
          0: "warning",
          1: "primary",
          2: "info",
        };
        return typeMap[params] || "info";
      },
      formatData: cell => {
        const statusMap = {
          0: "待下发",
          1: "部分下发",
          2: "已下发",
        };
        return statusMap[cell] || "";
      },
    },
    {
      label: "已下发方数",
      prop: "assignedQuantity",
      width: "150px",
      className: "spec-cell",
      formatData: cell => (cell ? `${cell}方` : 0),
    },
    {
      label: "长",
      prop: "length",
      className: "dimension-cell",
      formatData: cell => (cell ? `${cell}mm` : ""),
    },
    {
      label: "宽",
      prop: "width",
      className: "dimension-cell",
      formatData: cell => (cell ? `${cell}mm` : ""),
    },
    {
      label: "高",
      prop: "height",
      className: "dimension-cell",
      formatData: cell => (cell ? `${cell}mm` : ""),
    },
    {
      label: "计划开始日期",
      prop: "startDate",
      width: "150px",
      className: "date-cell",
      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
    },
    {
      label: "计划结束日期",
      prop: "endDate",
      width: "150px",
      className: "date-cell",
      formatData: cell => (cell ? dayjs(cell).format("YYYY-MM-DD") : ""),
    },
    {
      label: "强度",
      prop: "strength",
      formatData: cell => {
        if (!cell) return "";
        return cell;
      },
    },
    {
      label: "备注 1",
      width: "150px",
      prop: "remarkOne",
    },
    {
      label: "备注 2",
      width: "150px",
      prop: "remarkTwo",
    },
  ]);
  const sourceTableLoading = ref(false);
  const sourcePage = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const data = reactive({
    searchForm: {
      customerName: "",
@@ -266,6 +603,11 @@
      projectName: "",
      productName: "",
      model: "",
      dictCode: null,
      startTime: null,
      endTime: null,
      strength: null,
      status: "",
    },
  });
  const { searchForm } = toRefs(data);
@@ -388,7 +730,11 @@
      productName: "",
      model: "",
      status: "",
      strength: null,
      startTime: null,
      endTime: null,
    };
    createTime.value = [];
    handleQuery();
  };
  // 查询列表
@@ -412,11 +758,25 @@
    }
    handleQuery();
  };
  const createTime = ref([]);
  const getList = () => {
    tableLoading.value = true;
    // 构造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    params.startTime =
      createTime.value.length > 0
        ? dayjs(createTime.value[0]).format("YYYY-MM-DD HH:mm:ss")
        : undefined;
    params.endTime =
      createTime.value.length > 0
        ? dayjs(createTime.value[1])
            .hour(23)
            .minute(59)
            .second(59)
            .format("YYYY-MM-DD HH:mm:ss")
        : undefined;
    productOrderListPage(params)
      .then(res => {
        tableLoading.value = false;
@@ -474,21 +834,66 @@
    });
  };
  // 选择申请单
  const selectApplyNo = item => {
    selectedApplyNo.value = item.applyNo;
    selectedSourceData.value = item;
  };
  // 查看来源生产计划数据
  const showSourceData = row => {
    // 这里需要根据实际的API和路由进行调整
    // 假设生产订单中有生产计划ID字段,比如productionPlanId
    if (row.productionPlanId) {
      // 跳转到生产计划详情页面
      router.push({
        path: "/productionManagement/productionPlan",
        query: {
          id: row.productionPlanId,
        },
    // 存储点击来源按钮时传递的row参数
    sourceRowData.value = row;
    // 调用API获取来源数据
    getProductOrderSource(row.id)
      .then(res => {
        if (res.code === 200) {
          // 处理接口返回的数据,调整为我们需要的格式
          sourceTableData.value = res.data.map(item => {
            return {
              applyNo: item.applyNo,
              customerName: item.productPlans[0]?.customerName || "",
              items: item.productPlans.map(plan => {
                return {
                  id: plan.id,
                  dataSourceType: plan.dataSourceType,
                  productName: plan.productName,
                  model: plan.model,
                  materialCode: plan.materialCode,
                  quantity: plan.quantity,
                  volume: plan.volume,
                  status: plan.status,
                  assignedQuantity: plan.assignedQuantity,
                  length: plan.length,
                  width: plan.width,
                  height: plan.height,
                  startDate: plan.startDate,
                  endDate: plan.endDate,
                  strength: plan.strength,
                  remarkOne: plan.remarkOne,
                  remarkTwo: plan.remarkTwo,
                };
              }),
            };
          });
          sourcePage.total = sourceTableData.value.length;
          // 默认选择第一个申请单
          if (sourceTableData.value.length > 0) {
            selectApplyNo(sourceTableData.value[0]);
          } else {
            selectedApplyNo.value = "";
            selectedSourceData.value = null;
          }
          // 打开弹窗
          sourceDataDialogVisible.value = true;
        } else {
          proxy.$modal.msgError(res.msg || "获取来源数据失败");
        }
      })
      .catch(err => {
        proxy.$modal.msgError("获取来源数据失败");
        console.error(err);
      });
    } else {
      proxy.$modal.msgWarning("当前订单没有关联的生产计划");
    }
  };
  // 表格选择数据
@@ -496,13 +901,13 @@
    selectedRows.value = selection;
  };
  const handleDeleteSolo = row => {
    ElMessageBox.confirm("选中的内容将被退回,是否确认退回?", "导出", {
    ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(() => {
        delProductOrder([row.id]).then(res => {
        delProductOrder(row.id).then(res => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
@@ -526,8 +931,8 @@
      type: "warning",
    })
      .then(() => {
        delProductOrder(ids).then(res => {
          proxy.$modal.msgSuccess("删除成功");
        revokeProductOrder(ids).then(res => {
          proxy.$modal.msgSuccess("退回成功");
          getList();
        });
      })
@@ -556,8 +961,22 @@
  };
  const handleConfirmRoute = () => {};
  const productTypeOptions2 = ref([]);
  // 获取产品类型字典
  const getProductTypeOptions = () => {
    getDicts("block_strength")
      .then(res => {
        if (res.code === 200) {
          productTypeOptions2.value = res.data;
        }
      })
      .catch(err => {
        console.error("获取产品类型字典失败:", err);
      });
  };
  onMounted(() => {
    getProductTypeOptions();
    getList();
  });
</script>
@@ -583,3 +1002,233 @@
    background-color: #f4defa;
  }
</style>
<style lang="scss">
  .status-cell {
    font-weight: 600;
    color: #409eff;
    font-family: "Courier New", monospace;
    text-shadow: 0 1px 2px rgba(64, 158, 255, 0.2);
  }
  .source-data-container {
    display: flex;
    gap: 20px;
    height: 500px;
    .applyno-list {
      width: 250px;
      background: #fff;
      border-radius: 8px;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
      overflow: hidden;
      .list-header {
        padding: 12px 16px;
        background: #f5f7fa;
        border-bottom: 1px solid #e4e7ed;
        font-weight: 600;
        color: #303133;
      }
      .list-body {
        height: calc(100% - 48px);
        overflow-y: auto;
        .applyno-item {
          padding: 12px 16px;
          border-bottom: 1px solid #f0f2f5;
          cursor: pointer;
          transition: all 0.3s;
          &:hover {
            background: #f5f7fa;
          }
          &.active {
            background: #ecf5ff;
            border-left: 4px solid #409eff;
          }
          .applyno-text {
            font-weight: 600;
            color: #303133;
            font-family: "Courier New", monospace;
            margin-bottom: 4px;
          }
          .applyno-info {
            font-size: 12px;
            color: #909399;
          }
        }
      }
    }
    .detail-info {
      flex: 1;
      background: #fff;
      border-radius: 8px;
      // box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
      overflow: auto;
      display: flex;
      flex-direction: column;
      .applyno-summary {
        padding: 16px 20px;
        background: #f5f7fa;
        border-bottom: 1px solid #e4e7ed;
        display: flex;
        flex-wrap: wrap;
        gap: 16px;
        .summary-item {
          display: flex;
          align-items: center;
          .summary-label {
            font-size: 13px;
            color: #909399;
            margin-right: 8px;
            font-weight: 500;
          }
          .summary-value {
            font-size: 14px;
            color: #303133;
            font-weight: 500;
          }
        }
      }
      .empty-state {
        flex: 1;
        display: flex;
        align-items: center;
        justify-content: center;
      }
      .source-data-card {
        flex: 1;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        margin-top: 20px;
        margin-right: 20px;
        box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
        .card-header {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 16px 20px;
          background: #f5f7fa;
          border-bottom: 1px solid #e4e7ed;
          .data-source-tag {
            flex-shrink: 0;
          }
          .card-title {
            font-weight: 600;
            color: #303133;
            font-size: 14px;
          }
        }
        .card-body {
          flex: 1;
          padding: 20px;
          overflow-y: auto;
          background-color: #f5f7fa;
          .info-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 16px;
            margin-bottom: 20px;
            .info-item {
              display: flex;
              flex-direction: column;
              .info-label {
                font-size: 12px;
                color: #909399;
                margin-bottom: 4px;
                font-weight: 500;
              }
              .info-value {
                font-size: 14px;
                color: #303133;
                font-weight: 500;
              }
            }
            .info-item.full-width {
              grid-column: 1 / -1;
            }
          }
          .remarks-section {
            display: flex;
            // flex-direction: column;
            gap: 12px;
            border-top: 1px solid #e4e7ed;
            padding-top: 16px;
            .info-item {
              display: flex;
              width: 50%;
              flex-direction: column;
              .info-label {
                font-size: 12px;
                color: #909399;
                margin-bottom: 4px;
                font-weight: 500;
              }
              .info-value {
                font-size: 14px;
                color: #303133;
                line-height: 1.5;
                padding: 8px;
                background: #f9fafc;
                border-radius: 4px;
                border: 1px solid #ecf5ff;
              }
            }
          }
        }
      }
    }
  }
  .applyno-summary1 {
    padding: 16px 20px;
    background: #f5f7fa;
    border-bottom: 1px solid #e4e7ed;
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    .summary-item {
      display: flex;
      align-items: center;
      margin-right: 20px;
      .summary-label {
        font-size: 13px;
        color: #909399;
        margin-right: 8px;
        font-weight: 500;
      }
      .summary-value {
        font-size: 14px;
        color: #303133;
        font-weight: 500;
      }
    }
  }
</style>