feat: 添加Cookie搜索和分组功能
- 添加搜索框支持按Tag名称实时过滤 - 根据Tag冒号前缀自动分组(格式: xx1:xxxx2) - 支持分组折叠/展开功能 - 分组显示数量统计 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8a57945cd2
commit
3bda268cb8
1 file changed
+175
-12
+175
-12
@@ -377,6 +377,80 @@
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 40px 12px 16px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
color: #999;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-header {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 15px 20px;
|
||||||
|
margin: 15px 0 10px 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-header:hover {
|
||||||
|
background: #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-count {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-toggle {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #667eea;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-toggle.collapsed {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-content.collapsed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.api-docs {
|
.api-docs {
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -443,7 +517,12 @@
|
|||||||
<div class="data-list">
|
<div class="data-list">
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
|
||||||
<h2 style="margin: 0;">📋 数据列表</h2>
|
<h2 style="margin: 0;">📋 数据列表</h2>
|
||||||
<button class="btn btn-primary" onclick="loadDataList()">🔄 刷新</button>
|
<button class="btn btn-primary" onclick="loadData()">🔄 刷新</button>
|
||||||
|
</div>
|
||||||
|
<!-- Search Box -->
|
||||||
|
<div class="search-box">
|
||||||
|
<input type="text" class="search-input" id="search-input" placeholder="🔍 搜索 Tag..." oninput="filterData()">
|
||||||
|
<span class="search-icon">🔍</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="data-container">
|
<div id="data-container">
|
||||||
<div class="loading">正在加载...</div>
|
<div class="loading">正在加载...</div>
|
||||||
@@ -516,6 +595,8 @@
|
|||||||
<script>
|
<script>
|
||||||
let currentViewData = null;
|
let currentViewData = null;
|
||||||
let deleteCallback = null;
|
let deleteCallback = null;
|
||||||
|
let allDataItems = []; // 存储所有数据项
|
||||||
|
let collapsedGroups = new Set(); // 存储折叠的分组
|
||||||
|
|
||||||
// Toast 通知
|
// Toast 通知
|
||||||
function showToast(message, type = 'success') {
|
function showToast(message, type = 'success') {
|
||||||
@@ -615,15 +696,65 @@
|
|||||||
document.getElementById('warning-count').textContent = warningCount;
|
document.getElementById('warning-count').textContent = warningCount;
|
||||||
document.getElementById('expired-count').textContent = expiredCount;
|
document.getElementById('expired-count').textContent = expiredCount;
|
||||||
|
|
||||||
|
// 保存所有数据项
|
||||||
|
allDataItems = tagArray;
|
||||||
|
|
||||||
// 渲染列表
|
// 渲染列表
|
||||||
const container = document.getElementById('data-container');
|
renderDataList(tagArray);
|
||||||
|
|
||||||
if (tagArray.length === 0) {
|
} catch (error) {
|
||||||
container.innerHTML = '<div class="empty">📭 暂无数据</div>';
|
console.error('加载失败:', error);
|
||||||
return;
|
document.getElementById('data-container').innerHTML =
|
||||||
|
'<div class="empty">❌ 加载失败,请刷新重试</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染数据列表(支持分组)
|
||||||
|
function renderDataList(items) {
|
||||||
|
const container = document.getElementById('data-container');
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
container.innerHTML = '<div class="empty">📭 暂无数据</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按分组组织数据
|
||||||
|
const groups = {};
|
||||||
|
items.forEach(item => {
|
||||||
|
const colonIndex = item.tag.indexOf(':');
|
||||||
|
const groupName = colonIndex > 0 ? item.tag.substring(0, colonIndex) : '未分组';
|
||||||
|
|
||||||
|
if (!groups[groupName]) {
|
||||||
|
groups[groupName] = [];
|
||||||
}
|
}
|
||||||
|
groups[groupName].push(item);
|
||||||
|
});
|
||||||
|
|
||||||
container.innerHTML = tagArray.map((item) => {
|
// 按分组名称排序
|
||||||
|
const sortedGroupNames = Object.keys(groups).sort((a, b) => {
|
||||||
|
if (a === '未分组') return 1;
|
||||||
|
if (b === '未分组') return -1;
|
||||||
|
return a.localeCompare(b);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 渲染分组
|
||||||
|
let html = '';
|
||||||
|
sortedGroupNames.forEach(groupName => {
|
||||||
|
const groupItems = groups[groupName];
|
||||||
|
const isCollapsed = collapsedGroups.has(groupName);
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<div class="group-header" onclick="toggleGroup('${groupName.replace(/'/g, "\\'")}')">
|
||||||
|
<div>
|
||||||
|
<span class="group-title">${groupName}</span>
|
||||||
|
<span class="group-count">(${groupItems.length})</span>
|
||||||
|
</div>
|
||||||
|
<span class="group-toggle ${isCollapsed ? 'collapsed' : ''}">▼</span>
|
||||||
|
</div>
|
||||||
|
<div class="group-content ${isCollapsed ? 'collapsed' : ''}" id="group-${groupName}">
|
||||||
|
`;
|
||||||
|
|
||||||
|
groupItems.forEach(item => {
|
||||||
const statusClass = `status-${item.status}`;
|
const statusClass = `status-${item.status}`;
|
||||||
const statusText = {
|
const statusText = {
|
||||||
'valid': '正常',
|
'valid': '正常',
|
||||||
@@ -633,7 +764,7 @@
|
|||||||
|
|
||||||
const escapedTag = item.tag.replace(/'/g, "\\'").replace(/"/g, '\\"');
|
const escapedTag = item.tag.replace(/'/g, "\\'").replace(/"/g, '\\"');
|
||||||
|
|
||||||
return `
|
html += `
|
||||||
<div class="data-item">
|
<div class="data-item">
|
||||||
<div class="data-info">
|
<div class="data-info">
|
||||||
<div class="data-tag">
|
<div class="data-tag">
|
||||||
@@ -654,13 +785,45 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
});
|
||||||
|
|
||||||
} catch (error) {
|
html += '</div>';
|
||||||
console.error('加载失败:', error);
|
});
|
||||||
document.getElementById('data-container').innerHTML =
|
|
||||||
'<div class="empty">❌ 加载失败,请刷新重试</div>';
|
container.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换分组折叠/展开
|
||||||
|
function toggleGroup(groupName) {
|
||||||
|
if (collapsedGroups.has(groupName)) {
|
||||||
|
collapsedGroups.delete(groupName);
|
||||||
|
} else {
|
||||||
|
collapsedGroups.add(groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重新渲染当前显示的数据
|
||||||
|
const searchTerm = document.getElementById('search-input').value.trim().toLowerCase();
|
||||||
|
if (searchTerm) {
|
||||||
|
filterData();
|
||||||
|
} else {
|
||||||
|
renderDataList(allDataItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索过滤
|
||||||
|
function filterData() {
|
||||||
|
const searchTerm = document.getElementById('search-input').value.trim().toLowerCase();
|
||||||
|
|
||||||
|
if (!searchTerm) {
|
||||||
|
renderDataList(allDataItems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredItems = allDataItems.filter(item =>
|
||||||
|
item.tag.toLowerCase().includes(searchTerm)
|
||||||
|
);
|
||||||
|
|
||||||
|
renderDataList(filteredItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看数据
|
// 查看数据
|
||||||
|
|||||||
Reference in new issue
Block a user