fs-lawrisk/static/v2_tester.html

448 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>