v1.1.0: 真实网页采集(可读正文全文入库) + 大模型接口多预置一键切换(SiliconFlow默认) + 数据源可编辑
- 新增 crawler.py: requests+bs4 readability风格清洗, 提取候选链接按文章相似度排序, 前5条抓全文存 articles.full_text, 详情页展示; example.com占位源走模拟, 真实源失败标记error不造假 - 大模型: 新增 llm_providers 表(预置 SiliconFlow/DeepSeek官方/Autodl/Local Qwen), 设置页增删改/测试/一键切换, 激活接口失败自动切换备用 - 数据源: 前端补编辑按钮+弹窗(后端 update 已支持), 列表显示URL与采集状态 - 数据库迁移: articles.full_text 列 + llm_providers 表
This commit is contained in:
+21
-3
@@ -12,7 +12,7 @@
|
||||
<div class="form-row">
|
||||
<input id="f_name" placeholder="名称(如:36氪 AI频道)">
|
||||
<input id="f_type" placeholder="类型(科技媒体/公司动态/学术/政策...)">
|
||||
<input id="f_url" placeholder="URL / RSS 地址">
|
||||
<input id="f_url" placeholder="URL / RSS 地址(example.com 开头走模拟数据)">
|
||||
<input id="f_weight" type="number" step="0.1" value="0.8" placeholder="权重">
|
||||
</div>
|
||||
<textarea id="f_desc" placeholder="描述(该源提供哪些内容)" rows="2"></textarea>
|
||||
@@ -24,15 +24,16 @@
|
||||
<thead><tr><th>ID</th><th>名称</th><th>类型</th><th>权重</th><th>状态</th><th>最近采集</th><th>新增条数</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
{% for s in sources %}
|
||||
<tr>
|
||||
<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 }}">
|
||||
<td>{{ s.id }}</td>
|
||||
<td><b>{{ s.name }}</b><br><span class="muted small">{{ s.description }}</span></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>{{ s.type }}</td>
|
||||
<td>{{ s.weight }}</td>
|
||||
<td>{% if s.enabled %}<span class="tag ok">启用</span>{% else %}<span class="tag">停用</span>{% endif %}</td>
|
||||
<td>{{ s.last_fetch or '—' }}</td>
|
||||
<td>{{ s.last_count }}</td>
|
||||
<td>
|
||||
<button class="btn mini" onclick="editSource({{ s.id }})">✏️ 编辑</button>
|
||||
<button class="btn mini" onclick="toggleSource({{ s.id }})">{{ '停用' if s.enabled else '启用' }}</button>
|
||||
<button class="btn mini danger" onclick="delSource({{ s.id }})">删除</button>
|
||||
</td>
|
||||
@@ -40,6 +41,7 @@
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="muted small" style="margin-top:8px;">💡 真实 URL 源会自动抓取页面并清洗可读正文入库;example.com 等占位地址走模拟数据。</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
@@ -52,6 +54,22 @@ async function saveSource(){
|
||||
});
|
||||
if (r.ok) { toast('✅ 已添加'); setTimeout(()=>location.reload(), 600); } else toast('❌ 添加失败', false);
|
||||
}
|
||||
async function editSource(id){
|
||||
const row = document.querySelector(`tr[data-sid="${id}"]`);
|
||||
if (!row) return;
|
||||
const name = prompt('名称', row.dataset.name);
|
||||
if (name === null) return;
|
||||
const type = prompt('类型', row.dataset.type);
|
||||
if (type === null) return;
|
||||
const url = prompt('URL / RSS 地址', row.dataset.url);
|
||||
if (url === null) return;
|
||||
const weight = parseFloat(prompt('权重', row.dataset.weight) || '1') || 1;
|
||||
const desc = prompt('描述', row.dataset.desc);
|
||||
if (desc === null) return;
|
||||
const r = await API.json('/api/sources', {action:'update', id, name, type, url, weight, description:desc});
|
||||
toast(r.ok ? '✅ 已更新' : '❌ 更新失败', r.ok);
|
||||
if (r.ok) setTimeout(()=>location.reload(), 400);
|
||||
}
|
||||
async function toggleSource(id){ await API.json('/api/sources', {action:'toggle', id}); location.reload(); }
|
||||
async function delSource(id){
|
||||
if (!confirm('确认删除该数据源?')) return;
|
||||
|
||||
Reference in New Issue
Block a user