spring
2 天以前 261f2ed00235d47df3754291a4fdca9ba5cb8e7a
src/views/financialManagement/receivable/reconciliation.vue
@@ -2,8 +2,8 @@
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="客户:">
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
        <el-select v-model="filters.customerId" placeholder="请选择客户" clearable filterable style="width: 200px;">
          <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="对账期间:">
@@ -29,6 +29,7 @@
        rowKey="id"
        :column="columns"
        :tableData="dataList"
        :tableLoading="tableLoading"
        :page="{
          current: pagination.currentPage,
          size: pagination.pageSize,
@@ -36,21 +37,22 @@
        }"
        @pagination="changePage"
      >
        <template #beginBalance="{ row }">
          <span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.beginBalance) }}</span>
        <template #openingBalance="{ row }">
          <span :class="row.openingBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.openingBalance) }}</span>
        </template>
        <template #currentReceivable="{ row }">
          <span class="text-primary">¥{{ formatMoney(row.currentReceivable) }}</span>
        <template #currentPlan="{ row }">
          <span class="text-primary">¥{{ formatMoney(row.currentPlan) }}</span>
        </template>
        <template #currentReceipt="{ row }">
          <span class="text-success">¥{{ formatMoney(row.currentReceipt) }}</span>
        <template #currentActually="{ row }">
          <span class="text-success">¥{{ formatMoney(row.currentActually) }}</span>
        </template>
        <template #endBalance="{ row }">
          <span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.endBalance) }}</span>
        <template #closingBalance="{ row }">
          <span :class="row.closingBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.closingBalance) }}</span>
        </template>
        <template #operation="{ row }">
          <el-button type="primary" link @click="viewDetail(row)">查看明细</el-button>
          <el-button type="primary" link @click="printStatement(row)">打印</el-button>
          <!-- <el-button type="primary" link @click="printStatement(row)">打印</el-button> -->
          <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
        </template>
      </PIMTable>
    </div>
@@ -60,7 +62,7 @@
        <h3>{{ currentCustomer }} 应收对账单</h3>
        <p>对账期间: {{ currentPeriod }}</p>
      </div>
      <el-table :data="detailData" border style="width: 100%">
      <el-table :data="detailData" border style="width: 100%" v-loading="detailLoading">
        <el-table-column prop="date" label="日期" width="120" />
        <el-table-column prop="type" label="类型" width="100">
          <template #default="{ row }">
@@ -98,52 +100,68 @@
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="选择客户" prop="customerId">
              <el-select v-model="generateForm.customerId" placeholder="请选择客户" style="width: 100%;" @change="onCustomerChange">
                <el-option v-for="item in customerList" :key="item.id" :label="item.name" :value="item.id" />
              <el-select
                v-model="generateForm.customerId"
                placeholder="请选择客户"
                style="width: 100%;"
                filterable
                @change="onCustomerChange"
              >
                <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="对账月份" prop="period">
              <el-date-picker v-model="generateForm.period" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onPeriodChange" />
            <el-form-item label="对账月份" prop="statementMonth">
              <el-date-picker v-model="generateForm.statementMonth" type="month" placeholder="选择月份" value-format="YYYY-MM" style="width: 100%;" @change="onStatementMonthChange" />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div v-if="salesData.length > 0" class="sales-section">
        <div class="section-title">本月销售数据</div>
        <el-table :data="salesData" border style="width: 100%; margin-bottom: 15px;" v-loading="salesLoading" @selection-change="handleSalesSelectionChange">
      <div v-if="statementDetailLoaded" class="sales-section">
        <div v-if="salesData.length > 0" class="section-title">本月销售数据</div>
        <el-table
          v-if="salesData.length > 0"
          ref="salesTableRef"
          :data="salesData"
          border
          row-key="id"
          style="width: 100%; margin-bottom: 15px;"
          v-loading="salesLoading"
          @selection-change="handleSalesSelectionChange"
        >
          <el-table-column type="selection" width="55" align="center" />
          <el-table-column prop="date" label="日期" width="120" />
          <el-table-column prop="code" label="单据编号" width="150" />
          <el-table-column prop="occurrenceDate" label="日期" width="120" />
          <el-table-column prop="receiptNumber" label="单据编号" width="150" />
          <el-table-column prop="type" label="类型" width="100">
            <template #default="{ row }">
              <el-tag :type="row.type === '出库' ? 'success' : row.type === '收款' ? 'primary' : 'danger'">{{ row.type }}</el-tag>
              <el-tag :type="getDetailTypeTagType(row.type)">{{ row.typeLabel }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column prop="amount" label="金额" width="120">
            <template #default="{ row }">
              <span :class="row.type === '出库' ? 'text-primary' : row.type === '收款' ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.amount) }}</span>
              <span :class="getDetailAmountClass(row.type)">¥{{ formatMoney(row.amount) }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="remark" label="备注" />
        </el-table>
        <el-empty v-else description="该客户本月暂无明细数据" :image-size="80" />
        <div class="summary-row">
          <span>期初余额: <strong class="text-primary">¥{{ formatMoney(generateForm.beginBalance) }}</strong></span>
          <span>本期应收: <strong class="text-primary">¥{{ formatMoney(generateForm.currentReceivable) }}</strong></span>
          <span>本期收款: <strong class="text-success">¥{{ formatMoney(generateForm.currentReceipt) }}</strong></span>
          <span>期末余额: <strong :class="calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt) >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt)) }}</strong></span>
          <span>期初余额: <strong class="text-primary">¥{{ formatMoney(generateForm.openingBalance) }}</strong></span>
          <span>本期应收: <strong class="text-primary">¥{{ formatMoney(generateForm.currentPlan) }}</strong></span>
          <span>本期收款: <strong class="text-success">¥{{ formatMoney(generateForm.currentActually) }}</strong></span>
          <span>期末余额: <strong :class="displayClosingBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(displayClosingBalance) }}</strong></span>
        </div>
      </div>
      <div v-else-if="generateForm.customerId && !salesLoading" class="empty-tip">
      <div v-else-if="generateForm.customerId && generateForm.statementMonth && !salesLoading" class="empty-tip">
        <el-empty description="该客户本月暂无销售数据" />
      </div>
      <template #footer>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">确认生成</el-button>
        <el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate" :loading="submitLoading">确认生成</el-button>
        <el-button @click="generateDialogVisible = false">取消</el-button>
      </template>
    </FormDialog>
@@ -151,9 +169,20 @@
</template>
<script setup>
import { ref, reactive, onMounted, computed } from "vue";
import { ElMessage } from "element-plus";
import { ref, reactive, onMounted, computed, nextTick, getCurrentInstance } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import FormDialog from "@/components/Dialog/FormDialog.vue";
import { listCustomer } from "@/api/basicData/customer.js";
import {
  getAccountStatementDetailsByMonth,
  addAccountStatement,
  listPageAccountStatement,
  deleteAccountStatement,
} from "@/api/financialManagement/accountStatement.js";
const ACCOUNT_TYPE_RECEIVABLE = 1;
const { proxy } = getCurrentInstance();
defineOptions({
  name: "应收对账",
@@ -172,56 +201,192 @@
});
const columns = [
  { label: "对账单号", prop: "statementCode", width: "150" },
  { label: "对账单号", prop: "statementNumber", width: "150" },
  { label: "客户名称", prop: "customerName", width: "180" },
  { label: "对账期间", prop: "period", width: "150" },
  { label: "期初余额", prop: "beginBalance", slot: "beginBalance" },
  { label: "本期应收", prop: "currentReceivable", slot: "currentReceivable" },
  { label: "本期收款", prop: "currentReceipt", slot: "currentReceipt" },
  { label: "期末余额", prop: "endBalance", slot: "endBalance" },
  { label: "操作", prop: "operation", slot: "operation", width: "150", fixed: "right" },
  { label: "对账期间", prop: "statementMonth", width: "150" },
  { label: "期初余额", prop: "openingBalance", dataType: "slot", slot: "openingBalance" },
  { label: "本期应收", prop: "currentPlan", dataType: "slot", slot: "currentPlan" },
  { label: "本期收款", prop: "currentActually", dataType: "slot", slot: "currentActually" },
  { label: "期末余额", prop: "closingBalance", dataType: "slot", slot: "closingBalance" },
  { label: "操作", prop: "operation", dataType: "slot", slot: "operation", width: "200", fixed: "right" },
];
const dataList = ref([]);
const tableLoading = ref(false);
const submitLoading = ref(false);
const detailDialogVisible = ref(false);
const currentCustomer = ref("");
const currentPeriod = ref("");
const detailData = ref([]);
const detailLoading = ref(false);
const generateDialogVisible = ref(false);
const salesLoading = ref(false);
const statementDetailLoaded = ref(false);
const salesData = ref([]);
const selectedSales = ref([]);
const salesTableRef = ref(null);
const customerList = ref([]);
/** 明细 type:1出库 2入库 3收款 4付款 5退货 */
const STATEMENT_DETAIL_TYPE_MAP = {
  1: "出库",
  2: "入库",
  3: "收款",
  4: "付款",
  5: "退货",
};
const calculateEndBalance = (openingBalance, currentPlan, currentActually) => {
  return openingBalance + currentPlan - currentActually;
};
const getDetailTypeLabel = (type) => STATEMENT_DETAIL_TYPE_MAP[Number(type)] ?? "";
const getDetailTypeTagType = (type) => {
  const t = Number(type);
  if (t === 1) return "success";
  if (t === 3) return "primary";
  if (t === 5) return "danger";
  return "info";
};
const getDetailAmountClass = (type) => {
  const t = Number(type);
  if (t === 1) return "text-primary";
  if (t === 3) return "text-success";
  return "text-danger";
};
const generateForm = reactive({
  customerId: "",
  customerName: "",
  period: "",
  beginBalance: 0,
  currentReceivable: 0,
  currentReceipt: 0,
  statementMonth: "",
  openingBalance: 0,
  currentPlan: 0,
  currentActually: 0,
  closingBalance: 0,
});
const displayClosingBalance = computed(() => {
  return calculateEndBalance(
    generateForm.openingBalance,
    generateForm.currentPlan,
    generateForm.currentActually
  );
});
const canGenerate = computed(() => {
  return generateForm.customerId && generateForm.period && selectedSales.value.length > 0;
  return generateForm.customerId && generateForm.statementMonth && selectedSales.value.length > 0;
});
const customerList = [
  { id: 1, name: "北京科技有限公司" },
  { id: 2, name: "上海贸易公司" },
  { id: 3, name: "广州实业有限公司" },
  { id: 4, name: "深圳电子公司" },
];
const mockData = [
  { id: 1, statementCode: "DZ202401001", customerId: 1, customerName: "北京科技有限公司", period: "2024-01", beginBalance: 10000, currentReceivable: 15000, currentReceipt: 8000, endBalance: 17000 },
  { id: 2, statementCode: "DZ202401002", customerId: 2, customerName: "上海贸易公司", period: "2024-01", beginBalance: 5000, currentReceivable: 12000, currentReceipt: 10000, endBalance: 7000 },
  { id: 3, statementCode: "DZ202402001", customerId: 1, customerName: "北京科技有限公司", period: "2024-02", beginBalance: 17000, currentReceivable: 20000, currentReceipt: 15000, endBalance: 22000 },
];
const calculateEndBalance = (beginBalance, currentReceivable, currentReceipt) => {
  return beginBalance + currentReceivable - currentReceipt;
const applyStatementSummary = (data) => {
  generateForm.openingBalance = Number(data.openingBalance ?? 0);
  generateForm.currentPlan = Number(data.currentPlan ?? 0);
  generateForm.currentActually = Number(data.currentActually ?? 0);
  generateForm.closingBalance = Number(
    data.closingBalance ??
      calculateEndBalance(
        generateForm.openingBalance,
        generateForm.currentPlan,
        generateForm.currentActually
      )
  );
};
const getCustomerList = () => {
  listCustomer({ current: -1, size: -1, type: 0 }).then((res) => {
    if (res.code === 200) {
      customerList.value = res.data?.records || [];
    }
  });
};
const normalizeSalesRows = (list) => {
  const rows = Array.isArray(list) ? list : [];
  return rows.map((item, index) => {
    const type = Number(item.type);
    return {
      id: item.id ?? `detail-${index}`,
      accountStatementId: item.accountStatementId,
      occurrenceDate: item.occurrenceDate ?? "",
      receiptNumber: item.receiptNumber ?? "",
      type,
      typeLabel: getDetailTypeLabel(type),
      amount: Math.abs(Number(item.amount ?? 0)),
      remark: item.remark ?? "",
    };
  });
};
const selectAllSalesRows = (keepApiSummary = false) => {
  nextTick(() => {
    const table = salesTableRef.value;
    if (!table) return;
    table.clearSelection();
    salesData.value.forEach((row) => table.toggleRowSelection(row, true));
    selectedSales.value = [...salesData.value];
    if (!keepApiSummary) {
      calculateSummary();
    }
  });
};
const isNumericId = (id) => id !== undefined && id !== null && id !== "" && /^\d+$/.test(String(id));
const buildFilterParams = (params = {}) => {
  const result = { ...params, accountType: ACCOUNT_TYPE_RECEIVABLE };
  if (filters.customerId) {
    result.customerId = filters.customerId;
  }
  if (filters.startMonth && filters.endMonth && filters.startMonth === filters.endMonth) {
    result.statementMonth = filters.startMonth;
  } else if (filters.startMonth) {
    result.startMonth = filters.startMonth;
  }
  if (filters.endMonth && filters.startMonth !== filters.endMonth) {
    result.endMonth = filters.endMonth;
  }
  return result;
};
const buildListParams = () =>
  buildFilterParams({
    current: pagination.currentPage,
    size: pagination.pageSize,
  });
const buildExportParams = () => buildFilterParams({});
const buildDetailSubmitItem = (row) => {
  const item = {
    occurrenceDate: row.occurrenceDate,
    receiptNumber: row.receiptNumber,
    type: row.type,
    amount: row.amount,
    remark: row.remark ?? "",
  };
  if (isNumericId(row.id)) {
    item.id = Number(row.id);
  }
  if (row.accountStatementId) {
    item.accountStatementId = row.accountStatementId;
  }
  return item;
};
const buildAddPayload = () => ({
  customerId: generateForm.customerId,
  customerName: generateForm.customerName,
  statementMonth: generateForm.statementMonth,
  accountType: ACCOUNT_TYPE_RECEIVABLE,
  statementNumber: "",
  openingBalance: generateForm.openingBalance,
  currentPlan: generateForm.currentPlan,
  currentActually: generateForm.currentActually,
  closingBalance: generateForm.closingBalance,
  accountStatementDetails: selectedSales.value.map(buildDetailSubmitItem),
});
const formatMoney = (value) => {
  if (value === undefined || value === null) return "0.00";
@@ -229,15 +394,27 @@
};
const getTableData = () => {
  let result = [...mockData];
  if (filters.customerId) {
    result = result.filter(item => item.customerId === filters.customerId);
  }
  if (filters.startMonth && filters.endMonth) {
    result = result.filter(item => item.period >= filters.startMonth && item.period <= filters.endMonth);
  }
  pagination.total = result.length;
  dataList.value = result.slice((pagination.currentPage - 1) * pagination.pageSize, pagination.currentPage * pagination.pageSize);
  tableLoading.value = true;
  listPageAccountStatement(buildListParams())
    .then((res) => {
      const ok = res.code === 200 || res.code === 0;
      if (ok && res.data) {
        pagination.total = res.data.total ?? 0;
        dataList.value = res.data.records ?? [];
      } else {
        ElMessage.error(res.msg || "查询失败");
        dataList.value = [];
        pagination.total = 0;
      }
    })
    .catch(() => {
      dataList.value = [];
      pagination.total = 0;
      ElMessage.error("查询失败");
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
const resetFilters = () => {
@@ -257,83 +434,97 @@
const generateStatement = () => {
  generateForm.customerId = "";
  generateForm.customerName = "";
  generateForm.period = "";
  generateForm.beginBalance = 0;
  generateForm.currentReceivable = 0;
  generateForm.currentReceipt = 0;
  generateForm.statementMonth = "";
  generateForm.openingBalance = 0;
  generateForm.currentPlan = 0;
  generateForm.currentActually = 0;
  generateForm.closingBalance = 0;
  statementDetailLoaded.value = false;
  salesData.value = [];
  selectedSales.value = [];
  generateDialogVisible.value = true;
};
const onCustomerChange = (customerId) => {
  const customer = customerList.find(item => item.id === customerId);
  if (customer) {
    generateForm.customerName = customer.name;
  }
  const customer = customerList.value.find((item) => item.id === customerId);
  generateForm.customerName = customer?.customerName ?? "";
  loadSalesData();
};
const onPeriodChange = () => {
const onStatementMonthChange = () => {
  loadSalesData();
};
const loadSalesData = () => {
  if (!generateForm.customerId || !generateForm.period) {
  if (!generateForm.customerId || !generateForm.statementMonth) {
    salesData.value = [];
    selectedSales.value = [];
    statementDetailLoaded.value = false;
    generateForm.openingBalance = 0;
    generateForm.currentPlan = 0;
    generateForm.currentActually = 0;
    generateForm.closingBalance = 0;
    return;
  }
  salesLoading.value = true;
  selectedSales.value = [];
  statementDetailLoaded.value = false;
  setTimeout(() => {
    const mockSalesData = [
      { id: 1, date: generateForm.period + "-03", code: "CK2024001", type: "出库", amount: 8000, remark: "产品A销售" },
      { id: 2, date: generateForm.period + "-08", code: "SK2024001", type: "收款", amount: 5000, remark: "客户回款" },
      { id: 3, date: generateForm.period + "-12", code: "CK2024002", type: "出库", amount: 12000, remark: "产品B销售" },
      { id: 4, date: generateForm.period + "-15", code: "TH2024001", type: "退货", amount: 2000, remark: "质量问题退货" },
      { id: 5, date: generateForm.period + "-20", code: "CK2024003", type: "出库", amount: 5000, remark: "产品C销售" },
      { id: 6, date: generateForm.period + "-25", code: "SK2024002", type: "收款", amount: 8000, remark: "客户回款" },
    ];
  getAccountStatementDetailsByMonth({
    accountType: ACCOUNT_TYPE_RECEIVABLE,
    customerId: generateForm.customerId,
    statementMonth: generateForm.statementMonth,
  })
    .then((res) => {
      if (res.code === 200) {
        const data = res.data ?? {};
        const details = data.accountStatementDetails;
        const list = Array.isArray(details) ? details : [];
        salesData.value = normalizeSalesRows(list);
        applyStatementSummary(data);
        statementDetailLoaded.value = true;
    salesData.value = mockSalesData;
    const lastPeriod = getLastPeriod(generateForm.period);
    const lastStatement = mockData.find(item =>
      item.customerId === generateForm.customerId && item.period === lastPeriod
    );
    generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0;
    calculateSummary();
    salesLoading.value = false;
  }, 500);
};
const getLastPeriod = (period) => {
  const [year, month] = period.split("-").map(Number);
  if (month === 1) {
    return `${year - 1}-12`;
  }
  return `${year}-${String(month - 1).padStart(2, "0")}`;
        if (salesData.value.length > 0) {
          selectAllSalesRows(true);
        }
      } else {
        salesData.value = [];
        statementDetailLoaded.value = false;
        ElMessage.error(res.msg || "查询对账明细失败");
      }
    })
    .catch(() => {
      salesData.value = [];
      statementDetailLoaded.value = false;
      ElMessage.error("查询对账明细失败");
    })
    .finally(() => {
      salesLoading.value = false;
    });
};
const calculateSummary = () => {
  let receivable = 0;
  let receipt = 0;
  selectedSales.value.forEach(item => {
    if (item.type === "出库") {
  selectedSales.value.forEach((item) => {
    if (item.type === 1) {
      receivable += item.amount;
    } else if (item.type === "退货") {
    } else if (item.type === 5) {
      receivable -= item.amount;
    } else if (item.type === "收款") {
    } else if (item.type === 3) {
      receipt += item.amount;
    }
  });
  generateForm.currentReceivable = receivable;
  generateForm.currentReceipt = receipt;
  generateForm.currentPlan = receivable;
  generateForm.currentActually = receipt;
  generateForm.closingBalance = calculateEndBalance(
    generateForm.openingBalance,
    generateForm.currentPlan,
    generateForm.currentActually
  );
};
const handleSalesSelectionChange = (selection) => {
@@ -342,51 +533,127 @@
};
const confirmGenerate = () => {
  const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1;
  const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt);
  if (!canGenerate.value) return;
  submitLoading.value = true;
  addAccountStatement(buildAddPayload())
    .then((res) => {
      if (res.code === 200) {
        generateDialogVisible.value = false;
        ElMessage.success("对账单生成成功");
        pagination.currentPage = 1;
        getTableData();
      } else {
        ElMessage.error(res.msg || "生成失败");
      }
    })
    .catch(() => {
      ElMessage.error("生成失败");
    })
    .finally(() => {
      submitLoading.value = false;
    });
};
  mockData.unshift({
    id: newId,
    statementCode: "DZ" + Date.now(),
    customerId: generateForm.customerId,
    customerName: generateForm.customerName,
    period: generateForm.period,
    beginBalance: generateForm.beginBalance,
    currentReceivable: generateForm.currentReceivable,
    currentReceipt: generateForm.currentReceipt,
    endBalance,
const handleDelete = (row) => {
  ElMessageBox.confirm(`确认删除对账单「${row.statementNumber || row.id}」吗?`, "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  }).then(() => {
    deleteAccountStatement([row.id])
      .then((res) => {
        if (res.code === 200) {
          ElMessage.success("删除成功");
          getTableData();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      })
      .catch(() => {
        ElMessage.error("删除失败");
      });
  });
};
const buildDetailTableFromApi = (data, statementMonth) => {
  const details = Array.isArray(data.accountStatementDetails) ? data.accountStatementDetails : [];
  let runningBalance = Number(data.openingBalance ?? 0);
  const rows = [
    {
      date: statementMonth ?? "",
      type: "期初",
      code: "-",
      debit: 0,
      credit: 0,
      balance: runningBalance,
      remark: "期初余额",
    },
  ];
  details.forEach((item) => {
    const amount = Math.abs(Number(item.amount ?? 0));
    const type = Number(item.type);
    let debit = 0;
    let credit = 0;
    if (type === 1) {
      debit = amount;
      runningBalance += amount;
    } else if (type === 3 || type === 5) {
      credit = amount;
      runningBalance -= amount;
    }
    rows.push({
      date: item.occurrenceDate ?? "",
      type: getDetailTypeLabel(type),
      code: item.receiptNumber ?? "",
      debit,
      credit,
      balance: runningBalance,
      remark: item.remark ?? "",
    });
  });
  generateDialogVisible.value = false;
  ElMessage.success("对账单生成成功");
  getTableData();
  return rows;
};
const viewDetail = (row) => {
  currentCustomer.value = row.customerName;
  currentPeriod.value = row.period;
  if (!row.customerId || !row.statementMonth) {
    ElMessage.warning("缺少客户或对账月份,无法查询明细");
    return;
  }
  const saleOutAmount = Math.floor(row.currentReceivable * 0.6);
  const returnAmount = Math.floor(row.currentReceivable * 0.1);
  const firstReceipt = Math.floor(row.currentReceipt * 0.4);
  const secondReceipt = row.currentReceipt - firstReceipt;
  let runningBalance = row.beginBalance;
  detailData.value = [
    { date: row.period + "-01", type: "期初", code: "-", debit: 0, credit: 0, balance: runningBalance, remark: "期初余额" },
    { date: row.period + "-05", type: "出库", code: "CK2024001", debit: saleOutAmount, credit: 0, balance: runningBalance += saleOutAmount, remark: "销售出库" },
    { date: row.period + "-10", type: "收款", code: "SK2024001", debit: 0, credit: firstReceipt, balance: runningBalance -= firstReceipt, remark: "客户回款" },
    { date: row.period + "-15", type: "出库", code: "CK2024002", debit: row.currentReceivable - saleOutAmount - returnAmount, credit: 0, balance: runningBalance += (row.currentReceivable - saleOutAmount - returnAmount), remark: "销售出库" },
    { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: returnAmount, balance: runningBalance -= returnAmount, remark: "销售退货" },
    { date: row.period + "-25", type: "收款", code: "SK2024002", debit: 0, credit: secondReceipt, balance: runningBalance -= secondReceipt, remark: "客户回款" },
  ];
  currentCustomer.value = row.customerName ?? "";
  currentPeriod.value = row.statementMonth ?? "";
  detailData.value = [];
  detailDialogVisible.value = true;
  detailLoading.value = true;
  getAccountStatementDetailsByMonth({
    accountType: ACCOUNT_TYPE_RECEIVABLE,
    customerId: row.customerId,
    statementMonth: row.statementMonth,
  })
    .then((res) => {
      if (res.code === 200) {
        detailData.value = buildDetailTableFromApi(res.data ?? {}, row.statementMonth);
      } else {
        ElMessage.error(res.msg || "查询明细失败");
        detailDialogVisible.value = false;
      }
    })
    .catch(() => {
      ElMessage.error("查询明细失败");
      detailDialogVisible.value = false;
    })
    .finally(() => {
      detailLoading.value = false;
    });
};
const printStatement = (row) => {
  ElMessage.info(`打印对账单: ${row.statementCode}`);
  ElMessage.info(`打印对账单: ${row.statementNumber}`);
};
const printDetail = () => {
@@ -394,10 +661,12 @@
};
const handleOut = () => {
  ElMessage.success("导出成功");
  const params = buildExportParams();
  proxy.download("/accountStatement/exportAccountStatement", params, `应收对账单_${Date.now()}.xlsx`);
};
onMounted(() => {
  getCustomerList();
  getTableData();
});
</script>