|
@@ -1,5 +1,7 @@
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
-import { computed, onMounted, ref, useTemplateRef, watch } from 'vue';
|
|
|
|
|
|
+import { computed, nextTick, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
|
|
|
|
+import BScroll from '@better-scroll/core';
|
|
|
|
+import MouseWheel from '@better-scroll/mouse-wheel';
|
|
import { message } from 'ant-design-vue';
|
|
import { message } from 'ant-design-vue';
|
|
|
|
|
|
import ButtonTabs from '@/components/ButtonTabs.vue';
|
|
import ButtonTabs from '@/components/ButtonTabs.vue';
|
|
@@ -57,6 +59,7 @@ interface EnvMonitorStyle {
|
|
interface TimeList {
|
|
interface TimeList {
|
|
time: string;
|
|
time: string;
|
|
integral: boolean;
|
|
integral: boolean;
|
|
|
|
+ index: number;
|
|
}
|
|
}
|
|
|
|
|
|
const envMonitorListRef = useTemplateRef('envMonitorList');
|
|
const envMonitorListRef = useTemplateRef('envMonitorList');
|
|
@@ -92,6 +95,7 @@ const monitoringId = ref<number>();
|
|
const formRef = ref<FormInstance>();
|
|
const formRef = ref<FormInstance>();
|
|
const listDisplay = ref<boolean>(true);
|
|
const listDisplay = ref<boolean>(true);
|
|
const timeList = ref<TimeList[]>([]);
|
|
const timeList = ref<TimeList[]>([]);
|
|
|
|
+const timeLineIndex = ref<number>(0);
|
|
|
|
|
|
const envMonitorStyle = ref<EnvMonitorStyle>({
|
|
const envMonitorStyle = ref<EnvMonitorStyle>({
|
|
background: `url(${envMonitorBgc})`,
|
|
background: `url(${envMonitorBgc})`,
|
|
@@ -497,13 +501,17 @@ const switchDisplay = () => {
|
|
envMonitorListRef.value?.getMonitoringList();
|
|
envMonitorListRef.value?.getMonitoringList();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
+const scrollWrapper = ref<HTMLElement | null>(null);
|
|
|
|
+const scrollContent = ref<HTMLElement | null>(null); // 新增内容层 ref
|
|
|
|
+let bs: BScroll | null = null;
|
|
|
|
|
|
-const generateTimeArray = () => {
|
|
|
|
|
|
+const generateTimeArray = async () => {
|
|
const now = new Date();
|
|
const now = new Date();
|
|
const currentHour = now.getHours();
|
|
const currentHour = now.getHours();
|
|
const currentMinute = now.getMinutes();
|
|
const currentMinute = now.getMinutes();
|
|
const minutes = [0, 12, 24, 36, 48];
|
|
const minutes = [0, 12, 24, 36, 48];
|
|
const result = [];
|
|
const result = [];
|
|
|
|
+ let index = 0;
|
|
|
|
|
|
for (let hour = 0; hour <= currentHour; hour++) {
|
|
for (let hour = 0; hour <= currentHour; hour++) {
|
|
for (const minute of minutes) {
|
|
for (const minute of minutes) {
|
|
@@ -515,13 +523,71 @@ const generateTimeArray = () => {
|
|
result.push({
|
|
result.push({
|
|
time: timeStr,
|
|
time: timeStr,
|
|
integral: minute === 0, // 分钟为0时标记为整点
|
|
integral: minute === 0, // 分钟为0时标记为整点
|
|
|
|
+ index: index++,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- return result;
|
|
|
|
|
|
+ timeList.value = result;
|
|
|
|
+
|
|
|
|
+ // 关键步骤:数据更新后刷新滚动
|
|
|
|
+ await nextTick();
|
|
|
|
+ timeLineIndex.value = timeList.value.length - 1;
|
|
|
|
+ // 关键修复:延迟确保渲染完成
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ returnCurrently();
|
|
|
|
+ }, 50); // 增加短延迟
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 返回当前
|
|
|
|
+const returnCurrently = () => {
|
|
|
|
+ bs?.refresh();
|
|
|
|
+
|
|
|
|
+ if (scrollContent.value && scrollWrapper.value) {
|
|
|
|
+ const contentWidth = scrollContent.value.scrollWidth;
|
|
|
|
+ const wrapperWidth = scrollWrapper.value.clientWidth;
|
|
|
|
+ const maxScrollX = contentWidth - wrapperWidth;
|
|
|
|
+
|
|
|
|
+ if (maxScrollX > 0) {
|
|
|
|
+ bs?.scrollTo(-maxScrollX, 0, 800);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+const addTimeLine = (index: number) => {
|
|
|
|
+ timeLineIndex.value = index;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+// 注册鼠标滚轮插件
|
|
|
|
+BScroll.use(MouseWheel);
|
|
|
|
+
|
|
|
|
+// 初始化 Better-Scroll
|
|
|
|
+const initScroll = async () => {
|
|
|
|
+ await nextTick(); // 等待 DOM 更新
|
|
|
|
+ if (scrollWrapper.value) {
|
|
|
|
+ bs?.destroy(); // 销毁旧实例避免内存泄漏
|
|
|
|
+ bs = new BScroll(scrollWrapper.value, {
|
|
|
|
+ scrollX: true, // 启用横向滚动
|
|
|
|
+ scrollY: false, // 禁用纵向滚动
|
|
|
|
+ probeType: 3, // 实时派发滚动事件
|
|
|
|
+ mouseWheel: {
|
|
|
|
+ speed: 20, // 滚动速度
|
|
|
|
+ invert: false, // 是否反转滚动方向
|
|
|
|
+ easeTime: 300, // 动画缓动时间
|
|
|
|
+ discreteTime: 300, // 离散滚动间隔
|
|
|
|
+ },
|
|
|
|
+ click: true, // 启用点击事件
|
|
|
|
+ disableMouse: false, // 不禁用鼠标控制
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+onUnmounted(() => {
|
|
|
|
+ if (bs) {
|
|
|
|
+ bs.destroy(); // 组件卸载时销毁实例
|
|
|
|
+ }
|
|
|
|
+});
|
|
|
|
+
|
|
watch(
|
|
watch(
|
|
() => gradeOne.value,
|
|
() => gradeOne.value,
|
|
(count) => {
|
|
(count) => {
|
|
@@ -545,7 +611,8 @@ watch(
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
addAllGatewayList(-1);
|
|
addAllGatewayList(-1);
|
|
getDeviceGroup();
|
|
getDeviceGroup();
|
|
- timeList.value = generateTimeArray();
|
|
|
|
|
|
+ generateTimeArray();
|
|
|
|
+ initScroll();
|
|
});
|
|
});
|
|
|
|
|
|
const areaEditorRef = useTemplateRef('areaEditor');
|
|
const areaEditorRef = useTemplateRef('areaEditor');
|
|
@@ -664,13 +731,40 @@ const copyAreaCanvas = () => {
|
|
</AFlex>
|
|
</AFlex>
|
|
|
|
|
|
<div class="content-monitoring" v-show="listDisplay">
|
|
<div class="content-monitoring" v-show="listDisplay">
|
|
- <div class="content-monitoring-top">
|
|
|
|
- <AFlex>
|
|
|
|
- <div v-for="item in timeList" :key="item.time">
|
|
|
|
- <div>1</div>
|
|
|
|
|
|
+ <AFlex class="content-monitoring-top" align="flex-end">
|
|
|
|
+ <div class="scroll-wrapper" ref="scrollWrapper">
|
|
|
|
+ <div class="scroll-content" ref="scrollContent">
|
|
|
|
+ <AFlex align="flex-end" class="time-line">
|
|
|
|
+ <div> </div>
|
|
|
|
+
|
|
|
|
+ <div v-for="item in timeList" :key="item.time">
|
|
|
|
+ <AFlex :vertical="true">
|
|
|
|
+ <div v-if="item.integral" class="time-style">{{ item.time }}</div>
|
|
|
|
+ <div @click="addTimeLine(item.index)">
|
|
|
|
+ <AFlex align="flex-end" class="integral-style">
|
|
|
|
+ <AFlex
|
|
|
|
+ :vertical="true"
|
|
|
|
+ justify="center"
|
|
|
|
+ align="center"
|
|
|
|
+ v-if="item.index === timeLineIndex"
|
|
|
|
+ class="confirm-axis-div"
|
|
|
|
+ >
|
|
|
|
+ <div class="confirm-axis-circle"></div>
|
|
|
|
+ <div class="confirm-axis"></div>
|
|
|
|
+ </AFlex>
|
|
|
|
+ <div v-else :class="item.integral ? 'integral' : 'integral-height'"></div>
|
|
|
|
+ </AFlex>
|
|
|
|
+ </div>
|
|
|
|
+ </AFlex>
|
|
|
|
+ </div>
|
|
|
|
+ </AFlex>
|
|
</div>
|
|
</div>
|
|
|
|
+ </div>
|
|
|
|
+ <AFlex>
|
|
|
|
+ <AButton type="text" class="return-currently" @click="returnCurrently">返回当前</AButton>
|
|
|
|
+ <AButton type="primary" class="timeline-confirm">确定</AButton>
|
|
</AFlex>
|
|
</AFlex>
|
|
- </div>
|
|
|
|
|
|
+ </AFlex>
|
|
<AFlex class="content-monitoring-canvas">
|
|
<AFlex class="content-monitoring-canvas">
|
|
<div class="content-monitoring-canvas-left">
|
|
<div class="content-monitoring-canvas-left">
|
|
<div v-for="item in monitoringPointData" :key="item.id" :data-point-id="item.id">
|
|
<div v-for="item in monitoringPointData" :key="item.id" :data-point-id="item.id">
|
|
@@ -1088,6 +1182,78 @@ const copyAreaCanvas = () => {
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
<style lang="scss" scoped>
|
|
|
|
+.confirm-axis-div {
|
|
|
|
+ margin-left: -3px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.confirm-axis-circle {
|
|
|
|
+ width: 10px;
|
|
|
|
+ height: 10px;
|
|
|
|
+ background: #32bac0;
|
|
|
|
+ border-radius: 50%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.return-currently {
|
|
|
|
+ margin-left: 10px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.timeline-confirm {
|
|
|
|
+ width: 76px;
|
|
|
|
+ height: 32px;
|
|
|
|
+ margin-right: 24px;
|
|
|
|
+ margin-left: 10px;
|
|
|
|
+ border-radius: 16px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.scroll-wrapper {
|
|
|
|
+ position: relative;
|
|
|
|
+ width: 100%;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.scroll-content {
|
|
|
|
+ display: inline-block; /* 关键:横向布局 */
|
|
|
|
+ white-space: nowrap; /* 禁止内容换行 */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.time-style {
|
|
|
|
+ margin-left: -10px;
|
|
|
|
+ font-size: 10px;
|
|
|
|
+ font-style: normal;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ color: rgb(0 0 0 / 50%);
|
|
|
|
+ text-align: left;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.time-line {
|
|
|
|
+ height: 39px;
|
|
|
|
+ border-bottom: 1px solid #979797;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.confirm-axis {
|
|
|
|
+ width: 1px;
|
|
|
|
+ height: 20px;
|
|
|
|
+ border: 1px solid #32bac0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.integral-style {
|
|
|
|
+ width: 31px;
|
|
|
|
+ height: 20px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.integral-height {
|
|
|
|
+ width: 1px;
|
|
|
|
+ height: 5px;
|
|
|
|
+ border: 1px solid #979797;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.integral {
|
|
|
|
+ width: 1px;
|
|
|
|
+ height: 10px;
|
|
|
|
+ border: 1px solid #979797;
|
|
|
|
+}
|
|
|
|
+
|
|
.icon-button-margin {
|
|
.icon-button-margin {
|
|
margin: 16px 0 0 16px;
|
|
margin: 16px 0 0 16px;
|
|
}
|
|
}
|