diff --git a/assets/js/forum-governance.js b/assets/js/forum-governance.js index 61216627..581617b1 100644 --- a/assets/js/forum-governance.js +++ b/assets/js/forum-governance.js @@ -718,6 +718,110 @@ }); } + function formatFollowPayLabel(pay) { + pay = pay || {}; + if (pay.is_free) { + return 'VIP 免费关注'; + } + if (pay.label) { + return String(pay.label); + } + if (pay.modo === 'points') { + return String(pay.amount || 0) + ' 积分'; + } + return '¥' + String(pay.amount || '0'); + } + + function purchasePlateFollow(plateId, button, payHint) { + var confirmMsg = '确认付费关注该版块?'; + if (payHint && payHint.is_free) { + confirmMsg = '确认使用 VIP 免费关注该版块?'; + } else if (payHint) { + confirmMsg = '确认支付 ' + formatFollowPayLabel(payHint) + ' 关注该版块?'; + } + if (!window.confirm(confirmMsg)) { + button.disabled = false; + return; + } + + var data = new FormData(); + data.append('action', 'aksrt_forum_purchase_plate_follow'); + data.append('plate_id', plateId); + data.append('_nonce', rootData.forumNonce || ''); + + fetch((rootData.ajaxUrl || '/wp-admin/admin-ajax.php'), { + method: 'POST', + body: data, + credentials: 'same-origin', + }).then(function (r) { return r.json(); }).then(function (payload) { + if (!payload.success) { + throw new Error((payload.data && payload.data.message) || '支付失败'); + } + var res = payload.data || {}; + button.setAttribute('data-followed', '1'); + button.classList.add('is-followed'); + var label = button.querySelector('span') || button; + if (button.querySelector('i') && button.childNodes.length) { + // keep icon, replace trailing text via last text-ish content + button.innerHTML = ' 已关注'; + } else { + button.textContent = '已关注'; + } + if (typeof window.aksrtToast === 'function') { + window.aksrtToast(res.message || '关注成功', 'success'); + } + setTimeout(function () { window.location.reload(); }, 600); + }).catch(function (error) { + var msg = error.message || '支付失败'; + if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); } + else { window.alert(msg); } + button.disabled = false; + }); + } + + function bindFollowPlateButton(button) { + button.addEventListener('click', function () { + var plateId = parseInt(button.getAttribute('data-plate-id') || '0', 10); + if (!plateId || button.disabled) { + return; + } + button.disabled = true; + + var data = new FormData(); + data.append('action', 'aksrt_forum_follow_plate'); + data.append('plate_id', plateId); + data.append('_nonce', rootData.forumNonce || ''); + + fetch((rootData.ajaxUrl || '/wp-admin/admin-ajax.php'), { + method: 'POST', + body: data, + credentials: 'same-origin', + }).then(function (r) { return r.json(); }).then(function (payload) { + // Paid follow required + if (!payload.success && payload.data && payload.data.require_pay) { + purchasePlateFollow(plateId, button, payload.data.pay || {}); + return; + } + if (!payload.success) { + throw new Error((payload.data && payload.data.message) || '操作失败'); + } + var res = payload.data || {}; + var followed = !!res.followed; + button.setAttribute('data-followed', followed ? '1' : '0'); + button.classList.toggle('is-followed', followed); + button.innerHTML = followed + ? ' 已关注' + : ' 关注'; + button.disabled = false; + }).catch(function (error) { + var msg = error.message || '操作失败'; + if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); } + else { window.alert(msg); } + button.disabled = false; + }); + }); + } + function bindComposeCoverToggle(form) { var coverTypeSelect = form.querySelector('#compose-cover-type'); var imageField = form.querySelector('#compose-cover-image-field'); @@ -1455,6 +1559,7 @@ document.querySelectorAll('.js-aksrt-adopt-answer').forEach(bindAdoptButton); document.querySelectorAll('.js-aksrt-unlock-topic').forEach(bindUnlockButton); document.querySelectorAll('.js-aksrt-purchase-topic').forEach(bindPurchaseButton); + document.querySelectorAll('.js-aksrt-follow-plate').forEach(bindFollowPlateButton); bindForumCommentEngine(); bindForumHomeTabs(); bindBoardSortTabs(); diff --git a/docs/admin-ia-map.md b/docs/admin-ia-map.md index ec40ee42..7b074bbe 100644 --- a/docs/admin-ia-map.md +++ b/docs/admin-ia-map.md @@ -27,6 +27,7 @@ Do **not** reintroduce operational workbenches into the SPA once native pages sh 支付中心 (aksrt_pay) pos ~36–38, dashicons-cart ├─ 概览 aksrt_pay ├─ 订单 aksrt_orders +├─ 付费文章 aksrt_pay_products ├─ 发货及物流 aksrt_shipping ├─ 售后管理 aksrt_after_sale ├─ 优惠码 aksrt_coupons diff --git a/docs/zibll-gap-matrix.md b/docs/zibll-gap-matrix.md index 48496f4e..4a8822e6 100644 --- a/docs/zibll-gap-matrix.md +++ b/docs/zibll-gap-matrix.md @@ -99,8 +99,8 @@ Note: zibll「商品管理」`zibpay_product_page` is **paid article sales**, no | 2b Shipping/after-sale/coupons/income/rebate | done | | 3 users.php submenus | done | | 4 SPA strip | done (ops routes redirect to native) | -| 5+ behavior parity | done — native ops shell modularized (orders/shipping/after-sale/coupons/charge-cards/income/rebate/withdraw) + plate moderator roles/advanced limits/icon/sort + coupon product_scope create/edit/batch + paid-post sales report (`aksrt_pay_products`) + topic pay/points visibility admin/runtime alignment | -| 5 remaining | plate follow-pay (zibll paid follow) still optional; topic-level pay/points/hide-part done | +| 5+ behavior parity | done — native ops shell modularized (orders/shipping/after-sale/coupons/charge-cards/income/rebate/withdraw) + plate moderator roles/advanced limits/icon/sort + coupon product_scope create/edit/batch + paid-post sales report (`aksrt_pay_products`) + topic pay/points visibility admin/runtime alignment + plate follow-pay MVP + post list sales column + order type tabs + topic plate re-check | +| 5 remaining | download-log admin page; topic VIP1/2 full matrix; hidecontent shortcode partial-hide; users commerce columns | ## Dual-UI rule diff --git a/inc/classes/admin/native/class-aksrt-admin-pay-orders.php b/inc/classes/admin/native/class-aksrt-admin-pay-orders.php index 615fff0b..009ba899 100644 --- a/inc/classes/admin/native/class-aksrt-admin-pay-orders.php +++ b/inc/classes/admin/native/class-aksrt-admin-pay-orders.php @@ -77,6 +77,29 @@ class AKSRT_Admin_Pay_Orders { ? AKSRT_Domain_Model::order_status_labels() : []; + // Type tabs (zibll order subsubsub parity). + $type_tabs = [ + '' => __('全部类型', 'aksrt'), + 'resource' => __('内容付费', 'aksrt'), + 'vip' => __('VIP', 'aksrt'), + 'recharge' => __('充值', 'aksrt'), + 'shop_product' => __('商城商品', 'aksrt'), + 'plate_follow' => __('版块关注', 'aksrt'), + ]; + echo '