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

537 lines
16 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="stlc">
<a-input
v-model:value="formData.stlc"
placeholder="请输入站址"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="经度(°)" name="lgtd">
<a-input-number v-model:value="formData.lgtd" placeholder="请输入" :precision="6" style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="纬度(°)" name="lttd">
<a-input-number v-model:value="formData.lttd" placeholder="请输入" :precision="6" 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="jcdt">
<a-date-picker
v-model:value="formData.jcdt"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择建成日期"
style="width: 100%"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="类别" name="dtinType">
<a-select
v-model:value="formData.dtinType"
placeholder="请选择类别"
allow-clear
>
<a-select-option :value="0">自建</a-select-option>
<a-select-option :value="1">国家建</a-select-option>
<a-select-option :value="2">人工建</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="水质要求" name="wwqtg">
<a-select
v-model:value="formData.wwqtg"
placeholder="请选择水质要求"
allow-clear
show-search
:filter-option="filterOption"
>
<a-select-option
v-for="item in wwqtgOptions"
: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="mway">
<a-select
v-model:value="formData.mway"
placeholder="请选择监测方式"
allow-clear
>
<a-select-option :value="1">人工</a-select-option>
<a-select-option :value="2">自动</a-select-option>
</a-select>
</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 { addWqInfo, updateWqInfo } from '@/api/DataQueryMenuModule';
import { getEngInfoDropdown } from '@/api/select';
import { getDictItemsByCode } from '@/api/dict';
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 wwqtgOptions = 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: '测站类型',
stlc: '站址',
baseId: '水电基地',
hbrvcd: '所属流域',
rstcd: '所属电站',
jcdt: '建成日期',
dtinType: '类别',
wwqtg: '水质要求',
mway: '监测方式',
lgtd: '经度(°)',
lttd: '纬度(°)'
};
// select 选项映射
const selectOptionsMap: Record<string, () => any[]> = {
sttp: () => sttpOptions.value,
mway: () => [
{ label: '人工', value: 1 },
{ label: '自动', value: 2 }
],
dtinType: () => [
{ label: '自建', value: 0 },
{ label: '国家建', value: 1 },
{ label: '人工建', value: 2 }
],
wwqtg: () => wwqtgOptions.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;
const keyword = inputValue || '';
return label.includes(keyword);
};
// 监听 formData 变化
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, wwqtgOptions],
() => {
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;
}
);
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: ['WQ', 'WQFB', 'WQFP', 'WQH']
}
]
}
}).then(res => {
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
label: item.sttpName,
value: item.sttpCode
}));
});
// 加载水质要求
getDictItemsByCode({ dictCode: 'WWQTG' }).then(res => {
if (res.data?.length > 0) {
wwqtgOptions.value = res.data.map((item: any) => ({
value: item.itemCode,
label: item.dictName
}));
}
});
// 从 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) {
loadDropdownData();
if (props.isAdd) {
formData.value = {};
originalRecord.value = {};
changeOrder.value = [];
loadEngOptions();
}
}
}
);
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 addWqInfo({
engInfo,
source: source.trim() || '新增'
});
} else {
engInfo.stcd = formData.value.stcd;
changeOrder.value.forEach(item => {
engInfo[item.field] = formData.value[item.field];
});
res = await updateWqInfo({
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>