| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- <!-- Login Modal -->
- <app-login-modal [visible]="needLogin" (loginSuccess)="onLoginSuccess($event)"></app-login-modal>
- <!-- Header -->
- <div class="page-header">
- <div class="logo">BRAIN<span>HACK</span></div>
- <div class="subtitle">API 服务充值</div>
- <div class="user-bar" *ngIf="loggedInUser">
- <span class="user-badge">👤 {{loggedInUser}}</span>
- <button class="token-copy-btn" (click)="copyToken()" *ngIf="sessionToken"
- title="复制到终端执行,自动将 token 写入 OpenClaw">
- {{tokenCopied ? '✅ 已复制命令' : '📋 复制 OpenClaw 授权命令'}}
- </button>
- <button class="logout-btn" (click)="logout()">退出</button>
- </div>
- </div>
- <!-- Main -->
- <div class="main-container">
- <!-- API Info Card -->
- <div class="api-info-card neu-raised" *ngIf="!showSuccess && !needLogin && apigId">
- <div class="api-title" *ngIf="!apig">
- <span class="skeleton" style="display:inline-block;width:200px;height:24px;"> </span>
- </div>
- <div class="api-title" *ngIf="apig">
- {{apig.title}}<span class="badge">HTTPS</span>
- </div>
- <div class="api-desc" *ngIf="!apig">
- <span class="skeleton" style="display:inline-block;width:100%;height:16px;margin-top:8px;"> </span>
- </div>
- <div class="api-desc" *ngIf="apig">
- {{apig.content || '专业API数据服务'}}
- </div>
- <div class="api-stats" *ngIf="apig">
- <div class="stat-item">
- <span class="stat-label">当前余额</span>
- <span class="stat-value">{{apig.count != null ? apig.count + ' 次' : '—'}}</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">服务状态</span>
- <span class="stat-value" style="color:var(--success);">正常</span>
- </div>
- </div>
- </div>
- <!-- APIG Switcher (always visible after login) -->
- <div class="apig-list-section" *ngIf="loggedInUser && !needLogin && !showSuccess">
- <h3 class="apig-list-title">选择要充值的 API 服务</h3>
- <!-- Loading -->
- <div class="apig-list-loading" *ngIf="apigListLoading && apigList.length === 0">
- <span class="polling-dot"></span> 加载服务列表...
- </div>
- <!-- List -->
- <div class="apig-list-grid" [class.compact]="!!apigId" *ngIf="apigList.length > 0">
- <div class="apig-list-card neu-raised"
- *ngFor="let item of apigList"
- [class.current]="isCurrentApig(item.objectId)"
- (click)="navigateToApig(item.objectId)">
- <div class="apig-list-card-row">
- <div class="apig-list-card-title">
- {{item.title}}
- <span class="current-badge" *ngIf="isCurrentApig(item.objectId)">当前</span>
- </div>
- <div class="apig-list-card-balance"
- [class.zero]="(item.userBalance || 0) === 0"
- [class.low]="(item.userBalance || 0) > 0 && (item.userBalance || 0) < 100">
- <span class="balance-label">余额</span>
- <span class="balance-value">{{item.userBalance != null ? item.userBalance : '—'}}</span>
- <span class="balance-unit">次</span>
- </div>
- </div>
- <div class="apig-list-card-desc" *ngIf="!apigId">{{item.content || '专业API数据服务'}}</div>
- <div class="apig-list-card-footer" *ngIf="item.priceStep && item.priceStep.length > 0">
- <span class="apig-list-card-price">
- <span class="symbol">¥</span>{{item.priceStep[0].price}}
- <span class="price-suffix">起</span>
- </span>
- <span class="apig-list-card-action" *ngIf="!isCurrentApig(item.objectId)">点击切换 →</span>
- </div>
- </div>
- </div>
- <!-- Empty -->
- <div class="apig-list-empty" *ngIf="!apigListLoading && apigList.length === 0">
- 暂无可用的 API 服务
- </div>
- </div>
- <!-- Error -->
- <div class="error-bar" *ngIf="errorMsg">{{errorMsg}}</div>
- <!-- Tier Selection -->
- <ng-container *ngIf="!showSuccess && !needLogin && apigId">
- <div class="section-title">选择套餐</div>
- <div class="tier-grid" *ngIf="!apig || !apig.priceStep">
- <div class="tier-card neu-raised skeleton" style="height:100px;"></div>
- <div class="tier-card neu-raised skeleton" style="height:100px;"></div>
- <div class="tier-card neu-raised skeleton" style="height:100px;"></div>
- </div>
- <div class="tier-grid" *ngIf="apig && apig.priceStep">
- <div
- *ngFor="let tier of apig.priceStep; let i = index"
- class="tier-card"
- [class.neu-raised]="selectedIndex !== i"
- [class.neu-pressed]="selectedIndex === i"
- [class.active]="selectedIndex === i"
- (click)="selectTier(i)">
- <div class="tier-price"><span class="symbol">¥</span>{{tier.price}}</div>
- <div class="tier-count">含 {{tier.count}} {{unitLabel}}</div>
- <div class="tier-unit-price">≈ {{(tier.price / tier.count).toFixed(3)}} 元/{{unitLabel}}</div>
- </div>
- </div>
- <!-- Summary -->
- <div class="summary-bar neu-groove" *ngIf="selectedTier">
- <div>
- <div class="label">套餐配额</div>
- <div style="font-size:16px;font-weight:600;color:var(--text-bright);margin-top:2px;">
- {{selectedTier.count}} {{unitLabel}}
- </div>
- <div class="validity">有效期 730 天(自购买之日起)</div>
- </div>
- <div style="text-align:right;">
- <div class="label">应付金额</div>
- <div class="total"><span class="symbol">¥</span>{{selectedTier.price}}</div>
- </div>
- </div>
- <!-- Pay Button -->
- <button class="pay-btn" [disabled]="!apig || paying" (click)="startPayment()">
- <span *ngIf="!paying"><span class="btn-icon">💳</span> 微信支付</span>
- <span *ngIf="paying">正在生成支付码...</span>
- </button>
- </ng-container>
- <!-- Order History -->
- <div class="order-history" *ngIf="apig && !showSuccess">
- <div class="section-header">
- <div class="section-title">历史订单</div>
- <button class="refresh-btn" [disabled]="ordersLoading" (click)="loadOrderHistory()">刷新</button>
- </div>
- <div class="neu-groove" style="padding:2px 16px;overflow-x:auto;">
- <!-- Loading -->
- <div class="order-loading" *ngIf="ordersLoading && orders.length === 0">
- <span class="polling-dot"></span> 加载中...
- </div>
- <!-- Empty -->
- <div class="order-empty" *ngIf="!ordersLoading && orders.length === 0">
- <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>
- <!-- Table -->
- <table class="order-table" *ngIf="orders.length > 0">
- <thead>
- <tr><th>时间</th><th>套餐</th><th>金额</th><th>状态</th></tr>
- </thead>
- <tbody>
- <tr *ngFor="let o of orders">
- <td class="col-time">{{formatTime(o.createdAt)}}</td>
- <td class="col-count">{{formatCount(o)}}</td>
- <td class="col-amount">{{formatAmount(o)}}</td>
- <td><span class="order-status" [ngClass]="getStatusClass(o)">{{getStatusText(o)}}</span></td>
- </tr>
- </tbody>
- </table>
- <!-- Load more -->
- <button class="order-more-btn" *ngIf="hasMoreOrders" (click)="loadOrderHistory(true)">加载更多...</button>
- <!-- Error -->
- <div class="order-error" *ngIf="orderError">订单加载失败: {{orderError}}</div>
- </div>
- </div>
- <!-- Success Screen -->
- <div class="success-screen" [class.show]="showSuccess">
- <div class="success-icon">
- <svg viewBox="0 0 24 24" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
- <polyline points="20 6 9 17 4 12"></polyline>
- </svg>
- </div>
- <div class="success-title">充值成功</div>
- <div class="success-detail" [innerHTML]="successDetailHtml"></div>
- <button class="back-btn" (click)="handleDone()">完成</button>
- </div>
- </div>
- <!-- QR Modal -->
- <div class="modal-overlay" [class.show]="showQrModal">
- <div class="modal-box neu-raised">
- <button class="modal-close" (click)="cancelPayment()">×</button>
- <div class="modal-title">微信扫码支付</div>
- <div class="modal-amount"><span class="symbol">¥</span>{{selectedTier?.price || 0}}</div>
- <div class="qr-wrapper">
- <canvas #qrCanvas></canvas>
- </div>
- <div class="scan-hint">
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
- <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path>
- <circle cx="12" cy="13" r="4"></circle>
- </svg>
- 打开微信扫一扫
- </div>
- <div class="polling-status">
- <span *ngIf="!paySuccess" class="polling-dot"></span>
- <span *ngIf="!paySuccess">等待支付中...</span>
- <span *ngIf="paySuccess" style="color:var(--success);">✓ 支付成功,正在充值...</span>
- </div>
- </div>
- </div>
|