水温监测右侧弹框接口对接
This commit is contained in:
parent
42bd4c66a0
commit
ca83d4be94
@ -1,7 +1,65 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
export function getKendoListCust(data: any) {
|
export function getKendoListCust(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/sw/alongList/qgc/GetKendoListCust',
|
url: '/api/wmp-env-server/sw/alongList/qgc/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取水温下拉框
|
||||||
|
export function wbsbGetKendoList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/wbsb/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取垂向水温变化树形站点数据
|
||||||
|
* @param data 请求参数
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
export function getChuiXiangShuiWenTreeStcd(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/base/sdrvwts/default/treeStcd',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取垂向水温变化列表数据(按月份聚合)
|
||||||
|
* @param data 请求参数 { filter, sort }
|
||||||
|
* @returns Promise
|
||||||
|
*/
|
||||||
|
export function getCxswList(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/sw/dzCxList/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//获取出入库水温下拉选则树
|
||||||
|
export function getVmsstbprpt(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/vmsstbprpt/GetKendoList',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//出入库水温图表数据
|
||||||
|
//
|
||||||
|
export function inOutOneGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/wmp-env-server/sw/inOutOne/GetKendoListCust',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//水温监测工作开展情况
|
||||||
|
export function baseEvnmAutoMonitorGetKendoListCust(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/api/dec-lygk-base-server/base/evnmAutoMonitor/GetKendoListCust',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
|
|||||||
@ -26,32 +26,52 @@
|
|||||||
<div v-if="select.show">
|
<div v-if="select.show">
|
||||||
<a-select v-model:value="selectValue" show-search placeholder="请选择" :size="'small'"
|
<a-select v-model:value="selectValue" show-search placeholder="请选择" :size="'small'"
|
||||||
style="width: 120px" :options="select.options" :filter-option="filterOption"
|
style="width: 120px" :options="select.options" :filter-option="filterOption"
|
||||||
@focus="handleFocus" @blur="handleBlur" @change="handleChange"></a-select>
|
@focus="handleFocus" @blur="handleBlur" @change="handleChange" ></a-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
<div v-if="shrink" class="title_shrink" @click="isExpand = !isExpand">
|
||||||
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="">
|
<img v-if="isExpand" src="@/assets/components/arrow-up.png" alt="">
|
||||||
<img v-else src="@/assets/components/arrow-down.png" alt="">
|
<img v-else src="@/assets/components/arrow-down.png" alt="">
|
||||||
</div>
|
</div>
|
||||||
<div v-if="moreSelect.show">
|
<div v-if="moreSelect.show">
|
||||||
<a-tree-select v-model:value="moreSelectValue" show-search
|
<a-tree-select v-model:value="moreSelectValue" show-search :size="'small'" style="width: 110px"
|
||||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" placeholder="Please select"
|
:dropdown-style="{ maxHeight: '400px', overflow: 'auto',minWidth: '180px' }" placeholder=" "
|
||||||
allow-clear tree-default-expand-all :tree-data="moreSelect.options"
|
:tree-data="processedMoreSelectOptions"
|
||||||
tree-node-filter-prop="label">
|
:field-names="{ label: 'title', value: 'value', children: 'children' }"
|
||||||
|
tree-node-filter-prop="label"
|
||||||
|
:expanded-keys="expandedKeys"
|
||||||
|
popup-class-name="no-wrap-tree-select"
|
||||||
|
@select="handleTreeSelect"
|
||||||
|
@expand="handleTreeExpand"
|
||||||
|
@dropdownVisibleChange="handleDropdownVisibleChange">
|
||||||
</a-tree-select>
|
</a-tree-select>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="datetimePicker.show">
|
<div v-if="datetimePicker.show">
|
||||||
<!-- 添加 locale 属性来设置语言 -->
|
<!-- 添加 locale 属性来设置语言 -->
|
||||||
<a-date-picker v-model:value="datetimeValue" show-time
|
<a-date-picker
|
||||||
:style="{ width: datetimePicker.picker === 'year' ? '80px' : '120px' }"
|
v-model:value="datetimeValue"
|
||||||
|
show-time
|
||||||
|
:style="{ width: datetimePicker.picker === 'year' ? '80px' : '130px' }"
|
||||||
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
|
:format="datetimePicker.format !== null ? datetimePicker.format : undefined"
|
||||||
:picker="datetimePicker.picker" placeholder=" " @change="handleDateTimeChange"
|
:picker="datetimePicker.picker"
|
||||||
:size="'small'" />
|
:allowClear="false"
|
||||||
|
placeholder=" "
|
||||||
|
@change="handleDateTimeChange"
|
||||||
|
:size="'small'"
|
||||||
|
:disabledDate="createDisabledDateFn(datetimePicker.picker)"
|
||||||
|
:disabledTime="disabledTimeForSinglePicker" />
|
||||||
<!-- 修改为 locale 变量 -->
|
<!-- 修改为 locale 变量 -->
|
||||||
</div>
|
</div>
|
||||||
<div v-if="scopeDate.show" class="title_scopeDate">
|
<div v-if="scopeDate.show" class="title_scopeDate">
|
||||||
<a-range-picker v-model:value="scopeDateValue" :picker="scopeDate.picker"
|
<a-range-picker
|
||||||
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '150px' : '') }"
|
v-model:value="scopeDateValue"
|
||||||
:format="scopeDate.format" :range-separator="' 至 '" :size="'small'" />
|
:picker="scopeDate.picker"
|
||||||
|
:allowClear="false"
|
||||||
|
:style="{ width: scopeDate.picker === 'year' ? '80px' : (scopeDate.picker === 'month' ? '180px' : '') }"
|
||||||
|
:format="scopeDate.format"
|
||||||
|
:range-separator="' 至 '"
|
||||||
|
:size="'small'"
|
||||||
|
:presets="computedScopeDatePresets"
|
||||||
|
:disabledDate="createDisabledDateFn(scopeDate.picker)" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tabs.show" class="typeOne">
|
<div v-if="tabs.show" class="typeOne">
|
||||||
<div @click="handleTabClick('one')" :class="tabsValue == 'one' ? 'typezhong' : ''">图片</div>
|
<div @click="handleTabClick('one')" :class="tabsValue == 'one' ? 'typezhong' : ''">图片</div>
|
||||||
@ -66,7 +86,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, watch, computed } from 'vue';
|
||||||
import {
|
import {
|
||||||
QuestionCircleOutlined,
|
QuestionCircleOutlined,
|
||||||
InfoCircleOutlined
|
InfoCircleOutlined
|
||||||
@ -169,10 +189,10 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['tab-change']);
|
const emit = defineEmits(['tab-change', 'update-values']);
|
||||||
const isExpand = ref(true);
|
const isExpand = ref(true);
|
||||||
const selectValue = ref(props.select.value)
|
const selectValue = ref(props.select.value)
|
||||||
const moreSelectValue = ref(props.select.value)
|
const moreSelectValue = ref(props.moreSelect.value)
|
||||||
const datetimeValue = ref<Dayjs | null>(props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null);
|
const datetimeValue = ref<Dayjs | null>(props.datetimePicker.value ? dayjs(props.datetimePicker.value) : null);
|
||||||
const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
|
const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
|
||||||
props.scopeDate.value && Array.isArray(props.scopeDate.value)
|
props.scopeDate.value && Array.isArray(props.scopeDate.value)
|
||||||
@ -180,21 +200,338 @@ const scopeDateValue = ref<[Dayjs, Dayjs] | undefined>(
|
|||||||
: undefined
|
: undefined
|
||||||
);
|
);
|
||||||
const tabsValue = ref(props.tabs.value)
|
const tabsValue = ref(props.tabs.value)
|
||||||
|
|
||||||
|
// 树选择器展开状态管理
|
||||||
|
const expandedKeys = ref<string[]>([]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建针对不同 picker 类型的日期禁用函数
|
||||||
|
* @param pickerType - 选择器类型: year | month | quarter | week | date
|
||||||
|
* @returns disabledDate 回调函数
|
||||||
|
*/
|
||||||
|
const createDisabledDateFn = (pickerType: string) => {
|
||||||
|
return (current: Dayjs) => {
|
||||||
|
if (!current) return false
|
||||||
|
|
||||||
|
const now = dayjs()
|
||||||
|
|
||||||
|
switch (pickerType) {
|
||||||
|
case 'year':
|
||||||
|
// 年份选择器:禁用 > 当前年份
|
||||||
|
return current.year() > now.year()
|
||||||
|
|
||||||
|
case 'month':
|
||||||
|
// 月份选择器:禁用 > 当前年月
|
||||||
|
return current.isAfter(now, 'month')
|
||||||
|
|
||||||
|
case 'quarter':
|
||||||
|
// 季度选择器:禁用 > 当前季度
|
||||||
|
return current.isAfter(now, 'quarter')
|
||||||
|
|
||||||
|
case 'week':
|
||||||
|
// 周选择器:禁用 > 当前周
|
||||||
|
return current.isAfter(now, 'week')
|
||||||
|
|
||||||
|
case 'date':
|
||||||
|
default:
|
||||||
|
// 日期选择器:禁用 > 当前日期
|
||||||
|
return current.isAfter(now, 'day')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单日期选择器的时间禁用函数(针对 show-time)
|
||||||
|
* 精确限制到当前时刻的时分秒
|
||||||
|
*/
|
||||||
|
const disabledTimeForSinglePicker = () => {
|
||||||
|
// 只在 datetimePicker 显示且为 date 类型时生效
|
||||||
|
if (!props.datetimePicker.show || props.datetimePicker.picker !== 'date') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = dayjs()
|
||||||
|
const selectedDate = datetimeValue.value
|
||||||
|
|
||||||
|
// 如果未选择日期,或选择的不是今天,则不限制时间
|
||||||
|
if (!selectedDate || !selectedDate.isSame(now, 'day')) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:生成从 start 到 end-1 的数组
|
||||||
|
const range = (start: number, end: number) => {
|
||||||
|
const result: number[] = []
|
||||||
|
for (let i = start; i < end; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// 禁用当前小时之后的所有小时
|
||||||
|
disabledHours: () => {
|
||||||
|
return range(now.hour() + 1, 24)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 禁用当前分钟之后的所有分钟(仅在当前小时)
|
||||||
|
disabledMinutes: (selectedHour: number) => {
|
||||||
|
if (selectedHour === now.hour()) {
|
||||||
|
return range(now.minute() + 1, 60)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 禁用当前秒数之后的所有秒数(仅在当前小时和分钟)
|
||||||
|
disabledSeconds: (selectedHour: number, selectedMinute: number) => {
|
||||||
|
if (selectedHour === now.hour() && selectedMinute === now.minute()) {
|
||||||
|
return range(now.second() + 1, 60)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // 定义 locale 变量
|
// // 定义 locale 变量
|
||||||
// const locale = zhCN;
|
// const locale = zhCN;
|
||||||
|
|
||||||
// console.log(locale, "zhCN");
|
// console.log(locale, "zhCN");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归处理树数据,为父节点添加 selectable: false
|
||||||
|
* @param data 原始树数据
|
||||||
|
* @returns 处理后的树数据
|
||||||
|
*/
|
||||||
|
const processTreeData = (data: any[]): any[] => {
|
||||||
|
if (!data || !Array.isArray(data)) return [];
|
||||||
|
|
||||||
|
return data.map(item => {
|
||||||
|
const newItem = { ...item };
|
||||||
|
|
||||||
|
// 如果有子项,标记为不可选中
|
||||||
|
if (newItem.children && newItem.children.length > 0) {
|
||||||
|
newItem.selectable = false;
|
||||||
|
// 递归处理子项
|
||||||
|
newItem.children = processTreeData(newItem.children);
|
||||||
|
} else {
|
||||||
|
// 叶子节点默认可选中(显式声明更清晰)
|
||||||
|
newItem.selectable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newItem;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算属性:处理后的树数据
|
||||||
|
const processedMoreSelectOptions = computed(() => {
|
||||||
|
return processTreeData(props.moreSelect.options || []);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算属性:根据 picker 类型动态生成快捷日期选项
|
||||||
|
*/
|
||||||
|
const computedScopeDatePresets = computed(() => {
|
||||||
|
const now = dayjs();
|
||||||
|
const presets: Array<{ label: string; value: [Dayjs, Dayjs] }> = [];
|
||||||
|
|
||||||
|
// 根据 picker 类型生成对应的快捷选项
|
||||||
|
switch (props.scopeDate.picker) {
|
||||||
|
case 'year':
|
||||||
|
// 年份选择器:只显示今年
|
||||||
|
presets.push({
|
||||||
|
label: '今年',
|
||||||
|
value: [now.startOf('year'), now]
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'month':
|
||||||
|
// 月份选择器:显示今天、昨天、最近七天、最近一个月、最近三个月、今年
|
||||||
|
presets.push(
|
||||||
|
{
|
||||||
|
label: '今天',
|
||||||
|
value: [now.startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '昨天',
|
||||||
|
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近七天',
|
||||||
|
value: [now.subtract(6, 'day').startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近一个月',
|
||||||
|
value: [now.subtract(1, 'month').startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近三个月',
|
||||||
|
value: [now.subtract(3, 'month').startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '今年',
|
||||||
|
value: [now.startOf('year'), now.endOf('day')]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'quarter':
|
||||||
|
// 季度选择器:显示最近一季度、今年
|
||||||
|
presets.push(
|
||||||
|
{
|
||||||
|
label: '最近一季度',
|
||||||
|
value: [now.subtract(1, 'quarter').startOf('quarter'), now.endOf('quarter')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '今年',
|
||||||
|
value: [now.startOf('year'), now.endOf('quarter')]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'week':
|
||||||
|
// 周选择器:显示本周、上周、最近四周
|
||||||
|
presets.push(
|
||||||
|
{
|
||||||
|
label: '本周',
|
||||||
|
value: [now.startOf('week'), now.endOf('week')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '上周',
|
||||||
|
value: [now.subtract(1, 'week').startOf('week'), now.subtract(1, 'week').endOf('week')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近四周',
|
||||||
|
value: [now.subtract(3, 'week').startOf('week'), now.endOf('week')]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'date':
|
||||||
|
default:
|
||||||
|
// 日期选择器:显示所有常用选项
|
||||||
|
presets.push(
|
||||||
|
{
|
||||||
|
label: '今天',
|
||||||
|
value: [now.startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '昨天',
|
||||||
|
value: [now.subtract(1, 'day').startOf('day'), now.subtract(1, 'day').endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近七天',
|
||||||
|
value: [now.subtract(6, 'day').startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近一个月',
|
||||||
|
value: [now.subtract(1, 'month').startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '最近三个月',
|
||||||
|
value: [now.subtract(3, 'month').startOf('day'), now.endOf('day')]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '今年',
|
||||||
|
value: [now.startOf('year'), now.endOf('day')]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return presets;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理树节点选择事件
|
||||||
|
* 由于父节点已设置 selectable: false,这里只会接收到叶子节点
|
||||||
|
* @param selectedKeys 选中的键值(可能是字符串或数组)
|
||||||
|
* @param info 节点信息对象(本身就是节点,不是包含 node 的对象)
|
||||||
|
*/
|
||||||
|
const handleTreeSelect = (selectedKeys: string | string[], info: any) => {
|
||||||
|
// selectedKeys 可能是字符串(单选模式)或数组(多选模式)
|
||||||
|
let selectedValue: string;
|
||||||
|
|
||||||
|
if (typeof selectedKeys === 'string') {
|
||||||
|
// 单选模式:selectedKeys 直接就是字符串
|
||||||
|
selectedValue = selectedKeys;
|
||||||
|
} else if (Array.isArray(selectedKeys)) {
|
||||||
|
// 多选模式:取第一个元素
|
||||||
|
selectedValue = selectedKeys[0];
|
||||||
|
} else {
|
||||||
|
// 兜底:尝试从 info 中获取
|
||||||
|
selectedValue = info?.value || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
moreSelectValue.value = selectedValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理树节点展开/收起事件
|
||||||
|
* @param keys 展开的节点键值数组
|
||||||
|
*/
|
||||||
|
const handleTreeExpand = (keys: string[]) => {
|
||||||
|
expandedKeys.value = keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找节点的父节点路径
|
||||||
|
* @param treeData 树数据
|
||||||
|
* @param targetValue 目标节点的值
|
||||||
|
* @returns 父节点值的数组(从根到直接父节点)
|
||||||
|
*/
|
||||||
|
const findParentKeys = (treeData: any[], targetValue: string): string[] => {
|
||||||
|
const parentKeys: string[] = [];
|
||||||
|
|
||||||
|
const search = (nodes: any[], target: string, parents: string[]): boolean => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.value === target) {
|
||||||
|
// 找到目标节点,记录所有父节点
|
||||||
|
parentKeys.push(...parents);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
// 递归搜索子节点,将当前节点加入父节点列表
|
||||||
|
if (search(node.children, target, [...parents, node.value])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
search(treeData, targetValue, []);
|
||||||
|
return parentKeys;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理下拉框显示/隐藏变化事件
|
||||||
|
* 当下拉框打开且有选中值时,自动展开该节点的所有父节点
|
||||||
|
* @param visible 下拉框是否可见
|
||||||
|
*/
|
||||||
|
const handleDropdownVisibleChange = (visible: boolean) => {
|
||||||
|
if (visible && moreSelectValue.value && processedMoreSelectOptions.value.length > 0) {
|
||||||
|
// 查找选中节点的所有父节点
|
||||||
|
const parentKeys = findParentKeys(processedMoreSelectOptions.value, moreSelectValue.value);
|
||||||
|
|
||||||
|
if (parentKeys.length > 0) {
|
||||||
|
// 合并现有的展开节点和父节点,去重
|
||||||
|
const mergedKeys = Array.from(new Set([...expandedKeys.value, ...parentKeys]));
|
||||||
|
expandedKeys.value = mergedKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 下拉选择框事件处理
|
// 下拉选择框事件处理
|
||||||
const handleChange = (value: string) => {
|
const handleChange = (value: string) => {
|
||||||
console.log(`selected ${value}`);
|
// Handle change
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBlur = () => {
|
const handleBlur = () => {
|
||||||
console.log('blur');
|
// Handle blur
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFocus = () => {
|
const handleFocus = () => {
|
||||||
console.log('focus');
|
// Handle focus
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterOption = (input: string, option?: { value: string }) => {
|
const filterOption = (input: string, option?: { value: string }) => {
|
||||||
@ -209,7 +546,7 @@ const getPopupContainer = (trigger: HTMLElement) => {
|
|||||||
|
|
||||||
//时间选择框事件处理
|
//时间选择框事件处理
|
||||||
const handleDateTimeChange = (date: any | null, dateString: string) => {
|
const handleDateTimeChange = (date: any | null, dateString: string) => {
|
||||||
console.log('Selected DateTime:', date, dateString);
|
// Handle date time change
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTabClick = (value: string) => {
|
const handleTabClick = (value: string) => {
|
||||||
@ -220,9 +557,137 @@ const handleTabClick = (value: string) => {
|
|||||||
tabLabel: value === 'one' ? '图片' : '视频'
|
tabLabel: value === 'one' ? '图片' : '视频'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 统一收集并发送所有控件的值
|
||||||
|
const emitAllValues = () => {
|
||||||
|
const payload: any = {
|
||||||
|
select: selectValue.value,
|
||||||
|
moreSelect: moreSelectValue.value,
|
||||||
|
tabs: tabsValue.value,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理单个时间选择器
|
||||||
|
if (datetimeValue.value) {
|
||||||
|
// 使用传入的 format,如果没有则根据 picker 类型设置默认格式
|
||||||
|
const format = props.datetimePicker.format || getDefaultFormat(props.datetimePicker.picker);
|
||||||
|
payload.datetime = datetimeValue.value.format(format);
|
||||||
|
} else {
|
||||||
|
payload.datetime = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理时间范围选择器
|
||||||
|
if (scopeDateValue.value && Array.isArray(scopeDateValue.value)) {
|
||||||
|
// 使用传入的 format,如果没有则根据 picker 类型设置默认格式
|
||||||
|
const format = props.scopeDate.format || getDefaultFormat(props.scopeDate.picker);
|
||||||
|
payload.scopeDate = scopeDateValue.value.map(d => d ? d.format(format) : null);
|
||||||
|
} else {
|
||||||
|
payload.scopeDate = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('update-values', payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据 picker 类型获取默认格式
|
||||||
|
const getDefaultFormat = (picker: string): string => {
|
||||||
|
switch (picker) {
|
||||||
|
case 'year':
|
||||||
|
return 'YYYY';
|
||||||
|
case 'month':
|
||||||
|
return 'YYYY-MM';
|
||||||
|
case 'quarter':
|
||||||
|
return 'YYYY-[Q]Q';
|
||||||
|
case 'week':
|
||||||
|
return 'YYYY-wo';
|
||||||
|
case 'date':
|
||||||
|
default:
|
||||||
|
return 'YYYY-MM-DD';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听各个变量的变化
|
||||||
|
watch([selectValue, moreSelectValue, datetimeValue, scopeDateValue, tabsValue], () => {
|
||||||
|
emitAllValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听 props 变化,实现父子组件数据同步
|
||||||
|
watch(
|
||||||
|
() => props.select.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal !== selectValue.value) {
|
||||||
|
selectValue.value = newVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.moreSelect.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal !== moreSelectValue.value) {
|
||||||
|
moreSelectValue.value = newVal;
|
||||||
|
|
||||||
|
// 如果有新值且树数据已加载,自动展开其父节点
|
||||||
|
if (newVal && processedMoreSelectOptions.value.length > 0) {
|
||||||
|
const parentKeys = findParentKeys(processedMoreSelectOptions.value, newVal);
|
||||||
|
if (parentKeys.length > 0) {
|
||||||
|
expandedKeys.value = Array.from(new Set([...expandedKeys.value, ...parentKeys]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.datetimePicker.value,
|
||||||
|
(newVal) => {
|
||||||
|
const newDayjs = newVal ? dayjs(newVal) : null;
|
||||||
|
if (!datetimeValue.value || !newDayjs || !datetimeValue.value.isSame(newDayjs)) {
|
||||||
|
datetimeValue.value = newDayjs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.scopeDate.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal && Array.isArray(newVal) && newVal.length === 2) {
|
||||||
|
const newRange: [Dayjs, Dayjs] = [dayjs(newVal[0]), dayjs(newVal[1])];
|
||||||
|
const currentRange = scopeDateValue.value;
|
||||||
|
|
||||||
|
// 检查是否需要更新(避免不必要的触发)
|
||||||
|
if (!currentRange ||
|
||||||
|
!currentRange[0].isSame(newRange[0]) ||
|
||||||
|
!currentRange[1].isSame(newRange[1])) {
|
||||||
|
scopeDateValue.value = newRange;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (scopeDateValue.value) {
|
||||||
|
scopeDateValue.value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.tabs.value,
|
||||||
|
(newVal) => {
|
||||||
|
if (newVal !== tabsValue.value) {
|
||||||
|
tabsValue.value = newVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 页面加载时执行的逻辑
|
// 页面加载时执行的逻辑
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log(props.select);
|
// 初始化时发送一次默认值
|
||||||
|
emitAllValues();
|
||||||
|
|
||||||
|
// 如果 tree-select 有默认值,预先展开其父节点
|
||||||
|
if (moreSelectValue.value && processedMoreSelectOptions.value.length > 0) {
|
||||||
|
const parentKeys = findParentKeys(processedMoreSelectOptions.value, moreSelectValue.value);
|
||||||
|
if (parentKeys.length > 0) {
|
||||||
|
expandedKeys.value = parentKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -338,4 +803,4 @@ onMounted(() => {
|
|||||||
padding: 0px !important;
|
padding: 0px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,150 +1,429 @@
|
|||||||
<!-- 垂向水温变化图 -->
|
<!-- 垂向水温变化图 -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="垂向水温变化">
|
<SidePanelItem title="垂向水温变化" :moreSelect="select" :datetimePicker="datetimePicker"
|
||||||
|
@update-values="handlePanelChange1">
|
||||||
<div class="chart-wrapper">
|
<div class="chart-wrapper">
|
||||||
<!-- 图例与翻页控制 -->
|
|
||||||
<div class="chart-header">
|
|
||||||
<div class="legend-container">
|
|
||||||
<div
|
|
||||||
v-for="item in currentLegendItems"
|
|
||||||
:key="item.name"
|
|
||||||
class="legend-item"
|
|
||||||
:class="{ inactive: legendInactiveSet.has(item.name) }"
|
|
||||||
@click="toggleLegend(item.name)"
|
|
||||||
>
|
|
||||||
<span class="legend-dot" :style="{ backgroundColor: legendInactiveSet.has(item.name) ? '#ccc' : item.color }"></span>
|
|
||||||
<span class="legend-text" :style="{ color: legendInactiveSet.has(item.name) ? '#ccc' : '#333' }">{{ item.name }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="pagination">
|
|
||||||
<span class="page-btn" :class="{ disabled: currentPage === 1 }" @click="prevPage">◀</span>
|
|
||||||
<span class="page-info">{{ currentPage }}/{{ totalPages }}</span>
|
|
||||||
<span class="page-btn" :class="{ disabled: currentPage === totalPages }" @click="nextPage">▶</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 图表容器 -->
|
<!-- 图表容器 -->
|
||||||
<div ref="chartRef" class="chart-container"></div>
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
|
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div>
|
||||||
|
<div v-show="!showemit && !loading" class="chart-container">
|
||||||
|
<a-empty />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
</div>
|
</div>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import type { ECharts } from 'echarts';
|
import type { ECharts } from 'echarts';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
|
import { getChuiXiangShuiWenTreeStcd, getCxswList } from "@/api/sw";
|
||||||
|
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
|
const baseid = ref('');
|
||||||
|
|
||||||
// 定义组件名
|
// 定义组件名
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'chuixiangshuiwenChangeMod'
|
name: 'chuixiangshuiwenChangeMod'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ==================== 加载状态与显示控制 ====================
|
||||||
|
const showemit = ref(false); // 控制图表是否显示
|
||||||
|
const loading = ref(false); // 控制加载状态
|
||||||
|
|
||||||
|
// ==================== 工具函数 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 智能刻度计算函数
|
||||||
|
*/
|
||||||
|
function chartMax(
|
||||||
|
targetTickCount: number,
|
||||||
|
max: number,
|
||||||
|
min: number,
|
||||||
|
round: boolean = true,
|
||||||
|
base: number = 0
|
||||||
|
): { min: number; max: number; scale: number } {
|
||||||
|
const range = max - min;
|
||||||
|
|
||||||
|
if (range === 0) {
|
||||||
|
const expandedMax = max * 1.1;
|
||||||
|
const expandedMin = min * 0.9;
|
||||||
|
return {
|
||||||
|
min: expandedMin,
|
||||||
|
max: expandedMax,
|
||||||
|
scale: (expandedMax - expandedMin) / targetTickCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let idealScale = range / targetTickCount;
|
||||||
|
const magnitude = Math.pow(10, Math.floor(Math.log10(idealScale)));
|
||||||
|
const normalized = idealScale / magnitude;
|
||||||
|
|
||||||
|
let friendlyScale: number;
|
||||||
|
if (normalized < 1.5) {
|
||||||
|
friendlyScale = 1 * magnitude;
|
||||||
|
} else if (normalized < 3) {
|
||||||
|
friendlyScale = 2 * magnitude;
|
||||||
|
} else if (normalized < 7) {
|
||||||
|
friendlyScale = 5 * magnitude;
|
||||||
|
} else {
|
||||||
|
friendlyScale = 10 * magnitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newMin = Math.floor(min / friendlyScale) * friendlyScale;
|
||||||
|
let newMax = Math.ceil(max / friendlyScale) * friendlyScale;
|
||||||
|
|
||||||
|
if (newMin > min) newMin -= friendlyScale;
|
||||||
|
if (newMax < max) newMax += friendlyScale;
|
||||||
|
|
||||||
|
if (round) {
|
||||||
|
newMin = Math.round(newMin);
|
||||||
|
newMax = Math.round(newMax);
|
||||||
|
friendlyScale = Math.round(friendlyScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
min: newMin,
|
||||||
|
max: newMax,
|
||||||
|
scale: friendlyScale
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组最大值
|
||||||
|
*/
|
||||||
|
function arrMax(arr: number[]): number {
|
||||||
|
return Math.max(...arr.filter(v => v !== null && v !== undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组最小值
|
||||||
|
*/
|
||||||
|
function arrMin(arr: number[]): number {
|
||||||
|
return Math.min(...arr.filter(v => v !== null && v !== undefined));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 API 返回的 aggregates 数据转换为图表数据格式
|
||||||
|
*/
|
||||||
|
function transformAggregatesToChartData(aggregates: Record<string, any[]>): Array<{
|
||||||
|
name: string;
|
||||||
|
dataXy: Array<[number, number]>;
|
||||||
|
}> {
|
||||||
|
const result: Array<{ name: string; dataXy: Array<[number, number]> }> = [];
|
||||||
|
|
||||||
|
for (let month = 1; month <= 12; month++) {
|
||||||
|
const key = month.toString();
|
||||||
|
const monthData = aggregates[key];
|
||||||
|
|
||||||
|
// ✅ 不跳过空月份,而是创建空数组(与 React 版本保持一致)
|
||||||
|
if (!monthData || monthData.length === 0) {
|
||||||
|
result.push({
|
||||||
|
name: `${month}月`,
|
||||||
|
dataXy: [] // 空数组,ECharts 会显示为断开
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换数据格式:{ wthg, vwt } → [vwt, wthg]
|
||||||
|
const dataXy: Array<[number, number]> = monthData.map((item: any) => [
|
||||||
|
parseFloat(item.vwt), // 温度
|
||||||
|
parseInt(item.wthg) // 深度/高程
|
||||||
|
]);
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
name: `${month}月`,
|
||||||
|
dataXy
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据当前年月生成颜色和图例显隐状态
|
||||||
|
*/
|
||||||
|
function generateColorsAndVisibility(
|
||||||
|
dataLength: number, // ⚠️ 现在固定传入 12
|
||||||
|
sDate: string | number
|
||||||
|
): {
|
||||||
|
colors: string[];
|
||||||
|
selected: Record<string, boolean>;
|
||||||
|
} {
|
||||||
|
const baseColors = [
|
||||||
|
'#6ca4f7', '#78c300', '#9556a4', '#df91ab',
|
||||||
|
'#7399c6', '#dbb629', '#56c2e3', '#f56a06',
|
||||||
|
'#cdba75', '#76523b', '#df75b6', '#00a050'
|
||||||
|
];
|
||||||
|
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const currentMonth = new Date().getMonth() + 1;
|
||||||
|
const selectedYear = typeof sDate === 'string' ? parseInt(sDate) : sDate;
|
||||||
|
|
||||||
|
const colors: string[] = [];
|
||||||
|
const selected: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
// ✅ 修改:固定循环 12 次,而非 dataLength
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
const monthName = `${i + 1}月`;
|
||||||
|
|
||||||
|
if (selectedYear < currentYear) {
|
||||||
|
// 历史年份:所有月份正常显示
|
||||||
|
colors.push(baseColors[i]);
|
||||||
|
selected[monthName] = true;
|
||||||
|
} else {
|
||||||
|
// 当前年份:未来月份灰色且隐藏
|
||||||
|
if (i + 1 < currentMonth) {
|
||||||
|
colors.push(baseColors[i]);
|
||||||
|
selected[monthName] = true;
|
||||||
|
} else {
|
||||||
|
colors.push('#D3D3D3');
|
||||||
|
selected[monthName] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { colors, selected };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 原有逻辑保持不变 ====================
|
||||||
|
|
||||||
|
const getCurrentHourTime = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const year = now.getFullYear();
|
||||||
|
return `${year}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCurrentHourTimeTwo = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const endTime = new Date(now);
|
||||||
|
endTime.setHours(endTime.getHours() - 1);
|
||||||
|
endTime.setMinutes(0);
|
||||||
|
endTime.setSeconds(0);
|
||||||
|
endTime.setMilliseconds(0);
|
||||||
|
|
||||||
|
const startTime = new Date(endTime);
|
||||||
|
startTime.setMonth(startTime.getMonth() - 1);
|
||||||
|
|
||||||
|
const formatDateTime = (date) => {
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('开始时间:', formatDateTime(startTime));
|
||||||
|
console.log('结束时间:', formatDateTime(endTime));
|
||||||
|
return [formatDateTime(startTime), formatDateTime(endTime)];
|
||||||
|
};
|
||||||
|
|
||||||
|
const select = ref({
|
||||||
|
show: true,
|
||||||
|
value: '',
|
||||||
|
options: [],
|
||||||
|
picker: undefined,
|
||||||
|
format: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const datetimePicker = ref({
|
||||||
|
show: true,
|
||||||
|
value: computed(() => getCurrentHourTime()),
|
||||||
|
format: "YYYY",
|
||||||
|
picker: "year" as const,
|
||||||
|
options: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取站点下拉选项数据
|
||||||
|
*/
|
||||||
|
const selectOptions = async () => {
|
||||||
|
loading.value = true; // 开始加载站点数据
|
||||||
|
|
||||||
|
try {
|
||||||
|
let filters = [
|
||||||
|
{
|
||||||
|
"field": "mway",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "tm",
|
||||||
|
"operator": "gte",
|
||||||
|
"dataType": "date",
|
||||||
|
"value": getCurrentHourTimeTwo()[0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "tm",
|
||||||
|
"operator": "lte",
|
||||||
|
"dataType": "date",
|
||||||
|
"value": getCurrentHourTimeTwo()[1]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
|
filters.push({
|
||||||
|
"field": "baseId",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": baseid.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let filter = {
|
||||||
|
"logic": "and",
|
||||||
|
"filters": filters
|
||||||
|
};
|
||||||
|
|
||||||
|
let sort = baseid.value && baseid.value !== 'all' ? [
|
||||||
|
{
|
||||||
|
"field": "rstcdStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
}
|
||||||
|
] : [
|
||||||
|
{
|
||||||
|
"field": "baseId",
|
||||||
|
"dir": "asc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "rstcdStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const data = { filter, sort };
|
||||||
|
const res = await getChuiXiangShuiWenTreeStcd(data);
|
||||||
|
let dataOne = res?.data?.data || res.data
|
||||||
|
|
||||||
|
if (dataOne.length > 0) {
|
||||||
|
const treeData = dataOne.map(item => {
|
||||||
|
return {
|
||||||
|
title: item.ennm,
|
||||||
|
value: item.rstcd,
|
||||||
|
selectable: false,
|
||||||
|
children: (item.stcdVo || []).map(child => ({
|
||||||
|
title: child.stnm,
|
||||||
|
value: child.stcd
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (baseid.value == 'all') {
|
||||||
|
select.value.value = '008640203800001014'
|
||||||
|
select.value.options = treeData;
|
||||||
|
} else {
|
||||||
|
select.value.value = treeData[0].children[0].value
|
||||||
|
select.value.options = treeData;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
select.value.options = []
|
||||||
|
select.value.value = ''
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取站点数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false; // 关闭loading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 新增图表相关状态 ====================
|
||||||
|
|
||||||
const chartRef = ref<HTMLElement | null>(null);
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
let chartInstance: ECharts | null = null;
|
let chartInstance: ECharts | null = null;
|
||||||
|
|
||||||
// 月份配置
|
// 图表数据
|
||||||
const ITEMS_PER_PAGE = 5; // 每页显示5个月份
|
const chartData = ref<Array<{ name: string; dataXy: Array<[number, number]> }>>([]);
|
||||||
const currentPage = ref(1);
|
|
||||||
|
|
||||||
// 图例 inactive 状态集合
|
// Y轴类型(1=水深,2=高程)
|
||||||
const legendInactiveSet = ref<Set<string>>(new Set());
|
const type = ref(1);
|
||||||
|
|
||||||
// 12个月份的颜色配置(与设计稿一致)
|
// 颜色和图例状态
|
||||||
const monthColors: Record<string, string> = {
|
const colors = ref<string[]>([]);
|
||||||
'1月': '#5B9BD5',
|
const selected = ref<Record<string, boolean>>({});
|
||||||
'2月': '#70AD47',
|
|
||||||
'3月': '#7030A0',
|
|
||||||
'4月': '#ED7D31',
|
|
||||||
'5月': '#A5A5A5',
|
|
||||||
'6月': '#FFC000',
|
|
||||||
'7月': '#4472C4',
|
|
||||||
'8月': '#548235',
|
|
||||||
'9月': '#6F35A0',
|
|
||||||
'10月': '#C55A11',
|
|
||||||
'11月': '#8497B0',
|
|
||||||
'12月': '#7F7F7F'
|
|
||||||
};
|
|
||||||
|
|
||||||
// 生成12个月份的图例数据
|
// 加载状态
|
||||||
const allLegendItems = ref(
|
const dataLoading = ref(false);
|
||||||
Object.keys(monthColors).map(month => ({
|
|
||||||
name: month,
|
|
||||||
color: monthColors[month]
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
// 计算总页数
|
// ==================== 数据获取逻辑 ====================
|
||||||
const totalPages = computed(() => {
|
|
||||||
return Math.ceil(allLegendItems.value.length / ITEMS_PER_PAGE);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 当前页的图例项
|
const fetchChartData = async (stcd: string, year: string | number) => {
|
||||||
const currentLegendItems = computed(() => {
|
if (loading.value) return; // 防止重复请求
|
||||||
const start = (currentPage.value - 1) * ITEMS_PER_PAGE;
|
loading.value = true; // 开始请求前设置loading
|
||||||
const end = start + ITEMS_PER_PAGE;
|
|
||||||
return allLegendItems.value.slice(start, end);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 静态数据 - 深度数组
|
try {
|
||||||
const depths = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: "and",
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
field: "year",
|
||||||
|
operator: "eq",
|
||||||
|
dataType: "string",
|
||||||
|
value: String(year)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "stcd",
|
||||||
|
operator: "eq",
|
||||||
|
dataType: "string",
|
||||||
|
value: stcd
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
sort: [
|
||||||
|
{
|
||||||
|
field: "depth",
|
||||||
|
dir: "asc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
// 生成月份数据(模拟真实水温变化特征)
|
const res = await getCxswList(params);
|
||||||
const generateMonthData = (monthIndex: number) => {
|
const aggregates = res?.data?.aggregates;
|
||||||
const baseSurfaceTemp = 2 + monthIndex * 2.2;
|
|
||||||
const deepWaterTemp = 3.5 + monthIndex * 0.3;
|
if (aggregates) {
|
||||||
|
// 转换数据
|
||||||
return depths.map((depth, index) => {
|
chartData.value = transformAggregatesToChartData(aggregates);
|
||||||
let temp;
|
|
||||||
|
// ✅ 修改:传入固定值 12,而非 chartData.value.length
|
||||||
if (depth <= 10) {
|
const { colors: newColors, selected: newSelected } = generateColorsAndVisibility(
|
||||||
temp = baseSurfaceTemp - depth * 0.15 + Math.sin(index * 0.8) * 0.5;
|
12, // ⚠️ 固定传入 12
|
||||||
} else if (depth <= 40) {
|
year
|
||||||
const middleFactor = (depth - 10) / 30;
|
);
|
||||||
temp = baseSurfaceTemp - 1.5 - middleFactor * (baseSurfaceTemp - deepWaterTemp) * 0.6;
|
colors.value = newColors;
|
||||||
|
selected.value = newSelected;
|
||||||
|
|
||||||
|
// 根据数据长度决定showemit
|
||||||
|
if (chartData.value.length > 0 && chartData.value.some(item => item.dataXy.length > 0)) {
|
||||||
|
showemit.value = true; // 有数据则显示图表
|
||||||
|
} else {
|
||||||
|
showemit.value = false; // 无数据显示空状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新图表
|
||||||
|
await nextTick();
|
||||||
|
updateChart();
|
||||||
} else {
|
} else {
|
||||||
temp = deepWaterTemp + Math.sin(index * 0.3) * 0.2;
|
chartData.value = [];
|
||||||
|
colors.value = [];
|
||||||
|
selected.value = {};
|
||||||
|
showemit.value = false; // 无数据显示空状态
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
return Math.max(0, Math.round(temp * 100) / 100);
|
// console.error('获取图表数据失败:', error);
|
||||||
});
|
chartData.value = [];
|
||||||
};
|
showemit.value = false; // 出错也显示空状态
|
||||||
|
} finally {
|
||||||
// 生成所有月份的静态数据,格式为 [温度, 深度] 的二维数组
|
loading.value = false; // 无论成功失败都关闭loading
|
||||||
const monthData = ref(
|
|
||||||
Array.from({ length: 12 }, (_, i) => ({
|
|
||||||
month: `${i + 1}月`,
|
|
||||||
data: generateMonthData(i).map((temp, idx) => [temp, depths[idx]]) // [温度, 深度]
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
// 切换图例显示/隐藏
|
|
||||||
const toggleLegend = (name: string) => {
|
|
||||||
if (legendInactiveSet.value.has(name)) {
|
|
||||||
legendInactiveSet.value.delete(name);
|
|
||||||
} else {
|
|
||||||
legendInactiveSet.value.add(name);
|
|
||||||
}
|
|
||||||
updateChart();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 上一页
|
|
||||||
const prevPage = () => {
|
|
||||||
if (currentPage.value > 1) {
|
|
||||||
currentPage.value--;
|
|
||||||
updateChart();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 下一页
|
// ==================== 图表初始化和更新 ====================
|
||||||
const nextPage = () => {
|
|
||||||
if (currentPage.value < totalPages.value) {
|
|
||||||
currentPage.value++;
|
|
||||||
updateChart();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化图表
|
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
if (!chartRef.value) return;
|
if (!chartRef.value) return;
|
||||||
|
|
||||||
@ -155,186 +434,207 @@ const initChart = () => {
|
|||||||
|
|
||||||
chartInstance = echarts.init(chartRef.value);
|
chartInstance = echarts.init(chartRef.value);
|
||||||
updateChart();
|
updateChart();
|
||||||
|
|
||||||
// 绑定图例点击事件 (如果需要与ECharts内部状态同步,虽然这里主要用自定义图例)
|
|
||||||
// chartInstance.on('legendselectchanged', (params: any) => { ... });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 更新图表
|
|
||||||
const updateChart = () => {
|
const updateChart = () => {
|
||||||
if (!chartInstance) return;
|
if (!chartInstance) return;
|
||||||
|
|
||||||
const option = {
|
// ===== 数据预处理 =====
|
||||||
|
const xData: number[] = [];
|
||||||
|
const yData: any[] = [];
|
||||||
|
const legend: string[] = [];
|
||||||
|
|
||||||
|
chartData.value.forEach((item) => {
|
||||||
|
if (item.dataXy.length > 0) {
|
||||||
|
xData.push(item.dataXy[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
legend.push(item.name);
|
||||||
|
|
||||||
|
yData.push({
|
||||||
|
name: item.name,
|
||||||
|
data: item.dataXy,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
connectNulls: true,
|
||||||
|
symbolSize: 2
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== 坐标轴范围计算 =====
|
||||||
|
const maxy1 = arrMax(xData);
|
||||||
|
const miny1 = arrMin(xData);
|
||||||
|
|
||||||
|
const y1 = chartMax(
|
||||||
|
5,
|
||||||
|
miny1 === maxy1 ? maxy1 * 1.1 : maxy1,
|
||||||
|
miny1 === maxy1 ? miny1 * 0.9 : miny1,
|
||||||
|
true,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
// ✅ X 轴固定从 0 开始
|
||||||
|
const curMin = 0;
|
||||||
|
|
||||||
|
// ===== 构建图表配置 =====
|
||||||
|
const option: any = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
show: true,
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
|
||||||
borderColor: '#ccc',
|
|
||||||
borderWidth: 1,
|
|
||||||
textStyle: {
|
|
||||||
color: '#333',
|
|
||||||
fontSize: 12
|
|
||||||
},
|
|
||||||
formatter: (params: any) => {
|
formatter: (params: any) => {
|
||||||
if (!params || !params.seriesName) return '';
|
if (!params || !params.seriesName) return '';
|
||||||
|
|
||||||
const temperature = params.data[0]; // 温度值
|
const temperature = params.data[0];
|
||||||
const depth = params.data[1]; // 深度值
|
const depth = params.data[1];
|
||||||
|
|
||||||
return `<div style="font-weight:bold;margin-bottom:8px;">${params.seriesName}</div>
|
return `<span>${params.seriesName}</span><br />
|
||||||
<div>水温(°C):${temperature}</div>
|
<span>水温(℃):${temperature}</span><br />
|
||||||
<div>深度(m):${depth}</div>`;
|
<span>深度(m):${depth}</span>`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
legend: {
|
color: colors.value,
|
||||||
show: true,
|
|
||||||
data: allLegendItems.value.map(item => ({
|
|
||||||
name: item.name,
|
|
||||||
icon: 'circle'
|
|
||||||
})),
|
|
||||||
selected: allLegendItems.value.reduce((acc, item) => {
|
|
||||||
acc[item.name] = !legendInactiveSet.value.has(item.name);
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<string, boolean>),
|
|
||||||
// 隐藏ECharts默认图例,使用自定义图例
|
|
||||||
top: -9999
|
|
||||||
},
|
|
||||||
grid: {
|
grid: {
|
||||||
left: '70px',
|
right: '15%',
|
||||||
right: '100px',
|
bottom: '10%'
|
||||||
bottom: '60px',
|
},
|
||||||
top: '30px'
|
legend: {
|
||||||
|
type: 'scroll',
|
||||||
|
data: legend,
|
||||||
|
selected: selected.value,
|
||||||
|
textStyle: {
|
||||||
|
align: 'middle' as const
|
||||||
|
},
|
||||||
|
top: '1%',
|
||||||
|
left: 'center' as const
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'value',
|
|
||||||
position: 'top',
|
|
||||||
name: '水温(°C)',
|
name: '水温(°C)',
|
||||||
nameLocation: 'end',
|
type: 'value' as const,
|
||||||
nameGap: 20,
|
boundaryGap: false,
|
||||||
nameTextStyle: {
|
position: 'top' as const,
|
||||||
color: '#666',
|
nameLocation: 'end' as const,
|
||||||
fontSize: 12,
|
|
||||||
align: 'right',
|
|
||||||
verticalAlign: 'top'
|
|
||||||
},
|
|
||||||
min: 0,
|
|
||||||
max: 8,
|
|
||||||
interval: null,
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#d0d0d0',
|
|
||||||
width: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: {
|
axisTick: {
|
||||||
show: true,
|
show: true
|
||||||
length: 4,
|
|
||||||
inside: false,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#d0d0d0'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#666',
|
|
||||||
fontSize: 11,
|
|
||||||
margin: 6,
|
|
||||||
formatter: (value: number) => {
|
|
||||||
const showTicks = [0, 3, 6, 8];
|
|
||||||
if (showTicks.includes(value)) {
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
interval: y1.scale,
|
||||||
splitLine: {
|
splitLine: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value' as const,
|
||||||
position: 'left',
|
name: type.value === 2 ? '高程(m)' : '水深(m)',
|
||||||
inverse: true,
|
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 80,
|
scale: false, // ✅ 新增:确保 Y 轴从 0 开始,防止自动调整
|
||||||
interval: 20,
|
nameGap: type.value === 2 ? 22 : 10,
|
||||||
name: '水深(m)',
|
inverse: type.value !== 2
|
||||||
nameLocation: 'end',
|
|
||||||
nameGap: 25,
|
|
||||||
nameRotate: 0,
|
|
||||||
nameTextStyle: {
|
|
||||||
color: '#666',
|
|
||||||
fontSize: 12,
|
|
||||||
align: 'center',
|
|
||||||
verticalAlign: 'top'
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#d0d0d0',
|
|
||||||
width: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: true,
|
|
||||||
length: 3,
|
|
||||||
inside: false,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#d0d0d0'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#666',
|
|
||||||
fontSize: 11,
|
|
||||||
inside: false,
|
|
||||||
margin: 8
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#e5e5e5',
|
|
||||||
type: 'dashed',
|
|
||||||
width: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
series: monthData.value.map((monthItem: any) => ({
|
dataZoom: [{ type: 'inside' as const }],
|
||||||
name: monthItem.month,
|
series: yData
|
||||||
type: 'line',
|
|
||||||
smooth: true,
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 2,
|
|
||||||
lineStyle: {
|
|
||||||
width: 1,
|
|
||||||
color: monthColors[monthItem.month]
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: monthColors[monthItem.month]
|
|
||||||
},
|
|
||||||
data: monthItem.data.map((item: any) => [item[0], item[1]]),
|
|
||||||
connectNulls: false
|
|
||||||
}))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
option.xAxis.min = curMin;
|
||||||
|
|
||||||
chartInstance.setOption(option, { notMerge: true });
|
chartInstance.setOption(option, { notMerge: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
// 组件挂载时初始化
|
const handleChartClick = (params: any) => {
|
||||||
|
if (!params || !params.seriesName) return;
|
||||||
|
|
||||||
|
const matchMonth = params.seriesName.match(/\d+/);
|
||||||
|
const monthNum = matchMonth ? parseInt(matchMonth[0]) : 1;
|
||||||
|
|
||||||
|
const year = typeof datetimePicker.value.value === 'string'
|
||||||
|
? parseInt(datetimePicker.value.value)
|
||||||
|
: datetimePicker.value.value;
|
||||||
|
|
||||||
|
const startTime = `${year}-${String(monthNum).padStart(2, '0')}-01 00:00:00`;
|
||||||
|
const lastDay = new Date(year, monthNum, 0).getDate();
|
||||||
|
const endTime = `${year}-${String(monthNum).padStart(2, '0')}-${lastDay} 23:59:59`;
|
||||||
|
|
||||||
|
const currentStcd = select.value.value;
|
||||||
|
let currentStnm = '';
|
||||||
|
|
||||||
|
select.value.options.forEach((station: any) => {
|
||||||
|
station.children?.forEach((child: any) => {
|
||||||
|
if (child.value === currentStcd) {
|
||||||
|
currentStnm = child.title;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
import('@/store/modules/model').then(({ useModelStore }) => {
|
||||||
|
import('@/components/MapModal/setting.config').then(({ handleTabs }) => {
|
||||||
|
const modelStore = useModelStore();
|
||||||
|
|
||||||
|
modelStore.params = {
|
||||||
|
sttp: 'WT',
|
||||||
|
stcd: currentStcd,
|
||||||
|
titleName: currentStnm,
|
||||||
|
enfc: '1',
|
||||||
|
timeRange: [startTime, endTime]
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
modelStore.title = currentStnm ? `${currentStnm} 详情信息` : '详情信息';
|
||||||
|
modelStore.tabList = handleTabs(modelStore.params);
|
||||||
|
modelStore.modalVisible = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// ==================== 生命周期和监听 ====================
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initChart();
|
initChart();
|
||||||
|
|
||||||
// 监听窗口大小变化
|
|
||||||
window.addEventListener('resize', () => {
|
window.addEventListener('resize', () => {
|
||||||
chartInstance?.resize();
|
chartInstance?.resize();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时清理
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
chartInstance?.dispose();
|
chartInstance?.dispose();
|
||||||
window.removeEventListener('resize', () => {
|
window.removeEventListener('resize', () => {
|
||||||
chartInstance?.resize();
|
chartInstance?.resize();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
async (newVal) => {
|
||||||
|
if (!newVal || !newVal.wbsCode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
await selectOptions();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// // 监听测站和年份变化,触发数据加载
|
||||||
|
// watch(
|
||||||
|
// () => [select.value.value, datetimePicker.value.value],
|
||||||
|
// async ([newStcd, newYear]) => {
|
||||||
|
// if (newStcd && newYear) {
|
||||||
|
// await fetchChartData(newStcd, newYear);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// { deep: true }
|
||||||
|
// );
|
||||||
|
|
||||||
|
const handlePanelChange1 = (data: any) => {
|
||||||
|
console.log('当前所有控件状态:', data);
|
||||||
|
if (data.moreSelect && data.datetime) {
|
||||||
|
fetchChartData(data.moreSelect, data.datetime)
|
||||||
|
} else {
|
||||||
|
showemit.value = true
|
||||||
|
chartData.value = [];
|
||||||
|
colors.value = [];
|
||||||
|
selected.value = {};
|
||||||
|
showemit.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -345,80 +645,16 @@ onBeforeUnmount(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 12px;
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
border-bottom: 1px solid #e8e8e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 12px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-item.inactive {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-dot {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-text {
|
|
||||||
font-size: 12px;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pagination {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-btn {
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 2px 8px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 12px;
|
|
||||||
user-select: none;
|
|
||||||
transition: all 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-btn:hover:not(.disabled) {
|
|
||||||
border-color: #1890ff;
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-btn.disabled {
|
|
||||||
cursor: not-allowed;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-info {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 300px;
|
min-height: 252px !important;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
:deep(.ant-spin-nested-loading) {
|
||||||
|
height: 252px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,240 +1,736 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
|
<!--
|
||||||
|
出入库水温图表组件
|
||||||
|
功能:展示水库入库水温和出库水温的时序变化曲线
|
||||||
|
数据来源:inOutOneGetKendoListCust接口
|
||||||
|
特性:支持月份选择、站点筛选、数据缩放、空值断开显示
|
||||||
|
-->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="出入库水温">
|
<SidePanelItem title="出入库水温" :moreSelect="select" :scopeDate="scopeDate" @update-values="handlePanelChange1">
|
||||||
<div ref="chartContainer" class="chart-container"></div>
|
<!-- ECharts图表容器 -->
|
||||||
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
|
<div v-show="showemit && !loading" ref="chartContainer" class="chart-container"></div>
|
||||||
|
<div v-show="!showemit && !loading" class="chart-container">
|
||||||
|
<a-empty />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onUnmounted } from 'vue';
|
import { ref, onMounted, onUnmounted, watch } from 'vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
||||||
|
import { getVmsstbprpt, inOutOneGetKendoListCust } from '@/api/sw';
|
||||||
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
|
|
||||||
|
// ==================== 组件基础配置 ====================
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'churukushuiwenMod'
|
name: 'churukushuiwenMod'
|
||||||
});
|
});
|
||||||
|
|
||||||
const chartContainer = ref<HTMLDivElement | null>(null);
|
// ==================== 状态管理初始化 ====================
|
||||||
let chartInstance: echarts.ECharts | null = null;
|
const JidiSelectEventStore = useJidiSelectEventStore(); // 基地选择事件Store
|
||||||
|
|
||||||
// 页面加载时执行的逻辑
|
// ==================== DOM引用与图表实例 ====================
|
||||||
|
const chartContainer = ref<HTMLDivElement | null>(null); // 图表容器DOM引用
|
||||||
|
let chartInstance: echarts.ECharts | null = null; // ECharts实例
|
||||||
|
const currentMonth = new Date().toISOString().slice(0, 7); // 当前月份(格式: YYYY-MM)
|
||||||
|
const chartDataLoaded = ref(false); // 数据加载状态标志,防止重复请求
|
||||||
|
|
||||||
|
// ==================== 加载状态与显示控制 ====================
|
||||||
|
const showemit = ref(false); // 控制图表是否显示
|
||||||
|
const loading = ref(false); // 控制加载状态
|
||||||
|
|
||||||
|
// ==================== 下拉框配置(站点选择) ====================
|
||||||
|
const select = ref({
|
||||||
|
show: true, // 是否显示下拉框
|
||||||
|
value: '', // 当前选中值(站点编码)
|
||||||
|
options: [], // 下拉选项列表
|
||||||
|
picker: undefined, // 选择器类型(未使用)
|
||||||
|
format: undefined, // 格式化字符串(未使用)
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 日期范围选择器配置 ====================
|
||||||
|
const scopeDate = ref({
|
||||||
|
show: true, // 是否显示日期选择器
|
||||||
|
value: [currentMonth, currentMonth] as any, // 默认选中当前月份范围(使用 as any 绕过类型检查)
|
||||||
|
format: "YYYY-MM", // 显示格式
|
||||||
|
picker: "month" as const, // 选择器类型:月份选择
|
||||||
|
options: [], // 可选日期范围(未使用)
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 业务变量 ====================
|
||||||
|
const baseid = ref(''); // 当前选中的基地ID
|
||||||
|
|
||||||
|
// ==================== 基地列表数据(硬编码) ====================
|
||||||
|
const jiDiList: any = ref([
|
||||||
|
{
|
||||||
|
"baseid": "all",
|
||||||
|
"basename": "当前全部",
|
||||||
|
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "01",
|
||||||
|
"basename": "金沙江干流",
|
||||||
|
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "02",
|
||||||
|
"basename": "雅砻江干流",
|
||||||
|
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "03",
|
||||||
|
"basename": "大渡河干流",
|
||||||
|
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "04",
|
||||||
|
"basename": "乌江干流",
|
||||||
|
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "05",
|
||||||
|
"basename": "长江上游干流",
|
||||||
|
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "10",
|
||||||
|
"basename": "湘西",
|
||||||
|
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "08",
|
||||||
|
"basename": "黄河上游干流",
|
||||||
|
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "09",
|
||||||
|
"basename": "黄河中游干流",
|
||||||
|
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "06",
|
||||||
|
"basename": "南盘江-红水河",
|
||||||
|
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "12",
|
||||||
|
"basename": "东北",
|
||||||
|
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "07",
|
||||||
|
"basename": "澜沧江干流",
|
||||||
|
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "13",
|
||||||
|
"basename": "怒江干流",
|
||||||
|
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "11",
|
||||||
|
"basename": "闽浙赣",
|
||||||
|
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "other",
|
||||||
|
"basename": "其他",
|
||||||
|
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
// ==================== 数据处理函数 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换图表数据格式
|
||||||
|
* 将后端返回的扁平数据结构转换为ECharts所需的双线数据格式
|
||||||
|
* @param rawData - 原始数据数组,包含tm(时间)、dwtp(类型:IWT/DWT)、wt(水温)字段
|
||||||
|
* @returns 包含日期数组、入库水温数组、出库水温数组的对象
|
||||||
|
*/
|
||||||
|
function transformChartData(rawData: any[]) {
|
||||||
|
// 1. 提取所有唯一日期并排序
|
||||||
|
const dateSet = new Set<string>()
|
||||||
|
rawData.forEach(item => {
|
||||||
|
const date = item.tm?.split(' ')[0] // "2026-04-01 00:00:00" -> "2026-04-01"
|
||||||
|
if (date) dateSet.add(date)
|
||||||
|
})
|
||||||
|
const dates = Array.from(dateSet).sort()
|
||||||
|
|
||||||
|
// 2. 构建两条线的数据(入库水温和出库水温)
|
||||||
|
const iwtData: (number | null)[] = [] // 入库水温(Inlet Water Temperature)
|
||||||
|
const dwtData: (number | null)[] = [] // 出库水温(Discharge Water Temperature)
|
||||||
|
|
||||||
|
dates.forEach(date => {
|
||||||
|
// 查找该日期的入库水温(dwtp='IWT')
|
||||||
|
const iwtItem = rawData.find(item =>
|
||||||
|
item.tm?.startsWith(date) && item.dwtp === 'IWT'
|
||||||
|
)
|
||||||
|
// 保留一位小数,缺失则填null(曲线断开)
|
||||||
|
iwtData.push(iwtItem ? Number(iwtItem.wt.toFixed(1)) : null)
|
||||||
|
|
||||||
|
// 查找该日期的出库水温(dwtp='DWT')
|
||||||
|
const dwtItem = rawData.find(item =>
|
||||||
|
item.tm?.startsWith(date) && item.dwtp === 'DWT'
|
||||||
|
)
|
||||||
|
// 保留一位小数,缺失则填null(曲线断开)
|
||||||
|
dwtData.push(dwtItem ? Number(dwtItem.wt.toFixed(1)) : null)
|
||||||
|
})
|
||||||
|
|
||||||
|
return { dates, iwtData, dwtData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算Y轴显示范围
|
||||||
|
* 基于数据最小值和最大值,上下各扩展0.2的缓冲空间
|
||||||
|
* @param data - 水温数据数组(可能包含null值)
|
||||||
|
* @returns Y轴的最小值和最大值对象
|
||||||
|
*/
|
||||||
|
function calculateYAxisRange(data: (number | null)[]) {
|
||||||
|
// 过滤出有效数值
|
||||||
|
const validValues = data.filter(v => v !== null) as number[]
|
||||||
|
if (validValues.length === 0) return { min: 0, max: 10 } // 无数据时返回默认范围
|
||||||
|
|
||||||
|
const min = Math.min(...validValues)
|
||||||
|
const max = Math.max(...validValues)
|
||||||
|
|
||||||
|
// 上下各扩展0.2,避免曲线贴边
|
||||||
|
return {
|
||||||
|
min: Number((min - 0.2).toFixed(1)), // 最小值减0.2
|
||||||
|
max: Number((max + 0.2).toFixed(1)) // 最大值加0.2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算X轴刻度间隔
|
||||||
|
* 确保最多显示4个刻度标签,避免标签重叠
|
||||||
|
* @param dateCount - 日期总数
|
||||||
|
* @returns X轴刻度间隔值(interval属性)
|
||||||
|
*/
|
||||||
|
function calculateXAxisInterval(dateCount: number): number {
|
||||||
|
if (dateCount <= 4) return 0 // 数据点≤4时,显示所有刻度
|
||||||
|
return Math.ceil(dateCount / 4) - 1 // 动态计算间隔,保证最多4个刻度
|
||||||
|
}
|
||||||
|
const paramsOne = ref({
|
||||||
|
value: '',
|
||||||
|
tm: [currentMonth, currentMonth]
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* 获取图表数据
|
||||||
|
* 调用后端接口获取出入库水温数据,并触发图表更新
|
||||||
|
*/
|
||||||
|
async function fetchChartData() {
|
||||||
|
if (loading.value) return; // 防止重复请求
|
||||||
|
|
||||||
|
loading.value = true; // 开始请求前设置loading
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 动态计算时间范围(考虑不同月份的实际天数)
|
||||||
|
const startDate = paramsOne.value.tm[0] + '-01 00:00:00';
|
||||||
|
const endDate = dayjs(paramsOne.value.tm[1]).endOf('month').format('YYYY-MM-DD') + ' 23:59:59';
|
||||||
|
|
||||||
|
// 构建查询参数(固定参数,后续可扩展为动态)
|
||||||
|
const params = {
|
||||||
|
filter: {
|
||||||
|
logic: "and",
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
// OR逻辑:查询入库或出库水温
|
||||||
|
logic: "or",
|
||||||
|
filters: [
|
||||||
|
{ field: "engDwtCode", operator: "eq", value: paramsOne.value.value }, // 出库站点编码
|
||||||
|
{ field: "engIwtCode", operator: "eq", value: paramsOne.value.value } // 入库站点编码
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 时间范围过滤
|
||||||
|
{ field: "dt", operator: "gte", dataType: "date", value: startDate },
|
||||||
|
{ field: "dt", operator: "lte", dataType: "date", value: endDate }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用API获取数据
|
||||||
|
const res = await inOutOneGetKendoListCust(params)
|
||||||
|
const rawData = res?.data?.data || []
|
||||||
|
|
||||||
|
console.log('原始数据:', rawData)
|
||||||
|
|
||||||
|
// 根据数据长度决定showemit
|
||||||
|
if (rawData.length > 0) {
|
||||||
|
showemit.value = true; // 有数据则显示图表
|
||||||
|
|
||||||
|
// 转换数据格式
|
||||||
|
const { dates, iwtData, dwtData } = transformChartData(rawData)
|
||||||
|
|
||||||
|
console.log('转换后的数据:', { dates, iwtData, dwtData })
|
||||||
|
|
||||||
|
// 数据就绪后初始化或更新图表
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!chartInstance) {
|
||||||
|
initChartWithData(dates, iwtData, dwtData); // 带数据初始化
|
||||||
|
} else {
|
||||||
|
updateChart(dates, iwtData, dwtData, 0); // 已有实例则更新
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制重绘,确保图表尺寸正确适配容器
|
||||||
|
setTimeout(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.resize();
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
showemit.value = false; // 无数据显示空状态
|
||||||
|
}
|
||||||
|
|
||||||
|
chartDataLoaded.value = true; // 标记数据已加载
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取图表数据失败:', error);
|
||||||
|
showemit.value = false; // 出错也显示空状态
|
||||||
|
chartDataLoaded.value = true; // 即使失败也标记为已加载,避免重复请求
|
||||||
|
} finally {
|
||||||
|
loading.value = false; // 无论成功失败都关闭loading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带数据初始化图表
|
||||||
|
* @param dates - 日期数组
|
||||||
|
* @param iwtData - 入库水温数据
|
||||||
|
* @param dwtData - 出库水温数据
|
||||||
|
*/
|
||||||
|
const initChartWithData = (dates: string[], iwtData: (number | null)[], dwtData: (number | null)[]) => {
|
||||||
|
if (!chartContainer.value) return;
|
||||||
|
|
||||||
|
const containerHeight = chartContainer.value.offsetHeight;
|
||||||
|
if (containerHeight === 0) {
|
||||||
|
setTimeout(() => initChartWithData(dates, iwtData, dwtData), 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chartInstance = echarts.init(chartContainer.value);
|
||||||
|
updateChart(dates, iwtData, dwtData, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新图表配置
|
||||||
|
* 根据处理后的数据动态生成ECharts配置项
|
||||||
|
* @param dates - 日期数组(X轴数据)
|
||||||
|
* @param iwtData - 入库水温数据数组
|
||||||
|
* @param dwtData - 出库水温数据数组
|
||||||
|
* @param yAxisMax - 废弃参数,保留以兼容旧代码
|
||||||
|
*/
|
||||||
|
function updateChart(dates: string[], iwtData: (number | null)[], dwtData: (number | null)[], yAxisMax: number) {
|
||||||
|
if (!chartInstance) {
|
||||||
|
console.warn('图表实例未初始化')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ==================== 动态计算坐标轴范围 ====================
|
||||||
|
const xAxisInterval = calculateXAxisInterval(dates.length) // X轴刻度间隔
|
||||||
|
const { min: yAxisMin, max: yAxisMaxValue } = calculateYAxisRange([...iwtData, ...dwtData]) // Y轴范围
|
||||||
|
|
||||||
|
console.log('X轴间隔:', xAxisInterval, 'Y轴范围:', { min: yAxisMin, max: yAxisMaxValue })
|
||||||
|
|
||||||
|
// ==================== ECharts配置项 ====================
|
||||||
|
const option = {
|
||||||
|
// 提示框配置
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis', // 触发类型:坐标轴触发
|
||||||
|
backgroundColor: 'rgba(50, 50, 50, 0.9)', // 背景色
|
||||||
|
borderColor: 'transparent', // 边框透明
|
||||||
|
textStyle: {
|
||||||
|
color: '#fff', // 文字颜色
|
||||||
|
fontSize: 14 // 字体大小
|
||||||
|
},
|
||||||
|
axisPointer: {
|
||||||
|
type: 'line', // 指示器类型:直线
|
||||||
|
lineStyle: {
|
||||||
|
color: 'rgba(91, 143, 249, 0.15)', // 指示线颜色
|
||||||
|
width: 30, // 指示线宽度
|
||||||
|
type: 'solid' // 线型:实线
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 自定义提示框内容
|
||||||
|
formatter: function (params: any) {
|
||||||
|
if (!params || params.length === 0) return '';
|
||||||
|
const date = params[0].name; // 获取日期
|
||||||
|
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
|
||||||
|
params.forEach((item: any) => {
|
||||||
|
// 空值显示'-',符合项目规范
|
||||||
|
const value = item.value !== null && item.value !== undefined ? `${item.value}°C` : '-'
|
||||||
|
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
|
||||||
|
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
|
||||||
|
<span>${item.seriesName}</span>
|
||||||
|
<span style="font-weight: bold; margin-left: auto;">${value}</span>
|
||||||
|
</div>`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 标题配置
|
||||||
|
title: {
|
||||||
|
text: '水温(°C)', // 标题文本
|
||||||
|
left: 17, // 左侧位置
|
||||||
|
top: 13, // 顶部位置
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#000000',
|
||||||
|
fontWeight: 'normal'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 图例配置
|
||||||
|
legend: {
|
||||||
|
data: ['入库水温', '出库水温'], // 图例项
|
||||||
|
top: 13, // 顶部对齐
|
||||||
|
left: 'center', // 水平居中
|
||||||
|
itemWidth: 10, // 图例图标宽度
|
||||||
|
itemHeight: 10, // 图例图标高度
|
||||||
|
itemGap: 20, // 图例间距
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: '#000000'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 网格配置(图表区域边距)
|
||||||
|
grid: {
|
||||||
|
left: '10%',
|
||||||
|
right: '5%',
|
||||||
|
top: '15%',
|
||||||
|
bottom: '10%'
|
||||||
|
},
|
||||||
|
|
||||||
|
// X轴配置
|
||||||
|
xAxis: {
|
||||||
|
type: 'category', // 类目轴
|
||||||
|
data: dates, // 日期数据
|
||||||
|
boundaryGap: true, // 两侧留白
|
||||||
|
interval: xAxisInterval, // 动态刻度间隔,最多显示4个标签
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#000000' // 轴线颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false // 隐藏刻度线
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#000000', // 标签颜色
|
||||||
|
fontSize: 13, // 字体大小
|
||||||
|
margin: 10 // 标签与轴线距离
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true, // 显示分割线
|
||||||
|
lineStyle: {
|
||||||
|
color: '#bfbfbf', // 分割线颜色
|
||||||
|
type: 'solid' // 实线
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 数据缩放配置(鼠标滚轮缩放)
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside', // 内置型数据区域缩放
|
||||||
|
start: 0, // 起始位置0%
|
||||||
|
end: 100, // 结束位置100%
|
||||||
|
minValueSpan: 2, // 最小显示2个数据点
|
||||||
|
zoomOnMouseWheel: true, // 允许滚轮缩放
|
||||||
|
moveOnMouseMove: true, // 允许鼠标拖拽平移
|
||||||
|
moveOnMouseWheel: false, // 滚轮不触发平移
|
||||||
|
zoomLock: false, // 不锁定缩放比例
|
||||||
|
throttle: 50 // 节流50ms
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// Y轴配置
|
||||||
|
yAxis: {
|
||||||
|
type: 'value', // 数值轴
|
||||||
|
min: yAxisMin, // 动态最小值(数据最小值-0.2)
|
||||||
|
max: yAxisMaxValue, // 动态最大值(数据最大值+0.2)
|
||||||
|
scale: true, // 自适应缩放,不强制从0开始
|
||||||
|
axisLine: {
|
||||||
|
show: true,
|
||||||
|
lineStyle: {
|
||||||
|
color: '#000000',
|
||||||
|
width: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: true, // 显示刻度线
|
||||||
|
lineStyle: {
|
||||||
|
color: '#000000',
|
||||||
|
width: 1
|
||||||
|
},
|
||||||
|
length: 4 // 刻度线长度
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#000000',
|
||||||
|
fontSize: 12,
|
||||||
|
formatter: '{value}' // 标签格式
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: true, // 显示网格线
|
||||||
|
lineStyle: {
|
||||||
|
color: '#bfbfbf',
|
||||||
|
type: 'solid'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 系列数据配置(两条折线)
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '入库水温', // 系列名称
|
||||||
|
type: 'line', // 图表类型:折线图
|
||||||
|
smooth: true, // 平滑曲线
|
||||||
|
symbol: 'circle', // 数据点形状:圆形
|
||||||
|
symbolSize: 4, // 数据点大小
|
||||||
|
connectNulls: true, // 关键:不连接空值,实现曲线断开效果
|
||||||
|
lineStyle: {
|
||||||
|
width: 2, // 线宽
|
||||||
|
color: '#4b79ab' // 线条颜色:蓝色
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#4b79ab' // 数据点颜色
|
||||||
|
},
|
||||||
|
data: iwtData // 入库水温数据
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '出库水温', // 系列名称
|
||||||
|
type: 'line', // 图表类型:折线图
|
||||||
|
smooth: true, // 平滑曲线
|
||||||
|
symbol: 'circle', // 数据点形状:圆形
|
||||||
|
symbolSize: 4, // 数据点大小
|
||||||
|
connectNulls: true, // 不连接空值,实现断开效果
|
||||||
|
lineStyle: {
|
||||||
|
width: 2, // 线宽
|
||||||
|
color: '#78c300' // 线条颜色:绿色
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: '#78c300' // 数据点颜色
|
||||||
|
},
|
||||||
|
data: dwtData // 出库水温数据
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// 应用配置到图表实例
|
||||||
|
chartInstance.setOption(option, true); // true表示完全替换配置
|
||||||
|
console.log('图表更新成功')
|
||||||
|
|
||||||
|
// 强制重绘,确保图表尺寸正确适配容器
|
||||||
|
setTimeout(() => {
|
||||||
|
if (chartInstance) {
|
||||||
|
chartInstance.resize(); // 重新计算图表尺寸
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新图表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 生命周期钩子 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件挂载时执行
|
||||||
|
* 初始化图表并获取数据
|
||||||
|
*/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 延迟初始化,确保 DOM 渲染完成
|
// 延迟100ms初始化,确保DOM渲染完成
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (chartContainer.value) {
|
if (chartContainer.value) {
|
||||||
initChart();
|
// fetchChartData(); // 先获取真实数据
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener('resize', handleResize); // 监听窗口大小变化
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件卸载时执行
|
||||||
|
* 清理资源,防止内存泄漏
|
||||||
|
*/
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.dispose();
|
chartInstance.dispose(); // 销毁图表实例
|
||||||
chartInstance = null;
|
chartInstance = null;
|
||||||
}
|
}
|
||||||
window.removeEventListener('resize', handleResize);
|
window.removeEventListener('resize', handleResize); // 移除事件监听
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 窗口大小变化处理函数
|
||||||
|
* 自适应调整图表尺寸
|
||||||
|
*/
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.resize();
|
chartInstance.resize(); // 重新计算图表尺寸
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const initChart = () => {
|
// ==================== 事件处理函数 ====================
|
||||||
if (!chartContainer.value) return;
|
|
||||||
|
|
||||||
// 检查容器实际高度,如果为 0 则延迟重试
|
/**
|
||||||
const containerHeight = chartContainer.value.offsetHeight;
|
* SidePanelItem面板变化回调
|
||||||
if (containerHeight === 0) {
|
* 处理下拉框和日期选择器的值变化
|
||||||
setTimeout(() => initChart(), 100);
|
* @param data - 包含moreSelect(下拉框值)和datetime(日期范围)的对象
|
||||||
return;
|
*/
|
||||||
|
const handlePanelChange1 = (data: any) => {
|
||||||
|
// console.log('当前所有控件状态:', data);
|
||||||
|
// debugger
|
||||||
|
// TODO: 后续可扩展联动逻辑
|
||||||
|
if (data.moreSelect) {
|
||||||
|
paramsOne.value.value = data.moreSelect;
|
||||||
}
|
}
|
||||||
|
paramsOne.value.tm = data.scopeDate
|
||||||
|
fetchChartData()
|
||||||
|
};
|
||||||
|
|
||||||
chartInstance = echarts.init(chartContainer.value);
|
/**
|
||||||
|
* 获取站点下拉选项数据
|
||||||
const option = {
|
* 根据选中的基地ID查询对应的站点列表
|
||||||
tooltip: {
|
*/
|
||||||
trigger: 'axis',
|
const getselsectData = async () => {
|
||||||
backgroundColor: 'rgba(50, 50, 50, 0.9)',
|
loading.value = true; // 开始加载站点数据
|
||||||
borderColor: 'transparent',
|
|
||||||
textStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 14
|
|
||||||
},
|
|
||||||
axisPointer: {
|
|
||||||
type: 'line',
|
|
||||||
lineStyle: {
|
|
||||||
color: 'rgba(91, 143, 249, 0.15)',
|
|
||||||
width: 30,
|
|
||||||
type: 'solid'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
formatter: function(params: any) {
|
|
||||||
if (!params || params.length === 0) return '';
|
|
||||||
const date = params[0].name;
|
|
||||||
let result = `<div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">${date}</div>`;
|
|
||||||
params.forEach((item: any) => {
|
|
||||||
result += `<div style="display: flex; align-items: center; gap: 6px; margin: 4px 0; font-size: 14px;">
|
|
||||||
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color};"></span>
|
|
||||||
<span>${item.seriesName}</span>
|
|
||||||
<span style="font-weight: bold; margin-left: auto;">${item.value}°C</span>
|
|
||||||
</div>`;
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: '水温(°C)',
|
|
||||||
left: 17,
|
|
||||||
top: 13,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: 13,
|
|
||||||
color: '#000000',
|
|
||||||
fontWeight: 'normal'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
data: ['入库水温', '出库水温'],
|
|
||||||
top: 13,
|
|
||||||
left: 'center',
|
|
||||||
itemWidth: 10,
|
|
||||||
itemHeight: 10,
|
|
||||||
itemGap: 20,
|
|
||||||
textStyle: {
|
|
||||||
fontSize: 13,
|
|
||||||
color: '#000000'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
left: '10%',
|
|
||||||
right: '5%',
|
|
||||||
top: '15%',
|
|
||||||
bottom: '18%'
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: ['2026-04-01', '2026-04-02', '2026-04-03', '2026-04-04', '2026-04-05', '2026-04-06', '2026-04-07'],
|
|
||||||
boundaryGap: true,
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#000000'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#000000',
|
|
||||||
fontSize: 13,
|
|
||||||
margin: 10,
|
|
||||||
interval: 2
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#bfbfbf',
|
|
||||||
type: 'solid'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dataZoom: [
|
|
||||||
{
|
|
||||||
type: 'inside',
|
|
||||||
start: 0,
|
|
||||||
end: 100,
|
|
||||||
minValueSpan: 2,
|
|
||||||
zoomOnMouseWheel: true,
|
|
||||||
moveOnMouseMove: true,
|
|
||||||
moveOnMouseWheel: false,
|
|
||||||
zoomLock: false,
|
|
||||||
throttle: 50
|
|
||||||
}
|
|
||||||
],
|
|
||||||
yAxis: {
|
|
||||||
type: 'value',
|
|
||||||
min: 5.2,
|
|
||||||
max: 5.9,
|
|
||||||
interval: 0.1,
|
|
||||||
axisLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#000000',
|
|
||||||
width: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#000000',
|
|
||||||
width: 1
|
|
||||||
},
|
|
||||||
length: 4
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#000000',
|
|
||||||
fontSize: 12,
|
|
||||||
formatter: '{value}'
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#bfbfbf',
|
|
||||||
type: 'solid'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '入库水温',
|
|
||||||
type: 'line',
|
|
||||||
smooth: true,
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 6,
|
|
||||||
lineStyle: {
|
|
||||||
width: 2,
|
|
||||||
color: '#4b79ab'
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: '#4b79ab'
|
|
||||||
},
|
|
||||||
data: [5.4, 5.3, 5.6, 5.4, 5.5, 5.6, 5.6]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '出库水温',
|
|
||||||
type: 'line',
|
|
||||||
smooth: true,
|
|
||||||
symbol: 'circle',
|
|
||||||
symbolSize: 6,
|
|
||||||
lineStyle: {
|
|
||||||
width: 2,
|
|
||||||
color: '#78c300'
|
|
||||||
},
|
|
||||||
itemStyle: {
|
|
||||||
color: '#78c300'
|
|
||||||
},
|
|
||||||
data: [5.8, 5.8, 5.8, 5.8, 5.8, 5.8, 5.8]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
chartInstance.setOption(option);
|
|
||||||
|
|
||||||
// 强制重绘,确保图表尺寸正确适配容器
|
try {
|
||||||
setTimeout(() => {
|
let params = {
|
||||||
if (chartInstance) {
|
"filter": {
|
||||||
chartInstance.resize();
|
"logic": "and",
|
||||||
|
"filters": [
|
||||||
|
// 如果baseid不是'all',则添加基地过滤条件
|
||||||
|
baseid.value != 'all' ? {
|
||||||
|
"field": "baseId",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": baseid.value
|
||||||
|
} : null,
|
||||||
|
// 站点类型过滤:只查询ENG类型站点
|
||||||
|
{
|
||||||
|
"field": "sttpCode",
|
||||||
|
"operator": "contains",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": "ENG"
|
||||||
|
},
|
||||||
|
// 只查询有效站点(dtin=1)
|
||||||
|
{
|
||||||
|
"field": "dtin",
|
||||||
|
"operator": "eq",
|
||||||
|
"dataType": "string",
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
].filter(Boolean) // 过滤掉null值
|
||||||
|
},
|
||||||
|
"sort": [
|
||||||
|
// 如果是'all',按基地排序;否则不排序
|
||||||
|
baseid.value == 'all' ? {
|
||||||
|
"field": "baseStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
} : null,
|
||||||
|
{
|
||||||
|
"field": "rvcdStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field": "siteStepSort",
|
||||||
|
"dir": "asc"
|
||||||
|
}
|
||||||
|
].filter(Boolean), // 过滤掉null值
|
||||||
|
"select": [
|
||||||
|
"stcd", // 站点编码
|
||||||
|
"stnm", // 站点名称
|
||||||
|
"lgtd", // 经度
|
||||||
|
"lttd", // 纬度
|
||||||
|
"siteStepSort", // 站点排序
|
||||||
|
"baseId", // 基地ID
|
||||||
|
"baseName" // 基地名称
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}, 50);
|
|
||||||
};
|
// 调用API获取站点数据
|
||||||
|
const res = await getVmsstbprpt(params)
|
||||||
|
console.log('res', res)
|
||||||
|
const data = res?.data?.data || res?.data
|
||||||
|
const obj: any = {}
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
// 将扁平数据转换为树形结构(按基地分组)
|
||||||
|
data.map((e) => {
|
||||||
|
if (obj[e.baseId]) {
|
||||||
|
// 如果基地已存在,添加子节点
|
||||||
|
obj[e.baseId] = { ...obj[e.baseId], children: [...obj[e.baseId].children, { ...e, value: e.stcd, title: e.stnm }] }
|
||||||
|
} else {
|
||||||
|
// 创建新的基地节点
|
||||||
|
obj[e.baseId] = { value: e.baseId, title: e.baseName, selectable: false, key: e.baseId, children: [{ ...e, value: e.stcd, title: e.stnm }] }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 转换为数组并按jiDiList顺序排序
|
||||||
|
let treeData: any = []
|
||||||
|
const dataMapNameArr: any = Object.entries(obj).map(([, v]) => v)
|
||||||
|
treeData = dataMapNameArr?.sort((a: any, b: any) => {
|
||||||
|
const indexA = jiDiList.value.findIndex(item => item.baseid === a.value);
|
||||||
|
const indexB = jiDiList.value.findIndex(item => item.baseid === b.value);
|
||||||
|
return indexA - indexB;
|
||||||
|
})
|
||||||
|
console.log('treeData', treeData)
|
||||||
|
|
||||||
|
// 更新下拉框选项
|
||||||
|
select.value.options = treeData
|
||||||
|
|
||||||
|
// 设置默认选中值
|
||||||
|
if (baseid.value == 'all') {
|
||||||
|
select.value.value = '008640203800000001' // 默认选中第一个站点
|
||||||
|
} else {
|
||||||
|
select.value.value = treeData?.[0]?.children[0]?.value // 选中当前基地的第一个站点
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取站点数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false; // 关闭loading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== Watch监听器 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听基地选择变化
|
||||||
|
* 当用户切换基地时,重新加载站点下拉选项
|
||||||
|
*/
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem, // 监听Store中的选中项
|
||||||
|
(newVal) => {
|
||||||
|
baseid.value = newVal.wbsCode; // 更新基地ID
|
||||||
|
getselsectData() // 重新获取站点数据
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true } // 深度监听,立即执行
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
/* 图表容器样式 */
|
||||||
.chart-container {
|
.chart-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 252px;
|
height: 252px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
/* 固定高度252px */
|
||||||
|
}
|
||||||
|
:deep(.ant-spin-nested-loading ){
|
||||||
|
height: 252px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1,43 +1,40 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem title="水温监测工作开展情况">
|
<SidePanelItem title="水温监测工作开展情况">
|
||||||
<div class="facility-grid">
|
<a-spin :spinning="loading" tip="加载中...">
|
||||||
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
<div class="facility-grid">
|
||||||
<div
|
<div v-for="facility in facilities" :key="facility.name" class="facility-card">
|
||||||
style="
|
<div style="
|
||||||
width: 60px;
|
width: 60px;
|
||||||
height: 62px;
|
height: 62px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
"
|
">
|
||||||
>
|
<div class="facility-icon">
|
||||||
<div class="facility-icon">
|
<i style="color: #fff" :class="facility.icon" type="icon-shengtailiuliang2"></i>
|
||||||
<i
|
</div>
|
||||||
style="color: #fff"
|
|
||||||
:class="facility.icon"
|
|
||||||
type="icon-shengtailiuliang2"
|
|
||||||
></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="facility-info">
|
<div class="facility-info">
|
||||||
<div class="facility-name">{{ facility.name }}</div>
|
<div class="facility-name">{{ facility.name }}</div>
|
||||||
<div style="font-size: 16px">
|
<div style="font-size: 16px">
|
||||||
<span class="facility-count">{{ facility.count }}</span
|
<span class="facility-count">{{ facility.count }}</span><span>个</span>
|
||||||
><span>个</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</a-spin>
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted, watch } from "vue";
|
||||||
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
||||||
import { getKendoListCust } from "@/api/sw";
|
import { getKendoListCust, baseEvnmAutoMonitorGetKendoListCust } from "@/api/sw";
|
||||||
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
|
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "shuiwenjiancegongzuokaizhangqingkuang",
|
name: "shuiwenjiancegongzuokaizhangqingkuang",
|
||||||
@ -47,34 +44,72 @@ defineOptions({
|
|||||||
const facilities = ref([
|
const facilities = ref([
|
||||||
{
|
{
|
||||||
name: "表层水温",
|
name: "表层水温",
|
||||||
count: 145,
|
count: 0,
|
||||||
icon: "icon iconfont icon-shuizhijiancezhan",
|
icon: "icon iconfont icon-shuizhijiancezhan",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "垂向水温",
|
name: "垂向水温",
|
||||||
count: 24,
|
count: 0,
|
||||||
icon: "icon iconfont icon-diwenshuijianhuan",
|
icon: "icon iconfont icon-diwenshuijianhuan",
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Loading 状态
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
const params = {
|
// 防重复请求
|
||||||
filter: {
|
if (loading.value) return;
|
||||||
logic: "and",
|
|
||||||
filters: [
|
try {
|
||||||
{ field: "rvcd", operator: "eq", dataType: "string", value: "SJLY1U" },
|
loading.value = true;
|
||||||
{ field: "tm", operator: "gte", dataType: "date", value: "2026-04-17 00:00:00" },
|
const params = {
|
||||||
{ field: "tm", operator: "lte", dataType: "date", value: "2026-05-17 23:00:00" },
|
"filter": {
|
||||||
],
|
"logic": "and",
|
||||||
},
|
"filters": [
|
||||||
sort: [{ field: "sort", dir: "asc" }],
|
{
|
||||||
};
|
"field": "showIds",
|
||||||
await getKendoListCust(params);
|
"operator": "in",
|
||||||
|
"value": [
|
||||||
|
"26",
|
||||||
|
"27"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
baseid.value != 'all' ? {
|
||||||
|
"field": "baseId",
|
||||||
|
"operator": "eq",
|
||||||
|
"value": baseid.value
|
||||||
|
}:null
|
||||||
|
].filter(Boolean)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let res = await baseEvnmAutoMonitorGetKendoListCust(params);
|
||||||
|
console.log(res);
|
||||||
|
let data = res.data.data || res.data
|
||||||
|
data.forEach((item: any, index: any) => {
|
||||||
|
facilities.value[index].count = item.cnt
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取水温监测数据失败:', error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 页面加载时执行的逻辑
|
// 页面加载时执行的逻辑
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const baseid = ref("")
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
(newVal) => {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
init();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@ -1,47 +1,139 @@
|
|||||||
<!-- SidePanelItem.vue -->
|
<!-- SidePanelItem.vue -->
|
||||||
<template>
|
<template>
|
||||||
<SidePanelItem
|
<SidePanelItem title="沿程水温变化" :prompt="prompts" :moreSelect="select" :datetimePicker="datetimePicker"
|
||||||
title="沿程水温变化"
|
@update-values="handlePanelChange1">
|
||||||
:prompt="prompts"
|
<a-spin :spinning="loading" tip="加载中..." >
|
||||||
:select="select"
|
<div v-show="showemit && !loading" ref="chartRef" class="chart-container"></div>
|
||||||
:datetimePicker="datetimePicker"
|
<div v-show="!showemit && !loading" class="chart-container"> <a-empty /></div>
|
||||||
>
|
</a-spin>
|
||||||
<div ref="chartRef" class="chart-container"></div>
|
|
||||||
</SidePanelItem>
|
</SidePanelItem>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onBeforeUnmount } from "vue";
|
import { ref, onMounted, onBeforeUnmount, watch, computed } from "vue";
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import type { ECharts } from "echarts";
|
import type { ECharts } from "echarts";
|
||||||
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
import SidePanelItem from "@/components/SidePanelItem/index.vue";
|
||||||
import { getKendoListCust } from "@/api/sw";
|
import { getKendoListCust, wbsbGetKendoList } from "@/api/sw";
|
||||||
|
import { useJidiSelectEventStore } from "@/store/modules/jidiSelectEvent";
|
||||||
|
import { da } from "element-plus/es/locale/index.mjs";
|
||||||
|
import { f } from "vue-router/dist/router-CWoNjPRp.mjs";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 定义组件名(便于调试和递归)
|
// 定义组件名(便于调试和递归)
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "yanchengshuiwenChangeMod",
|
name: "yanchengshuiwenChangeMod",
|
||||||
});
|
});
|
||||||
// 选择器配置
|
// 改为动态获取当前时间
|
||||||
const select = ref({
|
const getCurrentHourTime = () => {
|
||||||
|
const now = new Date();
|
||||||
|
// 减去一小时
|
||||||
|
now.setHours(now.getHours() - 1);
|
||||||
|
const year = now.getFullYear();
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(now.getDate()).padStart(2, '0');
|
||||||
|
const hour = String(now.getHours()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hour}`;
|
||||||
|
};
|
||||||
|
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||||
|
const baseid: any = ref(null);
|
||||||
|
const paramsOne = {
|
||||||
|
rvcd: '',
|
||||||
|
tm: []
|
||||||
|
}
|
||||||
|
const dataOne = ref('')
|
||||||
|
// 使用 computed 使 prompts 响应 dataOne.value 的变化
|
||||||
|
const prompts = computed(() => ({
|
||||||
show: true,
|
show: true,
|
||||||
value: undefined,
|
value: `注:最新数据时间为${dataOne.value || getCurrentHourTime()}`,
|
||||||
options: [],
|
}));
|
||||||
picker: undefined,
|
const jiDiList: any = ref([
|
||||||
format: undefined,
|
{
|
||||||
});
|
"baseid": "all",
|
||||||
|
"basename": "当前全部",
|
||||||
// 日期选择器配置
|
"id": "9BFEC848-83EA-AD22-6DE2-10E969476693"
|
||||||
const datetimePicker = ref({
|
},
|
||||||
show: true,
|
{
|
||||||
value: "2026-05-15 15",
|
"baseid": "01",
|
||||||
format: "YYYY-MM-DD HH",
|
"basename": "金沙江干流",
|
||||||
picker: "date" as const,
|
"id": "A33040B7-8977-D9F1-5E02-D7F0241AB8AA"
|
||||||
options: [],
|
},
|
||||||
});
|
{
|
||||||
const prompts = ref({
|
"baseid": "02",
|
||||||
show: true,
|
"basename": "雅砻江干流",
|
||||||
value: "注:最新数据时间为2026-04-08 23",
|
"id": "C63D6020-995D-FE97-2F4A-F619A7142C79"
|
||||||
});
|
},
|
||||||
|
{
|
||||||
|
"baseid": "03",
|
||||||
|
"basename": "大渡河干流",
|
||||||
|
"id": "CCB0766B-F1D4-7DD6-650F-5C556B7231B3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "04",
|
||||||
|
"basename": "乌江干流",
|
||||||
|
"id": "E8A66641-B4F4-CC85-0815-38C5B2F93DBD"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "05",
|
||||||
|
"basename": "长江上游干流",
|
||||||
|
"id": "E02C11E9-CEA5-A030-202F-3BFE84465D03"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "10",
|
||||||
|
"basename": "湘西",
|
||||||
|
"id": "B1D1D52D-CEEF-6DC4-27DF-9990EB572F8D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "08",
|
||||||
|
"basename": "黄河上游干流",
|
||||||
|
"id": "BC6AF135-263D-09A9-D5CA-C99B2598FE6E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "09",
|
||||||
|
"basename": "黄河中游干流",
|
||||||
|
"id": "60EEEC28-128F-A2A8-44F3-6EAAC8FD8BB6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "06",
|
||||||
|
"basename": "南盘江-红水河",
|
||||||
|
"id": "7BB9A8F4-34B5-42B4-A7FC-CE910AD7F203"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "12",
|
||||||
|
"basename": "东北",
|
||||||
|
"id": "47F8EF06-924E-E161-FCAF-62A66BBF252D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "07",
|
||||||
|
"basename": "澜沧江干流",
|
||||||
|
"id": "A966A4C9-278C-B0DA-2B97-2D10B7A6E96A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "13",
|
||||||
|
"basename": "怒江干流",
|
||||||
|
"id": "FA89E8CB-67A8-76DA-DC1E-23AE4C54F9E4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "11",
|
||||||
|
"basename": "闽浙赣",
|
||||||
|
"id": "CD98F995-EEB2-1021-D807-DA1B1AD9E49A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"baseid": "other",
|
||||||
|
"basename": "其他",
|
||||||
|
"id": "AFBDFC67-B955-4EFD-959A-014CFB59EBFC"
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const showemit = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
// const selectOptions: any = ref([])
|
||||||
|
const optionsFmt = (options: any) =>
|
||||||
|
options.map((item: any) => ({
|
||||||
|
label: item.wbsName,
|
||||||
|
value: item.wbsCode
|
||||||
|
}))
|
||||||
|
|
||||||
const chartRef = ref<HTMLElement | null>(null);
|
const chartRef = ref<HTMLElement | null>(null);
|
||||||
let chartInstance: ECharts | null = null;
|
let chartInstance: ECharts | null = null;
|
||||||
@ -52,9 +144,25 @@ const stationNames = ref([]);
|
|||||||
// 静态数据 - 水温值 (°C),班多无数据设为 null
|
// 静态数据 - 水温值 (°C),班多无数据设为 null
|
||||||
const waterTemperatures = ref([]);
|
const waterTemperatures = ref([]);
|
||||||
|
|
||||||
// 静态时间数据
|
// 使用 computed 使 currentTime 响应 dataOne.value 的变化
|
||||||
const currentTime = "2026-04-08 23";
|
const currentTime = computed(() => dataOne.value || getCurrentHourTime());
|
||||||
|
// 选择器配置
|
||||||
|
const select = ref({
|
||||||
|
show: true,
|
||||||
|
value: '',
|
||||||
|
options: [],
|
||||||
|
picker: undefined,
|
||||||
|
format: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 日期选择器配置
|
||||||
|
const datetimePicker = ref({
|
||||||
|
show: true,
|
||||||
|
value: computed(() => getCurrentHourTime()),
|
||||||
|
format: "YYYY-MM-DD HH",
|
||||||
|
picker: "date" as const,
|
||||||
|
options: [],
|
||||||
|
});
|
||||||
// 初始化图表
|
// 初始化图表
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
if (!chartRef.value) return;
|
if (!chartRef.value) return;
|
||||||
@ -72,6 +180,16 @@ const initChart = () => {
|
|||||||
fontWeight: "normal",
|
fontWeight: "normal",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
zoomOnMouseWheel: true,
|
||||||
|
moveOnMouseMove: true,
|
||||||
|
filterMode: 'filter'
|
||||||
|
}
|
||||||
|
],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
formatter: (params: any) => {
|
formatter: (params: any) => {
|
||||||
@ -79,7 +197,7 @@ const initChart = () => {
|
|||||||
const data = params[0];
|
const data = params[0];
|
||||||
// 过滤掉 null 值的数据点
|
// 过滤掉 null 值的数据点
|
||||||
if (data.value !== null && data.value !== undefined) {
|
if (data.value !== null && data.value !== undefined) {
|
||||||
return `${currentTime}<br/>${data.name}:${data.value}°C`;
|
return `${currentTime.value}<br/>${data.name}:${data.value}°C`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@ -199,58 +317,250 @@ const handleResize = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
const params = {
|
if (loading.value) return; // 防止重复请求
|
||||||
filter: {
|
|
||||||
logic: "and",
|
loading.value = true;
|
||||||
filters: [
|
|
||||||
{ field: "rvcd", operator: "eq", dataType: "string", value: "SJLY1U" },
|
try {
|
||||||
{ field: "tm", operator: "gte", dataType: "date", value: "2026-04-17 00:00:00" },
|
const params = {
|
||||||
{ field: "tm", operator: "lte", dataType: "date", value: "2026-05-17 23:00:00" },
|
filter: {
|
||||||
],
|
logic: "and",
|
||||||
},
|
filters: [
|
||||||
sort: [{ field: "sort", dir: "asc" }],
|
{ field: "rvcd", operator: "eq", dataType: "string", value: paramsOne.rvcd },
|
||||||
};
|
{ field: "tm", operator: "gte", dataType: "date", value: paramsOne.tm[0] },
|
||||||
let res = await getKendoListCust(params);
|
{ field: "tm", operator: "lte", dataType: "date", value: paramsOne.tm[1] },
|
||||||
stationNames.value = res.data.data
|
|
||||||
.filter((item: any) => item.sttp == "1")
|
|
||||||
.map((item: any) => item.stnm);
|
|
||||||
console.log(stationNames.value);
|
|
||||||
|
|
||||||
waterTemperatures.value = res.data.data
|
|
||||||
.filter((item: any) => item.sttp == "2")
|
|
||||||
.map((item: any) => item.temperature);
|
|
||||||
// waterTemperatures.value = res.data.data.map((item: any) => item.temperature);
|
|
||||||
console.log(waterTemperatures.value);
|
|
||||||
|
|
||||||
// 数据获取完成后,初始化或更新图表
|
|
||||||
setTimeout(() => {
|
|
||||||
if (!chartInstance) {
|
|
||||||
initChart();
|
|
||||||
} else {
|
|
||||||
// 如果图表已存在,只更新数据
|
|
||||||
chartInstance.setOption({
|
|
||||||
xAxis: {
|
|
||||||
data: stationNames.value,
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
data: waterTemperatures.value,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
},
|
||||||
|
sort: [{ field: "sort", dir: "asc" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = await getKendoListCust(params);
|
||||||
|
let data = res.data.data || res.data
|
||||||
|
|
||||||
|
if(data.length > 0){
|
||||||
|
showemit.value = true
|
||||||
|
}else{
|
||||||
|
showemit.value = false
|
||||||
}
|
}
|
||||||
// 强制重绘,确保尺寸正确
|
|
||||||
setTimeout(() => {
|
stationNames.value = data
|
||||||
chartInstance?.resize();
|
.filter((item: any) => item.sttp == "1")
|
||||||
}, 100);
|
.map((item: any) => item.stnm);
|
||||||
}, 50);
|
console.log(stationNames.value);
|
||||||
};
|
|
||||||
|
|
||||||
|
waterTemperatures.value = data
|
||||||
|
.filter((item: any) => item.sttp == "2")
|
||||||
|
.map((item: any) => item.temperature);
|
||||||
|
// waterTemperatures.value = res.data.data.map((item: any) => item.temperature);
|
||||||
|
console.log(waterTemperatures.value);
|
||||||
|
|
||||||
|
// 数据获取完成后,初始化或更新图表
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!chartInstance) {
|
||||||
|
initChart();
|
||||||
|
} else {
|
||||||
|
// 如果图表已存在,只更新数据
|
||||||
|
chartInstance.setOption({
|
||||||
|
xAxis: {
|
||||||
|
data: stationNames.value,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: waterTemperatures.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 强制重绘,确保尺寸正确
|
||||||
|
setTimeout(() => {
|
||||||
|
chartInstance?.resize();
|
||||||
|
}, 100);
|
||||||
|
}, 50);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('数据加载失败:', error);
|
||||||
|
showemit.value = false;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//获取选择器配置参数
|
||||||
|
const getSelectConfig = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let obj: any = {}
|
||||||
|
if (baseid.value === 'all') {
|
||||||
|
obj = { rstcdStepSort: 'asc', siteStepSort: 'asc' }
|
||||||
|
} else {
|
||||||
|
obj = { siteStepSort: 'asc' }
|
||||||
|
}
|
||||||
|
const filters = [
|
||||||
|
{
|
||||||
|
field: 'wbsType',
|
||||||
|
operator: 'eq',
|
||||||
|
value: 'PSB_RVCD'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
if (baseid.value && baseid.value !== 'all') {
|
||||||
|
filters.push({
|
||||||
|
field: 'objId',
|
||||||
|
operator: 'eq', // contains
|
||||||
|
value: baseid.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let data: any = {
|
||||||
|
filter: {
|
||||||
|
logic: 'and',
|
||||||
|
filters
|
||||||
|
}
|
||||||
|
// select: ['WBS_CODE', 'WBS_NAME']
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
data.sort = [obj]
|
||||||
|
} else {
|
||||||
|
data = {
|
||||||
|
...data,
|
||||||
|
group: [
|
||||||
|
{ dir: 'asc', field: 'orderIndex' },
|
||||||
|
{ dir: 'asc', field: 'wbsCode' },
|
||||||
|
{ dir: 'asc', field: 'wbsName' },
|
||||||
|
{ dir: 'asc', field: 'objid' },
|
||||||
|
],
|
||||||
|
groupResultFlat: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let res = await wbsbGetKendoList(data)
|
||||||
|
let dataOne = res?.data?.data || res?.data
|
||||||
|
|
||||||
|
if (dataOne.length > 0) {
|
||||||
|
let dataMapNameMap: any = {}
|
||||||
|
let jiDiListMap: any = {}
|
||||||
|
jiDiList.value.forEach((item: any) => {
|
||||||
|
jiDiListMap[item.baseid] = item.basename
|
||||||
|
})
|
||||||
|
|
||||||
|
dataOne.map((item: any, index: number) => {
|
||||||
|
const { stcd, stnm, objId } = item
|
||||||
|
if (dataMapNameMap[objId]) {
|
||||||
|
dataMapNameMap[objId].push({ ...item, baseName: jiDiListMap[objId], baseId: objId })
|
||||||
|
} else {
|
||||||
|
dataMapNameMap[objId] = [{ ...item, baseName: jiDiListMap[objId], baseId: objId }]
|
||||||
|
}
|
||||||
|
return { label: item.wbsName, value: item.wbsCode, baseId: objId, baseName: jiDiListMap[objId] }
|
||||||
|
})
|
||||||
|
// debugger
|
||||||
|
let dataMapNameArr: any = []
|
||||||
|
|
||||||
|
Object.keys(dataMapNameMap).forEach((item: any) => {
|
||||||
|
dataMapNameArr.push({
|
||||||
|
title: dataMapNameMap[item]?.[0]?.baseName,
|
||||||
|
value: item,
|
||||||
|
selectable: false,
|
||||||
|
// baseId:dataMapNameMap[item]?.[0]?.baseId,
|
||||||
|
children: dataMapNameMap[item].map((item2: any) => {
|
||||||
|
return {
|
||||||
|
title: item2.wbsName,
|
||||||
|
value: item2.wbsCode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
dataMapNameArr = dataMapNameArr.sort((a: any, b: any) => {
|
||||||
|
const indexA = jiDiList.value.findIndex(item => item.baseid === a.value);
|
||||||
|
const indexB = jiDiList.value.findIndex(item => item.baseid === b.value);
|
||||||
|
return indexA - indexB;
|
||||||
|
})
|
||||||
|
select.value.options = filterSelectOptions(dataMapNameArr)
|
||||||
|
if (baseid.value === 'all') {
|
||||||
|
select.value.value = 'SJLY1U'
|
||||||
|
} else if (filterSelectOptions(dataMapNameArr)[0]?.children) {
|
||||||
|
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.children[0]?.value
|
||||||
|
} else if (filterSelectOptions(dataMapNameArr)[0]?.value) {
|
||||||
|
select.value.value = filterSelectOptions(dataMapNameArr)[0]?.value
|
||||||
|
} else {
|
||||||
|
select.value.value = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('选择器配置加载失败:', error);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterSelectOptions = (selectOptions: any) => {
|
||||||
|
if (selectOptions?.length === 1 && selectOptions?.[0]?.children?.length === 1) {
|
||||||
|
return [{ ...selectOptions?.[0]?.children?.[0], isLeaf: true }]
|
||||||
|
} else if (selectOptions.length) {
|
||||||
|
return selectOptions.map((e) => {
|
||||||
|
if (e?.children?.length > 1) {
|
||||||
|
return { ...e, isLeaf: false, selectable: true, children: e.children.map((item) => ({ ...item, isLeaf: true })) }
|
||||||
|
} else {
|
||||||
|
return { ...e.children[0], isLeaf: true }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
//监听子组件的数据变化
|
||||||
|
const handlePanelChange1 = (data) => {
|
||||||
|
console.log('当前所有控件状态:', data);
|
||||||
|
if (data.moreSelect) {
|
||||||
|
paramsOne.rvcd = data.moreSelect
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.datetime) {
|
||||||
|
dataOne.value = data.datetime
|
||||||
|
const datetime = data.datetime;
|
||||||
|
|
||||||
|
// 解析日期和时间
|
||||||
|
const [datePart, hourPart] = datetime.split(' ');
|
||||||
|
const [year, month, day] = datePart.split('-').map(Number);
|
||||||
|
|
||||||
|
// 创建当前时间的 Date 对象
|
||||||
|
const currentDate = new Date(year, month - 1, day, parseInt(hourPart), 0, 0);
|
||||||
|
|
||||||
|
// 创建一个月前的 Date 对象
|
||||||
|
const previousDate = new Date(year, month - 2, day, 0, 0, 0);
|
||||||
|
|
||||||
|
// 格式化函数:将 Date 对象转为 "YYYY-MM-DD HH:mm:ss" 格式
|
||||||
|
function formatDate(date) {
|
||||||
|
const y = date.getFullYear();
|
||||||
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const d = String(date.getDate()).padStart(2, '0');
|
||||||
|
const h = String(date.getHours()).padStart(2, '0');
|
||||||
|
const min = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const s = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return `${y}-${m}-${d} ${h}:${min}:${s}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成结果数组
|
||||||
|
const result = [
|
||||||
|
formatDate(currentDate), // "2026-05-15 15:00:00"
|
||||||
|
formatDate(previousDate) // "2026-04-15 00:00:00"
|
||||||
|
];
|
||||||
|
paramsOne.tm = result
|
||||||
|
console.log(paramsOne.tm, 'paramsOne.tm')
|
||||||
|
}
|
||||||
|
if (paramsOne.rvcd || paramsOne.tm.length > 0) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => JidiSelectEventStore.selectedItem,
|
||||||
|
(newVal) => {
|
||||||
|
baseid.value = newVal.wbsCode;
|
||||||
|
getSelectConfig()
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
);
|
||||||
// 页面加载时执行的逻辑
|
// 页面加载时执行的逻辑
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 延迟初始化,确保 DOM 渲染完成
|
// 延迟初始化,确保 DOM 渲染完成
|
||||||
init();
|
init();
|
||||||
|
|
||||||
window.addEventListener("resize", handleResize);
|
window.addEventListener("resize", handleResize);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -264,6 +574,12 @@ onBeforeUnmount(() => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.chart-container {
|
.chart-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 252px;
|
height: 252px ;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.ant-spin-nested-loading ){
|
||||||
|
height: 252px !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -19,14 +19,14 @@ service.interceptors.request.use(
|
|||||||
`Expected 'config' and 'config.headers' not to be undefined`
|
`Expected 'config' and 'config.headers' not to be undefined`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (config.url.includes('/dec-lygk-base-server')) {
|
if (config.url.includes('/dec-lygk-base-server') || config.url.includes('/wmp-env-server')) {
|
||||||
config.headers._appid = '974975A6-47FD-4C04-9ACD-68938D2992BD';
|
config.headers._appid = '974975A6-47FD-4C04-9ACD-68938D2992BD';
|
||||||
config.headers._isolateid = '5b34aecb-adfb-4dfc-ad95-21505a9eb388';
|
config.headers._isolateid = '5b34aecb-adfb-4dfc-ad95-21505a9eb388';
|
||||||
config.headers._platformid = '31e6d13f-c359-4a19-8e67-b6f868f2401b';
|
config.headers._platformid = '31e6d13f-c359-4a19-8e67-b6f868f2401b';
|
||||||
config.headers._sysid = '10EC2E0B-AEA9-4757-83A2-201BA1BC54E9';
|
config.headers._sysid = '10EC2E0B-AEA9-4757-83A2-201BA1BC54E9';
|
||||||
|
|
||||||
config.headers.authorization =
|
config.headers.authorization =
|
||||||
'bearer f96d0008-f77f-497a-b05d-82cded9ccd89';
|
'bearer e5445e2e-798c-4cfe-9cfe-0926e606c85c';
|
||||||
config.baseURL = '/';
|
config.baseURL = '/';
|
||||||
} else {
|
} else {
|
||||||
const user = useUserStoreHook();
|
const user = useUserStoreHook();
|
||||||
|
|||||||
@ -54,6 +54,16 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
|||||||
new RegExp('^/api/dec-lygk-base-server'),
|
new RegExp('^/api/dec-lygk-base-server'),
|
||||||
'/api/dec-lygk-base-server'
|
'/api/dec-lygk-base-server'
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
'/api/wmp-env-server': {
|
||||||
|
target: 'https://211.99.26.225:12122',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
rewrite: path =>
|
||||||
|
path.replace(
|
||||||
|
new RegExp('^/api/wmp-env-server'),
|
||||||
|
'/api/wmp-env-server'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user