286 lines
17 KiB
PHP
286 lines
17 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
/** @var array $me */
|
||
?>
|
||
<div class="card fin-tool-page">
|
||
<h3 class="section-title">承兑贴现计算器</h3>
|
||
<div class="quick-links-col" style="margin-bottom:14px">
|
||
<a class="tree-link" href="index.php?action=quick_queries">← 返回便捷查询</a>
|
||
</div>
|
||
<div class="fin-two-col">
|
||
<div class="fin-panel">
|
||
<h4>票据信息</h4>
|
||
<div class="fin-row">
|
||
<label>票据类型</label>
|
||
<div>
|
||
<label class="inline small"><input type="radio" name="dc_bill" value="bank" checked> 银行承兑汇票</label>
|
||
<label class="inline small" style="margin-left:12px"><input type="radio" name="dc_bill" value="commercial"> 商业承兑汇票</label>
|
||
<p class="fin-muted" id="dc_risk_hint" style="margin:4px 0 0">银承信用风险相对较低;商承请结合承兑人资信评估。</p>
|
||
</div>
|
||
</div>
|
||
<div class="fin-row">
|
||
<label for="dc_face">票面金额(元)</label>
|
||
<div>
|
||
<input id="dc_face" type="text" inputmode="decimal" style="width:100%;max-width:280px">
|
||
<div id="dc_face_fmt" class="fin-muted"></div>
|
||
<div id="dc_face_cap" class="fin-cn-cap"></div>
|
||
</div>
|
||
</div>
|
||
<div class="fin-row">
|
||
<label for="dc_issue">出票日期</label>
|
||
<input id="dc_issue" type="date">
|
||
</div>
|
||
<div class="fin-row">
|
||
<label>到期日</label>
|
||
<div>
|
||
<label class="inline small"><input type="radio" name="dc_mat_mode" value="date" checked> 指定日期</label>
|
||
<label class="inline small" style="margin-left:10px"><input type="radio" name="dc_mat_mode" value="after"> 见票后定期</label>
|
||
</div>
|
||
</div>
|
||
<div class="fin-row" id="dc_row_mat_date">
|
||
<label for="dc_maturity">到期日期</label>
|
||
<input id="dc_maturity" type="date">
|
||
</div>
|
||
<div class="fin-row" id="dc_row_mat_after" style="display:none">
|
||
<label for="dc_months">期限(月)</label>
|
||
<div>
|
||
<input id="dc_months" type="number" min="1" max="12" step="1" value="6" style="width:80px">
|
||
<button type="button" class="toolbar-btn" id="dc_apply_months" style="margin-left:8px">推算到期日</button>
|
||
<span class="fin-muted" id="dc_mat_preview"></span>
|
||
</div>
|
||
</div>
|
||
<div class="fin-row">
|
||
<label for="dc_discount">贴现日期</label>
|
||
<input id="dc_discount" type="date">
|
||
</div>
|
||
<div class="fin-row">
|
||
<label class="inline"><input type="checkbox" id="dc_holiday"> 到期日遇双休日/法定节假日顺延至下一工作日</label>
|
||
</div>
|
||
<h4 style="margin:16px 0 10px">贴现参数</h4>
|
||
<div class="fin-row">
|
||
<label for="dc_rate">贴现年利率 %</label>
|
||
<div>
|
||
<input id="dc_rate" type="text" inputmode="decimal" style="width:100px">
|
||
<button type="button" class="toolbar-btn" data-ref="1.85" style="margin-left:6px">国股参考</button>
|
||
<button type="button" class="toolbar-btn" data-ref="2.15">城商参考</button>
|
||
<button type="button" class="toolbar-btn" data-ref="2.45">农商参考</button>
|
||
</div>
|
||
</div>
|
||
<div class="fin-row">
|
||
<label>计息基准</label>
|
||
<span>360 天/年(票据贴现常用)</span>
|
||
</div>
|
||
<div class="fin-row">
|
||
<label class="inline"><input type="checkbox" id="dc_remote"> 异地(+3 天在途)</label>
|
||
</div>
|
||
<div class="fin-row">
|
||
<label for="dc_day_rule">计息方式</label>
|
||
<select id="dc_day_rule">
|
||
<option value="exclusive" selected>记首不记尾(贴现日至到期前一日)</option>
|
||
<option value="inclusive">记首记尾(对比用)</option>
|
||
</select>
|
||
</div>
|
||
<h4 style="margin:16px 0 10px">反向推算(选填其一)</h4>
|
||
<div class="fin-row">
|
||
<label for="dc_net_in">实付金额(元)</label>
|
||
<input id="dc_net_in" type="text" inputmode="decimal" placeholder="反推利率">
|
||
</div>
|
||
<div class="fin-row">
|
||
<label for="dc_per10">每10万扣息(元)</label>
|
||
<input id="dc_per10" type="text" inputmode="decimal" placeholder="反推利率">
|
||
</div>
|
||
<div class="fin-actions">
|
||
<button type="button" class="toolbar-btn primary" id="dc_calc">计算</button>
|
||
<button type="button" class="toolbar-btn" id="dc_copy">复制结果</button>
|
||
<button type="button" class="toolbar-btn" id="dc_save">保存记录</button>
|
||
<button type="button" class="btn-gray" id="dc_reset">重置</button>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="fin-result-card" id="dc_result">
|
||
<h4 style="margin:0 0 10px">计算结果</h4>
|
||
<p id="dc_result_placeholder" class="fin-muted" style="margin:0">填写左侧并点击「计算」</p>
|
||
<div id="dc_result_body" style="display:none;font-size:13px;line-height:1.65">
|
||
<p style="margin:0"><strong>贴现天数</strong>:<span id="dc_out_days">—</span> <span id="dc_out_day_note" class="fin-muted"></span></p>
|
||
<p style="margin:8px 0 0"><strong>贴现利息</strong>:<span class="fin-out-num" style="font-size:18px" id="dc_out_int">—</span> 元</p>
|
||
<p style="margin:8px 0 0"><strong>实付金额</strong>:<span id="dc_out_net">—</span> 元</p>
|
||
<p style="margin:8px 0 0"><strong>年化成本率</strong>:<span id="dc_out_ann">—</span></p>
|
||
<p style="margin:8px 0 0"><strong>每10万扣息</strong>:<span id="dc_out_p10">—</span> 元</p>
|
||
<p style="margin:8px 0 0" id="dc_rev_line" class="fin-muted"></p>
|
||
</div>
|
||
</div>
|
||
<div class="fin-panel" style="margin-top:12px">
|
||
<h4 style="margin:0 0 8px">本地历史记录</h4>
|
||
<p class="fin-muted" style="margin:0 0 8px">保存在本浏览器,可对比多次试算。</p>
|
||
<ul id="dc_hist_list" style="margin:0;padding-left:18px;font-size:12px;max-height:220px;overflow:auto"></ul>
|
||
<button type="button" class="btn-gray" id="dc_hist_clear" style="margin-top:8px">清空历史</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
(function(){
|
||
var H2025=['2025-01-01','2025-01-28','2025-01-29','2025-01-30','2025-01-31','2025-02-01','2025-02-02','2025-02-03','2025-02-04','2025-04-04','2025-04-05','2025-04-06','2025-05-01','2025-05-02','2025-05-03','2025-05-04','2025-05-05','2025-05-31','2025-06-01','2025-06-02','2025-10-01','2025-10-02','2025-10-03','2025-10-04','2025-10-05','2025-10-06','2025-10-07','2025-10-08'];
|
||
var H2026=['2026-01-01','2026-01-02','2026-01-03','2026-02-15','2026-02-16','2026-02-17','2026-02-18','2026-02-19','2026-02-20','2026-02-21','2026-02-22','2026-02-23','2026-04-04','2026-04-05','2026-04-06','2026-05-01','2026-05-02','2026-05-03','2026-05-04','2026-05-05','2026-06-19','2026-06-20','2026-06-21','2026-09-25','2026-09-26','2026-09-27','2026-10-01','2026-10-02','2026-10-03','2026-10-04','2026-10-05','2026-10-06','2026-10-07'];
|
||
var HSET={}; [].concat(H2025,H2026).forEach(function(x){HSET[x]=1;});
|
||
function pad(n){return (n<10?'0':'')+n;}
|
||
function ymd(d){return d.getFullYear()+'-'+pad(d.getMonth()+1)+'-'+pad(d.getDate());}
|
||
function parseYmd(s){
|
||
var p=s.split('-'); if(p.length!==3) return null;
|
||
var d=new Date(+p[0],+p[1]-1,+p[2]); return isNaN(d.getTime())?null:d;
|
||
}
|
||
function isWeekend(d){var w=d.getDay(); return w===0||w===6;}
|
||
function isHoliday(d){return !!HSET[ymd(d)];}
|
||
function isNonWork(d){return isWeekend(d)||isHoliday(d);}
|
||
function nextWorkdayFrom(ymdStr){
|
||
var d=parseYmd(ymdStr); if(!d) return ymdStr;
|
||
for(var i=0;i<20;i++){
|
||
if(!isNonWork(d)) return ymd(d);
|
||
d.setDate(d.getDate()+1);
|
||
}
|
||
return ymd(d);
|
||
}
|
||
function addMonthsKeepDom(y,m,day,addM){
|
||
var nm=m-1+addM, ny=y+Math.floor(nm/12), nmo=((nm%12)+12)%12;
|
||
var last=new Date(ny,nmo+1,0).getDate();
|
||
var nd=Math.min(day,last);
|
||
return new Date(ny,nmo,nd);
|
||
}
|
||
function syncBillHint(){
|
||
var v=document.querySelector('input[name="dc_bill"]:checked').value;
|
||
document.getElementById('dc_risk_hint').textContent=v==='bank'?'银承:流通性较好,贴现利率通常低于商承。':'商承:请重点评估承兑人信用;贴现利率通常高于银承。';
|
||
}
|
||
document.querySelectorAll('input[name="dc_bill"]').forEach(function(r){r.addEventListener('change',syncBillHint);});
|
||
syncBillHint();
|
||
function parseNum(s){s=String(s||'').replace(/,/g,'').trim(); if(s==='')return NaN; var v=parseFloat(s); return isFinite(v)?v:NaN;}
|
||
function fmtMoney(n){if(!isFinite(n))return''; var p=(Math.round(n*100)/100).toFixed(2).split('.'); p[0]=p[0].replace(/\B(?=(\d{3})+(?!\d))/g,','); return p.join('.');}
|
||
var DIG='零壹贰叁肆伍陆柒捌玖', UNIT=['','拾','佰','仟'];
|
||
function sectionFour(n){var s='',str=('0000'+n).slice(-4); for(var i=0;i<4;i++){var d=+str[i]; if(d===0){s+=DIG[0];continue;} s=s.replace(/零+$/,''); s+=DIG[d]+UNIT[3-i];} return (s.replace(/零+/g,'零').replace(/零$/,''))||DIG[0];}
|
||
function toRmbUpper(x){if(!isFinite(x)||x<=0)return''; var yi=Math.floor(x/1e8),wan=Math.floor((x%1e8)/1e4),ge=Math.floor(x%1e4); var out=''; if(yi>0)out+=sectionFour(yi)+'亿'; if(wan>0||(yi>0&&ge>0))out+=sectionFour(wan)+'万'; return (out+sectionFour(ge)+'元整').replace(/^壹拾/g,'拾');}
|
||
function syncFace(){var v=parseNum(document.getElementById('dc_face').value); document.getElementById('dc_face_fmt').textContent=isFinite(v)&&v>0?('格式化:'+fmtMoney(v)):'';
|
||
document.getElementById('dc_face_cap').textContent=isFinite(v)&&v>0?('大写:'+toRmbUpper(Math.floor(v))):'';}
|
||
document.getElementById('dc_face').addEventListener('input',syncFace);
|
||
document.querySelectorAll('input[name="dc_mat_mode"]').forEach(function(r){
|
||
r.addEventListener('change',function(){
|
||
var m=document.querySelector('input[name="dc_mat_mode"]:checked').value;
|
||
document.getElementById('dc_row_mat_date').style.display=m==='date'?'':'none';
|
||
document.getElementById('dc_row_mat_after').style.display=m==='after'?'':'none';
|
||
});
|
||
});
|
||
document.getElementById('dc_apply_months').addEventListener('click',function(){
|
||
var iss=document.getElementById('dc_issue').value; if(!iss){alert('请先填出票日');return;}
|
||
var mo=parseInt(document.getElementById('dc_months').value,10); if(!(mo>=1&&mo<=12)){alert('期限月数须为1-12');return;}
|
||
var p=iss.split('-').map(Number); var d=addMonthsKeepDom(p[0],p[1],p[2],mo);
|
||
document.getElementById('dc_maturity').value=ymd(d);
|
||
document.getElementById('dc_mat_preview').textContent='推算到期:'+ymd(d);
|
||
});
|
||
document.querySelectorAll('[data-ref]').forEach(function(btn){
|
||
btn.addEventListener('click',function(){ document.getElementById('dc_rate').value=this.getAttribute('data-ref'); });
|
||
});
|
||
if(!document.getElementById('dc_discount').value){
|
||
var t=new Date(); document.getElementById('dc_discount').value=ymd(t);
|
||
}
|
||
function effectiveMaturity(){
|
||
var m=document.querySelector('input[name="dc_mat_mode"]:checked').value;
|
||
var mat=document.getElementById('dc_maturity').value;
|
||
if(m==='after' && !mat){ alert('请先「推算到期日」或改选指定到期日'); return null; }
|
||
if(!mat){ alert('请填写到期日'); return null; }
|
||
if(document.getElementById('dc_holiday').checked) return nextWorkdayFrom(mat);
|
||
return mat;
|
||
}
|
||
function discountDays(dis, matEff, rule, remote){
|
||
var d0=parseYmd(dis), d1=parseYmd(matEff);
|
||
if(!d0||!d1||d1<=d0) return NaN;
|
||
var diff=Math.round((d1-d0)/86400000);
|
||
var base=rule==='inclusive'?diff+1:diff;
|
||
if(base<0) return NaN;
|
||
var extra=remote?3:0;
|
||
return base+extra;
|
||
}
|
||
function calcForward(face,ratePct,days){
|
||
return face*(ratePct/100)*(days/360);
|
||
}
|
||
function rateFromNet(face,net,days){
|
||
if(!(face>0)||!(net>0)||!(days>0)||net>=face) return NaN;
|
||
return (face-net)*360/(face*days)*100;
|
||
}
|
||
function calc(){
|
||
document.getElementById('dc_rev_line').textContent='';
|
||
var face=parseNum(document.getElementById('dc_face').value);
|
||
var dis=document.getElementById('dc_discount').value;
|
||
var matEff=effectiveMaturity();
|
||
var rule=document.getElementById('dc_day_rule').value;
|
||
var remote=document.getElementById('dc_remote').checked;
|
||
var days=discountDays(dis,matEff,rule,remote);
|
||
if(!(face>0)){ alert('票面金额须大于0'); return; }
|
||
if(!dis||!matEff){ alert('请完善贴现日与到期日'); return; }
|
||
if(!isFinite(days)||days<=0){ alert('贴现日须早于到期日,且计息天数须为正'); return; }
|
||
var netIn=parseNum(document.getElementById('dc_net_in').value);
|
||
var per10=parseNum(document.getElementById('dc_per10').value);
|
||
var rate=parseNum(document.getElementById('dc_rate').value);
|
||
var intr;
|
||
if(isFinite(netIn)&&netIn>0&&netIn<face){
|
||
rate=rateFromNet(face,netIn,days);
|
||
if(!isFinite(rate)||!(rate>0)){ alert('无法根据实付金额反推利率,请检查天数与金额'); return; }
|
||
intr=face-netIn;
|
||
document.getElementById('dc_rate').value=rate.toFixed(4);
|
||
document.getElementById('dc_rev_line').textContent='已按实付金额反推贴现年利率:'+rate.toFixed(4)+'%';
|
||
} else if(isFinite(per10)&&per10>0){
|
||
intr=per10*(face/100000);
|
||
rate=(intr*360/(face*days))*100;
|
||
if(!isFinite(rate)||!(rate>0)){ alert('无法根据每10万扣息反推利率'); return; }
|
||
document.getElementById('dc_rate').value=rate.toFixed(4);
|
||
document.getElementById('dc_rev_line').textContent='已按每10万扣息反推贴现年利率:'+rate.toFixed(4)+'%';
|
||
} else {
|
||
if(!(rate>0)){ alert('请输入贴现年利率,或填写反推字段'); return; }
|
||
intr=calcForward(face,rate,days);
|
||
}
|
||
var net=face-intr;
|
||
var ann=net>0 ? (intr/net)*(360/days)*100 : NaN;
|
||
var p10=face>0? intr/(face/100000):NaN;
|
||
document.getElementById('dc_out_days').textContent=String(days);
|
||
document.getElementById('dc_out_day_note').textContent=(remote?'(含异地+3天)':'')+(document.getElementById('dc_holiday').checked?';到期日已按工作日顺延':'');
|
||
document.getElementById('dc_out_int').textContent=fmtMoney(intr);
|
||
document.getElementById('dc_out_net').textContent=fmtMoney(net);
|
||
document.getElementById('dc_out_ann').textContent=isFinite(ann)?(ann.toFixed(4)+'%'):'—';
|
||
document.getElementById('dc_out_p10').textContent=isFinite(p10)?fmtMoney(p10):'—';
|
||
document.getElementById('dc_result_placeholder').style.display='none';
|
||
document.getElementById('dc_result_body').style.display='block';
|
||
window._dc_last={face:face,intr:intr,net:net,days:days,w:face/10000};
|
||
}
|
||
function copyRes(){
|
||
if(!window._dc_last){ alert('请先计算'); return; }
|
||
var w=window._dc_last.w, t='票面'+w.toFixed(2)+'万 | 贴现息'+fmtMoney(window._dc_last.intr)+'元 | 实付'+fmtMoney(window._dc_last.net)+'元 | 天数'+window._dc_last.days+'天';
|
||
if(navigator.clipboard) navigator.clipboard.writeText(t).then(function(){alert('已复制');},function(){alert(t);});
|
||
else alert(t);
|
||
}
|
||
function saveHist(){
|
||
if(!window._dc_last){ alert('请先计算'); return; }
|
||
var key='dc_hist_v1', arr=JSON.parse(localStorage.getItem(key)||'[]');
|
||
arr.unshift({t:Date.now(),...window._dc_last,rate:document.getElementById('dc_rate').value});
|
||
arr=arr.slice(0,30); localStorage.setItem(key,JSON.stringify(arr)); renderHist();
|
||
}
|
||
function renderHist(){
|
||
var arr=JSON.parse(localStorage.getItem('dc_hist_v1')||'[]');
|
||
var ul=document.getElementById('dc_hist_list'); ul.innerHTML='';
|
||
arr.forEach(function(r,i){
|
||
var li=document.createElement('li');
|
||
li.textContent=new Date(r.t).toLocaleString()+' — 实付 '+fmtMoney(r.net)+' / 息 '+fmtMoney(r.intr)+' / '+r.days+'天 / 利率'+r.rate+'%';
|
||
ul.appendChild(li);
|
||
});
|
||
}
|
||
document.getElementById('dc_calc').addEventListener('click',calc);
|
||
document.getElementById('dc_copy').addEventListener('click',copyRes);
|
||
document.getElementById('dc_save').addEventListener('click',saveHist);
|
||
document.getElementById('dc_hist_clear').addEventListener('click',function(){ localStorage.removeItem('dc_hist_v1'); renderHist(); });
|
||
document.getElementById('dc_reset').addEventListener('click',function(){
|
||
document.getElementById('dc_face').value=''; document.getElementById('dc_rate').value='';
|
||
document.getElementById('dc_issue').value=''; document.getElementById('dc_maturity').value='';
|
||
document.getElementById('dc_net_in').value=''; document.getElementById('dc_per10').value='';
|
||
syncFace(); document.getElementById('dc_result_body').style.display='none';
|
||
document.getElementById('dc_result_placeholder').style.display='block';
|
||
});
|
||
renderHist();
|
||
})();
|
||
</script>
|