Files
jzsj-vue/src/views/pwl/pwlProject/components/EditModal.vue

310 lines
8.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<a-modal
:visible="visible"
title="编辑行数据"
@ok="handleOk"
@cancel="handleCancel"
: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="activeFormData[field.dataIndex]"
:rows="4"
:placeholder="`请输入${field.title}`"
/>
</template>
<template v-else-if="field.dataIndex === 'workPaperIndex'">
<a-textarea
v-model:value="activeFormData[field.dataIndex]"
:rows="4"
:placeholder="'每行一个文件格式为file_id||文件名||url'"
/>
</template>
<template v-else>
<a-textarea
:rows="4"
v-model:value="activeFormData[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="activeFormData[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[];
records?: any[];
}>();
const emit = defineEmits(['update:visible', 'save']);
const loading = ref(false);
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) => {
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) {
selectedRecordIndex.value = 0;
formDataList.value = displayRecords.value.map((rec) =>
transformRecordToFormData(rec)
);
}
},
{ immediate: true }
);
const handleOk = () => {
if (!formDataList.value || formDataList.value.length === 0) {
message.warning('没有数据可保存');
return;
}
// 处理workPaperIndex将字符串转换回对象数组
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);
loading.value = false;
emit('update:visible', false);
};
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>
.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;
}
.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>