| | |
| | | </PIMTable> |
| | | </div> |
| | | |
| | | <el-dialog title="对账明细" v-model="detailDialogVisible" width="900px" append-to-body> |
| | | <FormDialog title="对账明细" v-model="detailDialogVisible" width="900px" @confirm="printDetail" @cancel="detailDialogVisible = false" operationType="detail"> |
| | | <div class="statement-header"> |
| | | <h3>{{ currentCustomer }} 应收对账单</h3> |
| | | <p>对账期间: {{ currentPeriod }}</p> |
| | |
| | | <el-table-column prop="remark" label="备注" show-overflow-tooltip /> |
| | | </el-table> |
| | | <template #footer> |
| | | <el-button @click="detailDialogVisible = false">关闭</el-button> |
| | | <el-button type="primary" @click="printDetail">打印</el-button> |
| | | <el-button @click="detailDialogVisible = false">关闭</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | </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="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> |
| | | </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="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"> |
| | | <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' : row.type === '收款' ? 'primary' : '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-primary' : row.type === '收款' ? 'text-success' : 'text-danger'">¥{{ 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-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> |
| | | </div> |
| | | </div> |
| | | |
| | | <div v-else-if="generateForm.customerId && !salesLoading" 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 } from "vue"; |
| | | import { ref, reactive, onMounted, computed } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import FormDialog from "@/components/Dialog/FormDialog.vue"; |
| | | |
| | | defineOptions({ |
| | | name: "应收对账", |
| | |
| | | const currentPeriod = ref(""); |
| | | const detailData = ref([]); |
| | | |
| | | const generateDialogVisible = ref(false); |
| | | const salesLoading = ref(false); |
| | | const salesData = ref([]); |
| | | const selectedSales = ref([]); |
| | | |
| | | const generateForm = reactive({ |
| | | customerId: "", |
| | | customerName: "", |
| | | period: "", |
| | | beginBalance: 0, |
| | | currentReceivable: 0, |
| | | currentReceipt: 0, |
| | | }); |
| | | |
| | | const canGenerate = computed(() => { |
| | | return generateForm.customerId && generateForm.period && selectedSales.value.length > 0; |
| | | }); |
| | | |
| | | const customerList = [ |
| | | { id: 1, name: "北京科技有限公司" }, |
| | | { id: 2, name: "上海贸易公司" }, |
| | |
| | | { 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 formatMoney = (value) => { |
| | | if (value === undefined || value === null) return "0.00"; |
| | |
| | | }; |
| | | |
| | | const generateStatement = () => { |
| | | ElMessage.success("对账单生成成功"); |
| | | generateForm.customerId = ""; |
| | | generateForm.customerName = ""; |
| | | generateForm.period = ""; |
| | | generateForm.beginBalance = 0; |
| | | generateForm.currentReceivable = 0; |
| | | generateForm.currentReceipt = 0; |
| | | salesData.value = []; |
| | | selectedSales.value = []; |
| | | generateDialogVisible.value = true; |
| | | }; |
| | | |
| | | const onCustomerChange = (customerId) => { |
| | | const customer = customerList.find(item => item.id === customerId); |
| | | if (customer) { |
| | | generateForm.customerName = customer.name; |
| | | } |
| | | loadSalesData(); |
| | | }; |
| | | |
| | | const onPeriodChange = () => { |
| | | loadSalesData(); |
| | | }; |
| | | |
| | | const loadSalesData = () => { |
| | | if (!generateForm.customerId || !generateForm.period) { |
| | | salesData.value = []; |
| | | return; |
| | | } |
| | | |
| | | salesLoading.value = true; |
| | | |
| | | 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: "客户回款" }, |
| | | ]; |
| | | |
| | | 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")}`; |
| | | }; |
| | | |
| | | const calculateSummary = () => { |
| | | let receivable = 0; |
| | | let receipt = 0; |
| | | |
| | | selectedSales.value.forEach(item => { |
| | | if (item.type === "出库") { |
| | | receivable += item.amount; |
| | | } else if (item.type === "退货") { |
| | | receivable -= item.amount; |
| | | } else if (item.type === "收款") { |
| | | receipt += item.amount; |
| | | } |
| | | }); |
| | | |
| | | generateForm.currentReceivable = receivable; |
| | | generateForm.currentReceipt = receipt; |
| | | }; |
| | | |
| | | const handleSalesSelectionChange = (selection) => { |
| | | selectedSales.value = selection; |
| | | calculateSummary(); |
| | | }; |
| | | |
| | | const confirmGenerate = () => { |
| | | const newId = mockData.length > 0 ? Math.max(...mockData.map(item => item.id)) + 1 : 1; |
| | | const customer = customerList[Math.floor(Math.random() * customerList.length)]; |
| | | const endBalance = calculateEndBalance(generateForm.beginBalance, generateForm.currentReceivable, generateForm.currentReceipt); |
| | | |
| | | mockData.unshift({ |
| | | id: newId, |
| | | statementCode: "DZ" + Date.now(), |
| | | customerId: customer.id, |
| | | customerName: customer.name, |
| | | period: "2024-03", |
| | | beginBalance: Math.floor(Math.random() * 10000), |
| | | currentReceivable: Math.floor(Math.random() * 20000), |
| | | currentReceipt: Math.floor(Math.random() * 15000), |
| | | endBalance: Math.floor(Math.random() * 20000), |
| | | customerId: generateForm.customerId, |
| | | customerName: generateForm.customerName, |
| | | period: generateForm.period, |
| | | beginBalance: generateForm.beginBalance, |
| | | currentReceivable: generateForm.currentReceivable, |
| | | currentReceipt: generateForm.currentReceipt, |
| | | endBalance, |
| | | }); |
| | | |
| | | generateDialogVisible.value = false; |
| | | ElMessage.success("对账单生成成功"); |
| | | getTableData(); |
| | | }; |
| | | |
| | | const viewDetail = (row) => { |
| | | currentCustomer.value = row.customerName; |
| | | currentPeriod.value = row.period; |
| | | |
| | | 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: row.beginBalance, remark: "期初余额" }, |
| | | { date: row.period + "-05", type: "出库", code: "CK2024001", debit: 5000, credit: 0, balance: row.beginBalance + 5000, remark: "" }, |
| | | { date: row.period + "-10", type: "收款", code: "SK2024001", debit: 0, credit: 3000, balance: row.beginBalance + 2000, remark: "" }, |
| | | { date: row.period + "-15", type: "出库", code: "CK2024002", debit: 8000, credit: 0, balance: row.beginBalance + 10000, remark: "" }, |
| | | { date: row.period + "-20", type: "退货", code: "TH2024001", debit: 0, credit: 2000, balance: row.beginBalance + 8000, remark: "" }, |
| | | { date: row.period + "-25", type: "收款", code: "SK2024002", credit: row.currentReceipt - 3000, balance: row.endBalance, remark: "" }, |
| | | { 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: "客户回款" }, |
| | | ]; |
| | | |
| | | detailDialogVisible.value = true; |
| | | }; |
| | | |
| | |
| | | margin: 0; |
| | | } |
| | | } |
| | | |
| | | .sales-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; |
| | | } |
| | | </style> |