更新Swagger API文档
This commit is contained in:
422
js/modules/rules.js
Normal file
422
js/modules/rules.js
Normal file
@@ -0,0 +1,422 @@
|
||||
// 屏蔽规则管理模块
|
||||
|
||||
// 全局变量
|
||||
let rules = [];
|
||||
let currentPage = 1;
|
||||
let itemsPerPage = 50; // 默认每页显示50条规则
|
||||
let filteredRules = [];
|
||||
|
||||
// 初始化屏蔽规则面板
|
||||
function initRulesPanel() {
|
||||
// 加载规则列表
|
||||
loadRules();
|
||||
|
||||
// 绑定添加规则按钮事件
|
||||
document.getElementById('add-rule-btn').addEventListener('click', addNewRule);
|
||||
|
||||
// 绑定刷新规则按钮事件
|
||||
document.getElementById('reload-rules-btn').addEventListener('click', reloadRules);
|
||||
|
||||
// 绑定搜索框事件
|
||||
document.getElementById('rule-search').addEventListener('input', filterRules);
|
||||
|
||||
// 绑定每页显示数量变更事件
|
||||
document.getElementById('items-per-page').addEventListener('change', () => {
|
||||
itemsPerPage = parseInt(document.getElementById('items-per-page').value);
|
||||
currentPage = 1; // 重置为第一页
|
||||
renderRulesList();
|
||||
});
|
||||
|
||||
// 绑定分页按钮事件
|
||||
document.getElementById('prev-page-btn').addEventListener('click', goToPreviousPage);
|
||||
document.getElementById('next-page-btn').addEventListener('click', goToNextPage);
|
||||
document.getElementById('first-page-btn').addEventListener('click', goToFirstPage);
|
||||
document.getElementById('last-page-btn').addEventListener('click', goToLastPage);
|
||||
}
|
||||
|
||||
// 加载规则列表
|
||||
async function loadRules() {
|
||||
try {
|
||||
const rulesPanel = document.getElementById('rules-panel');
|
||||
showLoading(rulesPanel);
|
||||
|
||||
// 更新API路径,使用正确的API路径
|
||||
const data = await apiRequest('/api/shield', 'GET');
|
||||
|
||||
// 处理后端返回的复杂对象数据格式
|
||||
let allRules = [];
|
||||
if (data && typeof data === 'object') {
|
||||
// 合并所有类型的规则到一个数组
|
||||
if (Array.isArray(data.domainRules)) allRules = allRules.concat(data.domainRules);
|
||||
if (Array.isArray(data.domainExceptions)) allRules = allRules.concat(data.domainExceptions);
|
||||
if (Array.isArray(data.regexRules)) allRules = allRules.concat(data.regexRules);
|
||||
if (Array.isArray(data.regexExceptions)) allRules = allRules.concat(data.regexExceptions);
|
||||
}
|
||||
|
||||
rules = allRules;
|
||||
filteredRules = [...rules];
|
||||
currentPage = 1; // 重置为第一页
|
||||
renderRulesList();
|
||||
|
||||
// 更新规则数量统计卡片
|
||||
if (window.updateRulesCount && typeof window.updateRulesCount === 'function') {
|
||||
window.updateRulesCount(rules.length);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载规则失败:', error);
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('加载规则失败', 'danger');
|
||||
}
|
||||
} finally {
|
||||
const rulesPanel = document.getElementById('rules-panel');
|
||||
hideLoading(rulesPanel);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染规则列表
|
||||
function renderRulesList() {
|
||||
const rulesList = document.getElementById('rules-list');
|
||||
const paginationInfo = document.getElementById('pagination-info');
|
||||
|
||||
// 清空列表
|
||||
rulesList.innerHTML = '';
|
||||
|
||||
if (filteredRules.length === 0) {
|
||||
// 使用更友好的空状态显示
|
||||
rulesList.innerHTML = '<tr><td colspan="4" class="text-center py-4">' +
|
||||
'<div class="empty-state">' +
|
||||
'<div class="empty-icon"><i class="fas fa-shield-alt text-muted"></i></div>' +
|
||||
'<div class="empty-title text-muted">暂无规则</div>' +
|
||||
'<div class="empty-description text-muted">点击添加按钮或刷新规则来获取规则列表</div>' +
|
||||
'</div>' +
|
||||
'</td></tr>';
|
||||
paginationInfo.textContent = '共0条规则';
|
||||
updatePaginationButtons();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算分页数据
|
||||
const totalPages = Math.ceil(filteredRules.length / itemsPerPage);
|
||||
const startIndex = (currentPage - 1) * itemsPerPage;
|
||||
const endIndex = Math.min(startIndex + itemsPerPage, filteredRules.length);
|
||||
const currentRules = filteredRules.slice(startIndex, endIndex);
|
||||
|
||||
// 渲染当前页的规则
|
||||
currentRules.forEach((rule, index) => {
|
||||
const row = document.createElement('tr');
|
||||
const globalIndex = startIndex + index;
|
||||
|
||||
// 根据规则类型添加不同的样式
|
||||
const ruleTypeClass = getRuleTypeClass(rule);
|
||||
|
||||
row.innerHTML = `
|
||||
<td class="rule-id">${globalIndex + 1}</td>
|
||||
<td class="rule-content ${ruleTypeClass}"><pre>${escapeHtml(rule)}</pre></td>
|
||||
<td class="rule-actions">
|
||||
<button class="btn btn-danger btn-sm delete-rule" data-index="${globalIndex}">
|
||||
<i class="fas fa-trash"></i> 删除
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
|
||||
// 添加行动画效果
|
||||
row.style.opacity = '0';
|
||||
row.style.transform = 'translateY(10px)';
|
||||
rulesList.appendChild(row);
|
||||
|
||||
// 使用requestAnimationFrame确保动画平滑
|
||||
requestAnimationFrame(() => {
|
||||
row.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
|
||||
row.style.opacity = '1';
|
||||
row.style.transform = 'translateY(0)';
|
||||
});
|
||||
});
|
||||
|
||||
// 绑定删除按钮事件
|
||||
document.querySelectorAll('.delete-rule').forEach(button => {
|
||||
button.addEventListener('click', (e) => {
|
||||
const index = parseInt(e.currentTarget.dataset.index);
|
||||
deleteRule(index);
|
||||
});
|
||||
});
|
||||
|
||||
// 更新分页信息
|
||||
paginationInfo.textContent = `显示 ${startIndex + 1}-${endIndex} 条,共 ${filteredRules.length} 条规则,第 ${currentPage}/${totalPages} 页`;
|
||||
|
||||
// 更新分页按钮状态
|
||||
updatePaginationButtons();
|
||||
}
|
||||
|
||||
// 更新分页按钮状态
|
||||
function updatePaginationButtons() {
|
||||
const totalPages = Math.ceil(filteredRules.length / itemsPerPage);
|
||||
const prevBtn = document.getElementById('prev-page-btn');
|
||||
const nextBtn = document.getElementById('next-page-btn');
|
||||
const firstBtn = document.getElementById('first-page-btn');
|
||||
const lastBtn = document.getElementById('last-page-btn');
|
||||
|
||||
prevBtn.disabled = currentPage === 1;
|
||||
nextBtn.disabled = currentPage === totalPages || totalPages === 0;
|
||||
firstBtn.disabled = currentPage === 1;
|
||||
lastBtn.disabled = currentPage === totalPages || totalPages === 0;
|
||||
}
|
||||
|
||||
// 上一页
|
||||
function goToPreviousPage() {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
renderRulesList();
|
||||
}
|
||||
}
|
||||
|
||||
// 下一页
|
||||
function goToNextPage() {
|
||||
const totalPages = Math.ceil(filteredRules.length / itemsPerPage);
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
renderRulesList();
|
||||
}
|
||||
}
|
||||
|
||||
// 第一页
|
||||
function goToFirstPage() {
|
||||
currentPage = 1;
|
||||
renderRulesList();
|
||||
}
|
||||
|
||||
// 最后一页
|
||||
function goToLastPage() {
|
||||
currentPage = Math.ceil(filteredRules.length / itemsPerPage);
|
||||
renderRulesList();
|
||||
}
|
||||
|
||||
// 添加新规则
|
||||
async function addNewRule() {
|
||||
const ruleInput = document.getElementById('rule-input');
|
||||
const rule = ruleInput.value.trim();
|
||||
|
||||
if (!rule) {
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('请输入规则内容', 'warning');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 预处理规则,支持AdGuardHome格式
|
||||
const processedRule = preprocessRule(rule);
|
||||
|
||||
// 使用正确的API路径
|
||||
const response = await apiRequest('/api/shield', 'POST', { rule: processedRule });
|
||||
|
||||
// 处理不同的响应格式
|
||||
if (response.success || response.status === 'success') {
|
||||
rules.push(processedRule);
|
||||
filteredRules = [...rules];
|
||||
ruleInput.value = '';
|
||||
|
||||
// 添加后跳转到最后一页,显示新添加的规则
|
||||
currentPage = Math.ceil(filteredRules.length / itemsPerPage);
|
||||
renderRulesList();
|
||||
|
||||
// 更新规则数量统计
|
||||
if (window.updateRulesCount && typeof window.updateRulesCount === 'function') {
|
||||
window.updateRulesCount(rules.length);
|
||||
}
|
||||
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('规则添加成功', 'success');
|
||||
}
|
||||
} else {
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('规则添加失败:' + (response.message || '未知错误'), 'danger');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('添加规则失败:', error);
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('添加规则失败', 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除规则
|
||||
async function deleteRule(index) {
|
||||
if (!confirm('确定要删除这条规则吗?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const rule = filteredRules[index];
|
||||
const rowElement = document.querySelectorAll('#rules-list tr')[index];
|
||||
|
||||
// 添加删除动画
|
||||
if (rowElement) {
|
||||
rowElement.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
|
||||
rowElement.style.opacity = '0';
|
||||
rowElement.style.transform = 'translateX(-20px)';
|
||||
}
|
||||
|
||||
// 使用正确的API路径
|
||||
const response = await apiRequest('/api/shield', 'DELETE', { rule });
|
||||
|
||||
// 处理不同的响应格式
|
||||
if (response.success || response.status === 'success') {
|
||||
// 在原规则列表中找到并删除
|
||||
const originalIndex = rules.indexOf(rule);
|
||||
if (originalIndex !== -1) {
|
||||
rules.splice(originalIndex, 1);
|
||||
}
|
||||
|
||||
// 在过滤后的列表中删除
|
||||
filteredRules.splice(index, 1);
|
||||
|
||||
// 如果当前页没有数据了,回到上一页
|
||||
const totalPages = Math.ceil(filteredRules.length / itemsPerPage);
|
||||
if (currentPage > totalPages && totalPages > 0) {
|
||||
currentPage = totalPages;
|
||||
}
|
||||
|
||||
// 等待动画完成后重新渲染列表
|
||||
setTimeout(() => {
|
||||
renderRulesList();
|
||||
|
||||
// 更新规则数量统计
|
||||
if (window.updateRulesCount && typeof window.updateRulesCount === 'function') {
|
||||
window.updateRulesCount(rules.length);
|
||||
}
|
||||
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('规则删除成功', 'success');
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
// 恢复行样式
|
||||
if (rowElement) {
|
||||
rowElement.style.opacity = '1';
|
||||
rowElement.style.transform = 'translateX(0)';
|
||||
}
|
||||
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('规则删除失败:' + (response.message || '未知错误'), 'danger');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除规则失败:', error);
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('删除规则失败', 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载规则
|
||||
async function reloadRules() {
|
||||
if (!confirm('确定要重新加载所有规则吗?这将覆盖当前内存中的规则。')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const rulesPanel = document.getElementById('rules-panel');
|
||||
showLoading(rulesPanel);
|
||||
|
||||
// 使用正确的API路径和方法 - PUT请求到/api/shield
|
||||
await apiRequest('/api/shield', 'PUT');
|
||||
|
||||
// 重新加载规则列表
|
||||
await loadRules();
|
||||
|
||||
// 触发数据刷新事件,通知其他模块数据已更新
|
||||
if (typeof window.triggerDataRefresh === 'function') {
|
||||
window.triggerDataRefresh('rules');
|
||||
}
|
||||
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('规则重新加载成功', 'success');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('重新加载规则失败:', error);
|
||||
if (typeof window.showNotification === 'function') {
|
||||
window.showNotification('重新加载规则失败', 'danger');
|
||||
}
|
||||
} finally {
|
||||
const rulesPanel = document.getElementById('rules-panel');
|
||||
hideLoading(rulesPanel);
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤规则
|
||||
function filterRules() {
|
||||
const searchTerm = document.getElementById('rule-search').value.toLowerCase();
|
||||
|
||||
if (searchTerm) {
|
||||
filteredRules = rules.filter(rule => rule.toLowerCase().includes(searchTerm));
|
||||
} else {
|
||||
filteredRules = [...rules];
|
||||
}
|
||||
|
||||
currentPage = 1; // 重置为第一页
|
||||
renderRulesList();
|
||||
}
|
||||
|
||||
// HTML转义,防止XSS攻击
|
||||
function escapeHtml(text) {
|
||||
const map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.replace(/[&<>'"]/g, m => map[m]);
|
||||
}
|
||||
|
||||
// 根据规则类型返回对应的CSS类名
|
||||
function getRuleTypeClass(rule) {
|
||||
// 简单的规则类型判断
|
||||
if (rule.startsWith('||') || rule.startsWith('|http')) {
|
||||
return 'rule-type-url';
|
||||
} else if (rule.startsWith('@@')) {
|
||||
return 'rule-type-exception';
|
||||
} else if (rule.startsWith('#')) {
|
||||
return 'rule-type-comment';
|
||||
} else if (rule.includes('$')) {
|
||||
return 'rule-type-filter';
|
||||
}
|
||||
return 'rule-type-standard';
|
||||
}
|
||||
|
||||
// 预处理规则,支持多种规则格式
|
||||
function preprocessRule(rule) {
|
||||
// 移除首尾空白字符
|
||||
let processed = rule.trim();
|
||||
|
||||
// 处理AdGuardHome格式的规则
|
||||
if (processed.startsWith('0.0.0.0 ') || processed.startsWith('127.0.0.1 ')) {
|
||||
const parts = processed.split(' ');
|
||||
if (parts.length >= 2) {
|
||||
// 转换为AdBlock Plus格式
|
||||
processed = '||' + parts[1] + '^';
|
||||
}
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
// 导出函数,供其他模块调用
|
||||
window.updateRulesCount = function(count) {
|
||||
const rulesCountElement = document.getElementById('rules-count');
|
||||
if (rulesCountElement) {
|
||||
rulesCountElement.textContent = count;
|
||||
}
|
||||
}
|
||||
|
||||
// 导出初始化函数
|
||||
window.initRulesPanel = initRulesPanel;
|
||||
|
||||
// 注册到面板导航系统
|
||||
if (window.registerPanelModule) {
|
||||
window.registerPanelModule('rules-panel', {
|
||||
init: initRulesPanel,
|
||||
refresh: loadRules
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user