完成审计报告生成功能页面
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<!-- User编辑弹窗 -->
|
||||
<template>
|
||||
<a-drawer
|
||||
:width="`70%`"
|
||||
@@ -37,24 +37,30 @@
|
||||
|
||||
<a-card style="margin-bottom: 20px; text-align: center; background: transparent" :bordered="false">
|
||||
<a-space>
|
||||
<a-button size="large" type="primary" @click="handleGenerateAll" :loading="generatingAll" class="generate-all-button">
|
||||
<template #icon>
|
||||
<UngroupOutlined/>
|
||||
</template>
|
||||
生成全部方案
|
||||
</a-button>
|
||||
<a-button size="large">
|
||||
<template #icon>
|
||||
<DownloadOutlined/>
|
||||
</template>
|
||||
保存草稿
|
||||
</a-button>
|
||||
<a-button size="large">
|
||||
<template #icon>
|
||||
<RedoOutlined/>
|
||||
</template>
|
||||
加载草稿
|
||||
</a-button>
|
||||
<a-tooltip title="并行生成所有 9 个章节的内容(包含各小节)">
|
||||
<a-button size="large" type="primary" @click="handleGenerateAll" :loading="generatingAll" class="generate-all-button">
|
||||
<template #icon>
|
||||
<UngroupOutlined/>
|
||||
</template>
|
||||
生成全部方案
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="依次保存所有章节到数据库(每个章节独立保存)">
|
||||
<a-button size="large" @click="saveAllDrafts">
|
||||
<template #icon>
|
||||
<DownloadOutlined/>
|
||||
</template>
|
||||
保存草稿
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="从数据库加载所有已保存的章节内容">
|
||||
<a-button size="large" @click="loadDrafts">
|
||||
<template #icon>
|
||||
<RedoOutlined/>
|
||||
</template>
|
||||
加载草稿
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-button size="large" type="danger" class="export-button" @click="handleExport">
|
||||
<template #icon>
|
||||
<UploadOutlined/>
|
||||
@@ -120,16 +126,44 @@
|
||||
:bordered="false"
|
||||
>
|
||||
<template #extra>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="generateContent(index)"
|
||||
:loading="item.generating"
|
||||
>
|
||||
<template #icon>
|
||||
<RobotOutlined/>
|
||||
</template>
|
||||
AI生成
|
||||
</a-button>
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="reloadSectionData(index)"
|
||||
:loading="sectionReloading[index]"
|
||||
class="section-action-button reload-button"
|
||||
>
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
重载数据
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="saveSectionContent(index)"
|
||||
:loading="sectionSaving[index]"
|
||||
class="section-action-button save-button"
|
||||
>
|
||||
<template #icon>
|
||||
<SaveOutlined />
|
||||
</template>
|
||||
保存
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="generateContent(index)"
|
||||
:loading="item.generating"
|
||||
class="section-action-button ai-generate-button"
|
||||
>
|
||||
<template #icon>
|
||||
<RobotOutlined/>
|
||||
</template>
|
||||
AI 生成
|
||||
</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
<!-- <div v-if="item.description" class="section-description">-->
|
||||
<!-- {{ item.description }}-->
|
||||
@@ -172,21 +206,47 @@
|
||||
|
||||
<!-- 多内容部分 -->
|
||||
<template v-else>
|
||||
<div v-for="(child, childIndex) in item.children" :key="childIndex" class="child-section">
|
||||
<div class="child-title">
|
||||
{{ child.name }}
|
||||
<a-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click.stop="generateContent(index, childIndex)"
|
||||
:loading="child.generating"
|
||||
style="margin-left: 12px"
|
||||
>
|
||||
<template #icon>
|
||||
<RobotOutlined/>
|
||||
</template>
|
||||
AI生成
|
||||
</a-button>
|
||||
<div v-for="(child, childIndex) in item.children" :key="childIndex" class="child-section" style="border-left: 3px solid #1890ff; padding-left: 16px; margin-bottom: 20px;">
|
||||
<div class="child-title" style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<span style="font-weight: bold;">{{ child.name }}</span>
|
||||
<a-space>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="saveChildContent(index, childIndex)"
|
||||
:loading="child.saving"
|
||||
class="child-action-button save-button"
|
||||
>
|
||||
<template #icon>
|
||||
<SaveOutlined />
|
||||
</template>
|
||||
保存
|
||||
</a-button>
|
||||
<a-button
|
||||
type="default"
|
||||
size="small"
|
||||
@click="reloadChildContent(index, childIndex)"
|
||||
:loading="child.reloading"
|
||||
class="child-action-button reload-button"
|
||||
>
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
重载
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="generateContent(index, childIndex)"
|
||||
:loading="child.generating"
|
||||
class="child-action-button ai-generate-button"
|
||||
>
|
||||
<template #icon>
|
||||
<RobotOutlined/>
|
||||
</template>
|
||||
AI 生成
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-textarea
|
||||
v-model:value="child.content"
|
||||
@@ -244,9 +304,11 @@ import {
|
||||
UploadOutlined,
|
||||
RedoOutlined,
|
||||
RobotOutlined,
|
||||
QuestionCircleOutlined
|
||||
QuestionCircleOutlined,
|
||||
ReloadOutlined,
|
||||
SaveOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import {generateAuditReport, downloadAuditReport} from "@/api/ai/auditReport";
|
||||
import {generateAuditReport, downloadAuditReport, queryAuditReport, saveAuditReport} from "@/api/ai/auditReport";
|
||||
import {getPwlProjectLibraryByIds} from '@/api/pwl/pwlProjectLibrary';
|
||||
|
||||
const useForm = Form.useForm;
|
||||
@@ -264,6 +326,10 @@ const maxAble = ref(true);
|
||||
const generatingAll = ref(false);
|
||||
// 当前选中的章节
|
||||
const currentSection = ref(0);
|
||||
// 每个章节的重载状态
|
||||
const sectionReloading = reactive<Record<number, boolean>>({});
|
||||
// 每个章节的保存状态
|
||||
const sectionSaving = reactive<Record<number, boolean>>({});
|
||||
|
||||
// 存储拼接后的 kbIds
|
||||
const combinedKbIds = ref('');
|
||||
@@ -373,7 +439,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 51,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(二)公司发展战略规划的制定、执行和效果情况以及年度责任目标完成情况',
|
||||
@@ -381,7 +450,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 52,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(三)重大经济事项的决策、执行和效果情况',
|
||||
@@ -389,7 +461,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 53,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(四)公司法人治理结构的建立、健全和运行情况,内部控制制度的制定和执行情况',
|
||||
@@ -397,7 +472,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 54,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(五)公司财务的真实合法效益情况,风险管控情况,境外资产管理情况',
|
||||
@@ -405,7 +483,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 55,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(六)在经济活动中落实有关党风廉政建设责任和遵守廉洁从业规定情况',
|
||||
@@ -413,7 +494,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 56,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(七)对以往审计中发现问题的整改情况',
|
||||
@@ -421,7 +505,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 57,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(八)其他需要审计的事项',
|
||||
@@ -429,7 +516,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 58,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -437,7 +527,7 @@ const navigationItems = ref([
|
||||
number: '六',
|
||||
name: '重要风险的识别及应对',
|
||||
title: '6、重要风险的识别及应对',
|
||||
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
||||
description: '点击"AI 生成"按钮让 AI 为您生成该部分内容,或直接在下方编辑',
|
||||
generating: false,
|
||||
children: [
|
||||
{
|
||||
@@ -446,7 +536,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 61,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
},
|
||||
{
|
||||
name: '(二)风险的应对策略',
|
||||
@@ -454,7 +547,10 @@ const navigationItems = ref([
|
||||
content: '',
|
||||
suggestion: '',
|
||||
formCommit: 62,
|
||||
rows: 6
|
||||
rows: 6,
|
||||
generating: false,
|
||||
saving: false,
|
||||
reloading: false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -625,13 +721,24 @@ const getScrollContainer = () => {
|
||||
return document.querySelector('.ant-modal-body');
|
||||
};
|
||||
|
||||
/* 批量生成全部内容 */
|
||||
const handleGenerateAll2 = async () => {
|
||||
/* 批量生成全部内容 - 清空后并行生成 */
|
||||
const handleGenerateAll = async () => {
|
||||
generatingAll.value = true;
|
||||
try {
|
||||
// 使用Promise.all进行并行生成
|
||||
// 先清空所有内容
|
||||
navigationItems.value.forEach(item => {
|
||||
if (item.children) {
|
||||
item.children.forEach(child => {
|
||||
child.content = '';
|
||||
});
|
||||
} else {
|
||||
item.content = '';
|
||||
}
|
||||
});
|
||||
|
||||
// 使用 Promise.all 并行生成所有 9 个方案
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
|
||||
navigationItems.value.forEach((item, index) => {
|
||||
if (item.children) {
|
||||
// 有子项的章节,为每个子项生成内容
|
||||
@@ -643,7 +750,7 @@ const handleGenerateAll2 = async () => {
|
||||
promises.push(generateContent(index));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
await Promise.all(promises);
|
||||
message.success('全部内容生成完成');
|
||||
} catch (error) {
|
||||
@@ -654,39 +761,6 @@ const handleGenerateAll2 = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/* 批量生成全部内容 */
|
||||
const handleGenerateAll = async () => {
|
||||
generatingAll.value = true;
|
||||
try {
|
||||
// 改为顺序生成
|
||||
for (const [index, item] of navigationItems.value.entries()) {
|
||||
// 跳过未完成的依赖项
|
||||
if (index === 4 && !hasContent(3)) {
|
||||
message.warning('请先生成第四项「被审计单位基本情况」');
|
||||
continue;
|
||||
}
|
||||
if (index === 5 && !hasContent(4)) {
|
||||
message.warning('请先生成第五项「审计内容和重点及审计方法」');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.children) {
|
||||
for (const [childIndex] of item.children.entries()) {
|
||||
await generateContent(index, childIndex);
|
||||
}
|
||||
} else {
|
||||
await generateContent(index);
|
||||
}
|
||||
}
|
||||
message.success('全部内容生成完成');
|
||||
} catch (error) {
|
||||
console.error('批量生成失败:', error);
|
||||
message.error('部分内容生成失败,请检查');
|
||||
} finally {
|
||||
generatingAll.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 新增内容检查方法
|
||||
const hasContent = (index: number) => {
|
||||
const section = navigationItems.value[index];
|
||||
@@ -698,6 +772,541 @@ const hasContent = (index: number) => {
|
||||
return !!section.content?.trim();
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存所有方案到数据库(保存草稿)- 按章节分别保存
|
||||
*/
|
||||
const saveAllDrafts = async () => {
|
||||
if (!form.id) {
|
||||
message.warning('无项目信息');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 按章节顺序依次保存
|
||||
let savedCount = 0;
|
||||
|
||||
for (const navItem of navigationItems.value) {
|
||||
// 如果章节有子项,为每个子项单独保存一条记录
|
||||
if (navItem.children && navItem.children.length > 0) {
|
||||
// 有子项,为每个小节单独保存
|
||||
for (const child of navItem.children) {
|
||||
const currentChildData = {
|
||||
number: navItem.number,
|
||||
name: navItem.name,
|
||||
title: navItem.title,
|
||||
tableType: 'audit_report',
|
||||
content: '', // 主章节内容为空
|
||||
formCommit: navItem.formCommit,
|
||||
records: [{
|
||||
name: child.name,
|
||||
content: child.content || '',
|
||||
formCommit: child.formCommit
|
||||
}]
|
||||
};
|
||||
|
||||
// 构建当前小节的预览 HTML
|
||||
let previewHtml = '<div class="audit-report">';
|
||||
previewHtml += `<h3>${currentChildData.number}、${currentChildData.name}${currentChildData.title ? ' - ' + currentChildData.title : ''}</h3>`;
|
||||
previewHtml += `<div><strong>${child.name}:</strong> ${child.content || ''}</div>`;
|
||||
previewHtml += '</div>';
|
||||
|
||||
await saveAuditReport({
|
||||
projectId: form.id,
|
||||
projectName: form.name || '',
|
||||
caseIndex: form.caseIndex || '',
|
||||
auditedTarget: form.name || '',
|
||||
reportContent: JSON.stringify({
|
||||
sections: [currentChildData]
|
||||
}),
|
||||
previewHtml: previewHtml,
|
||||
sectionCount: 1,
|
||||
formCommit: child.formCommit || 1
|
||||
});
|
||||
|
||||
savedCount++;
|
||||
console.log(`[保存草稿] 已保存:${navItem.number}、${navItem.name} - ${child.name}, formCommit: ${child.formCommit}`);
|
||||
}
|
||||
} else {
|
||||
// 无子项,直接保存主章节
|
||||
const currentSectionData = {
|
||||
number: navItem.number,
|
||||
name: navItem.name,
|
||||
title: navItem.title,
|
||||
tableType: 'audit_report',
|
||||
content: navItem.content || '',
|
||||
formCommit: navItem.formCommit,
|
||||
records: []
|
||||
};
|
||||
|
||||
// 构建当前章节的预览 HTML
|
||||
let previewHtml = '<div class="audit-report">';
|
||||
previewHtml += `<h3>${currentSectionData.number}、${currentSectionData.name}${currentSectionData.title ? ' - ' + currentSectionData.title : ''}</h3>`;
|
||||
if (currentSectionData.content) {
|
||||
previewHtml += `<div>${currentSectionData.content}</div>`;
|
||||
}
|
||||
previewHtml += '</div>';
|
||||
|
||||
await saveAuditReport({
|
||||
projectId: form.id,
|
||||
projectName: form.name || '',
|
||||
caseIndex: form.caseIndex || '',
|
||||
auditedTarget: form.name || '',
|
||||
reportContent: JSON.stringify({
|
||||
sections: [currentSectionData]
|
||||
}),
|
||||
previewHtml: previewHtml,
|
||||
sectionCount: 1,
|
||||
formCommit: navItem.formCommit || 1
|
||||
});
|
||||
|
||||
savedCount++;
|
||||
console.log(`[保存草稿] 已保存:${navItem.number}、${navItem.name}, formCommit: ${navItem.formCommit}`);
|
||||
}
|
||||
}
|
||||
|
||||
message.success(`草稿已保存到数据库(共 ${savedCount} 条记录)`);
|
||||
} catch (e: any) {
|
||||
console.error('[保存草稿] 失败:', e);
|
||||
message.error('保存草稿失败:' + (e?.message || '未知错误'));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 从数据库加载草稿(根据每个方案的 formCommit 并行加载)
|
||||
*/
|
||||
const loadDrafts = async () => {
|
||||
if (!form.id) {
|
||||
message.warning('无项目信息');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[加载草稿] 开始加载,projectId:', form.id);
|
||||
|
||||
// 收集所有的 formCommit 值(包括主章节和所有小节)
|
||||
const formCommits: number[] = [];
|
||||
navigationItems.value.forEach(item => {
|
||||
if (item.formCommit) {
|
||||
// 主章节有 formCommit,直接使用
|
||||
formCommits.push(item.formCommit);
|
||||
} else if (item.children && item.children.length > 0) {
|
||||
// 有子项的章节,收集所有小节的 formCommit
|
||||
item.children.forEach(child => {
|
||||
if (child.formCommit) {
|
||||
formCommits.push(child.formCommit);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[加载草稿] 收集到的 formCommits:', formCommits);
|
||||
|
||||
// 并行查询所有 formCommit 的数据
|
||||
const promises = formCommits.map(async (fc) => {
|
||||
try {
|
||||
console.log('[加载草稿] 查询 formCommit:', fc);
|
||||
const res: any = await queryAuditReport({
|
||||
projectId: form.id,
|
||||
formCommit: fc
|
||||
});
|
||||
return { formCommit: fc, data: res?.data || null };
|
||||
} catch (e) {
|
||||
console.warn(`[加载草稿] 查询 formCommit=${fc} 失败:`, e);
|
||||
return { formCommit: fc, data: null };
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
console.log('[加载草稿] 查询结果:', results);
|
||||
|
||||
// 解析并填充数据
|
||||
let hasData = false;
|
||||
results.forEach(result => {
|
||||
if (result.data) {
|
||||
hasData = true;
|
||||
const reportContent = JSON.parse(result.data.reportContent || '{}');
|
||||
|
||||
if (reportContent.sections && Array.isArray(reportContent.sections)) {
|
||||
const section = reportContent.sections[0];
|
||||
if (section) {
|
||||
// 找到对应的 navigationItem(通过匹配任意一个 formCommit)
|
||||
const navItemIndex = navigationItems.value.findIndex(item =>
|
||||
item.formCommit === result.formCommit ||
|
||||
(item.children && item.children.some(child => child.formCommit === result.formCommit))
|
||||
);
|
||||
|
||||
if (navItemIndex >= 0) {
|
||||
const navItem = navigationItems.value[navItemIndex];
|
||||
|
||||
console.log(`[加载草稿] 恢复章节:${navItem.number}、${navItem.name}, formCommit: ${result.formCommit}`);
|
||||
|
||||
// 恢复内容
|
||||
if (navItem.children && section.records && Array.isArray(section.records)) {
|
||||
// 有子项,根据 formCommit 匹配每个小节
|
||||
section.records.forEach((rec: any) => {
|
||||
const childIndex = navItem.children.findIndex(child => child.formCommit === rec.formCommit);
|
||||
if (childIndex >= 0) {
|
||||
navItem.children[childIndex].content = rec.content || '';
|
||||
console.log(` - 恢复小节:${navItem.children[childIndex].name}, content length: ${rec.content?.length || 0}`);
|
||||
}
|
||||
});
|
||||
} else if (!navItem.children && section.content) {
|
||||
// 无子项,直接恢复主章节内容
|
||||
navItem.content = section.content || '';
|
||||
}
|
||||
} else {
|
||||
console.warn('[加载草稿] 未找到匹配的章节,formCommit:', result.formCommit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (hasData) {
|
||||
message.success('草稿已加载');
|
||||
} else {
|
||||
message.info('暂无保存的草稿');
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('[加载草稿] 失败:', e);
|
||||
message.error('加载草稿失败:' + (e?.message || '未知错误'));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存单个章节的内容到数据库
|
||||
*/
|
||||
const saveSectionContent = async (sectionIndex: number) => {
|
||||
if (!form.id) {
|
||||
message.warning('无项目信息');
|
||||
return;
|
||||
}
|
||||
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
if (!section) return;
|
||||
|
||||
sectionSaving[sectionIndex] = true;
|
||||
|
||||
try {
|
||||
// 如果章节有子项,为每个子项单独保存一条记录
|
||||
if (section.children && section.children.length > 0) {
|
||||
// 有子项,为每个小节单独保存
|
||||
let savedChildrenCount = 0;
|
||||
for (const child of section.children) {
|
||||
// 检查内容是否为空,为空则跳过不保存
|
||||
if (!child.content || !child.content.trim()) {
|
||||
console.log(`[保存整章] 跳过空内容:${section.number}、${section.name} - ${child.name}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentChildData = {
|
||||
number: section.number,
|
||||
name: section.name,
|
||||
title: section.title,
|
||||
tableType: 'audit_report',
|
||||
content: '', // 主章节内容为空
|
||||
formCommit: section.formCommit,
|
||||
records: [{
|
||||
name: child.name,
|
||||
content: child.content || '',
|
||||
formCommit: child.formCommit
|
||||
}]
|
||||
};
|
||||
|
||||
// 构建当前小节的预览 HTML
|
||||
let previewHtml = '<div class="audit-report">';
|
||||
previewHtml += `<h3>${currentChildData.number}、${currentChildData.name}${currentChildData.title ? ' - ' + currentChildData.title : ''}</h3>`;
|
||||
previewHtml += `<div><strong>${child.name}:</strong> ${child.content || ''}</div>`;
|
||||
previewHtml += '</div>';
|
||||
|
||||
await saveAuditReport({
|
||||
projectId: form.id,
|
||||
projectName: form.name || '',
|
||||
caseIndex: form.caseIndex || '',
|
||||
auditedTarget: form.name || '',
|
||||
reportContent: JSON.stringify({
|
||||
sections: [currentChildData]
|
||||
}),
|
||||
previewHtml: previewHtml,
|
||||
sectionCount: 1,
|
||||
formCommit: child.formCommit || 1
|
||||
});
|
||||
|
||||
savedChildrenCount++;
|
||||
console.log(`[保存整章] 已保存:${section.number}、${section.name} - ${child.name}, formCommit: ${child.formCommit}`);
|
||||
}
|
||||
|
||||
if (savedChildrenCount === 0) {
|
||||
message.info('该章节没有需要保存的内容(所有小节均为空)');
|
||||
} else {
|
||||
message.success(`${section.number}、${section.name} 已保存(共 ${savedChildrenCount}/${section.children.length} 个小节)`);
|
||||
}
|
||||
} else {
|
||||
// 无子项,直接保存主章节
|
||||
// 检查内容是否为空,为空则跳过不保存
|
||||
if (!section.content || !section.content.trim()) {
|
||||
console.log(`[保存整章] 跳过空内容:${section.number}、${section.name}`);
|
||||
message.info('该章节没有需要保存的内容');
|
||||
sectionSaving[sectionIndex] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSectionData = {
|
||||
number: section.number,
|
||||
name: section.name,
|
||||
title: section.title,
|
||||
tableType: 'audit_report',
|
||||
content: section.content || '',
|
||||
formCommit: section.formCommit,
|
||||
records: []
|
||||
};
|
||||
|
||||
// 只构建当前章节的预览 HTML
|
||||
let previewHtml = '<div class="audit-report">';
|
||||
previewHtml += `<h3>${currentSectionData.number}、${currentSectionData.name}${currentSectionData.title ? ' - ' + currentSectionData.title : ''}</h3>`;
|
||||
if (currentSectionData.content) {
|
||||
previewHtml += `<div>${currentSectionData.content}</div>`;
|
||||
}
|
||||
previewHtml += '</div>';
|
||||
|
||||
// 调用保存接口(使用当前章节的 formCommit)
|
||||
await saveAuditReport({
|
||||
projectId: form.id,
|
||||
projectName: form.name || '',
|
||||
caseIndex: form.caseIndex || '',
|
||||
auditedTarget: form.name || '',
|
||||
reportContent: JSON.stringify({
|
||||
sections: [currentSectionData] // 只保存当前章节
|
||||
}),
|
||||
previewHtml: previewHtml, // 只包含当前章节的 HTML
|
||||
sectionCount: 1, // 只有 1 个章节
|
||||
formCommit: section.formCommit || 1
|
||||
});
|
||||
|
||||
message.success(`${section.number}、${section.name} 已保存`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('保存失败:', e);
|
||||
message.error('保存失败:' + (e?.message || '未知错误'));
|
||||
} finally {
|
||||
sectionSaving[sectionIndex] = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存单个小节的内容到数据库
|
||||
*/
|
||||
const saveChildContent = async (sectionIndex: number, childIndex: number) => {
|
||||
if (!form.id) {
|
||||
message.warning('无项目信息');
|
||||
return;
|
||||
}
|
||||
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
if (!section || !section.children || !section.children[childIndex]) return;
|
||||
|
||||
const child = section.children[childIndex];
|
||||
|
||||
// 检查内容是否为空,为空则不保存
|
||||
if (!child.content || !child.content.trim()) {
|
||||
message.info('该小节内容为空,无需保存');
|
||||
return;
|
||||
}
|
||||
|
||||
child.saving = true;
|
||||
|
||||
try {
|
||||
// 构建只包含当前小节的数据结构
|
||||
const currentChildData = {
|
||||
number: section.number,
|
||||
name: section.name,
|
||||
title: section.title,
|
||||
tableType: 'audit_report',
|
||||
content: '', // 主章节内容为空
|
||||
formCommit: section.formCommit,
|
||||
records: [{
|
||||
name: child.name,
|
||||
content: child.content || '',
|
||||
formCommit: child.formCommit
|
||||
}]
|
||||
};
|
||||
|
||||
// 构建当前小节的预览 HTML
|
||||
let previewHtml = '<div class="audit-report">';
|
||||
previewHtml += `<h3>${currentChildData.number}、${currentChildData.name}${currentChildData.title ? ' - ' + currentChildData.title : ''}</h3>`;
|
||||
previewHtml += `<div><strong>${child.name}:</strong> ${child.content || ''}</div>`;
|
||||
previewHtml += '</div>';
|
||||
|
||||
// 调用保存接口(使用当前小节的 formCommit)
|
||||
await saveAuditReport({
|
||||
projectId: form.id,
|
||||
projectName: form.name || '',
|
||||
caseIndex: form.caseIndex || '',
|
||||
auditedTarget: form.name || '',
|
||||
reportContent: JSON.stringify({
|
||||
sections: [currentChildData]
|
||||
}),
|
||||
previewHtml: previewHtml,
|
||||
sectionCount: 1,
|
||||
formCommit: child.formCommit || 1
|
||||
});
|
||||
|
||||
message.success(`${child.name} 已保存`);
|
||||
} catch (e: any) {
|
||||
console.error('保存小节失败:', e);
|
||||
message.error('保存失败:' + (e?.message || '未知错误'));
|
||||
} finally {
|
||||
child.saving = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 重载单个章节的数据(包括所有子小节)
|
||||
*/
|
||||
const reloadSectionData = async (sectionIndex: number) => {
|
||||
if (!form.id) {
|
||||
message.warning('无项目信息');
|
||||
return;
|
||||
}
|
||||
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
if (!section) return;
|
||||
|
||||
sectionReloading[sectionIndex] = true;
|
||||
|
||||
try {
|
||||
// 如果章节有子项,需要查询所有子项的数据
|
||||
if (section.children && section.children.length > 0) {
|
||||
console.log(`[重载章节数据] 开始重载有子项的章节:${section.number}、${section.name}`);
|
||||
|
||||
// 收集所有子项的 formCommit
|
||||
const childFormCommits = section.children.map(child => child.formCommit).filter(fc => fc);
|
||||
console.log(`[重载章节数据] 子项 formCommits:`, childFormCommits);
|
||||
|
||||
// 并行查询所有子项的数据
|
||||
const promises = childFormCommits.map(async (fc) => {
|
||||
try {
|
||||
const res: any = await queryAuditReport({
|
||||
projectId: form.id,
|
||||
formCommit: fc
|
||||
});
|
||||
return { formCommit: fc, data: res?.data || null };
|
||||
} catch (e) {
|
||||
console.warn(`[重载章节数据] 查询 formCommit=${fc} 失败:`, e);
|
||||
return { formCommit: fc, data: null };
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
console.log(`[重载章节数据] 查询结果:`, results);
|
||||
|
||||
// 解析并填充每个子项的数据
|
||||
let loadedCount = 0;
|
||||
results.forEach(result => {
|
||||
if (result.data) {
|
||||
const reportContent = JSON.parse(result.data.reportContent || '{}');
|
||||
|
||||
if (reportContent.sections && Array.isArray(reportContent.sections)) {
|
||||
const sectionData = reportContent.sections[0];
|
||||
if (sectionData && sectionData.records && Array.isArray(sectionData.records)) {
|
||||
// 找到对应的子项并填充数据
|
||||
sectionData.records.forEach((rec: any) => {
|
||||
const childIndex = section.children.findIndex(child => child.formCommit === rec.formCommit);
|
||||
if (childIndex >= 0) {
|
||||
section.children[childIndex].content = rec.content || '';
|
||||
loadedCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[重载章节数据] 已加载 ${loadedCount}/${section.children.length} 个小节的数据`);
|
||||
|
||||
if (loadedCount === 0) {
|
||||
message.info('该章节暂无保存的数据');
|
||||
} else {
|
||||
message.success(`${section.number}、${section.name} 数据已重新加载(共 ${loadedCount}/${section.children.length} 个小节)`);
|
||||
}
|
||||
} else {
|
||||
// 无子项,直接加载主章节
|
||||
const formCommitToUse = section.formCommit || 1;
|
||||
|
||||
console.log(`[重载章节数据] 开始重载主章节,sectionIndex: ${sectionIndex}, formCommit: ${formCommitToUse}`);
|
||||
|
||||
const res: any = await queryAuditReport({
|
||||
projectId: form.id,
|
||||
formCommit: formCommitToUse
|
||||
});
|
||||
|
||||
if (res.data) {
|
||||
const reportContent = JSON.parse(res.data.reportContent || '{}');
|
||||
|
||||
if (reportContent.sections && Array.isArray(reportContent.sections)) {
|
||||
const sectionData = reportContent.sections[0];
|
||||
if (sectionData) {
|
||||
section.content = sectionData.content || '';
|
||||
console.log(`[重载章节数据] 已加载主章节内容,长度:${section.content?.length || 0}`);
|
||||
message.success(`${section.number}、${section.name} 数据已重新加载`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 没有数据时不提示
|
||||
}
|
||||
} catch (e: any) {
|
||||
console.error('重载章节数据失败:', e);
|
||||
message.error('重载失败:' + (e?.message || '未知错误'));
|
||||
} finally {
|
||||
sectionReloading[sectionIndex] = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 重载单个小节的数据
|
||||
*/
|
||||
const reloadChildContent = async (sectionIndex: number, childIndex: number) => {
|
||||
if (!form.id) {
|
||||
message.warning('无项目信息');
|
||||
return;
|
||||
}
|
||||
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
if (!section || !section.children || !section.children[childIndex]) return;
|
||||
|
||||
const child = section.children[childIndex];
|
||||
child.reloading = true;
|
||||
|
||||
try {
|
||||
const res: any = await queryAuditReport({
|
||||
projectId: form.id,
|
||||
formCommit: child.formCommit
|
||||
});
|
||||
|
||||
if (res.data) {
|
||||
const reportContent = JSON.parse(res.data.reportContent || '{}');
|
||||
|
||||
if (reportContent.sections && Array.isArray(reportContent.sections)) {
|
||||
const sectionData = reportContent.sections[0]; // 取第一个章节
|
||||
if (sectionData && sectionData.records && Array.isArray(sectionData.records)) {
|
||||
// 找到对应的小节记录
|
||||
const record = sectionData.records.find((r: any) => r.formCommit === child.formCommit);
|
||||
if (record) {
|
||||
child.content = record.content || '';
|
||||
message.success(`${child.name} 数据已重新加载`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 没有数据时不提示
|
||||
} catch (e: any) {
|
||||
console.error('重载小节数据失败:', e);
|
||||
message.error('重载失败:' + (e?.message || '未知错误'));
|
||||
} finally {
|
||||
child.reloading = false;
|
||||
}
|
||||
};
|
||||
|
||||
/* 新增:构建导出数据的公共方法 */
|
||||
const buildExportData = () => {
|
||||
const exportData: any = {
|
||||
@@ -914,6 +1523,9 @@ watch(
|
||||
|
||||
// 重置到第一个章节
|
||||
currentSection.value = 0;
|
||||
|
||||
// 自动从数据库加载审计报告
|
||||
await loadAuditReportFromDB();
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
@@ -1067,6 +1679,53 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/* 章节操作按钮统一样式 */
|
||||
.section-action-button {
|
||||
border-radius: 20px !important;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
/* 重载数据按钮 - 蓝色 */
|
||||
:deep(.reload-button) {
|
||||
background-color: #1890ff !important;
|
||||
border-color: #1890ff !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #40a9ff !important;
|
||||
border-color: #40a9ff !important;
|
||||
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.4) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 保存按钮 - 绿色 */
|
||||
:deep(.save-button) {
|
||||
background-color: #52c41a !important;
|
||||
border-color: #52c41a !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #73d13d !important;
|
||||
border-color: #73d13d !important;
|
||||
box-shadow: 0 4px 12px rgba(82, 196, 26, 0.4) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* AI 生成按钮 - 紫色 */
|
||||
:deep(.ai-generate-button) {
|
||||
background-color: #722ed1 !important;
|
||||
border-color: #722ed1 !important;
|
||||
|
||||
&:hover {
|
||||
background-color: #9254de !important;
|
||||
border-color: #9254de !important;
|
||||
box-shadow: 0 4px 12px rgba(114, 46, 209, 0.4) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-container {
|
||||
.nav-grid {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user