wujianwei
2025-12-25 ccfedfe40d734687f1a8b72ea47abe160f2fa9a7
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,53 +24,58 @@
          </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">
        <h3 class="section-title">账单明细</h3>
      </div>
      <el-table :data="detailList" border stripe height="300px" style="width: 100%">
        <el-table-column prop="systemNo" label="系统编号" width="120" />
        <el-table-column prop="sourceSystem" label="来源系统" width="120">
        <el-table-column prop="systemNo" align="center" label="系统编号" width="140" />
        <el-table-column prop="sourceSystem" align="center" label="来源系统" width="120">
          <template #default="scope">
            {{ dictFormat(sys_system, scope.row.sourceSystem) }}
          </template>
        </el-table-column><!-- sys_system -->
        <el-table-column prop="businessSector" label="业务板块" width="120">
        </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" label="单据类型" width="120">
        <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 prop="documentNo" label="单据编号" width="150" />
        <el-table-column prop="customerName" label="客户名称" width="150" />
        <el-table-column prop="projectName" label="项目名称" width="150" />
        <el-table-column prop="receivableAmount" label="应收金额" align="right" />
        <el-table-column prop="currency" label="币制" width="100">
        </el-table-column>
        <el-table-column prop="documentNo" 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="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) }}
          </template>
@@ -79,7 +85,7 @@
    <template #footer>
      <el-button @click="cancel">取消</el-button>
      <el-button type="primary" @click="handleConfirm" >确认生成</el-button>
      <el-button type="primary" @click="handleConfirm">确认生成</el-button>
    </template>
  </el-dialog>
</template>
@@ -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,19 +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) {
    if (data) {
      // 假设 data 的结构就是 mainForm 需要的结构
      Object.assign(statistics.value, data);
      // 确保后端返回的明细字段名与此一致
      if (selectionList.length > 0) {
        detailList.value = [...selectionList];
      }
    // 1. 处理明细数据拆分(你原有的逻辑)
    let processedList = [];
    if (selectionList && selectionList.length > 0) {
      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 => {
          const match = part.match(/([\d.]+)([\u4e00-\u9fa5]+)/);
          let amount = mode === 'receivable' ? item.receivableAmount : item.payableAmount;
          let currencyValue = item.currency;
          if (match) {
            amount = parseFloat(match[1]);
            const currencyName = match[2];
            if (currencyName.includes('人民币')) currencyValue = 'RMB';
            else if (currencyName.includes('港币')) currencyValue = 'HKD';
          }
          const newItem = { ...item };
          if (mode === 'receivable') newItem.receivableAmount = amount;
          else newItem.payableAmount = amount;
          newItem.currency = currencyValue;
          return newItem;
        });
      });
    }
    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 () => {
@@ -208,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>