feat:项目管理-审计内容-优化表格内容编辑、保存、删除功能
This commit is contained in:
@@ -163,6 +163,13 @@ function parseWorkPaperIndex(workPaperIndex) {
|
|||||||
if (!workPaperIndex || !Array.isArray(workPaperIndex)) return [];
|
if (!workPaperIndex || !Array.isArray(workPaperIndex)) return [];
|
||||||
|
|
||||||
return workPaperIndex.map(item => {
|
return workPaperIndex.map(item => {
|
||||||
|
if (item && typeof item === 'object') {
|
||||||
|
return {
|
||||||
|
fileId: item.fileId || item.file_id || '',
|
||||||
|
fileName: item.fileName || item.file_name || '',
|
||||||
|
fileUrl: item.fileUrl || item.url || ''
|
||||||
|
};
|
||||||
|
}
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
const parts = item.split('||');
|
const parts = item.split('||');
|
||||||
if (parts.length >= 3) {
|
if (parts.length >= 3) {
|
||||||
@@ -231,22 +238,20 @@ export function createDataMapper(type: string) {
|
|||||||
budgetManage: (data) => data.map((item, index) => ({
|
budgetManage: (data) => data.map((item, index) => ({
|
||||||
key: index,
|
key: index,
|
||||||
index: index + 1,
|
index: index + 1,
|
||||||
|
...item,
|
||||||
budgetSubject: item.budgetSubject || '-',
|
budgetSubject: item.budgetSubject || '-',
|
||||||
// 指标来源部分字段映射
|
indicatorSource: item.indicatorSource,
|
||||||
indicatorSource: item.indicatorSource, // 指标来源
|
total: formatAmount(item.indicatorSourceTotal),
|
||||||
total: formatAmount(item.indicatorSourceTotal), // 指标来源-合计
|
lastYearBalance: formatAmount(item.indicatorSourceLastYearBalance),
|
||||||
lastYearBalance: formatAmount(item.indicatorSourceLastYearBalance), // 指标来源-上年结余
|
initialBudget: formatAmount(item.indicatorSourceInitialBudget),
|
||||||
initialBudget: formatAmount(item.indicatorSourceInitialBudget), // 指标来源-年初部门预算
|
additionalBudget: formatAmount(item.indicatorSourceAdditionalBudget),
|
||||||
additionalBudget: formatAmount(item.indicatorSourceAdditionalBudget), // 指标来源-追加(减)预算
|
indicatorUseTotal: formatAmount(item.indicatorUseTotal),
|
||||||
// 指标运用部分字段映射
|
appropriationSubtotal: formatAmount(item.indicatorUseAppropriationSubtotal),
|
||||||
indicatorUseTotal: formatAmount(item.indicatorUseTotal), // 指标运用-合计
|
appropriation: formatAmount(item.indicatorUseAppropriation),
|
||||||
appropriationSubtotal: formatAmount(item.indicatorUseAppropriationSubtotal), // 指标运用-拨款小计
|
salaryPayment: formatAmount(item.indicatorUseSalaryPayment),
|
||||||
appropriation: formatAmount(item.indicatorUseAppropriation), // 指标运用-拨款
|
govProcurement: formatAmount(item.indicatorUseGovProcurement),
|
||||||
salaryPayment: formatAmount(item.indicatorUseSalaryPayment), // 指标运用-工资统发
|
indicatorBalance: formatAmount(item.indicatorBalance),
|
||||||
govProcurement: formatAmount(item.indicatorUseGovProcurement), // 指标运用-政府采购
|
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex),
|
||||||
// 其他字段
|
|
||||||
indicatorBalance: formatAmount(item.indicatorBalance), // 指标结余
|
|
||||||
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex), // 工作底稿索引
|
|
||||||
})),
|
})),
|
||||||
investmentSituation: (data) => data.map((item, index) => ({
|
investmentSituation: (data) => data.map((item, index) => ({
|
||||||
key: index,
|
key: index,
|
||||||
|
|||||||
@@ -156,14 +156,29 @@
|
|||||||
<a-space>
|
<a-space>
|
||||||
<!-- 所有表格都显示导出按钮 -->
|
<!-- 所有表格都显示导出按钮 -->
|
||||||
<template v-if="item.mode === 'table' && item.tableOptions.length > 0">
|
<template v-if="item.mode === 'table' && item.tableOptions.length > 0">
|
||||||
|
<a-button
|
||||||
|
danger
|
||||||
|
@click="clearCurrentTable(index)"
|
||||||
|
>
|
||||||
|
清空表格
|
||||||
|
</a-button>
|
||||||
<a-button
|
<a-button
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="handleExportTable(index)"
|
@click="handleExportTable(index)"
|
||||||
:loading="exportStates[`${item.tableType}_${item.tableOptions[item.currentTableIndex].value}`]"
|
:loading="exportStates[getTableKey(index)]"
|
||||||
>
|
>
|
||||||
<DownloadOutlined />
|
<DownloadOutlined />
|
||||||
导出表格
|
导出表格
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<!-- 添加保存按钮 -->
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click="saveCurrentTable(index)"
|
||||||
|
:loading="savingStates[getTableKey(index)]"
|
||||||
|
>
|
||||||
|
<SaveOutlined />
|
||||||
|
保存表格
|
||||||
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- AI生成按钮 -->
|
<!-- AI生成按钮 -->
|
||||||
@@ -303,9 +318,6 @@
|
|||||||
<template v-if="column.key === 'auditContent'">
|
<template v-if="column.key === 'auditContent'">
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
{{ record[column.key] }}
|
{{ record[column.key] }}
|
||||||
<!-- <a-tooltip v-if="record[column.key] && record[column.key].length > 50" :title="record[column.key]">
|
|
||||||
<MoreOutlined class="ml-1" />
|
|
||||||
</a-tooltip>-->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -313,9 +325,6 @@
|
|||||||
<template v-if="column.key === 'checkEvidence'">
|
<template v-if="column.key === 'checkEvidence'">
|
||||||
<div class="text-left">
|
<div class="text-left">
|
||||||
{{ record[column.key] || '待填写' }}
|
{{ record[column.key] || '待填写' }}
|
||||||
<!-- <a-tooltip v-if="record[column.key] && record[column.key].length > 30" :title="record[column.key]">
|
|
||||||
<MoreOutlined class="ml-1" />
|
|
||||||
</a-tooltip>-->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -405,11 +414,19 @@
|
|||||||
:project-id="props.data?.id"
|
:project-id="props.data?.id"
|
||||||
@select="handleHistorySelect"
|
@select="handleHistorySelect"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 编辑弹窗 -->
|
||||||
|
<EditModal
|
||||||
|
v-model:visible="editModalVisible"
|
||||||
|
:record="editingRecord"
|
||||||
|
:fields="editingFields"
|
||||||
|
@save="saveEdit"
|
||||||
|
/>
|
||||||
</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 } from 'vue';
|
||||||
import { Form, message } from 'ant-design-vue';
|
import { Form, message, Modal } 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';
|
||||||
import type { PwlProject } from '@/api/pwl/pwlProject/model';
|
import type { PwlProject } from '@/api/pwl/pwlProject/model';
|
||||||
@@ -421,15 +438,16 @@ import {
|
|||||||
DownloadOutlined,
|
DownloadOutlined,
|
||||||
UploadOutlined,
|
UploadOutlined,
|
||||||
FolderOpenOutlined,
|
FolderOpenOutlined,
|
||||||
FileOutlined,
|
SaveOutlined
|
||||||
MoreOutlined
|
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import { generateAuditReport, downloadAuditReport } from '@/api/ai/auditReport';
|
import { generateAuditReport, downloadAuditReport } from '@/api/ai/auditReport';
|
||||||
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
|
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
|
||||||
|
import { addAiHistory } from '@/api/ai/aiHistory';
|
||||||
import * as auditContentApi from '@/api/ai/auditContent';
|
import * as auditContentApi from '@/api/ai/auditContent';
|
||||||
|
|
||||||
import FileModal from '@/views/pwl/pwlProject/components/components/FileModal.vue';
|
import FileModal from '@/views/pwl/pwlProject/components/components/FileModal.vue';
|
||||||
import HistoryModal from '@/views/pwl/pwlProject/components/components/HistoryModal.vue';
|
import HistoryModal from '@/views/pwl/pwlProject/components/components/HistoryModal.vue';
|
||||||
|
import EditModal from './EditModal.vue';
|
||||||
|
|
||||||
import useNavigationItems from './data/navigationItems';
|
import useNavigationItems from './data/navigationItems';
|
||||||
import { tableConfigs, getTableConfig, createDataMapper, apiMethodMap, findTableConfigByInterface } from './data/tableCommon';
|
import { tableConfigs, getTableConfig, createDataMapper, apiMethodMap, findTableConfigByInterface } from './data/tableCommon';
|
||||||
@@ -469,6 +487,16 @@ const navigationItems = useNavigationItems();
|
|||||||
// ========== 导出状态管理 ==========
|
// ========== 导出状态管理 ==========
|
||||||
const exportStates = reactive({});
|
const exportStates = reactive({});
|
||||||
|
|
||||||
|
// ========== 保存状态管理 ==========
|
||||||
|
const savingStates = reactive({});
|
||||||
|
|
||||||
|
// ========== 编辑相关状态 ==========
|
||||||
|
const editModalVisible = ref(false);
|
||||||
|
const editingRecord = ref<any>(null);
|
||||||
|
const editingSectionIndex = ref(0);
|
||||||
|
const editingTableKey = ref('');
|
||||||
|
const editingFields = ref<any[]>([]);
|
||||||
|
|
||||||
// ========== 文档选择相关 ==========
|
// ========== 文档选择相关 ==========
|
||||||
const fileModal = ref();
|
const fileModal = ref();
|
||||||
const showDocSelect = ref(false);
|
const showDocSelect = ref(false);
|
||||||
@@ -492,6 +520,14 @@ const stateAssetsData = ref(null);
|
|||||||
// ========== 表格数据存储 ==========
|
// ========== 表格数据存储 ==========
|
||||||
const tableData = reactive({});
|
const tableData = reactive({});
|
||||||
|
|
||||||
|
// ========== 统一的表格生成数据存储 ==========
|
||||||
|
const tableGenerationData = reactive<Record<string, {
|
||||||
|
requestData?: any;
|
||||||
|
responseData?: any;
|
||||||
|
interfaceName?: string;
|
||||||
|
timestamp?: number;
|
||||||
|
}>>({});
|
||||||
|
|
||||||
// ========== 表单数据 ==========
|
// ========== 表单数据 ==========
|
||||||
const form = reactive<PwlProject>({
|
const form = reactive<PwlProject>({
|
||||||
// ... 表单属性保持不变
|
// ... 表单属性保持不变
|
||||||
@@ -505,6 +541,29 @@ const hasTripleOneData = computed(() => {
|
|||||||
return section.data && section.data.length > 0;
|
return section.data && section.data.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ========== 工具函数 ==========
|
||||||
|
const getTableKey = (sectionIndex: number) => {
|
||||||
|
const section = navigationItems.value[sectionIndex];
|
||||||
|
if (!section || !section.tableOptions || section.tableOptions.length === 0) return '';
|
||||||
|
const currentTable = section.tableOptions[section.currentTableIndex];
|
||||||
|
if (!currentTable) return '';
|
||||||
|
return `${section.tableType}_${currentTable.value}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTableInfo = (sectionIndex: number) => {
|
||||||
|
const section = navigationItems.value[sectionIndex];
|
||||||
|
if (!section || !section.tableOptions || section.tableOptions.length === 0) return null;
|
||||||
|
const currentTable = section.tableOptions[section.currentTableIndex];
|
||||||
|
if (!currentTable) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
section,
|
||||||
|
currentTable,
|
||||||
|
tableKey: `${section.tableType}_${currentTable.value}`,
|
||||||
|
tableValue: currentTable.value
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// ========== 通用方法 ==========
|
// ========== 通用方法 ==========
|
||||||
|
|
||||||
/* 更新visible */
|
/* 更新visible */
|
||||||
@@ -529,9 +588,9 @@ const handleTableChange = async (sectionIndex: number, tableIndex: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 加载对应的数据
|
// 加载对应的数据
|
||||||
const dataKey = `${section.tableType}_${tableOption.value}`;
|
const tableKey = `${section.tableType}_${tableOption.value}`;
|
||||||
if (tableData[dataKey]) {
|
if (tableData[tableKey]) {
|
||||||
section.data = tableData[dataKey];
|
section.data = tableData[tableKey];
|
||||||
} else {
|
} else {
|
||||||
section.data = [];
|
section.data = [];
|
||||||
}
|
}
|
||||||
@@ -544,32 +603,59 @@ const openDocSelect = (sectionIndex: number) => {
|
|||||||
currentSectionIndex.value = sectionIndex;
|
currentSectionIndex.value = sectionIndex;
|
||||||
showDocSelect.value = true;
|
showDocSelect.value = true;
|
||||||
|
|
||||||
const section = navigationItems.value[sectionIndex];
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
if (!tableInfo) return;
|
||||||
const dataKey = `${section.tableType}_${currentTable.value}`;
|
|
||||||
|
|
||||||
checkedDirKeys.value = [...lastSelectedDirKeys.value];
|
checkedDirKeys.value = [...lastSelectedDirKeys.value];
|
||||||
selectedFileKeys.value = [...lastSelectedFileKeys.value];
|
selectedFileKeys.value = [...lastSelectedFileKeys.value];
|
||||||
selectedDocList.value = lastSelectedDirKeys.value.map(key => key.toString());
|
selectedDocList.value = lastSelectedDirKeys.value.map(key => key.toString());
|
||||||
selectedFileList.value = lastSelectedFileKeys.value.map(key => key.toString());
|
selectedFileList.value = lastSelectedFileKeys.value.map(key => key.toString());
|
||||||
|
|
||||||
fileModal.value.open(dataKey);
|
fileModal.value.open(tableInfo.tableKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 保存表格生成数据 */
|
||||||
|
const saveTableGenerationData = (tableKey: string, requestData: any, responseData: any, tableValue?: string) => {
|
||||||
|
// 根据tableKey找到对应的表格配置,获取正确的接口名称
|
||||||
|
let correctInterfaceName = tableKey;
|
||||||
|
|
||||||
|
// 从tableKey中提取sectionType和tableValue
|
||||||
|
const [sectionType, value] = tableKey.split('_');
|
||||||
|
|
||||||
|
// 查找对应的表格配置
|
||||||
|
const sectionKeys = Object.keys(tableConfigs);
|
||||||
|
const sectionIndex = sectionKeys.findIndex(key => key === sectionType);
|
||||||
|
|
||||||
|
if (sectionIndex !== -1) {
|
||||||
|
const tableConfig = getTableConfig(sectionIndex);
|
||||||
|
if (tableConfig && tableConfig.interfaceMap && tableConfig.interfaceMap[value || tableValue]) {
|
||||||
|
correctInterfaceName = tableConfig.interfaceMap[value || tableValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tableGenerationData[tableKey] = {
|
||||||
|
requestData,
|
||||||
|
responseData,
|
||||||
|
interfaceName: correctInterfaceName, // 保存正确的接口名称
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 获取表格生成数据 */
|
||||||
|
const getTableGenerationData = (tableKey: string) => {
|
||||||
|
return tableGenerationData[tableKey] || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 通用表格内容生成方法 */
|
/* 通用表格内容生成方法 */
|
||||||
const generateTableContent = async (sectionIndex: number) => {
|
const generateTableContent = async (sectionIndex: number) => {
|
||||||
const section = navigationItems.value[sectionIndex];
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
if (!section) {
|
if (!tableInfo) {
|
||||||
message.error('章节不存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
|
||||||
if (!currentTable) {
|
|
||||||
message.error('表格配置不存在');
|
message.error('表格配置不存在');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { section, currentTable, tableKey } = tableInfo;
|
||||||
|
|
||||||
section.generating = true;
|
section.generating = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -602,13 +688,15 @@ const generateTableContent = async (sectionIndex: number) => {
|
|||||||
const result = await generateMethod(requestData);
|
const result = await generateMethod(requestData);
|
||||||
|
|
||||||
if (result.code === 0 && result.data?.success) {
|
if (result.code === 0 && result.data?.success) {
|
||||||
|
// 保存表格生成数据
|
||||||
|
saveTableGenerationData(tableKey, requestData, result.data, currentTable.value);
|
||||||
|
|
||||||
// 使用对应的数据映射函数
|
// 使用对应的数据映射函数
|
||||||
const dataMapper = createDataMapper(currentTable.value);
|
const dataMapper = createDataMapper(currentTable.value);
|
||||||
const mappedData = dataMapper(result.data.data);
|
const mappedData = dataMapper(result.data.data);
|
||||||
|
|
||||||
// 保存数据
|
// 保存数据
|
||||||
const dataKey = `${section.tableType}_${currentTable.value}`;
|
tableData[tableKey] = mappedData;
|
||||||
tableData[dataKey] = mappedData;
|
|
||||||
|
|
||||||
// 特殊处理数据存储
|
// 特殊处理数据存储
|
||||||
if (currentTable.value === 'tripleOne') {
|
if (currentTable.value === 'tripleOne') {
|
||||||
@@ -634,8 +722,10 @@ const generateTableContent = async (sectionIndex: number) => {
|
|||||||
|
|
||||||
/* 通用表格导出方法 */
|
/* 通用表格导出方法 */
|
||||||
const handleExportTable = async (sectionIndex: number) => {
|
const handleExportTable = async (sectionIndex: number) => {
|
||||||
const section = navigationItems.value[sectionIndex];
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
if (!tableInfo) return;
|
||||||
|
|
||||||
|
const { section, currentTable, tableKey } = tableInfo;
|
||||||
|
|
||||||
if (!section.data || section.data.length === 0) {
|
if (!section.data || section.data.length === 0) {
|
||||||
message.warning(`没有可导出的${currentTable.title}数据`);
|
message.warning(`没有可导出的${currentTable.title}数据`);
|
||||||
@@ -647,8 +737,7 @@ const handleExportTable = async (sectionIndex: number) => {
|
|||||||
const auditTime = form.expirationTime || props.data?.expirationTime || '未知时间';
|
const auditTime = form.expirationTime || props.data?.expirationTime || '未知时间';
|
||||||
|
|
||||||
// 设置导出状态
|
// 设置导出状态
|
||||||
const exportKey = `${section.tableType}_${currentTable.value}`;
|
exportStates[tableKey] = true;
|
||||||
exportStates[exportKey] = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const exportData = {
|
const exportData = {
|
||||||
@@ -681,7 +770,7 @@ const handleExportTable = async (sectionIndex: number) => {
|
|||||||
console.error(`导出表格失败:`, error);
|
console.error(`导出表格失败:`, error);
|
||||||
message.error(`导出失败: ${error.message || '未知错误'}`);
|
message.error(`导出失败: ${error.message || '未知错误'}`);
|
||||||
} finally {
|
} finally {
|
||||||
exportStates[exportKey] = false;
|
exportStates[tableKey] = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -744,8 +833,10 @@ const generateDefaultContent = async (sectionIndex: number, childIndex?: number)
|
|||||||
|
|
||||||
/* 打开历史记录 */
|
/* 打开历史记录 */
|
||||||
const openHistory = (sectionIndex: number) => {
|
const openHistory = (sectionIndex: number) => {
|
||||||
const section = navigationItems.value[sectionIndex];
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
if (!tableInfo) return;
|
||||||
|
|
||||||
|
const { section, currentTable } = tableInfo;
|
||||||
|
|
||||||
if (currentTable) {
|
if (currentTable) {
|
||||||
// 通过表格配置获取接口名称
|
// 通过表格配置获取接口名称
|
||||||
@@ -783,7 +874,7 @@ const handleHistorySelect = (record: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 应用历史数据
|
// 应用历史数据
|
||||||
applyHistoryData(sectionIndex, responseData.data, tableConfig.tableValue);
|
applyHistoryData(sectionIndex, responseData.data, tableConfig.tableValue, record.requestData, record.interfaceName);
|
||||||
message.success('已应用选择的历史记录');
|
message.success('已应用选择的历史记录');
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('解析历史记录数据失败:', error);
|
console.error('解析历史记录数据失败:', error);
|
||||||
@@ -792,7 +883,7 @@ const handleHistorySelect = (record: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* 应用历史数据 */
|
/* 应用历史数据 */
|
||||||
const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: string) => {
|
const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: string, requestDataStr?: string, interfaceName?: string) => {
|
||||||
const section = navigationItems.value[sectionIndex];
|
const section = navigationItems.value[sectionIndex];
|
||||||
if (!section) return;
|
if (!section) return;
|
||||||
|
|
||||||
@@ -803,13 +894,31 @@ const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: s
|
|||||||
const tableOptionIndex = section.tableOptions.findIndex(option => option.value === tableValue);
|
const tableOptionIndex = section.tableOptions.findIndex(option => option.value === tableValue);
|
||||||
if (tableOptionIndex === -1) return;
|
if (tableOptionIndex === -1) return;
|
||||||
|
|
||||||
|
// 解析请求数据
|
||||||
|
let requestData = null;
|
||||||
|
try {
|
||||||
|
requestData = requestDataStr ? JSON.parse(requestDataStr) : null;
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('解析历史请求数据失败:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建响应数据对象
|
||||||
|
const responseData = {
|
||||||
|
success: true,
|
||||||
|
data: data,
|
||||||
|
data_source: interfaceName || tableValue
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存表格生成数据
|
||||||
|
const tableKey = `${section.tableType}_${tableValue}`;
|
||||||
|
saveTableGenerationData(tableKey, requestData, responseData, interfaceName);
|
||||||
|
|
||||||
// 使用数据映射函数
|
// 使用数据映射函数
|
||||||
const dataMapper = createDataMapper(tableValue);
|
const dataMapper = createDataMapper(tableValue);
|
||||||
const mappedData = dataMapper(data);
|
const mappedData = dataMapper(data);
|
||||||
|
|
||||||
// 保存数据
|
// 保存数据
|
||||||
const dataKey = `${section.tableType}_${tableValue}`;
|
tableData[tableKey] = mappedData;
|
||||||
tableData[dataKey] = mappedData;
|
|
||||||
|
|
||||||
// 切换表格(如果当前不是这个表格)
|
// 切换表格(如果当前不是这个表格)
|
||||||
if (section.currentTableIndex !== tableOptionIndex) {
|
if (section.currentTableIndex !== tableOptionIndex) {
|
||||||
@@ -831,20 +940,293 @@ const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: s
|
|||||||
/* 编辑行 */
|
/* 编辑行 */
|
||||||
const editRow = (record: any, sectionIndex: number) => {
|
const editRow = (record: any, sectionIndex: number) => {
|
||||||
const section = navigationItems.value[sectionIndex];
|
const section = navigationItems.value[sectionIndex];
|
||||||
message.info(`编辑第${record.index}行数据`);
|
if (!section || !section.columns) return;
|
||||||
// 这里可以打开一个编辑弹窗
|
|
||||||
|
// 获取可编辑的字段(排除操作列)
|
||||||
|
editingFields.value = section.columns.filter(col =>
|
||||||
|
col.key !== 'action' &&
|
||||||
|
col.dataIndex !== 'key' &&
|
||||||
|
col.dataIndex !== 'index' &&
|
||||||
|
col.dataIndex !== 'workPaperIndex' &&
|
||||||
|
col.key !== 'workPaperIndex'
|
||||||
|
);
|
||||||
|
|
||||||
|
editingRecord.value = { ...record };
|
||||||
|
editingSectionIndex.value = sectionIndex;
|
||||||
|
|
||||||
|
// 记录当前编辑的表格key
|
||||||
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
|
if (tableInfo) {
|
||||||
|
editingTableKey.value = tableInfo.tableKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
editModalVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 保存编辑 */
|
||||||
|
const saveEdit = (updatedRecord: any) => {
|
||||||
|
const section = navigationItems.value[editingSectionIndex.value];
|
||||||
|
if (!section || !section.data) return;
|
||||||
|
|
||||||
|
const index = section.data.findIndex((item: any) => item.key === updatedRecord.key);
|
||||||
|
if (index !== -1) {
|
||||||
|
// 保留原有数据的其他字段
|
||||||
|
const originalRecord = section.data[index];
|
||||||
|
section.data[index] = { ...originalRecord, ...updatedRecord };
|
||||||
|
|
||||||
|
// 更新对应的响应数据
|
||||||
|
if (editingTableKey.value && tableGenerationData[editingTableKey.value]) {
|
||||||
|
// 获取当前的响应数据
|
||||||
|
const responseData = tableGenerationData[editingTableKey.value].responseData;
|
||||||
|
|
||||||
|
// 如果响应数据存在且包含data数组
|
||||||
|
if (responseData && responseData.data && Array.isArray(responseData.data)) {
|
||||||
|
// 找到对应的原始数据索引
|
||||||
|
const dataIndex = (() => {
|
||||||
|
const hasValidId =
|
||||||
|
updatedRecord.id !== undefined &&
|
||||||
|
updatedRecord.id !== null &&
|
||||||
|
updatedRecord.id !== '';
|
||||||
|
|
||||||
|
if (hasValidId) {
|
||||||
|
const byId = responseData.data.findIndex((item: any) => item.id === updatedRecord.id);
|
||||||
|
if (byId !== -1) return byId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认 key 由 mapper 使用 index 生成,后端原始数据通常没有 key
|
||||||
|
if (
|
||||||
|
typeof updatedRecord.key === 'number' &&
|
||||||
|
updatedRecord.key >= 0 &&
|
||||||
|
updatedRecord.key < responseData.data.length
|
||||||
|
) {
|
||||||
|
return updatedRecord.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasKey = updatedRecord.key !== undefined && updatedRecord.key !== null;
|
||||||
|
if (hasKey) {
|
||||||
|
const byKey = responseData.data.findIndex(
|
||||||
|
(item: any) => item.key !== undefined && item.key === updatedRecord.key
|
||||||
|
);
|
||||||
|
if (byKey !== -1) return byKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (dataIndex !== -1) {
|
||||||
|
// 更新原始响应数据中的对应记录
|
||||||
|
responseData.data[dataIndex] = {
|
||||||
|
...responseData.data[dataIndex],
|
||||||
|
...(() => {
|
||||||
|
const { workPaperIndex, key, index, ...rest } = updatedRecord;
|
||||||
|
return rest;
|
||||||
|
})()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新tableGenerationData中的响应数据
|
||||||
|
tableGenerationData[editingTableKey.value].responseData = responseData;
|
||||||
|
} else {
|
||||||
|
// 如果没找到对应的原始数据,直接将编辑后的数据添加到响应数据中
|
||||||
|
// 这可能发生在手动添加行的情况下
|
||||||
|
responseData.data.push((() => {
|
||||||
|
const { workPaperIndex, key, index, ...rest } = updatedRecord;
|
||||||
|
return rest;
|
||||||
|
})());
|
||||||
|
tableGenerationData[editingTableKey.value].responseData = responseData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message.success('编辑成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
editModalVisible.value = false;
|
||||||
|
editingTableKey.value = ''; // 清空编辑的表格key
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 删除行 */
|
/* 删除行 */
|
||||||
const deleteRow = (record: any, sectionIndex: number) => {
|
const deleteRow = (record: any, sectionIndex: number) => {
|
||||||
const section = navigationItems.value[sectionIndex];
|
const section = navigationItems.value[sectionIndex];
|
||||||
if (section.data) {
|
if (!section || !section.data) return;
|
||||||
const index = section.data.findIndex((item: any) => item.key === record.key);
|
|
||||||
if (index !== -1) {
|
const uiIndex = section.data.findIndex((item: any) => item.key === record.key);
|
||||||
section.data.splice(index, 1);
|
if (uiIndex === -1) return;
|
||||||
message.success('删除成功');
|
|
||||||
|
// 同步删除原始 responseData(删除前就计算 rawIndex,避免 key 重排导致错位)
|
||||||
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
|
if (tableInfo && tableInfo.tableKey && tableGenerationData[tableInfo.tableKey]) {
|
||||||
|
const responseData = tableGenerationData[tableInfo.tableKey].responseData;
|
||||||
|
if (responseData && responseData.data && Array.isArray(responseData.data)) {
|
||||||
|
const rawIndex = (() => {
|
||||||
|
const hasValidId = record?.id !== undefined && record?.id !== null && record?.id !== '';
|
||||||
|
if (hasValidId) {
|
||||||
|
const byId = responseData.data.findIndex((item: any) => item.id === record.id);
|
||||||
|
if (byId !== -1) return byId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认 key 由 mapper 使用 index 生成
|
||||||
|
if (
|
||||||
|
typeof record.key === 'number' &&
|
||||||
|
record.key >= 0 &&
|
||||||
|
record.key < responseData.data.length
|
||||||
|
) {
|
||||||
|
return record.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasKey = record?.key !== undefined && record?.key !== null;
|
||||||
|
if (hasKey) {
|
||||||
|
const byKey = responseData.data.findIndex((item: any) => item.key !== undefined && item.key === record.key);
|
||||||
|
if (byKey !== -1) return byKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后兜底:使用 UI 索引
|
||||||
|
if (uiIndex >= 0 && uiIndex < responseData.data.length) return uiIndex;
|
||||||
|
return -1;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (rawIndex !== -1) {
|
||||||
|
responseData.data.splice(rawIndex, 1);
|
||||||
|
tableGenerationData[tableInfo.tableKey].responseData = responseData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除 UI 行
|
||||||
|
section.data.splice(uiIndex, 1);
|
||||||
|
|
||||||
|
// 删除后重排 key/index,保证后续编辑/保存按 key(索引) 能正确匹配
|
||||||
|
section.data = section.data.map((item: any, index: number) => {
|
||||||
|
const next: any = { ...item, key: index };
|
||||||
|
if (item && Object.prototype.hasOwnProperty.call(item, 'index')) {
|
||||||
|
next.index = index + 1;
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tableInfo && tableInfo.tableKey) {
|
||||||
|
tableData[tableInfo.tableKey] = section.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.success('删除成功');
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 保存当前表格到历史记录 */
|
||||||
|
const saveCurrentTable = async (sectionIndex: number) => {
|
||||||
|
const tableInfo = getTableInfo(sectionIndex);
|
||||||
|
if (!tableInfo) return;
|
||||||
|
|
||||||
|
const { section, currentTable, tableKey } = tableInfo;
|
||||||
|
|
||||||
|
if (!section || !section.data || section.data.length === 0) {
|
||||||
|
message.warning('没有数据可保存');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置保存状态
|
||||||
|
savingStates[tableKey] = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取表格生成数据
|
||||||
|
const generationData = getTableGenerationData(tableKey);
|
||||||
|
|
||||||
|
if (!generationData || !generationData.responseData) {
|
||||||
|
message.warning('没有找到该表格的生成记录,请先生成数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关键修复:从表格配置中获取正确的接口名称
|
||||||
|
const tableConfig = getTableConfig(sectionIndex);
|
||||||
|
let correctInterfaceName = generationData.interfaceName; // 默认为当前值
|
||||||
|
|
||||||
|
if (tableConfig && tableConfig.interfaceMap && tableConfig.interfaceMap[currentTable.value]) {
|
||||||
|
correctInterfaceName = tableConfig.interfaceMap[currentTable.value];
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedResponseData = (() => {
|
||||||
|
const src = generationData.responseData;
|
||||||
|
if (!src || !src.data || !Array.isArray(src.data)) return src;
|
||||||
|
|
||||||
|
const cloned = JSON.parse(JSON.stringify(src));
|
||||||
|
cloned.data = cloned.data.map((row: any) => {
|
||||||
|
const workPaperIndex = row?.workPaperIndex;
|
||||||
|
if (!Array.isArray(workPaperIndex)) return row;
|
||||||
|
|
||||||
|
const normalized = workPaperIndex.map((item: any) => {
|
||||||
|
if (typeof item === 'string') return item;
|
||||||
|
if (item && typeof item === 'object') {
|
||||||
|
const fileId = item.fileId || item.file_id || '';
|
||||||
|
const fileName = item.fileName || item.file_name || '';
|
||||||
|
const fileUrl = item.fileUrl || item.url || '';
|
||||||
|
return `${fileId}||${fileName}||${fileUrl}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}).filter((s: string) => s && s.trim() !== '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
workPaperIndex: normalized
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return cloned;
|
||||||
|
})();
|
||||||
|
|
||||||
|
const aiHistory = {
|
||||||
|
projectId: props.data?.id,
|
||||||
|
interfaceName: correctInterfaceName, // 使用正确的接口名称
|
||||||
|
requestData: generationData.requestData ? JSON.stringify(generationData.requestData) : '',
|
||||||
|
responseData: JSON.stringify(normalizedResponseData),
|
||||||
|
// 其他字段后端会自动设置
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调用后端保存接口
|
||||||
|
await addAiHistory(aiHistory);
|
||||||
|
message.success('保存成功');
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('保存表格失败:', error);
|
||||||
|
message.error('保存失败: ' + (error.message || '未知错误'));
|
||||||
|
} finally {
|
||||||
|
savingStates[tableKey] = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCurrentTable = (sectionIndex: number) => {
|
||||||
|
const section = navigationItems.value[sectionIndex];
|
||||||
|
if (!section || section.mode !== 'table' || !section.tableOptions || section.tableOptions.length === 0) return;
|
||||||
|
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认清空表格?',
|
||||||
|
content: '将清空当前章节下所有表格选项卡的数据,且无法恢复。',
|
||||||
|
okText: '清空',
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: '取消',
|
||||||
|
onOk: () => {
|
||||||
|
section.tableOptions.forEach((option: any) => {
|
||||||
|
const tableKey = `${section.tableType}_${option.value}`;
|
||||||
|
|
||||||
|
tableData[tableKey] = [];
|
||||||
|
|
||||||
|
const gen = tableGenerationData[tableKey];
|
||||||
|
if (gen && gen.responseData) {
|
||||||
|
const next = { ...gen.responseData, data: [] };
|
||||||
|
tableGenerationData[tableKey].responseData = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exportStates[tableKey] !== undefined) delete exportStates[tableKey];
|
||||||
|
if (savingStates[tableKey] !== undefined) delete savingStates[tableKey];
|
||||||
|
|
||||||
|
if (option.value === 'tripleOne') {
|
||||||
|
tripleOneData.value = null;
|
||||||
|
}
|
||||||
|
if (option.value === 'assets') {
|
||||||
|
stateAssetsData.value = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
section.data = [];
|
||||||
|
message.success('已清空表格');
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 键盘快捷键处理 */
|
/* 键盘快捷键处理 */
|
||||||
@@ -956,6 +1338,10 @@ watch(
|
|||||||
Object.keys(tableData).forEach(key => {
|
Object.keys(tableData).forEach(key => {
|
||||||
delete tableData[key];
|
delete tableData[key];
|
||||||
});
|
});
|
||||||
|
// 清空表格生成数据
|
||||||
|
Object.keys(tableGenerationData).forEach(key => {
|
||||||
|
delete tableGenerationData[key];
|
||||||
|
});
|
||||||
// 清空特殊数据
|
// 清空特殊数据
|
||||||
tripleOneData.value = null;
|
tripleOneData.value = null;
|
||||||
stateAssetsData.value = null;
|
stateAssetsData.value = null;
|
||||||
@@ -1012,4 +1398,4 @@ export default {
|
|||||||
color: #8c8c8c;
|
color: #8c8c8c;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user