elementUI输入框/选项卡与百度地图联动

输入框/选项卡与百度地图联动

示例

输入框/选项卡与百度地图联动

html部分

<template>
	<el-form ref="Froms" :model="formData" label-width="120px">
		<el-form-item label="经营地区:" prop="businessArea">
			<v-region-group
	             :town="false"
	             v-model="regionArea"
	             @change="regionChange"
	           >
	         </v-region-group>
	      </el-form-item>
	      <el-form-item label="详细地址:" prop="shopAddrExt">
	        <el-input v-model="formData.shopAddrExt" placeholder="请输入详细地址" size="small" @input="inputShopAddr" />
	        <div class="map">
                <baidu-map 
                  @ready="initMap"
                  @click="getLocation"
                  :center="center"
                   class="map"
                  :zoom="zoom"
                  :scroll-wheel-zoom="true"
                >
                <bm-navigation anchor="BMAP_ANCHOR_TOP_RIGHT" />
                </baidu-map>
            </div>
          </el-form-item>
	</el-form>
</template>

<script>
	export default {
		data(){
			return{
				formData: {
					shopAddrExt: ''
				},
				regionArea: {}, // 所选区域
		        center: { lng: 0, lat: 0 }, // 坐标
		        centerPoint: {},
		        zoom: 3, 
				BMap: null,
			    map: null,
			    geoCoder: null // 地址解析器
			}
		},
		methods: {
			initMap({ BMap, map }) {
				this.zoom = 15;
      			this.BMap = BMap;
      			this.map = map;
      			this.geoCoder = new BMap.Geocoder(); // 创建地址解析器的实例
      			let geolocation = new BMap.Geolocation(); // 获取当前经纬度用作地图显示中心点
      			geolocation.getCurrentPosition((e)=>{ // 方法获取设备在当前所处的坐标位置
      				console.log(e);
      				this.center.lng = e.longitude;
      				this.center.lat = e.latitude;
      				this.setCenterZoom(e); // e获取到经纬度设置地图中心点
      				this.setIcon(e); // 创建点坐标
      			})
      			// 监听地图缩放结束事件 lng表示经度,lat表示纬度(处理百度地图放大之后中心点偏移问题)
      			this.map.addEventListener('zoomend', (e)=> {
      				this.map.centerAndZoom(
      					new BMap.Point(this.center.lng, this.center.lat),
      					this.map.getZoom();
      				);
      			});
      			// enableMapClick:false 表示禁止地图内景点信息点击
      			// this.map = new BMap.Map('baiduMap', { enableMapClick: false });
      			// 设置地图允许的最大最小级别
      			// this.map.setMinZoom(5);
      			// this.map.setMaxZoom(20);
      			// 开启鼠标滚轮缩放
      			// this.map.enableScrollWheelZoom(true);
			},
			// 设置显示中心点
			setCenterZoom(e){
				this.center.lng = e.longitude;
				this.center.lat = e.latitude;
				this.centerPoint = new BMap.Point(e.longitude, e.latitude); // 选择一个经纬度作为中心点
				this.map.centerAndZoom(this.centerPoint, 14); // 设置地图中心点和缩放级别
			},
			// 创建点坐标
			setIcon(latLng){
      			// 创建自定义标记 参数:自定义图片路径 大小 偏移量
      			const icon = new BMap.Icon(
       			 	require('../../../../../static/icon/position4.png'),
       			 	new BMap.Size(32, 32),
			        // { anchor: new BMap.Size(0, 0) }
			     );
			     // 生成自定义图标点
			     // 创建点
			     const point = new BMap.Point(latLng.longitude, latLng.latitude);
			     // 创建标注
			     const marker = new BMap.Marker(point, { icon: icon });
			     this.map.clearOverlays(); // 先删除
			     this.map.addOverlay(marker);// 将标注添加到地图中
			     // 给标记点添加点击事件
			     marker.addEventListener('click', (e) => {
			     	// 执行想进行的操作(经个人测试在此处注册点击事件效果最佳, 该有的数据项都可以获取)
			     	console.log(e)
			     })
      		},
      		// 获取地图点击的地址
      		getLocation(e){
      			// console.log(e.point)
      			let latLng = {
			        longitude: e.point.lng,
			        latitude: e.point.lat
			    }
			    this.setCenterZoom(latLng);// 更改地图显示中心点
			    this.setIcon(latLng);// 创建点坐标
			    this.geoCoder.getLocation(e.point, (rs) => {
			    	// console.log(rs.surroundingPois) // 附近的POI点(array) 
			    	// console.log(rs.business) // 商圈字段
			    	let adr = rs.addressComponents;
			    	let address = adr.province + adr.city + adr.district + adr.street + adr.streetNumber; // 省市区街道门牌号
			    	this.formData.shopAddrExt = address; // 给input框赋值
			    });
      		},
      		// 根据输入的地址定位获取当前经纬度/根据当前地址获取经纬度
      		inputShopAddr(inputValue){
      			this.geoCoder.getPoint(inputValue, (point) => {
      				let latLng = {
			          longitude: point.lng,
			          latitude: point.lat
			        }
			        this.setCenterZoom(latLng);// 更改地图显示中心点
			        this.setIcon(latLng);// 创建点坐标
      			})
      		},
      		// 选择经营地区
      		regionChange (data) {
      			console.log(data);
      			let province = data.province ? data.province.value : '';
      			let city = data.city ? data.city.value : '';
      			let area = data.area ? data.area.value : '';
      			this.formData.businessArea = province + city + area;
      			this.inputShopAddr(this.formData.businessArea);
      		}
		}
	}
</script>

用到的插件

// v-region插件
npm i v-region
// 百度地图
npm install vue-baidu-map --save

main.js

// v-region插件
import Region from 'v-region'
Vue.use(Region);

// 百度地图
import BaiduMap from 'vue-baidu-map'
Vue.use(BaiduMap, {
  ak: '************************************'
})
前端vue2+elementui帮我生成一个前端页面,一下需求1.2.3.4.5.6部分已经完成完成的代码在下边,仔细分析现有代码,优化一下第三部分的部件和接口选项的宽度问题,完善7.8部分的需求,尤其是接口选项多选和主体中间部分的跨容器拖拽功能实现 1.竖向分为头部和主体部分,头部宽度100%,高度60px,头部左侧两个input框,lable分别为:用例名称,脚本编号,右侧3个按钮是生成用例,生成脚本,调试 2.主体部分占据剩下高度,分为左,中,右三部分,宽度占比分别为40%,40%,20%, 3.主体左侧为左右两部分,分别为部件和接口选项,两个均为可以手动打开关闭的抽屉(使用elementui中的el-drawer),重要一点:我需要的是将部件用el-drawer包裹和接口选项整体用el-drawer包裹,有一个箭头的按钮在整体的右侧部分控制el-drawer的开关,部件宽度为35%,接口选项宽度65%,两个抽屉没有层级关系,独立开启关闭,不会相互影响,无论开启或关闭组件在左,接口选项在右 4.部件部分:竖向一个搜索框,输入关键字可对下面具体组件进行筛选,搜索框下是组件名称和图标,组件名称分别为:车门,车窗,座椅,前舱盖,雨刮器,光机幕布,后视镜这部分可使用 列表渲染,每个组件展示为一行,左侧组件图标,右侧组件名称,,一行一列,依次向下 5.接口选项部分,竖向一个搜索框,输入关键字可对下面具体接口进行筛选,接口选项和左侧部件是有联动的,两者存在跨容器拖拽关系,拖动左侧部件中的一个(每次只能拖动一个),到右侧接口选项区域,会触发事件,调取后端接口获取对应组件的接口列表(这里可以使用模拟数据),展示在接口选项部分, 接口内容为:打开所有车窗。open_all_wins,控制车辆上电。power_on_off_control...,(可以放点模拟数据) 6.拖动组件到接口选项部分,每一个组件对应的接口展示都是一个独立的二级树形结构,树形结构一级lable为组件名称,右侧有一个可以X,可以删除当前组件,树形结构二级内容就为每个具体的接口,每个接口头部都有一个多选按钮,可以展示多个,依次竖向向下展示 7.主体中间部分竖向依次为3部分,预置条件,测试步骤,环境恢复,宽度均占据主体中间部分宽度100%,高度分别为30%,40%,20%,左侧接口选项部分通过多选框勾选接口,勾选的接口组成一个list,通过拖拽将接口选项中的接口拖拽到右侧主体中间部分展示,支多选批量拖拽 每个区域可视为独立区域,相互不影响,若一个区域拖入多个接口,根据先后依次排序 ,拖拽至中间主体部分的接口,在各自独立区域内,接口排序可以通过上下拖拽调换, 8.主体右侧为参数配置:由上至下第一行两个单选按钮,Machine,Relay,剩下为3个输入框,各占一行,lable分别为:机械控制踏板踩下去和抬起来之间的时间间隔(秒),踏板踩下去和抬起来的时间间隔(秒),踏板踩下去或抬起来需要继电器打开的时间(秒) <template> <div id="app"> <!-- 头部区域 --> <div class="header"> <div class="header-left"> <el-input placeholder="请输入用例名称" class="header-input" v-model="caseName" > <template slot="prepend">用例名称</template> </el-input> <el-input placeholder="请输入脚本编号" class="header-input" v-model="scriptId" > <template slot="prepend">脚本编号</template> </el-input> </div> <div class="header-right"> <el-button type="primary" class="header-button">生成用例</el-button> <el-button type="success" class="header-button">生成脚本</el-button> <el-button type="warning" class="header-button">调试</el-button> </div> </div> <!-- 主体区域 --> <div class="main-container"> <!-- 左侧区域 --> <div class="left-section"> <!-- 部件抽屉 --> <div class="drawer-container"> <div class="drawer-wrapper" :style="{width: componentsDrawerOpen ? '280px' : '0'}"> <div class="components-drawer" v-show="componentsDrawerOpen"> <div class="components-container"> <div class="components-header"> <i class="el-icon-s-grid"></i> 车辆部件列表 </div> <el-input placeholder="搜索部件..." v-model="componentSearch" prefix-icon="el-icon-search" clearable ></el-input> <div style="margin-top: 15px; overflow-y: auto; height: calc(100% - 100px);"> <div class="component-item" v-for="component in filteredComponents" :key="component.id" draggable="true" @dragstart="dragStart(component)" > <div class="icon"> <i :class="component.icon"></i> </div> <div class="name">{{ component.name }}</div> </div> </div> </div> </div> </div> <div class="drawer-toggle" :class="{collapsed: !componentsDrawerOpen}" @click="componentsDrawerOpen = !componentsDrawerOpen" > <i class="el-icon-arrow-left"></i> </div> </div> <!-- 接口抽屉 --> <div class="drawer-container"> <div class="drawer-wrapper" :style="{width: interfacesDrawerOpen ? '500px' : '0'}"> <div class="interfaces-drawer" v-show="interfacesDrawerOpen"> <div class="interfaces-container"> <div class="interfaces-header"> <i class="el-icon-connection"></i> 接口选项 </div> <el-input placeholder="搜索接口..." v-model="interfaceSearch" prefix-icon="el-icon-search" clearable ></el-input> <div class="tree-container" @dragover.prevent @drop="dropComponent" > <div v-if="interfaceGroups.length === 0" class="drop-area"> <i class="el-icon-upload"></i> <div class="drop-hint">请从左侧拖动部件到此处以加载接口</div> </div> <div v-else> <transition-group name="fade"> <div class="tree-node" v-for="group in filteredInterfaceGroups" :key="group.id" > <div class="tree-header"> <div>{{ group.componentName }} 接口</div> <div class="delete-btn" @click="removeGroup(group.id)"> <i class="el-icon-close"></i> </div> </div> <div class="tree-content"> <div class="interface-item" v-for="item in group.interfaces" :key="item.id" > <el-checkbox v-model="item.selected"></el-checkbox> <div class="interface-name">{{ item.name }}</div> <div class="interface-id">{{ item.id }}</div> </div> </div> </div> </transition-group> </div> </div> </div> </div> </div> <div class="drawer-toggle" :class="{collapsed: !interfacesDrawerOpen}" @click="interfacesDrawerOpen = !interfacesDrawerOpen" > <i class="el-icon-arrow-left"></i> </div> </div> </div> <!-- 中间区域 --> <div class="center-section"> <div class="center-placeholder"> <i class="el-icon-s-marketing"></i> <div>用例执行区域</div> <div style="font-size: 14px; margin-top: 10px;">此处将展示用例执行流程和结果</div> </div> </div> <!-- 右侧区域 --> <div class="right-section"> <div class="right-placeholder"> <i class="el-icon-setting"></i> <div>参数配置区域</div> <div style="font-size: 14px; margin-top: 10px;">此处将展示脚本参数配置选项</div> </div> </div> </div> </div> </template> <script> import axios from "axios"; import { } from "@/api/api.js"; import draggable from "vuedraggable"; import Papa from "papaparse"; import * as echarts from "echarts"; import { Message } from "element-ui"; export default { components: { draggable }, data() { return { caseName: '', scriptId: '', componentsDrawerOpen: true, interfacesDrawerOpen: true, componentSearch: '', interfaceSearch: '', draggedComponent: null, // 部件数据 components: [ { id: 1, name: '车门', icon: 'el-icon-s-help' }, { id: 2, name: '车窗', icon: 'el-icon-s-promotion' }, { id: 3, name: '座椅', icon: 'el-icon-s-opportunity' }, { id: 4, name: '前舱盖', icon: 'el-icon-s-flag' }, { id: 5, name: '雨刮器', icon: 'el-icon-s-release' }, { id: 6, name: '光机幕布', icon: 'el-icon-s-platform' }, { id: 7, name: '后视镜', icon: 'el-icon-s-claim' } ], // 接口分组数据 interfaceGroups: [], nextGroupId: 1, // 模拟接口数据 mockInterfaces: { 1: [ // 车门接口 { id: 'door_lock', name: '车门锁定', selected: false }, { id: 'door_unlock', name: '车门解锁', selected: false }, { id: 'door_open', name: '打开车门', selected: false }, { id: 'door_close', name: '关闭车门', selected: false } ], 2: [ // 车窗接口 { id: 'window_open_all', name: '打开所有车窗', selected: false }, { id: 'window_close_all', name: '关闭所有车窗', selected: false }, { id: 'window_open_driver', name: '打开驾驶员车窗', selected: false }, { id: 'window_open_passenger', name: '打开乘客车窗', selected: false } ], 3: [ // 座椅接口 { id: 'seat_heat', name: '座椅加热', selected: false }, { id: 'seat_cool', name: '座椅通风', selected: false }, { id: 'seat_adjust', name: '座椅调节', selected: false }, { id: 'seat_massage', name: '座椅按摩', selected: false } ], 4: [ // 前舱盖接口 { id: 'hood_open', name: '打开前舱盖', selected: false }, { id: 'hood_close', name: '关闭前舱盖', selected: false } ], 5: [ // 雨刮器接口 { id: 'wiper_on', name: '开启雨刮器', selected: false }, { id: 'wiper_off', name: '关闭雨刮器', selected: false }, { id: 'wiper_speed', name: '调节雨刮速度', selected: false } ], 6: [ // 光机幕布接口 { id: 'screen_up', name: '升起幕布', selected: false }, { id: 'screen_down', name: '降下幕布', selected: false }, { id: 'screen_brightness', name: '调节亮度', selected: false } ], 7: [ // 后视镜接口 { id: 'mirror_adjust', name: '调节后视镜', selected: false }, { id: 'mirror_fold', name: '折叠后视镜', selected: false }, { id: 'mirror_heat', name: '后视镜加热', selected: false } ] } }; }, mounted() { }, computed: { // 过滤后的部件列表 filteredComponents() { if (!this.componentSearch) return this.components; return this.components.filter(comp => comp.name.toLowerCase().includes(this.componentSearch.toLowerCase()) ); }, // 过滤后的接口分组 filteredInterfaceGroups() { if (!this.interfaceSearch) return this.interfaceGroups; return this.interfaceGroups.map(group => { const filteredInterfaces = group.interfaces.filter(item => item.name.toLowerCase().includes(this.interfaceSearch.toLowerCase()) || item.id.toLowerCase().includes(this.interfaceSearch.toLowerCase()) ); // 只返回包含匹配接口的分组 if (filteredInterfaces.length > 0) { return { ...group, interfaces: filteredInterfaces }; } return null; }).filter(group => group !== null); } }, methods: { // 开始拖拽 dragStart(component) { this.draggedComponent = component; }, // 放置组件 dropComponent() { if (!this.draggedComponent) return; console.log( '11',JSON.stringify(this.draggedComponent.id)); // 检查是否已存在该组件的接口组 const exists = this.interfaceGroups.some(group => group.componentId === this.draggedComponent.id ); if (exists) { this.$message.warning(`已加载过${this.draggedComponent.name}的接口`); return; } // 模拟API请求获取接口数据 this.$message.success(`正在加载${this.draggedComponent.name}接口...`); // 模拟API延迟 setTimeout(() => { const interfaces = [...this.mockInterfaces[this.draggedComponent.id]]; this.interfaceGroups.push({ id: this.nextGroupId++, componentId: this.draggedComponent.id, componentName: this.draggedComponent.name, interfaces: interfaces }); this.$message.success(`成功加载${this.draggedComponent.name}接口`); }, 500); //this.draggedComponent = null; }, // 删除接口组 removeGroup(groupId) { this.interfaceGroups = this.interfaceGroups.filter(group => group.id !== groupId); this.$message.success('接口组已移除'); } }, watch: { }, beforeDestroy() { }, async created() { }, }; </script> <style scoped lang="less"> * { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', sans-serif; background-color: #f5f7fa; overflow: hidden; } #app { height: 100%; display: flex; flex-direction: column; } /* 头部样式 */ .header { height: 60px; background: linear-gradient(135deg, #1e3c72, #2a5298); box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); display: flex; align-items: center; padding: 0 20px; color: white; z-index: 1000; } .header-left { display: flex; align-items: center; flex: 1; } .header-input { width: 280px; margin-right: 20px; background-color: rgba(255, 255, 255, 0.15); border-radius: 4px; border: none; } .header-input .el-input__inner { background-color: transparent; color: white; border: none; } .header-input .el-input__inner::placeholder { color: rgba(255, 255, 255, 0.6); } .header-right { display: flex; } .header-button { margin-left: 15px; background: linear-gradient(to right, #4facfe, #00f2fe); border: none; color: white; font-weight: bold; transition: all 0.3s; } .header-button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); } /* 主体样式 */ .main-container { flex: 1; display: flex; overflow: hidden; background-color: #f0f2f5; } /* 左侧区域 */ .left-section { flex: 0 0 40%; display: flex; position: relative; background-color: #fff; box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05); z-index: 1; } /* 中间区域 */ .center-section { flex: 0 0 40%; background-color: #fff; margin: 10px; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); display: flex; flex-direction: column; align-items: center; justify-content: center; color: #909399; font-size: 18px; position: relative; overflow: hidden; } .center-placeholder { text-align: center; } .center-placeholder i { font-size: 48px; margin-bottom: 20px; color: #c0c4cc; } /* 右侧区域 */ .right-section { flex: 0 0 20%; background-color: #fff; margin: 10px 10px 10px 0; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); display: flex; flex-direction: column; align-items: center; justify-content: center; color: #909399; font-size: 18px; } .right-placeholder { text-align: center; } .right-placeholder i { font-size: 48px; margin-bottom: 20px; color: #c0c4cc; } /* 抽屉样式 */ .drawer-container { height: 100%; display: flex; position: relative; } .drawer-wrapper { height: 100%; overflow: hidden; transition: all 0.3s; } .components-drawer { width: 280px; height: 100%; border-right: 1px solid #ebeef5; background-color: #fff; box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05); } .interfaces-drawer { width: 500px; height: 100%; background-color: #fff; border-right: 1px solid #ebeef5; } .drawer-toggle { position: absolute; top: 50%; transform: translateY(-50%); right: -15px; width: 30px; height: 60px; background-color: #1e3c72; color: white; display: flex; align-items: center; justify-content: center; border-radius: 0 15px 15px 0; cursor: pointer; z-index: 10; box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1); transition: all 0.3s; } .drawer-toggle:hover { background-color: #2a5298; } .drawer-toggle i { transition: transform 0.3s; } .drawer-toggle.collapsed i { transform: rotate(180deg); } /* 部件区域样式 */ .components-container { height: 100%; display: flex; flex-direction: column; padding: 15px; } .components-header { margin-bottom: 15px; font-weight: bold; color: #1e3c72; border-bottom: 1px solid #ebeef5; padding-bottom: 10px; font-size: 16px; } .component-item { display: flex; align-items: center; padding: 12px 15px; margin-bottom: 8px; border-radius: 4px; cursor: move; transition: all 0.2s; border: 1px solid #ebeef5; background-color: #f9fafc; } .component-item:hover { background-color: #ecf5ff; border-color: #c6e2ff; transform: translateX(5px); } .component-item .icon { width: 24px; height: 24px; margin-right: 10px; background-color: #1e3c72; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: white; font-size: 14px; } .component-item .name { font-size: 14px; color: #606266; } /* 接口选项区域 */ .interfaces-container { height: 100%; display: flex; flex-direction: column; padding: 15px; } .interfaces-header { margin-bottom: 15px; font-weight: bold; color: #1e3c72; border-bottom: 1px solid #ebeef5; padding-bottom: 10px; font-size: 16px; } .tree-container { flex: 1; overflow-y: auto; padding: 10px; border: 1px solid #ebeef5; border-radius: 4px; background-color: #f9fafc; } .tree-node { margin-bottom: 10px; border-radius: 4px; overflow: hidden; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } .tree-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; background: linear-gradient(to right, #4facfe, #00f2fe); color: white; font-weight: bold; } .delete-btn { cursor: pointer; transition: all 0.2s; } .delete-btn:hover { transform: scale(1.2); } .tree-content { padding: 10px; background-color: white; } .interface-item { display: flex; align-items: center; padding: 8px 10px; border-bottom: 1px dashed #ebeef5; } .interface-item:last-child { border-bottom: none; } .interface-name { margin-left: 8px; font-size: 14px; color: #606266; } .interface-id { margin-left: 8px; font-size: 12px; color: #909399; font-family: monospace; } /* 拖拽区域 */ .drop-area { height: 100%; display: flex; align-items: center; justify-content: center; color: #909399; font-size: 16px; flex-direction: column; } .drop-area i { font-size: 48px; margin-bottom: 15px; color: #c0c4cc; } .drop-hint { text-align: center; max-width: 300px; } /* 动画效果 */ .fade-enter-active, .fade-leave-active { transition: opacity 0.5s; } .fade-enter, .fade-leave-to { opacity: 0; } </style>
最新发布
07-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值