Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 234b376adb | |||
| 65235ec961 |
@@ -780,7 +780,7 @@ export async function generateAuditEvidence(data: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载审计取证单Word文档
|
* 下载审计取证单 Word 文档
|
||||||
*/
|
*/
|
||||||
export async function downloadAuditEvidence(data: {
|
export async function downloadAuditEvidence(data: {
|
||||||
caseIndex?: string;
|
caseIndex?: string;
|
||||||
@@ -815,3 +815,40 @@ export async function downloadAuditEvidence(data: {
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error('文件下载失败'));
|
return Promise.reject(new Error('文件下载失败'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存审计取证单到数据库
|
||||||
|
*/
|
||||||
|
export async function saveAuditEvidence(data: {
|
||||||
|
caseIndex?: string;
|
||||||
|
projectId?: number;
|
||||||
|
projectName?: string;
|
||||||
|
auditedTarget?: string;
|
||||||
|
contentType?: number;
|
||||||
|
auditMatterType?: string;
|
||||||
|
auditMatter?: string;
|
||||||
|
summaryTitle?: string;
|
||||||
|
auditRecord?: string;
|
||||||
|
auditFinding?: string;
|
||||||
|
evidenceBasis?: string;
|
||||||
|
handling?: string;
|
||||||
|
suggestion?: string;
|
||||||
|
attachment?: string;
|
||||||
|
auditors?: string;
|
||||||
|
compileDate?: string;
|
||||||
|
providerOpinion?: string;
|
||||||
|
providerDate?: string;
|
||||||
|
attachmentPages?: string;
|
||||||
|
feedbackDeadline?: string;
|
||||||
|
history?: string;
|
||||||
|
}) {
|
||||||
|
const res = await request.post<ApiResult<any>>(
|
||||||
|
MODULES_API_URL + '/ai/auditEvidence/save',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
@@ -68,3 +68,119 @@ export async function generateAuditReport2(
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.data.message));
|
return Promise.reject(new Error(res.data.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存审计报告到数据库
|
||||||
|
*/
|
||||||
|
export async function saveAuditReport(data: {
|
||||||
|
projectId?: number;
|
||||||
|
projectName?: string;
|
||||||
|
caseIndex?: string;
|
||||||
|
auditedTarget?: string;
|
||||||
|
reportContent?: string;
|
||||||
|
previewHtml?: string;
|
||||||
|
sectionCount?: number;
|
||||||
|
formCommit?: number;
|
||||||
|
}) {
|
||||||
|
const res = await request.post<ApiResult<any>>(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/save',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询审计报告
|
||||||
|
*/
|
||||||
|
export async function queryAuditReport(params: {
|
||||||
|
projectId: number;
|
||||||
|
formCommit: number;
|
||||||
|
}) {
|
||||||
|
const res = await request.post<ApiResult<any>>(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/query',
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
params
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据项目 ID 查询审计报告和取证单数据
|
||||||
|
* @param projectId 项目 ID
|
||||||
|
* @param evidenceIds 勾选的取证单 ID 列表(可选)
|
||||||
|
*/
|
||||||
|
export async function queryAuditDataByProjectId(projectId: number, evidenceIds?: number[]) {
|
||||||
|
console.log('=== 前端发起请求 ===');
|
||||||
|
console.log('projectId:', projectId);
|
||||||
|
console.log('evidenceIds:', evidenceIds);
|
||||||
|
|
||||||
|
const res = await request.post<ApiResult<any>>(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/queryAuditDataByProjectId',
|
||||||
|
evidenceIds ? { evidenceIds } : {},
|
||||||
|
{
|
||||||
|
params: { projectId },
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json;charset=UTF-8'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('=== 后端返回响应 ===', res);
|
||||||
|
console.log('res.data.code:', res.data.code);
|
||||||
|
console.log('res.data.message:', res.data.message);
|
||||||
|
console.log('res.data.data:', res.data.data);
|
||||||
|
|
||||||
|
if (res.data.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
console.error('API 返回错误 - code:', res.data.code, ', message:', res.data.message);
|
||||||
|
return Promise.reject(new Error(res.data.message || '操作失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据项目 ID 生成审计报告并下载
|
||||||
|
*/
|
||||||
|
export async function generateAuditReportByProjectId(projectId: number) {
|
||||||
|
const res = await request.post(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/generateByProjectId',
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
params: { projectId },
|
||||||
|
responseType: 'blob' // 处理二进制流响应
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error('文件下载失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据项目 ID 和选中的取证单生成审计报告并下载
|
||||||
|
*/
|
||||||
|
export async function generateAuditReportWithEvidences(projectId: number, evidenceIds: number[]) {
|
||||||
|
const res = await request.post(
|
||||||
|
MODULES_API_URL + '/ai/auditReport/generateWithEvidences',
|
||||||
|
{ evidenceIds },
|
||||||
|
{
|
||||||
|
params: { projectId },
|
||||||
|
responseType: 'blob' // 处理二进制流响应
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error('文件下载失败'));
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@ export interface PwlProject {
|
|||||||
name?: string;
|
name?: string;
|
||||||
// 项目标识
|
// 项目标识
|
||||||
code?: string;
|
code?: string;
|
||||||
|
// 针对用户名称
|
||||||
|
personName?: string;
|
||||||
|
// 职务
|
||||||
|
position?: string;
|
||||||
// 案引号
|
// 案引号
|
||||||
caseIndex?: string;
|
caseIndex?: string;
|
||||||
// 上级id, 0是顶级
|
// 上级id, 0是顶级
|
||||||
|
|||||||
@@ -58,7 +58,8 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-input
|
<a-textarea
|
||||||
|
:rows="4"
|
||||||
v-model:value="activeFormData[field.dataIndex]"
|
v-model:value="activeFormData[field.dataIndex]"
|
||||||
:placeholder="`请输入${field.title}`"
|
:placeholder="`请输入${field.title}`"
|
||||||
/>
|
/>
|
||||||
@@ -90,31 +91,31 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch, computed } from 'vue';
|
import { ref, watch, computed } from 'vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
record: any;
|
record: any;
|
||||||
fields: any[];
|
fields: any[];
|
||||||
records?: any[];
|
records?: any[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'save']);
|
const emit = defineEmits(['update:visible', 'save']);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const formDataList = ref<any[]>([]);
|
const formDataList = ref<any[]>([]);
|
||||||
const selectedRecordIndex = ref(0);
|
const selectedRecordIndex = ref(0);
|
||||||
|
|
||||||
const displayRecords = computed(() => {
|
const displayRecords = computed(() => {
|
||||||
if (props.records && Array.isArray(props.records) && props.records.length) {
|
if (props.records && Array.isArray(props.records) && props.records.length) {
|
||||||
return props.records;
|
return props.records;
|
||||||
}
|
}
|
||||||
if (props.record) return [props.record];
|
if (props.record) return [props.record];
|
||||||
return [];
|
return [];
|
||||||
});
|
});
|
||||||
|
|
||||||
const transformRecordToFormData = (record: any) => {
|
const transformRecordToFormData = (record: any) => {
|
||||||
if (!record) return {};
|
if (!record) return {};
|
||||||
const recordCopy = JSON.parse(JSON.stringify(record));
|
const recordCopy = JSON.parse(JSON.stringify(record));
|
||||||
|
|
||||||
@@ -126,7 +127,9 @@ const transformRecordToFormData = (record: any) => {
|
|||||||
recordCopy.workPaperIndex = recordCopy.workPaperIndex
|
recordCopy.workPaperIndex = recordCopy.workPaperIndex
|
||||||
.map((item: any) => {
|
.map((item: any) => {
|
||||||
if (typeof item === 'object') {
|
if (typeof item === 'object') {
|
||||||
return `${item.fileId || ''}||${item.fileName || ''}||${item.fileUrl || ''}`;
|
return `${item.fileId || ''}||${item.fileName || ''}||${
|
||||||
|
item.fileUrl || ''
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
})
|
})
|
||||||
@@ -134,24 +137,27 @@ const transformRecordToFormData = (record: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return recordCopy;
|
return recordCopy;
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasWorkPaperIndexField = computed(() => {
|
const hasWorkPaperIndexField = computed(() => {
|
||||||
return (props.fields || []).some((field) => {
|
return (props.fields || []).some((field) => {
|
||||||
return field?.dataIndex === 'workPaperIndex' || field?.key === 'workPaperIndex';
|
return (
|
||||||
|
field?.dataIndex === 'workPaperIndex' || field?.key === 'workPaperIndex'
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// 处理字段,将嵌套结构展平
|
// 处理字段,将嵌套结构展平
|
||||||
const processedFields = computed(() => {
|
const processedFields = computed(() => {
|
||||||
const processed: any[] = [];
|
const processed: any[] = [];
|
||||||
|
|
||||||
(props.fields || []).forEach(field => {
|
(props.fields || []).forEach((field) => {
|
||||||
if (field.children && Array.isArray(field.children)) {
|
if (field.children && Array.isArray(field.children)) {
|
||||||
// 处理有子字段的情况(如职务)
|
// 处理有子字段的情况(如职务)
|
||||||
processed.push({
|
processed.push({
|
||||||
...field,
|
...field,
|
||||||
children: field.children.flatMap(child =>
|
children: field.children.flatMap(
|
||||||
|
(child) =>
|
||||||
child.children && Array.isArray(child.children)
|
child.children && Array.isArray(child.children)
|
||||||
? child.children // 如果是多层嵌套,直接取孙子字段
|
? child.children // 如果是多层嵌套,直接取孙子字段
|
||||||
: child // 否则就是子字段
|
: child // 否则就是子字段
|
||||||
@@ -163,9 +169,9 @@ const processedFields = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(visible) => {
|
(visible) => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@@ -176,9 +182,9 @@ watch(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
if (!formDataList.value || formDataList.value.length === 0) {
|
if (!formDataList.value || formDataList.value.length === 0) {
|
||||||
message.warning('没有数据可保存');
|
message.warning('没有数据可保存');
|
||||||
return;
|
return;
|
||||||
@@ -187,8 +193,14 @@ const handleOk = () => {
|
|||||||
// 处理workPaperIndex:将字符串转换回对象数组
|
// 处理workPaperIndex:将字符串转换回对象数组
|
||||||
const dataToSave = formDataList.value.map((item) => {
|
const dataToSave = formDataList.value.map((item) => {
|
||||||
const cloned = JSON.parse(JSON.stringify(item || {}));
|
const cloned = JSON.parse(JSON.stringify(item || {}));
|
||||||
if (hasWorkPaperIndexField.value && cloned.workPaperIndex && typeof cloned.workPaperIndex === 'string') {
|
if (
|
||||||
const lines = cloned.workPaperIndex.split('\n').filter((line: string) => line.trim() !== '');
|
hasWorkPaperIndexField.value &&
|
||||||
|
cloned.workPaperIndex &&
|
||||||
|
typeof cloned.workPaperIndex === 'string'
|
||||||
|
) {
|
||||||
|
const lines = cloned.workPaperIndex
|
||||||
|
.split('\n')
|
||||||
|
.filter((line: string) => line.trim() !== '');
|
||||||
cloned.workPaperIndex = lines.map((line: string) => {
|
cloned.workPaperIndex = lines.map((line: string) => {
|
||||||
const parts = line.split('||');
|
const parts = line.split('||');
|
||||||
if (parts.length >= 3) {
|
if (parts.length >= 3) {
|
||||||
@@ -208,13 +220,13 @@ const handleOk = () => {
|
|||||||
emit('save', dataToSave);
|
emit('save', dataToSave);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectRecord = (index: number) => {
|
const selectRecord = (index: number) => {
|
||||||
if (index < 0 || index >= displayRecords.value.length) return;
|
if (index < 0 || index >= displayRecords.value.length) return;
|
||||||
selectedRecordIndex.value = index;
|
selectedRecordIndex.value = index;
|
||||||
if (!formDataList.value[index]) {
|
if (!formDataList.value[index]) {
|
||||||
@@ -222,13 +234,14 @@ const selectRecord = (index: number) => {
|
|||||||
displayRecords.value[index]
|
displayRecords.value[index]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const activeFormData = computed({
|
const activeFormData = computed({
|
||||||
get() {
|
get() {
|
||||||
if (!displayRecords.value.length) return {};
|
if (!displayRecords.value.length) return {};
|
||||||
if (!formDataList.value[selectedRecordIndex.value]) {
|
if (!formDataList.value[selectedRecordIndex.value]) {
|
||||||
formDataList.value[selectedRecordIndex.value] = transformRecordToFormData(
|
formDataList.value[selectedRecordIndex.value] =
|
||||||
|
transformRecordToFormData(
|
||||||
displayRecords.value[selectedRecordIndex.value]
|
displayRecords.value[selectedRecordIndex.value]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -238,59 +251,59 @@ const activeFormData = computed({
|
|||||||
if (!displayRecords.value.length) return;
|
if (!displayRecords.value.length) return;
|
||||||
formDataList.value[selectedRecordIndex.value] = val || {};
|
formDataList.value[selectedRecordIndex.value] = val || {};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.nested-fields {
|
.nested-fields {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
border: 1px solid #f0f0f0;
|
border: 1px solid #f0f0f0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-group-title {
|
.field-group-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
color: #1890ff;
|
color: #1890ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-group-content {
|
.field-group-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nested-field-item {
|
.nested-field-item {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-title {
|
.modal-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-label {
|
.record-label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 36px;
|
width: 36px;
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-text {
|
.record-text {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-item {
|
.record-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background-color 0.2s;
|
transition: background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-item:hover {
|
.record-item:hover {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-item.active {
|
.record-item.active {
|
||||||
background-color: #e6f7ff;
|
background-color: #e6f7ff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -11,9 +11,11 @@
|
|||||||
<a-space>
|
<a-space>
|
||||||
<a-button @click="resetFields">重置内容</a-button>
|
<a-button @click="resetFields">重置内容</a-button>
|
||||||
<a-button type="primary" @click="handleExport" :loading="exporting">
|
<a-button type="primary" @click="handleExport" :loading="exporting">
|
||||||
导出Word文档
|
导出 Word 文档
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="handleSave" :loading="saving">
|
||||||
|
保存到数据库
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button v-if="false" @click="printEvidence">打印预览</a-button>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
<div class="action-tip"
|
<div class="action-tip"
|
||||||
>可直接在表格中编辑,导出即可生成与效果图一致的取证单</div
|
>可直接在表格中编辑,导出即可生成与效果图一致的取证单</div
|
||||||
@@ -70,11 +72,22 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>审计(调查)事项</th>
|
<th>审计(调查)事项</th>
|
||||||
<td>
|
<td>
|
||||||
<textarea
|
<a-select style="min-width: 600px"
|
||||||
v-model="form.auditMatter"
|
v-model:value="form.auditMatter"
|
||||||
class="cell-input single"
|
placeholder="请选择审计(调查)事项"
|
||||||
placeholder="填写审计(调查)事项"
|
>
|
||||||
></textarea>
|
<a-select-option
|
||||||
|
v-for="(item, index) in auditMatterOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="item"
|
||||||
|
>{{ item }}</a-select-option
|
||||||
|
>
|
||||||
|
</a-select>
|
||||||
|
<!-- <textarea-->
|
||||||
|
<!-- v-model="form.auditMatter"-->
|
||||||
|
<!-- class="cell-input single"-->
|
||||||
|
<!-- placeholder="填写审计(调查)事项"-->
|
||||||
|
<!-- ></textarea>-->
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@@ -253,15 +266,19 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PropType, reactive, ref, watch } from 'vue';
|
import { PropType, reactive, ref, watch } from 'vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
import { PwlProject } from '@/api/pwl/pwlProject/model';
|
import { PwlProject } from '@/api/pwl/pwlProject/model';
|
||||||
import { downloadAuditEvidence } from '@/api/ai/auditContent';
|
import {
|
||||||
|
downloadAuditEvidence,
|
||||||
|
saveAuditEvidence
|
||||||
|
} from '@/api/ai/auditContent';
|
||||||
|
|
||||||
type BaseInfo = {
|
type BaseInfo = {
|
||||||
caseIndex?: string;
|
caseIndex?: string;
|
||||||
projectName?: string;
|
projectName?: string;
|
||||||
auditedTarget?: string;
|
auditedTarget?: string;
|
||||||
auditMatter?: string;
|
auditMatter?: string;
|
||||||
|
auditMatterType?: string; // 审计事项类型
|
||||||
};
|
};
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -283,12 +300,24 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const auditMatterOptions = [
|
||||||
|
'贯彻执行党和国家经济方针政策、决策部署情况',
|
||||||
|
'企业发展战略规划的制定、执行和效果情况',
|
||||||
|
'重大经济事项的决策、执行和效果情况',
|
||||||
|
'企业法人治理结构的建立、健全和运行情况,内部控制制度的制定和执行情况',
|
||||||
|
'企业财务的真实合法效益情况,风险管控情况,境外资产管理情况,生态环境保护情况',
|
||||||
|
'在经济活动中落实有关党风廉政建设责任和遵守廉洁从业规定情况',
|
||||||
|
'以往审计发现问题的整改情况',
|
||||||
|
'其他需要审计的内容'
|
||||||
|
];
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: 'update:visible', value: boolean): void;
|
(e: 'update:visible', value: boolean): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const printArea = ref<HTMLElement | null>(null);
|
const printArea = ref<HTMLElement | null>(null);
|
||||||
const exporting = ref(false);
|
const exporting = ref(false);
|
||||||
|
const saving = ref(false);
|
||||||
|
|
||||||
const defaultForm = () => ({
|
const defaultForm = () => ({
|
||||||
caseIndex: '',
|
caseIndex: '',
|
||||||
@@ -297,6 +326,7 @@
|
|||||||
projectName: '',
|
projectName: '',
|
||||||
auditedTarget: '',
|
auditedTarget: '',
|
||||||
auditMatter: '',
|
auditMatter: '',
|
||||||
|
auditMatterType: '', // 审计事项类型编码(code)
|
||||||
summaryTitle: '',
|
summaryTitle: '',
|
||||||
auditRecord: '',
|
auditRecord: '',
|
||||||
auditFinding: '',
|
auditFinding: '',
|
||||||
@@ -334,21 +364,51 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const applyBaseInfo = () => {
|
const applyBaseInfo = () => {
|
||||||
console.log('applyBaseInfo called, selectedRows:', props.selectedRows);
|
console.log('===== applyBaseInfo 开始执行 =====');
|
||||||
console.log('baseInfo:', props.baseInfo);
|
console.log('1. selectedRows:', props.selectedRows);
|
||||||
|
console.log('2. baseInfo:', props.baseInfo);
|
||||||
|
console.log(
|
||||||
|
'3. baseInfo.auditMatterType:',
|
||||||
|
props.baseInfo?.auditMatterType
|
||||||
|
);
|
||||||
|
|
||||||
// 重置表单为默认值
|
// 重置表单为默认值
|
||||||
Object.assign(form, defaultForm(), props.baseInfo || {});
|
Object.assign(form, defaultForm(), props.baseInfo || {});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'4. Object.assign 后的 form.auditMatterType:',
|
||||||
|
form.auditMatterType
|
||||||
|
);
|
||||||
|
|
||||||
|
// 特殊处理:确保 auditMatterType 被正确复制(Object.assign 可能会覆盖)
|
||||||
|
if (props.baseInfo?.auditMatterType) {
|
||||||
|
form.auditMatterType = props.baseInfo.auditMatterType;
|
||||||
|
console.log(
|
||||||
|
'✓ 5. 显式设置后的 form.auditMatterType:',
|
||||||
|
form.auditMatterType
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.warn('✗ 5. auditMatterType 为空或未设置,无法复制');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('===== applyBaseInfo 执行完毕 =====');
|
||||||
|
|
||||||
// 如果有传入的selectedRows,直接使用第一个对象的数据(生成取证单时通常只有一个)
|
// 如果有传入的selectedRows,直接使用第一个对象的数据(生成取证单时通常只有一个)
|
||||||
if (props.selectedRows && props.selectedRows.length > 0) {
|
if (props.selectedRows && props.selectedRows.length > 0) {
|
||||||
const evidenceData = props.selectedRows[0] || {};
|
const evidenceData = props.selectedRows[0] || {};
|
||||||
console.log('Evidence data from selectedRows:', evidenceData);
|
// console.log('Evidence data from selectedRows:', evidenceData);
|
||||||
|
|
||||||
// 直接将后端返回的数据映射到表单字段
|
// 直接将后端返回的数据映射到表单字段
|
||||||
form.caseIndex =
|
form.caseIndex =
|
||||||
props.baseInfo?.caseIndex || props.project?.caseIndex || form.caseIndex || '';
|
props.baseInfo?.caseIndex ||
|
||||||
form.projectName = props.project?.code || evidenceData.projectName || form.projectName || '';
|
props.project?.caseIndex ||
|
||||||
|
form.caseIndex ||
|
||||||
|
'';
|
||||||
|
form.projectName =
|
||||||
|
props.project?.code ||
|
||||||
|
evidenceData.projectName ||
|
||||||
|
form.projectName ||
|
||||||
|
'';
|
||||||
form.auditedTarget =
|
form.auditedTarget =
|
||||||
evidenceData.auditedTarget || form.auditedTarget || '';
|
evidenceData.auditedTarget || form.auditedTarget || '';
|
||||||
form.auditMatter = evidenceData.auditMatter || form.auditMatter || '';
|
form.auditMatter = evidenceData.auditMatter || form.auditMatter || '';
|
||||||
@@ -402,10 +462,10 @@
|
|||||||
if (!form.pageIndex) form.pageIndex = '1';
|
if (!form.pageIndex) form.pageIndex = '1';
|
||||||
if (!form.pageTotal) form.pageTotal = '1';
|
if (!form.pageTotal) form.pageTotal = '1';
|
||||||
|
|
||||||
console.log(
|
// console.log(
|
||||||
'Form data after applyBaseInfo:',
|
// 'Form data after applyBaseInfo:',
|
||||||
JSON.stringify(form, null, 2)
|
// JSON.stringify(form, null, 2)
|
||||||
);
|
// );
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -419,7 +479,11 @@
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.baseInfo,
|
() => props.baseInfo,
|
||||||
() => {
|
(newVal) => {
|
||||||
|
console.log(
|
||||||
|
'watch baseInfo triggered, newVal.auditMatterType:',
|
||||||
|
newVal?.auditMatterType
|
||||||
|
);
|
||||||
if (props.visible) {
|
if (props.visible) {
|
||||||
applyBaseInfo();
|
applyBaseInfo();
|
||||||
}
|
}
|
||||||
@@ -456,6 +520,74 @@
|
|||||||
message.success('内容已重置');
|
message.success('内容已重置');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存审计取证单到数据库
|
||||||
|
*/
|
||||||
|
const handleSave = async () => {
|
||||||
|
// 弹出确认对话框
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认保存',
|
||||||
|
content: '是否确认将当前取证单数据保存到数据库?',
|
||||||
|
okText: '是',
|
||||||
|
cancelText: '否',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
saving.value = true;
|
||||||
|
|
||||||
|
// 准备保存数据
|
||||||
|
const saveData = {
|
||||||
|
caseIndex: form.caseIndex,
|
||||||
|
projectId: props.project?.id,
|
||||||
|
projectName: form.projectName,
|
||||||
|
auditedTarget: form.auditedTarget,
|
||||||
|
contentType: 1, // 默认值,可根据实际情况调整
|
||||||
|
auditMatterType: form.auditMatterType, // 从 form 中获取(必须是 code)
|
||||||
|
auditMatter: form.auditMatter,
|
||||||
|
summaryTitle: form.summaryTitle,
|
||||||
|
auditRecord: form.auditRecord,
|
||||||
|
auditFinding: form.auditFinding,
|
||||||
|
evidenceBasis: form.evidenceBasis,
|
||||||
|
handling: form.handling,
|
||||||
|
suggestion: form.suggestion,
|
||||||
|
attachment: form.attachment,
|
||||||
|
auditors: form.auditors,
|
||||||
|
compileDate: form.compileDate,
|
||||||
|
providerOpinion: form.providerOpinion,
|
||||||
|
providerDate: form.providerDate,
|
||||||
|
attachmentPages: form.attachmentPages,
|
||||||
|
feedbackDeadline: form.feedbackDeadline,
|
||||||
|
history: '' // 历史内容,如果需要的话
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('=== 保存取证单 ===');
|
||||||
|
console.log('form:', form);
|
||||||
|
console.log('form.auditMatterType:', form.auditMatterType);
|
||||||
|
console.log('saveData.auditMatterType:', saveData.auditMatterType);
|
||||||
|
|
||||||
|
// 调用保存接口
|
||||||
|
const result = await saveAuditEvidence(saveData);
|
||||||
|
|
||||||
|
if (result.code === 0) {
|
||||||
|
message.success('取证单保存成功');
|
||||||
|
// 可以选择关闭弹窗或者清空表单
|
||||||
|
// emit('update:visible', false);
|
||||||
|
} else {
|
||||||
|
message.error('取证单保存失败:' + (result.message || '未知错误'));
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('保存失败:', error);
|
||||||
|
message.error('取证单保存失败:' + (error.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
// 用户选择"否",不做任何操作
|
||||||
|
console.log('用户取消保存');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出Word文档
|
* 导出Word文档
|
||||||
*/
|
*/
|
||||||
@@ -494,7 +626,9 @@
|
|||||||
link.href = url;
|
link.href = url;
|
||||||
|
|
||||||
// 设置文件名
|
// 设置文件名
|
||||||
const fileName = `审计取证单_${form.projectName || '取证单'}_${form.caseIndex || ''}.docx`;
|
const fileName = `审计取证单_${form.projectName || '取证单'}_${
|
||||||
|
form.caseIndex || ''
|
||||||
|
}.docx`;
|
||||||
link.setAttribute('download', fileName);
|
link.setAttribute('download', fileName);
|
||||||
|
|
||||||
// 触发下载
|
// 触发下载
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
styleResponsive ? { md: 19, sm: 19, xs: 24 } : { flex: '1' }
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
<!-- 项目信息标题置顶 -->
|
||||||
|
<a-divider orientation="left">项目信息</a-divider>
|
||||||
<a-form-item label="被审计单位" name="name">
|
<a-form-item label="被审计单位" name="name">
|
||||||
<SelectCompany v-model:value="form.name" @done="onCompany" />
|
<SelectCompany v-model:value="form.name" @done="onCompany" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -49,6 +51,22 @@
|
|||||||
v-model:value="form.code"
|
v-model:value="form.code"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="针对用户名称" name="personName">
|
||||||
|
<a-input
|
||||||
|
allow-clear
|
||||||
|
style="width: 400px"
|
||||||
|
placeholder="请输入针对用户名称"
|
||||||
|
v-model:value="form.personName"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="职务" name="position">
|
||||||
|
<a-input
|
||||||
|
allow-clear
|
||||||
|
style="width: 400px"
|
||||||
|
placeholder="请输入职务"
|
||||||
|
v-model:value="form.position"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label="案引号" name="caseIndex">
|
<a-form-item label="案引号" name="caseIndex">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
@@ -86,12 +104,11 @@
|
|||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-divider orientation="left" style="margin-top: 50px">项目信息</a-divider>
|
|
||||||
<a-form-item label="开票单位/汇款人" name="itemName">
|
<a-form-item label="开票单位/汇款人" name="itemName">
|
||||||
<a-input
|
<a-input
|
||||||
allow-clear
|
allow-clear
|
||||||
style="width: 400px"
|
style="width: 400px"
|
||||||
placeholder="请输入项目信息-开票单位/汇款人"
|
placeholder="请输入项目信息 - 开票单位/汇款人"
|
||||||
v-model:value="form.itemName"
|
v-model:value="form.itemName"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -378,6 +395,8 @@ const form = reactive<PwlProject>({
|
|||||||
price: undefined,
|
price: undefined,
|
||||||
recommend: undefined,
|
recommend: undefined,
|
||||||
expirationTime: undefined,
|
expirationTime: undefined,
|
||||||
|
personName: undefined,
|
||||||
|
position: undefined,
|
||||||
itemName: undefined,
|
itemName: undefined,
|
||||||
itemYear: undefined,
|
itemYear: undefined,
|
||||||
itemType: undefined,
|
itemType: undefined,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!-- 用户编辑弹窗 -->
|
<!-- User编辑弹窗 -->
|
||||||
<template>
|
<template>
|
||||||
<a-drawer
|
<a-drawer
|
||||||
:width="`70%`"
|
:width="`70%`"
|
||||||
@@ -37,24 +37,30 @@
|
|||||||
|
|
||||||
<a-card style="margin-bottom: 20px; text-align: center; background: transparent" :bordered="false">
|
<a-card style="margin-bottom: 20px; text-align: center; background: transparent" :bordered="false">
|
||||||
<a-space>
|
<a-space>
|
||||||
|
<a-tooltip title="并行生成所有 9 个章节的内容(包含各小节)">
|
||||||
<a-button size="large" type="primary" @click="handleGenerateAll" :loading="generatingAll" class="generate-all-button">
|
<a-button size="large" type="primary" @click="handleGenerateAll" :loading="generatingAll" class="generate-all-button">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<UngroupOutlined/>
|
<UngroupOutlined/>
|
||||||
</template>
|
</template>
|
||||||
生成全部方案
|
生成全部方案
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button size="large">
|
</a-tooltip>
|
||||||
|
<a-tooltip title="依次保存所有章节到数据库(每个章节独立保存)">
|
||||||
|
<a-button size="large" @click="saveAllDrafts">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<DownloadOutlined/>
|
<DownloadOutlined/>
|
||||||
</template>
|
</template>
|
||||||
保存草稿
|
保存草稿
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button size="large">
|
</a-tooltip>
|
||||||
|
<a-tooltip title="从数据库加载所有已保存的章节内容">
|
||||||
|
<a-button size="large" @click="loadDrafts">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<RedoOutlined/>
|
<RedoOutlined/>
|
||||||
</template>
|
</template>
|
||||||
加载草稿
|
加载草稿
|
||||||
</a-button>
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
<a-button size="large" type="danger" class="export-button" @click="handleExport">
|
<a-button size="large" type="danger" class="export-button" @click="handleExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<UploadOutlined/>
|
<UploadOutlined/>
|
||||||
@@ -120,16 +126,44 @@
|
|||||||
:bordered="false"
|
:bordered="false"
|
||||||
>
|
>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
|
<a-space>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
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)"
|
@click="generateContent(index)"
|
||||||
:loading="item.generating"
|
:loading="item.generating"
|
||||||
|
class="section-action-button ai-generate-button"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<RobotOutlined/>
|
<RobotOutlined/>
|
||||||
</template>
|
</template>
|
||||||
AI生成
|
AI 生成
|
||||||
</a-button>
|
</a-button>
|
||||||
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<!-- <div v-if="item.description" class="section-description">-->
|
<!-- <div v-if="item.description" class="section-description">-->
|
||||||
<!-- {{ item.description }}-->
|
<!-- {{ item.description }}-->
|
||||||
@@ -172,21 +206,47 @@
|
|||||||
|
|
||||||
<!-- 多内容部分 -->
|
<!-- 多内容部分 -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-for="(child, childIndex) in item.children" :key="childIndex" class="child-section">
|
<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">
|
<div class="child-title" style="display: flex; align-items: center; justify-content: space-between;">
|
||||||
{{ child.name }}
|
<span style="font-weight: bold;">{{ child.name }}</span>
|
||||||
|
<a-space>
|
||||||
<a-button
|
<a-button
|
||||||
type="text"
|
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"
|
size="small"
|
||||||
@click.stop="generateContent(index, childIndex)"
|
@click.stop="generateContent(index, childIndex)"
|
||||||
:loading="child.generating"
|
:loading="child.generating"
|
||||||
style="margin-left: 12px"
|
class="child-action-button ai-generate-button"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<RobotOutlined/>
|
<RobotOutlined/>
|
||||||
</template>
|
</template>
|
||||||
AI生成
|
AI 生成
|
||||||
</a-button>
|
</a-button>
|
||||||
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model:value="child.content"
|
v-model:value="child.content"
|
||||||
@@ -244,9 +304,11 @@ import {
|
|||||||
UploadOutlined,
|
UploadOutlined,
|
||||||
RedoOutlined,
|
RedoOutlined,
|
||||||
RobotOutlined,
|
RobotOutlined,
|
||||||
QuestionCircleOutlined
|
QuestionCircleOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
SaveOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} 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';
|
import {getPwlProjectLibraryByIds} from '@/api/pwl/pwlProjectLibrary';
|
||||||
|
|
||||||
const useForm = Form.useForm;
|
const useForm = Form.useForm;
|
||||||
@@ -264,6 +326,10 @@ const maxAble = ref(true);
|
|||||||
const generatingAll = ref(false);
|
const generatingAll = ref(false);
|
||||||
// 当前选中的章节
|
// 当前选中的章节
|
||||||
const currentSection = ref(0);
|
const currentSection = ref(0);
|
||||||
|
// 每个章节的重载状态
|
||||||
|
const sectionReloading = reactive<Record<number, boolean>>({});
|
||||||
|
// 每个章节的保存状态
|
||||||
|
const sectionSaving = reactive<Record<number, boolean>>({});
|
||||||
|
|
||||||
// 存储拼接后的 kbIds
|
// 存储拼接后的 kbIds
|
||||||
const combinedKbIds = ref('');
|
const combinedKbIds = ref('');
|
||||||
@@ -373,7 +439,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 51,
|
formCommit: 51,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(二)公司发展战略规划的制定、执行和效果情况以及年度责任目标完成情况',
|
name: '(二)公司发展战略规划的制定、执行和效果情况以及年度责任目标完成情况',
|
||||||
@@ -381,7 +450,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 52,
|
formCommit: 52,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(三)重大经济事项的决策、执行和效果情况',
|
name: '(三)重大经济事项的决策、执行和效果情况',
|
||||||
@@ -389,7 +461,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 53,
|
formCommit: 53,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(四)公司法人治理结构的建立、健全和运行情况,内部控制制度的制定和执行情况',
|
name: '(四)公司法人治理结构的建立、健全和运行情况,内部控制制度的制定和执行情况',
|
||||||
@@ -397,7 +472,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 54,
|
formCommit: 54,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(五)公司财务的真实合法效益情况,风险管控情况,境外资产管理情况',
|
name: '(五)公司财务的真实合法效益情况,风险管控情况,境外资产管理情况',
|
||||||
@@ -405,7 +483,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 55,
|
formCommit: 55,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(六)在经济活动中落实有关党风廉政建设责任和遵守廉洁从业规定情况',
|
name: '(六)在经济活动中落实有关党风廉政建设责任和遵守廉洁从业规定情况',
|
||||||
@@ -413,7 +494,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 56,
|
formCommit: 56,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(七)对以往审计中发现问题的整改情况',
|
name: '(七)对以往审计中发现问题的整改情况',
|
||||||
@@ -421,7 +505,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 57,
|
formCommit: 57,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(八)其他需要审计的事项',
|
name: '(八)其他需要审计的事项',
|
||||||
@@ -429,7 +516,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 58,
|
formCommit: 58,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -437,7 +527,7 @@ const navigationItems = ref([
|
|||||||
number: '六',
|
number: '六',
|
||||||
name: '重要风险的识别及应对',
|
name: '重要风险的识别及应对',
|
||||||
title: '6、重要风险的识别及应对',
|
title: '6、重要风险的识别及应对',
|
||||||
description: '点击"AI生成"按钮让AI为您生成该部分内容,或直接在下方编辑',
|
description: '点击"AI 生成"按钮让 AI 为您生成该部分内容,或直接在下方编辑',
|
||||||
generating: false,
|
generating: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -446,7 +536,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 61,
|
formCommit: 61,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '(二)风险的应对策略',
|
name: '(二)风险的应对策略',
|
||||||
@@ -454,7 +547,10 @@ const navigationItems = ref([
|
|||||||
content: '',
|
content: '',
|
||||||
suggestion: '',
|
suggestion: '',
|
||||||
formCommit: 62,
|
formCommit: 62,
|
||||||
rows: 6
|
rows: 6,
|
||||||
|
generating: false,
|
||||||
|
saving: false,
|
||||||
|
reloading: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -625,11 +721,22 @@ const getScrollContainer = () => {
|
|||||||
return document.querySelector('.ant-modal-body');
|
return document.querySelector('.ant-modal-body');
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 批量生成全部内容 */
|
/* 批量生成全部内容 - 清空后并行生成 */
|
||||||
const handleGenerateAll2 = async () => {
|
const handleGenerateAll = async () => {
|
||||||
generatingAll.value = true;
|
generatingAll.value = true;
|
||||||
try {
|
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>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
navigationItems.value.forEach((item, index) => {
|
navigationItems.value.forEach((item, index) => {
|
||||||
@@ -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 hasContent = (index: number) => {
|
||||||
const section = navigationItems.value[index];
|
const section = navigationItems.value[index];
|
||||||
@@ -698,6 +772,541 @@ const hasContent = (index: number) => {
|
|||||||
return !!section.content?.trim();
|
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 buildExportData = () => {
|
||||||
const exportData: any = {
|
const exportData: any = {
|
||||||
@@ -914,6 +1523,9 @@ watch(
|
|||||||
|
|
||||||
// 重置到第一个章节
|
// 重置到第一个章节
|
||||||
currentSection.value = 0;
|
currentSection.value = 0;
|
||||||
|
|
||||||
|
// 自动从数据库加载审计报告
|
||||||
|
await loadAuditReportFromDB();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resetFields();
|
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 {
|
.navigation-container {
|
||||||
.nav-grid {
|
.nav-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@@ -513,7 +513,7 @@
|
|||||||
</a-drawer>
|
</a-drawer>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, watch, onMounted, onUnmounted, computed } from 'vue';
|
import { ref, reactive, watch, onMounted, onUnmounted, computed, nextTick } from 'vue';
|
||||||
import { Form, message, Modal, TableProps } from 'ant-design-vue';
|
import { Form, message, Modal, TableProps } from 'ant-design-vue';
|
||||||
import { assignObject } from 'ele-admin-pro';
|
import { assignObject } from 'ele-admin-pro';
|
||||||
import { copyText } from '@/utils/common';
|
import { copyText } from '@/utils/common';
|
||||||
@@ -608,7 +608,8 @@
|
|||||||
caseIndex: '',
|
caseIndex: '',
|
||||||
projectName: '',
|
projectName: '',
|
||||||
auditedTarget: '',
|
auditedTarget: '',
|
||||||
auditMatter: ''
|
auditMatter: '',
|
||||||
|
auditMatterType: '' // 审计事项类型编码(code)
|
||||||
});
|
});
|
||||||
const evidenceSelectedRows = ref<any[]>([]);
|
const evidenceSelectedRows = ref<any[]>([]);
|
||||||
|
|
||||||
@@ -750,25 +751,63 @@
|
|||||||
fileModal.value.open(tableInfo.tableKey);
|
fileModal.value.open(tableInfo.tableKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据表格类型获取审计事项类型编码
|
||||||
|
*/
|
||||||
|
const getAuditMatterTypeByTable = (tableValue: string): string => {
|
||||||
|
// 根据 AuditMatterTypeEnum 枚举映射
|
||||||
|
const mapping: Record<string, string> = {
|
||||||
|
'eightReg': 'eightReg', // 八项规定
|
||||||
|
'expense': 'expense', // 决策支出表
|
||||||
|
'leaderList': 'leaderList', // 领导班子名单
|
||||||
|
'strategyAudit': 'strategyAudit', // 单位发展战略执行
|
||||||
|
'decisionTable': 'decisionTable', // 重大经济决策调查表
|
||||||
|
'tripleOne': 'tripleOne', // 三重一大
|
||||||
|
'target': 'target', // 目标责任制完成情况表
|
||||||
|
'budgetExecution': 'budgetExecution', // 预算执行情况审计
|
||||||
|
'budgetManage': 'budgetManage', // 预算管理审计
|
||||||
|
'assets': 'assets', // 国有资产管理审计
|
||||||
|
'investmentSituation': 'investmentSituation', // 重大投资情况
|
||||||
|
'internalControl': 'internalControl', // 内部控制测试表
|
||||||
|
'personnel': 'personnel', // 人员编制管理审计
|
||||||
|
'partyConduct': 'partyConduct', // 党风廉政建设责任制审计
|
||||||
|
'history': 'history' // 历史审计问题整改
|
||||||
|
};
|
||||||
|
return mapping[tableValue] || tableValue;
|
||||||
|
};
|
||||||
|
|
||||||
/* 打开取证单预览弹窗 */
|
/* 打开取证单预览弹窗 */
|
||||||
const openEvidenceModal = (sectionIndex: number) => {
|
const openEvidenceModal = (sectionIndex: number) => {
|
||||||
const section: any = navigationItems.value[sectionIndex];
|
const section: any = navigationItems.value[sectionIndex];
|
||||||
const tableInfo = getTableInfo(sectionIndex);
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
|
|
||||||
evidenceBaseInfo.caseIndex =
|
|
||||||
(form as any).caseIndex || props.data?.caseIndex || '';
|
|
||||||
evidenceBaseInfo.projectName = form.name || props.data?.name || '';
|
|
||||||
evidenceBaseInfo.auditedTarget =
|
|
||||||
(form as any).nickname || (props.data as any)?.nickname || '';
|
|
||||||
|
|
||||||
const baseMatter = section?.title || section?.name || '';
|
const baseMatter = section?.title || section?.name || '';
|
||||||
const tableTitle = tableInfo?.currentTable?.title || '';
|
const tableTitle = tableInfo?.currentTable?.title || '';
|
||||||
|
|
||||||
|
// 设置审计事项类型(根据当前表格类型)- 使用 code 而不是 value
|
||||||
|
const auditMatterTypeCode = getAuditMatterTypeByTable(tableInfo?.currentTable?.value);
|
||||||
|
console.log('openEvidenceModal - tableValue:', tableInfo?.currentTable?.value);
|
||||||
|
console.log('openEvidenceModal - auditMatterTypeCode:', auditMatterTypeCode);
|
||||||
|
|
||||||
|
// 逐个字段赋值确保响应式更新
|
||||||
|
evidenceBaseInfo.caseIndex = (form as any).caseIndex || props.data?.caseIndex || '';
|
||||||
|
evidenceBaseInfo.projectName = form.name || props.data?.name || '';
|
||||||
|
evidenceBaseInfo.auditedTarget = (form as any).nickname || (props.data as any)?.nickname || '';
|
||||||
evidenceBaseInfo.auditMatter = tableTitle
|
evidenceBaseInfo.auditMatter = tableTitle
|
||||||
? `${baseMatter ? `${baseMatter} - ` : ''}${tableTitle}`
|
? `${baseMatter ? `${baseMatter} - ` : ''}${tableTitle}`
|
||||||
: baseMatter;
|
: baseMatter;
|
||||||
|
evidenceBaseInfo.auditMatterType = auditMatterTypeCode; // 关键:设置审计事项类型编码
|
||||||
|
|
||||||
|
console.log('openEvidenceModal - evidenceBaseInfo:', evidenceBaseInfo);
|
||||||
|
console.log('openEvidenceModal - evidenceBaseInfo.auditMatterType:', evidenceBaseInfo.auditMatterType);
|
||||||
|
|
||||||
evidenceSelectedRows.value = selectedRowsMap[sectionIndex] || [];
|
evidenceSelectedRows.value = selectedRowsMap[sectionIndex] || [];
|
||||||
|
|
||||||
|
// 关键:使用 nextTick 确保响应式更新完成后再打开弹窗
|
||||||
|
nextTick(() => {
|
||||||
|
console.log('nextTick - evidenceBaseInfo.auditMatterType:', evidenceBaseInfo.auditMatterType);
|
||||||
evidenceModalVisible.value = true;
|
evidenceModalVisible.value = true;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 保存表格生成数据 */
|
/* 保存表格生成数据 */
|
||||||
@@ -1061,6 +1100,12 @@
|
|||||||
section?.name ||
|
section?.name ||
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
// 关键:设置审计事项类型编码(根据当前表格类型)
|
||||||
|
const auditMatterTypeCode = getAuditMatterTypeByTable(currentTable.value);
|
||||||
|
console.log('generateEvidence - tableValue:', currentTable.value);
|
||||||
|
console.log('generateEvidence - auditMatterTypeCode:', auditMatterTypeCode);
|
||||||
|
evidenceBaseInfo.auditMatterType = auditMatterTypeCode;
|
||||||
|
|
||||||
// 将生成的取证单数据作为选中的行数据传入,包含所有字段
|
// 将生成的取证单数据作为选中的行数据传入,包含所有字段
|
||||||
const evidenceData = {
|
const evidenceData = {
|
||||||
// 基础信息字段
|
// 基础信息字段
|
||||||
@@ -1097,7 +1142,12 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
evidenceSelectedRows.value = [evidenceData];
|
evidenceSelectedRows.value = [evidenceData];
|
||||||
|
|
||||||
|
// 关键:使用 nextTick 确保响应式更新完成后再打开弹窗
|
||||||
|
nextTick(() => {
|
||||||
|
console.log('generateEvidence nextTick - evidenceBaseInfo.auditMatterType:', evidenceBaseInfo.auditMatterType);
|
||||||
evidenceModalVisible.value = true;
|
evidenceModalVisible.value = true;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
apiResult.data?.error || apiResult.message || '生成取证单失败'
|
apiResult.data?.error || apiResult.message || '生成取证单失败'
|
||||||
|
|||||||
583
src/views/pwl/pwlProject/components/reportGenerate.vue
Normal file
583
src/views/pwl/pwlProject/components/reportGenerate.vue
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 生成审核报告弹窗 -->
|
||||||
|
<a-drawer
|
||||||
|
:width="`90%`"
|
||||||
|
:visible="visible"
|
||||||
|
:confirm-loading="loading"
|
||||||
|
:maxable="true"
|
||||||
|
:title="`生成审核报告 - ${form.name || ''}`"
|
||||||
|
:body-style="{ paddingBottom: '8px', background: '#f5f5f5' }"
|
||||||
|
@update:visible="updateVisible"
|
||||||
|
:maskClosable="false"
|
||||||
|
:footer="null"
|
||||||
|
>
|
||||||
|
<div class="report-generate-container">
|
||||||
|
<!-- 顶部操作区 -->
|
||||||
|
<div class="action-bar">
|
||||||
|
<div class="title">审核报告生成</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="reloadData" :loading="reloading" :disabled="!projectId" class="action-button">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
重载数据
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="generatePreview" :loading="generating" :disabled="!hasAnySelection" class="action-button">
|
||||||
|
<template #icon><FileTextOutlined /></template>
|
||||||
|
生成报告预览
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="saveReport" :loading="saving" :disabled="!previewContent" class="action-button">
|
||||||
|
<template #icon><SaveOutlined /></template>
|
||||||
|
保存入库
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<a-card title="基本信息" style="margin-bottom: 16px" :bordered="false">
|
||||||
|
<a-descriptions>
|
||||||
|
<a-descriptions-item label="被审计单位" :labelStyle="{ width: '100px' }">
|
||||||
|
{{ form.name }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="项目名称" :labelStyle="{ width: '100px' }">
|
||||||
|
{{ form.code }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="案引号" :labelStyle="{ width: '100px' }">
|
||||||
|
{{ form.caseIndex || '-' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 步骤:选择取证单记录 -->
|
||||||
|
<a-card title="一、选择取证单记录(按审核内容)" style="margin-bottom: 16px" :bordered="false">
|
||||||
|
<a-collapse v-model:activeKey="activeSections">
|
||||||
|
<a-collapse-panel
|
||||||
|
v-for="(section, index) in sectionList"
|
||||||
|
:key="String(index)"
|
||||||
|
:header="`${section.number}、${section.name}${section.title ? ' - ' + section.title : ''}`"
|
||||||
|
>
|
||||||
|
<template #extra>
|
||||||
|
<a-tag color="blue">{{ getSectionSelectionCount(index) }} 条已选</a-tag>
|
||||||
|
</template>
|
||||||
|
<div v-if="section.records.length === 0" class="empty-tip">
|
||||||
|
暂无已保存的取证单记录,请先在「审计内容」中生成并保存对应表格。
|
||||||
|
</div>
|
||||||
|
<a-table
|
||||||
|
v-else
|
||||||
|
:dataSource="section.records"
|
||||||
|
:columns="evidenceColumns"
|
||||||
|
:row-selection="getRowSelection(index)"
|
||||||
|
:pagination="false"
|
||||||
|
rowKey="__rowKey"
|
||||||
|
size="small"
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record, index: rowIndex }">
|
||||||
|
<template v-if="column.dataIndex === '_index'">
|
||||||
|
{{ rowIndex + 1 }}
|
||||||
|
</template>
|
||||||
|
<template v-else-if="column.dataIndex === '_summary'">
|
||||||
|
<span class="summary-cell">{{ getRecordSummary(record) }}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 步骤:报告预览 -->
|
||||||
|
<a-card title="二、审计报告预览" style="margin-bottom: 16px" :bordered="false">
|
||||||
|
<div v-if="!previewContent" class="empty-preview">
|
||||||
|
请先选择取证单记录并点击「生成报告预览」。
|
||||||
|
</div>
|
||||||
|
<div v-else class="preview-content" v-html="previewContent"></div>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, watch, computed } from 'vue';
|
||||||
|
import { message, Modal } from 'ant-design-vue';
|
||||||
|
import type { TableProps } from 'ant-design-vue';
|
||||||
|
import { FileTextOutlined, SaveOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||||
|
import type { PwlProject } from '@/api/pwl/pwlProject/model';
|
||||||
|
import { addAiHistory } from '@/api/ai/aiHistory';
|
||||||
|
import { tableConfigs } from './data/tableCommon';
|
||||||
|
import { saveAuditReport, queryAuditReport } from '@/api/ai/auditReport';
|
||||||
|
|
||||||
|
const AUDIT_REPORT_INTERFACE = 'auditReport';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
data?: PwlProject | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'done'): void;
|
||||||
|
(e: 'update:visible', visible: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const form = reactive<PwlProject>({
|
||||||
|
id: undefined,
|
||||||
|
name: undefined,
|
||||||
|
code: undefined,
|
||||||
|
caseIndex: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const generating = ref(false);
|
||||||
|
const saving = ref(false);
|
||||||
|
const reloading = ref(false);
|
||||||
|
const activeSections = ref<string[]>([]);
|
||||||
|
const projectId = ref<number | undefined>(undefined);
|
||||||
|
|
||||||
|
// 审核内容列表(与 reportContent 的 navigationItems 对应)
|
||||||
|
const sectionList = ref<Array<{
|
||||||
|
number: string;
|
||||||
|
name: string;
|
||||||
|
title?: string;
|
||||||
|
tableType: string;
|
||||||
|
records: any[];
|
||||||
|
}>>([]);
|
||||||
|
|
||||||
|
// 每个 section 选中的行: sectionIndex -> rowKey[]
|
||||||
|
const selectedRowKeysMap = reactive<Record<number, (string | number)[]>>({});
|
||||||
|
|
||||||
|
// 报告预览 HTML
|
||||||
|
const previewContent = ref('');
|
||||||
|
|
||||||
|
// 历史记录可能带 responseData(部分后端实现会返回)
|
||||||
|
const evidenceColumns = [
|
||||||
|
{ title: '序号', dataIndex: '_index', key: '_index', width: 60 },
|
||||||
|
{ title: '内容摘要', dataIndex: '_summary', key: '_summary', ellipsis: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
const getChineseNumber = (num: number) => {
|
||||||
|
const arr = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一'];
|
||||||
|
return arr[num - 1] || String(num);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getSectionListFromConfig() {
|
||||||
|
const keys = Object.keys(tableConfigs);
|
||||||
|
return keys.map((key, index) => {
|
||||||
|
const config = (tableConfigs as any)[key];
|
||||||
|
return {
|
||||||
|
index: index+1,
|
||||||
|
number: getChineseNumber(index + 1),
|
||||||
|
name: `审计内容${index + 1}`,
|
||||||
|
title: config?.title,
|
||||||
|
tableType: key,
|
||||||
|
records: [] as any[]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRecordSummary(record: any): string {
|
||||||
|
if (!record || typeof record !== 'object') return '-';
|
||||||
|
const parts: string[] = [];
|
||||||
|
const prefer = ['auditFinding', 'auditRecord', 'problemDescription', 'summaryTitle', 'title', 'content', 'auditContent', 'checkEvidence'];
|
||||||
|
for (const p of prefer) {
|
||||||
|
if (record[p] && String(record[p]).trim()) {
|
||||||
|
const s = String(record[p]).trim();
|
||||||
|
parts.push(s.length > 80 ? s.slice(0, 80) + '...' : s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parts.length === 0) {
|
||||||
|
const firstStr = Object.values(record).find(v => typeof v === 'string' && v.length > 0);
|
||||||
|
if (firstStr) parts.push(String(firstStr).slice(0, 60) + (String(firstStr).length > 60 ? '...' : ''));
|
||||||
|
}
|
||||||
|
return parts.length > 0 ? parts.join(' ') : JSON.stringify(record).slice(0, 80) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowSelection(sectionIndex: number): TableProps['rowSelection'] {
|
||||||
|
return {
|
||||||
|
selectedRowKeys: selectedRowKeysMap[sectionIndex] || [],
|
||||||
|
onChange: (keys: (string | number)[]) => {
|
||||||
|
selectedRowKeysMap[sectionIndex] = keys;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSectionSelectionCount = (sectionIndex: number) => {
|
||||||
|
return (selectedRowKeysMap[sectionIndex] || []).length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasAnySelection = computed(() => {
|
||||||
|
return sectionList.value.some((_, index) => (selectedRowKeysMap[index] || []).length > 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadSectionDataFromEvidence() {
|
||||||
|
const projectIdVal = props.data?.id;
|
||||||
|
const caseIndex = props.data?.caseIndex;
|
||||||
|
if (!projectIdVal && !caseIndex) {
|
||||||
|
message.warning('无项目信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res: any = await pageAuditEvidence({
|
||||||
|
projectId: projectIdVal,
|
||||||
|
caseIndex,
|
||||||
|
page: 1,
|
||||||
|
limit: 1000
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const data = res?.data;
|
||||||
|
const allRows: any[] = (() => {
|
||||||
|
if (!data) return [];
|
||||||
|
if (Array.isArray(data)) return data;
|
||||||
|
if (Array.isArray(data.records)) return data.records;
|
||||||
|
if (Array.isArray(data.rows)) return data.rows;
|
||||||
|
if (Array.isArray(data.list)) return data.list;
|
||||||
|
return [];
|
||||||
|
})();
|
||||||
|
|
||||||
|
const sections = getSectionListFromConfig();
|
||||||
|
const sectionRecordsMap: Record<number, any[]> = {};
|
||||||
|
sections.forEach((_, i) => {
|
||||||
|
sectionRecordsMap[i] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
allRows.forEach((row: any, idx: number) => {
|
||||||
|
const contentType =
|
||||||
|
row.contentType ?? row.content_type ?? row.contentNo ?? row.contentIndex;
|
||||||
|
const sectionIndex =
|
||||||
|
typeof contentType === 'number' ? contentType - 1 : -1;
|
||||||
|
|
||||||
|
if (sectionIndex < 0 || sectionIndex >= sections.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowKey = `${row.id ?? ''}-${idx}`;
|
||||||
|
sectionRecordsMap[sectionIndex].push({
|
||||||
|
...row,
|
||||||
|
__rowKey: rowKey
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sectionList.value = sections.map((s, i) => ({
|
||||||
|
...s,
|
||||||
|
records: sectionRecordsMap[i] || []
|
||||||
|
}));
|
||||||
|
activeSections.value = sectionList.value.map((_, i) => String(i));
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error('加载失败:' + (e?.message || '未知错误'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据库加载审计报告
|
||||||
|
*/
|
||||||
|
async function loadAuditReportFromDB(formCommit: number = 1) {
|
||||||
|
const projectIdVal = props.data?.id;
|
||||||
|
if (!projectIdVal) {
|
||||||
|
message.warning('无项目信息');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存 projectId 供重载按钮使用
|
||||||
|
projectId.value = projectIdVal;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res: any = await queryAuditReport({
|
||||||
|
projectId: projectIdVal,
|
||||||
|
formCommit
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data) {
|
||||||
|
// 解析报告内容
|
||||||
|
const reportContent = JSON.parse(res.data.reportContent || '{}');
|
||||||
|
previewContent.value = res.data.previewHtml || '';
|
||||||
|
|
||||||
|
// 恢复选中的行
|
||||||
|
if (reportContent.sections && Array.isArray(reportContent.sections)) {
|
||||||
|
reportContent.sections.forEach((section: any, sectionIndex: number) => {
|
||||||
|
if (section.records && Array.isArray(section.records)) {
|
||||||
|
// 找到对应的记录并选中
|
||||||
|
const currentSection = sectionList.value[sectionIndex];
|
||||||
|
if (currentSection) {
|
||||||
|
const selectedKeys: (string | number)[] = [];
|
||||||
|
section.records.forEach((rec: any) => {
|
||||||
|
const foundRecord = currentSection.records.find(
|
||||||
|
r => r.id === rec.id || r.summaryTitle === rec.summaryTitle
|
||||||
|
);
|
||||||
|
if (foundRecord) {
|
||||||
|
selectedKeys.push(foundRecord.__rowKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
selectedRowKeysMap[sectionIndex] = selectedKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
message.success('已从数据库加载审计报告');
|
||||||
|
return res.data;
|
||||||
|
} else {
|
||||||
|
message.info('数据库中暂无审计报告记录');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('加载审计报告失败:', e);
|
||||||
|
// 不显示错误,静默失败
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重载数据
|
||||||
|
*/
|
||||||
|
async function reloadData() {
|
||||||
|
const projectIdVal = props.data?.id;
|
||||||
|
if (!projectIdVal) {
|
||||||
|
message.warning('无项目信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reloading.value = true;
|
||||||
|
try {
|
||||||
|
// 先清空当前内容
|
||||||
|
previewContent.value = '';
|
||||||
|
Object.keys(selectedRowKeysMap).forEach((k) => {
|
||||||
|
selectedRowKeysMap[Number(k)] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 从数据库重新加载
|
||||||
|
await loadAuditReportFromDB(1);
|
||||||
|
|
||||||
|
if (previewContent.value) {
|
||||||
|
message.success('数据已重新加载');
|
||||||
|
} else {
|
||||||
|
message.info('数据库中无相关记录,页面无变化');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error('重载失败:' + (e?.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
reloading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPreviewFromSelections(): string {
|
||||||
|
const parts: string[] = [];
|
||||||
|
parts.push('<div class="report-preview">');
|
||||||
|
parts.push(`<h2>审计报告</h2>`);
|
||||||
|
parts.push(`<p><strong>被审计单位:</strong>${escapeHtml(form.name || '')}</p>`);
|
||||||
|
parts.push(`<p><strong>项目名称:</strong>${escapeHtml(form.code || '')}</p>`);
|
||||||
|
parts.push(`<p><strong>案引号:</strong>${escapeHtml(form.caseIndex || '')}</p>`);
|
||||||
|
parts.push('<hr/>');
|
||||||
|
|
||||||
|
sectionList.value.forEach((section, sectionIndex) => {
|
||||||
|
const keys = selectedRowKeysMap[sectionIndex] || [];
|
||||||
|
if (keys.length === 0) return;
|
||||||
|
const records = section.records.filter((r: any) => keys.includes(r.__rowKey));
|
||||||
|
parts.push(`<h3>${section.number}、${section.name}${section.title ? ' - ' + section.title : ''}</h3>`);
|
||||||
|
parts.push('<table class="preview-table"><thead><tr><th>序号</th><th>内容摘要</th></tr></thead><tbody>');
|
||||||
|
records.forEach((rec: any, i: number) => {
|
||||||
|
const summary = getRecordSummary(rec);
|
||||||
|
parts.push(`<tr><td>${i + 1}</td><td>${escapeHtml(summary)}</td></tr>`);
|
||||||
|
});
|
||||||
|
parts.push('</tbody></table>');
|
||||||
|
});
|
||||||
|
|
||||||
|
parts.push('</div>');
|
||||||
|
return parts.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(s: string): string {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = s;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePreview() {
|
||||||
|
generating.value = true;
|
||||||
|
try {
|
||||||
|
// AI 生成前清空当前内容
|
||||||
|
previewContent.value = '';
|
||||||
|
|
||||||
|
previewContent.value = buildPreviewFromSelections();
|
||||||
|
message.success('报告预览已生成');
|
||||||
|
} finally {
|
||||||
|
generating.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReportPayload(): { sections: any[]; previewHtml: string } {
|
||||||
|
const sections = sectionList.value.map((section, sectionIndex) => {
|
||||||
|
const keys = selectedRowKeysMap[sectionIndex] || [];
|
||||||
|
const records = section.records.filter((r: any) => keys.includes(r.__rowKey));
|
||||||
|
return {
|
||||||
|
number: section.number,
|
||||||
|
name: section.name,
|
||||||
|
title: section.title,
|
||||||
|
tableType: section.tableType,
|
||||||
|
records: records.map((r: any) => {
|
||||||
|
const { __rowKey, __historyId, __interfaceName, ...rest } = r;
|
||||||
|
return rest;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
sections,
|
||||||
|
previewHtml: previewContent.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveReport() {
|
||||||
|
if (!previewContent.value) {
|
||||||
|
message.warning('请先生成报告预览');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const projectId = props.data?.id;
|
||||||
|
if (!projectId) {
|
||||||
|
message.warning('无项目信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹出确认对话框
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认保存',
|
||||||
|
content: '是否确认将当前审计报告保存到数据库?',
|
||||||
|
okText: '是',
|
||||||
|
cancelText: '否',
|
||||||
|
onOk: async () => {
|
||||||
|
saving.value = true;
|
||||||
|
try {
|
||||||
|
const payload = getReportPayload();
|
||||||
|
|
||||||
|
// 调用新的保存接口
|
||||||
|
await saveAuditReport({
|
||||||
|
projectId,
|
||||||
|
projectName: form.name || '',
|
||||||
|
caseIndex: form.caseIndex || '',
|
||||||
|
auditedTarget: form.name || '',
|
||||||
|
reportContent: JSON.stringify(payload),
|
||||||
|
previewHtml: previewContent.value,
|
||||||
|
sectionCount: payload.sections.length,
|
||||||
|
formCommit: 1 // 表单提交标识,可根据实际需求调整
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success('报告已保存入库');
|
||||||
|
emit('done');
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error('保存失败:' + (e?.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
saving.value = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel() {
|
||||||
|
console.log('用户取消保存');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVisible(value: boolean) {
|
||||||
|
emit('update:visible', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
sectionList.value = getSectionListFromConfig();
|
||||||
|
Object.keys(selectedRowKeysMap).forEach((k) => delete selectedRowKeysMap[Number(k)]);
|
||||||
|
previewContent.value = '';
|
||||||
|
activeSections.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
async (visible) => {
|
||||||
|
if (visible && props.data) {
|
||||||
|
Object.assign(form, props.data);
|
||||||
|
reset();
|
||||||
|
await loadSectionDataFromEvidence();
|
||||||
|
// 自动从数据库加载审计报告
|
||||||
|
await loadAuditReportFromDB(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(data) => {
|
||||||
|
if (data) Object.assign(form, data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.report-generate-container {
|
||||||
|
.action-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
background: white;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-button {
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-tip {
|
||||||
|
color: #999;
|
||||||
|
padding: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-cell {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-preview {
|
||||||
|
color: #999;
|
||||||
|
padding: 24px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px dashed #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-content {
|
||||||
|
padding: 16px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.report-preview) {
|
||||||
|
.preview-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 12px 0;
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hr {
|
||||||
|
margin: 16px 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
797
src/views/pwl/pwlProject/components/reportView.vue
Normal file
797
src/views/pwl/pwlProject/components/reportView.vue
Normal file
@@ -0,0 +1,797 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 审计报告查看页面 -->
|
||||||
|
<a-drawer
|
||||||
|
:width="`90%`"
|
||||||
|
:visible="visible"
|
||||||
|
:confirm-loading="loading"
|
||||||
|
:maxable="true"
|
||||||
|
:title="`审计报告 - ${form.name || ''}`"
|
||||||
|
:body-style="{ paddingBottom: '8px', background: '#f5f5f5' }"
|
||||||
|
@update:visible="updateVisible"
|
||||||
|
:maskClosable="false"
|
||||||
|
:footer="null"
|
||||||
|
>
|
||||||
|
<div class="audit-report-container">
|
||||||
|
<!-- 顶部操作区 -->
|
||||||
|
<div class="action-bar">
|
||||||
|
<div class="title">审计报告</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="loadReport" :loading="loading" class="action-button">
|
||||||
|
<template #icon><ReloadOutlined /></template>
|
||||||
|
加载报告
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" @click="generateReport" :loading="generating" class="action-button">
|
||||||
|
<template #icon><RobotOutlined /></template>
|
||||||
|
生成报告
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 审计报告内容 -->
|
||||||
|
<a-card :bordered="false" class="report-content-card">
|
||||||
|
<!-- 取证单选择区域 -->
|
||||||
|
<div class="evidence-selection-area" v-if="evidenceList.length > 0">
|
||||||
|
<a-alert
|
||||||
|
message="请先勾选需要加载或生成的取证单记录"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
style="margin-bottom: 16px;"
|
||||||
|
/>
|
||||||
|
<a-table
|
||||||
|
:dataSource="evidenceList"
|
||||||
|
:columns="evidenceColumns"
|
||||||
|
:row-selection="{ selectedRowKeys: selectedEvidenceIds, onChange: onSelectChange }"
|
||||||
|
:scroll="{ x: 1800 }"
|
||||||
|
:pagination="false"
|
||||||
|
row-key="id"
|
||||||
|
size="middle"
|
||||||
|
bordered
|
||||||
|
@dblclick="openEvidenceDetail"
|
||||||
|
>
|
||||||
|
<template #bodyCell="{ column, record }">
|
||||||
|
<template v-if="column.dataIndex === 'contentType'">
|
||||||
|
{{ getContentTypeText(record.contentType) }}
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'auditMatter'">
|
||||||
|
<div style="white-space: pre-wrap; max-height: 100px; overflow-y: auto;">
|
||||||
|
{{ record.auditMatter }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.dataIndex === 'compileDate'">
|
||||||
|
{{ record.compileDate }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<div class="selected-info">
|
||||||
|
已选择 <a-tag color="blue">{{ selectedEvidenceIds.length }}</a-tag> 条取证单记录
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-empty v-else description="暂无取证单数据,请先在审计核查中添加问题并保存" />
|
||||||
|
|
||||||
|
<!-- 审计报告内容 -->
|
||||||
|
<div v-if="reportContent" class="report-content" v-html="reportContent"></div>
|
||||||
|
<a-empty v-if="!reportContent && evidenceList.length > 0" description="暂无报告内容,请点击「加载报告」或「生成报告」" />
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 取证单详情弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="evidenceModalVisible"
|
||||||
|
title="取证单详情"
|
||||||
|
width="900px"
|
||||||
|
:footer="null"
|
||||||
|
@cancel="evidenceModalVisible = false"
|
||||||
|
>
|
||||||
|
<a-descriptions bordered :column="2" v-if="currentEvidence">
|
||||||
|
<a-descriptions-item label="内容类型">
|
||||||
|
{{ getContentTypeText(currentEvidence.contentType) }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="审计事项" :span="2">
|
||||||
|
<div style="white-space: pre-wrap;">{{ currentEvidence.auditMatter }}</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="案引号" :span="2">
|
||||||
|
{{ currentEvidence.caseIndex }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="被审计单位/个人" :span="2">
|
||||||
|
{{ currentEvidence.auditedTarget }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="审计事项" :span="2">
|
||||||
|
{{ currentEvidence.auditMatter }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="核心问题标题" :span="2">
|
||||||
|
{{ currentEvidence.summaryTitle }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="审计核查事实记录" :span="2">
|
||||||
|
<div style="white-space: pre-wrap;">{{ currentEvidence.auditRecord }}</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="审计发现的问题" :span="2">
|
||||||
|
<div style="white-space: pre-wrap;">{{ currentEvidence.auditFinding }}</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="定性依据" :span="2">
|
||||||
|
<div style="white-space: pre-wrap;">{{ currentEvidence.evidenceBasis }}</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="拟采取的处理措施" :span="2">
|
||||||
|
<div style="white-space: pre-wrap;">{{ currentEvidence.handling }}</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="改进或整改建议" :span="2">
|
||||||
|
<div style="white-space: pre-wrap;">{{ currentEvidence.suggestion }}</div>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="附件页数">
|
||||||
|
{{ currentEvidence.attachmentPages || 0 }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="反馈期限">
|
||||||
|
{{ currentEvidence.feedbackDeadline }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="编制日期">
|
||||||
|
{{ currentEvidence.compileDate }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="审计人员">
|
||||||
|
{{ currentEvidence.auditors }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, watch, h } from 'vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import { ReloadOutlined, RobotOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { PwlProject } from "@/api/pwl/pwlProject/model";
|
||||||
|
import { queryAuditDataByProjectId, generateAuditReportByProjectId, generateAuditReportWithEvidences } from "@/api/ai/auditReport";
|
||||||
|
import { getPwlProject } from "@/api/pwl/pwlProject";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
visible: boolean;
|
||||||
|
data?: PwlProject | null;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(e: 'done'): void;
|
||||||
|
(e: 'update:visible', value: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = reactive<PwlProject>({});
|
||||||
|
// 加载状态
|
||||||
|
const loading = ref(false);
|
||||||
|
// 生成状态
|
||||||
|
const generating = ref(false);
|
||||||
|
// 报告内容
|
||||||
|
const reportContent = ref('');
|
||||||
|
// 项目名称(用于动态显示)
|
||||||
|
const projectName = ref('');
|
||||||
|
// 人员姓名(用于动态显示)
|
||||||
|
const personName = ref('');
|
||||||
|
// 取证单列表
|
||||||
|
const evidenceList = ref<any[]>([]);
|
||||||
|
// 选中的取证单 ID 列表
|
||||||
|
const selectedEvidenceIds = ref<number[]>([]);
|
||||||
|
// 取证单详情弹窗可见性
|
||||||
|
const evidenceModalVisible = ref(false);
|
||||||
|
// 当前查看的取证单数据
|
||||||
|
const currentEvidence = ref<any>(null);
|
||||||
|
// 表格列配置
|
||||||
|
const evidenceColumns = ref([
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'index',
|
||||||
|
key: 'index',
|
||||||
|
width: 60,
|
||||||
|
customRender: ({ index }: { index: number }) => index + 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '内容类型',
|
||||||
|
dataIndex: 'contentType',
|
||||||
|
key: 'contentType',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '审计事项',
|
||||||
|
dataIndex: 'auditMatter',
|
||||||
|
key: 'auditMatter',
|
||||||
|
width: 250,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '被审计单位/个人',
|
||||||
|
dataIndex: 'auditedTarget',
|
||||||
|
key: 'auditedTarget',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '核心问题标题',
|
||||||
|
dataIndex: 'summaryTitle',
|
||||||
|
key: 'summaryTitle',
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '编制日期',
|
||||||
|
dataIndex: 'compileDate',
|
||||||
|
key: 'compileDate',
|
||||||
|
width: 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 80,
|
||||||
|
fixed: 'right' as const,
|
||||||
|
customRender: ({ record }: { record: any }) => {
|
||||||
|
return h('a', {
|
||||||
|
onClick: () => openEvidenceDetail(record)
|
||||||
|
}, '查看');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 更新 visible
|
||||||
|
const updateVisible = (val: boolean) => {
|
||||||
|
emit('update:visible', val);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听数据变化
|
||||||
|
watch(() => props.data, (newData) => {
|
||||||
|
if (newData) {
|
||||||
|
Object.assign(form, newData);
|
||||||
|
// 数据加载完成后,自动查询取证单列表
|
||||||
|
loadEvidenceList();
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载取证单列表
|
||||||
|
*/
|
||||||
|
const loadEvidenceList = async () => {
|
||||||
|
if (!form.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 查询该项目的所有审计数据(包括报告和取证单)
|
||||||
|
const res: any = await queryAuditDataByProjectId(form.id);
|
||||||
|
const data = res.data || {};
|
||||||
|
const evidences = data.evidences || [];
|
||||||
|
|
||||||
|
// 存储所有取证单到列表
|
||||||
|
evidenceList.value = evidences.map((item: any, index: number) => ({
|
||||||
|
...item,
|
||||||
|
key: item.id || index
|
||||||
|
}));
|
||||||
|
|
||||||
|
console.log(`加载到 ${evidenceList.value.length} 条取证单记录`);
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('=== 加载取证单列表失败,完整错误对象 ===', e);
|
||||||
|
console.error('错误消息:', e.message);
|
||||||
|
console.error('错误堆栈:', e.stack);
|
||||||
|
if (e.response) {
|
||||||
|
console.error('HTTP 响应状态:', e.response.status);
|
||||||
|
console.error('HTTP 响应数据:', e.response.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载审计报告
|
||||||
|
*/
|
||||||
|
const loadReport = async () => {
|
||||||
|
if (!form.id) {
|
||||||
|
message.warning('无项目信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否选择了取证单
|
||||||
|
if (selectedEvidenceIds.value.length === 0) {
|
||||||
|
message.warning('请先勾选需要加载的取证单记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
// 1. 先获取项目信息(用于获取 caseIndex)
|
||||||
|
const projectInfo = await getPwlProject(form.id);
|
||||||
|
const caseIndex = projectInfo?.caseIndex || 'XX 专字 [202X]4501Z000X 号';
|
||||||
|
|
||||||
|
// 2. 查询审计报告数据(包含 evaluate 和 suggestion),传入勾选的取证单 ID 列表
|
||||||
|
const res: any = await queryAuditDataByProjectId(form.id, selectedEvidenceIds.value);
|
||||||
|
const data = res.data || {};
|
||||||
|
const reports = data.reports || [];
|
||||||
|
const evaluate = data.evaluate || ''; // 审计总体评价
|
||||||
|
const suggestion = data.suggestion || ''; // 改进意见和建议
|
||||||
|
projectName.value = data.projectName || ''; // 委托单位名称(从 pwl_project.name 获取)
|
||||||
|
personName.value = data.personName || ''; // 被审计人员姓名(从 pwl_project.person_name 获取)
|
||||||
|
const auditIntro = data.auditIntro || ''; // 审计引言(从后端接口获取)
|
||||||
|
const otherMatters = data.otherMatters || ''; // 其他事项说明(从后端接口获取)
|
||||||
|
|
||||||
|
// 3. 筛选出用户选中的取证单
|
||||||
|
const selectedEvidences = evidenceList.value.filter((e: any) =>
|
||||||
|
selectedEvidenceIds.value.includes(e.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 合并所有报告内容
|
||||||
|
let htmlContent = '<div class="audit-report-full">';
|
||||||
|
|
||||||
|
// 1. 添加主标题(经济责任审计报告)
|
||||||
|
htmlContent += `<h1 style="text-align: center; font-size: 24px; font-weight: 600; margin: 30px 0 20px 0;">经济责任审计报告</h1>`;
|
||||||
|
|
||||||
|
// 2. 添加文号(右对齐,蓝色)- 从 caseIndex 获取
|
||||||
|
htmlContent += `<div style="text-align: right; margin: 20px 0 30px 0; color: #449ED4; font-size: 14px;">${caseIndex}</div>`;
|
||||||
|
|
||||||
|
// 3. 添加委托单位名称(蓝色,加粗)- 从接口获取 pwl_project.name
|
||||||
|
const clientUnitName = projectName.value || 'XX 单位';
|
||||||
|
htmlContent += `<div style="margin: 20px 0; color: #449ED4; font-size: 14px; font-weight: 600;">${clientUnitName}:</div>`;
|
||||||
|
|
||||||
|
// 构建内容映射(从接口返回的数据中提取)
|
||||||
|
let hasData = false;
|
||||||
|
const contentMap: Record<number, string> = {};
|
||||||
|
|
||||||
|
reports.forEach((report: any) => {
|
||||||
|
if (report.formCommit && report.reportContent) {
|
||||||
|
hasData = true; // 只要有报告记录,就标记为有数据
|
||||||
|
try {
|
||||||
|
const content = JSON.parse(report.reportContent);
|
||||||
|
let extractedContent = '';
|
||||||
|
|
||||||
|
// 如果是 JSON 格式
|
||||||
|
if (content.sections && Array.isArray(content.sections)) {
|
||||||
|
content.sections.forEach((section: any) => {
|
||||||
|
if (section.content) {
|
||||||
|
extractedContent += section.content + '\n';
|
||||||
|
}
|
||||||
|
if (section.records && Array.isArray(section.records)) {
|
||||||
|
section.records.forEach((rec: any) => {
|
||||||
|
if (rec.content) {
|
||||||
|
extractedContent += rec.content + '\n';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果是纯文本
|
||||||
|
extractedContent = report.reportContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存储到对应的 formCommit 下
|
||||||
|
contentMap[report.formCommit] = extractedContent.trim();
|
||||||
|
} catch (e) {
|
||||||
|
// JSON 解析失败,直接存储原文本
|
||||||
|
contentMap[report.formCommit] = report.reportContent;
|
||||||
|
console.log(`formCommit ${report.formCommit}: JSON 解析失败,使用原文本`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`formCommit ${report?.formCommit}: 无数据或 reportContent 为空`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. 添加审计引言段落(整段显示,不换行)- 从后端接口获取
|
||||||
|
const introText = auditIntro || `我们接受委托,自 20XX 年 XX 月 XX 日至 20XX 年 XX 月 XX 日,对${personName.value || 'XX'}同志自 20XX 年 XX 月 XX 日至 20XX 年 XX 月 XX 日担任公司董事长(具体职务)期间的经济责任履行情况进行了就地审计。公司及${personName.value || 'XX'}同志的责任是提供与本次审计相关的全部资料,并保证其真实、完整。我们的责任是在实施审计工作的基础上发表审计意见。审计工作为发表意见提供了合理基础。现将审计情况报告如下:`;
|
||||||
|
|
||||||
|
htmlContent += `<p style="margin: 8px 0; line-height: 1.8; color: #555; font-size: 14px;">${introText}</p>`;
|
||||||
|
|
||||||
|
htmlContent += `<div style="margin: 30px 0;"></div>`; // 添加间距
|
||||||
|
|
||||||
|
// 定义固定的目录结构(根据图片中的对应关系)
|
||||||
|
const outlineStructure = [
|
||||||
|
{
|
||||||
|
title: '一、基本情况',
|
||||||
|
children: [
|
||||||
|
{ formCommit: 10, title: '(一)审计依据', children: [] },
|
||||||
|
{ formCommit: 41, title: `(二)${projectName.value || 'ABC 公司'}概况`, children: [] },
|
||||||
|
{ formCommit: 42, title: `(三)${personName.value || 'XX'}同志任职及分工情况`, children: [] },
|
||||||
|
{ formCommit: 43, title: '(四)实施审计的基本情况', children: [] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{ title: `二、${personName.value || 'XX'}同志主要业绩`, children: [] },
|
||||||
|
{
|
||||||
|
title: '三、履行经济责任的主要情况',
|
||||||
|
children: [
|
||||||
|
{ formCommit: 51, title: '(一)贯彻执行党和国家经济方针政策、决策部署情况', children: [] },
|
||||||
|
{ formCommit: 52, title: '(二)企业发展战略规划的制定、执行和效果情况', children: [] },
|
||||||
|
{ formCommit: 53, title: '(三)重大经济事项的决策、执行和效果情况', children: [] },
|
||||||
|
{ formCommit: 54, title: '(四)企业法人治理结构的建立、健全和运行情况,内部控制制度的制定和执行情况', children: [] },
|
||||||
|
{ formCommit: 55, title: '(五)企业财务的真实合法效益情况,风险管控情况,境外资产管理情况,生态环境保护情况', children: [] },
|
||||||
|
{ formCommit: 56, title: '(六)在经济活动中落实有关党风廉政建设责任和遵守廉洁从业规定情况', children: [] },
|
||||||
|
{ formCommit: 57, title: '(七)以往审计发现问题的整改情况', children: [] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '四、审计总体评价',
|
||||||
|
children: [],
|
||||||
|
customContent: evaluate // 从接口获取的 evaluate 字段
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '五、改进意见和建议',
|
||||||
|
children: [],
|
||||||
|
customContent: suggestion // 从接口获取的 suggestion 字段
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '六、其他事项说明',
|
||||||
|
children: [],
|
||||||
|
customContent: otherMatters // 从接口获取的其他事项说明字段
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// 渲染固定的目录结构
|
||||||
|
const renderOutline = (outline: any[], level: number = 0) => {
|
||||||
|
outline.forEach(item => {
|
||||||
|
const paddingLeft = level * 20;
|
||||||
|
|
||||||
|
// 调试信息:如果是评价和建议部分
|
||||||
|
// if (item.title && (item.title.includes('审计总体评价') || item.title.includes('改进意见和建议'))) {
|
||||||
|
// console.log(`渲染 ${item.title}:`, {
|
||||||
|
// customContent: item.customContent,
|
||||||
|
// customContentType: typeof item.customContent,
|
||||||
|
// customContentLength: item.customContent ? item.customContent.length : 0,
|
||||||
|
// customContentValue: JSON.stringify(item.customContent)
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 优先使用 customContent(从接口获取),其次使用 fixedContent,最后从 contentMap 获取
|
||||||
|
const content = item.customContent !== undefined ? item.customContent :
|
||||||
|
(item.fixedContent || (item.formCommit ? (contentMap[item.formCommit] || '') : ''));
|
||||||
|
const hasContent = content && content.trim().length > 0;
|
||||||
|
|
||||||
|
// 调试信息:如果是评价和建议部分
|
||||||
|
// if (item.title && (item.title.includes('审计总体评价') || item.title.includes('改进意见和建议'))) {
|
||||||
|
// console.log(`${item.title} 最终内容:`, {
|
||||||
|
// hasContent,
|
||||||
|
// contentLength: content ? content.length : 0,
|
||||||
|
// contentFirst50Chars: content ? content.substring(0, 50) : '无'
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (level === 0 && item.formCommit) {
|
||||||
|
// 一级目录(有 formCommit 的)
|
||||||
|
htmlContent += `<div class="report-section" style="margin: 20px 0 10px 0;">`;
|
||||||
|
htmlContent += `<h2 style="font-size: 18px; font-weight: 600; margin: 10px 0; color: #2c3e50;">`;
|
||||||
|
htmlContent += `<span>${item.title}</span>`;
|
||||||
|
htmlContent += `</h2>`;
|
||||||
|
if (hasContent) {
|
||||||
|
htmlContent += `<div style="margin: 10px 0; line-height: 1.8; color: #555; white-space: pre-wrap;">${content}</div>`;
|
||||||
|
} else {
|
||||||
|
// 调试:如果没有内容
|
||||||
|
console.warn(`${item.title} 没有显示内容,hasContent=false`);
|
||||||
|
}
|
||||||
|
htmlContent += `</div>`;
|
||||||
|
} else if (level === 1 && item.formCommit) {
|
||||||
|
// 二级目录(有 formCommit 的)
|
||||||
|
htmlContent += `<div class="subsection" style="margin: 8px 0; padding-left: ${paddingLeft + 20}px;">`;
|
||||||
|
htmlContent += `<h3 style="font-size: 16px; font-weight: 500; margin: 8px 0; color: #34495e;">`;
|
||||||
|
htmlContent += `<span>${item.title}</span>`;
|
||||||
|
htmlContent += `</h3>`;
|
||||||
|
|
||||||
|
// 如果有内容,显示内容
|
||||||
|
if (hasContent) {
|
||||||
|
htmlContent += `<div style="margin: 6px 0; line-height: 1.8; color: #555; white-space: pre-wrap;">${content}</div>`;
|
||||||
|
}
|
||||||
|
htmlContent += `</div>`;
|
||||||
|
} else if (level === 1 && !item.formCommit) {
|
||||||
|
// 二级目录(没有 formCommit,直接显示标题作为内容)
|
||||||
|
htmlContent += `<div class="subsection" style="margin: 8px 0; padding-left: ${paddingLeft + 20}px;">`;
|
||||||
|
htmlContent += `<div style="font-size: 14px; line-height: 1.8; color: #555;">`;
|
||||||
|
htmlContent += `<span>${item.title}</span>`;
|
||||||
|
htmlContent += `</div>`;
|
||||||
|
htmlContent += `</div>`;
|
||||||
|
} else if (level === 0 && !item.formCommit) {
|
||||||
|
// 一级分组标题(如"六、其他事项说明")或自定义内容(如评价和建议)
|
||||||
|
htmlContent += `<div class="report-section" style="margin: 20px 0 10px 0;">`;
|
||||||
|
htmlContent += `<h2 style="font-size: 18px; font-weight: 600; margin: 10px 0; color: #2c3e50;">`;
|
||||||
|
htmlContent += `<span>${item.title}</span>`;
|
||||||
|
htmlContent += `</h2>`;
|
||||||
|
|
||||||
|
// 如果有 customContent,显示它
|
||||||
|
if (hasContent) {
|
||||||
|
// 判断是否是评价或建议章节
|
||||||
|
const isEvaluateOrSuggestion = item.title &&
|
||||||
|
(item.title.includes('审计总体评价') || item.title.includes('改进意见和建议'));
|
||||||
|
|
||||||
|
if (isEvaluateOrSuggestion && content) {
|
||||||
|
// 对于评价和建议,后端已经拼接好标题和内容,直接按换行符分割显示
|
||||||
|
const lines = content.split(/\n+/);
|
||||||
|
let currentTitle = '';
|
||||||
|
let currentParagraphs: string[] = [];
|
||||||
|
|
||||||
|
const flushParagraph = () => {
|
||||||
|
if (currentTitle && currentParagraphs.length > 0) {
|
||||||
|
htmlContent += `<div style="margin: 15px 0;">`;
|
||||||
|
htmlContent += `<h4 style="font-size: 15px; font-weight: 600; color: #2c3e50; margin: 10px 0 8px 0;">${currentTitle}</h4>`;
|
||||||
|
currentParagraphs.forEach((p: string) => {
|
||||||
|
htmlContent += `<p style="margin: 6px 0; line-height: 1.8; color: #555; text-indent: 2em;">${p}</p>`;
|
||||||
|
});
|
||||||
|
htmlContent += `</div>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
if (!trimmedLine) continue; // 跳过空行
|
||||||
|
|
||||||
|
// 如果行首有 4 个空格缩进,说明是段落内容
|
||||||
|
if (line.startsWith(' ')) {
|
||||||
|
currentParagraphs.push(trimmedLine);
|
||||||
|
} else {
|
||||||
|
// 否则是标题
|
||||||
|
flushParagraph(); // 先保存之前的段落
|
||||||
|
currentTitle = trimmedLine;
|
||||||
|
currentParagraphs = []; // 重置段落数组
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理最后一组
|
||||||
|
flushParagraph();
|
||||||
|
} else {
|
||||||
|
// 其他章节保持原有逻辑
|
||||||
|
const paragraphs = content.split(/\n+/).filter((p: string) => p.trim().length > 0);
|
||||||
|
if (paragraphs.length > 0) {
|
||||||
|
paragraphs.forEach((paragraph: string) => {
|
||||||
|
htmlContent += `<p style="margin: 8px 0; line-height: 1.8; color: #555; text-indent: 2em;">${paragraph}</p>`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
htmlContent += `<div style="margin: 10px 0; line-height: 1.8; color: #555; white-space: pre-wrap; text-indent: 2em;">${content}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
renderOutline(item.children, level + 1);
|
||||||
|
}
|
||||||
|
htmlContent += `</div>`;
|
||||||
|
return; // 跳过后续的递归
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归渲染子目录
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
renderOutline(item.children, level + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderOutline(outlineStructure);
|
||||||
|
|
||||||
|
// 调试:检查最终生成的 HTML
|
||||||
|
console.log('=== 最终生成的 HTML(包含评价和建议的部分)===');
|
||||||
|
const evaluateMatch = htmlContent.match(/四、审计总体评价[\s\S]{0,500}/);
|
||||||
|
const suggestionMatch = htmlContent.match(/五、改进意见和建议[\s\S]{0,500}/);
|
||||||
|
|
||||||
|
htmlContent += '</div>';
|
||||||
|
|
||||||
|
if (hasData) {
|
||||||
|
reportContent.value = htmlContent;
|
||||||
|
message.success('报告加载成功');
|
||||||
|
} else {
|
||||||
|
reportContent.value = '';
|
||||||
|
message.info('暂无保存的审计报告和取证单');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('加载报告失败:', e);
|
||||||
|
message.error('加载报告失败:' + (e?.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取内容类型文本
|
||||||
|
*/
|
||||||
|
const getContentTypeText = (contentType: number): string => {
|
||||||
|
const typeMap: Record<number, string> = {
|
||||||
|
1: '审计依据',
|
||||||
|
2: `${projectName.value || 'ABC 公司'}概况`,
|
||||||
|
3: `${personName.value || 'XX'}同志任职及分工情况`,
|
||||||
|
4: '实施审计情况',
|
||||||
|
5: '主要业绩',
|
||||||
|
6: '贯彻执行方针政策',
|
||||||
|
7: '企业发展战略',
|
||||||
|
8: '重大经济事项',
|
||||||
|
9: '法人治理结构',
|
||||||
|
10: '财务真实合法效益',
|
||||||
|
11: '党风廉政建设'
|
||||||
|
};
|
||||||
|
return typeMap[contentType] || `类型${contentType}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格复选框选择变化
|
||||||
|
*/
|
||||||
|
const onSelectChange = (selectedRowKeys: number[]) => {
|
||||||
|
selectedEvidenceIds.value = selectedRowKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开取证单详情弹窗
|
||||||
|
*/
|
||||||
|
const openEvidenceDetail = (record: any) => {
|
||||||
|
currentEvidence.value = record;
|
||||||
|
evidenceModalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成审计报告
|
||||||
|
*/
|
||||||
|
const generateReport = async () => {
|
||||||
|
if (!form.id) {
|
||||||
|
message.warning('无项目信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否选择了取证单
|
||||||
|
if (selectedEvidenceIds.value.length === 0) {
|
||||||
|
message.warning('请先勾选需要生成的取证单记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
generating.value = true;
|
||||||
|
try {
|
||||||
|
// 调用后端接口,根据 project_id 和选中的取证单 ID 生成 Word 文档
|
||||||
|
const blob = await generateAuditReportWithEvidences(form.id, selectedEvidenceIds.value);
|
||||||
|
|
||||||
|
// 创建下载链接
|
||||||
|
const url = window.URL.createObjectURL(new Blob([blob]));
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.setAttribute('download', `审计报告_${form.name || form.code}.docx`);
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
message.success('报告生成成功,已开始下载');
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error('生成报告失败:', e);
|
||||||
|
message.error('生成报告失败:' + (e?.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
generating.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.audit-report-container {
|
||||||
|
min-height: calc(100vh - 100px);
|
||||||
|
|
||||||
|
.action-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
.action-button {
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-content-card {
|
||||||
|
min-height: 500px;
|
||||||
|
|
||||||
|
.evidence-selection-area {
|
||||||
|
.selected-info {
|
||||||
|
margin-top: 16px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-content {
|
||||||
|
line-height: 1.8;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: pre-wrap; /* 自动换行 */
|
||||||
|
word-wrap: break-word; /* 长单词换行 */
|
||||||
|
word-break: break-all; /* 所有字符可换行 */
|
||||||
|
|
||||||
|
:deep(h1) {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
white-space: normal; /* 标题正常换行 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(h2) {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 16px 0;
|
||||||
|
color: #2c3e50;
|
||||||
|
border-left: 4px solid #1890ff;
|
||||||
|
padding-left: 12px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(h3) {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 12px 0;
|
||||||
|
color: #34495e;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(p) {
|
||||||
|
margin: 8px 0;
|
||||||
|
text-indent: 2em;
|
||||||
|
color: #555;
|
||||||
|
white-space: pre-wrap; /* 段落自动换行 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.report-section) {
|
||||||
|
margin: 30px 0;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.section-content {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-left: 3px solid #1890ff;
|
||||||
|
white-space: pre-wrap; /* 内容自动换行 */
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subsection {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px 15px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
white-space: pre-wrap; /* 子节自动换行 */
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.evidence-item) {
|
||||||
|
margin: 15px 0;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
white-space: pre-wrap; /* 取证单内容自动换行 */
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(table) {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 16px 0;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre-wrap; /* 表格内容自动换行 */
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: #fafafa;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -99,11 +99,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<a-space>
|
<a-space>
|
||||||
<a class="action-btn bg-blue-500" @click="openReport(record)"
|
<a class="action-btn bg-blue-500" @click="openReport(record)"
|
||||||
>1生成审计方案</a
|
>1 生成审计方案</a
|
||||||
>
|
>
|
||||||
<a class="action-btn bg-green-600" @click="openReportContent(record)">2审计内容</a>
|
<a class="action-btn bg-green-600" @click="openReportContent(record)">2 审计内容</a>
|
||||||
<a class="action-btn bg-red-600" @click="openAuditCheck(record)"
|
<a class="action-btn bg-red-600" @click="openReportView(record)"
|
||||||
>3审计核查</a
|
>3 审计报告</a
|
||||||
>
|
>
|
||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
@@ -128,6 +128,8 @@
|
|||||||
<!-- 生成报告 -->
|
<!-- 生成报告 -->
|
||||||
<Report v-model:visible="showReport" :data="current" @done="reload" />
|
<Report v-model:visible="showReport" :data="current" @done="reload" />
|
||||||
<ReportContent v-model:visible="showReportContent" :data="current" @done="reload" />
|
<ReportContent v-model:visible="showReportContent" :data="current" @done="reload" />
|
||||||
|
<!-- 审计报告查看 -->
|
||||||
|
<ReportView v-model:visible="showReportView" :data="current" @done="reload" />
|
||||||
<!-- 审计核查弹窗 -->
|
<!-- 审计核查弹窗 -->
|
||||||
<AuditCheck
|
<AuditCheck
|
||||||
v-model:visible="showAuditCheck"
|
v-model:visible="showAuditCheck"
|
||||||
@@ -202,6 +204,8 @@
|
|||||||
import Edit from './components/pwlProjectEdit.vue';
|
import Edit from './components/pwlProjectEdit.vue';
|
||||||
import Report from './components/report.vue';
|
import Report from './components/report.vue';
|
||||||
import ReportContent from './components/reportContent.vue';
|
import ReportContent from './components/reportContent.vue';
|
||||||
|
import ReportView from './components/reportView.vue';
|
||||||
|
import AuditCheck from './components/auditCheck.vue';
|
||||||
import {
|
import {
|
||||||
pagePwlProject,
|
pagePwlProject,
|
||||||
removePwlProject,
|
removePwlProject,
|
||||||
@@ -215,7 +219,6 @@
|
|||||||
getKnowledgeBaseDocuments,
|
getKnowledgeBaseDocuments,
|
||||||
deleteKnowledgeBaseDocument
|
deleteKnowledgeBaseDocument
|
||||||
} from '@/api/ai/knowledgeBase';
|
} from '@/api/ai/knowledgeBase';
|
||||||
import AuditCheck from './components/auditCheck.vue';
|
|
||||||
|
|
||||||
// 表格实例
|
// 表格实例
|
||||||
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
|
||||||
@@ -228,6 +231,10 @@
|
|||||||
const showEdit = ref(false);
|
const showEdit = ref(false);
|
||||||
// 是否显示报告弹窗
|
// 是否显示报告弹窗
|
||||||
const showReport = ref(false);
|
const showReport = ref(false);
|
||||||
|
// 是否显示审计内容弹窗
|
||||||
|
const showReportContent = ref(false);
|
||||||
|
// 是否显示审计报告查看弹窗
|
||||||
|
const showReportView = ref(false);
|
||||||
// 是否显示审计核查弹窗
|
// 是否显示审计核查弹窗
|
||||||
const showAuditCheck = ref(false);
|
const showAuditCheck = ref(false);
|
||||||
// 是否显示批量移动弹窗
|
// 是否显示批量移动弹窗
|
||||||
@@ -273,6 +280,12 @@
|
|||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 打开审计报告查看弹窗
|
||||||
|
const openReportView = (record: PwlProject) => {
|
||||||
|
current.value = record;
|
||||||
|
showReportView.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
// 打开审计核查弹窗
|
// 打开审计核查弹窗
|
||||||
const openAuditCheck = (record: PwlProject) => {
|
const openAuditCheck = (record: PwlProject) => {
|
||||||
current.value = record;
|
current.value = record;
|
||||||
@@ -610,7 +623,6 @@
|
|||||||
showReport.value = true;
|
showReport.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const showReportContent = ref(false)
|
|
||||||
const openReportContent = (row?: PwlProject) => {
|
const openReportContent = (row?: PwlProject) => {
|
||||||
current.value = row ?? null;
|
current.value = row ?? null;
|
||||||
showReportContent.value = true;
|
showReportContent.value = true;
|
||||||
|
|||||||
60
yarn.lock
60
yarn.lock
@@ -661,7 +661,7 @@
|
|||||||
|
|
||||||
"@esbuild/linux-loong64@0.14.54":
|
"@esbuild/linux-loong64@0.14.54":
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
|
resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz"
|
||||||
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
|
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.3.1":
|
"@eslint/eslintrc@^1.3.1":
|
||||||
@@ -2652,17 +2652,17 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3:
|
|||||||
|
|
||||||
esbuild-android-64@0.14.54:
|
esbuild-android-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
|
resolved "https://registry.npmmirror.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz"
|
||||||
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
|
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
|
||||||
|
|
||||||
esbuild-android-arm64@0.14.54:
|
esbuild-android-arm64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
|
resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz"
|
||||||
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
|
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
|
||||||
|
|
||||||
esbuild-darwin-64@0.14.54:
|
esbuild-darwin-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
|
resolved "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz"
|
||||||
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
|
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
|
||||||
|
|
||||||
esbuild-darwin-arm64@0.14.54:
|
esbuild-darwin-arm64@0.14.54:
|
||||||
@@ -2672,82 +2672,82 @@ esbuild-darwin-arm64@0.14.54:
|
|||||||
|
|
||||||
esbuild-freebsd-64@0.14.54:
|
esbuild-freebsd-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
|
resolved "https://registry.npmmirror.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz"
|
||||||
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
|
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
|
||||||
|
|
||||||
esbuild-freebsd-arm64@0.14.54:
|
esbuild-freebsd-arm64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
|
resolved "https://registry.npmmirror.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz"
|
||||||
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
|
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
|
||||||
|
|
||||||
esbuild-linux-32@0.14.54:
|
esbuild-linux-32@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
|
resolved "https://registry.npmmirror.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz"
|
||||||
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
|
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
|
||||||
|
|
||||||
esbuild-linux-64@0.14.54:
|
esbuild-linux-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
|
resolved "https://registry.npmmirror.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz"
|
||||||
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
|
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
|
||||||
|
|
||||||
esbuild-linux-arm64@0.14.54:
|
esbuild-linux-arm64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
|
resolved "https://registry.npmmirror.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz"
|
||||||
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
|
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
|
||||||
|
|
||||||
esbuild-linux-arm@0.14.54:
|
esbuild-linux-arm@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
|
resolved "https://registry.npmmirror.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz"
|
||||||
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
|
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
|
||||||
|
|
||||||
esbuild-linux-mips64le@0.14.54:
|
esbuild-linux-mips64le@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
|
resolved "https://registry.npmmirror.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz"
|
||||||
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
|
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
|
||||||
|
|
||||||
esbuild-linux-ppc64le@0.14.54:
|
esbuild-linux-ppc64le@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
|
resolved "https://registry.npmmirror.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz"
|
||||||
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
|
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
|
||||||
|
|
||||||
esbuild-linux-riscv64@0.14.54:
|
esbuild-linux-riscv64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
|
resolved "https://registry.npmmirror.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz"
|
||||||
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
|
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
|
||||||
|
|
||||||
esbuild-linux-s390x@0.14.54:
|
esbuild-linux-s390x@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
|
resolved "https://registry.npmmirror.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz"
|
||||||
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
|
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
|
||||||
|
|
||||||
esbuild-netbsd-64@0.14.54:
|
esbuild-netbsd-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
|
resolved "https://registry.npmmirror.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz"
|
||||||
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
|
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
|
||||||
|
|
||||||
esbuild-openbsd-64@0.14.54:
|
esbuild-openbsd-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
|
resolved "https://registry.npmmirror.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz"
|
||||||
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
|
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
|
||||||
|
|
||||||
esbuild-sunos-64@0.14.54:
|
esbuild-sunos-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
|
resolved "https://registry.npmmirror.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz"
|
||||||
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
|
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
|
||||||
|
|
||||||
esbuild-windows-32@0.14.54:
|
esbuild-windows-32@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
|
resolved "https://registry.npmmirror.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz"
|
||||||
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
|
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
|
||||||
|
|
||||||
esbuild-windows-64@0.14.54:
|
esbuild-windows-64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
|
resolved "https://registry.npmmirror.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz"
|
||||||
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
|
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
|
||||||
|
|
||||||
esbuild-windows-arm64@0.14.54:
|
esbuild-windows-arm64@0.14.54:
|
||||||
version "0.14.54"
|
version "0.14.54"
|
||||||
resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
|
resolved "https://registry.npmmirror.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz"
|
||||||
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
|
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
|
||||||
|
|
||||||
esbuild@^0.14.47:
|
esbuild@^0.14.47:
|
||||||
@@ -5665,16 +5665,7 @@ stream-wormhole@^1.0.4:
|
|||||||
resolved "https://registry.npmmirror.com/stream-wormhole/-/stream-wormhole-1.1.0.tgz"
|
resolved "https://registry.npmmirror.com/stream-wormhole/-/stream-wormhole-1.1.0.tgz"
|
||||||
integrity sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==
|
integrity sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
||||||
version "4.2.3"
|
|
||||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
|
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
|
||||||
dependencies:
|
|
||||||
emoji-regex "^8.0.0"
|
|
||||||
is-fullwidth-code-point "^3.0.0"
|
|
||||||
strip-ansi "^6.0.1"
|
|
||||||
|
|
||||||
string-width@^4.1.0:
|
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
|
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@@ -5742,7 +5733,7 @@ stringify-entities@^4.0.2:
|
|||||||
character-entities-html4 "^2.0.0"
|
character-entities-html4 "^2.0.0"
|
||||||
character-entities-legacy "^3.0.0"
|
character-entities-legacy "^3.0.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
@@ -5756,13 +5747,6 @@ strip-ansi@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ansi-regex "^2.0.0"
|
ansi-regex "^2.0.0"
|
||||||
|
|
||||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|
||||||
version "6.0.1"
|
|
||||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
|
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
|
||||||
dependencies:
|
|
||||||
ansi-regex "^5.0.1"
|
|
||||||
|
|
||||||
strip-ansi@^7.0.1:
|
strip-ansi@^7.0.1:
|
||||||
version "7.1.0"
|
version "7.1.0"
|
||||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz"
|
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user