757 lines
28 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">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智慧监督AI文书写作 - API接口测试</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 28px;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
font-size: 14px;
}
.tabs {
display: flex;
background: #f5f5f5;
border-bottom: 2px solid #667eea;
}
.tab {
flex: 1;
padding: 15px 20px;
text-align: center;
cursor: pointer;
background: transparent;
border: none;
font-size: 16px;
font-weight: 500;
color: #666;
transition: all 0.3s;
}
.tab:hover {
background: #e0e0e0;
}
.tab.active {
background: #667eea;
color: white;
}
.tab-content {
display: none;
padding: 30px;
}
.tab-content.active {
display: block;
}
.section {
margin-bottom: 30px;
}
.section-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #667eea;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
color: #555;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #667eea;
}
.form-group textarea {
min-height: 100px;
resize: vertical;
font-family: inherit;
}
.field-row {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 15px;
margin-bottom: 15px;
align-items: start;
}
.btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-secondary {
background: #4caf50;
}
.btn-danger {
background: #f44336;
padding: 8px 15px;
font-size: 14px;
}
.result-box {
background: #f8f9fa;
border: 2px solid #e0e0e0;
border-radius: 6px;
padding: 20px;
margin-top: 20px;
max-height: 600px;
overflow-y: auto;
}
.result-box pre {
margin: 0;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
white-space: pre-wrap;
word-wrap: break-word;
}
.result-item {
background: white;
padding: 15px;
margin-bottom: 10px;
border-radius: 6px;
border-left: 4px solid #667eea;
}
.result-item strong {
color: #667eea;
display: inline-block;
min-width: 150px;
}
.loading {
display: none;
text-align: center;
padding: 20px;
}
.loading.active {
display: block;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
background: #fee;
border-left-color: #f44336;
color: #c62828;
}
.success {
background: #e8f5e9;
border-left-color: #4caf50;
}
.info-link {
text-align: center;
margin-top: 20px;
padding: 15px;
background: #e3f2fd;
border-radius: 6px;
}
.info-link a {
color: #667eea;
text-decoration: none;
font-weight: 500;
}
.info-link a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>智慧监督AI文书写作</h1>
<p>API接口测试工具 - 解析接口 & 文档生成接口</p>
</div>
<!-- 标签页 -->
<div class="tabs">
<button class="tab active" onclick="switchTab('extract')">AI解析接口</button>
<button class="tab" onclick="switchTab('generate')">文档生成接口</button>
</div>
<!-- 解析接口标签页 -->
<div id="extract-tab" class="tab-content active">
<div class="section">
<div class="section-title">输入数据</div>
<div class="form-group">
<label>输入字段数据</label>
<div id="inputFieldsContainer">
<!-- 动态生成的输入字段 -->
</div>
<button class="btn btn-secondary" onclick="addInputField()">+ 添加输入字段</button>
</div>
<div class="form-group">
<label>输出字段列表(需要提取的字段)</label>
<div id="outputFieldsContainer">
<!-- 动态生成的输出字段 -->
</div>
<button class="btn btn-secondary" onclick="addOutputField()">+ 添加输出字段</button>
</div>
</div>
<div class="section">
<button class="btn" onclick="extractData()" id="extractBtn">开始解析</button>
</div>
<div class="loading" id="extractLoading">
<div class="spinner"></div>
<p>AI正在解析中请稍候...</p>
</div>
<div class="section" id="extractResultSection" style="display: none;">
<div class="section-title">解析结果</div>
<div class="result-box" id="extractResultBox"></div>
</div>
</div>
<!-- 文档生成接口标签页 -->
<div id="generate-tab" class="tab-content">
<div class="section">
<div class="section-title">输入数据</div>
<div class="form-group">
<label>字段数据(用于填充模板)</label>
<div id="generateFieldsContainer">
<!-- 动态生成的字段 -->
</div>
<button class="btn btn-secondary" onclick="addGenerateField()">+ 添加字段</button>
</div>
<div class="form-group">
<label>文件列表</label>
<div id="fileListContainer">
<!-- 动态生成的文件列表 -->
</div>
<button class="btn btn-secondary" onclick="addFileItem()">+ 添加文件</button>
</div>
</div>
<div class="section">
<button class="btn" onclick="generateDocument()" id="generateBtn">生成文档</button>
</div>
<div class="loading" id="generateLoading">
<div class="spinner"></div>
<p>正在生成文档,请稍候...</p>
</div>
<div class="section" id="generateResultSection" style="display: none;">
<div class="section-title">生成结果</div>
<div class="result-box" id="generateResultBox"></div>
</div>
</div>
<div class="info-link">
<a href="/api-docs" target="_blank">📖 查看完整的API文档 (Swagger)</a>
</div>
</div>
<script>
// 页面加载时初始化
window.onload = function() {
initExtractTab();
initGenerateTab();
};
// 切换标签页
function switchTab(tabName) {
// 隐藏所有标签页内容
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
// 移除所有标签页的active类
document.querySelectorAll('.tab').forEach(tab => {
tab.classList.remove('active');
});
// 显示选中的标签页
document.getElementById(tabName + '-tab').classList.add('active');
event.target.classList.add('active');
}
// ==================== 解析接口相关 ====================
function initExtractTab() {
// 初始化默认输入字段(虚拟测试数据)
addInputField('clue_info', '被举报用户名称是张三年龄44岁某公司总经理男性1980年5月出生本科文化程度中共党员正处级。主要问题线索违反国家计划生育有关政策规定于2010年10月生育二胎。线索来源群众举报。');
addInputField('target_basic_info_clue', '被核查人员工作基本情况张三1980年5月生本科文化中共党员现为某公司总经理正处级。');
// 初始化默认输出字段(包含完整的字段列表)
addOutputField('target_name');
addOutputField('target_gender');
addOutputField('target_age');
addOutputField('target_date_of_birth');
addOutputField('target_organization_and_position');
addOutputField('target_organization');
addOutputField('target_position');
addOutputField('target_education_level');
addOutputField('target_political_status');
addOutputField('target_professional_rank');
addOutputField('clue_source');
addOutputField('target_issue_description');
}
function addInputField(fieldCode = 'clue_info', fieldValue = '') {
const container = document.getElementById('inputFieldsContainer');
const fieldDiv = document.createElement('div');
fieldDiv.className = 'field-row';
fieldDiv.innerHTML = `
<input type="text" placeholder="字段编码 (如: clue_info)" value="${fieldCode}" class="field-code">
<div style="display: flex; gap: 10px;">
<textarea placeholder="字段值(原始文本)" class="field-value" style="flex: 1; min-height: 60px;">${fieldValue}</textarea>
<button class="btn btn-danger" onclick="removeField(this)">删除</button>
</div>
`;
container.appendChild(fieldDiv);
}
function addOutputField(fieldCode = '') {
const container = document.getElementById('outputFieldsContainer');
const fieldDiv = document.createElement('div');
fieldDiv.className = 'field-row';
fieldDiv.innerHTML = `
<input type="text" placeholder="字段编码 (如: target_name)" value="${fieldCode}" class="output-field-code">
<div style="display: flex; gap: 10px;">
<div style="flex: 1;"></div>
<button class="btn btn-danger" onclick="removeField(this)">删除</button>
</div>
`;
container.appendChild(fieldDiv);
}
async function extractData() {
const extractBtn = document.getElementById('extractBtn');
const loading = document.getElementById('extractLoading');
const resultSection = document.getElementById('extractResultSection');
const resultBox = document.getElementById('extractResultBox');
// 收集输入数据
const inputData = [];
const fieldContainers = document.querySelectorAll('#inputFieldsContainer .field-row');
fieldContainers.forEach(container => {
const fieldCode = container.querySelector('.field-code').value.trim();
const fieldValue = container.querySelector('.field-value').value.trim();
if (fieldCode && fieldValue) {
inputData.push({
fieldCode: fieldCode,
fieldValue: fieldValue
});
}
});
// 收集输出字段
const outputData = [];
const outputContainers = document.querySelectorAll('#outputFieldsContainer .field-row');
outputContainers.forEach(container => {
const fieldCode = container.querySelector('.output-field-code').value.trim();
if (fieldCode) {
outputData.push({
fieldCode: fieldCode
});
}
});
if (inputData.length === 0) {
alert('请至少添加一个输入字段');
return;
}
if (outputData.length === 0) {
alert('请至少添加一个输出字段');
return;
}
// 构建请求数据(新接口格式)
const requestData = {
inputData: inputData,
outputData: outputData
};
// 显示加载状态
extractBtn.disabled = true;
loading.classList.add('active');
resultSection.style.display = 'none';
try {
const response = await fetch('/ai/extract', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});
const result = await response.json();
displayExtractResult(result, requestData);
} catch (error) {
displayError('extract', error.message);
} finally {
extractBtn.disabled = false;
loading.classList.remove('active');
}
}
function displayExtractResult(result, requestData) {
const resultSection = document.getElementById('extractResultSection');
const resultBox = document.getElementById('extractResultBox');
resultSection.style.display = 'block';
let html = '';
// 显示请求数据
html += '<div class="result-item"><strong>请求数据:</strong></div>';
html += '<div class="result-item"><pre>' + JSON.stringify(requestData, null, 2) + '</pre></div>';
// 显示响应结果
if (result.isSuccess) {
html += '<div class="result-item success"><strong>✓ 解析成功!</strong></div>';
if (result.data && result.data.outData) {
html += '<div class="result-item"><strong>提取的字段:</strong></div>';
result.data.outData.forEach(item => {
const value = item.fieldValue || '(空)';
html += `<div class="result-item">
<strong>${item.fieldCode}:</strong> ${value}
</div>`;
});
}
} else {
html += `<div class="result-item error">
<strong>✗ 解析失败!</strong><br>
错误码: ${result.code}<br>
错误信息: ${result.errorMsg}
</div>`;
}
// 显示完整响应
html += '<div class="result-item"><strong>完整响应:</strong></div>';
html += '<div class="result-item"><pre>' + JSON.stringify(result, null, 2) + '</pre></div>';
resultBox.innerHTML = html;
resultSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// ==================== 文档生成接口相关 ====================
function initGenerateTab() {
// 初始化默认字段(完整的虚拟测试数据)
addGenerateField('target_name', '张三');
addGenerateField('target_gender', '男');
addGenerateField('target_age', '44');
addGenerateField('target_date_of_birth', '198005');
addGenerateField('target_organization_and_position', '某公司总经理');
addGenerateField('target_organization', '某公司');
addGenerateField('target_position', '总经理');
addGenerateField('target_education_level', '本科');
addGenerateField('target_political_status', '中共党员');
addGenerateField('target_professional_rank', '正处级');
addGenerateField('clue_source', '群众举报');
addGenerateField('target_issue_description', '违反国家计划生育有关政策规定于2010年10月生育二胎。');
addGenerateField('department_opinion', '建议进行初步核实');
addGenerateField('filler_name', '李四');
// 初始化默认文件(包含多个模板用于测试)
addFileItem(1, '初步核实审批表.doc', 'PRELIMINARY_VERIFICATION_APPROVAL');
addFileItem(2, '请示报告卡.doc', 'REPORT_CARD');
}
function addGenerateField(fieldCode = '', fieldValue = '') {
const container = document.getElementById('generateFieldsContainer');
const fieldDiv = document.createElement('div');
fieldDiv.className = 'field-row';
fieldDiv.innerHTML = `
<input type="text" placeholder="字段编码 (如: target_name)" value="${fieldCode}" class="generate-field-code">
<div style="display: flex; gap: 10px;">
<input type="text" placeholder="字段值" value="${fieldValue}" class="generate-field-value" style="flex: 1;">
<button class="btn btn-danger" onclick="removeField(this)">删除</button>
</div>
`;
container.appendChild(fieldDiv);
}
function addFileItem(fileId = '', fileName = '', templateCode = '') {
const container = document.getElementById('fileListContainer');
const fileDiv = document.createElement('div');
fileDiv.className = 'field-row';
fileDiv.innerHTML = `
<input type="number" placeholder="文件ID" value="${fileId}" class="file-id" style="width: 150px;">
<div style="display: flex; gap: 10px; flex: 1;">
<input type="text" placeholder="文件名称 (如: 初步核实审批表.doc)" value="${fileName}" class="file-name" style="flex: 1;">
<input type="text" placeholder="模板编码 (如: PRELIMINARY_VERIFICATION_APPROVAL)" value="${templateCode}" class="template-code" style="flex: 1;">
<button class="btn btn-danger" onclick="removeField(this)">删除</button>
</div>
`;
container.appendChild(fileDiv);
}
async function generateDocument() {
const generateBtn = document.getElementById('generateBtn');
const loading = document.getElementById('generateLoading');
const resultSection = document.getElementById('generateResultSection');
const resultBox = document.getElementById('generateResultBox');
// 收集字段数据
const inputData = [];
const fieldContainers = document.querySelectorAll('#generateFieldsContainer .field-row');
fieldContainers.forEach(container => {
const fieldCode = container.querySelector('.generate-field-code').value.trim();
const fieldValue = container.querySelector('.generate-field-value').value.trim();
if (fieldCode) {
inputData.push({
fieldCode: fieldCode,
fieldValue: fieldValue || ''
});
}
});
// 收集文件列表
const fileList = [];
const fileContainers = document.querySelectorAll('#fileListContainer .field-row');
fileContainers.forEach(container => {
const fileId = container.querySelector('.file-id').value.trim();
const fileName = container.querySelector('.file-name').value.trim();
const templateCode = container.querySelector('.template-code').value.trim();
if (fileId && fileName && templateCode) {
fileList.push({
fileId: parseInt(fileId),
fileName: fileName,
templateCode: templateCode
});
}
});
if (inputData.length === 0) {
alert('请至少添加一个字段');
return;
}
if (fileList.length === 0) {
alert('请至少添加一个文件');
return;
}
// 构建请求数据
const requestData = {
inputData: inputData,
fpolicFieldParamFileList: fileList
};
// 显示加载状态
generateBtn.disabled = true;
loading.classList.add('active');
resultSection.style.display = 'none';
try {
const response = await fetch('/ai/generate-document', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
});
const result = await response.json();
displayGenerateResult(result, requestData);
} catch (error) {
displayError('generate', error.message);
} finally {
generateBtn.disabled = false;
loading.classList.remove('active');
}
}
function displayGenerateResult(result, requestData) {
const resultSection = document.getElementById('generateResultSection');
const resultBox = document.getElementById('generateResultBox');
resultSection.style.display = 'block';
let html = '';
// 显示请求数据
html += '<div class="result-item"><strong>请求数据:</strong></div>';
html += '<div class="result-item"><pre>' + JSON.stringify(requestData, null, 2) + '</pre></div>';
// 显示响应结果
if (result.isSuccess) {
html += '<div class="result-item success"><strong>✓ 文档生成成功!</strong></div>';
if (result.data) {
if (result.data.documentId) {
html += `<div class="result-item">
<strong>文档ID:</strong> ${result.data.documentId}
</div>`;
}
if (result.data.documentName) {
html += `<div class="result-item">
<strong>文档名称:</strong> ${result.data.documentName}
</div>`;
}
if (result.data.fpolicFieldParamFileList && result.data.fpolicFieldParamFileList.length > 0) {
html += '<div class="result-item"><strong>生成的文件:</strong></div>';
result.data.fpolicFieldParamFileList.forEach(file => {
html += `<div class="result-item">
<strong>${file.fileName}:</strong><br>
文件路径: ${file.filePath || '(无路径)'}
</div>`;
});
}
}
} else {
html += `<div class="result-item error">
<strong>✗ 文档生成失败!</strong><br>
错误码: ${result.code}<br>
错误信息: ${result.errorMsg}
</div>`;
}
// 显示完整响应
html += '<div class="result-item"><strong>完整响应:</strong></div>';
html += '<div class="result-item"><pre>' + JSON.stringify(result, null, 2) + '</pre></div>';
resultBox.innerHTML = html;
resultSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// ==================== 通用函数 ====================
function removeField(btn) {
btn.closest('.field-row').remove();
}
function displayError(tabType, errorMsg) {
const resultSection = document.getElementById(tabType + 'ResultSection');
const resultBox = document.getElementById(tabType + 'ResultBox');
resultSection.style.display = 'block';
resultBox.innerHTML = `<div class="result-item error">
<strong>请求失败!</strong><br>
${errorMsg}
</div>`;
}
</script>
</body>
</html>