735 lines
26 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', '被举报用户名称是张三年龄30岁某公司总经理男性1980年5月出生中共党员正处级');
// 初始化默认输出字段
addOutputField('target_name');
addOutputField('target_gender');
addOutputField('target_organization_and_position');
}
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_organization_and_position', '某公司总经理');
// 初始化默认文件
addFileItem(1, '初步核实审批表.doc', 'PRELIMINARY_VERIFICATION_APPROVAL');
}
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>