<template>
|
<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>
|
</el-form-item>
|
<el-form-item label="对账期间:">
|
<el-date-picker v-model="filters.startMonth" type="month" placeholder="开始月份" value-format="YYYY-MM" style="width: 140px;" />
|
<span style="margin: 0 10px;">至</span>
|
<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 @click="resetFilters">重置</el-button>
|
</el-form-item>
|
</el-form>
|
<div class="table_list">
|
<div class="actions">
|
<div>
|
<el-button type="primary" @click="generateStatement" icon="Document">生成对账单</el-button>
|
</div>
|
<div>
|
<el-button @click="handleOut" icon="Download">导出对账单</el-button>
|
</div>
|
</div>
|
<PIMTable
|
rowKey="id"
|
:column="columns"
|
:tableData="dataList"
|
:page="{
|
current: pagination.currentPage,
|
size: pagination.pageSize,
|
total: pagination.total,
|
}"
|
@pagination="changePage"
|
>
|
<template #beginBalance="{ row }">
|
<span :class="row.beginBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.beginBalance) }}</span>
|
</template>
|
<template #currentPayable="{ row }">
|
<span class="text-danger">¥{{ formatMoney(row.currentPayable) }}</span>
|
</template>
|
<template #currentPayment="{ row }">
|
<span class="text-success">¥{{ formatMoney(row.currentPayment) }}</span>
|
</template>
|
<template #endBalance="{ row }">
|
<span :class="row.endBalance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.endBalance) }}</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>
|
</template>
|
</PIMTable>
|
</div>
|
|
<FormDialog title="对账明细" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail">
|
<div class="statement-header">
|
<h3>{{ currentSupplier }} 应付对账单</h3>
|
<p>对账期间: {{ currentPeriod }}</p>
|
</div>
|
<el-table :data="detailData" border style="width: 100%">
|
<el-table-column prop="date" label="日期" width="120" />
|
<el-table-column prop="type" label="类型" width="100">
|
<template #default="{ row }">
|
<el-tag :type="row.type === '入库' ? 'success' : row.type === '退货' ? 'danger' : 'primary'">{{ row.type }}</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column prop="code" label="单据编号" width="150" />
|
<el-table-column prop="debit" label="借方(付款)" width="120">
|
<template #default="{ row }">
|
<span v-if="row.debit > 0" class="text-success">¥{{ formatMoney(row.debit) }}</span>
|
<span v-else>-</span>
|
</template>
|
</el-table-column>
|
<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>-</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="balance" label="余额" width="120">
|
<template #default="{ row }">
|
<span :class="row.balance >= 0 ? 'text-success' : 'text-danger'">¥{{ formatMoney(row.balance) }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column prop="remark" label="备注" show-overflow-tooltip />
|
</el-table>
|
<template #footer>
|
<el-button type="primary" @click="printDetail">打印</el-button>
|
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
</template>
|
</FormDialog>
|
|
<FormDialog title="生成对账单" v-model="generateDialogVisible" width="1000px" @confirm="confirmGenerate" @cancel="generateDialogVisible = false">
|
<el-form :model="generateForm" label-width="100px">
|
<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>
|
</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>
|
</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">
|
<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="type" label="类型" width="100">
|
<template #default="{ row }">
|
<el-tag :type="row.type === '入库' ? 'success' : 'danger'">{{ row.type }}</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>
|
</template>
|
</el-table-column>
|
<el-table-column prop="remark" label="备注" />
|
</el-table>
|
|
<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>
|
</div>
|
</div>
|
|
<div v-else-if="generateForm.supplierId && !purchaseLoading" class="empty-tip">
|
<el-empty description="该供应商本月暂无采购数据" />
|
</div>
|
|
<template #footer>
|
<el-button type="primary" @click="confirmGenerate" :disabled="!canGenerate">确认生成</el-button>
|
<el-button @click="generateDialogVisible = false">取消</el-button>
|
</template>
|
</FormDialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted, computed } from "vue";
|
import { ElMessage } from "element-plus";
|
import FormDialog from "@/components/Dialog/FormDialog.vue";
|
|
defineOptions({
|
name: "应付对账",
|
});
|
|
const filters = reactive({
|
supplierId: "",
|
startMonth: "",
|
endMonth: "",
|
});
|
|
const pagination = reactive({
|
currentPage: 1,
|
pageSize: 10,
|
total: 0,
|
});
|
|
const columns = [
|
{ label: "对账单号", prop: "statementCode", 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" },
|
];
|
|
const dataList = ref([]);
|
const detailDialogVisible = ref(false);
|
const currentSupplier = ref("");
|
const currentPeriod = ref("");
|
const detailData = ref([]);
|
|
const generateDialogVisible = ref(false);
|
const purchaseLoading = ref(false);
|
const purchaseData = ref([]);
|
const selectedPurchases = ref([]);
|
|
const generateForm = reactive({
|
supplierId: "",
|
supplierName: "",
|
period: "",
|
beginBalance: 0,
|
currentPayable: 0,
|
currentPayment: 0,
|
});
|
|
const canGenerate = computed(() => {
|
return generateForm.supplierId && generateForm.period && selectedPurchases.value.length > 0;
|
});
|
|
const supplierList = [
|
{ id: 1, name: "北京原材料供应商" },
|
{ id: 2, name: "上海电子元器件公司" },
|
{ id: 3, name: "广州包装材料厂" },
|
{ id: 4, name: "深圳五金配件公司" },
|
];
|
|
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 formatMoney = (value) => {
|
if (value === undefined || value === null) return "0.00";
|
return Number(value).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
};
|
|
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);
|
};
|
|
const resetFilters = () => {
|
filters.supplierId = "";
|
filters.startMonth = "";
|
filters.endMonth = "";
|
pagination.currentPage = 1;
|
getTableData();
|
};
|
|
const changePage = ({ current, size }) => {
|
pagination.currentPage = current;
|
pagination.pageSize = size;
|
getTableData();
|
};
|
|
const generateStatement = () => {
|
generateForm.supplierId = "";
|
generateForm.supplierName = "";
|
generateForm.period = "";
|
generateForm.beginBalance = 0;
|
generateForm.currentPayable = 0;
|
generateForm.currentPayment = 0;
|
purchaseData.value = [];
|
selectedPurchases.value = [];
|
generateDialogVisible.value = true;
|
};
|
|
const onSupplierChange = (supplierId) => {
|
const supplier = supplierList.find(item => item.id === supplierId);
|
if (supplier) {
|
generateForm.supplierName = supplier.name;
|
}
|
loadPurchaseData();
|
};
|
|
const onPeriodChange = () => {
|
loadPurchaseData();
|
};
|
|
const loadPurchaseData = () => {
|
if (!generateForm.supplierId || !generateForm.period) {
|
purchaseData.value = [];
|
return;
|
}
|
|
purchaseLoading.value = true;
|
|
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: "支付货款" },
|
];
|
|
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")}`;
|
};
|
|
const calculateSummary = () => {
|
let payable = 0;
|
let payment = 0;
|
|
selectedPurchases.value.forEach(item => {
|
if (item.type === "入库") {
|
payable += item.amount;
|
} else if (item.type === "退货") {
|
payable -= item.amount;
|
} else if (item.type === "付款") {
|
payment += item.amount;
|
}
|
});
|
|
generateForm.currentPayable = payable;
|
generateForm.currentPayment = payment;
|
};
|
|
const handlePurchaseSelectionChange = (selection) => {
|
selectedPurchases.value = selection;
|
calculateSummary();
|
};
|
|
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);
|
|
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,
|
});
|
|
generateDialogVisible.value = false;
|
ElMessage.success("对账单生成成功");
|
getTableData();
|
};
|
|
const viewDetail = (row) => {
|
currentSupplier.value = row.supplierName;
|
currentPeriod.value = row.period;
|
|
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: "支付货款" },
|
];
|
|
detailDialogVisible.value = true;
|
};
|
|
const printStatement = (row) => {
|
ElMessage.info(`打印对账单: ${row.statementCode}`);
|
};
|
|
const printDetail = () => {
|
ElMessage.info("打印明细");
|
};
|
|
const handleOut = () => {
|
ElMessage.success("导出成功");
|
};
|
|
onMounted(() => {
|
getTableData();
|
});
|
</script>
|
|
<style lang="scss" scoped>
|
.actions {
|
display: flex;
|
justify-content: space-between;
|
margin-bottom: 15px;
|
}
|
|
.text-success {
|
color: #67c23a;
|
}
|
|
.text-danger {
|
color: #f56c6c;
|
}
|
|
.statement-header {
|
text-align: center;
|
margin-bottom: 20px;
|
h3 {
|
margin: 0 0 10px 0;
|
}
|
p {
|
color: #909399;
|
margin: 0;
|
}
|
}
|
|
.purchase-section {
|
margin-top: 20px;
|
|
.section-title {
|
font-size: 16px;
|
font-weight: bold;
|
margin-bottom: 15px;
|
padding-left: 10px;
|
border-left: 4px solid #409eff;
|
}
|
}
|
|
.summary-row {
|
display: flex;
|
justify-content: space-around;
|
padding: 15px;
|
background-color: #f5f7fa;
|
border-radius: 4px;
|
margin-top: 15px;
|
|
span {
|
font-size: 14px;
|
|
strong {
|
font-size: 16px;
|
margin-left: 5px;
|
}
|
}
|
}
|
|
.empty-tip {
|
margin-top: 30px;
|
}
|
|
.text-primary {
|
color: #409eff;
|
}
|
</style>
|