1243 lines
39 KiB
Vue
1243 lines
39 KiB
Vue
<!-- 用户编辑弹窗 -->
|
||
<template>
|
||
<a-drawer
|
||
:width="`95%`"
|
||
:visible="visible"
|
||
:confirm-loading="loading"
|
||
:maxable="maxAble"
|
||
title="AI审计内容生成器"
|
||
:body-style="{ paddingBottom: '8px', background: '#f3f3f3' }"
|
||
@update:visible="updateVisible"
|
||
:maskClosable="false"
|
||
:footer="null"
|
||
@ok="save"
|
||
>
|
||
<a-card title="基本信息" style="margin-bottom: 20px" :bordered="false">
|
||
<a-descriptions>
|
||
<a-descriptions-item
|
||
label="公司名称"
|
||
:labelStyle="{ width: '90px', color: '#808080' }"
|
||
>
|
||
<span @click="copyText(form.name)">{{ form.name }}</span>
|
||
</a-descriptions-item>
|
||
<a-descriptions-item
|
||
label="被审计人"
|
||
:labelStyle="{ width: '90px', color: '#808080' }"
|
||
>
|
||
<span>{{ form.nickname || '暂无' }}</span>
|
||
</a-descriptions-item>
|
||
<a-descriptions-item
|
||
label="审计时间"
|
||
:labelStyle="{ width: '90px', color: '#808080' }"
|
||
>
|
||
<span>{{ form.expirationTime }}</span>
|
||
</a-descriptions-item>
|
||
</a-descriptions>
|
||
</a-card>
|
||
|
||
<a-card
|
||
style="margin-bottom: 20px; text-align: center; background: transparent"
|
||
:bordered="false"
|
||
>
|
||
<a-space>
|
||
<a-button
|
||
size="large"
|
||
type="primary"
|
||
@click="handleGenerateAll"
|
||
:loading="generatingAll"
|
||
class="generate-all-button"
|
||
:disabled="!hasTripleOneData"
|
||
>
|
||
<template #icon>
|
||
<UngroupOutlined />
|
||
</template>
|
||
生成全部方案
|
||
<a-tooltip
|
||
v-if="!hasTripleOneData"
|
||
title="请先生成三重一大制度对比分析表"
|
||
>
|
||
<QuestionCircleOutlined style="margin-left: 5px" />
|
||
</a-tooltip>
|
||
</a-button>
|
||
<a-button size="large">
|
||
<template #icon>
|
||
<DownloadOutlined />
|
||
</template>
|
||
保存草稿
|
||
</a-button>
|
||
<a-button size="large">
|
||
<template #icon>
|
||
<RedoOutlined />
|
||
</template>
|
||
加载草稿
|
||
</a-button>
|
||
<a-button
|
||
size="large"
|
||
type="danger"
|
||
class="export-button"
|
||
@click="handleExport"
|
||
>
|
||
<template #icon>
|
||
<UploadOutlined />
|
||
</template>
|
||
下载文件
|
||
</a-button>
|
||
</a-space>
|
||
</a-card>
|
||
|
||
<!-- 快速导航 -->
|
||
<a-card style="margin-bottom: 20px" :bordered="false">
|
||
<template #title>
|
||
<div
|
||
style="
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
"
|
||
>
|
||
<span>快速导航</span>
|
||
<a-tooltip title="快捷键:Ctrl+1~9 快速跳转,Ctrl+↑↓ 上下导航">
|
||
<QuestionCircleOutlined style="color: #999; cursor: help" />
|
||
</a-tooltip>
|
||
</div>
|
||
</template>
|
||
|
||
<div class="navigation-container">
|
||
<div class="nav-grid">
|
||
<a-button
|
||
v-for="(item, index) in navigationItems"
|
||
:key="index"
|
||
:type="currentSection === index ? 'primary' : 'default'"
|
||
size="small"
|
||
@click="scrollToSection(index)"
|
||
class="nav-button"
|
||
:class="{ active: currentSection === index }"
|
||
>
|
||
<span class="nav-number">{{ item.number }}</span>
|
||
<span class="nav-text">{{ item.name }}</span>
|
||
</a-button>
|
||
</div>
|
||
|
||
<!-- 进度指示器 -->
|
||
<div class="progress-container">
|
||
<div class="progress-bar">
|
||
<div
|
||
class="progress-fill"
|
||
:style="{
|
||
width: `${
|
||
((currentSection + 1) / navigationItems.length) * 100
|
||
}%`
|
||
}"
|
||
></div>
|
||
</div>
|
||
<div class="progress-text">
|
||
{{ currentSection + 1 }} / {{ navigationItems.length }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
|
||
<!-- 审计方案内容 -->
|
||
<div class="audit-content">
|
||
<div
|
||
v-for="(item, index) in navigationItems"
|
||
:key="index"
|
||
:id="`section-${index}`"
|
||
class="audit-section"
|
||
>
|
||
<a-card style="margin-bottom: 20px" :bordered="false">
|
||
<template #title>
|
||
<span class="font-bold">{{ `${item.number}、${item.name}` }}</span>
|
||
<span class="ml-4 text-gray-400" v-if="item.extra1">{{
|
||
table1Title
|
||
}}</span>
|
||
<span class="ml-4 text-gray-400" v-if="item.file1">
|
||
<!-- 修改选择文件组件 -->
|
||
<a-button
|
||
type="link"
|
||
@click="openDocSelect(index)"
|
||
class="select-file-btn"
|
||
>
|
||
<FolderOpenOutlined />
|
||
选择文件
|
||
</a-button>
|
||
</span>
|
||
<span class="ml-4 text-gray-400" v-if="item.extra3">{{
|
||
table3Title
|
||
}}</span>
|
||
<span class="ml-4 text-gray-400" v-if="item.file3">
|
||
<!-- 修改选择文件组件 -->
|
||
<a-button
|
||
type="link"
|
||
@click="openDocSelect(index)"
|
||
class="select-file-btn"
|
||
>
|
||
<FolderOpenOutlined />
|
||
选择文件
|
||
</a-button>
|
||
</span>
|
||
</template>
|
||
<template #extra>
|
||
<a-space>
|
||
<!-- 添加导出重大经济决策调查表按钮 -->
|
||
<a-button
|
||
v-if="item.extra3 && table3Title === '重大经济决策调查表'"
|
||
type="primary"
|
||
@click="handleExportDecisionTable"
|
||
:loading="exportingDecisionTable"
|
||
>
|
||
<template #icon>
|
||
<DownloadOutlined />
|
||
</template>
|
||
导出调查表
|
||
</a-button>
|
||
<!-- 添加导出三重一大按钮 -->
|
||
<a-button
|
||
v-if="item.extra3 && table3Title === '三重一大'"
|
||
type="primary"
|
||
@click="handleExportTripleOne"
|
||
:loading="exportingTripleOne"
|
||
>
|
||
<template #icon>
|
||
<DownloadOutlined />
|
||
</template>
|
||
导出三重一大
|
||
</a-button>
|
||
<a-button
|
||
type="primary"
|
||
@click="generateContent(index)"
|
||
:loading="item.generating"
|
||
>
|
||
<template #icon>
|
||
<RobotOutlined />
|
||
</template>
|
||
AI生成
|
||
</a-button>
|
||
</a-space>
|
||
</template>
|
||
<div class="flex justify-between items-center">
|
||
<div class="flex justify-start items-center">
|
||
<template v-if="item.extra1">
|
||
<div class="mb-2">
|
||
<TableSwitcher
|
||
:title="table1Title"
|
||
:btns="['贯彻决策部署', '领导班子名单', '决策支出表']"
|
||
@change="changeTable1"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<template v-if="item.extra3">
|
||
<div class="mb-2">
|
||
<TableSwitcher
|
||
:title="table3Title"
|
||
:btns="['重大经济决策调查表', '三重一大']"
|
||
@change="changeTable3"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<span class="ml-2"
|
||
>共生成
|
||
<span class="text-red-400 mx-1 font-bold">{{ item.data ? item.data.length : 0 }}</span>
|
||
条数据</span
|
||
>
|
||
</div>
|
||
<a @click="openHistory(index)" class="cursor-pointer">历史记录</a>
|
||
</div>
|
||
<a-table
|
||
v-if="item.mode === 'table'"
|
||
:columns="item.columns"
|
||
:scroll="{ y: 500 }"
|
||
:pagination="false"
|
||
bordered
|
||
:data-source="item.data"
|
||
>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.key === 'action'">
|
||
<a-button type="primary" danger>删除</a-button>
|
||
</template>
|
||
<template v-if="column.key === 'testResult'">
|
||
<span class="text-green-400" v-if="record.testResult === '通过'"
|
||
>通过</span
|
||
>
|
||
<span
|
||
class="text-red-400"
|
||
v-else-if="record.testResult === '不通过'"
|
||
>不通过</span
|
||
>
|
||
<span class="text-gray-400" v-else>{{
|
||
record.testResult || '待检查'
|
||
}}</span>
|
||
</template>
|
||
<template v-if="column.key === 'workPaperIndex'">
|
||
<div
|
||
v-for="(fileItem, fileIndex) in record.workPaperIndex"
|
||
:key="fileIndex"
|
||
>
|
||
<img
|
||
src="@/assets/word.png"
|
||
style="width: 20px; height: 20px"
|
||
alt=""
|
||
/>
|
||
<span class="cursor-pointer text-wrap">{{ fileItem }}</span>
|
||
</div>
|
||
</template>
|
||
<template v-if="column.key === 'fileIndex'">
|
||
<div
|
||
v-for="(fileItem, fileIndex) in record.fileIndex"
|
||
:key="fileIndex"
|
||
>
|
||
<img
|
||
src="@/assets/word.png"
|
||
style="width: 20px; height: 20px"
|
||
alt=""
|
||
/>
|
||
<span class="cursor-pointer text-wrap">{{ fileItem }}</span>
|
||
</div>
|
||
</template>
|
||
<template v-if="column.key === 'goods'">
|
||
<span v-if="record.goods === '是'">✅</span>
|
||
<span v-else></span>
|
||
</template>
|
||
<template v-if="column.key === 'normal'">
|
||
<span v-if="record.normal === '是'">✅</span>
|
||
<span v-else></span>
|
||
</template>
|
||
<template v-if="column.key === 'bad'">
|
||
<span v-if="record.bad === '是'">✅</span>
|
||
<span v-else></span>
|
||
</template>
|
||
</template>
|
||
</a-table>
|
||
<template v-else-if="item.mode === 'radio'">
|
||
<div
|
||
class="mb-1"
|
||
v-for="(radio, radioIndex) in item.radioList"
|
||
:key="radioIndex"
|
||
>
|
||
<p class="mb-1">{{ radio.label }}</p>
|
||
<a-radio-group v-model:value="radio.content" :name="radio.label">
|
||
<a-radio value="是">是</a-radio>
|
||
<a-radio value="否">否</a-radio>
|
||
</a-radio-group>
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<template v-if="!item.textareaList || !item.textareaList.length">
|
||
<a-textarea
|
||
v-model:value="item.content"
|
||
:rows="item.rows || 8"
|
||
:placeholder="'点击(AI生成)按钮让AI为您生成该部分内容,或直接填入'"
|
||
class="content-textarea"
|
||
style="margin-top: 16px; background-color: #f0fdf4"
|
||
/>
|
||
</template>
|
||
</template>
|
||
<template v-if="item.textareaList && item.textareaList.length">
|
||
<div
|
||
class="py-2 box-border"
|
||
:key="textareaIndex"
|
||
v-for="(textarea, textareaIndex) in item.textareaList"
|
||
>
|
||
<p class="mb-1">{{ textarea.label }}</p>
|
||
<a-textarea
|
||
v-model:value="textarea.content"
|
||
:rows="item.rows || 8"
|
||
:placeholder="'点击(AI生成)按钮让AI为您生成该部分内容,或直接填入'"
|
||
class="content-textarea"
|
||
style="margin-top: 16px; background-color: #f0fdf4"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<div style="margin-top: 12px">
|
||
<div class="question-prompt">AI小助手</div>
|
||
<div class="textarea-with-button" style="width: 600px">
|
||
<a-textarea
|
||
v-model:value="item.suggestion"
|
||
:rows="3"
|
||
placeholder="请输入您的要求并回车..."
|
||
class="suggestion-textarea-inner"
|
||
@pressEnter="generateContent(index)"
|
||
/>
|
||
<a-button
|
||
type="danger"
|
||
size="small"
|
||
@click="generateContent(index)"
|
||
:loading="item.generating"
|
||
class="send-button-inner"
|
||
>
|
||
<template #icon>
|
||
<RobotOutlined />
|
||
</template>
|
||
发送
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
</a-card>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 返回顶部按钮 -->
|
||
<a-back-top :target="getScrollContainer" />
|
||
<FileModal ref="fileModal" :current-company-id="currentCompanyId" />
|
||
<HistoryModal
|
||
v-model:visible="showHistory"
|
||
:interface-name="currentInterfaceName"
|
||
@select="handleHistorySelect"
|
||
/>
|
||
|
||
<!-- 文档选择弹窗 -->
|
||
</a-drawer>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, reactive, watch, onMounted, onUnmounted, computed } from 'vue';
|
||
import { Form, message } from 'ant-design-vue';
|
||
import { assignObject } from 'ele-admin-pro';
|
||
import { copyText } from '@/utils/common';
|
||
import { PwlProject } from '@/api/pwl/pwlProject/model';
|
||
import { RedoOutlined, RobotOutlined } from '@ant-design/icons-vue';
|
||
import {
|
||
generateAuditReport,
|
||
downloadAuditReport
|
||
} from '@/api/ai/auditReport';
|
||
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
|
||
import {
|
||
generateTripleOneTable,
|
||
generateDecisionTable,
|
||
exportTripleOneTable,
|
||
exportDecisionTable
|
||
} from '@/api/ai/auditContent';
|
||
|
||
import FileModal from '@/views/pwl/pwlProject/components/components/FileModal.vue';
|
||
import TableSwitcher from '@/views/pwl/pwlProject/components/components/TableSwitcher.vue';
|
||
|
||
import navigationItems from './data/navigationItems';
|
||
import table1Columns from './data/table1Columns';
|
||
import table3Columns from './data/table3Columns';
|
||
|
||
import {
|
||
scrollToSection,
|
||
getScrollContainer,
|
||
handleKeydown,
|
||
handleScroll,
|
||
buildExportData,
|
||
hasContent
|
||
} from './data/funcs';
|
||
import HistoryModal from '@/views/pwl/pwlProject/components/components/HistoryModal.vue';
|
||
|
||
const useForm = Form.useForm;
|
||
|
||
const props = defineProps<{
|
||
// 弹窗是否打开
|
||
visible: boolean;
|
||
// 修改回显的数据
|
||
data?: PwlProject | null;
|
||
}>();
|
||
|
||
// 是否显示最大化切换按钮
|
||
const maxAble = ref(true);
|
||
// 添加生成状态
|
||
const generatingAll = ref(false);
|
||
// 当前选中的章节
|
||
const currentSection = ref(0);
|
||
|
||
// 存储拼接后的 kbIds
|
||
const combinedKbIds = ref('');
|
||
|
||
// 九大审计项目导航配置
|
||
const table3Title = ref('重大经济决策调查表');
|
||
const table1Title = ref('贯彻决策部署');
|
||
// 存储两种表格的数据状态
|
||
const tripleOneTableData = ref([]);
|
||
const decisionTableData = ref([]);
|
||
|
||
// 文档选择相关变量
|
||
const showDocSelect = ref(false);
|
||
const currentSectionIndex = ref(2); // 默认是审计内容3
|
||
const selectedDocList = ref<string[]>([]);
|
||
const selectedFileList = ref<string[]>([]);
|
||
const selectedFileKeys = ref<(string | number)[]>([]);
|
||
const checkedDirKeys = ref<(string | number)[]>([]); // 新增:勾选的目录keys
|
||
|
||
// 新增:保存上次选择的状态
|
||
const lastSelectedDirKeys = ref<(string | number)[]>([]);
|
||
const lastSelectedFileKeys = ref<(string | number)[]>([]);
|
||
|
||
// 文档管理相关变量
|
||
const currentCompanyId = ref<number>();
|
||
|
||
// 历史记录接口相关变量
|
||
const currentInterfaceName = ref('');
|
||
|
||
// 添加计算属性检查是否已生成三重一大数据
|
||
const hasTripleOneData = computed(() => {
|
||
const section = navigationItems.value[2];
|
||
return (
|
||
section.data &&
|
||
section.data.length > 0 &&
|
||
table3Title.value === '三重一大'
|
||
);
|
||
});
|
||
|
||
// 存储三重一大数据
|
||
const tripleOneData = ref(null);
|
||
|
||
// 添加导出状态
|
||
const exportingTripleOne = ref(false);
|
||
const exportingDecisionTable = ref(false);
|
||
|
||
const emit = defineEmits<{
|
||
(e: 'done'): void;
|
||
(e: 'update:visible', visible: boolean): void;
|
||
}>();
|
||
|
||
// 订单信息
|
||
const form = reactive<PwlProject>({
|
||
// ID
|
||
id: undefined,
|
||
// 项目名称
|
||
name: undefined,
|
||
// 项目标识
|
||
code: undefined,
|
||
// 上级id, 0是顶级
|
||
parentId: undefined,
|
||
// 项目类型
|
||
type: undefined,
|
||
// 项目图标
|
||
image: undefined,
|
||
// 二维码
|
||
qrcode: undefined,
|
||
// 链接地址
|
||
url: undefined,
|
||
// 应用截图
|
||
images: undefined,
|
||
// 底稿情况
|
||
files: undefined,
|
||
// 应用介绍
|
||
content: undefined,
|
||
// 年末资产总额(万元)
|
||
totalAssets: undefined,
|
||
// 合同金额
|
||
contractPrice: undefined,
|
||
// 实收金额
|
||
payPrice: undefined,
|
||
// 软件定价
|
||
price: undefined,
|
||
// 是否推荐
|
||
recommend: undefined,
|
||
// 到期时间
|
||
expirationTime: undefined,
|
||
// 项目信息-开票单位/汇款人
|
||
itemName: undefined,
|
||
// 项目信息-年度
|
||
itemYear: undefined,
|
||
// 项目信息-类型
|
||
itemType: undefined,
|
||
// 项目信息-审计意见
|
||
itemOpinion: undefined,
|
||
// 到账信息-银行名称
|
||
bankName: undefined,
|
||
// 到账日期
|
||
bankPayTime: undefined,
|
||
// 到账金额
|
||
bankPrice: undefined,
|
||
// 发票类型
|
||
invoiceType: undefined,
|
||
// 开票日期
|
||
invoiceTime: undefined,
|
||
// 开票金额
|
||
invoicePrice: undefined,
|
||
// 报告份数
|
||
reportNum: undefined,
|
||
// 底稿人员
|
||
draftUserId: undefined,
|
||
// 底稿人员
|
||
draftUser: undefined,
|
||
// 参与成员
|
||
userIds: undefined,
|
||
users: undefined,
|
||
// 签字注会
|
||
signUserId: undefined,
|
||
signUser: undefined,
|
||
// 展业人员
|
||
saleUserId: undefined,
|
||
saleUser: undefined,
|
||
// 备注
|
||
comments: undefined,
|
||
// 排序(数字越小越靠前)
|
||
sortNumber: undefined,
|
||
// 状态, 0正常, 1冻结
|
||
status: undefined,
|
||
// 电子报告是否已完成
|
||
electron: undefined,
|
||
// 纸质报告是否已完成
|
||
paper: undefined,
|
||
// 是否删除, 0否, 1是
|
||
deleted: undefined,
|
||
// 创建人ID
|
||
userId: undefined,
|
||
// 创建人
|
||
realName: undefined,
|
||
// 租户id
|
||
tenantId: undefined,
|
||
// 创建时间
|
||
createTime: undefined,
|
||
// 修改时间
|
||
updateTime: undefined,
|
||
// 知识库id
|
||
kbId: undefined
|
||
});
|
||
|
||
// 请求状态
|
||
const loading = ref(true);
|
||
|
||
const { resetFields } = useForm(form);
|
||
|
||
/* 更新visible */
|
||
const updateVisible = (value: boolean) => {
|
||
emit('update:visible', value);
|
||
};
|
||
|
||
/* 批量生成全部内容 */
|
||
// const handleGenerateAll2 = async () => {
|
||
// generatingAll.value = true;
|
||
// try {
|
||
// // 使用Promise.all进行并行生成
|
||
// const promises: Promise<void>[] = [];
|
||
//
|
||
// navigationItems.value.forEach((item: any, index) => {
|
||
// if (item.children) {
|
||
// // 有子项的章节,为每个子项生成内容
|
||
// item.children.forEach((child, childIndex) => {
|
||
// promises.push(generateContent(index, childIndex));
|
||
// });
|
||
// } else {
|
||
// // 没有子项的章节
|
||
// promises.push(generateContent(index));
|
||
// }
|
||
// });
|
||
//
|
||
// await Promise.all(promises);
|
||
// message.success('全部内容生成完成');
|
||
// } catch (error) {
|
||
// console.error('批量生成失败:', error);
|
||
// message.error('部分内容生成失败,请检查');
|
||
// } finally {
|
||
// generatingAll.value = false;
|
||
// }
|
||
// };
|
||
|
||
/* 批量生成全部内容 */
|
||
const handleGenerateAll = async () => {
|
||
generatingAll.value = true;
|
||
try {
|
||
// 改为顺序生成
|
||
for (const [index, item] of navigationItems.value.entries()) {
|
||
// 跳过未完成的依赖项
|
||
if (index === 4 && !hasContent(navigationItems.value[3])) {
|
||
message.warning('请先生成第四项「被审计单位基本情况」');
|
||
continue;
|
||
}
|
||
if (index === 5 && !hasContent(navigationItems.value[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 handleExport = async () => {
|
||
try {
|
||
const exportData = buildExportData();
|
||
const blob = await downloadAuditReport(exportData);
|
||
|
||
// 创建下载链接
|
||
const url = window.URL.createObjectURL(new Blob([blob]));
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.setAttribute('download', `审计报告_${form.name}.docx`);
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
window.URL.revokeObjectURL(url);
|
||
} catch (error) {
|
||
console.error('导出失败:', error);
|
||
message.error('文档导出失败');
|
||
}
|
||
};
|
||
|
||
/* 导出三重一大表格 */
|
||
const handleExportTripleOne = async () => {
|
||
const section = navigationItems.value[2];
|
||
if (!section.data || section.data.length === 0) {
|
||
message.warning('没有可导出的三重一大数据');
|
||
return;
|
||
}
|
||
|
||
exportingTripleOne.value = true;
|
||
try {
|
||
// 构建导出数据
|
||
const exportData = {
|
||
data: section.data,
|
||
companyName: form.name || '未知公司',
|
||
auditTime: form.expirationTime || '未知时间'
|
||
};
|
||
|
||
const blob = await exportTripleOneTable(exportData);
|
||
|
||
// 创建下载链接
|
||
const url = window.URL.createObjectURL(new Blob([blob]));
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.setAttribute(
|
||
'download',
|
||
`三重一大制度对比分析表_${form.name || '未知公司'}.xlsx`
|
||
);
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
window.URL.revokeObjectURL(url);
|
||
|
||
message.success('三重一大表格导出成功');
|
||
} catch (error: any) {
|
||
console.error('导出三重一大表格失败:', error);
|
||
message.error('导出失败: ' + (error.message || '未知错误'));
|
||
} finally {
|
||
exportingTripleOne.value = false;
|
||
}
|
||
};
|
||
|
||
// 导出重大经济决策调查表
|
||
const handleExportDecisionTable = async () => {
|
||
const section = navigationItems.value[2];
|
||
if (!section.data || section.data.length === 0) {
|
||
message.warning('没有可导出的重大经济决策调查表数据');
|
||
return;
|
||
}
|
||
|
||
exportingDecisionTable.value = true;
|
||
try {
|
||
// 构建导出数据
|
||
const exportData = {
|
||
data: section.data,
|
||
companyName: form.name || '未知公司',
|
||
auditTime: form.expirationTime || '未知时间'
|
||
};
|
||
|
||
const blob = await exportDecisionTable(exportData);
|
||
|
||
// 创建下载链接
|
||
const url = window.URL.createObjectURL(new Blob([blob]));
|
||
const link = document.createElement('a');
|
||
link.href = url;
|
||
link.setAttribute(
|
||
'download',
|
||
`重大经济决策调查表_${form.name || '未知公司'}.xlsx`
|
||
);
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
window.URL.revokeObjectURL(url);
|
||
|
||
message.success('重大经济决策调查表导出成功');
|
||
} catch (error: any) {
|
||
console.error('导出重大经济决策调查表失败:', error);
|
||
message.error('导出失败: ' + (error.message || '未知错误'));
|
||
} finally {
|
||
exportingDecisionTable.value = false;
|
||
}
|
||
};
|
||
|
||
/* AI生成内容 */
|
||
const generateContent = async (sectionIndex: number, childIndex?: number) => {
|
||
// 特殊处理审计内容3(根据当前表格类型调用不同的生成方法)
|
||
if (sectionIndex === 2) {
|
||
const section: any = navigationItems.value[sectionIndex];
|
||
if (section.generateMethod) {
|
||
await section.generateMethod();
|
||
} else {
|
||
// 默认根据表格标题调用相应方法
|
||
if (table3Title.value === '三重一大') {
|
||
await generateContent3();
|
||
} else {
|
||
await generateDecisionContent();
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
const section: any = navigationItems.value[sectionIndex];
|
||
|
||
// 处理主项生成(当主项没有formCommit时)
|
||
if (childIndex === undefined && !section.formCommit && section.children) {
|
||
section.generating = true;
|
||
try {
|
||
await Promise.all(
|
||
section.children.map((_, i) => generateContent(sectionIndex, i))
|
||
);
|
||
} finally {
|
||
section.generating = false;
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 处理单个项生成
|
||
let item: any;
|
||
let formCommit: number;
|
||
const isChild = childIndex !== undefined;
|
||
|
||
if (isChild) {
|
||
item = section.children[childIndex];
|
||
formCommit = item.formCommit;
|
||
} else {
|
||
item = section;
|
||
formCommit = item.formCommit;
|
||
}
|
||
|
||
// 新增:构建上下文数据
|
||
const contextData = await buildExportData(); // 复用导出数据的构建逻辑
|
||
|
||
item.generating = true;
|
||
try {
|
||
item.content = await generateAuditReport({
|
||
kbId: form.kbId || '',
|
||
kbIds: combinedKbIds.value,
|
||
formCommit: formCommit,
|
||
history: item.content || '',
|
||
suggestion: item.suggestion || '',
|
||
// 新增:传递完整的上下文数据
|
||
...contextData
|
||
});
|
||
} finally {
|
||
item.generating = false;
|
||
}
|
||
};
|
||
|
||
// 三重一大生成方法
|
||
const generateContent3 = async () => {
|
||
const section: any = navigationItems.value[2];
|
||
section.generating = true;
|
||
|
||
// 构建history - 将当前已有的数据作为历史记录
|
||
const history =
|
||
section.data && section.data.length > 0
|
||
? JSON.stringify(section.data, null, 2)
|
||
: '';
|
||
|
||
// 获取用户输入的建议
|
||
const suggestion = section.suggestion || '';
|
||
|
||
console.log('生成三重一大参数:', {
|
||
kbIds: props.data?.kbId,
|
||
libraryIds: props.data?.libraryIds,
|
||
projectLibrary: props.data?.projectLibrary,
|
||
history: history ? `历史数据(${section.data.length}条)` : '无',
|
||
suggestion: suggestion || '无',
|
||
docList: checkedDirKeys.value, // 修复:使用勾选的目录keys
|
||
fileList: selectedFileKeys.value // 修复:使用勾选的文件keys
|
||
});
|
||
|
||
try {
|
||
// 构建请求参数对象
|
||
const requestData = {
|
||
kbIds: props.data?.kbId || '',
|
||
libraryIds: props.data?.libraryIds || '',
|
||
projectLibrary: props.data?.projectLibrary || '',
|
||
history: history,
|
||
suggestion: suggestion,
|
||
docList: checkedDirKeys.value, // 修复:传入目录id数组
|
||
fileList: selectedFileKeys.value // 修复:传入文件id数组
|
||
};
|
||
|
||
// 调用三重一大生成接口
|
||
const result = await generateTripleOneTable(requestData);
|
||
|
||
console.log('三重一大接口返回结果:', result);
|
||
|
||
if (result.code === 0 && result.data && result.data.success) {
|
||
const tableData = mapTripleOneData(result.data.data);
|
||
|
||
// 保存到三重一大数据
|
||
tripleOneTableData.value = tableData;
|
||
section.data = tableData;
|
||
|
||
// 保存三重一大数据用于Sheet1生成
|
||
tripleOneData.value = result.data.data;
|
||
|
||
// 生成成功后清空建议输入框
|
||
section.suggestion = '';
|
||
|
||
message.success(`成功生成 ${tableData.length} 条三重一大制度对比记录`);
|
||
} else {
|
||
const errorMsg = result.data?.error || result.message || '生成失败';
|
||
throw new Error(errorMsg);
|
||
}
|
||
} catch (error: any) {
|
||
console.error('生成三重一大制度对比分析表失败:', error);
|
||
message.error('生成失败: ' + (error.message || '未知错误'));
|
||
} finally {
|
||
section.generating = false;
|
||
}
|
||
};
|
||
|
||
// 添加生成重大经济决策调查表的方法
|
||
const generateDecisionContent = async () => {
|
||
const section: any = navigationItems.value[2];
|
||
section.generating = true;
|
||
|
||
// 构建history - 将当前已有的数据作为历史记录
|
||
const history =
|
||
section.data && section.data.length > 0
|
||
? JSON.stringify(section.data, null, 2)
|
||
: '';
|
||
|
||
// 获取用户输入的建议
|
||
const suggestion: any = section.suggestion || '';
|
||
|
||
console.log('生成重大经济决策调查表参数:', {
|
||
kbIds: props.data?.kbId,
|
||
libraryIds: props.data?.libraryIds,
|
||
projectLibrary: props.data?.projectLibrary,
|
||
history: history ? `历史数据(${section.data.length}条)` : '无',
|
||
suggestion: suggestion || '无',
|
||
data: tripleOneData.value
|
||
? `三重一大数据(${tripleOneData.value.length}条)`
|
||
: '无',
|
||
docList: checkedDirKeys.value,
|
||
fileList: selectedFileKeys.value
|
||
});
|
||
|
||
try {
|
||
// 构建请求参数对象
|
||
const requestData = {
|
||
kbIds: props.data?.kbId || '',
|
||
libraryIds: props.data?.libraryIds || '',
|
||
projectLibrary: props.data?.projectLibrary || '',
|
||
history: history,
|
||
suggestion: suggestion,
|
||
data: tripleOneData.value, // 传入三重一大数据
|
||
docList: checkedDirKeys.value,
|
||
fileList: selectedFileKeys.value
|
||
};
|
||
|
||
// 调用重大经济决策调查表生成接口
|
||
const result = await generateDecisionTable(requestData);
|
||
|
||
console.log('重大经济决策调查表接口返回结果:', result);
|
||
|
||
if (result.code === 0 && result.data && result.data.success) {
|
||
const tableData = mapDecisionTableData(result.data.data);
|
||
|
||
console.log('生成的表格数据:', tableData);
|
||
|
||
// 保存到重大经济决策调查表数据
|
||
decisionTableData.value = tableData;
|
||
section.data = tableData;
|
||
|
||
// 生成成功后清空建议输入框
|
||
section.suggestion = '';
|
||
|
||
message.success(`成功生成 ${tableData.length} 条重大经济决策调查记录`);
|
||
} else {
|
||
const errorMsg = result.data?.error || result.message || '生成失败';
|
||
throw new Error(errorMsg);
|
||
}
|
||
} catch (error: any) {
|
||
console.error('生成重大经济决策调查表失败:', error);
|
||
message.error('生成失败: ' + (error.message || '未知错误'));
|
||
} finally {
|
||
section.generating = false;
|
||
}
|
||
};
|
||
|
||
/* 保存编辑 */
|
||
const save = () => {};
|
||
|
||
// 打开文档选择弹窗
|
||
const fileModal = ref();
|
||
const openDocSelect = (sectionIndex: number) => {
|
||
currentSectionIndex.value = sectionIndex;
|
||
showDocSelect.value = true;
|
||
|
||
// 恢复上次选择的状态
|
||
checkedDirKeys.value = [...lastSelectedDirKeys.value];
|
||
selectedFileKeys.value = [...lastSelectedFileKeys.value];
|
||
selectedDocList.value = lastSelectedDirKeys.value.map((key) =>
|
||
key.toString()
|
||
);
|
||
selectedFileList.value = lastSelectedFileKeys.value.map((key) =>
|
||
key.toString()
|
||
);
|
||
|
||
// 加载文档目录
|
||
// loadAllCloudDocs();
|
||
fileModal.value.open();
|
||
};
|
||
|
||
const changeTable1 = (title) => {
|
||
table1Title.value = title;
|
||
const section: any = navigationItems.value[0];
|
||
if (title === '贯彻决策部署') {
|
||
section.columns = table1Columns.columns0;
|
||
section.data = tripleOneTableData.value || [];
|
||
section.generateMethod = generateContent3;
|
||
} else if (title === '领导班子名单') {
|
||
section.columns = table1Columns.columns1;
|
||
section.data = tripleOneTableData.value || [];
|
||
section.generateMethod = generateContent3;
|
||
} else {
|
||
section.columns = table1Columns.columns2;
|
||
section.data = decisionTableData.value || [];
|
||
section.generateMethod = generateDecisionContent;
|
||
}
|
||
};
|
||
|
||
// const file3List = ref([])
|
||
const changeTable3 = (title) => {
|
||
const section: any = navigationItems.value[2];
|
||
table3Title.value = title;
|
||
if (title === '三重一大') {
|
||
// 切换到三重一大表格
|
||
|
||
section.columns = table3Columns.columns0;
|
||
// 恢复三重一大数据,如果没有生成过则为空数组
|
||
section.data = tripleOneTableData.value || [];
|
||
// 更新生成方法
|
||
section.generateMethod = generateContent3;
|
||
} else {
|
||
// 切换到重大经济决策调查表
|
||
section.columns = table3Columns.columns1;
|
||
// 恢复重大经济决策调查表数据,如果没有生成过则为空数组
|
||
section.data = decisionTableData.value || [];
|
||
// 更新生成方法
|
||
section.generateMethod = generateDecisionContent;
|
||
}
|
||
};
|
||
|
||
/* 键盘快捷键处理 */
|
||
const handleKeydown = (event: KeyboardEvent) => {
|
||
// 只在弹窗打开时处理快捷键
|
||
if (!props.visible) return;
|
||
|
||
// Ctrl/Cmd + 数字键 1-9 快速跳转
|
||
if (
|
||
(event.ctrlKey || event.metaKey) &&
|
||
event.key >= '1' &&
|
||
event.key <= '9'
|
||
) {
|
||
event.preventDefault();
|
||
const index = parseInt(event.key) - 1;
|
||
if (index < navigationItems.value.length) {
|
||
scrollToSection(index);
|
||
currentSection.value = index;
|
||
}
|
||
}
|
||
|
||
// 上下箭头键导航
|
||
if (event.key === 'ArrowUp' && event.ctrlKey) {
|
||
event.preventDefault();
|
||
const prevIndex = Math.max(0, currentSection.value - 1);
|
||
scrollToSection(prevIndex);
|
||
currentSection.value = prevIndex;
|
||
} else if (event.key === 'ArrowDown' && event.ctrlKey) {
|
||
event.preventDefault();
|
||
const nextIndex = Math.min(
|
||
navigationItems.value.length - 1,
|
||
currentSection.value + 1
|
||
);
|
||
scrollToSection(nextIndex);
|
||
currentSection.value = nextIndex;
|
||
}
|
||
};
|
||
|
||
// 组件挂载时添加滚动监听和键盘监听
|
||
onMounted(() => {
|
||
const scrollContainer = getScrollContainer();
|
||
if (scrollContainer) {
|
||
scrollContainer.addEventListener('scroll', handleScroll);
|
||
}
|
||
document.addEventListener('keydown', handleKeydown);
|
||
});
|
||
|
||
// 组件卸载时移除监听
|
||
onUnmounted(() => {
|
||
const scrollContainer = getScrollContainer();
|
||
if (scrollContainer) {
|
||
scrollContainer.removeEventListener('scroll', handleScroll);
|
||
}
|
||
document.removeEventListener('keydown', handleKeydown);
|
||
});
|
||
|
||
watch(
|
||
() => props.visible,
|
||
async (visible) => {
|
||
if (visible) {
|
||
if (props.data) {
|
||
loading.value = true;
|
||
assignObject(form, props.data);
|
||
|
||
// 获取知识库并拼接 kbIds
|
||
if (props.data && props.data.libraryIds) {
|
||
if (props.data.libraryIds?.length > 0) {
|
||
try {
|
||
const result = await getPwlProjectLibraryByIds(
|
||
props.data.libraryIds
|
||
);
|
||
const kbIds = result
|
||
.map((lib) => lib.kbId)
|
||
.filter((kbId) => kbId);
|
||
if (form.kbId) {
|
||
kbIds.unshift(form.kbId);
|
||
}
|
||
combinedKbIds.value = kbIds.join(',');
|
||
} catch (error) {
|
||
console.error('获取知识库失败:', error);
|
||
combinedKbIds.value = form.kbId || '';
|
||
}
|
||
} else {
|
||
combinedKbIds.value = form.kbId || '';
|
||
}
|
||
}
|
||
// 设置当前公司ID用于文档管理
|
||
currentCompanyId.value = props.data.companyId;
|
||
|
||
// 重置到第一个章节
|
||
currentSection.value = 0;
|
||
}
|
||
} else {
|
||
resetFields();
|
||
combinedKbIds.value = ''; // 关闭时清空
|
||
}
|
||
}
|
||
);
|
||
|
||
// 数据映射工具函数
|
||
const mapTripleOneData = (data: any[]) => {
|
||
return data.map((item, index) => ({
|
||
key: index,
|
||
category: item.category || '',
|
||
policyContent: item.policyContent || '',
|
||
groupSystem: item.groupSystem || '',
|
||
companyFormulation: item.companyFormulation || '',
|
||
checkEvidence: item.checkEvidence || '',
|
||
testResult: item.testResult || '待检查',
|
||
workPaperIndex: item.workPaperIndex || []
|
||
}));
|
||
};
|
||
|
||
const mapDecisionTableData = (data: any[]) => {
|
||
return data.map((item, index) => ({
|
||
key: index,
|
||
index: index + 1,
|
||
name: item.decisionItem || item.name || '',
|
||
meetingTime: item.meetingTime || '',
|
||
decisionAmount: item.decisionAmount || '',
|
||
procedure: item.procedure || '',
|
||
executionStatus: item.executionStatus || '否',
|
||
goods: item.executionEffect?.good || item.goods || '否',
|
||
normal: item.executionEffect?.normal || item.normal || '否',
|
||
bad: item.executionEffect?.bad || item.bad || '否'
|
||
}));
|
||
};
|
||
|
||
// 应用历史记录数据到指定章节
|
||
const applyHistoryData = (sectionIndex: number, data: any[], dataType: 'tripleOne' | 'decisionTable') => {
|
||
const section = navigationItems.value[sectionIndex];
|
||
|
||
if (dataType === 'tripleOne') {
|
||
const tableData = mapTripleOneData(data);
|
||
section.data = tableData;
|
||
tripleOneTableData.value = tableData;
|
||
|
||
// 强制刷新当前显示的表格
|
||
if (table3Title.value === '三重一大') {
|
||
section.columns = table3Columns.columns0;
|
||
section.data = [...tableData];
|
||
}
|
||
} else {
|
||
const tableData = mapDecisionTableData(data);
|
||
section.data = tableData;
|
||
decisionTableData.value = tableData;
|
||
|
||
// 强制刷新当前显示的表格
|
||
if (table3Title.value === '重大经济决策调查表') {
|
||
section.columns = table3Columns.columns1;
|
||
section.data = [...tableData];
|
||
}
|
||
}
|
||
};
|
||
|
||
const showHistory = ref(false);
|
||
const openHistory = (sectionIndex: number) => {
|
||
// 重置接口名称
|
||
currentInterfaceName.value = '';
|
||
|
||
// 根据当前章节和表格类型确定接口名称
|
||
if (sectionIndex === 2) { // 审计内容3章节
|
||
if (table3Title.value === '三重一大') {
|
||
currentInterfaceName.value = '/api/ai/auditContent3/generateTripleOneTable';
|
||
} else if (table3Title.value === '重大经济决策调查表') {
|
||
currentInterfaceName.value = '/api/ai/auditContent3/generateDecisionTable';
|
||
}
|
||
}
|
||
|
||
showHistory.value = true;
|
||
};
|
||
|
||
// 历史记录选择处理方法
|
||
const handleHistorySelect = (record: any) => {
|
||
// 关闭历史记录弹窗
|
||
showHistory.value = false;
|
||
|
||
try {
|
||
const responseData = JSON.parse(record.responseData);
|
||
if (!responseData.success || !responseData.data) {
|
||
throw new Error('历史记录数据格式错误');
|
||
}
|
||
|
||
// 根据接口名称确定数据类型
|
||
if (record.interfaceName === '/api/ai/auditContent3/generateTripleOneTable') {
|
||
applyHistoryData(2, responseData.data, 'tripleOne');
|
||
message.success('已应用选择的三重一大历史记录');
|
||
} else if (record.interfaceName === '/api/ai/auditContent3/generateDecisionTable') {
|
||
applyHistoryData(2, responseData.data, 'decisionTable');
|
||
message.success('已应用选择的重大经济决策调查表历史记录');
|
||
} else {
|
||
message.warning('不支持的历史记录类型');
|
||
}
|
||
} catch (error) {
|
||
console.error('解析历史记录数据失败:', error);
|
||
message.error('历史记录数据解析失败');
|
||
}
|
||
};
|
||
|
||
</script>
|
||
|
||
<script lang="ts">
|
||
import * as MenuIcons from '@/layout/menu-icons';
|
||
|
||
export default {
|
||
name: 'PwlProjectInfo',
|
||
components: MenuIcons
|
||
};
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
@import './style/style.scss';
|
||
</style>
|