更新日志
点击查看更新日志
2026/01/29 20:30 针对10楼反馈问题优化更新
[!info]
1.新增“到期日期”输入框:虽然系统会根据周期自动计算一个默认到期日,但您现在可以手动修改它。
2.重构价值算法:剩余价值不再受限于“单倍周期价格”。现在的算法是:(周期价格 ÷ 周期天数)× 实际剩余天数。
计算器web界面


WEB截图模板

Markdown 模板
🛒 [出售] 交易详情
| 项目 | 内容 | 备注 |
|---|---|---|
| 📦 商品 | 测试 | |
| 💰 售价 | ¥4.62 | 原价转让 |
| 🏷️ 原价 | €1.00 | (≈ ¥8.33) 汇率 8.33 |
| 💰 日均 | ¥0.27 | 日均消费 |
| 📉 剩余 | 价值 ¥4.62 | 剩 17 天 / 已用 13 天 |
| 📅 周期 | 月付 | 到期 2026-02-15 |
| 📦 交付 | 站内 Push | 带原邮 |
| 💳 支付 | 支付宝口令 |
🖥️ 硬件配置
CPU:E5-2690 v2
内存:12G
硬盘:500G
流量:50T
Generated by VPS Calculator | 2026/1/29 03:23:08
源代码
点击查看代码
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<title>VPS 交易计算器 v19</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/3.5.0/remixicon.min.css" rel="stylesheet">
<style>
:root {
--bg: #050505;
--surface: #121214;
--surface-light: #1c1c1f;
--border: #2e2e33;
--text-main: #f4f4f5;
--text-dim: #a1a1aa;
--accent: #3b82f6;
--accent-grad: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
--radius: 12px;
--font: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "JetBrains Mono", Consolas, monospace;
}
body {
margin: 0; background-color: var(--bg); color: var(--text-main);
font-family: var(--font); display: flex; justify-content: center;
padding: 40px 20px; min-height: 100vh; box-sizing: border-box;
}
.main-grid {
display: grid; grid-template-columns: 1fr; gap: 40px;
width: 100%; max-width: 1150px; align-items: start;
}
@media(min-width: 960px) { .main-grid { grid-template-columns: 380px 1fr; } }
/* === 输入区 === */
.editor-panel {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 24px;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.panel-head {
font-size: 1.1rem; font-weight: 700; margin-bottom: 24px;
padding-bottom: 15px; border-bottom: 1px solid var(--border);
display: flex; align-items: center; gap: 8px; color: #fff;
}
.f-group { margin-bottom: 18px; }
/* 优化 Label 显示 */
.label-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
label { display: block; font-size: 0.85rem; color: var(--text-dim); font-weight: 500; margin: 0; }
.label-tip { font-size: 0.75rem; color: var(--accent); opacity: 0.9; font-weight: 400; }
input, select, textarea {
width: 100%; box-sizing: border-box;
background: var(--surface-light); border: 1px solid var(--border);
color: var(--text-main); padding: 12px; border-radius: 8px;
font-size: 0.95rem; transition: 0.2s; font-family: inherit;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); background: #27272a; }
textarea { resize: vertical; min-height: 110px; line-height: 1.6; }
.row-split { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: end; }
.input-combine { display: flex; }
.input-combine input { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right: 0; }
.input-combine select { width: 85px; border-top-left-radius: 0; border-bottom-left-radius: 0; background: #27272a; flex: none; }
.mode-switch {
background: var(--surface-light); padding: 4px; border-radius: 8px;
display: flex; border: 1px solid var(--border); margin-bottom: 18px;
}
.switch-btn {
flex: 1; border: none; background: none; color: var(--text-dim);
padding: 8px; font-size: 0.9rem; cursor: pointer; border-radius: 6px;
transition: 0.2s; font-weight: 500;
}
.switch-btn.active { background: #3f3f46; color: #fff; font-weight: 600; }
.btn-calc {
width: 100%; background: var(--accent-grad); color: white; border: none;
padding: 14px; border-radius: 8px; font-weight: 600; font-size: 1.05rem;
cursor: pointer; margin-top: 10px; transition: transform 0.1s, opacity 0.2s;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.btn-calc:hover { opacity: 0.9; transform: translateY(-1px); }
.highlight-label { color: var(--accent); font-weight: 600; }
/* === 预览区 === */
.preview-col { display: flex; flex-direction: column; align-items: center; gap: 24px; position: relative; }
#renderNode {
width: 100%; max-width: 600px;
background: #09090b;
border: 1px solid #27272a;
border-radius: 20px;
padding: 40px;
box-sizing: border-box;
position: relative; overflow: hidden;
box-shadow: 0 30px 60px -15px rgba(0,0,0,0.6);
display: none;
}
.wm-grid {
position: absolute; inset: -50%; width: 200%; height: 200%;
display: flex; flex-wrap: wrap; justify-content: center; align-content: center;
gap: 100px 80px; transform: rotate(-20deg);
pointer-events: none;
opacity: 0.04;
z-index: 99;
}
.wm-txt { font-size: 2rem; font-weight: 900; color: #fff; white-space: nowrap; }
.card-inner { position: relative; z-index: 1; }
/* 头部 */
.c-head { display: flex; justify-content: space-between; align-items: start; margin-bottom: 30px; }
.c-title { font-size: 2.2rem; font-weight: 800; color: #fff; line-height: 1.1; letter-spacing: -0.03em; }
.c-meta { display: flex; gap: 10px; margin-top: 14px; }
.tag { font-size: 0.85rem; padding: 6px 14px; background: #27272a; color: #e4e4e7; border-radius: 8px; font-weight: 600; display: inline-flex; align-items: center; gap: 6px; }
/* 价格面板 */
.price-grid {
background: rgba(255,255,255,0.02);
border: 1px solid #27272a; border-radius: 16px;
margin-bottom: 30px;
overflow: hidden;
}
.pg-top {
padding: 24px;
display: grid; grid-template-columns: 1fr 1fr; gap: 24px;
}
.price-item { display: flex; flex-direction: column; justify-content: space-between; }
.price-item.right { align-items: flex-end; text-align: right; }
.p-lbl {
font-size: 0.85rem; color: #a1a1aa; margin-bottom: 8px;
text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700;
display: flex; align-items: center; gap: 6px;
}
.p-lbl i { font-size: 1rem; color: #71717a; }
.p-val-big { font-family: var(--font-mono); font-size: 2.2rem; font-weight: 700; color: #fff; letter-spacing: -0.05em; line-height: 1; }
.p-val-sub {
font-size: 1.1rem;
color: #71717a;
margin-top: 10px;
font-family: var(--font-mono);
font-weight: 500;
}
.delta-badge {
font-size: 0.9rem; font-weight: 600; padding: 5px 12px; border-radius: 6px;
display: inline-block; margin-top: 8px;
}
.remain-row {
background: rgba(255, 255, 255, 0.03);
border-top: 1px solid #27272a;
padding: 20px 24px;
display: flex; justify-content: space-between; align-items: center;
}
.p-val-mid { font-family: var(--font-mono); font-size: 1.8rem; font-weight: 800; color: #fff; letter-spacing: -1px; }
/* 硬件配置 */
.spec-box {
background: rgba(0, 0, 0, 0.6);
border: 1px solid #27272a;
border-radius: 12px;
padding: 24px;
margin-bottom: 30px;
font-size: 1.05rem; color: #d4d4d8; line-height: 1.7;
white-space: pre-wrap; font-family: var(--font-mono);
position: relative;
z-index: 1; /* 低于水印 */
}
.spec-box::before {
content: ""; position: absolute; top: 12px; left: 12px;
width: 8px; height: 8px; border-radius: 50%;
background: #3f3f46; box-shadow: 14px 0 0 #3f3f46, 28px 0 0 #3f3f46;
opacity: 0.5;
}
/* 进度条 */
.time-wrap { margin-bottom: 30px; }
.time-info {
display: flex; justify-content: space-between;
font-size: 0.95rem; color: #a1a1aa; margin-bottom: 12px;
font-family: var(--font-mono); font-weight: 500;
}
.prog-bg { height: 10px; background: #27272a; border-radius: 5px; overflow: hidden; }
.prog-fg { height: 100%; background: #52525b; width: 0; border-radius: 5px; }
/* 详情列表 */
.info-list {
display: grid; grid-template-columns: 1fr 1fr; gap: 30px 20px; margin-bottom: 35px;
padding-top: 30px; border-top: 1px solid #27272a;
}
.ii-lbl {
font-size: 0.85rem; color: #71717a; margin-bottom: 8px;
display: flex; align-items: center; gap: 6px; font-weight: 700; text-transform: uppercase;
}
.ii-val { font-size: 1.2rem; color: #fff; font-weight: 600; display: block; }
/* 底部 */
.c-foot {
display: flex; justify-content: space-between; align-items: center;
opacity: 0.7;
}
.sign-main { font-size: 0.95rem; font-weight: 600; color: #71717a; display: flex; align-items: center; gap: 8px; }
.time-stamp { font-size: 0.95rem; font-weight: 600; color: #71717a; font-family: var(--font-mono); }
/* 操作栏 */
.dock-actions {
display: flex; gap: 12px; width: 100%; max-width: 600px;
background: #18181b; padding: 10px; border-radius: 14px;
border: 1px solid #27272a; display: none; margin-top: -10px; z-index: 100;
}
.act-btn {
flex: 1; display: flex; align-items: center; justify-content: center; gap: 8px;
background: transparent; border: none; padding: 12px;
color: #a1a1aa; border-radius: 10px; cursor: pointer;
font-size: 0.95rem; font-weight: 600; transition: all 0.2s;
}
.act-btn:hover { background: #27272a; color: #fff; }
.act-btn.primary { background: #27272a; color: #fff; }
.toast {
position: fixed; top: 40px; left: 50%; transform: translateX(-50%) translateY(-20px);
background: #fff; color: #000; padding: 12px 24px; border-radius: 50px;
font-weight: 600; font-size: 0.95rem; box-shadow: 0 10px 30px rgba(0,0,0,0.5);
opacity: 0; pointer-events: none; transition: 0.3s; z-index: 999;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
</style>
</head>
<body>
<div class="toast" id="toast">✅ 已复制</div>
<div class="main-grid">
<div class="editor-panel">
<div class="panel-head"><i class="ri-calculator-line"></i> VPS 交易计算器 v19</div>
<form onsubmit="event.preventDefault(); runCalc();">
<div class="f-group">
<label>商品名称</label>
<input type="text" id="iName" placeholder="例如:NetCup RS 1000 G11" required>
</div>
<div class="f-group">
<label>硬件配置 / 备注 (显示在截图中间)</label>
<textarea id="iConfig" placeholder="例如:
CPU: 4 vCore EPYC 9634
RAM: 8GB DDR5
DISK: 160GB NVMe"></textarea>
</div>
<div class="row-split">
<div class="f-group">
<label>续费价格 (原价)</label>
<div class="input-combine">
<input type="number" id="iPrice" placeholder="10.00" step="0.01" required>
<select id="iCur"></select>
</div>
</div>
<div class="f-group">
<label>续费周期</label>
<select id="iPeriod" onchange="autoCalcEnd()">
<option value="M1" selected>月付</option>
<option value="M3">季付</option>
<option value="M6">半年</option>
<option value="Y1">年付</option>
<option value="Y2">两年</option>
<option value="Y3">三年</option>
</select>
</div>
</div>
<div class="row-split">
<div class="f-group">
<div class="label-row">
<label>起算日期</label>
</div>
<input type="date" id="iDate" required onchange="autoCalcEnd()">
</div>
<div class="f-group">
<div class="label-row">
<label class="highlight-label">到期日期(自动计算/可改)</label>
</div>
<input type="date" id="iEndDate" required>
</div>
</div>
<div class="row-split">
<div class="f-group">
<label>自定义汇率</label>
<input type="number" id="iRate" placeholder="留空自动" step="0.01">
</div>
<div class="f-group">
<label>支付方式</label>
<input type="text" id="iPay" list="payList" value="支付宝口令" placeholder="输入或选择...">
<datalist id="payList">
<option value="支付宝口令">
<option value="微信支付">
<option value="USDT (TRC20)">
<option value="USDT (ERC20)">
<option value="PayPal (F&F)">
<option value="混合支付 (CNY+USDT)">
</datalist>
</div>
</div>
<div class="row-split">
<div class="f-group">
<label>原邮状态</label>
<select id="iEmail">
<option>带原邮</option>
<option>无原邮</option>
<option>别名邮箱</option>
<option>仅改邮箱</option>
</select>
</div>
<div class="f-group">
<label>交付方式</label>
<select id="iTrans">
<option>站内 Push</option>
<option>带号出 (With Account)</option>
<option>账号密码 (Account)</option>
<option>工单过户 (Ticket)</option>
<option>API Key</option>
</select>
</div>
</div>
<div class="mode-switch">
<button type="button" class="switch-btn active" onclick="setMode('exact')">精确售价</button>
<button type="button" class="switch-btn" onclick="setMode('delta')">溢价模式</button>
</div>
<div class="f-group">
<label id="lblAsk">期望售价 (CNY)</label>
<input type="number" id="iAsk" placeholder="留空则默认为剩余价值">
</div>
<div class="f-group">
<label>水印内容</label>
<input type="text" id="iWm" value="仅供交易参考">
</div>
<div class="f-group">
<label>底部签名</label>
<input type="text" id="iSign" value="Generated by VPS Calculator">
</div>
<button class="btn-calc">生成交易卡片</button>
</form>
</div>
<div class="preview-col">
<div id="renderNode">
<div class="wm-grid" id="wmLayer"></div>
<div class="card-inner">
<div class="c-head">
<div>
<div class="c-title" id="oName">Server Name</div>
<div class="c-meta">
<span class="tag"><i class="ri-calendar-event-fill"></i> <span id="oPeriod">Monthly</span></span>
<span class="tag"><i class="ri-arrow-left-right-fill"></i> <span id="oTrans">Push</span></span>
</div>
</div>
</div>
<div class="price-grid">
<div class="pg-top">
<div class="price-item">
<div class="p-lbl"><i class="ri-price-tag-3-fill"></i> 续费原价</div>
<div class="p-val-big" id="oPriceDisplay">¥30.00</div>
<div class="p-val-sub" id="oRateInfo">汇率 1.00</div>
</div>
<div class="price-item right">
<div class="p-lbl"><i class="ri-hand-coin-fill"></i> 期望售价</div>
<div class="p-val-big" id="oAsk">¥30.00</div>
<div class="delta-badge" id="oDelta">原价转让</div>
</div>
</div>
<div class="remain-row">
<div class="p-lbl" style="margin:0; color:#d4d4d8"><i class="ri-hourglass-fill"></i> 剩余价值 (参考)</div>
<div class="p-val-mid" id="oRemain">¥30.00</div>
</div>
</div>
<div id="oConfigBox" class="spec-box" style="display:none"></div>
<div class="time-wrap">
<div class="time-info">
<span id="oStart"><i class="ri-play-circle-fill"></i> Start</span>
<span id="oUsedTime" style="color:#f4f4f5">Used</span>
<span id="oEnd">End <i class="ri-stop-circle-fill"></i></span>
</div>
<div class="prog-bg"><div class="prog-fg" id="oBar"></div></div>
</div>
<div class="info-list">
<div class="info-item">
<span class="ii-lbl"><i class="ri-mail-fill"></i> 原邮状态</span>
<span class="ii-val" id="oEmail">-</span>
</div>
<div class="info-item">
<span class="ii-lbl"><i class="ri-alipay-fill"></i> 支付方式</span>
<span class="ii-val" id="oPay">-</span>
</div>
<div class="info-item">
<span class="ii-lbl"><i class="ri-pie-chart-2-fill"></i> 日均成本</span>
<span class="ii-val" id="oDaily">-</span>
</div>
<div class="info-item">
<span class="ii-lbl"><i class="ri-exchange-dollar-fill"></i> 计算汇率</span>
<span class="ii-val" id="oRate">-</span>
</div>
</div>
<div class="c-foot">
<span class="sign-main"><i class="ri-shield-check-fill"></i> <span id="oSign">Sign</span></span>
<span class="time-stamp" id="oTime">Time</span>
</div>
</div>
</div>
<div class="dock-actions" id="actionDock">
<button class="act-btn primary" onclick="dlImg()">
<i class="ri-download-2-line"></i> 保存图片 (含复制)
</button>
<button class="act-btn" onclick="cpMd()">
<i class="ri-markdown-line"></i> Markdown
</button>
<button class="act-btn" onclick="cpTxt()">
<i class="ri-file-copy-line"></i> 纯文本
</button>
</div>
<div id="tipPh" style="color:var(--text-dim); margin-top:50px;">
<i class="ri-arrow-left-line"></i> 请填写左侧信息开始计算
</div>
</div>
</div>
<script>
const CURS = ["USD","CNY","EUR","GBP","HKD","JPY"];
// m: months for calculation, l: label
const PERIODS = {
M1:{m:1,l:"月付"}, M3:{m:3,l:"季付"}, M6:{m:6,l:"半年"},
Y1:{m:12,l:"年付"}, Y2:{m:24,l:"两年"}, Y3:{m:36,l:"三年"}
};
let STATE = { rates: {}, data: null, mode: 'exact' };
const $ = id => document.getElementById(id);
(async() => {
$('iCur').innerHTML = CURS.map(c=>`<option ${c==='EUR'?'selected':''}>${c}</option>`).join('');
// Set default dates
const today = new Date();
$('iDate').valueAsDate = today;
autoCalcEnd(); // Init end date based on default period
try {
let r = await fetch("https://open.er-api.com/v6/latest/CNY");
let j = await r.json();
STATE.rates = j.rates;
} catch(e){}
})();
// Auto-calculate End Date when Start Date or Period changes
function autoCalcEnd() {
const startStr = $('iDate').value;
if(!startStr) return;
const start = new Date(startStr);
const pCode = $('iPeriod').value;
const pMonth = PERIODS[pCode].m;
const end = new Date(start);
end.setMonth(end.getMonth() + pMonth);
$('iEndDate').valueAsDate = end;
}
function setMode(m) {
STATE.mode = m;
document.querySelectorAll('.switch-btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
$('lblAsk').textContent = m === 'exact' ? "期望售价 (CNY)" : "溢价金额 (CNY)";
$('iAsk').placeholder = m === 'exact' ? "留空则默认为剩余价值" : "+50 (正数溢价)";
}
function runCalc() {
const name = $('iName').value;
const price = parseFloat($('iPrice').value);
const cur = $('iCur').value;
const startDateStr = $('iDate').value;
const endDateStr = $('iEndDate').value;
if(!name || !price || !startDateStr || !endDateStr) return toast("❌ 请填写完整信息");
let rate = parseFloat($('iRate').value);
if(!rate) rate = cur==='CNY'?1 : (STATE.rates[cur] ? 1/STATE.rates[cur] : 1);
const start = new Date(startDateStr);
const end = new Date(endDateStr);
const now = new Date();
const pCode = $('iPeriod').value;
const pData = PERIODS[pCode];
// === 核心逻辑 ===
const cycleDurationMs = pData.m * 30.44 * 24 * 60 * 60 * 1000;
const totalSpanMs = end - start;
let usedMs = now - start;
let remainMs = end - now;
if (usedMs < 0) { usedMs = 0; remainMs = totalSpanMs; } // Start is in future
const priceCNY = price * rate;
const dailyCost = priceCNY / (cycleDurationMs / 86400000);
const remainValRaw = (remainMs / 86400000) * dailyCost;
const remainVal = Math.max(0, remainValRaw);
const percent = Math.min(100, Math.max(0, (usedMs / totalSpanMs) * 100));
const totalDays = Math.max(1, totalSpanMs / 86400000);
const remainDays = Math.max(0, Math.floor(remainMs / 86400000));
const usedDays = Math.max(0, Math.floor(usedMs / 86400000));
let askInput = parseFloat($('iAsk').value);
let finalAsk = remainVal;
let delta = 0;
if(isNaN(askInput)) {
finalAsk = remainVal;
delta = 0;
} else {
if(STATE.mode === 'exact') {
finalAsk = askInput;
delta = finalAsk - remainVal;
} else {
delta = askInput;
finalAsk = remainVal + delta;
}
}
STATE.data = {
name, price, cur, rate, priceCNY,
start, end, remainDays, usedDays, percent,
remainVal, finalAsk, delta,
daily: dailyCost,
period: pData.l,
config: $('iConfig').value,
email: $('iEmail').value,
trans: $('iTrans').value,
pay: $('iPay').value,
wm: $('iWm').value,
sign: $('iSign').value
};
render();
}
const fmt = (n, c="CNY") => new Intl.NumberFormat('zh-CN', {style:'currency', currency:c}).format(n);
function render() {
const d = STATE.data;
$('renderNode').style.display = "block";
$('actionDock').style.display = "flex";
$('tipPh').style.display = "none";
const wm = $('wmLayer');
wm.innerHTML = "";
for(let i=0; i<40; i++) {
let s = document.createElement("div");
s.className = "wm-txt";
s.textContent = d.wm;
wm.appendChild(s);
}
$('oName').textContent = d.name;
$('oPeriod').textContent = d.period;
$('oTrans').textContent = d.trans;
if(d.config.trim()) {
$('oConfigBox').textContent = d.config;
$('oConfigBox').style.display = "block";
} else {
$('oConfigBox').style.display = "none";
}
// === 价格渲染 ===
$('oPriceDisplay').textContent = fmt(d.price, d.cur);
$('oRateInfo').textContent = d.cur === 'CNY' ? "汇率 1.00" : `≈ ${fmt(d.priceCNY)} (汇率 ${d.rate.toFixed(2)})`;
$('oAsk').textContent = fmt(d.finalAsk);
$('oRemain').textContent = fmt(d.remainVal);
const del = $('oDelta');
if(d.delta > 1) {
del.textContent = `溢价 +${d.delta.toFixed(0)}`;
del.style.cssText = "background:rgba(220,38,38,0.2); color:#fca5a5";
} else if(d.delta < -1) {
del.textContent = `优惠 ${d.delta.toFixed(0)}`;
del.style.cssText = "background:rgba(22,163,74,0.2); color:#86efac";
} else {
del.textContent = "原价转让";
del.style.cssText = "background:#27272a; color:#a1a1aa";
}
$('oStart').innerHTML = `<i class="ri-play-circle-fill"></i> ${d.start.toISOString().split('T')[0]}`;
$('oEnd').innerHTML = `${d.end.toISOString().split('T')[0]} <i class="ri-stop-circle-fill"></i>`;
if(d.remainDays > 0) {
$('oUsedTime').textContent = `已用 ${d.usedDays} 天 / 剩 ${d.remainDays} 天`;
} else {
$('oUsedTime').textContent = "已过期";
}
$('oBar').style.width = d.percent + "%";
$('oEmail').textContent = d.email;
$('oPay').textContent = d.pay;
$('oDaily').textContent = `¥${d.daily.toFixed(2)} /天`;
$('oRate').textContent = d.rate.toFixed(2);
$('oSign').textContent = d.sign;
$('oTime').textContent = new Date().toLocaleString('zh-CN', {hour12:false});
}
function cpMd() {
if(!STATE.data) return;
const d = STATE.data;
const originalCNY = d.cur === 'CNY' ? "" : `(≈ ${fmt(d.priceCNY)})`;
const timeStr = $('oTime').textContent;
const configSection = d.config.trim() ? `\n### 🖥️ 硬件配置\n\`\`\`text\n${d.config}\n\`\`\`` : "";
const txt = `### 🛒 [出售] 交易详情
| 项目 | 内容 | 备注 |
| :--- | :--- | :--- |
| **📦 商品** | **${d.name}** | |
| **💰 售价** | **${fmt(d.finalAsk)}** | ${d.delta>0?`溢价 ${d.delta.toFixed(0)}`:(d.delta<-1?`优惠 ${Math.abs(d.delta).toFixed(0)}`:'原价转让')} |
| **🏷️ 原价** | **${fmt(d.price, d.cur)}** | ${originalCNY} 汇率 ${d.rate.toFixed(2)} |
| **💰 日均** | ¥${d.daily.toFixed(2)} | 基于${d.period}周期|
| **📉 剩余** | 价值 ${fmt(d.remainVal)} | 剩 ${d.remainDays} 天 / 已用 ${d.usedDays} 天 |
| **📅 周期** | ${d.period} | 到期 ${d.end.toISOString().split('T')[0]} |
| **📦 交付** | ${d.trans} | ${d.email} |
| **💳 支付** | ${d.pay} | |
${configSection}
> ${d.sign} | ${timeStr}`;
navigator.clipboard.writeText(txt).then(()=>toast("📋 Markdown 已复制"));
}
function cpTxt() {
if(!STATE.data) return;
const d = STATE.data;
const txt = `【出售】${d.name}
-------------------------
售价:${fmt(d.finalAsk)} (${d.delta>0?'+'+d.delta.toFixed(0):d.delta.toFixed(0)})
原价:${fmt(d.price, d.cur)} (≈ ${fmt(d.priceCNY)})
剩余:${fmt(d.remainVal)} (已用 ${d.usedDays} 天)
-------------------------
配置:
${d.config}
-------------------------
到期:${d.end.toISOString().split('T')[0]}
交付:${d.trans} / ${d.email}
支付:${d.pay}
-------------------------
${d.sign}`;
navigator.clipboard.writeText(txt).then(()=>toast("📄 纯文本已复制"));
}
function dlImg() {
if(!STATE.data) return;
const node = $('renderNode');
const oldShadow = node.style.boxShadow;
node.style.boxShadow = "none";
toast("⏳ 处理中...");
html2canvas(node, {
backgroundColor: null,
scale: 2,
useCORS: true,
allowTaint: true
}).then(cvs => {
node.style.boxShadow = oldShadow;
try {
const link = document.createElement('a');
link.download = `Deal_${STATE.data.name.replace(/\s/g,'_')}.png`;
link.href = cvs.toDataURL('image/png');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch(err) {
console.error("下载失败", err);
}
if (typeof ClipboardItem !== 'undefined') {
cvs.toBlob(blob => {
try {
const item = new ClipboardItem({ 'image/png': blob });
navigator.clipboard.write([item]).then(() => {
toast("📸 已保存并复制到剪贴板");
}).catch(err => {
console.warn("剪贴板写入被拒", err);
toast("📸 图片已下载");
});
} catch (err) {
console.warn("剪贴板功能异常", err);
toast("📸 图片已下载");
}
});
} else {
toast("📸 图片已下载");
}
}).catch(err => {
console.error(err);
toast("❌ 生成失败");
});
}
let tTimer;
function toast(msg) {
const t = $('toast');
t.textContent = msg;
t.classList.add('show');
clearTimeout(tTimer);
tTimer = setTimeout(()=>t.classList.remove('show'), 2000);
}
</script>
</body>
</html>

厉害了,感谢分享
支持
感谢🙏
谢谢分享
感谢分享
跟着佬 学习
感谢分享
感谢分享
发一个我自己用的哈哈哈哈哈哈哈哈哈哈哈
https://hivps.cc
@顽皮的小石头 #0 用上了,但有个bug:对于没到期而且提前续费的机无法正确算出日期与价值。。
