WholeProcessPlatform/frontend-sjgl/src/views/conventionalHydropower/shiPinJianKong/EditVdModal.vue
2026-08-04 09:01:23 +08:00

284 lines
12 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 v-model:open="visible" :title="isAdd ? '新增视频监控点' : '编辑视频监控点'" width="60vw" :confirm-loading="confirmLoading" @ok="handleOk" @cancel="handleCancel">
<a-form ref="formRef" :model="formData" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" :rules="formRules" class="max-h-[70vh] overflow-y-auto pr-4">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="监控点编码" name="stcd">
<a-input v-model:value="formData.stcd" placeholder="请输入监控点编码" :disabled="!isAdd" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监控点名" name="stnm">
<a-input v-model:value="formData.stnm" placeholder="请输入监控点名" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监控点类型" name="sttp">
<a-select v-model:value="formData.sttp" placeholder="请选择监控点类型" allow-clear show-search :filter-option="filterOption">
<a-select-option v-for="item in sttpOptions" :key="item.value" :label="item.label" :value="item.value">{{ item.label }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="所属行政区" name="addvcd">
<a-input v-model:value="formData.addvcd" placeholder="请输入所属行政区" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="水电基地" name="baseId">
<a-select v-model:value="formData.baseId" placeholder="请选择水电基地" allow-clear show-search :filter-option="filterOption">
<a-select-option v-for="item in baseNameOptions" :key="item.value" :label="item.label" :value="item.value">{{ item.label }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="所属电站" name="rstcd">
<a-select v-model:value="formData.rstcd" placeholder="请选择所属电站" allow-clear show-search :loading="engLoading" :filter-option="filterOption">
<a-select-option v-for="item in engInfoOptions" :key="item.value" :label="item.label" :value="item.value">{{ item.label }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="监控点地址" name="stlc">
<a-input v-model:value="formData.stlc" placeholder="请输入监控点地址" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
<ConfirmModal v-model:open="confirmModalVisible" :diff-list="diffList" :confirm-loading="confirmLoading" @confirm="handleConfirmSubmit" @cancel="handleConfirmCancel" />
</template>
<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue';
import { message } from 'ant-design-vue';
import { addVdInfo, updateVdInfo } from '@/api/DataQueryMenuModule';
import { getEngInfoDropdown } from '@/api/select';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
import ConfirmModal from '@/components/ConfirmModal/index.vue';
import { useDraggable } from '@/utils/drag';
const props = defineProps<{ open: boolean; record?: any; isAdd?: boolean }>();
const emit = defineEmits(['update:open', 'success']);
const jidiSelectEventStore = useJidiSelectEventStore();
const visible = computed({ get: () => props.open, set: val => emit('update:open', val) });
useDraggable(visible, { boundary: true, resetOnOpen: true });
const formRef = ref();
const confirmLoading = ref(false);
const formData = ref<any>({});
const originalRecord = ref<any>({});
const originalLabels = ref<Record<string, string>>({});
const confirmModalVisible = ref(false);
const formRules = ref<any>({
stcd: [{ required: true, message: '请输入监控点编码', trigger: 'blur' }],
stnm: [{ required: true, message: '请输入监控点名', trigger: 'blur' }],
sttp: [{ required: true, message: '请选择监控点类型', trigger: 'change' }]
});
const engInfoOptions = ref<any[]>([]);
const sttpOptions = ref<any[]>([]);
const baseNameOptions = computed(() =>
jidiSelectEventStore.jidiData
.filter((item: any) => item.wbsCode !== 'all')
.map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
);
const engLoading = ref(false);
const fieldLabelMap: Record<string, string> = {
stcd: '监控点编码', stnm: '监控点名', sttp: '监控点类型',
addvcd: '所属行政区', baseId: '水电基地', rstcd: '所属电站', stlc: '监控点地址',
lgtd: '经度(°)',
lttd: '纬度(°)'
};
const selectOptionsMap: Record<string, () => any[]> = {
sttp: () => sttpOptions.value,
baseId: () => baseNameOptions.value,
rstcd: () => engInfoOptions.value
};
const changeOrder = ref<{ field: string; label: string; oldValue: string; newValue: string }[]>([]);
const diffList = changeOrder;
const resolveSelectLabel = (field: string, value: any): string => {
if (value === null || value === undefined || value === '') return '空';
const getOptions = selectOptionsMap[field];
if (getOptions) {
const opt = getOptions().find((o: any) => String(o.value) === String(value));
if (opt) return opt.label;
}
return String(value);
};
// 解析原始值的显示名rstcd 直接用 record 自带的电站名称 ennm不依赖异步选项
const resolveOriginalLabel = (field: string): string => {
const original = originalRecord.value;
if (field === 'rstcd') {
const ennm = original.ennm || original.rstcdName;
if (ennm) return String(ennm);
}
return resolveSelectLabel(field, original[field]);
};
const filterOption = (inputValue: string, option: any) => {
const label = option.label || option.value;
return label.includes(inputValue || '');
};
watch(() => formData.value, newData => {
const original = originalRecord.value;
for (const key in newData) {
const oldVal = original[key];
const newVal = newData[key];
if (oldVal !== newVal) {
const existingIdx = changeOrder.value.findIndex(c => c.field === key);
const oldNorm = oldVal === null || oldVal === undefined || oldVal === '-' ? null : oldVal;
const newNorm = newVal === null || newVal === undefined ? null : newVal;
if (oldNorm === newNorm) continue;
if (oldNorm !== null && newNorm !== null && !isNaN(Number(oldNorm)) && !isNaN(Number(newNorm)) && Number(oldNorm) === Number(newNorm)) continue;
const isSelect = !!selectOptionsMap[key];
const oldDisplay = isSelect ? (originalLabels.value[key] ?? resolveSelectLabel(key, oldNorm)) : oldNorm === null ? '空' : String(oldNorm);
const newDisplay = isSelect ? resolveSelectLabel(key, newNorm) : newNorm === null ? '空' : String(newNorm);
const entry = { field: key, label: fieldLabelMap[key] || key, oldValue: oldDisplay, newValue: newDisplay };
if (existingIdx >= 0) changeOrder.value[existingIdx] = entry;
else changeOrder.value.push(entry);
} else {
changeOrder.value = changeOrder.value.filter(c => c.field !== key);
}
}
}, { deep: true });
// 监听 record 变化
const isInitLoading = ref(false);
watch(() => props.record, newRecord => {
if (newRecord) {
isInitLoading.value = true;
const converted = { ...newRecord };
// 将 "-" 还原为 null避免 date-picker 显示 Invalid Date
for (const key in converted) {
if (converted[key] === '-') {
converted[key] = null;
}
}
originalRecord.value = { ...converted };
formData.value = { ...converted };
changeOrder.value = [];
// 编辑时必定加载电站选项,保证 rstcd 回显与 diff 解析
loadEngOptions(converted.baseId || undefined);
// 提前解析 select 字段的原始显示名,防止联动刷新选项后丢失 label
const labels: Record<string, string> = {};
for (const key in converted) {
if (selectOptionsMap[key]) {
labels[key] = resolveOriginalLabel(key);
}
}
originalLabels.value = labels;
// 表单填充完成后自动复位标志,避免残留 true 导致后续切换流域不清空电站
nextTick(() => {
isInitLoading.value = false;
});
}
}, { deep: true });
// 选项加载完成后重新解析原始显示名,避免 record 加载时选项未就绪导致存成 code
watch(
[baseNameOptions, engInfoOptions, sttpOptions],
() => {
const original = originalRecord.value;
if (!original || Object.keys(original).length === 0) return;
const labels: Record<string, string> = {};
for (const key in original) {
if (selectOptionsMap[key]) {
labels[key] = resolveOriginalLabel(key);
}
}
originalLabels.value = labels;
}
);
// 加载电站下拉(支持按 baseId 筛选)
const loadEngOptions = async (baseId?: string) => {
engLoading.value = true;
try {
const params = baseId ? { baseId } : {};
const res = await getEngInfoDropdown(params);
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
} catch (error) {
console.error('获取电站列表失败:', error);
} finally {
engLoading.value = false;
}
};
const loadDropdownData = () => {
sttpGetKendoList({
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['VD', 'VD_DW', 'VD_EQ', 'VD_EQS', 'VD_FB', 'VD_FBFM', 'VD_FBI', 'VD_FBP', 'VD_FC', 'VD_FH', 'VD_FP', 'VD_FPB', 'VD_FPC', 'VD_GZFC', 'VD_OTTE', 'VD_OTWE', 'VD_PR', 'VD_SG', 'VD_TE', 'VD_VA', 'VD_VP', 'VD_WE', 'VD_WQ', 'VD_WT'] }] }
}).then(res => {
sttpOptions.value = (res.data?.data || []).map((item: any) => ({ label: item.sttpName, value: item.sttpCode }));
});
// 从 store 获取水电基地数据computed 已自动响应,无需手动赋值)
};
// 水电基地变化时联动电站
watch(() => formData.value?.baseId, (newBaseId) => {
// record 加载时已在 record watch 中加载过电站选项,这里仅跳过清空
if (isInitLoading.value) {
return;
}
formData.value.rstcd = undefined;
loadEngOptions(newBaseId || undefined);
});
watch(() => props.open, val => {
if (val) {
if (props.isAdd) {
formData.value = {};
originalRecord.value = {};
changeOrder.value = [];
loadEngOptions();
}
loadDropdownData();
}
});
const handleOk = async () => {
try {
await formRef.value?.validateFields();
if (!props.isAdd && changeOrder.value.length === 0) { message.info('未检测到任何修改'); return; }
confirmModalVisible.value = true;
} catch (error) { console.error('验证失败:', error); }
};
const handleConfirmSubmit = async (source: string) => {
confirmLoading.value = true;
try {
const engInfo: Record<string, any> = {};
let res: any;
if (props.isAdd) {
Object.assign(engInfo, formData.value);
res = await addVdInfo({ engInfo, source: source.trim() || '新增' });
} else {
engInfo.stcd = formData.value.stcd;
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
res = await updateVdInfo({ engInfo, source: source.trim() || '编辑' });
}
if (res?.code == 0 || res?.success) {
message.success(props.isAdd ? '新增成功' : '编辑成功');
confirmModalVisible.value = false;
visible.value = false;
emit('success');
} else { message.error(res?.msg || (props.isAdd ? '新增失败' : '编辑失败')); }
} catch (error) { message.error('提交失败,请重试'); }
finally { confirmLoading.value = false; }
};
const handleConfirmCancel = () => { confirmModalVisible.value = false; };
const handleCancel = () => { visible.value = false; confirmModalVisible.value = false; formRef.value?.resetFields(); };
</script>