Compare commits
2 Commits
392e51701d
...
f751d5eda4
| Author | SHA1 | Date | |
|---|---|---|---|
| f751d5eda4 | |||
| 20675364e5 |
182
src/views/pwl/pwlProject/components/EditModal.vue
Normal file
182
src/views/pwl/pwlProject/components/EditModal.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
title="编辑行数据"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:confirm-loading="loading"
|
||||
width="600px"
|
||||
>
|
||||
<a-form layout="vertical">
|
||||
<template v-for="field in processedFields" :key="field.key">
|
||||
<a-form-item :label="field.title" v-if="!field.children">
|
||||
<template v-if="field.type === 'textarea'">
|
||||
<a-textarea
|
||||
v-model:value="formData[field.dataIndex]"
|
||||
:rows="4"
|
||||
:placeholder="`请输入${field.title}`"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="field.dataIndex === 'workPaperIndex'">
|
||||
<a-textarea
|
||||
v-model:value="formData[field.dataIndex]"
|
||||
:rows="4"
|
||||
:placeholder="'每行一个文件,格式为:file_id||文件名||url'"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-input
|
||||
v-model:value="formData[field.dataIndex]"
|
||||
:placeholder="`请输入${field.title}`"
|
||||
/>
|
||||
</template>
|
||||
</a-form-item>
|
||||
|
||||
<!-- 处理嵌套字段(如职务下的党内、行政) -->
|
||||
<template v-else-if="field.children">
|
||||
<div class="nested-fields">
|
||||
<div class="field-group-title">{{ field.title }}</div>
|
||||
<div class="field-group-content">
|
||||
<a-form-item
|
||||
v-for="childField in field.children"
|
||||
:key="childField.key"
|
||||
:label="childField.title"
|
||||
class="nested-field-item"
|
||||
>
|
||||
<a-input
|
||||
v-model:value="formData[childField.dataIndex]"
|
||||
:placeholder="`请输入${childField.title}`"
|
||||
/>
|
||||
</a-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
record: any;
|
||||
fields: any[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:visible', 'save']);
|
||||
|
||||
const loading = ref(false);
|
||||
const formData = ref({});
|
||||
|
||||
const hasWorkPaperIndexField = computed(() => {
|
||||
return (props.fields || []).some((field) => {
|
||||
return field?.dataIndex === 'workPaperIndex' || field?.key === 'workPaperIndex';
|
||||
});
|
||||
});
|
||||
|
||||
// 处理字段,将嵌套结构展平
|
||||
const processedFields = computed(() => {
|
||||
const processed: any[] = [];
|
||||
|
||||
props.fields.forEach(field => {
|
||||
if (field.children && Array.isArray(field.children)) {
|
||||
// 处理有子字段的情况(如职务)
|
||||
processed.push({
|
||||
...field,
|
||||
children: field.children.flatMap(child =>
|
||||
child.children && Array.isArray(child.children)
|
||||
? child.children // 如果是多层嵌套,直接取孙子字段
|
||||
: child // 否则就是子字段
|
||||
)
|
||||
});
|
||||
} else {
|
||||
processed.push(field);
|
||||
}
|
||||
});
|
||||
|
||||
return processed;
|
||||
});
|
||||
|
||||
watch(() => props.visible, (visible) => {
|
||||
if (visible && props.record) {
|
||||
// 深拷贝记录数据
|
||||
const recordCopy = JSON.parse(JSON.stringify(props.record));
|
||||
|
||||
// 特殊处理workPaperIndex:如果是对象数组,转换为字符串数组
|
||||
if (hasWorkPaperIndexField.value && recordCopy.workPaperIndex && Array.isArray(recordCopy.workPaperIndex)) {
|
||||
recordCopy.workPaperIndex = recordCopy.workPaperIndex.map(item => {
|
||||
if (typeof item === 'object') {
|
||||
// 如果是对象,转换为字符串格式:file_id||文件名||url
|
||||
return `${item.fileId || ''}||${item.fileName || ''}||${item.fileUrl || ''}`;
|
||||
}
|
||||
return item;
|
||||
}).join('\n');
|
||||
}
|
||||
|
||||
formData.value = recordCopy;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
const handleOk = () => {
|
||||
if (!formData.value) {
|
||||
message.warning('没有数据可保存');
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理workPaperIndex:将字符串转换回对象数组
|
||||
const dataToSave = JSON.parse(JSON.stringify(formData.value));
|
||||
|
||||
if (hasWorkPaperIndexField.value && dataToSave.workPaperIndex && typeof dataToSave.workPaperIndex === 'string') {
|
||||
const lines = dataToSave.workPaperIndex.split('\n').filter(line => line.trim() !== '');
|
||||
dataToSave.workPaperIndex = lines.map(line => {
|
||||
const parts = line.split('||');
|
||||
if (parts.length >= 3) {
|
||||
return {
|
||||
fileId: parts[0] || '',
|
||||
fileName: parts[1] || '',
|
||||
fileUrl: parts[2] || ''
|
||||
};
|
||||
}
|
||||
return line;
|
||||
});
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
emit('save', dataToSave);
|
||||
loading.value = false;
|
||||
emit('update:visible', false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nested-fields {
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.field-group-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.field-group-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.nested-field-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -163,6 +163,13 @@ function parseWorkPaperIndex(workPaperIndex) {
|
||||
if (!workPaperIndex || !Array.isArray(workPaperIndex)) return [];
|
||||
|
||||
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') {
|
||||
const parts = item.split('||');
|
||||
if (parts.length >= 3) {
|
||||
@@ -231,22 +238,20 @@ export function createDataMapper(type: string) {
|
||||
budgetManage: (data) => data.map((item, index) => ({
|
||||
key: index,
|
||||
index: index + 1,
|
||||
...item,
|
||||
budgetSubject: item.budgetSubject || '-',
|
||||
// 指标来源部分字段映射
|
||||
indicatorSource: item.indicatorSource, // 指标来源
|
||||
total: formatAmount(item.indicatorSourceTotal), // 指标来源-合计
|
||||
lastYearBalance: formatAmount(item.indicatorSourceLastYearBalance), // 指标来源-上年结余
|
||||
initialBudget: formatAmount(item.indicatorSourceInitialBudget), // 指标来源-年初部门预算
|
||||
additionalBudget: formatAmount(item.indicatorSourceAdditionalBudget), // 指标来源-追加(减)预算
|
||||
// 指标运用部分字段映射
|
||||
indicatorUseTotal: formatAmount(item.indicatorUseTotal), // 指标运用-合计
|
||||
appropriationSubtotal: formatAmount(item.indicatorUseAppropriationSubtotal), // 指标运用-拨款小计
|
||||
appropriation: formatAmount(item.indicatorUseAppropriation), // 指标运用-拨款
|
||||
salaryPayment: formatAmount(item.indicatorUseSalaryPayment), // 指标运用-工资统发
|
||||
govProcurement: formatAmount(item.indicatorUseGovProcurement), // 指标运用-政府采购
|
||||
// 其他字段
|
||||
indicatorBalance: formatAmount(item.indicatorBalance), // 指标结余
|
||||
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex), // 工作底稿索引
|
||||
indicatorSource: item.indicatorSource,
|
||||
total: formatAmount(item.indicatorSourceTotal),
|
||||
lastYearBalance: formatAmount(item.indicatorSourceLastYearBalance),
|
||||
initialBudget: formatAmount(item.indicatorSourceInitialBudget),
|
||||
additionalBudget: formatAmount(item.indicatorSourceAdditionalBudget),
|
||||
indicatorUseTotal: formatAmount(item.indicatorUseTotal),
|
||||
appropriationSubtotal: formatAmount(item.indicatorUseAppropriationSubtotal),
|
||||
appropriation: formatAmount(item.indicatorUseAppropriation),
|
||||
salaryPayment: formatAmount(item.indicatorUseSalaryPayment),
|
||||
govProcurement: formatAmount(item.indicatorUseGovProcurement),
|
||||
indicatorBalance: formatAmount(item.indicatorBalance),
|
||||
workPaperIndex: parseWorkPaperIndex(item.workPaperIndex),
|
||||
})),
|
||||
investmentSituation: (data) => data.map((item, index) => ({
|
||||
key: index,
|
||||
|
||||
@@ -156,14 +156,29 @@
|
||||
<a-space>
|
||||
<!-- 所有表格都显示导出按钮 -->
|
||||
<template v-if="item.mode === 'table' && item.tableOptions.length > 0">
|
||||
<a-button
|
||||
danger
|
||||
@click="clearCurrentTable(index)"
|
||||
>
|
||||
清空表格
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="handleExportTable(index)"
|
||||
:loading="exportStates[`${item.tableType}_${item.tableOptions[item.currentTableIndex].value}`]"
|
||||
:loading="exportStates[getTableKey(index)]"
|
||||
>
|
||||
<DownloadOutlined />
|
||||
导出表格
|
||||
</a-button>
|
||||
<!-- 添加保存按钮 -->
|
||||
<a-button
|
||||
type="primary"
|
||||
@click="saveCurrentTable(index)"
|
||||
:loading="savingStates[getTableKey(index)]"
|
||||
>
|
||||
<SaveOutlined />
|
||||
保存表格
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<!-- AI生成按钮 -->
|
||||
@@ -303,9 +318,6 @@
|
||||
<template v-if="column.key === 'auditContent'">
|
||||
<div class="text-left">
|
||||
{{ 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>
|
||||
</template>
|
||||
|
||||
@@ -313,9 +325,6 @@
|
||||
<template v-if="column.key === 'checkEvidence'">
|
||||
<div class="text-left">
|
||||
{{ 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>
|
||||
</template>
|
||||
</template>
|
||||
@@ -405,11 +414,19 @@
|
||||
:project-id="props.data?.id"
|
||||
@select="handleHistorySelect"
|
||||
/>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<EditModal
|
||||
v-model:visible="editModalVisible"
|
||||
:record="editingRecord"
|
||||
:fields="editingFields"
|
||||
@save="saveEdit"
|
||||
/>
|
||||
</a-drawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
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 { copyText } from '@/utils/common';
|
||||
import type { PwlProject } from '@/api/pwl/pwlProject/model';
|
||||
@@ -421,15 +438,16 @@ import {
|
||||
DownloadOutlined,
|
||||
UploadOutlined,
|
||||
FolderOpenOutlined,
|
||||
FileOutlined,
|
||||
MoreOutlined
|
||||
SaveOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { generateAuditReport, downloadAuditReport } from '@/api/ai/auditReport';
|
||||
import { getPwlProjectLibraryByIds } from '@/api/pwl/pwlProjectLibrary';
|
||||
import { addAiHistory } from '@/api/ai/aiHistory';
|
||||
import * as auditContentApi from '@/api/ai/auditContent';
|
||||
|
||||
import FileModal from '@/views/pwl/pwlProject/components/components/FileModal.vue';
|
||||
import HistoryModal from '@/views/pwl/pwlProject/components/components/HistoryModal.vue';
|
||||
import EditModal from './EditModal.vue';
|
||||
|
||||
import useNavigationItems from './data/navigationItems';
|
||||
import { tableConfigs, getTableConfig, createDataMapper, apiMethodMap, findTableConfigByInterface } from './data/tableCommon';
|
||||
@@ -469,6 +487,16 @@ const navigationItems = useNavigationItems();
|
||||
// ========== 导出状态管理 ==========
|
||||
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 showDocSelect = ref(false);
|
||||
@@ -492,6 +520,14 @@ const stateAssetsData = ref(null);
|
||||
// ========== 表格数据存储 ==========
|
||||
const tableData = reactive({});
|
||||
|
||||
// ========== 统一的表格生成数据存储 ==========
|
||||
const tableGenerationData = reactive<Record<string, {
|
||||
requestData?: any;
|
||||
responseData?: any;
|
||||
interfaceName?: string;
|
||||
timestamp?: number;
|
||||
}>>({});
|
||||
|
||||
// ========== 表单数据 ==========
|
||||
const form = reactive<PwlProject>({
|
||||
// ... 表单属性保持不变
|
||||
@@ -505,6 +541,29 @@ const hasTripleOneData = computed(() => {
|
||||
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 */
|
||||
@@ -529,9 +588,9 @@ const handleTableChange = async (sectionIndex: number, tableIndex: number) => {
|
||||
}
|
||||
|
||||
// 加载对应的数据
|
||||
const dataKey = `${section.tableType}_${tableOption.value}`;
|
||||
if (tableData[dataKey]) {
|
||||
section.data = tableData[dataKey];
|
||||
const tableKey = `${section.tableType}_${tableOption.value}`;
|
||||
if (tableData[tableKey]) {
|
||||
section.data = tableData[tableKey];
|
||||
} else {
|
||||
section.data = [];
|
||||
}
|
||||
@@ -544,32 +603,59 @@ const openDocSelect = (sectionIndex: number) => {
|
||||
currentSectionIndex.value = sectionIndex;
|
||||
showDocSelect.value = true;
|
||||
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
||||
const dataKey = `${section.tableType}_${currentTable.value}`;
|
||||
const tableInfo = getTableInfo(sectionIndex);
|
||||
if (!tableInfo) return;
|
||||
|
||||
checkedDirKeys.value = [...lastSelectedDirKeys.value];
|
||||
selectedFileKeys.value = [...lastSelectedFileKeys.value];
|
||||
selectedDocList.value = lastSelectedDirKeys.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 section = navigationItems.value[sectionIndex];
|
||||
if (!section) {
|
||||
message.error('章节不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
||||
if (!currentTable) {
|
||||
const tableInfo = getTableInfo(sectionIndex);
|
||||
if (!tableInfo) {
|
||||
message.error('表格配置不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
const { section, currentTable, tableKey } = tableInfo;
|
||||
|
||||
section.generating = true;
|
||||
|
||||
try {
|
||||
@@ -602,13 +688,15 @@ const generateTableContent = async (sectionIndex: number) => {
|
||||
const result = await generateMethod(requestData);
|
||||
|
||||
if (result.code === 0 && result.data?.success) {
|
||||
// 保存表格生成数据
|
||||
saveTableGenerationData(tableKey, requestData, result.data, currentTable.value);
|
||||
|
||||
// 使用对应的数据映射函数
|
||||
const dataMapper = createDataMapper(currentTable.value);
|
||||
const mappedData = dataMapper(result.data.data);
|
||||
|
||||
// 保存数据
|
||||
const dataKey = `${section.tableType}_${currentTable.value}`;
|
||||
tableData[dataKey] = mappedData;
|
||||
tableData[tableKey] = mappedData;
|
||||
|
||||
// 特殊处理数据存储
|
||||
if (currentTable.value === 'tripleOne') {
|
||||
@@ -634,8 +722,10 @@ const generateTableContent = async (sectionIndex: number) => {
|
||||
|
||||
/* 通用表格导出方法 */
|
||||
const handleExportTable = async (sectionIndex: number) => {
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
||||
const tableInfo = getTableInfo(sectionIndex);
|
||||
if (!tableInfo) return;
|
||||
|
||||
const { section, currentTable, tableKey } = tableInfo;
|
||||
|
||||
if (!section.data || section.data.length === 0) {
|
||||
message.warning(`没有可导出的${currentTable.title}数据`);
|
||||
@@ -647,8 +737,7 @@ const handleExportTable = async (sectionIndex: number) => {
|
||||
const auditTime = form.expirationTime || props.data?.expirationTime || '未知时间';
|
||||
|
||||
// 设置导出状态
|
||||
const exportKey = `${section.tableType}_${currentTable.value}`;
|
||||
exportStates[exportKey] = true;
|
||||
exportStates[tableKey] = true;
|
||||
|
||||
try {
|
||||
const exportData = {
|
||||
@@ -681,7 +770,7 @@ const handleExportTable = async (sectionIndex: number) => {
|
||||
console.error(`导出表格失败:`, error);
|
||||
message.error(`导出失败: ${error.message || '未知错误'}`);
|
||||
} finally {
|
||||
exportStates[exportKey] = false;
|
||||
exportStates[tableKey] = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -744,8 +833,10 @@ const generateDefaultContent = async (sectionIndex: number, childIndex?: number)
|
||||
|
||||
/* 打开历史记录 */
|
||||
const openHistory = (sectionIndex: number) => {
|
||||
const section = navigationItems.value[sectionIndex];
|
||||
const currentTable = section.tableOptions[section.currentTableIndex];
|
||||
const tableInfo = getTableInfo(sectionIndex);
|
||||
if (!tableInfo) return;
|
||||
|
||||
const { section, currentTable } = tableInfo;
|
||||
|
||||
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('已应用选择的历史记录');
|
||||
} catch (error: any) {
|
||||
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];
|
||||
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);
|
||||
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 mappedData = dataMapper(data);
|
||||
|
||||
// 保存数据
|
||||
const dataKey = `${section.tableType}_${tableValue}`;
|
||||
tableData[dataKey] = mappedData;
|
||||
tableData[tableKey] = mappedData;
|
||||
|
||||
// 切换表格(如果当前不是这个表格)
|
||||
if (section.currentTableIndex !== tableOptionIndex) {
|
||||
@@ -831,20 +940,293 @@ const applyHistoryData = async (sectionIndex: number, data: any[], tableValue: s
|
||||
/* 编辑行 */
|
||||
const editRow = (record: any, sectionIndex: number) => {
|
||||
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 section = navigationItems.value[sectionIndex];
|
||||
if (section.data) {
|
||||
const index = section.data.findIndex((item: any) => item.key === record.key);
|
||||
if (index !== -1) {
|
||||
section.data.splice(index, 1);
|
||||
message.success('删除成功');
|
||||
if (!section || !section.data) return;
|
||||
|
||||
const uiIndex = section.data.findIndex((item: any) => item.key === record.key);
|
||||
if (uiIndex === -1) return;
|
||||
|
||||
// 同步删除原始 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 => {
|
||||
delete tableData[key];
|
||||
});
|
||||
// 清空表格生成数据
|
||||
Object.keys(tableGenerationData).forEach(key => {
|
||||
delete tableGenerationData[key];
|
||||
});
|
||||
// 清空特殊数据
|
||||
tripleOneData.value = null;
|
||||
stateAssetsData.value = null;
|
||||
|
||||
Reference in New Issue
Block a user