概念
计算公式
月供金额 = 月供本金 + 月供利息
月供利息 =(总贷款额 - 已还本金)* 月利率
等额本金
等额本金是每个月偿还的本金相同,利息逐月减少,总贷款利息较少,起始月供较高,每月递减。
月供本金 = 总贷款额/月供期数
月供金额 = (总贷款额/月供期数) + 月供利息
等额本息
每个月偿还的月供金额相同,月供本金随利息的减少逐月递增,总贷款利息较高,月供固定不变,相对月供较低。
月供金额 = 总贷款额 * 月利率 * ( 1+月利率 ) ^ 还款期数 / ( ( 1+月利率 ) ^ 还款期数 -1 )
月供本金 = 月供金额 - 月供利息
代码
/*
* @Description: 房贷计算器
* @Date: 2025-03-27 20:00:07
*/
import React, { useState, useEffect } from 'react';
import { Button, Form, InputNumber, Table, Tabs } from 'antd';
const FormItem = Form.Item;
const { TabPane } = Tabs;
const MortgageCalculator = () => {
const [form] = Form.useForm();
const [activeKey, setActiveKey] = useState('1');
/** 等额本息表格 */
const [interestData, setInterestData] = useState<any>([]);
/** 等额本金表格 */
const [principalData, setPrincipalData] = useState<any>([]);
useEffect(() => {
if (activeKey === '1') {
equalPrincipal();
} else {
equalInterest();
}
}, [activeKey])
const calculateLoans = () => {
if (activeKey === '1') {
equalPrincipal();
} else {
equalInterest();
}
}
/** 等额本息 */
const equalInterest = () => {
form.validateFields();
const formValue = form.getFieldsValue();
// 贷款期数
const month = Number(formValue.years || 0) * 12;
// 公积金总金额
const providentFundAmount = Number(formValue.providentFundAmount || 0) * 10000;
// 商业贷总金额
const commercialLoanAmount = Number(formValue.commercialLoanAmount || 0) * 10000;
// 公积金月利率
const monthProvidentFundRate = Number(formValue.providentFundRate || 0) / 100 / 12;
// 商业贷月利率
const monthCommercialLoanRate = Number(formValue.commercialLoanRate || 0) / 100 / 12;
// 每月还款额=总贷款额 * 月利率 * ( 1+月利率 ) ^ 还款期数 / ( ( 1+月利率 ) ^ 还款期数 -1 )
let monthProvidentAmount = 0;
if (providentFundAmount) {
monthProvidentAmount = (providentFundAmount * monthProvidentFundRate * (Math.pow(1 + monthProvidentFundRate, month))) / (Math.pow(1 + monthProvidentFundRate, month) - 1);
}
let monthCommercialAmount = 0;
if (commercialLoanAmount) {
monthCommercialAmount = (commercialLoanAmount * monthCommercialLoanRate * (Math.pow(1 + monthCommercialLoanRate, month))) / (Math.pow(1 + monthCommercialLoanRate, month) - 1);
}
// 每月月供金额
const monthAmount = monthProvidentAmount + monthCommercialAmount;
// 已还公积金本金
let repaymentProvidentPrincipal = 0;
// 已还商贷本金
let repaymentCommercialPrincipal = 0;
// 已还公积金利息
let repaymentProvidentInterest = 0;
// 已还商贷利息
let repaymentCommercialInterest = 0;
const newInterestData: Record<string, any> = [];
for (let i = 0; i < month; i++) {
// 公积金月利息
const monthProvidentInterest = getMonthInterest(providentFundAmount, repaymentProvidentPrincipal, Number(formValue.providentFundRate || 0));
// 商业贷月利息
const monthCommercialInterest = getMonthInterest(commercialLoanAmount, repaymentCommercialPrincipal, Number(formValue.commercialLoanRate || 0));
// 月利息
const monthInterest = monthProvidentInterest + monthCommercialInterest;
// 已还本金 = 商业贷已还本金 + 公积金已还本金
const repaymentAmount = repaymentProvidentPrincipal + repaymentCommercialPrincipal;
newInterestData.push({
period: i + 1,
monthAmount: monthAmount,
remainingAmount: providentFundAmount + commercialLoanAmount - repaymentAmount,
repaymentAmount: repaymentAmount,
repaymentInterest: repaymentProvidentInterest + repaymentCommercialInterest,
monthPrincipal: monthAmount - monthInterest,
monthInterest: monthInterest
});
repaymentProvidentPrincipal += (monthProvidentAmount - monthProvidentInterest);
repaymentCommercialPrincipal += monthCommercialAmount - monthCommercialInterest;
repaymentProvidentInterest += monthProvidentInterest;
repaymentCommercialInterest += monthCommercialInterest;
}
setInterestData(newInterestData);
}
/** 等额本金 */
const equalPrincipal = () => {
form.validateFields();
const formValue = form.getFieldsValue();
// 贷款期数
const month = Number(formValue.years || 0) * 12;
// 公积金总金额
const providentFundAmount = Number(formValue.providentFundAmount || 0) * 10000;
// 商业贷总金额
const commercialLoanAmount = Number(formValue.commercialLoanAmount || 0) * 10000
// 公积金贷款月供本金
const monthProvidentPrincipal = providentFundAmount / month;
// 商业贷款月供本金
const monthCommercialLoanPrincipal = commercialLoanAmount / month;
// 月供本金
const monthPrincipal = monthProvidentPrincipal + monthCommercialLoanPrincipal;
// 已还公积金本金
let repaymentProvidentPrincipal = 0;
// 已还商贷本金
let repaymentCommercialPrincipal = 0;
// 已还公积金利息
let repaymentProvidentInterest = 0;
// 已还商贷利息
let repaymentCommercialInterest = 0;
const newPrincipalData: Record<string, any> = [];
for (let i = 0; i < month; i++) {
// 公积金月利息
const monthProvidentInterest = getMonthInterest(providentFundAmount, repaymentProvidentPrincipal, Number(formValue.providentFundRate || 0));
// 商业贷月利息
const monthCommercialInterest = getMonthInterest(commercialLoanAmount, repaymentCommercialPrincipal, Number(formValue.commercialLoanRate || 0));
// 月利息
const monthInterest = monthProvidentInterest + monthCommercialInterest;
// 月供金额 = 月利息 + 月供本金
const monthAmount = monthInterest + monthPrincipal;
// 已还本金 = 商业贷已还本金 + 公积金已还本金
const repaymentAmount = repaymentProvidentPrincipal + repaymentCommercialPrincipal;
newPrincipalData.push({
period: i + 1,
monthAmount: monthAmount,
remainingAmount: providentFundAmount + commercialLoanAmount - repaymentAmount,
repaymentAmount: repaymentAmount,
repaymentInterest: repaymentProvidentInterest + repaymentCommercialInterest,
monthPrincipal: monthPrincipal,
monthInterest: monthInterest
});
repaymentProvidentPrincipal += monthProvidentPrincipal;
repaymentCommercialPrincipal += monthCommercialLoanPrincipal;
repaymentProvidentInterest += monthProvidentInterest;
repaymentCommercialInterest += monthCommercialInterest;
}
setPrincipalData(newPrincipalData);
}
/** 每月应还利息 */
const getMonthInterest = (
amount: number,
repaymentPrincipal: number,
rate: number,
) => {
// 月利率
const monthRate = rate / 100 / 12;
// 每月应还利息 = (贷款本金-已还本金)*月利率
return (amount - repaymentPrincipal) * monthRate;
}
/** 金额格式化 */
const formatMoney = (value: number) => {
if (value > 10000) {
return (value / 10000).toFixed(2) + "万元";
}
return value.toFixed(2) + "元";
}
const columns = [
{
title: '期数',
dataIndex: 'period',
},
{
title: '月供总金额',
dataIndex: 'monthAmount',
render: (text, record) => {
return formatMoney(text);
},
},
{
title: '月供本金',
dataIndex: 'monthPrincipal',
render: (text, record) => {
return formatMoney(text);
},
},
{
title: '月供利息',
dataIndex: 'monthInterest',
render: (text, record) => {
return formatMoney(text);
},
},
{
title: '剩余本金',
dataIndex: 'remainingAmount',
render: (text, record) => {
return formatMoney(text);
},
},
{
title: '已还本金',
dataIndex: 'repaymentAmount',
render: (text, record) => {
return formatMoney(text);
},
},
{
title: '已还利息',
dataIndex: 'repaymentInterest',
render: (text, record) => {
return formatMoney(text);
},
},
]
return (
<div>
<Form layout="vertical" form={form} initialValues={{
years: 30,
providentFundAmount: 60,
providentFundRate: 2.85,
commercialLoanAmount: 16,
commercialLoanRate: 3.0,
}}
>
<FormItem label="贷款年限" name="years" required rules={[{ required: true, message: '请输入贷款年限' }]}>
<InputNumber addonAfter="年" style={{ width: 300 }} />
</FormItem>
<FormItem label="公积金贷款金额" name="providentFundAmount">
<InputNumber addonAfter="万" style={{ width: 300 }} />
</FormItem>
<FormItem label="公积金贷款年利率" name="providentFundRate" >
<InputNumber addonAfter="%" style={{ width: 300 }} />
</FormItem>
<FormItem label="商业贷款金额" name="commercialLoanAmount">
<InputNumber addonAfter="万" style={{ width: 300 }} />
</FormItem>
<FormItem label="商业贷款年利率" name="commercialLoanRate" >
<InputNumber addonAfter="%" style={{ width: 300 }} />
</FormItem>
<FormItem >
<Button type="primary" onClick={calculateLoans}>计算</Button>
</FormItem>
</Form>
<Tabs onChange={(activeKey: string) => setActiveKey(activeKey)}>
<TabPane tab="等额本金" key="1">
<div>概念:每个月偿还的本金不变,利息逐月减少,总贷款利息较少,起始月供较高,每月递减。</div>
<div style={{ display: 'flex' }}>
<div style={{ marginRight: 24 }}>贷款总额度:{formatMoney(principalData[0]?.remainingAmount || 0)}</div>
<div>贷款总利息:{formatMoney(principalData[principalData.length - 1]?.repaymentInterest || 0)}</div>
</div>
<Table columns={columns} pagination={false} dataSource={principalData} />
</TabPane>
<TabPane tab="等额本息" key="2">
<div>概念:每个月偿还的月供固定,月供本金随利息的减少逐月递增,总贷款利息较高,月供固定不变,相对每月偿还基数较低。</div>
<div style={{ display: 'flex' }}>
<div style={{ marginRight: 24 }}>贷款总额度:{formatMoney(interestData[0]?.remainingAmount || 0)}</div>
<div>贷款总利息:{formatMoney(interestData[interestData.length - 1]?.repaymentInterest || 0)}</div>
</div>
<Table columns={columns} pagination={false} dataSource={interestData} />
</TabPane>
</Tabs>
</div>
)
}
export default MortgageCalculator;
效果图