主题色

This commit is contained in:
zhx 2026-06-24 14:26:39 +08:00
parent f88ace4673
commit 0d0d2c42c6
6 changed files with 628 additions and 118 deletions

View File

@ -130,7 +130,9 @@ var default_api = 'https://api.global-interaction.com/';
if (!api) return ''; if (!api) return '';
if (env === 'local' && api.local) return normalizeBaseURL(api.local); if (env === 'local' && api.local) return normalizeBaseURL(api.local);
if (env === 'test' && api.test) return normalizeBaseURL(api.test); if (env === 'test' && api.test) return normalizeBaseURL(api.test);
return api.prod || api.default ? normalizeBaseURL(api.prod || api.default) : ''; return api.prod || api.default
? normalizeBaseURL(api.prod || api.default)
: '';
} }
function buildURL(path, query) { function buildURL(path, query) {
@ -191,17 +193,51 @@ var default_api = 'https://api.global-interaction.com/';
return storedToken; return storedToken;
} }
function themeAppCode(appCode) {
appCode = String(appCode || 'lalu')
.trim()
.toLowerCase();
return appCode === 'yumi' || appCode === 'aslan' ? appCode : 'lalu';
}
function applyAppTheme(appCode) {
if (!window.document || !window.document.documentElement) return;
window.document.documentElement.setAttribute(
'data-hy-app-code',
themeAppCode(appCode)
);
}
function peekAppCode() {
var queryAppCode =
readQuery('app') || readQuery('app_code') || readQuery('appCode');
if (queryAppCode) return queryAppCode;
if (memoryAppCode) return memoryAppCode;
if (!shouldPersist()) return 'lalu';
return window.localStorage.getItem(APP_CODE_KEY) || 'lalu';
}
function getAppCode() { function getAppCode() {
var queryAppCode = readQuery('app') || readQuery('app_code') || readQuery('appCode'); var queryAppCode =
readQuery('app') || readQuery('app_code') || readQuery('appCode');
if (queryAppCode) { if (queryAppCode) {
memoryAppCode = queryAppCode; memoryAppCode = queryAppCode;
if (shouldPersist()) if (shouldPersist())
window.localStorage.setItem(APP_CODE_KEY, queryAppCode); window.localStorage.setItem(APP_CODE_KEY, queryAppCode);
applyAppTheme(queryAppCode);
return queryAppCode; return queryAppCode;
} }
if (memoryAppCode) return memoryAppCode; if (memoryAppCode) {
if (!shouldPersist()) return 'lalu'; applyAppTheme(memoryAppCode);
return window.localStorage.getItem(APP_CODE_KEY) || 'lalu'; return memoryAppCode;
}
if (!shouldPersist()) {
applyAppTheme('lalu');
return 'lalu';
}
var storedAppCode = window.localStorage.getItem(APP_CODE_KEY) || 'lalu';
applyAppTheme(storedAppCode);
return storedAppCode;
} }
function setAccessToken(token) { function setAccessToken(token) {
@ -218,6 +254,7 @@ var default_api = 'https://api.global-interaction.com/';
function setAppCode(appCode) { function setAppCode(appCode) {
memoryAppCode = appCode || ''; memoryAppCode = appCode || '';
applyAppTheme(appCode || 'lalu');
if (appCode) { if (appCode) {
if (shouldPersist()) if (shouldPersist())
window.localStorage.setItem(APP_CODE_KEY, appCode); window.localStorage.setItem(APP_CODE_KEY, appCode);
@ -226,6 +263,8 @@ var default_api = 'https://api.global-interaction.com/';
if (shouldPersist()) window.localStorage.removeItem(APP_CODE_KEY); if (shouldPersist()) window.localStorage.removeItem(APP_CODE_KEY);
} }
applyAppTheme(peekAppCode());
function setAppConfig(config) { function setAppConfig(config) {
activeAppConfig = config || null; activeAppConfig = config || null;
if (activeAppConfig && activeAppConfig.app) { if (activeAppConfig && activeAppConfig.app) {
@ -239,17 +278,27 @@ var default_api = 'https://api.global-interaction.com/';
} }
function loadAppConfig(scope) { function loadAppConfig(scope) {
var appCode = String(getAppCode() || 'lalu').trim().toLowerCase(); var appCode = String(getAppCode() || 'lalu')
.trim()
.toLowerCase();
if (!appCode || appCode === 'lalu') { if (!appCode || appCode === 'lalu') {
activeAppConfig = null; activeAppConfig = null;
return Promise.resolve(null); return Promise.resolve(null);
} }
var scopePath = String(scope || '').replace(/^\/+|\/+$/g, ''); var scopePath = String(scope || '').replace(/^\/+|\/+$/g, '');
var configPath = var configPath =
(scopePath ? '/' + scopePath : '.') + '/config/' + appCode + '.json'; (scopePath ? '/' + scopePath : '.') +
var configURL = new URL(configPath, window.location.origin + window.location.pathname); '/config/' +
appCode +
'.json';
var configURL = new URL(
configPath,
window.location.origin + window.location.pathname
);
return window return window
.fetch(configURL.toString(), { cache: shouldPersist() ? 'default' : 'no-store' }) .fetch(configURL.toString(), {
cache: shouldPersist() ? 'default' : 'no-store',
})
.then(function (response) { .then(function (response) {
if (!response.ok) return null; if (!response.ok) return null;
return response.json(); return response.json();
@ -322,7 +371,7 @@ var default_api = 'https://api.global-interaction.com/';
: payload && : payload &&
Object.prototype.hasOwnProperty.call(payload, 'body') Object.prototype.hasOwnProperty.call(payload, 'body')
? payload.body ? payload.body
: payload; : payload;
}); });
} }
@ -494,7 +543,8 @@ var default_api = 'https://api.global-interaction.com/';
).toUpperCase(); ).toUpperCase();
var candidates = countries.filter(function (country) { var candidates = countries.filter(function (country) {
return ( return (
String(country.country_code || '').toUpperCase() === countryCode || String(country.country_code || '').toUpperCase() ===
countryCode ||
String(country.country_code || '').toUpperCase() === 'GLOBAL' String(country.country_code || '').toUpperCase() === 'GLOBAL'
); );
}); });
@ -616,9 +666,15 @@ var default_api = 'https://api.global-interaction.com/';
return { return {
id: item.id || '', id: item.id || '',
pay_country_id: pay_country_id:
item.payCountryId || item.pay_country_id || item.id || '', item.payCountryId ||
item.pay_country_id ||
item.id ||
'',
payCountryId: payCountryId:
item.payCountryId || item.pay_country_id || item.id || '', item.payCountryId ||
item.pay_country_id ||
item.id ||
'',
country_id: item.countryId || item.country_id || '', country_id: item.countryId || item.country_id || '',
currency_code: item.currency || item.currency_code || '', currency_code: item.currency || item.currency_code || '',
currencyCode: item.currency || item.currency_code || '', currencyCode: item.currency || item.currency_code || '',
@ -640,8 +696,12 @@ var default_api = 'https://api.global-interaction.com/';
var products = (options.commodity || options.products || []).map( var products = (options.commodity || options.products || []).map(
function (item) { function (item) {
var content = Number(item.content || 0); var content = Number(item.content || 0);
var awardContent = Number(item.awardContent || item.award_content || 0); var awardContent = Number(
var amountUsd = normalizeAmountUsd(item.amountUsd || item.amount_usd); item.awardContent || item.award_content || 0
);
var amountUsd = normalizeAmountUsd(
item.amountUsd || item.amount_usd
);
return { return {
product_id: item.id, product_id: item.id,
productId: item.id, productId: item.id,
@ -707,7 +767,8 @@ var default_api = 'https://api.global-interaction.com/';
} }
function yumiPaymentMethods(options) { function yumiPaymentMethods(options) {
var channels = (options.raw && options.raw.channels) || options.channels || []; var channels =
(options.raw && options.raw.channels) || options.channels || [];
var supported = {}; var supported = {};
channels.forEach(function (item) { channels.forEach(function (item) {
var channel = item.channel || {}; var channel = item.channel || {};
@ -740,7 +801,8 @@ var default_api = 'https://api.global-interaction.com/';
trade_no: order.tradeNo || order.trade_no || '', trade_no: order.tradeNo || order.trade_no || '',
provider_code: factoryCode === 'MIFA_PAY' ? 'mifapay' : factoryCode, provider_code: factoryCode === 'MIFA_PAY' ? 'mifapay' : factoryCode,
factory_code: factoryCode, factory_code: factoryCode,
pay_url: order.requestUrl || order.request_url || order.pay_url || '', pay_url:
order.requestUrl || order.request_url || order.pay_url || '',
currency_code: order.currency || order.currency_code || '', currency_code: order.currency || order.currency_code || '',
country_code: order.countryCode || order.country_code || '', country_code: order.countryCode || order.country_code || '',
provider_amount_minor: decimalToMinor(order.amount), provider_amount_minor: decimalToMinor(order.amount),
@ -777,16 +839,22 @@ var default_api = 'https://api.global-interaction.com/';
var rechargeAPI = { var rechargeAPI = {
context: function (displayUserID) { context: function (displayUserID) {
if (isAslanWebPay()) { if (isAslanWebPay()) {
return request(appEndpoint('context', '/order/web/pay/h5/context'), { return request(
method: 'GET', appEndpoint('context', '/order/web/pay/h5/context'),
query: aslanUserQuery(displayUserID), {
}); method: 'GET',
query: aslanUserQuery(displayUserID),
}
);
} }
if (isYumiWebPay()) { if (isYumiWebPay()) {
return request(appEndpoint('context', '/order/web/pay/user-profile'), { return request(
method: 'GET', appEndpoint('context', '/order/web/pay/user-profile'),
query: configuredWebPayUserQuery(displayUserID), {
}).then(mapYumiContext); method: 'GET',
query: configuredWebPayUserQuery(displayUserID),
}
).then(mapYumiContext);
} }
return request('/api/v1/recharge/h5/context', { return request('/api/v1/recharge/h5/context', {
method: 'GET', method: 'GET',
@ -803,10 +871,13 @@ var default_api = 'https://api.global-interaction.com/';
regionId: extra && (extra.region_id || extra.regionId), regionId: extra && (extra.region_id || extra.regionId),
userId: extra && (extra.user_id || extra.userId), userId: extra && (extra.user_id || extra.userId),
}); });
return request(appEndpoint('options', '/order/web/pay/h5/options'), { return request(
method: 'GET', appEndpoint('options', '/order/web/pay/h5/options'),
query: query, {
}).then(function (options) { method: 'GET',
query: query,
}
).then(function (options) {
options = options || {}; options = options || {};
if (isYumiWebPay()) { if (isYumiWebPay()) {
return mapYumiOptions( return mapYumiOptions(
@ -828,37 +899,45 @@ var default_api = 'https://api.global-interaction.com/';
}, },
createOrder: function (payload) { createOrder: function (payload) {
if (isAslanWebPay()) { if (isAslanWebPay()) {
return request(appEndpoint('orders', '/order/web/pay/h5/orders'), { return request(
method: 'POST', appEndpoint('orders', '/order/web/pay/h5/orders'),
body: { {
applicationId: activeAppConfig.application_id, method: 'POST',
goodsId: payload && payload.product_id, body: {
payCountryId: payload && payload.pay_country_id, applicationId: activeAppConfig.application_id,
userId: payload && payload.user_id, goodsId: payload && payload.product_id,
providerCode: payload && payload.provider_code, payCountryId: payload && payload.pay_country_id,
payWay: payload && payload.pay_way, userId: payload && payload.user_id,
payType: payload && payload.pay_type, providerCode: payload && payload.provider_code,
channelCode: payload && payload.channel_code, payWay: payload && payload.pay_way,
returnUrl: payload && payload.return_url, payType: payload && payload.pay_type,
commandId: payload && payload.command_id, channelCode: payload && payload.channel_code,
type: (activeAppConfig && activeAppConfig.type) || 'GOLD', returnUrl: payload && payload.return_url,
language: readLanguageQuery() || 'en', commandId: payload && payload.command_id,
}, type:
}); (activeAppConfig && activeAppConfig.type) ||
'GOLD',
language: readLanguageQuery() || 'en',
},
}
);
} }
if (isYumiWebPay()) { if (isYumiWebPay()) {
return request(appEndpoint('orders', '/order/web/pay/recharge'), { return request(
method: 'POST', appEndpoint('orders', '/order/web/pay/recharge'),
body: { {
applicationId: activeAppConfig.application_id, method: 'POST',
goodsId: payload && payload.product_id, body: {
payCountryId: payload && payload.pay_country_id, applicationId: activeAppConfig.application_id,
userId: payload && payload.user_id, goodsId: payload && payload.product_id,
channelCode: payload && payload.channel_code, payCountryId: payload && payload.pay_country_id,
newVersion: true, userId: payload && payload.user_id,
appVersion: 'h5', channelCode: payload && payload.channel_code,
}, newVersion: true,
}).then(mapYumiOrder); appVersion: 'h5',
},
}
).then(mapYumiOrder);
} }
return request('/api/v1/recharge/h5/orders', { return request('/api/v1/recharge/h5/orders', {
method: 'POST', method: 'POST',
@ -880,21 +959,24 @@ var default_api = 'https://api.global-interaction.com/';
getOrder: function (orderID, displayUserID) { getOrder: function (orderID, displayUserID) {
if (isAslanWebPay()) { if (isAslanWebPay()) {
return request( return request(
appEndpoint('order', '/order/web/pay/h5/orders/{orderId}').replace( appEndpoint(
'{orderId}', 'order',
encodeURIComponent(orderID || '') '/order/web/pay/h5/orders/{orderId}'
), ).replace('{orderId}', encodeURIComponent(orderID || '')),
{ method: 'GET' } { method: 'GET' }
); );
} }
if (isYumiWebPay()) { if (isYumiWebPay()) {
return request(appEndpoint('order', '/order/web/pay/order-status'), { return request(
method: 'GET', appEndpoint('order', '/order/web/pay/order-status'),
query: { {
orderId: orderID || '', method: 'GET',
userId: displayUserID || '', query: {
}, orderId: orderID || '',
}).then(mapYumiOrderStatus); userId: displayUserID || '',
},
}
).then(mapYumiOrderStatus);
} }
return request( return request(
'/api/v1/recharge/h5/orders/' + '/api/v1/recharge/h5/orders/' +
@ -1107,15 +1189,21 @@ var default_api = 'https://api.global-interaction.com/';
method: 'GET', method: 'GET',
}); });
}, },
searchUsers: function (keyword, pageSize) { searchUsers: function (keyword, pageSize, scope) {
return request('/api/v1/manager-center/users/search', { return request('/api/v1/manager-center/users/search', {
method: 'GET', method: 'GET',
query: { query: {
keyword: keyword, keyword: keyword,
page_size: pageSize || 20, page_size: pageSize || 20,
scope: scope,
}, },
}); });
}, },
listCountries: function () {
return request('/api/v1/countries', {
method: 'GET',
});
},
listResources: function (resourceType, pageSize) { listResources: function (resourceType, pageSize) {
return request('/api/v1/manager-center/resource-grants/resources', { return request('/api/v1/manager-center/resource-grants/resources', {
method: 'GET', method: 'GET',
@ -1175,6 +1263,12 @@ var default_api = 'https://api.global-interaction.com/';
body: payload || {}, body: payload || {},
}); });
}, },
transferUserCountry: function (payload) {
return request('/api/v1/manager-center/users/country', {
method: 'POST',
body: payload || {},
});
},
}; };
var superadminCenterAPI = bdLeaderCenterAPI; var superadminCenterAPI = bdLeaderCenterAPI;

View File

@ -18,6 +18,19 @@
--hy-theme-button-disabled-text: #9a8faa; --hy-theme-button-disabled-text: #9a8faa;
--hy-theme-button-shadow: 0 8px 18px rgba(125, 87, 199, 0.18); --hy-theme-button-shadow: 0 8px 18px rgba(125, 87, 199, 0.18);
--hy-theme-button-disabled-shadow: none; --hy-theme-button-disabled-shadow: none;
--hy-theme-focus-ring: rgba(197, 166, 246, 0.18);
--hy-theme-role-shadow: 0 6px 16px rgba(91, 62, 142, 0.08);
--hy-theme-modal-shadow: 0 22px 54px rgba(40, 25, 72, 0.26);
--hy-theme-checkout-shadow: 0 -10px 28px rgba(40, 25, 72, 0.12);
--hy-theme-radius-tiny: 3px;
--hy-theme-radius-icon: 11px;
--hy-theme-radius-small: 12px;
--hy-theme-radius-control: 14px;
--hy-theme-radius-card: 16px;
--hy-theme-radius-language: 17px;
--hy-theme-radius-modal: 22px;
--hy-theme-radius-pill: 999px;
--hy-theme-radius-circle: 50%;
--hy-theme-hero: linear-gradient( --hy-theme-hero: linear-gradient(
151deg, 151deg,
#f0e6ff 0%, #f0e6ff 0%,
@ -25,3 +38,65 @@
rgba(251, 248, 255, 0.88) 100% rgba(251, 248, 255, 0.88) 100%
); );
} }
:root[data-hy-app-code='yumi'] {
--hy-theme-bg: #f6fff8;
--hy-theme-surface: rgba(255, 255, 255, 0.97);
--hy-theme-surface-soft: #fbfffc;
--hy-theme-line: #d7efdf;
--hy-theme-primary: #bceccc;
--hy-theme-primary-strong: #6fd58f;
--hy-theme-primary-deep: #148846;
--hy-theme-primary-soft: #e8f8ee;
--hy-theme-accent: #8de0a8;
--hy-theme-success: #20b977;
--hy-theme-shadow: 0 10px 24px rgba(20, 136, 70, 0.075);
--hy-theme-button: #148846;
--hy-theme-button-disabled: #ccefd8;
--hy-theme-button-disabled-text: #6c9378;
--hy-theme-button-shadow: 0 8px 18px rgba(20, 136, 70, 0.18);
--hy-theme-focus-ring: rgba(20, 136, 70, 0.16);
--hy-theme-role-shadow: 0 6px 16px rgba(20, 136, 70, 0.08);
--hy-theme-modal-shadow: 0 22px 54px rgba(20, 84, 42, 0.22);
--hy-theme-checkout-shadow: 0 -10px 28px rgba(20, 84, 42, 0.1);
--hy-theme-radius-tiny: 50%;
--hy-theme-radius-icon: 50%;
--hy-theme-radius-small: 50%;
--hy-theme-radius-control: 50%;
--hy-theme-radius-card: 50%;
--hy-theme-radius-language: 50%;
--hy-theme-radius-modal: 50%;
--hy-theme-radius-pill: 50%;
--hy-theme-radius-circle: 50%;
}
:root[data-hy-app-code='aslan'] {
--hy-theme-bg: #fffdf3;
--hy-theme-surface: rgba(255, 255, 255, 0.98);
--hy-theme-surface-soft: #fffbf0;
--hy-theme-line: #efdc9f;
--hy-theme-primary: #ffe071;
--hy-theme-primary-strong: #f1bd2d;
--hy-theme-primary-deep: #8a5a00;
--hy-theme-primary-soft: #fff2bd;
--hy-theme-accent: #f6c43c;
--hy-theme-success: #2aa875;
--hy-theme-shadow: 0 10px 24px rgba(138, 90, 0, 0.08);
--hy-theme-button: #9b6500;
--hy-theme-button-disabled: #f0dfa9;
--hy-theme-button-disabled-text: #8f7b4c;
--hy-theme-button-shadow: 0 8px 18px rgba(138, 90, 0, 0.18);
--hy-theme-focus-ring: rgba(155, 101, 0, 0.18);
--hy-theme-role-shadow: 0 6px 16px rgba(138, 90, 0, 0.08);
--hy-theme-modal-shadow: 0 22px 54px rgba(92, 60, 0, 0.22);
--hy-theme-checkout-shadow: 0 -10px 28px rgba(92, 60, 0, 0.1);
--hy-theme-radius-tiny: 0;
--hy-theme-radius-icon: 0;
--hy-theme-radius-small: 0;
--hy-theme-radius-control: 0;
--hy-theme-radius-card: 0;
--hy-theme-radius-language: 0;
--hy-theme-radius-modal: 0;
--hy-theme-radius-pill: 0;
--hy-theme-radius-circle: 0;
}

View File

@ -107,6 +107,31 @@
> >
</button> </button>
<button
class="manager-tile"
type="button"
data-action="send-badge"
>
<span
class="tile-icon badge-icon"
aria-hidden="true"
>
<svg viewBox="0 0 48 48">
<path
d="M24 7 37 13v10c0 9-5.2 15-13 18-7.8-3-13-9-13-18V13Z"
/>
<path
d="m24 16 2.5 5.1 5.6.8-4.1 4 .9 5.7-4.9-2.7-4.9 2.7.9-5.7-4.1-4 5.6-.8Z"
/>
</svg>
</span>
<span
class="tile-title"
data-i18n="manager_center.send_badge"
>Send Badge</span
>
</button>
<button <button
class="manager-tile" class="manager-tile"
type="button" type="button"
@ -237,6 +262,30 @@
> >
</button> </button>
<button
class="manager-tile manager-tile-wide"
type="button"
data-action="transfer-user-country"
>
<span
class="tile-icon country-icon"
aria-hidden="true"
>
<svg viewBox="0 0 48 48">
<path
d="M24 42c8-8 13-15 13-23A13 13 0 1 0 11 19c0 8 5 15 13 23Z"
/>
<path d="M18 19h12" />
<path d="M24 13v12" />
</svg>
</span>
<span
class="tile-title"
data-i18n="manager_center.transfer_user_country"
>Transfer User Country</span
>
</button>
<button <button
class="manager-tile manager-tile-wide" class="manager-tile manager-tile-wide"
type="button" type="button"
@ -470,10 +519,10 @@
</div> </div>
<script src="../../common/toast.js"></script> <script src="../../common/toast.js"></script>
<script src="../../common/api.js?v=20260612-manager-bd-leader"></script> <script src="../../common/api.js?v=20260624-manager-badge-country"></script>
<script src="../../common/params.js?v=20260608-manager-real"></script> <script src="../../common/params.js?v=20260608-manager-real"></script>
<script src="../../common/jsbridge.js"></script> <script src="../../common/jsbridge.js"></script>
<script src="../../common/i18n.js?v=20260608"></script> <script src="../../common/i18n.js?v=20260608"></script>
<script src="./script.js?v=20260618-manager-permissions"></script> <script src="./script.js?v=20260624-manager-badge-country"></script>
</body> </body>
</html> </html>

View File

@ -3,26 +3,31 @@
'block-user': 'Block User', 'block-user': 'Block User',
'send-frame': 'Send Frame', 'send-frame': 'Send Frame',
'send-car': 'Send Car', 'send-car': 'Send Car',
'send-badge': 'Send Badge',
'send-vip': 'Send VIP', 'send-vip': 'Send VIP',
'update-user-level': 'Update User Level', 'update-user-level': 'Update User Level',
'add-bd-leader': 'Add BD Leader', 'add-bd-leader': 'Add BD Leader',
'add-admin': 'Add Admin', 'add-admin': 'Add Admin',
'add-superadmin': 'Add Superadmin', 'add-superadmin': 'Add Superadmin',
'transfer-user-country': 'Transfer User Country',
}; };
var DAY_MS = 24 * 60 * 60 * 1000; var DAY_MS = 24 * 60 * 60 * 1000;
var PERMANENT_BLOCK_MS = 10 * 365 * DAY_MS; var PERMANENT_BLOCK_MS = 10 * 365 * DAY_MS;
var RESOURCE_TYPES = { var RESOURCE_TYPES = {
'send-frame': 'avatar_frame', 'send-frame': 'avatar_frame',
'send-car': 'vehicle', 'send-car': 'vehicle',
'send-badge': 'badge',
}; };
var ACTION_PERMISSION_KEYS = { var ACTION_PERMISSION_KEYS = {
'send-frame': 'canGrantAvatarFrame', 'send-frame': 'canGrantAvatarFrame',
'send-car': 'canGrantVehicle', 'send-car': 'canGrantVehicle',
'send-badge': 'canGrantBadge',
'update-user-level': 'canUpdateUserLevel', 'update-user-level': 'canUpdateUserLevel',
'add-bd-leader': 'canAddBdLeader', 'add-bd-leader': 'canAddBdLeader',
'add-admin': 'canAddAdmin', 'add-admin': 'canAddAdmin',
'add-superadmin': 'canAddSuperadmin', 'add-superadmin': 'canAddSuperadmin',
'block-user': 'canBlockUser', 'block-user': 'canBlockUser',
'transfer-user-country': 'canTransferUserCountry',
}; };
var RESOURCE_GRANT_DURATIONS = [ var RESOURCE_GRANT_DURATIONS = [
{ label: '7 days', ms: 7 * DAY_MS }, { label: '7 days', ms: 7 * DAY_MS },
@ -43,6 +48,7 @@
overview: null, overview: null,
permissions: {}, permissions: {},
resourcesByType: {}, resourcesByType: {},
countries: [],
}; };
var blockState = { var blockState = {
searchedUser: null, searchedUser: null,
@ -73,6 +79,11 @@
searchedUser: null, searchedUser: null,
submitting: false, submitting: false,
}; };
var countryState = {
searchedUser: null,
selectedCountryCode: '',
submitting: false,
};
function $(id) { function $(id) {
return document.getElementById(id); return document.getElementById(id);
@ -235,6 +246,12 @@
: raw.can_grant_vehicle !== undefined : raw.can_grant_vehicle !== undefined
? Boolean(raw.can_grant_vehicle) ? Boolean(raw.can_grant_vehicle)
: true, : true,
canGrantBadge:
raw.canGrantBadge !== undefined
? Boolean(raw.canGrantBadge)
: raw.can_grant_badge !== undefined
? Boolean(raw.can_grant_badge)
: true,
canUpdateUserLevel: canUpdateUserLevel:
raw.canUpdateUserLevel !== undefined raw.canUpdateUserLevel !== undefined
? Boolean(raw.canUpdateUserLevel) ? Boolean(raw.canUpdateUserLevel)
@ -265,6 +282,12 @@
: raw.can_block_user !== undefined : raw.can_block_user !== undefined
? Boolean(raw.can_block_user) ? Boolean(raw.can_block_user)
: true, : true,
canTransferUserCountry:
raw.canTransferUserCountry !== undefined
? Boolean(raw.canTransferUserCountry)
: raw.can_transfer_user_country !== undefined
? Boolean(raw.can_transfer_user_country)
: true,
}; };
} }
@ -287,11 +310,11 @@
return false; return false;
} }
function apiSearchUser(keyword) { function apiSearchUser(keyword, scope) {
if (!api || !api.searchUsers) { if (!api || !api.searchUsers) {
return Promise.reject(new Error('API not available.')); return Promise.reject(new Error('API not available.'));
} }
return api.searchUsers(keyword, 20).then(function (data) { return api.searchUsers(keyword, 20, scope).then(function (data) {
var items = (data && data.items) || []; var items = (data && data.items) || [];
return items.map(normalizeUser); return items.map(normalizeUser);
}); });
@ -313,7 +336,9 @@
); );
renderActionPermissions(); renderActionPermissions();
setDenied(false); setDenied(false);
return canUseAction('block-user') ? refreshBlocks() : Promise.resolve(); return canUseAction('block-user')
? refreshBlocks()
: Promise.resolve();
}) })
.catch(function (error) { .catch(function (error) {
setDenied(true); setDenied(true);
@ -1095,6 +1120,239 @@
}); });
} }
function normalizeCountry(item) {
item = item || {};
var code = String(
item.country_code ||
item.countryCode ||
item.code ||
item.alphaTwo ||
item.alpha_two ||
''
)
.trim()
.toUpperCase();
return {
code: code,
name:
item.country_display_name ||
item.countryDisplayName ||
item.country_name ||
item.countryName ||
item.name ||
code,
};
}
function countryLabel(country) {
if (!country) return '';
return country.name && country.name !== country.code
? country.name + ' · ' + country.code
: country.code;
}
function selectedCountry() {
for (var i = 0; i < pageState.countries.length; i += 1) {
if (
pageState.countries[i].code === countryState.selectedCountryCode
) {
return pageState.countries[i];
}
}
return null;
}
function loadCountries() {
if (pageState.countries.length) {
renderCountryOptions();
return Promise.resolve(pageState.countries);
}
if (!api || !api.listCountries) {
return Promise.reject(new Error('API not available.'));
}
return api
.listCountries()
.then(function (data) {
var list = (data && (data.countries || data.items)) || [];
pageState.countries = list
.map(normalizeCountry)
.filter(function (country) {
return !!country.code;
});
renderCountryOptions();
return pageState.countries;
})
.catch(showError);
}
function ensureCountryModal() {
if ($('countryModal')) return;
var modal = createNode('div', 'block-modal country-modal');
modal.id = 'countryModal';
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
modal.innerHTML =
'<button class="block-backdrop" id="countryBackdrop" type="button" aria-label="Close"></button>' +
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="countryDialogTitle">' +
'<div class="block-dialog-header"><h2 id="countryDialogTitle">Transfer User Country</h2>' +
'<button class="block-close" id="countryCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
'<div class="block-dialog-body"><div class="block-field"><label for="countrySearchInput">User ID</label>' +
'<div class="block-search-row"><input id="countrySearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="countrySearchButton" type="button">Search</button></div></div>' +
'<div class="block-search-result" id="countrySearchResult" aria-live="polite"></div>' +
'<div class="level-panel" id="countryPanel" hidden><div class="block-section-title">Select country</div><div class="grant-option-grid" id="countryOptionGrid"></div><button class="confirm-level-button" id="confirmCountryButton" type="button" disabled>Confirm Transfer</button></div></div></section>';
document.querySelector('.manager-center').appendChild(modal);
$('countryBackdrop').addEventListener('click', closeCountryModal);
$('countryCloseButton').addEventListener('click', closeCountryModal);
$('countrySearchButton').addEventListener('click', searchCountryUser);
$('countrySearchInput').addEventListener('keydown', function (event) {
if (event.key === 'Enter') searchCountryUser();
});
$('confirmCountryButton').addEventListener(
'click',
confirmCountryTransfer
);
}
function openCountryModal() {
if (pageState.denied) {
toast('Permission denied.');
return;
}
if (!ensureActionAllowed('transfer-user-country')) return;
ensureCountryModal();
countryState.searchedUser = null;
countryState.selectedCountryCode = '';
countryState.submitting = false;
$('countrySearchInput').value = '';
$('countrySearchResult').innerHTML = '';
$('countryPanel').hidden = true;
$('countryModal').hidden = false;
$('countryModal').setAttribute('aria-hidden', 'false');
loadCountries();
renderCountryActions();
setTimeout(function () {
$('countrySearchInput').focus();
}, 30);
}
function closeCountryModal() {
var modal = $('countryModal');
if (!modal) return;
modal.hidden = true;
modal.setAttribute('aria-hidden', 'true');
}
function searchCountryUser() {
var value = $('countrySearchInput').value.trim();
if (!value) {
toast('Enter user ID.');
return;
}
apiSearchUser(value, 'all')
.then(function (items) {
if (!items.length) {
countryState.searchedUser = null;
$('countrySearchResult').innerHTML = '';
$('countryPanel').hidden = true;
renderCountryActions();
toast('No user found.');
return;
}
countryState.searchedUser = items[0];
var container = $('countrySearchResult');
container.innerHTML = '';
container.appendChild(
renderUserRow(
countryState.searchedUser,
'Select',
function () {
$('countryPanel').hidden = false;
renderCountryActions();
}
)
);
$('countryPanel').hidden = false;
renderCountryActions();
})
.catch(showError);
}
function renderCountryOptions() {
var grid = $('countryOptionGrid');
if (!grid) return;
grid.innerHTML = '';
if (!pageState.countries.length) {
grid.appendChild(
createNode('div', 'blocked-empty', 'No country available.')
);
renderCountryActions();
return;
}
pageState.countries.forEach(function (country) {
var button = createNode('button', 'country-option');
button.type = 'button';
button.classList.toggle(
'is-selected',
countryState.selectedCountryCode === country.code
);
button.appendChild(createNode('span', '', countryLabel(country)));
button.addEventListener('click', function () {
countryState.selectedCountryCode = country.code;
renderCountryOptions();
renderCountryActions();
});
grid.appendChild(button);
});
renderCountryActions();
}
function renderCountryActions() {
var confirm = $('confirmCountryButton');
if (!confirm) return;
var currentCountry = String(
(countryState.searchedUser && countryState.searchedUser.country) ||
''
).toUpperCase();
confirm.disabled =
!countryState.searchedUser ||
!countryState.selectedCountryCode ||
countryState.selectedCountryCode === currentCountry ||
countryState.submitting;
var country = selectedCountry();
confirm.textContent = country
? 'Transfer to ' + countryLabel(country)
: 'Confirm Transfer';
}
function confirmCountryTransfer() {
if (!countryState.searchedUser) {
toast('Search a user first.');
return;
}
if (!countryState.selectedCountryCode) {
toast('Select a country.');
return;
}
if (countryState.submitting) return;
countryState.submitting = true;
renderCountryActions();
api.transferUserCountry({
command_id: commandId('manager-country'),
target_user_id: userId(countryState.searchedUser),
country: countryState.selectedCountryCode,
reason: 'manager_center_country_transfer',
})
.then(function () {
toast('User country transferred.');
closeCountryModal();
})
.catch(showError)
.finally(function () {
countryState.submitting = false;
renderCountryActions();
});
}
function bindEvents() { function bindEvents() {
var backButton = $('backButton'); var backButton = $('backButton');
if (backButton) backButton.addEventListener('click', goBack); if (backButton) backButton.addEventListener('click', goBack);
@ -1112,6 +1370,10 @@
openLevelModal(); openLevelModal();
return; return;
} }
if (action === 'transfer-user-country') {
openCountryModal();
return;
}
if ( if (
action === 'add-bd-leader' || action === 'add-bd-leader' ||
action === 'add-admin' || action === 'add-admin' ||

View File

@ -162,6 +162,11 @@
background: #eef4ff; background: #eef4ff;
} }
.badge-icon {
color: #3c8a6b;
background: #eaf8f1;
}
.vip-icon { .vip-icon {
color: #d5a032; color: #d5a032;
background: #fff7dc; background: #fff7dc;
@ -187,6 +192,11 @@
background: #fff2e6; background: #fff2e6;
} }
.country-icon {
color: #4e78d7;
background: #eef4ff;
}
.block-modal[hidden] { .block-modal[hidden] {
display: none; display: none;
} }
@ -486,6 +496,19 @@
white-space: nowrap; white-space: nowrap;
} }
.country-option {
display: grid;
min-height: 44px;
align-content: center;
line-height: 1.15;
}
.country-option span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.confirm-block-button, .confirm-block-button,
.confirm-level-button { .confirm-level-button {
height: 46px; height: 46px;

View File

@ -28,6 +28,19 @@
body { body {
overflow-x: hidden; overflow-x: hidden;
--recharge-focus-ring: var(--hy-theme-focus-ring);
--recharge-role-shadow: var(--hy-theme-role-shadow);
--recharge-modal-shadow: var(--hy-theme-modal-shadow);
--recharge-checkout-shadow: var(--hy-theme-checkout-shadow);
--recharge-radius-tiny: var(--hy-theme-radius-tiny);
--recharge-radius-icon: var(--hy-theme-radius-icon);
--recharge-radius-small: var(--hy-theme-radius-small);
--recharge-radius-control: var(--hy-theme-radius-control);
--recharge-radius-card: var(--hy-theme-radius-card);
--recharge-radius-language: var(--hy-theme-radius-language);
--recharge-radius-modal: var(--hy-theme-radius-modal);
--recharge-radius-pill: var(--hy-theme-radius-pill);
--recharge-radius-circle: var(--hy-theme-radius-circle);
} }
button, button,
@ -90,7 +103,7 @@
height: 34px; height: 34px;
padding: 0 12px; padding: 0 12px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 17px; border-radius: var(--recharge-radius-language);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
color: var(--hy-theme-text); color: var(--hy-theme-text);
font-weight: 800; font-weight: 800;
@ -104,7 +117,7 @@
width: 128px; width: 128px;
padding: 6px; padding: 6px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 16px; border-radius: var(--recharge-radius-card);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
box-shadow: var(--hy-theme-shadow); box-shadow: var(--hy-theme-shadow);
} }
@ -112,7 +125,7 @@
.language-menu button { .language-menu button {
width: 100%; width: 100%;
min-height: 34px; min-height: 34px;
border-radius: 12px; border-radius: var(--recharge-radius-small);
background: transparent; background: transparent;
color: var(--hy-theme-text); color: var(--hy-theme-text);
text-align: left; text-align: left;
@ -132,7 +145,7 @@
margin-top: 10px; margin-top: 10px;
padding: 14px; padding: 14px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 16px; border-radius: var(--recharge-radius-card);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
box-shadow: var(--hy-theme-shadow); box-shadow: var(--hy-theme-shadow);
} }
@ -176,7 +189,7 @@
height: 46px; height: 46px;
padding: 0 14px; padding: 0 14px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 14px; border-radius: var(--recharge-radius-control);
background: var(--hy-theme-surface-soft); background: var(--hy-theme-surface-soft);
color: var(--hy-theme-text); color: var(--hy-theme-text);
outline: none; outline: none;
@ -184,7 +197,7 @@
.input:focus { .input:focus {
border-color: var(--hy-theme-primary-strong); border-color: var(--hy-theme-primary-strong);
box-shadow: 0 0 0 3px rgba(197, 166, 246, 0.18); box-shadow: 0 0 0 3px var(--recharge-focus-ring);
} }
.primary-button, .primary-button,
@ -194,7 +207,7 @@
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 16px; padding: 0 16px;
border-radius: 14px; border-radius: var(--recharge-radius-control);
font-weight: 850; font-weight: 850;
letter-spacing: 0; letter-spacing: 0;
} }
@ -224,7 +237,7 @@
align-items: center; align-items: center;
gap: 10px; gap: 10px;
padding: 10px; padding: 10px;
border-radius: 14px; border-radius: var(--recharge-radius-control);
background: var(--hy-theme-primary-soft); background: var(--hy-theme-primary-soft);
} }
@ -237,7 +250,7 @@
height: 44px; height: 44px;
flex: 0 0 auto; flex: 0 0 auto;
overflow: hidden; overflow: hidden;
border-radius: 16px; border-radius: var(--recharge-radius-card);
background: var(--hy-theme-primary); background: var(--hy-theme-primary);
} }
@ -292,12 +305,12 @@
flex: 0 0 auto; flex: 0 0 auto;
max-width: 112px; max-width: 112px;
padding: 4px 8px; padding: 4px 8px;
border-radius: 999px; border-radius: var(--recharge-radius-pill);
border: 1px solid rgba(255, 255, 255, 0.62); border: 1px solid rgba(255, 255, 255, 0.62);
background: rgba(255, 255, 255, 0.48); background: rgba(255, 255, 255, 0.48);
box-shadow: box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.58), inset 0 1px 0 rgba(255, 255, 255, 0.58),
0 6px 16px rgba(91, 62, 142, 0.08); var(--recharge-role-shadow);
color: var(--hy-theme-primary-deep); color: var(--hy-theme-primary-deep);
font-size: 11px; font-size: 11px;
font-weight: 850; font-weight: 850;
@ -314,7 +327,7 @@
min-width: 58px; min-width: 58px;
min-height: 34px; min-height: 34px;
padding: 0 10px; padding: 0 10px;
border-radius: 999px; border-radius: var(--recharge-radius-pill);
font-size: 12px; font-size: 12px;
} }
@ -329,7 +342,7 @@
min-height: 72px; min-height: 72px;
padding: 12px; padding: 12px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 14px; border-radius: var(--recharge-radius-control);
background: var(--hy-theme-surface-soft); background: var(--hy-theme-surface-soft);
color: var(--hy-theme-text); color: var(--hy-theme-text);
text-align: left; text-align: left;
@ -371,7 +384,7 @@
gap: 8px; gap: 8px;
padding: 9px; padding: 9px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 14px; border-radius: var(--recharge-radius-control);
background: var(--hy-theme-surface-soft); background: var(--hy-theme-surface-soft);
color: var(--hy-theme-text); color: var(--hy-theme-text);
text-align: left; text-align: left;
@ -389,7 +402,7 @@
flex: 0 0 auto; flex: 0 0 auto;
place-items: center; place-items: center;
overflow: hidden; overflow: hidden;
border-radius: 11px; border-radius: var(--recharge-radius-icon);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
color: var(--hy-theme-primary-deep); color: var(--hy-theme-primary-deep);
font-size: 11px; font-size: 11px;
@ -464,7 +477,7 @@
min-width: 38px; min-width: 38px;
min-height: 38px; min-height: 38px;
padding: 0; padding: 0;
border-radius: 50%; border-radius: var(--recharge-radius-circle);
font-size: 0; font-size: 0;
line-height: 1; line-height: 1;
} }
@ -475,7 +488,7 @@
position: absolute; position: absolute;
width: 16px; width: 16px;
height: 2px; height: 2px;
border-radius: 999px; border-radius: var(--recharge-radius-pill);
background: currentColor; background: currentColor;
} }
@ -501,7 +514,7 @@
gap: 4px; gap: 4px;
padding: 9px 10px; padding: 9px 10px;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 14px; border-radius: var(--recharge-radius-control);
background: var(--hy-theme-surface-soft); background: var(--hy-theme-surface-soft);
} }
@ -539,7 +552,7 @@
min-height: 30px; min-height: 30px;
padding: 0; padding: 0;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 10px; border-radius: var(--recharge-radius-icon);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
color: var(--hy-theme-primary-deep); color: var(--hy-theme-primary-deep);
} }
@ -551,7 +564,7 @@
width: 12px; width: 12px;
height: 14px; height: 14px;
border: 2px solid currentColor; border: 2px solid currentColor;
border-radius: 3px; border-radius: var(--recharge-radius-tiny);
} }
.copy-icon-button::before { .copy-icon-button::before {
@ -582,7 +595,7 @@
content: ''; content: '';
width: 8px; width: 8px;
height: 8px; height: 8px;
border-radius: 50%; border-radius: var(--recharge-radius-circle);
background: var(--hy-theme-primary-deep); background: var(--hy-theme-primary-deep);
box-shadow: 0 0 0 4px var(--hy-theme-primary-soft); box-shadow: 0 0 0 4px var(--hy-theme-primary-soft);
} }
@ -591,7 +604,7 @@
display: grid; display: grid;
gap: 8px; gap: 8px;
padding: 12px; padding: 12px;
border-radius: 14px; border-radius: var(--recharge-radius-control);
background: var(--hy-theme-primary-soft); background: var(--hy-theme-primary-soft);
} }
@ -616,7 +629,7 @@
.copy-address-button { .copy-address-button {
min-height: 34px; min-height: 34px;
padding: 0 12px; padding: 0 12px;
border-radius: 12px; border-radius: var(--recharge-radius-small);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
font-size: 12px; font-size: 12px;
} }
@ -652,9 +665,9 @@
margin: 0 auto; margin: 0 auto;
overflow: auto; overflow: auto;
border: 1px solid var(--hy-theme-line); border: 1px solid var(--hy-theme-line);
border-radius: 22px; border-radius: var(--recharge-radius-modal);
background: var(--hy-theme-surface); background: var(--hy-theme-surface);
box-shadow: 0 22px 54px rgba(40, 25, 72, 0.26); box-shadow: var(--recharge-modal-shadow);
} }
.modal-header { .modal-header {
@ -711,7 +724,7 @@
padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
border-top: 1px solid var(--hy-theme-line); border-top: 1px solid var(--hy-theme-line);
background: rgba(255, 255, 255, 0.94); background: rgba(255, 255, 255, 0.94);
box-shadow: 0 -10px 28px rgba(40, 25, 72, 0.12); box-shadow: var(--recharge-checkout-shadow);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
} }
@ -868,9 +881,7 @@
<div class="account-name" id="account-name"> <div class="account-name" id="account-name">
- -
</div> </div>
<div class="role-pill" id="account-role"> <div class="role-pill" id="account-role">-</div>
-
</div>
</div> </div>
<div class="account-meta" id="account-meta">-</div> <div class="account-meta" id="account-meta">-</div>
</div> </div>
@ -1161,8 +1172,7 @@
.then(function (config) { .then(function (config) {
state.appConfig = state.appConfig =
config || config ||
(window.HyAppAPI && (window.HyAppAPI && window.HyAppAPI.getAppConfig
window.HyAppAPI.getAppConfig
? window.HyAppAPI.getAppConfig() ? window.HyAppAPI.getAppConfig()
: null); : null);
}) })
@ -1592,8 +1602,7 @@
.join(' · '), .join(' · '),
logoURL: method.logo_url || '', logoURL: method.logo_url || '',
amount: convertedAmount(product, method), amount: convertedAmount(product, method),
providerCode: providerCode: method.provider_code || 'mifapay',
method.provider_code || 'mifapay',
methodID: method.method_id, methodID: method.method_id,
payCountryID: payCountryID:
method.pay_country_id || method.pay_country_id ||
@ -1653,8 +1662,7 @@
pay_type: method.payType || '', pay_type: method.payType || '',
channel_code: method.channelCode || '', channel_code: method.channelCode || '',
country_code: country_code:
method.countryCode || method.countryCode || state.selectedCountryCode,
state.selectedCountryCode,
currency_code: currency_code:
method.currencyCode || method.currencyCode ||
state.selectedCurrencyCode, state.selectedCurrencyCode,
@ -1670,9 +1678,7 @@
return; return;
} }
startPolling(); startPolling();
if ( if (isRedirectPaymentOrder(state.order)) {
isRedirectPaymentOrder(state.order)
) {
savePendingExternalOrder(state.order); savePendingExternalOrder(state.order);
window.location.href = state.order.pay_url; window.location.href = state.order.pay_url;
} }
@ -2127,9 +2133,7 @@
var usdtMethod = methods.find(function (method) { var usdtMethod = methods.find(function (method) {
return method.key === 'usdt_trc20'; return method.key === 'usdt_trc20';
}); });
state.selectedMethodKey = ( state.selectedMethodKey = (usdtMethod || methods[0]).key;
usdtMethod || methods[0]
).key;
} }
// token 模式不回传 display_user_id防止前端把 token 用户和输入账号混用。 // token 模式不回传 display_user_id防止前端把 token 用户和输入账号混用。
@ -2290,7 +2294,8 @@
} }
var countries = var countries =
(context && (context.country_list || context.countryList)) || (context &&
(context.country_list || context.countryList)) ||
[]; [];
var selected = countries[0] || {}; var selected = countries[0] || {};
state.selectedPayCountryID = state.selectedPayCountryID =
@ -2499,7 +2504,9 @@
var minor = Number(value || 0); var minor = Number(value || 0);
if (!Number.isFinite(minor) || minor <= 0) return '-'; if (!Number.isFinite(minor) || minor <= 0) return '-';
return ( return (
(minor / 100).toFixed(currencyFractionDigits(currency)) + (minor / 100).toFixed(
currencyFractionDigits(currency)
) +
' ' + ' ' +
currency currency
); );