yuan
2026-08-03 bdfab89d8afbc60faeb89715a461f77e877d7f5d
src/views/productionManagement/productionOrder/index.vue
@@ -75,6 +75,26 @@
                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
        <template #processRouteStatus="{ row }">
          <div v-if="row.processRouteStatus && row.processRouteStatus.length"
               class="process-progress-container">
            <div v-for="(item, index) in row.processRouteStatus"
                 :key="index"
                 class="process-step">
              <div class="step-content">
                <div class="step-circle"
                     :class="{ 'is-completed': item.percentage >= 100 }">
                  <span class="step-percentage"
                        :style="{ color: item.percentage >= 70 ? item.percentage >= 100 ? '#67c23a' : '#f56c6c' : '#000' }">{{ item.percentage }}%</span>
                </div>
                <div class="step-name">{{ item.name }}</div>
              </div>
              <div v-if="index < row.processRouteStatus.length - 1"
                   class="step-line"></div>
            </div>
          </div>
          <span v-else>-</span>
        </template>
      </PIMTable>
    </div>
    <el-dialog v-model="bindRouteDialogVisible"
@@ -174,7 +194,7 @@
    </el-dialog>
    <MaterialLedgerDialog v-model="materialDialogVisible"
                          :order-row="currentMaterialOrder"
                          @saved="getList" />
                           />
    <MaterialDetailDialog v-model="materialDetailDialogVisible"
                          :order-row="currentMaterialDetailOrder"
                          @confirmed="getList" />
@@ -241,7 +261,17 @@
    total: 0,
  });
  const tableColumn = ref([
  const processColumnWidth = computed(() => {
    if (!tableData.value || tableData.value.length === 0) return "200px";
    const maxProcesses = Math.max(
      ...tableData.value.map(row => row.processRouteStatus?.length || 0)
    );
    if (maxProcesses === 0) return "100px";
    // 每个工序圆圈 36px + 线条 30px = 66px,额外加 60px 边距和文字空间
    return `${maxProcesses * 66 + 60}px`;
  });
  const tableColumn = computed(() => [
    {
      label: "生产订单号",
      prop: "npsNo",
@@ -296,6 +326,13 @@
    {
      label: "完成数量",
      prop: "completeQuantity",
    },
    {
      label: "工序生产进度",
      prop: "processRouteStatus",
      dataType: "slot",
      slot: "processRouteStatus",
      width: processColumnWidth.value,
    },
    {
      dataType: "slot",
@@ -364,7 +401,7 @@
          name: "领料",
          type: "text",
          color: "#5EC7AB",
          showHide: row => !row.endOrder,
          showHide: row => !row.endOrder && !row.returned,
          clickFun: row => {
            openMaterialDialog(row);
          },
@@ -373,7 +410,7 @@
          name: "补料",
          type: "text",
          color: "#5EC7AB",
          showHide: row => !row.endOrder,
          showHide: row => !row.endOrder && !row.returned,
          clickFun: row => {
            openMaterialSupplementDialog(row);
          },
@@ -532,7 +569,7 @@
  const openBindRouteDialog = async (row, type) => {
    bindForm.orderId = row.id;
    bindForm.routeId = type === "add" ? null : row.processRouteCode;
    bindForm.routeId = type === "add" ? null : row.technologyRoutingId;
    bindRouteDialogVisible.value = true;
    routeOptions.value = [];
    if (!row.productModelId) {
@@ -626,14 +663,13 @@
  };
  const getList = () => {
    tableLoading.value = true;
    // 构造一个新的对象,不包含entryDate字段
    const params = { ...searchForm.value, ...page };
    params.entryDate = undefined;
    productOrderListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        tableData.value = res.data.records || [];
        page.total = res.data.total;
        tableLoading.value = false;
      })
      .catch(() => {
        tableLoading.value = false;
@@ -660,6 +696,7 @@
          bomNo: row.bomNo || "",
          description: data.description || "",
          quantity: row.quantity || 0,
          technologyRoutingId: data.technologyRoutingId,
          orderId,
          type: "order",
          editable: !row.endOrder,
@@ -747,9 +784,9 @@
    })
      .then(() => {
        proxy.download(
          "/productOrder/export",
          "/productionOrder/export",
          { ...searchForm.value },
          "生产订单.xlsx"
          "生产订单数据.xlsx"
        );
      })
      .catch(() => {
@@ -813,6 +850,64 @@
  .table_list {
    margin-top: unset;
  }
  .process-progress-container {
    display: inline-flex;
    align-items: center;
    padding: 10px 0;
    white-space: nowrap;
    .process-step {
      display: flex;
      align-items: center;
      position: relative;
      .step-content {
        display: flex;
        flex-direction: column;
        align-items: center;
        z-index: 1;
        .step-circle {
          width: 36px;
          height: 36px;
          border-radius: 50%;
          border: 2px solid #409eff;
          display: flex;
          align-items: center;
          justify-content: center;
          background-color: #fff;
          margin-bottom: 4px;
          .step-percentage {
            font-size: 11px;
            font-weight: bold;
          }
          &.is-completed {
            border-color: #67c23a;
            .step-percentage {
              color: #67c23a;
            }
          }
        }
        .step-name {
          font-size: 12px;
          color: #606266;
          white-space: nowrap;
        }
      }
      .step-line {
        width: 30px;
        height: 1px;
        background-color: #dcdfe6;
        margin: 0 -2px;
        margin-top: -20px; // 向上偏移以对齐圆心
      }
    }
  }
</style>
<style lang="scss">
  .status-cell {