gangvy пре 5 месеци
родитељ
комит
4080279d6d
1 измењених фајлова са 322 додато и 0 уклоњено
  1. 322 0
      payment/apig-pay.html

+ 322 - 0
payment/apig-pay.html

@@ -495,12 +495,169 @@ body::before {
 }
 .error-bar.show { display: block; }
 
+/* ─── Order History ─── */
+.order-history {
+  margin-top: 36px;
+  padding-top: 28px;
+  border-top: 1px solid rgba(0,212,255,0.08);
+}
+.order-history .section-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 16px;
+}
+.order-history .section-header .section-title {
+  margin-bottom: 0;
+}
+.order-history .refresh-btn {
+  background: none;
+  border: 1px solid rgba(0,212,255,0.2);
+  color: var(--neon-dim);
+  font-size: 12px;
+  padding: 4px 12px;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+.order-history .refresh-btn:hover {
+  border-color: var(--neon);
+  color: var(--neon);
+  background: var(--neon-glow);
+}
+.order-history .refresh-btn:disabled {
+  opacity: 0.4;
+  cursor: not-allowed;
+}
+
+.order-table {
+  width: 100%;
+  border-collapse: separate;
+  border-spacing: 0;
+  font-size: 13px;
+}
+.order-table thead th {
+  text-align: left;
+  padding: 10px 12px;
+  color: var(--text-dim);
+  font-weight: 500;
+  font-size: 12px;
+  letter-spacing: 0.5px;
+  border-bottom: 1px solid rgba(255,255,255,0.06);
+  white-space: nowrap;
+}
+.order-table tbody tr {
+  transition: background 0.15s;
+}
+.order-table tbody tr:hover {
+  background: rgba(0,212,255,0.03);
+}
+.order-table tbody td {
+  padding: 12px 12px;
+  border-bottom: 1px solid rgba(255,255,255,0.03);
+  color: var(--text);
+  vertical-align: middle;
+}
+.order-table tbody tr:last-child td {
+  border-bottom: none;
+}
+.order-table .col-time {
+  color: var(--text-dim);
+  font-size: 12px;
+  white-space: nowrap;
+}
+.order-table .col-amount {
+  font-weight: 600;
+  color: var(--neon);
+  white-space: nowrap;
+}
+.order-table .col-count {
+  white-space: nowrap;
+}
+
+.order-status {
+  display: inline-block;
+  font-size: 11px;
+  font-weight: 500;
+  padding: 2px 8px;
+  border-radius: 4px;
+  white-space: nowrap;
+}
+.order-status.status-paid {
+  color: var(--success);
+  background: rgba(0,255,136,0.08);
+  border: 1px solid rgba(0,255,136,0.2);
+}
+.order-status.status-pending {
+  color: #FFB800;
+  background: rgba(255,184,0,0.08);
+  border: 1px solid rgba(255,184,0,0.2);
+}
+.order-status.status-failed {
+  color: var(--danger);
+  background: rgba(255,77,106,0.08);
+  border: 1px solid rgba(255,77,106,0.2);
+}
+.order-status.status-recharged {
+  color: var(--neon);
+  background: var(--neon-glow);
+  border: 1px solid rgba(0,212,255,0.2);
+}
+
+.order-empty {
+  text-align: center;
+  padding: 32px 16px;
+  color: var(--text-dim);
+  font-size: 13px;
+}
+.order-empty svg {
+  display: block;
+  margin: 0 auto 12px;
+  opacity: 0.3;
+}
+.order-loading {
+  text-align: center;
+  padding: 24px;
+  color: var(--text-dim);
+  font-size: 13px;
+}
+.order-loading .polling-dot {
+  margin-right: 6px;
+}
+.order-error {
+  text-align: center;
+  padding: 16px;
+  color: var(--text-dim);
+  font-size: 12px;
+}
+.order-more-btn {
+  display: block;
+  width: 100%;
+  padding: 10px;
+  margin-top: 8px;
+  background: none;
+  border: 1px solid rgba(255,255,255,0.06);
+  border-radius: 8px;
+  color: var(--text-dim);
+  font-size: 12px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+.order-more-btn:hover {
+  border-color: rgba(0,212,255,0.2);
+  color: var(--neon-dim);
+  background: rgba(0,212,255,0.03);
+}
+
 /* ─── Responsive ─── */
 @media (max-width: 480px) {
   .main-container { padding: 20px 16px; }
   .api-info-card { padding: 20px; }
   .tier-grid { grid-template-columns: 1fr; }
   .modal-box { padding: 24px 20px; }
+  .order-table { font-size: 12px; }
+  .order-table thead th,
+  .order-table tbody td { padding: 8px 6px; }
 }
 </style>
 </head>
@@ -564,6 +721,19 @@ body::before {
     <span class="btn-icon">💳</span> 微信支付
   </button>
 
+  <!-- Order History -->
+  <div class="order-history" id="orderHistory" style="display:none;">
+    <div class="section-header">
+      <div class="section-title">历史订单</div>
+      <button class="refresh-btn" id="refreshOrdersBtn" onclick="loadOrderHistory()">刷新</button>
+    </div>
+    <div class="neu-groove" style="padding:2px 16px;overflow-x:auto;">
+      <div id="orderListBody">
+        <div class="order-loading"><span class="polling-dot"></span> 加载中...</div>
+      </div>
+    </div>
+  </div>
+
   <!-- Success Screen -->
   <div class="success-screen" id="successScreen">
     <div class="success-icon">
@@ -640,6 +810,7 @@ async function loadApig() {
       applyTestTier();
       renderApigInfo();
       renderTiers();
+      loadOrderHistory();
       return;
     }
 
@@ -659,6 +830,7 @@ async function loadApig() {
         applyTestTier();
         renderApigInfo();
         renderTiers();
+        loadOrderHistory();
         return;
       }
     }
@@ -681,6 +853,7 @@ async function loadApig() {
       applyTestTier();
       renderApigInfo();
       renderTiers();
+      loadOrderHistory();
       return;
     }
 
@@ -905,6 +1078,8 @@ async function doRecharge() {
   } catch (e) {
     console.warn('Recharge call failed (non-critical):', e.message);
   }
+  // Refresh order history to show the new order
+  loadOrderHistory();
 }
 
 async function refreshBalance() {
@@ -999,6 +1174,153 @@ function cancelPayment() {
   document.getElementById('qrModal').classList.remove('show');
 }
 
+// ─── Order History ───
+let orderSkip = 0;
+const ORDER_PAGE_SIZE = 10;
+
+async function loadOrderHistory(append) {
+  const container = document.getElementById('orderListBody');
+  const section = document.getElementById('orderHistory');
+  const refreshBtn = document.getElementById('refreshOrdersBtn');
+  section.style.display = '';
+  if (refreshBtn) refreshBtn.disabled = true;
+
+  if (!append) {
+    orderSkip = 0;
+    container.innerHTML = '<div class="order-loading"><span class="polling-dot"></span> 加载中...</div>';
+  }
+
+  try {
+    // Query APIGOrder class via Parse REST API
+    // Orders link to user via fromCompany/fromUser Pointer whose objectId = userId or authId
+    const userVal = userId || authId;
+    const where = JSON.stringify({
+      '$or': [
+        { fromCompany: { __type: 'Pointer', className: 'Company', objectId: userVal } },
+        { fromUser: { __type: 'Pointer', className: '_User', objectId: userVal } }
+      ]
+    });
+    const qs = new URLSearchParams({
+      where: where,
+      order: '-createdAt',
+      limit: String(ORDER_PAGE_SIZE),
+      skip: String(orderSkip)
+    });
+    const resp = await fetch(API_BASE + '/parse/classes/APIGOrder?' + qs.toString(), {
+      headers: {
+        'X-Parse-Application-Id': APP_ID
+      }
+    });
+    if (!resp.ok) throw new Error('HTTP ' + resp.status);
+    const data = await resp.json();
+    const orders = data.results || [];
+    console.log('Order history loaded:', orders.length, 'skip:', orderSkip);
+
+    if (!append) {
+      if (orders.length === 0) {
+        container.innerHTML =
+          '<div class="order-empty">' +
+          '<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/></svg>' +
+          '暂无订单记录</div>';
+        if (refreshBtn) refreshBtn.disabled = false;
+        return;
+      }
+      container.innerHTML = '';
+      // Build table
+      const table = document.createElement('table');
+      table.className = 'order-table';
+      table.innerHTML =
+        '<thead><tr>' +
+        '<th>时间</th>' +
+        '<th>套餐</th>' +
+        '<th>金额</th>' +
+        '<th>状态</th>' +
+        '</tr></thead>';
+      const tbody = document.createElement('tbody');
+      tbody.id = 'orderTbody';
+      table.appendChild(tbody);
+      container.appendChild(table);
+    }
+
+    const tbody = document.getElementById('orderTbody');
+    orders.forEach(o => {
+      const tr = document.createElement('tr');
+      tr.innerHTML = formatOrderRow(o);
+      tbody.appendChild(tr);
+    });
+
+    // Remove old "load more" button
+    const oldMore = container.querySelector('.order-more-btn');
+    if (oldMore) oldMore.remove();
+
+    // Show "load more" if there might be more records
+    if (orders.length >= ORDER_PAGE_SIZE) {
+      orderSkip += ORDER_PAGE_SIZE;
+      const moreBtn = document.createElement('button');
+      moreBtn.className = 'order-more-btn';
+      moreBtn.textContent = '加载更多...';
+      moreBtn.onclick = () => loadOrderHistory(true);
+      container.appendChild(moreBtn);
+    }
+  } catch (e) {
+    console.warn('Order history error:', e);
+    if (!append) {
+      container.innerHTML =
+        '<div class="order-error">订单加载失败: ' + e.message + '</div>';
+    }
+  }
+  if (refreshBtn) refreshBtn.disabled = false;
+}
+
+function formatOrderRow(o) {
+  // Time
+  const dt = o.createdAt ? new Date(o.createdAt) : null;
+  const timeStr = dt
+    ? dt.getFullYear() + '-' +
+      String(dt.getMonth() + 1).padStart(2, '0') + '-' +
+      String(dt.getDate()).padStart(2, '0') + ' ' +
+      String(dt.getHours()).padStart(2, '0') + ':' +
+      String(dt.getMinutes()).padStart(2, '0')
+    : '—';
+
+  // Count — from detail.addCount or count field
+  const count = (o.detail && o.detail.addCount) || o.count || '—';
+  const unitLabel = (apig && (apig.objectId === 'MYM5zJBKgw' || apig.objectId === 'FQtTgjcqIZ')) ? 'token' : '次';
+  const countStr = count !== '—' ? count + ' ' + unitLabel : '—';
+
+  // Amount — from price field
+  const fee = o.price != null ? o.price : null;
+  const amountStr = fee != null ? '¥' + fee : '—';
+
+  // Status
+  const status = resolveOrderStatus(o);
+
+  return '<td class="col-time">' + timeStr + '</td>' +
+    '<td class="col-count">' + countStr + '</td>' +
+    '<td class="col-amount">' + amountStr + '</td>' +
+    '<td>' + status + '</td>';
+}
+
+function resolveOrderStatus(o) {
+  // isPay is the primary payment status flag in APIGOrder
+  if (o.isPay === true) {
+    // Check if recharge was applied (detail.oldCount exists and addCount exists)
+    if (o.detail && o.detail.addCount && o.detail.oldCount != null) {
+      return '<span class="order-status status-recharged">已充值</span>';
+    }
+    return '<span class="order-status status-paid">已支付</span>';
+  }
+  if (o.isPay === false) {
+    // Unpaid — check if expired (>30 min old)
+    const age = Date.now() - new Date(o.createdAt).getTime();
+    if (age > 30 * 60 * 1000) {
+      return '<span class="order-status status-failed">已过期</span>';
+    }
+    return '<span class="order-status status-pending">待支付</span>';
+  }
+  return '<span class="order-status status-pending">未知</span>';
+}
+
 // ─── Helpers ───
 function showError(msg) {
   const el = document.getElementById('errorBar');