- 新增 .editorconfig 文件统一代码风格配置 - 新增 .env 环境变量配置文件 - 添加开发和生产环境的环境变量配置 - 配置 ESLint 忽略规则文件 - 设置代码检查配置文件 .eslintrc.js - 添加 Git 忽略文件规则 - 创建 Prettier 格式化忽略规则 - 添加隐私政策和服务协议HTML文件 - 实现访问密钥编辑组件基础结构
636 lines
17 KiB
Vue
636 lines
17 KiB
Vue
<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> |