refactor(revenue): drop standalone QR block and align promotion card with reference

The 推广收益 detail card no longer renders the QR code as its own row. The
card becomes a fixed-label info list: 推广链接 [Url][复制链接][推广海报] /
佣金比例 / 累计佣金 / 返佣详解, mirroring the reference theme's 推广中心 card.

- profile-revenue.php: QR row removed; poster button moved into the link row and
  gated server-side on the poster switch; copy label now 复制链接; 推广人数 row
  dropped; 返佣详解 heading reads the existing pay_rebate_text_details_title;
  dead $vip_ratios, aksrt-revenue-page class and data-min-amount removed
- new affiliate-poster-modal.php: poster previews in a modal before download
  (reuses .uc-modal + single-render guard), since the QR now only exists inside
  the poster
- credits.js: renderAffiliateQr and its call deleted, along with the dead
  [data-affiliate-payers] lookup and the inline ch-width hack; initAffiliatePoster
  builds the poster into the modal stage and reveals 保存海报
- PaymentRuntime: expose affiliate details_title
- user-center.js: remove never-defined AKSRTRevenueCenterInit guards (2 sites)
- CSS: revenue-earnings.css pruned to live rules plus the new link-row grid and
  poster stage styles; routes/user-center/revenue.css pruned to its only
  definitions; dead leaves routes/revenue.css and routes/commerce.css deleted and
  dequeued from the user-center part list
- tests: referral-qr-contract test 2 rewritten for the QR-free card, link row and
  poster modal; post-visual-contract leaf list updated with regression guards

qrcode.min.js stays enqueued — buildPosterCanvas still draws the QR inside the
poster. Net -833 lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 21:42:57 +08:00
co-authored by Claude Fable 5
parent 8d16ce6e02
commit 7761435a81
12 changed files with 136 additions and 933 deletions
+38 -73
View File
@@ -125,19 +125,12 @@
function setFeedback(text, error) { if (feedback) { feedback.textContent = text; feedback.classList.toggle('is-error', !!error); } }
api('/revenue/me').then(function (r) {
if (!r.ok) { setFeedback((r.json && r.json.message) || '推广数据加载失败。', true); return; }
var j = r.json || {}, stats = j.stats || {};
var j = r.json || {};
if (input) {
input.value = j.url || '';
/* Keep the complete ref URL readable on desktop; mobile CSS still caps it. */
var linkLength = String(j.url || '').length;
input.setAttribute('size', String(Math.max(28, Math.min(96, linkLength || 28))));
input.style.width = Math.max(28, Math.min(96, linkLength || 28)) + 'ch';
}
if (copy) copy.disabled = !j.url;
var ratio = root.querySelector('[data-affiliate-ratio]'); if (ratio) ratio.textContent = String(j.current_ratio || 0) + '%';
var promoters = root.querySelector('[data-affiliate-promoters]'); if (promoters) promoters.textContent = String(stats.promoters || 0);
var payers = root.querySelector('[data-affiliate-payers]'); if (payers) payers.textContent = String(stats.effective_payers || 0);
renderAffiliateQr(root, j.url || '');
initAffiliatePoster(root, j.url || '', j.poster || {}, j.site_name || document.title);
}).catch(function () { setFeedback('推广数据加载失败。', true); });
if (copy) copy.addEventListener('click', function () {
@@ -148,78 +141,50 @@
});
}
// ===== Referral QR code (bundled qrcode-generator, no CDN) =====
function renderAffiliateQr(root, url) {
var row = root.querySelector('[data-affiliate-qr-row]');
var canvas = root.querySelector('[data-affiliate-qr]');
if (!row || !canvas) return;
if (!url || typeof window.qrcode !== 'function') {
row.hidden = true;
return;
}
var feedback = root.querySelector('[data-affiliate-qr-feedback]');
function setQrFeedback(text, error) { if (feedback) { feedback.textContent = text; feedback.classList.toggle('is-error', !!error); } }
try {
var qr = window.qrcode(0, 'M');
qr.addData(url);
qr.make();
var count = qr.getModuleCount();
var cell = 4, margin = 4;
var size = (count + margin * 2) * cell;
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = '#0f172a';
for (var r = 0; r < count; r++) {
for (var c = 0; c < count; c++) {
if (qr.isDark(r, c)) {
ctx.fillRect((c + margin) * cell, (r + margin) * cell, cell, cell);
}
}
}
} catch (e) {
row.hidden = true;
return;
}
var download = root.querySelector('[data-affiliate-qr-download]');
if (download) download.addEventListener('click', function () {
try {
var link = document.createElement('a');
link.download = 'referral-qr.png';
link.href = canvas.toDataURL('image/png');
document.body.appendChild(link);
link.click();
link.remove();
setQrFeedback('二维码已下载。');
} catch (err) {
setQrFeedback('二维码下载失败,请截图保存。', true);
}
});
}
// ===== Referral poster (canvas: hero image + copy + QR + link) =====
// ===== Referral poster (canvas preview modal: hero image + copy + QR + link) =====
function initAffiliatePoster(root, url, poster, siteName) {
var button = root.querySelector('[data-affiliate-poster]');
if (!button) return;
var modal = document.querySelector('[data-aksrt-affiliate-poster-modal]');
var stage = document.querySelector('[data-affiliate-poster-stage]');
var download = document.querySelector('[data-affiliate-poster-download]');
if (!button || !modal) return;
if (!url || !poster || poster.enabled !== true) return;
button.hidden = false;
[].forEach.call(modal.querySelectorAll('[data-affiliate-poster-close]'), function (closer) {
closer.addEventListener('click', function () { closeModal(modal); });
});
var canvas = null;
if (download) {
download.addEventListener('click', function () {
if (!canvas) return;
try {
var link = document.createElement('a');
link.download = 'referral-poster.png';
link.href = canvas.toDataURL('image/png');
document.body.appendChild(link);
link.click();
link.remove();
} catch (err) {
/* Canvas is same-origin generated; download can only fail if the browser blocks it. */
}
});
}
button.addEventListener('click', function () {
var feedback = root.querySelector('[data-affiliate-qr-feedback]');
function setPosterFeedback(text, error) { if (feedback) { feedback.textContent = text; feedback.classList.toggle('is-error', !!error); } }
if (typeof window.qrcode !== 'function') { setPosterFeedback('二维码组件未加载。', true); return; }
if (typeof window.qrcode !== 'function') {
if (stage) { stage.innerHTML = '<p class="muted-2-color em09">' + '二维码组件未加载。' + '</p>'; }
openModal(modal);
return;
}
if (stage) { stage.innerHTML = '<p class="muted-2-color em09">' + '正在生成海报…' + '</p>'; }
if (download) download.hidden = true;
openModal(modal);
button.disabled = true;
buildPosterCanvas(url, poster, siteName).then(function (canvas) {
var link = document.createElement('a');
link.download = 'referral-poster.png';
link.href = canvas.toDataURL('image/png');
document.body.appendChild(link);
link.click();
link.remove();
setPosterFeedback('推广海报已生成并下载。');
buildPosterCanvas(url, poster, siteName).then(function (built) {
canvas = built;
if (stage) { stage.innerHTML = ''; stage.appendChild(built); }
if (download) download.hidden = false;
}).catch(function () {
setPosterFeedback('海报生成失败,请重试。', true);
if (stage) { stage.innerHTML = '<p class="muted-2-color em09">' + '海报生成失败,请重试。' + '</p>'; }
}).finally(function () {
button.disabled = false;
});