feat(pwl):添加审计核查功能初版

This commit is contained in:
2025-10-16 15:55:34 +08:00
parent 39e79f2844
commit ae5d429630
2 changed files with 198 additions and 34 deletions

View File

@@ -17,6 +17,20 @@
<div class="title">审计发现问题情况表</div>
<div class="buttons">
<a-space>
<!-- 附件上传 -->
<a-upload
v-model:file-list="fileList"
:before-upload="beforeUpload"
:max-count="1"
:show-upload-list="true"
accept=".pdf,.doc,.docx,.xls,.xlsx,.txt"
>
<a-button class="action-button">
<template #icon><UploadOutlined /></template>
上传附件
</a-button>
</a-upload>
<a-button type="primary" @click="addProblem" class="action-button">
<template #icon><PlusOutlined /></template>
新增问题
@@ -37,12 +51,27 @@
</div>
</div>
<!-- 附件信息显示 -->
<a-card v-if="fileList.length > 0" :bordered="false" class="file-info-card">
<template #title>
<PaperClipOutlined /> 已上传附件
</template>
<div class="file-info">
<div class="file-item" v-for="file in fileList" :key="file.uid">
<div class="file-name">{{ file.name }}</div>
<a-button type="link" size="small" @click="removeFile(file.uid)" danger>
删除
</a-button>
</div>
</div>
</a-card>
<!-- 问题列表表格 -->
<a-card :bordered="false" class="table-card">
<a-table
:dataSource="problemList"
:columns="columns"
:scroll="{ x: 1500 }"
:scroll="{ x: 1800 }"
:pagination="false"
rowKey="id"
size="middle"
@@ -54,6 +83,13 @@
{{ index + 1 }}
</template>
<!-- 是否存在问题列 -->
<template v-if="column.dataIndex === 'hasProblem'">
<a-tag :color="record.hasProblem === 'true' ? 'red' : 'green'">
{{ record.hasProblem === 'true' ? '是' : '否' }}
</a-tag>
</template>
<!-- 金额列 -->
<template v-if="column.dataIndex === 'amount'">
<a-input-number
@@ -80,10 +116,11 @@
<!-- 整改时限列 -->
<template v-if="column.dataIndex === 'deadline'">
<a-date-picker
<a-input
v-model:value="record.deadline"
style="width: 100%"
:bordered="false"
placeholder="如1个月"
/>
</template>
@@ -121,6 +158,12 @@
@cancel="cancelEdit"
>
<a-form layout="vertical">
<a-form-item label="是否存在相关问题" required>
<a-radio-group v-model:value="editingProblem.hasProblem">
<a-radio value="true"></a-radio>
<a-radio value="false"></a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="问题表述" required>
<a-textarea
v-model:value="editingProblem.problemDescription"
@@ -143,7 +186,7 @@
/>
</a-form-item>
<a-row :gutter="16">
<a-col :span="12">
<a-col :span="8">
<a-form-item label="涉及金额(万元)">
<a-input-number
v-model:value="editingProblem.amount"
@@ -153,7 +196,7 @@
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-col :span="8">
<a-form-item label="整改类型">
<a-select v-model:value="editingProblem.rectificationType">
<a-select-option value="立行立改">立行立改</a-select-option>
@@ -162,6 +205,14 @@
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="整改时限">
<a-input
v-model:value="editingProblem.deadline"
placeholder="如1个月"
/>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="具体责任界定">
<a-textarea
@@ -177,12 +228,6 @@
placeholder="请输入整改要求"
/>
</a-form-item>
<a-form-item label="整改时限">
<a-date-picker
v-model:value="editingProblem.deadline"
style="width: 100%"
/>
</a-form-item>
<a-form-item label="审计建议">
<a-textarea
v-model:value="editingProblem.auditSuggestion"
@@ -199,13 +244,17 @@
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { message } from 'ant-design-vue';
import type { UploadProps } from 'ant-design-vue';
import {
PlusOutlined,
RobotOutlined,
ExportOutlined,
SaveOutlined
SaveOutlined,
UploadOutlined,
PaperClipOutlined
} from '@ant-design/icons-vue';
import type { PwlProject } from '@/api/pwl/pwlProject/model';
import { generateAuditReport2 } from "@/api/ai/auditReport";
// Props 定义
const props = defineProps<{
@@ -232,7 +281,9 @@ const otherProblems = ref('');
const loading = ref(false);
const generating = ref(false);
const showEditModal = ref(false);
const fileList = ref<any[]>([]);
const editingProblem = ref<any>({
hasProblem: 'true',
problemDescription: '',
problemDetails: '',
legalBasis: '',
@@ -240,11 +291,11 @@ const editingProblem = ref<any>({
responsibility: '',
rectificationType: '',
rectificationRequirement: '',
deadline: null,
deadline: '',
auditSuggestion: ''
});
// 表格列定义
// 表格列定义 - 优化后
const columns = [
{
title: '序号',
@@ -252,6 +303,12 @@ const columns = [
width: 60,
fixed: 'left'
},
{
title: '是否存在问题',
dataIndex: 'hasProblem',
width: 100,
fixed: 'left'
},
{
title: '问题表述',
dataIndex: 'problemDescription',
@@ -311,6 +368,18 @@ const columns = [
}
];
// 上传前处理
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
// 添加到文件列表
fileList.value = [file];
return false; // 阻止自动上传
};
// 删除文件
const removeFile = (uid: string) => {
fileList.value = fileList.value.filter(file => file.uid !== uid);
};
// 更新visible
const updateVisible = (value: boolean) => {
emit('update:visible', value);
@@ -319,6 +388,7 @@ const updateVisible = (value: boolean) => {
// 新增问题
const addProblem = () => {
editingProblem.value = {
hasProblem: 'true',
problemDescription: '',
problemDetails: '',
legalBasis: '',
@@ -326,7 +396,7 @@ const addProblem = () => {
responsibility: '',
rectificationType: '',
rectificationRequirement: '',
deadline: null,
deadline: '',
auditSuggestion: ''
};
showEditModal.value = true;
@@ -370,29 +440,71 @@ const deleteProblem = (index: number) => {
message.success('删除成功');
};
// AI生成问题
// AI生成问题 - 优化版
const generateProblems = async () => {
// 检查是否有上传文件
if (fileList.value.length === 0) {
message.warning('请先上传附件文件');
return;
}
generating.value = true;
try {
await new Promise(resolve => setTimeout(resolve, 2000));
const sampleProblems = [
{
id: Date.now(),
problemDescription: '内部控制制度的制定和执行情况,未建立代管业务相关的管理办法',
problemDetails: '审计发现,企业管理公司未建立代管业务的管理办法及代管业务流程机制。',
legalBasis: '上述情况不符合《企业内部控制基本规范》第四条"企业建立与实施内部控制,应当贯穿决策、执行和监督全过程,覆盖企业及其所属单位的各种业务和事项。"的规定。',
amount: 0,
responsibility: '罗世东同志、莫咏麟同志对任期内问题负有领导责任',
rectificationType: '限期整改',
rectificationRequirement: '建立健全代管业务管理制度',
deadline: null,
auditSuggestion: '建议尽快制定代管业务相关管理办法,明确业务流程和职责分工'
}
];
problemList.value = [...problemList.value, ...sampleProblems];
message.success('问题生成成功');
} catch (error) {
message.error('问题生成失败');
console.log('当前行数据:', props.data);
console.log('项目名称:', props.data?.name);
console.log('项目编号:', props.data?.code);
console.log('分析文库:', props.data?.analysisLibrary);
console.log('项目文库1:', props.data?.projectLibrary);
console.log('项目文库2:', props.data?.kbId);
console.log('项目文库3:', props.data?.libraryIds);
// 获取上传的文件
const fileItem = fileList.value[0];
const file = fileItem.originFileObj || fileItem;
// 调用后端接口
const result = await generateAuditReport2(
file,
props.data?.kbId,
props.data?.libraryIds,
props.data?.analysisLibrary,
props.data?.projectLibrary
);
// 处理返回结果 - 优化后的数据映射
if (result && result.findings) {
const generatedProblems = result.findings.map((finding: any, index: number) => {
// 统一处理字段映射
const problem = {
id: Date.now() + index,
hasProblem: finding['是否存在相关问题'] || 'true',
problemDescription: finding['问题表述'] || finding.problemDescription || '',
problemDetails: finding['问题明细'] || finding.problemDetails || '',
legalBasis: finding['法规依据'] || finding.legalBasis || '',
amount: parseFloat(finding['涉及金额(万元)']) || finding.amount || 0,
responsibility: finding['具体责任界定'] || finding.responsibility || '',
rectificationType: finding['整改类型'] || finding.rectificationType || '',
rectificationRequirement: finding['整改要求'] || finding.rectificationRequirement || '',
deadline: finding['整改时限'] || finding.deadline || '',
auditSuggestion: finding['审计建议'] || finding.auditSuggestion || ''
};
// 处理金额字段的特殊情况
if (finding['涉及金额(万元)'] === '/') {
problem.amount = 0;
}
return problem;
});
problemList.value = generatedProblems;
message.success(`成功生成 ${generatedProblems.length} 个审计问题`);
} else {
throw new Error('生成结果格式错误');
}
} catch (error: any) {
console.error('AI生成问题失败:', error);
message.error('生成失败: ' + (error.message || '未知错误'));
} finally {
generating.value = false;
}
@@ -417,6 +529,7 @@ const saveData = () => {
const loadAuditCheckData = () => {
problemList.value = [];
otherProblems.value = '';
fileList.value = [];
};
// 监听数据变化
@@ -459,6 +572,23 @@ watch(
}
}
.file-info-card {
margin-bottom: 16px;
.file-info {
.file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
.file-name {
font-weight: 500;
}
}
}
}
.table-card {
margin-bottom: 16px;