small-packages/luci-app-oaf/luasrc/view/admin_network/time.htm
2026-05-10 09:48:30 +08:00

1114 lines
51 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<link rel="stylesheet" href="<%=resource%>/css/common.css">
<style>
/* 页面最小宽度保证手机端正常显示 */
.cbi-section.cbi-tblsection {
overflow-x: auto;
}
/* 保存按钮样式 - 覆盖 common.css */
.submit-button {
padding: 8px 20px;
width: 150px;
height: auto;
margin-top: 0;
background-color: #1a73e8;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
line-height: 1.4;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
}
.submit-button:hover {
background-color: #1557b0;
}
/* 时间进度条样式 */
.time-progress-row {
display: flex;
align-items: center;
margin-top: 6px;
}
.time-progress-label {
width: 32px;
font-size: 12px;
color: #666;
opacity: 0.9;
flex-shrink: 0;
font-weight: 500;
}
.time-progress-bar {
flex: 1;
height: 6px;
background-color: #d1d5db;
border-radius: 3px;
overflow: hidden;
margin: 0 8px;
min-width: 80px;
max-width: 220px;
}
.time-progress-fill {
height: 100%;
border-radius: 3px;
background-color: #1a73e8;
transition: width 0.5s ease;
}
.time-progress-text {
font-size: 12px;
white-space: nowrap;
flex-shrink: 0;
}
/* 清除按钮样式 */
.clear-time-btn {
padding: 4px 12px;
background-color: #ea4335;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
transition: background-color 0.2s;
}
.clear-time-btn:hover {
background-color: #c5221f;
}
.time-rules {
margin-top: 20px;
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
min-width: 600px;
}
.weekday-selector {
margin-bottom: 15px;
}
.weekday-item {
display: inline-block;
margin-right: 10px;
}
.time-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
table-layout: fixed;
}
.time-table th, .time-table td {
padding: 8px;
border: 1px solid #ddd;
text-align: center;
overflow: visible;
}
.time-table td {
vertical-align: top;
}
.daily-limit-input-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.daily-limit-input-wrapper input {
width: 150px;
}
.daily-limit-error {
color: red;
font-size: 11px;
margin-top: 2px;
display: none;
text-align: center;
line-height: 1.2;
}
.time-table th:nth-child(1),
.time-table td:nth-child(1) {
width: 50%;
}
.time-table th:nth-child(2),
.time-table td:nth-child(2) {
width: 20%;
}
.time-table th:nth-child(3),
.time-table td:nth-child(3) {
width: 20%;
}
.time-table th:nth-child(4),
.time-table td:nth-child(4) {
width: 10%;
}
.time-table input[type="time"] {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
.add-time-btn {
margin-top: 10px;
padding: 5px 15px;
background-color: #2885e8;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}
.delete-time-btn {
padding: 3px 8px;
background-color: #ff4444;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}
.whitelist-settings {
margin-top: 20px;
}
.setting-item {
margin-bottom: 10px;
}
.setting-item label {
display: inline-block;
width: 170px;
}
.setting-item input {
width: 100px;
padding: 5px;
}
.dynamic-time-settings {
margin-top: 20px;
}
.advanced-settings {
margin-top: 20px;
border-top: 1px solid #ddd;
padding-top: 15px;
}
.advanced-toggle {
display: flex;
align-items: center;
cursor: pointer;
color: #2885e8;
font-size: 14px;
margin-bottom: 15px;
}
.advanced-toggle .arrow {
margin-right: 8px;
width: 12px;
height: 12px;
background-image: url('<%=resource%>/icons/arrow.png');
background-size: contain;
background-repeat: no-repeat;
transition: transform 0.3s ease;
display: inline-block;
transform: rotate(0deg);
}
.advanced-toggle.expanded .arrow {
transform: rotate(90deg);
}
.advanced-content {
display: block;
}
.advanced-content.expanded {
display: block;
}
</style>
<script type="text/javascript">//<![CDATA[
let time_data1 = {
"time_list":[{"start":"00:00","end":"23:59"}, {"start":"00:00","end":"23:59"}],
"weekday_list":[1,2,3,4,5,6,0],
"mode":1
}
let time_data = {}
function formatTime(minutes) {
// 如果值为空或undefined显示0min
if (minutes === undefined || minutes === null || minutes === '') {
return '0<%:min%>';
}
var mins = parseInt(minutes);
if (isNaN(mins) || mins < 0) {
return '0<%:min%>';
}
// 如果小于60分钟直接显示分钟
if (mins < 60) {
return mins + '<%:min%>';
}
// 大于等于60分钟转换为小时和分钟
var hours = Math.floor(mins / 60);
var remainingMins = mins % 60;
if (remainingMins === 0) {
return hours + '<%:h%>';
} else {
return hours + '<%:h%> ' + remainingMins + '<%:min%>';
}
}
function buildProgressBar(label, used, limit) {
if (limit <= 0) return `<div class="time-progress-row"><span class="time-progress-label">${label}</span><span class="time-progress-text" style="color:#999;"><%:Not set%></span></div>`;
var pct = Math.min(Math.round((used / limit) * 100), 100);
var text = used >= limit ? `<span style="color:#d93025;"><%:Used up%></span> <span style="color:#999;">(<%:Total%>: ${limit} <%:min%>)</span>` : `${used} / ${limit} <%:min%>`;
return `<div class="time-progress-row"><span class="time-progress-label">${label}</span><div class="time-progress-bar"><div class="time-progress-fill" style="width:${pct}%"></div></div><span class="time-progress-text">${text}</span></div>`;
}
function init_data() {
getAppFilterTimeData().then(() => {
renderTimeData();
});
}
function getAppFilterTimeData() {
return new Promise((resolve, reject) => {
new XHR().get('<%=url('admin/network/get_app_filter_time')%>', null,
function (x, data) {
time_data = data.data;
console.log(data.data);
// Display current time
if (data.data.current_time) {
const currentTimeDisplay = document.getElementById('currentTimeDisplay');
const currentTimeValue = document.getElementById('currentTimeValue');
if (currentTimeDisplay && currentTimeValue) {
currentTimeValue.textContent = data.data.current_time;
currentTimeDisplay.style.display = 'block';
}
}
resolve();
}
);
});
}
function submitHandle() {
const time_data = getTimeFormData();
const maxEntries = 64;
const mode = document.getElementById('timeMode').value;
if (mode === "0") {
if (!time_data.time_list || time_data.time_list.length === 0) {
const errorContainer = document.getElementById('errorContainer');
errorContainer.innerHTML = '<%:Please add at least one time period%>';
return;
}
if (time_data.time_list.length > maxEntries) {
const errorContainer = document.getElementById('errorContainer');
errorContainer.innerHTML = '<%:Time periods cannot exceed 64 entries%>';
return;
}
// Check each time period has at least one weekday selected
for (let i = 0; i < time_data.time_list.length; i++) {
if (!time_data.time_list[i].weekday_list || time_data.time_list[i].weekday_list.length === 0) {
const errorContainer = document.getElementById('errorContainer');
errorContainer.innerHTML = '<%:Please select at least one weekday%>';
return;
}
}
} else if (mode === "1") {
// Dynamic time mode validation
if (!time_data.weekday_list || time_data.weekday_list.length === 0) {
const dynamicWeekdayErrorContainer = document.getElementById('dynamicWeekdayErrorContainer');
dynamicWeekdayErrorContainer.innerHTML = '<%:Please select at least one weekday%>';
dynamicWeekdayErrorContainer.style.display = 'block';
return;
}
if (!time_data.start_time || !time_data.end_time) {
const dynamicErrorContainer = document.getElementById('dynamicErrorContainer');
dynamicErrorContainer.innerHTML = '<%:Please set start and end time%>';
return;
}
if (time_data.start_time >= time_data.end_time) {
const dynamicErrorContainer = document.getElementById('dynamicErrorContainer');
dynamicErrorContainer.innerHTML = '<%:End time must be greater than start time%>';
return;
}
} else if (mode === "2") {
// Daily time limit mode validation
if (!time_data.daily_time_list || time_data.daily_time_list.length !== 7) {
console.error("daily_time_list is invalid");
return;
}
// Validate all enabled inputs before submission
const weekdayValues = [0, 1, 2, 3, 4, 5, 6];
let firstErrorInput = null;
for (let i = 0; i < weekdayValues.length; i++) {
const dayValue = weekdayValues[i];
const enableCheckbox = document.getElementById(`dailyLimit_${dayValue}_enable`);
if (enableCheckbox && enableCheckbox.checked) {
// Validate AM input
const amInput = document.getElementById(`dailyLimit_${dayValue}_am`);
if (amInput) {
validateDailyLimit(dayValue, 'am');
const amError = document.getElementById(`dailyLimit_${dayValue}_am_error`);
if (amError && amError.style.display !== 'none' && amError.textContent) {
if (!firstErrorInput) {
firstErrorInput = amInput;
}
}
}
// Validate PM input
const pmInput = document.getElementById(`dailyLimit_${dayValue}_pm`);
if (pmInput) {
validateDailyLimit(dayValue, 'pm');
const pmError = document.getElementById(`dailyLimit_${dayValue}_pm_error`);
if (pmError && pmError.style.display !== 'none' && pmError.textContent) {
if (!firstErrorInput) {
firstErrorInput = pmInput;
}
}
}
}
}
// If any error found, prevent submission
if (firstErrorInput) {
firstErrorInput.focus();
firstErrorInput.scrollIntoView({ behavior: 'smooth', block: 'center' });
return;
}
}
var data_str=JSON.stringify(time_data);
var xhr = new XHR();
xhr.post('<%=url('admin/network/set_app_filter_time')%>', {
data:data_str
},
function (x, data) {
const modal = document.getElementById('modal');
modal.style.display = 'flex';
setTimeout(() => {
modal.style.display = 'none';
// Refresh the entire page to ensure all data is updated
location.reload();
}, 1000);
}
);
}
function openModal() {
document.getElementById('modal').style.display = 'block';
}
function closeModal() {
document.getElementById('modal').style.display = 'none';
}
function clearActiveTime() {
if (!confirm('<%:Are you sure you want to clear active time of all users?%>')) {
return;
}
new XHR().post('<%=url('admin/network/cmd')%>', {
action: 'clear_active_time'
},
function (x, data) {
if (data && data.code === 0) {
const modal = document.getElementById('modal');
modal.style.display = 'flex';
setTimeout(() => {
modal.style.display = 'none';
}, 1000);
// Refresh time data to update display
getAppFilterTimeData().then(() => {
renderTimeData();
});
} else {
alert(data ? (data.message || '<%:Failed to clear active time%>') : '<%:Failed to clear active time%>');
}
});
}
function getAppFilterBaseData() {
new XHR().get('<%=url('admin/network/get_app_filter_base')%>', null,
function (x, data) {
if (data.data.enable == 1) {
document.getElementById('filterSwitch').checked = true;
} else {
document.getElementById('filterSwitch').checked = false;
}
document.getElementById('workMode').value = data.data.work_mode;
}
);
}
function addTimeRow() {
const tbody = document.getElementById('timeTableBody');
const row = document.createElement('tr');
const weekdayCell = document.createElement('td');
weekdayCell.innerHTML = `
<div class="weekday-selector" style="margin: 0;">
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="1"> <%:Mon%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="2"> <%:Tue%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="3"> <%:Wed%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="4"> <%:Thur%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="5"> <%:Fri%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="6"> <%:Sat%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="0"> <%:Sun%></div>
</div>
`;
row.appendChild(weekdayCell);
row.innerHTML += `
<td><input type="time" class="start-time" required></td>
<td><input type="time" class="end-time" required></td>
<td><button type="button" class="delete-time-btn" onclick="deleteTimeRow(this)"><%:Delete%></button></td>
`;
tbody.appendChild(row);
setTimeout(() => {
const startTimeInput = row.querySelector('.start-time');
const endTimeInput = row.querySelector('.end-time');
startTimeInput.value = "00:00";
endTimeInput.value = "23:59";
row.querySelectorAll('.weekday-checkbox').forEach(cb => cb.checked = true);
}, 0);
addTimeValidation();
}
function deleteTimeRow(btn) {
const row = btn.closest('tr');
row.remove();
}
function toggleModeContent() {
const mode = document.getElementById('timeMode').value;
const staticTimeContent = document.getElementById('staticTimeContent');
const dynamicTimeContent = document.getElementById('dynamicTimeContent');
const dailyTimeContent = document.getElementById('dailyTimeContent');
if (mode === "1") {
staticTimeContent.style.display = 'none';
dynamicTimeContent.style.display = 'block';
dailyTimeContent.style.display = 'none';
} else if (mode === "2") {
staticTimeContent.style.display = 'none';
dynamicTimeContent.style.display = 'none';
dailyTimeContent.style.display = 'block';
} else {
staticTimeContent.style.display = 'block';
dynamicTimeContent.style.display = 'none';
dailyTimeContent.style.display = 'none';
}
}
function getTimeFormData() {
const mode = document.getElementById('timeMode').value;
let timeData = {
mode: parseInt(mode),
weekday_list: [] // Keep for backward compatibility, but not used in mode 0
};
if (mode === "0") {
const time_list = Array.from(document.getElementById('timeTableBody').rows)
.map(row => {
const weekdays = Array.from(row.querySelectorAll('.weekday-checkbox:checked'))
.map(cb => parseInt(cb.value));
return {
start: row.querySelector('.start-time').value,
end: row.querySelector('.end-time').value,
weekday_list: weekdays
};
});
timeData.time_list = time_list;
} else if (mode === "1") {
// Dynamic mode: still use global weekday_list
const weekdays = Array.from(document.querySelectorAll('.advanced-content input[name="weekday"]:checked'))
.map(cb => parseInt(cb.value));
timeData.weekday_list = weekdays;
timeData.start_time = document.getElementById('startTime').value;
timeData.end_time = document.getElementById('endTime').value;
timeData.allow_time = parseInt(document.getElementById('allowTime').value);
timeData.deny_time = parseInt(document.getElementById('denyTime').value);
} else if (mode === "2") {
// Daily time limit mode: weekday duration settings
// Array order: 0(Sun), 1(Mon), 2(Tue), 3(Wed), 4(Thu), 5(Fri), 6(Sat)
const dailyTimeList = [];
const weekdayValues = [0, 1, 2, 3, 4, 5, 6]; // Sunday first in array
weekdayValues.forEach((dayValue) => {
const enableCheckbox = document.getElementById(`dailyLimit_${dayValue}_enable`);
const amInput = document.getElementById(`dailyLimit_${dayValue}_am`);
const pmInput = document.getElementById(`dailyLimit_${dayValue}_pm`);
if (enableCheckbox && amInput && pmInput) {
dailyTimeList.push({
enable: enableCheckbox.checked ? 1 : 0,
am_time: parseInt(amInput.value) || 0,
pm_time: parseInt(pmInput.value) || 0
});
}
});
timeData.daily_time_list = dailyTimeList;
}
return timeData;
}
function toggleAdvanced(element) {
element.classList.toggle('expanded');
element.nextElementSibling.classList.toggle('expanded');
}
function clearDynamicWeekdayError() {
const dynamicWeekdayErrorContainer = document.getElementById('dynamicWeekdayErrorContainer');
if (dynamicWeekdayErrorContainer) {
dynamicWeekdayErrorContainer.style.display = 'none';
dynamicWeekdayErrorContainer.innerHTML = '';
}
}
function addTimeValidation() {
document.querySelectorAll('.start-time, .end-time').forEach(input => {
input.addEventListener('blur', validateTime);
});
}
function validateTime(event) {
const row = event.target.closest('tr');
const startTimeInput = row.querySelector('.start-time');
const endTimeInput = row.querySelector('.end-time');
const startTime = startTimeInput.value;
const endTime = endTimeInput.value;
const errorContainer = document.getElementById('errorContainer');
errorContainer.innerHTML = '';
let errorMessage = '';
if (!isValidTimeFormat(startTime) || !isValidTimeFormat(endTime)) {
errorMessage = '<%:Invalid time format, please enter a valid time format (HH:MM)%>';
} else if (startTime >= endTime) {
errorMessage = '<%:End time must be greater than start time%>';
}
if (errorMessage) {
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.style.color = 'red';
errorElement.style.fontSize = '12px';
errorElement.textContent = errorMessage;
errorContainer.appendChild(errorElement);
}
}
function isValidTimeFormat(time) {
const timePattern = /^([01]\d|2[0-3]):([0-5]\d)$/;
return timePattern.test(time);
}
function renderTimeData() {
const timeMode = document.getElementById('timeMode');
timeMode.value = time_data.mode;
// Clear dynamic mode weekday checkboxes
document.querySelectorAll('.advanced-content input[name="weekday"]').forEach(cb => cb.checked = false);
// Set dynamic mode weekday checkboxes if weekday_list exists and is an array
if (time_data.weekday_list && Array.isArray(time_data.weekday_list)) {
time_data.weekday_list.forEach(day => {
const dynamicCheckbox = document.querySelector(`.advanced-content input[name="weekday"][value="${day}"]`);
if (dynamicCheckbox) dynamicCheckbox.checked = true;
});
}
const tbody = document.getElementById('timeTableBody');
tbody.innerHTML = '';
if (time_data.time_list) {
time_data.time_list.forEach(timeRange => {
const row = document.createElement('tr');
const weekdayCell = document.createElement('td');
weekdayCell.innerHTML = `
<div class="weekday-selector" style="margin: 0;">
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="1"> <%:Mon%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="2"> <%:Tue%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="3"> <%:Wed%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="4"> <%:Thur%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="5"> <%:Fri%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="6"> <%:Sat%></div>
<div class="weekday-item" style="display: inline-block; margin-right: 8px;"><input type="checkbox" class="weekday-checkbox" value="0"> <%:Sun%></div>
</div>
`;
row.appendChild(weekdayCell);
row.innerHTML += `
<td><input type="time" class="start-time" value="${timeRange.start}" required></td>
<td><input type="time" class="end-time" value="${timeRange.end}" required></td>
<td><button type="button" class="delete-time-btn" onclick="deleteTimeRow(this)"><%:Delete%></button></td>
`;
tbody.appendChild(row);
// Set weekday checkboxes for this time period
if (timeRange.weekday_list && timeRange.weekday_list.length > 0) {
timeRange.weekday_list.forEach(day => {
const checkbox = row.querySelector(`.weekday-checkbox[value="${day}"]`);
if (checkbox) checkbox.checked = true;
});
} else {
// If no weekday_list, use global weekday_list as fallback (for backward compatibility)
if (time_data.weekday_list && Array.isArray(time_data.weekday_list) && time_data.weekday_list.length > 0) {
time_data.weekday_list.forEach(day => {
const checkbox = row.querySelector(`.weekday-checkbox[value="${day}"]`);
if (checkbox) checkbox.checked = true;
});
} else {
// Default: select all weekdays
row.querySelectorAll('.weekday-checkbox').forEach(cb => cb.checked = true);
}
}
});
}
// Render dynamic mode data
if (time_data.start_time) {
document.getElementById('startTime').value = time_data.start_time;
}
if (time_data.end_time) {
document.getElementById('endTime').value = time_data.end_time;
}
if (time_data.allow_time) {
document.getElementById('allowTime').value = time_data.allow_time;
}
if (time_data.deny_time) {
document.getElementById('denyTime').value = time_data.deny_time;
}
// Render daily time limit mode data
// Array order: 0(Sun), 1(Mon), 2(Tue), 3(Wed), 4(Thu), 5(Fri), 6(Sat)
if (time_data.daily_time_list && Array.isArray(time_data.daily_time_list)) {
const weekdayValues = [0, 1, 2, 3, 4, 5, 6]; // Sunday first in array
weekdayValues.forEach((dayValue, index) => {
if (time_data.daily_time_list[index]) {
const enableCheckbox = document.getElementById(`dailyLimit_${dayValue}_enable`);
const amInput = document.getElementById(`dailyLimit_${dayValue}_am`);
const pmInput = document.getElementById(`dailyLimit_${dayValue}_pm`);
if (enableCheckbox) {
enableCheckbox.checked = (time_data.daily_time_list[index].enable == 1);
toggleDailyLimitInputs(dayValue);
}
if (amInput) {
amInput.value = time_data.daily_time_list[index].am_time || 0;
}
if (pmInput) {
pmInput.value = time_data.daily_time_list[index].pm_time || 0;
}
}
});
// 显示当前上网时长信息
if (time_data.current_weekday !== undefined) {
const currentWeekday = time_data.current_weekday;
const weekdayNames = ['<%:Sunday%>', '<%:Monday%>', '<%:Tuesday%>', '<%:Wednesday%>', '<%:Thursday%>', '<%:Friday%>', '<%:Saturday%>'];
const currentWeekdayName = weekdayNames[currentWeekday] || '';
const amUsed = time_data.current_am_used_time || 0;
const pmUsed = time_data.current_pm_used_time || 0;
const amLimit = time_data.current_am_limit || 0;
const pmLimit = time_data.current_pm_limit || 0;
const dayEnabled = time_data.current_day_enabled || 0;
const infoDiv = document.getElementById('currentTimeUsageInfo');
const contentDiv = document.getElementById('currentTimeUsageContent');
if (infoDiv && contentDiv) {
if (dayEnabled) {
infoDiv.style.display = 'block';
let html = `<div><strong><%:Today is%>: ${currentWeekdayName}</strong></div>`;
html += buildProgressBar('<%:AM%>', amUsed, amLimit);
html += buildProgressBar('<%:PM%>', pmUsed, pmLimit);
contentDiv.innerHTML = html;
} else {
infoDiv.style.display = 'block';
contentDiv.innerHTML = `<div><strong><%:Today is%>: ${currentWeekdayName}</strong></div><div style="margin-top: 8px; opacity: 0.6;"><%:Time limit not enabled for today%></div>`;
}
}
// 高亮当前星期几的行
const currentRow = document.querySelector(`#dailyLimit_${currentWeekday}_enable`)?.closest('tr');
if (currentRow) {
currentRow.style.backgroundColor = '#e8f0fe';
currentRow.style.fontWeight = 'bold';
}
}
}
toggleModeContent();
addTimeValidation();
}
function validateDynamicTime() {
const startTimeInput = document.getElementById('startTime');
const endTimeInput = document.getElementById('endTime');
const dynamicErrorContainer = document.getElementById('dynamicErrorContainer');
dynamicErrorContainer.innerHTML = '';
const startTime = startTimeInput.value;
const endTime = endTimeInput.value;
let errorMessage = '';
if (!isValidTimeFormat(startTime) || !isValidTimeFormat(endTime)) {
errorMessage = '<%:Invalid time format, please enter a valid time format (HH:MM)%>';
} else if (startTime >= endTime) {
errorMessage = '<%:End time must be greater than start time%>';
}
if (errorMessage) {
const errorElement = document.createElement('div');
errorElement.className = 'error-message';
errorElement.style.color = 'red';
errorElement.style.fontSize = '12px';
errorElement.textContent = errorMessage;
dynamicErrorContainer.appendChild(errorElement);
}
}
function isValidTimeFormat(time) {
const timePattern = /^([01]\d|2[0-3]):([0-5]\d)$/;
return timePattern.test(time);
}
function toggleDailyLimitInputs(weekday) {
const enableCheckbox = document.getElementById(`dailyLimit_${weekday}_enable`);
const amInput = document.getElementById(`dailyLimit_${weekday}_am`);
const pmInput = document.getElementById(`dailyLimit_${weekday}_pm`);
if (enableCheckbox && amInput && pmInput) {
const isEnabled = enableCheckbox.checked;
amInput.disabled = !isEnabled;
pmInput.disabled = !isEnabled;
// Clear errors when disabled
if (!isEnabled) {
const amError = document.getElementById(`dailyLimit_${weekday}_am_error`);
const pmError = document.getElementById(`dailyLimit_${weekday}_pm_error`);
if (amError) {
amError.style.display = 'none';
amError.textContent = '';
}
if (pmError) {
pmError.style.display = 'none';
pmError.textContent = '';
}
}
}
}
function validateDailyLimit(weekday, period) {
const input = document.getElementById(`dailyLimit_${weekday}_${period}`);
const errorSpan = document.getElementById(`dailyLimit_${weekday}_${period}_error`);
if (!input || !errorSpan) return;
// Check if input is empty
if (input.value === '' || input.value === null || input.value === undefined) {
errorSpan.textContent = '<%:Value cannot be empty%>';
errorSpan.style.display = 'block';
input.style.borderColor = 'red';
return;
}
const value = parseInt(input.value);
const maxMinutes = 720; // 12 hours = 720 minutes
if (isNaN(value)) {
errorSpan.textContent = '<%:Please enter a valid number%>';
errorSpan.style.display = 'block';
input.style.borderColor = 'red';
} else if (value < 0) {
errorSpan.textContent = '<%:Value cannot be negative%>';
errorSpan.style.display = 'block';
input.style.borderColor = 'red';
} else if (value > maxMinutes) {
errorSpan.textContent = '<%:Value cannot exceed 720 minutes (12 hours)%>';
errorSpan.style.display = 'block';
input.style.borderColor = 'red';
} else {
errorSpan.style.display = 'none';
errorSpan.textContent = '';
input.style.borderColor = '';
}
}
window.onload = init_data;
//]]></script>
<div id="modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; justify-content: center; align-items: center;">
<div style="background-color: rgba(0, 0, 0, 0.5); padding: 10px; border-radius: 5px; text-align: center; width: 100px; height: 70px; color: white; display: flex; justify-content: center; align-items: center;">
<p style="margin: 0;color: white;"><%:Settings saved successfully%></p>
</div>
</div>
<div class="cbi-section cbi-tblsection">
<div style="max-width: 1000px; margin-left: 5px;padding: 5px;">
<div class="time-rules">
<h3><%:Time Rules%></h3>
<div id="currentTimeDisplay" style="margin-bottom: 12px; font-size: 13px; opacity: 0.7; display: none;">
<%:Current Time%>: <span id="currentTimeValue"></span>
</div>
<div class="mode-selector" style="margin-bottom: 15px;">
<label><%:Mode Selection%></label>
<select id="timeMode" onchange="toggleModeContent()">
<option value="0"><%:Fixed Time%></option>
<option value="1"><%:Dynamic Time%></option>
<option value="2"><%:Daily Time Limit%></option>
</select>
</div>
<div id="staticTimeContent">
<table class="time-table">
<thead>
<tr>
<th><%:Weekdays%></th>
<th><%:Start Time%></th>
<th><%:End Time%></th>
<th><%:Actions%></th>
</tr>
</thead>
<tbody id="timeTableBody">
</tbody>
</table>
<div id="errorContainer" style="color: red; font-size: 12px; margin-top: 10px;"></div>
<button class="add-time-btn" onclick="addTimeRow()"><%:Add Time Period%></button>
</div>
<div id="dynamicTimeContent" style="display: none;">
<div style="opacity: 0.6; font-size: 13px; margin-bottom: 15px;">
<%:Dynamic time mode refers to dynamically adjusting app filter switches, such as allowing children to play games for 20 minutes after studying for 1 hour, automatically enabling filtering after exceeding entertainment time, and repeating the cycle.%>
</div>
<div class="dynamic-time-settings">
<div class="setting-item">
<label><%:Enable Duration Each Time (Study Time)%>:</label>
<input type="number" id="denyTime" min="1" value="60">
<span><%:minutes%></span>
</div>
<div class="setting-item" style="margin-bottom: 15px;">
<label><%:Disable Duration Each Time (Break Time)%>:</label>
<input type="number" id="allowTime" min="1" value="10">
<span><%:minutes%></span>
</div>
<div class="advanced-settings">
<div class="advanced-toggle expanded" onclick="toggleAdvanced(this)">
<span class="arrow"></span>
<span><%:Advanced Settings%></span>
</div>
<div class="advanced-content expanded">
<div class="weekday-selector" style="margin-bottom: 15px;">
<label><%:Weekday Selection%></label>
<div class="weekday-item"><input type="checkbox" name="weekday" value="1" onchange="clearDynamicWeekdayError()"> <%:Mon%></div>
<div class="weekday-item"><input type="checkbox" name="weekday" value="2" onchange="clearDynamicWeekdayError()"> <%:Tue%></div>
<div class="weekday-item"><input type="checkbox" name="weekday" value="3" onchange="clearDynamicWeekdayError()"> <%:Wed%></div>
<div class="weekday-item"><input type="checkbox" name="weekday" value="4" onchange="clearDynamicWeekdayError()"> <%:Thur%></div>
<div class="weekday-item"><input type="checkbox" name="weekday" value="5" onchange="clearDynamicWeekdayError()"> <%:Fri%></div>
<div class="weekday-item"><input type="checkbox" name="weekday" value="6" onchange="clearDynamicWeekdayError()"> <%:Sat%></div>
<div class="weekday-item"><input type="checkbox" name="weekday" value="0" onchange="clearDynamicWeekdayError()"> <%:Sun%></div>
</div>
<div id="dynamicWeekdayErrorContainer" style="color: red; font-size: 12px; margin-top: 5px; display: none;"></div>
<div class="setting-item">
<label><%:Daily Study Time Range%></label>
<input type="time" id="startTime" required style="margin-right: 5px;" onchange="validateDynamicTime()">
<span>-</span>
<input type="time" id="endTime" required style="margin-left: 5px;" onchange="validateDynamicTime()">
</div>
<div class="setting-item" style="opacity: 0.6; font-size: 12px; margin-top: 5px;">
<span><%:This time range represents the daily school time period, which is a continuous time period. During this time period, filtering rules are dynamically enabled and disabled. Outside the time range, filtering is disabled by default.%></span>
</div>
</div>
</div>
</div>
<div id="dynamicErrorContainer" style="color: red; font-size: 12px; margin-top: 10px;"></div>
</div>
<div id="dailyTimeContent" style="display: none;">
<div style="opacity: 0.6; font-size: 13px; margin-bottom: 15px;">
<%:Daily time limit mode allows you to set a daily internet time limit for each weekday. Once the limit is reached, internet access will be restricted.%>
</div>
<!-- 当前上网时长信息显示 -->
<div id="currentTimeUsageInfo" style="border: 1px solid #1a73e8; border-radius: 5px; padding: 15px; margin-bottom: 15px; display: none;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<div style="font-weight: bold; color: #1a73e8; font-size: 14px;">
<%:Today Time Usage%>
</div>
<button type="button" class="clear-time-btn" onclick="clearActiveTime()"><%:Clear%></button>
</div>
<div id="currentTimeUsageContent" style="font-size: 13px; line-height: 1.8;">
<!-- 内容将通过JavaScript填充 -->
</div>
</div>
<table class="time-table" style="margin-top: 10px;">
<thead>
<tr>
<th style="width: 15%;"><%:Weekday%></th>
<th style="width: 15%;"><%:Enable%></th>
<th style="width: 35%;"><%:Morning Time Limit (minutes)%></th>
<th style="width: 35%;"><%:Afternoon Time Limit (minutes)%></th>
</tr>
</thead>
<tbody>
<tr>
<td><%:Monday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_1_enable" onchange="toggleDailyLimitInputs(1)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_1_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(1, 'am')">
<div id="dailyLimit_1_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_1_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(1, 'pm')">
<div id="dailyLimit_1_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
<tr>
<td><%:Tuesday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_2_enable" onchange="toggleDailyLimitInputs(2)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_2_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(2, 'am')">
<div id="dailyLimit_2_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_2_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(2, 'pm')">
<div id="dailyLimit_2_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
<tr>
<td><%:Wednesday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_3_enable" onchange="toggleDailyLimitInputs(3)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_3_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(3, 'am')">
<div id="dailyLimit_3_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_3_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(3, 'pm')">
<div id="dailyLimit_3_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
<tr>
<td><%:Thursday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_4_enable" onchange="toggleDailyLimitInputs(4)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_4_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(4, 'am')">
<div id="dailyLimit_4_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_4_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(4, 'pm')">
<div id="dailyLimit_4_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
<tr>
<td><%:Friday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_5_enable" onchange="toggleDailyLimitInputs(5)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_5_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(5, 'am')">
<div id="dailyLimit_5_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_5_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(5, 'pm')">
<div id="dailyLimit_5_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
<tr>
<td><%:Saturday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_6_enable" onchange="toggleDailyLimitInputs(6)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_6_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(6, 'am')">
<div id="dailyLimit_6_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_6_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(6, 'pm')">
<div id="dailyLimit_6_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
<tr>
<td><%:Sunday%></td>
<td><label class="switch"><input type="checkbox" id="dailyLimit_0_enable" onchange="toggleDailyLimitInputs(0)"><span class="slider"></span></label></td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_0_am" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(0, 'am')">
<div id="dailyLimit_0_am_error" class="daily-limit-error"></div>
</div>
</td>
<td>
<div class="daily-limit-input-wrapper">
<input type="number" id="dailyLimit_0_pm" min="0" max="720" value="0" style="padding: 5px;" disabled onblur="validateDailyLimit(0, 'pm')">
<div id="dailyLimit_0_pm_error" class="daily-limit-error"></div>
</div>
</td>
</tr>
</tbody>
</table>
<div style="color: #000; font-size: 12px; margin-top: 10px;">
<span><%:Note: Set to 0 to disable time limit for that period.%></span>
</div>
</div>
</div>
<div style="margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px;">
<button type="button" class="submit-button" onclick="location.reload()" style="background: transparent; color: #1a73e8; border: 1px solid var(--border-color-medium, #d1d5db);"><%:Refresh%></button>
<button type="button" class="submit-button" onclick="submitHandle()"><%:Save%></button>
</div>
</div>
</div>