Эх сурвалжийг харах

feat(views): 环境监控初步编写”时间轴滚动查询“功能

wangshun 2 сар өмнө
parent
commit
8112fc61f1

+ 175 - 9
src/views/env-monitor/EnvMonitor.vue

@@ -1,5 +1,7 @@
 <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 ButtonTabs from '@/components/ButtonTabs.vue';
@@ -57,6 +59,7 @@ interface EnvMonitorStyle {
 interface TimeList {
   time: string;
   integral: boolean;
+  index: number;
 }
 
 const envMonitorListRef = useTemplateRef('envMonitorList');
@@ -92,6 +95,7 @@ const monitoringId = ref<number>();
 const formRef = ref<FormInstance>();
 const listDisplay = ref<boolean>(true);
 const timeList = ref<TimeList[]>([]);
+const timeLineIndex = ref<number>(0);
 
 const envMonitorStyle = ref<EnvMonitorStyle>({
   background: `url(${envMonitorBgc})`,
@@ -497,13 +501,17 @@ const switchDisplay = () => {
     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 currentHour = now.getHours();
   const currentMinute = now.getMinutes();
   const minutes = [0, 12, 24, 36, 48];
   const result = [];
+  let index = 0;
 
   for (let hour = 0; hour <= currentHour; hour++) {
     for (const minute of minutes) {
@@ -515,13 +523,71 @@ const generateTimeArray = () => {
       result.push({
         time: timeStr,
         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(
   () => gradeOne.value,
   (count) => {
@@ -545,7 +611,8 @@ watch(
 onMounted(() => {
   addAllGatewayList(-1);
   getDeviceGroup();
-  timeList.value = generateTimeArray();
+  generateTimeArray();
+  initScroll();
 });
 
 const areaEditorRef = useTemplateRef('areaEditor');
@@ -664,13 +731,40 @@ const copyAreaCanvas = () => {
     </AFlex>
 
     <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>&nbsp;&nbsp;&nbsp;</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>
+        <AFlex>
+          <AButton type="text" class="return-currently" @click="returnCurrently">返回当前</AButton>
+          <AButton type="primary" class="timeline-confirm">确定</AButton>
         </AFlex>
-      </div>
+      </AFlex>
       <AFlex class="content-monitoring-canvas">
         <div class="content-monitoring-canvas-left">
           <div v-for="item in monitoringPointData" :key="item.id" :data-point-id="item.id">
@@ -1088,6 +1182,78 @@ const copyAreaCanvas = () => {
 </template>
 
 <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 {
   margin: 16px 0 0 16px;
 }