修复编辑、删除无法操作的问题

This commit is contained in:
2026-01-14 09:56:19 +08:00
parent 1adf7de021
commit c3c3a8c3d1
2 changed files with 435 additions and 204 deletions

View File

@@ -7,26 +7,59 @@
:confirm-loading="loading"
width="600px"
>
<template #title>
<div class="modal-title">
<span>编辑行数据</span>
<a-tag v-if="displayRecords.length > 1" color="blue" class="ml-2">
同步编辑 {{ displayRecords.length }}
</a-tag>
</div>
</template>
<a-alert
v-if="displayRecords.length > 1"
type="info"
show-icon
style="margin-bottom: 12px"
:message="`已选择 ${displayRecords.length} 条记录,将把当前修改同步到这些记录`"
/>
<a-list
v-if="displayRecords.length > 1"
size="small"
bordered
:data-source="displayRecords"
style="margin-bottom: 12px; max-height: 160px; overflow-y: auto"
>
<template #renderItem="{ item, index }">
<a-list-item
:class="['record-item', { active: index === selectedRecordIndex }]"
@click="selectRecord(index)"
>
<span class="record-label">#{{ index + 1 }}</span>
</a-list-item>
</template>
</a-list>
<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]"
v-model:value="activeFormData[field.dataIndex]"
:rows="4"
:placeholder="`请输入${field.title}`"
/>
</template>
<template v-else-if="field.dataIndex === 'workPaperIndex'">
<a-textarea
v-model:value="formData[field.dataIndex]"
v-model:value="activeFormData[field.dataIndex]"
:rows="4"
:placeholder="'每行一个文件格式为file_id||文件名||url'"
/>
</template>
<template v-else>
<a-input
v-model:value="formData[field.dataIndex]"
v-model:value="activeFormData[field.dataIndex]"
:placeholder="`请输入${field.title}`"
/>
</template>
@@ -44,7 +77,7 @@
class="nested-field-item"
>
<a-input
v-model:value="formData[childField.dataIndex]"
v-model:value="activeFormData[childField.dataIndex]"
:placeholder="`请输入${childField.title}`"
/>
</a-form-item>
@@ -64,12 +97,44 @@ const props = defineProps<{
visible: boolean;
record: any;
fields: any[];
records?: any[];
}>();
const emit = defineEmits(['update:visible', 'save']);
const loading = ref(false);
const formData = ref({});
const formDataList = ref<any[]>([]);
const selectedRecordIndex = ref(0);
const displayRecords = computed(() => {
if (props.records && Array.isArray(props.records) && props.records.length) {
return props.records;
}
if (props.record) return [props.record];
return [];
});
const transformRecordToFormData = (record: any) => {
if (!record) return {};
const recordCopy = JSON.parse(JSON.stringify(record));
if (
hasWorkPaperIndexField.value &&
recordCopy.workPaperIndex &&
Array.isArray(recordCopy.workPaperIndex)
) {
recordCopy.workPaperIndex = recordCopy.workPaperIndex
.map((item: any) => {
if (typeof item === 'object') {
return `${item.fileId || ''}||${item.fileName || ''}||${item.fileUrl || ''}`;
}
return item;
})
.join('\n');
}
return recordCopy;
};
const hasWorkPaperIndexField = computed(() => {
return (props.fields || []).some((field) => {
@@ -81,7 +146,7 @@ const hasWorkPaperIndexField = computed(() => {
const processedFields = computed(() => {
const processed: any[] = [];
props.fields.forEach(field => {
(props.fields || []).forEach(field => {
if (field.children && Array.isArray(field.children)) {
// 处理有子字段的情况(如职务)
processed.push({
@@ -100,49 +165,44 @@ const processedFields = computed(() => {
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');
watch(
() => props.visible,
(visible) => {
if (visible) {
selectedRecordIndex.value = 0;
formDataList.value = displayRecords.value.map((rec) =>
transformRecordToFormData(rec)
);
}
formData.value = recordCopy;
}
}, { immediate: true });
},
{ immediate: true }
);
const handleOk = () => {
if (!formData.value) {
if (!formDataList.value || formDataList.value.length === 0) {
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;
});
}
const dataToSave = formDataList.value.map((item) => {
const cloned = JSON.parse(JSON.stringify(item || {}));
if (hasWorkPaperIndexField.value && cloned.workPaperIndex && typeof cloned.workPaperIndex === 'string') {
const lines = cloned.workPaperIndex.split('\n').filter((line: string) => line.trim() !== '');
cloned.workPaperIndex = lines.map((line: string) => {
const parts = line.split('||');
if (parts.length >= 3) {
return {
fileId: parts[0] || '',
fileName: parts[1] || '',
fileUrl: parts[2] || ''
};
}
return line;
});
}
return cloned;
});
loading.value = true;
emit('save', dataToSave);
@@ -153,6 +213,32 @@ const handleOk = () => {
const handleCancel = () => {
emit('update:visible', false);
};
const selectRecord = (index: number) => {
if (index < 0 || index >= displayRecords.value.length) return;
selectedRecordIndex.value = index;
if (!formDataList.value[index]) {
formDataList.value[index] = transformRecordToFormData(
displayRecords.value[index]
);
}
};
const activeFormData = computed({
get() {
if (!displayRecords.value.length) return {};
if (!formDataList.value[selectedRecordIndex.value]) {
formDataList.value[selectedRecordIndex.value] = transformRecordToFormData(
displayRecords.value[selectedRecordIndex.value]
);
}
return formDataList.value[selectedRecordIndex.value] || {};
},
set(val) {
if (!displayRecords.value.length) return;
formDataList.value[selectedRecordIndex.value] = val || {};
}
});
</script>
<style scoped>
@@ -179,4 +265,32 @@ const handleCancel = () => {
.nested-field-item {
margin-bottom: 0;
}
</style>
.modal-title {
display: flex;
align-items: center;
}
.record-label {
display: inline-block;
width: 36px;
color: #888;
}
.record-text {
color: #333;
}
.record-item {
cursor: pointer;
transition: background-color 0.2s;
}
.record-item:hover {
background-color: #f5f5f5;
}
.record-item.active {
background-color: #e6f7ff;
}
</style>

View File

@@ -212,15 +212,8 @@
>
<div class="flex justify-start items-center">
<a-space>
<a-button type="primary" @click.native="editRow(record, index)"
>编辑</a-button
>
<a-button
type="primary"
danger
@click.native="deleteRow(record, index)"
>删除</a-button
>
<a-button type="primary" @click.native="editRow(index)">编辑</a-button>
<a-button type="primary" danger @click.native="deleteRow(index)">删除</a-button>
<a-button>导出</a-button>
<a-button @click="openEvidenceModal(index)"
>导出取证单</a-button
@@ -264,6 +257,7 @@
:scroll="{ y: 500, x: 1000 }"
:pagination="false"
bordered
:row-key="(row) => row?.key ?? row?.id ?? row?.index"
:data-source="item.data"
>
<template #bodyCell="{ column, record }">
@@ -497,6 +491,7 @@
<EditModal
v-model:visible="editModalVisible"
:record="editingRecord"
:records="editingTargetRows"
:fields="editingFields"
@save="saveEdit"
/>
@@ -581,6 +576,7 @@
const editingSectionIndex = ref(0);
const editingTableKey = ref('');
const editingFields = ref<any[]>([]);
const editingTargetRows = ref<any[]>([]);
// ========== 文档选择相关 ==========
const fileModal = ref();
@@ -605,21 +601,21 @@
// ========== 表格选择 ==========
const selectedRowsMap = reactive<Record<number, any[]>>({});
const rowSelections = reactive<Record<number, TableProps['rowSelection']>>(
{}
);
const selectedRowKeysMap = reactive<Record<number, (string | number)[]>>({});
const getRowSelection = (
sectionIndex: number
): TableProps['rowSelection'] => {
if (!rowSelections[sectionIndex]) {
rowSelections[sectionIndex] = {
onChange: (selectedRowKeys: string[], selectedRows: any[]) => {
selectedRowsMap[sectionIndex] = selectedRows;
}
};
}
return rowSelections[sectionIndex];
return {
selectedRowKeys: selectedRowKeysMap[sectionIndex] || [],
onChange: (
selectedRowKeys: (string | number)[],
selectedRows: any[]
) => {
selectedRowsMap[sectionIndex] = selectedRows;
selectedRowKeysMap[sectionIndex] = selectedRowKeys;
}
};
};
// ========== 历史记录相关 ==========
const showHistory = ref(false);
@@ -718,6 +714,9 @@
section.data = [];
}
selectedRowsMap[sectionIndex] = [];
selectedRowKeysMap[sectionIndex] = [];
message.success(`已切换到:${tableOption.title}`);
};
@@ -1134,10 +1133,16 @@
};
/* 编辑行 */
const editRow = (record: any, sectionIndex: number) => {
const editRow = (sectionIndex: number) => {
const section: any = navigationItems.value[sectionIndex];
if (!section || !section.columns) return;
const selectedRows = selectedRowsMap[sectionIndex] || [];
if (!selectedRows.length) {
message.warning('请先选择要编辑的行');
return;
}
// 获取可编辑的字段(排除操作列)
editingFields.value = section.columns.filter(
(col) =>
@@ -1148,183 +1153,272 @@
col.key !== 'workPaperIndex'
);
editingRecord.value = { ...record };
// 将所有选中的行传递给编辑弹窗
editingRecord.value = { ...selectedRows[0] };
editingTargetRows.value = selectedRows.map((row: any) => ({ ...row }));
editingSectionIndex.value = sectionIndex;
// 记录当前编辑的表格key
const tableInfo = getTableInfo(sectionIndex);
if (tableInfo) {
editingTableKey.value = tableInfo.tableKey;
} else {
editingTableKey.value = '';
}
editModalVisible.value = true;
};
/* 保存编辑 */
const saveEdit = (updatedRecord: any) => {
const saveEdit = (updatedRecords: any | any[]) => {
const section: any = 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 };
const targets =
editingTargetRows.value && editingTargetRows.value.length
? editingTargetRows.value
: editingRecord.value
? [editingRecord.value]
: [];
// 更新对应的响应数据
if (editingTableKey.value && tableGenerationData[editingTableKey.value]) {
// 获取当前的响应数据
if (!targets.length) {
message.warning('未找到需要编辑的行');
return;
}
const updatesArray = Array.isArray(updatedRecords)
? updatedRecords
: [updatedRecords];
// 只提取允许编辑的字段,避免把首行的其他值覆盖到所有行
const allowedKeys = new Set<string>();
const collectKeys = (field: any) => {
if (field?.dataIndex) allowedKeys.add(field.dataIndex);
if (field?.children && Array.isArray(field.children)) {
field.children.forEach((child: any) => collectKeys(child));
}
};
editingFields.value.forEach((field: any) => collectKeys(field));
const buildApplyFields = (source: any) => {
const applied: Record<string, any> = {};
allowedKeys.forEach((key) => {
if (key === 'key' || key === 'index') return;
if (Object.prototype.hasOwnProperty.call(source, key)) {
applied[key] = source[key];
}
});
return applied;
};
const stripResponseFields = (data: any) => {
const { workPaperIndex, key, index, ...rest } = data;
return rest;
};
const findRawIndex = (
responseList: any[],
targetRow: any,
fallbackUiIndex: number
) => {
const hasValidId =
targetRow?.id !== undefined &&
targetRow?.id !== null &&
targetRow?.id !== '';
if (hasValidId) {
const byId = responseList.findIndex(
(item: any) => item.id === targetRow.id
);
if (byId !== -1) return byId;
}
if (
typeof targetRow?.key === 'number' &&
targetRow.key >= 0 &&
targetRow.key < responseList.length
) {
return targetRow.key;
}
if (targetRow?.key !== undefined && targetRow?.key !== null) {
const byKey = responseList.findIndex(
(item: any) => item.key !== undefined && item.key === targetRow.key
);
if (byKey !== -1) return byKey;
}
if (
fallbackUiIndex !== -1 &&
fallbackUiIndex >= 0 &&
fallbackUiIndex < responseList.length
) {
return fallbackUiIndex;
}
return -1;
};
let hasApplied = false;
targets.forEach((targetRow: any, idx: number) => {
const updateSource = updatesArray[idx] || updatesArray[0] || {};
const appliedFields = buildApplyFields(updateSource);
if (Object.keys(appliedFields).length === 0) {
return;
}
hasApplied = true;
const uiIndex = section.data.findIndex(
(item: any) => item.key === targetRow.key
);
if (uiIndex !== -1) {
const originalRecord = section.data[uiIndex];
const merged = { ...originalRecord, ...appliedFields };
merged.key = originalRecord.key;
if (Object.prototype.hasOwnProperty.call(originalRecord, 'index')) {
merged.index = originalRecord.index;
}
section.data.splice(uiIndex, 1, merged);
}
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 (responseData && responseData.data && Array.isArray(responseData.data)) {
const rawIndex = findRawIndex(
responseData.data,
targetRow,
uiIndex
);
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;
})()
if (rawIndex !== -1) {
responseData.data[rawIndex] = {
...responseData.data[rawIndex],
...stripResponseFields(appliedFields)
};
// 更新tableGenerationData中的响应数据
tableGenerationData[editingTableKey.value].responseData =
responseData;
} else {
// 如果没找到对应的原始数据,直接将编辑后的数据添加到响应数据中
// 这可能发生在手动添加行的情况下
responseData.data.push(
(() => {
const { workPaperIndex, key, index, ...rest } = updatedRecord;
return rest;
})()
);
tableGenerationData[editingTableKey.value].responseData =
responseData;
responseData.data.push(stripResponseFields(appliedFields));
}
tableGenerationData[editingTableKey.value].responseData =
responseData;
}
}
});
message.success('编辑成功');
if (editingTableKey.value) {
tableData[editingTableKey.value] = section.data;
}
const currentSelections = selectedRowsMap[editingSectionIndex.value] || [];
selectedRowsMap[editingSectionIndex.value] = currentSelections
.map(
(row: any) =>
section.data.find((item: any) => item.key === row.key) || row
);
if (!hasApplied) {
message.warning('没有可应用的字段');
return;
}
message.success('编辑成功');
editModalVisible.value = false;
editingTableKey.value = ''; // 清空编辑的表格key
editingTableKey.value = '';
editingTargetRows.value = [];
};
/* 删除行 */
const deleteRow = (record: any, sectionIndex: number) => {
/* 删除行(多选) */
const deleteRow = (sectionIndex: number) => {
const section: any = navigationItems.value[sectionIndex];
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;
}
}
const selectedRows = selectedRowsMap[sectionIndex] || [];
if (!selectedRows.length) {
message.warning('请先选择要删除的行');
return;
}
// 删除 UI 行
section.data.splice(uiIndex, 1);
const tableInfo = getTableInfo(sectionIndex);
const responseData =
tableInfo &&
tableInfo.tableKey &&
tableGenerationData[tableInfo.tableKey] &&
tableGenerationData[tableInfo.tableKey].responseData &&
tableGenerationData[tableInfo.tableKey].responseData.data &&
Array.isArray(tableGenerationData[tableInfo.tableKey].responseData.data)
? tableGenerationData[tableInfo.tableKey].responseData
: null;
// 删除后重排 key/index保证后续编辑/保存按 key(索引) 能正确匹配
const findRawIndex = (
responseList: any[],
targetRow: any,
fallbackUiIndex: number
) => {
const hasValidId =
targetRow?.id !== undefined &&
targetRow?.id !== null &&
targetRow?.id !== '';
if (hasValidId) {
const byId = responseList.findIndex((item: any) => item.id === targetRow.id);
if (byId !== -1) return byId;
}
if (
typeof targetRow?.key === 'number' &&
targetRow.key >= 0 &&
targetRow.key < responseList.length
) {
return targetRow.key;
}
if (targetRow?.key !== undefined && targetRow?.key !== null) {
const byKey = responseList.findIndex(
(item: any) => item.key !== undefined && item.key === targetRow.key
);
if (byKey !== -1) return byKey;
}
if (
fallbackUiIndex !== -1 &&
fallbackUiIndex >= 0 &&
fallbackUiIndex < responseList.length
) {
return fallbackUiIndex;
}
return -1;
};
const uiIndexesToDelete: number[] = [];
const rawIndexesToDelete: number[] = [];
selectedRows.forEach((row: any) => {
const uiIndex = section.data.findIndex((item: any) => item.key === row.key);
if (uiIndex !== -1) uiIndexesToDelete.push(uiIndex);
if (responseData) {
const rawIndex = findRawIndex(responseData.data, row, uiIndex);
if (rawIndex !== -1) rawIndexesToDelete.push(rawIndex);
}
});
const uniqueUiIndexes = Array.from(new Set(uiIndexesToDelete)).sort(
(a, b) => b - a
);
uniqueUiIndexes.forEach((idx) => {
section.data.splice(idx, 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')) {
@@ -1337,7 +1431,20 @@
tableData[tableInfo.tableKey] = section.data;
}
message.success('删除成功');
if (responseData && tableInfo && tableInfo.tableKey) {
const uniqueRawIndexes = Array.from(new Set(rawIndexesToDelete)).sort(
(a, b) => b - a
);
uniqueRawIndexes.forEach((idx) => {
responseData.data.splice(idx, 1);
});
tableGenerationData[tableInfo.tableKey].responseData = responseData;
}
selectedRowsMap[sectionIndex] = [];
selectedRowKeysMap[sectionIndex] = [];
message.success(`已删除${uniqueUiIndexes.length || selectedRows.length}条记录`);
};
/* 保存当前表格到历史记录 */
@@ -1471,6 +1578,9 @@
section.data = [];
message.success('已清空表格');
selectedRowsMap[sectionIndex] = [];
selectedRowKeysMap[sectionIndex] = [];
}
});
};
@@ -1609,6 +1719,13 @@
// 清空特殊数据
tripleOneData.value = null;
stateAssetsData.value = null;
// 清空选中行
Object.keys(selectedRowsMap).forEach((key) => {
delete selectedRowsMap[key as any];
});
Object.keys(selectedRowKeysMap).forEach((key) => {
delete selectedRowKeysMap[key as any];
});
}
}
);