SourceTermAnalysisSystem_vue/src/pages/sjfx/one/index.vue
2026-08-03 18:09:36 +08:00

813 lines
22 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang='ts'>
import type { FormInstance, FormRules } from 'element-plus';
import { Search } from '@element-plus/icons-vue';
import dayjs from 'dayjs';
import { ElMessage } from 'element-plus';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import JulianDate from '@/gis/common/clock/JulianData/JulianDate';
import { initMap2D } from '@/gis/ol/map';
import { removeClockOnTickEvent, setAnimationTime, setClockOnTickEvent, setcurrentTime, setMultiplier, setShouldAnimate } from '@/gis/ol/olClock';
import { SjfxPointHandle } from '@/gis/ol/olSJFX';
import MapRightMenuEvent from '@/layout/Container/mapRightMenu.vue';
import { getSampleMonitor } from '@/utils/axios/sjfx';
const { t } = useI18n();
// gis地图功能实例存储
const sjfxPointHandle = ref<SjfxPointHandle | null>(null);
const sampleTypeList = ref<any>([
{ label: '颗粒物', value: 'P' },
{ label: '惰性气体', value: 'B' },
]);
const dataSourceList = ref<any>([
{ label: '自动处理', value: '1' },
{ label: '交互分析', value: '2' },
]);
const form = ref<any>({
// sampleType: 'P',
// dataSource: '1',
// startDate: '2025-01-01',
// endDate: '2025-09-15',
// createTime: ['2025-01-01', '2025-09-15'],
});
const formRef = ref<FormInstance>();
const rules = reactive<FormRules<any>>({
sampleType: [{ required: true, message: '请选择样品类型', trigger: 'change' }],
dataSource: [{ required: true, message: '请选择数据来源', trigger: 'change' }],
createTime: [{ required: true, message: '请选择创建时间范围', trigger: 'change', validator: (rule, value, callback) => {
if (!value || value.length === 0) {
callback(new Error('请选择创建时间范围'));
}
else {
callback();
}
} }],
});
interface TimeListData {
[key: string]: any; // key 是日期字符串(如 "2025-01-17"value 是对应的数据类型
}
const searchResultTimeListData = ref<TimeListData>({});
const initStationLevelCount = ref([]);
interface StationBaseData {
stationCode: string; // 明确包含 stationCode 属性
lon: string;
lat: string;
}
const searchResultBaseData = ref<StationBaseData[]>([]);
// 滑块数据
const sliderValue = ref(0);
const sliderStart = ref<dayjs.Dayjs | null>(null);
const sliderEnd = ref<dayjs.Dayjs | null>(null);
const sliderMarks = computed(() => {
const obj: Record<number, string> = {};
if (!sliderStart.value || !sliderEnd.value)
return obj;
const totalDays = Math.max(sliderEnd.value.diff(sliderStart.value, 'day'), 0);
// 计算刻度间隔总天数越多间隔越大确保最多显示10个刻度避免重叠
const interval = Math.max(Math.ceil(totalDays / 10), 1); // 最小间隔为1
for (let i = 0; i <= totalDays; i++) {
// 只显示间隔倍数的刻度如i=0,3,6...
if (i % interval === 0) {
const date = sliderStart.value.add(i, 'day');
obj[i] = date.format('MM-DD'); // 简化日期格式(只显示月-日,节省空间)
}
}
return obj;
});
const sliderMax = computed(() => {
if (!sliderStart.value || !sliderEnd.value)
return 0;
return Math.max(sliderEnd.value.diff(sliderStart.value, 'day'), 0);
});
// 6. 滑块变化回调
function handleChange(value: number) {
const currentTime = sliderStart.value?.add(value, 'day');
setcurrentTime(currentTime?.valueOf());
console.log('当前选中:', sliderMarks.value[value]);
}
function setTime(val: any[]) {
form.value.startDate = val[0];
form.value.endDate = val[1];
console.log(val);
}
// 其他代码保持不变...
const currentPickervalue = ref<Date | null>(null); // 明确类型为Date或null
const showCalendar = ref(false); // 控制日历显示隐藏
const showSilder = ref(false); // 控制时间轴显示隐藏
const playFalg = ref<boolean>(false);
const flagMaxRunHandle = ref<boolean>(false);//判断是否已经到了最大值
const checkBoxList = ref([{
isCheck: true,
label: '级别1',
color: '#1569c7',
isMargin: true,
key: 'level1',
}, {
isCheck: true,
label: '级别2',
color: '#008000',
isMargin: true,
key: 'level2',
}, {
isCheck: true,
label: '级别3',
color: '#ffff00',
key: 'level3',
}, {
isCheck: true,
label: '级别4',
color: '#ff9933',
isMargin: true,
key: 'level4',
}, {
isCheck: true,
label: '级别5',
color: '#ff0000',
key: 'level5',
}]);
// 禁用逻辑
function disableDateRange(date: Date) {
// 无有效日期范围时不禁用
if (!form.value.createTime || form.value.createTime.length !== 2) {
return false;
}
const [startDateStr, endDateStr] = form.value.createTime;
// 转换为日期对象并清零时间部分(只保留年月日)
const startDate = new Date(startDateStr);
startDate.setHours(0, 0, 0, 0); // 开始日期的零点
const endDate = new Date(endDateStr);
endDate.setHours(0, 0, 0, 0); // 结束日期的零点
// 待判断的日期也清零时间部分
const targetDate = new Date(date);
targetDate.setHours(0, 0, 0, 0);
// 禁用逻辑:小于开始日期 或 大于结束日期(等于时不禁用)
const isDisabled = targetDate < startDate || targetDate > endDate;
return isDisabled;
}
// 获取搜索数据
function getData() {
const formEl = formRef.value;
if (!formEl)
return;
formEl.validate((valid, fields) => {
if (valid) {
getSampleMonitor(form.value, (res) => {
console.log(res, '样品监测结果数据');
// 显示日历
if (res.result.stationInfo.length) {
showCalendar.value = true;
const [startDateStr, endDateStr] = form.value.createTime; // 获取查询的时间范围
// 关键:给滑块起止时间赋值(初始化滑块范围)
sliderStart.value = dayjs(startDateStr);
sliderEnd.value = dayjs(endDateStr);
// 日历选中值初始化(原有逻辑保留)
// currentPickervalue.value = new Date(startDateStr);
// 基础数据赋值(原有逻辑保留)
searchResultBaseData.value = res.result.stationInfo;
searchResultTimeListData.value = res.result.sampleMonitorResultList;
initStationLevelCount.value = res.result.stationLevelCount
initPointSt()
}
else {
ElMessage({
type: 'error',
message: `未查询到数据,请重试!`,
});
}
});
}
});
}
// 初始化赋值上图
function initPointSt(){
const currentList = initStationLevelCount.value||[];
const data: any = [];
currentList.forEach((item: any) => {
const find = searchResultBaseData.value.find((v) => {
return item.stationCode === v.stationCode;
});
if (find) {
data.push({
id: find.stationCode,
longitude: find.lon,
latitude: find.lat,
label: find.stationCode,
level: item.categoryCount ? JSON.parse(JSON.stringify(item.categoryCount)) : {},
});
}
});
const noCheckData = checkBoxList.value.filter((item: any) => !item.isCheck).map((item: any) => item.key);
data.forEach((item: any) => {
Object.keys(item.level).forEach((levelKey) => {
// 如果该键在未勾选列表中,则删除
if (noCheckData.includes(levelKey)) {
delete item.level[levelKey as keyof typeof item.level];
}
});
});
console.log(data);
sjfxPointHandle.value?.addPoints(data);
}
// 运行按钮点击事件
function runHandle() {
showSilder.value = true;
playFalg.value = !playFalg.value;
const [startDateStr, endDateStr] = form.value.createTime;
setMultiplier(60 * 60 * 30);
setAnimationTime({
startTime: startDateStr,
endTime: endDateStr,
});
setcurrentTime(startDateStr);
setShouldAnimate(playFalg.value);
}
// 进行起止动画
function playAnimation() {
playFalg.value = !playFalg.value;
setShouldAnimate(playFalg.value);
}
// 重置按钮点击事件
function resetHandle() {
// const [startDateStr] = form.value.createTime; // 获取查询的时间范围
// currentPickervalue.value = new Date(startDateStr);
// checkBoxList.value.forEach((item) => {
// item.isCheck = true;
// });
currentPickervalue.value = null;
initPointSt();
playFalg.value = false;
showSilder.value = false;
setShouldAnimate(false);
}
// 根据选中值获取当前的数据
function getTimeDataFromDate(date: string) {
console.log(date,"222222")
const currentList = searchResultTimeListData.value[date];
const data: any = [];
currentList.forEach((item: any) => {
const find = searchResultBaseData.value.find((v) => {
return item.stationCode === v.stationCode;
});
if (find) {
data.push({
id: find.stationCode,
longitude: find.lon,
latitude: find.lat,
label: find.stationCode,
level: item.categoryCount ? JSON.parse(JSON.stringify(item.categoryCount)) : {},
});
}
});
const noCheckData = checkBoxList.value.filter((item: any) => !item.isCheck).map((item: any) => item.key);
data.forEach((item: any) => {
Object.keys(item.level).forEach((levelKey) => {
// 如果该键在未勾选列表中,则删除
if (noCheckData.includes(levelKey)) {
delete item.level[levelKey as keyof typeof item.level];
}
});
});
console.log(data);
sjfxPointHandle.value?.addPoints(data);
}
// 关闭日历
function closeCalendar() {
showCalendar.value = false;
EventBusTool.getEventBus().publish('mapRightMenuUIEvent', {
key: 'calendar',
falg: false,
});
// sjfxPointHandle.value?.clearPoints();
}
// 监听选中值变化
watch(currentPickervalue, (newDate) => {
console.log(newDate);
if (newDate) {
const date = dayjs(newDate);
const ymd = date.format('YYYY-MM-DD');
// 计算当前日期对应滑块的位置(距离 start 的天数差)
const sliderVal = Math.max(date.diff(sliderStart.value, 'day'), 0);
// 同步滑块值(避免超出 max
sliderValue.value = Math.min(sliderVal, sliderMax.value);
console.log(sliderVal,sliderValue.value,sliderMax.value,"12121")
if((sliderVal==sliderMax.value&&!flagMaxRunHandle)){
flagMaxRunHandle.value = true;
getTimeDataFromDate(ymd);
}else if(sliderVal!=sliderMax.value){
getTimeDataFromDate(ymd);
}else{
playFalg.value = false;
// showSilder.value = false;
setShouldAnimate(false);
}
}
else {
console.log('取消了日期选中');
}
});
onMounted(() => {
// 初始化二维地图实例
initMap2D('sjfxOlContainer');
sjfxPointHandle.value = new SjfxPointHandle();
nextTick(() => {
// 注册业务帧回调函数
setClockOnTickEvent({
key: '数据分析',
callback(timeClock: any) {
if (timeClock.shouldAnimate) {
const timestamp = JulianDate.toDate(timeClock.currentTime);
currentPickervalue.value = dayjs(timestamp).toDate();
}
},
});
EventBusTool.getEventBus().subscribe('sjfxCalendarEvent', (res: any) => {
showCalendar.value = res.falg;
});
});
});
// 组件即将销毁时执行清理
onUnmounted(() => {
removeClockOnTickEvent('数据分析');
});
</script>
<template>
<div class="sjfx-one-content">
<div class="head">
<el-form ref="formRef" :model="form" label-width="100px" class="queryForm" :rules="rules">
<el-row :gutter="10">
<el-col :span="6">
<el-form-item :label="t('sampleType')" prop="sampleType">
<el-select
v-model="form.sampleType" :placeholder="t('pleaseSelect')" style="width: 160px;"
popper-style="background-color: transparent;border: solid 1px #0da397;width:100px;overflow: hidden;border-radius: 0;"
>
<el-option
v-for="item in sampleTypeList" :key="item.value"
style="background-color: transparent; height: 30px;color: #fff;" :label="item.label" :value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<!-- <el-col :span="3">
<el-form-item :label="t('stationName')">
<el-input v-model="form.stationName" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item :label="t('nuclideName')">
<el-input v-model="form.nuclideName" style="width: 100%;" />
</el-form-item>
</el-col> -->
<el-col :span="6">
<el-form-item :label="t('dataSource')" prop="dataSource">
<el-select
v-model="form.dataSource" :placeholder="t('pleaseSelect')" style="width: 160px;"
popper-style="background-color: transparent;border: solid 1px #0da397;width:100px;overflow: hidden;border-radius: 0;"
>
<el-option
v-for="item in dataSourceList" :key="item.value"
style="background-color: transparent; height: 30px;color: #fff;" :label="item.label" :value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="t('createTime')" prop="createTime">
<el-date-picker
v-model="form.createTime" unlink-panels type="daterange" :default-time="new Date()"
range-separator="--" :start-placeholder="t('startDate')" :end-placeholder="t('endDate')"
value-format="YYYY-MM-DD" style="width: 100%;" @change="setTime"
/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item>
<el-button :icon="Search" @click="getData">
{{ t('refresh') }}
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div id="sjfxOlContainer" class="sjfxOlContainer">
<MapRightMenuEvent />
</div>
<!-- 日历卡片通过showCalendar控制显示 -->
<div v-if="showCalendar" class="card-calendar">
<div class="calendarHeader">
<div class="text">
日历
</div>
<div class="icon" @click="closeCalendar">
<SvgIcon name="drawer_header" width="20" height="20" />
</div>
</div>
<div class="calendarContent">
<el-date-picker-panel
v-model="currentPickervalue" :border="false" class="pickerTime"
:disabled-date="disableDateRange"
/>
<div class="checkBoxGroup">
<el-checkbox
v-for="(v, i) in checkBoxList" :key="i" v-model="v.isCheck" :label="v.label" size="large"
:style="{ marginRight: v.isMargin ? '55px' : '0' }" :data-color="v.color"
/>
</div>
<div class="btnGruop">
<div class="runBtn" @click="runHandle" v-if="!showSilder">
运行
</div>
<div class="runBtn" @click="playAnimation()" v-else-if="!playFalg">
播放
</div>
<div class="runBtn" @click="playAnimation()" v-else-if="playFalg">
暂停
</div>
<div class="resetBtn" @click="resetHandle">
重置
</div>
</div>
</div>
</div>
<div v-if="showCalendar" class="levelLegend">
<div class="text">
级别
</div>
<div class="legendItem">
<div class="color" :style="{ backgroundColor: '#fff' }" />
<div class="label">
</div>
</div>
<div v-for="(v, i) in checkBoxList" :key="i" class="legendItem">
<div class="color" :style="{ backgroundColor: v.color }" />
<div class="label">
{{ v.label }}
</div>
</div>
</div>
<div v-if="showSilder" class="sliderDom">
<div class="icon" @click="playAnimation()">
<SvgIcon
:name="!playFalg ? 'playAnimation' : 'zanting'" :title="!playFalg ? '播放' : '暂停'" width="34"
height="34"
/>
</div>
<div class="slider">
<el-slider
v-model="sliderValue" :style="{ width: '52vw' }" :show-tooltip="false" :max="sliderMax"
:marks="sliderMarks" @input="handleChange"
/>
</div>
</div>
</div>
</template>
<style scoped lang='less'>
.sjfx-one-content {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
position: relative;
.head {
width: 100%;
height: 50px;
line-height: 50px;
display: flex;
justify-content: space-between;
margin: 10px 0 16px;
padding-top: 5px;
.queryForm {
display: flex;
align-items: center;
:deep(.el-form) {
padding-top: 20px;
}
:deep(.el-form-item__content) {
&:nth-last-child(1) {
margin-left: 0 !important;
}
}
:deep(.el-range-separator) {
color: #fff;
}
:deep(.el-select__wrapper),
:deep(.el-date-editor) {
background-color: rgba(3, 53, 63, 0.51) !important;
border-radius: 0%;
border: solid 1px #0b8c82 !important;
}
}
:deep(.el-button) {
width: 90px;
height: 32px;
margin: 0 5px;
background-color: #1397a3;
font-size: 16px;
font-weight: normal;
color: #ffffff;
border-radius: 0;
border: 1px solid #1397a3;
}
}
.sjfxOlContainer {
width: 100%;
height: calc(100% - 50px);
position: relative;
}
.card-calendar {
width: 325px;
background-color: #021a1d;
border: solid 1px #0a544e;
opacity: 0.9;
position: absolute;
top: 375px;
right: 50px;
.calendarHeader {
width: 100%;
height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
color: #0cebc9;
font-size: 16px;
padding-left: 20px;
padding-right: 15px;
.icon {
transform: rotate(-90deg);
cursor: pointer;
}
}
.calendarContent {
padding-bottom: 15px;
:deep(.pickerTime) {
background: transparent;
.el-date-picker__header {
background-color: #26575d !important;
padding: 0 !important;
margin: 0 15px;
.el-date-picker__header-label {
color: #0cebc9 !important;
}
.arrow-left,
.arrow-right {
transform: scale(1.5);
color: #ade6ee;
margin-top: 0;
}
.d-arrow-left,
.d-arrow-right {
display: none !important;
}
}
.el-picker-panel__content {
margin-top: 5px;
.next-month {
display: none !important;
}
.prev-month {
opacity: 0 !important;
cursor: default;
pointer-events: none;
}
.el-date-table td.today.disabled .el-date-table-cell__text {
color: var(--el-text-color-placeholder);
}
.el-date-table td.today .el-date-table-cell__text {
color: #fff;
}
.available {
color: #fff;
}
.el-date-table td .el-date-table-cell .el-date-table-cell__text {
border-radius: 0;
}
.el-date-table td.disabled .el-date-table-cell {
background-color: transparent;
}
.el-date-table th {
border-bottom: none;
}
}
}
.checkBoxGroup {
display: flex;
justify-content: start;
flex-wrap: wrap;
align-items: center;
margin: 0 15px;
border-top: 1px solid #26575d;
padding-top: 15px;
.el-checkbox {
margin-right: 0;
}
:deep(.el-checkbox__label) {
color: #fff;
}
:deep(.el-checkbox__inner) {
background-color: #0a4f44;
border: solid 1px #2c887a;
}
:deep(.el-checkbox__input.is-checked .el-checkbox__inner:after) {
border-color: #000 !important;
}
:deep(.el-checkbox__input.is-checked + .el-checkbox__label) {
color: #fff !important;
}
:deep(.el-checkbox[data-color='#1569c7'] .el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #1569c7;
border-color: #1569c7;
}
:deep(.el-checkbox[data-color='#008000'] .el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #008000;
border-color: #008000;
}
:deep(.el-checkbox[data-color='#ffff00'] .el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #ffff00;
border-color: #ffff00;
}
:deep(.el-checkbox[data-color='#ff9933'] .el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #ff9933;
border-color: #ff9933;
}
:deep(.el-checkbox[data-color='#ff0000'] .el-checkbox__input.is-checked .el-checkbox__inner) {
background-color: #ff0000;
border-color: #ff0000;
}
}
.btnGruop {
width: 100%;
height: 32px;
display: flex;
justify-content: space-between;
padding: 0 15px;
margin-top: 15px;
.runBtn,
.resetBtn {
width: 48%;
height: 32px;
font-size: 16px;
color: #ffffff;
box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.4);
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
// user-select: none;
}
.runBtn {
background-color: #1397a3;
}
.resetBtn {
background-color: #406979;
}
}
}
}
.levelLegend {
width: 75px;
height: 138px;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: #021a1d;
border: solid 1px #0a544e;
opacity: 0.9;
position: absolute;
bottom: 25px;
left: 25px;
padding: 5px;
font-size: 12px;
.text {
color: #ffffff;
}
.legendItem {
display: flex;
justify-content: start;
align-items: center;
color: #9ab1bc;
.color {
width: 13px;
height: 13px;
border-radius: 50%;
margin-right: 5px;
}
}
}
}
.sliderDom {
width: 55vw;
margin: 0 auto;
z-index: 1000;
position: fixed;
left: 450px;
bottom: 60px;
display: flex;
justify-content: space-between;
align-items: center;
.icon {
cursor: pointer;
}
}
:deep(.el-slider__bar) {
background-color: #09b59b;
}
:deep(.el-slider__button) {
border: 5px solid #09b59b;
}
:deep(.el-slider__marks-text) {
margin-top: 25px;
color: #ffffff;
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.83);
}
</style>