Dash 常用交互组件

下面是一套 Dash 常用交互组件(Interactive Components),专为 Web 钱包、支付页面、DApp 设计,基于 HTML + JavaScript + DashJS,支持 实时交互、用户反馈、动画、错误处理,提升用户体验。


核心技术栈

<script src="https://cdn.jsdelivr.net/npm/dash@4.0.0/dist/dash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>

1. 余额实时刷新组件(带加载动画)

<div class="card shadow-sm">
  <div class="card-body text-center">
    <div class="spinner-border text-primary" role="status" id="balance-spinner">
      <span class="visually-hidden">加载中...</span>
    </div>
    <h5 class="mt-3">钱包余额</h5>
    <h2 id="balance-value" class="text-primary">0.00</h2>
    <small class="text-muted" id="balance-address"></small>
    <button class="btn btn-sm btn-outline-primary mt-2" onclick="refreshBalance()">刷新</button>
  </div>
</div>

<script>
let balanceInterval;
async function refreshBalance() {
  const spinner = document.getElementById('balance-spinner');
  const value = document.getElementById('balance-value');
  const addr = document.getElementById('balance-address');
  spinner.style.display = 'block';
  value.textContent = '加载中...';

  try {
    const account = await getAccount();
    const balance = account.getTotalBalance() / 1e8;
    const address = account.getUnusedAddress().address;
    value.textContent = `${balance.toFixed(6)} DASH`;
    addr.textContent = address;
  } catch (e) {
    value.textContent = '错误';
    addr.textContent = e.message;
  } finally {
    spinner.style.display = 'none';
  }
}

// 自动每30秒刷新
balanceInterval = setInterval(refreshBalance, 30000);
refreshBalance();
</script>

2. 扫码支付按钮(相机 + 解析地址)

<button class="btn dash-btn w-100" onclick="scanQR()">
  扫码支付
</button>

<script>
async function scanQR() {
  if (!('mediaDevices' in navigator)) {
    Swal.fire('错误', '您的浏览器不支持摄像头', 'error');
    return;
  }

  const html5QrCode = new Html5Qrcode("qr-reader");
  const video = document.createElement('div');
  video.id = 'qr-reader';
  document.body.appendChild(video);

  Swal.fire({
    title: '扫码支付',
    html: video,
    showConfirmButton: false,
    width: '400px'
  });

  html5QrCode.start(
    { facingMode: "environment" },
    { fps: 10, qrbox: 250 },
    (text) => {
      if (text.startsWith('y') || text.startsWith('x') || text.endsWith('.dash')) {
        document.getElementById('to').value = text;
        Swal.close();
        html5QrCode.stop();
      }
    },
    () => {}
  ).catch(err => Swal.fire('错误', err, 'error'));
}
</script>
<!-- 引入 QR 扫描库 -->
<script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>

3. 发送 DASH 交互表单(带确认 + 进度)

<form id="sendDashForm" class="card p-4">
  <div class="mb-3">
    <label>收款人</label>
    <div class="input-group">
      <input type="text" class="form-control" id="to" placeholder="地址或 alice.dash" required />
      <button type="button" class="btn btn-outline-secondary" onclick="scanQR()">扫一扫</button>
    </div>
  </div>

  <div class="mb-3">
    <label>金额 (DASH)</label>
    <input type="number" step="0.00000001" class="form-control" id="amount" required />
  </div>

  <div class="form-check mb-3">
    <input type="checkbox" class="form-check-input" id="instantsend" checked />
    <label class="form-check-label">InstantSend(1秒到账)</label>
  </div>

  <button type="submit" class="btn dash-btn w-100" id="sendBtn">
    <span id="sendText">发送</span>
    <span id="sendSpinner" class="spinner-border spinner-border-sm ms-2" style="display:none"></span>
  </button>

  <div id="sendResult" class="mt-3"></div>
</form>

<script>
document.getElementById('sendDashForm').onsubmit = async (e) => {
  e.preventDefault();
  const btn = document.getElementById('sendBtn');
  const text = document.getElementById('sendText');
  const spinner = document.getElementById('sendSpinner');
  const result = document.getElementById('sendResult');
  btn.disabled = true;
  text.style.display = 'none';
  spinner.style.display = 'inline-block';

  const to = document.getElementById('to').value.trim();
  const amount = parseFloat(document.getElementById('amount').value);
  const instant = document.getElementById('instantsend').checked;

  try {
    const account = await getAccount();
    const tx = account.createTransaction({
      recipient: to,
      satoshis: Math.floor(amount * 1e8),
      instantSend: instant
    });

    Swal.fire({
      title: '确认发送?',
      text: `发送 ${amount} DASH 给 ${to.slice(0,10)}...`,
      icon: 'question',
      showCancelButton: true
    }).then(async (res) => {
      if (res.isConfirmed) {
        const txid = await account.broadcastTransaction(tx);
        result.innerHTML = `
          <div class="alert alert-success p-2">
            发送成功!<br>
            <a href="https://testnet-insight.dashevo.org/insight/tx/${txid}" target="_blank">
              查看交易 ${txid.slice(0,16)}...
            </a>
          </div>`;
        refreshBalance();
      }
    });
  } catch (err) {
    result.innerHTML = `<div class="alert alert-danger p-2">${err.message}</div>`;
  } finally {
    btn.disabled = false;
    text.style.display = 'inline';
    spinner.style.display = 'none';
  }
};
</script>

4. 交易状态实时监听(WebSocket)

<div class="card">
  <div class="card-body">
    <h6>交易通知</h6>
    <div id="txNotifications"></div>
  </div>
</div>

<script>
let ws;
async function startTxListener() {
  const account = await getAccount();
  const address = account.getUnusedAddress().address;

  ws = new WebSocket('wss://testnet-insight.dashevo.org/insight-api');
  ws.onopen = () => {
    ws.send(JSON.stringify({ "op": "addr_sub", "addr": address }));
  };

  ws.onmessage = (e) => {
    const data = JSON.parse(e.data);
    if (data.op === 'utx') {
      const value = data.x.vout[0].value / 1e8;
      const txid = data.x.hash;
      const div = document.createElement('div');
      div.className = 'alert alert-info py-1 px-2 mb-1';
      div.innerHTML = `新收入: +${value} DASH <a href="https://testnet-insight.dashevo.org/insight/tx/${txid}" target="_blank">查看</a>`;
      document.getElementById('txNotifications').prepend(div);
      refreshBalance();
    }
  };
}
startTxListener();
</script>

5. DashPay 联系人浮窗(悬浮按钮)

<button class="btn btn-floating dash-btn position-fixed bottom-0 end-0 m-3 rounded-circle shadow-lg" 
        style="width:60px;height:60px;font-size:24px" onclick="openContacts()">
  联系人
</button>

<div class="modal fade" id="contactsModal">
  <div class="modal-dialog modal-sm">
    <div class="modal-content">
      <div class="modal-header"><h5>我的联系人</h5></div>
      <div class="modal-body" id="contactsList">加载中...</div>
    </div>
  </div>
</div>

<script>
async function openContacts() {
  const list = document.getElementById('contactsList');
  list.innerHTML = '<div class="text-center"><div class="spinner-border"></div></div>';

  try {
    const platform = client.platform;
    const contacts = await platform.names.search('', 'dashpay');
    list.innerHTML = '';
    contacts.forEach(c => {
      const item = document.createElement('div');
      item.className = 'd-flex justify-content-between align-items-center p-2 border-bottom';
      item.innerHTML = `
        <span>${c.label}</span>
        <button class="btn btn-sm btn-outline-primary" onclick="selectContact('${c.data.to}')">发送</button>
      `;
      list.appendChild(item);
    });
  } catch {
    list.innerHTML = '<small class="text-muted">暂无联系人</small>';
  }
  new bootstrap.Modal(document.getElementById('contactsModal')).show();
}

function selectContact(addr) {
  document.getElementById('to').value = addr;
  bootstrap.Modal.getInstance(document.getElementById('contactsModal')).hide();
}
</script>

6. 一键复制 + 成功动画

<div class="input-group">
  <input type="text" class="form-control" id="copyInput" readonly />
  <button class="btn btn-outline-secondary" onclick="copyAndAnimate()">
    复制
  </button>
</div>

<script>
function copyAndAnimate() {
  const input = document.getElementById('copyInput');
  input.select();
  document.execCommand('copy');

  const btn = input.nextElementSibling;
  const original = btn.innerHTML;
  btn.innerHTML = '已复制!';
  btn.classList.add('btn-success');
  setTimeout(() => {
    btn.innerHTML = original;
    btn.classList.remove('btn-success');
  }, 2000);
}
</script>

7. 完整交互页面模板(复制即用)

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>Dash 交互钱包</title>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <script src="https://cdn.jsdelivr.net/npm/dash@4.0.0/dist/dash.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  <script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    .dash-btn { background: #008DE4; color: white; }
    .dash-btn:hover { background: #006bb3; }
    .btn-floating { z-index: 1000; }
  </style>
</head>
<body class="bg-light">

<div class="container py-4">

  <!-- 余额 -->
  <div class="card shadow-sm mb-3">
    <div class="card-body text-center">
      <div class="spinner-border text-primary" id="balance-spinner" style="width:2rem;height:2rem"></div>
      <h5 class="mt-3">余额</h5>
      <h2 id="balance-value" class="text-primary">0.00</h2>
      <small class="text-muted" id="balance-address"></small>
      <button class="btn btn-sm btn-outline-primary mt-2" onclick="refreshBalance()">刷新</button>
    </div>
  </div>

  <!-- 接收 -->
  <div class="text-center mb-4">
    <h5>接收 DASH</h5>
    <div id="qrcode" class="d-inline-block border p-2 bg-white"></div>
    <p><code id="receive-address" class="small"></code></p>
    <button class="btn btn-sm btn-outline-secondary" onclick="copyAndAnimate()">复制地址</button>
  </div>

  <!-- 发送 -->
  <form id="sendDashForm" class="card p-4 mb-4">
    <h5>发送</h5>
    <div class="input-group mb-3">
      <input type="text" class="form-control" id="to" placeholder="地址或用户名" required />
      <button type="button" class="btn btn-outline-secondary" onclick="scanQR()">扫一扫</button>
    </div>
    <input type="number" step="0.00000001" class="form-control mb-3" id="amount" placeholder="金额" required />
    <div class="form-check mb-3">
      <input type="checkbox" class="form-check-input" id="instantsend" checked />
      <label>InstantSend</label>
    </div>
    <button type="submit" class="btn dash-btn w-100" id="sendBtn">
      <span id="sendText">发送</span>
      <span id="sendSpinner" class="spinner-border spinner-border-sm ms-2" style="display:none"></span>
    </button>
    <div id="sendResult"></div>
  </form>

  <!-- 通知 -->
  <div class="card">
    <div class="card-body">
      <h6>通知</h6>
      <div id="txNotifications" class="small"></div>
    </div>
  </div>

  <!-- 联系人浮窗 -->
  <button class="btn btn-floating dash-btn position-fixed bottom-0 end-0 m-3 rounded-circle shadow-lg" 
          style="width:60px;height:60px;font-size:24px" onclick="openContacts()">
    联系人
  </button>

</div>

<script>
// 替换为你的测试网助记词
const MNEMONIC = 'abandon ... art';
const client = new Dash.Client({ network: 'testnet', wallet: { mnemonic: MNEMONIC } });

async function getAccount() {
  return await client.wallet.getAccount();
}

// 初始化
async function init() {
  const account = await getAccount();
  const addr = account.getUnusedAddress().address;
  document.getElementById('receive-address').textContent = addr;
  document.getElementById('copyInput') && (document.getElementById('copyInput').value = addr);
  new QRCode(document.getElementById("qrcode"), { text: addr, width: 160, height: 160, colorDark: "#008DE4" });
  refreshBalance();
  startTxListener();
}
init();
</script>

<!-- 包含上面所有脚本:refreshBalance, scanQR, sendDashForm, startTxListener, openContacts, copyAndAnimate -->
</body>
</html>

交互组件汇总

组件交互方式用户体验
余额刷新按钮 + 自动轮询实时更新 + 加载动画
扫码支付相机 + 实时解析一扫即填
发送确认SweetAlert2 弹窗二次确认防误操作
交易通知WebSocket 推送实时到账提醒
联系人浮窗悬浮按钮 + 模态框快速选择
复制反馈按钮变色 + 文字明确反馈

立即测试:保存为 dash-interactive.html,用浏览器打开,替换 MNEMONIC 即可体验完整交互!


需要我生成 React / Vue 组件版、移动端 PWA、或集成到 Shopify 吗? 随时告诉我!

文章已创建 2305

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部