BUG更改

This commit is contained in:
王兴凯 2026-07-14 16:08:10 +08:00
parent d7c33850b5
commit 3623a47546
48 changed files with 2962 additions and 380 deletions

View File

@ -79,6 +79,16 @@ export function vmsstbprptGetKendoList(data: any) {
data: data
});
}
// 环保自动监测工作开展情弹窗
export function overviewvmsstbprptGetKendoList(data: any) {
return request({
url: '/overview/vmsstbprpt/GetKendoList',
method: 'post',
data: data
});
}
// 获取基本信息
export function baseMsstbprptGetKendoList(data: any) {

View File

@ -3,7 +3,7 @@ import request from '@/utils/request';
// 获取所有倾斜摄影
export function getAllTiltPhotoTree(data: any) {
return request({
url: '/api/wmp-sys-server/sys/oblique/GetKendoListCust',
url: '/sys/oblique/GetKendoListCust',
method: 'post',
data: data
});
@ -11,7 +11,7 @@ export function getAllTiltPhotoTree(data: any) {
// 保存倾斜摄影
export function saveTiltPhoto(data: any) {
return request({
url: '/api/wmp-sys-server/sys/oblique/addOrUpdate',
url: '/sys/oblique/addOrUpdate',
method: 'post',
data: data
});
@ -19,7 +19,7 @@ export function saveTiltPhoto(data: any) {
// 删除倾斜摄影
export function deleteTiltPhoto(data: any) {
return request({
url: '/api/wmp-sys-server/sys/oblique/delete',
url: '/sys/oblique/delete',
method: 'get',
params: data
});

View File

@ -468,7 +468,7 @@ const triggerManualValuesChange = (changedKey: string, newValue: any) => {
emit('valuesChange', changedValues, { ...formData });
};
const dataDimensionDataChange = (value: any, fieldName: string = 'baseId') => {
const dataDimensionDataChange = (value: any, fieldName = 'baseId') => {
formData[fieldName] = value;
formData.rstcd = "";
shuJuTianBaoStore.getEngOption(value,'baseId');

View File

@ -192,6 +192,7 @@ const getList = async (filter?: Record<string, any>) => {
total.value = 0;
if (filter !== undefined) {
lastFilter.value = filter;
page.value = 1;
}
try {
// 使使 searchParams

View File

@ -5,7 +5,7 @@
<div class="card_left">
<div class="card_img">
<div class="carousel-wrapper">
<div class="carousel-inner">
<div class="carousel-inner" ref="carouselInnerRef">
<div class="carousel-track" :style="trackStyle">
<div v-for="(item, index) in originalMediaData" :key="index" class="carousel-item">
<a-image :src="item.url" />
@ -43,7 +43,7 @@
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, watch } from 'vue';
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
defineOptions({
@ -67,6 +67,15 @@ interface MediaItem {
const originalMediaData = ref<MediaItem[]>([]);
const currentIndex = ref(0);
const carouselInnerRef = ref<HTMLElement | null>(null);
const itemWidth = ref(0);
let resizeObserver: ResizeObserver | null = null;
const updateItemWidth = () => {
if (carouselInnerRef.value) {
itemWidth.value = carouselInnerRef.value.clientWidth;
}
};
//
const initOriginalData = () => {
@ -89,10 +98,9 @@ const setCurrentIndexFromProps = () => {
}
};
// - React
const trackStyle = computed(() => {
return {
transform: `translateX(calc((68.8px - 48vw) * ${currentIndex.value}))`
transform: `translateX(-${itemWidth.value * currentIndex.value}px)`
};
});
@ -122,12 +130,23 @@ const currentDescription = computed(() => {
const currentTitle = computed(() => {
return originalMediaData.value[currentIndex.value]?.title || '';
});
//
onMounted(() => {
console.log('ArtsDetail mounted - props.index:', props.index, 'props.dataSource.length:', props.dataSource?.length);
initOriginalData();
setCurrentIndexFromProps();
console.log('After setCurrentIndexFromProps - currentIndex.value:', currentIndex.value);
updateItemWidth();
if (carouselInnerRef.value) {
resizeObserver = new ResizeObserver(() => {
updateItemWidth();
});
resizeObserver.observe(carouselInnerRef.value);
}
});
onUnmounted(() => {
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
});
// dataSource index
@ -196,10 +215,9 @@ watch(
transition: transform 0.3s ease-in-out;
.carousel-item {
width: calc(48vw - 80.8px);
flex: 0 0 calc(48vw - 80.8px);
width: 100%;
flex: 0 0 100%;
height: 100%;
margin-right: 12px;
display: flex;
justify-content: center;
align-items: center;

View File

@ -39,6 +39,7 @@ import { PointLayerManager } from './ol/point-layer-manager';
import { PopupManager } from './ol/popup-manager';
import { RegionMaskManager } from './ol/region-mask-manager';
import { getNearbyPointDensityDisplayRules } from '@/modules/map/domain/nearby-point-rules';
import router from '@/router';
const modelStore = useModelStore();
const VITE_APP_MAP_URL = import.meta.env.VITE_APP_MAP_URL;
@ -167,6 +168,13 @@ export class MapOl implements MapInterface {
this.handleZoomChange();
});
this.map.on('click', evt => {
if (
router.currentRoute.value.path.includes(
'shengTaiLiuLiangManZuQingKuangJiangJu'
)
) {
return;
}
this.popupManager.showPopup(undefined, undefined);
this.popupManager.handleMapClick(evt.pixel, detectedFeature => {
if (detectedFeature.values_.sttpMap == 'ylfb') {

View File

@ -318,7 +318,7 @@ export class PointLayerManager {
layerKey: string,
matchValue: string,
checked: boolean,
fieldKey: string = 'anchoPointState'
fieldKey = 'anchoPointState'
): void {
const vectorLayer = this.layerRegistry.get(layerKey);
if (!vectorLayer) return;

View File

@ -209,7 +209,7 @@ export class RegionMaskManager {
}
// 备注:清除当前底图遮罩并按需恢复默认视野,供切换基地和异常兜底复用。
clearMapMask(resetView: boolean = true): void {
clearMapMask(resetView = true): void {
const maskedLayers = this.maskBindings.map(binding => binding.layer);
this.clearMaskEvents();

View File

@ -4,7 +4,7 @@
<a-form ref="formRef" :model="formState" layout="inline" class="search-form">
<a-space wrap>
<a-form-item label="所属基地" name="dataDimensionVal">
<a-select v-model:value="formState.dataDimensionVal" placeholder="请选择" style="width: 120px"
<a-select v-model:value="formState.dataDimensionVal" placeholder="请选择" style="width: 140px"
show-search :filter-option="filterBaseOption" option-filter-prop="label"
@change="handleBaseChange">
<a-select-option v-for="item in baseList" :key="item.wbsCode" :value="item.wbsCode"
@ -32,7 +32,7 @@
</a-form-item>
<a-form-item label="达标状态" name="sfdb">
<a-select v-model:value="formState.sfdb" placeholder="请选择" style="width: 100px" show-search
<a-select v-model:value="formState.sfdb" placeholder="请选择" style="width: 160px" show-search
:filter-option="filterOption" option-filter-prop="label">
<a-select-option value="" label="全部">全部</a-select-option>
<a-select-option value="0" label="不达标">不达标</a-select-option>
@ -44,12 +44,12 @@
<a-form-item label="时间" name="dateValues">
<a-date-picker v-model:value="formState.dateValues"
:format="formState.types === 'month' ? 'YYYY-MM' : 'YYYY'" :picker="formState.types"
placeholder="请选择月份" style="width: 140px" :disabled-date="disabledMonthDate" />
placeholder="请选择月份" style="width: 100px" :disabled-date="disabledMonthDate" />
</a-form-item>
<a-form-item label="断面名称" name="stnm">
<a-input v-model:value="formState.stnm" placeholder="请输入断面名称" style="width: 200px" allow-clear />
<a-input v-model:value="formState.stnm" placeholder="请输入断面名称" style="width: 180px" allow-clear />
</a-form-item>
<a-form-item>
@ -90,7 +90,7 @@
<!-- 详情弹窗 -->
<a-modal v-model:open="detailModalVisible" :title="`${detailData.stnm || ''} 详细信息`" width="1536px" :footer="null"
@cancel="handleDetailModalClose">
<HJMZDTwoLays :tc="{
<HJMZDTwoLays v-if="detailModalVisible" :tc="{
stcd: detailData.stcd || '',
startTime: detailData.tm,
endTime: (typeof detailData.tm === 'string' ? detailData.tm.split(' ')[0] : dayjs(detailData.tm).format('YYYY-MM-DD')) + ' 23:59:59',
@ -231,7 +231,7 @@ const columns = [
title: '断面名称',
dataIndex: 'stnm',
key: 'stnm',
width: 180,
width: 200,
ellipsis: true,
customCell: (record: any, rowIndex: number) => {
const tableData = tableRef.value?.tableData || []
@ -243,13 +243,13 @@ const columns = [
title: '测站类型',
dataIndex: 'sttypeName',
key: 'sttypeName',
width: 120
width: 80
},
{
title: '达标状态',
dataIndex: 'sfdbName',
key: 'sfdbName',
width: 120,
width: 80,
customRender: ({ record }: any) => {
const sfdb = record.sfdbName
let color = '#000000'
@ -267,19 +267,19 @@ const columns = [
title: '水质要求',
dataIndex: 'wwqtgName',
key: 'wwqtgName',
width: 120
width: 80
},
{
title: '当前水质',
dataIndex: 'wqGrdName',
key: 'wqGrdName',
width: 120
width: 80
},
{
title: '去年同期水质',
dataIndex: 'beforeWqGrdName',
key: 'beforeWqGrdName',
width: 150
width: 140
},
{
title: '操作',
@ -674,7 +674,7 @@ const handleReset = () => {
formState.dtinType = ''
formState.types = props.type || 'month'
formState.sfdb = ''
formState.dateValues = dayjs()
formState.dateValues =props.dateValue ? (typeof props.dateValue === 'string' ? dayjs(props.dateValue, props.type === 'month' ? 'YYYY-MM' : 'YYYY') : props.dateValue) : dayjs(),
formState.stnm = ''
pieCode.value = []

View File

@ -13,7 +13,7 @@
v-model:value="formState.sfdb"
placeholder="请选择"
allow-clear
style="width: 150px"
style="width: 160px"
>
<a-select-option value="">全部</a-select-option>
<a-select-option :value="'0'">不达标</a-select-option>
@ -114,7 +114,8 @@ const columnList = [
elementCode: '',
disabledDate: true,
sorter: true,
multiLink: false
multiLink: false,
width: 160
},
{
dataIndexRaw: '',
@ -558,18 +559,18 @@ const transUnit = (value: any, tbCode: string, ys: string) => {
// ==================== ====================
// tc formState.sfdb
watch(
() => [props.tc.stcd, props.tc.startTime, props.tc.endTime, formState.sfdb],
([stcd, startTime, endTime]) => {
if (!stcd) {
return;
}
//
tableRef.value.tableData = [];
getCol({ stcd, startTime, endTime });
},
{ immediate: false } // onMounted
);
// watch(
// () => [props.tc.stcd, props.tc.startTime, props.tc.endTime, formState.sfdb],
// ([stcd, startTime, endTime]) => {
// if (!stcd) {
// return;
// }
// //
// tableRef.value.tableData = [];
// getCol({ stcd, startTime, endTime });
// },
// { immediate: false } // onMounted
// );
//
onMounted(() => {

View File

@ -167,6 +167,7 @@ const columns = [
dataIndex: 'dtinTm',
key: 'dtinTm',
width: 120,
sort: true, //
customRender: ({ record }: any) => {
const date = record.dtinTm
if (!date) return '-'
@ -192,6 +193,7 @@ const columns = [
dataIndex: 'ststdt',
key: 'ststdt',
width: 120,
sort: true, //
customRender: ({ record }: any) => {
const date = record.ststdt
if (!date) return '-'
@ -211,6 +213,7 @@ const columns = [
dataIndex: 'jcdt',
key: 'jcdt',
width: 120,
sort: true, //
customRender: ({ record }: any) => {
const date = record.jcdt
if (!date) return '-'

View File

@ -48,7 +48,7 @@
ref="tableRef"
:scrollY="400"
:columns="columns"
:list-url="vmsstbprptGetKendoList"
:list-url="overviewvmsstbprptGetKendoList"
:search-params="searchParams"
:transform-data="customTransform"
>
@ -73,8 +73,10 @@
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import { vmsstbprptGetKendoList } from '@/api/home';
import { ref, computed, onMounted, h } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
import { vmsstbprptGetKendoList,overviewvmsstbprptGetKendoList } from '@/api/home';
import BasicTable from '@/components/BasicTable/index.vue';
import { useModelStore } from '@/store/modules/model';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
@ -144,10 +146,9 @@ const columns = computed(() => {
},
{
key: 'coenvwState',
title: '监测状态',
title: h(Tooltip, { title: '最近1天有监测数据为在线否则为离线' }, { default: () => h('span', null, ['监测状态', h(QuestionCircleOutlined, { style: { marginLeft: '4px', color: '#000' } })]) }),
dataIndex: 'coenvwState',
width: '120px',
slots: { customRender: 'coenvwState' }
},
{
@ -203,7 +204,7 @@ const columns = computed(() => {
if (tabIndex.value !== '4') {
baseColumns.push({
key: 'coenvwState',
title: '监测状态',
title: h(Tooltip, { title: '最近1天有监测数据为在线否则为离线' }, { default: () => h('span', null, ['监测状态', h(QuestionCircleOutlined, { style: { marginLeft: '4px', color: '#999' } })]) }),
dataIndex: 'coenvwState',
width: '120px',
slots: { customRender: 'coenvwState' }
@ -313,7 +314,7 @@ const getRstcdData = async () => {
],
select: ['stcd', 'stnm']
};
const res = await vmsstbprptGetKendoList(parmas);
const res = await overviewvmsstbprptGetKendoList(parmas);
rstcdOptions.value = res.data.data.map((item) => {
return {
value: item.stcd,

View File

@ -317,7 +317,7 @@ import dayjs from 'dayjs';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import videoCover from '@/assets/images/videFm.jpg';
import { useDraggable } from '@/utils/drag';
;
const baseUrl = import.meta.env.VITE_APP_ATTACHMENT_URL;
// ==================== Props ====================

View File

@ -33,9 +33,12 @@
<a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
<!-- <a-button style="margin-left: 8px" @click="handleReset">重置</a-button> -->
<a-tooltip title="导出(暂未实现)">
<a-button style="margin-left: 8px">导出</a-button>
</a-tooltip>
<a-button
style="margin-left: 8px"
@click="handleExport"
:loading="exportLoading"
>导出</a-button
>
</a-form-item>
</a-form>
@ -66,11 +69,12 @@
</template>
<script lang="ts" setup>
import { ref, computed, reactive, watch,onMounted } from 'vue';
import { ref, computed, reactive, watch, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import { getStllGaojingDetail } from '@/api/zngj';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useModelStore } from '@/store/modules/model';
import dayjs, { Dayjs } from 'dayjs';
defineOptions({
name: 'ModeGaoJing'
});
@ -262,6 +266,19 @@ const opentan = (record: any) => {
modelStore.title = record.ennm;
modelStore.params.stcd = record.rstcd;
};
/* 导出逻辑 */
const exportLoading = ref(false);
const handleExport = () => {
exportLoading.value = true;
tableRef.value
.exportTable({
fileName: `生态流量低于限值告警统计_${dayjs().format('YYYY-MM-DD HH-mm-ss')}`
})
.finally(() => {
exportLoading.value = false;
});
};
//
// handleSearch();
onMounted(() => {
@ -283,7 +300,6 @@ onMounted(() => {
.table-wrapper {
flex: 1;
}
}
</style>

View File

@ -32,6 +32,12 @@
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button
style="margin-left: 8px"
@click="handleExport"
:loading="exportLoading"
>导出</a-button
>
</a-form-item>
</a-form>
@ -67,7 +73,7 @@ import BasicTable from '@/components/BasicTable/index.vue';
import { getStllGaojingDetail } from '@/api/zngj';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import { useModelStore } from '@/store/modules/model';
import dayjs, { Dayjs } from 'dayjs';
defineOptions({
name: 'ModeGaoJing'
});
@ -266,6 +272,19 @@ const opentan = (record: any) => {
modelStore.title = record.ennm;
modelStore.params.stcd = record.rstcd;
};
/* 导出逻辑 */
const exportLoading = ref(false);
const handleExport = () => {
exportLoading.value = true;
tableRef.value
.exportTable({
fileName: `生态流量低于限值告警统计_${dayjs().format('YYYY-MM-DD HH-mm-ss')}`
})
.finally(() => {
exportLoading.value = false;
});
};
onMounted(() => {
handleSearch();
});

View File

@ -1,15 +1,31 @@
<!-- SidePanelItem.vue -->
<template>
<div>
<SidePanelItem title="水质监测数据告警情况" :select="select" @update-values="handlePanelChange">
<SidePanelItem
title="水质监测数据告警情况"
:select="select"
@update-values="handlePanelChange"
>
<a-spin :spinning="dataLoading" tip="加载中...">
<div class="body_box">
<div class="box" v-for="(item, index) in data" :key="index" @click="handleCardClick(item.warnState)" style="cursor: pointer;">
<div
class="box"
v-for="(item, index) in data"
:key="index"
@click="handleCardClick(item.warnState)"
style="cursor: pointer"
>
<div>
<div :class="item.icon" :style="{background:item.background}"></div>
<div
:class="item.icon"
:style="{ background: item.background }"
></div>
<div class="title_text">{{ item.name }}</div>
</div>
<div><span class="text_num">{{ item.num }}</span><span></span></div>
<div>
<span class="text_num">{{ item.num }}</span
><span></span>
</div>
</div>
</div>
</a-spin>
@ -33,15 +49,15 @@
</template>
<script lang="ts" setup>
import { ref, watch } from "vue";
import { ref, watch } from 'vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from "@/components/SidePanelItem/index.vue";
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import { getShuizhiGaojingList } from '@/api/zngj';
import ModeGaoJing from './ModeGaoJing.vue';
import { useDraggable } from '@/utils/drag';
defineOptions({
name: "shuizhijianceshuju",
name: 'shuizhijianceshuju'
});
const JidiSelectEventStore = useJidiSelectEventStore();
@ -68,10 +84,10 @@ const select = ref({
options: [
{ label: '全部', value: '' },
{ label: '国家站', value: '1' },
{ label: '自建站', value: '0' },
{ label: '自建站', value: '0' }
],
width: '140px',
minWidth: '140px',
minWidth: '140px'
});
const data = ref([
@ -80,22 +96,22 @@ const data = ref([
num: '0',
warnState: '1',
icon: 'iconfont icon-map-rgszzDabiao zhuangji',
background: '#78c300',
background: '#78c300'
},
{
name: '告警',
num: '0',
warnState: '2',
icon: 'iconfont icon-map-rgszzDabiao shebeiZhengchang',
background: '#e55555',
background: '#e55555'
},
{
name: '无数据',
num: '0',
warnState: '0',
icon: 'iconfont icon-map-rgszzDabiao shebeiLixian',
background: '#8d8d8d',
},
background: '#8d8d8d'
}
]);
const handlePanelChange = (payload: any) => {
@ -117,26 +133,28 @@ const getCardData = async () => {
field: 'sttpCode',
operator: 'in',
dataType: 'string',
value: ['WQ'],
value: ['WQ']
},
{
field: 'mway',
operator: 'eq',
dataType: 'number',
value: 2,
value: 2
},
{
field: 'usfl',
operator: 'eq',
dataType: 'string',
value: '1',
value: '1'
},
baseid.value !='all'? {
baseid.value != 'all'
? {
field: 'baseId',
operator: 'eq',
dataType: 'string',
value: baseid.value,
}: null,
value: baseid.value
}
: null
].filter(Boolean);
if (defaultValue.value === '') {
@ -144,21 +162,21 @@ const getCardData = async () => {
field: 'dtinType',
operator: 'in',
dataType: 'number',
value: [0, 1],
value: [0, 1]
});
} else {
filters.push({
field: 'dtinType',
operator: 'eq',
dataType: 'number',
value: Number(defaultValue.value),
value: Number(defaultValue.value)
});
}
const params = {
filter: {
logic: 'and',
filters,
filters
},
group: [
{
@ -167,12 +185,12 @@ const getCardData = async () => {
aggregates: [
{
field: 'stcd',
aggregate: 'count',
},
aggregate: 'count'
}
]
}
],
},
],
select: [],
select: []
};
const res = await getShuizhiGaojingList(params);
@ -181,7 +199,7 @@ const getCardData = async () => {
const result = {
normal: 0,
abnormal: 0,
noData: 0,
noData: 0
};
list.forEach((item: any) => {
@ -195,25 +213,25 @@ const getCardData = async () => {
data.value = [
{
name: '正常',
num: result.normal.toString(),
num: '0',
warnState: '1',
icon: 'iconfont icon-shuidianzhan1 zhuangji',
background: '#78c300',
icon: 'iconfont icon-map-rgszzDabiao zhuangji',
background: '#78c300'
},
{
name: '告警',
num: result.abnormal.toString(),
num: '0',
warnState: '2',
icon: 'iconfont icon-shuidianzhanGaojing shebeiZhengchang',
background: '#e55555',
icon: 'iconfont icon-map-rgszzDabiao shebeiZhengchang',
background: '#e55555'
},
{
name: '无数据',
num: result.noData.toString(),
num: '0',
warnState: '0',
icon: 'iconfont icon-shuidianzhanWushujv shebeiLixian',
background: '#8d8d8d',
},
icon: 'iconfont icon-map-rgszzDabiao shebeiLixian',
background: '#8d8d8d'
}
];
} catch (error) {
console.error('获取水质监测告警数据失败:', error);
@ -261,11 +279,11 @@ watch(
font-size: 24px;
color: #fff;
}
.title_text{
.title_text {
width: 100%;
text-align: center;
}
.text_num{
.text_num {
width: 100%;
text-align: center;
font-size: 24px;

View File

@ -63,7 +63,7 @@
<script lang="ts" setup>
import { ref, onMounted, onUnmounted, nextTick,watch } from 'vue';
import { ref, watch } from 'vue';
// import { ref, watch } from 'vue';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import SidePanelItem from '@/components/SidePanelItem/index.vue';
import LsstjkTk from './LsstjkTk.vue';

View File

@ -55,7 +55,9 @@ const select = ref({
value: 'all',
options: jiDiList.value,
picker: undefined,
format: undefined
format: undefined,
width: '160px',
minWidth:'160px'
});
const baseid = ref('');
//

View File

@ -39,7 +39,7 @@ const baseid = ref('');
const modelStore = useModelStore();
// Loading
const loading = ref(false);
const loading = ref(true);
const hasData = ref(true); // true initChart
const select = ref({
show: true,
@ -844,4 +844,7 @@ watch(
z-index: 10;
}
}
:deep(.ant-spin-nested-loading >div>.ant-spin){
background: rgba(255, 255, 255) !important;
}
</style>

View File

@ -41,7 +41,7 @@ let chartInstance: echarts.ECharts | null = null;
const baseid = ref('');
// Loading
const loading = ref(false);
const loading = ref(true);
const hasData = ref(true); // true initChart
//
@ -732,4 +732,7 @@ onUnmounted(() => {
z-index: 10;
}
}
:deep(.ant-spin-nested-loading >div>.ant-spin){
background: rgba(255, 255, 255) !important;
}
</style>

View File

@ -587,12 +587,12 @@ const fetchTableData = async () => {
{
field: 'stnm',
operator: 'contains',
value: '12'
value: params.value.stnm
},
{
field: 'ennm',
operator: 'contains',
value: '12'
value: params.value.stnm
}
]
}

View File

@ -501,7 +501,7 @@ const updateChart = () => {
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderRadius: 5,
borderColor: '#fff',
borderWidth: 2
},

View File

@ -14,7 +14,7 @@ import {
getMsstbprptKendoList
} from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import dayjs from 'dayjs';
const props = defineProps<{
dataDimensionVal?: string;
name?: string;
@ -337,17 +337,27 @@ const customTransform = (res: any) => ({
total: res?.data?.total || 0
});
const exportLoading = ref(false);
function handleExport() {
if (exportLoading.value) return;
exportLoading.value = true;
const currentTab = tabKey.value;
if (
tableRefs.value[currentTab] &&
typeof tableRefs.value[currentTab].exportTable === 'function'
) {
tableRefs.value[currentTab].exportTable({
fileName: '环境监测数据接入情况'
tableRefs.value[currentTab]
.exportTable({
fileName: `水电开发情况_${dayjs().format('YYYY-MM-DD HH-mm-ss')}`
})
.finally(() => {
exportLoading.value = false;
});
} else {
message.info('导出功能待实现');
message.info('当前Tab暂无导出数据');
exportLoading.value = false;
}
}
@ -640,6 +650,7 @@ const searchList = computed(() => [
type: 'Input',
name: 'stnm',
label: '',
placeholder:'请输入电站名称',
fieldProps: {
allowClear: true,
placeholder: '请输入电站名称'
@ -866,7 +877,7 @@ onUnmounted(() => {
@reset="handleReset"
>
<template #actions>
<a-button @click="handleExport">
<a-button @click="handleExport" :loading ="exportLoading" >
<template #icon><DownloadOutlined /></template>
导出
</a-button>

View File

@ -15,7 +15,7 @@ import {
getBaseWbsb
} from '@/api/home';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
import dayjs from 'dayjs';
const props = defineProps<{
defaultJidiInfo?: Record<string, string | number | boolean>;
seriesName?: string;
@ -300,12 +300,27 @@ const customTransform = (res: any) => ({
records: res?.data?.data || res?.data?.items || [],
total: res?.data?.total || 0
});
const exportLoading = ref(false);
function handleExport() {
if (tableRef.value && typeof tableRef.value.export === 'function') {
tableRef.value.export();
if (exportLoading.value) return;
exportLoading.value = true;
const currentTab = tabKey.value;
if (
tableRefs.value[currentTab] &&
typeof tableRefs.value[currentTab].exportTable === 'function'
) {
tableRefs.value[currentTab]
.exportTable({
fileName: `水电开发情况_${dayjs().format('YYYY-MM-DD HH-mm-ss')}`
})
.finally(() => {
exportLoading.value = false;
});
} else {
message.info('导出功能待实现');
message.info('当前Tab暂无导出数据');
exportLoading.value = false;
}
}
@ -575,6 +590,7 @@ const searchList = computed(() => [
type: 'Input',
name: 'stnm',
label: '',
placeholder: '请输入电站名称',
fieldProps: {
allowClear: true,
placeholder: '请输入电站名称'
@ -946,7 +962,7 @@ onUnmounted(() => {
@reset="handleReset"
>
<template #actions>
<a-button @click="handleExport">
<a-button @click="handleExport" :loading ="exportLoading" >
<template #icon><DownloadOutlined /></template>
导出
</a-button>

View File

@ -404,7 +404,7 @@ const getselectTwo = async (code: any) => {
"filters": [
code != 'all' ? {
"field": "baseId",
"operator": "contains",
"operator": "eq",
"dataType": "string",
"value": code
} : null,

View File

@ -56,11 +56,28 @@ const paramsOne = {
tm: []
};
const dataOne = ref('');
// 使 computed 使 prompts dataOne.value
const prompts = computed(() => ({
// minTm
const rawTableData = ref<any[]>([]);
// 使 computed 使 prompts minTm
const prompts = computed(() => {
//
if (!rawTableData.value || rawTableData.value.length === 0) {
return { show: true, value: '' };
}
// minTm
const minTm = rawTableData.value[0]?.minTm;
// minTm
if (!minTm) {
return { show: true, value: '' };
}
return {
show: true,
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`
}));
value: `注:最新数据时间为${minTm}`
};
});
const showemit = ref(false);
const loading = ref(false);
// const selectOptions: any = ref([])
@ -308,6 +325,9 @@ const init = async () => {
let res = await getKendoListCust(params);
let data = res.data.data || res.data;
// minTm
rawTableData.value = data;
if (data.length > 0) {
showemit.value = true;
} else {

View File

@ -23,7 +23,7 @@ export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
};
const getJidiData = async () => {
loading.value = true;
let params = {
const params = {
filter: {
logic: 'and',
filters: [
@ -50,7 +50,7 @@ export const useJidiSelectEventStore = defineStore('jidiSelectEvent', () => {
select: ['wbsCode', 'wbsName']
};
const res = await wbsbGetKendoList(params);
let data = res?.data?.data || [
const data = res?.data?.data || [
{
wbsCode: 'all',
wbsName: '当前全部',

View File

@ -66,7 +66,7 @@ export const useShuJuTianBaoStore = defineStore('shuJuTianBao', () => {
if (type == 'baseId') {
param = rvcd === 'all' ? {} : { baseId: rvcd };
} else {
param = rvcd === 'all' ? {} : { reachcd: rvcd };
param = rvcd === 'all' ? {} : { rvcd: rvcd };
}
const res = await getEngInfoDropdown(param);
if (res.data && Array.isArray(res.data)) {

View File

@ -2451,10 +2451,10 @@ const themecc_list = [
}
];
const getThemee = (fieldName?: string) => {
let themeecList = themecc_list;
const themeecList = themecc_list;
let getitem: any = {};
if (fieldName && themeecList.length > 0) {
let getitemList = themeecList.filter(
const getitemList = themeecList.filter(
(i: any) => i.fieldName.toUpperCase() == fieldName.toUpperCase()
);
if (getitemList.length > 0) {

View File

@ -246,6 +246,23 @@ const buildSearchParams = (values: any) => {
return {
logic: 'and',
filters: [
values.rvcd && values.rvcd !== 'all'
? {
field: 'rvcd',
operator: 'contains',
dataType: 'string',
value: values.rvcd
}
: null,
values.rstcd
? {
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: values.rstcd
}
: null,
values.basin
? {
field: 'basin',

View File

@ -510,6 +510,23 @@ const buildSearchParams = (values: any) => {
return {
logic: 'and',
filters: [
values.rvcd && values.rvcd !== 'all'
? {
field: 'rvcd',
operator: 'contains',
dataType: 'string',
value: values.rvcd
}
: null,
values.rstcd
? {
field: 'stcd',
operator: 'eq',
dataType: 'string',
value: values.rstcd
}
: null,
values.ptype && values.ptype !== 'all'
? {
field: 'ptype',

View File

@ -22,7 +22,7 @@ let initialLeft = 0;
let initialTop = 0;
const onMouseDown = (e: MouseEvent) => {
;
isDragging = true;
startX = e.clientX;
startY = e.clientY;

View File

@ -238,6 +238,9 @@ const statusFilter = ref<"online" | "offline">("online");
//
const videoList = ref<any[]>(Array(9).fill(null));
//
const pendingVideoQueue = ref<any[]>([]);
//
const removeVideo = (index: number, nodeKey?: string) => {
const videoData = videoList.value[index];
@ -255,6 +258,19 @@ const removeVideo = (index: number, nodeKey?: string) => {
}
//
videoList.value[index] = null;
//
fillEmptySlot(index);
};
//
const fillEmptySlot = (emptyIndex: number) => {
if (pendingVideoQueue.value.length > 0) {
const nextVideo = pendingVideoQueue.value.shift();
if (nextVideo) {
videoList.value[emptyIndex] = nextVideo;
}
}
};
//
@ -269,18 +285,49 @@ const addVideo = (videoData: any) => {
} else if (videoList.value.filter((v, i) => i < maxCount).length < maxCount) {
videoList.value.push(videoData);
} else {
//
videoList.value[0] = videoData;
//
if (!pendingVideoQueue.value.find((v) => v.key === videoData.key)) {
pendingVideoQueue.value.push(videoData);
}
}
};
//
watch(viewLayout, (newLayout) => {
const maxCount = parseInt(newLayout);
watch(viewLayout, (newLayout, oldLayout) => {
const newMaxCount = parseInt(newLayout);
const oldMaxCount = oldLayout ? parseInt(oldLayout) : newMaxCount;
//
if (newMaxCount < oldMaxCount) {
for (let i = newMaxCount; i < oldMaxCount && i < videoList.value.length; i++) {
const video = videoList.value[i];
if (video && !pendingVideoQueue.value.find((v) => v.key === video.key)) {
pendingVideoQueue.value.push(video);
}
videoList.value[i] = null;
}
}
//
while (videoList.value.length < maxCount) {
while (videoList.value.length < newMaxCount) {
videoList.value.push(null);
}
//
if (newMaxCount > oldMaxCount) {
const availableSlots = newMaxCount - oldMaxCount;
for (let i = 0; i < availableSlots && pendingVideoQueue.value.length > 0; i++) {
const emptyIndex = videoList.value.findIndex(
(v, i) => i < newMaxCount && (v === null || v === undefined)
);
if (emptyIndex !== -1) {
const nextVideo = pendingVideoQueue.value.shift();
if (nextVideo) {
videoList.value[emptyIndex] = nextVideo;
}
}
}
}
});
//
@ -408,8 +455,8 @@ const handlePlayRecord = (record: any) => {
if (videoPath) {
const maxCount = parseInt(viewLayout.value);
//
const existingIndex = videoList.value.findIndex((v) => v && v.flnm === record.flnm);
//
const existingIndex = videoList.value.findIndex((v) => v && v.url === videoPath);
if (existingIndex !== -1) {
return;
@ -560,10 +607,11 @@ const initTree = async () => {
},
};
//
//
videoList.value = Array(9).fill(null);
checkedKeys.value = [];
expandedKeys.value = [];
pendingVideoQueue.value = [];
treeLoading.value = true;
try {
@ -679,6 +727,7 @@ const handleMonitorTypeChange = () => {
//
videoList.value = Array(9).fill(null);
checkedKeys.value = [];
pendingVideoQueue.value = [];
}
// 线
@ -729,11 +778,15 @@ const handleTreeSelect = (checkedKeysValue: any[], info: any) => {
// checkedKeys
addParentKeys(nodeData.key);
} else {
// 使 setTimeout Tree
setTimeout(() => {
// 使 filter
checkedKeys.value = checkedKeys.value.filter((key) => key !== nodeData.key);
}, 50);
//
if (!pendingVideoQueue.value.find((v) => v.key === nodeData.key)) {
pendingVideoQueue.value.push({
flnm: nodeData.name,
stnm: nodeData.stnm,
url: videoUrl,
...nodeData,
});
}
}
} else {
}
@ -744,6 +797,17 @@ const handleTreeSelect = (checkedKeysValue: any[], info: any) => {
} else {
//
if (!nodeData.children || nodeData.children.length === 0) {
//
const queueIndex = pendingVideoQueue.value.findIndex(
(v) => v.key === nodeData.key
);
if (queueIndex !== -1) {
//
pendingVideoQueue.value.splice(queueIndex, 1);
removeUnnecessaryParentKeys(nodeData.key);
return;
}
//
removeVideoByNodeKey(nodeData.key);
@ -912,16 +976,23 @@ const addVideosFromNodeWithLimit = (node: any) => {
const remainingSlots = maxCount - currentCount;
if (remainingSlots <= 0) {
//
setTimeout(() => {
cancelNodeCheck(node);
}, 100);
//
allLeafNodes.forEach((leafNode: any) => {
if (!pendingVideoQueue.value.find((v) => v.key === leafNode.key)) {
pendingVideoQueue.value.push({
flnm: leafNode.name,
stnm: leafNode.stnm,
url: leafNode.url || leafNode.videoUrl || leafNode.playUrl,
...leafNode,
});
}
});
return;
}
// remainingSlots
const nodesToAdd = allLeafNodes.slice(0, remainingSlots);
const nodesToCancel = allLeafNodes.slice(remainingSlots);
const nodesToQueue = allLeafNodes.slice(remainingSlots);
//
nodesToAdd.forEach((leafNode: any) => {
@ -938,37 +1009,26 @@ const addVideosFromNodeWithLimit = (node: any) => {
}
});
//
if (nodesToCancel.length > 0) {
setTimeout(() => {
cancelNodesCheck(nodesToCancel);
//
nodesToAdd.forEach((leafNode: any) => {
addParentKeys(leafNode.key);
//
if (nodesToQueue.length > 0) {
nodesToQueue.forEach((leafNode: any) => {
if (!pendingVideoQueue.value.find((v) => v.key === leafNode.key)) {
pendingVideoQueue.value.push({
flnm: leafNode.name,
stnm: leafNode.stnm,
url: leafNode.url || leafNode.videoUrl || leafNode.playUrl,
...leafNode,
});
}, 50);
} else {
//
setTimeout(() => {
nodesToAdd.forEach((leafNode: any) => {
addParentKeys(leafNode.key);
});
}, 50);
}
};
//
const cancelNodeCheck = (node: any) => {
// 使 filter
checkedKeys.value = checkedKeys.value.filter((key) => key !== node.key);
};
//
const cancelNodesCheck = (nodes: any[]) => {
nodes.forEach((node: any) => {
cancelNodeCheck(node);
});
}
//
setTimeout(() => {
nodesToAdd.forEach((leafNode: any) => {
addParentKeys(leafNode.key);
});
}, 50);
};
//
@ -986,9 +1046,20 @@ const removeVideosFromNode = (node: any) => {
// key
const removeVideoByNodeKey = (nodeKey: string) => {
//
const queueIndex = pendingVideoQueue.value.findIndex(
(v) => v.key === nodeKey
);
if (queueIndex !== -1) {
pendingVideoQueue.value.splice(queueIndex, 1);
return;
}
const index = videoList.value.findIndex((video) => video && video.key === nodeKey);
if (index !== -1) {
videoList.value[index] = null;
//
fillEmptySlot(index);
}
};

View File

@ -354,8 +354,8 @@ export const handleCellEdit = (params: any, fWorksheet: any, year: string) => {
);
// 重建该组的所有单元格引用
let groupCells = [];
let currentDayCount = groupStartDayOfYear;
const groupCells = [];
const currentDayCount = groupStartDayOfYear;
let currentMonth = 1;
let cumulativeDays = 0;
@ -389,7 +389,7 @@ export const handleCellEdit = (params: any, fWorksheet: any, year: string) => {
console.log('组中单元格:', groupCells);
// 构建公式
let formula = `=ROUND(AVERAGE(${groupCells.join(',')}), 1)`;
const formula = `=ROUND(AVERAGE(${groupCells.join(',')}), 1)`;
// 找到该组的结束位置用于显示公式
let endMonth = 1;
@ -446,7 +446,7 @@ export const getParams = (searchData: any, Psort?: any, Rstcd?: boolean) => {
return result.slice(1, -1);
};
// if (searchData) {
let params: any = [];
const params: any = [];
// if (sttpCode) {
// params.push(
// {
@ -783,7 +783,7 @@ export const getParams = (searchData: any, Psort?: any, Rstcd?: boolean) => {
].filter((el: any) => el != null);
}
let filter = {
const filter = {
logic: 'and',
filters: params
};
@ -917,7 +917,7 @@ export const calculateCrossMonthAverages = async (
let groupValues: string[] = []; // 存储单元格引用
let groupPositions: { month: number; day: number }[] = []; // 记录每个值的位置
let validDataCount = 0; // 记录当前组中有效数据的数量
let protectedRanges: any[] = [];
const protectedRanges: any[] = [];
for (let month = 1; month <= 12; month++) {
const dataRow = month * 2 + 1; // 当前月份的数据行
const daysInMonth = getDaysInMonth(year, month); // 根据年份获取该月天数
@ -958,7 +958,7 @@ export const calculateCrossMonthAverages = async (
if (validDataCount > 0) {
// 构建平均值公式
const range = groupValues.join(',');
let formula = `=ROUND(AVERAGE(${range}), 1)`;
const formula = `=ROUND(AVERAGE(${range}), 1)`;
// 在组的最后一天显示平均值
const lastPosition = groupPositions[groupValues.length - 1];
@ -989,7 +989,7 @@ export const calculateCrossMonthAverages = async (
if (groupValues.length > 0 && groupValues.length < 5 && validDataCount > 0) {
// 构建平均值公式
const range = groupValues.join(',');
let formula = `=ROUND(AVERAGE(${range}), 1)`;
const formula = `=ROUND(AVERAGE(${range}), 1)`;
// 在组的最后一天显示平均值
const lastPosition = groupPositions[groupValues.length - 1];

View File

@ -0,0 +1,828 @@
<template>
<a-modal
:title="isEdit ? '编辑沿程配置' : '新增沿程配置'"
v-model:open="modalVisible"
:confirm-loading="localLoading"
width="1200px"
:destroy-on-close="true"
ok-text="保存"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-row>
<a-col :span="12">
<a-form-item label="沿程配置名称" name="name">
<a-input
v-model:value="formData.name"
style="width: 100%"
placeholder="请输入沿程配置名称"
/> </a-form-item
></a-col>
<a-col :span="12">
<a-form-item label="沿程配置编码" name="code">
<a-input
v-model:value="formData.code"
style="width: 100%"
placeholder="请输入沿程配置编码"
/>
</a-form-item>
</a-col>
</a-row>
<a-row>
<a-col :span="12">
<a-form-item label="所在河段" name="rvcd">
<a-select
v-model:value="formData.rvcd"
show-search
style="width: 100%"
placeholder="请选择所在河段"
:options="riverOptions"
:filter-option="filterOption"
@change="handleRiverSelect"
:disabled="isEdit"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="备注" name="remark">
<a-textarea
:rows="4"
v-model:value="formData.remark"
style="width: 100%"
placeholder="请输入备注"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
<div class="config-tree-container">
<!-- 新增一级节点区域 -->
<div v-if="addingNewNode" class="add-node-row">
<a-select
v-model:value="newNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleNodeSelect"
>
<a-select-option
v-for="item in stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click="handleSaveNewNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button @click="handleCancelAdd" style="margin-left: 8px">
<MinusOutlined />
</a-button>
</div>
<a-button v-else class="mb-4" type="primary" @click="handleAdd">
<PlusOutlined /> 新增一级节点
</a-button>
<!-- 树形结构 -->
<a-tree
v-if="treeData.length > 0"
:tree-data="treeData"
:default-expand-all="false"
:selectable="false"
block-node
style="width: 400px"
>
<template #title="{ dataRef }">
<!-- 编辑状态只显示编辑区域 -->
<div
v-if="editingNode && editingNodeKey === dataRef.key"
class="add-child-node-row w-[300px]"
>
<a-select
v-model:value="editNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="() => handleEditNodeSelect(dataRef)"
>
<a-select-option
v-for="item in dataRef.parentId
? childStationList
: stationList"
:key="item.stcd"
:value="item.stcd"
>
{{ dataRef.parentId ? item.stnm : item.ennm }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click.stop="handleSaveEditNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button
@click.stop="handleCancelEditNode"
style="margin-left: 8px"
>
<MinusOutlined />
</a-button>
</div>
<!-- 非编辑状态显示正常节点内容 -->
<template v-else>
<div class="tree-node-wrapper">
<span class="tree-node-title"> {{ getNodeTitle(dataRef) }} </span>
<div class="tree-node-actions">
<!-- 一级节点支持新增子节点编辑排序删除 -->
<template v-if="!dataRef.parentId">
<a-button
type="link"
size="small"
@click.stop="handleAddChild(dataRef)"
:title="
dataRef.sttpName
? `子级添加-${dataRef.sttpName}`
: '子级添加'
"
>
<PlusOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleEditNode(dataRef)"
title="编辑"
>
<EditOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveUp(dataRef)"
title="上移"
:disabled="isFirstNode(dataRef)"
>
<ArrowUpOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveDown(dataRef)"
title="下移"
:disabled="isLastNode(dataRef)"
>
<ArrowDownOutlined />
</a-button>
<a-button
type="link"
size="small"
danger
@click.stop="handleDeleteNode(dataRef)"
title="删除"
>
<DeleteOutlined />
</a-button>
</template>
<!-- 二级节点只支持编辑排序删除 -->
<template v-else>
<a-button
type="link"
size="small"
@click.stop="handleEditNode(dataRef)"
title="编辑"
>
<EditOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveUp(dataRef)"
title="上移"
:disabled="isFirstNode(dataRef)"
>
<ArrowUpOutlined />
</a-button>
<a-button
type="link"
size="small"
@click.stop="handleMoveDown(dataRef)"
title="下移"
:disabled="isLastNode(dataRef)"
>
<ArrowDownOutlined />
</a-button>
<a-button
type="link"
size="small"
danger
@click.stop="handleDeleteNode(dataRef)"
title="删除"
>
<DeleteOutlined />
</a-button>
</template>
</div>
</div>
<!-- 子节点添加区域 -->
<div
v-if="addingChildNode && currentParentKey === dataRef.key"
class="add-child-node-row"
>
<a-select
v-model:value="childNodeData.stcd"
show-search
placeholder="请选择站点"
style="width: 300px"
:filter-option="filterOption"
@change="handleChildNodeSelect"
>
<a-select-option
v-for="item in childStationList"
:key="item.stcd"
:value="item.stcd"
>
{{ item.stnm }}-{{ item.sttpName }}
</a-select-option>
</a-select>
<a-button
type="primary"
@click.stop="handleSaveChildNode"
style="margin-left: 8px"
>
保存
</a-button>
<a-button
@click.stop="handleCancelAddChild"
style="margin-left: 8px"
>
<MinusOutlined />
</a-button>
</div>
</template>
</template>
</a-tree>
<a-empty v-else description="暂无数据" style="margin-top: 20px" />
</div>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message, Modal } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import dayjs from 'dayjs';
import {
PlusOutlined,
MinusOutlined,
EditOutlined,
DeleteOutlined,
ArrowUpOutlined,
ArrowDownOutlined
} from '@ant-design/icons-vue';
import { getEngInfoDropdown, getAddvcdDropdownByRvcd } from '@/api/select';
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
riverOptions?: any[];
}
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false,
riverOptions: () => []
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any): void;
}>();
const formRef = ref();
const defaultFormData = reactive({
id: undefined,
name: undefined,
code: undefined,
rvcd: undefined,
remark: undefined
});
const formData: any = reactive({ ...defaultFormData });
const rules: Record<string, Rule[]> = {
name: [{ required: true, message: '请输入沿程配置名称', trigger: 'blur' }],
code: [{ required: true, message: '请输入配置编码', trigger: 'blur' }],
rvcd: [{ required: true, message: '请选择所在河段', trigger: 'change' }]
};
const handleRiverSelect = async (value: string) => {
const res = await getEngInfoDropdown({ rvcd: value });
stationList.value = (res.data || []).map((item: any) => ({
...item,
stnm: item.ennm
}));
};
const isEdit = computed(() => !!props.initialValues);
//
const treeData = ref<any[]>([]);
//
const addingNewNode = ref(false);
const newNodeData = ref({
stcd: undefined,
ennm: '',
sttpName: ''
});
//
const addingChildNode = ref(false);
const currentParentKey = ref<string | null>(null);
const childNodeData = ref({
stcd: undefined,
ennm: '',
sttpName: ''
});
//
const editingNode = ref(false);
const editingNodeKey = ref<string | null>(null);
const editNodeData = ref({
stcd: undefined,
ennm: '',
sttpName: '',
originalData: null as any
});
const stationList = ref([]);
const childStationList = ref([]);
// ID
let nodeIdCounter = 0;
const generateNodeId = () => `node_${Date.now()}_${++nodeIdCounter}`;
//
const getNodeTitle = (dataRef: any) => {
if (!dataRef.parentId) {
return dataRef.ennm || '';
} else {
return `${dataRef.ennm || ''} ${dataRef.sttpName || ''}`;
}
};
//
const filterOption = (inputValue: string, option: any) => {
const label = option.label || option.name || '';
const reachcdName = option.reachcdName || '';
const keyword = inputValue || '';
return label.indexOf(keyword) !== -1 || reachcdName.indexOf(keyword) !== -1;
};
//
const isFirstNode = (dataRef: any) => {
const siblings = getSiblings(dataRef);
return siblings.length > 0 && siblings[0].key === dataRef.key;
};
//
const isLastNode = (dataRef: any) => {
const siblings = getSiblings(dataRef);
return (
siblings.length > 0 && siblings[siblings.length - 1].key === dataRef.key
);
};
//
const getSiblings = (dataRef: any) => {
if (!dataRef.parentId) {
//
return treeData.value;
} else {
// children
const parent = findNodeByKey(treeData.value, dataRef.parentId);
return parent?.children || [];
}
};
// key
const findNodeByKey = (nodes: any[], key: string): any => {
for (const node of nodes) {
if (node.key === key) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findNodeByKey(node.children, key);
if (found) return found;
}
}
return null;
};
//
const handleAdd = () => {
if (!formData.rvcd) {
message.warning('请先选择所在河段');
return;
}
addingNewNode.value = true;
newNodeData.value = {
stcd: undefined,
ennm: '',
sttpName: ''
};
};
//
const handleNodeSelect = (value: string) => {
const station = stationList.value.find(item => item.stcd === value);
if (station) {
newNodeData.value.ennm = station.stnm;
newNodeData.value.sttpName = station.sttpName;
}
};
//
const handleSaveNewNode = () => {
if (!newNodeData.value.stcd) {
message.error('请选择站点');
return;
}
const newNode = {
key: generateNodeId(),
stcd: newNodeData.value.stcd,
ennm: newNodeData.value.ennm,
sttpName: newNodeData.value.sttpName,
parentId: null,
children: []
};
treeData.value.push(newNode);
addingNewNode.value = false;
message.success('添加成功');
};
//
const handleCancelAdd = () => {
addingNewNode.value = false;
newNodeData.value = {
stcd: undefined,
ennm: '',
sttpName: ''
};
};
//
const handleAddChild = async (parentData: any) => {
addingChildNode.value = true;
currentParentKey.value = parentData.key;
childNodeData.value = {
stcd: undefined,
ennm: '',
sttpName: ''
};
const res = await getAddvcdDropdownByRvcd({ stcd: parentData.stcd });
childStationList.value = res.data || [];
};
//
const handleChildNodeSelect = (value: string) => {
const station = childStationList.value.find(item => item.stcd === value);
if (station) {
childNodeData.value.ennm = station.stnm;
childNodeData.value.sttpName = station.sttpName;
}
};
//
const handleSaveChildNode = () => {
if (!childNodeData.value.stcd) {
message.error('请选择站点');
return;
}
const parent = findNodeByKey(treeData.value, currentParentKey.value!);
if (!parent) {
message.error('父节点不存在');
return;
}
const newChild = {
key: generateNodeId(),
stcd: childNodeData.value.stcd,
ennm: childNodeData.value.ennm,
sttpName: childNodeData.value.sttpName,
parentId: parent.key,
children: []
};
if (!parent.children) {
parent.children = [];
}
//
parent.children.unshift(newChild);
addingChildNode.value = false;
currentParentKey.value = null;
message.success('添加子节点成功');
};
//
const handleCancelAddChild = () => {
addingChildNode.value = false;
currentParentKey.value = null;
childNodeData.value = {
stcd: undefined,
ennm: '',
sttpName: ''
};
};
//
const handleEditNode = async (dataRef: any) => {
editingNode.value = true;
editingNodeKey.value = dataRef.key;
//
editNodeData.value = {
stcd: dataRef.stcd,
ennm: dataRef.ennm,
sttpName: dataRef.sttpName,
originalData: { ...dataRef }
};
if (dataRef.parentId) {
const parent = findNodeByKey(treeData.value, dataRef.parentId);
if (parent) {
const res = await getAddvcdDropdownByRvcd({ stcd: parent.stcd });
childStationList.value = res.data || [];
}
}
};
//
const handleEditNodeSelect = (dataRef: any) => {
const value = editNodeData.value.stcd;
const list = dataRef.parentId ? childStationList.value : stationList.value;
const station = list.find(item => item.stcd === value);
if (station) {
editNodeData.value.ennm = station.stnm;
editNodeData.value.sttpName = station.sttpName;
editNodeData.value.stcd = station.stcd;
}
};
//
const handleSaveEditNode = () => {
if (!editNodeData.value.stcd) {
message.error('请选择站点');
return;
}
//
const node = findNodeByKey(treeData.value, editingNodeKey.value!);
if (!node) {
message.error('节点不存在');
return;
}
//
node.stcd = editNodeData.value.stcd;
node.ennm = editNodeData.value.ennm;
node.sttpName = editNodeData.value.sttpName;
editingNode.value = false;
editingNodeKey.value = null;
message.success('编辑成功');
};
//
const handleCancelEditNode = () => {
editingNode.value = false;
editingNodeKey.value = null;
editNodeData.value = {
stcd: undefined,
ennm: '',
sttpName: '',
originalData: null
};
};
//
const handleMoveUp = (dataRef: any) => {
const siblings = getSiblings(dataRef);
const index = siblings.findIndex(item => item.key === dataRef.key);
if (index > 0) {
//
[siblings[index - 1], siblings[index]] = [
siblings[index],
siblings[index - 1]
];
message.success('上移成功');
}
};
//
const handleMoveDown = (dataRef: any) => {
const siblings = getSiblings(dataRef);
const index = siblings.findIndex(item => item.key === dataRef.key);
if (index < siblings.length - 1) {
//
[siblings[index], siblings[index + 1]] = [
siblings[index + 1],
siblings[index]
];
message.success('下移成功');
}
};
//
const handleDeleteNode = (dataRef: any) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除节点"${dataRef.ennm}"吗?`,
okText: '确定',
cancelText: '取消',
onOk: () => {
if (!dataRef.parentId) {
//
const index = treeData.value.findIndex(
item => item.key === dataRef.key
);
if (index > -1) {
treeData.value.splice(index, 1);
}
} else {
//
const parent = findNodeByKey(treeData.value, dataRef.parentId);
if (parent && parent.children) {
const index = parent.children.findIndex(
item => item.key === dataRef.key
);
if (index > -1) {
parent.children.splice(index, 1);
}
}
}
message.success('删除成功');
}
});
};
const handleOk = async () => {
try {
await formRef.value.validate();
const submitValues = {
...formData,
captureDate: formData.captureDate
? dayjs(formData.captureDate).format('YYYY-MM-DD')
: undefined,
configNodes: treeData.value //
};
emit('ok', submitValues);
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = async () => {
if (props.initialValues) {
const values = props.initialValues;
Object.keys(formData).forEach(key => {
if (values.hasOwnProperty(key)) {
if (key === 'captureDate' && values[key]) {
formData[key] = dayjs(values[key]);
} else {
formData[key] = values[key];
}
}
});
//
if (values.configNodes && Array.isArray(values.configNodes)) {
treeData.value = values.configNodes.map((node: any) => ({
...node,
children:
node.children?.map((child: any) => ({
...child
})) || []
}));
} else {
treeData.value = [];
}
//
if (formData.rvcd) {
const res = await getEngInfoDropdown({ rvcd: formData.rvcd });
stationList.value = (res.data || []).map((item: any) => ({
...item,
stnm: item.ennm
}));
}
} else {
resetForm();
}
};
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
treeData.value = [];
addingNewNode.value = false;
};
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
}
},
{ immediate: false }
);
defineExpose({
localLoading
});
</script>
<style scoped lang="scss">
.config-tree-container {
margin-top: 16px;
.add-node-row {
display: flex;
align-items: center;
margin-bottom: 16px;
}
}
.tree-node-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding-right: 8px;
.tree-node-title {
flex: 1;
}
.tree-node-actions {
opacity: 0;
transition: opacity 0.2s;
}
}
//
.tree-node-wrapper:hover {
.tree-node-actions {
opacity: 1;
}
}
//
.add-child-node-row {
display: flex;
align-items: center;
padding: 8px 0 8px 24px;
margin-top: 4px;
background-color: #f5f5f5;
border-radius: 4px;
}
</style>

View File

@ -0,0 +1,83 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增配置">
<a-button @click="props.handleAdd" type="primary">
新增沿程配置
</a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import BasicSearch from '@/components/BasicSearch/index.vue';
interface Props {
handleAdd: () => void;
riverOptions: any[];
riverOptionsLoad: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: 'reset', values: any): void;
(e: 'searchFinish', values: any): void;
}>();
const initSearchData = {
name: null,
rvcd: null
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: 'Input',
name: 'name',
label: '沿程配置名称',
fieldProps: {
allowClear: true
}
},
{
type: 'Select',
name: 'rvcd',
label: '所在河段',
width: 300,
fieldProps: {
allowClear: true
},
options: props.riverOptions,
loading: props.riverOptionsLoad
}
]);
const onSearchFinish = (values: any) => {
emit('searchFinish', values);
};
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
const handleReset = () => {
emit('reset', initSearchData);
};
onMounted(() => {
emit('searchFinish', initSearchData);
});
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,379 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\ConfigManagement\index.vue -->
<template>
<div class="content">
<ConfigManagementSearch
ref="configManagementSearch"
:handle-add="handleAdd"
:river-options="riverOptions"
:river-options-load="riverOptionsLoad"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
ref="basicTable"
:columns="columns"
:list-url="getAllConfigTree"
:search-params="searchParams"
>
<template #action="{ record }">
<div class="flex gap-[6px]">
<a-button
class="!p-0"
type="link"
size="small"
@click="handleEdit(record)"
>编辑</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</div>
</template>
</BasicTable>
<ConfigManagementForm
ref="configManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
:river-options="riverOptions"
:river-options-load="riverOptionsLoad"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import ConfigManagementSearch from './ConfigManagementSearch.vue';
import ConfigManagementForm from './ConfigManagementForm.vue';
import { message, Modal } from 'ant-design-vue';
import {
getAllConfigTree,
deleteBaseWbsb,
saveBaseWbsbChild,
saveBaseWbsbChildDetail,
getChildConfigTree
} from '@/api/system/map/ConfigManagement';
import { getRvcdDropdown } from '@/api/select';
import dayjs from 'dayjs';
//
const basicTable = ref<any>(null);
//
const columns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
width: 60,
align: 'center',
customRender: ({ index }) => index + 1
},
{
title: '沿程配置名称',
dataIndex: 'name',
key: 'name',
width: 150
},
{
title: '配置编码',
key: 'code',
dataIndex: 'code',
width: 120
},
{
title: '所在河段',
dataIndex: 'rvnm',
key: 'rvnm',
width: 300,
ellipsis: true
},
{
title: '创建人',
key: 'recordUser',
width: 120,
dataIndex: 'recordUser'
},
{
title: '创建时间',
key: 'recordTime',
dataIndex: 'recordTime',
width: 150
},
{
title: '更新时间',
key: 'modifyTime',
dataIndex: 'modifyTime',
width: 150
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
width: 360
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const searchParams = ref({
"sort": [
{
"field": "recordTime",
"dir": "desc"
}
],
});
//
const currentRecord = ref<any | null>(null);
const editModalVisible = ref(false);
//
const handleAdd = () => {
currentRecord.value = null;
editModalVisible.value = true;
};
// '-'
const filterRecord = (record: any) => {
const filtered: any = {};
Object.keys(record).forEach(key => {
if (record[key] !== '-') {
filtered[key] = record[key];
}
});
return filtered;
};
//
const handleEdit = async (record: any) => {
currentRecord.value = filterRecord(record);
const params = {
filter: {
logic: 'and',
filters: [
{
field: 'alongId',
operator: 'eq',
value: record.id
}
]
},
sort: [
{
field: 'sort',
dir: 'asc'
}
]
};
const res = await getChildConfigTree(params);
if (res.code == 0) {
const data = res.data.data || [];
const firstLevelNodes = data.filter((item: any) => !item.rstcd);
const secondLevelNodes = data.filter((item: any) => item.rstcd);
const configNodes = firstLevelNodes.map((node: any, index: number) => {
const children = secondLevelNodes.filter(
(child: any) => child.rstcd === node.stcd
);
return {
key: `parent-${index}`,
stcd: node.stcd,
ennm: node.stnm,
sttpName: node.sttpName,
parentId: null,
children: children.map((child: any, childIndex: number) => ({
key: `child-${index}-${childIndex}`,
stcd: child.stcd,
ennm: child.stnm,
sttpName: child.sttpName,
parentId: `parent-${index}`,
children: []
}))
};
});
currentRecord.value.configNodes = configNodes;
}
editModalVisible.value = true;
};
//
const handleDelete = (record: any) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除选中的记录吗?',
zIndex: 2002,
onOk: async () => {
try {
let res = await deleteBaseWbsb([record.id]);
if (res.code == 0) {
message.success('删除成功');
basicTable.value.refresh();
} else {
message.error('删除失败');
}
} catch (error) {
message.error('删除失败');
return;
}
}
});
};
//
const onSearchFinish = (values: any) => {
const params = {
logic: 'and',
filters: [
values.rvcd
? {
field: 'rvcd',
operator: 'eq',
dataType: 'string',
value: values.rvcd
}
: null,
values.name
? {
field: 'name',
operator: 'contains',
dataType: 'string',
value: values.name
}
: null
].filter(Boolean)
};
basicTable.value.getList(params);
};
//
const handleReset = () => {
basicTable.value.getList({
logic: 'and',
filters: []
});
};
//
const editModalCancel = () => {
currentRecord.value = null;
editModalVisible.value = false;
};
//
const handleEditSubmit = async (values: any) => {
try {
const formData = {
...currentRecord.value,
...values,
modifyTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
};
delete formData.configNodes;
Object.keys(formData).forEach(key => {
if (key.includes('-')) {
delete formData[key];
}
});
let res = await saveBaseWbsbChild(formData);
if (res.code == 0) {
const alongId = res.data;
const treeData = values.configNodes || [];
if (treeData.length > 0) {
const formatTreeData = (
nodes: any[],
parentId: string | null = null,
parentStcd: string | null = null
) => {
return nodes.map(node => ({
alongId,
stcd: node.stcd,
stnm: node.ennm || node.stnm,
sttpName: node.sttpName,
parentId: parentId ? parentId : null,
rstcd: parentStcd || null,
children:
node.children && node.children.length > 0
? formatTreeData(node.children, node.key, node.stcd)
: []
}));
};
const detailData = formatTreeData(treeData);
const detailRes = await saveBaseWbsbChildDetail(detailData);
if (detailRes.code == 0) {
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
} else {
message.error(`保存子节点失败`);
}
} else {
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
}
} else {
message.error(`保存失败`);
}
} catch (error) {
message.error(`保存失败`);
editModalVisible.value = false;
return;
}
};
const riverOptions = ref<any>([]);
const riverOptionsLoad = ref(false);
const initOption = () => {
const params = {};
riverOptionsLoad.value = true;
getRvcdDropdown(params).then((res: any) => {
res.data.forEach((item: any) => {
item.value = item.rvcd;
item.label = item.rvnm;
});
riverOptions.value = res.data || [];
riverOptionsLoad.value = false;
});
};
onMounted(() => {
initOption();
});
</script>
<style scoped lang="scss">
.content {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
background-color: #ffffff;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
overflow: hidden;
}
.config-management {
height: 100%;
padding: 16px;
}
</style>

View File

@ -0,0 +1,160 @@
<template>
<a-modal
:title="isEdit ? '编辑电站' : '新增电站'"
v-model:open="modalVisible"
:confirm-loading="localLoading"
width="600px"
:destroy-on-close="true"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-form-item label="电站名称" name="stationName">
<a-input
v-model:value="formData.stationName"
style="width: 100%"
placeholder="请输入电站名称"
/>
</a-form-item>
<a-form-item label="所属流域" name="basin">
<a-input
v-model:value="formData.basin"
style="width: 100%"
placeholder="请输入所属流域"
/>
</a-form-item>
<a-form-item label="装机容量(MW)" name="capacity">
<a-input-number
v-model:value="formData.capacity"
style="width: 100%"
placeholder="请输入装机容量"
:min="0"
/>
</a-form-item>
<a-form-item label="状态" name="status">
<a-select v-model:value="formData.status" style="width: 100%">
<a-select-option value="running">运行中</a-select-option>
<a-select-option value="stopped">停运</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="位置坐标" name="coordinates">
<a-input
v-model:value="formData.coordinates"
style="width: 100%"
placeholder="请输入位置坐标"
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
}
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any): void;
}>();
const formRef = ref();
const defaultFormData = reactive({
id: undefined,
stationName: undefined,
basin: undefined,
capacity: undefined,
status: undefined,
coordinates: undefined
});
const formData: any = reactive({ ...defaultFormData });
const rules: Record<string, Rule[]> = {
stationName: [{ required: true, message: '请输入电站名称', trigger: 'blur' }],
basin: [{ required: true, message: '请输入所属流域', trigger: 'blur' }],
capacity: [{ required: true, message: '请输入装机容量', trigger: 'blur' }]
};
const isEdit = computed(() => !!props.initialValues);
const handleOk = async () => {
try {
await formRef.value.validate();
const submitValues = {
...formData
};
emit('ok', submitValues);
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = () => {
if (props.initialValues) {
const values = props.initialValues;
Object.keys(formData).forEach(key => {
if (values.hasOwnProperty(key)) {
formData[key] = values[key];
}
});
} else {
resetForm();
}
};
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
};
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
}
},
{ immediate: false }
);
defineExpose({
localLoading
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,67 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增电站">
<a-button @click="props.handleAdd" type="primary"> 新增电站 </a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue";
interface Props {
handleAdd: () => void;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
}>();
const initSearchData = {
stationName: "",
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "stationName",
label: "电站名称",
fieldProps: {
allowClear: true,
},
},
]);
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
};
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
const handleReset = () => {
emit("reset", initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
});
</script>
<style lang="scss"></style>

View File

@ -0,0 +1,203 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\StationManagement\index.vue -->
<template>
<div class="content">
<StationManagementSearch
ref="stationManagementSearch"
:handle-add="handleAdd"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
:columns="columns"
:data="dataSource"
:list-url="fetchStationData"
:search-params="searchParams"
:enable-row-selection="true"
@selection-change="handleSelectionChange"
>
<template #action="{ column, record }">
<a-space>
<a-button type="link" size="small" @click="handleEdit(record)"
>编辑</a-button
>
<a-button
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</a-space>
</template>
<template #status="{ column, record }">
<a-tag :color="record.status === 'running' ? 'green' : 'red'">
{{ record.status === 'running' ? '运行中' : '停运' }}
</a-tag>
</template>
</BasicTable>
<StationManagementForm
ref="stationManagementForm"
v-model:visible="editModalVisible"
:initial-values="currentRecord"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import StationManagementSearch from './StationManagementSearch.vue';
import StationManagementForm from './StationManagementForm.vue';
import { message } from 'ant-design-vue';
//
const columns = [
{
title: '电站名称',
dataIndex: 'stationName',
key: 'stationName',
width: 150
},
{
title: '所属流域',
dataIndex: 'basin',
key: 'basin',
width: 120
},
{
title: '装机容量(MW)',
dataIndex: 'capacity',
key: 'capacity',
width: 120
},
{
title: '状态',
key: 'status',
dataIndex: 'status',
width: 100,
slots: { customRender: 'status' }
},
{
title: '位置坐标',
dataIndex: 'coordinates',
key: 'coordinates',
ellipsis: true
},
{
title: '操作',
key: 'action',
width: 150,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const searchParams = ref({});
//
const currentRecord = ref<any | null>(null);
const editModalVisible = ref(false);
//
const dataSource = ref([]);
//
const fetchStationData = (params: any) => {
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: {
records: [
{
id: 1,
stationName: '三峡电站',
basin: '长江流域',
capacity: 22500,
status: 'running',
coordinates: '111.29,30.83'
},
{
id: 2,
stationName: '葛洲坝电站',
basin: '长江流域',
capacity: 2715,
status: 'running',
coordinates: '111.28,30.75'
},
{
id: 3,
stationName: '溪洛渡电站',
basin: '金沙江流域',
capacity: 13860,
status: 'stopped',
coordinates: '103.65,28.25'
}
],
total: 3
}
});
}, 500);
});
};
const handleAdd = () => {
currentRecord.value = null;
editModalVisible.value = true;
};
//
const handleEdit = (record: any) => {
currentRecord.value = { ...record };
editModalVisible.value = true;
};
//
const handleDelete = (record: any) => {
message.warning(`删除电站: ${record.stationName}`);
};
//
const onSearchFinish = (values: any) => {
console.log(values);
};
//
const handleReset = (params: any) => {};
//
const handleSelectionChange = (selectedRows: any[]) => {
console.log('选中的行:', selectedRows);
};
//
const editModalCancel = () => {
editModalVisible.value = false;
};
//
const handleEditSubmit = (values: any) => {
console.log(values);
};
</script>
<style scoped lang="scss">
.content {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
background-color: #ffffff;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
overflow: hidden;
}
.station-management {
height: 100%;
padding: 16px;
}
</style>

View File

@ -0,0 +1,249 @@
<template>
<a-modal
:title="isEdit ? '编辑倾斜摄影' : '新增倾斜摄影'"
v-model:open="modalVisible"
:confirm-loading="localLoading"
width="600px"
:destroy-on-close="true"
ok-text="保存"
@cancel="handleCancel"
@ok="handleOk"
>
<a-form
ref="formRef"
:model="formData"
:rules="rules"
name="edit_form"
:labelCol="{ span: 5 }"
>
<a-form-item label="倾斜影像名称" name="stnm">
<a-input
v-model:value="formData.stnm"
style="width: 100%"
placeholder="请输入倾斜影像名称"
/>
</a-form-item>
<a-form-item label="所在河段" name="hbrvcd">
<a-select
v-model:value="formData.hbrvcd"
show-search
style="width: 100%"
placeholder="请选择所在河段"
:options="riverOptions"
:filter-option="filterOption"
:disabled="isEdit"
@change="handleRiverSelect"
/>
</a-form-item>
<a-form-item label="所属电站" name="rstcd">
<a-select
v-model:value="formData.rstcd"
show-search
style="width: 100%"
placeholder="请选择所属电站"
:options="ennmOptions"
:filter-option="filterOption"
:disabled="isEdit"
/>
<!-- <a-input
v-model:value="formData.ennm"
style="width: 100%"
placeholder="请输入所属电站"
/> -->
</a-form-item>
<a-form-item label="倾斜影像URL" name="url">
<a-input
v-model:value="formData.url"
style="width: 100%"
placeholder="请输入倾斜影像URL"
/>
</a-form-item>
<a-form-item label="模型坐标" name="location">
<a-textarea
v-model:value="formData.location"
:rows="4"
style="width: 100%"
placeholder="请输入模型坐标"
/>
</a-form-item>
<a-form-item label="模型精度" name="accuracy">
<a-input-number
v-model:value="formData.accuracy"
style="width: 100%"
placeholder="请输入模型精度"
:min="0"
/>
</a-form-item>
<a-form-item label="边界范围" name="boundary">
<a-textarea
v-model:value="formData.boundary"
:rows="4"
style="width: 100%"
placeholder="请输入边界范围"
/>
</a-form-item>
<a-form-item label="模型高度" name="height">
<a-input-number
v-model:value="formData.height"
style="width: 100%"
placeholder="请输入模型高度"
:min="0"
/>
</a-form-item>
<a-form-item label="备注" name="comments">
<a-textarea
v-model:value="formData.comments"
:rows="4"
style="width: 100%"
placeholder="请输入备注"
/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, watch } from 'vue';
import { message } from 'ant-design-vue';
import type { Rule } from 'ant-design-vue/es/form';
import dayjs from 'dayjs';
import { getEngInfoDropdown } from '@/api/select';
interface Props {
visible: boolean;
initialValues?: any | null;
loading?: boolean;
riverOptions?: any[];
}
const localLoading = ref(false);
const props = withDefaults(defineProps<Props>(), {
visible: false,
initialValues: null,
loading: false,
riverOptions: () => []
});
const modalVisible = computed({
get: () => props.visible,
set: val => emit('update:visible', val)
});
const emit = defineEmits<{
(e: 'update:visible', value: boolean): void;
(e: 'cancel'): void;
(e: 'ok', values: any): void;
}>();
const formRef = ref();
const defaultFormData = reactive({
id: undefined,
stnm: undefined,
hbrvcd: undefined,
rstcd: undefined,
url: undefined,
location: undefined,
accuracy: undefined,
boundary: undefined,
height: undefined,
comments: undefined
});
const formData: any = reactive({ ...defaultFormData });
const rules: Record<string, Rule[]> = {
stnm: [{ required: true, message: '请输入倾斜影像名称', trigger: 'blur' }],
hbrvcd: [{ required: true, message: '请输入所在河段', trigger: 'blur' }],
rstcd: [{ required: true, message: '请输入所属电站', trigger: 'blur' }],
url: [{ required: true, message: '请输入倾斜影像URL', trigger: 'blur' }],
location: [{ required: true, message: '请输入模型坐标', trigger: 'blur' }]
};
const isEdit = computed(() => !!props.initialValues);
const handleOk = async () => {
try {
await formRef.value.validate();
const ennmItem = ennmOptions.value.find(item => item.value == formData.rstcd);
const riverItem = props.riverOptions.find(item => item.value == formData.hbrvcd);
const submitValues = {
...formData,
ennm: ennmItem?.ennm || '',
hbrvcdName: riverItem?.label || '',
captureDate: formData.captureDate
? dayjs(formData.captureDate).format('YYYY-MM-DD')
: undefined,
};
emit('ok', submitValues);
} catch (error) {
console.error('Validate Failed:', error);
message.error('请检查表单填写是否正确');
}
};
const initForm = () => {
if (props.initialValues) {
const values = props.initialValues;
Object.keys(formData).forEach(key => {
if (values.hasOwnProperty(key)) {
if (key === 'captureDate' && values[key]) {
formData[key] = dayjs(values[key]);
} else {
formData[key] = values[key];
}
}
});
} else {
resetForm();
}
};
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, defaultFormData);
};
const handleCancel = () => {
emit('update:visible', false);
emit('cancel');
resetForm();
};
//
const filterOption = (inputValue: string, option: any) => {
const label = option.label || option.name || '';
const reachcdName = option.reachcdName || '';
const keyword = inputValue || '';
return label.indexOf(keyword) !== -1 || reachcdName.indexOf(keyword) !== -1;
};
const ennmOptions = ref([]);
const handleRiverSelect = async (value: any) => {
let res: any = await getEngInfoDropdown({ reachcd: value });
if (res?.data?.length > 0) {
res.data.forEach((item: any) => {
item.value = item.stcd;
item.label = item.ennm;
});
ennmOptions.value = res.data;
} else {
ennmOptions.value = [];
}
};
watch(
() => props.visible,
newVisible => {
if (newVisible) {
initForm();
handleRiverSelect('');
}
},
{ immediate: false }
);
defineExpose({
localLoading
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,81 @@
<template>
<div class="content-search">
<BasicSearch
ref="basicSearchRef"
:searchList="searchList"
:initial-values="initSearchData"
@reset="handleReset"
@finish="onSearchFinish"
@values-change="onValuesChange"
>
<template #actions>
<a-tooltip title="新增倾斜摄影">
<a-button @click="props.handleAdd" type="primary"> 新增倾斜摄影 </a-button>
</a-tooltip>
</template>
</BasicSearch>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import BasicSearch from "@/components/BasicSearch/index.vue";
interface Props {
handleAdd: () => void;
riverOptions: any[];
riverOptionsLoad: boolean;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "reset", values: any): void;
(e: "searchFinish", values: any): void;
}>();
const initSearchData = {
stnm: "",
rvcd:""
};
const searchData = ref<any>({ ...initSearchData });
const searchList: any = computed(() => [
{
type: "Input",
name: "stnm",
label: "倾斜影像名称",
fieldProps: {
allowClear: true,
},
},
{
type: 'Select',
name: 'rvcd',
label: '所在河段',
width: 300,
fieldProps: {
allowClear: true
},
options: props.riverOptions,
loading: props.riverOptionsLoad
}
]);
const onSearchFinish = (values: any) => {
emit("searchFinish", values);
};
const onValuesChange = (changedValues: any, allValues: any) => {
searchData.value = { ...searchData.value, ...allValues };
};
const handleReset = () => {
emit("reset", initSearchData);
};
onMounted(() => {
emit("searchFinish", initSearchData);
});
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,269 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\TiltPhotoManagement\index.vue -->
<template>
<div class="content">
<TiltPhotoManagementSearch
ref="tiltPhotoManagementSearch"
:river-options="riverOptions"
:river-options-load="riverOptionsLoad"
:handle-add="handleAdd"
@reset="handleReset"
@search-finish="onSearchFinish"
/>
<BasicTable
ref="basicTable"
:columns="columns"
:list-url="getAllTiltPhotoTree"
:search-params="searchParams"
>
<template #action="{ column, record }">
<div class="flex gap-[6px]">
<a-button
class="!p-0"
type="link"
size="small"
@click="handleEdit(record)"
>编辑</a-button
>
<a-button
class="!p-0"
type="link"
size="small"
danger
@click="handleDelete(record)"
>删除</a-button
>
</div>
</template>
</BasicTable>
<TiltPhotoManagementForm
ref="tiltPhotoManagementForm"
v-model:visible="editModalVisible"
:river-options="riverOptions"
:river-options-load="riverOptionsLoad"
:initial-values="currentRecord"
@cancel="editModalCancel"
@ok="handleEditSubmit"
/>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, onMounted } from 'vue';
import BasicTable from '@/components/BasicTable/index.vue';
import TiltPhotoManagementSearch from './TiltPhotoManagementSearch.vue';
import TiltPhotoManagementForm from './TiltPhotoManagementForm.vue';
import { message, Modal } from 'ant-design-vue';
import {
getAllTiltPhotoTree,
saveTiltPhoto,
deleteTiltPhoto
} from '@/api/system/map/TiltPhotoManagement';
import { getRvcdDropdown } from '@/api/select';
//
const columns = [
{
title: '序号',
dataIndex: 'index',
key: 'index',
width: 60,
align: 'center',
customRender: ({ text, record, index }) => index + 1
},
{
title: '倾斜影像名称',
dataIndex: 'stnm',
key: 'stnm',
width: 150
},
{
title: 'URL',
dataIndex: 'url',
key: 'url'
// width: 360
},
{
title: '所在河段',
dataIndex: 'hbrvcdName',
key: 'hbrvcdName',
width: 280
},
{
title: '所属电站',
key: 'ennm',
dataIndex: 'ennm',
width: 140
},
{
title: '创建人',
key: 'recordUser',
dataIndex: 'recordUser',
width: 150
},
{
title: '创建时间',
key: 'recordTime',
dataIndex: 'recordTime',
width: 150
},
{
title: '更新时间',
key: 'modifyTime',
dataIndex: 'modifyTime',
width: 150
},
{
title: '操作',
key: 'action',
width: 120,
fixed: 'right',
slots: { customRender: 'action' }
}
];
//
const basicTable = ref<any>(null);
//
const searchParams = ref({});
//
const currentRecord = ref<any | null>(null);
const editModalVisible = ref(false);
//
const handleAdd = () => {
currentRecord.value = null;
editModalVisible.value = true;
};
//
const handleEdit = (record: any) => {
currentRecord.value = { ...record };
editModalVisible.value = true;
};
//
const handleDelete = (record: any) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除选中的记录吗?',
zIndex: 2002,
onOk: async () => {
try {
let res = await deleteTiltPhoto({ id: record.id });
message.success('删除成功');
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');
return;
}
}
});
};
//
const onSearchFinish = (values: any) => {
const params = {
logic: 'and',
filters: [
values.parentId
? {
field: 'parentId',
operator: 'eq',
dataType: 'string',
value: values.parentId
}
: null,
values.stnm
? {
field: 'stnm',
operator: 'contains',
dataType: 'string',
value: values.stnm
}
: null,
values.rvcd
? {
field: 'hbrvcd',
operator: 'contains',
dataType: 'string',
value: values.rvcd
}
: null
].filter(Boolean)
};
basicTable.value.getList(params);
};
//
const handleReset = () => {
nextTick(() => {
basicTable.value.getList({
logic: 'and',
filters: []
});
});
};
//
const editModalCancel = () => {
currentRecord.value = null;
editModalVisible.value = false;
};
//
const handleEditSubmit = async (values: any) => {
try {
let res = await saveTiltPhoto({
...currentRecord.value,
...values,
recordTime:currentRecord.value.recordTime != '-' ? currentRecord.value.recordTime : null
});
message.success(`保存成功`);
editModalVisible.value = false;
basicTable.value.refresh();
} catch (error) {
message.error(`保存失败`);
editModalVisible.value = false;
return;
}
};
const riverOptions = ref<any>([]);
const riverOptionsLoad = ref(false);
const initOption = () => {
const params = {};
riverOptionsLoad.value = true;
getRvcdDropdown(params).then((res: any) => {
res.data.forEach((item: any) => {
item.value = item.rvcd;
item.label = item.rvnm;
});
riverOptions.value = res.data || [];
riverOptionsLoad.value = false;
});
};
onMounted(() => {
initOption();
});
</script>
<style scoped lang="scss">
.tilt-photo-management {
height: 100%;
padding: 16px;
}
.content {
position: relative;
z-index: 900;
pointer-events: all;
width: 100%;
height: 100%;
background-color: #ffffff;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
overflow: hidden;
padding: 16px;
}
</style>

View File

@ -20,6 +20,7 @@
v-model:value="formData.title"
style="width: 100%"
placeholder="请输入图层名称"
:maxlength="50"
/>
</a-form-item>
<a-form-item label="图层编码" name="code">

View File

@ -265,7 +265,10 @@ const handleDelete = async (record: any) => {
onOk: async () => {
try {
let res = await deleteMapLayer([record.id]);
if(res.code == 0){
message.success('删除成功');
}
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');

View File

@ -250,7 +250,9 @@ const handleDelete = (record: any) => {
onOk: async () => {
try {
let res = await deleteMapLegend([record.id]);
if (res.code == 0) {
message.success('删除成功');
}
basicTable.value.refresh();
} catch (error) {
message.error('删除失败');
@ -375,11 +377,13 @@ const handleEditSubmit = async (values: any, type: string) => {
...values,
parentId: parentId.value
});
if (res.code == 0) {
if (type === 'switch') {
message.success('修改状态成功');
} else {
message.success(`保存成功`);
}
}
editModalVisible.value = false;
basicTable.value.refresh();
} catch (error) {

View File

@ -27,18 +27,18 @@ const tabs = [
name: "图例数据管理",
value: "legendData",
},
{
name: "电站专题管理",
value: "station",
},
{
name: "沿程配置管理",
value: "config",
},
{
name: "倾斜摄影管理",
value: "tilt",
},
// {
// name: "",
// value: "station",
// },
// {
// name: "沿",
// value: "config",
// },
// {
// name: "",
// value: "tilt",
// },
];
// tab

View File

@ -1,7 +1,7 @@
<!-- d:\wordpack\WholeProcessPlatform\frontend\src\views\system\map\components\ConfigManagement\index.vue -->
<template>
<div class="content">
推送配置
<!-- 推送配置 -->
<!-- <ConfigManagementSearch
ref="configManagementSearch"
:handle-add="handleAdd"