数据管理-初版
This commit is contained in:
commit
1fb16a1385
@ -308,3 +308,723 @@ export function deleteFlowStationInfo(data) {
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水温监测 - 分页列表
|
||||
export function getWtPageList(data) {
|
||||
return request({
|
||||
url: '/base/wt/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水温监测 - 新增
|
||||
export function addWtInfo(data) {
|
||||
return request({
|
||||
url: '/base/wt/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水温监测 - 编辑
|
||||
export function updateWtInfo(data) {
|
||||
return request({
|
||||
url: '/base/wt/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水温监测 - 删除
|
||||
export function deleteWtInfo(data) {
|
||||
return request({
|
||||
url: '/base/wt/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水质监测 - 分页列表
|
||||
export function getWqPageList(data) {
|
||||
return request({
|
||||
url: '/base/wq/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水质监测 - 新增
|
||||
export function addWqInfo(data) {
|
||||
return request({
|
||||
url: '/base/wq/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水质监测 - 编辑
|
||||
export function updateWqInfo(data) {
|
||||
return request({
|
||||
url: '/base/wq/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水质监测 - 删除
|
||||
export function deleteWqInfo(data) {
|
||||
return request({
|
||||
url: '/base/wq/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 栖息地 - 分页列表
|
||||
export function getFhPageList(data) {
|
||||
return request({
|
||||
url: '/base/fhbt/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 栖息地 - 新增
|
||||
export function addFhInfo(data) {
|
||||
return request({
|
||||
url: '/base/fhbt/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 栖息地 - 编辑
|
||||
export function updateFhInfo(data) {
|
||||
return request({
|
||||
url: '/base/fhbt/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 栖息地 - 删除
|
||||
export function deleteFhInfo(data) {
|
||||
return request({
|
||||
url: '/base/fhbt/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类增殖站 - 分页列表
|
||||
export function getFbPageList(data) {
|
||||
return request({
|
||||
url: '/base/fbrd/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类增殖站 - 新增
|
||||
export function addFbInfo(data) {
|
||||
return request({
|
||||
url: '/base/fbrd/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类增殖站 - 编辑
|
||||
export function updateFbInfo(data) {
|
||||
return request({
|
||||
url: '/base/fbrd/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类增殖站 - 删除
|
||||
export function deleteFbInfo(data) {
|
||||
return request({
|
||||
url: '/base/fbrd/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 珍稀植物园 - 分页列表
|
||||
export function getVpPageList(data) {
|
||||
return request({
|
||||
url: '/base/vp/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 珍稀植物园 - 新增
|
||||
export function addVpInfo(data) {
|
||||
return request({
|
||||
url: '/base/vp/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 珍稀植物园 - 编辑
|
||||
export function updateVpInfo(data) {
|
||||
return request({
|
||||
url: '/base/vp/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 珍稀植物园 - 删除
|
||||
export function deleteVpInfo(data) {
|
||||
return request({
|
||||
url: '/base/vp/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 视频监控 - 分页列表
|
||||
export function getVdPageList(data) {
|
||||
return request({
|
||||
url: '/base/vdinfo/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 视频监控 - 新增
|
||||
export function addVdInfo(data) {
|
||||
return request({
|
||||
url: '/base/vdinfo/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 视频监控 - 编辑
|
||||
export function updateVdInfo(data) {
|
||||
return request({
|
||||
url: '/base/vdinfo/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 视频监控 - 删除
|
||||
export function deleteVdInfo(data) {
|
||||
return request({
|
||||
url: '/base/vdinfo/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 生态流量 - 分页列表
|
||||
export function getEqPageList(data) {
|
||||
return request({
|
||||
url: '/base/eq/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 生态流量 - 新增
|
||||
export function addEqInfo(data) {
|
||||
return request({
|
||||
url: '/base/eq/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 生态流量 - 编辑
|
||||
export function updateEqInfo(data) {
|
||||
return request({
|
||||
url: '/base/eq/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 生态流量 - 删除
|
||||
export function deleteEqInfo(data) {
|
||||
return request({
|
||||
url: '/base/eq/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施 - 分页列表
|
||||
export function getFpPageList(data) {
|
||||
return request({
|
||||
url: '/env/fpss/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施 - 新增
|
||||
export function addFpInfo(data) {
|
||||
return request({
|
||||
url: '/env/fpss/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施 - 编辑
|
||||
export function updateFpInfo(data) {
|
||||
return request({
|
||||
url: '/env/fpss/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 过鱼设施 - 删除
|
||||
export function deleteFpInfo(data) {
|
||||
return request({
|
||||
url: '/env/fpss/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 陆生生态 - 分页列表
|
||||
export function getTePageList(data) {
|
||||
return request({
|
||||
url: '/base/te/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 陆生生态 - 新增
|
||||
export function addTeInfo(data) {
|
||||
return request({
|
||||
url: '/base/te/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 陆生生态 - 编辑
|
||||
export function updateTeInfo(data) {
|
||||
return request({
|
||||
url: '/base/te/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 陆生生态 - 删除
|
||||
export function deleteTeInfo(data) {
|
||||
return request({
|
||||
url: '/base/te/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态 - 分页列表
|
||||
export function getWePageList(data) {
|
||||
return request({
|
||||
url: '/base/we/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态 - 新增
|
||||
export function addWeInfo(data) {
|
||||
return request({
|
||||
url: '/base/we/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态 - 编辑
|
||||
export function updateWeInfo(data) {
|
||||
return request({
|
||||
url: '/base/we/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 水生生态 - 删除
|
||||
export function deleteWeInfo(data) {
|
||||
return request({
|
||||
url: '/base/we/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类调查 - 分页列表
|
||||
export function getAiPageList(data) {
|
||||
return request({
|
||||
url: '/base/aimonitor/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类调查 - 新增
|
||||
export function addAiInfo(data) {
|
||||
return request({
|
||||
url: '/base/aimonitor/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类调查 - 编辑
|
||||
export function updateAiInfo(data) {
|
||||
return request({
|
||||
url: '/base/aimonitor/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 鱼类调查 - 删除
|
||||
export function deleteAiInfo(data) {
|
||||
return request({
|
||||
url: '/base/aimonitor/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 智能告警 - 分页列表
|
||||
export function getWarnPageList(data) {
|
||||
return request({
|
||||
url: '/base/warnRule/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 智能告警 - 新增
|
||||
export function addWarnInfo(data) {
|
||||
return request({
|
||||
url: '/base/warnRule/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 智能告警 - 编辑
|
||||
export function updateWarnInfo(data) {
|
||||
return request({
|
||||
url: '/base/warnRule/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 智能告警 - 删除
|
||||
export function deleteWarnInfo(data) {
|
||||
return request({
|
||||
url: '/base/warnRule/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI边缘计算盒子 - 分页列表
|
||||
export function getAiboxPageList(data) {
|
||||
return request({
|
||||
url: '/base/aibox/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI边缘计算盒子 - 新增
|
||||
export function addAiboxInfo(data) {
|
||||
return request({
|
||||
url: '/base/aibox/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI边缘计算盒子 - 编辑
|
||||
export function updateAiboxInfo(data) {
|
||||
return request({
|
||||
url: '/base/aibox/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// AI边缘计算盒子 - 删除
|
||||
export function deleteAiboxInfo(data) {
|
||||
return request({
|
||||
url: '/base/aibox/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 人工产卵场 - 分页列表
|
||||
export function getSgPageList(data) {
|
||||
return request({
|
||||
url: '/base/artsg/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 人工产卵场 - 新增
|
||||
export function addSgInfo(data) {
|
||||
return request({
|
||||
url: '/base/artsg/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 人工产卵场 - 编辑
|
||||
export function updateSgInfo(data) {
|
||||
return request({
|
||||
url: '/base/artsg/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 人工产卵场 - 删除
|
||||
export function deleteSgInfo(data) {
|
||||
return request({
|
||||
url: '/base/artsg/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 低温水减缓设施 - 分页列表
|
||||
export function getDwPageList(data) {
|
||||
return request({
|
||||
url: '/base/dw/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 低温水减缓设施 - 新增
|
||||
export function addDwInfo(data) {
|
||||
return request({
|
||||
url: '/base/dw/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 低温水减缓设施 - 编辑
|
||||
export function updateDwInfo(data) {
|
||||
return request({
|
||||
url: '/base/dw/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 低温水减缓设施 - 删除
|
||||
export function deleteDwInfo(data) {
|
||||
return request({
|
||||
url: '/base/dw/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 倾斜摄影 - 分页列表
|
||||
export function getOpPageList(data) {
|
||||
return request({
|
||||
url: '/base/opinfo/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 倾斜摄影 - 新增
|
||||
export function addOpInfo(data) {
|
||||
return request({
|
||||
url: '/base/opinfo/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 倾斜摄影 - 编辑
|
||||
export function updateOpInfo(data) {
|
||||
return request({
|
||||
url: '/base/opinfo/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 倾斜摄影 - 删除
|
||||
export function deleteOpInfo(data) {
|
||||
return request({
|
||||
url: '/base/opinfo/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他陆生生态保护 - 分页列表
|
||||
export function getOttePageList(data) {
|
||||
return request({
|
||||
url: '/base/otte/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他陆生生态保护 - 新增
|
||||
export function addOtteInfo(data) {
|
||||
return request({
|
||||
url: '/base/otte/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他陆生生态保护 - 编辑
|
||||
export function updateOtteInfo(data) {
|
||||
return request({
|
||||
url: '/base/otte/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他陆生生态保护 - 删除
|
||||
export function deleteOtteInfo(data) {
|
||||
return request({
|
||||
url: '/base/otte/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他水生生态保护 - 分页列表
|
||||
export function getOtwePageList(data) {
|
||||
return request({
|
||||
url: '/base/otwe/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他水生生态保护 - 新增
|
||||
export function addOtweInfo(data) {
|
||||
return request({
|
||||
url: '/base/otwe/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他水生生态保护 - 编辑
|
||||
export function updateOtweInfo(data) {
|
||||
return request({
|
||||
url: '/base/otwe/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 其他水生生态保护 - 删除
|
||||
export function deleteOtweInfo(data) {
|
||||
return request({
|
||||
url: '/base/otwe/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 声呐及水下摄像头 - 分页列表
|
||||
export function getSonarPageList(data) {
|
||||
return request({
|
||||
url: '/base/sonar/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 声呐及水下摄像头 - 新增
|
||||
export function addSonarInfo(data) {
|
||||
return request({
|
||||
url: '/base/sonar/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 声呐及水下摄像头 - 编辑
|
||||
export function updateSonarInfo(data) {
|
||||
return request({
|
||||
url: '/base/sonar/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 声呐及水下摄像头 - 删除
|
||||
export function deleteSonarInfo(data) {
|
||||
return request({
|
||||
url: '/base/sonar/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 动物救助站 - 分页列表
|
||||
export function getVaPageList(data) {
|
||||
return request({
|
||||
url: '/base/va/page',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 动物救助站 - 新增
|
||||
export function addVaInfo(data) {
|
||||
return request({
|
||||
url: '/base/va/add',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 动物救助站 - 编辑
|
||||
export function updateVaInfo(data) {
|
||||
return request({
|
||||
url: '/base/va/update',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 动物救助站 - 删除
|
||||
export function deleteVaInfo(data) {
|
||||
return request({
|
||||
url: '/base/va/delete',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
75
frontend-sjgl/src/components/ConfirmModal/index.vue
Normal file
75
frontend-sjgl/src/components/ConfirmModal/index.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="title"
|
||||
width="800px"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="handleConfirm"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<div style="max-height: 50vh; overflow-y: auto; margin-bottom: 16px">
|
||||
<a-descriptions bordered :column="1" size="small">
|
||||
<a-descriptions-item
|
||||
v-for="item in diffList"
|
||||
:key="item.field"
|
||||
:label="item.label"
|
||||
>
|
||||
<span style="color: #ff4d4f; text-decoration: line-through">{{
|
||||
item.oldValue
|
||||
}}</span>
|
||||
<span style="margin: 0 8px">→</span>
|
||||
<span style="color: #52c41a">{{ item.newValue }}</span>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
<a-form-item label="修改依据">
|
||||
<a-textarea
|
||||
v-model:value="sourceValue"
|
||||
placeholder="请输入修改依据"
|
||||
:rows="5"
|
||||
:maxlength="500"
|
||||
show-count
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
open: boolean;
|
||||
confirmLoading?: boolean;
|
||||
diffList?: { field: string; label: string; oldValue: string; newValue: string }[];
|
||||
title?: string;
|
||||
}>(),
|
||||
{
|
||||
confirmLoading: false,
|
||||
diffList: () => [],
|
||||
title: '确认修改'
|
||||
}
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:open', val: boolean): void;
|
||||
(e: 'confirm', sourceValue: string): void;
|
||||
(e: 'cancel'): void;
|
||||
}>();
|
||||
|
||||
const sourceValue = ref('');
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.open,
|
||||
set: val => emit('update:open', val)
|
||||
});
|
||||
|
||||
const handleConfirm = () => {
|
||||
emit('confirm', sourceValue.value);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
sourceValue.value = '';
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<ApprovalSearch
|
||||
@export-btn="exportBtn"
|
||||
@ -327,4 +327,10 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.body_one{
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1071,40 +1071,13 @@
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
<!-- 确认修改弹框 -->
|
||||
<a-modal
|
||||
<ConfirmModal
|
||||
v-model:open="confirmModalVisible"
|
||||
title="确认修改"
|
||||
width="800px"
|
||||
:diff-list="diffList"
|
||||
:confirm-loading="confirmLoading"
|
||||
@ok="handleConfirmSubmit"
|
||||
@confirm="handleConfirmSubmit"
|
||||
@cancel="handleConfirmCancel"
|
||||
>
|
||||
<div style="max-height: 50vh; overflow-y: auto; margin-bottom: 16px">
|
||||
<a-descriptions bordered :column="1" size="small">
|
||||
<a-descriptions-item
|
||||
v-for="item in diffList"
|
||||
:key="item.field"
|
||||
:label="item.label"
|
||||
>
|
||||
<span style="color: #ff4d4f; text-decoration: line-through">{{
|
||||
item.oldValue
|
||||
}}</span>
|
||||
<span style="margin: 0 8px">→</span>
|
||||
<span style="color: #52c41a">{{ item.newValue }}</span>
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</div>
|
||||
<a-form-item label="修改依据">
|
||||
<a-textarea
|
||||
v-model:value="sourceValue"
|
||||
placeholder="请输入修改依据"
|
||||
:rows="5"
|
||||
:maxlength="500"
|
||||
show-count
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-modal>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -1114,6 +1087,7 @@ import { updatePowerInfo } from '@/api/DataQueryMenuModule';
|
||||
import { BasicColumns } from '@/components/MapModal/column.config';
|
||||
import { getRvcdDropdown, getAddvcdDropdown } from '@/api/select';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import ConfirmModal from '@/components/ConfirmModal/index.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean;
|
||||
@ -1152,7 +1126,6 @@ const formRules = ref<any>({
|
||||
|
||||
// 确认弹框状态
|
||||
const confirmModalVisible = ref(false);
|
||||
const sourceValue = ref('');
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {};
|
||||
@ -1345,14 +1318,13 @@ const handleOk = async () => {
|
||||
}
|
||||
|
||||
// 显示确认弹框
|
||||
sourceValue.value = '';
|
||||
confirmModalVisible.value = true;
|
||||
} catch (error) {
|
||||
console.error('验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmSubmit = async () => {
|
||||
const handleConfirmSubmit = async (source: string) => {
|
||||
confirmLoading.value = true;
|
||||
try {
|
||||
// 只传修改过的字段 + stcd
|
||||
@ -1364,7 +1336,7 @@ const handleConfirmSubmit = async () => {
|
||||
});
|
||||
const res = await updatePowerInfo({
|
||||
engInfo,
|
||||
source: sourceValue.value.trim()
|
||||
source: source.trim()
|
||||
});
|
||||
if (res?.code == 0 || res?.success) {
|
||||
message.success('编辑成功');
|
||||
@ -1383,13 +1355,11 @@ const handleConfirmSubmit = async () => {
|
||||
|
||||
const handleConfirmCancel = () => {
|
||||
confirmModalVisible.value = false;
|
||||
sourceValue.value = '';
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
confirmModalVisible.value = false;
|
||||
sourceValue.value = '';
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<BasicDataSearch
|
||||
:topHynmList="topHynmList"
|
||||
@ -1054,4 +1054,10 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.body_one{
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<FishResourceSearch
|
||||
:fish_ptype="fish_ptype_list"
|
||||
@ -510,7 +510,7 @@ const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rvcd && values.rvcd !== 'all'
|
||||
values.rvcd && values.rvcd !== 'all'
|
||||
? {
|
||||
field: 'rvcd',
|
||||
operator: 'contains',
|
||||
@ -640,4 +640,9 @@ onBeforeUnmount(() => {
|
||||
height: 306px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '站名',
|
||||
placeholder: '请输入站名',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,503 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="isAdd ? '新增AI边缘计算盒子' : '编辑AI边缘计算盒子'"
|
||||
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-input
|
||||
v-model:value="formData.sttp"
|
||||
placeholder="请输入站类"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</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="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
|
||||
: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="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-col :span="12">
|
||||
<a-form-item label="是否启用" name="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="服务对象" name="fwdx">
|
||||
<a-input
|
||||
v-model:value="formData.fwdx"
|
||||
placeholder="请输入服务对象"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="供电方式" name="gdfs">
|
||||
<a-input
|
||||
v-model:value="formData.gdfs"
|
||||
placeholder="请输入供电方式"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="AI盒子配置" name="aipz">
|
||||
<a-input
|
||||
v-model:value="formData.aipz"
|
||||
placeholder="请输入AI盒子配置"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="模型版本" name="mxbb">
|
||||
<a-input
|
||||
v-model:value="formData.mxbb"
|
||||
placeholder="请输入模型版本"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="IP地址" name="ipaddr">
|
||||
<a-input
|
||||
v-model:value="formData.ipaddr"
|
||||
placeholder="请输入IP地址"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="SIM卡信息" name="siminfo">
|
||||
<a-input
|
||||
v-model:value="formData.siminfo"
|
||||
placeholder="请输入SIM卡信息"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="联系人" name="lxr">
|
||||
<a-input
|
||||
v-model:value="formData.lxr"
|
||||
placeholder="请输入联系人及联系电话"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="用途" name="purpose">
|
||||
<a-input
|
||||
v-model:value="formData.purpose"
|
||||
placeholder="请输入用途"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addAiboxInfo, updateAiboxInfo } from '@/api/DataQueryMenuModule';
|
||||
import { getEngInfoDropdown } from '@/api/select';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import ConfirmModal from '@/components/ConfirmModal/index.vue';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入站码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入站名', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '站码',
|
||||
stnm: '站名',
|
||||
sttp: '站类',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
mway: '监测方式',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
dtfrqcy: '监测频次',
|
||||
fwdx: '服务对象',
|
||||
gdfs: '供电方式',
|
||||
aipz: 'AI盒子配置',
|
||||
mxbb: '模型版本',
|
||||
ipaddr: 'IP地址',
|
||||
siminfo: 'SIM卡信息',
|
||||
lxr: '联系人',
|
||||
purpose: '用途',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
mway: () => [
|
||||
{ label: '人工', value: 1 },
|
||||
{ label: '自动', value: 2 }
|
||||
],
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addAiboxInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateAiboxInfo({
|
||||
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>
|
||||
286
frontend-sjgl/src/views/conventionalHydropower/aiBox/index.vue
Normal file
286
frontend-sjgl/src/views/conventionalHydropower/aiBox/index.vue
Normal file
@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<AiBoxSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getAiboxPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditAiBoxModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteAiboxFn"
|
||||
:label="'AI边缘计算盒子'"
|
||||
title="删除AI边缘计算盒子"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import AiBoxSearch from './AiBoxSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditAiBoxModal from './EditAiBoxModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getAiboxPageList, deleteAiboxInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '站名',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '站码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '站类',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'mwayName',
|
||||
title: '监测方式',
|
||||
dataIndex: 'mwayName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'ipaddr',
|
||||
title: 'IP地址',
|
||||
dataIndex: 'ipaddr',
|
||||
visible: true,
|
||||
width: 140,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'purpose',
|
||||
title: '用途',
|
||||
dataIndex: 'purpose',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteAiboxFn = async (record: any, reason: string) => {
|
||||
return deleteAiboxInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,484 @@
|
||||
<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="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
|
||||
: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="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="inv">
|
||||
<a-input-number
|
||||
v-model:value="formData.inv"
|
||||
placeholder="请输入投资"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="基本特性参数" name="jbtxcs">
|
||||
<a-input
|
||||
v-model:value="formData.jbtxcs"
|
||||
placeholder="请输入基本特性参数"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所属建设阶段" name="blprd">
|
||||
<a-select
|
||||
v-model:value="formData.blprd"
|
||||
placeholder="请选择建设阶段"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1001000">规划</a-select-option>
|
||||
<a-select-option :value="1002000">预可研</a-select-option>
|
||||
<a-select-option :value="1003000">环评</a-select-option>
|
||||
<a-select-option :value="1004000">可研</a-select-option>
|
||||
<a-select-option :value="1005000">在建</a-select-option>
|
||||
<a-select-option :value="1009000">建设</a-select-option>
|
||||
<a-select-option :value="1010000">蓄水阶段环保验收</a-select-option>
|
||||
<a-select-option :value="1011000">竣工</a-select-option>
|
||||
<a-select-option :value="1012000">竣工环保验收</a-select-option>
|
||||
<a-select-option :value="1013000">环境影响后评价</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addOtweInfo, updateOtweInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入工程编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入工程名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '工程编码',
|
||||
stnm: '工程名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '位置',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
inv: '投资',
|
||||
dtfrqcy: '监测频次',
|
||||
jbtxcs: '基本特性参数',
|
||||
blprd: '所属建设阶段',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
blprd: () => [
|
||||
{ label: '规划', value: 1001000 },
|
||||
{ label: '预可研', value: 1002000 },
|
||||
{ label: '环评', value: 1003000 },
|
||||
{ label: '可研', value: 1004000 },
|
||||
{ label: '在建', value: 1005000 },
|
||||
{ label: '建设', value: 1009000 },
|
||||
{ label: '蓄水阶段环保验收', value: 1010000 },
|
||||
{ label: '竣工', value: 1011000 },
|
||||
{ label: '竣工环保验收', value: 1012000 },
|
||||
{ label: '环境影响后评价', value: 1013000 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || option.value;
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['OTWE']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addOtweInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateOtweInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '工程名称',
|
||||
placeholder: '请输入工程名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["OTWE"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
274
frontend-sjgl/src/views/conventionalHydropower/aquaEco/index.vue
Normal file
274
frontend-sjgl/src/views/conventionalHydropower/aquaEco/index.vue
Normal file
@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<OtweSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getOtwePageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditOtweModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteOtweFn"
|
||||
:label="'其他水生生态保护工程'"
|
||||
title="删除其他水生生态保护工程"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import OtweSearch from './OtweSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditOtweModal from './EditOtweModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getOtwePageList, deleteOtweInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '工程名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '工程编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '位置',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'inv',
|
||||
title: '投资(亿元)',
|
||||
dataIndex: 'inv',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
const deleteOtweFn = async (record: any, reason: string) => {
|
||||
return deleteOtweInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,535 @@
|
||||
<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="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
|
||||
: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="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-col :span="12">
|
||||
<a-form-item label="是否启用" name="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="服务对象" name="fwdx">
|
||||
<a-input
|
||||
v-model:value="formData.fwdx"
|
||||
placeholder="请输入服务对象"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="供电方式" name="gdfs">
|
||||
<a-input
|
||||
v-model:value="formData.gdfs"
|
||||
placeholder="请输入供电方式"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="IP地址" name="ipaddr">
|
||||
<a-input
|
||||
v-model:value="formData.ipaddr"
|
||||
placeholder="请输入IP地址"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="用途" name="purpose">
|
||||
<a-input
|
||||
v-model:value="formData.purpose"
|
||||
placeholder="请输入用途"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="AI盒子配置" name="aipz">
|
||||
<a-input
|
||||
v-model:value="formData.aipz"
|
||||
placeholder="请输入AI盒子配置"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="模型版本" name="mxbb">
|
||||
<a-input
|
||||
v-model:value="formData.mxbb"
|
||||
placeholder="请输入模型版本"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="SIM卡信息" name="siminfo">
|
||||
<a-input
|
||||
v-model:value="formData.siminfo"
|
||||
placeholder="请输入SIM卡信息"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="观察方式" name="observe">
|
||||
<a-input
|
||||
v-model:value="formData.observe"
|
||||
placeholder="请输入观察方式"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addAiInfo, updateAiInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入站码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入站名', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '站码',
|
||||
stnm: '站名',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
mway: '监测方式',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
dtfrqcy: '监测频次',
|
||||
fwdx: '服务对象',
|
||||
gdfs: '供电方式',
|
||||
ipaddr: 'IP地址',
|
||||
purpose: '用途',
|
||||
aipz: 'AI盒子配置',
|
||||
mxbb: '模型版本',
|
||||
siminfo: 'SIM卡信息',
|
||||
observe: '观察方式',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
mway: () => [
|
||||
{ label: '人工', value: 1 },
|
||||
{ label: '自动', value: 2 }
|
||||
],
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
// 加载测站类型
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ["AI_5014", "WVA", "AI_5005", "FPRD", "AI_5012", "AI_5001", "AI_5006", "AI_5004", "AI_5007", "AI_5002", "AI_5008", "AI_5003", "AI_5010"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
// 从 store 获取水电基地数据
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addAiInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateAiInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '站名',
|
||||
placeholder: '请输入站名',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["AI_5014", "WVA", "AI_5005", "FPRD", "AI_5012", "AI_5001", "AI_5006", "AI_5004", "AI_5007", "AI_5002", "AI_5008", "AI_5003", "AI_5010"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<FishSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getAiPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditFishModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteAiFn"
|
||||
:label="'鱼类调查'"
|
||||
title="删除鱼类调查"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import FishSearch from './FishSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditFishModal from './EditFishModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getAiPageList, deleteAiInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '站名',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '站码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'mwayName',
|
||||
title: '监测方式',
|
||||
dataIndex: 'mwayName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'ipaddr',
|
||||
title: 'IP地址',
|
||||
dataIndex: 'ipaddr',
|
||||
visible: true,
|
||||
width: 140,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'purpose',
|
||||
title: '用途',
|
||||
dataIndex: 'purpose',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteAiFn = async (record: any, reason: string) => {
|
||||
return deleteAiInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<a-modal v-model:open="visible" :title="isAdd ? '新增过鱼设施' : '编辑过鱼设施'" width="80vw" :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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<a-form-item label="所属电站" name="rstcd">
|
||||
<a-select v-model:value="formData.rstcd" placeholder="请选择所属电站" allow-clear show-search :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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<a-form-item label="高程(m)" name="elev">
|
||||
<a-input-number v-model:value="formData.elev" placeholder="请输入" :precision="6" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="开工日期" name="swdt">
|
||||
<a-date-picker v-model:value="formData.swdt" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开工日期" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<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="8">
|
||||
<a-form-item label="建设状态" name="bldsttCode">
|
||||
<a-select v-model:value="formData.bldsttCode" 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="8">
|
||||
<a-form-item label="投资(亿元)" name="inv">
|
||||
<a-input-number v-model:value="formData.inv" placeholder="请输入" :precision="4" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="上游码头位置" name="symtlc">
|
||||
<a-input v-model:value="formData.symtlc" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="上游码头型式" name="symtxs">
|
||||
<a-input v-model:value="formData.symtxs" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="下游码头位置" name="xymtlc">
|
||||
<a-input v-model:value="formData.xymtlc" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="下游码头型式" name="xymtxs">
|
||||
<a-input v-model:value="formData.xymtxs" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="集诱鱼方式" name="jyfs">
|
||||
<a-input v-model:value="formData.jyfs" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="运鱼设施方式" name="yyfs">
|
||||
<a-input v-model:value="formData.yyfs" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="过鱼规模(尾)" name="sjgycnt">
|
||||
<a-input v-model:value="formData.sjgycnt" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="过鱼月份" name="fpssmn">
|
||||
<a-input v-model:value="formData.fpssmn" placeholder="多个月份用,隔开" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number v-model:value="formData.dtfrqcy" placeholder="请输入" :precision="0" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addFpInfo, updateFpInfo } 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';
|
||||
|
||||
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) });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入设施编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入设施名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '设施编码', stnm: '设施名称', sttp: '设施类型', stlc: '站址/位置',
|
||||
baseId: '水电基地', rstcd: '所属电站', lgtd: '经度(°)', lttd: '纬度(°)',
|
||||
elev: '高程(m)', swdt: '开工日期', jcdt: '建成日期', bldsttCode: '建设状态',
|
||||
inv: '投资(亿元)', symtlc: '上游码头位置', symtxs: '上游码头型式',
|
||||
xymtlc: '下游码头位置', xymtxs: '下游码头型式', jyfs: '集诱鱼方式',
|
||||
yyfs: '运鱼设施方式', sjgycnt: '过鱼规模(尾)', fpssmn: '过鱼月份',
|
||||
dtfrqcy: '监测频次(min)', mway: '监测方式'
|
||||
};
|
||||
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
baseId: () => baseNameOptions.value,
|
||||
rstcd: () => engInfoOptions.value,
|
||||
bldsttCode: () => [
|
||||
{ label: '规划', value: 0 },
|
||||
{ label: '在建', value: 1 },
|
||||
{ label: '已建', value: 2 }
|
||||
],
|
||||
mway: () => [
|
||||
{ label: '人工', value: 1 },
|
||||
{ label: '自动', value: 2 }
|
||||
]
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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 ? 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 });
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['FP', 'FP_1', 'FP_2', 'FP_3', 'FP_4', 'FP_5'] }] }
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => { if (val) { loadDropdownData(); if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; } } });
|
||||
|
||||
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 addFpInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
res = await updateFpInfo({ 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>
|
||||
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<BasicSearch ref="basicSearchRef" :searchList="searchList" :initial-values="initSearchData" :zhujianfujian="'fu'" @reset="handleReset" @finish="onSearchFinish">
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search-finish', values: any): void; (e: 'reset', values: any): void; (e: 'add'): void }>();
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = { baseId: 'all', rstcd: null, stnm: null, sttp: null };
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{ type: 'jidiData', name: 'baseId', label: '水电基地', placeholder: '请输入水电基地名称', fieldProps: { allowClear: true }, options: [] },
|
||||
{ type: 'Input', name: 'stnm', label: '设施名称', placeholder: '请输入设施名称', width: 160, fieldProps: { allowClear: true } },
|
||||
{ col: isHide.value ? 0 : null, type: 'Select', name: 'sttp', label: '设施类型', width: 160, fieldProps: { allowClear: true, placeholder: '请选择设施类型' }, options: sttpOption.value }
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
const res = await sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['FP', 'FP_1', 'FP_2', 'FP_3', 'FP_4', 'FP_5'] }] }
|
||||
});
|
||||
sttpOption.value = res.data.data.map(item => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => emit('search-finish', { ...values });
|
||||
const hideBtn = () => { isHide.value = !isHide.value; };
|
||||
const handleReset = () => emit('reset', initSearchData);
|
||||
const handleAdd = () => emit('add');
|
||||
|
||||
onMounted(() => { getBaseList(); emit('search-finish', { ...initSearchData }); });
|
||||
defineExpose({ basicSearchRef });
|
||||
</script>
|
||||
@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<FpSearch @search-finish="onSearchFinish" @reset="onReset" @add="handleAdd" ref="searchRef" />
|
||||
|
||||
<BasicTable ref="tableRef" :enableEllipsis="true" :scrollX="tableScrollX" :scrollY="tableScrollY" :columns="columns" :list-url="getFpPageList">
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditFpModal v-model:open="editVisible" :record="editRecord" :is-add="isAdd" @success="handleEditSuccess" />
|
||||
|
||||
<DeleteConfirmModal ref="deleteModalRef" :delete-fn="deleteFpFn" :label="'过鱼设施'" title="删除过鱼设施" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import FpSearch from './FpSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditFpModal from './EditFpModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getFpPageList, deleteFpInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const bldsttMap: Record<number, string> = { 0: '规划', 1: '在建', 2: '已建' };
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '设施名称', dataIndex: 'stnm', visible: true, width: 200, ellipsis: true },
|
||||
{ key: 'stcd', title: '设施编码', dataIndex: 'stcd', visible: true, width: 180, fixed: 'left', ellipsis: true },
|
||||
|
||||
{ key: 'sttpName', title: '设施类型', dataIndex: 'sttpName', visible: true, width: 100, ellipsis: true },
|
||||
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 80, ellipsis: true },
|
||||
|
||||
{ key: 'bldsttCode', title: '建设状态', dataIndex: 'bldsttCode', visible: true, width: 80, customRender: ({ text }) => (text !== null && text !== undefined ? (bldsttMap[text] || text) : '-') },
|
||||
{ key: 'sjgycnt', title: '过鱼规模(尾)', dataIndex: 'sjgycnt', visible: true, width: 120 },
|
||||
{ key: 'inv', title: '投资(亿元)', dataIndex: 'inv', visible: true, width: 120 },
|
||||
{ key: 'mwayName', title: '监测方式', dataIndex: 'mwayName', visible: true, width: 100, ellipsis: true },
|
||||
{ key: 'swdt', title: '开工日期', dataIndex: 'swdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'jcdt', title: '建成日期', dataIndex: 'jcdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'stlc', title: '站址/位置', dataIndex: 'stlc', visible: true, ellipsis: true },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 }
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() => Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600));
|
||||
|
||||
const buildSearchParams = (values: any) => ({
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null,
|
||||
values.baseId !== 'all' && values.baseId != null ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null,
|
||||
values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null,
|
||||
values.sttp ? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp } : null
|
||||
].filter(Boolean)
|
||||
});
|
||||
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { tableRef.value.getList(buildSearchParams(values)); };
|
||||
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
|
||||
const deleteFpFn = async (record: any, reason: string) => deleteFpInfo({ ids: [record.stcd], source: reason });
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => initTable(currentSearchParams.value)); };
|
||||
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -0,0 +1,412 @@
|
||||
<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="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
|
||||
: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="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addTeInfo, updateTeInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入断面编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入断面名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '断面编码',
|
||||
stnm: '断面名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
jcdt: '建成日期',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
// 加载测站类型
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['TE', 'OTTE']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
// 从 store 获取水电基地数据
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addTeInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
// 编辑时只传 stcd + 修改过的字段
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateTeInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '断面名称',
|
||||
placeholder: '请输入断面名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["TE", "OTTE"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<TeSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getTePageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditTeModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteTeFn"
|
||||
:label="'陆生生态调查断面'"
|
||||
title="删除陆生生态调查断面"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import TeSearch from './TeSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditTeModal from './EditTeModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getTePageList, deleteTeInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '断面名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '断面编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 140,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteTeFn = async (record: any, reason: string) => {
|
||||
return deleteTeInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,464 @@
|
||||
<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="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
|
||||
: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="url">
|
||||
<a-input
|
||||
v-model:value="formData.url"
|
||||
placeholder="请输入在线地址"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="是否启用" name="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="blprd">
|
||||
<a-select
|
||||
v-model:value="formData.blprd"
|
||||
placeholder="请选择建设阶段"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1001000">规划</a-select-option>
|
||||
<a-select-option :value="1002000">预可研</a-select-option>
|
||||
<a-select-option :value="1003000">环评</a-select-option>
|
||||
<a-select-option :value="1004000">可研</a-select-option>
|
||||
<a-select-option :value="1005000">在建</a-select-option>
|
||||
<a-select-option :value="1009000">建设</a-select-option>
|
||||
<a-select-option :value="1010000">蓄水阶段环保验收</a-select-option>
|
||||
<a-select-option :value="1011000">竣工</a-select-option>
|
||||
<a-select-option :value="1012000">竣工环保验收</a-select-option>
|
||||
<a-select-option :value="1013000">环境影响后评价</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addOpInfo, updateOpInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入站点编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入站点名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '站点编码',
|
||||
stnm: '站点名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '位置',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
url: '在线地址',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
blprd: '所属建设阶段',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
blprd: () => [
|
||||
{ label: '规划', value: 1001000 },
|
||||
{ label: '预可研', value: 1002000 },
|
||||
{ label: '环评', value: 1003000 },
|
||||
{ label: '可研', value: 1004000 },
|
||||
{ label: '在建', value: 1005000 },
|
||||
{ label: '建设', value: 1009000 },
|
||||
{ label: '蓄水阶段环保验收', value: 1010000 },
|
||||
{ label: '竣工', value: 1011000 },
|
||||
{ label: '竣工环保验收', value: 1012000 },
|
||||
{ label: '环境影响后评价', value: 1013000 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || option.value;
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['OSGB']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addOpInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateOpInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '站点名称',
|
||||
placeholder: '请输入站点名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["OSGB"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<PhotoSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getOpPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditPhotoModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteOpFn"
|
||||
:label="'倾斜摄影站点'"
|
||||
title="删除倾斜摄影站点"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import PhotoSearch from './PhotoSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditPhotoModal from './EditPhotoModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getOpPageList, deleteOpInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '站点名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '站点编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '位置',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'url',
|
||||
title: '在线地址',
|
||||
dataIndex: 'url',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteOpFn = async (record: any, reason: string) => {
|
||||
return deleteOpInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="isAdd ? '新增栖息地' : '编辑栖息地'"
|
||||
width="80vw"
|
||||
: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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<a-form-item label="所属电站" name="rstcd">
|
||||
<a-select v-model:value="formData.rstcd" placeholder="请选择所属电站" allow-clear show-search :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="8">
|
||||
<a-form-item label="保护对象" name="bhdx">
|
||||
<a-input v-model:value="formData.bhdx" placeholder="请输入保护对象" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保护范围" name="bhfw">
|
||||
<a-input v-model:value="formData.bhfw" placeholder="请输入保护范围" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保护核心长度(km)" name="bhhxcd">
|
||||
<a-input-number v-model:value="formData.bhhxcd" placeholder="请输入" :precision="2" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保护河流" name="bhhl">
|
||||
<a-input v-model:value="formData.bhhl" placeholder="请输入保护河流" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保护河段" name="bhhd">
|
||||
<a-input v-model:value="formData.bhhd" placeholder="请输入保护河段" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保护措施" name="bhcs">
|
||||
<a-input v-model:value="formData.bhcs" placeholder="请输入保护措施" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="保护方式" name="bhfs">
|
||||
<a-input v-model:value="formData.bhfs" placeholder="请输入保护方式" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="投资(亿元)" name="inv">
|
||||
<a-input-number v-model:value="formData.inv" placeholder="请输入" :precision="3" 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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addFhInfo, updateFhInfo } from '@/api/DataQueryMenuModule';
|
||||
import { getEngInfoDropdown } from '@/api/select';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import ConfirmModal from '@/components/ConfirmModal/index.vue';
|
||||
|
||||
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) });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入栖息地站码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入栖息地名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '栖息地站码', stnm: '栖息地名称', stlc: '站址', baseId: '水电基地',
|
||||
rstcd: '所属电站', bhdx: '保护对象', bhfw: '保护范围',
|
||||
bhhxcd: '保护核心长度(km)',
|
||||
bhhl: '保护河流', bhhd: '保护河段', bhcs: '保护措施', bhfs: '保护方式', inv: '投资(亿元)'
|
||||
};
|
||||
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
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);
|
||||
};
|
||||
|
||||
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 ? 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 });
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
const loadDropdownData = () => {
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; }
|
||||
}
|
||||
});
|
||||
|
||||
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 addFhInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
res = await updateFhInfo({ 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>
|
||||
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: { allowClear: true },
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '栖息地名称',
|
||||
placeholder: '请输入栖息地名称',
|
||||
width: 160,
|
||||
fieldProps: { allowClear: true }
|
||||
}
|
||||
]);
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({ basicSearchRef });
|
||||
</script>
|
||||
109
frontend-sjgl/src/views/conventionalHydropower/qiXiDi/index.vue
Normal file
109
frontend-sjgl/src/views/conventionalHydropower/qiXiDi/index.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<FhSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getFhPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditFhModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteFhFn"
|
||||
:label="'栖息地'"
|
||||
title="删除栖息地"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import FhSearch from './FhSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditFhModal from './EditFhModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getFhPageList, deleteFhInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '栖息地名称', dataIndex: 'stnm', visible: true, width: 260, ellipsis: true },
|
||||
{ key: 'stcd', title: '栖息地站码', dataIndex: 'stcd', visible: true, width: 320, fixed: 'left', ellipsis: true },
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 150, ellipsis: true },
|
||||
{ key: 'bhdx', title: '保护对象', dataIndex: 'bhdx', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'bhhl', title: '保护河流', dataIndex: 'bhhl', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'bhhd', title: '保护河段', dataIndex: 'bhhd', visible: true, width: 240, ellipsis: true },
|
||||
{ key: 'bhfw', title: '保护范围', dataIndex: 'bhfw', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'bhhxcd', title: '保护核心长度(km)', dataIndex: 'bhhxcd', visible: true, width: 140 },
|
||||
|
||||
{ key: 'bhcs', title: '保护措施', dataIndex: 'bhcs', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'bhfs', title: '保护方式', dataIndex: 'bhfs', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'inv', title: '投资(亿元)', dataIndex: 'inv', visible: true, width: 120 },
|
||||
{ key: 'stlc', title: '站址', dataIndex: 'stlc', visible: true, ellipsis: true,width: 220 },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 },
|
||||
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => ({
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null,
|
||||
values.baseId !== 'all' && values.baseId != null ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null,
|
||||
values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null
|
||||
].filter(Boolean)
|
||||
});
|
||||
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { tableRef.value.getList(buildSearchParams(values)); };
|
||||
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
|
||||
const deleteFhFn = async (record: any, reason: string) => deleteFhInfo({ ids: [record.stcd], source: reason });
|
||||
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => initTable(currentSearchParams.value)); };
|
||||
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -0,0 +1,406 @@
|
||||
<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="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
|
||||
: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="place">
|
||||
<a-input
|
||||
v-model:value="formData.place"
|
||||
placeholder="请输入地点"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="面积(km²)" name="area">
|
||||
<a-input-number
|
||||
v-model:value="formData.area"
|
||||
placeholder="请输入面积"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保护方式" name="bhfs">
|
||||
<a-input
|
||||
v-model:value="formData.bhfs"
|
||||
placeholder="请输入保护方式"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保护原因" name="bhyy">
|
||||
<a-select
|
||||
v-model:value="formData.bhyy"
|
||||
placeholder="请选择保护原因"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">是</a-select-option>
|
||||
<a-select-option :value="0">否</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="保护对象" name="bhdx">
|
||||
<a-input
|
||||
v-model:value="formData.bhdx"
|
||||
placeholder="请输入保护对象"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="是否启用" name="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="inv">
|
||||
<a-input-number
|
||||
v-model:value="formData.inv"
|
||||
placeholder="请输入投资"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addVaInfo, updateVaInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入救助站编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入救助站名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '救助站编码',
|
||||
stnm: '救助站名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
place: '地点',
|
||||
area: '面积',
|
||||
bhfs: '保护方式',
|
||||
bhyy: '保护原因',
|
||||
bhdx: '保护对象',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
inv: '投资',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
bhyy: () => [
|
||||
{ label: '是', value: 1 },
|
||||
{ label: '否', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || option.value;
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
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 ? 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 }
|
||||
);
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['VA'] }]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => {
|
||||
if (val) { loadDropdownData(); if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; } }
|
||||
});
|
||||
|
||||
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 addVaInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
res = await updateVaInfo({ 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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '救助站名称',
|
||||
placeholder: '请输入救助站名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["VA"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<VaSearch @search-finish="onSearchFinish" @reset="onReset" @add="handleAdd" ref="searchRef" />
|
||||
<BasicTable ref="tableRef" :enableEllipsis="true" :scrollX="tableScrollX" :scrollY="tableScrollY" :columns="columns" :list-url="getVaPageList">
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<EditVaModal v-model:open="editVisible" :record="editRecord" :is-add="isAdd" @success="handleEditSuccess" />
|
||||
<DeleteConfirmModal ref="deleteModalRef" :delete-fn="deleteVaFn" :label="'动物救助站'" title="删除动物救助站" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import VaSearch from './VaSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditVaModal from './EditVaModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getVaPageList, deleteVaInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
const tableRef = ref(), searchRef = ref(), tableScrollY = ref<string | number>(0), currentSearchParams = ref<any>({});
|
||||
const editVisible = ref(false), editRecord = ref<any>(null), isAdd = ref(false), deleteModalRef = ref();
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '救助站名称', dataIndex: 'stnm', visible: true, width: 200, fixed: 'left', ellipsis: true },
|
||||
{ key: 'stcd', title: '救助站编码', dataIndex: 'stcd', visible: true, width: 160, ellipsis: true },
|
||||
{ key: 'sttpName', title: '测站类型', dataIndex: 'sttpName', visible: true, width: 100, ellipsis: true },
|
||||
{ key: 'stlc', title: '站址', dataIndex: 'stlc', visible: true, width: 160, ellipsis: true },
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 100, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 100, ellipsis: true },
|
||||
{ key: 'place', title: '地点', dataIndex: 'place', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'usflName', title: '是否启用', dataIndex: 'usflName', visible: true, width: 80, ellipsis: true },
|
||||
{ key: 'jcdt', title: '建成日期', dataIndex: 'jcdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'remark', title: '备注', dataIndex: 'remark', visible: true, ellipsis: true },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 }
|
||||
]);
|
||||
const tableScrollX = computed(() => Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600));
|
||||
const buildSearchParams = (values: any) => ({ logic: 'and', filters: [values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null, values.baseId !== 'all' ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null, values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null, values.sttp ? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp } : null].filter(Boolean) });
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { const params = buildSearchParams(values); tableRef.value.getList(params); };
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
const deleteVaFn = async (record: any, reason: string) => deleteVaInfo({ ids: [record.stcd], source: reason });
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => { initTable(currentSearchParams.value); }); };
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<a-modal v-model:open="visible" :title="isAdd ? '新增生态流量设施' : '编辑生态流量设施'" width="80vw" :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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<a-form-item label="所属电站" name="rstcd">
|
||||
<a-select v-model:value="formData.rstcd" placeholder="请选择所属电站" allow-clear show-search :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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<a-form-item label="高程(m)" name="elev">
|
||||
<a-input-number v-model:value="formData.elev" placeholder="请输入" :precision="6" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="开工日期" name="swdt">
|
||||
<a-date-picker v-model:value="formData.swdt" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开工日期" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<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="8">
|
||||
<a-form-item label="建设状态" name="bldsttCode">
|
||||
<a-select v-model:value="formData.bldsttCode" 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="8">
|
||||
<a-form-item label="投资(亿元)" name="inv">
|
||||
<a-input-number v-model:value="formData.inv" placeholder="请输入" :precision="4" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="放流孔结构形式" name="flkjg">
|
||||
<a-input v-model:value="formData.flkjg" placeholder="请输入" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="最大泄放流量(m³/s)" name="flkq">
|
||||
<a-input-number v-model:value="formData.flkq" placeholder="请输入" :precision="2" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="最小下泄生态流量(m³/s)" name="minFlow">
|
||||
<a-input-number v-model:value="formData.minFlow" placeholder="请输入" :precision="4" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="考核方式" name="assessMethod">
|
||||
<a-input v-model:value="formData.assessMethod" placeholder="请输入考核方式" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="备注" name="remark">
|
||||
<a-input v-model:value="formData.remark" 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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addEqInfo, updateEqInfo } 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';
|
||||
|
||||
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) });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入设施编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入设施名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '设施编码', stnm: '设施名称', sttp: '设施类型', stlc: '站址/位置',
|
||||
baseId: '水电基地', rstcd: '所属电站', lgtd: '经度(°)', lttd: '纬度(°)',
|
||||
elev: '高程(m)', swdt: '开工日期', jcdt: '建成日期', bldsttCode: '建设状态',
|
||||
inv: '投资(亿元)', flkjg: '放流孔结构形式', flkq: '最大泄放流量(m³/s)',
|
||||
minFlow: '最小下泄生态流量(m³/s)', assessMethod: '考核方式', remark: '备注'
|
||||
};
|
||||
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
baseId: () => baseNameOptions.value,
|
||||
rstcd: () => engInfoOptions.value,
|
||||
bldsttCode: () => [
|
||||
{ label: '规划', value: 0 },
|
||||
{ label: '在建', value: 1 },
|
||||
{ label: '已建', value: 2 }
|
||||
]
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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 ? 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 });
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['EQ', 'EQ_1', 'EQ_2', 'EQ_3', 'EQ_4', 'EQ_5', 'EQ_6'] }] }
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => { if (val) { loadDropdownData(); if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; } } });
|
||||
|
||||
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 addEqInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
res = await updateEqInfo({ 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>
|
||||
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<BasicSearch ref="basicSearchRef" :searchList="searchList" :initial-values="initSearchData" :zhujianfujian="'fu'" @reset="handleReset" @finish="onSearchFinish">
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search-finish', values: any): void; (e: 'reset', values: any): void; (e: 'add'): void }>();
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = { baseId: 'all', rstcd: null, stnm: null, sttp: null };
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{ type: 'jidiData', name: 'baseId', label: '水电基地', placeholder: '请输入水电基地名称', fieldProps: { allowClear: true }, options: [] },
|
||||
{ type: 'Input', name: 'stnm', label: '设施名称', placeholder: '请输入设施名称', width: 160, fieldProps: { allowClear: true } },
|
||||
{ col: isHide.value ? 0 : null, type: 'Select', name: 'sttp', label: '设施类型', width: 160, fieldProps: { allowClear: true, placeholder: '请选择设施类型' }, options: sttpOption.value }
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
const res = await sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['EQ', 'EQ_1', 'EQ_2', 'EQ_3', 'EQ_4', 'EQ_5', 'EQ_6'] }] }
|
||||
});
|
||||
sttpOption.value = res.data.data.map(item => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => emit('search-finish', { ...values });
|
||||
const hideBtn = () => { isHide.value = !isHide.value; };
|
||||
const handleReset = () => emit('reset', initSearchData);
|
||||
const handleAdd = () => emit('add');
|
||||
|
||||
onMounted(() => { getBaseList(); emit('search-finish', { ...initSearchData }); });
|
||||
defineExpose({ basicSearchRef });
|
||||
</script>
|
||||
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<EqSearch @search-finish="onSearchFinish" @reset="onReset" @add="handleAdd" ref="searchRef" />
|
||||
|
||||
<BasicTable ref="tableRef" :enableEllipsis="true" :scrollX="tableScrollX" :scrollY="tableScrollY" :columns="columns" :list-url="getEqPageList">
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditEqModal v-model:open="editVisible" :record="editRecord" :is-add="isAdd" @success="handleEditSuccess" />
|
||||
|
||||
<DeleteConfirmModal ref="deleteModalRef" :delete-fn="deleteEqFn" :label="'生态流量设施'" title="删除生态流量设施" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import EqSearch from './EqSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditEqModal from './EditEqModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getEqPageList, deleteEqInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const bldsttMap: Record<number, string> = { 0: '规划', 1: '在建', 2: '已建' };
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '设施名称', dataIndex: 'stnm', visible: true, width: 160, fixed: 'left',ellipsis: true },
|
||||
{ key: 'stcd', title: '设施编码', dataIndex: 'stcd', visible: true, width: 180, ellipsis: true },
|
||||
{ key: 'sttpName', title: '设施类型', dataIndex: 'sttpName', visible: true, width: 100, ellipsis: true },
|
||||
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 100, ellipsis: true },
|
||||
|
||||
{ key: 'bldsttCode', title: '建设状态', dataIndex: 'bldsttCode', visible: true, width: 80, customRender: ({ text }) => (text !== null && text !== undefined ? (bldsttMap[text] || text) : '-') },
|
||||
{ key: 'minFlow', title: '最小下泄流量(m³/s)', dataIndex: 'minFlow', visible: true, width: 150 },
|
||||
{ key: 'swdt', title: '开工日期', dataIndex: 'swdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'jcdt', title: '建成日期', dataIndex: 'jcdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'stlc', title: '站址/位置', dataIndex: 'stlc', visible: true, ellipsis: true },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 }
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() => Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600));
|
||||
|
||||
const buildSearchParams = (values: any) => ({
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null,
|
||||
values.baseId !== 'all' && values.baseId != null ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null,
|
||||
values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null,
|
||||
values.sttp ? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp } : null
|
||||
].filter(Boolean)
|
||||
});
|
||||
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { tableRef.value.getList(buildSearchParams(values)); };
|
||||
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
|
||||
const deleteEqFn = async (record: any, reason: string) => deleteEqInfo({ ids: [record.stcd], source: reason });
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => initTable(currentSearchParams.value)); };
|
||||
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -0,0 +1,194 @@
|
||||
<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 :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 } 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';
|
||||
|
||||
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) });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入监控点编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入监控点名', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '监控点编码', stnm: '监控点名', sttp: '监控点类型',
|
||||
addvcd: '所属行政区', baseId: '水电基地', rstcd: '所属电站', stlc: '监控点地址'
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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 ? 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 });
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
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 }));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => { if (val) { loadDropdownData(); if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; } } });
|
||||
|
||||
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>
|
||||
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<BasicSearch ref="basicSearchRef" :searchList="searchList" :initial-values="initSearchData" :zhujianfujian="'fu'" @reset="handleReset" @finish="onSearchFinish">
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search-finish', values: any): void; (e: 'reset', values: any): void; (e: 'add'): void }>();
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = { baseId: 'all', rstcd: null, stnm: null, sttp: null };
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{ type: 'jidiData', name: 'baseId', label: '水电基地', placeholder: '请输入水电基地名称', fieldProps: { allowClear: true }, options: [] },
|
||||
{ type: 'Input', name: 'stnm', label: '监控点名', placeholder: '请输入监控点名', width: 160, fieldProps: { allowClear: true } },
|
||||
{ col: isHide.value ? 0 : null, type: 'Select', name: 'sttp', label: '监控点类型', width: 160, fieldProps: { allowClear: true, placeholder: '请选择监控点类型' }, options: sttpOption.value }
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
const res = await 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'] }] }
|
||||
});
|
||||
sttpOption.value = res.data.data.map(item => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => emit('search-finish', { ...values });
|
||||
const hideBtn = () => { isHide.value = !isHide.value; };
|
||||
const handleReset = () => emit('reset', initSearchData);
|
||||
const handleAdd = () => emit('add');
|
||||
|
||||
onMounted(() => { getBaseList(); emit('search-finish', { ...initSearchData }); });
|
||||
defineExpose({ basicSearchRef });
|
||||
</script>
|
||||
@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<VdSearch @search-finish="onSearchFinish" @reset="onReset" @add="handleAdd" ref="searchRef" />
|
||||
|
||||
<BasicTable ref="tableRef" :enableEllipsis="true" :scrollX="tableScrollX" :scrollY="tableScrollY" :columns="columns" :list-url="getVdPageList">
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditVdModal v-model:open="editVisible" :record="editRecord" :is-add="isAdd" @success="handleEditSuccess" />
|
||||
|
||||
<DeleteConfirmModal ref="deleteModalRef" :delete-fn="deleteVdFn" :label="'视频监控'" title="删除视频监控点" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import VdSearch from './VdSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditVdModal from './EditVdModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getVdPageList, deleteVdInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '监控点名', dataIndex: 'stnm', visible: true, width: 200, ellipsis: true },
|
||||
{ key: 'stcd', title: '监控点编码', dataIndex: 'stcd', visible: true, width: 300, fixed: 'left', ellipsis: true },
|
||||
{ key: 'sttpName', title: '监控点类型', dataIndex: 'sttpName', visible: true, width: 140, ellipsis: true },
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 100, ellipsis: true },
|
||||
{ key: 'addvcdName', title: '所属行政区', dataIndex: 'addvcdName', visible: true, width: 120, ellipsis: true },
|
||||
|
||||
{ key: 'stlc', title: '监控点地址', dataIndex: 'stlc', visible: true, ellipsis: true },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 }
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() => Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600));
|
||||
|
||||
const buildSearchParams = (values: any) => ({
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null,
|
||||
values.baseId !== 'all' && values.baseId != null ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null,
|
||||
values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null,
|
||||
values.sttp ? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp } : null
|
||||
].filter(Boolean)
|
||||
});
|
||||
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { tableRef.value.getList(buildSearchParams(values)); };
|
||||
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
|
||||
const deleteVdFn = async (record: any, reason: string) => deleteVdInfo({ ids: [record.stcd], source: reason });
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => initTable(currentSearchParams.value)); };
|
||||
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -0,0 +1,412 @@
|
||||
<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="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
|
||||
: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="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addWeInfo, updateWeInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入断面编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入断面名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '断面编码',
|
||||
stnm: '断面名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
jcdt: '建成日期',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
// 加载测站类型
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['WE', 'OTWE']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
// 从 store 获取水电基地数据
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addWeInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
// 编辑时只传 stcd + 修改过的字段
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateWeInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '断面名称',
|
||||
placeholder: '请输入断面名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["WE", "OTWE"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<WeSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getWePageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditWeModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteWeFn"
|
||||
:label="'水生生态调查断面'"
|
||||
title="删除水生生态调查断面"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import WeSearch from './WeSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditWeModal from './EditWeModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getWePageList, deleteWeInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '断面名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '断面编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 280,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 240,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteWeFn = async (record: any, reason: string) => {
|
||||
return deleteWeInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,410 @@
|
||||
<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="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
|
||||
: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="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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addWtInfo, updateWtInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入水温站站码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入测站名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '水温站站码',
|
||||
stnm: '测站名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
jcdt: '建成日期',
|
||||
mway: '监测方式',
|
||||
rstcd: '所属电站'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
mway: () => [
|
||||
{ label: '人工', value: 1 },
|
||||
{ label: '自动', value: 2 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
// 加载测站类型
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['WT', 'WTRV', 'WTVT']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
// 从 store 获取水电基地数据
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addWtInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
// 编辑时只传 stcd + 修改过的字段
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateWtInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '测站名称',
|
||||
placeholder: '请输入测站名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["WT", "WTRV", "WTVT"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<WtSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getWtPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditWtModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteWtFn"
|
||||
:label="'水温测站'"
|
||||
title="删除水温测站"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import WtSearch from './WtSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditWtModal from './EditWtModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getWtPageList, deleteWtInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '测站名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 240,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '水温站站码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width:80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'mwayName',
|
||||
title: '监测方式',
|
||||
dataIndex: 'mwayName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
// debugger
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteWtFn = async (record: any, reason: string) => {
|
||||
return deleteWtInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,457 @@
|
||||
<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="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
|
||||
: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 } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入水质站站码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入测站名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
const wwqtgOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '水质站站码',
|
||||
stnm: '测站名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
hbrvcd: '所属流域',
|
||||
rstcd: '所属电站',
|
||||
jcdt: '建成日期',
|
||||
dtinType: '类别',
|
||||
wwqtg: '水质要求',
|
||||
mway: '监测方式'
|
||||
};
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
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
|
||||
}));
|
||||
}
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([])
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '测站名称',
|
||||
placeholder: '请输入测站名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options:sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter":
|
||||
{
|
||||
"logic": "and",
|
||||
"filters":
|
||||
[
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["WQ", "WQFB", "WQFP", "WQH"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList()
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<WqSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getWqPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditWqModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteWqFn"
|
||||
:label="'水质测站'"
|
||||
title="删除水质测站"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import WqSearch from './WqSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditWqModal from './EditWqModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getWqPageList, deleteWqInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '测站名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '水质站站码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 150,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 120,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 120,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 150,
|
||||
ellipsis: true
|
||||
},
|
||||
|
||||
{
|
||||
key: 'dtinTypeName',
|
||||
title: '类别',
|
||||
dataIndex: 'dtinTypeName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'wwqtgName',
|
||||
title: '水质要求',
|
||||
dataIndex: 'wwqtgName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'mwayName',
|
||||
title: '监测方式',
|
||||
dataIndex: 'mwayName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd }
|
||||
: null,
|
||||
values.baseId !== 'all' && values.baseId != null
|
||||
? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId }
|
||||
: null,
|
||||
values.stnm
|
||||
? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm }
|
||||
: null,
|
||||
values.sttp
|
||||
? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp }
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
const deleteWqFn = async (record: any, reason: string) => {
|
||||
return deleteWqInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,476 @@
|
||||
<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="mntp">
|
||||
<a-input
|
||||
v-model:value="formData.mntp"
|
||||
placeholder="请输入监控类型"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</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="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
|
||||
: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="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-col :span="12">
|
||||
<a-form-item label="是否启用" name="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="服务对象" name="fwdx">
|
||||
<a-input
|
||||
v-model:value="formData.fwdx"
|
||||
placeholder="请输入服务对象"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="观察方式" name="gcfs">
|
||||
<a-input
|
||||
v-model:value="formData.gcfs"
|
||||
placeholder="请输入观察方式"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addSonarInfo, updateSonarInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入站码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入站名', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '站码',
|
||||
stnm: '站名',
|
||||
sttp: '监控类别',
|
||||
mntp: '监控类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
mway: '监测方式',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
dtfrqcy: '监测频次',
|
||||
fwdx: '服务对象',
|
||||
gcfs: '观察方式',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
mway: () => [
|
||||
{ label: '人工', value: 1 },
|
||||
{ label: '自动', value: 2 }
|
||||
],
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || option.value;
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ["VD_SN", "VD_WVD"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addSonarInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateSonarInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '站名',
|
||||
placeholder: '请输入站名',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '监控类别',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择监控类别'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["VD_SN", "VD_WVD"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<SonarSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getSonarPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditSonarModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteSonarFn"
|
||||
:label="'声呐及水下摄像头'"
|
||||
title="删除声呐及水下摄像头"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import SonarSearch from './SonarSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditSonarModal from './EditSonarModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getSonarPageList, deleteSonarInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '站名',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '站码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '监控类别',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'mwayName',
|
||||
title: '监测方式',
|
||||
dataIndex: 'mwayName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
const deleteSonarFn = async (record: any, reason: string) => {
|
||||
return deleteSonarInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,504 @@
|
||||
<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="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
|
||||
: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="prtcts">
|
||||
<a-input
|
||||
v-model:value="formData.prtcts"
|
||||
placeholder="请输入保护对象"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="产卵场数量(个)" name="cnt">
|
||||
<a-input-number
|
||||
v-model:value="formData.cnt"
|
||||
placeholder="请输入数量"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="面积(km²)" name="ar">
|
||||
<a-input-number
|
||||
v-model:value="formData.ar"
|
||||
placeholder="请输入面积"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="营造型式" name="tp">
|
||||
<a-input
|
||||
v-model:value="formData.tp"
|
||||
placeholder="请输入营造型式"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="实施时间" name="attm">
|
||||
<a-date-picker
|
||||
v-model:value="formData.attm"
|
||||
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="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="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="投资(亿元)" name="inv">
|
||||
<a-input-number
|
||||
v-model:value="formData.inv"
|
||||
placeholder="请输入投资"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addSgInfo, updateSgInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入产卵场编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入产卵场名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '产卵场编码',
|
||||
stnm: '产卵场名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
prtcts: '保护对象',
|
||||
cnt: '产卵场数量',
|
||||
ar: '面积',
|
||||
tp: '营造型式',
|
||||
attm: '实施时间',
|
||||
jcdt: '建成日期',
|
||||
usfl: '是否启用',
|
||||
inv: '投资',
|
||||
dtfrqcy: '监测频次',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
// 加载测站类型
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['SG']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addSgInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateSgInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '产卵场名称',
|
||||
placeholder: '请输入产卵场名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["SG"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<SpawnSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getSgPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditSpawnModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteSgFn"
|
||||
:label="'人工产卵场'"
|
||||
title="删除人工产卵场"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import SpawnSearch from './SpawnSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditSpawnModal from './EditSpawnModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getSgPageList, deleteSgInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '产卵场名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '产卵场编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'prtcts',
|
||||
title: '保护对象',
|
||||
dataIndex: 'prtcts',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'cnt',
|
||||
title: '数量(个)',
|
||||
dataIndex: 'cnt',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ar',
|
||||
title: '面积(km²)',
|
||||
dataIndex: 'ar',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteSgFn = async (record: any, reason: string) => {
|
||||
return deleteSgInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '设施名称',
|
||||
placeholder: '请输入设施名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '设施类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择设施类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["DW", "DW_1", "DW_2", "DW_3", "DW_4", "DW_5", "DW_6", "DW_9"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,476 @@
|
||||
<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="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
|
||||
: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="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="inv">
|
||||
<a-input-number
|
||||
v-model:value="formData.inv"
|
||||
placeholder="请输入投资"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="是否有调度规程" name="dispatch">
|
||||
<a-select
|
||||
v-model:value="formData.dispatch"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">有</a-select-option>
|
||||
<a-select-option :value="0">无</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="调度规程描述" name="dispatchDes">
|
||||
<a-input
|
||||
v-model:value="formData.dispatchDes"
|
||||
placeholder="请输入调度规程描述"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addDwInfo, updateDwInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入设施编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入设施名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '设施编码',
|
||||
stnm: '设施名称',
|
||||
sttp: '设施类型',
|
||||
stlc: '站址',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
inv: '总投资',
|
||||
dtfrqcy: '监测频次',
|
||||
dispatch: '是否有调度规程',
|
||||
dispatchDes: '调度规程描述',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
dispatch: () => [
|
||||
{ label: '有', value: 1 },
|
||||
{ label: '无', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
// 加载设施类型
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ["DW", "DW_1", "DW_2", "DW_3", "DW_4", "DW_5", "DW_6", "DW_9"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addDwInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateDwInfo({
|
||||
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>
|
||||
279
frontend-sjgl/src/views/conventionalHydropower/tempMit/index.vue
Normal file
279
frontend-sjgl/src/views/conventionalHydropower/tempMit/index.vue
Normal file
@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<DwSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getDwPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditDwModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteDwFn"
|
||||
:label="'低温水减缓设施'"
|
||||
title="删除低温水减缓设施"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import DwSearch from './DwSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditDwModal from './EditDwModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getDwPageList, deleteDwInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '设施名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '设施编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '设施类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '站址',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'inv',
|
||||
title: '投资(亿元)',
|
||||
dataIndex: 'inv',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteDwFn = async (record: any, reason: string) => {
|
||||
return deleteDwInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,487 @@
|
||||
<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="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
|
||||
: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="usfl">
|
||||
<a-select
|
||||
v-model:value="formData.usfl"
|
||||
placeholder="请选择是否启用"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">启用</a-select-option>
|
||||
<a-select-option :value="0">禁用</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="inv">
|
||||
<a-input-number
|
||||
v-model:value="formData.inv"
|
||||
placeholder="请输入投资"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
:precision="4"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="监测频次(min)" name="dtfrqcy">
|
||||
<a-input-number
|
||||
v-model:value="formData.dtfrqcy"
|
||||
placeholder="请输入监测频次"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="基本特性参数" name="jbtxcs">
|
||||
<a-input
|
||||
v-model:value="formData.jbtxcs"
|
||||
placeholder="请输入基本特性参数"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所属建设阶段" name="blprd">
|
||||
<a-select
|
||||
v-model:value="formData.blprd"
|
||||
placeholder="请选择建设阶段"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1001000">规划</a-select-option>
|
||||
<a-select-option :value="1002000">预可研</a-select-option>
|
||||
<a-select-option :value="1003000">环评</a-select-option>
|
||||
<a-select-option :value="1004000">可研</a-select-option>
|
||||
<a-select-option :value="1005000">在建</a-select-option>
|
||||
<a-select-option :value="1009000">建设</a-select-option>
|
||||
<a-select-option :value="1010000">蓄水阶段环保验收</a-select-option>
|
||||
<a-select-option :value="1011000">竣工</a-select-option>
|
||||
<a-select-option :value="1012000">竣工环保验收</a-select-option>
|
||||
<a-select-option :value="1013000">环境影响后评价</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="简介" name="introduce" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.introduce"
|
||||
placeholder="请输入简介"
|
||||
:rows="2"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="remark" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.remark"
|
||||
placeholder="请输入备注"
|
||||
:rows="2"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addOtteInfo, updateOtteInfo } 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';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入工程编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入工程名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '工程编码',
|
||||
stnm: '工程名称',
|
||||
sttp: '测站类型',
|
||||
stlc: '位置',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
usfl: '是否启用',
|
||||
jcdt: '建成日期',
|
||||
inv: '投资',
|
||||
dtfrqcy: '监测频次',
|
||||
jbtxcs: '基本特性参数',
|
||||
blprd: '所属建设阶段',
|
||||
introduce: '简介',
|
||||
remark: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
sttp: () => sttpOptions.value,
|
||||
usfl: () => [
|
||||
{ label: '启用', value: 1 },
|
||||
{ label: '禁用', value: 0 }
|
||||
],
|
||||
blprd: () => [
|
||||
{ label: '规划', value: 1001000 },
|
||||
{ label: '预可研', value: 1002000 },
|
||||
{ label: '环评', value: 1003000 },
|
||||
{ label: '可研', value: 1004000 },
|
||||
{ label: '在建', value: 1005000 },
|
||||
{ label: '建设', value: 1009000 },
|
||||
{ label: '蓄水阶段环保验收', value: 1010000 },
|
||||
{ label: '竣工', value: 1011000 },
|
||||
{ label: '竣工环保验收', value: 1012000 },
|
||||
{ label: '环境影响后评价', value: 1013000 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
const filterOption = (inputValue: string, option: any) => {
|
||||
const label = option.label || option.value;
|
||||
const keyword = inputValue || '';
|
||||
return label.includes(keyword);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 }
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{
|
||||
field: 'sttpCode',
|
||||
operator: 'in',
|
||||
dataType: 'string',
|
||||
value: ['OTTE']
|
||||
}
|
||||
]
|
||||
}
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addOtteInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateOtteInfo({
|
||||
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>
|
||||
@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '工程名称',
|
||||
placeholder: '请输入工程名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true,
|
||||
placeholder: '请选择测站类型'
|
||||
},
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
let params = {
|
||||
"filter": {
|
||||
"logic": "and",
|
||||
"filters": [
|
||||
{
|
||||
"field": "sttpCode",
|
||||
"operator": "in",
|
||||
"dataType": "string",
|
||||
"value": ["OTTE"]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const res = await sttpGetKendoList(params);
|
||||
sttpOption.value = res.data.data.map(item => {
|
||||
return {
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
279
frontend-sjgl/src/views/conventionalHydropower/terrEco/index.vue
Normal file
279
frontend-sjgl/src/views/conventionalHydropower/terrEco/index.vue
Normal file
@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<OtteSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getOttePageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditOtteModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteOtteFn"
|
||||
:label="'其他陆生生态保护工程'"
|
||||
title="删除其他陆生生态保护工程"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import OtteSearch from './OtteSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditOtteModal from './EditOtteModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getOttePageList, deleteOtteInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'stnm',
|
||||
title: '工程名称',
|
||||
dataIndex: 'stnm',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '工程编码',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'sttpName',
|
||||
title: '测站类型',
|
||||
dataIndex: 'sttpName',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stlc',
|
||||
title: '位置',
|
||||
dataIndex: 'stlc',
|
||||
visible: true,
|
||||
width: 200,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'usflName',
|
||||
title: '是否启用',
|
||||
dataIndex: 'usflName',
|
||||
visible: true,
|
||||
width: 80,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'jcdt',
|
||||
title: '建成日期',
|
||||
dataIndex: 'jcdt',
|
||||
visible: true,
|
||||
width: 110,
|
||||
customRender: ({ text }) => {
|
||||
if (!text || text === '-') return '-';
|
||||
const date = dayjs(text);
|
||||
return date.isValid() ? date.format('YYYY-MM-DD') : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'inv',
|
||||
title: '投资(亿元)',
|
||||
dataIndex: 'inv',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'remark',
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.stnm
|
||||
? {
|
||||
field: 'stnm',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.stnm
|
||||
}
|
||||
: null,
|
||||
values.sttp
|
||||
? {
|
||||
field: 'sttp',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.sttp
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const deleteOtteFn = async (record: any, reason: string) => {
|
||||
return deleteOtteInfo({
|
||||
ids: [record.stcd],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,392 @@
|
||||
<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="ruleName">
|
||||
<a-input
|
||||
v-model:value="formData.ruleName"
|
||||
placeholder="请输入预警规则名称"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="预警规则类型" name="ruleType">
|
||||
<a-select
|
||||
v-model:value="formData.ruleType"
|
||||
placeholder="请选择预警规则类型"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option value="WQLVL">水质预警</a-select-option>
|
||||
<a-select-option value="EQMN">电站生态流量(环保部)</a-select-option>
|
||||
<a-select-option value="EQMNMWR">电站生态流量(水利部)</a-select-option>
|
||||
<a-select-option value="RSVRFSR">水位预警</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="规则编码" name="ruleCode">
|
||||
<a-select
|
||||
v-model:value="formData.ruleCode"
|
||||
placeholder="请选择规则编码"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option value="global">全局规则</a-select-option>
|
||||
<a-select-option value="common">通用告警规则</a-select-option>
|
||||
<a-select-option value="custom">自定义告警规则</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="所属测站" name="stcd">
|
||||
<a-input
|
||||
v-model:value="formData.stcd"
|
||||
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
|
||||
: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="isShow">
|
||||
<a-select
|
||||
v-model:value="formData.isShow"
|
||||
placeholder="请选择"
|
||||
allow-clear
|
||||
>
|
||||
<a-select-option :value="1">展示</a-select-option>
|
||||
<a-select-option :value="0">不展示</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="备注" name="description" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-textarea
|
||||
v-model:value="formData.description"
|
||||
placeholder="请输入备注"
|
||||
:rows="3"
|
||||
/>
|
||||
</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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addWarnInfo, updateWarnInfo } from '@/api/DataQueryMenuModule';
|
||||
import { getEngInfoDropdown } from '@/api/select';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import ConfirmModal from '@/components/ConfirmModal/index.vue';
|
||||
|
||||
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)
|
||||
});
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
ruleName: [{ required: true, message: '请输入预警规则名称', trigger: 'blur' }],
|
||||
ruleType: [{ required: true, message: '请选择预警规则类型', trigger: 'change' }]
|
||||
});
|
||||
|
||||
// 下拉选项
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}))
|
||||
);
|
||||
|
||||
// 字段名 → 中文名映射
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
ruleName: '预警规则名称',
|
||||
ruleType: '预警规则类型',
|
||||
ruleCode: '规则编码',
|
||||
stcd: '所属测站',
|
||||
baseId: '水电基地',
|
||||
rstcd: '所属电站',
|
||||
isShow: '是否展示告警等级',
|
||||
description: '备注'
|
||||
};
|
||||
|
||||
// select 选项映射
|
||||
const selectOptionsMap: Record<string, () => any[]> = {
|
||||
ruleType: () => [
|
||||
{ label: '水质预警', value: 'WQLVL' },
|
||||
{ label: '电站生态流量(环保部)', value: 'EQMN' },
|
||||
{ label: '电站生态流量(水利部)', value: 'EQMNMWR' },
|
||||
{ label: '水位预警', value: 'RSVRFSR' }
|
||||
],
|
||||
ruleCode: () => [
|
||||
{ label: '全局规则', value: 'global' },
|
||||
{ label: '通用告警规则', value: 'common' },
|
||||
{ label: '自定义告警规则', value: 'custom' }
|
||||
],
|
||||
isShow: () => [
|
||||
{ label: '展示', value: 1 },
|
||||
{ label: '不展示', value: 0 }
|
||||
],
|
||||
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);
|
||||
};
|
||||
|
||||
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
|
||||
? 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 变化
|
||||
watch(
|
||||
() => props.record,
|
||||
newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 加载下拉数据
|
||||
const loadDropdownData = () => {
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({
|
||||
label: item.wbsName,
|
||||
value: item.wbsCode
|
||||
}));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({
|
||||
label: item.ennm,
|
||||
value: item.stcd
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.open,
|
||||
val => {
|
||||
if (val) {
|
||||
loadDropdownData();
|
||||
if (props.isAdd) {
|
||||
formData.value = {};
|
||||
originalRecord.value = {};
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
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 addWarnInfo({
|
||||
engInfo,
|
||||
source: source.trim() || '新增'
|
||||
});
|
||||
} else {
|
||||
engInfo.id = formData.value.id;
|
||||
changeOrder.value.forEach(item => {
|
||||
engInfo[item.field] = formData.value[item.field];
|
||||
});
|
||||
res = await updateWarnInfo({
|
||||
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>
|
||||
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
ruleName: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
},
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'ruleName',
|
||||
label: '规则名称',
|
||||
placeholder: '请输入规则名称',
|
||||
width: 160,
|
||||
fieldProps: {
|
||||
allowClear: true
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
emit('search-finish', { ...values });
|
||||
};
|
||||
|
||||
const hideBtn = () => {
|
||||
isHide.value = !isHide.value;
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
emit('reset', initSearchData);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
emit('add');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
basicSearchRef
|
||||
});
|
||||
</script>
|
||||
@ -0,0 +1,255 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<WarnSearch
|
||||
@search-finish="onSearchFinish"
|
||||
@reset="onReset"
|
||||
@add="handleAdd"
|
||||
ref="searchRef"
|
||||
/>
|
||||
|
||||
<!-- 表格组件 -->
|
||||
<BasicTable
|
||||
ref="tableRef"
|
||||
:enableEllipsis="true"
|
||||
:scrollX="tableScrollX"
|
||||
:scrollY="tableScrollY"
|
||||
:columns="columns"
|
||||
:list-url="getWarnPageList"
|
||||
>
|
||||
<template #action="{ record }">
|
||||
<a-button
|
||||
type="link"
|
||||
class="text-[#2f6b98]"
|
||||
size="small"
|
||||
@click="handleEdit(record)"
|
||||
>编辑</a-button
|
||||
>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)"
|
||||
>删除</a-button
|
||||
>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<!-- 编辑/新增 Modal -->
|
||||
<EditWarnModal
|
||||
v-model:open="editVisible"
|
||||
:record="editRecord"
|
||||
:is-add="isAdd"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 删除确认 Modal -->
|
||||
<DeleteConfirmModal
|
||||
ref="deleteModalRef"
|
||||
:delete-fn="deleteWarnFn"
|
||||
:label="'智能告警'"
|
||||
title="删除智能告警"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import WarnSearch from './WarnSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditWarnModal from './EditWarnModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getWarnPageList, deleteWarnInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
// 编辑相关
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{
|
||||
key: 'ruleName',
|
||||
title: '预警规则名称',
|
||||
dataIndex: 'ruleName',
|
||||
visible: true,
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ruleType',
|
||||
title: '预警规则类型',
|
||||
dataIndex: 'ruleType',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ruleCode',
|
||||
title: '规则编码',
|
||||
dataIndex: 'ruleCode',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'stcd',
|
||||
title: '所属测站',
|
||||
dataIndex: 'stcd',
|
||||
visible: true,
|
||||
width: 160,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'baseName',
|
||||
title: '水电基地',
|
||||
dataIndex: 'baseName',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'ennm',
|
||||
title: '所属电站',
|
||||
dataIndex: 'ennm',
|
||||
visible: true,
|
||||
width: 100,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'isShow',
|
||||
title: '展示告警等级',
|
||||
dataIndex: 'isShow',
|
||||
visible: true,
|
||||
width: 100,
|
||||
customRender: ({ text }) => {
|
||||
if (text === 1) return '展示';
|
||||
if (text === 0) return '不展示';
|
||||
return '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
title: '备注',
|
||||
dataIndex: 'description',
|
||||
visible: true,
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
key: 'action',
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(
|
||||
columns.value.reduce(
|
||||
(sum: number, col: any) => sum + (col.width || 180),
|
||||
0
|
||||
),
|
||||
600
|
||||
)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => {
|
||||
return {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd
|
||||
? {
|
||||
field: 'rstcd',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.rstcd
|
||||
}
|
||||
: null,
|
||||
values.baseId !== 'all'
|
||||
? {
|
||||
field: 'baseId',
|
||||
operator: 'eq',
|
||||
dataType: 'string',
|
||||
value: values.baseId
|
||||
}
|
||||
: null,
|
||||
values.ruleName
|
||||
? {
|
||||
field: 'ruleName',
|
||||
operator: 'contains',
|
||||
dataType: 'string',
|
||||
value: values.ruleName
|
||||
}
|
||||
: null
|
||||
].filter(Boolean)
|
||||
};
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const onReset = (values: any) => {
|
||||
currentSearchParams.value = values;
|
||||
initTable(values);
|
||||
};
|
||||
|
||||
const initTable = (values: any) => {
|
||||
const params = buildSearchParams(values);
|
||||
tableRef.value.getList(params);
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
isAdd.value = true;
|
||||
editRecord.value = null;
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record: any) => {
|
||||
isAdd.value = false;
|
||||
editRecord.value = { ...record };
|
||||
editVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑成功回调
|
||||
const handleEditSuccess = () => {
|
||||
initTable(currentSearchParams.value);
|
||||
};
|
||||
|
||||
// 删除(使用 id 作为主键)
|
||||
const deleteWarnFn = async (record: any, reason: string) => {
|
||||
return deleteWarnInfo({
|
||||
ids: [record.id],
|
||||
source: reason
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = (record: any) => {
|
||||
deleteModalRef.value?.open(record, () => {
|
||||
initTable(currentSearchParams.value);
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
tableScrollY.value = calcTableScrollY();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,265 @@
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="visible"
|
||||
:title="isAdd ? '新增鱼类增殖站' : '编辑鱼类增殖站'"
|
||||
width="80vw"
|
||||
: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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<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="8">
|
||||
<a-form-item label="所属电站" name="rstcd">
|
||||
<a-select v-model:value="formData.rstcd" placeholder="请选择所属电站" allow-clear show-search :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="8">
|
||||
<a-form-item label="开工日期" name="swdt">
|
||||
<a-date-picker v-model:value="formData.swdt" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择开工日期" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<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="8">
|
||||
<a-form-item label="放流对象" name="zzfldx">
|
||||
<a-input v-model:value="formData.zzfldx" placeholder="请输入放流对象" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="放流规模(尾)" name="zzflcnt">
|
||||
<a-input-number v-model:value="formData.zzflcnt" placeholder="请输入" :precision="0" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="放流地点" name="zzflfllc">
|
||||
<a-input v-model:value="formData.zzflfllc" placeholder="请输入放流地点" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="放流时间" name="zzfltm">
|
||||
<a-date-picker v-model:value="formData.zzfltm" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择放流时间" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="养殖模式" name="zzflyzms">
|
||||
<a-input v-model:value="formData.zzflyzms" placeholder="请输入养殖模式" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="生产工艺" name="zzflgy">
|
||||
<a-input v-model:value="formData.zzflgy" placeholder="请输入生产工艺" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="标记方式" name="zzflbjfs">
|
||||
<a-input v-model:value="formData.zzflbjfs" placeholder="请输入标记方式" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="总占地面积(km²)" name="zzflar">
|
||||
<a-input-number v-model:value="formData.zzflar" placeholder="请输入" :precision="2" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="投资(亿元)" name="inv">
|
||||
<a-input-number v-model:value="formData.inv" placeholder="请输入" :precision="6" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8">
|
||||
<a-form-item label="承担放流任务" name="zzflrw">
|
||||
<a-input v-model:value="formData.zzflrw" 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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addFbInfo, updateFbInfo } 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';
|
||||
|
||||
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) });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入增殖站编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入增殖站名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '增殖站编码', stnm: '增殖站名称', stlc: '地址', sttp: '测站类型',
|
||||
baseId: '水电基地', rstcd: '所属电站', swdt: '开工日期', jcdt: '建成日期',
|
||||
zzfldx: '放流对象', zzflcnt: '放流规模(尾)', zzflfllc: '放流地点',
|
||||
zzfltm: '放流时间', zzflyzms: '养殖模式', zzflgy: '生产工艺', zzflbjfs: '标记方式',
|
||||
zzflar: '总占地面积(km²)', inv: '投资(亿元)', zzflrw: '承担放流任务'
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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 ? 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 });
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['FB', 'VD_FB', 'VD_FBP'] }] }
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData
|
||||
.filter((item: any) => item.wbsCode !== 'all')
|
||||
.map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => {
|
||||
if (val) { loadDropdownData(); if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; } }
|
||||
});
|
||||
|
||||
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 addFbInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
res = await updateFbInfo({ 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>
|
||||
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<BasicSearch
|
||||
ref="basicSearchRef"
|
||||
:searchList="searchList"
|
||||
:initial-values="initSearchData"
|
||||
:zhujianfujian="'fu'"
|
||||
@reset="handleReset"
|
||||
@finish="onSearchFinish"
|
||||
>
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-finish', values: any): void;
|
||||
(e: 'reset', values: any): void;
|
||||
(e: 'add'): void;
|
||||
}>();
|
||||
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref<boolean>(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = {
|
||||
baseId: 'all',
|
||||
rstcd: null,
|
||||
stnm: null,
|
||||
sttp: null
|
||||
};
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{
|
||||
type: 'jidiData',
|
||||
name: 'baseId',
|
||||
label: '水电基地',
|
||||
placeholder: '请输入水电基地名称',
|
||||
fieldProps: { allowClear: true },
|
||||
options: []
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
name: 'stnm',
|
||||
label: '增殖站名称',
|
||||
placeholder: '请输入增殖站名称',
|
||||
width: 160,
|
||||
fieldProps: { allowClear: true }
|
||||
},
|
||||
{
|
||||
col: isHide.value ? 0 : null,
|
||||
type: 'Select',
|
||||
name: 'sttp',
|
||||
label: '测站类型',
|
||||
width: 160,
|
||||
fieldProps: { allowClear: true, placeholder: '请选择测站类型' },
|
||||
options: sttpOption.value
|
||||
}
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
const res = await sttpGetKendoList({
|
||||
filter: {
|
||||
logic: 'and',
|
||||
filters: [
|
||||
{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['FB', 'VD_FB', 'VD_FBP'] }
|
||||
]
|
||||
}
|
||||
});
|
||||
sttpOption.value = res.data.data.map(item => ({
|
||||
label: item.sttpName,
|
||||
value: item.sttpCode
|
||||
}));
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => emit('search-finish', { ...values });
|
||||
const hideBtn = () => { isHide.value = !isHide.value; };
|
||||
const handleReset = () => emit('reset', initSearchData);
|
||||
const handleAdd = () => emit('add');
|
||||
|
||||
onMounted(() => {
|
||||
getBaseList();
|
||||
emit('search-finish', { ...initSearchData });
|
||||
});
|
||||
|
||||
defineExpose({ basicSearchRef });
|
||||
</script>
|
||||
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<FbSearch @search-finish="onSearchFinish" @reset="onReset" @add="handleAdd" ref="searchRef" />
|
||||
|
||||
<BasicTable ref="tableRef" :enableEllipsis="true" :scrollX="tableScrollX" :scrollY="tableScrollY" :columns="columns" :list-url="getFbPageList">
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditFbModal v-model:open="editVisible" :record="editRecord" :is-add="isAdd" @success="handleEditSuccess" />
|
||||
|
||||
<DeleteConfirmModal ref="deleteModalRef" :delete-fn="deleteFbFn" :label="'鱼类增殖站'" title="删除鱼类增殖站" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import FbSearch from './FbSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditFbModal from './EditFbModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getFbPageList, deleteFbInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '增殖站名称', dataIndex: 'stnm', visible: true, width: 160,fixed: 'left', ellipsis: true },
|
||||
{ key: 'stcd', title: '增殖站编码', dataIndex: 'stcd', visible: true, width: 180, ellipsis: true },
|
||||
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 100, ellipsis: true },
|
||||
|
||||
{ key: 'zzfldxName', title: '放流对象', dataIndex: 'zzfldxName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'zzflcnt', title: '放流规模(尾)', dataIndex: 'zzflcnt', visible: true, width: 120 },
|
||||
|
||||
{ key: 'zzflbjfs', title: '标记方式', dataIndex: 'zzflbjfs', visible: true, width: 100, ellipsis: true },
|
||||
{ key: 'zzflar', title: '总占地面积(km²)', dataIndex: 'zzflar', visible: true, width: 140 },
|
||||
{ key: 'inv', title: '投资(亿元)', dataIndex: 'inv', visible: true, width: 120 },
|
||||
{ key: 'zzflrw', title: '承担放流任务', dataIndex: 'zzflrw', visible: true, width: 130, ellipsis: true },
|
||||
{ key: 'zzflfllc', title: '放流地点', dataIndex: 'zzflfllc', visible: true, width: 260, ellipsis: true },
|
||||
|
||||
{ key: 'zzflyzms', title: '养殖模式', dataIndex: 'zzflyzms', visible: true, width: 220, ellipsis: true },
|
||||
{ key: 'zzflgy', title: '生产工艺', dataIndex: 'zzflgy', visible: true, width: 180, ellipsis: true },
|
||||
{ key: 'swdt', title: '开工日期', dataIndex: 'swdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'jcdt', title: '建成日期', dataIndex: 'jcdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'zzfltm', title: '放流时间', dataIndex: 'zzfltm', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'addvcdName', title: '地址', dataIndex: 'addvcdName', visible: true, width: 200, ellipsis: true },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 }
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() =>
|
||||
Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600)
|
||||
);
|
||||
|
||||
const buildSearchParams = (values: any) => ({
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null,
|
||||
values.baseId !== 'all' && values.baseId != null ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null,
|
||||
values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null,
|
||||
values.sttp ? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp } : null
|
||||
].filter(Boolean)
|
||||
});
|
||||
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { tableRef.value.getList(buildSearchParams(values)); };
|
||||
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
|
||||
const deleteFbFn = async (record: any, reason: string) => deleteFbInfo({ ids: [record.stcd], source: reason });
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => initTable(currentSearchParams.value)); };
|
||||
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -0,0 +1,215 @@
|
||||
<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="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 :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="swdt">
|
||||
<a-date-picker v-model:value="formData.swdt" 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="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="bhfs">
|
||||
<a-input v-model:value="formData.bhfs" placeholder="请输入保护方式" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="面积(km²)" name="area">
|
||||
<a-input-number v-model:value="formData.area" placeholder="请输入" :precision="2" style="width: 100%" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="投资(亿元)" name="inv">
|
||||
<a-input-number v-model:value="formData.inv" placeholder="请输入" :precision="4" 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 } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { addVpInfo, updateVpInfo } 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';
|
||||
|
||||
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) });
|
||||
|
||||
const formRef = ref();
|
||||
const confirmLoading = ref(false);
|
||||
const formData = ref<any>({});
|
||||
const originalRecord = ref<any>({});
|
||||
const confirmModalVisible = ref(false);
|
||||
|
||||
const formRules = ref<any>({
|
||||
stcd: [{ required: true, message: '请输入植物园编码', trigger: 'blur' }],
|
||||
stnm: [{ required: true, message: '请输入植物园名称', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
const engInfoOptions = ref<any[]>([]);
|
||||
const sttpOptions = ref<any[]>([]);
|
||||
|
||||
const baseNameOptions = ref<any[]>(
|
||||
jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }))
|
||||
);
|
||||
|
||||
const fieldLabelMap: Record<string, string> = {
|
||||
stcd: '植物园编码', stnm: '植物园名称', stlc: '站址', sttp: '测站类型',
|
||||
baseId: '水电基地', rstcd: '所属电站', swdt: '开工日期', jcdt: '建成日期',
|
||||
bhfs: '保护方式', area: '面积(km²)', inv: '投资(亿元)'
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
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 ? 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 });
|
||||
|
||||
watch(() => props.record, newRecord => {
|
||||
if (newRecord) {
|
||||
const converted = { ...newRecord };
|
||||
originalRecord.value = { ...converted };
|
||||
formData.value = { ...converted };
|
||||
changeOrder.value = [];
|
||||
}
|
||||
}, { deep: true });
|
||||
|
||||
const loadDropdownData = () => {
|
||||
sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['VP', 'VD_VP', 'AI_VP_GRAZE', 'AI_VP_WATER'] }] }
|
||||
}).then(res => {
|
||||
sttpOptions.value = (res.data?.data || []).map((item: any) => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
});
|
||||
baseNameOptions.value = jidiSelectEventStore.jidiData.filter((item: any) => item.wbsCode !== 'all').map((item: any) => ({ label: item.wbsName, value: item.wbsCode }));
|
||||
getEngInfoDropdown({}).then(res => {
|
||||
engInfoOptions.value = (res.data || []).map((item: any) => ({ label: item.ennm, value: item.stcd }));
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.open, val => { if (val) { loadDropdownData(); if (props.isAdd) { formData.value = {}; originalRecord.value = {}; changeOrder.value = []; } } });
|
||||
|
||||
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 addVpInfo({ engInfo, source: source.trim() || '新增' });
|
||||
} else {
|
||||
engInfo.stcd = formData.value.stcd;
|
||||
changeOrder.value.forEach(item => { engInfo[item.field] = formData.value[item.field]; });
|
||||
res = await updateVpInfo({ 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>
|
||||
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<BasicSearch ref="basicSearchRef" :searchList="searchList" :initial-values="initSearchData" :zhujianfujian="'fu'" @reset="handleReset" @finish="onSearchFinish">
|
||||
<template #actions>
|
||||
<a-button @click="hideBtn">{{ isHide ? '展开' : '隐藏' }}</a-button>
|
||||
<a-button type="primary" @click="handleAdd">新增</a-button>
|
||||
</template>
|
||||
</BasicSearch>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import BasicSearch from '@/components/BasicSearch/index.vue';
|
||||
import { sttpGetKendoList } from '@/api/system/map/ConfigManagement';
|
||||
|
||||
const emit = defineEmits<{ (e: 'search-finish', values: any): void; (e: 'reset', values: any): void; (e: 'add'): void }>();
|
||||
const basicSearchRef = ref();
|
||||
const isHide = ref(false);
|
||||
const sttpOption = ref([]);
|
||||
|
||||
const initSearchData = { baseId: 'all', rstcd: null, stnm: null, sttp: null };
|
||||
|
||||
const searchList: any = computed(() => [
|
||||
{ type: 'jidiData', name: 'baseId', label: '水电基地', placeholder: '请输入水电基地名称', fieldProps: { allowClear: true }, options: [] },
|
||||
{ type: 'Input', name: 'stnm', label: '植物园名称', placeholder: '请输入植物园名称', width: 160, fieldProps: { allowClear: true } },
|
||||
{ col: isHide.value ? 0 : null, type: 'Select', name: 'sttp', label: '测站类型', width: 160, fieldProps: { allowClear: true, placeholder: '请选择测站类型' }, options: sttpOption.value }
|
||||
]);
|
||||
|
||||
const getBaseList = async () => {
|
||||
const res = await sttpGetKendoList({
|
||||
filter: { logic: 'and', filters: [{ field: 'sttpCode', operator: 'in', dataType: 'string', value: ['VP', 'VD_VP', 'AI_VP_GRAZE', 'AI_VP_WATER'] }] }
|
||||
});
|
||||
sttpOption.value = res.data.data.map(item => ({ label: item.sttpName, value: item.sttpCode }));
|
||||
};
|
||||
|
||||
const onSearchFinish = (values: any) => emit('search-finish', { ...values });
|
||||
const hideBtn = () => { isHide.value = !isHide.value; };
|
||||
const handleReset = () => emit('reset', initSearchData);
|
||||
const handleAdd = () => emit('add');
|
||||
|
||||
onMounted(() => { getBaseList(); emit('search-finish', { ...initSearchData }); });
|
||||
defineExpose({ basicSearchRef });
|
||||
</script>
|
||||
@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<VpSearch @search-finish="onSearchFinish" @reset="onReset" @add="handleAdd" ref="searchRef" />
|
||||
|
||||
<BasicTable ref="tableRef" :enableEllipsis="true" :scrollX="tableScrollX" :scrollY="tableScrollY" :columns="columns" :list-url="getVpPageList">
|
||||
<template #action="{ record }">
|
||||
<a-button type="link" class="text-[#2f6b98]" size="small" @click="handleEdit(record)">编辑</a-button>
|
||||
<a-button type="link" danger size="small" @click="handleDelete(record)">删除</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
|
||||
<EditVpModal v-model:open="editVisible" :record="editRecord" :is-add="isAdd" @success="handleEditSuccess" />
|
||||
|
||||
<DeleteConfirmModal ref="deleteModalRef" :delete-fn="deleteVpFn" :label="'珍稀植物园'" title="删除珍稀植物园" @success="handleEditSuccess" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import VpSearch from './VpSearch.vue';
|
||||
import BasicTable from '@/components/BasicTable/index.vue';
|
||||
import EditVpModal from './EditVpModal.vue';
|
||||
import DeleteConfirmModal from '@/views/conventionalHydropower/BasicData/DeleteConfirmModal.vue';
|
||||
import { getVpPageList, deleteVpInfo } from '@/api/DataQueryMenuModule';
|
||||
import { calcTableScrollY } from '@/utils/index';
|
||||
|
||||
const tableRef = ref();
|
||||
const searchRef = ref();
|
||||
const tableScrollY = ref<string | number>(0);
|
||||
const currentSearchParams = ref<any>({});
|
||||
const editVisible = ref(false);
|
||||
const editRecord = ref<any>(null);
|
||||
const isAdd = ref(false);
|
||||
const deleteModalRef = ref();
|
||||
|
||||
const columns = ref<any[]>([
|
||||
{ key: 'stnm', title: '植物园名称', dataIndex: 'stnm', visible: true, width: 150, ellipsis: true },
|
||||
{ key: 'stcd', title: '植物园编码', dataIndex: 'stcd', visible: true, width: 150, fixed: 'left', ellipsis: true },
|
||||
{ key: 'baseName', title: '水电基地', dataIndex: 'baseName', visible: true, width: 120, ellipsis: true },
|
||||
{ key: 'ennm', title: '所属电站', dataIndex: 'ennm', visible: true, width: 150, ellipsis: true },
|
||||
|
||||
|
||||
{ key: 'area', title: '面积(km²)', dataIndex: 'area', visible: true, width: 120 },
|
||||
{ key: 'inv', title: '投资(亿元)', dataIndex: 'inv', visible: true, width: 120 },
|
||||
{ key: 'swdt', title: '开工日期', dataIndex: 'swdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'jcdt', title: '建成日期', dataIndex: 'jcdt', visible: true, width: 110, customRender: ({ text }) => { if (!text || text === '-') return '-'; const date = dayjs(text); return date.isValid() ? date.format('YYYY-MM-DD') : '-'; } },
|
||||
{ key: 'bhfs', title: '保护方式', dataIndex: 'bhfs', visible: true, width: 200, ellipsis: true },
|
||||
{ key: 'stlc', title: '站址', dataIndex: 'stlc', visible: true, width: 200, ellipsis: true },
|
||||
{ key: 'action', title: '操作', dataIndex: 'action', fixed: 'right', width: 120 }
|
||||
]);
|
||||
|
||||
const tableScrollX = computed(() => Math.max(columns.value.reduce((sum: number, col: any) => sum + (col.width || 180), 0), 600));
|
||||
|
||||
const buildSearchParams = (values: any) => ({
|
||||
logic: 'and',
|
||||
filters: [
|
||||
values.rstcd ? { field: 'rstcd', operator: 'eq', dataType: 'string', value: values.rstcd } : null,
|
||||
values.baseId !== 'all' && values.baseId != null ? { field: 'baseId', operator: 'eq', dataType: 'string', value: values.baseId } : null,
|
||||
values.stnm ? { field: 'stnm', operator: 'contains', dataType: 'string', value: values.stnm } : null,
|
||||
values.sttp ? { field: 'sttp', operator: 'eq', dataType: 'string', value: values.sttp } : null
|
||||
].filter(Boolean)
|
||||
});
|
||||
|
||||
const onSearchFinish = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const onReset = (values: any) => { currentSearchParams.value = values; initTable(values); };
|
||||
const initTable = (values: any) => { tableRef.value.getList(buildSearchParams(values)); };
|
||||
|
||||
const handleAdd = () => { isAdd.value = true; editRecord.value = null; editVisible.value = true; };
|
||||
const handleEdit = (record: any) => { isAdd.value = false; editRecord.value = { ...record }; editVisible.value = true; };
|
||||
const handleEditSuccess = () => { initTable(currentSearchParams.value); };
|
||||
|
||||
const deleteVpFn = async (record: any, reason: string) => deleteVpInfo({ ids: [record.stcd], source: reason });
|
||||
const handleDelete = (record: any) => { deleteModalRef.value?.open(record, () => initTable(currentSearchParams.value)); };
|
||||
|
||||
onMounted(() => { nextTick(() => { tableScrollY.value = calcTableScrollY(); }); });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.body_one { position: relative; z-index: 900; pointer-events: all; }
|
||||
</style>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<FishDataSearch
|
||||
@export-btn="exportBtn"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<FishReleaseSearch
|
||||
@export-btn="exportBtn"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<FlowDataSearch
|
||||
@export-btn="exportBtn"
|
||||
@ -462,4 +462,10 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<FlowStationSearch
|
||||
@export-btn="exportBtn"
|
||||
@ -328,4 +328,10 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<SurfaceTempSearch
|
||||
@export-btn="exportBtn"
|
||||
@ -292,4 +292,10 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<VerticalTempSearch
|
||||
@export-btn="exportBtn"
|
||||
@ -696,4 +696,9 @@ onBeforeUnmount(() => {
|
||||
:deep(.ant-table-tbody .ant-table-row > td) {
|
||||
cursor: default !important;
|
||||
}
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px]">
|
||||
|
||||
<div class="w-full h-full flex flex-col pt-[20px] pl-[20px] body_one">
|
||||
<!-- 搜索组件 -->
|
||||
<WaterDataSearch
|
||||
@export-btn="exportBtn"
|
||||
@ -450,4 +449,10 @@ onMounted(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss">
|
||||
.body_one {
|
||||
position: relative;
|
||||
z-index: 900;
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user