福利模块 H5:赚钱中心壳(资产卡/签到/任务行+welfareTask桥接契约)+ 收益明细 + 活动规则,素材取自原型
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,298 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../shared/fonts.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title>收益明细 · 傻瓜比价</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||||
/* WebView 是 MATCH_PARENT,height:100% 链即可拿到真实视口高;页面本身不滚,内部列表面板才滚。 */
|
||||
html, body { height: 100%; overflow: hidden; }
|
||||
body { font-family: 'PuHuiTi', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; background: #FFFFFF; color: #1A1A1A; }
|
||||
|
||||
/* 【去框】原型 .device 375x812 预览框 → 全屏自适应容器(无 min-height:100vh,避免顶栏下移) */
|
||||
.device { position: relative; width: 100%; height: 100%; background: #FFFFFF; overflow: hidden; }
|
||||
.screen { position: absolute; inset: 0; display: flex; flex-direction: column; }
|
||||
|
||||
/* header —— 与设置/记录等已改造页统一:48px 高、返回居左、标题居中 */
|
||||
.hdr { height: 48px; flex-shrink: 0; display: flex; align-items: center; padding: 0 8px; position: relative; background: #FFFFFF; }
|
||||
.hdr-back { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; background: none; border: none; cursor: pointer; padding: 0; }
|
||||
.hdr-back:active { opacity: .5; }
|
||||
.hdr-back svg { width: 24px; height: 24px; display: block; }
|
||||
.hdr-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: #1A1A1A; }
|
||||
|
||||
/* 数字用 DIN(对齐原生货币表现) */
|
||||
.num { font-family: "DIN Alternate", "Helvetica Neue", sans-serif; font-variant-numeric: tabular-nums; }
|
||||
|
||||
/* 兑换比例说明条 */
|
||||
.rate { flex-shrink: 0; padding: 0 16px 8px; font-size: 12px; line-height: 18px; font-weight: 400; color: #666666; }
|
||||
|
||||
/* 收益汇总卡 */
|
||||
.sum-card { flex-shrink: 0; margin: 0 16px 16px; padding: 16px; border-radius: 18px; background: linear-gradient(180deg, #FFE86A 0%, #FFD600 100%); box-shadow: 0 6px 18px rgba(255,179,0,.22); }
|
||||
.sum-cols { display: flex; gap: 22px; }
|
||||
.sum-col { flex: 1; min-width: 0; cursor: pointer; }
|
||||
.sum-label { font-size: 15px; font-weight: 600; color: #1A1A1A; line-height: 20px; }
|
||||
.sum-valrow { display: flex; align-items: center; gap: 8px; min-height: 31px; margin-top: 8px; }
|
||||
.sum-val { font-size: 28px; font-weight: 700; color: #FF5A1F; line-height: 30px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.chip { flex-shrink: 0; display: inline-flex; align-items: center; gap: 2px; height: 26px; padding: 0 10px; border-radius: 999px; background: #FFFFFF; border: none; box-shadow: 0 2px 6px rgba(139,94,0,.13); font-size: 12px; font-weight: 700; color: #1A1A1A; white-space: nowrap; cursor: pointer; }
|
||||
.chip:active { transform: translateY(1px) scale(.98); }
|
||||
.chip svg, .chip img { width: 16px; height: 16px; display: block; border-radius: 50%; }
|
||||
.sum-note { font-size: 11px; font-weight: 400; color: rgba(26,26,26,.58); line-height: 15.4px; margin-top: 8px; }
|
||||
|
||||
/* Tab 切换 */
|
||||
.tabs { position: relative; flex-shrink: 0; display: flex; padding: 0 16px; border-bottom: 2px solid #E5E5E5; }
|
||||
.tab { flex: 1; text-align: center; padding: 16px 0 12px; font-size: 14px; font-weight: 400; color: #999999; background: none; border: none; cursor: pointer; }
|
||||
.tab.active { color: #000000; }
|
||||
.indicator { position: absolute; bottom: -2px; left: 0; width: 44px; height: 3px; border-radius: 999px; background: #1A1A1A; transition: left .24s ease; }
|
||||
|
||||
/* 列表:两面板可左右切 */
|
||||
.list-shell { flex: 1; min-height: 0; overflow: hidden; position: relative; }
|
||||
.track { display: flex; height: 100%; width: 200%; transition: transform .24s ease; }
|
||||
.panel { width: 50%; height: 100%; overflow-y: auto; -webkit-overflow-scrolling: touch; padding-bottom: 16px; }
|
||||
.panel::-webkit-scrollbar { display: none; }
|
||||
|
||||
.row { min-height: 72px; display: flex; align-items: center; padding: 0 16px; border-bottom: .5px solid #F5F5F5; }
|
||||
.row-main { flex: 1; min-width: 0; }
|
||||
.row-title { font-size: 14px; font-weight: 400; color: #1A1A1A; line-height: 21px; word-break: break-word; }
|
||||
.row-date { font-size: 12px; font-weight: 400; color: #B3B3B3; line-height: 16px; margin-top: 4px; }
|
||||
.row-amt { margin-left: 8px; font-size: 15px; font-weight: 400; line-height: 22px; white-space: nowrap; }
|
||||
.row-amt.pos { color: #FF5722; }
|
||||
.row-amt.neg { color: #1A1A1A; }
|
||||
|
||||
.empty { text-align: center; font-size: 13px; font-weight: 400; color: #B3B3B3; line-height: 20px; padding: 40px 0 4px; }
|
||||
.load-more { width: 100%; border: 0; background: transparent; font-size: 13px; font-weight: 400; color: #B3B3B3; line-height: 20px; padding: 16px 0; cursor: pointer; }
|
||||
.load-more:active { color: #999999; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="device">
|
||||
<div class="screen">
|
||||
<div class="hdr">
|
||||
<button class="hdr-back" onclick="onBack()" aria-label="返回">
|
||||
<svg viewBox="0 0 24 24" fill="none"><path d="M15 6L9 12L15 18" stroke="#1A1A1A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="hdr-title">收益明细</div>
|
||||
</div>
|
||||
|
||||
<div class="rate" id="rate">兑换比例:10000 金币=1元;金币到账可能会有延迟</div>
|
||||
|
||||
<div class="sum-card">
|
||||
<div class="sum-cols">
|
||||
<div class="sum-col" onclick="switchTab(0)">
|
||||
<div class="sum-label">我的金币</div>
|
||||
<div class="sum-valrow"><span class="sum-val num" id="coinBal">0</span></div>
|
||||
</div>
|
||||
<div class="sum-col" onclick="switchTab(1)">
|
||||
<div class="sum-label">我的现金</div>
|
||||
<div class="sum-valrow">
|
||||
<span class="sum-val num" id="cashBal">0.00</span>
|
||||
<button class="chip" onclick="event.stopPropagation(); goWithdraw()" aria-label="去提现">
|
||||
<img src="assets/wechat-pay-check-icon.png" alt="">去提现
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sum-note">0点自动兑现金(可能存在延迟)</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<button class="tab active" id="tab0" onclick="switchTab(0)">金币记录</button>
|
||||
<button class="tab" id="tab1" onclick="switchTab(1)">现金记录</button>
|
||||
<div class="indicator" id="indicator"></div>
|
||||
</div>
|
||||
|
||||
<div class="list-shell" id="listShell">
|
||||
<div class="track" id="track">
|
||||
<div class="panel" id="panelCoin"></div>
|
||||
<div class="panel" id="panelCash"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../shared/bridge.js"></script>
|
||||
<script src="../shared/api.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var curTab = (new URLSearchParams(location.search).get('tab') === 'cash') ? 1 : 0;
|
||||
var coinCursor = null, cashCursor = null; // 下一页游标(末条 id);null=首屏未拉或已到底
|
||||
var coinLoaded = false, cashLoaded = false; // 该 tab 是否已拉过首屏(用于区分"加载中" vs "空")
|
||||
var coinPerYuan = 10000;
|
||||
|
||||
// ---------- 工具 ----------
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
||||
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
||||
});
|
||||
}
|
||||
function thousands(n) { return Number(n || 0).toLocaleString('zh-CN'); }
|
||||
function yuan(cents) {
|
||||
return (Number(cents || 0) / 100).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
}
|
||||
function dateOf(iso) { return String(iso || '').slice(0, 10); }
|
||||
|
||||
// 标题映射(对齐原生 CoinHistoryViewModel.coinTitle / cashTitle)
|
||||
function coinTitle(bt, remark) {
|
||||
if (bt === 'exchange_out') return '金币兑换现金';
|
||||
if (remark && remark.trim()) return remark;
|
||||
if (bt === 'signin') return '每日签到';
|
||||
if (bt === 'signin_boost') return '签到膨胀奖励';
|
||||
if (bt === 'task_enable_notification') return '打开消息提醒奖励';
|
||||
if (bt && bt.indexOf('task_') === 0) return '任务奖励';
|
||||
if (bt === 'reward_video' || bt === 'ad_reward') return '看视频奖励金币';
|
||||
if (bt === 'feed_ad_reward') return '信息流广告奖励';
|
||||
if (bt === 'invite') return '邀请好友';
|
||||
if (bt === 'compare_milestone') return '比价奖励';
|
||||
return '奖励';
|
||||
}
|
||||
function cashTitle(bt) {
|
||||
if (bt === 'exchange_in') return '金币兑换现金';
|
||||
if (bt === 'withdraw') return '提现';
|
||||
if (bt === 'withdraw_refund') return '提现退款';
|
||||
return '其他';
|
||||
}
|
||||
function signedCoin(a) { return (a >= 0 ? '+ ' : '- ') + thousands(Math.abs(a)) + ' 金币'; }
|
||||
function signedCash(c) { return (c >= 0 ? '+ ' : '- ') + yuan(Math.abs(c)) + ' 元'; }
|
||||
|
||||
function rowHtml(title, date, amountStr) {
|
||||
var neg = amountStr.replace(/\s/g, '').charAt(0) === '-';
|
||||
return '<div class="row"><div class="row-main"><div class="row-title">' + esc(title) +
|
||||
'</div><div class="row-date num">' + esc(date) + '</div></div>' +
|
||||
'<div class="row-amt num ' + (neg ? 'neg' : 'pos') + '">' + esc(amountStr) + '</div></div>';
|
||||
}
|
||||
|
||||
// ---------- 渲染 ----------
|
||||
function renderPanel(tab) {
|
||||
var panel = document.getElementById(tab === 0 ? 'panelCoin' : 'panelCash');
|
||||
var rows = tab === 0 ? coinRows : cashRows;
|
||||
var loaded = tab === 0 ? coinLoaded : cashLoaded;
|
||||
var cursor = tab === 0 ? coinCursor : cashCursor;
|
||||
if (!loaded) { panel.innerHTML = ''; return; } // 首屏还没回来:留白(不闪"暂无")
|
||||
if (!rows.length) {
|
||||
panel.innerHTML = '<div class="empty">' + (tab === 0 ? '暂无金币记录' : '暂无现金记录') + '</div>';
|
||||
return;
|
||||
}
|
||||
var html = rows.join('');
|
||||
if (cursor != null) {
|
||||
html += '<button class="load-more" onclick="window.__loadMore(' + tab + ')">查看更多历史明细</button>';
|
||||
}
|
||||
panel.innerHTML = html;
|
||||
}
|
||||
|
||||
var coinRows = [], cashRows = [];
|
||||
|
||||
function applyCoinPage(page, append) {
|
||||
var mapped = (page.items || []).map(function (it) {
|
||||
return rowHtml(coinTitle(it.biz_type, it.remark), dateOf(it.created_at), signedCoin(it.amount));
|
||||
});
|
||||
coinRows = append ? coinRows.concat(mapped) : mapped;
|
||||
coinCursor = page.next_cursor != null ? page.next_cursor : null;
|
||||
coinLoaded = true;
|
||||
renderPanel(0);
|
||||
}
|
||||
function applyCashPage(page, append) {
|
||||
var mapped = (page.items || []).map(function (it) {
|
||||
return rowHtml(cashTitle(it.biz_type), dateOf(it.created_at), signedCash(it.amount_cents));
|
||||
});
|
||||
cashRows = append ? cashRows.concat(mapped) : mapped;
|
||||
cashCursor = page.next_cursor != null ? page.next_cursor : null;
|
||||
cashLoaded = true;
|
||||
renderPanel(1);
|
||||
}
|
||||
|
||||
// ---------- 数据 ----------
|
||||
function loadBalance() {
|
||||
return SGApi.get('/wallet/account').then(function (a) {
|
||||
if (!a) return;
|
||||
document.getElementById('coinBal').textContent = String(a.coin_balance || 0);
|
||||
document.getElementById('cashBal').textContent = yuan(a.cash_balance_cents);
|
||||
}).catch(function () {});
|
||||
}
|
||||
function loadRate() {
|
||||
return SGApi.get('/wallet/exchange-info').then(function (info) {
|
||||
if (info && info.coin_per_yuan) {
|
||||
coinPerYuan = info.coin_per_yuan;
|
||||
document.getElementById('rate').textContent =
|
||||
'兑换比例:' + coinPerYuan + ' 金币=1元;金币到账可能会有延迟';
|
||||
}
|
||||
}).catch(function () {});
|
||||
}
|
||||
function loadCoin() {
|
||||
return SGApi.get('/wallet/coin-transactions?limit=20').then(function (p) { applyCoinPage(p || { items: [] }, false); })
|
||||
.catch(function () { coinLoaded = true; renderPanel(0); });
|
||||
}
|
||||
function loadCash() {
|
||||
return SGApi.get('/wallet/cash-transactions?limit=20').then(function (p) { applyCashPage(p || { items: [] }, false); })
|
||||
.catch(function () { cashLoaded = true; renderPanel(1); });
|
||||
}
|
||||
|
||||
window.__loadMore = function (tab) {
|
||||
if (tab === 0 && coinCursor != null) {
|
||||
SGApi.get('/wallet/coin-transactions?limit=20&cursor=' + coinCursor).then(function (p) { applyCoinPage(p || { items: [] }, true); }).catch(function () {});
|
||||
} else if (tab === 1 && cashCursor != null) {
|
||||
SGApi.get('/wallet/cash-transactions?limit=20&cursor=' + cashCursor).then(function (p) { applyCashPage(p || { items: [] }, true); }).catch(function () {});
|
||||
}
|
||||
};
|
||||
|
||||
// ---------- Tab / 指示器 / 滑动 ----------
|
||||
function positionIndicator() {
|
||||
var tabs = document.querySelector('.tabs');
|
||||
var ind = document.getElementById('indicator');
|
||||
if (!tabs || !ind) return;
|
||||
var side = 16, w = tabs.clientWidth, tabW = (w - side * 2) / 2;
|
||||
ind.style.left = (side + tabW * curTab + (tabW - 44) / 2) + 'px';
|
||||
}
|
||||
window.switchTab = function (idx) {
|
||||
curTab = idx;
|
||||
document.getElementById('track').style.transform = 'translateX(' + (idx === 1 ? '-50%' : '0') + ')';
|
||||
document.getElementById('tab0').classList.toggle('active', idx === 0);
|
||||
document.getElementById('tab1').classList.toggle('active', idx === 1);
|
||||
positionIndicator();
|
||||
};
|
||||
|
||||
// 横滑切 tab:仅在明显水平手势时触发,纵向留给列表滚动
|
||||
(function bindSwipe() {
|
||||
var shell = document.getElementById('listShell'), x0 = 0, y0 = 0, tracking = false;
|
||||
shell.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1) { tracking = false; return; }
|
||||
x0 = e.touches[0].clientX; y0 = e.touches[0].clientY; tracking = true;
|
||||
}, { passive: true });
|
||||
shell.addEventListener('touchend', function (e) {
|
||||
if (!tracking) return; tracking = false;
|
||||
var t = e.changedTouches[0], dx = t.clientX - x0, dy = t.clientY - y0;
|
||||
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) {
|
||||
if (dx < 0 && curTab === 0) switchTab(1);
|
||||
else if (dx > 0 && curTab === 1) switchTab(0);
|
||||
}
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
window.onBack = function () {
|
||||
if (window.SGBridge && SGBridge.closePage) { SGBridge.closePage(); }
|
||||
else if (history.length > 1) { history.back(); }
|
||||
};
|
||||
window.goWithdraw = function () {
|
||||
if (window.SGBridge && SGBridge.navigate) { SGBridge.navigate('withdrawal'); }
|
||||
};
|
||||
|
||||
// 初始 tab(不触发动画,直接就位)
|
||||
document.getElementById('track').style.transform = 'translateX(' + (curTab === 1 ? '-50%' : '0') + ')';
|
||||
document.getElementById('tab0').classList.toggle('active', curTab === 0);
|
||||
document.getElementById('tab1').classList.toggle('active', curTab === 1);
|
||||
window.addEventListener('resize', positionIndicator);
|
||||
requestAnimationFrame(positionIndicator);
|
||||
|
||||
// 首拉 + 可见时轻量刷余额(后台审核发币等外部变更停在本页也能刷出)
|
||||
loadBalance(); loadRate(); loadCoin(); loadCash();
|
||||
if (window.SGBridge && SGBridge.on) {
|
||||
SGBridge.on('onResume', loadBalance);
|
||||
SGBridge.on('onAuthChange', function () { loadBalance(); loadRate(); coinLoaded = cashLoaded = false; coinCursor = cashCursor = null; loadCoin(); loadCash(); });
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
After Width: | Height: | Size: 744 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,429 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../shared/fonts.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title>赚钱中心 · 傻瓜比价</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||||
html, body { height: 100%; overflow: hidden; }
|
||||
body { font-family: 'PuHuiTi', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; background: #F5F5F5; color: #1A1A1A; }
|
||||
|
||||
:root {
|
||||
--orange: #FF5722; --bg: #F5F5F5; --card: #FFFFFF; --aux: #999999; --text: #1A1A1A;
|
||||
--cta-grad: linear-gradient(180deg, #FFE066 0%, #FFC400 100%);
|
||||
--cta-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35);
|
||||
}
|
||||
.num { font-family: "DIN Alternate", "Helvetica Neue", sans-serif; font-variant-numeric: tabular-nums; }
|
||||
|
||||
.device { position: relative; width: 100%; height: 100%; background: var(--bg); overflow: hidden; }
|
||||
.screen { position: absolute; inset: 0; display: flex; flex-direction: column; }
|
||||
/* 底部留出原生底部 tab 栏高度(内容区铺满、tab 栏盖其上)+ 手势条安全区 */
|
||||
.scroll { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 0 12px calc(80px + env(safe-area-inset-bottom, 0px)); }
|
||||
.scroll::-webkit-scrollbar { display: none; }
|
||||
|
||||
.page-header { height: 50px; display: flex; align-items: center; justify-content: center; padding: 4px 4px 8px; }
|
||||
.page-title { font-size: 20px; font-weight: 700; color: var(--text); line-height: 1; }
|
||||
|
||||
/* ===== 资产卡(与收益明细汇总卡同一套 UI:黄渐变) ===== */
|
||||
.asset-card { position: relative; border-radius: 18px; padding: 16px; margin: 0 4px; overflow: hidden;
|
||||
background: linear-gradient(180deg, #FFE86A 0%, #FFD600 100%); box-shadow: 0 6px 18px rgba(255,179,0,.22); }
|
||||
.asset-cols { display: flex; gap: 22px; }
|
||||
.asset-col { flex: 1; min-width: 0; cursor: pointer; }
|
||||
.asset-label { display: inline-flex; align-items: center; gap: 2px; font-size: 15px; font-weight: 600; color: #1A1A1A; line-height: 20px; }
|
||||
.asset-arrow { width: 13px; height: 13px; flex-shrink: 0; }
|
||||
.asset-valrow { display: flex; align-items: center; gap: 8px; min-height: 31px; margin-top: 8px; }
|
||||
.asset-val { font-size: 28px; font-weight: 700; color: #FF5A1F; line-height: 30px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.chip { flex-shrink: 0; display: inline-flex; align-items: center; gap: 2px; height: 26px; padding: 0 10px; border-radius: 999px; background: #FFFFFF; border: none; box-shadow: 0 2px 6px rgba(139,94,0,.13); font-size: 12px; font-weight: 700; color: #1A1A1A; white-space: nowrap; cursor: pointer; }
|
||||
.chip:active { transform: translateY(1px) scale(.98); }
|
||||
.chip svg, .chip img { width: 16px; height: 16px; display: block; border-radius: 50%; }
|
||||
.asset-note { font-size: 11px; font-weight: 400; color: rgba(26,26,26,.58); line-height: 15.4px; margin-top: 8px; }
|
||||
|
||||
/* 未登录卡:暖白 + 钱包插画 */
|
||||
.asset-card.unlogged { background: radial-gradient(120% 90% at 100% 0%, rgba(255,214,0,.28) 0%, rgba(255,214,0,0) 55%), #FFFDF5; box-shadow: 0 4px 14px rgba(204,153,0,.08); min-height: 132px; display: flex; align-items: center; padding: 0 16px; }
|
||||
.asset-illustration { position: absolute; right: 6px; bottom: 17px; width: 96px; z-index: 2; pointer-events: none; }
|
||||
.asset-unlogged-body { display: flex; flex-direction: column; gap: 16px; padding-right: 112px; position: relative; z-index: 3; }
|
||||
.asset-unlogged-title { font-size: 17px; font-weight: 700; color: var(--text); }
|
||||
.login-btn { align-self: flex-start; height: 40px; padding: 0 28px; border-radius: 20px; border: none; background: var(--cta-grad); box-shadow: var(--cta-shadow); color: var(--text); font-size: 14px; font-weight: 700; letter-spacing: .04em; cursor: pointer; }
|
||||
.login-btn:active { transform: translateY(1px) scale(.99); }
|
||||
|
||||
/* ===== 任务卡 ===== */
|
||||
.task-card { background: var(--card); border-radius: 14px; padding: 20px 16px 16px; box-shadow: 0 4px 12px rgba(0,0,0,.04); margin-top: 16px; }
|
||||
.section-title { font-size: 16px; font-weight: 600; color: var(--text); margin: 0 0 14px; }
|
||||
|
||||
/* 通用黄色 CTA + 红点 */
|
||||
.cta { flex-shrink: 0; position: relative; border: none; border-radius: 32px; background: var(--cta-grad); box-shadow: var(--cta-shadow); color: var(--text); font-size: 12px; font-weight: 600; line-height: 29px; height: 29px; cursor: pointer; white-space: nowrap; }
|
||||
.cta:active { transform: translateY(1px) scale(.98); }
|
||||
.cta.gray { background: #F5F5F5; border: 1px solid #E5E5E5; box-shadow: none; color: var(--aux); }
|
||||
.dot { position: absolute; top: -1px; right: -1px; width: 11px; height: 11px; border-radius: 50%; background: #FF3B30; box-shadow: 0 1px 4px rgba(255,59,48,.35); pointer-events: none; }
|
||||
|
||||
/* 签到行 + 时间线 */
|
||||
.checkin-row { padding: 2px 0 24px; border-bottom: 1px solid #F0F0F0; }
|
||||
.checkin-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
|
||||
.checkin-copy { min-width: 0; }
|
||||
.checkin-title { font-size: 15px; font-weight: 500; color: var(--text); white-space: nowrap; }
|
||||
.checkin-sub { font-size: 11px; font-weight: 400; color: var(--aux); margin-top: 6px; white-space: nowrap; }
|
||||
.checkin-cta { width: 60px; min-width: 60px; padding: 0 12px; }
|
||||
.timeline { position: relative; display: grid; grid-template-columns: repeat(5, minmax(0,1fr)); column-gap: 2px; margin-top: 20px; }
|
||||
.step { position: relative; display: grid; grid-template-rows: 18px 22px 15px; row-gap: 2px; justify-items: center; }
|
||||
.step-label { font-size: 11px; font-weight: 650; color: var(--aux); line-height: 18px; white-space: nowrap; }
|
||||
.step-badge { padding: 0 8px; border-radius: 999px; background: #fff; color: #FFA000; font-size: 11px; line-height: 18px; font-weight: 900; box-shadow: 0 2px 8px rgba(0,0,0,.08); white-space: nowrap; }
|
||||
.step-icon { box-sizing: border-box; width: 22px; height: 22px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
|
||||
.step-icon img { display: block; width: 22px; height: 22px; object-fit: contain; }
|
||||
.step.done .step-icon { background: #FFF3E0; border: 1px solid #FFE066; box-shadow: inset 0 1px 0 rgba(255,255,255,.72); }
|
||||
.step.done .step-icon::before { content: '✓'; color: #FFA000; font-size: 13px; line-height: 1; font-weight: 900; }
|
||||
.step-amt { color: var(--aux); font-size: 11px; line-height: 1; font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||||
.step.current .step-amt { color: #FF6A00; font-weight: 800; }
|
||||
/* 连线:节点内容 z-index:1 盖住线,线 z-index:0(勿用 -1,会沉到卡片背景下看不见) */
|
||||
.step > * { position: relative; z-index: 1; }
|
||||
.step:not(:last-child)::after { content: ''; position: absolute; top: 30px; left: 50%; width: calc(100% + 2px); height: 2px; border-radius: 32px; background: #E5E5E5; z-index: 0; }
|
||||
.step.gap-next::after { border-radius: 0; background: repeating-linear-gradient(90deg,#E5E5E5 0 3px, transparent 3px 8px); background-size: 8px 2px; background-repeat: repeat-x; }
|
||||
|
||||
/* 任务行 */
|
||||
.task-row { display: flex; align-items: center; margin-top: 30px; }
|
||||
.task-info { flex: 1; min-width: 0; }
|
||||
.task-titlerow { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
||||
.task-title { font-size: 15px; font-weight: 500; color: var(--text); line-height: 1.3; }
|
||||
.task-coin { display: inline-flex; align-items: center; gap: 0; font-size: 12px; font-weight: 600; color: var(--orange); }
|
||||
.task-coin .ci { width: 18px; height: 18px; flex-shrink: 0; margin-right: -2px; transform: scale(1.12); }
|
||||
.task-sub { font-size: 11px; color: var(--aux); margin-top: 6px; font-weight: 400; line-height: 1.4; display: flex; align-items: center; gap: 6px; }
|
||||
.task-tag { padding: 2px 5px 1px; border-radius: 4px; background: #FFF3E0; color: #E65100; font-size: 10px; font-weight: 700; }
|
||||
.task-cta { min-width: 60px; padding: 0 12px; }
|
||||
.task-row[hidden] { display: none; }
|
||||
|
||||
/* 规则入口(在任务卡外) */
|
||||
.rule-entry { margin: 16px auto 8px; border: none; background: transparent; color: #999999; font-size: 12px; font-weight: 500; line-height: 18px; display: flex; align-items: center; justify-content: center; gap: 2px; width: 100%; cursor: pointer; }
|
||||
.rule-entry span { font-size: 13px; }
|
||||
|
||||
/* 调试预览(检查签到 1-7 天图案) */
|
||||
.dbg { margin: 8px auto 0; display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; opacity: .85; }
|
||||
.dbg button { font-size: 11px; padding: 5px 10px; border-radius: 8px; border: 1px solid #E5E5E5; background: #fff; color: #666; cursor: pointer; }
|
||||
.dbg .lbl { font-size: 11px; color: #999; }
|
||||
|
||||
/* ===== 签到弹窗(1:1 对齐原型 #checkinPopup) ===== */
|
||||
.ck-layer { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.55); -webkit-backdrop-filter: blur(1px); backdrop-filter: blur(1px); display: none; align-items: center; justify-content: center; padding: 0 16px; }
|
||||
.ck-layer.show { display: flex; }
|
||||
.ck-card { position: relative; width: 100%; max-width: 340px; border-radius: 18px; padding: 16px 12px 12px; background: linear-gradient(180deg, #FFE0B2 0%, #FFFDE7 30%, #FFFFFF 72%); box-shadow: 0 20px 40px rgba(0,0,0,.12); }
|
||||
.ck-close { position: absolute; left: 50%; bottom: -52px; width: 34px; height: 34px; transform: translateX(-50%); border: none; border-radius: 50%; background: rgba(255,255,255,.58); color: rgba(0,0,0,.46); box-shadow: 0 6px 18px rgba(0,0,0,.08); display: flex; align-items: center; justify-content: center; font-size: 22px; line-height: 1; cursor: pointer; }
|
||||
.ck-banner { width: 206px; height: 16px; margin: -4px auto 8px; border-radius: 999px; background: rgba(255,255,255,.92); box-shadow: 0 4px 14px rgba(255,174,0,.14); border: 1px solid rgba(255,184,0,.24); display: flex; align-items: center; justify-content: center; gap: 4px; color: #7A4F00; font-size: 10px; font-weight: 700; white-space: nowrap; }
|
||||
.ck-banner .star { width: 9px; height: 9px; background: #FFB300; clip-path: polygon(50% 0%,62% 37%,100% 50%,62% 63%,50% 100%,38% 63%,0% 50%,38% 37%); flex-shrink: 0; }
|
||||
.ck-title { margin: 0; text-align: center; color: #1A1A1A; font-size: 20px; line-height: 1.1; font-weight: 900; }
|
||||
.ck-sub { margin-top: 8px; text-align: center; color: #666; font-size: 11px; line-height: 1.2; font-weight: 500; white-space: nowrap; }
|
||||
.ck-sub b { color: #FF6A00; font-weight: 800; }
|
||||
.ck-rewards { margin-top: 12px; display: grid; grid-template-columns: repeat(5,1fr); gap: 6px; }
|
||||
.ck-rcard { position: relative; min-height: 64px; border-radius: 8px; background: rgba(255,255,255,.88); border: 1px solid #F0F0F0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 8px 2px 6px; box-shadow: inset 0 1px 0 rgba(255,255,255,.75); }
|
||||
.ck-rcard.claimed { background: rgba(247,247,247,.96); border-color: #E5E5E5; box-shadow: none; }
|
||||
.ck-rcard.current { border-color: #FFB300; box-shadow: 0 8px 15px rgba(255,184,0,.14); background: rgba(255,255,255,.96); }
|
||||
.ck-ricon { min-height: 26px; display: flex; align-items: center; justify-content: center; color: #999; font-size: 10px; line-height: 1; font-weight: 800; white-space: nowrap; }
|
||||
.ck-ricon img { width: 29px; height: 29px; object-fit: contain; display: block; }
|
||||
.ck-ricon img.chest { width: 36px; height: 33px; }
|
||||
.ck-ramt { color: #1A1A1A; font-size: 12px; line-height: 1; font-weight: 800; }
|
||||
.ck-rcard.claimed .ck-ramt { color: #999; }
|
||||
.ck-progress { margin: 12px 0 0; position: relative; display: grid; grid-template-columns: repeat(5,1fr); gap: 6px; align-items: start; }
|
||||
.ck-lines { position: absolute; top: 4.5px; left: 0; right: 0; height: 3px; pointer-events: none; z-index: 0; }
|
||||
.ck-seg { position: absolute; top: 0; height: 3px; border-radius: 32px; }
|
||||
.ck-seg.solid { background: var(--lc,#FFB800); }
|
||||
.ck-seg.dashed { background: repeating-linear-gradient(90deg,var(--lc,#D9D9D9) 0 3px, transparent 3px 8px); background-size: 8px 3px; background-repeat: repeat-x; }
|
||||
.ck-node { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; gap: 8px; color: #666; font-size: 10px; line-height: 1; white-space: nowrap; }
|
||||
.ck-dot { box-sizing: border-box; width: 12px; height: 12px; border-radius: 50%; background: #fff; border: 2px solid #DDD; }
|
||||
.ck-node.done .ck-dot { background: #FFB300; border-color: #FFB300; }
|
||||
.ck-node.current .ck-dot { background: #fff; border: 2.5px solid #FFB300; }
|
||||
.ck-cta { position: relative; margin: 12px auto 0; width: fit-content; min-width: 252px; max-width: calc(100% - 24px); height: 40px; padding: 0 22px; border: none; border-radius: 24px; background: linear-gradient(180deg,#FFE066 0%,#FFB300 100%); color: #1A1A1A; font-size: 16px; font-weight: 900; line-height: 1; white-space: nowrap; box-shadow: 0 14px 28px rgba(255,184,0,.22); display: flex; align-items: center; justify-content: center; gap: 4px; cursor: pointer; }
|
||||
.ck-cta:active { transform: translateY(1px) scale(.99); }
|
||||
.ck-cta img { width: 18px; height: 18px; object-fit: contain; display: block; }
|
||||
.ck-cta b { color: #FF6A00; font-size: 22px; line-height: 1; }
|
||||
|
||||
/* Toast */
|
||||
.toast { position: fixed; left: 50%; top: 45%; transform: translate(-50%,-50%); background: rgba(56,56,68,.94); color: #fff; font-size: 14px; padding: 12px 20px; border-radius: 12px; z-index: 60; opacity: 0; transition: opacity .2s; pointer-events: none; max-width: 78vw; text-align: center; }
|
||||
.toast.show { opacity: 1; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="device">
|
||||
<div class="screen">
|
||||
<div class="scroll">
|
||||
<div class="page-header"><span class="page-title">赚钱中心</span></div>
|
||||
<div id="assetSlot"></div>
|
||||
<div class="task-card">
|
||||
<div class="section-title">赚金币</div>
|
||||
<div id="checkinSlot"></div>
|
||||
<div id="tasksSlot"></div>
|
||||
</div>
|
||||
<button class="rule-entry" onclick="onRules()">查看活动规则<span>›</span></button>
|
||||
<div class="dbg" id="dbgSlot">
|
||||
<button onclick="dbgStep(-1)">◀ 天</button>
|
||||
<span class="lbl" id="dbgLabel">预览签到:关</span>
|
||||
<button onclick="dbgStep(1)">天 ▶</button>
|
||||
<button onclick="dbgToggleSigned()">切已签</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ck-layer" id="ckMask" onclick="if(event.target===this)closeCheckin()">
|
||||
<div class="ck-card" onclick="event.stopPropagation()">
|
||||
<button class="ck-close" onclick="closeCheckin()" aria-label="关闭">×</button>
|
||||
<div class="ck-banner"><span class="star"></span><span id="ckBanner">连续签到7天,必得2500金币</span></div>
|
||||
<h2 class="ck-title" id="ckTitle">签到领取今日金币</h2>
|
||||
<div class="ck-sub" id="ckSub">—</div>
|
||||
<div class="ck-rewards" id="ckRewards"></div>
|
||||
<div class="ck-progress" id="ckProgress"></div>
|
||||
<button class="ck-cta" id="ckBtn" onclick="doCheckin()"></button>
|
||||
<button class="ck-cta" id="ckBoostBtn" onclick="onSigninBoost()" style="display:none">看广告最高膨胀至 3000 金币</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script src="../shared/bridge.js"></script>
|
||||
<script src="../shared/api.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
var A = 'assets/';
|
||||
var state = { logged: false, coin: 0, cashCents: 0, signin: null, notif: null, ad: null, couponDone: false };
|
||||
var dbg = { day: 0, signed: false }; // day 0 = 关(用真实数据)
|
||||
var SIGN_AMT = [200, 200, 300, 200, 400, 400, 800];
|
||||
|
||||
function el(id) { return document.getElementById(id); }
|
||||
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }
|
||||
function thousands(n) { return Number(n || 0).toLocaleString('zh-CN'); }
|
||||
function yuan(cents) { return (Number(cents || 0) / 100).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
|
||||
var toastTimer;
|
||||
function toast(msg) { var t = el('toast'); t.textContent = msg; t.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(function () { t.classList.remove('show'); }, 1800); }
|
||||
|
||||
function loggedIn() {
|
||||
try { if (window.SGBridge) { var t = SGBridge.getToken && SGBridge.getToken(); if (t) return true; var a = SGBridge.getAuthState && SGBridge.getAuthState(); return !!(a && (a.loggedIn || a.logged_in)); } } catch (e) {}
|
||||
return false;
|
||||
}
|
||||
function requestLogin() { if (window.SGBridge && SGBridge.requestLogin) SGBridge.requestLogin(); }
|
||||
function nav(route) { if (window.SGBridge && SGBridge.navigate) SGBridge.navigate(route); else console.log('[nav]', route); }
|
||||
function guard(fn) { if (!state.logged) { requestLogin(); return; } fn(); }
|
||||
|
||||
// ---------- CTA ----------
|
||||
window.onRules = function () { if (state.logged) nav('welfareRules'); else requestLogin(); };
|
||||
window.onWithdraw = function () { nav('withdrawal'); };
|
||||
window.onCoinHistory = function () { nav('coinHistory'); };
|
||||
window.onCashHistory = function () { nav('cashHistory'); };
|
||||
window.onLogin = function () { requestLogin(); };
|
||||
window.onCoupon = function () { guard(function () { nav('welfareTask:coupon'); }); };
|
||||
window.onCompare = function () { guard(function () { nav('welfareTask:compare'); }); };
|
||||
window.onNotif = function () { guard(function () { nav('welfareTask:notification'); }); };
|
||||
window.onReport = function () { guard(function () { nav('reportFlow'); }); };
|
||||
window.onInvite = function () { guard(function () { nav('invite'); }); };
|
||||
window.onVideo = function () {
|
||||
guard(function () {
|
||||
if (state.ad && state.ad.remaining <= 0) { toast('今日看视频奖励已达上限,明天再来'); return; }
|
||||
if (cooldownLeft() > 0) return;
|
||||
nav('welfareTask:rewardVideo');
|
||||
});
|
||||
};
|
||||
|
||||
// ---------- 资产卡 ----------
|
||||
function renderAsset() {
|
||||
var slot = el('assetSlot');
|
||||
if (!state.logged) {
|
||||
slot.innerHTML =
|
||||
'<div class="asset-card unlogged"><img class="asset-illustration" src="' + A + 'asset-illustration.png" alt="">' +
|
||||
'<div class="asset-unlogged-body"><div class="asset-unlogged-title">登录就能赚金币</div>' +
|
||||
'<button class="login-btn" onclick="onLogin()">一键登录</button></div></div>';
|
||||
return;
|
||||
}
|
||||
var wx = '<img src="' + A + 'wechat-pay-check-icon.png" alt="">';
|
||||
var arw = '<svg class="asset-arrow" viewBox="0 0 24 24" fill="none"><path d="M9 6L15 12L9 18" stroke="rgba(26,26,26,.4)" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||||
slot.innerHTML =
|
||||
'<div class="asset-card"><div class="asset-cols">' +
|
||||
'<div class="asset-col" onclick="onCoinHistory()"><div class="asset-label">我的金币' + arw + '</div>' +
|
||||
'<div class="asset-valrow"><span class="asset-val num" id="coinVal">' + String(state.coin) + '</span></div></div>' +
|
||||
'<div class="asset-col" onclick="onCashHistory()"><div class="asset-label">我的现金' + arw + '</div>' +
|
||||
'<div class="asset-valrow"><span class="asset-val num" id="cashVal">' + yuan(state.cashCents) + '</span>' +
|
||||
'<button class="chip" onclick="event.stopPropagation();onWithdraw()">' + wx + '去提现</button></div></div>' +
|
||||
'</div><div class="asset-note">0点自动兑现金(可能存在延迟)</div></div>';
|
||||
}
|
||||
function refreshAssetNumbers() { var c = el('coinVal'), k = el('cashVal'); if (c) c.textContent = String(state.coin); if (k) k.textContent = yuan(state.cashCents); }
|
||||
|
||||
// ---------- 签到时间线 ----------
|
||||
function effSignin() {
|
||||
if (dbg.day > 0) {
|
||||
return {
|
||||
steps: SIGN_AMT.map(function (c, i) { return { day: i + 1, coin: c }; }),
|
||||
today_signed: dbg.signed, today_cycle_day: dbg.day,
|
||||
consecutive_days: dbg.day, today_coin: SIGN_AMT[dbg.day - 1],
|
||||
};
|
||||
}
|
||||
return state.signin;
|
||||
}
|
||||
// 签到视图态(对齐原型 buildCheckin7Steps):5 格滑窗跟随 focus(未签=今天/已签=明天),
|
||||
// done=已领✓;current=「今天领」(未签在今天)或「明天领」(已签移到明天);已签的今天节点标签变「今天」。
|
||||
// ⚠️不依赖后端 step.status——签到后后端把今天标 claimed、不再有 today 节点,badge 会整个消失(bug)。
|
||||
function ckSteps(s, preview) {
|
||||
var steps = (s && s.steps && s.steps.length) ? s.steps : SIGN_AMT.map(function (c, i) { return { day: i + 1, coin: c }; });
|
||||
var maxDay = steps.length;
|
||||
var today = preview ? 0 : Math.min(Math.max((s && s.today_cycle_day) || 1, 1), maxDay);
|
||||
var signed = !preview && !!(s && s.today_signed);
|
||||
var focus = preview ? 1 : (signed ? Math.min(today + 1, maxDay) : today);
|
||||
var start = Math.min(3, Math.max(1, focus - 2));
|
||||
var days = [start, start + 1, start + 2, start + 3, maxDay];
|
||||
return days.map(function (d) {
|
||||
var st = steps[d - 1] || { day: d, coin: 0 };
|
||||
var isToday = d === today, isTomorrow = d === today + 1;
|
||||
var isDone = signed ? d <= today : d < today;
|
||||
var isCurrent = (!signed && isToday) || (signed && isTomorrow);
|
||||
return {
|
||||
day: d, coin: st.coin, done: isDone, current: isCurrent, isLastDay: d === maxDay,
|
||||
badge: isCurrent ? (isTomorrow ? '明天领' : '今天领') : null,
|
||||
label: (isToday && signed) ? '今天' : ('第' + d + '天'),
|
||||
};
|
||||
});
|
||||
}
|
||||
function stepIconImg(v) {
|
||||
var src = (v.isLastDay || v.coin >= 800) ? 'checkin-chest-8888.png' : (v.coin >= 300 ? 'checkin-coins-600.png' : 'checkin-coin-single.png');
|
||||
return '<img src="' + A + src + '" alt="">';
|
||||
}
|
||||
function stepNode(v, gapNext) {
|
||||
var cls = 'step' + (v.done ? ' done' : '') + (v.current ? ' current' : '') + (gapNext ? ' gap-next' : '');
|
||||
var top = v.badge ? '<span class="step-badge">' + v.badge + '</span>' : '<span class="step-label">' + v.label + '</span>';
|
||||
var icon = v.done ? '<span class="step-icon"></span>' : '<span class="step-icon">' + stepIconImg(v) + '</span>';
|
||||
return '<div class="' + cls + '">' + top + icon + '<span class="step-amt num">' + v.coin + '</span></div>';
|
||||
}
|
||||
function renderCheckin() {
|
||||
var slot = el('checkinSlot'), s = effSignin(), logged = state.logged;
|
||||
var signed = !!(s && s.today_signed);
|
||||
var sub = logged ? (s ? '连续签到7天必得2500金币' : '连续签到领金币') : '登录后每日签到,连续7天最高领800金币';
|
||||
var ctaText = (logged && signed) ? '去查看' : '去签到';
|
||||
var ctaDot = (!signed) ? '<span class="dot"></span>' : '';
|
||||
var view = ckSteps(s, !(logged && s));
|
||||
var html = view.map(function (v, i) {
|
||||
return stepNode(v, view[i + 1] ? (view[i + 1].day - v.day > 1) : false);
|
||||
}).join('');
|
||||
slot.innerHTML =
|
||||
'<div class="checkin-row"><div class="checkin-head"><div class="checkin-copy">' +
|
||||
'<div class="checkin-title">新人签到领金币</div><div class="checkin-sub">' + esc(sub) + '</div></div>' +
|
||||
'<button class="cta checkin-cta" onclick="onCheckinEntry()">' + ctaText + ctaDot + '</button></div>' +
|
||||
'<div class="timeline">' + html + '</div></div>';
|
||||
}
|
||||
window.onCheckinEntry = function () { if (!state.logged) { requestLogin(); return; } openCheckin(); };
|
||||
|
||||
// ---------- 任务行 ----------
|
||||
function cooldownLeft() { if (!state.ad || !state.ad.cooldown_until) return 0; var u = Date.parse(state.ad.cooldown_until); return isNaN(u) ? 0 : Math.max(0, u - Date.now()); }
|
||||
function videoCta() {
|
||||
if (!state.logged) return { cls: '', label: '去观看', click: 'onVideo()' };
|
||||
if (state.ad && state.ad.remaining <= 0) return { cls: 'gray', label: '已领完', click: 'onVideo()' };
|
||||
var left = cooldownLeft();
|
||||
if (left > 0) { var s = Math.ceil(left / 1000), mm = Math.floor(s / 60), ss = s % 60; return { cls: 'gray', label: (mm < 10 ? '0' + mm : mm) + ':' + (ss < 10 ? '0' + ss : ss), click: '' }; }
|
||||
return { cls: '', label: '去赚取', click: 'onVideo()' };
|
||||
}
|
||||
function coinTag(t) { return '<span class="task-coin"><img class="ci" src="' + A + 'coin-icon.png" alt="">' + esc(t) + '</span>'; }
|
||||
function row(o) {
|
||||
var sub = o.subHtml ? '<div class="task-sub">' + o.subHtml + '</div>' : '';
|
||||
var coin = o.coin ? coinTag(o.coin) : '';
|
||||
var dot = o.dot ? '<span class="dot"></span>' : '';
|
||||
return '<div class="task-row" id="' + o.id + '"><div class="task-info"><div class="task-titlerow"><span class="task-title">' + esc(o.title) + '</span>' + coin + '</div>' + sub +
|
||||
'</div><button class="cta task-cta ' + (o.cls || '') + '" ' + (o.click ? 'onclick="' + o.click + '"' : '') + '>' + esc(o.cta) + dot + '</button></div>';
|
||||
}
|
||||
function renderTasks() {
|
||||
var logged = state.logged, vs = videoCta(), notifCoin = state.notif ? state.notif.coin : 750;
|
||||
el('tasksSlot').innerHTML = [
|
||||
row({ id: 'tCoupon', title: '大额优惠券免费领', coin: '最高500/次', subHtml: '全网神券 AI 帮你一键领齐', cta: '去领券', click: 'onCoupon()', dot: !state.couponDone }),
|
||||
row({ id: 'tCompare', title: '天天比价赚金币', coin: '最高500/次', subHtml: '同款菜品全网比,自动找底价', cta: '去比价', click: 'onCompare()' }),
|
||||
row({ id: 'tVideo', title: '看视频赚海量金币', coin: '最高1000000', subHtml: logged ? '单日最高赚 100 万金币' : '登录后立得海量金币', cta: vs.label, cls: vs.cls, click: vs.click }),
|
||||
row({ id: 'tNotif', title: '打开消息提醒', coin: String(notifCoin), subHtml: '开启推送即得金币', cta: '去开启', click: 'onNotif()' }),
|
||||
row({ id: 'tReport', title: '上报更低价赚金币', coin: '1000/次', subHtml: '每次审核通过得 1000 金币', cta: '去上报', click: 'onReport()' }),
|
||||
row({ id: 'tInvite', title: '邀请好友赚现金', subHtml: '<span class="task-tag">限时大额福利</span>每邀1人必得2元现金', cta: '去邀请', click: 'onInvite()' }),
|
||||
].join('');
|
||||
}
|
||||
setInterval(function () { if (cooldownLeft() > 0 || (state.ad && state.ad.remaining > 0)) { var vs = videoCta(); var b = document.querySelector('#tVideo .task-cta'); if (b) { b.innerHTML = esc(vs.label); b.className = 'cta task-cta ' + vs.cls; b.setAttribute('onclick', vs.click); } } }, 1000);
|
||||
|
||||
// ---------- 签到弹窗(1:1 对齐原型 #checkinPopup) ----------
|
||||
function ckRewardImg(st, isLast) {
|
||||
var src = (isLast || st.coin >= 800) ? 'checkin-chest-8888.png' : (st.coin >= 300 ? 'checkin-coins-600.png' : 'checkin-coin-single.png');
|
||||
var chest = src.indexOf('chest') >= 0 ? ' class="chest"' : '';
|
||||
return '<img' + chest + ' src="' + A + src + '" alt="">';
|
||||
}
|
||||
function roundTotal(s) {
|
||||
if (s && s.steps && s.steps.length) { var t = 0; for (var i = 0; i < Math.min(7, s.steps.length); i++) t += (s.steps[i].coin || 0); return t; }
|
||||
return 2500;
|
||||
}
|
||||
function nextReward(s) {
|
||||
// 明天那档的金币(today_cycle_day 的下一档);第 7 天签完无"明天"档,返回 0(文案走特殊分支)。
|
||||
var steps = (s && s.steps) || [];
|
||||
var today = (s && s.today_cycle_day) || 1;
|
||||
return (steps[today] && steps[today].coin) || 0;
|
||||
}
|
||||
function renderCkLines(view) {
|
||||
var c = el('ckLines'); if (!c) return;
|
||||
var centers = [10, 30, 50, 70, 90], html = '';
|
||||
for (var i = 0; i < view.length - 1; i++) {
|
||||
var jump = (view[i + 1].day - view[i].day) > 1;
|
||||
var reached = view[i + 1].done || view[i + 1].current; // 连到"已达/当前"节点的线段亮黄
|
||||
var lc = (reached && !jump) ? '#FFB800' : '#D9D9D9';
|
||||
html += '<span class="ck-seg ' + (jump ? 'dashed' : 'solid') + '" style="left:' + centers[i] + '%;width:' + (centers[i + 1] - centers[i]) + '%;--lc:' + lc + '"></span>';
|
||||
}
|
||||
c.innerHTML = html;
|
||||
}
|
||||
function renderCkPopup(s) {
|
||||
var view = ckSteps(s, false);
|
||||
var maxDay = (s.steps && s.steps.length) || 7;
|
||||
var lastDaySigned = s.today_signed && (s.today_cycle_day >= maxDay);
|
||||
el('ckBanner').textContent = '连续签到7天,必得' + roundTotal(s) + '金币';
|
||||
el('ckTitle').textContent = s.today_signed ? '今日金币已领取' : '签到领取今日金币';
|
||||
el('ckSub').innerHTML = lastDaySigned
|
||||
? '本轮大额奖励已入账,明日领新一轮大奖'
|
||||
: (s.today_signed
|
||||
? '已连续签到 <b>' + (s.consecutive_days || 0) + '</b> 天,明天可领 <b>' + nextReward(s) + '</b> 金币'
|
||||
: '已连续签到 <b>' + (s.consecutive_days || 0) + '</b> 天,今天可领 <b>' + (s.today_coin || 0) + '</b> 金币');
|
||||
el('ckRewards').innerHTML = view.map(function (v) {
|
||||
if (v.done) return '<div class="ck-rcard claimed"><div class="ck-ricon">已领取</div><div class="ck-ramt">' + v.coin + '</div></div>';
|
||||
return '<div class="ck-rcard' + (v.current ? ' current' : '') + '"><div class="ck-ricon">' + ckRewardImg(v, v.isLastDay) + '</div><div class="ck-ramt">' + v.coin + '</div></div>';
|
||||
}).join('');
|
||||
el('ckProgress').innerHTML = '<div class="ck-lines" id="ckLines"></div>' + view.map(function (v) {
|
||||
var cls = (v.done ? ' done' : '') + (v.current ? ' current' : '');
|
||||
return '<div class="ck-node' + cls + '"><span class="ck-dot"></span><span>' + (v.badge || v.label) + '</span></div>';
|
||||
}).join('');
|
||||
renderCkLines(view);
|
||||
var btn = el('ckBtn'), boost = el('ckBoostBtn');
|
||||
if (s.today_signed) { btn.style.display = 'none'; boost.style.display = 'flex'; }
|
||||
else { btn.style.display = 'flex'; boost.style.display = 'none'; btn.innerHTML = '签到领 <img src="' + A + 'checkin-coin-single.png" alt=""><b>' + (s.today_coin || 0) + '</b>'; }
|
||||
}
|
||||
window.openCheckin = function () {
|
||||
var s = effSignin(); if (!s) { toast('签到信息加载中…'); return; }
|
||||
renderCkPopup(s); el('ckMask').classList.add('show');
|
||||
};
|
||||
window.closeCheckin = function () { el('ckMask').classList.remove('show'); };
|
||||
window.doCheckin = function () {
|
||||
if (dbg.day > 0) { toast('预览模式不发真金币'); return; }
|
||||
var s = state.signin; if (!s || s.today_signed) return;
|
||||
SGApi.post('/signin', {}).then(function (r) {
|
||||
if (r && r.coin_balance != null) { state.coin = r.coin_balance; refreshAssetNumbers(); }
|
||||
toast('签到成功 +' + ((r && r.coin_awarded) || s.today_coin || 0) + ' 金币');
|
||||
// 重拉签到态,弹窗刷成已签(显示膨胀 CTA),不关弹窗(对齐原型)
|
||||
SGApi.get('/signin/status').then(function (ns) { state.signin = ns; renderCheckin(); if (el('ckMask').classList.contains('show')) renderCkPopup(ns); }).catch(function () {});
|
||||
}).catch(function (e) {
|
||||
if (String(e).indexOf('409') >= 0) { toast('今天已签到'); loadSignin(); } else { toast('签到失败,请稍后再试'); }
|
||||
});
|
||||
};
|
||||
window.onSigninBoost = function () { closeCheckin(); guard(function () { nav('welfareTask:signinBoost'); }); };
|
||||
|
||||
// ---------- 调试预览 ----------
|
||||
window.dbgStep = function (d) { dbg.day += d; if (dbg.day > 7) dbg.day = 0; if (dbg.day < 0) dbg.day = 7; el('dbgLabel').textContent = dbg.day ? ('预览签到:第' + dbg.day + '天') : '预览签到:关'; renderCheckin(); };
|
||||
window.dbgToggleSigned = function () { dbg.signed = !dbg.signed; renderCheckin(); };
|
||||
|
||||
// ---------- 数据 ----------
|
||||
function loadAccount() { return SGApi.get('/wallet/account').then(function (a) { if (!a) return; state.coin = a.coin_balance || 0; state.cashCents = a.cash_balance_cents || 0; refreshAssetNumbers(); }).catch(function () {}); }
|
||||
function loadSignin() { return SGApi.get('/signin/status').then(function (s) { state.signin = s; if (dbg.day === 0) renderCheckin(); }).catch(function () {}); }
|
||||
function loadTasks() { return SGApi.get('/tasks').then(function (t) { if (t && t.items) for (var i = 0; i < t.items.length; i++) if (t.items[i].task_key === 'enable_notification') { state.notif = t.items[i]; break; } renderTasks(); }).catch(function () {}); }
|
||||
function loadAd() { return SGApi.get('/ad/reward-status').then(function (a) { state.ad = a; renderTasks(); }).catch(function () {}); }
|
||||
function loadAll() { state.logged = loggedIn(); renderAsset(); renderCheckin(); renderTasks(); if (!state.logged) return; loadAccount(); loadSignin(); loadTasks(); loadAd(); }
|
||||
|
||||
loadAll();
|
||||
setInterval(function () { if (state.logged && !document.hidden) { loadAccount(); loadAd(); } }, 12000);
|
||||
if (window.SGBridge && SGBridge.on) {
|
||||
SGBridge.on('onResume', function () { var was = state.logged; state.logged = loggedIn(); if (state.logged !== was) loadAll(); else if (state.logged) { loadAccount(); loadSignin(); loadTasks(); loadAd(); } });
|
||||
SGBridge.on('onAuthChange', function () { loadAll(); });
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="../shared/fonts.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
||||
<title>活动规则 · 傻瓜比价</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
|
||||
/* WebView 是 MATCH_PARENT,height:100% 链即可拿到真实视口高;页面本身不滚动,内部 .body 才滚。 */
|
||||
html, body { height: 100%; overflow: hidden; }
|
||||
body { font-family: 'PuHuiTi', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; background: #FAFAFA; color: #1A1A1A; }
|
||||
|
||||
/* 【去框】原型 .device 375x812 预览框 → 全屏自适应容器(无 min-height:100vh,避免顶栏下移) */
|
||||
.device { position: relative; width: 100%; height: 100%; background: #FAFAFA; overflow: hidden; }
|
||||
.screen { position: absolute; inset: 0; display: flex; flex-direction: column; }
|
||||
|
||||
/* header —— 与设置/记录等已改造页统一:48px 高、返回居左、标题居中 */
|
||||
.hdr { height: 48px; flex-shrink: 0; display: flex; align-items: center; padding: 0 8px; position: relative; background: #FAFAFA; }
|
||||
.hdr-back { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; background: none; border: none; cursor: pointer; padding: 0; }
|
||||
.hdr-back:active { opacity: .5; }
|
||||
.hdr-back svg { width: 24px; height: 24px; display: block; }
|
||||
.hdr-title { position: absolute; left: 50%; transform: translateX(-50%); font-size: 17px; font-weight: 600; color: #1A1A1A; }
|
||||
|
||||
/* body 内容区可滚 */
|
||||
.body { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 12px 16px calc(24px + env(safe-area-inset-bottom, 0px)); }
|
||||
.body::-webkit-scrollbar { display: none; }
|
||||
|
||||
/* 规则卡片 */
|
||||
.card { background: #FFFFFF; border-radius: 14px; padding: 16px; box-shadow: 0 4px 14px rgba(0,0,0,.04); }
|
||||
.card-title { font-size: 18px; font-weight: 600; color: #1A1A1A; margin-bottom: 12px; }
|
||||
.rules-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.rule-item { display: grid; grid-template-columns: 20px 1fr; gap: 8px; }
|
||||
.rule-no { width: 20px; height: 20px; border-radius: 50%; background: #FFF3C4; color: #8B6914; font-size: 11px; font-weight: 600; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.rule-text { font-size: 13px; line-height: 20px; font-weight: 400; color: #666666; }
|
||||
.rule-text strong { display: block; font-size: 14px; font-weight: 700; color: #1A1A1A; margin-bottom: 2px; line-height: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="device">
|
||||
<div class="screen">
|
||||
<div class="hdr">
|
||||
<button class="hdr-back" onclick="onBack()" aria-label="返回">
|
||||
<svg viewBox="0 0 24 24" fill="none"><path d="M15 6L9 12L15 18" stroke="#1A1A1A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
</button>
|
||||
<div class="hdr-title">活动规则</div>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<div class="card">
|
||||
<div class="card-title">活动规则</div>
|
||||
<div class="rules-list">
|
||||
<div class="rule-item"><span class="rule-no">1</span><div class="rule-text"><strong>金币获取</strong>用户可通过签到、观看视频、打开消息提醒等任务获得金币,具体奖励以页面展示为准。</div></div>
|
||||
<div class="rule-item"><span class="rule-no">2</span><div class="rule-text"><strong>任务发放</strong>完成任务后,金币通常会即时到账;如遇网络延迟或系统审核,到账可能会稍有延迟。</div></div>
|
||||
<div class="rule-item"><span class="rule-no">3</span><div class="rule-text"><strong>兑换说明</strong>金币可按页面展示比例兑换现金,当前按 10000 金币 = 1 元展示,实际兑换以活动页说明为准。</div></div>
|
||||
<div class="rule-item"><span class="rule-no">4</span><div class="rule-text"><strong>邀请奖励</strong>邀请好友奖励按元/现金展示,需好友完成有效注册或指定任务后发放,同一设备、同一账号重复参与不重复计奖。</div></div>
|
||||
<div class="rule-item"><span class="rule-no">5</span><div class="rule-text"><strong>异常处理</strong>如发现刷奖励、虚假邀请、异常设备等违规行为,平台有权取消对应奖励和提现资格。</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../shared/bridge.js"></script>
|
||||
<script>
|
||||
// 返回:交给原生关闭本 H5 路由(回到来源页:赚钱中心 / 邀请页)。系统返回键也走这里。
|
||||
window.onBack = function () {
|
||||
if (window.SGBridge && SGBridge.closePage) { SGBridge.closePage(); }
|
||||
else if (history.length > 1) { history.back(); }
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||