张诺
10 小时以前 c0a0631e00ff77d54d1d310c0f5f1fcb16e0144a
src/views/productionManagement/productionOrder/index.vue
@@ -1,182 +1,540 @@
<template>
   <div class="app-container">
      <div class="search_form">
         <div>
            <span class="search_title">客户名称:</span>
            <el-input
               v-model="searchForm.customerName"
               style="width: 240px"
               placeholder="请输入"
               @change="handleQuery"
               clearable
               prefix-icon="Search"
            />
            <span class="search_title ml10">项目名称:</span>
            <el-input
               v-model="searchForm.customerName"
               style="width: 240px"
               placeholder="请输入"
               @change="handleQuery"
               clearable
               prefix-icon="Search"
            />
            <span class="search_title ml10">录入日期:</span>
            <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                                    placeholder="请选择" clearable @change="changeDaterange" />
            <el-button type="primary" @click="handleQuery" style="margin-left: 10px"
            >搜索</el-button
            >
         </div>
         <div>
            <el-button @click="handleOut">导出</el-button>
         </div>
      </div>
      <div class="table_list">
         <PIMTable
            rowKey="id"
            :column="tableColumn"
            :tableData="tableData"
            :page="page"
            :tableLoading="tableLoading"
            @pagination="pagination"
         ></PIMTable>
      </div>
   </div>
  <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-item label="产品名称:">
          <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-form-item>
        <el-form-item>
          <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="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">
        <template #completionStatus="{ row }">
          <el-progress
              :percentage="toProgressPercentage(row?.completionStatus)"
              :color="progressColor(toProgressPercentage(row?.completionStatus))"
              :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
          />
        </template>
      </PIMTable>
    </div>
    <BindRouteDialog
        ref="BindRouteDialogRef"
        v-model="bindRouteDialogVisible"
        :type="bindDialogType"
        :rowData="rowData"
        @confirm="handleBindRouteSubmit"
    />
    <new-product-order v-if="isShowNewModal"
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery"/>
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {
   listCustomer,
} from "@/api/basicData/customerFile.js";
import { ElMessageBox } from "element-plus";
import {defineAsyncComponent, getCurrentInstance, onMounted, reactive, ref, toRefs} from "vue";
import {ElMessageBox} from "element-plus";
import dayjs from "dayjs";
const { proxy } = getCurrentInstance();
import {useRouter} from "vue-router";
import {
  productOrderListPage,
  bindingRoute,
  delProductOrder,
  saveProductionProductInput,
  viewGetByProductWordId,
  exportProductInventory
} from "@/api/productionManagement/productionOrder.js";
import {listMain as getOrderProcessRouteMain} from "@/api/productionManagement/productProcessRoute.js";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
import BindRouteDialog from "./BindRouteDialog.vue";
const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
const {proxy} = getCurrentInstance();
const router = useRouter();
const isShowNewModal = ref(false);
const MOCK_MODE = true;
const loading = ref(false)
const dialogVisible = ref(false)
const bindDialogType = ref('add')
const BindRouteDialogRef = ref(null)
const handleBindRouteSubmit = async (data) => {
  const res = await saveProductionProductInput(data)
  if (res.code === 200) {
    proxy.$modal.msgSuccess("绑定成功");
    bindRouteDialogVisible.value = false
    handleQuery()
  } else {
    proxy.$modal.msgError(res.msg || "绑定失败")
  }
}
const tableColumn = ref([
   {
      label: "录入日期",
      prop: "customerName",
      width: 120,
   },
   {
      label: "合同号",
      prop: "taxpayerIdentificationNumber",
      width: 220,
   },
   {
      label: "客户合同号",
      prop: "addressPhone",
      width: 250,
   },
   {
      label: "客户名称",
      prop: "contactPerson",
   },
   {
      label: "项目名称",
      prop: "contactPhone",
      width:150
   },
   {
      label: "产品大类",
      prop: "basicBankAccount",
      width: 220,
   },
   {
      label: "规格型号",
      prop: "bankAccount",
      width: 220,
   },
   {
      label: "单位",
      prop: "bankCode",
      width:220
   },
   {
      label: "数量",
      prop: "maintainer",
   },
   {
      label: "排产数量",
      prop: "maintenanceTime",
      width: 100,
   },
   {
      label: "完工数量",
      prop: "maintenanceTime",
      width: 100,
   },
  {
    label: "生产订单号",
    prop: "npsNo",
    width: '120px',
  },
  {
    label: "订单编号",
    prop: "salesContractNo",
    width: '150px',
  },
  {
    label: "厂家",
    prop: "customerName",
    width: '200px',
  },
  {
    label: "产品名称",
    prop: "productCategory",
    width: '120px',
  },
  {
    label: "纸张规格",
    prop: "specificationModel",
    width: '120px',
  },
  {
    label: "工艺路线编号",
    prop: "processRouteCode",
    width: '200px',
  },
  {
    label: "印刷数量",
    prop: "quantity",
  },
  {
    label: "尺寸",
    prop: "cutSize",
    width: '120px',
  },
  {
    label: "小盒数量",
    prop: "smallBoxQty",
    width: '120px',
  },
  {
    label: "中盒数量",
    prop: "mediumBoxQty",
    width: '120px',
  },
  {
    label: "色数",
    prop: "printColorCount",
    width: '120px',
  },
  {
    label: "完成数量",
    prop: "completeQuantity",
  },
  {
    dataType: "slot",
    label: "完成进度",
    prop: "completionStatus",
    slot: "completionStatus",
    width: 180,
  },
  {
    label: "开始日期",
    prop: "startTime",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "结束日期",
    prop: "endTime",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "计划交货日期",
    prop: "deliveryDate",
    formatData: val => (val ? dayjs(val).format("YYYY-MM-DD") : ""),
    width: 120,
  },
  {
    label: "备注",
    prop: "remark",
    width: 120,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 300,
    operation: [
      {
        name: "绑定工艺路线",
        type: "text",
        showHide: row => !row.printId,
        clickFun: row => {
          openBindRouteDialog(row);
        },
      },
      {
        name: "查看工艺路线",
        type: "text",
        showHide: row => row.printId,
        clickFun: row => {
          openBindRouteDialog(row, "view");
        },
      },
      {
        name: "产品结构",
        type: "text",
        clickFun: row => {
          showProductStructure(row);
        },
      },
      {
        name: "导出定印单",
        type: "text",
        clickFun: row => {
          exportThePrintingJobOrder(row);
        },
        disabled: row => !row.printId,
      },
    ],
  },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const page = reactive({
   current: 1,
   size: 100,
   total: 0,
  current: 1,
  size: 100,
  total: 0,
});
const selectedRows = ref([]);
const data = reactive({
   searchForm: {
      customerName: "",
      entryDate: [
         dayjs().format("YYYY-MM-DD"),
         dayjs().add(1, "day").format("YYYY-MM-DD"),
      ], // 录入日期
      entryDateStart: dayjs().format("YYYY-MM-DD"),
      entryDateEnd: dayjs().add(1, "day").format("YYYY-MM-DD"),
   },
  searchForm: {
    customerName: "",
    salesContractNo: "",
    projectName: "",
    productCategory: "",
    specificationModel: "",
  },
});
const { searchForm } = toRefs(data);
const {searchForm} = toRefs(data);
const toProgressPercentage = val => {
  const n = Number(val);
  if (!Number.isFinite(n)) return 0;
  if (n <= 0) return 0;
  if (n >= 100) return 100;
  return Math.round(n);
};
// 30/50/80/100 分段颜色:红/橙/蓝/绿
const progressColor = percentage => {
  const p = toProgressPercentage(percentage);
  if (p < 30) return "#f56c6c";
  if (p < 50) return "#e6a23c";
  if (p < 80) return "#409eff";
  return "#67c23a";
};
// 添加表行类名方法
const tableRowClassName = ({row}) => {
  if (!row.deliveryDate) return '';
  if (row.isFh) return '';
  const diff = row.deliveryDaysDiff;
  if (diff === 15) {
    return 'yellow';
  } else if (diff === 10) {
    return 'pink';
  } else if (diff === 2) {
    return 'purple';
  } else if (diff < 2) {
    return 'red';
  }
};
// 绑定工艺路线弹框
const bindRouteDialogVisible = ref(false);
const bindRouteLoading = ref(false);
const bindRouteSaving = ref(false);
const routeOptions = ref([]);
const rowData = ref(null)
const bindForm = reactive({
  orderId: null,
  routeId: null,
});
const openBindRouteDialog = async (row, type) => {
  bindForm.orderId = row.id;
  bindForm.routeId = null;
  routeOptions.value = [];
  bindRouteLoading.value = true;
  try {
    BindRouteDialogRef.value?.resetForm?.()
    if (type === "view") {
      bindDialogType.value = "detail"
      const res = await viewGetByProductWordId(row.id)
      if (res?.cuttingFileVo?.id == null) {
        res.cuttingFileVo = []
      } else {
        res.cuttingFileVo = [res.cuttingFileVo]
      }
      rowData.value = res?.data || res
    } else {
      bindDialogType.value = "add"
      rowData.value = row
      rowData.value.finishedSize = row.specificationModel
    }
    bindRouteDialogVisible.value = true;
  } catch (e) {
    console.error("获取工艺路线列表失败:", e);
    proxy.$modal.msgError("获取工艺路线列表失败");
  } finally {
    bindRouteLoading.value = false;
  }
};
const handleBindRouteConfirm = async () => {
  if (!bindForm.routeId) {
    proxy.$modal.msgWarning("请选择工艺路线");
    return;
  }
  bindRouteSaving.value = true;
  try {
    await bindingRoute({
      id: bindForm.orderId,
      routeId: bindForm.routeId,
    });
    proxy.$modal.msgSuccess("绑定成功");
    bindRouteDialogVisible.value = false;
    getList();
  } catch (e) {
    console.error("绑定工艺路线失败:", e);
    proxy.$modal.msgError("绑定工艺路线失败");
  } finally {
    bindRouteSaving.value = false;
  }
};
// 查询列表
/** 搜索按钮操作 */
const handleQuery = () => {
   page.current = 1;
   getList();
  page.current = 1;
  getList();
};
const pagination = (obj) => {
   page.current = obj.page;
   page.size = obj.limit;
   getList();
const pagination = obj => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const changeDaterange = (value) => {
   if (value) {
      searchForm.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
      searchForm.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
   } else {
      searchForm.entryDateStart = undefined;
      searchForm.entryDateEnd = undefined;
   }
   handleQuery();
const changeDaterange = value => {
  if (value) {
    searchForm.value.entryDateStart = value[0];
    searchForm.value.entryDateEnd = value[1];
  } else {
    searchForm.value.entryDateStart = undefined;
    searchForm.value.entryDateEnd = undefined;
  }
  handleQuery();
};
const getList = () => {
   tableLoading.value = true;
   listCustomer({ ...searchForm.value, ...page }).then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
      page.total = res.total;
   });
  tableLoading.value = true;
  // 构造一个新的对象,不包含entryDate字段
  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;
      });
};
const showRouteItemModal = async row => {
  const orderId = row.id;
  try {
    const res = await getOrderProcessRouteMain(orderId);
    const data = res.data || {};
    if (!data || !data.id) {
      proxy.$modal.msgWarning("未找到关联的工艺路线");
      return;
    }
    router.push({
      path: "/productionManagement/processRouteItem",
      query: {
        id: data.id,
        processRouteCode: data.processRouteCode || "",
        productName: data.productName || "",
        model: data.model || "",
        bomNo: data.bomNo || "",
        description: data.description || "",
        orderId,
        type: "order",
      },
    });
  } catch (e) {
    console.error("获取工艺路线主信息失败:", e);
    proxy.$modal.msgError("获取工艺路线信息失败");
  }
};
const exportThePrintingJobOrder = async (row) => {
  const res = await exportProductInventory(row.id)
  const blob = new Blob([res])
  const url = window.URL.createObjectURL(blob)
  const link = document.createElement("a")
  link.href = url
  link.download = row.customerName + "-" + row.salesContractNo + "-" +  "工艺路线.xlsx"
  link.click()
  window.URL.revokeObjectURL(url)
}
const showProductStructure = row => {
  router.push({
    path: "/productionManagement/productStructureDetail",
    query: {
      id: row.id,
      bomNo: row.bomNo || "",
      productName: row.productCategory || "",
      productModelName: row.specificationModel || "",
      orderId: row.id,
      type: "order",
    },
  });
};
// 表格选择数据
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    delProductOrder(ids).then((res) => {
      proxy.$modal.msgSuccess("删除成功");
      getList();
    });
  }).catch(() => {
    proxy.$modal.msg("已取消");
  });
};
// 导出
const handleOut = () => {
   ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
   })
      .then(() => {
         proxy.download("/basic/customer/export", {}, "客户档案.xlsx");
      })
      .catch(() => {
         proxy.$modal.msg("已取消");
      });
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/productOrder/export", {...searchForm.value}, "生产订单.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
};
onMounted(() => {
   getList();
  getList();
});
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.search_form {
  align-items: flex-start; // ⭐ start → flex-start 更标准
}
:deep(.yellow) {
  background-color: #faf0de;
}
:deep(.pink) {
  background-color: #fae1de;
}
:deep(.red) {
  background-color: #ffe5e5;
}
:deep(.purple) {
  background-color: #f4defa;
}
/* 描述组件 */
:deep(.fixed-desc .el-descriptions__table) {
  table-layout: fixed;
  width: 100%;
}
:deep(.fixed-desc .el-descriptions__cell) {
  width: 25%;
  word-break: break-word;
}
</style>