update 2026-08-13 00:52:18
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 640 KiB |
|
After Width: | Height: | Size: 550 KiB |
|
After Width: | Height: | Size: 547 KiB |
|
After Width: | Height: | Size: 659 KiB |
|
After Width: | Height: | Size: 650 KiB |
@@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
|
||||
|
||||
PKG_MAINTAINER:=timsaya
|
||||
|
||||
PKG_VERSION:=0.12.8
|
||||
PKG_VERSION:=0.12.9
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 149 KiB |
@@ -9,6 +9,11 @@
|
||||
var BANDIX_COLOR_UPLOAD = '#f97316'; // 橙色 - 上传/上行
|
||||
var BANDIX_COLOR_DOWNLOAD = '#06b6d4'; // 青色 - 下载/下行
|
||||
|
||||
// 远程广告配置
|
||||
var BANDIX_AD_CONFIG_URL = 'https://raw.githubusercontent.com/timsaya/bandix-ads/main/ads.json';
|
||||
var BANDIX_AD_DEFAULT_REFRESH_INTERVAL = 3600;
|
||||
var BANDIX_AD_RETRY_INTERVAL = 900;
|
||||
|
||||
// 悬浮框背景色定义
|
||||
var BANDIX_TOOLTIP_BG_OPENWRT2020_LIGHT = '#ffffff'; // OpenWrt 2020 浅色主题悬浮框背景
|
||||
var BANDIX_TOOLTIP_BG_OPENWRT2020_DARK = '#2a2a2a'; // OpenWrt 2020 深色主题悬浮框背景
|
||||
@@ -556,6 +561,117 @@ return view.extend({
|
||||
background-color: rgba(239, 68, 68, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.bandix-ad-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--bandix-ad-count, 6), minmax(112px, 1fr));
|
||||
gap: 8px;
|
||||
margin: 12px 0;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.bandix-ad-slot {
|
||||
flex: 1 1 0;
|
||||
min-width: 112px;
|
||||
padding: 0;
|
||||
aspect-ratio: 3 / 1;
|
||||
border: 1px solid rgba(107, 114, 128, 0.35);
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(135deg, rgba(225, 37, 27, 0.08), rgba(107, 114, 128, 0.04));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
transition: border-color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.bandix-ad-slot:not(.bandix-ad-disabled):hover,
|
||||
.bandix-ad-slot:not(.bandix-ad-disabled):focus-visible {
|
||||
border-color: rgba(225, 37, 27, 0.75);
|
||||
box-shadow: 0 4px 12px rgba(225, 37, 27, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.bandix-ad-disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.bandix-ad-label {
|
||||
align-self: flex-start;
|
||||
padding: 1px 5px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
background: rgba(225, 37, 27, 0.88);
|
||||
color: #ffffff;
|
||||
font-size: 0.5625rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.bandix-ad-copy {
|
||||
align-self: stretch;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
padding: 6px 4px 6px 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
gap: 3px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bandix-ad-title {
|
||||
width: 100%;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.bandix-ad-media {
|
||||
align-self: stretch;
|
||||
flex: 0 0 33.333333%;
|
||||
min-width: 0;
|
||||
aspect-ratio: 1 / 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
.bandix-ad-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.bandix-ad-placeholder {
|
||||
flex: 0 0 33.333333%;
|
||||
aspect-ratio: 1 / 1;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 3px;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
color: #e1251b;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/* 移动端隐藏版本信息和更新徽章 */
|
||||
@@ -563,6 +679,26 @@ return view.extend({
|
||||
.bandix-version-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bandix-ad-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.bandix-ad-slot {
|
||||
flex: 0 0 180px;
|
||||
min-width: 180px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.bandix-ad-copy {
|
||||
padding-left: 7px;
|
||||
}
|
||||
|
||||
.bandix-ad-title {
|
||||
font-size: 0.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
.device-mode-group {
|
||||
@@ -2820,6 +2956,25 @@ return view.extend({
|
||||
])
|
||||
]),
|
||||
|
||||
// 广告数据从公开仓库动态加载
|
||||
E('div', {
|
||||
'class': 'bandix-ad-row',
|
||||
'id': 'bandix-ad-row',
|
||||
'aria-label': _('Advertising spaces')
|
||||
}, [1, 2, 3, 4, 5, 6].map(function (index) {
|
||||
return E('div', {
|
||||
'class': 'bandix-ad-slot bandix-ad-disabled',
|
||||
'data-ad-slot': String(index),
|
||||
'aria-disabled': 'true'
|
||||
}, [
|
||||
E('div', { 'class': 'bandix-ad-copy' }, [
|
||||
E('span', { 'class': 'bandix-ad-label' }, _('AD')),
|
||||
E('span', { 'class': 'bandix-ad-title' }, _('Advertising space') + ' ' + index)
|
||||
]),
|
||||
E('span', { 'class': 'bandix-ad-placeholder' }, _('Advertising space'))
|
||||
]);
|
||||
})),
|
||||
|
||||
// 警告提示(包含在线设备数)
|
||||
E('div', {
|
||||
'class': 'bandix-alert' + (getThemeType() === 'wide' ? ' wide-theme' : '')
|
||||
@@ -2975,7 +3130,8 @@ return view.extend({
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'usage-ranking-query-presets' }, [
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'today' }, _('Today')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-positive', 'data-preset': 'today' }, _('Today')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'yesterday' }, _('Yesterday')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'thisweek' }, _('This Week')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'lastweek' }, _('Last Week')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'thismonth' }, _('This Month')),
|
||||
@@ -2983,7 +3139,7 @@ return view.extend({
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '7days' }, _('Last 7 Days')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '30days' }, _('Last 30 Days')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '90days' }, _('Last 90 Days')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-positive', 'data-preset': '1year' }, _('Last Year'))
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '1year' }, _('Last Year'))
|
||||
]),
|
||||
E('div', { 'class': 'usage-ranking-timeline', 'id': 'usage-ranking-timeline' }, [
|
||||
E('div', { 'class': 'usage-ranking-timeline-range', 'id': 'usage-ranking-timeline-range' })
|
||||
@@ -3047,7 +3203,8 @@ return view.extend({
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'usage-ranking-query-presets' }, [
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'today' }, _('Today')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-positive', 'data-preset': 'today' }, _('Today')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'yesterday' }, _('Yesterday')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'thisweek' }, _('This Week')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'lastweek' }, _('Last Week')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': 'thismonth' }, _('This Month')),
|
||||
@@ -3055,7 +3212,7 @@ return view.extend({
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '7days' }, _('Last 7 Days')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '30days' }, _('Last 30 Days')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '90days' }, _('Last 90 Days')),
|
||||
E('button', { 'class': 'cbi-button cbi-button-positive', 'data-preset': '1year' }, _('Last Year'))
|
||||
E('button', { 'class': 'cbi-button cbi-button-neutral', 'data-preset': '1year' }, _('Last Year'))
|
||||
]),
|
||||
E('div', { 'class': 'usage-ranking-timeline', 'id': 'traffic-increments-timeline' }, [
|
||||
E('div', { 'class': 'usage-ranking-timeline-range', 'id': 'traffic-increments-timeline-range' })
|
||||
@@ -7504,7 +7661,6 @@ return view.extend({
|
||||
updateDeviceData();
|
||||
refreshWhitelistStatus();
|
||||
fetchAllScheduleRules();
|
||||
updateTrafficStatistics();
|
||||
|
||||
// 初始化时间范围查询功能
|
||||
setTimeout(function () {
|
||||
@@ -7631,6 +7787,12 @@ return view.extend({
|
||||
startDate = new Date(today);
|
||||
endDate = new Date(today);
|
||||
break;
|
||||
case 'yesterday':
|
||||
var yesterdayDate = new Date(today);
|
||||
yesterdayDate.setDate(yesterdayDate.getDate() - 1);
|
||||
startDate = new Date(yesterdayDate);
|
||||
endDate = new Date(yesterdayDate);
|
||||
break;
|
||||
case 'thisweek':
|
||||
// 本周:周一到今天
|
||||
var dayOfWeek = today.getDay(); // 0=周日, 1=周一, ..., 6=周六
|
||||
@@ -7724,18 +7886,16 @@ return view.extend({
|
||||
// 重置按钮
|
||||
if (resetBtn) {
|
||||
resetBtn.addEventListener('click', function () {
|
||||
var oneYearAgo = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate());
|
||||
setDateRange(oneYearAgo, today, '1year');
|
||||
setDateRange(today, today, 'today');
|
||||
queryData();
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化:默认选择最近一年
|
||||
var oneYearAgo = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate());
|
||||
setDateRange(oneYearAgo, today, '1year');
|
||||
// 初始化:默认选择今天
|
||||
setDateRange(today, today, 'today');
|
||||
|
||||
// 设置初始时间范围并自动加载数据(结束时间为当天的 23:59:59)
|
||||
var startMs = oneYearAgo.getTime();
|
||||
var startMs = todayMs;
|
||||
var endToday = new Date(today);
|
||||
endToday.setHours(23, 59, 59, 999);
|
||||
var endMs = endToday.getTime();
|
||||
@@ -7896,6 +8056,12 @@ return view.extend({
|
||||
startDate = new Date(today);
|
||||
endDate = new Date(today);
|
||||
break;
|
||||
case 'yesterday':
|
||||
var yesterdayDate = new Date(today);
|
||||
yesterdayDate.setDate(yesterdayDate.getDate() - 1);
|
||||
startDate = new Date(yesterdayDate);
|
||||
endDate = new Date(yesterdayDate);
|
||||
break;
|
||||
case 'thisweek':
|
||||
var dayOfWeek = today.getDay();
|
||||
var mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
|
||||
@@ -7952,15 +8118,13 @@ return view.extend({
|
||||
// 重置按钮
|
||||
if (resetBtn) {
|
||||
resetBtn.addEventListener('click', function () {
|
||||
var oneYearAgo = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate());
|
||||
setDateRange(oneYearAgo, today, '1year');
|
||||
setDateRange(today, today, 'today');
|
||||
queryData();
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化:默认选择最近一年
|
||||
var oneYearAgo = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate());
|
||||
setDateRange(oneYearAgo, today, '1year');
|
||||
// 初始化:默认选择今天
|
||||
setDateRange(today, today, 'today');
|
||||
|
||||
startDateInput.addEventListener('change', function () {
|
||||
updateTimeline(this.value, endDateInput.value);
|
||||
@@ -7971,7 +8135,7 @@ return view.extend({
|
||||
});
|
||||
|
||||
// 设置初始时间范围并自动加载数据(不显示 loading)
|
||||
var startMs = oneYearAgo.getTime();
|
||||
var startMs = todayMs;
|
||||
var endToday = new Date(today);
|
||||
endToday.setHours(23, 59, 59, 999);
|
||||
var endMs = endToday.getTime();
|
||||
@@ -7983,6 +8147,205 @@ return view.extend({
|
||||
updateTrafficIncrements(startMs, endMs, null, null);
|
||||
}, 700);
|
||||
|
||||
// 异步加载远程广告(不阻塞主流程)
|
||||
(function () {
|
||||
var refreshTimer = null;
|
||||
|
||||
function resolveHttpsUrl(value, baseUrl) {
|
||||
if (typeof value !== 'string' || !value) return null;
|
||||
|
||||
try {
|
||||
var resolved = new URL(value, baseUrl);
|
||||
return resolved.protocol === 'https:' ? resolved.href : null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleRefresh(seconds) {
|
||||
var interval = Number(seconds);
|
||||
if (!Number.isFinite(interval)) interval = BANDIX_AD_DEFAULT_REFRESH_INTERVAL;
|
||||
interval = Math.max(300, Math.min(86400, interval));
|
||||
|
||||
if (refreshTimer !== null) window.clearTimeout(refreshTimer);
|
||||
refreshTimer = window.setTimeout(loadAdvertisements, interval * 1000);
|
||||
}
|
||||
|
||||
function normalizeAdvertisementLocale(value) {
|
||||
if (typeof value !== 'string') return '';
|
||||
|
||||
var locale = value.trim().replace(/_/g, '-').toLowerCase();
|
||||
if (!locale || locale === 'auto' || locale === 'und') return '';
|
||||
|
||||
if (locale === 'zh' || locale === 'zh-cn' || locale === 'zh-sg' || locale === 'zh-hans') {
|
||||
return 'zh-hans';
|
||||
}
|
||||
if (locale === 'zh-tw' || locale === 'zh-hk' || locale === 'zh-mo' || locale === 'zh-hant') {
|
||||
return 'zh-hant';
|
||||
}
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
function getAdvertisementLocale() {
|
||||
var candidates = [];
|
||||
if (L.env) candidates.push(L.env.lang || L.env.language || '');
|
||||
var htmlLanguage = document.documentElement.getAttribute('lang');
|
||||
if (htmlLanguage) candidates.push(htmlLanguage);
|
||||
if (window.navigator) candidates.push(window.navigator.language || '');
|
||||
|
||||
for (var i = 0; i < candidates.length; i++) {
|
||||
var locale = normalizeAdvertisementLocale(candidates[i]);
|
||||
if (locale) return locale;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function selectLocalizedAdvertisements(config) {
|
||||
// 兼容旧版单语言配置,方便两个仓库分开部署
|
||||
if (config.version === 1 && Array.isArray(config.ads)) {
|
||||
return { locale: 'legacy', ads: config.ads };
|
||||
}
|
||||
|
||||
if (config.version !== 2 || !config.locales || typeof config.locales !== 'object') {
|
||||
throw new Error('Invalid advertising configuration');
|
||||
}
|
||||
|
||||
var currentLocale = getAdvertisementLocale();
|
||||
var defaultLocale = normalizeAdvertisementLocale(config.default_locale);
|
||||
var localeCandidates = [];
|
||||
|
||||
if (currentLocale) {
|
||||
localeCandidates.push(currentLocale);
|
||||
var separatorIndex = currentLocale.indexOf('-');
|
||||
if (separatorIndex > 0) localeCandidates.push(currentLocale.substring(0, separatorIndex));
|
||||
}
|
||||
if (defaultLocale) localeCandidates.push(defaultLocale);
|
||||
|
||||
for (var i = 0; i < localeCandidates.length; i++) {
|
||||
var candidate = localeCandidates[i];
|
||||
if (Object.prototype.hasOwnProperty.call(config.locales, candidate) &&
|
||||
Array.isArray(config.locales[candidate])) {
|
||||
return { locale: candidate, ads: config.locales[candidate] };
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('No advertising locale is available');
|
||||
}
|
||||
|
||||
function renderAdvertisements(config) {
|
||||
var adRow = document.getElementById('bandix-ad-row');
|
||||
if (!adRow || !config) {
|
||||
throw new Error('Invalid advertising configuration');
|
||||
}
|
||||
|
||||
var selection = selectLocalizedAdvertisements(config);
|
||||
adRow.setAttribute('data-ad-locale', selection.locale);
|
||||
|
||||
var ads = selection.ads.slice(0, 6).map(function (ad, index) {
|
||||
if (!ad || typeof ad !== 'object') ad = {};
|
||||
|
||||
var href = resolveHttpsUrl(ad.href, BANDIX_AD_CONFIG_URL);
|
||||
var image = resolveHttpsUrl(ad.image, BANDIX_AD_CONFIG_URL);
|
||||
var title = typeof ad.title === 'string' ? ad.title.trim() : _('Advertising space') + ' ' + (index + 1);
|
||||
if (!title) title = _('Advertising space') + ' ' + (index + 1);
|
||||
var alt = typeof ad.alt === 'string' ? ad.alt.trim() : title;
|
||||
|
||||
return {
|
||||
enabled: ad.enabled === true && href !== null,
|
||||
href: href,
|
||||
image: image,
|
||||
title: title,
|
||||
alt: alt || title
|
||||
};
|
||||
});
|
||||
|
||||
while (ads.length < 6) {
|
||||
ads.push({
|
||||
enabled: false,
|
||||
href: null,
|
||||
image: null,
|
||||
title: _('Advertising space') + ' ' + (ads.length + 1),
|
||||
alt: _('Advertising space')
|
||||
});
|
||||
}
|
||||
|
||||
while (adRow.firstChild) adRow.removeChild(adRow.firstChild);
|
||||
|
||||
ads.forEach(function (ad, index) {
|
||||
var tagName = ad.enabled ? 'a' : 'div';
|
||||
var attributes = {
|
||||
'class': 'bandix-ad-slot' + (ad.enabled ? '' : ' bandix-ad-disabled'),
|
||||
'data-ad-slot': String(index + 1),
|
||||
'title': ad.title
|
||||
};
|
||||
|
||||
if (ad.enabled) {
|
||||
attributes.href = ad.href;
|
||||
attributes.target = '_blank';
|
||||
attributes.rel = 'noopener noreferrer sponsored';
|
||||
} else {
|
||||
attributes['aria-disabled'] = 'true';
|
||||
}
|
||||
|
||||
var content = [E('div', { 'class': 'bandix-ad-copy' }, [
|
||||
E('span', { 'class': 'bandix-ad-label' }, _('AD')),
|
||||
E('span', { 'class': 'bandix-ad-title' }, ad.title)
|
||||
])];
|
||||
if (ad.enabled && ad.image) {
|
||||
content.push(E('div', { 'class': 'bandix-ad-media' }, [
|
||||
E('img', {
|
||||
'class': 'bandix-ad-image',
|
||||
'src': ad.image,
|
||||
'alt': ad.alt,
|
||||
'loading': 'lazy',
|
||||
'referrerpolicy': 'no-referrer'
|
||||
})
|
||||
]));
|
||||
} else {
|
||||
content.push(E('span', { 'class': 'bandix-ad-placeholder' }, _('Advertising space')));
|
||||
}
|
||||
|
||||
adRow.appendChild(E(tagName, attributes, content));
|
||||
});
|
||||
|
||||
adRow.style.setProperty('--bandix-ad-count', '6');
|
||||
}
|
||||
|
||||
function loadAdvertisements() {
|
||||
var adRow = document.getElementById('bandix-ad-row');
|
||||
if (!adRow) return;
|
||||
|
||||
if (typeof window.fetch !== 'function') {
|
||||
console.debug('Failed to load advertisements: Fetch API is unavailable');
|
||||
scheduleRefresh(BANDIX_AD_RETRY_INTERVAL);
|
||||
return;
|
||||
}
|
||||
|
||||
var separator = BANDIX_AD_CONFIG_URL.indexOf('?') === -1 ? '?' : '&';
|
||||
var configUrl = BANDIX_AD_CONFIG_URL + separator + '_=' + Date.now();
|
||||
|
||||
window.fetch(configUrl, {
|
||||
method: 'GET',
|
||||
mode: 'cors',
|
||||
credentials: 'omit',
|
||||
cache: 'no-store'
|
||||
}).then(function (response) {
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
return response.json();
|
||||
}).then(function (config) {
|
||||
renderAdvertisements(config);
|
||||
scheduleRefresh(config.refresh_interval);
|
||||
}).catch(function (err) {
|
||||
console.debug('Failed to load advertisements:', err);
|
||||
scheduleRefresh(BANDIX_AD_RETRY_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
window.setTimeout(loadAdvertisements, 200);
|
||||
})();
|
||||
|
||||
// 异步加载版本信息(不阻塞主流程)
|
||||
(function () {
|
||||
// 延迟执行,确保页面先完成初始化
|
||||
|
||||
@@ -673,6 +673,46 @@ return view.extend({
|
||||
return false;
|
||||
};
|
||||
|
||||
// 支持作者(基本设置的最后一项)
|
||||
var alipayImage = L.resource('bandix/donate/alipay.jpg');
|
||||
var wechatImage = L.resource('bandix/donate/wechat-pay.png');
|
||||
|
||||
function donationCard(title, imageUrl, altText, dotClass) {
|
||||
return E('div', { 'class': 'bandix-donation-card' }, [
|
||||
E('h4', { 'class': 'bandix-donation-card-title' }, [
|
||||
E('span', { 'class': 'bandix-donation-dot ' + dotClass }),
|
||||
title
|
||||
]),
|
||||
E('a', {
|
||||
'class': 'bandix-donation-image-link',
|
||||
'href': imageUrl,
|
||||
'target': '_blank',
|
||||
'rel': 'noopener noreferrer',
|
||||
'title': altText
|
||||
}, [
|
||||
E('img', {
|
||||
'class': 'bandix-donation-image',
|
||||
'src': imageUrl,
|
||||
'alt': altText,
|
||||
'loading': 'lazy'
|
||||
})
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
o = s.option(form.DummyValue, '_donation_qr_codes', _('Support the Author'));
|
||||
o.renderWidget = function () {
|
||||
return E('div', { 'class': 'bandix-donation-content' }, [
|
||||
E('p', { 'class': 'bandix-donation-description' },
|
||||
_('If Bandix is useful to you, you can support its continued development.')),
|
||||
E('div', { 'class': 'bandix-donation-grid' }, [
|
||||
donationCard(_('Alipay'), alipayImage, _('Scan with Alipay to support'), 'bandix-donation-dot-alipay'),
|
||||
donationCard(_('WeChat Pay'), wechatImage, _('Scan with WeChat Pay to support'), 'bandix-donation-dot-wechat')
|
||||
]),
|
||||
E('p', { 'class': 'bandix-donation-note' }, _('Donations are voluntary. Thank you for supporting Bandix.'))
|
||||
]);
|
||||
};
|
||||
|
||||
// 2. 流量监控设置部分 (traffic)
|
||||
s = m.section(form.NamedSection, 'traffic', 'traffic', _('Traffic Monitor Settings'));
|
||||
s.description = _('Configure traffic monitoring related parameters');
|
||||
@@ -826,6 +866,98 @@ return view.extend({
|
||||
// 将 view 实例关联到 form map,以便在 cfgvalue 中访问
|
||||
m.view = this;
|
||||
|
||||
return m.render();
|
||||
return m.render().then(function (mapEl) {
|
||||
var oldStyle = document.getElementById('bandix-donation-styles');
|
||||
if (oldStyle && oldStyle.parentNode) oldStyle.parentNode.removeChild(oldStyle);
|
||||
|
||||
document.head.appendChild(E('style', { 'id': 'bandix-donation-styles' }, `
|
||||
.bandix-donation-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bandix-donation-description {
|
||||
margin: 0 auto 18px;
|
||||
max-width: 620px;
|
||||
opacity: 0.72;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.bandix-donation-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(220px, 280px));
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.bandix-donation-card {
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(107, 114, 128, 0.3);
|
||||
border-radius: 10px;
|
||||
background: rgba(107, 114, 128, 0.04);
|
||||
box-sizing: border-box;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.bandix-donation-card:hover {
|
||||
border-color: rgba(59, 130, 246, 0.55);
|
||||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.bandix-donation-card-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 7px;
|
||||
margin: 0 0 10px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.bandix-donation-dot {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.bandix-donation-dot-alipay {
|
||||
background: #1677ff;
|
||||
}
|
||||
|
||||
.bandix-donation-dot-wechat {
|
||||
background: #07c160;
|
||||
}
|
||||
|
||||
.bandix-donation-image-link {
|
||||
display: block;
|
||||
border-radius: 7px;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.bandix-donation-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.bandix-donation-note {
|
||||
margin: 14px 0 0;
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.bandix-donation-grid {
|
||||
grid-template-columns: minmax(200px, 280px);
|
||||
gap: 14px;
|
||||
}
|
||||
}
|
||||
`));
|
||||
|
||||
return mapEl;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1112,6 +1112,30 @@ msgstr "Restablecer"
|
||||
msgid "Today"
|
||||
msgstr "Hoy"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "Ayer"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "Apoyar al autor"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "Si Bandix te resulta útil, puedes apoyar su desarrollo continuo."
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "Alipay"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "Escanea con Alipay para apoyar"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "WeChat Pay"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "Escanea con WeChat Pay para apoyar"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "Las donaciones son voluntarias. Gracias por apoyar a Bandix."
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "Esta Semana"
|
||||
|
||||
|
||||
@@ -1112,6 +1112,30 @@ msgstr "Réinitialiser"
|
||||
msgid "Today"
|
||||
msgstr "Aujourd'hui"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "Hier"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "Soutenir l’auteur"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "Si Bandix vous est utile, vous pouvez soutenir la poursuite de son développement."
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "Alipay"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "Scannez avec Alipay pour nous soutenir"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "WeChat Pay"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "Scannez avec WeChat Pay pour nous soutenir"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "Les dons sont facultatifs. Merci de soutenir Bandix."
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "Cette Semaine"
|
||||
|
||||
|
||||
@@ -1112,6 +1112,30 @@ msgstr "リセット"
|
||||
msgid "Today"
|
||||
msgstr "今日"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "昨日"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "作者を支援"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "Bandix が役に立った場合は、継続的な開発をご支援いただけます。"
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "Alipay"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "Alipay でスキャンして支援"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "WeChat Pay"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "WeChat Pay でスキャンして支援"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "寄付は任意です。Bandix をご支援いただきありがとうございます。"
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "今週"
|
||||
|
||||
|
||||
@@ -1112,6 +1112,30 @@ msgstr "Resetuj"
|
||||
msgid "Today"
|
||||
msgstr "Dzisiaj"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "Wczoraj"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "Wesprzyj autora"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "Jeśli Bandix jest dla Ciebie przydatny, możesz wesprzeć jego dalszy rozwój."
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "Alipay"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "Zeskanuj w Alipay, aby wesprzeć"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "WeChat Pay"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "Zeskanuj w WeChat Pay, aby wesprzeć"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "Darowizny są dobrowolne. Dziękujemy za wsparcie Bandix."
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "Ten Tydzień"
|
||||
|
||||
|
||||
@@ -1112,6 +1112,30 @@ msgstr "Сбросить"
|
||||
msgid "Today"
|
||||
msgstr "Сегодня"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "Вчера"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "Поддержать автора"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "Если Bandix полезен вам, вы можете поддержать его дальнейшую разработку."
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "Alipay"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "Отсканируйте в Alipay, чтобы поддержать"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "WeChat Pay"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "Отсканируйте в WeChat Pay, чтобы поддержать"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "Пожертвования добровольны. Спасибо за поддержку Bandix."
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "На этой неделе"
|
||||
|
||||
|
||||
@@ -1094,6 +1094,15 @@ msgstr "有更新"
|
||||
msgid "Update available, click to go to settings"
|
||||
msgstr "有更新,点击前往设置"
|
||||
|
||||
msgid "Advertising spaces"
|
||||
msgstr "广告位"
|
||||
|
||||
msgid "Advertising space"
|
||||
msgstr "广告位"
|
||||
|
||||
msgid "AD"
|
||||
msgstr "广告"
|
||||
|
||||
msgid "Traffic Statistics"
|
||||
msgstr "流量统计"
|
||||
|
||||
@@ -1115,6 +1124,30 @@ msgstr "重置"
|
||||
msgid "Today"
|
||||
msgstr "今天"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "昨天"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "支持作者"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "如果 Bandix 对您有帮助,您可以支持项目的持续开发。"
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "支付宝"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "使用支付宝扫码支持"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "微信支付"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "使用微信支付扫码支持"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "赞助完全自愿。感谢您对 Bandix 的支持。"
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "本周"
|
||||
|
||||
|
||||
@@ -1112,6 +1112,30 @@ msgstr "重置"
|
||||
msgid "Today"
|
||||
msgstr "今天"
|
||||
|
||||
msgid "Yesterday"
|
||||
msgstr "昨天"
|
||||
|
||||
msgid "Support the Author"
|
||||
msgstr "支持作者"
|
||||
|
||||
msgid "If Bandix is useful to you, you can support its continued development."
|
||||
msgstr "如果 Bandix 對您有幫助,您可以支持專案的持續開發。"
|
||||
|
||||
msgid "Alipay"
|
||||
msgstr "支付寶"
|
||||
|
||||
msgid "Scan with Alipay to support"
|
||||
msgstr "使用支付寶掃碼支持"
|
||||
|
||||
msgid "WeChat Pay"
|
||||
msgstr "微信支付"
|
||||
|
||||
msgid "Scan with WeChat Pay to support"
|
||||
msgstr "使用微信支付掃碼支持"
|
||||
|
||||
msgid "Donations are voluntary. Thank you for supporting Bandix."
|
||||
msgstr "贊助完全自願。感謝您對 Bandix 的支持。"
|
||||
|
||||
msgid "This Week"
|
||||
msgstr "本週"
|
||||
|
||||
@@ -1255,3 +1279,12 @@ msgstr "TCX 出站錨點程式 ID"
|
||||
|
||||
msgid "Used when tc_order is before/after. Must be a valid egress program id on the same interface."
|
||||
msgstr "當 tc_order 為 before/after 時使用。必須是同一介面上的有效出站程式 ID。"
|
||||
|
||||
msgid "Advertising spaces"
|
||||
msgstr "廣告位"
|
||||
|
||||
msgid "Advertising space"
|
||||
msgstr "廣告位"
|
||||
|
||||
msgid "AD"
|
||||
msgstr "廣告"
|
||||
|
||||