✨✨使用vue3打造一个el-form表单及高德地图的关联组件实例✨

✨1. 实现功能

  1. 🌟表单内显示省市县以及详细地址
    • 点击省市县输入框时,打开对应地图弹窗,进行位置选择
    • 选择位置回显入对应输入框
    • 表单内的省市县以及地址输入框同外嵌表单走相同的校验方式
    • 触发校验后点击reset实现清除校验与清空数据
  2. 🌟地图内展示地址搜索框以及地图坐标图
    • 搜索框展示当前经纬度地址
    • 搜索框可输入自定义地址,下拉菜单展示范围兴趣点和道路信息,点击可进行搜索
  3. 🌟 单独封装每个组件,使form-itemdialog以及amap三个组件可单独使用

✨2. 示例图

  1. 💖示例图1:💖
    在这里插入图片描述

  2. 💖💖示例图2:💖
    在这里插入图片描述

  3. 💖💖💖示例图3:💖
    在这里插入图片描述

  4. 💖💖💖💖示例图4:💖
    在这里插入图片描述

  5. 💖💖💖💖💖示例图5:💖
    在这里插入图片描述

✨3. 组件代码

🌹1. 组件目录结构

在这里插入图片描述

2. 🍗 🍖地图组件AmapContainer.vue
<template><div v-loading="loading"><input type="text" class="address" v-model="iMap.address" id="inputAddress" /><div id="container"></div></div>
</template><script setup lang="ts" name="AmapContainer">
import { ref, reactive, computed, watch, onMounted, onUnmounted } from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";
import { AMAP_MAP_KEY, AMAP_SECRET_KEY } from "@/config";
import { getBrowserLang } from "@/utils";
import { useGlobalStore } from "@/stores/modules/global";
import { IMap } from "../interface/index";const globalStore = useGlobalStore();
const language = computed(() => {if (globalStore.language == "zh") return "zh_cn";if (globalStore.language == "en") return "en";return getBrowserLang() == "zh" ? "zh_cn" : "en";
});const loading = ref(true);interface ExtendsWindow extends Window {_AMapSecurityConfig?: {securityJsCode: string;};
}
let _window: ExtendsWindow = window;// 定义map实例
let map: any = null;const iMap = reactive<IMap>({province: "",city: "",district: "",address: "",lnglat: [114.525918, 38.032612],canSubmit: true
});watch(() => iMap.address,() => {iMap.canSubmit = !iMap.address;}
);onMounted(() => {initMap();
});onUnmounted(() => {map?.destroy();
});// 初始化地图
const initMap = async () => {_window._AMapSecurityConfig = {securityJsCode: AMAP_SECRET_KEY // ❓高德秘钥👇👇下方会有👇👇};AMapLoader.load({key: AMAP_MAP_KEY, // ❓申请好的Web端开发者Key,首次调用 load 时必填👇👇下方会有👇👇version: "2.0", // ❓指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15plugins: ["AMap.ToolBar", "AMap.Scale", "AMap.Marker", "AMap.Geocoder", "AMap.AutoComplete"] //需要使用的的插件列表}).then(AMap => {map = new AMap.Map("container", {// 设置地图容器idviewMode: "2D", // 是否为3D地图模式zoom: 11, // 初始化地图级别center: iMap.lnglat // 初始化地图中心点位置});//创建工具条插件实例const toolbar = new AMap.ToolBar({position: {top: "110px",right: "40px"}});map.addControl(toolbar);//创建比例尺插件实例const Scale = new AMap.Scale();map.addControl(Scale);//创建标记插件实例const Marker = new AMap.Marker({position: iMap.lnglat});map.addControl(Marker);//创建地理编码插件实例const Geocoder: any = new AMap.Geocoder({radius: 1000, //以已知坐标为中心点,radius为半径,返回范围内兴趣点和道路信息extensions: "base", //返回地址描述以及附近兴趣点和道路信息,默认“base | all”lang: language.value});//返回地理编码结果Geocoder.getAddress(iMap.lnglat, (status, result) => {if (status === "complete" && result.info === "OK") {iMap.province = result.regeocode.addressComponent.province;iMap.city = result.regeocode.addressComponent.city;iMap.district = result.regeocode.addressComponent.district;iMap.address = result.regeocode.formattedAddress;AutoComplete.setCity(iMap.address);loading.value = false;}});// 根据输入关键字提示匹配信息const AutoComplete = new AMap.AutoComplete({input: "inputAddress",city: iMap.address,datatype: "all",lang: language.value});AutoComplete.on("select", result => {iMap.lnglat = [result.poi.location.lng, result.poi.location.lat];setPointOrAddress();});//点击地图事件map.on("click", e => {iMap.lnglat = [e.lnglat.lng, e.lnglat.lat];setPointOrAddress();});// 设置地图点坐标与位置交互const setPointOrAddress = () => {Marker.setPosition(iMap.lnglat);map.setCenter(iMap.lnglat);map.setZoom(12);Geocoder.getAddress(iMap.lnglat, (status, result) => {if (status === "complete" && result.info === "OK") {iMap.province = result.regeocode.addressComponent.province;iMap.city = result.regeocode.addressComponent.city;iMap.district = result.regeocode.addressComponent.district;iMap.address = result.regeocode.formattedAddress;}});};}).catch(e => {console.log(e);});
};defineExpose({iMap
});
</script><style scoped lang="scss">
@import "../index.scss";
</style><style lang="scss">
.amap-sug-result {z-index: 10000;
}
</style>
🍀3. 弹窗组件AmapDialog.vue 🍀
<template><el-dialog :model-value="visible" title="请选择" width="800" :before-close="handleClose"><AmapContainer ref="amapContainer" /><template #footer><div class="dialog-footer"><el-button @click="handleClose">取消</el-button><el-button type="primary" :disabled="amapContainer?.iMap?.canSubmit" @click="handleConfirm">确认</el-button></div></template></el-dialog>
</template><script setup lang="ts" name="AmapExplore">
/*🔻// 使用方式// ❤️amapFlag: 控制弹窗显隐// ❤️iMap必须ref定义, 接收选择地址数据// 示例:// 💥<AmapExplore v-model:visible="amapFlag" v-model:amap="iMap" />💥
*/ 🔺 
import { ref, withDefaults } from "vue";
import { IAddress } from "../interface/index";
import AmapContainer from "./AmapContainer.vue";withDefaults(defineProps<{visible: boolean;amap: Partial<IAddress>;}>(),{visible: false}
);const amapContainer = ref();// 定义emits
const emits = defineEmits<{"update:amap": [value: IAddress];"update:visible": [value: boolean];
}>();const handleConfirm = () => {// delete amapContainer.value?.iMap?.canSubmit;emits("update:amap", amapContainer.value?.iMap);handleClose();
};const handleClose = () => {emits("update:visible", false);
};
</script><style scoped lang="scss"></style>
🌼4. 表单组件AmapExplore/index.vue 🌼
<template><el-row :gutter="gutter" :style="gutterStyle"><el-col :span="8"><el-form-item prop="province"><el-inputv-model="iMapForm.province"ref="provinceRef"placeholder="省"size="large"style="width: 100%"@click="handleAmapChange"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="city"><el-inputv-model="iMapForm.city"ref="cityRef"placeholder="市"size="large"style="width: 100%"@click="handleAmapChange"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="district"><el-inputv-model="iMapForm.district"ref="districtRef"placeholder="县"size="large"style="width: 100%"@click="handleAmapChange"></el-input></el-form-item></el-col></el-row><el-col :span="24"><el-form-item prop="address"><el-input v-model="iMapForm.address" placeholder="请输入详细地址" size="large" style="width: 100%"></el-input></el-form-item></el-col><AmapDialog v-model:visible="amapFlag" v-model:amap="iMapForm" />
</template><script setup lang="ts" name="AmapExplore">
import { ref, reactive, watch, inject, watchEffect } from "vue";
import type { FormRules } from "element-plus";
import { IAddress } from "./interface/index";
import AmapDialog from "./components/AmapDialog.vue";// 栅格间隔与样式
const gutter = 20;
const gutterStyle = {width: `calc(100% + ${gutter}px)`,"margin-bottom": `${gutter}px`
};// 接收传入的formData和formRules
const { ruleForm, rules } = inject<{ ruleForm: Object; rules: any }>("aMap", { ruleForm: reactive({}), rules: reactive({}) });const iMapForm = ref<IAddress>({province: "",city: "",district: "",address: "",lnglat: []
});// 若地址有值,则赋予formData
watch(() => iMapForm,n => {// 为防止重复赋值if (n.value.province || n.value.city || n.value.district || n.value.address) {Object.assign(ruleForm, { ...iMapForm.value });}},{deep: true}
);// 另处理经纬度lnglat
watch([() => iMapForm.value.province, () => iMapForm.value.city, () => iMapForm.value.district], n => {if (n.some(item => !item)) {iMapForm.value.lnglat = [];}
});watch(() => iMapForm.value.lnglat,n => {if (!n.length) {Object.assign(ruleForm, iMapForm.value);}}
);// 将formData赋值给iMapForm-主要作用为清空重置
watchEffect(() => {Object.assign(iMapForm.value, { ...ruleForm });
});// form校验;
const iMapRules = reactive<FormRules<IAddress>>({province: [{ required: true, message: "请选择省", trigger: ["blur", "change"] }],city: [{ required: true, message: "请选择市", trigger: ["blur", "change"] }],district: [{ required: true, message: "请选择区、县", trigger: ["blur", "change"] }],address: [{ required: true, message: "请输入详细地址", trigger: ["blur", "change"] }]
});// 合并校验数据;
watch(rules, () => Object.assign(rules, { ...iMapRules }), {immediate: true,deep: true
});// 地图弹窗
const amapFlag = ref<boolean>(false);
const provinceRef = ref();
const cityRef = ref();
const districtRef = ref();
const handleAmapChange = () => {amapFlag.value = true;provinceRef.value.blur();cityRef.value.blur();districtRef.value.blur();
};
</script><style scoped lang="scss"></style>
5. 🌿scss文件 🌿
// AmapContainer
.address {box-sizing: border-box;width: 100%;height: 30px;padding: 0 12px;margin-bottom: 10px;line-height: 30px;border: 1px solid #ececec;border-radius: 4px;
}
#container {width: 100%;height: 400px;padding: 0;margin: 0;
}
🌴6. 类型定义interface/index.ts 🌴
export interface IAddress {province: string;city: string;district: string;address: string;lnglat: number[];
}
export interface IMap extends IAddress {canSubmit: boolean;
}
❕ ❕7. 地图组件内使用的高德AMAP_MAP_KEY和秘钥AMAP_SECRET_KEY可以自行设置

// 高德地图 key
export const AMAP_MAP_KEY: string = "****";
// 高德地图 安全密钥
export const AMAP_SECRET_KEY: string = "*****";

✨4. 父组件使用😎

  1. ☝️ 使用组件

<!-- 1. 使用组件 -->
<AmapExplore />
  1. ✌️使用provide向后代传入表单数据formData)和校验规则formRules

// 2. 传入formData和formRules
provide("aMap", { ruleForm, rules });
  1. 👋完整代码示例:
<template><div class="card amap-example"><el-form ref="ruleFormRef" :model="ruleForm" :rules label-width="auto" style="max-width: 600px"><el-form-item label="Activity name" prop="name"><el-input v-model="ruleForm.name" /></el-form-item><el-form-item label="地址" required><!-- 1. 使用组件 --><AmapExplore /></el-form-item><el-form-item label="备注" prop="remark"><el-input v-model="ruleForm.remark" /></el-form-item><el-form-item><el-button type="primary" @click="submitForm(ruleFormRef)"> Create </el-button><el-button @click="resetForm(ruleFormRef)">Reset</el-button></el-form-item></el-form></div>
</template><script setup lang="ts" name="amapExample">
import { reactive, ref, provide } from "vue";
import type { FormInstance, FormRules } from "element-plus";
import AmapExplore from "@/components/AmapExplore/index.vue";interface RuleForm {name: string;remark: string;
}
const ruleFormRef = ref<FormInstance>();
let ruleForm = reactive<RuleForm>({name: "",remark: ""
});let rules = reactive<FormRules<RuleForm>>({name: [{ required: true, message: "请输入姓名", trigger: "blur" }],remark: [{ required: true, message: "请输入备注", trigger: "blur" }]
});// 2. 传入formData和formRules
provide("aMap", { ruleForm, rules });const submitForm = async (formEl: FormInstance | undefined) => {console.log(ruleForm, "s");if (!formEl) return;await formEl.validate((valid, fields) => {if (valid) {console.log("submit!");} else {console.log("error submit!", fields);}});
};const resetForm = (formEl: FormInstance | undefined) => {if (!formEl) return;formEl.resetFields();
};
</script>

❗️ 5. 封装实例缺点💦

  1. 当选择地址之后,再次打开地图弹窗,更改地图标记点,地址会实时变更,
  2. 不论点击取消还是确认按钮,都会改变表单内部值
  3. 💢初始不会出现此问题💢
  4. 💪后续会改进😁😁😁😁

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

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

相关文章

Base64在线编码解码方法

Base64在线编码解码 打开网站 在线工具网-梦幻加菲猫 选择“Base64编码解码” 输入需要编码/解码的内容&#xff0c;点击“编码”/“解码”按钮 编码&#xff1a; 解码&#xff1a; 4. 复制已经编码/解码后的内容。

【云原生】Kubeadm搭建K8S

一、部署Kubernetes 实验环境 服务器主机名IP地址主要组件k8s集群master01 etcd01master01192.168.10.100kube-apiserver kube-controller-manager kube-schedular etcdk8s集群node01 etcd02node01192.168.10.101kubelet kube-proxy docker flannelk8s集群node02 etcd03nod…

数据结构-题目

1.已知一颗完全二叉树的第6曾&#xff08;设根为第1层&#xff09;&#xff0c;有8个结点&#xff0c;则完全二叉树的结点个数&#xff0c;最少和最多分别是多少&#xff1f; 因此最少为39&#xff0c;最多为111 2.假设一棵三叉树的结点数为50&#xff0c;则它的最小高度为&…

【声呐仿真】学习记录3-待续

【声呐仿真】学习记录3-后续 第五阶段-获取数据1.运行赫尔库勒斯沉船的世界&#xff1a;2.键盘操纵rov至合适的位置&#xff0c;调整Image topic&#xff0c;查看输出图像3.RVIZ SONAR 图像查看器插件&#xff08;没有对应的topic&#xff09;4.点云5.录制rosbag 第六阶段-查看…

守护数据安全:精选七款数据加密软件

在数字化日益普及的今天&#xff0c;数据安全成为了企业和个人不可忽视的重要问题。加密软件作为数据安全的第一道防线&#xff0c;扮演着至关重要的角色。本文将为读者精选七款优秀的加密软件&#xff0c;帮助大家更好地守护数据安全。 Ping32数据加密软件 Ping32数据加密软…

Timestamp Unix时间戳在线转换

Timestamp Unix时间戳在线转换 打开网站 在线工具网-梦幻加菲猫 选择“时间戳转换” 在前半部分输入框输入时间/时间戳&#xff0c;点击“转换>>”按钮&#xff0c;即可转换完成 得到转换结果

Python自动化SQL注入和数据库取证工具库之sqlmap使用详解

概要 在网络安全领域,SQL注入仍然是最常见的攻击之一。sqlmap是一个开源的自动化SQL注入和数据库取证工具,它提供了广泛的功能来检测和利用SQL注入漏洞。本文将详细介绍sqlmap的安装、特性、基本与高级功能,并结合实际应用场景,展示其在网络安全测试中的应用。 安装 sqlm…

银行监管报送系统系列介绍(十七):一表通2.0

国家金融监督管理总局于9月发布了【一表通2.0&#xff08;试用版&#xff09;】&#xff08;简称&#xff1a;一表通2.0&#xff09;&#xff0c;在原试点报送范围的基础上扩大了试点报送区域&#xff0c;意味着将陆续扩大试报送机构范围&#xff0c;推进的速度已明显加快。尽早…

HQChart使用教程98-右键菜单2.0使用介绍

HQChart使用教程98-右键菜单2.0使用介绍 内置右键菜单启用右键菜单定制右键菜单内容1. 注册内置右键菜单创建回调事件2. 修改内置菜单的显示内容回调函数格式菜单数据结构示例 3. 注册菜单项点击事件回调 右键事件完整示例HQChart代码地址 内置右键菜单 HQChart h5版本内置提供…

Spring Boot + Mybatis-plus代码生成器 自动生成项目结构

首先创建一个新的springboot项目 项目初始化结构如下&#xff1a; 运行自动生成结构代码后的效果如下&#xff1a; 对比初始化项目结构可以发现结构中多了以下几个部分; controller文件夹存储接口类mapper文佳夹存储数据库映射model文件夹存储数据库模型类Service文件夹存储业…

未授权访问:Docker未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、通过crontab反弹宿主机shell 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好…

万字长文带你掌握 IDEA 2024 的30个Debug调试绝技,让你开发与修复 Bug 的效率提升亿倍

万字长文带你掌握 IDEA 2024 的30个Debug调试绝技&#xff0c;让你开发与修复 Bug 的效率提升亿倍 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — …

QCC---Aptx Lossless验证

因为aptx Lossless属于高通骁龙声音的一部分&#xff0c;一般支持高通骁龙声音的设备会支持到&#xff0c;比如说手机&#xff0c;而且还要支持最新的aptx adaptive协议R2.2版本。但是如果手上没有这样的手机的话&#xff0c;有source芯片也可以去做测试验证。在最新的784.1版本…

钽酸锂集成光子芯片:引领光电集成新纪元

在信息技术飞速发展的今天&#xff0c;光电集成技术已成为推动全球集成电路产业持续创新的重要力量。随着全球集成电路产业发展步入“后摩尔时代”&#xff0c;芯片性能提升的难度和成本不断攀升&#xff0c;业界急需寻找新的技术突破口。在这一背景下&#xff0c;中国科学院上…

微软发布研究报告:75%的知识工作者在工作中使用AI

“人工智能正在使整个劳动力的专业知识民主化&#xff0c;我们的最新研究强调了每个组织都有机会应用这项技术来推动更好的决策、协作&#xff0c;并最终实现业务成果。” ——Microsoft董事长兼首席执行官萨蒂亚纳德拉&#xff08;Satya Nadella&#xff09; 一年前&#xff0…

pycharm连接远程服务器,解决终端出现乱码问题

在终端输入命令时会有乱码问题&#xff0c;是字体编码设置错误。 根据上述步骤&#xff0c;设置完成后重启就可以了。

重生之霸总的项目管理之道

在都市的摩天大楼之间&#xff0c;顾辰&#xff0c;这位被人们称为“霸总”的项目经理&#xff0c;以他独特的魅力和高效的项目管理方式&#xff0c;成为了行业内的传奇人物。他并非传统意义上的冷酷霸总&#xff0c;而是一个懂得运用现代项目管理工具&#xff0c;带领团队一次…

MySQL基础指南:从入门到精通

MySQL基础指南&#xff1a;从入门到精通 MySQL是一个流行的开源关系型数据库管理系统&#xff0c;被广泛用于Web应用程序和服务器端开发。本文将从MySQL的基本概念开始&#xff0c;逐步介绍MySQL的安装、常用操作、数据类型、查询语句等内容&#xff0c;帮助你快速入门MySQL数…

地下车库导航地图怎么做?停车场地图绘制软件哪个好?

上海懒图科技以先进技术和丰富的行业服务经验为用户提供停车场景下的全流程服务平台&#xff0c;用户基于平台可自主快速绘制酷炫的停车场地图&#xff0c;通过提供完善的停车场应用功能集和扩展API服务包&#xff0c;可以方便地实现电子地图服务于您的各类停车场应用中&#x…

利用香港多IP服务器进行大数据分析的潜在优势?

利用香港多IP服务器进行大数据分析的潜在优势? 在当今数据驱动的时代&#xff0c;大数据分析已经成为企业获取竞争优势的不二选择。而香港作为一个拥有世界级通信基础设施的城市&#xff0c;提供了理想的环境来部署多IP服务器&#xff0c;从而为大数据分析提供了独特的优势。…