张诺
14 小时以前 7e1c5eb99eda42ff9d54b7ecf1bbab9920dc17e6
src/views/salesManagement/salesLedger/index.vue
@@ -51,7 +51,9 @@
                                              width="100px"
                                              align="center">
                <template #default="scope">
                           <el-tag v-if="scope.row.approveStatus === 1"
               <el-tag v-if="getShippingStatusText(scope.row) === '已发货'"
                                       type="success">已出库</el-tag>
                           <el-tag v-else-if="scope.row.hasSufficientStock"
                                       type="success">充足</el-tag>
                           <el-tag v-else
                                       type="danger">不足</el-tag>
@@ -85,7 +87,7 @@
                  </div>
                </template>
              </el-table-column>
              <el-table-column label="数量" prop="quantity" />
              <el-table-column label="数量" prop="quantity" :formatter="formattedNumber" />
              <el-table-column label="税率(%)" prop="taxRate" />
              <el-table-column label="含税单价(元)" prop="taxInclusiveUnitPrice" :formatter="formattedNumber" />
              <el-table-column label="含税总价(元)" prop="taxInclusiveTotalPrice" :formatter="formattedNumber" />
@@ -118,10 +120,11 @@
        <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">
        <el-table-column label="其它说明事项" prop="remarks" width="200" show-overflow-tooltip />
        <el-table-column fixed="right" label="操作" min-width="200" 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="exportSalesContracts(scope.row)">导出销售合同</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="openDeliveryForm(scope.row)">发货</el-button>-->
@@ -150,7 +153,7 @@
          </el-col>
          <el-col :span="12">
            <el-form-item label="业务员:" prop="salesman">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'">
              <el-select v-model="form.salesman" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName"
                  :value="item.nickName" />
              </el-select>
@@ -160,10 +163,10 @@
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="客户名称:" prop="customerId">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'">
              <el-select v-model="form.customerId" placeholder="请选择" clearable :disabled="operationType === 'view'" filterable>
                <el-option v-for="item in customerOption" :key="item.id" :label="item.customerName" :value="item.id">
                  {{
                    item.customerName + "——" + item.taxpayerIdentificationNumber
               {{
                    item.customerName
                  }}
                </el-option>
              </el-select>
@@ -213,6 +216,11 @@
                              type="date" placeholder="请选择" clearable />
            </el-form-item>
          </el-col>
        <el-col :span="12">
            <el-form-item label="签订地点:" prop="placeOfSinging">
            <el-input v-model="form.placeOfSinging" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
            <el-row>
               <el-form-item label="产品信息:" prop="entryDate">
@@ -243,14 +251,14 @@
            </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">
@@ -309,7 +317,7 @@
            <el-table-column prop="status" label="审批状态" width="120" align="center" />
            <el-table-column prop="totalAmount" label="报价金额(元)" width="160" align="right">
               <template #default="scope">
                  {{ Number(scope.row.totalAmount ?? 0).toFixed(2) }}
                  {{ Number(scope.row.totalAmount ?? 0).toFixed(3) }}
               </template>
            </el-table-column>
            <el-table-column fixed="right" label="操作" width="120" align="center">
@@ -318,6 +326,15 @@
               </template>
            </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>
@@ -335,11 +352,8 @@
            <el-row :gutter="30">
               <el-col :span="24">
                  <el-form-item label="产品大类:" prop="productCategory">
                     <!-- <el-select v-model="productForm.productCategory" placeholder="请选择" clearable>
                        <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
                     </el-select> -->
                     <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable check-strictly
                                             @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
                     <el-tree-select v-model="productForm.productCategory" placeholder="请选择" clearable filterable check-strictly
                                     @change="getModels" :data="productOptions" :render-after-expand="false" style="width: 100%" />
                  </el-form-item>
               </el-col>
            </el-row>
@@ -372,14 +386,14 @@
               <el-col :span="12">
                  <el-form-item label="含税单价(元):" prop="taxInclusiveUnitPrice">
                     <el-input-number :step="0.01" :min="0" v-model="productForm.taxInclusiveUnitPrice" style="width: 100%"
                                              :precision="2"
                                              :precision="3"
                                              placeholder="请输入" clearable @change="calculateFromUnitPrice" />
                  </el-form-item>
               </el-col>
               <el-col :span="12">
                  <el-form-item label="数量:" prop="quantity">
                     <el-input-number  :step="0.1" :min="0" v-model="productForm.quantity" placeholder="请输入" clearable
                                                :precision="2"
                                                :precision="3"
                                                @change="calculateFromQuantity" style="width: 100%" />
                  </el-form-item>
               </el-col>
@@ -472,7 +486,7 @@
               <div v-for="(item, index) in printData" :key="index" class="print-page">
                  <div class="delivery-note">
                     <div class="header">
                        <div class="company-name">鼎诚瑞实业有限责任公司</div>
                        <div class="company-name">阳光印刷有限责任公司</div>
                        <div class="document-title">零售发货单</div>
                     </div>
                     
@@ -490,7 +504,7 @@
                        <div class="info-row">
                           <div>
                              <span class="label">客户名称:</span>
                              <span class="value">{{ item.customerName || '张爱有' }}</span>
                              <span class="value">{{ item.customerName }}</span>
                           </div>
                           <span class="label">单号:</span>
                           <span class="value">{{ item.salesContractNo }}</span>
@@ -666,11 +680,13 @@
   addOrUpdateSalesLedgerProduct,
   delProduct,
   delLedgerFile, getProductInventory,
   exportSalesContract
} from "@/api/salesManagement/salesLedger.js";
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useFormData from "@/hooks/useFormData.js";
import dayjs from "dayjs";
import { getCurrentDate } from "@/utils/index.js";
import {listCustomerPrivatePool} from "@/api/basicData/customerFile.js";
const userStore = useUserStore();
const { proxy } = getCurrentInstance();
@@ -719,6 +735,7 @@
      entryDate: [{ required: true, message: "请选择", trigger: "change" }],
    deliveryDate: [{ required: true, message: "请选择", trigger: "change" }],
      executionDate: [{ required: true, message: "请选择", trigger: "change" }],
      placeOfSinging: [{ required: true, message: "请输入", trigger: "blur" }],
   },
});
const { form, rules } = toRefs(data);
@@ -780,6 +797,12 @@
const quotationSearchForm = reactive({
   quotationNo: "",
   customer: "",
});
// 报价单弹框分页
const quotationPage = reactive({
   current: 1,
   size: 10,
   total: 0,
});
const selectedQuotation = ref(null);
@@ -913,7 +936,7 @@
   });
};
const formattedNumber = (row, column, cellValue) => {
   return parseFloat(cellValue).toFixed(2);
   return parseFloat(cellValue).toFixed(3);
};
// 获取tree子数据
const getModels = (value) => {
@@ -1005,6 +1028,7 @@
// 添加表行类名方法
const tableRowClassName = ({ row }) => {
  if (!row.deliveryDate) return '';
  if (row.isFh) return '';
  const diff = row.deliveryDaysDiff;
@@ -1024,7 +1048,11 @@
      "contractAmount",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
   ]);
   ], {
      contractAmount: { decimalPlaces: 3 },
      taxInclusiveTotalPrice: { decimalPlaces: 3 },
      taxExclusiveTotalPrice: { decimalPlaces: 3 },
   });
};
// 子表合计方法
const summarizeChildrenTable = (param) => {
@@ -1032,18 +1060,43 @@
      "taxInclusiveUnitPrice",
      "taxInclusiveTotalPrice",
      "taxExclusiveTotalPrice",
   ]);
   ], {
      taxInclusiveUnitPrice: { decimalPlaces: 3 },
      taxInclusiveTotalPrice: { decimalPlaces: 3 },
      taxExclusiveTotalPrice: { decimalPlaces: 3 },
   });
};
// 判断是否能编辑
const canEditLedger = (row) => {
   // 如果是维护人,则可以编辑
   return Number(row.entryPerson) === Number(userStore.id);
};
// 判断是否能删除
const canDeleteLedger = (row) => {
   // 如果是维护人,则可以删除
   return Number(row.entryPerson) === Number(userStore.id);
};
// 判断是否是维护人(用于权限校验)
const isMaintainer = (row) => {
   return row.entryPerson === userStore.id;
};
// 打开弹框
const openForm = async (type, row) => {
   if (type === "edit" && row && !canEditLedger(row)) {
      proxy.$modal.msgWarning("当前系统登录人不是维护人,不能编辑数据");
      return;
   }
   operationType.value = type;
   form.value = {};
   productData.value = [];
   selectedQuotation.value = null;
   let userLists = await userListNoPage();
   userList.value = userLists.data;
   customerList().then((res) => {
      customerOption.value = res;
   listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
      customerOption.value = res.data.records;
   });
   form.value.entryPerson = userStore.id;
   if (type === "add") {
@@ -1074,11 +1127,14 @@
const openQuotationDialog = async () => {
   if (operationType.value === "view") return;
   quotationDialogVisible.value = true;
   // 打开弹窗时重置分页到第一页
   quotationPage.current = 1;
   // 先确保客户列表已加载,便于后续回填 customerId
   if (!customerOption.value || customerOption.value.length === 0) {
      try {
         const res = await customerList();
         customerOption.value = res;
         listCustomerPrivatePool({current: -1,size:-1}).then((res) => {
            customerOption.value = res.data.records;
         });
      } catch (e) {
         // ignore,允许用户后续手动选择客户
      }
@@ -1090,14 +1146,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;
   }
@@ -1106,7 +1163,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();
};
// 选中报价单后回填到台账表单
@@ -1136,8 +1201,8 @@
      const quantity = Number(p.quantity ?? 0) || 0;
      const unitPrice = Number(p.unitPrice ?? 0) || 0;
      const taxRate = "13"; // 默认 13%,便于直接提交(如需可在产品中自行修改)
      const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
      const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate);
      const taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(3);
      const taxExclusiveTotalPrice = proxy.calculateTaxExclusiveTotalPrice(taxInclusiveTotalPrice, taxRate, 3);
      return {
         // 台账字段
         productCategory: p.product || p.productName || "",
@@ -1145,7 +1210,7 @@
         unit: p.unit || "",
         quantity: quantity,
         taxRate: taxRate,
         taxInclusiveUnitPrice: unitPrice.toFixed(2),
         taxInclusiveUnitPrice: unitPrice.toFixed(3),
         taxInclusiveTotalPrice: taxInclusiveTotalPrice,
         taxExclusiveTotalPrice: taxExclusiveTotalPrice,
         invoiceType: "增普票",
@@ -1213,6 +1278,7 @@
         addOrUpdateSalesLedger(form.value).then((res) => {
            proxy.$modal.msgSuccess("提交成功");
            closeDia();
            expandedRowKeys.value = [];
            getList();
         });
      }
@@ -1384,6 +1450,7 @@
/** 判断单个产品是否已发货(根据shippingStatus判断,已发货或审核通过不可编辑和删除) */
const isProductShipped = (product) => {
   if (!product) return false;
   const status = String(product.shippingStatus || "").trim();
   // 如果发货状态是"已发货"或"审核通过",则不可编辑和删除
   return status === "已发货" || status === "审核通过";
@@ -1405,6 +1472,11 @@
const handleDelete = async () => {
   if (selectedRows.value.length === 0) {
      proxy.$modal.msgWarning("请选择数据");
      return;
   }
   const unauthorizedRows = selectedRows.value.filter((row) => !canDeleteLedger(row));
   if (unauthorizedRows.length > 0) {
      proxy.$modal.msgWarning("当前登录用户不是录入人,不能删除该数据");
      return;
   }
   const ids = selectedRows.value.map((item) => item.id);
@@ -1643,7 +1715,7 @@
      <div class="print-page">
        <div class="delivery-note">
          <div class="header">
            <div class="company-name">鼎诚瑞实业有限责任公司</div>
            <div class="company-name">阳光彩印有限责任公司</div>
            <div class="document-title">零售发货单</div>
          </div>
          
@@ -1778,7 +1850,7 @@
   const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
   }, 0);
   return total.toFixed(2);
   return total.toFixed(3);
};
// 计算产品总金额
@@ -1787,7 +1859,7 @@
   const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
   }, 0);
   return total.toFixed(2);
   return total.toFixed(3);
};
// 用于打印的计算函数
@@ -1796,7 +1868,7 @@
   const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.quantity) || 0);
   }, 0);
   return total.toFixed(2);
   return total.toFixed(3);
};
const getTotalAmountForPrint = (products) => {
@@ -1804,7 +1876,7 @@
   const total = products.reduce((sum, product) => {
      return sum + (parseFloat(product.taxInclusiveTotalPrice) || 0);
   }, 0);
   return total.toFixed(2);
   return total.toFixed(3);
};
const mathNum = () => {
@@ -1819,14 +1891,16 @@
   productForm.value.taxInclusiveTotalPrice =
      proxy.calculateTaxIncludeTotalPrice(
         productForm.value.taxInclusiveUnitPrice,
         productForm.value.quantity
         productForm.value.quantity,
         3
      );
   if (productForm.value.taxRate) {
      // 不含税总价计算
      productForm.value.taxExclusiveTotalPrice =
         proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
            productForm.value.taxRate,
            3
         );
   }
};
@@ -1845,14 +1919,15 @@
   isCalculating.value = true;
   
   // 计算含税单价 = 含税总价 / 数量
   productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
   productForm.value.taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(3);
   
   // 如果有税率,计算不含税总价
   if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
         proxy.calculateTaxExclusiveTotalPrice(
            totalPrice,
            productForm.value.taxRate
            productForm.value.taxRate,
            3
         );
   }
   
@@ -1880,10 +1955,10 @@
   // 先计算含税总价 = 不含税总价 / (1 - 税率/100)
   const taxRateDecimal = taxRate / 100;
   const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
   productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
   productForm.value.taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(3);
   
   // 计算含税单价 = 含税总价 / 数量
   productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
   productForm.value.taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(3);
   
   isCalculating.value = false;
};
@@ -1906,14 +1981,15 @@
   isCalculating.value = true;
   
   // 计算含税总价
   productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
   productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(3);
   
   // 如果有税率,计算不含税总价
   if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
         proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
            productForm.value.taxRate,
            3
         );
   }
   
@@ -1938,14 +2014,15 @@
   isCalculating.value = true;
   
   // 计算含税总价
   productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
   productForm.value.taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(3);
   
   // 如果有税率,计算不含税总价
   if (productForm.value.taxRate) {
      productForm.value.taxExclusiveTotalPrice =
         proxy.calculateTaxExclusiveTotalPrice(
            productForm.value.taxInclusiveTotalPrice,
            productForm.value.taxRate
            productForm.value.taxRate,
            3
         );
   }
   
@@ -1973,7 +2050,8 @@
   productForm.value.taxExclusiveTotalPrice =
      proxy.calculateTaxExclusiveTotalPrice(
         inclusiveTotalPrice,
         taxRate
         taxRate,
         3
      );
   
   isCalculating.value = false;
@@ -2046,8 +2124,8 @@
 * @param row 行数据
 */
const canShip = (row) => {
   // 产品状态必须是充足(approveStatus === 1)
   if (row.approveStatus !== 1) {
   // 产品状态必须是充足(基于 hasSufficientStock 判断)
   if (!row || !row.hasSufficientStock) {
      return false;
   }
   
@@ -2063,6 +2141,34 @@
   const statusStr = shippingStatus ? String(shippingStatus).trim() : '';
   return statusStr === '待发货' || statusStr === '审核拒绝';
};
/**
 * 导出销售合同
 *
 * @param row 导出销售合同的相关信息对象
 */
const exportSalesContracts = (row) => {
   exportSalesContract({ id: row.id }).then((res) => {
      if (res) {
         const downloadUrl = window.URL.createObjectURL(res);
      const link = document.createElement('a');
      link.href = downloadUrl;
     console.log(row.executionDate)
      link.download = row.projectName+row.executionDate + "销售合同.docx"; // 设置下载文件名
      link.style.display = 'none'; // 隐藏a标签
      document.body.appendChild(link);
      link.click(); // 触发点击下载
      // 4. 清理资源(避免内存泄漏)
      document.body.removeChild(link);
      window.URL.revokeObjectURL(downloadUrl);
      // 5. 提示导出成功
      proxy.$modal.msgSuccess("导出销售合同成功");
      } else {
         proxy.$modal.msgError(res.msg || "导出销售合同失败");
      }
   });
}
/**
 * 下载文件
@@ -2169,19 +2275,19 @@
   margin-left: 10px;
}
::v-deep .yellow {
:deep(.yellow) {
  background-color: #FAF0DE;
}
::v-deep .pink {
:deep(.pink) {
  background-color: #FAE1DE;
}
::v-deep .red {
:deep(.red) {
  background-color: #FAE1DE;
}
::v-deep .purple{
:deep(.purple){
  background-color: #F4DEFA;
}