2025-12-11 15:10:46 +08:00

901 lines
41 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 style="margin-bottom: 10px;">
<button class="btn btn-secondary" onclick="loadAvailableFiles()" style="margin-right: 10px;">📋 加载全部可用模板</button>
<button class="btn btn-secondary" onclick="addFileItem()" style="margin-right: 10px;">+ 手动添加文件</button>
<button class="btn btn-danger" onclick="clearAllFiles()">🗑️ 清空列表</button>
</div>
<div style="margin-bottom: 10px; padding: 10px; background: #f0f0f0; border-radius: 4px; font-size: 13px; color: #666;">
💡 提示:点击"加载全部可用模板"可以加载所有可用的文档模板类型,方便测试不同模板的生成效果
</div>
<div id="fileListContainer">
<!-- 动态生成的文件列表 -->
</div>
</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', '张三多次在私下聚会、网络群组中发表抹黑党中央决策部署的言论传播歪曲党的理论和路线方针政策的错误观点频繁接受管理服务对象安排的高档宴请、私人会所聚餐以及高尔夫球、高端足浴等娱乐活动相关费用均由对方全额承担在干部选拔任用、岗位调整工作中利用职务便利收受他人财物利用职权为其亲属经营的公司谋取不正当利益帮助该公司违规承接本单位及关联单位工程项目3个合同总额超200万元从中收受亲属给予的"感谢费"15万元其本人沉迷赌博活动每周至少参与1次大额赌资赌博单次赌资超1万元累计赌资达数十万元。');
addInputField('target_basic_info_clue', '张三汉族1990年9月出生云南普洱人研究生学历2005年8月参加工作2006年10月加入中国共产党。2004年8月至2005年2月在云南省农业机械公司工作2005年2月至2012年2月历任云南省农业机械公司办公室副主任、主任、团委书记2012年2月至2018年3月任云南省农业机械公司支部书记、厂长2018年3月至2020年3月任云南省农业机械公司总经理助理、销售部部长2020年3月至2022年3月任云南省农业机械公司总经理助理2022年3月至2022年7月任云南省农业机械公司大理分公司副经理2022年7月至2023年12月任云南省农业机械公司西双版纳分公司经理2023年12月至今任云南省农业机械公司党支部书记、经理。');
// 初始化默认输出字段(包含完整的字段列表)
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' });
}
// ==================== 文档生成接口相关 ====================
async function loadAvailableFiles() {
try {
const response = await fetch('/api/file-configs');
const result = await response.json();
if (result.isSuccess && result.data && result.data.fileConfigs) {
const container = document.getElementById('fileListContainer');
container.innerHTML = ''; // 清空现有列表
// 只添加有filePath的文件有模板文件的
const filesWithPath = result.data.fileConfigs.filter(f => f.filePath);
if (filesWithPath.length === 0) {
alert('没有找到可用的文件配置需要有filePath');
return;
}
// 加载所有可用文件
filesWithPath.forEach(file => {
addFileItem(file.fileId, file.fileName);
});
alert(`已加载全部 ${filesWithPath.length} 个可用文件模板`);
} else {
alert('获取文件列表失败: ' + (result.errorMsg || '未知错误'));
}
} catch (error) {
alert('加载文件列表失败: ' + error.message);
}
}
async function initGenerateTab() {
// 初始化所有字段(完整的虚拟测试数据)
// 基本信息字段
addGenerateField('target_name', '张三');
addGenerateField('target_gender', '男');
addGenerateField('target_age', '34');
addGenerateField('target_date_of_birth', '199009');
addGenerateField('target_date_of_birth_full', '1990年9月');
addGenerateField('target_id_number', '530123199009123456');
addGenerateField('target_ethnicity', '汉族');
addGenerateField('target_place_of_origin', '云南普洱');
addGenerateField('target_address', '云南省昆明市五华区某某街道某某小区1栋1单元101室');
addGenerateField('target_registered_address', '云南省昆明市五华区某某街道某某小区1栋1单元101室');
addGenerateField('target_contact', '13800138000');
// 组织和工作信息
addGenerateField('target_organization_and_position', '云南省农业机械公司党支部书记、经理');
addGenerateField('target_organization', '云南省农业机械公司');
addGenerateField('target_position', '党支部书记、经理');
addGenerateField('target_education_level', '研究生');
addGenerateField('target_education', '研究生');
addGenerateField('target_political_status', '中共党员');
addGenerateField('target_professional_rank', '高级工程师');
addGenerateField('target_occupation', '企业管理人员');
addGenerateField('target_work_basic_info', '2005年8月参加工作现任云南省农业机械公司党支部书记、经理');
addGenerateField('target_work_history', '2004年8月至2005年2月在云南省农业机械公司工作2005年2月至2012年2月历任云南省农业机械公司办公室副主任、主任、团委书记2012年2月至2018年3月任云南省农业机械公司支部书记、厂长2018年3月至2020年3月任云南省农业机械公司总经理助理、销售部部长2020年3月至2022年3月任云南省农业机械公司总经理助理2022年3月至2022年7月任云南省农业机械公司大理分公司副经理2022年7月至2023年12月任云南省农业机械公司西双版纳分公司经理2023年12月至今任云南省农业机械公司党支部书记、经理。');
addGenerateField('target_basic_info', '张三汉族1990年9月出生云南普洱人研究生学历中共党员现任云南省农业机械公司党支部书记、经理。');
// 线索和问题信息
addGenerateField('clue_info', '张三多次在私下聚会、网络群组中发表抹黑党中央决策部署的言论传播歪曲党的理论和路线方针政策的错误观点频繁接受管理服务对象安排的高档宴请、私人会所聚餐以及高尔夫球、高端足浴等娱乐活动相关费用均由对方全额承担在干部选拔任用、岗位调整工作中利用职务便利收受他人财物利用职权为其亲属经营的公司谋取不正当利益帮助该公司违规承接本单位及关联单位工程项目3个合同总额超200万元从中收受亲属给予的"感谢费"15万元其本人沉迷赌博活动每周至少参与1次大额赌资赌博单次赌资超1万元累计赌资达数十万元。');
addGenerateField('target_basic_info_clue', '张三汉族1990年9月出生云南普洱人研究生学历2005年8月参加工作2006年10月加入中国共产党。2004年8月至2005年2月在云南省农业机械公司工作2005年2月至2012年2月历任云南省农业机械公司办公室副主任、主任、团委书记2012年2月至2018年3月任云南省农业机械公司支部书记、厂长2018年3月至2020年3月任云南省农业机械公司总经理助理、销售部部长2020年3月至2022年3月任云南省农业机械公司总经理助理2022年3月至2022年7月任云南省农业机械公司大理分公司副经理2022年7月至2023年12月任云南省农业机械公司西双版纳分公司经理2023年12月至今任云南省农业机械公司党支部书记、经理。');
addGenerateField('clue_source', '群众举报');
addGenerateField('target_issue_description', '张三多次在私下聚会、网络群组中发表抹黑党中央决策部署的言论传播歪曲党的理论和路线方针政策的错误观点频繁接受管理服务对象安排的高档宴请、私人会所聚餐以及高尔夫球、高端足浴等娱乐活动相关费用均由对方全额承担在干部选拔任用、岗位调整工作中利用职务便利收受他人财物利用职权为其亲属经营的公司谋取不正当利益帮助该公司违规承接本单位及关联单位工程项目3个合同总额超200万元从中收受亲属给予的"感谢费"15万元其本人沉迷赌博活动每周至少参与1次大额赌资赌博单次赌资超1万元累计赌资达数十万元。');
addGenerateField('target_problem_description', '违反政治纪律、组织纪律、廉洁纪律,涉嫌违纪违法');
addGenerateField('target_issue_severity', '严重');
addGenerateField('target_issue_severity_level', '严重');
addGenerateField('target_other_issues_possibility', '较大');
// 个人情况评估
addGenerateField('target_family_situation', '家庭关系和谐稳定');
addGenerateField('target_social_relations', '社会交往较多,人际关系基本正常');
addGenerateField('target_health_status', '良好');
addGenerateField('target_personality', '开朗');
addGenerateField('target_tolerance', '较强');
addGenerateField('target_previous_investigation', '无');
addGenerateField('target_negative_events', '无');
addGenerateField('target_other_situation', '无');
// 谈话和调查相关
addGenerateField('target_attitude', '配合调查');
addGenerateField('target_confession_level', '部分承认');
addGenerateField('target_behavior_during_interview', '情绪稳定,配合调查');
addGenerateField('target_behavior_after_relief', '情绪有所缓解');
addGenerateField('target_mental_burden_level', '中等');
addGenerateField('target_risk_level', '中');
addGenerateField('risk_level', '中');
addGenerateField('pre_interview_risk_assessment_result', '风险等级:中,已制定安全预案');
// 调查组织和人员
addGenerateField('investigation_unit_name', '纪检监察室');
addGenerateField('investigation_team_code', 'JC2024001');
addGenerateField('investigation_team_leader_name', '赵六');
addGenerateField('investigation_team_member_names', '赵六、钱七、孙八');
addGenerateField('investigation_location', '纪检监察室谈话室');
addGenerateField('handler_name', '王五');
addGenerateField('handling_department', '纪检监察室');
addGenerateField('commission_name', '中共某某市纪律检查委员会');
// 谈话相关
addGenerateField('interview_location', '纪检监察室谈话室');
addGenerateField('proposed_interview_location', '纪检监察室谈话室');
addGenerateField('notification_location', '纪检监察室');
addGenerateField('appointment_location', '纪检监察室谈话室');
addGenerateField('interview_time', '2024年12月10日14:00');
addGenerateField('proposed_interview_time', '2024年12月10日14:00');
addGenerateField('notification_time', '2024年12月9日');
addGenerateField('appointment_time', '2024年12月10日14:00');
addGenerateField('interview_reason', '就相关问题进行核实了解');
addGenerateField('interview_count', '1');
addGenerateField('interviewer', '赵六');
addGenerateField('recorder', '钱七');
addGenerateField('interview_personnel', '赵六、钱七');
addGenerateField('interview_personnel_leader', '赵六');
addGenerateField('interview_personnel_safety_officer', '孙八');
addGenerateField('backup_personnel', '周九');
// 审批和意见
addGenerateField('approval_time', '2024年12月8日');
addGenerateField('report_card_request_time', '2024年12月8日');
addGenerateField('department_opinion', '经初步核实,建议立案调查');
addGenerateField('assessment_opinion', '建议进行谈话核实');
addGenerateField('filler_name', '李四');
// 自动加载所有可用的文件列表
try {
const response = await fetch('/api/file-configs');
const result = await response.json();
if (result.isSuccess && result.data && result.data.fileConfigs) {
// 只添加有filePath的文件有模板文件的
const filesWithPath = result.data.fileConfigs.filter(f => f.filePath);
// 加载所有可用文件
filesWithPath.forEach(file => {
addFileItem(file.fileId, file.fileName);
});
if (filesWithPath.length > 0) {
console.log(`已自动加载 ${filesWithPath.length} 个可用文件模板`);
}
} else {
console.warn('未找到可用的文件配置');
}
} catch (error) {
console.warn('自动加载文件列表失败:', error);
}
}
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 = '') {
const container = document.getElementById('fileListContainer');
const fileDiv = document.createElement('div');
fileDiv.className = 'field-row';
fileDiv.innerHTML = `
<input type="number" placeholder="文件ID (从f_polic_file_config表获取)" value="${fileId}" class="file-id" style="width: 200px;">
<div style="display: flex; gap: 10px; flex: 1;">
<input type="text" placeholder="文件名称 (如: 初步核实审批表.doc)" value="${fileName}" class="file-name" 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();
if (fileId) {
fileList.push({
fileId: parseInt(fileId),
fileName: fileName || 'generated.docx' // fileName可选
});
}
});
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 || '(无路径)'}<br>`;
// 如果有下载链接,显示可点击的链接
if (file.downloadUrl) {
html += `下载链接: <a href="${file.downloadUrl}" target="_blank" style="color: #667eea; text-decoration: underline; word-break: break-all;">${file.downloadUrl}</a><br>`;
html += `<button class="btn btn-secondary" onclick="window.open('${file.downloadUrl}', '_blank')" style="margin-top: 5px; padding: 6px 15px; font-size: 14px;">📥 下载文档</button>`;
}
html += `</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 clearAllFiles() {
if (confirm('确定要清空所有文件列表吗?')) {
document.getElementById('fileListContainer').innerHTML = '';
}
}
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>