riku
5 天以前 f19e5267cc23b1c714dc746239864f33ed715dd9
src/components/map/SceneMap.vue
@@ -1,56 +1,197 @@
<template>
  <BaseMap></BaseMap>
  <el-row class="left-top-wrap">
    <FYOptionScene
      label=""
      :allOption="true"
      :type="2"
      v-model:value="scenetype"
    ></FYOptionScene>
    <slot name="left-top"></slot>
  </el-row>
  <!-- <el-row class="right-wrap">
    <el-col :span="4">
      <el-button>close</el-button>
    </el-col>
    <el-col :span="20">
    </el-col>
  </el-row> -->
  <el-scrollbar class="right-wrap">
    <div v-for="s in selectedSceneList" :key="s.guid">
      <el-checkbox
        v-model="s._checked"
        :label="s.name"
        @change="handleChange(s)"
      />
      <!-- <el-text>{{ s.name }}</el-text> -->
    </div>
  </el-scrollbar>
  <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 { ref, watch, computed } 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
});
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 = computed(() => {
  return props.data.filter((v) => {
    v._checked = true;
// 选中的场景
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
      (scenetype.value == undefined ||
        scenetype.value.value == null ||
        v.typeid + '' == scenetype.value.value) &&
      (v.name.indexOf(filterText.value) != -1 ||
        v.index + '' == filterText.value)
    );
  });
});
@@ -59,7 +200,6 @@
  () => props.data,
  (nV, oV) => {
    if (nV != oV) {
      clearSceneMarks();
      createSceneMarks();
      filterMarkViews(true);
    }
@@ -69,23 +209,56 @@
watch(scenetype, (nV, oV) => {
  if (nV != oV) {
    clearSceneMarks();
    filterMarkViews(true);
  }
});
function handleChange(scene) {
// 监听外部选中或移除场景变化
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;
  });
  if (scene._checked) {
  scene._visible = !scene._visible;
  if (scene._visible) {
    map.add(mv);
  } else {
    map.remove(mv);
  }
  // filterMarkViews();
}
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(() => {
    allMarkViews = [];
@@ -93,30 +266,16 @@
      // 创建场景地图标注
      const mark = marks.createMarker({
        position: [d.longitude, d.latitude],
        img: sceneIcon(d.typeid),
        // label: d.name,
        // img: sceneIcon(d.typeid),
        content: markContentHtml(d.typeid),
        label: d.index,
        title: d.name,
        extData: d
      });
      // 添加点击事件
      mark.on('click', (ev) => {
        const _mark = ev.target;
        const _extData = _mark.getExtData();
        if (_extData._show) {
          ev.target.setLabel({
            content: ''
            // direction: 'bottom'
          });
          _extData._show = false;
          ev.target.setExtData(_extData);
        } else {
          ev.target.setLabel({
            content: _extData.name
            // direction: 'bottom'
          });
          _extData._show = true;
          ev.target.setExtData(_extData);
        }
      });
      // 鼠标事件
      // onMouseOverListener(mark);
      onClickListener(mark);
      allMarkViews.push(mark);
    });
  });
@@ -130,6 +289,7 @@
    if (markViewList.length > 0) {
      map.remove(markViewList);
    }
    // 1. 筛选场景类型
    if (scenetype.value == undefined) {
      markViewList = allMarkViews;
    } else {
@@ -140,49 +300,136 @@
        );
      });
    }
    markViewList = markViewList.filter((v) => {
      const _index = selectedSceneList.value.findIndex((s) => {
        return s.guid == v.getExtData().guid && s._checked;
      });
      return _index != -1;
    });
    map.add(markViewList);
    if (setFitView) {
      setTimeout(() => {
        map.setFitView(markViewList);
        // const list = markViewList.map((v) => {
        //   const _extData = v.getExtData();
        //   return [_extData.longitude, _extData.latitude];
        // });
        // mapUtil.setBound(list);
      }, 1000);
    }
  });
}
function clearSceneMarks() {
  onMapMounted(() => {
    if (markViewList.length > 0) {
      map.remove(markViewList);
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>
.left-top-wrap {
.wrap {
  position: absolute;
  left: 0;
  left: 0px;
  top: 0;
  width: 100%;
  height: 100%;
}
.right-wrap {
.top-left-wrap {
  position: absolute;
  right: 0px;
  bottom: 0;
  height: 50%;
  background-color: white;
  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;
  padding: 2px 8px;
  max-width: 300px;
  /* 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>