From f19e5267cc23b1c714dc746239864f33ed715dd9 Mon Sep 17 00:00:00 2001
From: riku <risaku@163.com>
Date: 星期五, 05 十二月 2025 17:55:02 +0800
Subject: [PATCH] 完成地图制作任务功能初版

---
 src/components/map/SceneMap.vue |  415 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 404 insertions(+), 11 deletions(-)

diff --git a/src/components/map/SceneMap.vue b/src/components/map/SceneMap.vue
index 7994a7f..bcb2076 100644
--- a/src/components/map/SceneMap.vue
+++ b/src/components/map/SceneMap.vue
@@ -1,42 +1,435 @@
 <template>
   <BaseMap></BaseMap>
+  <div class="wrap p-events-none">
+    <el-row class="p-events-none top-left-wrap">
+      <FYOptionScene
+        class="p-events-auto"
+        label=""
+        :allOption="true"
+        :type="2"
+        v-model:value="scenetype"
+      ></FYOptionScene>
+      <div class="p-events-auto">
+        <slot name="left-top"></slot>
+      </div>
+    </el-row>
+    <el-row
+      class="p-events-none bottom-left-wrap"
+      align="middle"
+      :style="leftCardWrapStyle"
+    >
+      <div class="card-left" ref="refLeftCard">
+        <div v-show="leftCardShow" class="p-events-auto">
+          <el-row ref="refLeftCardTitle" class="p-8" justify="space-between">
+            <el-text size="large">鍦烘櫙鍒楄〃</el-text>
+            <el-input
+              v-model="filterText"
+              icon="Search"
+              style="width: 250px"
+              placeholder="杈撳叆鍏抽敭瀛楋紝鎸夊洖杞﹂敭鎼滅储"
+              clearable
+            />
+            <!-- <el-badge
+              ref="sampleBadgeRef"
+              :value="1"
+              type="success"
+              :offset="[-2, 2]"
+            >
+              <img
+                style="width: 30px; height: 30px"
+                :src="sceneIcon(1)"
+                alt="宸ュ湴"
+              />
+              <template #content="{ value }">
+                <div class="custom-content">
+                  <el-icon :size="8"><Select /></el-icon>
+                </div>
+              </template>
+            </el-badge> -->
+          </el-row>
+          <el-scrollbar :height="scrollHeight" class="scrollbar">
+            <el-row
+              v-for="s in filteredSceneList"
+              :key="s.guid"
+              justify="space-between"
+              class="p-v-4 scene-item"
+            >
+              <el-text truncated style="width: 100%">
+                {{ s.index + '. ' + s.name }}
+              </el-text>
+              <el-row justify="space-between" style="margin-top: 4px">
+                <el-space>
+                  <el-tag type="info" effect="plain" size="small">
+                    {{ s.type }}
+                  </el-tag>
+                  <el-tag
+                    :type="s.extension1 == '0' ? 'info' : 'success'"
+                    size="small"
+                  >
+                    {{ onlineFormat(s.extension1) }}
+                  </el-tag>
+                </el-space>
+              </el-row>
+              <el-space>
+                <el-icon
+                  class="cursor-p"
+                  :color="
+                    s._checked ? 'rgb(121, 187, 255)' : 'rgb(200, 201, 204)'
+                  "
+                  @click="locateTo(s)"
+                >
+                  <LocationInformation />
+                </el-icon>
+                <el-icon
+                  class="cursor-p"
+                  :color="
+                    s._visible ? 'rgb(121, 187, 255)' : 'rgb(200, 201, 204)'
+                  "
+                  @click="handleVisibleChange(s)"
+                >
+                  <View />
+                </el-icon>
+              </el-space>
+            </el-row>
+          </el-scrollbar>
+        </div>
+      </div>
+      <el-button
+        class="close-btn-right p-events-auto"
+        type="success"
+        size="small"
+        :icon="leftCardShow ? 'ArrowLeft' : 'ArrowRight'"
+        @click="leftCardShow = !leftCardShow"
+      ></el-button>
+    </el-row>
+  </div>
 </template>
 <script setup>
-import { watch } from 'vue';
+import { ref, watch, computed, onMounted } from 'vue';
 import { map, onMapMounted } from '@/utils/map/index';
 import marks from '@/utils/map/marks';
+import mapUtil from '@/utils/map/util';
 import { sceneIcon } from '@/assets/scene-icon';
+
+const startHtml = `<div class="el-badge">`;
+const endHtml = `<sup
+      class="el-badge__content el-badge__content--success is-fixed"
+      style="margin-top: 2px; margin-right: 2px"
+    >
+      <div class="custom-content">
+        <i class="el-icon"  style="font-size: 8px">
+          <svg
+            xmlns="http://www.w3.org/2000/svg"
+            viewBox="0 0 1024 1024"
+          >
+            <path
+              fill="currentColor"
+              d="M77.248 415.04a64 64 0 0 1 90.496 0l226.304 226.304L846.528 188.8a64 64 0 1 1 90.56 90.496l-543.04 543.04-316.8-316.8a64 64 0 0 1 0-90.496"
+            ></path>
+          </svg>
+        </i>
+      </div>
+    </sup>
+  </div>`;
+//
+const markContentHtml = (sceneType, checked) => {
+  const imgHtml = `<img
+      style="width: 30px; height: 30px"
+      src="${sceneIcon(sceneType)}"
+    />`;
+  if (checked) {
+    return startHtml + imgHtml + endHtml;
+  } else {
+    return imgHtml;
+  }
+};
+
 const props = defineProps({
+  // 閫変腑鐨勫満鏅�
+  modelValue: {
+    type: Array,
+    default: () => []
+  },
   // 鍦烘櫙鐐逛綅淇℃伅
   data: Array
 });
 
-var markViewList = [];
+const emits = defineEmits(['update:modelValue']);
+
+onMounted(() => {
+  setTimeout(() => {
+    scrollHeight.value =
+      refLeftCard.value.offsetHeight -
+      refLeftCardTitle.value.offsetHeight +
+      'px';
+  }, 500);
+});
+
+const refLeftCard = ref();
+const refLeftCardTitle = ref();
+
+const leftCardShow = ref(true);
+const leftCardWrapStyle = ref();
+const scrollHeight = ref('400px');
+
+let allMarkViews = [];
+let markViewList = [];
+
+const scenetype = ref();
+
+// 閫変腑鐨勫満鏅�
+const selectedSceneList = ref(props.modelValue);
+
+const allSceneList = computed(() => props.data);
+const filterText = ref('');
+// 鏍规嵁鍦烘櫙绫诲瀷绛涢�夊満鏅垪琛�
+const filteredSceneList = computed(() => {
+  return allSceneList.value.filter((v) => {
+    v._visible = true;
+    return (
+      (scenetype.value == undefined ||
+        scenetype.value.value == null ||
+        v.typeid + '' == scenetype.value.value) &&
+      (v.name.indexOf(filterText.value) != -1 ||
+        v.index + '' == filterText.value)
+    );
+  });
+});
 
 watch(
   () => props.data,
   (nV, oV) => {
     if (nV != oV) {
-      drawSceneMarks();
+      createSceneMarks();
+      filterMarkViews(true);
     }
   },
   { immediate: true }
 );
 
-function drawSceneMarks() {
+watch(scenetype, (nV, oV) => {
+  if (nV != oV) {
+    filterMarkViews(true);
+  }
+});
+
+// 鐩戝惉澶栭儴閫変腑鎴栫Щ闄ゅ満鏅彉鍖�
+watch(
+  () => props.modelValue,
+  (nV, oV) => {
+    if (nV != oV) {
+      // 澶栭儴閫変腑鍦烘櫙鍙樺寲鏃讹紝鏇存柊鍐呴儴閫変腑鍦烘櫙鍒楄〃
+      selectedSceneList.value = nV;
+      // 澶栭儴閫変腑鍦烘櫙鍙樺寲鏃讹紝鏇存柊鍦板浘鏍囨敞鍐呭
+      allMarkViews.forEach((mv) => {
+        const scene = mv.getExtData();
+        const findOne = selectedSceneList.value.some((s) => {
+          return s.guid == scene.guid;
+        });
+        if (findOne) {
+          changeSceneMark(mv, true);
+        } else {
+          changeSceneMark(mv, false);
+        }
+      });
+    }
+  }
+);
+
+function handleVisibleChange(scene) {
+  const mv = markViewList.find((v) => {
+    return scene.guid == v.getExtData().guid;
+  });
+  scene._visible = !scene._visible;
+  if (scene._visible) {
+    map.add(mv);
+  } else {
+    map.remove(mv);
+  }
+}
+
+function locateTo(scene) {
+  const mv = markViewList.find((v) => {
+    return scene.guid == v.getExtData().guid;
+  });
+  if (mv) {
+    // mapUtil.setFitView(mv);
+    mapUtil.setCenter(mv.getPosition());
+  }
+}
+
+// 鍒涘缓鍦烘櫙鍦板浘鏍囨敞锛屾坊鍔犻紶鏍囦簨浠跺拰鐐瑰嚮浜嬩欢
+function createSceneMarks() {
   onMapMounted(() => {
-    markViewList = [];
+    allMarkViews = [];
     props.data.forEach((d) => {
+      // 鍒涘缓鍦烘櫙鍦板浘鏍囨敞
       const mark = marks.createMarker({
         position: [d.longitude, d.latitude],
-        icon: sceneIcon(d.typeid),
-        label: d.name,
-        extData: d.guid
+        // img: sceneIcon(d.typeid),
+        content: markContentHtml(d.typeid),
+        label: d.index,
+        title: d.name,
+        extData: d
       });
-      markViewList.push(mark);
+      // 榧犳爣浜嬩欢
+      // onMouseOverListener(mark);
+      onClickListener(mark);
+
+      allMarkViews.push(mark);
     });
-    map.setFitView(markViewList);
   });
 }
+
+/**
+ * 绛涢�夋墍閫夌被鍨嬬殑鍦烘櫙
+ */
+function filterMarkViews(setFitView) {
+  onMapMounted(() => {
+    if (markViewList.length > 0) {
+      map.remove(markViewList);
+    }
+    // 1. 绛涢�夊満鏅被鍨�
+    if (scenetype.value == undefined) {
+      markViewList = allMarkViews;
+    } else {
+      markViewList = allMarkViews.filter((v) => {
+        return (
+          scenetype.value.value == null ||
+          v.getExtData().typeid + '' == scenetype.value.value
+        );
+      });
+    }
+    map.add(markViewList);
+    if (setFitView) {
+      setTimeout(() => {
+        map.setFitView(markViewList);
+      }, 1000);
+    }
+  });
+}
+
+function onlineFormat(s) {
+  if (s == '0') {
+    return '涓嬬嚎';
+  } else {
+    return '涓婄嚎';
+  }
+}
+
+/** 鍦板浘鍦烘櫙鏍囪榧犳爣浜嬩欢 *********************************************/
+const onMouseOverListener = (mark) => {
+  let timeout;
+  // 娣诲姞鐐瑰嚮浜嬩欢
+  mark.on('mouseover', (ev) => {
+    if (timeout) {
+      clearTimeout(timeout);
+    }
+    const _mark = ev.target;
+    const _extData = _mark.getExtData();
+    _mark.setLabel({
+      content: _extData.name
+    });
+    timeout = setTimeout(() => {
+      _mark.setLabel({
+        content: ''
+      });
+    }, 2000);
+  });
+};
+const onClickListener = (mark) => {
+  mark.on('click', (ev) => {
+    const _mark = ev.target;
+    const extData = _mark.getExtData();
+    const index = selectedSceneList.value.indexOf(extData);
+    if (index == -1) {
+      selectedSceneList.value.push(extData);
+      changeSceneMark(_mark, true);
+    } else {
+      selectedSceneList.value.splice(index, 1);
+      changeSceneMark(_mark, false);
+    }
+    emits('update:modelValue', selectedSceneList.value);
+  });
+};
+/********************************************************************/
+
+/**
+ * 鏍规嵁閫変腑鐘舵�佷慨鏀瑰湴鍥炬爣璁扮殑鏍峰紡
+ * @param {AMap.Marker} mark 鍦板浘鏍囪
+ * @param checked
+ */
+function changeSceneMark(mark, checked) {
+  const scene = mark.getExtData();
+  if (checked && !scene._checked) {
+    scene._checked = true;
+    mark.setContent(markContentHtml(scene.typeid, true));
+  } else if (!checked && scene._checked) {
+    scene._checked = false;
+    mark.setContent(markContentHtml(scene.typeid, false));
+  }
+}
 </script>
-<style scoped></style>
+<style scoped>
+.wrap {
+  position: absolute;
+  left: 0px;
+  top: 0;
+  width: 100%;
+  height: 100%;
+}
+.top-left-wrap {
+  position: absolute;
+  left: 0px;
+  top: 1px;
+}
+.bottom-left-wrap {
+  position: absolute;
+  left: 0px;
+  bottom: 1px;
+}
+
+.card-left {
+  background-color: rgba(255, 255, 255, 0.8);
+  border-radius: 4px;
+  /* width: 350px; */
+  height: 50vh;
+  box-shadow: var(--el-box-shadow);
+  z-index: 0;
+  /* padding: 8px; */
+}
+
+.scrollbar {
+  padding-right: 8px;
+  width: 350px;
+}
+
+.close-btn-right {
+  /* margin-left: -3px; */
+  height: 60px;
+  width: 20px;
+}
+
+.p-events-auto {
+  pointer-events: auto;
+}
+
+.p-events-none {
+  pointer-events: none;
+}
+
+.scene-item {
+  /* background-color: aliceblue; */
+  padding: 8px;
+  border-radius: 8px;
+  border: 1px solid var(--el-border-color);
+  box-shadow: var(--el-box-shadow-lighter);
+  margin-bottom: 6px;
+}
+
+.custom-content {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+</style>

--
Gitblit v1.9.3