wujianwei
2025-12-26 b2ca7af3db0d1e2baf37829c33a82cd43d690751
ui/admin-ui3/src/components/GenerateBillDialog/index.vue
@@ -1,7 +1,8 @@
<template>
  <el-dialog v-model="visible" title="生成应收账单" width="1000px" destroy-on-close @closed="handleClosed">
  <el-dialog v-model="visible" :title="type === 'receivable' ? '生成应收账单' : '生成应付账单'" width="1000px" destroy-on-close
    @closed="handleClosed">
    <div class="dialog-content">
      <h3 class="section-title">是否根据以下数据生成应收账单</h3>
      <h3 class="section-title">是否根据以下数据生成{{ type === 'receivable' ? '应收' : '应付' }}账单</h3>
      <el-form :model="formData" ref="formRef" label-position="left" label-width="80px">
        <el-row :gutter="20">
@@ -23,25 +24,29 @@
          </el-col>
        </el-row>
      </el-form>
      <el-descriptions :column="3" border class="mb-6">
        <el-descriptions-item label="单据数量">
          {{ statistics.documentCount }}
        <el-descriptions-item label="单据数量">{{ statistics.documentCount }}</el-descriptions-item>
        <el-descriptions-item label="汇率 (港币兑人民币)">{{ statistics.rate }}</el-descriptions-item>
        <el-descriptions-item label="汇率 (人民币兑港币)">{{ statistics.rateRmb }}</el-descriptions-item>
        <el-descriptions-item :label="type === 'receivable' ? '应收人民币' : '应付人民币'">
          <span class="text-bold">{{ statistics.receivable }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="汇率 (港币兑人民币)">
          {{ statistics.rate }}
         <el-descriptions-item :label="type === 'receivable' ? '应收港币' : '应付港币'">
          <span class="text-bold">{{ statistics.amountReceivable }}</span>
        </el-descriptions-item>
        <!-- <el-descriptions-item label="汇率 (人民币兑港币)">
          {{ statistics.rateInverse || '-' }}
        </el-descriptions-item> -->
        <el-descriptions-item label="应收金额">
          <span class="text-bold">{{ statistics.totalReceivableAmount }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="应收金额 (以人民币计)">
        <el-descriptions-item :label="(type === 'receivable' ? '应收总金额' : '应付总金额') + ' (以人民币计)'">
          <span class="text-primary">{{ statistics.totalAmountRmb }}</span>
        </el-descriptions-item>
        <el-descriptions-item label="应收金额 (以港币计)">
        <el-descriptions-item :label="(type === 'receivable' ? '应收总金额' : '应付总金额') + ' (以港币计)'">
          <span class="text-success">{{ statistics.totalAmountHkd }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <div class="table-header">
@@ -53,22 +58,23 @@
          <template #default="scope">
            {{ dictFormat(sys_system, scope.row.sourceSystem) }}
          </template>
        </el-table-column><!-- sys_system -->
        </el-table-column>
        <el-table-column prop="businessSector" align="center" label="业务板块" width="120">
          <template #default="scope">
            {{ dictFormat(sys_business, scope.row.businessSector) }}
          </template>
        </el-table-column>
        <!-- sys_business -->
        <el-table-column prop="documentType" align="center" label="单据类型" width="120">
          <template #default="scope">
            {{ dictFormat(sys_receipts, scope.row.documentType) }}
          </template>
        </el-table-column><!-- sys_receipts -->
        </el-table-column>
        <el-table-column prop="documentNo" align="center" label="单据编号" width="150" />
        <el-table-column prop="customerName" align="center" label="客户名称" width="150" />
        <el-table-column :prop="type === 'receivable' ? 'customerName' : 'supplierName'" align="center"
          :label="type === 'receivable' ? '客户名称' : '供应商名称'" width="150" />
        <el-table-column prop="projectName" align="center" label="项目名称" width="150" />
        <el-table-column prop="receivableAmount" align="center" label="应收金额" />
        <el-table-column :prop="type === 'receivable' ? 'receivableAmount' : 'payableAmount'" align="center"
          :label="type === 'receivable' ? '应收金额' : '应付金额'" />
        <el-table-column prop="currency" align="center" label="币制" width="100">
          <template #default="scope">
            {{ dictFormat(sys_currency, scope.row.currency) }}
@@ -101,6 +107,8 @@
const dictFormat = (dict: any, value: any) => {
  return proxy.selectDictLabel(dict, value);
}
// 增加类型区分
const type = ref<'receivable' | 'payable'>('receivable');
// 定义接口结构
interface StatisticsData {
  documentCount: number;
@@ -124,53 +132,92 @@
const emit = defineEmits(['confirm']);
// 暴露给父组件的打开方法
const open = (data: any, selectionList: any[]) => {
// 修改 open 方法,增加 mode 参数
const open = (data: any, selectionList: any[], mode: 'receivable' | 'payable' = 'receivable') => {
  type.value = mode;
  visible.value = true;
  if (data) {
    Object.assign(statistics.value, data);
    // 1. 处理明细数据拆分(你原有的逻辑)
    let processedList = [];
    if (selectionList && selectionList.length > 0) {
      detailList.value = selectionList.flatMap(item => {
        if (!item.receivableAmountStr) return [item];
        // 1. 拆分多个币种字符串
        const amountParts = item.receivableAmountStr.trim().split(/\s+/);
      processedList = selectionList.flatMap(item => {
        const amountStr = mode === 'receivable' ? item.receivableAmountStr : item.payableAmountStr;
        if (!amountStr) return [item];
        const amountParts = amountStr.trim().split(/\s+/);
        return amountParts.map(part => {
          // 2. 正则解析提取数值和币种名称
          // ([\d.]+) 匹配数字和小数点
          // ([\u4e00-\u9fa5]+) 匹配中文字符(币种)
          const match = part.match(/([\d.]+)([\u4e00-\u9fa5]+)/);
          let amount = item.receivableAmount; // 默认值
          let currencyValue = item.currency;   // 默认值
          let amount = mode === 'receivable' ? item.receivableAmount : item.payableAmount;
          let currencyValue = item.currency;
          if (match) {
            amount = parseFloat(match[1]); // 提取的数字
            const currencyName = match[2]; // 提取的币种文字,如 "港币"
            // 3. 根据提取的文字匹配字典中的 Value
            // 假设字典 sys_currency.value: 0 是人民币, 1 是港币 (请根据您实际字典值调整)
            if (currencyName.includes('人民币')) {
              currencyValue = 'RMB'; // 对应字典的人民币value
            } else if (currencyName.includes('港币')) {
              currencyValue = 'HKD'; // 对应字典的港币value
            }
            amount = parseFloat(match[1]);
            const currencyName = match[2];
            if (currencyName.includes('人民币')) currencyValue = 'RMB';
            else if (currencyName.includes('港币')) currencyValue = 'HKD';
          }
          // 4. 返回新对象,覆盖金额和币制
          return {
            ...item,
            receivableAmount: amount, // 赋值提取的数字
            currency: currencyValue,   // 赋值匹配到的字典ID
            receivableAmountStr: part  // 保持拆分后的文本
          };
          const newItem = { ...item };
          if (mode === 'receivable') newItem.receivableAmount = amount;
          else newItem.payableAmount = amount;
          newItem.currency = currencyValue;
          return newItem;
        });
      });
    } else {
      detailList.value = [];
    }
    detailList.value = processedList;
    // 2. 执行计算逻辑
    const calcRes = calculateStatistics(processedList, mode);
    const rateHkdToRmb = data.rate || 0; // 港币兑人民币 (例如 0.91)
    // 计算 人民币兑港币 (1 / 0.91),保留4位小数
    const rateRmbToHkd = rateHkdToRmb !== 0
      ? Number((1 / rateHkdToRmb).toFixed(4))
      : 0;
    // 3. 组装最终的 statistics 对象
    const currentRate = data.rate || 0;
    statistics.value = {
      ...data,
      rateRmb: rateRmbToHkd,
      ...calcRes, // 将计算出的 receivable 和 amountReceivable 合并进去
      // 自动计算总金额(以人民币为准 = 人民币部分 + 港币部分 * 汇率)
      totalAmountRmb: Number((calcRes.receivable + calcRes.amountReceivable * currentRate).toFixed(2)),
      // 自动计算总金额(以港币为准 = 港币部分 + 人民币部分 / 汇率)
      totalAmountHkd: currentRate !== 0
        ? Number((calcRes.amountReceivable + calcRes.receivable / currentRate).toFixed(2))
        : 0
    };
  }
};
/**
 * 计算账单统计数据
 * @param list 拆分后的明细列表
 * @param mode 当前模式:应收或应付
 */
const calculateStatistics = (list: any[], mode: 'receivable' | 'payable') => {
  let rmbTotal = 0;
  let hkdTotal = 0;
  list.forEach(item => {
    // 根据模式确定取值字段
    const amount = mode === 'receivable' ? item.receivableAmount : item.payableAmount;
    const val = Number(amount) || 0;
    if (item.currency === 'RMB') {
      rmbTotal += val;
    } else if (item.currency === 'HKD') {
      hkdTotal += val;
    }
  });
  // 返回计算结果,保留两位小数(避免浮点误差)
  return {
    receivable: Number(rmbTotal.toFixed(2)),       // 对应你模板中的 statistics.receivable
    amountReceivable: Number(hkdTotal.toFixed(2))  // 对应你模板中的 statistics.amountReceivable
  };
};
// 3. 确认生成按钮逻辑
const handleConfirm = async () => {
@@ -242,4 +289,9 @@
  width: 180px;
  background-color: #f5f7fa;
}
::v-deep .el-descriptions__body .el-descriptions__table.is-bordered .el-descriptions__cell {
    border: var(--el-descriptions-table-border);
    padding: 8px 11px;
    width: 200px;
}
</style>