v1.4.2: 修复日报/定制汇总重复发送刷屏(_maybe_daily元组嵌套bug+发送日期持久化) + 一键全部启用/停用 + 数据源快速筛选
This commit is contained in:
+58
-3
@@ -4,15 +4,37 @@
|
||||
{% block content %}
|
||||
<div class="page-head">
|
||||
<h1>📡 数据源管理</h1>
|
||||
<button class="btn accent" onclick="openModal()">+ 新增数据源</button>
|
||||
<div class="actions">
|
||||
<button class="btn" onclick="setAllSources(1)">✅ 全部启用</button>
|
||||
<button class="btn" onclick="setAllSources(0)">⏸ 全部停用</button>
|
||||
<button class="btn accent" onclick="openModal()">+ 新增数据源</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-bottom:14px;">
|
||||
<div class="form-row" style="justify-content:flex-start;gap:10px;flex-wrap:wrap;">
|
||||
<input id="f_keyword" placeholder="🔍 搜索名称 / 类型 / URL / 描述..."
|
||||
style="flex:1;min-width:220px;" oninput="applyFilter()">
|
||||
<select id="f_kind" onchange="applyFilter()" style="width:150px;">
|
||||
<option value="">全部监控方式</option>
|
||||
<option value="normal">普通数据源</option>
|
||||
<option value="custom">🎯 定制监控</option>
|
||||
</select>
|
||||
<select id="f_enabled" onchange="applyFilter()" style="width:120px;">
|
||||
<option value="">全部状态</option>
|
||||
<option value="1">启用</option>
|
||||
<option value="0">停用</option>
|
||||
</select>
|
||||
<button class="btn mini" onclick="clearFilter()">重置</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<table>
|
||||
<table id="srcTable">
|
||||
<thead><tr><th>ID</th><th>名称</th><th>监控方式</th><th>获取方式</th><th>类型</th><th>权重</th><th>采集周期</th><th>状态</th><th>最近采样</th><th>条数</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
{% for s in sources %}
|
||||
<tr data-sid="{{ s.id }}" data-name="{{ s.name }}" data-type="{{ s.type }}" data-url="{{ s.url }}" data-desc="{{ s.description }}" data-weight="{{ s.weight }}" data-kind="{{ s.kind }}" data-standard="{{ s.monitor_standard or '' }}" data-interval="{{ s.scan_interval_min or 0 }}" data-fetch="{{ s.fetch_method or 'auto' }}" data-capture="{{ s.capture_params or '{}' }}">
|
||||
<tr data-sid="{{ s.id }}" data-enabled="{{ s.enabled }}" data-name="{{ s.name }}" data-type="{{ s.type }}" data-url="{{ s.url }}" data-desc="{{ s.description }}" data-weight="{{ s.weight }}" data-kind="{{ s.kind }}" data-standard="{{ s.monitor_standard or '' }}" data-interval="{{ s.scan_interval_min or 0 }}" data-fetch="{{ s.fetch_method or 'auto' }}" data-capture="{{ s.capture_params or '{}' }}">
|
||||
<td>{{ s.id }}</td>
|
||||
<td><b>{{ s.name }}</b><br><span class="muted small">{{ s.description }}</span><br><span class="muted small" style="word-break:break-all;">{{ s.url }}</span></td>
|
||||
<td>{% if s.kind == 'custom' %}<span class="tag hot">🎯 定制监控</span>{% else %}<span class="tag ok">普通</span>{% endif %}</td>
|
||||
@@ -304,11 +326,14 @@ async function toggleSource(id, cb){
|
||||
const r = await API.json('/api/sources', {action:'toggle', id});
|
||||
if (!r.ok){ toast('操作失败', false); cb.checked = orig; return; }
|
||||
const on = cb.checked;
|
||||
const row = document.querySelector(`tr[data-sid="${id}"]`);
|
||||
if (row) row.setAttribute('data-enabled', on ? '1' : '0');
|
||||
const tag = document.getElementById('st-'+id);
|
||||
if (tag){ tag.textContent = on ? '启用' : '停用'; tag.className = 'tag ' + (on ? 'ok' : ''); }
|
||||
const nameEl = document.querySelector(`tr[data-sid="${id}"] b`);
|
||||
const name = nameEl ? nameEl.textContent : '';
|
||||
toast((on ? '✅ 已启用「' : '⏸ 已停用「') + name + '」', true);
|
||||
applyFilter();
|
||||
}
|
||||
function toggleFromButton(id){
|
||||
const cb = document.querySelector(`tr[data-sid="${id}"] .switch input`);
|
||||
@@ -316,6 +341,36 @@ function toggleFromButton(id){
|
||||
cb.checked = !cb.checked;
|
||||
toggleSource(id, cb);
|
||||
}
|
||||
|
||||
// ---------- 快速筛选 ----------
|
||||
function applyFilter(){
|
||||
const kw = (document.getElementById('f_keyword').value || '').trim().toLowerCase();
|
||||
const kind = document.getElementById('f_kind').value;
|
||||
const en = document.getElementById('f_enabled').value;
|
||||
document.querySelectorAll('#srcTable tbody tr').forEach(tr => {
|
||||
const text = ((tr.dataset.name||'') + ' ' + (tr.dataset.type||'') + ' ' + (tr.dataset.url||'') + ' ' + (tr.dataset.desc||'')).toLowerCase();
|
||||
const okKw = !kw || text.includes(kw);
|
||||
const okKind = !kind || (tr.dataset.kind||'normal') === kind;
|
||||
const okEn = en === '' || (tr.dataset.enabled||'1') === en;
|
||||
tr.style.display = (okKw && okKind && okEn) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
function clearFilter(){
|
||||
document.getElementById('f_keyword').value = '';
|
||||
document.getElementById('f_kind').value = '';
|
||||
document.getElementById('f_enabled').value = '';
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
// ---------- 一键全部启用 / 全部停用 ----------
|
||||
async function setAllSources(val){
|
||||
const action = val ? 'enable_all' : 'disable_all';
|
||||
if (!confirm(val ? '确定要【全部启用】所有数据源吗?' : '确定要【全部停用】所有数据源吗?')) return;
|
||||
const r = await API.json('/api/sources', {action});
|
||||
if (!r.ok){ toast('操作失败', false); return; }
|
||||
toast((val ? '✅ 已全部启用 ' : '⏸ 已全部停用 ') + (r.count||0) + ' 个数据源', true);
|
||||
setTimeout(()=>location.reload(), 400);
|
||||
}
|
||||
async function delSource(id){
|
||||
if (!confirm('确认删除该数据源?')) return;
|
||||
await API.json('/api/sources', {action:'delete', id}); toast('✅ 已删除'); setTimeout(()=>location.reload(), 400);
|
||||
|
||||
Reference in New Issue
Block a user