[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能贷款计算器 | Loan Calculator</title>
<!-- 引入优质字体 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
/* 配色系统 */
--primary: #4f46e5;
--primary-hover: #4338ca;
--secondary: #ec4899;
--accent: #06b6d4;
--bg-gradient: linear-gradient(135deg, #f0f4ff 0%, #eef2f3 100%);
--card-bg: rgba(255, 255, 255, 0.95);
--text-main: #1e293b;
--text-sub: #64748b;
--border-color: #e2e8f0;
--success-bg: #dcfce7;
--success-text: #166534;
--shadow-sm: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
--shadow-lg: 0 20px 25px -5px rgba(0, 0, 0, 0.05), 0 10px 10px -5px rgba(0, 0, 0, 0.02);
--radius-xl: 24px;
--radius-md: 12px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
outline: none;
}
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background: var(--bg-gradient);
background-attachment: fixed;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: var(--text-main);
line-height: 1.6;
}
/* 背景装饰球 */
.bg-blob {
position: fixed;
border-radius: 50%;
filter: blur(80px);
z-index: -1;
opacity: 0.6;
animation: float 10s infinite ease-in-out;
}
.blob-1 { top: -10%; left: -10%; width: 500px; height: 500px; background: #c7d2fe; }
.blob-2 { bottom: -10%; right: -10%; width: 400px; height: 400px; background: #fbcfe8; animation-delay: -5s; }
@keyframes float {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(20px, 30px); }
}
.container {
width: 100%;
max-width: 800px;
background: var(--card-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
border: 1px solid rgba(255, 255, 255, 0.8);
overflow: hidden;
transition: transform 0.3s ease;
}
header {
padding: 30px 40px 20px;
text-align: center;
border-bottom: 1px solid var(--border-color);
background: linear-gradient(to bottom, rgba(255,255,255,0.5), transparent);
}
h1 {
font-size: 1.8rem;
font-weight: 800;
color: var(--text-main);
margin-bottom: 8px;
letter-spacing: -0.5px;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
h1 span {
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
color: var(--text-sub);
font-size: 0.95rem;
font-weight: 500;
}
.content {
padding: 30px 40px 40px;
}
/* 输入区域网格 */
.input-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 24px;
margin-bottom: 30px;
}
.input-group {
position: relative;
}
.input-group label {
display: block;
font-size: 0.85rem;
font-weight: 600;
color: var(--text-sub);
margin-bottom: 8px;
padding-left: 4px;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
}
.input-icon {
position: absolute;
left: 16px;
color: var(--text-sub);
font-size: 1.1rem;
pointer-events: none;
transition: color 0.2s;
}
input[type="number"] {
width: 100%;
padding: 14px 16px 14px 44px;
font-family: 'JetBrains Mono', monospace;
font-size: 1rem;
font-weight: 500;
color: var(--text-main);
background: #f8fafc;
border: 2px solid transparent;
border-radius: var(--radius-md);
transition: all 0.2s ease;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.02);
}
input[type="number"]:focus {
background: #fff;
border-color: var(--primary);
box-shadow: 0 0 0 4px rgba(79, 70, 229, 0.1);
}
input[type="number"]:focus + .input-icon,
.input-wrapper:focus-within .input-icon {
color: var(--primary);
}
/* 按钮样式 */
.btn-calc {
width: 100%;
padding: 16px;
border: none;
border-radius: var(--radius-md);
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 100%);
color: white;
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
box-shadow: 0 10px 20px -5px rgba(79, 70, 229, 0.4);
transition: all 0.2s ease;
position: relative;
overflow: hidden;
}
.btn-calc:hover {
transform: translateY(-2px);
box-shadow: 0 15px 25px -5px rgba(79, 70, 229, 0.5);
}
.btn-calc:active {
transform: translateY(0);
box-shadow: 0 5px 10px -5px rgba(79, 70, 229, 0.4);
}
.btn-calc::after {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background: linear-gradient(rgba(255,255,255,0.2), transparent);
opacity: 0;
transition: opacity 0.2s;
}
.btn-calc:hover::after { opacity: 1; }
/* 结果展示区 */
.results-container {
margin-top: 35px;
display: grid;
grid-template-columns: 1fr;
gap: 20px;
opacity: 0;
transform: translateY(10px);
transition: all 0.4s ease;
}
.results-container.show {
opacity: 1;
transform: translateY(0);
}
.result-card {
background: #fff;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
padding: 20px;
box-shadow: var(--shadow-sm);
}
.result-header {
font-size: 1.1rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px dashed var(--border-color);
display: flex;
align-items: center;
gap: 8px;
}
.result-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
}
.data-item {
display: flex;
flex-direction: column;
}
.data-label {
font-size: 0.8rem;
color: var(--text-sub);
margin-bottom: 4px;
}
.data-value {
font-family: 'JetBrains Mono', monospace;
font-size: 1.05rem;
font-weight: 600;
color: var(--text-main);
}
.data-value.highlight {
color: var(--primary);
font-size: 1.2rem;
}
/* 推荐徽章 */
.recommend-badge {
background: var(--success-bg);
color: var(--success-text);
padding: 16px 20px;
border-radius: var(--radius-md);
border: 1px solid #bbf7d0;
display: flex;
align-items: center;
gap: 12px;
font-weight: 600;
margin-top: 20px;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(22, 101, 52, 0.2); }
70% { box-shadow: 0 0 0 10px rgba(22, 101, 52, 0); }
100% { box-shadow: 0 0 0 0 rgba(22, 101, 52, 0); }
}
.badge-icon {
font-size: 1.4rem;
}
/* 响应式调整 */
@media (max-width: 600px) {
.content { padding: 20px; }
header { padding: 20px 20px 15px; }
h1 { font-size: 1.5rem; }
.result-grid { grid-template-columns: 1fr; }
.input-grid { grid-template-columns: 1fr; }
}
/* 滚动条美化 */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: #f1f5f9; border-radius: 4px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
</style>
</head>
<body>
<!-- 背景装饰 -->
<div class="bg-blob blob-1"></div>
<div class="bg-blob blob-2"></div>
<div class="container">
<header>
<h1><span>💰</span> 智能贷款计算器</h1>
<div class="subtitle">等额本息 · 等额本金 · 先息后本 · 一键对比最优方案</div>
</header>
<div class="content">
<!-- 输入区域 -->
<div class="input-grid">
<div class="input-group">
<label for="amount">贷款金额 (元)</label>
<div class="input-wrapper">
<input type="number" id="amount" value="100000" step="1000" min="1">
<span class="input-icon">🏦</span>
</div>
</div>
<div class="input-group">
<label for="rate">年利率 (%)</label>
<div class="input-wrapper">
<input type="number" id="rate" value="4.5" step="0.1" min="0.01" max="100">
<span class="input-icon">📈</span>
</div>
</div>
<div class="input-group">
<label for="years">贷款年限 (年)</label>
<div class="input-wrapper">
<input type="number" id="years" value="20" step="1" min="1" max="50">
<span class="input-icon">📅</span>
</div>
</div>
</div>
<button class="btn-calc" id="calculateBtn">
<span>⚡ 开始计算并推荐</span>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
</button>
<!-- 结果区域 -->
<div class="results-container" id="resultsArea">
<!-- 推荐徽章 -->
<div class="recommend-badge" id="recommendDisplay" style="display: none;">
<span class="badge-icon">🏆</span>
<span id="recommendText"></span>
</div>
<div class="result-grid">
<!-- 等额本息 -->
<div class="result-card">
<div class="result-header">
<span>🔄</span> 等额本息
</div>
<div class="data-item" style="margin-bottom: 15px;">
<span class="data-label">每月还款</span>
<span class="data-value highlight" id="eqpi-monthly">-</span>
</div>
<div class="result-grid" style="gap: 10px;">
<div class="data-item">
<span class="data-label">总利息</span>
<span class="data-value" id="eqpi-interest">-</span>
</div>
<div class="data-item">
<span class="data-label">总还款</span>
<span class="data-value" id="eqpi-total">-</span>
</div>
</div>
</div>
<!-- 等额本金 -->
<div class="result-card">
<div class="result-header">
<span>📉</span> 等额本金
</div>
<div class="data-item" style="margin-bottom: 15px;">
<span class="data-label">首月还款</span>
<span class="data-value highlight" id="eqp-first">-</span>
</div>
<div class="result-grid" style="gap: 10px;">
<div class="data-item">
<span class="data-label">每月递减</span>
<span class="data-value" id="eqp-decrease">-</span>
</div>
<div class="data-item">
<span class="data-label">总利息</span>
<span class="data-value" id="eqp-interest">-</span>
</div>
</div>
</div>
<!-- 先息后本 -->
<div class="result-card">
<div class="result-header">
<span>💸</span> 先息后本
</div>
<div class="data-item" style="margin-bottom: 15px;">
<span class="data-label">每月只还息</span>
<span class="data-value highlight" id="if-monthly">-</span>
</div>
<div class="result-grid" style="gap: 10px;">
<div class="data-item">
<span class="data-label">到期还本</span>
<span class="data-value" id="if-principal">-</span>
</div>
<div class="data-item">
<span class="data-label">总利息</span>
<span class="data-value" id="if-interest">-</span>
</div>
</div>
</div>
</div>
<div style="text-align: center; margin-top: 20px; font-size: 0.85rem; color: var(--text-sub);">
💡 数据仅供参考,具体以银行合同为准 · 每月递减精确到小数点后四位
</div>
</div>
</div>
</div>
<script>
(function() {
const amountInput = document.getElementById('amount');
const rateInput = document.getElementById('rate');
const yearsInput = document.getElementById('years');
const calcBtn = document.getElementById('calculateBtn');
const resultsArea = document.getElementById('resultsArea');
const recommendDisplay = document.getElementById('recommendDisplay');
const recommendText = document.getElementById('recommendText');
// 格式化货币
const fmtMoney = (num) => num.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const fmtNum = (num, digits = 2) => Number(num).toFixed(digits);
function validateInputs() {
let amount = parseFloat(amountInput.value);
let rate = parseFloat(rateInput.value);
let years = parseInt(yearsInput.value, 10);
if (isNaN(amount) || amount <= 0) { alert('❌ 贷款金额必须大于 0!'); return null; }
if (isNaN(rate) || rate <= 0 || rate > 100) { alert('❌ 年利率必须在 0~100 之间!'); return null; }
if (isNaN(years) || years <= 0 || years > 50) { alert('❌ 贷款年限必须在 1~50 年之间!'); return null; }
return { amount, rate, years };
}
function calcEqualPrincipalInterest(principal, annualRate, years) {
const monthlyRate = annualRate / 12 / 100;
const months = years * 12;
if (monthlyRate === 0) return { monthlyPayment: principal / months, totalInterest: 0, totalPayment: principal };
const factor = Math.pow(1 + monthlyRate, months);
const monthlyPayment = principal * monthlyRate * factor / (factor - 1);
const totalInterest = monthlyPayment * months - principal;
return { monthlyPayment, totalInterest, totalPayment: principal + totalInterest };
}
function calcEqualPrincipal(principal, annualRate, years) {
const monthlyRate = annualRate / 12 / 100;
const months = years * 12;
const monthlyPrincipal = principal / months;
const totalInterest = (months + 1) * principal * monthlyRate / 2;
const firstMonthPayment = monthlyPrincipal + principal * monthlyRate;
const monthlyDecrease = monthlyPrincipal * monthlyRate;
return { firstMonthPayment, monthlyDecrease, totalInterest, totalPayment: principal + totalInterest };
}
function calcInterestFirst(principal, annualRate, years) {
const monthlyRate = annualRate / 12 / 100;
const monthlyInterest = principal * monthlyRate;
const totalInterest = principal * annualRate * years / 100;
return { monthlyInterest, finalPrincipal: principal, totalInterest, totalPayment: principal + totalInterest };
}
function updateUI(eqPI, eqP, interestFirst) {
// 填充数据
document.getElementById('eqpi-monthly').textContent = fmtMoney(eqPI.monthlyPayment) + ' 元';
document.getElementById('eqpi-interest').textContent = fmtMoney(eqPI.totalInterest) + ' 元';
document.getElementById('eqpi-total').textContent = fmtMoney(eqPI.totalPayment) + ' 元';
document.getElementById('eqp-first').textContent = fmtMoney(eqP.firstMonthPayment) + ' 元';
document.getElementById('eqp-decrease').textContent = fmtNum(eqP.monthlyDecrease, 4) + ' 元';
document.getElementById('eqp-interest').textContent = fmtMoney(eqP.totalInterest) + ' 元';
document.getElementById('if-monthly').textContent = fmtMoney(interestFirst.monthlyInterest) + ' 元';
document.getElementById('if-principal').textContent = fmtMoney(interestFirst.finalPrincipal) + ' 元';
document.getElementById('if-interest').textContent = fmtMoney(interestFirst.totalInterest) + ' 元';
// 推荐逻辑
const plans = [
{ name: '等额本金', interest: eqP.totalInterest },
{ name: '等额本息', interest: eqPI.totalInterest },
{ name: '先息后本', interest: interestFirst.totalInterest }
];
plans.sort((a, b) => a.interest - b.interest);
const best = plans[0];
const second = plans[1];
const diff = second.interest - best.interest;
recommendText.innerHTML = `最优方案:<strong>${best.name}</strong> · 总利息仅需 ${fmtMoney(best.interest)} 元 <br><span style="font-size:0.9em; opacity:0.9">比第二名的 ${second.name} 节省 ${fmtMoney(diff)} 元</span>`;
recommendDisplay.style.display = 'flex';
// 动画显示
resultsArea.classList.add('show');
}
function calculate() {
const validated = validateInputs();
if (!validated) return;
const { amount, rate, years } = validated;
const eqPI = calcEqualPrincipalInterest(amount, rate, years);
const eqP = calcEqualPrincipal(amount, rate, years);
const interestFirst = calcInterestFirst(amount, rate, years);
updateUI(eqPI, eqP, interestFirst);
// 平滑滚动到结果区
setTimeout(() => {
resultsArea.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 100);
}
calcBtn.addEventListener('click', calculate);
[amountInput, rateInput, yearsInput].forEach(input => {
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') { e.preventDefault(); calculate(); }
});
});
// 初始化运行一次
window.addEventListener('load', calculate);
})();
</script>
</body>
</html>