yyb
2026-04-03 16c55c8b1939e2c5ac7f6cdaa34b273657e743e4
src/views/salesManagement/salesLedger/index.vue
@@ -18,6 +18,14 @@
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
            placeholder="请选择" clearable @change="changeDaterange" />
        </el-form-item>
        <el-form-item label="发货状态:">
          <el-select v-model="searchForm.deliveryStatus" placeholder="请选择" clearable style="width: 140px">
            <el-option label="未发货" :value="1" />
            <el-option label="审批中" :value="2" />
            <el-option label="审批失败" :value="3" />
            <el-option label="已发货" :value="4" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"> 搜索 </el-button>
        </el-form-item>
@@ -35,30 +43,30 @@
          :bound-route-name="processFlowSelectBoundRouteName"
          @confirm="handleProcessFlowSelectConfirm"
        />
        <div>
          <el-button type="primary" @click="openForm('add')">
            新增台账
          </el-button>
        <el-button type="primary"  @click="handleBulkDelivery">
            发货
          </el-button>
          <el-button type="primary" plain @click="handleImport">导入</el-button>
          <el-button @click="handleOut">导出</el-button>
          <el-button type="danger" plain @click="handleDelete">删除</el-button>
          <el-dropdown @command="handlePrintCommand">
            <el-button type="primary" plain>
              打印单据<el-icon class="el-icon--right"><ArrowDown /></el-icon>
            </el-button>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item command="finishedProcessCard">生产流程卡(成品)</el-dropdown-item>
                <el-dropdown-item command="salesOrder">销售订单</el-dropdown-item>
                <el-dropdown-item command="salesDeliveryNote">销售发货单</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
          <el-button type="primary" plain @click="handlePrintLabel">打印标签</el-button>
        </div>
         <el-space wrap>
               <el-button type="primary" @click="openForm('add')">新增台账</el-button>
               <el-button type="primary" @click="handleBulkDelivery">发货</el-button>
               <el-button type="primary" plain @click="handleImport">导入</el-button>
               <el-button @click="handleOut">导出</el-button>
               <el-button type="danger" plain @click="handleDelete">删除</el-button>
               <el-dropdown @command="handlePrintCommand">
                  <el-button type="primary" plain>
                     打印单据<el-icon class="el-icon--right">
                        <ArrowDown />
                     </el-icon>
                  </el-button>
                  <template #dropdown>
                     <el-dropdown-menu>
                        <el-dropdown-item command="finishedProcessCard">生产流程卡(成品)</el-dropdown-item>
                        <el-dropdown-item command="salesOrder">销售订单</el-dropdown-item>
                        <el-dropdown-item command="salesDeliveryNote">销售发货单</el-dropdown-item>
                     </el-dropdown-menu>
                  </template>
               </el-dropdown>
               <el-button type="primary" plain @click="handlePrintLabel">打印标签</el-button>
            </el-space>
      </div>
      <el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
        :expand-row-keys="expandedRowKeys" :row-key="(row) => row.id" :row-class-name="tableRowClassName" show-summary style="width: 100%"
@@ -68,6 +76,7 @@
          <template #default="props">
            <el-table :data="props.row.children" border show-summary :summary-method="summarizeChildrenTable">
              <el-table-column align="center" label="序号" type="index"/>
         <el-table-column label="楼层编号" prop="floorCode" min-width="100" show-overflow-tooltip />
              <el-table-column label="产品大类" prop="productCategory" />
              <el-table-column label="规格型号" prop="specificationModel" />
              <el-table-column label="厚度" prop="thickness" min-width="90">
@@ -75,6 +84,34 @@
                  {{ scope.row.thickness ?? "" }}
                </template>
              </el-table-column>
                     <el-table-column label="宽(mm)" prop="width" min-width="80">
                        <template #default="scope">
                           {{ scope.row.width ?? "" }}
                        </template>
                     </el-table-column>
                     <el-table-column label="高(mm)" prop="height" min-width="80">
                        <template #default="scope">
                           {{ scope.row.height ?? "" }}
                        </template>
                     </el-table-column>
                     <el-table-column label="周长(cm)" prop="perimeter" min-width="90">
                        <template #default="scope">
                           {{ scope.row.perimeter ?? "" }}
                        </template>
                     </el-table-column>
                     <el-table-column label="总面积(cm²)" prop="actualTotalArea" min-width="100">
                        <template #default="scope">
                           {{ scope.row.actualTotalArea ?? "" }}
                        </template>
                     </el-table-column>
                     <el-table-column label="加工要求" prop="processRequirement" min-width="120"
                        show-overflow-tooltip />
                     <el-table-column label="备注" prop="remark" min-width="120" show-overflow-tooltip />
                     <el-table-column label="重箱" prop="heavyBox" min-width="80">
                        <template #default="scope">
                           {{ scope.row.heavyBox ?? "" }}
                        </template>
                     </el-table-column>
                     <el-table-column label="产品状态"
                                              width="100px"
                                              align="center">
@@ -87,13 +124,13 @@
                           <el-tag v-else type="danger">不足</el-tag>
                </template>
              </el-table-column>
                     <el-table-column label="发货状态" width="140" align="center">
                     <!-- <el-table-column label="发货状态" width="140" align="center">
                        <template #default="scope">
                           <el-tag :type="getShippingStatusType(scope.row)" size="small">
                              {{ getShippingStatusText(scope.row) }}
                           </el-tag>
                        </template>
                     </el-table-column>
                     </el-table-column> -->
                     <el-table-column label="快递公司" prop="expressCompany" show-overflow-tooltip />
                     <el-table-column label="快递单号" prop="expressNumber" show-overflow-tooltip />
              <el-table-column label="发货车牌" minWidth="100px" align="center">
@@ -143,6 +180,15 @@
        <el-table-column label="付款方式" prop="paymentMethod" show-overflow-tooltip />
        <el-table-column label="合同金额(元)" prop="contractAmount" width="220" show-overflow-tooltip
          :formatter="formattedNumber" />
        <el-table-column label="发货状态" width="140" align="center">
         <template #default="scope">
            <el-tag v-if="Number(scope.row.deliveryStatus) === 1" type="info">未发货</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 2" type="warning">审批中</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 3" type="danger">审批失败</el-tag>
            <el-tag v-else-if="Number(scope.row.deliveryStatus) === 4" type="success">已发货</el-tag>
            <el-tag v-else type="info">-</el-tag>
         </template>
        </el-table-column>
        <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 />
@@ -258,6 +304,21 @@
               <el-table-column label="厚度" prop="thickness" min-width="90">
                  <template #default="scope">
                     {{ scope.row.thickness ?? "" }}
                  </template>
               </el-table-column>
               <el-table-column label="宽(mm)" prop="width" min-width="80">
                  <template #default="scope">
                     {{ scope.row.width ?? "" }}
                  </template>
               </el-table-column>
               <el-table-column label="高(mm)" prop="height" min-width="80">
                  <template #default="scope">
                     {{ scope.row.height ?? "" }}
                  </template>
               </el-table-column>
               <el-table-column label="面积(m²)" prop="actualTotalArea" min-width="100">
                  <template #default="scope">
                     {{ scope.row.actualTotalArea ?? "" }}
                  </template>
               </el-table-column>
               <el-table-column label="数量" prop="quantity" />
@@ -580,7 +641,7 @@
                        style="width: 100%"
                        placeholder="请输入"
                        clearable
                        @change="recalcAreaTotals"
                        @change="() => { recalcAreaTotals(); calculateFromUnitPrice(true); }"
                     />
                  </el-form-item>
               </el-col>
@@ -677,11 +738,12 @@
                                 <el-input-number
                                    v-model="item.quantity"
                                    :min="0"
                                    :step="0.1"
                                    :precision="2"
                                    :step="1"
                                    :precision="0"
                                    style="width: 100%;"
                                    placeholder="请输入数量"
                                    :disabled="operationType === 'view'"
                                    @change="calculateFromUnitPrice(true)"
                                 />
                              </div>
                              <el-button
@@ -942,6 +1004,7 @@
      entryDate: null, // 录入日期
      entryDateStart: undefined,
      entryDateEnd: undefined,
      deliveryStatus: undefined, // 发货状态:1未发货 2审批中 3审批失败 4已发货
   },
   form: {
      salesContractNo: "",
@@ -1049,7 +1112,7 @@
    type: "货车", // 货车, 快递
  },
  deliveryRules: {
    type: [
    type: [
      { required: true, message: "请选择发货类型", trigger: "change" }
    ]
  },
@@ -1075,6 +1138,7 @@
      otherAmountSelectOptions.value = records.map((item) => ({
         id: item.id,
         processName: item.processName ?? "",
         unitPrice: item.unitPrice ?? 0,
      }));
   } finally {
      otherAmountSelectOptionsLoading.value = false;
@@ -1146,6 +1210,7 @@
      return {
         id: s.id,
         processName: opt?.processName ?? s.processName ?? "",
         unitPrice: opt?.unitPrice ?? s.unitPrice ?? 0,
         quantity: Number(s.quantity ?? 0) || 0,
      };
   });
@@ -1201,8 +1266,10 @@
   productForm.value.salesProductProcessList.push({
      id: opt.id,
      processName: opt.processName,
      unitPrice: opt.unitPrice ?? 0,
      quantity: 0,
   });
   calculateFromUnitPrice(true);
   // 选择完成后关闭弹窗,下一次可再次点击“新增”继续添加
   otherAmountAddDialogVisible.value = false;
@@ -1217,6 +1284,7 @@
   if (operationType.value === "view") return;
   if (!Array.isArray(productForm.value?.salesProductProcessList)) return;
   productForm.value.salesProductProcessList.splice(index, 1);
   calculateFromUnitPrice(true);
};
// 发货审批人节点(仿协同审批 infoFormDia.vue)
@@ -1649,8 +1717,9 @@
   productData.value = products.map((p) => {
      const quantity = Number(p.quantity ?? 0) || 0;
      const unitPrice = Number(p.unitPrice ?? 0) || 0;
      const settlePieceArea = Number(p.settlePieceArea ?? 0) || 1;
      const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
      const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
      const taxInclusiveTotalPrice = (unitPrice * settlePieceArea * quantity).toFixed(2);
      const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
      return {
         // 台账字段
@@ -1842,15 +1911,16 @@
         recalcAreaTotals();
         // 其他金额只提交 {id, processName, quantity}(后端字段:salesProductProcessList)
         productForm.value.salesProductProcessList = (Array.isArray(productForm.value.salesProductProcessList)
            ? productForm.value.salesProductProcessList
            : []
         )
            .map((it) => ({
               id: it?.id,
               processName: it?.processName ?? "",
               quantity: Number(it?.quantity ?? 0) || 0,
            }))
            .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
         ? productForm.value.salesProductProcessList
         : []
      )
         .map((it) => ({
            id: it?.id,
            processName: it?.processName ?? "",
            unitPrice: Number(it?.unitPrice ?? 0) || 0,
            quantity: Number(it?.quantity ?? 0) || 0,
         }))
         .filter((it) => it.id !== null && it.id !== undefined && it.id !== "");
         if (operationType.value === "edit") {
            submitProductEdit();
@@ -2153,12 +2223,16 @@
   if (!productForm.value.quantity) {
      return;
   }
   // 含税总价计算
   productForm.value.taxInclusiveTotalPrice =
      proxy.calculateTaxIncludeTotalPrice(
         productForm.value.taxInclusiveUnitPrice,
         productForm.value.quantity
      );
   const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
   // 含税总价计算 = 单价 * 结算面积 * 数量 + 其他金额总和
   const basePrice = proxy.calculateTaxIncludeTotalPrice(
      productForm.value.taxInclusiveUnitPrice * settlePieceArea,
      productForm.value.quantity
   );
   const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
      return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
   }, 0);
   productForm.value.taxInclusiveTotalPrice = (parseFloat(basePrice) + otherAmountTotal).toFixed(2);
   if (productForm.value.taxRate) {
      // 不含税总价计算
      productForm.value.taxExclusiveTotalPrice =
@@ -2219,15 +2293,12 @@
   const computed = Number(computedPieceArea.toFixed(5));
   productForm.value.actualPieceArea = computed;
   // settlePieceArea:若用户未填写/为0,则默认使用宽高计算值
   const settlePieceRaw = Number(productForm.value.settlePieceArea ?? 0) || 0;
   if (!settlePieceRaw) {
      productForm.value.settlePieceArea = computed;
   }
   productForm.value.settlePieceArea = computed;
   recalcPerimeterFromWidthHeight();
   recalcAreaTotals();
   // 面积更新后,重新计算含税总价 = 单价 * 结算面积 * 数量
   calculateFromUnitPrice(true);
};
// 根据含税总价计算含税单价和数量
@@ -2243,8 +2314,12 @@
   
   isCalculating.value = true;
   
   // 计算含税单价 = 含税总价 / 数量
   productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
   // 计算含税单价 = (含税总价 - 其他金额总和) / 数量
   const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
      return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
   }, 0);
   const basePrice = totalPrice - otherAmountTotal;
   productForm.value.taxInclusiveUnitPrice = (basePrice / quantity).toFixed(2);
   
   // 如果有税率,计算不含税总价
   if (productForm.value.taxRate) {
@@ -2281,8 +2356,12 @@
   const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
   productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
   
   // 计算含税单价 = 含税总价 / 数量
   productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
   // 计算含税单价 = (含税总价 - 其他金额总和) / 数量
   const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
      return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
   }, 0);
   const basePrice = inclusiveTotalPrice - otherAmountTotal;
   productForm.value.taxInclusiveUnitPrice = (basePrice / quantity).toFixed(2);
   
   isCalculating.value = false;
};
@@ -2294,19 +2373,24 @@
      return;
   }
   if (isCalculating.value) return;
   const quantity = parseFloat(productForm.value.quantity);
   const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
   const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
   if (!quantity || quantity <= 0 || !unitPrice) {
      return;
   }
   isCalculating.value = true;
   // 计算含税总价
   productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
   // 计算含税总价 = 单价 * 结算面积 * 数量 + 其他金额总和
   const basePrice = unitPrice * settlePieceArea * quantity;
   const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
      return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
   }, 0);
   productForm.value.taxInclusiveTotalPrice = (basePrice + otherAmountTotal).toFixed(2);
   // 如果有税率,计算不含税总价
   if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
@@ -2315,30 +2399,35 @@
            productForm.value.taxRate
         );
   }
   isCalculating.value = false;
};
// 根据含税单价变化计算总价
const calculateFromUnitPrice = () => {
const calculateFromUnitPrice = (silent = false) => {
   if (!productForm.value.taxRate) {
      proxy.$modal.msgWarning("请先选择税率");
      if (!silent) proxy.$modal.msgWarning("请先选择税率");
      return;
   }
   if (isCalculating.value) return;
   const quantity = parseFloat(productForm.value.quantity);
   const unitPrice = parseFloat(productForm.value.taxInclusiveUnitPrice);
   const settlePieceArea = parseFloat(productForm.value.settlePieceArea) || 1;
   if (!quantity || quantity <= 0 || !unitPrice) {
      return;
   }
   isCalculating.value = true;
   // 计算含税总价
   productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
   // 计算含税总价 = 单价 * 结算面积 * 数量 + 其他金额总和
   const basePrice = unitPrice * settlePieceArea * quantity;
   const otherAmountTotal = (productForm.value.salesProductProcessList || []).reduce((total, item) => {
      return total + (Number(item.unitPrice) || 0) * (Number(item.quantity) || 0);
   }, 0);
   productForm.value.taxInclusiveTotalPrice = (basePrice + otherAmountTotal).toFixed(2);
   // 如果有税率,计算不含税总价
   if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
@@ -2347,7 +2436,7 @@
            productForm.value.taxRate
         );
   }
   isCalculating.value = false;
};
@@ -2445,11 +2534,23 @@
 * @param row 行数据
 */
const canShip = (row) => {
   // 产品状态必须是充足(approveStatus === 1)
   if (row.approveStatus !== 1) {
      return false;
   }
   
   // 如果后端返回了台账级发货状态(deliveryStatus)
   // 1=已发货,则禁止再次发货
   const deliveryStatus = row.deliveryStatus;
   if (
      deliveryStatus !== null &&
      deliveryStatus !== undefined &&
      String(deliveryStatus).trim() !== ""
   ) {
      if (Number(deliveryStatus) === 1) return false;
   }
   // 获取发货状态
   const shippingStatus = row.shippingStatus;
   
@@ -2467,6 +2568,44 @@
   if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
   }
   // 只允许【未发货/审批失败】进入发货流程
   const canDeliveryLedgers = selectedRows.value.filter((r) => {
      const status = Number(r.deliveryStatus);
      return status === 1 || status === 3;
   });
   if (canDeliveryLedgers.length === 0) {
      proxy.$modal.msgWarning("仅未发货或审批失败的台账可以发货");
      return;
   }
   // 已发货台账:弹窗提醒,不能再次发货(4 视为已发货)
   const shippedLedgers = selectedRows.value.filter((r) => Number(r.deliveryStatus) === 4);
   if (shippedLedgers.length === selectedRows.value.length) {
      try {
         await ElMessageBox.alert("所选销售台账均已发货,不能再次发货。", "提示", {
            type: "warning",
            confirmButtonText: "知道了",
         });
      } catch {
         /* 关闭弹窗 */
      }
      return;
   }
   if (shippedLedgers.length > 0) {
      try {
         await ElMessageBox.alert(
            "选中的销售台账中包含已发货记录,已发货的不能再次发货,系统将仅为未发货台账处理。",
            "提示",
            {
               type: "warning",
               confirmButtonText: "知道了",
            }
         );
      } catch {
         return;
      }
   }
   const customerNames = selectedRows.value.map((r) => String(r.customerName || "").trim());
@@ -2496,6 +2635,8 @@
   try {
      const targets = [];
      for (const ledger of selectedRows.value) {
         // 台账已发货:不允许再次发货
         if (Number(ledger.deliveryStatus) === 1) continue;
         let products = [];
         try {
            const res = await productList({ salesLedgerId: ledger.id, type: 1 });
@@ -2503,7 +2644,6 @@
         } catch {
            products = [];
         }
         for (const product of products) {
            if (!canShip(product)) continue;
            targets.push({
@@ -2544,14 +2684,15 @@
   });
}
// 打开发货弹框
// 打开发货弹框(单条)
const openDeliveryForm = (row) => {
   // 检查是否可以发货
   if (!canShip(row)) {
      proxy.$modal.msgWarning("只有在产品状态是充足,发货状态是待发货或审核拒绝的时候才可以发货");
   // 只允许【未发货/审批失败】发货;已发货/审批中不允许
   const status = Number(row.deliveryStatus);
   if (status !== 1 && status !== 3) {
      proxy.$modal.msgWarning("只有发货状态为未发货或审批失败的记录才可以发货");
      return;
   }
   currentDeliveryRows.value = [row];
  deliveryForm.value = {
    type: "货车",