zhanghua
2024-11-23 3f8204c31b44ca186f2d5d1f96682f8c46ad2996
map组件
4个文件已修改
2个文件已添加
695 ■■■■■ 已修改文件
src/assets/images/file1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/components/Map/index.vue 585 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/components/noticeTable.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/components/projectOverview.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/components/tidingsTable.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 98 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/file1.png
src/views/components/Map/index.vue
New file
@@ -0,0 +1,585 @@
<template>
  <div>
    <div :id="id" ref="contentRef" class="w-full h-full">
      <div
        v-if="isShowControl"
        :class="controlPosition"
        class="control-container bottom-right"
      >
        <div
          v-for="item in controlMapping"
          :key="item.id"
          class="control-content"
        >
          <template v-if="item.children">
            <div
              v-for="child in item.children"
              :key="child.id"
              class="control-item"
              @click="child.event"
            >
              <img :src="child.img" />
            </div>
          </template>
          <template v-else>
            <div class="control-item" @click="item.event">
              <img :src="item.img" />
            </div>
          </template>
        </div>
      </div>
    </div>
  </div>
</template>
<script setup>
import Location from "@/assets/images/location.png";
import FileImg from "@/assets/images/file1.png";
import MinusImg from "@/assets/images/minus.png";
import PlusImg from "@/assets/images/plus.png";
import FullScreenImg from "@/assets/images/fullScreen.png";
import PositionImg from "@/assets/images/position.png";
export default {
  data() {
    return {
      zoom: 15,
      map: null,
      contentRef: {},
      infoWin: {},
      defaultMaskInfo: {},
      mapKey: "ffb871cd7ca48883db3b04cfd00d469f",
      controlMapping: [
        {
          id: "定位",
          img: PositionImg,
          event: this.getCurrentPosition,
        },
        {
          id: "缩放",
          children: [
            {
              id: "放大",
              img: PlusImg,
              event: this.zoomIn,
            },
            {
              id: "缩小",
              img: MinusImg,
              event: this.zoomOut,
            },
          ],
        },
        {
          id: "全屏",
          img: FullScreenImg,
          event: this.fullScreen,
        },
      ],
    };
  },
  props: {
    id: String,
    mapList: Array,
    listType: Boolean,
    isShowControl: Boolean,
    controlPosition: Object,
    isImmediateLoad: { type: Boolean, default: true },
    mapType: Boolean,
  },
  created() {
    this.defaultMaskInfo = {
      lon: this.$props.mapList[0]?.lon
        ? this.$props.mapList[0].lon
        : "105.37281",
      lat: this.$props.mapList[0]?.lat
        ? this.$props.mapList[0].lat
        : "30.87145",
      name: this.$props.mapList[0]?.name ? this.$props.mapList[0].name : "创建",
    };
    if (this.$props.isImmediateLoad) this.onLoad();
  },
  methods: {
    async onLoad() {
      const scripts = document.body.querySelectorAll("script");
      if (!this.mapKey) {
        // const res = await getDicts('map_key')
        // mapKey = res[0].dictLabel
        this.mapKey = "ffb871cd7ca48883db3b04cfd00d469f";
      }
      const scriptSrc = `http://api.tianditu.gov.cn/api?v=4.0&tk=${this.mapKey}`;
      const mapScript = Array.from(scripts).find((item) => {
        return item.getAttribute("src") === scriptSrc;
      });
      if (!mapScript) {
        // 创建script标签
        const script = document.createElement("script");
        // 设置API的URL
        script.src = scriptSrc;
        script.type = "text/javascript";
        // 将script标签添加到HTML中
        document.body.appendChild(script);
        script.onload = () => {
          this.initTianMap();
        };
      } else {
        this.initTianMap();
      }
    },
    initTianMap() {
      var T = window.T;
      setTimeout(async () => {
        this.map = new T.Map(this.id, {
          projection: "EPSG:4326",
        });
        this.map.centerAndZoom(
          new T.LngLat(this.defaultMaskInfo.lon, this.defaultMaskInfo.lat),
          this.zoom
        );
        if (!this.$props.listType) {
          // 清除之前的标记
          // map && map.clearOverLays()
          //form表单中是否有坐标,有则定位到对应位置,没有则使用默认的
          // 104.65417 28.75572 测试数据
          // 四川省宜宾市翠屏区大观楼街道外南街71
          if (this.$props.mapList[0].addr) {
            const { lng: lon, lat } = await this.getGeocode(
              this.$props.mapList[0].addr
            );
            this.defaultMaskInfo.lon = String(lon);
            this.defaultMaskInfo.lat = String(lat);
          }
          this.map && this.makeDefaultMask(this.defaultMaskInfo);
          this.addEvent(this.map, "click", (e) => this.mapClick(e));
        } else {
          this.$props.mapList.length && this.makeAllMask(this.$props.mapList);
        }
      }, 0);
    },
    zoomIn() {
      this.map && this.map.zoomIn();
    },
    zoomOut() {
      this.map && this.map.zoomOut();
    },
    fullScreen() {
      this.map && this.contentRef?.requestFullscreen();
    },
    getCurrentPosition() {
      if (navigator.geolocation) {
        ElMessage.warning("该功能暂未开发");
        // TODO 地图定位
        // navigator.geolocation.getCurrentPosition(function (position) {
        //   var latitude = position.coords.latitude
        //   var longitude = position.coords.longitude
        //
        //   moveTo(String(longitude), String(latitude))
        // })
      } else {
        ElMessage.warning("当前浏览器不支持定位功能");
      }
    },
    moveTo(lon, lat) {
      this.map && this.map.panTo(new T.LngLat(lon, lat));
    },
    // 默认的点标记
    makeDefaultMask(mapInfo) {
      const { name, lon, lat } = mapInfo
      const option = {
        text: name,
        offset: [-35, -45],
        labelBg: "#3369FF",
        labelColor: "#fff",
        labelClick,
      };
      // 清除之前的标记
      this.map && this.map.clearOverLays();
      this.map.panTo(new T.LngLat(lon, lat));
      this.makeMask(lon, lat, { labelOptions: option });
    },
    // 地图点击事件
    mapClick(e) {
      e.originalEvent.stopPropagation();
      const { lat, lng: lon } = e.lnglat;
      this.makeDefaultMask({
        ...defaultMaskInfo,
        lon: String(lon),
        lat: String(lat),
      });
      this.getReverseGeocode(e).then((addr) => {
        this.$emit("mapClick", { e, addr });
      });
      console.log("点击事件", e);
    },
    // 逆地理编码 获取地址
    getReverseGeocode(e) {
      return new Promise((resolve) => {
        const geocode = new T.Geocoder();
        geocode.getLocation(e.lnglat, (result) => {
          if (result.status === "0") {
            resolve(result.getAddress());
          }
        });
      });
    },
    // 地理编码
    getGeocode(addr) {
      return new Promise((resolve) => {
        const geocoder = new T.Geocoder();
        geocoder.getPoint(addr, (result) => {
          if (result.status === "0") {
            resolve(result.getLocationPoint());
          }
        });
      });
    },
    // 生成点位内容
    // 生成点位内容
    generateContent(row, random) {
      if (row) {
        return `
    <div class="map-window-info">
      <div class='title-block'>
        <div class="title truncate-content">
          ${row.projectName ?? "暂无数据"}
        </div>
        <div class='close-btn close-btn-${random}'>
          ×
        </div>
      </div>
      <div class="detail-info">
        <span class="truncate-content">总投资:${
          row.totalMoney ?? "暂无数据"
        }</span>
        <span class="truncate-content">
          项目状态:
        </span>
         <span class="truncate-content">
          项目地址:${row.projectAddr ?? "暂无数据"}
        </span>
      </div>
    </div>`;
      } else {
        return `<div style="display: flex; justify-content:center; align-items:center; opacity: 0;">
    </div>`;
      }
    },
    // 唯一标识
    getUniqueId() {
      return Math.random().toString(36).substr(2) + Date.now();
    },
    markerMouseover(_e, infoWin, info, random) {
      // 点位标记移入显示不同内容
      if (this.$props.isShowControl) {
        infoWin.setContent(this.generateContent(info, random));
        const contentWrapper = document.querySelector(
          ".tdt-infowindow-content-wrapper"
        );
        if (contentWrapper) {
          contentWrapper.style.opacity = "1";
        }
        // 查找到那个info 界面
        // const allCloseBtn = document.querySelectorAll(`.close-btn-${random}`);
        // allCloseBtn[allCloseBtn.length - 1].onclick = (_e) => {
        //   infoWin.setContent(generateContent());
        // };
      }
    },
    markerMouseout(_e, infoWin) {
      infoWin.setContent(this.generateContent());
      const contentWrapper = document.querySelector(
        ".tdt-infowindow-content-wrapper"
      );
      if (contentWrapper) {
        contentWrapper.style.opacity = "0";
      }
    },
    // 打标记
    makeMask(lon, lat, options) {
      const { markerOptions, labelOptions } = options;
      console.log("options", markerOptions, labelOptions);
      const icon = new T.Icon({
        iconUrl: Location,
        iconSize: new T.Point(19, 27),
        iconAnchor: new T.Point(10, 25),
      });
      var marker = new T.Marker(new T.LngLat(lon, lat), {
        icon,
        id: markerOptions?.id,
      });
      this.map && this.map.addOverLay(marker);
      const label = new T.Label({
        text: labelOptions.text,
        position: new T.LngLat(lon, lat),
        offset: new T.Point(...labelOptions.offset),
        id: labelOptions?.id,
      });
      labelOptions.labelBg && label.setBackgroundColor(labelOptions.labelBg);
      labelOptions.labelColor && label.setFontColor(labelOptions.labelColor);
      //创建地图文本对象
      if (!this.$props.mapType) {
        this.map && this.map.addOverLay(label);
      }
      markerOptions?.markerClick &&
        this.addEvent(marker, "click", (e) => markerOptions.markerClick(label, e));
      labelOptions?.labelClick &&
      this.addEvent(label, "click", (e) => labelOptions.labelClick(label, e));
      const lnglat = new T.LngLat(lon, lat);
      const infoWin = new T.InfoWindow(this.generateContent(), {
        id: markerOptions?.id,
        offset: new T.Point(0, -15),
        closeButton: false,
      });
      infoWin.setLngLat(lnglat);
      // 向地图上添加信息窗口
      if (this.$props.mapType) {
        this.map && this.map.addOverLay(infoWin);
      }
      this.addEvent(marker, "mouseover", (e) =>
        markerMouseover(e, infoWin, labelOptions, getUniqueId())
      );
      this.addEvent(marker, "mouseout", (e) => markerMouseout(e, infoWin));
    },
    // 标记所有点
    makeAllMask(mapList) {
      var _this = this;
      this.mapList.map((item) => {
        const config = {
          markerOptions: {
            id: item.id,
            markerClick: _this.markerClick,
          },
          labelOptions: {
            text: item.name,
            offset: [-45, -45],
            id: item.id,
            markerClick: _this.labelClick,
          },
        };
        _this.makeMask(item.lon, item.lat, config);
      });
    },
    // 通用添加事件的方法
    addEvent(component, name, func) {
      // 移出事件
      this.removeEvent(component, name, func);
      // 添加事件
      component.addEventListener(name, func);
    },
    // 通用移出事件的方法
    removeEvent(component, name, func) {
      component.removeEventListener(name, func);
    },
    //标记点击事件
    markerClick(currentLabel, e) {
      const id = e.target.options.id;
      currentLabelStyleChange(currentLabel);
      console.log("点击事件", e, currentLabel);
      this.getReverseGeocode(e).then((addr) => {
        this.$emit(
          "markClick",
          id
            ? this.$props.mapList?.find((item) => item.id === id)
            : { e, addr: addr }
        );
      });
    },
    // label点击事件
    labelClick(currentLabel, e) {
      const id = e.target.options.id;
      currentLabelStyleChange(currentLabel);
      getReverseGeocode(e).then((addr) => {
        this.$emit(
          "labelClick",
          id
            ? this.$props.mapList?.find((item) => {
                return String(item.id) === String(id);
              })
            : { e, addr: addr }
        );
      });
    },
    currentLabelStyleChange(currentLabel) {
      this.$emit("currentLabelStyleChange", currentLabel);
    },
    resizeMap() {
      this.map.checkResize();
    },
  },
  watch: {
    listType: {
      handler(val) {
        if (val) {
          this.map && this.map.clearOverLays();
          if (this.$props.mapList.length && T) {
            this.makeAllMask(this.$props.mapList);
          }
        }
      },
    },
  },
  beforeUnmount() {
    this.map = null;
  },
};
// const emits = defineEmits<{
//   (e: 'labelClick', value: any): void;
//   (e: 'markClick', value: any): void;
//   (e: 'mapClick', value: any): void;
//   (e: 'currentLabelStyleChange', value: any): void;
// }>();
</script>
<style lang="scss" scoped>
:deep(.tdt-label) {
  border-radius: 15px;
  color: var(--el-color-primary);
  cursor: pointer;
}
// #53547f
:deep(.tdt-bar a, .tdt-bar a:hover) {
  border-bottom: 0;
  color: #53547f;
}
:deep(.tdt-touch .tdt-control-zoom-in) {
  font-size: 18px;
}
:deep(.tdt-bar) {
  border-radius: 3px;
  box-shadow: 5px 0px 12px 0px rgb(0 0 0 / 0.03);
}
.tdt-bar a.tdt-disabled,
.tdt-bar a:hover {
  background-color: #f4f4f4;
}
.control-container {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 10px;
  z-index: 999;
}
.top-left {
  top: 10px;
  left: 10px;
}
.top-right {
  top: 10px;
  right: 10px;
}
.bottom-left {
  bottom: 10px;
  left: 10px;
}
.bottom-right {
  bottom: 10px;
  right: 10px;
}
.control-content {
  border-radius: 3px;
  box-shadow: 5px 0px 12px 0px rgb(0 0 0 / 0.03);
  background-color: #fff;
}
.control-item {
  box-sizing: border-box;
  width: 30px;
  height: 30px;
  line-height: 28px;
  padding: 0 9px;
  img {
    display: inline-block;
  }
}
.control-item:hover {
  background-color: #f4f4f4;
}
</style>
<style lang="scss">
.map-window-info {
  padding: 0 20px;
  width: 300px;
  cursor: default;
  .title {
    color: #3369ff;
    font-size: 18px;
    margin-bottom: 13px;
  }
  .detail-info {
    display: flex;
    flex-direction: column;
    color: #7e809c;
    font-size: 12px;
    gap: 8px;
  }
}
.title-block {
  display: flex;
  justify-content: space-between;
  .close-btn {
    font-size: 18px;
    color: #d8dde6;
    cursor: pointer;
    z-index: 999999;
  }
}
//.tdt-control-copyright {
//  display: none;
//}
.tdt-infowindow-tip-container {
  opacity: 0;
}
.tdt-infowindow-content-wrapper {
  opacity: 0;
}
</style>
src/views/components/noticeTable.vue
@@ -97,8 +97,8 @@
        }
    },
    props: {
        calculation: [],
        countExceptionProjectData: {},
        calculation: Array,
        countExceptionProjectData:Object,
    },
    created() {
        this.getList();
src/views/components/projectOverview.vue
@@ -145,8 +145,8 @@
        return {}
    },
    props: {
        calculation: [],
        countExceptionProjectData: {},
        calculation: Array,
        countExceptionProjectData:Object,
    },
    methods: {
        setTextStyle(text) {
src/views/components/tidingsTable.vue
@@ -115,8 +115,8 @@
        }
    },
    props: {
        calculation: [],
        countExceptionProjectData: {},
        calculation: Array,
        countExceptionProjectData:Object,
    },
    created() {
        this.currentTabId = this.tabs[0].id; // 默认选中的tab的id
src/views/index.vue
@@ -36,10 +36,7 @@
                                    />
                                </el-select>
                            </el-form-item>
                            <el-form-item
                                label="投资金额"
                                style="margin-right: 50px"
                            >
              <el-form-item label="投资金额" style="margin-right: 50px">
                                <div class="from_input">
                                    <el-input
                                        v-model="queryParams.start"
@@ -59,15 +56,10 @@
                                </div>
                            </el-form-item>
                            <el-form-item style="margin-right: 20px">
                                <el-button
                                    icon="Search"
                                    type="primary"
                                    @click="handleQuery"
                <el-button icon="Search" type="primary" @click="handleQuery"
                                    >搜索</el-button
                                >
                                <el-button icon="Refresh" @click="resetQuery"
                                    >重置</el-button
                                >
                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
                            </el-form-item>
                        </div>
                    </el-form>
@@ -108,20 +100,15 @@
                                />
                            </el-form-item>
                            <el-form-item style="margin-right: 0px">
                                <el-button
                                    icon="Search"
                                    type="primary"
                                    @click="searchList"
                <el-button icon="Search" type="primary" @click="searchList"
                                    >搜索</el-button
                                >
                                <el-button icon="Refresh" @click="mapQuery"
                                    >重置</el-button
                                >
                <el-button icon="Refresh" @click="mapQuery">重置</el-button>
                            </el-form-item>
                        </el-form>
                    </div>
                    <div class="flex w-full h-[500px] border border-[#DBDEEA]">
                        <!-- <Map
            <Map
                            id="DangerSourceId"
                            ref="mapRef"
                            :is-show-control="true"
@@ -132,7 +119,7 @@
                            @label-click="showDetails"
                            @mark-click="showDetails"
                            @current-label-style-change="labelStyleChange"
                        /> -->
            />
                    </div>
                </div>
            </el-card>
@@ -141,11 +128,11 @@
</template>
<script>
import SvgIcon from '@/components/SvgIcon/index.vue';
import ProjectOverview from './components/projectOverview.vue';
import NoticeTable from './components/noticeTable.vue';
import TidingsTable from './components/tidingsTable.vue';
// import Map from './components/Map/index.vue';
import SvgIcon from "@/components/SvgIcon/index.vue";
import ProjectOverview from "./components/projectOverview.vue";
import NoticeTable from "./components/noticeTable.vue";
import TidingsTable from "./components/tidingsTable.vue";
import Map from "./components/Map/index.vue";
export default {
    name: "Index",
    data() {
@@ -156,21 +143,36 @@
            calculation: [],
            countExceptionProjectData: {},
            searchForm: {},
            tableDatas: []
      tableDatas: [
        {
          name: "射洪市",
          value: 105.37281,
          lat: 30.87145,
          lon: 105.37281,
          id: "222222222",
        },
        {
          name: "xxxx",
          value: 105.22332,
          lat: 31.52421,
          lon: 106.22332,
          id: "11112",
        },
      ],
        };
    },
    components: {
        SvgIcon,
        ProjectOverview,
        NoticeTable,
        TidingsTable
    TidingsTable,
    Map,
    },
    methods: {
        dataPickerChange(val) {
            if (!val) {
                this.queryParams.startTime = '';
                this.queryParams.endTime = '';
        this.queryParams.startTime = "";
        this.queryParams.endTime = "";
                return;
            }
@@ -184,7 +186,7 @@
                endDate: queryParams.endTime,
                areaCode: queryParams.area,
                minInvestment: queryParams.start,
                maxInvestment: queryParams.end
        maxInvestment: queryParams.end,
            };
            this.getCalculatioln(obj).then((res) => {
@@ -193,7 +195,7 @@
                    text: item.text,
                    mun: item.count,
                    statistics: item.type,
                    statisticsMun: item.amount
          statisticsMun: item.amount,
                }));
                // 创建一个对象,以便根据 text 属性快速查找 newArr 中的对象
@@ -225,20 +227,20 @@
        labelStyleChange(currentLabel) {
            if (lastLabel) {
                lastLabel.setBackgroundColor('#fff');
                lastLabel.setFontColor('var(--el-color-primary)');
        lastLabel.setBackgroundColor("#fff");
        lastLabel.setFontColor("var(--el-color-primary)");
            }
            currentLabel.setBackgroundColor('var(--el-color-primary)');
            currentLabel.setFontColor('#fff');
      currentLabel.setBackgroundColor("var(--el-color-primary)");
      currentLabel.setFontColor("#fff");
            lastLabel = currentLabel;
        },
        resetQuery() {
            this.queryParams = {
                area: '',
                start: '',
                end: '',
                startTime: '',
                endTime: ''
        area: "",
        start: "",
        end: "",
        startTime: "",
        endTime: "",
            };
            this.timeMerge = [];
            this.handleQuery();
@@ -249,11 +251,9 @@
            // await search()
            this.mapRef.moveTo(105.37281, 30.87145);
        },
        mapQuery() {
        }
    }
}
    mapQuery() {},
  },
};
</script>
<style scoped lang="scss">
@@ -318,12 +318,12 @@
::v-deep.el-select__placeholder {
    font-size: 12px;
}
::v-deep input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {
::v-deep input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type='number'] {
input[type="number"] {
    -moz-appearance: textfield;
}
.search_from {