Files
jzsj-vue/src/views/pwl/pwlProject/components/auditCheck.vue
赵忠林 92a6a32868 chore(config): 添加项目配置文件和隐私协议
- 新增 .editorconfig 文件统一代码风格配置
- 新增 .env 环境变量配置文件
- 添加开发和生产环境的环境变量配置
- 配置 ESLint 忽略规则文件
- 设置代码检查配置文件 .eslintrc.js
- 添加 Git 忽略文件规则
- 创建 Prettier 格式化忽略规则
- 添加隐私政策和服务协议HTML文件
- 实现访问密钥编辑组件基础结构
2026-02-07 16:33:13 +08:00

636 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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-container">
<!-- 顶部操作区 -->
<div class="action-bar">
<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>
新增问题
</a-button>
<a-button @click="generateProblems" :loading="generating" class="action-button">
<template #icon><RobotOutlined /></template>
AI生成问题
</a-button>
<a-button @click="exportExcel" class="action-button">
<template #icon><ExportOutlined /></template>
导出Excel
</a-button>
<a-button type="danger" @click="saveData" class="action-button">
<template #icon><SaveOutlined /></template>
保存数据
</a-button>
</a-space>
</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: 1800 }"
:pagination="false"
rowKey="id"
size="middle"
bordered
>
<template #bodyCell="{ column, record, index }">
<!-- 序号列 -->
<template v-if="column.dataIndex === 'index'">
{{ 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
v-model:value="record.amount"
:min="0"
:precision="2"
style="width: 100%"
:bordered="false"
/>
</template>
<!-- 整改类型列 -->
<template v-if="column.dataIndex === 'rectificationType'">
<a-select
v-model:value="record.rectificationType"
style="width: 100%"
:bordered="false"
>
<a-select-option value="立行立改">立行立改</a-select-option>
<a-select-option value="限期整改">限期整改</a-select-option>
<a-select-option value="长期整改">长期整改</a-select-option>
</a-select>
</template>
<!-- 整改时限列 -->
<template v-if="column.dataIndex === 'deadline'">
<a-input
v-model:value="record.deadline"
style="width: 100%"
:bordered="false"
placeholder="如1个月"
/>
</template>
<!-- 操作列 -->
<template v-if="column.dataIndex === 'action'">
<a-space>
<a-button type="link" @click="editProblem(record)">
编辑
</a-button>
<a-button type="link" danger @click="deleteProblem(index)">
删除
</a-button>
</a-space>
</template>
</template>
</a-table>
</a-card>
<!-- 其他问题区域 -->
<a-card :bordered="false" class="other-problems-card">
<template #title>审计发现的其他问题未在报告正文反映</template>
<a-textarea
v-model:value="otherProblems"
:rows="4"
placeholder="请输入其他审计发现问题..."
/>
</a-card>
<!-- 问题编辑弹窗 -->
<a-modal
v-model:visible="showEditModal"
:title="editingProblem.id ? '编辑问题' : '新增问题'"
width="800px"
@ok="saveProblem"
@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"
:rows="3"
placeholder="请输入问题表述"
/>
</a-form-item>
<a-form-item label="问题明细">
<a-textarea
v-model:value="editingProblem.problemDetails"
:rows="3"
placeholder="请输入问题明细"
/>
</a-form-item>
<a-form-item label="法规依据">
<a-textarea
v-model:value="editingProblem.legalBasis"
:rows="3"
placeholder="请输入法规依据"
/>
</a-form-item>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="涉及金额(万元)">
<a-input-number
v-model:value="editingProblem.amount"
:min="0"
:precision="2"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="整改类型">
<a-select v-model:value="editingProblem.rectificationType">
<a-select-option value="立行立改">立行立改</a-select-option>
<a-select-option value="限期整改">限期整改</a-select-option>
<a-select-option value="长期整改">长期整改</a-select-option>
</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
v-model:value="editingProblem.responsibility"
:rows="2"
placeholder="请输入具体责任界定"
/>
</a-form-item>
<a-form-item label="整改要求">
<a-textarea
v-model:value="editingProblem.rectificationRequirement"
:rows="2"
placeholder="请输入整改要求"
/>
</a-form-item>
<a-form-item label="审计建议">
<a-textarea
v-model:value="editingProblem.auditSuggestion"
:rows="3"
placeholder="请输入审计建议"
/>
</a-form-item>
</a-form>
</a-modal>
</div>
</a-drawer>
</template>
<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,
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<{
visible: boolean;
data?: PwlProject | null;
}>();
// Emits 定义
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 表单数据
const form = reactive<PwlProject>({
id: undefined,
name: undefined,
code: undefined,
});
// 状态变量
const problemList = ref<any[]>([]);
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: '',
amount: 0,
responsibility: '',
rectificationType: '',
rectificationRequirement: '',
deadline: '',
auditSuggestion: ''
});
// 表格列定义 - 优化后
const columns = [
{
title: '序号',
dataIndex: 'index',
width: 60,
fixed: 'left'
},
{
title: '是否存在问题',
dataIndex: 'hasProblem',
width: 100,
fixed: 'left'
},
{
title: '问题表述',
dataIndex: 'problemDescription',
width: 200,
ellipsis: true
},
{
title: '问题明细',
dataIndex: 'problemDetails',
width: 200,
ellipsis: true
},
{
title: '法规依据',
dataIndex: 'legalBasis',
width: 200,
ellipsis: true
},
{
title: '涉及金额(万元)',
dataIndex: 'amount',
width: 120
},
{
title: '具体责任界定',
dataIndex: 'responsibility',
width: 150,
ellipsis: true
},
{
title: '整改类型',
dataIndex: 'rectificationType',
width: 120
},
{
title: '整改要求',
dataIndex: 'rectificationRequirement',
width: 150,
ellipsis: true
},
{
title: '整改时限',
dataIndex: 'deadline',
width: 120
},
{
title: '审计建议',
dataIndex: 'auditSuggestion',
width: 150,
ellipsis: true
},
{
title: '操作',
dataIndex: 'action',
width: 120,
fixed: 'right'
}
];
// 上传前处理
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);
};
// 新增问题
const addProblem = () => {
editingProblem.value = {
hasProblem: 'true',
problemDescription: '',
problemDetails: '',
legalBasis: '',
amount: 0,
responsibility: '',
rectificationType: '',
rectificationRequirement: '',
deadline: '',
auditSuggestion: ''
};
showEditModal.value = true;
};
// 编辑问题
const editProblem = (problem: any) => {
editingProblem.value = { ...problem };
showEditModal.value = true;
};
// 保存问题
const saveProblem = () => {
if (!editingProblem.value.problemDescription) {
message.warning('请输入问题表述');
return;
}
if (!editingProblem.value.id) {
editingProblem.value.id = Date.now();
problemList.value.push({ ...editingProblem.value });
} else {
const index = problemList.value.findIndex(p => p.id === editingProblem.value.id);
if (index !== -1) {
problemList.value[index] = { ...editingProblem.value };
}
}
message.success('保存成功');
showEditModal.value = false;
};
// 取消编辑
const cancelEdit = () => {
showEditModal.value = false;
};
// 删除问题
const deleteProblem = (index: number) => {
problemList.value.splice(index, 1);
message.success('删除成功');
};
// AI生成问题 - 优化版
const generateProblems = async () => {
// 检查是否有上传文件
if (fileList.value.length === 0) {
message.warning('请先上传附件文件');
return;
}
generating.value = true;
try {
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;
}
};
// 导出Excel
const exportExcel = () => {
message.info('导出功能开发中...');
};
// 保存数据
const saveData = () => {
const dataToSave = {
problems: problemList.value,
otherProblems: otherProblems.value
};
console.log('保存的数据:', dataToSave);
message.success('数据保存成功');
};
// 加载审计核查数据
const loadAuditCheckData = () => {
problemList.value = [];
otherProblems.value = '';
fileList.value = [];
};
// 监听数据变化
watch(
() => props.data,
(data) => {
if (data) {
Object.assign(form, data);
loadAuditCheckData();
}
}
);
</script>
<style lang="less" scoped>
.audit-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;
transition: all 0.3s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
}
}
}
.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;
:deep(.ant-table) {
.ant-table-thead > tr > th {
background: #1890ff;
color: white;
text-align: center;
}
.ant-table-tbody > tr > td {
.ant-input,
.ant-input-number-input,
.ant-select-selector,
.ant-picker {
&:hover {
background: #f5f5f5;
}
}
}
.ant-table-row:hover {
.ant-input,
.ant-input-number-input,
.ant-select-selector,
.ant-picker {
background: #f5f5f5;
}
}
}
}
.other-problems-card {
:deep(.ant-card-head) {
background: #fff2e8;
border-bottom: 1px solid #ffd8bf;
}
:deep(.ant-card-head-title) {
color: #873800;
font-weight: bold;
}
}
}
</style>