| | |
| | | <div class="app-container"> |
| | | <el-form :model="filters" :inline="true"> |
| | | <el-form-item label="供应商:"> |
| | | <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable style="width: 200px;"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | <el-select v-model="filters.supplierId" placeholder="请选择供应商" clearable filterable style="width: 200px;"> |
| | | <el-option |
| | | v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="对账期间:"> |
| | |
| | | <el-date-picker v-model="filters.endMonth" type="month" placeholder="结束月份" value-format="YYYY-MM" style="width: 140px;" /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="getTableData">搜索</el-button> |
| | | <el-button type="primary" @click="onSearch">搜索</el-button> |
| | | <el-button @click="resetFilters">重置</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | |
| | | rowKey="id" |
| | | :column="columns" |
| | | :tableData="dataList" |
| | | :tableLoading="tableLoading" |
| | | :page="{ |
| | | current: pagination.currentPage, |
| | | size: pagination.pageSize, |
| | |
| | | }" |
| | | @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 #currentPayable="{ row }"> |
| | | <span class="text-danger">¥{{ formatMoney(row.currentPayable) }}</span> |
| | | <template #currentPlan="{ row }"> |
| | | <span class="text-danger">¥{{ formatMoney(row.currentPlan) }}</span> |
| | | </template> |
| | | <template #currentPayment="{ row }"> |
| | | <span class="text-success">¥{{ formatMoney(row.currentPayment) }}</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="danger" link @click="handleDelete(row)">删除</el-button> |
| | | </template> |
| | | </PIMTable> |
| | | </div> |
| | |
| | | <h3>{{ currentSupplier }} 应付对账单</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 }"> |
| | |
| | | <el-table-column prop="credit" label="贷方(应付)" width="120"> |
| | | <template #default="{ row }"> |
| | | <span v-if="row.credit > 0" class="text-danger">¥{{ formatMoney(row.credit) }}</span> |
| | | <span v-else-if="row.credit < 0" class="text-success">¥{{ formatMoney(Math.abs(row.credit)) }}</span> |
| | | <span v-else>-</span> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="选择供应商" prop="supplierId"> |
| | | <el-select v-model="generateForm.supplierId" placeholder="请选择供应商" style="width: 100%;" @change="onSupplierChange"> |
| | | <el-option v-for="item in supplierList" :key="item.id" :label="item.name" :value="item.id" /> |
| | | <el-select |
| | | v-model="generateForm.supplierId" |
| | | placeholder="请选择供应商" |
| | | style="width: 100%;" |
| | | filterable |
| | | @change="onSupplierChange" |
| | | > |
| | | <el-option |
| | | v-for="item in supplierList" |
| | | :key="item.id" |
| | | :label="item.supplierName" |
| | | :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="purchaseData.length > 0" class="purchase-section"> |
| | | <div class="section-title">本月采购数据</div> |
| | | <el-table :data="purchaseData" border style="width: 100%; margin-bottom: 15px;" v-loading="purchaseLoading" @selection-change="handlePurchaseSelectionChange"> |
| | | <div v-if="statementDetailLoaded" class="purchase-section"> |
| | | <div v-if="purchaseData.length > 0" class="section-title">本月采购数据</div> |
| | | <el-table |
| | | v-if="purchaseData.length > 0" |
| | | ref="purchaseTableRef" |
| | | :data="purchaseData" |
| | | border |
| | | row-key="id" |
| | | style="width: 100%; margin-bottom: 15px;" |
| | | v-loading="purchaseLoading" |
| | | @selection-change="handlePurchaseSelectionChange" |
| | | > |
| | | <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' : '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-danger' : 'text-success'">¥{{ 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-danger">¥{{ formatMoney(generateForm.currentPayable) }}</strong></span> |
| | | <span>本期付款: <strong class="text-success">¥{{ formatMoney(generateForm.currentPayment) }}</strong></span> |
| | | <span>期末余额: <strong class="text-primary">¥{{ formatMoney(calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment)) }}</strong></span> |
| | | <span>期初余额: <strong class="text-primary">¥{{ formatMoney(generateForm.openingBalance) }}</strong></span> |
| | | <span>本期应付: <strong class="text-danger">¥{{ 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.supplierId && !purchaseLoading" class="empty-tip"> |
| | | <div v-else-if="generateForm.supplierId && generateForm.statementMonth && !purchaseLoading" 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> |
| | |
| | | </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 { getOptions } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { |
| | | getAccountStatementDetailsByMonth, |
| | | addAccountStatement, |
| | | listPageAccountStatement, |
| | | deleteAccountStatement, |
| | | } from "@/api/financialManagement/accountStatement.js"; |
| | | |
| | | const ACCOUNT_TYPE_PAYABLE = 2; |
| | | |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | defineOptions({ |
| | | name: "应付对账", |
| | |
| | | }); |
| | | |
| | | const columns = [ |
| | | { label: "对账单号", prop: "statementCode", width: "150" }, |
| | | { label: "对账单号", prop: "statementNumber", width: "150" }, |
| | | { label: "供应商", prop: "supplierName", width: "180" }, |
| | | { label: "对账期间", prop: "period", width: "150" }, |
| | | { label: "期初余额", prop: "beginBalance", slot: "beginBalance" }, |
| | | { label: "本期应付", prop: "currentPayable", slot: "currentPayable" }, |
| | | { label: "本期付款", prop: "currentPayment", slot: "currentPayment" }, |
| | | { 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 currentSupplier = ref(""); |
| | | const currentPeriod = ref(""); |
| | | const detailData = ref([]); |
| | | const detailLoading = ref(false); |
| | | |
| | | const generateDialogVisible = ref(false); |
| | | const purchaseLoading = ref(false); |
| | | const statementDetailLoaded = ref(false); |
| | | const purchaseData = ref([]); |
| | | const selectedPurchases = ref([]); |
| | | const purchaseTableRef = ref(null); |
| | | const supplierList = 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 === 2) return "success"; |
| | | if (t === 4) return "primary"; |
| | | if (t === 5) return "danger"; |
| | | return "info"; |
| | | }; |
| | | |
| | | const getDetailAmountClass = (type) => { |
| | | const t = Number(type); |
| | | if (t === 2) return "text-danger"; |
| | | if (t === 4) return "text-success"; |
| | | return "text-danger"; |
| | | }; |
| | | |
| | | const generateForm = reactive({ |
| | | supplierId: "", |
| | | supplierName: "", |
| | | period: "", |
| | | beginBalance: 0, |
| | | currentPayable: 0, |
| | | currentPayment: 0, |
| | | statementMonth: "", |
| | | openingBalance: 0, |
| | | currentPlan: 0, |
| | | currentActually: 0, |
| | | closingBalance: 0, |
| | | }); |
| | | |
| | | const canGenerate = computed(() => { |
| | | return generateForm.supplierId && generateForm.period && selectedPurchases.value.length > 0; |
| | | }); |
| | | const displayClosingBalance = computed(() => |
| | | calculateEndBalance( |
| | | generateForm.openingBalance, |
| | | generateForm.currentPlan, |
| | | generateForm.currentActually |
| | | ) |
| | | ); |
| | | |
| | | const supplierList = [ |
| | | { id: 1, name: "北京原材料供应商" }, |
| | | { id: 2, name: "上海电子元器件公司" }, |
| | | { id: 3, name: "广州包装材料厂" }, |
| | | { id: 4, name: "深圳五金配件公司" }, |
| | | ]; |
| | | const canGenerate = computed( |
| | | () => generateForm.supplierId && generateForm.statementMonth && selectedPurchases.value.length > 0 |
| | | ); |
| | | |
| | | const mockData = [ |
| | | { id: 1, statementCode: "DZ202401001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-01", beginBalance: 20000, currentPayable: 15000, currentPayment: 10000, endBalance: 25000 }, |
| | | { id: 2, statementCode: "DZ202401002", supplierId: 2, supplierName: "上海电子元器件公司", period: "2024-01", beginBalance: 10000, currentPayable: 20000, currentPayment: 15000, endBalance: 15000 }, |
| | | { id: 3, statementCode: "DZ202402001", supplierId: 1, supplierName: "北京原材料供应商", period: "2024-02", beginBalance: 25000, currentPayable: 18000, currentPayment: 20000, endBalance: 23000 }, |
| | | ]; |
| | | |
| | | const calculateEndBalance = (beginBalance, currentPayable, currentPayment) => { |
| | | return beginBalance + currentPayable - currentPayment; |
| | | 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 getSupplierList = () => { |
| | | getOptions().then((res) => { |
| | | if (res.code === 200) { |
| | | supplierList.value = res.data ?? []; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const normalizePurchaseRows = (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 selectAllPurchaseRows = (keepApiSummary = false) => { |
| | | nextTick(() => { |
| | | const table = purchaseTableRef.value; |
| | | if (!table) return; |
| | | table.clearSelection(); |
| | | purchaseData.value.forEach((row) => table.toggleRowSelection(row, true)); |
| | | selectedPurchases.value = [...purchaseData.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_PAYABLE }; |
| | | if (filters.supplierId) { |
| | | result.customerId = filters.supplierId; |
| | | } |
| | | 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.supplierId, |
| | | customerName: generateForm.supplierName, |
| | | statementMonth: generateForm.statementMonth, |
| | | accountType: ACCOUNT_TYPE_PAYABLE, |
| | | statementNumber: "", |
| | | openingBalance: generateForm.openingBalance, |
| | | currentPlan: generateForm.currentPlan, |
| | | currentActually: generateForm.currentActually, |
| | | closingBalance: generateForm.closingBalance, |
| | | accountStatementDetails: selectedPurchases.value.map(buildDetailSubmitItem), |
| | | }); |
| | | |
| | | const formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | |
| | | }; |
| | | |
| | | const getTableData = () => { |
| | | let result = [...mockData]; |
| | | if (filters.supplierId) { |
| | | result = result.filter(item => item.supplierId === filters.supplierId); |
| | | } |
| | | 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 ?? []).map((row) => ({ |
| | | ...row, |
| | | supplierName: row.supplierName ?? row.customerName, |
| | | })); |
| | | } else { |
| | | ElMessage.error(res.msg || "查询失败"); |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | dataList.value = []; |
| | | pagination.total = 0; |
| | | ElMessage.error("查询失败"); |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const onSearch = () => { |
| | | pagination.currentPage = 1; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const resetFilters = () => { |
| | |
| | | getTableData(); |
| | | }; |
| | | |
| | | const changePage = ({ current, size }) => { |
| | | pagination.currentPage = current; |
| | | pagination.pageSize = size; |
| | | const changePage = ({ page, limit }) => { |
| | | pagination.currentPage = page; |
| | | pagination.pageSize = limit; |
| | | getTableData(); |
| | | }; |
| | | |
| | | const generateStatement = () => { |
| | | generateForm.supplierId = ""; |
| | | generateForm.supplierName = ""; |
| | | generateForm.period = ""; |
| | | generateForm.beginBalance = 0; |
| | | generateForm.currentPayable = 0; |
| | | generateForm.currentPayment = 0; |
| | | generateForm.statementMonth = ""; |
| | | generateForm.openingBalance = 0; |
| | | generateForm.currentPlan = 0; |
| | | generateForm.currentActually = 0; |
| | | generateForm.closingBalance = 0; |
| | | statementDetailLoaded.value = false; |
| | | purchaseData.value = []; |
| | | selectedPurchases.value = []; |
| | | generateDialogVisible.value = true; |
| | | }; |
| | | |
| | | const onSupplierChange = (supplierId) => { |
| | | const supplier = supplierList.find(item => item.id === supplierId); |
| | | if (supplier) { |
| | | generateForm.supplierName = supplier.name; |
| | | } |
| | | const supplier = supplierList.value.find((item) => item.id === supplierId); |
| | | generateForm.supplierName = supplier?.supplierName ?? ""; |
| | | loadPurchaseData(); |
| | | }; |
| | | |
| | | const onPeriodChange = () => { |
| | | const onStatementMonthChange = () => { |
| | | loadPurchaseData(); |
| | | }; |
| | | |
| | | const loadPurchaseData = () => { |
| | | if (!generateForm.supplierId || !generateForm.period) { |
| | | if (!generateForm.supplierId || !generateForm.statementMonth) { |
| | | purchaseData.value = []; |
| | | selectedPurchases.value = []; |
| | | statementDetailLoaded.value = false; |
| | | generateForm.openingBalance = 0; |
| | | generateForm.currentPlan = 0; |
| | | generateForm.currentActually = 0; |
| | | generateForm.closingBalance = 0; |
| | | return; |
| | | } |
| | | |
| | | purchaseLoading.value = true; |
| | | selectedPurchases.value = []; |
| | | statementDetailLoaded.value = false; |
| | | |
| | | setTimeout(() => { |
| | | const mockPurchaseData = [ |
| | | { id: 1, date: generateForm.period + "-05", code: "RK2024001", type: "入库", amount: 8000, remark: "原材料采购" }, |
| | | { id: 2, date: generateForm.period + "-10", code: "FK2024001", type: "付款", amount: 5000, remark: "支付货款" }, |
| | | { id: 3, date: generateForm.period + "-15", code: "RK2024002", type: "入库", amount: 12000, remark: "电子元器件" }, |
| | | { id: 4, date: generateForm.period + "-18", code: "TH2024001", type: "退货", amount: 2000, remark: "质量问题退货" }, |
| | | { id: 5, date: generateForm.period + "-22", code: "RK2024003", type: "入库", amount: 6000, remark: "包装材料" }, |
| | | { id: 6, date: generateForm.period + "-25", code: "FK2024002", type: "付款", amount: 8000, remark: "支付货款" }, |
| | | ]; |
| | | getAccountStatementDetailsByMonth({ |
| | | accountType: ACCOUNT_TYPE_PAYABLE, |
| | | customerId: generateForm.supplierId, |
| | | statementMonth: generateForm.statementMonth, |
| | | }) |
| | | .then((res) => { |
| | | if (res.code === 200) { |
| | | const data = res.data ?? {}; |
| | | const details = data.accountStatementDetails; |
| | | const list = Array.isArray(details) ? details : []; |
| | | purchaseData.value = normalizePurchaseRows(list); |
| | | applyStatementSummary(data); |
| | | statementDetailLoaded.value = true; |
| | | |
| | | purchaseData.value = mockPurchaseData; |
| | | |
| | | const lastPeriod = getLastPeriod(generateForm.period); |
| | | const lastStatement = mockData.find(item => |
| | | item.supplierId === generateForm.supplierId && item.period === lastPeriod |
| | | ); |
| | | generateForm.beginBalance = lastStatement ? lastStatement.endBalance : 0; |
| | | |
| | | calculateSummary(); |
| | | |
| | | purchaseLoading.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 (purchaseData.value.length > 0) { |
| | | selectAllPurchaseRows(true); |
| | | } |
| | | } else { |
| | | purchaseData.value = []; |
| | | statementDetailLoaded.value = false; |
| | | ElMessage.error(res.msg || "查询对账明细失败"); |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | purchaseData.value = []; |
| | | statementDetailLoaded.value = false; |
| | | ElMessage.error("查询对账明细失败"); |
| | | }) |
| | | .finally(() => { |
| | | purchaseLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const calculateSummary = () => { |
| | | let payable = 0; |
| | | let payment = 0; |
| | | |
| | | selectedPurchases.value.forEach(item => { |
| | | if (item.type === "入库") { |
| | | selectedPurchases.value.forEach((item) => { |
| | | if (item.type === 2) { |
| | | payable += item.amount; |
| | | } else if (item.type === "退货") { |
| | | } else if (item.type === 5) { |
| | | payable -= item.amount; |
| | | } else if (item.type === "付款") { |
| | | } else if (item.type === 4) { |
| | | payment += item.amount; |
| | | } |
| | | }); |
| | | |
| | | generateForm.currentPayable = payable; |
| | | generateForm.currentPayment = payment; |
| | | generateForm.currentPlan = payable; |
| | | generateForm.currentActually = payment; |
| | | generateForm.closingBalance = calculateEndBalance( |
| | | generateForm.openingBalance, |
| | | generateForm.currentPlan, |
| | | generateForm.currentActually |
| | | ); |
| | | }; |
| | | |
| | | const handlePurchaseSelectionChange = (selection) => { |
| | |
| | | }; |
| | | |
| | | const confirmGenerate = () => { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentPayable, generateForm.currentPayment); |
| | | 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(), |
| | | supplierId: generateForm.supplierId, |
| | | supplierName: generateForm.supplierName, |
| | | period: generateForm.period, |
| | | beginBalance: generateForm.beginBalance, |
| | | currentPayable: generateForm.currentPayable, |
| | | currentPayment: generateForm.currentPayment, |
| | | 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 === 2) { |
| | | credit = amount; |
| | | runningBalance += amount; |
| | | } else if (type === 4) { |
| | | debit = amount; |
| | | runningBalance -= amount; |
| | | } else if (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) => { |
| | | currentSupplier.value = row.supplierName; |
| | | currentPeriod.value = row.period; |
| | | const partnerId = row.customerId ?? row.supplierId; |
| | | if (!partnerId || !row.statementMonth) { |
| | | ElMessage.warning("缺少供应商或对账月份,无法查询明细"); |
| | | return; |
| | | } |
| | | |
| | | const purchaseInAmount = Math.floor(row.currentPayable * 0.7); |
| | | const returnAmount = Math.floor(row.currentPayable * 0.1); |
| | | const firstPayment = Math.floor(row.currentPayment * 0.5); |
| | | const secondPayment = row.currentPayment - firstPayment; |
| | | |
| | | 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: "RK2024001", debit: 0, credit: purchaseInAmount, balance: runningBalance += purchaseInAmount, remark: "采购入库" }, |
| | | { date: row.period + "-10", type: "付款", code: "FK2024001", debit: firstPayment, credit: 0, balance: runningBalance -= firstPayment, remark: "支付货款" }, |
| | | { date: row.period + "-15", type: "入库", code: "RK2024002", debit: 0, credit: row.currentPayable - purchaseInAmount - returnAmount, balance: runningBalance += (row.currentPayable - purchaseInAmount - returnAmount), remark: "采购入库" }, |
| | | { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: -returnAmount, balance: runningBalance -= returnAmount, remark: "采购退货" }, |
| | | { date: row.period + "-25", type: "付款", code: "FK2024002", debit: secondPayment, credit: 0, balance: runningBalance -= secondPayment, remark: "支付货款" }, |
| | | ]; |
| | | |
| | | currentSupplier.value = row.supplierName ?? row.customerName ?? ""; |
| | | currentPeriod.value = row.statementMonth ?? ""; |
| | | detailData.value = []; |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | detailLoading.value = true; |
| | | |
| | | const printStatement = (row) => { |
| | | ElMessage.info(`打印对账单: ${row.statementCode}`); |
| | | getAccountStatementDetailsByMonth({ |
| | | accountType: ACCOUNT_TYPE_PAYABLE, |
| | | customerId: partnerId, |
| | | 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 printDetail = () => { |
| | |
| | | }; |
| | | |
| | | const handleOut = () => { |
| | | ElMessage.success("导出成功"); |
| | | proxy.download( |
| | | "/accountStatement/exportAccountStatement", |
| | | buildExportParams(), |
| | | `应付对账单_${Date.now()}.xlsx` |
| | | ); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getSupplierList(); |
| | | getTableData(); |
| | | }); |
| | | </script> |
| | |
| | | |
| | | .text-danger { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | } |
| | | |
| | | .statement-header { |
| | |
| | | |
| | | .empty-tip { |
| | | margin-top: 30px; |
| | | } |
| | | |
| | | .text-primary { |
| | | color: #409eff; |
| | | } |
| | | </style> |