fix(frontend): route every user-center points label through the custom currency name

后台「积分配置 → 自定义货币名称」改名后,用户中心仍有大量硬编码
「积分」:做任务赚积分 / 购买积分 / 签到领积分、积分记录 / 积分任务、
每日可免费获取 XX 积分、我的积分、积分+1、创作中心积分收益等。

get_currency_name() 早已存在且可用,问题只是这些文案没有走它。
本PR 全部替换,无双路、无迁移:

服务端渲染(用户中心 renderers)
- credits-balance.php:购买/做任务赚/签到领 + 整个购买弹窗
- profile-credits.php:积分记录 / 积分任务 页签
- credits-points-tasks.php:每日上限三态与空态
- sidebar-creator.php / profile-creator.php:积分收益
- transfer-modal.php:积分转账提示
- Signin.php:签到详情弹窗(我的积分 / 奖励 tooltip / 积分+N)
- VipServiceModal.php:我的积分 / 积分兑换会员
- download-permission-card.php:购买价格

客户端渲染(JS 无法调用 PHP,注入 currencyName 到 aksrtData)
- signin.js:签到状态行「已签到 N 天 +1」
- credits.js:转账标题/金额标签 + 购买提示 toast
- cms-download.js:积分不足视图、我的积分 入口、余额不足文案
- pay-vip.js:积分兑换会员入口(优先 catalog.currency_name)
- order-center.js:订单行单位回退

契约测试同步改为断言模板构造而非死字符串,并新增两例:
改名后跟随、未注入时回退「积分」。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-11-01 15:09:49 +08:00
1 parent 1c95f94bca
commit 5c6b422866
19 files changed
+170 -59

No files matched your search

+18 -6
View File
@@ -20,6 +20,12 @@
const REST_BASE = window.aksrtData.siteUrl ? window.aksrtData.siteUrl.replace(/\/$/, '') + '/wp-json/aksrt/v1' : '/wp-json/aksrt/v1';
const PAY_CONTENT_PATH = '/pay/content/';
// 自定义货币名称(后台「积分配置 → 自定义货币名称」)由服务端注入。
// 余额不足提示在客户端拼装,必须跟随它。
function currencyName() {
return (window.aksrtData && window.aksrtData.currencyName) || '积分';
}
function escapeHtml(value) {
return String(value == null ? '' : value)
.replace(/&/g, '&amp;')
@@ -192,6 +198,8 @@
let viewKey = 'error';
let subtitle = '';
let action = null;
// credits 视图标题跟随自定义货币名称;其余视图沿用静态文案。
let unit = '';
data = data || {};
msg = msg || '';
@@ -215,8 +223,9 @@
viewKey = 'credits';
const balance = typeof data.balance === 'number' ? data.balance : 0;
const cost = typeof data.cost === 'number' ? data.cost : 0;
msg = '当前余额 ' + balance + ' 积分,需要 ' + cost + ' 积分。可通过签到、阅读、邀请好友等方式获取积分。';
if (data.credits_url) action = { kind: 'link', url: data.credits_url, label: '我的积分', icon: 'fa-coins' };
unit = currencyName();
msg = '当前余额 ' + balance + ' ' + unit + ',需要 ' + cost + ' ' + unit + '。可通过签到、阅读、邀请好友等方式获取' + unit + '。';
if (data.credits_url) action = { kind: 'link', url: data.credits_url, label: '我的' + unit, icon: 'fa-coins' };
} else if (code === 'aksrt_download_limit') {
viewKey = 'quota';
msg = msg || '您今日下载免费资源个数已超限,请明日再下载。';
@@ -232,6 +241,7 @@
open(
dlView(viewKey, {
title: viewKey === 'credits' && unit ? unit + '不足' : undefined,
subtitle: subtitle,
body: msg ? '<p class="aksrt-dl-modal__msg">' + escapeHtml(msg) + '</p>' : '',
actions: action ? [action] : [],
@@ -368,7 +378,8 @@
}
function formatAssetLabel(preview, value) {
const currency = escapeHtml(preview.currency || (preview.pay_modo === 'points' ? '积分' : '¥'));
// preview.currency 由服务端解析(积分价走自定义货币名称),缺省时回退同一来源。
const currency = escapeHtml(preview.currency || (preview.pay_modo === 'points' ? currencyName() : '¥'));
const amount = escapeHtml(value == null ? '' : value);
return preview.pay_modo === 'points' ? amount + currency : currency + amount;
}
@@ -483,15 +494,16 @@
body += '<div class="mb10 muted-box padding-h6 line-16">' + priceRows + '</div>';
if (isPoints) {
const unit = currencyName();
body += '<div class="mb10 muted-box padding-h10">' +
'<div class="flex jsb ac"><span class="muted-2-color">' +
'<i class="fa fas fa-database aksrt-cashier__asset-icon" aria-hidden="true"></i>我的积分' +
'<i class="fa fas fa-database aksrt-cashier__asset-icon" aria-hidden="true"></i>我的' + unit +
'</span><div class="c-green"><span class="em14">' + balance + '</span></div></div></div>';
if (!preview.balance_enough) {
body += '<div class="badg c-red btn-block mb20 aksrt-cashier__warn">抱歉,您的积分不足,暂时无法支付</div>';
body += '<div class="badg c-red btn-block mb20 aksrt-cashier__warn">抱歉,您的' + unit + '不足,暂时无法支付</div>';
body += '<div class="modal-buts but-average">';
if (preview.credits_url) {
body += '<a class="but padding-lg" href="' + escapeAttr(preview.credits_url) + '">我的积分</a>';
body += '<a class="but padding-lg" href="' + escapeAttr(preview.credits_url) + '">我的' + unit + '</a>';
}
body += '</div>';
openCashierContent(body, returnFocusEl);
+10 -4
View File
@@ -16,6 +16,12 @@
}
var activePoll = null;
// 自定义货币名称(后台「积分配置 → 自定义货币名称」)由服务端注入。
// 转账标题/金额标签与购买提示都在客户端拼装,必须跟随它。
function currencyName() {
return (aksrt && aksrt.currencyName) || '积分';
}
function operationKey(owner) {
if (!owner.dataset.aksrtOperationKey) {
owner.dataset.aksrtOperationKey = window.AksrtRequest.createIdempotencyKey();
@@ -495,7 +501,7 @@
? Math.max(1, parseInt(btn.getAttribute('data-aksrt-transfer-min') || '1', 10) || 1)
: 1;
if (typeInput) typeInput.value = type;
if (title) title.textContent = type === 'points' ? '积分转账' : '余额转账';
if (title) title.textContent = type === 'points' ? currencyName() + '转账' : '余额转账';
var amountEl = form ? form.querySelector('[data-aksrt-transfer-amount]') : null;
if (amountEl) {
amountEl.min = type === 'points' ? String(pointsTransferMin) : '0.01';
@@ -503,7 +509,7 @@
amountEl.inputMode = type === 'points' ? 'numeric' : 'decimal';
amountEl.value = '';
}
if (amountLabel) amountLabel.textContent = type === 'points' ? '积分' : '金额';
if (amountLabel) amountLabel.textContent = type === 'points' ? currencyName() : '金额';
if (balanceInfo) balanceInfo.hidden = type === 'points';
if (pointsInfo) pointsInfo.hidden = type !== 'points';
if (results) results.innerHTML = '';
@@ -744,11 +750,11 @@
selectProduct('custom');
}
if ((!pkg || pkg === 'custom') && !validCustom) {
notyf('请选择或输入需购买的积分', 'danger');
notyf('请选择或输入需购买的' + currencyName(), 'danger');
return;
}
busy = true;
notyf('正在购买积分…', 'load', '', 'aksrt-balance-points-toast');
notyf('正在购买' + currencyName() + '…', 'load', '', 'aksrt-balance-points-toast');
api('/pay/balance/points-exchange', {
method: 'POST',
headers: { 'Idempotency-Key': operationKey(form) },
+9 -1
View File
@@ -35,13 +35,21 @@
function formatMoney(order) {
var raw = String(order && order.amount != null ? order.amount : "0");
if (order && order.is_points) {
return escapeHtml(order.currency_symbol || "积分") + " " + (/^\d+$/.test(raw) ? raw : "0");
// 单位名由服务端随订单下发(UserOrders::currency_symbol),
// 缺省时回退到自定义货币名称,避免改名后残留「积分」。
var unit = order.currency_symbol || currencyName();
return escapeHtml(unit) + " " + (/^\d+$/.test(raw) ? raw : "0");
}
var match = /^(0|[1-9]\d*)(?:\.(\d{1,2}))?$/.exec(raw);
var amount = match ? match[1] + "." + (match[2] || "").padEnd(2, "0") : "0.00";
return "¥" + amount;
}
// 自定义货币名称(后台「积分配置 → 自定义货币名称」)由服务端注入。
function currencyName() {
return (window.aksrtData && window.aksrtData.currencyName) || "积分";
}
function orderTag(orderNumber) {
return "order:" + String(orderNumber || "").toLowerCase();
}
+4 -1
View File
@@ -493,8 +493,11 @@
var pointsLink = node("a", "btn-block but mb10 padding-lg c-green mini-radius");
pointsLink.href = "javascript:;";
pointsLink.setAttribute("data-aksrt-vip-points-open", "");
// 入口文案跟随自定义货币名称(后台「积分配置 → 自定义货币名称」),
// 与它打开的服务端静态弹窗标题保持同一命名。
var currencyName = (catalog.currency_name || (window.aksrtData && window.aksrtData.currencyName) || "积分");
pointsLink.innerHTML = '<svg class="icon" aria-hidden="true"><use xlink:href="#icon-points-color"></use></svg>'
+ "积分兑换会员"
+ currencyName + "兑换会员"
+ '<i class="fa fas fa-angle-right em12 ml10" aria-hidden="true"></i>';
append(products, pointsLink);
}
+9 -3
View File
@@ -13,6 +13,12 @@
var signinBusy = false;
var refreshSequence = 0;
// 自定义货币名称(后台「积分配置 → 自定义货币名称」)由服务端注入,
// 状态行文案必须跟随它,否则改名后这里仍显示「积分」。
function currencyName() {
return (window.aksrtData && window.aksrtData.currencyName) || '积分';
}
function api(path, options) {
return window.AksrtRequest.request(path, options || {}).then(function (response) {
return {
@@ -111,11 +117,11 @@
return;
}
if (data.signed) {
setStatusHtml('已签到 <strong>' + data.streak + '</strong> 天 +' + data.reward_today);
setStatusHtml('已签到 <strong>' + data.streak + '</strong> 天 +' + data.reward_today + ' ' + currencyName());
swapButtonsToDetailsLink();
return;
}
setStatusHtml('连续 <strong>' + data.streak + '</strong> 天,+' + data.reward_if_signin);
setStatusHtml('连续 <strong>' + data.streak + '</strong> 天,+' + data.reward_if_signin + ' ' + currencyName());
});
}
@@ -168,7 +174,7 @@
element.textContent = Number(result.json.balance || 0).toLocaleString();
element.title = element.textContent;
});
setStatusHtml('已签到 <strong>' + result.json.streak + '</strong> 天 +' + result.json.reward);
setStatusHtml('已签到 <strong>' + result.json.streak + '</strong> 天 +' + result.json.reward + ' ' + currencyName());
swapButtonsToDetailsLink();
}).catch(function (error) {
restoreButton();
@@ -24,6 +24,10 @@ $points_exchange = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Se
$recharge = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_recharge_settings() : [];
$points_exchange_on = !empty($points_exchange['enabled']) && !empty($recharge['enabled']);
$currency = (string) ($currency_name ?? __('积分', 'aksrt'));
// 文案里的货币名统一走自定义名称(后台「积分配置 → 自定义货币名称」)。
$points_buy_label = sprintf(__('购买%s', 'aksrt'), $currency);
$points_earn_label = sprintf(__('做任务赚%s', 'aksrt'), $currency);
$points_signin_label = sprintf(__('签到领%s', 'aksrt'), $currency);
?>
<div class="uc-asset-card uc-asset-card--points">
<div class="uc-asset-card__head flex jsb ac">
@@ -36,7 +40,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<?php echo aksrt_get_user_checkin_details_link('uc-asset-card__signin muted-color px12', '<span>' . esc_html__('已签到', 'aksrt') . '</span><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i>'); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php else : ?>
<button type="button" class="uc-asset-card__signin muted-color px12" data-signin-btn data-signin-done-label="<?php esc_attr_e('已签到', 'aksrt'); ?>">
<span data-signin-label><?php esc_html_e('签到领积分', 'aksrt'); ?></span><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i>
<span data-signin-label><?php echo esc_html($points_signin_label); ?></span><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i>
</button>
<?php endif; ?>
<span class="uc-asset-card__signin-status" data-signin-status></span>
@@ -52,11 +56,11 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<button type="button" class="but c-green mr6 hollow" data-aksrt-transfer-open="points" data-aksrt-transfer-min="<?php echo esc_attr((string) $points_transfer_min); ?>"><?php esc_html_e('转账', 'aksrt'); ?></button>
<?php endif; ?>
<?php if ($points_exchange_on) : ?>
<button type="button" class="but c-green hollow" data-aksrt-points-pay-open><?php esc_html_e('购买积分', 'aksrt'); ?></button>
<button type="button" class="but c-green hollow" data-aksrt-points-pay-open><?php echo esc_html($points_buy_label); ?></button>
<?php endif; ?>
</div>
</div>
<button type="button" class="uc-asset-card__link muted-2-color em09" data-aksrt-asset-tab="points_free"><?php esc_html_e('做任务赚积分', 'aksrt'); ?><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i></button>
<button type="button" class="uc-asset-card__link muted-2-color em09" data-aksrt-asset-tab="points_free"><?php echo esc_html($points_earn_label); ?><i class="fa fas fa-angle-right em12 ml3" aria-hidden="true"></i></button>
</div>
<?php if ($points_exchange_on) :
@@ -64,6 +68,15 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
$pb_rate = max(1, (int) ($points_exchange['rate'] ?? 100));
$pb_min_points = max(1, (int) ($points_exchange['min_points'] ?? 100));
$pb_daily_max_points = max(1, (int) ($points_exchange['daily_max_points'] ?? 100000));
// 依赖 $pb_min_points / $pb_daily_max_points,必须排在它们赋值之后。
$points_limit_html = sprintf(
/* translators: 1: currency name, 2: minimum purchase, 3: currency name, 4: maximum purchase */
__('最低购买%2$s%1$s,最高购买%4$s%3$s', 'aksrt'),
$currency,
$pb_min_points,
$currency,
$pb_daily_max_points
);
$points_packages = class_exists('\AKSRT\Credits\BalanceService')
? \AKSRT\Credits\BalanceService::get_instance()->parse_points_packages((string) ($points_exchange['products_raw'] ?? ''))
: [];
@@ -81,7 +94,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
if (function_exists('aksrt_sort_payment_methods')) {
$points_methods = aksrt_sort_payment_methods($points_methods);
}
$points_custom_limit_html = sprintf(__('最低购买%d积分,最高购买%d积分', 'aksrt'), $pb_min_points, $pb_daily_max_points);
$points_custom_limit_html = $points_limit_html;
$points_gateway = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_payment_gateway_settings() : [];
$points_mark = (string) ($points_gateway['currency']['mark'] ?? '¥');
$points_default_product = $points_packages ? (string) $points_packages[0]['key'] : 'custom';
@@ -90,11 +103,11 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<div class="modal-dialog modal-mini full-sm" role="document">
<div class="modal-content">
<div class="modal-body">
<div class="mb10 touch"><button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><svg class="ic-close" aria-hidden="true"><use xlink:href="#icon-close"></use></svg></button><b class="modal-title flex ac"><span class="mr6 em14"><i class="fa fas fa-coins" aria-hidden="true"></i></span><?php esc_html_e('购买积分', 'aksrt'); ?></b></div>
<div class="mb10 touch"><button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e('关闭', 'aksrt'); ?>"><svg class="ic-close" aria-hidden="true"><use xlink:href="#icon-close"></use></svg></button><b class="modal-title flex ac"><span class="mr6 em14"><i class="fa fas fa-coins" aria-hidden="true"></i></span><?php echo esc_html($points_buy_label); ?></b></div>
<form class="balance-charge-form mini-scrollbar scroll-y max-vh7" data-aksrt-points-pay-form>
<div class="charge-box mb20">
<?php if ($points_packages) : ?>
<div class="muted-color mb6"><?php esc_html_e('请选择需购买的积分', 'aksrt'); ?></div>
<div class="muted-color mb6"><?php printf(esc_html__('请选择需购买的%s', 'aksrt'), esc_html($currency)); ?></div>
<?php foreach ($points_packages as $pidx => $ppkg) : ?>
<div class="zib-widget vip-product relative product-box<?php echo $pidx === 0 ? ' active' : ''; ?>" data-for="points_product" data-value="<?php echo esc_attr((string) $ppkg['key']); ?>">
<?php if (!empty($ppkg['tag'])) : ?>
@@ -104,7 +117,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<div class="c-red"><span class="px12"><?php echo esc_html($points_mark); ?></span><span class="em12"><?php echo esc_html((string) $ppkg['pay_price']); ?></span></div>
</div>
<?php endforeach; ?>
<div class="muted-color mt20 mb6"><?php printf(esc_html__('自定义积分数量(1%1$s=%2$s积分)', 'aksrt'), esc_html($points_mark), esc_html((string) $pb_rate)); ?></div>
<div class="muted-color mt20 mb6"><?php printf(esc_html__('自定义%1$s数量(1%2$s=%3$s%1$s)', 'aksrt'), esc_html($currency), esc_html($points_mark), esc_html((string) $pb_rate)); ?></div>
<div data-for="points_product" data-value="custom">
<div class="relative flex ab">
<span class="ml6 mr10 muted-color shrink0"><i class="fa fas fa-coins" aria-hidden="true"></i></span>
@@ -114,7 +127,7 @@ $currency = (string) ($currency_name ?? __('积分', 'aksrt'));
<div class="muted-2-color em09 mt6"><?php echo esc_html($points_custom_limit_html); ?></div>
</div>
<?php else : ?>
<div class="muted-color mb6"><?php esc_html_e('请输入需购买的积分数额', 'aksrt'); ?></div>
<div class="muted-color mb6"><?php printf(esc_html__('请输入需购买的%s数额', 'aksrt'), esc_html($currency)); ?></div>
<div data-for="points_product" data-value="custom">
<div class="relative flex ab">
<span class="ml6 mr10 muted-color shrink0"><i class="fa fas fa-coins" aria-hidden="true"></i></span>
@@ -88,12 +88,14 @@ $day_max = (int) \AKSRT\Credits\PointsFree::settings()['day_max'];
$tasks = \AKSRT\Credits\PointsFree::task_list_for_user($user_id);
$detail = \AKSRT\Credits\PointsFree::date_detail($user_id);
$pane = isset($points_tasks_pane) ? sanitize_key((string) $points_tasks_pane) : 'tasks';
// 文案里的货币名统一走自定义名称(后台「积分配置 → 自定义货币名称」)。
$currency = (string) ($currency_name ?? __('积分', 'aksrt'));
?>
<?php if ($pane === 'date') : ?>
<div class="uc-points-date">
<div class="flex jsb ac mb10">
<div class="font-bold"><?php esc_html_e('每日免费积分明细', 'aksrt'); ?></div>
<div class="font-bold"><?php printf(esc_html__('每日免费%s明细', 'aksrt'), esc_html($currency)); ?></div>
<button type="button" class="muted-2-color" data-aksrt-asset-tab="points_free">
<?php esc_html_e('返回任务', 'aksrt'); ?><i class="fa fas fa-angle-right ml6 em12" aria-hidden="true"></i>
</button>
@@ -106,7 +108,7 @@ $pane = isset($points_tasks_pane) ? sanitize_key((string) $points_tasks_pane)
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="uc-transactions__empty"><p><?php esc_html_e('暂无免费积分获取明细', 'aksrt'); ?></p></div>
<div class="uc-transactions__empty"><p><?php printf(esc_html__('暂无免费%s获取明细', 'aksrt'), esc_html($currency)); ?></p></div>
<?php endif; ?>
</div>
<?php else : ?>
@@ -128,12 +130,12 @@ $pane = isset($points_tasks_pane) ? sanitize_key((string) $points_tasks_pane)
<?php
if ($day_max > 0) {
if ($today >= $day_max) {
printf(esc_html__('今日获取的免费积分已达到 %s,将不再获取', 'aksrt'), esc_html((string) $day_max));
printf(esc_html__('今日获取的免费%s已达到 %s,将不再获取', 'aksrt'), esc_html($currency), esc_html((string) $day_max));
} else {
printf(esc_html__('每日可免费获取 %s 积分,超过后将不再获取', 'aksrt'), esc_html((string) $day_max));
printf(esc_html__('每日可免费获取 %1$s %2$s,超过后将不再获取', 'aksrt'), esc_html((string) $day_max), esc_html($currency));
}
} else {
esc_html_e('免费积分无每日上限', 'aksrt');
printf(esc_html__('免费%s无每日上限', 'aksrt'), esc_html($currency));
}
?>
</div>
@@ -193,7 +195,7 @@ $pane = isset($points_tasks_pane) ? sanitize_key((string) $points_tasks_pane)
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="uc-transactions__empty"><p><?php esc_html_e('暂无可用积分任务', 'aksrt'); ?></p></div>
<div class="uc-transactions__empty"><p><?php printf(esc_html__('暂无可用%s任务', 'aksrt'), esc_html($currency)); ?></p></div>
<?php endif; ?>
</div>
<?php endif; ?>
@@ -56,14 +56,19 @@ $status_labels = [
'pending' => __('待结算', 'aksrt'),
'settled' => __('已结算', 'aksrt'),
];
// 收益单位名跟随自定义货币名称(后台「积分配置 → 自定义货币名称」)。
$currency_name = class_exists('\\AKSRT\\Settings\\Settings')
? \AKSRT\Settings\Settings::get_currency_name()
: __('积分', 'aksrt');
$points_income_label = sprintf(__('%s收益', 'aksrt'), $currency_name);
$currency_labels = [
'CNY' => __('现金收益', 'aksrt'),
'points' => __('积分收益', 'aksrt'),
'points' => $points_income_label,
];
?>
<div class="creator-income" data-creator-income>
<section class="creator-income__cards" aria-label="<?php esc_attr_e('收益概览', 'aksrt'); ?>">
<?php foreach (['CNY' => __('现金收益', 'aksrt'), 'points' => __('积分收益', 'aksrt')] as $unit => $label) : ?>
<?php foreach ($currency_labels as $unit => $label) : ?>
<article class="creator-income-card creator-income-card--<?php echo esc_attr(strtolower($unit)); ?>">
<h2><?php echo esc_html($label); ?></h2>
<div class="creator-income-card__metrics">
@@ -126,7 +131,7 @@ $currency_labels = [
<span class="creator-income-row__currency"><?php echo esc_html($currency_labels[$unit === 'points' ? 'points' : 'CNY']); ?></span>
<span class="creator-income-row__source" title="<?php echo esc_attr($source); ?>"><?php echo esc_html($source); ?></span>
<span class="creator-income-row__detail">
<strong class="creator-income-row__amount">+<?php echo esc_html($amount . ($unit === 'points' ? __('积分', 'aksrt') : __('现金', 'aksrt'))); ?></strong>
<strong class="creator-income-row__amount">+<?php echo esc_html($amount . ($unit === 'points' ? $currency_name : __('现金', 'aksrt'))); ?></strong>
<?php if ($time_label !== '') : ?>
<time class="creator-income-row__time" datetime="<?php echo esc_attr($time_datetime); ?>"><?php echo esc_html($time_label); ?></time>
<?php endif; ?>
@@ -15,6 +15,10 @@ extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_
$dir = __DIR__;
$show_wallet = !empty($balance_enabled);
$show_credits = !empty($credits_enabled);
// 记录/任务页签文案跟随自定义货币名称(后台「积分配置 → 自定义货币名称」)。
$currency_name = (string) ($currency_name ?? __('积分', 'aksrt'));
$points_record_label = sprintf(__('%s记录', 'aksrt'), $currency_name);
$points_tasks_label = sprintf(__('%s任务', 'aksrt'), $currency_name);
// Dual cards row (legacy row gutters-10).
if ($show_wallet || $show_credits) :
@@ -56,12 +60,12 @@ if ($show_wallet || $show_credits) :
<?php endif; ?>
<?php if ($show_credits) : ?>
<li class="<?php echo $active_record === 'points' ? 'active' : ''; ?>">
<a href="#record_tab_points" data-aksrt-asset-tab="points"><?php esc_html_e('积分记录', 'aksrt'); ?></a>
<a href="#record_tab_points" data-aksrt-asset-tab="points"><?php echo esc_html($points_record_label); ?></a>
</li>
<?php endif; ?>
<?php if (!empty($show_tasks)) : ?>
<li class="<?php echo in_array($active_record, ['points_free', 'points_date'], true) ? 'active' : ''; ?>">
<a href="#record_tab_points_free" data-aksrt-asset-tab="points_free"><?php esc_html_e('积分任务', 'aksrt'); ?></a>
<a href="#record_tab_points_free" data-aksrt-asset-tab="points_free"><?php echo esc_html($points_tasks_label); ?></a>
</li>
<?php endif; ?>
</ul>
@@ -28,6 +28,10 @@ if ($creation_url !== '') {
// 累计收益(pending + settled),现金与积分分开统计。
$cash_income = (float) ($income_stats['total']['CNY'] ?? 0);
$points_income = (int) ($income_stats['total']['points'] ?? 0);
// 收益单位名跟随自定义货币名称(后台「积分配置 → 自定义货币名称」)。
$currency_name = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_currency_name()
: __('积分', 'aksrt');
$centers[] = [
'url' => $creation_url,
@@ -37,7 +41,7 @@ if ($creation_url !== '') {
'first' => aksrt_cut_count($cash_income),
'first_l' => __('现金收益', 'aksrt'),
'second' => aksrt_cut_count($points_income),
'second_l' => __('积分收益', 'aksrt'),
'second_l' => sprintf(__('%s收益', 'aksrt'), $currency_name),
];
}
@@ -13,6 +13,8 @@ $transfer_recharge = class_exists('\AKSRT\Settings\Settings') && method_exists('
$transfer_all_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_all() : [];
$points_transfer_min = max(1, (int) ($transfer_all_settings['points_transfer_min'] ?? 1));
$points_transfer_fee = max(0, min(50, (int) ($transfer_all_settings['points_service_charge'] ?? 0)));
// 转账提示里的货币名跟随自定义名称(后台「积分配置 → 自定义货币名称」)。
$currency_name = (string) ($currency_name ?? __('积分', 'aksrt'));
?>
<div class="uc-modal" id="aksrt-transfer-modal" hidden data-aksrt-transfer-modal>
<div class="uc-modal__overlay" data-aksrt-transfer-close></div>
@@ -41,7 +43,7 @@ $points_transfer_fee = max(0, min(50, (int) ($transfer_all_settings['points_serv
<?php endif; ?>
</div>
<div class="muted-box em09 mb10" hidden data-aksrt-transfer-points-info>
<?php printf(esc_html__('积分转账仅支持正整数,单笔至少 %d 积分。', 'aksrt'), $points_transfer_min); ?>
<?php printf(esc_html__('%s转账仅支持正整数,单笔至少 %d %s。', 'aksrt'), esc_html($currency_name), $points_transfer_min, esc_html($currency_name)); ?>
<?php if ($points_transfer_fee > 0) : ?>
<?php printf(esc_html__(' 将扣除 %d%% 手续费。', 'aksrt'), $points_transfer_fee); ?>
<?php endif; ?>
+2 -2
View File
@@ -266,13 +266,13 @@ trait VipServiceModal {
. '<svg class="ic-close" aria-hidden="true"><use xlink:href="#icon-close"></use></svg></button>'
. '<b class="modal-title flex ac"><span class="mr6 em14">'
. '<svg class="icon" aria-hidden="true"><use xlink:href="#icon-vip_1"></use></svg></span>'
. esc_html__('积分兑换会员', 'aksrt') . '</b></div>';
. esc_html(sprintf(__('%s兑换会员', 'aksrt'), $currency)) . '</b></div>';
$user_box = '<div class="muted-box padding-h10 mb20">'
. '<a rel="nofollow" class="flex jsb ac" href="' . esc_url($points_center_url) . '">'
. '<span class="muted-2-color">'
. '<svg class="icon" aria-hidden="true"><use xlink:href="#icon-points"></use></svg> '
. esc_html__('我的积分', 'aksrt') . '</span>'
. esc_html(sprintf(__('我的%s', 'aksrt'), $currency)) . '</span>'
. '<span class="font-bold ml6 em14"><span class="c-green mr10">' . $user_points . '</span>'
. '<i class="fa fas fa-angle-right" aria-hidden="true"></i></span></a></div>';
+3
View File
@@ -462,6 +462,9 @@ trait ScriptsPart1c {
'captchaSignin' => function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('signin'),
// 已签到详情弹窗(签到成功后就地替换入口按钮用)。
'signinDetailsUrl' => esc_url_raw(rest_url('aksrt/v1/signin/details')),
// 自定义货币名称(后台「积分配置 → 自定义货币名称」),供前端
// 无法服务端渲染的文案(签到状态行、购买/转账提示)复用同一命名。
'currencyName' => class_exists('\\AKSRT\\Settings\\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt'),
'visibilityNonce' => wp_create_nonce('aksrt_visibility'),
'siteUrl' => home_url('/'),
'themeUrl' => AKSRT_URI,
+14 -8
View File
@@ -261,7 +261,7 @@ class Signin {
/**
* 签到详情弹窗 HTML(参考主题 zib_get_user_checkin_details_modal 1:1):
* 炫彩头部 + 用户卡片(我的积分/我的等级)+ 每日签到奖励卡 + 连续签到奖励 7 天迷你卡。
* 炫彩头部 + 用户卡片(我的{货币名}/我的等级)+ 每日签到奖励卡 + 连续签到奖励 7 天迷你卡。
*/
public function render_details_modal_html(int $user_id): string {
$user = get_userdata($user_id);
@@ -295,10 +295,11 @@ class Signin {
$html .= '<div class="mt10 em12 padding-w10">' . esc_html__('签到领奖励', 'aksrt') . '</div>';
$html .= '</div></div>';
// 用户卡片(头像/昵称 + 我的积分 + 我的等级)
// 用户卡片(头像/昵称 + 我的{货币名} + 我的等级)
$points_svg_mini = $points_svg('px12 mr6 muted-2-color');
$my_points_label = sprintf(__('我的%s', 'aksrt'), $currency);
$user_card = '<a rel="nofollow" class="block flex1 muted-box padding-10" href="' . esc_url(aksrt_get_user_center_url('credits')) . '">
<div class="muted-2-color em09 mb6">' . esc_html__('我的积分', 'aksrt') . '</div>
<div class="muted-2-color em09 mb6">' . esc_html($my_points_label) . '</div>
<div class="em12">' . $points_svg_mini . esc_html(number_format_i18n(aksrt_user_points($user_id))) . '<i class="muted-2-color px12 fa fas fa-angle-right ml6" aria-hidden="true"></i></div>
</a>';
@@ -322,12 +323,17 @@ class Signin {
}
$html .= '<div class="mb20"><div class="flex ac jsb"><div class="mb10">' . $avatar_img . esc_html($user->display_name) . $name_badge . '</div>' . $checkin_all_day . '</div><div class="flex ab jsb">' . $user_card . '</div></div>';
// 每日签到奖励卡片(参考主题:积分徽章 + 经验值徽章,经验值随等级系统展示)
$reward_badge = $base ? '<badge class=" p2-10 mr6 c-green" data-toggle="tooltip" title="' . esc_attr(sprintf(__('积分奖励%s', 'aksrt'), $base)) . '">' . $points_svg('icon') . '+' . esc_html((string) $base) . '</badge>' : '';
$reward_badge .= ($level_s && $base) ? '<badge class=" p2-10 mr6 c-purple" data-toggle="tooltip" title="' . esc_attr(sprintf(__('经验值奖励%s', 'aksrt'), $base)) . '"><i class="fa fas fa-chart-line" aria-hidden="true"></i>+' . esc_html((string) $base) . '</badge>' : '';
// 每日签到奖励卡片(参考主题:货币徽章 + 经验值徽章,经验值随等级系统展示)
// tooltip 先拼入数值再转义,避免把 %s 送进 esc_attr。
$reward_badge = $base
? '<badge class=" p2-10 mr6 c-green" data-toggle="tooltip" title="' . esc_attr(sprintf(__('%s奖励%s', 'aksrt'), $currency, $base)) . '">' . $points_svg('icon') . '+' . esc_html((string) $base) . '</badge>'
: '';
$reward_badge .= ($level_s && $base)
? '<badge class=" p2-10 mr6 c-purple" data-toggle="tooltip" title="' . esc_attr(sprintf(__('经验值奖励%s', 'aksrt'), $base)) . '"><i class="fa fas fa-chart-line" aria-hidden="true"></i>+' . esc_html((string) $base) . '</badge>'
: '';
if ($signed_today) {
$theday_reward = sprintf(__('积分', 'aksrt') . '+%d', (int) ($rec['reward'] ?? 0));
$theday_reward = sprintf(__('%s', 'aksrt') . '+%d', $currency, (int) ($rec['reward'] ?? 0));
if ($level_s) {
// 签到经验值与积分奖励同额发放(Level::on_checkin)。
$theday_reward .= ' ' . sprintf(__('经验值', 'aksrt') . '+%d', (int) ($rec['reward'] ?? 0));
@@ -364,7 +370,7 @@ class Signin {
}
$_points = $base + $extra;
$is_ed = $signed_today && $streak >= $d;
$_mini_card = $_points ? '<div class="badg badg-sm ' . ($is_ed ? 'b' : 'c') . '-green" data-toggle="tooltip" title="' . esc_attr(sprintf(__('积分奖励%s', 'aksrt'), $_points)) . '">' . $points_svg('icon') . '<br/>+' . esc_html((string) $_points) . '</div>' : '';
$_mini_card = $_points ? '<div class="badg badg-sm ' . ($is_ed ? 'b' : 'c') . '-green" data-toggle="tooltip" title="' . esc_attr(sprintf(__('%s奖励%s', 'aksrt'), $currency, $_points)) . '">' . $points_svg('icon') . '<br/>+' . esc_html((string) $_points) . '</div>' : '';
if ($level_s && $_points) {
// 经验值与积分奖励同额(Level::on_checkin),参考主题为紫色经验值迷你卡。
$_mini_card .= '<div class="badg badg-sm ' . ($is_ed ? 'b' : 'c') . '-purple" data-toggle="tooltip" title="' . esc_attr(sprintf(__('经验值奖励%s', 'aksrt'), $_points)) . '"><i class="fa fas fa-chart-line" aria-hidden="true"></i><br/>+' . esc_html((string) $_points) . '</div>';
@@ -90,9 +90,10 @@ $state_info = $state_icons[$status] ?? $state_icons['available'];
<?php
if ($pay_modo === 'points') {
printf(
/* translators: %d: points */
esc_html__('购买价格:%d 积分', 'aksrt'),
(int) ceil($amount)
/* translators: 1: points, 2: currency name */
esc_html__('购买价格:%1$d %2$s', 'aksrt'),
(int) ceil($amount),
esc_html((string) ($currency_name ?? __('积分', 'aksrt')))
);
} else {
printf(
@@ -71,7 +71,9 @@ test('购买积分弹窗:Zibll points-pay modal 结构 + 卡密渠道', () =>
assert.match(creditsRenderer, /data-aksrt-points-pay-form/);
assert.match(creditsRenderer, /data-aksrt-points-pay-open/);
assert.doesNotMatch(creditsRenderer, /data-aksrt-points-exchange-(open|modal|form|amount|preview|close)/);
assert.match(creditsRenderer, /请选择需购买的积分|请输入需购买的积分数额/);
// 商品/自定义提示语跟随自定义货币名称(后台「积分配置 → 自定义货币名称」)。
assert.match(creditsRenderer, /请选择需购买的%s', 'aksrt'\), esc_html\(\$currency\)/);
assert.match(creditsRenderer, /请输入需购买的%s数额', 'aksrt'\), esc_html\(\$currency\)/);
assert.match(creditsRenderer, /name="points_product"/);
assert.match(creditsRenderer, /name="points_custom"/);
assert.match(creditsRenderer, /name="payment_method"/);
+31 -4
View File
@@ -64,7 +64,7 @@ function deferred() {
return { promise, resolve, reject };
}
function setup({ buttons = [makeButton('btn uc-header__signin-btn', '签到')], captchaSignin = false, today = Promise.resolve({ ok: true, status: 200, json: { enabled: true, signed: false, streak: 1, reward_if_signin: 2 } }), signin = Promise.resolve({ ok: true, status: 200, json: { message: '签到成功,积分 +2', balance: 12 } }), captchaRun } = {}) {
function setup({ buttons = [makeButton('btn uc-header__signin-btn', '签到')], captchaSignin = false, currencyName = '', today = Promise.resolve({ ok: true, status: 200, json: { enabled: true, signed: false, streak: 1, reward_if_signin: 2 } }), signin = Promise.resolve({ ok: true, status: 200, json: { message: '签到成功,积分 +2', balance: 12 } }), captchaRun } = {}) {
const calls = [];
const notices = [];
const balance = { textContent: '10', title: '10' };
@@ -84,7 +84,7 @@ function setup({ buttons = [makeButton('btn uc-header__signin-btn', '签到')],
};
const jq = { off() { return this; }, on(event, callback) { listeners[event] = callback; return this; } };
const window = {
aksrtData: { captchaSignin, signinDetailsUrl: '/wp-json/aksrt/v1/signin/details' },
aksrtData: { captchaSignin, signinDetailsUrl: '/wp-json/aksrt/v1/signin/details', currencyName },
AksrtModalRuntime: { open: (...args) => modals.push(args), openRemote: (...args) => modals.push(args) },
AksrtRequest: { request(endpoint, options) { calls.push({ endpoint, options }); return endpoint === '/signin/today' ? today : signin; } },
};
@@ -99,6 +99,32 @@ function setup({ buttons = [makeButton('btn uc-header__signin-btn', '签到')],
const settle = () => new Promise((resolve) => setImmediate(resolve));
test('status line follows the custom currency name instead of hardcoding 积分', async () => {
// 后台「积分配置 → 自定义货币名称」改名后,签到状态行必须跟着变,
// 否则这里仍显示「积分」。
const button = makeButton('btn', '签到');
const success = deferred();
const h = setup({ buttons: [button], currencyName: '金币', signin: success.promise });
await settle();
button.click();
await settle();
success.resolve({ ok: true, status: 200, json: { message: '签到成功', balance: 12, streak: 3, reward: 2 } });
await settle();
assert.equal(h.status.innerHTML, '已签到 <strong>3</strong> 天 +2 金币');
});
test('status line falls back to 积分 when no custom currency name is injected', async () => {
const button = makeButton('btn', '签到');
const success = deferred();
const h = setup({ buttons: [button], signin: success.promise });
await settle();
button.click();
await settle();
success.resolve({ ok: true, status: 200, json: { message: '签到成功', balance: 12, streak: 3, reward: 2 } });
await settle();
assert.equal(h.status.innerHTML, '已签到 <strong>3</strong> 天 +2 积分');
});
test('uses reference loading, success toast, and replaces all entry points with detail links', async () => {
const first = makeButton('img-badge jb-yellow', '签到', '<i class="fa far fa-calendar-check ml3 mr6"></i>');
const second = makeButton('btn uc-header__signin-btn', '签到', '<i class="fa fa-calendar-check"></i>', '今日已签到');
@@ -136,7 +162,8 @@ test('uses reference loading, success toast, and replaces all entry points with
assert.equal(h.calls.filter((call) => call.endpoint === '/signin/today').length, 1, 'success does not issue another status request');
assert.equal(h.modals.length, 0, 'success does not open a modal');
assert.equal(h.balance.textContent, '12');
assert.equal(h.status.innerHTML, '已签到 <strong>2</strong> 天 +2');
// 状态行在奖励数值后追加自定义货币名称(未注入时回退「积分」)。
assert.equal(h.status.innerHTML, '已签到 <strong>2</strong> 天 +2 积分');
});
test('keeps the profile done style on its replacement link', async () => {
@@ -228,7 +255,7 @@ test('ignores an old today response after successful sign-in', async () => {
await settle();
assert.ok(button.replaced, 'success replaced the action with the details link');
assert.match(button.replaced.innerHTML, /已签到/);
assert.equal(h.status.innerHTML, '已签到 <strong>3</strong> 天 +2');
assert.equal(h.status.innerHTML, '已签到 <strong>3</strong> 天 +2 积分');
});
test('leaves a disabled sign-in feature disabled without turning it into a signed state', async () => {
@@ -78,7 +78,12 @@ test('其余侧栏部件按参考主题选型替换为 sprite 图标', () => {
assert.match(creator, /class="mb10-sm zib-widget padding-10 flex1 pointer"/);
assert.match(creator, /flex ab jsb col-ml6/);
assert.match(creator, /<i class="ml6 fa fas fa-angle-right em12"/);
for (const label of ['创作中心', '推广中心', '现金收益', '积分收益', '比例', '待提现', '累计佣金']) {
assert.match(creator, /'创作中心'/);
assert.match(creator, /'推广中心'/);
assert.match(creator, /'现金收益'/);
// 收益单位名跟随自定义货币名称,不再硬编码「积分收益」。
assert.match(creator, /sprintf\(__\('%s收益', 'aksrt'\), \$currency_name\)/);
for (const label of ['比例', '待提现', '累计佣金']) {
assert.ok(creator.includes(`'${label}'`), `creator card missing label: ${label}`);
}
// 创作中心不再统计商品数与今日收入。
@@ -46,16 +46,18 @@ test('弹窗为参考主题 refresh_modal 同构静态壳并带 data 钩子', ()
assert.match(modal, /data-aksrt-vip-points-form/);
});
test('弹窗内容保持参考主题结构:标题/我的积分/卡片选择/立即兑换', () => {
test('弹窗内容保持参考主题结构:标题/我的{货币名}/卡片选择/立即兑换', () => {
// 头部:.mb10.touch + modal-title.flex.ac + vip_1 图标。
assert.match(modal, /'<div class="mb10 touch">'/);
assert.match(modal, /class="modal-title flex ac"/);
assert.match(modal, /#icon-vip_1/);
// 我的积分盒(点回用户中心积分页)。
// 我的{货币名}盒(点回用户中心积分页)。货币名走自定义名称,不硬编码。
assert.match(modal, /muted-box padding-h10 mb20/);
assert.match(modal, /我的积分/);
assert.match(modal, /sprintf\(__\('我的%s', 'aksrt'\), \$currency\)/);
assert.match(modal, /#icon-points/);
assert.match(modal, /aksrt_get_user_center_tab_url\('credits'\)/);
// 标题同样跟随货币名。
assert.match(modal, /sprintf\(__\('%s兑换会员', 'aksrt'\), \$currency\)/);
// 卡片列表 + 参考主题 data-for/data-value 选中模型。
assert.match(modal, /请选择需兑换的会员/);
assert.match(modal, /flex hh exchange-card-box/);
@@ -81,7 +83,7 @@ test('弹窗壳随 wp_footer 全局输出且不进入 catalog 载荷', () => {
assert.doesNotMatch(modal, /'vip_url'/);
});
test('我的积分盒恢复参考主题 muted-box 的背景、圆角与内边距', () => {
test('我的{货币名}盒恢复参考主题 muted-box 的背景、圆角与内边距', () => {
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s+\.muted-box\.padding-h10\s*\{\s*background-color:\s*var\(--this-bg,\s*var\(--muted-border-color\)\);\s*border-radius:\s*var\(--main-radius\);\s*padding:\s*10px 15px;\s*\}/);
assert.match(vipCss, /\.modal\[data-aksrt-vip-points-modal\]\s+\.muted-box\.padding-10,\s*\.modal\[data-aksrt-vip-points-modal\]\s+\.aksrt-vip-points-desc\s*\{\s*background-color:\s*var\(--this-bg,\s*var\(--muted-border-color\)\);\s*border-radius:\s*var\(--main-radius\);\s*padding:\s*10px;\s*\}/);
});