• 纯JS实现房贷利率报表对比


    最近朋友买房,想计算下自己的房贷的还款情况,自己正好周末没事,从网上找来点代码修改,也算是对自己技术的巩固吧。

    目前这个还只是个初级版本,暂时可以在PC上正常访问,将来会一步一步的把相继功能都加上的,将要完成的功能:

    1. 可以在Android手机上访问的界面对齐、美化等。
    2. 图表的优化。
    3. 等额本金法和等额本息法的实现。

    以上功能暂列这么多,以后有想到的在慢慢往上加;也希望大家多多的提出你的意见。

    以后会把每次都更新版本都添加到文章的后面,方便历史版本的追溯。样图:


    版本一:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8"/>
    <title>JS计算器,贷款利率计算器</title>
    <!--参考出处:http://www.codefans.net/jscss/code/3613.shtml-->
    
    <style> /* 这是一个CSS样式表:定义了程序输出的样式 */
    body{ font-size:12px;}
    #payment { text-decoration: underline; } /* 定义 id="payment" 的元素样式 */
    #graph { border: solid black 1px; } /* 图表有一个1像素的边框 */
    th, td {vertical-align: top; } /* 表格单元格对齐方式为顶端对齐 */
    table{
        border-collapse: collapse;
        border: none;
    }
    td, th{
        border: solid #000 1px;
    }
    .output { font-weight: bold; } /* 计算结果定义为粗体 */
    .btn{
        font-weight: 700;
        line-height: 30px;
         100px;
        height: 35px;
    }
    .help
    {
        float:right;
        cursor:pointer;
        line-height: 10px;
        margin-top: -4px;
        margin-right: -4px;
    }
    .nr {
        color: #666;
        font-size: 12px;
        padding: 10px 0 5px
    }
    .tsk {
        background: #f8f8f8;
        border: 1px solid #CCC;
        box-shadow: 0 1px 5px #DDD;
        line-height: 20px;
        padding: 10px;
        position: absolute;
        display: none;
        z-index: 10000
    }
    
    .circle {
         15px;
        height: 15px;
        font-weight: 700;
        text-indent:4px;
    
            cursor:pointer;
        float:left;
        line-height:16px;
        background: #5A8BDC;
        -moz-border-radius: 50px;
        -webkit-border-radius: 50px;
        border-radius: 50px;
    }
    </style>
    
    <script>
        function $(id){ return document.getElementById(id); }
        function helpClick(obj)
        {
            $(obj.id+"_help").style.display = "block";
        }
        
        function hidHelp()
        {
            $("debxhk_help").style.display = "none";
            $("debjhk_help").style.display = "none";
        }
        
    </script>
    
    </head>
    <body>
    
      <table> 
       <tbody>
        <tr>
         <th colspan=2>输入数据:</th>      
        </tr> 
        <tr>
         <td width=100px>
          <!--Amount of the loan-->贷款总额:</td> 
         <td><input id="amount"  value="500000" /><span style="margin-left:-18px">元</span></td> 
        </tr> 
        <tr>
         <td>
          <!--Annual interest-->年利息:</td> 
         <td><input id="apr"  value="5.65" /><span style="margin-left:-14px">%</span></td>
        </tr> 
         <td>
          利率折扣:</td> 
         <td>
             <select id=llzk style="156px;">
                  <option value="0.7" >最新基准利率7折</option> 
                   <option value="0.8" >最新基准利率8折</option> 
                   <option value="0.83">最新基准利率8.3折</option> 
                   <option value="0.85">最新基准利率8.5折</option> 
                   <option value="0.88">最新基准利率8.8折</option> 
                   <option value="0.9" >最新基准利率9折</option> 
                   <option value="0.95">最新基准利率9.5折</option> 
                   <option value="1.0" selected=true>最新基准利率</option> 
                   <option value="1.05">最新基准利率1.05倍</option> 
                   <option value="1.1" >最新基准利率1.1倍</option> 
                   <option value="1.2" >最新基准利率1.2倍</option> 
                   <option value="1.3" >最新基准利率1.3倍</option>
                </select> 
         </td>
        </tr> 
        <tr>
         <td>
          <!--Repayment period (years)-->偿还期限:</td> 
         <td><input id="years"  value="30" /><span style="margin-left:-18px">年</span></td>
        </tr>
        <tr style="display:none"> 
         <td>
          <!--Zipcode (to find lenders)-->邮政编码(查找放贷人):</td> 
         <td><input id="zipcode"  /></td> 
        </tr>
        <tr> 
         <th colspan=2>
          <input class="btn" type=button onclick="calculate();" value="计 算" />&nbsp;&nbsp;&nbsp;
          <input class="btn" type=reset value="重 置" />
         </th>
        </tr> 
        <tr>
             <th colspan=2>
          <!--Approximate Payments-->输出结果:</th> 
        </tr>
        <tr>
         <td>
          <!--Monthly payment-->每月付款:</td> 
         <td><span class="output" id="payment">0</span></td>
        </tr> 
        <tr>
         <td>
          <!--Total payment-->付款总额:</td> 
         <td><span class="output" id="total">0</span></td>
        </tr> 
        <tr>
         <td>
          <!--Total interest-->利息总额:</td> 
         <td><span class="output" id="totalinterest">0</span></td>
        </tr> 
        <tr>
         <td colspan=2>图表:贷款金额,累计金额,利息支付</td>
        </tr> 
        <tr>
            <td colspan="2">
                <canvas id="graph" width="500" height="250"></canvas>
            </td>
        </tr> 
        <tr>
            <td colspan="2">
                <table id="result" style="100%"> 
           <tbody>
            <tr> 
             <td style="position:relative;50%;"> 
              <div>
               <span style="float:left">每月等额还款</span>
               <div class="circle" onclick=helpClick(this) id="debxhk">?</div>
              </div>
              <div class="tsk" id="debxhk_help" style="214px;left:1px;top:16px"> 
                  <div onclick="hidHelp()" class="help">&nbsp;x</div>
               <div class="nr">
                 每月等额还款即等额本息还款法,指借款人每月按相等的金额偿还贷款本息,其中每月贷款利息按月初剩余贷款本金计算并逐月结清。 
               </div> 
              </div>
              <table style="clear:both" cellpadding="0" cellspacing="0" class="tbl" > 
               <tbody>
                <tr> 
                 <td class="td1" width=100px>贷款总额</td> 
                 <td class="td2"><var id="_debx_dkje">0</var> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">还款月数</td> 
                 <td class="td2"><var id="_debx_dkqx">0</var> 月</td> 
                </tr> 
                <tr> 
                 <td class="td1">首月还款</td> 
                 <td class="td2"><em id="_debx_myhk">0</em> 元</td> 
                </tr> 
                <tr>
                    <td class="td1">每月递减</td>
                    <td class="td1"><em id="_debj_mydj">0</em> 元</td>
                </tr>
                <tr> 
                 <td class="td1">总支付利息</td> 
                 <td class="td2"><em id="_debx_zflx">0</em> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">本息合计</td> 
                 <td class="td2"><em id="_debx_hkze">1,039,024.42</em> 元</td> 
                </tr> 
               </tbody>
              </table> 
              </td> 
             <td style="position:relative"> 
              <div>
               <span style="float:left">逐月递减还款</span>
               <div  onclick=helpClick(this) class="circle" id="debjhk">?</div>
              </div> 
              <div class="tsk" id="debjhk_help" style="214px;left:1px;top:16px"> 
                  <div onclick="hidHelp()" class="help">&nbsp;X</div>
               <div class="nr">
                 逐月递减还款即等额本金还款法,指本金保持相同,利息逐月递减,月还款数递减;由于每月的还款本金额固定,而利息越来越少,贷款人起初还款压力较大,但是随时间的推移每月还款数也越来越少。 
               </div> 
              </div> 
              <table  style="clear:both" cellpadding="0" cellspacing="0" class="tbl"> 
               <tbody>
                <tr> 
                 <td class="td1" width=100px>贷款总额</td> 
                 <td class="td2"><var id="_debj_dkje">0</var> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">还款月数</td> 
                 <td class="td2"><var id="_debj_dkqx">0</var> 月</td> 
                </tr> 
                <tr> 
                 <td class="td1">首月还款</td> 
                 <td class="td2"><em id="_debj_syhk">0</em> 元
                 </td> 
                </tr> 
                <tr>
                    <td class="td1">每月递减</td>
                    <td class="td1"><em id="_debj_mydj">0</em> 元</td>
                </tr>
                <tr> 
                 <td class="td1">总支付利息</td> 
                 <td class="td2"><em id="_debj_zflx">0</em> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">本息合计</td> 
                 <td class="td2"><em id="_debj_hkze">1,039,024.42</em> 元</td> 
                </tr> 
               </tbody>
              </table> </td> 
            </tr> 
            <tr> 
             <td height="25" colspan="2"> 
                 <span class="result-info">此结果仅供参考,实际应缴费以当地为准</span>
              </td> 
            </tr> 
           </tbody>
          </table> 
            </td>
        </tr>
        
       </tbody>
      </table>
    
    
    <script>
    function calculate() {
        // 查找文档中用于输入输出的元素
        var amount = document.getElementById("amount");
        var apr = document.getElementById("apr");
        var years = document.getElementById("years");
        var zipcode = document.getElementById("zipcode");
        var payment = document.getElementById("payment");
        var total = document.getElementById("total");
        var totalinterest = document.getElementById("totalinterest");
    
        // 假设所有的输入都是合法的,将从input元素中获取输入数据
        // 将百分比格式转换为小数格式,并从年利率转换为月利率
        // 将年度赔付转换为月度赔付
        var principal = parseFloat(amount.value);
        var interest = parseFloat(apr.value) / 100 / 12;
        var payments = parseFloat(years.value) * 12;
    
        // 现在计算月度赔付的数据
        var x = Math.pow(1 + interest, payments); // Math.pow()进行幂次运算
        var monthly = (principal*x*interest)/(x-1);
    
        // 如果结果没有超过JavaScript能表示的数字范围,且用户的输入也正确
        // 这里所展示的结果就是合法的
        if (isFinite(monthly)) {
            // 将数据填充至输出字段的位置,四舍五入到小数点后两位数字
            payment.innerHTML = monthly.toFixed(2);
            total.innerHTML = (monthly * payments).toFixed(2);
            totalinterest.innerHTML = ((monthly*payments)-principal).toFixed(2);
    
            // 将用户的输入数据保存下来,这样在下次访问时也能取到数据
            save(amount.value, apr.value, years.value, zipcode.value);
            
            // 找到并展示本地放贷人,但忽略网络错误
            try { // 捕获这段代码抛出的所有异常
                getLenders(amount.value, apr.value, years.value, zipcode.value);
            }
            catch(e) { /* 忽略这些异常 */ }
            
            // 最后,用图表展示贷款余额、利息和资产收益
            chart(principal, interest, monthly, payments);
        }
        else {
            // 计算结果不是数字或者是无穷大,意味着输入数据是非法或不完整的
            // 清空之前的输出数据
            payment.innerHTML = "";              // 清空元素的文本内容
            total.innerHTML = ""
            totalinterest.innerHTML = "";
            chart();                             // 不传参数的话就是清除图表
        }
    }
    
    // 将用户的输入保存至localStorage对象的属性中
    // 这些属性在再次访问时还会继续保持在原位置
    // 如果你在浏览器中按照file:// URL的方式直接打开本地文件,
    // 则无法在某些浏览器中使用存储功能(比如FireFox)
    // 而通过HTTP打开文件是可行的
    function save(amount, apr, years, zipcode) {
        if (window.localStorage) { // 只有在浏览器支持的时候才运行这里的代码
            localStorage.loan_amount = amount;
            localStorage.loan_apr = apr;
            localStorage.loan_years = years;
            localStorage.loan_zipcode = zipcode;
        }
    }
    
    // 在文档首次加载时,将会尝试还原输入字段
    window.onload = function() {
        // 如果浏览器支持本地存储并且上次保存的值是存在的
        if (window.localStorage && localStorage.loan_amount) {
            document.getElementById("amount").value = localStorage.loan_amount;
            document.getElementById("apr").value = localStorage.loan_apr;
            document.getElementById("years").value = localStorage.loan_years;
            document.getElementById("zipcode").value = localStorage.loan_zipcode;
        }
    };
    
    // 将用户的输入发送至服务器端脚本(理论上)将
    // 返回一个本地放贷人的链接列表,在这个例子中并没有实现这种查找放贷人的服务
    // 但如果该服务存在,该函数会使用它
    function getLenders(amount, apr, years, zipcode) {
        // 如果浏览器不支持XMLHttpRequest对象,则退出
        if (!window.XMLHttpRequest) return;
    
        // 找到要显示放贷人列表的元素
        var ad = document.getElementById("lenders");
        if (!ad) return;                              // 如果返回为空,则退出
    
        // 将用户的输入数据进行URL编码,并作为查询参数附加在URL里
        var url = "getLenders.php" +                  // 处理数据的URL地址
        "?amt=" + encodeURIComponent(amount) +        // 使用查询串中的数据
            "&apr=" + encodeURIComponent(apr) +
            "&yrs=" + encodeURIComponent(years) +
            "&zip=" + encodeURIComponent(zipcode);
    
        // 通过XMLHttpRequest对象来提取返回数据
        var req = new XMLHttpRequest();               // 发起一个新的请求
        req.open("GET", url);                         // 通过URL发起一个HTTP GET请求
        req.send(null);                               // 不带任何正文发送这个请求
    
        // 在返回数据之前,注册了一个事件处理函数,这个处理函数
        // 将会在服务器的响应返回至客户端的时候调用
        // 这种异步编程模式在客户端JavaScript中是非常常见的
        req.onreadystatechange = function() {
            if (req.readyState == 4 && req.status == 200) {
                // 如果代码运行到这里,说明我们得到了一个合法且完整的HTTP响应
                var response = req.responseText;      // HTTP响应是以字符串的形式呈现的
                var lenders = JSON.parse(response);   // 将其解析为JS数组
    
                // 将数组中的放贷人对象转换为HTML字符串形式
                var list = "";
                for(var i = 0; i < lenders.length; i++) {
                    list += "<li><a href='" + lenders[i].url + "'>" +
                        lenders[i].name + "</a>";
                }
    
                // 将数据在HTML元素中呈现出来
                ad.innerHTML = "<ul>" + list + "</ul>";
            }
        }
    }
    
    // 在HTML<canvas>元素中用图表展示月度贷款余额、利息和资产收益
    // 如果不传入参数的话,则清空之前的图表数据
    function chart(principal, interest, monthly, payments) {
        var graph = document.getElementById("graph");   // 得到<canvas>标签
        graph.width = graph.width;                      // 用一种巧妙的手法清除并重置画布
        
        // 如果不传入参数,或者浏览器不支持画布,则直接返回
        if (arguments.length == 0 || !graph.getContext) return;
    
        // 获得画布元素的"context"对象,这个对象定义了一组绘画API
        var g = graph.getContext("2d");                 // 所有的绘画操作都将基于这个对象
        var width = graph.width, height = graph.height; // 获得画布大小
    
        // 这里的函数作用是将付款数字和美元数据转换为像素
        function paymentToX(n) { return n * width/payments; }
        function amountToY(a) { return height-(a * height/(monthly*payments*1.05));}
    
        // 付款数据是一条从(0,0)到(payments, monthly*payments)的直线
        g.moveTo(paymentToX(0), amountToY(0));          // 从左下方开始
        g.lineTo(paymentToX(payments),                  // 绘至右上方
        amountToY(monthly*payments));
        g.lineTo(paymentToX(payments), amountToY(0));   // 再至右下方
        g.closePath();                                  // 将结尾连接至开头
        g.fillStyle = "#f88";                           // 亮红色
        g.fill();                                       // 填充矩形
        g.font = "bold 12px sans-serif";                // 定义一种字体
        g.fillText("总支出", 20,20);   // 将文字绘制到图例中
    
        // 很多资产数据并不是线性的,很难将其反映至图表中
        var equity = 0;
        g.beginPath();                                  // 开始绘制新图形
        g.moveTo(paymentToX(0), amountToY(0));          // 从左下方开始
        for(var p = 1; p <= payments; p++) {
            // 计算出每一笔赔付的利息
            var thisMonthsInterest = (principal-equity)*interest;
            equity += (monthly - thisMonthsInterest);   // 得到资产额
            g.lineTo(paymentToX(p),amountToY(equity));  // 将数据绘制到画布上
        }
        g.lineTo(paymentToX(payments), amountToY(0));   // 将数据线绘制至X轴
        g.closePath();                                  // 将线条结尾连接至线条开头
        g.fillStyle = "chartreuse";                          // 使用绿色绘制图形
        g.fill();                                       // 曲线之下的部分均填充
        g.fillText("贷款数额", 20,35);              // 文本颜色设置为绿色
    
        // 再次循环,余额数据显示为黑色粗线条
        var bal = principal;
        g.beginPath();
        g.moveTo(paymentToX(0),amountToY(bal));
        for(var p = 1; p <= payments; p++) {
            var thisMonthsInterest = bal*interest;
            bal -= (monthly - thisMonthsInterest);     // 得到资产额
            g.lineTo(paymentToX(p),amountToY(bal));    // 将直线连接至某点
        }
        g.lineWidth = 3;                               // 将直线宽度加粗
        g.stroke();                                    // 绘制余额的曲线
        g.fillStyle = "black";                         // 使用黑色字体
        g.fillText("贷款余额", 20,50);             // 图例文字
    
        // 将年度数据在X轴做标记
        g.textAlign="center";                             // 文字居中对齐
        var y = amountToY(0);                             // Y坐标设为0
        for(var year=1; year*12 <= payments; year++) {    // 遍历每年
            var x = paymentToX(year*12);                  // 计算标记位置
            g.fillRect(x-0.5,y-3,1,3);                    // 开始绘制标记
            if (year == 1) g.fillText("Year", x, y-5);    // 在坐标轴做标记
            if (year % 5 == 0 && year*12 !== payments)    // 每5年的数据
            g.fillText(String(year), x, y-5);
        }
    
        // 将赔付数额标记在右边界
        g.textAlign = "right";                        // 文字右对齐
        g.textBaseline = "middle";                    // 文字垂直居中
        var ticks = [monthly*payments, principal];    // 我们将要用到的两个点
        var rightEdge = paymentToX(payments);         // 设置X坐标
        for(var i = 0; i < ticks.length; i++) {       // 对每两个点做循环
            var y = amountToY(ticks[i]);              // 计算每个标记的Y坐标
            g.fillRect(rightEdge-3, y-0.5, 3,1);      // 绘制标记
            g.fillText(String(ticks[i].toFixed(0)),   // 绘制文本
            rightEdge-5, y);
        }
    }
    </script>
    </body>
    </html>
    View Code

    版本二:  

    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv=Content-Type content="text/html; charset=gb2312">
    <meta charset="utf-8"/>
    <title>JS计算器,贷款利率计算器</title>
    <!--参考出处:http://www.codefans.net/jscss/code/3613.shtml-->
    
    <style> /* 这是一个CSS样式表:定义了程序输出的样式 */
    body{ font-size:12px;}
    #payment { } /* 定义 id="payment" 的元素样式 */
    #graph { border: solid black 1px; } /* 图表有一个1像素的边框 */
    th, td {vertical-align: top; } /* 表格单元格对齐方式为顶端对齐 */
    table{
        border-collapse: collapse;
        border: none;
    }
    td, th{
        border: solid #000 1px;
    }
    .output { font-weight: bold; } /* 计算结果定义为粗体 */
    .btn{
        font-weight: 700;
        line-height: 30px;
         100px;
        height: 35px;
    }
    .tbl{
        95%;
    }
    .td1{
        70px
    }
    .td2{
        140px
    }
    .help
    {
        float:right;
        cursor:pointer;
        line-height: 10px;
        margin-top: -6px;
        margin-right: -6px;
    }
    .nr {
        color: #666;
        font-size: 12px;
        
        padding-top:0px;
        margin: 8px -2px -2px;
    }
    .tsk {
        background: #f8f8f8;
        border: 1px solid #CCC;
        box-shadow: 0 1px 5px #DDD;
        line-height: 20px;
        padding: 10px;
        position: absolute;
        display: none;
        z-index: 10000;
        215px;
        left:1px;
        top:16px
    }
    
    .circle {
         15px;
        height: 15px;
        font-weight: 700;
        text-indent:4px;
    
            cursor:pointer;
        float:left;
        line-height:16px;
        background: #5A8BDC;
        -moz-border-radius: 50px;
        -webkit-border-radius: 50px;
        border-radius: 50px;
    }
    .resultInfo{
        font-weight:normal;
        font-size:10px;
        color:red;
    }
    
    </style>
    
    <script>
        function $(id){ return document.getElementById(id); }
        function helpClick(obj)
        {
            $(obj.id+"_help").style.display = "block";
        }
        
        function hidHelp(obj)
        {
            obj.parentElement.style.display = "none";
        }
        
    </script>
    
    </head>
    <body>
    
      <table width=500px>
       <tbody>
        <tr>
         <th colspan=2 style="text-align:left;font-size:24px;background-color:#3f3">输入数据:</th>      
        </tr> 
        <tr>
         <td width=100px>
          <!--Amount of the loan-->贷款总金额:</td> 
         <td><input id="amount"  value="500000" /><span style="margin-left:-18px">元</span></td> 
        </tr> 
        <tr>
         <td>
          <!--Annual interest-->基准年利率:</td> 
         <td><input id="apr"  value="3.5" /><span style="margin-left:-14px">%</span></td>
        </tr> 
         <td>
          利率折扣:</td> 
         <td>
             <select id=llzk style="173px;">
                   <option value="2.0" >基准利率2倍</option>
                   <option value="1.5" >基准利率1.5倍</option>
                   <option value="1.4" >基准利率1.4倍</option>
                   <option value="1.3" >基准利率1.3倍</option>
                   <option value="1.2" >基准利率1.2倍</option>
                   <option value="1.1" >基准利率1.1倍</option>
                   <option value="1.05">基准利率1.05倍</option>
                   <option value="1.0" selected=true>基准利率</option>
                   <option value="0.95">基准利率9.5折</option>
                   <option value="0.9" >基准利率9折</option>
                   <option value="0.88">基准利率8.8折</option>
                   <option value="0.85">基准利率8.5折</option>
                   <option value="0.83">基准利率8.3折</option>
                   <option value="0.8" >基准利率8折</option>
                   <option value="0.75" >基准利率7.5折</option>
                  <option value="0.7" >基准利率7折</option>
                </select>
         </td>
        </tr> 
        <tr>
         <td>
          <!--Repayment period (years)-->贷款期限:</td> 
         <td><input id="years"  value="30" /><span style="margin-left:-18px">年</span></td>
        </tr>
        <tr style="display:none"> 
         <td>
          <!--Zipcode (to find lenders)-->邮政编码(查找放贷人):</td> 
         <td><input id="zipcode"  /></td> 
        </tr>
        <tr> 
         <th colspan=2>
          <input class="btn" type=button onclick="calculate();" value="计 算" />&nbsp;&nbsp;
          <input class="btn" type=button onclick="RevCalculate();" value="反 推" />&nbsp;&nbsp;
          <input class="btn" type=reset value="重 置" />
         </th>
        </tr> 
        <tr>
          <th colspan=2 style="text-align:left;font-size:24px;background-color:#3f3">
          <!--Approximate Payments-->输出结果:<span class="resultInfo">此结果仅供参考,实际应缴费以当地为准</span>
          </th> 
        </tr>
        <tr>
         <td>
          <!--Monthly payment-->每月付款:</td> 
         <td>
            <input id="payment"  value="30" />
         </td>
        </tr> 
        <tr>
         <td>
          <!--Total payment-->付款总额:</td> 
         <td><span class="output" id="total">0</span></td>
        </tr> 
        <tr>
         <td>
          <!--Total interest-->利息总额:</td> 
         <td><span class="output" id="totalinterest">0</span></td>
        </tr> 
        <tr>
            <td colspan="2">
          <table id="result" style="100%"> 
           <tbody>
            <tr> 
             <td style="position:relative;50%;"> 
              <div>
               <span style="float:left">每月等额还款</span>
               <div class="circle" onclick="helpClick(this)" id="debxhk">?</div>
              </div>
              <div class="tsk" id="debxhk_help"> 
                  <div onclick="hidHelp(this)" class="help">&nbsp;X</div>
               <div class="nr">
                 每月等额还款即等额本息还款法,指借款人每月按相等的金额偿还贷款本息,其中每月贷款利息按月初剩余贷款本金计算并逐月结清。 
               </div> 
              </div>
              <table style="clear:both" cellpadding="0" cellspacing="0" class="tbl"> 
               <tbody>
                <tr> 
                 <td class="td1">贷款总额</td> 
                 <td class="td2"><var id="_debx_dkze">0</var> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">还款月数</td> 
                 <td class="td2"><var id="_debx_hkys">0</var> 月</td> 
                </tr> 
                <tr> 
                 <td class="td1">首月还款</td> 
                 <td class="td2"><em id="_debx_syhk">0</em> 元</td> 
                </tr> 
                <tr>
                    <td class="td1">每月递减</td>
                    <td class="td1"><em id="_debj_mydj">0</em> 元</td>
                </tr>
                <tr> 
                 <td class="td1">总付利息</td> 
                 <td class="td2"><em id="_debx_zflx">0</em> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">本息合计</td> 
                 <td class="td2"><em id="_debx_bxhj">1,039,024.42</em> 元</td> 
                </tr> 
               </tbody>
              </table> 
              </td> 
             <td style="position:relative"> 
              <div>
               <span style="float:left">逐月递减还款</span>
               <div onclick="helpClick(this)" class="circle" id="debjhk">?</div>
              </div> 
              <div class="tsk" id="debjhk_help"> 
                  <div onclick="hidHelp(this)" class="help">&nbsp;X</div>
               <div class="nr">
                 逐月递减还款即等额本金还款法,指本金保持相同,利息逐月递减,月还款数递减;由于每月的还款本金额固定,而利息越来越少,贷款人起初还款压力较大,但是随时间的推移每月还款数也越来越少。 
               </div> 
              </div> 
              <table style="clear:both" cellpadding="0" cellspacing="0" class="tbl"> 
               <tbody>
                <tr> 
                 <td class="td1">贷款总额</td> 
                 <td class="td2"><var id="_debj_dkze">0</var> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">还款月数</td> 
                 <td class="td2"><var id="_debj_hkys">0</var> 月</td> 
                </tr> 
                <tr> 
                 <td class="td1">首月还款</td> 
                 <td class="td2"><em id="_debj_syhk">0</em> 元
                 </td> 
                </tr> 
                <tr>
                    <td class="td1">每月递减</td>
                    <td class="td1"><em id="_debj_mydj">0</em> 元</td>
                </tr>
                <tr> 
                 <td class="td1">总付利息</td> 
                 <td class="td2"><em id="_debj_zflx">0</em> 元</td> 
                </tr> 
                <tr> 
                 <td class="td1">本息合计</td> 
                 <td class="td2"><em id="_debj_bxhj">1,039,024.42</em> 元</td> 
                </tr> 
               </tbody>
              </table> </td> 
            </tr> 
            <tr style="display:none"> 
             <td colspan="2"> 
                 <span class="resultInfo">此结果仅供参考,实际应缴费以当地为准</span>
              </td> 
            </tr> 
           </tbody>
          </table> 
            </td>
        </tr>
        
        
        
        
        <tr>
         <td colspan=2><span style="font-weight:bold;">图表:贷款金额,累计金额,利息支付</span></td>
        </tr> 
        <tr>
            <td colspan="2">
                <canvas id="graph" width="500" height="250"><span style="font-weight:bold;color:red;">请在支持HTML5的浏览器中打开</span></canvas>
            </td>
        </tr> 
       </tbody>
      </table>
    
    
    <script>
    function calculate() {
        // 查找文档中用于输入输出的元素
        var amount = document.getElementById("amount");//贷款总额
        var apr = document.getElementById("apr");//年利息
        var years = document.getElementById("years");//偿还期限
        var zipcode = document.getElementById("zipcode");//邮政编码(查找放贷人)
        var payment = document.getElementById("payment");//月还贷款额
        var total = document.getElementById("total");//还贷款总额
        var totalinterest = document.getElementById("totalinterest");//贷款总利息
    
        // 假设所有的输入都是合法的,将从input元素中获取输入数据
        // 将百分比格式转换为小数格式,并从年利率转换为月利率
        // 将年度赔付转换为月度赔付
        var principal = parseFloat(amount.value);
        var interest = parseFloat(apr.value) / 100 / 12;
        var payments = parseFloat(years.value) * 12;
    
        // 现在计算月度赔付的数据
        var x = Math.pow(1 + interest, payments); // Math.pow()进行幂次运算
        var monthly = (principal*x*interest)/(x-1);
    
        // 如果结果没有超过JavaScript能表示的数字范围,且用户的输入也正确
        // 这里所展示的结果就是合法的
        if (isFinite(monthly)) {
            // 将数据填充至输出字段的位置,四舍五入到小数点后两位数字
            //payment.innerHTML = monthly.toFixed(2);
            payment.value=monthly.toFixed(2);
            total.innerHTML = (monthly * payments).toFixed(2);
            totalinterest.innerHTML = ((monthly*payments)-principal).toFixed(2);
    
            // 将用户的输入数据保存下来,这样在下次访问时也能取到数据
            save(amount.value, apr.value, years.value, zipcode.value);
            
            // 找到并展示本地放贷人,但忽略网络错误
            try { // 捕获这段代码抛出的所有异常
                getLenders(amount.value, apr.value, years.value, zipcode.value);
            }
            catch(e) { /* 忽略这些异常 */ }
            
            // 最后,用图表展示贷款余额、利息和资产收益
            chart(principal, interest, monthly, payments);
        }
        else {
            // 计算结果不是数字或者是无穷大,意味着输入数据是非法或不完整的
            // 清空之前的输出数据
            payment.innerHTML = "";              // 清空元素的文本内容
            total.innerHTML = ""
            totalinterest.innerHTML = "";
            chart();                             // 不传参数的话就是清除图表
        }
    }
    
    // 将用户的输入保存至localStorage对象的属性中
    // 这些属性在再次访问时还会继续保持在原位置
    // 如果你在浏览器中按照file:// URL的方式直接打开本地文件,
    // 则无法在某些浏览器中使用存储功能(比如FireFox)
    // 而通过HTTP打开文件是可行的
    function save(amount, apr, years, zipcode) {
        if (window.localStorage) { // 只有在浏览器支持的时候才运行这里的代码
            localStorage.loan_amount = amount;
            localStorage.loan_apr = apr;
            localStorage.loan_years = years;
            localStorage.loan_zipcode = zipcode;
        }
    }
    
    // 在文档首次加载时,将会尝试还原输入字段
    window.onload = function() {
        // 如果浏览器支持本地存储并且上次保存的值是存在的
        if (window.localStorage && localStorage.loan_amount) {
            document.getElementById("amount").value = localStorage.loan_amount;
            document.getElementById("apr").value = localStorage.loan_apr;
            document.getElementById("years").value = localStorage.loan_years;
            document.getElementById("zipcode").value = localStorage.loan_zipcode;
        }
    };
    
    // 将用户的输入发送至服务器端脚本(理论上)将
    // 返回一个本地放贷人的链接列表,在这个例子中并没有实现这种查找放贷人的服务
    // 但如果该服务存在,该函数会使用它
    function getLenders(amount, apr, years, zipcode) {
        // 如果浏览器不支持XMLHttpRequest对象,则退出
        if (!window.XMLHttpRequest) return;
    
        // 找到要显示放贷人列表的元素
        var ad = document.getElementById("lenders");
        if (!ad) return;                              // 如果返回为空,则退出
    
        // 将用户的输入数据进行URL编码,并作为查询参数附加在URL里
        var url = "getLenders.php" +                  // 处理数据的URL地址
        "?amt=" + encodeURIComponent(amount) +        // 使用查询串中的数据
            "&apr=" + encodeURIComponent(apr) +
            "&yrs=" + encodeURIComponent(years) +
            "&zip=" + encodeURIComponent(zipcode);
    
        // 通过XMLHttpRequest对象来提取返回数据
        var req = new XMLHttpRequest();               // 发起一个新的请求
        req.open("GET", url);                         // 通过URL发起一个HTTP GET请求
        req.send(null);                               // 不带任何正文发送这个请求
    
        // 在返回数据之前,注册了一个事件处理函数,这个处理函数
        // 将会在服务器的响应返回至客户端的时候调用
        // 这种异步编程模式在客户端JavaScript中是非常常见的
        req.onreadystatechange = function() {
            if (req.readyState == 4 && req.status == 200) {
                // 如果代码运行到这里,说明我们得到了一个合法且完整的HTTP响应
                var response = req.responseText;      // HTTP响应是以字符串的形式呈现的
                var lenders = JSON.parse(response);   // 将其解析为JS数组
    
                // 将数组中的放贷人对象转换为HTML字符串形式
                var list = "";
                for(var i = 0; i < lenders.length; i++) {
                    list += "<li><a href='" + lenders[i].url + "'>" +
                        lenders[i].name + "</a>";
                }
    
                // 将数据在HTML元素中呈现出来
                ad.innerHTML = "<ul>" + list + "</ul>";
            }
        }
    }
    
    // 在HTML<canvas>元素中用图表展示月度贷款余额、利息和资产收益
    // 如果不传入参数的话,则清空之前的图表数据
    // principal:贷款金额
    // interest:利息
    // monthly:每月还款额
    // payments:还款期限
    function chart(principal, interest, monthly, payments) {
        var graph = document.getElementById("graph");   // 得到<canvas>标签
        graph.width = graph.width;                      // 用一种巧妙的手法清除并重置画布
        
        // 如果不传入参数,或者浏览器不支持画布,则直接返回
        if (arguments.length == 0 || !graph.getContext) return;
    
        // 获得画布元素的"context"对象,这个对象定义了一组绘画API
        var g = graph.getContext("2d");                 // 所有的绘画操作都将基于这个对象
        var width = graph.width, height = graph.height; // 获得画布大小
    
        // 这里的函数作用是将付款数字的数据转换为像素
        function paymentToX(n) { return n * width/payments; }
        function amountToY(a) { return height-(a * height/(monthly*payments*1.05));}
    
        // 总付款数据是一条从(0,0)到(payments, monthly*payments)的直线
        g.moveTo(paymentToX(0), amountToY(0));          // 从左下方开始
        g.lineTo(paymentToX(payments),amountToY(monthly*payments));// 绘至右上方
        g.lineTo(paymentToX(payments), amountToY(0));   // 再至右下方
        g.closePath();                                  // 将结尾连接至开头
        g.fillStyle = "#f88";                           // 亮红色
        g.fill();                                       // 填充矩形
        g.font = "bold 12px sans-serif";                // 定义一种字体
        g.fillText("总支出", 90,15);   // 将文字绘制到图例中
    
        // 总贷款额的还款进度,很多资产数据并不是线性的,很难将其反映至图表中
        var equity = 0;
        g.beginPath();                                  // 开始绘制新图形
        g.moveTo(paymentToX(0), amountToY(0));          // 从左下方开始
        for(var p = 1; p <= payments; p++) {            // 计算出每一笔赔付的利息
            var thisMonthsInterest = (principal-equity)*interest;
            equity += (monthly - thisMonthsInterest);   // 得到资产额
            g.lineTo(paymentToX(p),amountToY(equity));  // 将数据绘制到画布上
        }
        g.lineTo(paymentToX(payments), amountToY(0));   // 将数据线绘制至X轴
        g.closePath();                                  // 将线条结尾连接至线条开头
        g.fillStyle = "#78E682";                        // 使用绿色绘制图形
        g.fill();                                       // 曲线之下的部分均填充
        g.fillText("贷款数额", 90,30);                  // 文本颜色设置为绿色
    
        // 余额,再次循环,余额数据显示为黑色粗线条
        var bal = principal;
        g.beginPath();
        g.moveTo(paymentToX(0),amountToY(bal));
        for(var p = 1; p <= payments; p++) {
            var thisMonthsInterest = bal*interest;
            bal -= (monthly - thisMonthsInterest);     // 得到资产额
            g.lineTo(paymentToX(p),amountToY(bal));    // 将直线连接至某点
        }
        g.lineWidth = 2;                               // 将直线宽度加粗
        g.strokeStyle = "#E422CB";                       // 绘制线条的颜色
        g.stroke();                                    // 绘制余额的曲线
        g.fillStyle = "#E422CB";                         // 使用黑色字体
        g.fillText("贷款余额", 90,45);                 // 图例文字
    
        // 将年度数据在X轴做标记
        g.fillStyle = "black";                              // 使用黑色字体
        g.textAlign="center";                             // 文字居中对齐
        var y = amountToY(0);                             // Y坐标设为0
        for(var year=1; year*12 <= payments; year++) {    // 遍历每年
            var x = paymentToX(year*12);                  // 计算标记位置
            g.fillRect(x-0.5,y-3,1,3);                    // 开始绘制标记
            if (year == 1) g.fillText("1年", x, y-5);    // 在坐标轴做标记
            if (year % 5 == 0 && year*12 !== payments)    // 每5年的数据
            g.fillText(String(year)+"年", x, y-5);
        }
    
        // 将赔付数额标记在右边界
        g.textAlign = "right";                                    // 文字右对齐
        g.textBaseline = "middle";                                // 文字垂直居中
        var ticks = [monthly*payments, principal];              // 我们将要用到的两个点
        var rightEdge = paymentToX(payments);                   // 设置X坐标
        for(var i = 0; i < ticks.length; i++) {                 // 对每两个点做循环
            var y = amountToY(ticks[i]);                           // 计算每个标记的Y坐标
            g.fillRect(rightEdge-3, y-0.5, 5,1);                 // 绘制标记
            g.fillText(String(ticks[i].toFixed(0)),rightEdge-5, y);    // 绘制文本
        }
        g.textAlign = "left";
        g.fillRect(0, y-0.5, 6,1);                      // 绘制标记
        g.fillText(String(principal),15, y);        // 绘制文本
    }
    </script>
    </body>
    </html>
    View Code

     版本三:

    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv=Content-Type content="text/html; charset=gb2312">
    <meta charset="utf-8"/>
    <title>JS计算器,贷款利率计算器</title>
    <!--参考出处:http://www.codefans.net/jscss/code/3613.shtml-->
    
    <style> /* 这是一个CSS样式表:定义了程序输出的样式 */
    body{ font-size:12px;}
    #payment { } /* 定义 id="payment" 的元素样式 */
    #graph { border: solid black 1px; } /* 图表有一个1像素的边框 */
    th, td {vertical-align: top; } /* 表格单元格对齐方式为顶端对齐 */
    table{
        border-collapse: collapse;
        border: none;
    }
    td, th{
        border: solid #000 1px;
    }
    .output { font-weight: bold; } /* 计算结果定义为粗体 */
    .btn{
        font-weight: 700;
        line-height: 30px;
        width: 100px;
        height: 35px;
    }
    .tbl{
        width:95%;
    }
    .td1{
        width:70px
    }
    .td2{
        width:140px
    }
    .help
    {
        float:right;
        cursor:pointer;
        line-height: 10px;
        margin-top: -6px;
        margin-right: -6px;
    }
    .nr {
        color: #666;
        font-size: 12px;
        
        padding-top:0px;
        margin: 8px -2px -2px;
    }
    .tsk {
        background: #f8f8f8;
        border: 1px solid #CCC;
        box-shadow: 0 1px 5px #DDD;
        line-height: 20px;
        padding: 10px;
        position: absolute;
        display: none;
        z-index: 10000;
        width:215px;
        left:1px;
        top:16px
    }
    
    .circle {
        width: 15px;
        height: 15px;
        font-weight: 700;
        text-indent:4px;
    
            cursor:pointer;
        float:left;
        line-height:16px;
        background: #5A8BDC;
        -moz-border-radius: 50px;
        -webkit-border-radius: 50px;
        border-radius: 50px;
    }
    .resultInfo{
        font-weight:normal;
        font-size:10px;
        color:red;
    }
    
    </style>
    
    
    <script language="JavaScript"> 
    //格式化参数说明:
    //y:年,M:月,d:日,k周,h:时,m分,s:秒,S毫秒
    Date.prototype.Format = function(formatIn){
      var formatOut=formatIn;
      var o = {
      "M+" : this.getMonth()+1, //month
      "d+" : this.getDate(),    //day
      "h+" : this.getHours(),   //hour
      "m+" : this.getMinutes(), //minute
      "s+" : this.getSeconds(), //second
      "q+" : Math.floor((this.getMonth()+3)/3),  //quarter
      "k+" : Math.ceil((Math.ceil((this.valueOf()-new Date(this.getFullYear(),0,1).valueOf())/86400000)+((new Date(this.getFullYear(),0,1).getDay()+1)-1))/7)        //weeks
      }
      if(/(y+)/ig.test(formatIn))
      for(var i=0;i<(formatIn.match(/(y+)/ig)).length;i++){formatOut=formatOut.replace((formatIn.match(/(y+)/ig))[i],(this.getFullYear()+"").substr(4 - (formatIn.match(/(y+)/ig))[i].length));}
      for(var k in o){
          var reg=new RegExp("("+ k +")","g");
        if(reg.test(formatIn))var t=formatIn.match(reg);
        for(var i in t)formatOut=formatOut.replace(t[i],(o[k]+"").length==1?("0"+ o[k]).substr(t[i].length>2?-t[i].length:2-t[i].length):o[k]);
      }
      if(/(S+)/g.test(formatIn))
      formatOut=formatOut.replace(formatIn.match(/(S+)/g)[0],(this.getMilliseconds()+"").length<2?("00"+this.getMilliseconds()).substr(0,formatIn.match(/(S+)/g)[0].length):((this.getMilliseconds()+"").length==2?("0"+this.getMilliseconds()).substr(0,formatIn.match(/(S+)/g)[0].length):(this.getMilliseconds()+"").substr(0,formatIn.match(/(S+)/g)[0].length)));
      return formatOut;
    }
    //测试代码    
    var dt=new Date;
    var f1="yyyy-MM-dd 第k周 hh:mm:ss.SS";
    var f2="MM-dd kkk yy-M-d k h:m:s.S";
    var f3="hh:mm:ss.SSSS";
    //alert(dt.Format(f1));
    //alert(dt.Format(f2));
    
    ;          
           
    
        function $(id){ return document.getElementById(id); }
        function helpClick(obj)
        {
            $(obj.id+"_help").style.display = "block";
        }
        
        function hidHelp(obj)
        {
            obj.parentElement.style.display = "none";
        }
        
        
    
    function Test()
        {    
            var t1=document.createElement("table");
            t1.width="100%";
            t1.insertRow();
            t1.rows[0].innerHTML="<th width='10%'>期次</th><th>年月</th><th width='20%'>本金</th><th width='20%'>利息</th><th width='20%'>月供</th><th>剩余贷款</th>";
            
            /*
            t1.insertRow();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].cells[0].innerHTML = 1;
            t1.rows[t1.rows.length-1].cells[1].innerHTML = 2;
            t1.rows[t1.rows.length-1].cells[2].innerHTML = 3;
            */
            
            //t1.rows[t1.rows.length-1].insertCell()
            //tab.appendChild("<tr><td>1</td><td>2</td></tr>");
            //tab.appendChild(t1);
            
            //alert(newRow.cells.length);
            //div.appendChild(table); 
            //cell.innerHTML = '';
            //StringBuilder html = new StringBuilder("<table width='100%'>");   
            //document.getElementById('zhutiTable').appendChild(t); 
    
            
            
        // 查找文档中用于输入输出的元素
        var amount = document.getElementById("amount");//贷款总额
        var apr = document.getElementById("apr");//年利息
        var years = document.getElementById("yearsToM");//偿还期限
        var startYM = document.getElementById("startYM");//邮政编码(查找放贷人)
        var payment = document.getElementById("payment");//月还贷款额
        var total = document.getElementById("total");//还贷款总额
        var totalinterest = document.getElementById("totalinterest");//贷款总利息
    
        // 假设所有的输入都是合法的,将从input元素中获取输入数据
        // 将百分比格式转换为小数格式,并从年利率转换为月利率
        // 将年度赔付转换为月度赔付
        var principal = parseFloat(amount.value);
        var interest = parseFloat(apr.value) / 100 / 12;
        var payments = parseFloat(years.value) * 12;
        payments = years.value;
    
        // 现在计算月度赔付的数据
        var x = Math.pow(1 + interest, payments); // Math.pow()进行幂次运算
        var monthly = (principal*x*interest)/(x-1);
        
        
        
        //月还款额=贷款金额*年利率/12*(1+年利率/12)^(年期*12)/((1+年利率/12)^(年期*12)-1)
        var yue = principal*(interest)*x/(Math.pow(1+interest,payments)-1);
        var shy = principal;
        var dtt = new Date(startYM.value.split("-")[0],startYM.value.split("-")[1]-1,1)
        
        
        for(i=0;i<payments;i++){
            //第N月本金: 月还款额=(1+年利率/12)^(N-1-年期*12)
            var y_bj=Math.pow(interest+1,i-payments)
            //第N月利息: 月还款额=(1-(1+年利率/12)^(N-1-年期*12))
            var y_lx=1-Math.pow(1+interest,i-payments);
            t1.insertRow(t1.rows.length);
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].insertCell();
            t1.rows[t1.rows.length-1].cells[0].innerHTML = i+1;
            t1.rows[t1.rows.length-1].cells[1].innerHTML = dtt.Format("yyyy-MM");
            t1.rows[t1.rows.length-1].cells[2].innerHTML = (monthly*y_bj).toFixed(2);
            t1.rows[t1.rows.length-1].cells[3].innerHTML = (monthly*y_lx).toFixed(2);
            t1.rows[t1.rows.length-1].cells[4].innerHTML = monthly.toFixed(2);
            t1.rows[t1.rows.length-1].cells[5].innerHTML = (principal-=(monthly*y_bj)).toFixed(2);
            
            dtt = dtt.setMonth(dtt.getMonth()+1);
            dtt = new Date(dtt);
        }
        
        
        var tr=$("tr_tab");
        tr.cells[0].innerHTML=t1.outerHTML;    
            
    
        }
        
    </script>
    
    </head>
    <body>
    
      <table id="t_main" width=500px>
       <tbody>
        <tr>
         <th colspan=2 style="text-align:left;font-size:24px;background-color:#3f3">输入数据:</th>      
        </tr> 
        <tr>
         <td width=100px>
          <!--Amount of the loan-->贷款总金额:</td> 
         <td><input id="amount"  value="500000" /><span style="margin-left:-18px"></span></td> 
        </tr> 
        <tr>
         <td>
          <!--Annual interest-->基准年利率:</td> 
         <td><input id="apr"  value="3.5" /><span style="margin-left:-14px">%</span></td>
        </tr> 
         <td>
          利率折扣:</td> 
         <td>
             <select id=llzk style="173px;">
                   <option value="2.0" >基准利率2倍</option>
                   <option value="1.5" >基准利率1.5倍</option>
                   <option value="1.4" >基准利率1.4倍</option>
                   <option value="1.3" >基准利率1.3倍</option>
                   <option value="1.2" >基准利率1.2倍</option>
                   <option value="1.1" >基准利率1.1倍</option>
                   <option value="1.05">基准利率1.05倍</option>
                   <option value="1.0" selected=true>基准利率</option>
                   <option value="0.95">基准利率9.5折</option>
                   <option value="0.9" >基准利率9折</option>
                   <option value="0.88">基准利率8.8折</option>
                   <option value="0.85">基准利率8.5折</option>
                   <option value="0.83">基准利率8.3折</option>
                   <option value="0.8" >基准利率8折</option>
                   <option value="0.75" >基准利率7.5折</option>
                  <option value="0.7" >基准利率7折</option>
                </select>
         </td>
        </tr> 
        <tr>
         <td>
          <!--Repayment period (years)-->贷款期限:</td> 
         <td><input id="yearsToM"  value="180" /><span style="margin-left:-18px"></span></td>
        </tr>
        <tr> 
         <td>
          <!--Zipcode (to find lenders)-->首次还贷年月:</td> 
         <td><input id="startYM" value='2016-01' />
            <span id="lenders"></span>
         </td> 
        </tr>
        <tr> 
         <th colspan=2>
          <input class="btn" type=button onclick="calculate();" value="计 算" />&nbsp;&nbsp;
          <input class="btn" type=button onclick="Test();" value="测 试" />&nbsp;&nbsp;
          <input class="btn" type=reset value="重 置" />
         </th>
        </tr> 
        <tr>
          <th colspan=2 style="text-align:left;font-size:24px;background-color:#3f3">
          <!--Approximate Payments-->输出结果:<span class="resultInfo">此结果仅供参考,实际应缴费以当地为准</span>
          </th> 
        </tr>
        <tr>
         <td>
          <!--Monthly payment-->每月付款:</td> 
         <td>
            <input id="payment"  value="30" />
         </td>
        </tr> 
        <tr>
         <td>
          <!--Total payment-->付款总额:</td> 
         <td><span class="output" id="total">0</span></td>
        </tr> 
        <tr>
         <td>
          <!--Total interest-->利息总额:</td> 
         <td><span class="output" id="totalinterest">0</span></td>
        </tr> 
        <tr>
            <td colspan="2">
          <table id="result" style="100%"> 
           <tbody>
            <tr> 
             <td style="position:relative;50%;"> 
              <div>
               <span style="float:left">每月等额还款</span>
               <div class="circle" onclick="helpClick(this)" id="debxhk">?</div>
              </div>
              <div class="tsk" id="debxhk_help"> 
                  <div onclick="hidHelp(this)" class="help">&nbsp;X</div>
               <div class="nr">
                 每月等额还款即等额本息还款法,指借款人每月按相等的金额偿还贷款本息,其中每月贷款利息按月初剩余贷款本金计算并逐月结清。 
               </div> 
              </div>
              <table style="clear:both" cellpadding="0" cellspacing="0" class="tbl"> 
               <tbody>
                <tr> 
                 <td class="td1">贷款总额</td> 
                 <td class="td2"><var id="_debx_dkze">0</var></td> 
                </tr> 
                <tr> 
                 <td class="td1">还款月数</td> 
                 <td class="td2"><var id="_debx_hkys">0</var></td> 
                </tr> 
                <tr> 
                 <td class="td1">首月还款</td> 
                 <td class="td2"><em id="_debx_syhk">0</em></td> 
                </tr> 
                <tr>
                    <td class="td1">每月递减</td>
                    <td class="td1"><em id="_debj_mydj">0</em></td>
                </tr>
                <tr> 
                 <td class="td1">总付利息</td> 
                 <td class="td2"><em id="_debx_zflx">0</em></td> 
                </tr> 
                <tr> 
                 <td class="td1">本息合计</td> 
                 <td class="td2"><em id="_debx_bxhj">1,039,024.42</em></td> 
                </tr> 
               </tbody>
              </table> 
              </td> 
             <td style="position:relative"> 
              <div>
               <span style="float:left">逐月递减还款</span>
               <div onclick="helpClick(this)" class="circle" id="debjhk">?</div>
              </div> 
              <div class="tsk" id="debjhk_help"> 
                  <div onclick="hidHelp(this)" class="help">&nbsp;X</div>
               <div class="nr">
                 逐月递减还款即等额本金还款法,指本金保持相同,利息逐月递减,月还款数递减;由于每月的还款本金额固定,而利息越来越少,贷款人起初还款压力较大,但是随时间的推移每月还款数也越来越少。 
               </div> 
              </div> 
              <table style="clear:both" cellpadding="0" cellspacing="0" class="tbl"> 
               <tbody>
                <tr> 
                 <td class="td1">贷款总额</td> 
                 <td class="td2"><var id="_debj_dkze">0</var></td> 
                </tr> 
                <tr> 
                 <td class="td1">还款月数</td> 
                 <td class="td2"><var id="_debj_hkys">0</var></td> 
                </tr> 
                <tr> 
                 <td class="td1">首月还款</td> 
                 <td class="td2"><em id="_debj_syhk">0</em></td> 
                </tr> 
                <tr>
                    <td class="td1">每月递减</td>
                    <td class="td1"><em id="_debj_mydj">0</em></td>
                </tr>
                <tr> 
                 <td class="td1">总付利息</td> 
                 <td class="td2"><em id="_debj_zflx">0</em></td> 
                </tr> 
                <tr> 
                 <td class="td1">本息合计</td> 
                 <td class="td2"><em id="_debj_bxhj">1,039,024.42</em></td> 
                </tr> 
               </tbody>
              </table> </td> 
            </tr> 
            <tr style="display:none"> 
             <td colspan="2"> 
                 <span class="resultInfo">此结果仅供参考,实际应缴费以当地为准</span>
              </td> 
            </tr> 
           </tbody>
          </table> 
            </td>
        </tr>
        
        
        
        
        <tr>
         <td colspan=2><span style="font-weight:bold;">图表:贷款金额,累计金额,利息支付</span></td>
        </tr> 
        <tr>
            <td colspan="2">
                <canvas id="graph" width="500" height="250"></canvas>
            </td>
        </tr> 
        <tr id="tr_tab">
            <td colspan="2">
            </td>
        </tr> 
       </tbody>
      </table>
    <div id="list"></div>
    
    <script>
    function calculate() {
    
    //生成每月还款额表格
    Test();
    
        // 查找文档中用于输入输出的元素
        var amount = document.getElementById("amount");//贷款总额
        var apr = document.getElementById("apr");//年利息
        var years = document.getElementById("yearsToM");//偿还期限
        var startYM = document.getElementById("startYM");//邮政编码(查找放贷人)
        var payment = document.getElementById("payment");//月还贷款额
        var total = document.getElementById("total");//还贷款总额
        var totalinterest = document.getElementById("totalinterest");//贷款总利息
    
        // 假设所有的输入都是合法的,将从input元素中获取输入数据
        // 将百分比格式转换为小数格式,并从年利率转换为月利率
        // 将年度赔付转换为月度赔付
        var principal = parseFloat(amount.value);
        var interest = parseFloat(apr.value) / 100 / 12;
        var payments = parseFloat(years.value) * 12;
        payments = years.value;
    
        // 现在计算月度赔付的数据
        var x = Math.pow(1 + interest, payments); // Math.pow()进行幂次运算
        var monthly = (principal*x*interest)/(x-1);
        
        
        
        //月还款额=贷款金额*年利率/12*(1+年利率/12)^(年期*12)/((1+年利率/12)^(年期*12)-1)
        var yue = principal*(interest)*x/(Math.pow(1+interest,payments)-1)
        //第N月本金: 月还款额=(1+年利率/12)^(N-1-年期*12)
        var y_bj=Math.pow(interest+1,-payments)
        //第N月利息: 月还款额=(1-(1+年利率/12)^(N-1-年期*12))
        var y_lx=1-Math.pow(1+interest,-payments)
    
        // 如果结果没有超过JavaScript能表示的数字范围,且用户的输入也正确
        // 这里所展示的结果就是合法的
        if (isFinite(monthly)) {
            // 将数据填充至输出字段的位置,四舍五入到小数点后两位数字
            //payment.innerHTML = monthly.toFixed(2);
            payment.value=monthly.toFixed(2);
            total.innerHTML = (monthly * payments).toFixed(2);
            totalinterest.innerHTML = ((monthly*payments)-principal).toFixed(2);
    
            // 将用户的输入数据保存下来,这样在下次访问时也能取到数据
            save(amount.value, apr.value, years.value, startYM.value);
            
            // 找到并展示本地放贷人,但忽略网络错误
            try { // 捕获这段代码抛出的所有异常
                //getLenders(amount.value, apr.value, years.value, startYM.value);
            }
            catch(e) { /* 忽略这些异常 */ }
            
            // 最后,用图表展示贷款余额、利息和资产收益
            chart(principal, interest, monthly, payments);
        }
        else {
            // 计算结果不是数字或者是无穷大,意味着输入数据是非法或不完整的
            // 清空之前的输出数据
            payment.innerHTML = "";              // 清空元素的文本内容
            total.innerHTML = ""
            totalinterest.innerHTML = "";
            chart();                             // 不传参数的话就是清除图表
        }
    }
    
    // 将用户的输入保存至localStorage对象的属性中
    // 这些属性在再次访问时还会继续保持在原位置
    // 如果你在浏览器中按照file:// URL的方式直接打开本地文件,
    // 则无法在某些浏览器中使用存储功能(比如FireFox)
    // 而通过HTTP打开文件是可行的
    function save(amount, apr, years, zipcode) {
        if (window.localStorage) { // 只有在浏览器支持的时候才运行这里的代码
            localStorage.loan_amount = amount;
            localStorage.loan_apr = apr;
            localStorage.loan_years = years;
            localStorage.loan_zipcode = zipcode;
        }
    }
    
    // 在文档首次加载时,将会尝试还原输入字段
    window.onload = function() {
        // 如果浏览器支持本地存储并且上次保存的值是存在的
        if (window.localStorage && localStorage.loan_amount) {
            document.getElementById("amount").value = localStorage.loan_amount;
            document.getElementById("apr").value = localStorage.loan_apr;
            document.getElementById("yearsToM").value = localStorage.loan_years;
            document.getElementById("startYM").value = localStorage.loan_zipcode;
        }
    };
    
    // 将用户的输入发送至服务器端脚本(理论上)将
    // 返回一个本地放贷人的链接列表,在这个例子中并没有实现这种查找放贷人的服务
    // 但如果该服务存在,该函数会使用它
    function getLenders(amount, apr, years, zipcode) {
        // 如果浏览器不支持XMLHttpRequest对象,则退出
        if (!window.XMLHttpRequest) return;
    
        // 找到要显示放贷人列表的元素
        var ad = document.getElementById("lenders");
        if (!ad) return;                              // 如果返回为空,则退出
    
        // 将用户的输入数据进行URL编码,并作为查询参数附加在URL里
        var url = "getLenders.php" +                  // 处理数据的URL地址
        "?amt=" + encodeURIComponent(amount) +        // 使用查询串中的数据
            "&apr=" + encodeURIComponent(apr) +
            "&yrs=" + encodeURIComponent(years) +
            "&zip=" + encodeURIComponent(zipcode);
    
        // 通过XMLHttpRequest对象来提取返回数据
        var req = new XMLHttpRequest();               // 发起一个新的请求
        req.open("GET", url);                         // 通过URL发起一个HTTP GET请求
        req.send(null);                               // 不带任何正文发送这个请求
    
        // 在返回数据之前,注册了一个事件处理函数,这个处理函数
        // 将会在服务器的响应返回至客户端的时候调用
        // 这种异步编程模式在客户端JavaScript中是非常常见的
        req.onreadystatechange = function() {
            if (req.readyState == 4 && req.status == 200) {
                // 如果代码运行到这里,说明我们得到了一个合法且完整的HTTP响应
                var response = req.responseText;      // HTTP响应是以字符串的形式呈现的
                var lenders = JSON.parse(response);   // 将其解析为JS数组
    
                // 将数组中的放贷人对象转换为HTML字符串形式
                var list = "";
                for(var i = 0; i < lenders.length; i++) {
                    list += "<li><a href='" + lenders[i].url + "'>" +
                        lenders[i].name + "</a>";
                }
    
                // 将数据在HTML元素中呈现出来
                ad.innerHTML = "<ul>" + list + "</ul>";
            }
        }
    }
    
    // 在HTML<canvas>元素中用图表展示月度贷款余额、利息和资产收益
    // 如果不传入参数的话,则清空之前的图表数据
    // principal:贷款金额
    // interest:利息
    // monthly:每月还款额
    // payments:还款期限
    function chart(principal, interest, monthly, payments) {
        var graph = document.getElementById("graph");   // 得到<canvas>标签
        graph.width = graph.width;                      // 用一种巧妙的手法清除并重置画布
        
        // 如果不传入参数,或者浏览器不支持画布,则直接返回
        if (arguments.length == 0 || !graph.getContext) return;
    
        // 获得画布元素的"context"对象,这个对象定义了一组绘画API
        var g = graph.getContext("2d");                 // 所有的绘画操作都将基于这个对象
        var width = graph.width, height = graph.height; // 获得画布大小
    
        // 这里的函数作用是将付款数字的数据转换为像素
        function paymentToX(n) { return n * width/payments; }
        function amountToY(a) { return height-(a * height/(monthly*payments*1.05));}
    
        // 总付款数据是一条从(0,0)到(payments, monthly*payments)的直线
        g.moveTo(paymentToX(0), amountToY(0));          // 从左下方开始
        g.lineTo(paymentToX(payments),amountToY(monthly*payments));// 绘至右上方
        g.lineTo(paymentToX(payments), amountToY(0));   // 再至右下方
        g.closePath();                                  // 将结尾连接至开头
        g.fillStyle = "#f88";                           // 亮红色
        g.fill();                                       // 填充矩形
        g.font = "bold 12px sans-serif";                // 定义一种字体
        g.fillText("总支出", 15,20);   // 将文字绘制到图例中
    
        // 总贷款额的还款进度,很多资产数据并不是线性的,很难将其反映至图表中
        var equity = 0;
        g.beginPath();                                  // 开始绘制新图形
        g.moveTo(paymentToX(0), amountToY(0));          // 从左下方开始
        for(var p = 1; p <= payments; p++) {            // 计算出每一笔赔付的利息
            var thisMonthsInterest = (principal-equity)*interest;
            equity += (monthly - thisMonthsInterest);   // 得到资产额
            g.lineTo(paymentToX(p),amountToY(equity));  // 将数据绘制到画布上
        }
        g.lineTo(paymentToX(payments), amountToY(0));   // 将数据线绘制至X轴
        g.closePath();                                  // 将线条结尾连接至线条开头
        g.fillStyle = "#78E682";                     // 使用绿色绘制图形
        g.fill();                                       // 曲线之下的部分均填充
        g.fillText("贷款数额", 60,20);                  // 文本颜色设置为绿色
    
        // 余额,再次循环,余额数据显示为黑色粗线条
        var bal = principal;
        g.beginPath();
        g.moveTo(paymentToX(0),amountToY(bal));
        for(var p = 1; p <= payments; p++) {
            var thisMonthsInterest = bal*interest;
            bal -= (monthly - thisMonthsInterest);     // 得到资产额
            g.lineTo(paymentToX(p),amountToY(bal));    // 将直线连接至某点
        }
        g.lineWidth = 2;                               // 将直线宽度加粗
        g.strokeStyle = "#e2e";                           // 绘制线条的颜色
        g.stroke();                                    // 绘制余额的曲线
        g.fillStyle = "#e2e";                         // 使用黑色字体
        g.fillText("贷款余额", 120,20);                 // 图例文字
    
        // 将年度数据在X轴做标记
        g.fillStyle = "black";                         // 使用黑色字体
        g.textAlign="center";                             // 文字居中对齐
        var y = amountToY(0);                             // Y坐标设为0
        for(var year=1; year*12 <= payments; year++) {    // 遍历每年
            var x = paymentToX(year*12);                  // 计算标记位置
            g.fillRect(x-0.5,y-3,1,3);                    // 开始绘制标记
            if (year == 1) g.fillText("1年", x, y-5);    // 在坐标轴做标记
            if (year % 5 == 0 && year*12 !== payments)    // 每5年的数据
            g.fillText(String(year)+"", x, y-5);
        }
    
        // 将赔付数额标记在右边界
        g.textAlign = "right";                                    // 文字右对齐
        g.textBaseline = "middle";                                // 文字垂直居中
        var ticks = [monthly*payments, principal];   // 我们将要用到的两个点
        var rightEdge = paymentToX(payments);                   // 设置X坐标
        for(var i = 0; i < ticks.length; i++) {                 // 对每两个点做循环
            var y = amountToY(ticks[i]);                           // 计算每个标记的Y坐标
            g.fillRect(rightEdge-3, y-0.5, 5,1);                 // 绘制标记
            g.fillText(String(ticks[i].toFixed(0)),rightEdge-5, y);    // 绘制文本
        }
        g.textAlign = "left";
        g.fillRect(0, y-0.5, 6,1);                      // 绘制标记
        g.fillText(String(principal),15, y);        // 绘制文本
    }
    
    </script>
    
    </body>
    </html>
    View Code

    出处参考:

    http://www.codefans.net/jscss/code/3613.shtml

    http://www.rong360.com/calculator/fangdai.html

  • 相关阅读:
    【洛谷P3853】 [TJOI2007]路标设置
    【洛谷P1159】排行榜
    【洛谷P2921】[USACO08DEC]在农场万圣节
    【洛谷P1108】低价购买
    【洛谷P1363】幻象迷宫
    【题解】洛谷P2023 [AHOI2009] 维护序列(线段树)
    【数据结构】线段树的几种用法
    【题解】洛谷P1283 平板涂色(搜索+暴力)
    【题解】洛谷P1074 [NOIP2009TG] 靶形数独(DFS+剪枝)
    【题解】洛谷P1120 小木棍(搜索+剪枝+卡常)
  • 原文地址:https://www.cnblogs.com/mq0036/p/4604758.html
Copyright © 2020-2023  润新知