logo NodeSeekbeta

VPS交易计算器

更新日志

点击查看更新日志

2026/01/29 20:30 针对10楼反馈问题优化更新

[!done]
@0xIKKI #10 用上了,但有个bug:对于没到期而且提前续费的机无法正确算出日期与价值。。

[!info]
1.新增“到期日期”输入框:虽然系统会根据周期自动计算一个默认到期日,但您现在可以手动修改它。
2.重构价值算法:剩余价值不再受限于“单倍周期价格”。现在的算法是:(周期价格 ÷ 周期天数)× 实际剩余天数

image

计算器web界面

image

image

WEB截图模板

image

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>

基于这个计算器修改了一下 @gardenia6 VPS计算器

12
12

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有60098位seeker

🎉欢迎新用户🎉