fs-lawrisk/static/v2_tester.html

448 lines
15 KiB
HTML
Raw Permalink Normal View History

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>LawRisk V2 接口测试</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
color-scheme: light dark;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
body {
margin: 0;
padding: 0 1.5rem 2rem;
background: #f7f7f7;
}
h1, h2 {
font-weight: 600;
}
form {
margin-top: 1.5rem;
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
align-items: center;
}
input[type="text"],
select {
flex: 1 1 320px;
padding: 0.6rem 0.75rem;
font-size: 1rem;
border: 1px solid #bbb;
border-radius: 6px;
}
button {
padding: 0.6rem 1.1rem;
font-size: 1rem;
border-radius: 6px;
border: none;
cursor: pointer;
background: #2563eb;
color: #fff;
}
button[type="button"] {
background: #6b7280;
}
#status {
margin-top: 1rem;
min-height: 1.5rem;
color: #2563eb;
font-weight: 500;
}
.results {
margin-top: 1.5rem;
}
.subject-block {
background: #fff;
border-radius: 12px;
padding: 1.25rem;
margin-bottom: 1.25rem;
box-shadow: 0 8px 18px rgba(12, 45, 90, 0.05);
}
.subject-meta {
margin: 0.4rem 0 1rem;
color: #4b5563;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 0.75rem;
}
th, td {
border: 1px solid #d1d5db;
padding: 0.6rem 0.75rem;
text-align: left;
vertical-align: top;
}
th {
background: #eff6ff;
color: #1d4ed8;
font-weight: 600;
}
td ul {
margin: 0;
padding-left: 1.4rem;
}
td ul li {
margin-bottom: 0.4rem;
}
.risk-item-line + .risk-item-line {
margin-top: 0.35rem;
}
.empty-hint {
margin: 1rem 0 0;
padding: 0.75rem;
border-radius: 8px;
background: #fef3c7;
color: #7c2d12;
}
.supplementary {
margin-top: 1.5rem;
}
.supplementary h3 {
margin-bottom: 0.5rem;
}
.supplementary ul {
margin: 0;
padding-left: 1.5rem;
}
.raw-json {
margin-top: 1.25rem;
background: #111827;
color: #f3f4f6;
padding: 1rem;
border-radius: 8px;
overflow-x: auto;
font-family: ui-monospace, SFMono-Regular, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.9rem;
display: none;
}
.toggle-raw {
margin-top: 0.75rem;
background: #10b981;
}
@media (max-width: 720px) {
body {
padding: 0 0.75rem 1.5rem;
}
table, th, td {
font-size: 0.92rem;
}
}
</style>
</head>
<body>
<main>
<h1>LawRisk V2 接口测试台</h1>
<p>输入问题后调用 <code>/fs-ai-asistant/api/workflow/lawrisk/v2</code>,将返回的主题及许可详情以表格展示。</p>
<form id="query-form">
<label for="query-input" class="visually-hidden">问题</label>
<input id="query-input" type="text" placeholder="例如:开办电影院需要哪些许可事项" autocomplete="off" required>
<label for="region-select" class="visually-hidden">地区</label>
<select id="region-select" multiple size="4" aria-label="限定地区,可多选(可留空)">
<option value="" selected>全部地区(默认)</option>
</select>
<button type="submit">查询</button>
<button type="button" id="reset-btn">清空</button>
</form>
<div id="status"></div>
<section class="results" id="results"></section>
<section class="supplementary" id="supplementary"></section>
<button type="button" class="toggle-raw" id="toggle-raw-btn" style="display:none;">显示原始 JSON</button>
<pre class="raw-json" id="raw-json"></pre>
</main>
<script>
const formEl = document.getElementById("query-form");
const queryInputEl = document.getElementById("query-input");
const regionSelectEl = document.getElementById("region-select");
const resetBtnEl = document.getElementById("reset-btn");
const statusEl = document.getElementById("status");
const resultsEl = document.getElementById("results");
const supplementaryEl = document.getElementById("supplementary");
const rawBtnEl = document.getElementById("toggle-raw-btn");
const rawJsonEl = document.getElementById("raw-json");
let lastPayload = null;
formEl.addEventListener("submit", async (event) => {
event.preventDefault();
const query = queryInputEl.value.trim();
if (!query) {
statusEl.textContent = "请输入问题后再查询。";
return;
}
setLoading(true);
clearResults();
try {
const params = new URLSearchParams();
params.set("query", query);
const selectedRegions = Array.from(regionSelectEl.selectedOptions)
.map((option) => option.value)
.filter((value) => value && value.trim());
if (selectedRegions.length) {
selectedRegions.forEach((value) => params.append("region", value));
}
const response = await fetch(`/fs-ai-asistant/api/workflow/lawrisk/v2?${params.toString()}`, {
method: "GET",
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const payload = await response.json();
lastPayload = payload;
renderPayload(payload);
} catch (error) {
statusEl.textContent = `请求失败:${error.message}`;
statusEl.style.color = "#dc2626";
} finally {
setLoading(false);
}
});
resetBtnEl.addEventListener("click", () => {
queryInputEl.value = "";
Array.from(regionSelectEl.options).forEach((option, idx) => {
option.selected = idx === 0;
});
queryInputEl.focus();
statusEl.textContent = "";
statusEl.style.color = "#2563eb";
clearResults();
lastPayload = null;
rawBtnEl.style.display = "none";
rawJsonEl.style.display = "none";
});
rawBtnEl.addEventListener("click", () => {
if (!lastPayload) return;
const isHidden = rawJsonEl.style.display === "none";
rawJsonEl.style.display = isHidden ? "block" : "none";
rawBtnEl.textContent = isHidden ? "隐藏原始 JSON" : "显示原始 JSON";
});
function setLoading(isLoading) {
if (isLoading) {
statusEl.textContent = "查询中,请稍候…";
statusEl.style.color = "#2563eb";
formEl.querySelector("button[type='submit']").disabled = true;
} else {
formEl.querySelector("button[type='submit']").disabled = false;
}
}
async function initRegionOptions() {
try {
const response = await fetch("/fs-ai-asistant/api/workflow/lawrisk/v2/regions");
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const payload = await response.json();
const regions = (payload && payload.data && Array.isArray(payload.data.regions))
? payload.data.regions
: [];
if (!regions.length) {
return;
}
const existing = new Set(Array.from(regionSelectEl.options).map((opt) => opt.value));
regions.forEach((region) => {
if (!region || !region.id) return;
if (existing.has(region.id)) return;
const option = document.createElement("option");
option.value = region.id;
option.textContent = region.name || region.id;
regionSelectEl.appendChild(option);
});
} catch (error) {
console.warn("加载地区列表失败:", error);
}
}
function clearResults() {
resultsEl.innerHTML = "";
supplementaryEl.innerHTML = "";
rawJsonEl.textContent = "";
}
function renderPayload(payload) {
if (!payload || typeof payload !== "object") {
statusEl.textContent = "响应格式不正确。";
statusEl.style.color = "#dc2626";
return;
}
if (!payload.success) {
statusEl.textContent = payload.message ? `接口返回错误:${payload.message}` : "接口执行失败。";
statusEl.style.color = "#dc2626";
return;
}
const data = payload.data || {};
const subjects = Array.isArray(data.risk_subject) ? data.risk_subject : [];
statusEl.textContent = subjects.length
? `已匹配 ${subjects.length} 个主题事项,共含 ${countPermits(subjects)} 条许可信息。`
: "未检索到相关主题或许可事项。";
statusEl.style.color = subjects.length ? "#065f46" : "#dc2626";
if (subjects.length) {
subjects.forEach((subject, idx) => {
resultsEl.appendChild(renderSubjectBlock(subject, idx));
});
}
renderSupplementary(data);
rawBtnEl.style.display = "inline-block";
rawBtnEl.textContent = "显示原始 JSON";
rawJsonEl.style.display = "none";
rawJsonEl.textContent = JSON.stringify(payload, null, 2);
}
window.addEventListener("DOMContentLoaded", () => {
initRegionOptions();
});
function renderSubjectBlock(subject, index) {
const block = document.createElement("section");
block.className = "subject-block";
const title = document.createElement("h2");
title.textContent = subject.display_name || `主题 ${index + 1}`;
block.appendChild(title);
const meta = document.createElement("p");
meta.className = "subject-meta";
const regionName = (subject.region && subject.region.name) || "未知地区";
const themeName = (subject.theme && subject.theme.name) || "未知主题事项";
meta.textContent = `地区:${regionName} 主题事项:${themeName}`;
block.appendChild(meta);
const permits = Array.isArray(subject.permits) ? subject.permits : [];
if (!permits.length) {
const emptyHint = document.createElement("div");
emptyHint.className = "empty-hint";
emptyHint.textContent = "该主题未关联具体许可事项。";
block.appendChild(emptyHint);
return block;
}
const table = document.createElement("table");
const thead = document.createElement("thead");
const headerRow = document.createElement("tr");
[
"许可事项",
"事项属性",
"子项概述",
"经营范围",
"风险提示",
"责任联系方式",
"管辖范围",
].forEach((label) => {
const th = document.createElement("th");
th.textContent = label;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
const tbody = document.createElement("tbody");
permits.forEach((permit) => {
const tr = document.createElement("tr");
tr.appendChild(createTextCell(permit.name || "(未命名许可)"));
tr.appendChild(createTextCell(permit.permit_status || "—"));
tr.appendChild(createTextCell(permit.subitem_summary || "—"));
const scopes = Array.isArray(permit.business_scopes)
? permit.business_scopes
.map((scope) => typeof scope.description === "string" ? scope.description : "")
.filter(Boolean)
.join("、")
: "";
tr.appendChild(createTextCell(scopes || "—"));
tr.appendChild(createRiskCell(Array.isArray(permit.risks) ? permit.risks : []));
tr.appendChild(createTextCell(permit.responsible_contact || "—"));
tr.appendChild(createTextCell(permit.jurisdiction_scope || "—"));
tbody.appendChild(tr);
});
table.appendChild(tbody);
block.appendChild(table);
return block;
}
function createTextCell(text) {
const td = document.createElement("td");
td.textContent = text || "—";
return td;
}
function createRiskCell(risks) {
const td = document.createElement("td");
if (!risks.length) {
td.textContent = "—";
return td;
}
const list = document.createElement("ul");
risks.forEach((risk, idx) => {
const li = document.createElement("li");
const addLine = (text) => {
if (!text) return;
const div = document.createElement("div");
div.className = "risk-item-line";
div.textContent = text;
li.appendChild(div);
};
addLine(risk.risk_content ? `${idx + 1}. ${risk.risk_content}` : "");
addLine(risk.legal_basis ? `法律依据:${risk.legal_basis}` : "");
addLine(risk.document_no ? `文号:${risk.document_no}` : "");
addLine(risk.summary ? `摘要:${risk.summary}` : "");
if (li.childNodes.length) {
list.appendChild(li);
}
});
td.appendChild(list);
return td;
}
function renderSupplementary(data) {
const recQuestions = Array.isArray(data.questionExtend) ? data.questionExtend : [];
if (!recQuestions.length) return;
const title = document.createElement("h3");
title.textContent = "推荐追问";
supplementaryEl.appendChild(title);
const list = document.createElement("ul");
recQuestions.forEach((question) => {
const li = document.createElement("li");
li.textContent = question;
list.appendChild(li);
});
supplementaryEl.appendChild(list);
}
function countPermits(subjects) {
return subjects.reduce((sum, subject) => {
const permits = Array.isArray(subject.permits) ? subject.permits.length : 0;
return sum + permits;
}, 0);
}
</script>
</body>
</html>