liding
2026-04-15 53499f59c890387d8077ff3a9e81ee18e389e0e8
src/views/productionManagement/productionOrder/index.vue
@@ -1,100 +1,104 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm"
               :inline="true">
        <el-form-item label="客户名称:">
          <el-input v-model="searchForm.customerName"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery"/>
        </el-form-item>
        <el-form-item label="合同号:">
          <el-input v-model="searchForm.salesContractNo"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery"/>
        </el-form-item>
      <el-form :model="searchForm" :inline="true">
        <el-form-item label="产品名称:">
          <el-input v-model="searchForm.productCategory"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery"/>
          <el-input
            v-model="searchForm.productCategory"
            placeholder="请输入"
            clearable
            prefix-icon="Search"
            style="width: 160px"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item label="规格:">
          <el-input v-model="searchForm.specificationModel"
                    placeholder="请输入"
                    clearable
                    prefix-icon="Search"
                    style="width: 160px;"
                    @change="handleQuery"/>
          <el-input
            v-model="searchForm.specificationModel"
            placeholder="请输入"
            clearable
            prefix-icon="Search"
            style="width: 160px"
            @change="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary"
                     @click="handleQuery">搜索
          </el-button>
          <el-button type="primary" @click="handleQuery">搜索 </el-button>
        </el-form-item>
      </el-form>
      <div>
        <el-button type="primary" @click="isShowNewModal = true">新增</el-button>
        <el-button type="primary" @click="isShowNewModal = true"
          >新增</el-button
        >
        <el-button type="danger" @click="handleDelete">删除</el-button>
        <el-button @click="handleOut">导出</el-button>
      </div>
    </div>
    <div class="table_list">
      <PIMTable rowKey="id"
                :column="tableColumn"
                :tableData="tableData"
                :page="page"
                :tableLoading="tableLoading"
                :row-class-name="tableRowClassName"
                :isSelection="true"
                @selection-change="handleSelectionChange"
                @pagination="pagination">
      <PIMTable
        rowKey="id"
        :column="tableColumn"
        :tableData="tableData"
        :page="page"
        :tableLoading="tableLoading"
        :row-class-name="tableRowClassName"
        :isSelection="true"
        @selection-change="handleSelectionChange"
        @pagination="pagination"
      >
        <template #completionStatus="{ row }">
          <el-progress
              :percentage="toProgressPercentage(row?.completionStatus)"
              :color="progressColor(toProgressPercentage(row?.completionStatus))"
              :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
            :percentage="toProgressPercentage(row?.completionStatus)"
            :color="progressColor(toProgressPercentage(row?.completionStatus))"
            :status="
              toProgressPercentage(row?.completionStatus) >= 100
                ? 'success'
                : ''
            "
          />
        </template>
      </PIMTable>
    </div>
    <el-dialog v-model="bindRouteDialogVisible"
               title="绑定工艺路线"
               width="500px">
    <el-dialog
      v-model="bindRouteDialogVisible"
      title="绑定工艺路线"
      width="500px"
    >
      <el-form label-width="90px">
        <el-form-item label="工艺路线">
          <el-select v-model="bindForm.routeId"
                     placeholder="请选择工艺路线"
                     style="width: 100%;"
                     :loading="bindRouteLoading">
            <el-option v-for="item in routeOptions"
                       :key="item.id"
                       :label="`${item.processRouteCode || ''}`"
                       :value="item.id"/>
          <el-select
            v-model="bindForm.routeId"
            placeholder="请选择工艺路线"
            style="width: 100%"
            :loading="bindRouteLoading"
          >
            <el-option
              v-for="item in routeOptions"
              :key="item.id"
              :label="`${item.processRouteCode || ''}`"
              :value="item.id"
            />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     :loading="bindRouteSaving"
                     @click="handleBindRouteConfirm">确 认</el-button>
          <el-button
            type="primary"
            :loading="bindRouteSaving"
            @click="handleBindRouteConfirm"
            >确 认</el-button
          >
          <el-button @click="bindRouteDialogVisible = false">取 消</el-button>
        </span>
      </template>
    </el-dialog>
    <new-product-order v-if="isShowNewModal"
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery"/>
    <new-product-order
      v-if="isShowNewModal"
      v-model:visible="isShowNewModal"
      @completed="handleQuery"
    />
    <!-- 清场记录弹框 -->
    <clearance-record-dialog
@@ -113,26 +117,37 @@
</template>
<script setup>
import {onMounted, ref} from "vue";
import {ElMessageBox} from "element-plus";
import { onMounted, ref } from "vue";
import { ElMessageBox } from "element-plus";
import dayjs from "dayjs";
import {useRouter} from "vue-router";
import { useRouter } from "vue-router";
import {
  productOrderListPage,
  listProcessRoute,
  bindingRoute,
  delProductOrder, finishOrder,
  delProductOrder,
  finishOrder,
  saveCleanRecord,
} from "@/api/productionManagement/productionOrder.js";
import {listMain as getOrderProcessRouteMain} from "@/api/productionManagement/productProcessRoute.js";
import {fileDel} from "@/api/financialManagement/revenueManagement.js";
import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
import { fileDel } from "@/api/financialManagement/revenueManagement.js";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
const ClearanceRecordDialog = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/ClearanceRecordDialog.vue"));
const MaterialRequisitionDialog = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/MaterialRequisitionDialog.vue"));
const NewProductOrder = defineAsyncComponent(() =>
  import("@/views/productionManagement/productionOrder/New.vue")
);
const ClearanceRecordDialog = defineAsyncComponent(() =>
  import(
    "@/views/productionManagement/productionOrder/ClearanceRecordDialog.vue"
  )
);
const MaterialRequisitionDialog = defineAsyncComponent(() =>
  import(
    "@/views/productionManagement/productionOrder/MaterialRequisitionDialog.vue"
  )
);
const {proxy} = getCurrentInstance();
const { proxy } = getCurrentInstance();
const router = useRouter();
const isShowNewModal = ref(false);
@@ -141,37 +156,37 @@
  {
    label: "生产订单号",
    prop: "npsNo",
    width: '120px',
    width: "120px",
  },
  {
    label: "产品名称",
    prop: "productCategory",
    width: '120px',
    width: "120px",
  },
  {
    label: "规格",
    prop: "specificationModel",
    width: '120px',
    width: "120px",
  },
  {
    label: "UID码",
    prop: "uidNo",
    width: '120px',
    width: "120px",
  },
  {
    label: "批号",
    prop: "batchNo",
    width: '120px',
    width: "120px",
  },
  {
    label: "工艺路线编号",
    prop: "processRouteCode",
    width: '200px',
    width: "200px",
  },
  {
    label: "预计生产数量",
    prop: "quantity",
    width: '140px',
    width: "140px",
  },
  {
    label: "完成数量",
@@ -187,19 +202,19 @@
  {
    label: "开始日期",
    prop: "startTime",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    formatData: (val) => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "结束日期",
    prop: "endTime",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    formatData: (val) => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "交付日期",
    prop: "deliveryDate",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    formatData: (val) => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
@@ -222,46 +237,46 @@
      {
        name: "工艺路线",
        type: "text",
        clickFun: row => {
        clickFun: (row) => {
          showRouteItemModal(row);
        },
      },
      {
        name: "绑定工艺路线",
        type: "text",
        showHide: row => !row.processRouteCode,
        clickFun: row => {
        showHide: (row) => !row.processRouteCode,
        clickFun: (row) => {
          openBindRouteDialog(row);
        },
      },
      {
        name: "产品结构",
        type: "text",
        clickFun: row => {
        clickFun: (row) => {
          showProductStructure(row);
        },
      },
      {
        name: "结束生产",
        type: "text",
        showHide: row => !row.isEnd,
        clickFun: row => {
        showHide: (row) => !row.isEnd,
        clickFun: (row) => {
          handleFinishOrder(row);
        },
      },
      {
        name: "清场记录",
        type: "text",
        showHide: row => !row.isEnd,
        clickFun: row => {
        showHide: (row) => !row.isEnd,
        clickFun: (row) => {
          handleClearanceRecord(row);
        },
      },
      {
        name: "领料",
        type: "text",
        showHide: row => !row.isEnd,
        clickFun: row => {
        showHide: (row) => !row.isEnd,
        clickFun: (row) => {
          handleMaterialRequisition(row);
        },
      },
@@ -286,9 +301,9 @@
    specificationModel: "",
  },
});
const {searchForm} = toRefs(data);
const { searchForm } = toRefs(data);
const toProgressPercentage = val => {
const toProgressPercentage = (val) => {
  const n = Number(val);
  if (!Number.isFinite(n)) return 0;
  if (n <= 0) return 0;
@@ -297,7 +312,7 @@
};
// 30/50/80/100 分段颜色:红/橙/蓝/绿
const progressColor = percentage => {
const progressColor = (percentage) => {
  const p = toProgressPercentage(percentage);
  if (p < 30) return "#f56c6c";
  if (p < 50) return "#e6a23c";
@@ -306,19 +321,19 @@
};
// 添加表行类名方法
const tableRowClassName = ({row}) => {
  if (!row.deliveryDate) return '';
  if (row.isFh) return '';
const tableRowClassName = ({ row }) => {
  if (!row.deliveryDate) return "";
  if (row.isFh) return "";
  const diff = row.deliveryDaysDiff;
  if (diff === 15) {
    return 'yellow';
    return "yellow";
  } else if (diff === 10) {
    return 'pink';
    return "pink";
  } else if (diff === 2) {
    return 'purple';
    return "purple";
  } else if (diff < 2) {
    return 'red';
    return "red";
  }
};
@@ -339,7 +354,7 @@
  routeId: null,
});
const openBindRouteDialog = async row => {
const openBindRouteDialog = async (row) => {
  bindForm.orderId = row.id;
  bindForm.routeId = null;
  bindRouteDialogVisible.value = true;
@@ -351,7 +366,7 @@
  }
  bindRouteLoading.value = true;
  try {
    const res = await listProcessRoute({productModelId: row.productModelId});
    const res = await listProcessRoute({ productModelId: row.productModelId });
    routeOptions.value = res.data || [];
  } catch (e) {
    console.error("获取工艺路线列表失败:", e);
@@ -389,12 +404,12 @@
  page.current = 1;
  getList();
};
const pagination = obj => {
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const changeDaterange = value => {
const changeDaterange = (value) => {
  if (value) {
    searchForm.value.entryDateStart = value[0];
    searchForm.value.entryDateEnd = value[1];
@@ -407,20 +422,20 @@
const getList = () => {
  tableLoading.value = true;
  // 构造一个新的对象,不包含entryDate字段
  const params = {...searchForm.value, ...page};
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined;
  productOrderListPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.data.records;
      page.total = res.data.total;
    })
    .catch(() => {
      tableLoading.value = false;
    });
};
const showRouteItemModal = async row => {
const showRouteItemModal = async (row) => {
  const orderId = row.id;
  try {
    const res = await getOrderProcessRouteMain(orderId);
@@ -449,7 +464,7 @@
  }
};
const showProductStructure = row => {
const showProductStructure = (row) => {
  router.push({
    path: "/productionManagement/productStructureDetail",
    query: {
@@ -481,14 +496,16 @@
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    delProductOrder(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
  })
    .then(() => {
      delProductOrder(ids).then((res) => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
  }).catch(() => {
    proxy.$modal.msg("已取消");
  });
};
// 导出
@@ -498,16 +515,19 @@
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/productOrder/export", {...searchForm.value}, "生产订单.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
    .then(() => {
      proxy.download(
        "/productOrder/export",
        { ...searchForm.value },
        "生产订单.xlsx"
      );
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
const handleConfirmRoute = () => {
};
const handleConfirmRoute = () => {};
const handleFinishOrder = (row) => {
  ElMessageBox.confirm("是否确认结束?", "结束", {
@@ -515,15 +535,15 @@
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        finishOrder(row.id).then(res => {
          proxy.$modal.msgSuccess("结束成功");
          getList()
        })
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
    .then(() => {
      finishOrder(row.id).then((res) => {
        proxy.$modal.msgSuccess("结束成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// 打开清场记录弹框
@@ -547,7 +567,7 @@
    if (callback) callback();
    getList();
  } catch (error) {
    console.error('保存清场记录失败:', error);
    console.error("保存清场记录失败:", error);
    proxy.$modal.msgError("清场记录保存失败");
  }
};
@@ -569,11 +589,11 @@
}
::v-deep .yellow {
  background-color: #FAF0DE;
  background-color: #faf0de;
}
::v-deep .pink {
  background-color: #FAE1DE;
  background-color: #fae1de;
}
::v-deep .red {
@@ -581,6 +601,6 @@
}
::v-deep .purple {
  background-color: #F4DEFA;
  background-color: #f4defa;
}
</style>