@seasonkoh/webaz 0.1.20 → 0.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3256,7 +3256,7 @@ async function renderAdminDashboard(app) {
3256
3256
  ])
3257
3257
  const kpiTokenomics2 = kpiGrid([
3258
3258
  { label: t('累计分享分润'),value: Number(tk.commission_total || 0).toFixed(2), unit: 'WAZ' },
3259
- { label: t('累计对碰发放'),value: Number(tk.binary_waz_total || 0).toFixed(2), unit: 'WAZ' },
3259
+ { label: t('累计匹配发放'),value: Number(tk.binary_waz_total || 0).toFixed(2), unit: 'WAZ' },
3260
3260
  { label: t('PV 待处理'), value: tk.ledger_pending ?? 0 },
3261
3261
  ])
3262
3262
  // 异常告警 banner — 多条件聚合
@@ -3298,7 +3298,7 @@ async function renderAdminDashboard(app) {
3298
3298
  </div>
3299
3299
  <div style="font-size:13px;color:#6b7280;margin:16px 0 8px">⚛ ${t('Tokenomics')}</div>
3300
3300
  <div style="display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:16px">
3301
- ${quickAction('#admin/tokenomics', '⚛', t('双轨基金 / Tier 配置 / 高额榜'))}
3301
+ ${quickAction('#admin/tokenomics', '⚛', t('积分基金 / Tier 配置 / 高额榜'))}
3302
3302
  </div>`
3303
3303
 
3304
3304
  // A5 重设:渐变标题 + 分区标题 + 颜色块分组
@@ -4352,7 +4352,7 @@ async function renderApplyRewards(app) {
4352
4352
  <div class="card" style="margin-bottom:16px;background:#fef2f2;border-color:#fca5a5">
4353
4353
  <div style="font-weight:600;color:#991b1b;margin-bottom:6px;font-size:14px">⚠️ ${t('本流程与购物无关')}</div>
4354
4354
  <div style="font-size:12px;color:#7f1d1d;line-height:1.6">
4355
- ${t('你可以随时退出,不影响任何已下单或未来订单。本流程涉及经济关系登记(三级佣金 + 双轨配对),请仔细阅读全部条款。')}<br>
4355
+ ${t('你可以随时退出,不影响任何已下单或未来订单。本流程涉及经济关系登记(三级佣金 + 积分配对),请仔细阅读全部条款。')}<br>
4356
4356
  <span style="opacity:0.85">This flow is not part of shopping. You can leave anytime without affecting any orders. This is an economic-relationship registration — please read all terms.</span>
4357
4357
  </div>
4358
4358
  </div>
@@ -5536,7 +5536,7 @@ async function renderAdminTokenomics(app) {
5536
5536
  <h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🎁 ${t('管理津贴池')}</h2>
5537
5537
  <div class="card" style="margin-bottom:12px">
5538
5538
  <div style="font-size:18px;font-weight:700">${Number(mb.balance ?? 0).toFixed(2)} WAZ</div>
5539
- <div style="font-size:11px;color:#9ca3af;margin-top:4px">${t('来自协议费 50%,用于大博主对碰团队 10/5/2% 补贴')}</div>
5539
+ <div style="font-size:11px;color:#9ca3af;margin-top:4px">${t('来自协议费 50%,用于大博主匹配团队 10/5/2% 补贴')}</div>
5540
5540
  <div id="mgmt-bonus-control" style="margin-top:12px;padding:10px;background:#fef9c3;border:1px solid #fde047;border-radius:6px">
5541
5541
  <div style="font-size:12px;font-weight:600;margin-bottom:6px">⚠️ ${t('津贴门控')}</div>
5542
5542
  <div id="mgmt-bonus-status" style="font-size:11px;color:#92400e;margin-bottom:8px">${t('加载中...')}</div>
@@ -5560,7 +5560,7 @@ async function renderAdminTokenomics(app) {
5560
5560
  <div class="card" style="margin-bottom:12px">
5561
5561
  <div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:10px">
5562
5562
  <button class="btn btn-outline btn-sm" style="font-size:11px" onclick="doTkProcess()">${t('处理 PV 流水')}</button>
5563
- <button class="btn btn-outline btn-sm" style="font-size:11px" onclick="doTkSettle()">${t('触发对碰结算')}</button>
5563
+ <button class="btn btn-outline btn-sm" style="font-size:11px" onclick="doTkSettle()">${t('触发匹配结算')}</button>
5564
5564
  <button class="btn btn-outline btn-sm" style="font-size:11px;color:#dc2626;border-color:#dc2626" onclick="if(confirm('${t('确认分发 WAZ?这将清空池子按 N 比例分配')}'))doTkDistribute()">${t('分发 WAZ(清池)')}</button>
5565
5565
  </div>
5566
5566
  <div style="font-size:12px;color:#6b7280">${t('PV 待处理流水')}: ${data.pv_ledger?.pending ?? 0} ${t('条')} · ${t('待累积 PV')}: ${Number(data.pv_ledger?.pending_pv ?? 0).toLocaleString()}</div>
@@ -5580,10 +5580,10 @@ async function renderAdminTokenomics(app) {
5580
5580
  <div style="padding:8px 12px;font-size:11px;color:#6b7280">${t('调整:POST /api/admin/tokenomics/tier with body {tier, pv_threshold, score_per_hit, active}')}</div>
5581
5581
  </div>
5582
5582
 
5583
- <h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top 推土机佣金')} (Top 10)</h2>
5583
+ <h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top 三级佣金')} (Top 10)</h2>
5584
5584
  <div class="card" style="padding:0;margin-bottom:12px">${commRows}</div>
5585
5585
 
5586
- <h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top 对碰收益')} (Top 10)</h2>
5586
+ <h2 style="font-size:15px;font-weight:600;margin:16px 0 8px">🏆 ${t('Top 匹配收益')} (Top 10)</h2>
5587
5587
  <div class="card" style="padding:0;margin-bottom:12px">${binaryRows}</div>
5588
5588
  `, 'admin')
5589
5589
  setTimeout(loadRequireRefStatus, 100)
@@ -5643,7 +5643,7 @@ window.doTkProcess = async () => {
5643
5643
  window.doTkSettle = async () => {
5644
5644
  const res = await POST('/admin/atomic/run-settlement', {})
5645
5645
  const msg = document.getElementById('tk-msg')
5646
- if (msg) msg.innerHTML = res.error ? alert$('error', res.error) : alert$('success', `${t('已触发对碰')}: ${res.settled}`)
5646
+ if (msg) msg.innerHTML = res.error ? alert$('error', res.error) : alert$('success', `${t('已触发匹配')}: ${res.settled}`)
5647
5647
  setTimeout(() => renderAdminTokenomics(document.getElementById('app')), 800)
5648
5648
  }
5649
5649
 
@@ -6716,10 +6716,10 @@ async function renderPromoter(app) {
6716
6716
  const _kpiRestricted = _mlmMax <= 1
6717
6717
  // 两个语义独立的 gate(三级奖励 ≠ PV 双轨系统)—— 2026-06-04 已解耦:
6718
6718
  // - _kpiRestricted (max ≤ 1):紧张地区 — KPI 改时间线 / team 隐 WAZ
6719
- // - _pvAllowed:PV 双轨/对碰系统是否开启,读 region_pv_enabled(独立旋钮,不再绑 max≥3)
6719
+ // - _pvAllowed:PV 双轨/匹配系统是否开启,读 region_pv_enabled(独立旋钮,不再绑 max≥3)
6720
6720
  // commission 层级(max_levels) 与 PV 系统(pv_enabled) 分离:可单独开某辖区到 L2/L3 而 PV 仍关。
6721
6721
  const _pvAllowed = Number(state.user?.region_pv_enabled ?? 0) === 1
6722
- // 紧张地区:拼装最近奖励时间线(commission + 对碰 binary 混合,按时间倒序取 5 条)
6722
+ // 紧张地区:拼装最近奖励时间线(commission + 匹配 binary 混合,按时间倒序取 5 条)
6723
6723
  let kpiBar = ''
6724
6724
  if (_kpiRestricted) {
6725
6725
  const _cm = (data.recent || []).slice(0, 10).map(r => ({
@@ -7051,13 +7051,13 @@ async function renderPromoter(app) {
7051
7051
 
7052
7052
  // ─── ⑥ WebAZ 发展奖(默认折叠;紧张 PV 地区精简版)───
7053
7053
  // 合规:max_levels ≤ 1 的地区(GCC/越南/印尼/菲律宾 + 未审计地区)
7054
- // 只显示"累计推广 N 人"客观数字,全部金钱/tier/对碰话术隐藏
7054
+ // 只显示"累计推广 N 人"客观数字,全部金钱/tier/匹配话术隐藏
7055
7055
  // 复用顶部 _kpiRestricted(同一含义,避免重复定义)
7056
7056
  const _totalRecruits = Number(data.team?.l1 || 0) + Number(data.team?.l2 || 0) + Number(data.team?.l3 || 0)
7057
7057
  // 最右侧地区显示(所有模式都加)
7058
7058
  const _userRegion = state.user?.region || 'global'
7059
7059
  const _regionChip = `<span style="font-size:11px;color:#6b7280;white-space:nowrap;font-weight:400">${regionLabel(_userRegion)}</span>`
7060
- // PV 双轨系统:只在 _pvAllowed(region_pv_enabled=1,2026-06-04 解耦,独立于 max_levels)才显示完整 tier/对碰/弱腿;
7060
+ // PV 双轨系统:只在 _pvAllowed(region_pv_enabled=1,2026-06-04 解耦,独立于 max_levels)才显示完整 tier/匹配/弱侧;
7061
7061
  // 未开启 PV 的地区 → 走精简卡(分数仍后台计算累积,待 pv_enabled 开启/迁移后兑现)
7062
7062
  const atomicSection = atomic.left_invite_url
7063
7063
  ? (!_pvAllowed
@@ -7080,7 +7080,7 @@ async function renderPromoter(app) {
7080
7080
  <span>
7081
7081
  🌟 ${t('WebAZ 发展奖')}
7082
7082
  <span style="font-weight:400;font-size:12px;color:#6b7280;margin-left:6px">
7083
- ${t('弱腿')} ${weak.toLocaleString()} PV · ${t('累计')} ${(atomic.score?.settled_waz || 0).toFixed(2)} WAZ
7083
+ ${t('弱侧')} ${weak.toLocaleString()} PV · ${t('累计')} ${(atomic.score?.settled_waz || 0).toFixed(2)} WAZ
7084
7084
  </span>
7085
7085
  </span>
7086
7086
  ${_regionChip}
@@ -7266,7 +7266,7 @@ window.setBoughtKw = (kw) => {
7266
7266
  }, 200)
7267
7267
  }
7268
7268
 
7269
- // 双轨对碰内部内容(折叠展开后渲染,从 renderAtomicSection 简化抽出)
7269
+ // 积分匹配内部内容(折叠展开后渲染,从 renderAtomicSection 简化抽出)
7270
7270
  function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7271
7271
  const tiers = a.tier_config || []
7272
7272
  const tierTable = tiers.map(x => `
@@ -7276,7 +7276,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7276
7276
  <td style="padding:5px 8px;font-size:12px;text-align:right;font-weight:600">${x.score_per_hit}</td>
7277
7277
  </tr>`).join('')
7278
7278
 
7279
- // 本月对碰次数 + WAZ
7279
+ // 本月匹配次数 + WAZ
7280
7280
  const now = new Date()
7281
7281
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10)
7282
7282
  const monthBinary = (a.recent_binary || []).filter(r => (r.created_at || '').slice(0, 10) >= monthStart)
@@ -7293,7 +7293,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7293
7293
  <div>tier ${r.tier} · Score <strong>${r.score}</strong> · ${r.settled_at ? `<span style="color:#16a34a">${t('已结')} ${Number(r.waz_amount).toFixed(2)}</span>` : `<span style="color:#d97706">${t('待分配')}</span>`}</div>
7294
7294
  <div style="color:#9ca3af">${fmtTime(r.created_at)}</div>
7295
7295
  </div>`).join('')
7296
- : `<div style="text-align:center;color:#9ca3af;padding:10px;font-size:12px">${t('暂无对碰记录')}</div>`
7296
+ : `<div style="text-align:center;color:#9ca3af;padding:10px;font-size:12px">${t('暂无匹配记录')}</div>`
7297
7297
 
7298
7298
  return `
7299
7299
  <!-- 我的位置 -->
@@ -7302,7 +7302,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7302
7302
  📍 ${t('我挂位置')}: <strong>${escHtml(a.my_placement.name)}</strong> 的 ${a.my_placement.side === 'left' ? '🔵 左侧' : '🟢 右侧'}
7303
7303
  </div>` : `
7304
7304
  <div style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px;padding:8px 10px;font-size:12px;color:#9ca3af;margin-bottom:10px">
7305
- ${t('你还没加入任何上级的双轨树(独立根节点)')}
7305
+ ${t('你还没加入任何上级的积分树(独立根节点)')}
7306
7306
  </div>`}
7307
7307
 
7308
7308
  <!-- 左/右 PV -->
@@ -7319,25 +7319,25 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7319
7319
  </div>
7320
7320
  </div>
7321
7321
 
7322
- <!-- 本月对碰统计 + 总对碰 -->
7322
+ <!-- 本月匹配统计 + 总匹配 -->
7323
7323
  <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:6px;margin-bottom:10px">
7324
7324
  <div style="text-align:center;background:#fef3c7;border-radius:6px;padding:8px 4px">
7325
- <div style="font-size:10px;color:#92400e">${t('本月对碰次数')}</div>
7325
+ <div style="font-size:10px;color:#92400e">${t('本月匹配次数')}</div>
7326
7326
  <div style="font-size:14px;font-weight:700;color:#78350f">${monthHits}</div>
7327
7327
  </div>
7328
7328
  <div style="text-align:center;background:#dcfce7;border-radius:6px;padding:8px 4px">
7329
- <div style="font-size:10px;color:#166534">${t('本月对碰 WAZ')}</div>
7329
+ <div style="font-size:10px;color:#166534">${t('本月匹配 WAZ')}</div>
7330
7330
  <div style="font-size:14px;font-weight:700;color:#14532d">${monthWaz.toFixed(2)}</div>
7331
7331
  </div>
7332
7332
  <div style="text-align:center;background:#eef2ff;border-radius:6px;padding:8px 4px">
7333
- <div style="font-size:10px;color:#4338ca">${t('累计对碰次数')}</div>
7333
+ <div style="font-size:10px;color:#4338ca">${t('累计匹配次数')}</div>
7334
7334
  <div style="font-size:14px;font-weight:700;color:#3730a3">${totalHits}</div>
7335
7335
  </div>
7336
7336
  </div>
7337
7337
 
7338
7338
  ${renderBinaryTree(a.binary_tree)}
7339
7339
 
7340
- <div style="font-size:12px;color:#6b7280;margin-bottom:6px">${t('弱腿对碰量')}: <strong>${weak.toLocaleString()}</strong> PV</div>
7340
+ <div style="font-size:12px;color:#6b7280;margin-bottom:6px">${t('弱侧匹配量')}: <strong>${weak.toLocaleString()}</strong> PV</div>
7341
7341
  ${nextTier ? `
7342
7342
  <div style="font-size:11px;color:#6b7280;margin-bottom:4px">${t('距离 tier')} ${nextTier.tier} ${t('门槛')} ${Number(nextTier.pv_threshold).toLocaleString()} PV (+${nextTier.score_per_hit} Score)</div>
7343
7343
  <div style="background:#f3f4f6;height:6px;border-radius:3px;overflow:hidden;margin-bottom:10px">
@@ -7345,7 +7345,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7345
7345
  </div>` : ''}
7346
7346
 
7347
7347
  <details style="margin-bottom:10px">
7348
- <summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('对碰档位表')}</summary>
7348
+ <summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('匹配档位表')}</summary>
7349
7349
  <table style="width:100%;margin-top:6px;border-collapse:collapse">
7350
7350
  <tr style="background:#f9fafb;font-size:11px;color:#6b7280">
7351
7351
  <th style="padding:5px 8px;text-align:left">Tier</th>
@@ -7355,7 +7355,7 @@ function renderAtomicInner(a, leftPv, rightPv, weak, nextTier, nextProgress) {
7355
7355
  ${tierTable}
7356
7356
  </table>
7357
7357
  </details>
7358
- <h4 style="font-size:12px;font-weight:600;margin:6px 0">📊 ${t('最近对碰')}</h4>
7358
+ <h4 style="font-size:12px;font-weight:600;margin:6px 0">📊 ${t('最近匹配')}</h4>
7359
7359
  ${recentRows}`
7360
7360
  }
7361
7361
 
@@ -7510,10 +7510,10 @@ function renderAtomicSection(a) {
7510
7510
  <div>tier ${r.tier} · Score <strong>${r.score}</strong> · ${r.settled_at ? `<span style="color:#16a34a">已结 ${Number(r.waz_amount).toFixed(2)} WAZ</span>` : `<span style="color:#d97706">待分配</span>`}</div>
7511
7511
  <div style="color:#9ca3af">${fmtTime(r.created_at)}</div>
7512
7512
  </div>`).join('')
7513
- : `<div style="text-align:center;color:#9ca3af;padding:16px;font-size:12px">${t('暂无对碰记录')}</div>`
7513
+ : `<div style="text-align:center;color:#9ca3af;padding:16px;font-size:12px">${t('暂无匹配记录')}</div>`
7514
7514
 
7515
7515
  return `
7516
- <h2 style="font-size:15px;font-weight:600;margin:24px 0 8px">⚛ ${t('原子能双轨对碰')}</h2>
7516
+ <h2 style="font-size:15px;font-weight:600;margin:24px 0 8px">⚛ ${t('积分积分匹配')}</h2>
7517
7517
 
7518
7518
  <div class="card" style="margin-bottom:12px;background:linear-gradient(135deg,#dbeafe,#f0fdf4)">
7519
7519
  <div style="font-size:13px;color:#6b7280;margin-bottom:6px">🔗 ${t('左右码(完全对称 / 末端垂直挂靠)')}</div>
@@ -7568,7 +7568,7 @@ function renderAtomicSection(a) {
7568
7568
  ${renderBinaryTree(a.binary_tree)}
7569
7569
 
7570
7570
  <div class="card" style="margin-bottom:12px">
7571
- <div style="font-size:13px;color:#6b7280;margin-bottom:6px">${t('弱腿对碰量')}: <strong>${pair.toLocaleString()}</strong> PV</div>
7571
+ <div style="font-size:13px;color:#6b7280;margin-bottom:6px">${t('弱侧匹配量')}: <strong>${pair.toLocaleString()}</strong> PV</div>
7572
7572
  ${nextTier ? `
7573
7573
  <div style="font-size:11px;color:#6b7280;margin-bottom:4px">${t('距离 tier')} ${nextTier.tier} ${t('门槛')} ${Number(nextTier.pv_threshold).toLocaleString()} PV (${nextTier.score_per_hit} Score)</div>
7574
7574
  <div style="background:#f3f4f6;height:6px;border-radius:3px;overflow:hidden">
@@ -7588,7 +7588,7 @@ function renderAtomicSection(a) {
7588
7588
  </div>
7589
7589
 
7590
7590
  <details style="margin-bottom:12px">
7591
- <summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('对碰档位表')}</summary>
7591
+ <summary style="font-size:12px;color:#6366f1;cursor:pointer">${t('匹配档位表')}</summary>
7592
7592
  <table style="width:100%;margin-top:8px;border-collapse:collapse">
7593
7593
  <tr style="background:#f9fafb;font-size:11px;color:#6b7280">
7594
7594
  <th style="padding:6px 8px;text-align:left">Tier</th>
@@ -7599,13 +7599,13 @@ function renderAtomicSection(a) {
7599
7599
  </table>
7600
7600
  </details>
7601
7601
 
7602
- <h3 style="font-size:13px;font-weight:600;margin:8px 0">📊 ${t('最近对碰')}</h3>
7602
+ <h3 style="font-size:13px;font-weight:600;margin:8px 0">📊 ${t('最近匹配')}</h3>
7603
7603
  <div class="card" style="padding:0">
7604
7604
  ${recentRows}
7605
7605
  </div>`
7606
7606
  }
7607
7607
 
7608
- // P12: 三层双轨树(你 + 左右 + 各自左右)
7608
+ // P12: 三层积分树(你 + 左右 + 各自左右)
7609
7609
  function renderBinaryTree(tree) {
7610
7610
  if (!tree || !tree.me) return ''
7611
7611
  const node = (n, bg, fg, label) => n
@@ -7617,7 +7617,7 @@ function renderBinaryTree(tree) {
7617
7617
 
7618
7618
  return `
7619
7619
  <details style="margin-bottom:12px" open>
7620
- <summary style="font-size:12px;color:#6366f1;cursor:pointer;margin-bottom:6px">🌳 ${t('双轨组织图(3 层)')} <span style="color:#9ca3af;font-size:10px">${t('点击节点查看 PV')}</span></summary>
7620
+ <summary style="font-size:12px;color:#6366f1;cursor:pointer;margin-bottom:6px">🌳 ${t('团队组织图(3 层)')} <span style="color:#9ca3af;font-size:10px">${t('点击节点查看 PV')}</span></summary>
7621
7621
  <div style="padding:8px;background:#fff;border:1px solid #e5e7eb;border-radius:8px">
7622
7622
  <!-- L0: 你 -->
7623
7623
  <div style="margin-bottom:6px">
@@ -7693,8 +7693,8 @@ window.showNodePvModal = async (userId) => {
7693
7693
  </div>
7694
7694
 
7695
7695
  <div style="background:#f9fafb;border-radius:6px;padding:8px 10px;font-size:11px;color:#6b7280;line-height:1.7;margin-bottom:10px">
7696
- ${t('弱腿对碰量')}: <strong style="color:#374151">${weak.toLocaleString()}</strong> PV · ${t('双腿均衡')} ${ratio}%<br>
7697
- ${t('累计对碰')}: <strong style="color:#374151">${data.total_hits || 0}</strong> ${t('次')} · ${t('累计获 WAZ')}: <strong style="color:#16a34a">${Number(data.settled_waz || 0).toFixed(2)}</strong><br>
7696
+ ${t('弱侧匹配量')}: <strong style="color:#374151">${weak.toLocaleString()}</strong> PV · ${t('双腿均衡')} ${ratio}%<br>
7697
+ ${t('累计匹配')}: <strong style="color:#374151">${data.total_hits || 0}</strong> ${t('次')} · ${t('累计获 WAZ')}: <strong style="color:#16a34a">${Number(data.settled_waz || 0).toFixed(2)}</strong><br>
7698
7698
  ${t('待结 Score')}: <strong style="color:#d97706">${Number(data.pending_score || 0).toFixed(1)}</strong>
7699
7699
  </div>
7700
7700
 
@@ -7829,11 +7829,11 @@ async function maybePromptPlacementBind() {
7829
7829
  const st = await GET('/profile/placement-status')
7830
7830
  if (!st.can_bind) return // 已有 placement 或 有下线 → 不弹
7831
7831
  setTimeout(() => {
7832
- const ok = confirm(`📍 ${t('系统检测到邀请链接')}\n\n${t('inviter')}: ${inviter}\n\n${t('是否加入对方的双轨 PV 树?')}\n${t('系统将按推荐人偏好自动选择左/右区。一旦加入永久不变。')}`)
7832
+ const ok = confirm(`📍 ${t('系统检测到邀请链接')}\n\n${t('inviter')}: ${inviter}\n\n${t('是否加入对方的积分配对?')}\n${t('系统将按推荐人偏好自动选择左/右区。一旦加入永久不变。')}`)
7833
7833
  if (ok) {
7834
7834
  POST('/profile/bind-placement', { inviter_id: inviter, side: hint.placement_side || undefined }).then(res => {
7835
7835
  if (res.error) alert(`✗ ${res.error}`)
7836
- else alert(`✓ ${t('已加入双轨树')}\n${t('侧')}: ${res.side}\n${t('深度')}: ${res.depth}`)
7836
+ else alert(`✓ ${t('已加入积分树')}\n${t('侧')}: ${res.side}\n${t('深度')}: ${res.depth}`)
7837
7837
  })
7838
7838
  }
7839
7839
  localStorage.setItem(askedKey, Date.now().toString())
@@ -10209,7 +10209,7 @@ async function renderMyAgents(app) {
10209
10209
  <summary style="cursor:pointer;color:#6366f1;padding:4px 0">📊 ${t('为什么是')} ${lvlLabel} ${t('级?')}</summary>
10210
10210
  <div style="background:#f9fafb;border-radius:6px;padding:8px 10px;margin-top:4px;font-family:monospace;line-height:1.6">
10211
10211
  <div>${t('加分')}:${t('账号年龄')} +${rep.signals.age_pts} · ${t('订单数')} +${rep.signals.order_pts} · ${t('分享转化')} +${rep.signals.share_pts} · ${t('调用多样性')} +${rep.signals.diversity_pts}</div>
10212
- ${(rep.signals.dispute_penalty + rep.signals.sybil_penalty + rep.signals.cross_penalty + rep.signals.ratelimit_penalty) < 0 ? `<div style="color:#dc2626">${t('扣分')}:${t('争议败诉')} ${rep.signals.dispute_penalty} · ${t('同 IP 异户')} ${rep.signals.sybil_penalty} · ${t('双轨命中')} ${rep.signals.cross_penalty} · ${t('限速命中')} ${rep.signals.ratelimit_penalty}</div>` : ''}
10212
+ ${(rep.signals.dispute_penalty + rep.signals.sybil_penalty + rep.signals.cross_penalty + rep.signals.ratelimit_penalty) < 0 ? `<div style="color:#dc2626">${t('扣分')}:${t('争议败诉')} ${rep.signals.dispute_penalty} · ${t('同 IP 异户')} ${rep.signals.sybil_penalty} · ${t('匹配命中')} ${rep.signals.cross_penalty} · ${t('限速命中')} ${rep.signals.ratelimit_penalty}</div>` : ''}
10213
10213
  <div style="color:#9ca3af;margin-top:4px">${t('阈值')}:new&lt;20 · trusted≥20 · quality≥50 · legend≥80</div>
10214
10214
  </div>
10215
10215
  </details>
@@ -11688,8 +11688,8 @@ window.doLoginByPassword = async () => {
11688
11688
 
11689
11689
 
11690
11690
  // 从 URL 解析分享 hint(30 天 cookie 持久化)
11691
- // ?ref=usr_xxx → 仅推土机 sponsor
11692
- // ?placement=usr_xxx&side=left|right → 仅原子能挂靠
11691
+ // ?ref=usr_xxx → 仅三级佣金 sponsor
11692
+ // ?placement=usr_xxx&side=left|right → 仅积分挂靠
11693
11693
  // ?ref=xxx&side=left|right → 两轨道(ref 同时作 placement)
11694
11694
  // ?ref=xxx&placement=xxx&side=... → 显式两轨道
11695
11695
  function readShareHint() {
@@ -12059,10 +12059,10 @@ function showRegisterSuccessModal(res) {
12059
12059
  const targetBtn = (intended && intended !== '#' && intended !== '')
12060
12060
  ? `<button class="btn btn-primary" style="width:100%;margin-bottom:10px" onclick="window._closeRegModal(true)">🎯 ${t('回到原页')} ${tgt?.title ? '— ' + escHtml(tgt.title) : ''} →</button>`
12061
12061
  : ''
12062
- // P1 (QA 轮 14.a): 区分"邀请人"(referral sponsor) 与"双轨树挂靠"(binary placement)
12062
+ // P1 (QA 轮 14.a): 区分"邀请人"(referral sponsor) 与"积分树挂靠"(binary placement)
12063
12063
  // 旧版挤一行 "已绑定邀请人 X · 左区 depth N" → 用户误以为 depth 是相对邀请人
12064
12064
  const placementLine = res.placement
12065
- ? `<div style="font-size:11px;color:#9ca3af;margin-bottom:14px">${t('双轨树挂靠')}: ${res.placement.side === 'left' ? '🔵 ' + t('左区') : '🟢 ' + t('右区')} · ${t('深度')} ${res.placement.depth}<br><span style="font-size:10px">${t('(挂靠位置由系统按弱区自动决定,与邀请人不一定相邻)')}</span></div>`
12065
+ ? `<div style="font-size:11px;color:#9ca3af;margin-bottom:14px">${t('积分树挂靠')}: ${res.placement.side === 'left' ? '🔵 ' + t('左区') : '🟢 ' + t('右区')} · ${t('深度')} ${res.placement.depth}<br><span style="font-size:10px">${t('(挂靠位置由系统按弱区自动决定,与邀请人不一定相邻)')}</span></div>`
12066
12066
  : ''
12067
12067
  _openModal(`
12068
12068
  <h2 style="font-size:18px;font-weight:600;margin-bottom:8px;color:#16a34a">🎉 ${t('注册成功!')}</h2>
@@ -13570,7 +13570,7 @@ async function renderUserProfile(app, userId) {
13570
13570
  <input type="checkbox" id="feed-visible-tg" style="width:16px;height:16px" ${data.feed_visible ? 'checked' : ''} onchange="toggleFeedVisible(this.checked)">
13571
13571
  <label for="feed-visible-tg" style="font-size:13px;cursor:pointer">${t('在公开动态流显示我的活动')}</label>
13572
13572
  </div>
13573
- <p style="font-size:11px;color:#9ca3af;margin-top:4px;margin-left:26px">${t('关闭后,你的购买/对碰/分润事件不会出现在 发现好物 > 动态')}</p>
13573
+ <p style="font-size:11px;color:#9ca3af;margin-top:4px;margin-left:26px">${t('关闭后,你的购买/匹配/分润事件不会出现在 发现好物 > 动态')}</p>
13574
13574
 
13575
13575
  <div id="social-msg" style="margin-top:10px"></div>
13576
13576
 
@@ -20959,7 +20959,7 @@ window.handleArbitrate = async (disputeId) => {
20959
20959
  const amtEl = document.querySelector(`.arb-liable-amount[data-party="${partyId}"]`)
20960
20960
  const amt = parseFloat(amtEl?.value || '0')
20961
20961
  if (!amt || amt <= 0) {
20962
- msgEl.innerHTML = alert$('error', `${t('请填写 ')}${chk.dataset.name}${t(' 的赔付金额')}`); return
20962
+ msgEl.innerHTML = alert$('error', `${t('请填写 ')}${escHtml(chk.dataset.name)}${t(' 的赔付金额')}`); return
20963
20963
  }
20964
20964
  const entry = { user_id: partyId, role: chk.dataset.role, amount: amt }
20965
20965
  const insuranceChk = document.querySelector(`.arb-liable-insurance[data-party="${partyId}"]`)
@@ -21736,13 +21736,13 @@ async function renderSeller(app) {
21736
21736
  )}
21737
21737
  ${featureCard(
21738
21738
  '🎯',
21739
- t('买家 Agent 意图对碰'),
21739
+ t('买家 Agent 意图匹配'),
21740
21740
  t('零推广费'),
21741
21741
  t('全网买家 Agent 携精准需求(Alias)全天候巡航,与商品语义刚需匹配,订单全自动闭合成交。')
21742
21742
  )}
21743
21743
  ${featureCard(
21744
21744
  '🌊',
21745
- t('智能体裂变分润'),
21745
+ t('智能体扩散分润'),
21746
21746
  t('秒级自动分账'),
21747
21747
  t('商品由其他节点或达人 Agent 智能分享成交,下游分润通过智能合约去中心化自动清算,即刻到账。')
21748
21748
  )}
@@ -22122,8 +22122,9 @@ async function renderSeller(app) {
22122
22122
 
22123
22123
  const skillsSection = sellerSubTab === 'skills' ? `
22124
22124
  <div style="font-size:12px;color:#6b7280;margin-bottom:10px">${t('Skill 自动化 · 让 Agent 替你接单/报价/发货')}</div>
22125
- <div id="skill-mgmt-content">${loading$()}</div>
22126
22125
  ` : ''
22126
+ // 注:Skill 真实内容由下方 #my-skills-list 同步渲染(mySkills 已同步就绪);
22127
+ // 此前这里有个 #skill-mgmt-content loading$() 占位但全文件无人填充 → spinner 永转,已删除。
22127
22128
 
22128
22129
  const productsSection = sellerSubTab === 'products' ? `
22129
22130
  <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
@@ -24733,7 +24734,7 @@ function renderTrustCard(trust) {
24733
24734
  const neg = []
24734
24735
  if (s.dispute_penalty < 0) neg.push(`${t('仲裁败诉')} ${s.dispute_penalty}`)
24735
24736
  if (s.sybil_penalty < 0) neg.push(`${t('同 IP 多号')} ${s.sybil_penalty}`)
24736
- if (s.cross_penalty < 0) neg.push(`${t('双轨同支')} ${s.cross_penalty}`)
24737
+ if (s.cross_penalty < 0) neg.push(`${t('同支')} ${s.cross_penalty}`)
24737
24738
  if (s.ratelimit_penalty < 0) neg.push(`${t('限速命中')} ${s.ratelimit_penalty}`)
24738
24739
 
24739
24740
  const nextHint = trust.trust_score < trust.raw_mode_min_trust
@@ -24912,7 +24913,7 @@ async function renderWallet(app) {
24912
24913
  </div>
24913
24914
  </div>
24914
24915
 
24915
- <!-- 卖家收入分类速览(仅 seller 显示)- 2026-05-24 对碰列只在 PV 允许地区显示 -->
24916
+ <!-- 卖家收入分类速览(仅 seller 显示)- 2026-05-24 匹配列只在 PV 允许地区显示 -->
24916
24917
  ${state.user?.role === 'seller' ? (() => {
24917
24918
  const _pvOK = Number(state.user?.region_pv_enabled ?? 0) === 1 // 2026-06-04 解耦:PV 列读 pv_enabled,不再绑 max≥3
24918
24919
  const cols = _pvOK ? 4 : 3
@@ -24931,7 +24932,7 @@ async function renderWallet(app) {
24931
24932
  <div style="font-size:9px;color:#9ca3af">${((inc.commissions?.l1?.count||0)+(inc.commissions?.l2?.count||0)+(inc.commissions?.l3?.count||0))} ${t('单')}</div>
24932
24933
  </div>
24933
24934
  ${_pvOK ? `<div style="background:#fefce8;border-radius:6px;padding:8px">
24934
- <div style="font-size:9px;color:#854d0e">⚛ ${t('对碰已结')}</div>
24935
+ <div style="font-size:9px;color:#854d0e">⚛ ${t('匹配已结')}</div>
24935
24936
  <div style="font-size:14px;font-weight:700;color:#a16207;margin-top:2px">${(inc.binary?.settled_waz||0).toFixed(2)}</div>
24936
24937
  <div style="font-size:9px;color:#9ca3af">${inc.binary?.settled_count||0} ${t('次')}</div>
24937
24938
  </div>` : ''}
@@ -24967,7 +24968,7 @@ async function renderWallet(app) {
24967
24968
  })()}
24968
24969
  ${getMaxLevels() >= 3 ? `
24969
24970
  <div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-bottom:10px">
24970
- <div style="background:#fefce8;border-radius:6px;padding:10px"><div style="font-size:10px;color:#854d0e">⚛ ${t('双轨已结算')}</div><div style="font-size:15px;font-weight:700;color:#a16207">${inc.binary.settled_waz.toFixed(2)} WAZ</div><div style="font-size:9px;color:#9ca3af">${inc.binary.settled_count} ${t('次')}</div></div>
24971
+ <div style="background:#fefce8;border-radius:6px;padding:10px"><div style="font-size:10px;color:#854d0e">⚛ ${t('积分已结算')}</div><div style="font-size:15px;font-weight:700;color:#a16207">${inc.binary.settled_waz.toFixed(2)} WAZ</div><div style="font-size:9px;color:#9ca3af">${inc.binary.settled_count} ${t('次')}</div></div>
24971
24972
  <div style="background:#fef3c7;border-radius:6px;padding:10px"><div style="font-size:10px;color:#92400e">⏳ ${t('待结 Score')}</div><div style="font-size:15px;font-weight:700;color:#d97706">${inc.binary.pending_score.toFixed(1)}</div><div style="font-size:9px;color:#9ca3af">${t('每月结算')}</div></div>
24972
24973
  </div>` : ''}
24973
24974
  ${inc.sales.count > 0 ? `
@@ -25200,7 +25201,7 @@ window.doWithdraw = async () => {
25200
25201
  msgEl.innerHTML = ''
25201
25202
  return
25202
25203
  }
25203
- msgEl.innerHTML = alert$('success', `✅ ${res.message}`)
25204
+ msgEl.innerHTML = alert$('success', `✅ ${escHtml(res.message)}`)
25204
25205
  setTimeout(() => renderWallet(document.getElementById('app')), 2000)
25205
25206
  }
25206
25207
 
@@ -26636,7 +26637,7 @@ async function renderSkillPublish(app) {
26636
26637
  <textarea class="form-control" id="skm-preview" rows="3" maxlength="500" placeholder="${t('展示部分内容吸引购买,可留空')}" style="font-size:13px;resize:vertical;margin-bottom:12px"></textarea>
26637
26638
 
26638
26639
  <div style="font-size:12px;color:#374151;font-weight:600;margin-bottom:6px">${t('技能正文(购买后解锁)')}</div>
26639
- <textarea class="form-control" id="skm-content" rows="8" maxlength="20000" placeholder="${t('完整的模板 / 提示词 / 指南 / 清单内容…')}" style="font-size:13px;resize:vertical"></textarea>
26640
+ <textarea class="form-control" id="skm-body" rows="8" maxlength="20000" placeholder="${t('完整的模板 / 提示词 / 指南 / 清单内容…')}" style="font-size:13px;resize:vertical"></textarea>
26640
26641
  </div></div>
26641
26642
  <div id="skm-publish-msg" style="margin-bottom:8px"></div>
26642
26643
  <button class="btn btn-primary" style="width:100%;font-size:14px;padding:10px" onclick="doSkmPublish()">${t('提交审核')}</button>
@@ -26655,7 +26656,7 @@ window.doSkmPublish = async () => {
26655
26656
  const price = parseFloat(document.getElementById('skm-price')?.value) || 0
26656
26657
  const summary = document.getElementById('skm-summary')?.value?.trim()
26657
26658
  const preview = document.getElementById('skm-preview')?.value?.trim()
26658
- const content = document.getElementById('skm-content')?.value
26659
+ const content = document.getElementById('skm-body')?.value
26659
26660
  const msg = document.getElementById('skm-publish-msg')
26660
26661
  if (!title || title.length < 2) { msg.innerHTML = alert$('error', t('请填写标题')); return }
26661
26662
  if (!content || !content.trim()) { msg.innerHTML = alert$('error', t('请填写技能正文内容')); return }
@@ -27554,7 +27555,7 @@ async function renderListingFollow(app, listingId) {
27554
27555
  if (!state.user) { location.hash = '#login'; return }
27555
27556
  if (state.user.role !== 'seller') { app.innerHTML = `<div style="padding:24px">${alert$('warn', t('仅卖家可跟卖'))}</div>`; return }
27556
27557
  const r = await GET('/listings/' + encodeURIComponent(listingId))
27557
- if (r?.error) { app.innerHTML = `<div style="padding:24px">${alert$('error', r.error)}</div>`; return }
27558
+ if (r?.error) { app.innerHTML = `<div style="padding:24px">${alert$('error', escHtml(r.error))}</div>`; return }
27558
27559
  const l = r.listing
27559
27560
 
27560
27561
  app.innerHTML = `
@@ -33287,7 +33288,7 @@ window.submitNote = async (orderId, parentId, draftKey) => {
33287
33288
  body: buf,
33288
33289
  })
33289
33290
  const j = await upResp.json()
33290
- if (!upResp.ok || j.error) { msg.innerHTML = alert$('error', t('图片上传失败') + `:${j.error}`); btn.disabled = false; return }
33291
+ if (!upResp.ok || j.error) { msg.innerHTML = alert$('error', t('图片上传失败') + `:${escHtml(j.error)}`); btn.disabled = false; return }
33291
33292
  hashes.push(hash)
33292
33293
  }
33293
33294
  // 步骤 2: 创建笔记(带 parent_id 时为转发)