spring
12 小时以前 b937241a2c20f62f45b31b232b6cebdec03d41d7
src/views/salesManagement/salesLedger/index.vue
@@ -37,7 +37,7 @@
        </div>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" show-summary style="width: 100%"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
        :summary-method="summarizeMainTable" @expand-change="expandChange" height="calc(100vh - 18.5em)">
        <el-table-column align="center" type="selection" width="55" fixed="left"/>
        <el-table-column type="expand" width="60" fixed="left">
@@ -46,6 +46,8 @@
              <el-table-column align="center" label="序号" type="index"/>
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="批号" prop="batchNo" />
              <el-table-column label="UID码" prop="uidNo" />
              <el-table-column label="单位" prop="unit" />
                     <el-table-column label="产品状态"
                                              width="100px"
@@ -117,11 +119,14 @@
        <el-table-column label="录入人" prop="entryPersonName" width="100" show-overflow-tooltip />
        <el-table-column label="录入日期" prop="entryDate" width="120" show-overflow-tooltip />
        <el-table-column label="签订日期" prop="executionDate" width="120" show-overflow-tooltip />
        <el-table-column label="交付日期" prop="deliveryDate" width="120" show-overflow-tooltip />
        <el-table-column label="备注" prop="remarks" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="100" align="center">
          <template #default="scope">
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">编辑</el-button>
            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">编辑</el-button>
<!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">详情</el-button>-->
            <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">附件</el-button>
            <el-button link type="primary" size="small" @click="exportSaleOutbound(scope.row)">打印销售出库单</el-button>
<!--            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">发货</el-button>-->
          </template>
        </el-table-column>
@@ -204,6 +209,14 @@
                  </el-form-item>
               </el-col>
            </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="交货日期:" prop="entryDate">
              <el-date-picker style="width: 100%" v-model="form.deliveryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
                              type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        </el-row>
            <el-row>
               <el-form-item label="产品信息:" prop="entryDate">
                  <el-button v-if="operationType !== 'view'" type="primary" @click="openProductForm('add')">添加</el-button>
@@ -212,10 +225,13 @@
            </el-row>
            <el-table :data="productData" border @selection-change="productSelected" show-summary
                           :summary-method="summarizeMainTable">
               <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'" />
               <el-table-column align="center" type="selection" width="55" v-if="operationType !== 'view'"
                  :selectable="(row) => !isProductShipped(row)" />
               <el-table-column align="center" label="序号" type="index" width="60" />
               <el-table-column label="产品大类" prop="productCategory" />
               <el-table-column label="规格型号" prop="specificationModel" />
               <el-table-column label="UID码" prop="uidNo" />
               <el-table-column label="批号" prop="batchNo" />
               <el-table-column label="单位" prop="unit" />
               <el-table-column label="数量" prop="quantity" />
               <el-table-column label="税率(%)" prop="taxRate" />
@@ -224,20 +240,22 @@
               <el-table-column label="不含税总价(元)" prop="taxExclusiveTotalPrice" :formatter="formattedNumber" />
               <el-table-column fixed="right" label="操作" min-width="60" align="center" v-if="operationType !== 'view'">
                  <template #default="scope">
                     <el-button link type="primary" size="small" @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                     <el-button link type="primary" size="small"
                        :disabled="isProductShipped(scope.row)"
                        @click="openProductForm('edit', scope.row,scope.$index)">编辑</el-button>
                  </template>
               </el-table-column>
            </el-table>
            <el-row :gutter="30">
               <el-col :span="24">
                  <el-form-item label="备注·:" prop="remark">
                     <el-input v-model="form.remark" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
                  <el-form-item label="备注:" prop="remarks">
                     <el-input v-model="form.remarks" placeholder="请输入" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
                  </el-form-item>
               </el-col>
            </el-row>
            <el-row :gutter="30">
               <el-col :span="24">
                  <el-form-item label="附件材料:" prop="remark">
                  <el-form-item label="附件材料:" prop="salesLedgerFiles">
                     <el-upload v-model:file-list="fileList" :action="upload.url" multiple ref="fileUpload" auto-upload
                                     :headers="upload.headers" :before-upload="handleBeforeUpload" :on-error="handleUploadError"
                                     :on-success="handleUploadSuccess" :on-remove="handleRemove">
@@ -306,6 +324,15 @@
            </el-table-column>
         </el-table>
         
         <pagination
            v-show="quotationPage.total > 0"
            :total="quotationPage.total"
            layout="total, sizes, prev, pager, next, jumper"
            :page="quotationPage.current"
            :limit="quotationPage.size"
            @pagination="quotationPaginationChange"
         />
         <template #footer>
            <el-button @click="quotationDialogVisible = false">关闭</el-button>
         </template>
@@ -339,6 +366,39 @@
                  </el-form-item>
               </el-col>
            </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="UID码:" prop="uidNo">
              <el-input v-model="productForm.uidNo" placeholder="请输入" disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="批号:" prop="batchNo">
              <el-select v-model="productForm.batchNo"
                          placeholder="请选择"
                          clearable
                          filterable
                          @change="handleBatchNoChange">
                <el-option v-for="item in batchNoOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="24">
            <el-form-item label="供应商:" prop="customer">
              <el-select v-model="productForm.customer"
                          placeholder="请选择"
                          clearable
                          filterable
                          :disabled="!supplierOptions.length">
                <el-option v-for="item in supplierOptions" :key="item.value" :label="item.label" :value="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
            <el-row :gutter="30">
               <el-col :span="12">
                  <el-form-item label="单位:" prop="unit">
@@ -644,20 +704,22 @@
import FormDialog from '@/components/Dialog/FormDialog.vue';
import { getQuotationList } from "@/api/salesManagement/salesQuotation.js";
import {
   ledgerListPage,
   productList,
   customerList,
   addOrUpdateSalesLedger,
   getSalesLedgerWithProducts,
   delLedger,
   addOrUpdateSalesLedgerProduct,
   delProduct,
   delLedgerFile, getProductInventory,
  ledgerListPage,
  productList,
  customerList,
  addOrUpdateSalesLedger,
  getSalesLedgerWithProducts,
  delLedger,
  addOrUpdateSalesLedgerProduct,
  delProduct,
  delLedgerFile, getProductInventory, saleOutboundExport,
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import { getStockInventoryAll } from "@/api/inventoryManagement/stockInventory.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
// 由 /stockInventory/getStockInventoryAll 驱动“批号/供应商”联动
import {safeTrainingExport} from "@/api/safeProduction/safetyTrainingAssessment.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -669,6 +731,7 @@
const customerOption = ref([]);
const productOptions = ref([]);
const modelOptions = ref([]);
const supplierOptions = ref([]);
const tableLoading = ref(false);
const page = reactive({
   current: 1,
@@ -694,6 +757,7 @@
      customerId: "",
      entryPerson: "",
      entryDate: "",
    deliveryDate: "",
      maintenanceTime: "",
      productData: [],
      executionDate: "",
@@ -703,6 +767,7 @@
      customerId: [{ required: true, message: "请选择", trigger: "change" }],
      entryPerson: [{ required: true, message: "请选择", trigger: "change" }],
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    deliveryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
   },
});
@@ -715,7 +780,9 @@
const productFormData = reactive({
   productForm: {
      productCategory: "",
      customer: "",
      specificationModel: "",
    uidNo: "",
      unit: "",
      quantity: "",
      taxInclusiveUnitPrice: "",
@@ -723,10 +790,13 @@
      taxInclusiveTotalPrice: "",
      taxExclusiveTotalPrice: "",
      invoiceType: "",
    batchNo: "",
   },
   productRules: {
      productCategory: [{ required: true, message: "请选择", trigger: "change" }],
      productModelId: [{ required: true, message: "请选择", trigger: "change" }],
    batchNo: [{ required: true, message: "请选择", trigger: "change" }],
      customer: [{ required: true, message: "请选择", trigger: "change" }],
      specificationModel: [
         { required: true, message: "请选择", trigger: "change" },
      ],
@@ -765,6 +835,12 @@
const quotationSearchForm = reactive({
   quotationNo: "",
   customer: "",
});
// 报价单弹框分页
const quotationPage = reactive({
   current: 1,
   size: 10,
   total: 0,
});
const selectedQuotation = ref(null);
@@ -889,62 +965,202 @@
         tableLoading.value = false;
      });
};
// 获取产品大类tree数据
const getProductOptions = () => {
let stockInventoryAllTree = [];
let batchNodeByBatchNo = new Map();
const normalizeStockInventoryTree = (nodes = []) => {
   const normalizeNodeValue = (node) => {
      // 后端有时会出现 id=null 的层级,这里给一个可用的 key
      if (node?.id !== null && node?.id !== undefined) return String(node.id);
      if (node?.nodeType === "batch") return String(node.batchNo ?? node.label ?? "");
      if (node?.nodeType === "customer") return String(node.customer ?? node.label ?? "");
      if (node?.nodeType === "model") return String(node.model ?? node.label ?? "");
      return String(node.productName ?? node.label ?? "");
   };
   const normalized = (list) =>
      (list || []).map((n) => {
         const value = normalizeNodeValue(n);
         const label = n.label ?? n.productName ?? n.model ?? n.batchNo ?? n.customer ?? "";
         return {
            ...n,
            value,
            label,
            children: normalized(n.children),
         };
      });
   return normalized(nodes);
};
// 仅展示最多 3 个层级:第 1 层(product) -> 第 2 层(model) -> 第 3 层(batch),更深的节点不展示
const filterStockInventoryFirst3Levels = (nodes = []) => {
   const MAX_LEVEL = 3;
   const cloneAndFilterByLevel = (list = [], level = 1) => {
      return (list || [])
         .map((n) => {
            // 后续层级里如果还有 customer,直接剔除
            if (n.nodeType === "customer") return null;
            // 到达展示深度后,不再向下挂子节点
            if (level >= MAX_LEVEL) {
               return { ...n, children: [] };
            }
            // 特例:batch 节点本身也不再展示 children(保持与接口节点语义一致)
            if (n.nodeType === "batch") {
               return { ...n, children: [] };
            }
            return { ...n, children: cloneAndFilterByLevel(n.children, level + 1) };
         })
         .filter(Boolean);
   };
   return cloneAndFilterByLevel(nodes, 1);
};
const findNodeObjByValue = (nodes = [], value) => {
   for (let i = 0; i < (nodes || []).length; i++) {
      const node = nodes[i];
      if (String(node?.value) === String(value)) return node;
      const children = node?.children || [];
      if (children.length) {
         const found = findNodeObjByValue(children, value);
         if (found) return found;
      }
   }
   return null;
};
// 获取库存树(用于产品大类/规格型号联动)
const getProductOptions = async () => {
   // 返回 Promise,便于在编辑产品时等待加载完成
   return productTreeList().then((res) => {
      productOptions.value = convertIdToValue(res);
      return productOptions.value;
   });
   const res = await getStockInventoryAll();
   const data = res?.data || [];
   stockInventoryAllTree = normalizeStockInventoryTree(data);
   productOptions.value = filterStockInventoryFirst3Levels(stockInventoryAllTree);
   return productOptions.value;
};
const formattedNumber = (row, column, cellValue) => {
   return parseFloat(cellValue).toFixed(2);
};
// 获取tree子数据
// 获取tree子数据(先选产品,再选规格型号)
const getModels = (value) => {
   productForm.value.productCategory = findNodeById(productOptions.value, value);
   modelList({ id: value }).then((res) => {
      modelOptions.value = res;
   });
   const node = findNodeObjByValue(stockInventoryAllTree, value);
   if (!node) return;
   if (node.nodeType !== "product") return;
   // 选择产品后,重置下游字段
   productForm.value.productCategory = node.label;
   modelOptions.value = (node.children || [])
      .filter((c) => c.nodeType === "model")
      .map((m) => ({
         id: m.value,
         model: m.model ?? m.label ?? "",
         unit: m.unit ?? "",
         uidNo: m.uidNo ?? m.identifierCode ?? "",
      }));
   productForm.value.productModelId = null;
   productForm.value.specificationModel = "";
   productForm.value.uidNo = "";
   productForm.value.unit = "";
   productForm.value.batchNo = "";
   productForm.value.customer = "";
   productForm.value.taxInclusiveUnitPrice = "";
   productForm.value.taxInclusiveTotalPrice = "";
   productForm.value.taxExclusiveTotalPrice = "";
   modelOptions.value = modelOptions.value || [];
   batchNoOptions.value = [];
   supplierOptions.value = [];
   batchNodeByBatchNo = new Map();
};
// 规格型号选择后:回显 UID,并生成“批号下拉”
const getProductModel = (value) => {
   const index = modelOptions.value.findIndex((item) => item.id === value);
   if (index !== -1) {
      productForm.value.specificationModel = modelOptions.value[index].model;
      productForm.value.unit = modelOptions.value[index].unit;
   const modelNode = findNodeObjByValue(stockInventoryAllTree, value);
   if (!modelNode || modelNode.nodeType !== "model") return;
   const prevBatchNo = productForm.value.batchNo;
   const prevCustomer = productForm.value.customer;
   productForm.value.specificationModel = modelNode.model ?? modelNode.label ?? "";
   // 有些接口/树数据里可能不包含 unit,这种情况下不要覆盖编辑时已回显的值
   const nextUnit = modelNode.unit ?? "";
   if (nextUnit !== null && nextUnit !== undefined && String(nextUnit).trim() !== "") {
      productForm.value.unit = nextUnit;
   }
   // 有些接口/树数据里可能不包含 uidNo,这种情况下不要覆盖编辑时已回显的值
   const nextUidNo = modelNode.uidNo ?? modelNode.identifierCode ?? "";
   if (nextUidNo !== null && nextUidNo !== undefined && String(nextUidNo).trim() !== "") {
      productForm.value.uidNo = nextUidNo;
   }
   const batchNodes = (modelNode.children || []).filter((b) => b.nodeType === "batch");
   batchNodeByBatchNo = new Map(
      batchNodes.map((b) => {
         const key = String(b.batchNo ?? b.value ?? b.label ?? "").trim();
         return [key, b];
      })
   );
   batchNoOptions.value = batchNodes.map((b) => ({
      label: String(b.batchNo ?? b.label ?? "").trim(),
      value: String(b.batchNo ?? b.value ?? b.label ?? "").trim(),
   }));
   // 批号不再属于新规格时,清空
   const batchValues = new Set(batchNoOptions.value.map((x) => x.value));
   if (!prevBatchNo || !batchValues.has(prevBatchNo)) {
      productForm.value.batchNo = "";
   }
   // 需要供应商:批号回显后再生成
   productForm.value.customer = "";
   supplierOptions.value = [];
   if (productForm.value.batchNo) {
      handleBatchNoChange(productForm.value.batchNo, prevCustomer);
   }
};
const handleBatchNoChange = (batchNo, prevCustomer) => {
   const safeBatchNo = String(batchNo ?? "").trim();
   if (!safeBatchNo || !batchNodeByBatchNo.size) {
      productForm.value.customer = "";
      supplierOptions.value = [];
      return;
   }
   const batchNode = batchNodeByBatchNo.get(String(safeBatchNo));
   if (!batchNode) {
      productForm.value.customer = "";
      supplierOptions.value = [];
      return;
   }
   // UID码可能来源于 batch 节点(不同接口字段名不一致时尽量兜底)
   const nextUidNo = batchNode.uidNo ?? batchNode.identifierCode ?? batchNode.uid ?? "";
   if (nextUidNo !== null && nextUidNo !== undefined && String(nextUidNo).trim() !== "") {
      productForm.value.uidNo = nextUidNo;
   }
   const customers = (batchNode.children || [])
      .filter((c) => c.nodeType === "customer")
      .map((c) => c.customer ?? c.label ?? "")
      .filter(Boolean);
   const uniq = Array.from(new Set(customers));
   supplierOptions.value = uniq.map((s) => ({ label: s, value: s }));
   // 编辑场景尽量回显;新增场景不回显
   if (prevCustomer && uniq.includes(prevCustomer)) {
      productForm.value.customer = prevCustomer;
   } else {
      productForm.value.specificationModel = null;
      productForm.value.unit = null;
      productForm.value.customer = "";
   }
};
const findNodeById = (nodes, productId) => {
   for (let i = 0; i < nodes.length; i++) {
      if (nodes[i].value === productId) {
         return nodes[i].label; // 找到节点,返回该节点
      }
      if (nodes[i].children && nodes[i].children.length > 0) {
         const foundNode = findNodeById(nodes[i].children, productId);
         if (foundNode) {
            return foundNode; // 在子节点中找到,返回该节点
         }
      }
   }
   return null; // 没有找到节点,返回null
};
function convertIdToValue(data) {
   return data.map((item) => {
      const { id, children, ...rest } = item;
      const newItem = {
         ...rest,
         value: id, // 将 id 改为 value
      };
      if (children && children.length > 0) {
         newItem.children = convertIdToValue(children);
      }
      return newItem;
   });
}
// 根据名称反查产品大类 id,便于仅存名称时的反显
function findNodeIdByLabel(nodes, label) {
   if (!label) return null;
@@ -986,6 +1202,23 @@
   } else {
      expandedRowKeys.value = [];
   }
};
// 添加表行类名方法
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 summarizeMainTable = (param) => {
@@ -1043,6 +1276,8 @@
const openQuotationDialog = async () => {
   if (operationType.value === "view") return;
   quotationDialogVisible.value = true;
   // 打开弹窗时重置分页到第一页
   quotationPage.current = 1;
   // 先确保客户列表已加载,便于后续回填 customerId
   if (!customerOption.value || customerOption.value.length === 0) {
      try {
@@ -1059,14 +1294,15 @@
   quotationLoading.value = true;
   try {
      const params = {
         // 兼容后端分页字段:这里沿用报价页面已有可用的字段命名
         currentPage: 1,
         pageSize: 100,
         // 后端分页字段:current / size
         current: quotationPage.current,
         size: quotationPage.size,
         ...quotationSearchForm,
         status: "通过",
      };
      const res = await getQuotationList(params);
      quotationList.value = res?.data?.records || [];
      quotationPage.total = res?.data?.total || 0;
   } finally {
      quotationLoading.value = false;
   }
@@ -1075,7 +1311,15 @@
const resetQuotationSearch = async () => {
   quotationSearchForm.quotationNo = "";
   quotationSearchForm.customer = "";
   quotationPage.current = 1;
   await fetchQuotationList();
};
// 报价单弹框分页切换
const quotationPaginationChange = (obj) => {
   quotationPage.current = obj.page;
   quotationPage.size = obj.limit;
   fetchQuotationList();
};
// 选中报价单后回填到台账表单
@@ -1111,6 +1355,7 @@
         // 台账字段
         productCategory: p.product || p.productName || "",
         specificationModel: p.specification || "",
      uidNo: p.uidNo || "",
         unit: p.unit || "",
         quantity: quantity,
         taxRate: taxRate,
@@ -1187,6 +1432,8 @@
      }
   });
};
const batchNoOptions = ref([]);
// 关闭弹框
const closeDia = () => {
   proxy.resetForm("formRef");
@@ -1196,6 +1443,12 @@
const productIndex = ref(0);
// 打开产品弹框
const openProductForm = async (type, row, index) => {
   // 编辑时检查产品是否已发货或审核通过
   if (type === "edit" && isProductShipped(row)) {
      proxy.$modal.msgWarning("已发货或审核通过的产品不能编辑");
      return;
   }
   productOperationType.value = type;
   productForm.value = {};
   proxy.resetForm("productFormRef");
@@ -1204,25 +1457,44 @@
      productIndex.value = index;
      // 编辑时根据产品大类名称反查 tree 节点 id,并加载规格型号列表
      try {
         const options = productOptions.value && productOptions.value.length > 0
            ? productOptions.value
            : await getProductOptions();
         const categoryId = findNodeIdByLabel(options, productForm.value.productCategory);
         if (categoryId) {
            const models = await modelList({ id: categoryId });
            modelOptions.value = models || [];
            // 根据当前规格型号名称反查并设置 productModelId,便于下拉框显示已选值
            const currentModel = (modelOptions.value || []).find(
               (m) => m.model === productForm.value.specificationModel
            );
         if (!productOptions.value || productOptions.value.length === 0) {
            await getProductOptions();
         }
         // 回显:根据“产品大类”反查产品节点
         const categoryKey = findNodeIdByLabel(productOptions.value, productForm.value.productCategory);
         if (categoryKey) {
            const categoryNode = findNodeObjByValue(stockInventoryAllTree, categoryKey);
            const models = (categoryNode?.children || [])
               .filter((n) => n.nodeType === "model")
               .map((m) => ({
                  id: m.value,
                  model: m.model ?? m.label ?? "",
                  unit: m.unit ?? "",
                  uidNo: m.uidNo ?? m.identifierCode ?? "",
               }));
            modelOptions.value = models;
            // 根据当前规格型号回显
            const targetSpec = String(productForm.value.specificationModel ?? "").trim();
            const currentModel =
               (models || []).find((m) => String(m.model ?? "").trim() === targetSpec) ||
               (models || []).find((m) => String(m.model ?? "").trim().includes(targetSpec)) ||
               (models || []).find((m) => targetSpec.includes(String(m.model ?? "").trim()));
            if (currentModel) {
               productForm.value.customer = productForm.value.customer || row.customer || row.supplierName || "";
               productForm.value.productModelId = currentModel.id;
               getProductModel(currentModel.id);
            }
         }
      } catch (e) {
         // 加载失败时保持可编辑,不中断弹窗
         console.error("加载产品规格型号失败", e);
      }
      // 最终兜底:如果中途被重置清空,至少回显行数据里的 UID
      productForm.value.uidNo = row.uidNo ?? productForm.value.uidNo ?? "";
      // 最终兜底:同样保证单位不会因树数据缺失而被覆盖为空
      productForm.value.unit = row.unit ?? productForm.value.unit ?? "";
   } else {
      getProductOptions()
   }
@@ -1262,6 +1534,14 @@
      proxy.$modal.msgWarning("请选择数据");
      return;
   }
   // 检查是否有已发货或审核通过的产品
   const shippedProducts = productSelectedRows.value.filter(row => isProductShipped(row));
   if (shippedProducts.length > 0) {
      proxy.$modal.msgWarning("已发货或审核通过的产品不能删除");
      return;
   }
   if (operationType.value === "add") {
      productSelectedRows.value.forEach((selectedRow) => {
         const index = productData.value.findIndex(
@@ -1336,15 +1616,55 @@
         proxy.$modal.msg("已取消");
      });
};
/** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
const isProductShipped = (product) => {
   if (!product) return false;
   const status = String(product.shippingStatus || "").trim();
   // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除
   return status === "已发货" || status === "审核通过";
};
/** 判断销售订单下是否存在已发货/发货完成的产品(不可删除) */
const hasShippedProducts = (products) => {
   if (!products || !products.length) return false;
   return products.some((p) => {
      const status = String(p.shippingStatus || "").trim();
      // 有发货日期或车牌号视为已发货
      if (p.shippingDate || p.shippingCarNumber) return true;
      // 已进行发货、发货完成、已发货 均不可删除
      return status === "已进行发货" || status === "发货完成" || status === "已发货";
   });
};
// 删除
const handleDelete = () => {
   let ids = [];
   if (selectedRows.value.length > 0) {
      ids = selectedRows.value.map((item) => item.id);
   } else {
const handleDelete = async () => {
   if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
   }
   const ids = selectedRows.value.map((item) => item.id);
   // 检查是否有已进行发货或发货完成的销售订单,若有则不允许删除
   const cannotDeleteNames = [];
   for (const row of selectedRows.value) {
      let products = row.children && row.children.length > 0 ? row.children : null;
      if (!products) {
         try {
            const res = await productList({ salesLedgerId: row.id, type: 1 });
            products = res.data || [];
         } catch {
            products = [];
         }
      }
      if (hasShippedProducts(products)) {
         cannotDeleteNames.push(row.salesContractNo || `ID:${row.id}`);
      }
   }
   if (cannotDeleteNames.length > 0) {
      proxy.$modal.msgWarning("已进行发货或发货完成的销售订单不能删除:" + cannotDeleteNames.join("、"));
      return;
   }
   ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
@@ -2070,6 +2390,38 @@
   let res = await userStore.getInfo();
   currentFactoryName.value = res.user.currentFactoryName;
};
const exportSaleOutbound = row => {
  saleOutboundExport({id: row.id})
      .then(res => {
        // 创建Blob对象
        const blob = new Blob([res], {
          type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        });
        // 创建下载链接
        const url = window.URL.createObjectURL(blob);
        const link = document.createElement("a");
        link.href = url;
        link.download = `销售出库单.docx`;
        // 模拟点击下载
        document.body.appendChild(link);
        link.click();
        // 清理临时对象
        setTimeout(() => {
          document.body.removeChild(link);
          window.URL.revokeObjectURL(url);
        }, 100);
        ElMessage.success("导出成功");
      })
      .catch(err => {
        console.error("导出失败:", err);
        ElMessage.error("导出失败,请重试");
      });
};
onMounted(() => {
   getList();
   userListNoPage().then(res => {
@@ -2084,6 +2436,22 @@
   margin-left: 10px;
}
::v-deep .yellow {
  background-color: #FAF0DE;
}
::v-deep .pink {
  background-color: #FAE1DE;
}
::v-deep .red {
  background-color: #FAE1DE;
}
::v-deep .purple{
  background-color: #F4DEFA;
}
.table_list {
   margin-top: unset;
}