vue3+Ts+Ant Design Vue +天地图组件封装

news2024/11/27 6:28:53

​🌈个人主页:前端青山
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来Vue3+Ts+AntDesign-Vue组件+天地图组件的封装

示例图

首先,在index.html引入天地图资源,vue3选择v4版本

    <script src="http://api.tianditu.gov.cn/api?v=4.0&tk=你的秘钥" type="text/javascript"></script>
  1. 使用<BasicModal>组件作为模态框基础样式,并通过@register@ok@cancel等事件来注册、提交和取消选择点位操作。
  2. 组件内部使用天地图(Tianditu)进行地图展示和操作,包括初始化地图、点击地图获取坐标、根据地址搜索坐标等。
  3. 组件提供了一个搜索框,用户可以输入地址,通过点击搜索按钮来搜索对应的坐标,并在地图上标记。
  4. 组件可以接收初始的经纬度信息(通过initialLongitudeinitialLatitude两个props传递),并在地图上标记该位置。
  5. 组件内部使用了一些变量和函数来保存和操作地图、标记等信息,例如mapInstancecurrentMakerlongitudelatitude等。
  6. 组件提供了successcancel两个自定义事件,用于在选择点位成功或取消时通知父组件。

地图弹框示例代码

<template>
  <div style="width: 100%" @click="show">
    <slot></slot>
    <!-- 弹框组件 -->
    <BasicModal
      width="1000px"
      @register="registerModal"
      @ok="submit"
      @cancel="handleCancel"
      destroy-on-close
      :title="'请选择点位'"
    >
      <div class="mb-2">
        <Input id="tipinput" v-model:value="searchValue" :placeholder="'请填写详细地址'"/>
        <a-button type="primary" @click="search">搜索</a-button>
      </div>
      <!-- 使用天地图容器 -->
      <div id="mapDiv" ref="wrapRef" style="width: 100%; height: 500px">
        <div id="Tip" v-if="showCurrent">当前坐标:{{longitude}}-{{latitude}}</div>
      </div>
    </BasicModal>
  </div>
</template>

<script setup lang="ts">
 import { onMounted, ref ,nextTick,toRefs } from 'vue';
  import { BasicModal, useModal } from '/@/components/Modal';
  import { Input, message } from 'ant-design-vue';
  import GpsIcon from '/@/assets/images/gps.png';
  const emits = defineEmits(['success', 'cancel']);

  // 天地图相关数据绑定
  const mapInstance = ref(null as any);
  // 搜索区域内容
  const searchValue = ref('');
  // 变量保存Maker实例
  const currentMaker = ref(null as any);
  // 经度
  const longitude = ref('');
  // 纬度
  const latitude = ref('');
  // 判断当前坐标是否显示
  const showCurrent = ref(false);
// 在setup函数顶部声明缓存位置信息的ref
const lastSelectedLocation = ref({ longitude: '', latitude: '' });
  // 地理编码服务相关的变量
  let geocoder: any = null;
  const siteInfo = ref({
    longitude: '',
    address: '',
    latitude: ''
  });
  const props = defineProps({
  // 添加初始经纬度props
  initialLongitude: {
    type: String,
    default: '',
  },
  initialLatitude: {
    type: String,
    default: '',
  },
  // ... 其他props不变 ...
});
const { initialLongitude, initialLatitude } = toRefs(props);

  const [registerModal, { closeModal, openModal }] = useModal();

  declare global {
    interface Window {
      T: any;
      TMAP_VECTOR_MAP: any;
    }
  }
var T = window.T;
  // 初始化天地图

<style scoped>
  #mapDiv {
    width: 100%;
    height: 100vh;
  }
  .mb-2 {
    display: flex;
  }
  #Tip {
    position: absolute;
    color: #424B5A;
    font-weight: bold;
    font-size: 14px;
    bottom: 40px;
    left: 18px;
    z-index: 999;
  }
</style>

这段代码是Vue组件中的一部分,主要功能是实现了天地图的初始化和地图功能的绑定。

  • 首先,通过ref函数定义了一些变量,用于保存地图实例、搜索区域内容、Maker实例、经度、纬度、当前坐标是否显示等。

  • 然后,通过defineProps定义了组件接收的props,包括初始经度和纬度等。

  • 使用useModal函数定义了一个模态框的实例。

  • 在全局声明了一个Window接口,增加了TTMAP_VECTOR_MAP属性。

  • 最后,定义了一个initTiandituMap异步函数,用于初始化天地图。该函数会创建一个天地图实例,并根据传入的初始经纬度进行定位,并添加一个标记到地图上。

    • 首先,根据initialLongitudeinitialLatitude的值进行地图中心点的设置和缩放级别设定,并添加一个新的标记到地图上。

    • 如果没有传入初始经纬度,则将地图中心点设置为默认值(97.53662, 35.36499)。

  const initTiandituMap = async () => {
  var map: any = null;
  var T = window.T;
  map = new T.Map("mapDiv");
  map.setMapType(window.TMAP_VECTOR_MAP);
  // 根据传入的初始经纬度进行定位
  if (initialLongitude.value && initialLatitude.value) {
    map.centerAndZoom(new T.LngLat(Number(initialLongitude.value) ,Number(initialLatitude.value) ), 7);
    const newMarker = createNewMarker(new T.LngLat(initialLongitude.value, initialLatitude.value));
    map.addOverLay(newMarker);
    currentMaker.value = newMarker;
  } else {
    map.centerAndZoom(new T.LngLat(97.53662, 35.36499), 7);
  }
  mapInstance.value = map;
  map.addEventListener('click', (val: any) => {
    handleMapClick(val);
  });
};
  const handleMapClick = (val: any) => { 
    if (currentMaker.value) {
      console.log(currentMaker.value,'有之前点位信息');
      mapInstance.value.removeOverLay(currentMaker.value);
    }
    siteInfo.value.longitude = val.lnglat.lng;
    siteInfo.value.latitude = val.lnglat.lat;
    longitude.value = val.lnglat.lng;
    latitude.value = val.lnglat.lat;
    showCurrent.value = true;
    const icon = new T.Icon({
      iconUrl: GpsIcon,
      iconSize: new T.Point(30, 30),
      iconAnchor: new T.Point(15, 15),
    });
    const newMarker = new T.Marker(val.lnglat);
    newMarker.setIcon(icon);
    mapInstance.value.addOverLay(newMarker);
    currentMaker.value = newMarker;
  };
  onMounted(() => {
    geocoder = new window.T.Geocoder();
    initTiandituMap();
  });
  function show() {
    openModal();
    // 确保在模态框打开且DOM更新后初始化或重新初始化地图
    nextTick(() => {
      initTiandituMap(); // 直接调用 initTiandituMap
      searchValue.value= '';
      longitude.value = '';
      latitude.value = '';
      showCurrent.value = false; // 默认不显示当前坐标
    });
  }
  const search =  () => {
    console.log(searchValue.value,'search');
    const map:any = mapInstance.value;
    if (!geocoder || !searchValue.value) return;
    // 清除地图上覆盖物
    if (currentMaker.value) {
      mapInstance.value.removeOverLay(currentMaker.value);
    }
    geocoder.getPoint(searchValue.value, function(result) {
      if (result) {
        if (result.status == 0) {
          map.panTo(result.getLocationPoint(), 16);
          const newMarker = createNewMarker(result.getLocationPoint());
          map.addOverLay(newMarker);
          map.clearOverLays();
          updateCoordinates(newMarker.getPosition());
        } else {
          message.error(`搜索失败: ${result.getMsg()}`);
        }
      }
    });
  };
  function createNewMarker(position: any) {
    const icon = new T.Icon({
      iconUrl: GpsIcon,
      iconSize: new T.Point(30, 30),
      iconAnchor: new T.Point(15, 15),
    });
    const marker = new T.Marker(position);
    marker.setIcon(icon);
    return marker;
  }
  function updateCoordinates(position: any) {
    longitude.value = position.lng;
    latitude.value = position.lat;
    showCurrent.value = true;
    siteInfo.value.longitude = position.lng;
    siteInfo.value.latitude = position.lat;
  }
 // 在submit函数中更新缓存的位置信息
function submit() {
  lastSelectedLocation.value = { longitude: siteInfo.value.longitude, latitude: siteInfo.value.latitude };
  emits('success', {
    latitude: siteInfo.value.latitude.toString(),
    longitude: siteInfo.value.longitude.toString(),
  });
  closeModal();
}
  function handleCancel() {
    emits('cancel');
    closeModal();
  }
</script>
  1. 首先,将地图实例赋值给mapInstance,然后给地图添加点击事件监听器,当点击地图时,会调用handleMapClick函数。
  2. handleMapClick函数根据点击的坐标点信息,移除之前添加的标记(如果有),更新当前位置信息,并添加一个新的标记到地图上。
  3. onMounted生命周期函数用于在组件挂载后执行初始化地图的操作。
  4. show函数用于打开模态框并初始化或重新初始化地图。
  5. search函数用于根据输入的搜索值进行地点搜索,清除之前添加的标记,将搜索结果标记在地图上,并更新当前位置信息。
  6. createNewMarker函数用于创建一个新的标记,并设置标记的样式。
  7. updateCoordinates函数用于更新当前位置信息。
  8. submit函数用于在提交表单时更新缓存的位置信息,并触发success事件。
  9. handleCancel函数用于在取消操作时触发cancel事件,并关闭模态框。

表单封装

把我们封装好的地图弹框引入给于事件传参

表单组件示例代码


  <div>
    <FormItemRest>
      <MapModal v-model:value="location" @success="handleSuccess"
      :initial-longitude="location.longitude"
      :initial-latitude="location.latitude"
      >
      <div calss="map" style="display:flex">
        <div><a-input
          readonly
          :disabled="disabled"
          :placeholder="placeholder"
          v-model:value="location.longitude"
          :size="size"
          :bordered="bordered"
          @blur="emit('blur')"
          style="min-width: 150px"

        > 
        </a-input></div>
        <div style="margin:5px 10px">
          -
        </div>
        <div> <a-input
          readonly
          :disabled="disabled"
          :placeholder="placeholder"
          v-model:value="location.latitude"
          :size="size"
          :bordered="bordered"
          @blur="emit('blur')"
          style="min-width: 150px"
        >
        </a-input></div> 
        <div style="margin:5px 10px;">
            <Icon icon='mdi:map-marker-radius-outline' />
          </div>
      </div>
         
      </MapModal>
    </FormItemRest>
  </div>


<style scoped>
.map{
  display: flex;
}
</style>

这段代码是Vue的一个模板,用于渲染一个包含地图模态框和两个输入框的表单项。其中:

  • <FormItemRest> 是一个表单项容器组件;
  • <MapModal> 是一个地图模态框组件,通过 v-model:value 绑定到 location 属性,表示地图选择的地理位置,@success 事件在选择成功时触发,:initial-longitude 和 :initial-latitude 分别绑定到 location.longitude 和 location.latitude,表示地图的初始经度和纬度;
  • <a-input> 是一个输入框组件,通过 v-model:value 绑定到 location.longitude 和 location.latitude,表示经度和纬度的值,readonly 和 :disabled 表示输入框为只读且不可用状态,:placeholder 表示占位符,:size 和 :bordered 分别表示输入框的大小和边框样式,@blur 表示输入框失去焦点时触发的事件;
  • <Icon> 是一个图标组件,显示一个地图标记的图标。

 

<script>
  import { Form } from 'ant-design-vue';
  import { MapModal } from '/@/components/Map/index';
  import { watch, ref, inject } from 'vue';
  import { Icon } from '/@/components/Icon';

  import { camelCaseString } from '/@/utils/event/design';
  // 用于包裹弹窗的form组件 因为一个FormItem 只能收集一个表单组件  所以弹窗的form 必须排除
  const FormItemRest = Form.ItemRest;

  const props = defineProps({
    value: String,
    prefix: String,
    suffix: String,
    placeholder: String,
    readonly: Boolean,
    disabled: Boolean,
    size: String,
    bordered: {
      type: Boolean,
      default: true,
    },
    latitude: String,
    latiAndLong: String,
    index: Number,
    mainKey: String,
    longitude:String,
  });
  const location:any = ref<{ latitude: string; longitude: string }>({ latitude: '', longitude: '' });//对象形式接收经度维度
  const emit = defineEmits(['update:value', 'change', 'blur']);
  const formModel = inject<any>('formModel', null); // 注入表单数据
  const isCustomForm = inject<boolean>('isCustomForm', false);

  watch(
    () => props.value,
    () => {
      location.value.longitude = props.longitude,
      location.value.latitude = props.value
      console.log('props.value', props);
      if (!props.value) {
        //预览页面 重置
        location.value = { latitude: '', longitude: '' };
      }
    },
    {
      immediate: true,
    },
  );

  function handleSuccess(v) {
    location.value = { latitude: v.latitude, longitude: v.longitude };
    console.log('MAP handleSuccess ', v);
    changeFieldData(v);
    emit('update:value', location.value.latitude,location.value.longitude);
    emit('change',location.value.latitude,location.value.longitude);
  }
  function changeFieldData(v) {
    if (!formModel) return;
    if (props.latitude) {
      const latitudeField: any = isCustomForm ? props.latitude : camelCaseString(props.latitude);
      if (props.mainKey && props.index !== undefined && props.index !== null) {
        formModel[props.mainKey][props.index][latitudeField] = v.latitude;
      } else {
        formModel[latitudeField] = v.latitude;
        
      }
    }
    if (props.latiAndLong) {
      const latiAndLong: any = isCustomForm
        ? props.latiAndLong
        : camelCaseString(props.latiAndLong);
      if (props.mainKey && props.index != undefined && props.index !== null) {
        formModel[props.mainKey][props.index][latiAndLong] = v.lnglat.join(',');
      } else {
        formModel[latiAndLong] = v.lnglat.join(',');
      }
    }
  }
</script>
  • 引入Ant Design Vue的Form组件和自定义的MapModal组件。
  • 引入Vue 3的watchrefinject等函数。
  • 定义组件的props,包括输入框的值、前缀、后缀、占位符、只读、禁用、大小、是否带边框、经度、纬度等。
  • 使用ref定义一个location的响应式对象,用来保存经度和纬度数据。
  • 使用defineEmits定义组件可以发出的事件,包括update:valuechangeblur
  • 使用inject来注入表单数据的formModel和是否为自定义表单的isCustomForm
  • 使用watch来监听props.value的变化,并更新location的值。如果props.value为空,则重置location
  • 定义handleSuccess函数,用来处理地图弹窗成功获取到数据的情况。将数据更新到location,并调用changeFieldData函数更新表单数据,最后通过emit发出事件。

最后我们在页面进行组件引入即可

创作不易,点个关注不迷路~~~ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1579809.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM 内存溢出排查

说明&#xff1a;记录一次JVM内存溢出的排查过程&#xff1b; 场景 项目开发完成后&#xff0c;首次提交到测试环境。测试、产品同事反馈页面先是操作响应慢&#xff0c;抛出超时异常&#xff0c;最后直接无法使用。查看日志后得知是内存溢出。 重启服务后&#xff0c;我对前…

2024,还没掌握 JS 发布者、订阅者模式吗

面试中经常出现问到如何实现JS 发布者、订阅者模式。下面是ES5实现发布订阅模式。 1、直接上代码。 function EventEmitter() {this.events {}; }; // 订阅者 EventEmitter.prototype.on function(ename, callback) {if (!this.events[ename]) {// 初始化创建订阅&#xff…

算法训练营第二十一天(二叉树part7)

算法训练营第二十一天&#xff08;二叉树part7&#xff09; 530.二叉搜索树的最小绝对差 力扣题目链接(opens new window) 题目 给你一棵所有节点为非负值的二叉搜索树&#xff0c;请你计算树中任意两节点的差的绝对值的最小值。 示例&#xff1a; 提示&#xff1a;树中至…

9.枚举类与注解

文章目录 1. 枚举类1.1 自定义枚举类1.2 enum关键字定义枚举类1.3 Enum类主要方法 2. 注解2.1 注解作用2.1.1 生成文档相关注解2.1.2 JDK内置基本注解-在编译时进行格式检查2.1.3 跟踪代码依赖性&#xff0c;实现替代配置文件功能 2.2 自定义Annotation2.3 JDK中的元注解2.3.1 …

Python零基础从小白打怪升级中~~~~~~~流程控制语句

第三节&#xff1a;Python的流程控制语法 一、Python条件语句的语法 if 条件1:条件1成立执⾏的代码一条件1成⽴执⾏的代码二...... elif 条件2&#xff1a;条件2成立执⾏的代码三条件2成立执⾏的代码四...... ...... else:以上条件都不成⽴&#xff0c;执行的代码五以上条件都…

海外盲盒系统开发,盲盒出口成为企业新机遇

随着盲盒的兴起&#xff0c;国内消费市场形成了万物皆可盲盒的态势。并且&#xff0c;盲盒自带社交属性&#xff0c;也成为了年轻人的社交神器。 除了在国内&#xff0c;盲盒在海外也掀起了一股热潮&#xff0c;呈现出了爆发式增长形势&#xff0c;国内热门盲盒企业也出口到了…

Redis的三种部署方案

文章目录 单机模式主从复制哨兵模式分片集群 在Redis中提供的集群方案总共有三种&#xff1a;单机模式&#xff0c;主从复制集群、哨兵模式&#xff0c;Redis分片集群 单机模式 Redis 只运行在一台服务器上&#xff0c;并且所有的数据都存储在这一台服务器的内存中。 主从复制…

小程序商城免费搭建之java商城 电子商务Spring Cloud+Spring Boot+二次开发+mybatis+MQ+VR全景

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…

黄金白银价格上涨是投资机会吗?

黄金和白银&#xff0c;作为历史悠久的贵重金属&#xff0c;一直以来都被投资者视为避险资产。近年来&#xff0c;随着全球经济环境的变动&#xff0c;我们观察到黄金与白银的价格不断攀升&#xff0c;这是否预示着投资机会的到来&#xff1f;今天&#xff0c;就让我们来深度探…

【保姆级讲解SQL Server的详细使用教程】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

ATG-2021B功率信号源可以应用在哪些方面

功率信号源是一种能够产生一定功率的信号源&#xff0c;广泛应用于各个领域。下面将介绍功率信号源在电子、通信、工业和科研等方面的应用。 在电子行业中&#xff0c;功率信号源是一种重要的测试工具。它可以产生各种波形的信号&#xff0c;如正弦波、方波、脉冲波等&#xff…

vue中实现表格的局部刷新

背景&#xff1a; 列表中展示所有审核任务信息&#xff0c;包括审核状态、审核进度等&#xff0c;原来的实现是【查询】按钮绑定了一个定时器&#xff0c;定时查询整个列表&#xff0c;但是需要用户手动开启 领导觉得这种方式用户体验不好&#xff0c;希望能够实现【审核进度…

Golang | Leetcode Golang题解之第16题最接近的三数之和

题目&#xff1a; 题解&#xff1a; func threeSumClosest(nums []int, target int) int {sort.Ints(nums)var (n len(nums)best math.MaxInt32)// 根据差值的绝对值来更新答案update : func(cur int) {if abs(cur - target) < abs(best - target) {best cur}}// 枚举 a…

人工智能支持节能的七种方式

2024年2月&#xff0c;OpenAI公布了名为“Sora”的人工智能模型。从去年的chatgpt到今天Sora发布&#xff0c;OpenAI的每⼀次重要的动作和发布&#xff0c;都伴随着⼀场场精妙绝伦精巧的事件营销&#xff0c;它的设置议题的能⼒&#xff0c;节奏控制&#xff0c;公众引导堪称创…

P1731 [NOI1999] 生日蛋糕——典型的回溯和剪枝题目,值得一看

今天尝试了一下md的编辑器&#xff0c;不知道有没有什么改变 [NOI1999] 生日蛋糕 题目背景 数据加强版 link 题目描述 7 月 17 日是 Mr.W 的生日&#xff0c;ACM-THU 为此要制作一个体积为 N π N\pi Nπ 的 M M M 层生日蛋糕&#xff0c;每层都是一个圆柱体。 设从下往…

node后端上传文件到本地指定文件夹

实现 第一步&#xff0c;引入依赖 const fs require(fs) const multer require(multer) 第二步&#xff0c;先设置一个上传守卫&#xff0c;用于初步拦截异常请求 /*** 上传守卫* param req* param res* param next*/ function uploadFile (req, res, next) {// dest 值…

GD32F470_(4线制)火光/火焰传感器模块火源探测 红外接收传感器 智能车配件

2.16 火焰传感器 红外火焰传感器可以用来探测火源或其它一些波长在700纳米~1000纳米范围内的热源&#xff0c;在机器人比赛中&#xff0c;远红外火焰探头起到非常重要的作用&#xff0c;它可以用作机器人的眼睛来寻找火源或足球。利用它可以制作灭火机器人等。 红外火焰传感器…

10-热点文章-定时计算

xxl-Job分布式任务调度 1 今日内容 1.1 需求分析 目前实现的思路&#xff1a;从数据库直接按照发布时间倒序查询 问题1&#xff1a; 如何访问量较大&#xff0c;直接查询数据库&#xff0c;压力较大 问题2&#xff1a; 新发布的文章会展示在前面&#xff0c;并不是热点文章 …

R语言数据操纵:常用函数

目录 处理循环的函数 lapply函数 apply函数 mapply函数 tapply函数 split函数 排序的函数 sort函数与order函数 总结数据信息的函数 head函数与tail函数 summary函数 str函数 table函数 any函数 all函数 xtab函数 object.size函数 这篇文章主要介绍R语言中处理…

全网最全的APP测试面试题(含文档)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、基础篇 1、请介绍一下&#xff0c;APP测试流程&#xff1f…