src/views/productionManagement/productionOrder/index.vue
@@ -1,182 +1,1033 @@
<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.productCategory"
                    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>
        <template #productWorkOrders="{ row }">
          <div class="work-order-circles">
            <div
              v-for="(workOrder, index) in row.productWorkOrders"
              :key="index"
              class="work-order-circle"
              :class="getWorkOrderColorClass(workOrder.color)"
              :title="workOrder.processName || workOrder.workOrderNo"
            >
              {{ workOrder.processName ? workOrder.processName.substring(0, 2) : index + 1 }}
            </div>
          </div>
        </template>
      </PIMTable>
    </div>
    <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.processRouteName"
                       :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 @click="bindRouteDialogVisible = false">取 消</el-button>
        </span>
      </template>
    </el-dialog>
    <new-product-order v-if="isShowNewModal"
                         v-model:visible="isShowNewModal"
                         type="qualified"
                         @completed="handleQuery" />
    <!-- 查看投入弹框 -->
    <el-dialog v-model="inputDialogVisible"
               title="投入与退料"
               width="1200px"
               :close-on-click-modal="false">
      <el-table
        :data="inputTableData"
        border
        size="small"
        @selection-change="handleInputSelectionChange"
        :header-cell-style="{ background: '#f5f7fa' }"
        show-summary
        :summary-method="summarizeInputTable"
      >
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column label="投入产品名称" prop="productName" min-width="120" />
        <el-table-column label="图纸编号" prop="model" min-width="100" />
        <el-table-column label="投入数量" prop="quantity" min-width="100" align="center" />
        <el-table-column label="已退料数量" prop="returnQuantity" min-width="100" align="center" />
        <el-table-column label="单位" prop="unit" min-width="80" align="center" />
        <el-table-column label="本次退料数量" min-width="180" align="center" prop="currentReturnQuantity">
          <template #default="{ row }">
            <el-input-number
              v-model="row.currentReturnQuantity"
              :min="0"
              :max="row.quantity - (row.returnQuantity || 0)"
              :precision="0"
              size="small"
              style="width: 160px"
              @change="(val) => handleInputReturnQuantityChange(val, row)"
            />
          </template>
        </el-table-column>
      </el-table>
      <div class="picking-footer-info">
        <span>已选 {{ inputSelectedRows.length }} 条</span>
        <span>{{ inputTableData.length }} 条记录</span>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="warning" @click="handleReturnSubmit">确认退料</el-button>
          <el-button @click="inputDialogVisible = false">关闭</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 领料弹框 -->
    <el-dialog v-model="pickingDialogVisible"
               title="工单领料"
               width="1200px"
               :close-on-click-modal="false">
      <el-table
        :data="pickingTableData"
        border
        size="small"
        @selection-change="handlePickingSelectionChange"
        :header-cell-style="{ background: '#f5f7fa' }"
        show-summary
        :summary-method="summarizePickingTable"
      >
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column label="产品名称" prop="productName" min-width="120" />
        <el-table-column label="图纸编号" prop="model" min-width="100" />
        <el-table-column label="单位用量" prop="unitQuantity" min-width="100" align="center" />
        <el-table-column label="单位" prop="unit" min-width="100" align="center" />
        <el-table-column label="需求数量" prop="demandedQuantity" min-width="100" align="center" />
        <el-table-column label="已领料数量" prop="completedQuantity" min-width="100" align="center" />
        <el-table-column label="未领料数量" prop="unpickedQuantity" min-width="100" align="center" />
        <el-table-column label="领料数量" min-width="180" align="center" prop="quantity">
          <template #default="{ row }">
            <el-input-number
              v-model="row.quantity"
              :min="0"
              :max="row.unpickedQuantity"
              :precision="0"
              size="small"
              style="width: 160px"
              @change="(val) => handlePickingQuantityChange(val, row)"
            />
          </template>
        </el-table-column>
      </el-table>
      <div class="picking-footer-info">
        <span>已选 {{ pickingSelectedRows.length }} 条</span>
        <span>{{ pickingTableData.length }} 条记录</span>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="success" @click="handlePickingNext">确认</el-button>
          <el-button @click="pickingDialogVisible = false">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 查看图纸弹窗 -->
    <el-dialog
      v-model="drawingDialogVisible"
      title="查看图纸"
      width="800px"
      :close-on-click-modal="false"
    >
      <div class="drawing-container">
        <div v-if="currentDrawingFiles.length === 0" class="empty-drawing">
          <el-empty description="暂无图纸" />
        </div>
        <div v-else class="drawing-list">
          <div v-for="(file, index) in currentDrawingFiles" :key="index" class="drawing-item">
            <el-image
              v-if="isImage(file.name)"
              :src="getFileUrl(file)"
              :preview-src-list="imagePreviewList"
              fit="cover"
              style="width: 150px; height: 150px; border-radius: 4px; cursor: pointer;"
            />
            <div v-else class="file-item" @click="downloadFile(file)">
              <el-icon :size="50"><Document /></el-icon>
              <span class="file-name">{{ file.name }}</span>
            </div>
          </div>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="drawingDialogVisible = false">关闭</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import {onMounted, ref} from "vue";
import {
   listCustomer,
} from "@/api/basicData/customerFile.js";
import { ElMessageBox } from "element-plus";
import dayjs from "dayjs";
const { proxy } = getCurrentInstance();
  import { onMounted, ref, computed } from "vue";
  import { ElMessageBox } from "element-plus";
  import { Setting, Document } from '@element-plus/icons-vue';
  import dayjs from "dayjs";
  import { useRouter } from "vue-router";
  import {
  productOrderListPage,
  bindingRoute,
  listProcessBom, delProductOrder, startOrPause,
} from "@/api/productionManagement/productionOrder.js";
import { listPage as getProcessRouteList } from "@/api/productionManagement/processRoute.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import { productionProductInputListPage, returnMaterial } from "@/api/productionManagement/productionProductInput.js";
  import { listPage as listProductStructureRecord, pick as pickMaterial } from "@/api/productionManagement/productStructureRecord.js";
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,
   },
]);
const tableData = ref([]);
const tableLoading = ref(false);
const page = reactive({
   current: 1,
   size: 100,
   total: 0,
});
  import {fileDel} from "@/api/financialManagement/revenueManagement.js";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
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"),
   },
});
const { searchForm } = toRefs(data);
  const { proxy } = getCurrentInstance();
// 查询列表
/** 搜索按钮操作 */
const handleQuery = () => {
   page.current = 1;
   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 getList = () => {
   tableLoading.value = true;
   listCustomer({ ...searchForm.value, ...page }).then((res) => {
      tableLoading.value = false;
      tableData.value = res.records;
      page.total = res.total;
   });
};
  const router = useRouter();
  const isShowNewModal = ref(false);
// 导出
const handleOut = () => {
   ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
   })
      .then(() => {
         proxy.download("/basic/customer/export", {}, "客户档案.xlsx");
      })
      .catch(() => {
         proxy.$modal.msg("已取消");
      });
};
  // 查看图纸相关
  const drawingDialogVisible = ref(false);
  const currentDrawingFiles = ref([]);
onMounted(() => {
   getList();
});
  // 判断是否为图片
  const isImage = (fileName) => {
    if (!fileName) return false;
    const ext = fileName.toLowerCase().split('.').pop();
    return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext);
  };
  // 获取文件URL
  const getFileUrl = (file) => {
    if (file.url) {
      if (file.url.startsWith('http')) {
        return file.url;
      }
      return import.meta.env.VITE_APP_BASE_API + file.url;
    }
    return '';
  };
  // 图片预览列表
  const imagePreviewList = computed(() => {
    return currentDrawingFiles.value
      .filter(file => isImage(file.name))
      .map(file => getFileUrl(file));
  });
  // 下载文件
  const downloadFile = (file) => {
    const url = getFileUrl(file);
    if (url) {
      const link = document.createElement('a');
      link.href = url;
      link.download = file.name;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  };
  // 显示查看图纸弹窗
  const showDrawingDialog = (row) => {
    currentDrawingFiles.value = row.salesLedgerFiles || [];
    drawingDialogVisible.value = true;
  };
  // 获取工单圆圈颜色类名
  // color: 1-灰色 2-黄色 3-绿色 4-红色
  const getWorkOrderColorClass = (color) => {
    const colorMap = {
      1: 'gray',
      2: 'yellow',
      3: 'green',
      4: 'red'
    };
    return colorMap[color] || 'gray';
  };
  const tableColumn = ref([
    {
      label: "生产订单号",
      prop: "npsNo",
      width: '120px',
    },
    {
      label: "产品名称",
      prop: "productCategory",
      width: '120px',
    },
    {
      label: "图纸编号",
      prop: "specificationModel",
      width: '160px',
    },
    {
      label: "工艺路线编号",
      prop: "processRouteCode",
      width: '200px',
    },
    {
      label: "需求数量",
      prop: "quantity",
    },
    {
      label: "完成数量",
      prop: "completeQuantity",
    },
    {
      label: "库存数量",
      prop: "inventoryQuantity",
    },
    {
      dataType: "slot",
      label: "工序",
      prop: "productWorkOrders",
      slot: "productWorkOrders",
      width: 200,
    },
    {
      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,
    },
    {
      dataType: "action",
      label: "操作",
      align: "center",
      fixed: "right",
      width: 420,
      operation: [
        {
          name: "开始",
          type: "success",
          showHide: row => row.status === '待生产',
          clickFun: row => {
            handleStartOrPause(row);
          },
        },
        {
          name: "暂停",
          type: "danger",
          showHide: row => row.status === '生产中',
          clickFun: row => {
            handleStartOrPause(row);
          },
        },
        {
          name: "领料",
          type: "success",
          clickFun: row => {
            showPickingDialog(row);
          },
        },
        {
          name: "工艺路线",
          type: "text",
          clickFun: row => {
            showRouteItemModal(row);
          },
        },
        // {
        //   name: "绑定工艺路线",
        //   type: "text",
        //   showHide: row => !row.processRouteCode,
        //   clickFun: row => {
        //     openBindRouteDialog(row);
        //   },
        // },
        {
          name: "物料清单",
          type: "text",
          clickFun: row => {
            showProductStructure(row);
          },
        },
        {
          name: "投入/退料",
          type: "text",
          clickFun: row => {
            showInputDialog(row);
          },
        },
        {
          name: "查看图纸",
          type: "text",
          showHide: row => row.salesLedgerFiles && row.salesLedgerFiles.length > 0,
          clickFun: row => {
            showDrawingDialog(row);
          },
        },
      ],
    },
  ]);
  const tableData = ref([]);
  const tableLoading = ref(false);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const selectedRows = ref([]);
  // 查看投入相关
  const inputDialogVisible = ref(false);
  const inputTableData = ref([]);
  const inputTableLoading = ref(false);
  const inputCurrentRow = ref(null);
  const inputSelectedRows = ref([]);
  const inputPage = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  // 领料相关
  const pickingDialogVisible = ref(false);
  const pickingTableData = ref([]);
  const pickingSelectedRows = ref([]);
  const pickingForm = reactive({
    orderId: null,
  });
  const data = reactive({
    searchForm: {
      projectName: "",
      productCategory: "",
    },
  });
  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 === undefined || diff === null || diff === '' || diff < 0) return '';
    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 bindForm = reactive({
    orderId: null,
    routeId: null,
  });
  const openBindRouteDialog = async row => {
    bindForm.orderId = row.id;
    bindForm.routeId = null;
    bindRouteDialogVisible.value = true;
    routeOptions.value = [];
    bindRouteLoading.value = true;
    try {
      const res = await getProcessRouteList({ current: -1, size: -1 });
      routeOptions.value = res.data?.records || [];
    } 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();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  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;
    // 构造一个新的对象,不包含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 dataList = res.data || [];
      if (!dataList || dataList.length === 0 || !dataList[0].id) {
        proxy.$modal.msgWarning("未找到关联的工艺路线");
        return;
      }
      const data = dataList[0];
      router.push({
        path: "/productionManagement/processRouteItem",
        query: {
          id: data.id,
          processRouteCode: data.processRouteCode || "",
          productName: data.productName || "",
          drawingNumber: data.drawingNumber || "",
          model: data.model || "",
          bomNo: data.bomNo || "",
          description: data.description || "",
          orderId,
          type: "order",
        },
      });
    } catch (e) {
      console.error("获取工艺路线主信息失败:", e);
      proxy.$modal.msgError("获取工艺路线信息失败");
    }
  };
  const handleStartOrPause = async (row) => {
    const operation = row.status === '待生产' ? 1 : 2;
    const operationText = operation === 1 ? "开始" : "暂停";
    try {
      await startOrPause({ id: row.id, operation });
      proxy.$modal.msgSuccess(`${operationText}成功`);
      getList();
    } catch (e) {
      console.error(`${operationText}失败:`, e);
      proxy.$modal.msgError(`${operationText}失败`);
    }
  };
  const showProductStructure = async row => {
    let bomNo = row.bomNo || "";
    if (!bomNo && row.id) {
      try {
        const res = await getOrderProcessRouteMain(row.id);
        const dataList = res.data || [];
        if (dataList && dataList.length > 0 && dataList[0].bomNo) {
          bomNo = dataList[0].bomNo;
        }
      } catch (e) {
        console.error("获取BOM编号失败:", e);
      }
    }
    router.push({
      path: "/productionManagement/productStructureDetail",
      query: {
        id: row.id,
        bomNo: bomNo,
        drawingNumber: row.drawingNumber || "",
        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("/productOrder/export", {...searchForm.value}, "生产订单.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
  };
  const handleConfirmRoute = () => {};
  // 显示查看投入弹框
  const showInputDialog = async (row) => {
    inputCurrentRow.value = row;
    inputDialogVisible.value = true;
    inputPage.current = 1;
    inputPage.total = 0;
    inputSelectedRows.value = [];
    fetchInputData();
  };
  // 查看投入分页
  const handleInputPagination = (obj) => {
    inputPage.current = obj.page;
    inputPage.size = obj.limit;
    fetchInputData();
  };
  // 获取投入数据
  const fetchInputData = () => {
    inputTableLoading.value = true;
    const params = { productOrderId: inputCurrentRow.value.id, ...inputPage };
    productionProductInputListPage(params)
      .then(res => {
        inputTableLoading.value = false;
        inputTableData.value = res.data.records.map(item => ({
          ...item,
          currentReturnQuantity: 0,
        }));
        inputPage.total = res.data.total;
      })
      .catch(err => {
        inputTableLoading.value = false;
        console.error("获取投入数据失败:", err);
      });
  };
  // 显示领料弹框
  const showPickingDialog = async (row) => {
    pickingForm.orderId = row.id;
    pickingDialogVisible.value = true;
    pickingTableData.value = [];
    // 获取物料清单数据
    try {
      const res = await listProductStructureRecord({ productOrderId: row.id });
      const materials = res.data?.records || [];
      pickingTableData.value = materials.map(item => ({
        ...item,
        quantity: 0,
        unpickedQuantity: (item.demandedQuantity || 0) - (item.completedQuantity || 0),
      }));
    } catch (e) {
      console.error("获取物料清单失败:", e);
      proxy.$modal.msgError("获取物料清单失败");
    }
  };
  // 领料表格选择变化
  const handlePickingSelectionChange = (selection) => {
    pickingSelectedRows.value = selection;
  };
  // 领料数量变化处理
  const handlePickingQuantityChange = (val, row) => {
    if (val > row.unpickedQuantity) {
      proxy.$modal.msgWarning("领料数量不能超过未领料数量");
      row.quantity = row.unpickedQuantity;
    }
  };
  // 确认领料
  const handlePickingNext = async () => {
    if (pickingSelectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择要领料的物料");
      return;
    }
    // 校验领料数量
    for (const row of pickingSelectedRows.value) {
      if (row.quantity > row.unpickedQuantity) {
        proxy.$modal.msgWarning(`${row.productName} 的领料数量不能超过未领料数量`);
        return;
      }
      if (row.quantity <= 0) {
        proxy.$modal.msgWarning(`${row.productName} 的领料数量必须大于0`);
        return;
      }
    }
    // 提交领料数据
    try {
      const pickData = pickingSelectedRows.value.map(row => ({
        productOrderId: row.productOrderId,
        productStructureRecordId: row.id,
        productModelId: row.productModelId,
        quantity: row.quantity,
      }));
      await pickMaterial(pickData);
      proxy.$modal.msgSuccess("领料成功");
      pickingDialogVisible.value = false;
    } catch (e) {
      console.error("领料失败:", e);
      proxy.$modal.msgError("领料失败");
    }
  };
  // 领料表格合计方法
  const summarizePickingTable = (param) => {
    return proxy.summarizeTable(param, [
      "quantity",
      "unpickedQuantity",
      "inventoryQuantity",
      "demandedQuantity",
    ]);
  };
  // 投入表格选择变化
  const handleInputSelectionChange = (selection) => {
    inputSelectedRows.value = selection;
  };
  // 投入退料数量变化处理
  const handleInputReturnQuantityChange = (val, row) => {
    const maxReturn = row.quantity - (row.returnQuantity || 0);
    if (val > maxReturn) {
      proxy.$modal.msgWarning("本次退料数量不能超过剩余可退数量");
      row.currentReturnQuantity = maxReturn;
    }
  };
  // 确认退料
  const handleReturnSubmit = async () => {
    if (inputSelectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择要退料的物料");
      return;
    }
    // 校验退料数量
    const invalidRows = inputSelectedRows.value.filter(row => !row.currentReturnQuantity || row.currentReturnQuantity <= 0);
    if (invalidRows.length > 0) {
      proxy.$modal.msgWarning("请填写本次退料数量");
      return;
    }
    const returnData = inputSelectedRows.value.map(row => ({
      ...row,
      returnQuantity: row.currentReturnQuantity,
    }));
    try {
      await returnMaterial(returnData);
      proxy.$modal.msgSuccess("退料成功");
      inputDialogVisible.value = false;
      getList();
    } catch (e) {
      console.error("退料失败:", e);
      proxy.$modal.msgError("退料失败");
    }
  };
  // 投入表格合计方法
  const summarizeInputTable = (param) => {
    return proxy.summarizeTable(param, [
      "quantity",
      "returnQuantity",
      "currentReturnQuantity",
    ]);
  };
  onMounted(() => {
    getList();
  });
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.search_form{
  align-items: start;
}
:deep(.yellow) {
  background-color: #FAF0DE;
}
:deep(.pink) {
  background-color: #FAE1DE;
}
:deep(.red) {
  background-color: #FFCCCC;
}
:deep(.purple){
  background-color: #F4DEFA;
}
.picking-footer-info {
  display: flex;
  justify-content: space-between;
  padding: 10px;
  font-size: 14px;
  color: #606266;
}
.drawing-container {
  min-height: 200px;
}
.empty-drawing {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 200px;
}
.drawing-list {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  padding: 10px;
}
.drawing-item {
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  overflow: hidden;
  transition: all 0.3s;
  &:hover {
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  }
}
.file-item {
  width: 150px;
  height: 150px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #f5f7fa;
  cursor: pointer;
  padding: 10px;
  .file-name {
    font-size: 12px;
    color: #606266;
    margin-top: 10px;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 130px;
  }
  &:hover {
    background: #e4e7ed;
  }
}
// 工单圆圈样式
.work-order-circles {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
.work-order-circle {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 500;
  color: #fff;
  cursor: pointer;
  transition: all 0.2s ease;
  // 灰色 - 待开始
  &.gray {
    background-color: #909399;
    &:hover {
      background-color: #a6a9ad;
    }
  }
  // 黄色 - 进行中
  &.yellow {
    background-color: #e6a23c;
    &:hover {
      background-color: #ebb563;
    }
  }
  // 绿色 - 已完成
  &.green {
    background-color: #67c23a;
    &:hover {
      background-color: #85ce61;
    }
  }
  // 红色 - 异常/暂停
  &.red {
    background-color: #f56c6c;
    &:hover {
      background-color: #f78989;
    }
  }
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
  }
}
</style>