🎄 Sync 2025-11-27 00:12:02
This commit is contained in:
@@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
|
||||
|
||||
PKG_MAINTAINER:=timsaya
|
||||
|
||||
PKG_VERSION:=0.9.0
|
||||
PKG_VERSION:=0.9.1
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
@@ -375,27 +375,27 @@ return view.extend({
|
||||
|
||||
.bandix-table th:nth-child(1),
|
||||
.bandix-table td:nth-child(1) {
|
||||
width: 25%;
|
||||
width: 27%;
|
||||
}
|
||||
|
||||
.bandix-table th:nth-child(2),
|
||||
.bandix-table td:nth-child(2) {
|
||||
width: 20%;
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
.bandix-table th:nth-child(3),
|
||||
.bandix-table td:nth-child(3) {
|
||||
width: 20%;
|
||||
width: 22%;
|
||||
}
|
||||
|
||||
.bandix-table th:nth-child(4),
|
||||
.bandix-table td:nth-child(4) {
|
||||
width: 25%;
|
||||
width: 12.5%;
|
||||
}
|
||||
|
||||
.bandix-table th:nth-child(5),
|
||||
.bandix-table td:nth-child(5) {
|
||||
width: 10%;
|
||||
width: 16.5%;
|
||||
}
|
||||
|
||||
.schedule-rules-info {
|
||||
@@ -1352,6 +1352,82 @@ return view.extend({
|
||||
.history-tooltip .ht-kpi.up .ht-k-value { color: #f97316; }
|
||||
.history-tooltip .ht-divider { height: 1px; background-color: currentColor; opacity: 0.3; margin: 8px 0; }
|
||||
.history-tooltip .ht-section-title { font-weight: 600; font-size: 0.75rem; opacity: 0.7; margin: 4px 0 6px 0; }
|
||||
|
||||
/* Schedule Rules Tooltip */
|
||||
.schedule-rules-tooltip {
|
||||
position: fixed;
|
||||
display: none;
|
||||
width: 360px;
|
||||
max-width: 90vw;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
z-index: 10000;
|
||||
pointer-events: none;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5;
|
||||
background-color: rgba(255, 255, 255, 0.98);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.schedule-rules-tooltip {
|
||||
background-color: rgba(30, 30, 30, 0.98);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
color: #e5e7eb;
|
||||
}
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-rule-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.schedule-rules-tooltip .srt-rule-item {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-rule-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-rule-time {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-rule-days {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
opacity: 0.7;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-rule-limits {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.schedule-rules-tooltip .srt-rule-limits .srt-arrow {
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.schedule-rules-info {
|
||||
}
|
||||
`);
|
||||
|
||||
document.head.appendChild(style);
|
||||
@@ -1444,6 +1520,59 @@ return view.extend({
|
||||
])
|
||||
]);
|
||||
|
||||
// 创建全局的 Schedule Rules Tooltip 元素
|
||||
var scheduleRulesTooltip = E('div', { 'class': 'schedule-rules-tooltip', 'id': 'schedule-rules-tooltip' });
|
||||
document.body.appendChild(scheduleRulesTooltip);
|
||||
|
||||
// 构建规则列表的 HTML(用于 tooltip)
|
||||
function buildScheduleRulesTooltipHtml(allRules, activeRules, speedUnit) {
|
||||
if (!allRules || allRules.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var lines = [];
|
||||
lines.push('<div class="srt-title">' + _('Schedule Rules') + ' (' + allRules.length + ')</div>');
|
||||
|
||||
allRules.forEach(function(rule, index) {
|
||||
var startTime = rule.time_slot && rule.time_slot.start ? rule.time_slot.start : '';
|
||||
var endTime = rule.time_slot && rule.time_slot.end ? rule.time_slot.end : '';
|
||||
var days = rule.time_slot && rule.time_slot.days ? rule.time_slot.days : [];
|
||||
|
||||
var dayNames = {
|
||||
1: _('Mon'),
|
||||
2: _('Tue'),
|
||||
3: _('Wed'),
|
||||
4: _('Thu'),
|
||||
5: _('Fri'),
|
||||
6: _('Sat'),
|
||||
7: _('Sun')
|
||||
};
|
||||
var daysText = days.length > 0 ? days.map(function(d) { return dayNames[d] || d; }).join(', ') : '-';
|
||||
|
||||
var uploadLimit = rule.wide_tx_rate_limit || 0;
|
||||
var downloadLimit = rule.wide_rx_rate_limit || 0;
|
||||
|
||||
// 使用 isRuleActive 函数检查规则是否激活
|
||||
var isActive = isRuleActive(rule);
|
||||
|
||||
// 箭头固定颜色(橙色和青色),样式与 WAN 字段一致
|
||||
var uploadLimitText = '<span class="srt-arrow" style="color: #f97316;">↑</span>' + (uploadLimit > 0 ? formatByterate(uploadLimit, speedUnit) : _('Unlimited'));
|
||||
var downloadLimitText = '<span class="srt-arrow" style="color: #06b6d4;">↓</span>' + (downloadLimit > 0 ? formatByterate(downloadLimit, speedUnit) : _('Unlimited'));
|
||||
|
||||
var activeMark = isActive ? '<span style="color: #10b981; margin-right: 4px;">●</span>' : '';
|
||||
|
||||
lines.push(
|
||||
'<div class="srt-rule-item">' +
|
||||
'<div class="srt-rule-time">' + activeMark + startTime + ' - ' + endTime + '</div>' +
|
||||
'<div class="srt-rule-days">' + daysText + '</div>' +
|
||||
'<div class="srt-rule-limits">' + uploadLimitText + ' ' + downloadLimitText + '</div>' +
|
||||
'</div>'
|
||||
);
|
||||
});
|
||||
|
||||
return lines.join('');
|
||||
}
|
||||
|
||||
// 设备信息模式切换
|
||||
var deviceModeButtons = view.querySelectorAll('.device-mode-btn');
|
||||
|
||||
@@ -2256,6 +2385,80 @@ return view.extend({
|
||||
});
|
||||
}
|
||||
|
||||
// 合并多个生效规则的限制值
|
||||
// 返回合并后的上传和下载限制(取所有规则中非零的最小值)
|
||||
function mergeActiveRulesLimits(activeRules) {
|
||||
if (!activeRules || activeRules.length === 0) {
|
||||
return { uploadLimit: 0, downloadLimit: 0 };
|
||||
}
|
||||
|
||||
var uploadLimits = [];
|
||||
var downloadLimits = [];
|
||||
|
||||
activeRules.forEach(function(rule) {
|
||||
var uploadLimit = rule.wide_tx_rate_limit || 0;
|
||||
var downloadLimit = rule.wide_rx_rate_limit || 0;
|
||||
|
||||
// 只收集非零的限制值
|
||||
if (uploadLimit > 0) {
|
||||
uploadLimits.push(uploadLimit);
|
||||
}
|
||||
if (downloadLimit > 0) {
|
||||
downloadLimits.push(downloadLimit);
|
||||
}
|
||||
});
|
||||
|
||||
// 取最小值(如果有多个规则都有限制,取最严格的限制)
|
||||
var mergedUploadLimit = uploadLimits.length > 0 ? Math.min.apply(Math, uploadLimits) : 0;
|
||||
var mergedDownloadLimit = downloadLimits.length > 0 ? Math.min.apply(Math, downloadLimits) : 0;
|
||||
|
||||
return {
|
||||
uploadLimit: mergedUploadLimit,
|
||||
downloadLimit: mergedDownloadLimit
|
||||
};
|
||||
}
|
||||
|
||||
// 获取多个规则的时间段显示文本
|
||||
// 如果所有规则的时间段相同,显示时间段;如果不同,显示"多个时间段"
|
||||
function getTimeSlotDisplayText(activeRules) {
|
||||
if (!activeRules || activeRules.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (activeRules.length === 1) {
|
||||
// 单个规则,直接显示时间段
|
||||
var rule = activeRules[0];
|
||||
var startTime = rule.time_slot && rule.time_slot.start ? rule.time_slot.start : '';
|
||||
var endTime = rule.time_slot && rule.time_slot.end ? rule.time_slot.end : '';
|
||||
return startTime + '-' + endTime;
|
||||
}
|
||||
|
||||
// 多个规则,检查时间段是否相同
|
||||
var firstRule = activeRules[0];
|
||||
var firstStartTime = firstRule.time_slot && firstRule.time_slot.start ? firstRule.time_slot.start : '';
|
||||
var firstEndTime = firstRule.time_slot && firstRule.time_slot.end ? firstRule.time_slot.end : '';
|
||||
|
||||
var allSame = true;
|
||||
for (var i = 1; i < activeRules.length; i++) {
|
||||
var rule = activeRules[i];
|
||||
var startTime = rule.time_slot && rule.time_slot.start ? rule.time_slot.start : '';
|
||||
var endTime = rule.time_slot && rule.time_slot.end ? rule.time_slot.end : '';
|
||||
|
||||
if (startTime !== firstStartTime || endTime !== firstEndTime) {
|
||||
allSame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allSame) {
|
||||
// 所有规则时间段相同,显示时间段和规则数量
|
||||
return firstStartTime + '-' + firstEndTime + ' (' + activeRules.length + ' ' + _('rules') + ')';
|
||||
} else {
|
||||
// 时间段不同,显示"多个时间段"
|
||||
return _('Multiple time slots') + ' (' + activeRules.length + ' ' + _('rules') + ')';
|
||||
}
|
||||
}
|
||||
|
||||
// 排序状态管理
|
||||
var currentSortBy = localStorage.getItem('bandix_sort_by') || 'online'; // 默认按在线状态排序
|
||||
var currentSortOrder = localStorage.getItem('bandix_sort_order') === 'true'; // false = 降序, true = 升序
|
||||
@@ -3781,39 +3984,36 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) {
|
||||
if (allDeviceRules.length === 0) {
|
||||
rulesInfo.appendChild(E('div', { 'style': 'font-size: 0.75rem; opacity: 0.6;' }, '-'));
|
||||
} else {
|
||||
// 显示规则数量
|
||||
rulesInfo.appendChild(E('div', {
|
||||
'style': 'font-size: 0.75rem; font-weight: 600; margin-bottom: 4px;'
|
||||
}, allDeviceRules.length + ' ' + (allDeviceRules.length === 1 ? _('rule') : _('rules'))));
|
||||
|
||||
// 显示当前生效的规则
|
||||
if (activeRules.length > 0) {
|
||||
var activeRule = activeRules[0]; // 显示第一个生效的规则
|
||||
var startTime = activeRule.time_slot && activeRule.time_slot.start ? activeRule.time_slot.start : '';
|
||||
var endTime = activeRule.time_slot && activeRule.time_slot.end ? activeRule.time_slot.end : '';
|
||||
|
||||
var uploadLimit = activeRule.wide_tx_rate_limit || 0;
|
||||
var downloadLimit = activeRule.wide_rx_rate_limit || 0;
|
||||
|
||||
// 时间段和限速值放在同一行
|
||||
var timeSlotText = startTime + '-' + endTime;
|
||||
var limitsText = [];
|
||||
// 即使限速是0也显示
|
||||
limitsText.push('↑' + (uploadLimit > 0 ? formatByterate(uploadLimit, speedUnit) : _('Unlimited')));
|
||||
limitsText.push('↓' + (downloadLimit > 0 ? formatByterate(downloadLimit, speedUnit) : _('Unlimited')));
|
||||
// 合并多个规则的限制值
|
||||
var mergedLimits = mergeActiveRulesLimits(activeRules);
|
||||
var uploadLimit = mergedLimits.uploadLimit;
|
||||
var downloadLimit = mergedLimits.downloadLimit;
|
||||
|
||||
// 显示规则数量
|
||||
rulesInfo.appendChild(E('div', {
|
||||
'style': 'font-size: 0.75rem; color: #10b981; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;'
|
||||
}, [
|
||||
E('span', {}, '● ' + timeSlotText),
|
||||
E('span', { 'style': 'opacity: 0.8; font-size: 0.7rem;' }, limitsText.join(' '))
|
||||
]));
|
||||
'style': 'font-size: 0.75rem; font-weight: 600; margin-bottom: 4px;'
|
||||
}, activeRules.length + ' ' + (activeRules.length === 1 ? _('rule') : _('rules'))));
|
||||
|
||||
if (activeRules.length > 1) {
|
||||
rulesInfo.appendChild(E('div', {
|
||||
'style': 'font-size: 0.7rem; opacity: 0.6; margin-top: 2px;'
|
||||
}, '+' + (activeRules.length - 1) + ' ' + _('more')));
|
||||
}
|
||||
// 显示限速值(箭头固定颜色,文字默认颜色)
|
||||
var limitsContainer = E('div', {
|
||||
'style': 'font-size: 0.75rem; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;'
|
||||
});
|
||||
|
||||
// 上传限速(橙色箭头)
|
||||
var uploadSpan = E('span', {});
|
||||
uploadSpan.appendChild(E('span', { 'style': 'color: #f97316;' }, '↑'));
|
||||
uploadSpan.appendChild(document.createTextNode(uploadLimit > 0 ? formatByterate(uploadLimit, speedUnit) : _('Unlimited')));
|
||||
limitsContainer.appendChild(uploadSpan);
|
||||
|
||||
// 下载限速(青色箭头)
|
||||
var downloadSpan = E('span', {});
|
||||
downloadSpan.appendChild(E('span', { 'style': 'color: #06b6d4;' }, '↓'));
|
||||
downloadSpan.appendChild(document.createTextNode(downloadLimit > 0 ? formatByterate(downloadLimit, speedUnit) : _('Unlimited')));
|
||||
limitsContainer.appendChild(downloadSpan);
|
||||
|
||||
rulesInfo.appendChild(limitsContainer);
|
||||
} else {
|
||||
rulesInfo.appendChild(E('div', {
|
||||
'style': 'font-size: 0.75rem; opacity: 0.5;'
|
||||
@@ -3821,6 +4021,125 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) {
|
||||
}
|
||||
}
|
||||
|
||||
// PC 端添加鼠标悬浮事件(显示所有规则)- 只要有规则就绑定事件
|
||||
var screenWidth = window.innerWidth || document.documentElement.clientWidth;
|
||||
if (screenWidth > 768 && allDeviceRules.length > 0) {
|
||||
rulesInfo.onmouseenter = function(evt) {
|
||||
var tooltip = document.getElementById('schedule-rules-tooltip');
|
||||
if (!tooltip) return;
|
||||
|
||||
var html = buildScheduleRulesTooltipHtml(allDeviceRules, activeRules, speedUnit);
|
||||
if (!html) return;
|
||||
|
||||
tooltip.innerHTML = html;
|
||||
|
||||
// 应用主题颜色
|
||||
try {
|
||||
var cbiSection = document.querySelector('.cbi-section');
|
||||
var targetElement = cbiSection || document.querySelector('.main') || document.body;
|
||||
var computedStyle = window.getComputedStyle(targetElement);
|
||||
var bgColor = computedStyle.backgroundColor;
|
||||
var textColor = computedStyle.color;
|
||||
|
||||
if (bgColor && bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
|
||||
var rgbaMatch = bgColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
||||
if (rgbaMatch) {
|
||||
var r = parseInt(rgbaMatch[1]);
|
||||
var g = parseInt(rgbaMatch[2]);
|
||||
var b = parseInt(rgbaMatch[3]);
|
||||
var alpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
|
||||
if (alpha < 0.95) {
|
||||
tooltip.style.backgroundColor = 'rgb(' + r + ', ' + g + ', ' + b + ')';
|
||||
} else {
|
||||
tooltip.style.backgroundColor = bgColor;
|
||||
}
|
||||
} else {
|
||||
tooltip.style.backgroundColor = bgColor;
|
||||
}
|
||||
}
|
||||
|
||||
if (textColor && textColor !== 'rgba(0, 0, 0, 0)') {
|
||||
tooltip.style.color = textColor;
|
||||
}
|
||||
} catch(e) {}
|
||||
|
||||
// 先隐藏,设置内容后再显示以计算尺寸
|
||||
tooltip.style.display = 'block';
|
||||
tooltip.style.visibility = 'hidden';
|
||||
tooltip.style.left = '-9999px';
|
||||
tooltip.style.top = '-9999px';
|
||||
|
||||
// 强制浏览器计算尺寸
|
||||
var tw = tooltip.offsetWidth || 0;
|
||||
var th = tooltip.offsetHeight || 0;
|
||||
|
||||
if (tw === 0 || th === 0) {
|
||||
tooltip.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
tooltip.style.visibility = 'visible';
|
||||
|
||||
var padding = 12;
|
||||
var maxX = window.innerWidth - 4;
|
||||
var maxY = window.innerHeight - 4;
|
||||
|
||||
var rect = evt.currentTarget.getBoundingClientRect();
|
||||
var cx = rect.left + rect.width / 2;
|
||||
var cy = rect.top + rect.height / 2;
|
||||
|
||||
// 计算位置:优先显示在右侧,如果空间不足则显示在左侧
|
||||
var baseX = cx + padding;
|
||||
var baseY = cy - th / 2;
|
||||
|
||||
if (baseX + tw > maxX) {
|
||||
baseX = cx - tw - padding;
|
||||
}
|
||||
|
||||
if (baseY < 4) baseY = 4;
|
||||
if (baseY + th > maxY) baseY = maxY - th - 4;
|
||||
|
||||
tooltip.style.left = baseX + 'px';
|
||||
tooltip.style.top = baseY + 'px';
|
||||
};
|
||||
|
||||
rulesInfo.onmouseleave = function() {
|
||||
var tooltip = document.getElementById('schedule-rules-tooltip');
|
||||
if (tooltip) {
|
||||
tooltip.style.display = 'none';
|
||||
tooltip.style.visibility = 'visible';
|
||||
}
|
||||
};
|
||||
|
||||
rulesInfo.onmousemove = function(evt) {
|
||||
var tooltip = document.getElementById('schedule-rules-tooltip');
|
||||
if (!tooltip || tooltip.style.display === 'none') return;
|
||||
|
||||
var tw = tooltip.offsetWidth || 0;
|
||||
var th = tooltip.offsetHeight || 0;
|
||||
var padding = 12;
|
||||
var maxX = window.innerWidth - 4;
|
||||
var maxY = window.innerHeight - 4;
|
||||
|
||||
var rect = evt.currentTarget.getBoundingClientRect();
|
||||
var cx = rect.left + rect.width / 2;
|
||||
var cy = rect.top + rect.height / 2;
|
||||
|
||||
var baseX = cx + padding;
|
||||
var baseY = cy - th / 2;
|
||||
|
||||
if (baseX + tw > maxX) {
|
||||
baseX = cx - tw - padding;
|
||||
}
|
||||
|
||||
if (baseY < 4) baseY = 4;
|
||||
if (baseY + th > maxY) baseY = maxY - th - 4;
|
||||
|
||||
tooltip.style.left = baseX + 'px';
|
||||
tooltip.style.top = baseY + 'px';
|
||||
};
|
||||
}
|
||||
|
||||
return E('td', {}, rulesInfo);
|
||||
})(),
|
||||
|
||||
@@ -3889,41 +4208,25 @@ function downsampleForMobile(data, labels, upSeries, downSeries, maxPoints) {
|
||||
|
||||
var rulesContent = E('div', { 'class': 'device-card-rules-content' });
|
||||
|
||||
// 规则数量
|
||||
rulesContent.appendChild(E('div', {
|
||||
'class': 'device-card-rules-count'
|
||||
}, allDeviceRules.length + ' ' + (allDeviceRules.length === 1 ? _('rule') : _('rules'))));
|
||||
|
||||
if (activeRules.length > 0) {
|
||||
var activeRule = activeRules[0];
|
||||
var startTime = activeRule.time_slot && activeRule.time_slot.start ? activeRule.time_slot.start : '';
|
||||
var endTime = activeRule.time_slot && activeRule.time_slot.end ? activeRule.time_slot.end : '';
|
||||
// 合并多个规则的限制值
|
||||
var mergedLimits = mergeActiveRulesLimits(activeRules);
|
||||
var uploadLimit = mergedLimits.uploadLimit;
|
||||
var downloadLimit = mergedLimits.downloadLimit;
|
||||
|
||||
var uploadLimit = activeRule.wide_tx_rate_limit || 0;
|
||||
var downloadLimit = activeRule.wide_rx_rate_limit || 0;
|
||||
// 显示规则数量
|
||||
rulesContent.appendChild(E('div', {
|
||||
'class': 'device-card-rules-count'
|
||||
}, activeRules.length + ' ' + (activeRules.length === 1 ? _('rule') : _('rules'))));
|
||||
|
||||
// 时间段和限速值放在同一行
|
||||
var timeSlotText = startTime + '-' + endTime;
|
||||
// 显示限速值
|
||||
var limitsText = [];
|
||||
// 即使限速是0也显示
|
||||
limitsText.push('↑' + (uploadLimit > 0 ? formatByterate(uploadLimit, speedUnit) : _('Unlimited')));
|
||||
limitsText.push('↓' + (downloadLimit > 0 ? formatByterate(downloadLimit, speedUnit) : _('Unlimited')));
|
||||
|
||||
// 生效规则的时间段和限速值
|
||||
var activeTimeInfo = E('div', {
|
||||
rulesContent.appendChild(E('div', {
|
||||
'class': 'device-card-rules-active-time'
|
||||
}, [
|
||||
E('span', {}, '● ' + timeSlotText),
|
||||
E('span', { 'style': 'opacity: 0.8; font-size: 0.7rem; margin-left: 8px;' }, limitsText.join(' '))
|
||||
]);
|
||||
|
||||
if (activeRules.length > 1) {
|
||||
activeTimeInfo.appendChild(E('div', {
|
||||
'class': 'device-card-rules-more'
|
||||
}, '+' + (activeRules.length - 1) + ' ' + _('more')));
|
||||
}
|
||||
|
||||
rulesContent.appendChild(activeTimeInfo);
|
||||
}, limitsText.join(' ')));
|
||||
} else {
|
||||
rulesContent.appendChild(E('div', {
|
||||
'class': 'device-card-rules-inactive'
|
||||
|
||||
@@ -83,6 +83,7 @@ function index()
|
||||
entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true
|
||||
entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
|
||||
entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true
|
||||
entry({"admin", "services", appname, "save_node_order"}, call("save_node_order")).leaf = true
|
||||
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
|
||||
entry({"admin", "services", appname, "subscribe_del_node"}, call("subscribe_del_node")).leaf = true
|
||||
entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).leaf = true
|
||||
@@ -591,13 +592,12 @@ function delete_select_nodes()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function get_node()
|
||||
local id = http.formvalue("id")
|
||||
local result = {}
|
||||
local show_node_info = api.uci_get_type("global_other", "show_node_info", "0")
|
||||
|
||||
function add_is_ipv6_key(o)
|
||||
local function add_is_ipv6_key(o)
|
||||
if o and o.address and show_node_info == "1" then
|
||||
local f = api.get_ipv6_full(o.address)
|
||||
if f ~= "" then
|
||||
@@ -611,14 +611,35 @@ function get_node()
|
||||
result = uci:get_all(appname, id)
|
||||
add_is_ipv6_key(result)
|
||||
else
|
||||
local default_nodes = {}
|
||||
local other_nodes = {}
|
||||
uci:foreach(appname, "nodes", function(t)
|
||||
add_is_ipv6_key(t)
|
||||
result[#result + 1] = t
|
||||
if not t.group or t.group == "" then
|
||||
default_nodes[#default_nodes + 1] = t
|
||||
else
|
||||
other_nodes[#other_nodes + 1] = t
|
||||
end
|
||||
end)
|
||||
for i = 1, #default_nodes do result[#result + 1] = default_nodes[i] end
|
||||
for i = 1, #other_nodes do result[#result + 1] = other_nodes[i] end
|
||||
end
|
||||
http_write_json(result)
|
||||
end
|
||||
|
||||
function save_node_order()
|
||||
local ids = http.formvalue("ids") or ""
|
||||
local new_order = {}
|
||||
for id in ids:gmatch("([^,]+)") do
|
||||
new_order[#new_order + 1] = id
|
||||
end
|
||||
for idx, name in ipairs(new_order) do
|
||||
luci.sys.call(string.format("uci -q reorder %s.%s=%d", appname, name, idx - 1))
|
||||
end
|
||||
api.sh_uci_commit(appname)
|
||||
http_write_json({ status = "ok" })
|
||||
end
|
||||
|
||||
function update_rules()
|
||||
local update = http.formvalue("update")
|
||||
luci.sys.call("lua /usr/share/passwall/rule_update.lua log '" .. update .. "' > /dev/null 2>&1 &")
|
||||
|
||||
@@ -210,19 +210,28 @@ table td, .table .td {
|
||||
document.getElementById("set_node_name").innerHTML = "";
|
||||
}
|
||||
|
||||
function _cbi_row_top(id) {
|
||||
//此函数已经损坏,等待修复或其他解决方案。
|
||||
var dom = document.getElementById("cbi-passwall-" + id);
|
||||
if (dom) {
|
||||
var trs = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-section-table-row");
|
||||
if (trs && trs.length > 0) {
|
||||
for (var i = 0; i < trs.length; i++) {
|
||||
var up = dom.getElementsByClassName("cbi-button-up");
|
||||
if (up) {
|
||||
cbi_row_swap(up[0], true, 'cbi.sts.passwall.nodes');
|
||||
}
|
||||
}
|
||||
function row_swap(btn, up) {
|
||||
const row = btn.closest("tr");
|
||||
if (!row) return;
|
||||
const parent = row.parentNode;
|
||||
if (up) {
|
||||
const prev = row.previousElementSibling;
|
||||
if (prev && !prev.classList.contains("cbi-section-table-titles")) {
|
||||
parent.insertBefore(row, prev);
|
||||
}
|
||||
} else {
|
||||
const next = row.nextElementSibling;
|
||||
if (next) parent.insertBefore(next, row);
|
||||
}
|
||||
}
|
||||
|
||||
function row_top(btn) {
|
||||
const row = btn.closest("tr");
|
||||
if (!row) return;
|
||||
const parent = row.parentNode;
|
||||
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
||||
if (firstDataRow && firstDataRow !== row) {
|
||||
parent.insertBefore(row, firstDataRow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +314,39 @@ table td, .table .td {
|
||||
return { address: address, port: port };
|
||||
}
|
||||
|
||||
function save_current_page_order(group) {
|
||||
var table = document.getElementById("cbi-passwall-nodes-" + group + "-table");
|
||||
if (!table) {
|
||||
alert("<%:No table!%>");
|
||||
return;
|
||||
}
|
||||
var rows = table.querySelectorAll("tr.cbi-section-table-row");
|
||||
if (!rows || rows.length === 0) {
|
||||
alert("<%:No nodes!%>");
|
||||
return;
|
||||
}
|
||||
var btn = document.getElementById("save_order_btn_" + group);
|
||||
if (btn) btn.disabled = true;
|
||||
var ids = [];
|
||||
rows.forEach(function(row) {
|
||||
var id = row.id.replace("cbi-passwall-", "");
|
||||
ids.push(id);
|
||||
});
|
||||
XHR.get('<%=api.url("save_node_order")%>', {
|
||||
group: group,
|
||||
ids: ids.join(",")
|
||||
},
|
||||
function(x, result) {
|
||||
if (btn) btn.disabled = false;
|
||||
if (x && x.status === 200) {
|
||||
alert("<%:Saved current page order successfully.%>");
|
||||
} else {
|
||||
alert("<%:Save failed!%>");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
//获取当前使用的节点
|
||||
function get_now_use_node() {
|
||||
XHR.get('<%=api.url("get_now_use_node")%>', null,
|
||||
@@ -522,6 +564,7 @@ table td, .table .td {
|
||||
</table>
|
||||
<div class="cbi-section-create cbi-tblsection-create">
|
||||
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="to_add_node()">
|
||||
<input class="cbi-button cbi-button-apply" type="button" id="save_order_btn_{{group}}" value="<%:Save Order%>" onclick="save_current_page_order('{{group}}')">
|
||||
</div>
|
||||
</fieldset>
|
||||
</script>
|
||||
@@ -537,11 +580,12 @@ table td, .table .td {
|
||||
<td class="td cbi-value-field">{{url_test}}</td>
|
||||
<td class="td cbi-section-table-cell nowrap cbi-section-actions">
|
||||
<div class="node-wrapper">
|
||||
<!--It has been damaged and awaits repair or other solutions.-->
|
||||
<!--<input class="btn cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top('{{id}}')"/>-->
|
||||
<input class="cbi-input-checkbox nodes_select" type="checkbox" cbid="{{id}}" />
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:To Top%>" onclick="row_top(this)" title="<%:To Top%>"/>
|
||||
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_{{id}}" onclick="open_set_node_div('{{id}}')"/>
|
||||
<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node('{{id}}')"/>
|
||||
<input class="btn cbi-button cbi-button-up" type="button" value="<%:Move up%>" onclick="return row_swap(this, true)" title="<%:Move up%>">
|
||||
<input class="btn cbi-button cbi-button-down" type="button" value="<%:Move down%>" onclick="return row_swap(this, false)" title="<%:Move down%>">
|
||||
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="location.href='<%=api.url("node_config")%>/{{id}}'" alt="<%:Edit%>" title="<%:Edit%>">
|
||||
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Delete%>" onclick="del_node('{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
|
||||
</div>
|
||||
|
||||
@@ -421,6 +421,12 @@ msgstr "节点备注"
|
||||
msgid "Add Mode"
|
||||
msgstr "添加方式"
|
||||
|
||||
msgid "Save Order"
|
||||
msgstr "保存当前顺序"
|
||||
|
||||
msgid "Saved current page order successfully."
|
||||
msgstr "保存当前页面顺序成功。"
|
||||
|
||||
msgid "Type"
|
||||
msgstr "类型"
|
||||
|
||||
|
||||
@@ -760,13 +760,16 @@ local function processData(szType, content)
|
||||
-- 未指定peer(sni)默认使用remote addr
|
||||
result.tls_host = params.peer or params.sni
|
||||
end
|
||||
if params.allowInsecure then
|
||||
params.allowinsecure = params.allowinsecure or params.insecure
|
||||
if params.allowinsecure then
|
||||
-- 处理 insecure 参数
|
||||
if params.allowinsecure == "1" or params.allowinsecure == "0" then
|
||||
result.insecure = params.allowInsecure
|
||||
result.insecure = params.allowinsecure
|
||||
else
|
||||
result.insecure = string.lower(params.allowinsecure) == "true" and "1" or "0"
|
||||
end
|
||||
else
|
||||
result.insecure = "0"
|
||||
end
|
||||
if params.tfo then
|
||||
-- 处理 fast open 参数
|
||||
|
||||
Reference in New Issue
Block a user