| <!DOCTYPE html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>内容提取</title> |
| <link rel="stylesheet" href="/static/plugin-ui.css"> |
| <style> |
| .tabs { display: flex; gap: 4px; margin-bottom: 8px; flex-wrap: wrap; } |
| .tab { padding: 6px 12px; border-radius: 4px 4px 0 0; cursor: pointer; background: var(--bg-muted); color: var(--text-muted); border: 1px solid var(--border); border-bottom: none; } |
| .tab.active { background: var(--bg); color: var(--text); font-weight: 600; } |
| .tab-content { display: none; } |
| .tab-content.active { display: block; } |
| .result-actions { display: flex; gap: 8px; margin-bottom: 8px; } |
| </style> |
| </head> |
| <body> |
| <main class="plugin-shell"> |
| <header class="plugin-header"> |
| <div> |
| <h1 class="plugin-title">内容提取</h1> |
| <p class="plugin-subtitle">从支持的内容链接提取标题、正文和图片 OCR 文本。</p> |
| </div> |
| <span id="statusPill" class="plugin-status">检查中</span> |
| </header> |
|
|
| <div id="message" class="plugin-message"></div> |
| <section class="plugin-grid"> |
| <div class="plugin-panel"> |
| <h2 class="plugin-panel-title">提取参数</h2> |
| <div class="plugin-stack"> |
| <div class="plugin-field"> |
| <label class="plugin-label" for="url">内容链接</label> |
| <input id="url" type="url" placeholder="https://www.xiaohongshu.com/explore/... 或 https://weibo.com/..."> |
| </div> |
| <label class="plugin-row"> |
| <input id="includeOcr" type="checkbox" checked style="width:auto"> |
| <span>启用图片 OCR</span> |
| </label> |
| <button id="extractBtn" class="plugin-button" onclick="extractContent()">开始提取</button> |
| </div> |
| </div> |
|
|
| |
| <div id="logPanel"></div> |
| </section> |
|
|
| |
| <section id="resultSection" style="display:none;"> |
| <div class="plugin-panel"> |
| <h2 class="plugin-panel-title">提取结果</h2> |
| |
| <div class="result-actions"> |
| <button class="plugin-button secondary" onclick="copyResult()">复制结果</button> |
| <button class="plugin-button secondary" onclick="downloadResult()">下载 JSON</button> |
| </div> |
| |
| <div class="tabs"> |
| <div class="tab active" data-tab="normalized" onclick="switchTab('normalized')">结构化内容</div> |
| <div class="tab" data-tab="raw" onclick="switchTab('raw')">原始内容</div> |
| <div class="tab" data-tab="ocr" onclick="switchTab('ocr')">OCR 文本</div> |
| </div> |
| |
| <div id="tab-normalized" class="tab-content active"> |
| <pre id="outputNormalized" class="plugin-output"></pre> |
| </div> |
| <div id="tab-raw" class="tab-content"> |
| <pre id="outputRaw" class="plugin-output"></pre> |
| </div> |
| <div id="tab-ocr" class="tab-content"> |
| <pre id="outputOcr" class="plugin-output"></pre> |
| </div> |
| </div> |
| </section> |
| </main> |
|
|
| <script src="/static/plugin-ui.js"></script> |
| <script> |
| const API = "/plugins/content/api"; |
| let currentLogPanel = null; |
| let lastResult = null; |
| |
| async function loadStatus() { |
| try { |
| const data = await PluginUI.request(`${API}/status`); |
| const pill = document.getElementById("statusPill"); |
| pill.textContent = data.enabled === false ? "插件未启用" : "可用"; |
| pill.className = `plugin-status ${data.enabled === false ? "warn" : "ok"}`; |
| } catch { |
| document.getElementById("statusPill").textContent = "状态未知"; |
| } |
| } |
| |
| |
| function switchTab(tabName) { |
| document.querySelectorAll(".tab").forEach(t => t.classList.remove("active")); |
| document.querySelectorAll(".tab-content").forEach(c => c.classList.remove("active")); |
| document.querySelector(`[data-tab="${tabName}"]`).classList.add("active"); |
| document.getElementById(`tab-${tabName}`).classList.add("active"); |
| } |
| |
| |
| function copyResult() { |
| if (lastResult) { |
| PluginUI.copyText(JSON.stringify(lastResult, null, 2)); |
| PluginUI.showMessage("message", "已复制到剪贴板", "success"); |
| } |
| } |
| |
| |
| function downloadResult() { |
| if (lastResult) { |
| const blob = new Blob([JSON.stringify(lastResult, null, 2)], { type: "application/json" }); |
| PluginUI.downloadBlob(blob, `content_extract_${lastResult.source_type || 'result'}.json`); |
| } |
| } |
| |
| |
| async function extractContent() { |
| const btn = document.getElementById("extractBtn"); |
| btn.disabled = true; |
| |
| |
| if (currentLogPanel) { |
| PluginUI.stopPolling(currentLogPanel); |
| currentLogPanel.remove(); |
| } |
| |
| |
| document.getElementById("resultSection").style.display = "none"; |
| PluginUI.clearMessage("message"); |
| |
| try { |
| const url = document.getElementById("url").value.trim(); |
| if (!url) throw new Error("请输入内容链接"); |
| |
| const includeOcr = document.getElementById("includeOcr").checked; |
| |
| |
| const data = await PluginUI.request(`${API}/extract`, { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ url, include_ocr: includeOcr }), |
| }); |
| |
| if (data.run_id) { |
| |
| currentLogPanel = PluginUI.createRunLog({ title: "提取日志" }); |
| document.getElementById("logPanel").appendChild(currentLogPanel); |
| |
| |
| PluginUI.pollRunEvents("content", data.run_id, currentLogPanel, { |
| onRunComplete: (run) => { |
| btn.disabled = false; |
| if (run.status === "succeeded" && run.result) { |
| |
| document.getElementById("resultSection").style.display = "block"; |
| lastResult = run.result; |
| |
| |
| document.getElementById("outputNormalized").textContent = |
| run.result.normalized_content || run.result.content || "无结构化内容"; |
| document.getElementById("outputRaw").textContent = |
| run.result.raw_html || run.result.raw_text || "无原始内容"; |
| document.getElementById("outputOcr").textContent = |
| run.result.images_text || "无 OCR 文本"; |
| } |
| }, |
| onError: (err) => { |
| btn.disabled = false; |
| PluginUI.showMessage("message", `轮询错误: ${err.message}`, "error"); |
| }, |
| }); |
| } else { |
| |
| btn.disabled = false; |
| if (data.status === "succeeded") { |
| document.getElementById("resultSection").style.display = "block"; |
| lastResult = data; |
| |
| document.getElementById("outputNormalized").textContent = |
| data.normalized_content || data.content || "无结构化内容"; |
| document.getElementById("outputRaw").textContent = |
| data.raw_html || data.raw_text || "无原始内容"; |
| document.getElementById("outputOcr").textContent = |
| data.images_text || "无 OCR 文本"; |
| } else { |
| PluginUI.showMessage("message", data.error || "提取失败", "error"); |
| } |
| } |
| } catch (error) { |
| btn.disabled = false; |
| PluginUI.showMessage("message", error.message, "error"); |
| } |
| } |
| |
| loadStatus(); |
| </script> |
| </body> |
| </html> |
|
|