最近有個需求,需要輸入上車點,下車點,然后輸入上車點的時候還要在下方顯示地圖附近的車輛。百度了一波之后,完全莫得頭緒,很多代碼也都用不了,即便改了之后也不怎么生效。我用的是jeecg-boot.最后靜下心,還是老老實實的調用api接口請求數據,完成了這個需求。效果如下:
1.剛點開彈框時:
2.當輸入上車地點的時候,提示對應的地址,以及詳細地址
3.當選中了地址后,顯示地圖,並定位到選擇的位置,同時顯示附近車輛
4.選擇下車點,同理,不過下車點不會彈地圖,沒有地圖操作,單純的一個關鍵字搜索操作
下面說說具體的實現過程。前提說明,涉及到前后端代碼聯動。
前端方面:
我就直接丟代碼了:首先是我這個彈窗的代碼:
<template>
<j-modal
:title="title"
:width="1000"
:visible="visible"
:confirmLoading="confirmLoading"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel"
cancelText="關閉">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="上車點">
<a-input placeholder="請輸入上車點" @change="getAboard" v-model="aboard" />
<div id="result" class="result">
<div class="amap_lib_placeSearch_list amap-pl-pc" v-for="(item,index) in address"
@click="openMarkerTipById1(index,$event)"
@mouseout="onmouseout_MarkerStyle(index+1,$event)"
:key="index">
<div class="poibox" style="border-bottom: 1px solid #eaeaea">
<div class="amap_lib_placeSearch_poi poibox-icon" :class="index==selectedIndex?'selected':''">{{index+1}}</div>
<h3 class="poi-title" >
<span class="poi-name">{{item.name}}</span>
</h3>
<div class="poi-info">
<p class="poi-addr">地址:{{item.pname}}{{item.cityname}}{{item.adname}}{{item.address}}</p>
</div>
<div class="clear"></div>
</div>
</div>
</div>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="下車點">
<a-input placeholder="請輸入下車點" @change="getDeBus" v-model="debus" />
<div id="tips" class="result">
<div class="amap_lib_placeSearch_list amap-pl-pc" v-for="(item,index) in deBusAddress"
@click="openMarkerTipById2(index,$event)"
@mouseout="onmouseout_MarkerStyle(index+1,$event)"
:key="index">
<div class="poibox" style="border-bottom: 1px solid #eaeaea">
<div class="amap_lib_placeSearch_poi poibox-icon" :class="index==selectedIndex?'selected':''">{{index+1}}</div>
<h3 class="poi-title" >
<span class="poi-name">{{item.name}}</span>
</h3>
<div class="poi-info">
<p class="poi-addr">地址:{{item.pname}}{{item.cityname}}{{item.adname}}{{item.address}}</p>
</div>
<div class="clear"></div>
</div>
</div>
</div>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="乘客手機號">
<a-input placeholder="請輸入乘客手機號" v-decorator="['farePhone', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="乘客名稱">
<a-input placeholder="請輸入乘客名稱" v-decorator="['fareName', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="用車時間">
<a-date-picker showTime format='YYYY-MM-DD HH:mm:ss' v-decorator="[ 'forwardTime', {}]" />
</a-form-item>
<div v-if="showMap===1">
<AMapTemplate></AMapTemplate>
</div>
</a-form>
</a-spin>
</j-modal>
</template>
<script>
import { httpAction, getAction } from '@/api/manage'
import pick from 'lodash.pick'
import moment from "moment"
import AMapTemplate from '@/components/AMap/AMapTemplate'
import Utils from '@/components/_util/utils'
export default {
name: "OrderModal",
components: {
AMapTemplate
},
data () {
return {
title:"操作",
visible: false,
aboard: '',
debus: '',
showMap: 0,
record: {},
upGnote: '',
downGnote: '',
address: [],
deBusAddress: [],
selectedIndex: -1,
map:'',//保存地址的經緯度
poiArr: [],//左邊搜索出來的數組
windowsArr: [],//信息窗口的數組
marker: [],
mapObj: "",//地圖對象
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules:{
},
url: {
add: "/order/order/add",
edit: "/order/order/edit",
},
}
},
created () {
},
methods: {
openMarkerTipById1(pointid, thiss) {
this.aboard = this.address[pointid].name;
this.upGnote = this.address[pointid].location;
localStorage.setItem("aboard", this.upGnote);
Utils.$emit('mapAboard');
this.showMap = 1;
//根據id 打開搜索結果點tip
thiss.currentTarget.style.background = "#CAE1FF";
this.selectedIndex = pointid
this.map = this.marker[pointid]
window.removeEventListener("click", this.demo);
// this.windowsArr[pointid].open(this.mapObj, this.marker[pointid])
window.addEventListener("click", this.demo);
this.address = [];
document.getElementById("result").classList.remove("amap_lib_placeSearch");
},
openMarkerTipById2(pointid, thiss) {
this.debus = this.deBusAddress[pointid].name;
this.downGnote = this.deBusAddress[pointid].location;
//根據id 打開搜索結果點tip
thiss.currentTarget.style.background = "#CAE1FF";
this.selectedIndex = pointid
this.map = this.marker[pointid]
window.removeEventListener("click", this.demo);
// this.windowsArr[pointid].open(this.mapObj, this.marker[pointid])
window.addEventListener("click", this.demo);
this.deBusAddress = [];
document.getElementById("tips").classList.remove("amap_lib_placeSearch");
},
onmouseout_MarkerStyle(pointid, thiss) {
//鼠標移開后點樣式恢復
thiss.currentTarget.style.background = ""
},
close () {
this.$emit('close');
this.visible = false;
},
handleOk () {
const that = this;
// 觸發表單驗證
this.form.validateFields((err, values) => {
if (!err) {
that.confirmLoading = true;
let formData = Object.assign(this.model, values);
formData.aboard = this.aboard;
formData.debus = this.debus;
formData.id = this.record.id;
formData.upGnote = this.upGnote;
formData.downGnote = this.downGnote;
//時間格式化
formData.forwardTime = formData.forwardTime?formData.forwardTime.format('YYYY-MM-DD HH:mm:ss'):null;
console.log(formData)
httpAction('/order/order/sendOrder',formData,'post').then((res)=>{
if(res.success){
that.$message.success(res.message);
that.$emit('ok');
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
that.close();
})
}
})
},
handleCancel () {
this.close()
},
getAboard() {
if (!this.aboard) {
document.getElementById("result").classList.remove("amap_lib_placeSearch");
}else {
document.getElementById("result").classList.add("amap_lib_placeSearch");
}
let parameter = {keyword: this.aboard}
getAction('/order/order/getAddressItem',parameter, 'get').then((res)=>{
if(res.success){
this.address = res.result;
}else{
this.$message.warning(res.message);
}
})
},
getDeBus() {
if (!this.debus) {
document.getElementById("tips").classList.remove("amap_lib_placeSearch");
}else {
document.getElementById("tips").classList.add("amap_lib_placeSearch");
}
let parameter = {keyword: this.debus}
getAction('/order/order/getAddressItem',parameter, 'get').then((res)=>{
if(res.success){
this.deBusAddress = res.result;
}else{
this.$message.warning(res.message);
}
})
}
}
}
</script>
<style lang="less" scoped>
.amap_lib_placeSearch {
width: 100%;
position: relative;
height: 300px;
overflow-y: scroll;
border-right: 1px solid #ccc;
}
.amap_lib_placeSearch_page {
position: absolute;
bottom: 0;
width: 100%;
}
#me {
border-top: 1px solid #ccc;
margin-top: 6px;
padding-top: 6px;
width: 100%;
display: block;
}
.amap_lib_placeSearch .poibox {
border-bottom: 1px solid #eaeaea;
cursor: pointer;
padding: 5px 0 5px 10px;
position: relative;
min-height: 35px;
}
.amap_lib_placeSearch_poi {
background: url(https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png)
no-repeat;
height: 31px;
width: 19px;
cursor: pointer;
left: -1px;
text-align: center;
color: #fff;
font: 12px arial, simsun, sans-serif;
padding-top: 3px;
}
.amap_lib_placeSearch .poibox .poi-title {
margin-left: 25px;
font-size: 13px;
overflow: hidden;
}
.amap_lib_placeSearch .amap_lib_placeSearch_poi {
position: absolute;
}
.amap_lib_placeSearch .poibox .poi-info {
word-break: break-all;
margin: 0 0 0 25px;
overflow: hidden;
}
.amap_lib_placeSearch .poibox .poi-info p {
color: #999;
font-family: Tahoma;
line-height: 20px;
font-size: 12px;
}
.amap_lib_placeSearch .poibox .poibox-icon {
margin-left: 7px;
margin-top: 4px;
}
.amap-pl-pc .poi-img {
float: right;
margin: 3px 8px 0;
width: 90px;
height: 56px;
overflow: hidden;
}
.poibox {
cursor: pointer;
}
.poibox:hover {
background: #f6f6f6;
}
.selected {
background-image: url(https://webapi.amap.com/theme/v1.3/markers/n/mark_r.png) !important;
}
.amap-info-content {
width: 200px !important;
}
</style>
里邊涉及到兩個文件引入,一個是AmapTemplate,一個是utils,這兩個引入都是用來顯示地圖的,如果只是想要搜索關鍵字,不用管這兩個文件
AmapTemplate.vue按照上邊導入的路徑存放就好
<template lang="html">
<div style="width:100%;height:400px;">
<div class="container">
<!-- <div class="search-box">-->
<!-- -->
<!-- <a-input-->
<!-- v-model="searchKey"-->
<!-- type="search"-->
<!-- id="search">-->
<!-- </a-input>-->
<!-- <a-button @click="searchByHand">搜索</a-button>-->
<!-- <div class="tip-box" id="searchTip"></div>-->
<!-- -->
<!-- </div>-->
<!--
amap-manager: 地圖管理對象
vid:地圖容器節點的ID
zooms: 地圖顯示的縮放級別范圍,在PC上,默認范圍[3,18],取值范圍[3-18];在移動設備上,默認范圍[3-19],取值范圍[3-19]
center: 地圖中心點坐標值
plugin:地圖使用的插件
events: 事件
-->
<el-amap class="amap-box"
:amap-manager="amapManager"
:vid="'amap-vue'"
:zoom="zoom"
:plugin="plugin"
:center="center"
:events="events"
>
<!-- 標記 -->
<el-amap-marker v-for="(marker, index) in carmarkers" :key="index" :position="marker.position" :vid="index" :content="marker.content" :events="marker.events"></el-amap-marker>
<el-amap-info-window
:position="currentWindow.position"
:content="currentWindow.content"
:visible="currentWindow.visible"
:events="currentWindow.events">
</el-amap-info-window>
</el-amap>
</div>
</div>
</template>
<script>
import { AMapManager, lazyAMapApiLoaderInstance } from 'vue-amap'
import { httpAction } from '@/api/manage'
import Utils from '@/components/_util/utils'
let amapManager = new AMapManager()
export default {
name:'AMapTemplate',
data() {
let self = this
return {
carmarkers :[],
title: '',
visible: false,
confirmLoading: false,
dictItem: '',
aboard: '',
userType: '',
carIds: '',
currentWindow: {
position: '',
content: '111',
events: {},
visible: false
},
carAddrs: [],
address: null,
searchKey: '',
amapManager,
markers: [],
searchOption: {
city: '全國',
citylimit: true
},
center: '',
zoom: 17,
lng: 0,
lat: 0,
loaded: false,
events: {
init() {
lazyAMapApiLoaderInstance.load().then(() => {
self.initSearch()
})
},
// 點擊獲取地址的數據
click(e) {
self.markers = []
let { lng, lat } = e.lnglat
self.lng = lng
self.lat = lat
self.center = [lng, lat]
self.markers.push([lng, lat])
// 這里通過高德 SDK 完成。
let geocoder = new AMap.Geocoder({
radius: 1000,
extensions: 'all'
})
console.log(lng+","+lat) //控制台打印坐標
geocoder.getAddress([lng, lat], function(status, result) {
if (status === 'complete' && result.info === 'OK') {
if (result && result.regeocode) {
console.log(result.regeocode.formattedAddress) //控制台打印地址
self.address = result.regeocode.formattedAddress
self.searchKey = result.regeocode.formattedAddress
self.$nextTick()
}
}
})
}
},
// 一些工具插件
plugin: [
{
pName: 'Geocoder',
events: {
init (o) {
//console.log("一些工具插件--地址"+o.getAddress())
}
}
},
{
// 定位
pName: 'Geolocation',
events: {
init(o) {
// o是高德地圖定位插件實例
o.getCurrentPosition((status, result) => {
if (result && result.position) {
// 設置經度
self.lng = result.position.lng
// 設置維度
self.lat = result.position.lat
// 設置坐標
self.center = [self.lng, self.lat]
self.markers.push([self.lng, self.lat])
// load
self.loaded = true
// 頁面渲染好后
self.$nextTick()
}
})
}
}
},
{
// 工具欄
pName: 'ToolBar',
events: {
init(instance) {
//console.log("工具欄:"+instance);
}
}
},
{
// 鷹眼
pName: 'OverView',
events: {
init(instance) {
//console.log("鷹眼:"+instance);
}
}
},
{
// 地圖類型
pName: 'MapType',
defaultType: 0,
events: {
init(instance) {
//console.log("地圖類型:"+instance);
}
}
},
{
// 搜索
pName: 'PlaceSearch',
events: {
init(instance) {
//console.log("搜索:"+instance)
}
}
}
]
}
},
created() {
var aboard = localStorage.getItem("aboard");
var arr = aboard.split(",")
this.aboard = arr.map(Number);
this.center = this.aboard;
this.currentWindow.position = this.aboard;
this.dictItem = "sl_car_info, car_no, id, delete_status=1";
this.getCarAddress();
},
mounted(){
var that = this;
Utils.$on('mapAboard', function () {
that.getMapAboard();
})
},
methods: {
getMapAboard() {
var aboard = localStorage.getItem("aboard");
var arr = aboard.split(",")
this.aboard = arr.map(Number);
console.log("aboard", this.aboard);
this.center = this.aboard;
this.currentWindow.position = this.aboard;
this.getCarAddress();
},
handleCancel () {
this.close()
},
close () {
this.$emit('close');
this.visible = false;
},
handleOk () {
console.log(this.searchKey);
this.close();
},
initSearch() {
let vm = this
let map = this.amapManager.getMap()
AMapUI.loadUI(['misc/PoiPicker'], function(PoiPicker) {
var poiPicker = new PoiPicker({
input: 'search',
placeSearchOptions: {
map: map,
pageSize: 10
},
suggestContainer: 'searchTip',
searchResultsContainer: 'searchTip'
})
vm.poiPicker = poiPicker
// 監聽poi選中信息
poiPicker.on('poiPicked', function(poiResult) {
// console.log(poiResult)
let source = poiResult.source
let poi = poiResult.item
if (source !== 'search') {
poiPicker.searchByKeyword(poi.name)
} else {
poiPicker.clearSearchResults()
vm.markers = []
let lng = poi.location.lng
let lat = poi.location.lat
let address = poi.cityname + poi.adname + poi.name
vm.center = [lng, lat]
vm.markers.push([lng, lat])
vm.lng = lng
vm.lat = lat
vm.address = address
vm.searchKey = address
}
})
})
},
searchByHand() {
if (this.searchKey !== '') {
this.poiPicker.searchByKeyword(this.searchKey)
}
},
onChangeCar(){
//根據id查詢對應車輛信息
},
getCarAddress() {
this.carmarkers = [];
let formData = {};
formData.location=this.aboard;
formData.maxArea = 1000;
httpAction('/car/carInfo/getCarAddress',formData,'post').then((res)=>{
if(res.success){
this.carAddrs = res.result;
this.positionByHand(this.carAddrs);
}else{
this.$message.warning(res.message);
}
})
},
positionByHand(carAddrs) {
this.currentWindow = {
position: this.aboard,
events: {
close: (e) => {
this.currentWindow.visible = false
}
},
visible: true }
this.carmarkers.push({
position:this.aboard,
content: `<div><img style="width: 40px; height: 40px" src=${window._CONFIG['domianURL']}/img/address.png/></div>`,
events: {
click: (e) => {
this.currentWindow = {
position: this.aboard,
content: `<div style="color: #1c84c6;">起點</div>`,
events: {
close: (e) => {
this.currentWindow.visible = false
}
},
visible: true }
}
}
})
carAddrs.forEach((item,index)=> {
console.log(item);
this.carmarkers.push({
position:item.location,
content: `<div><img style="width: 40px; height: 40px" src="http://localhost:8099/sltx/upload/temp/car.png"/></div>`,
events: {
click: (e) => {
this.currentWindow = {
position: item.location,
content: `<div style="color: #1c84c6;">`+item.carNo+`</div>`,
events: {
close: (e) => {
this.currentWindow.visible = false
}
},
visible: true }
}
}
})
})
}
}
}
</script>
<style lang="css">
.container {
width: 100%;
height: 100%;
position: relative;
left: 50%;
top: 50%;
transform: translate3d(-50%, -50%, 0);
border: 1px solid #999;
}
.search-box {
position: absolute;
z-index: 5;
width: 30%;
left: 13%;
top: 10px;
height: 30px;
}
.search-box input {
float: left;
width: 80%;
height: 100%;
border: 1px solid #30ccc1;
padding: 0 8px;
outline: none;
}
.search-box button {
float: left;
width: 20%;
height: 100%;
background: #30ccc1;
border: 1px solid #30ccc1;
color: #fff;
outline: none;
}
.tip-box {
width: 100%;
max-height: 260px;
position: absolute;
top: 30px;
overflow-y: auto;
background-color: #fff;
}
</style>
utils.js
import Vue from 'vue'
export default new Vue
對,沒錯,只有這兩行。
剩下的就是后台java的操作了,相關代碼如下:
controller:
在對應的controller添加這么一個接口
/**
* 根據關鍵字查詢地理信息
*
* @param keyword
* @return
*/
@AutoLog(value = "訂單管理-根據關鍵字查詢地理信息")
@ApiOperation(value="訂單管理-根據關鍵字查詢地理信息", notes="訂單管理-根據關鍵字查詢地理信息")
@GetMapping(value = "/getAddressItem")
public Result<?> getAddressItem(@RequestParam(name="keyword",required=true) String keyword) {
List<AddressVo> addressVoList = orderService.getAddressItem(keyword);
return Result.ok(addressVoList);
}
新建AddressVo類
import lombok.Data;
@Data
public class AddressVo {
private String name;
private String pname;
private String cityname;
private String adname;
private String address;
private String location;
}
然后是對應的service實現,我寫的比較糟糕,你們用的時候可以適當改進一下:
@Override
public List<AddressVo> getAddressItem(String keyword) {
String parameter = "key="+key+"&"+"keywords="+keyword+"&offset=15&city=廣州";
String result = HttpRequest.sendGet("https://restapi.amap.com/v3/place/text", parameter);
Map<String, Object> map = new HashMap<String, Object>();
Gson gson = new Gson();
map = gson.fromJson(result, map.getClass());
Object pois = map.get("pois");
List<AddressVo> list = JSON.parseArray(JSON.toJSONString(pois), AddressVo.class);
return list;
}
沒有的依賴可以引入對應的pom依賴。
另外涉及到一個請求發送的工具包
HttpRequest.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;
/**
* 網絡請求(GET和POST)
* @編寫時間 2018年8月14日 上午9:36:57
* @文件名 HttpRequest.java
* @類名 HttpRequest
*/
public class HttpRequest {
/**
* 向指定URL發送GET方法的請求
*
* @param url 發送請求的URL
* @param param 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
* @return URL 所代表遠程資源的響應結果
*/
public static String sendGet(String url, String param) {
String result = "";
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
URL realUrl = new URL(urlNameString);
// 打開和URL之間的連接
URLConnection connection = realUrl.openConnection();
// 設置通用的請求屬性
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 建立實際的連接
connection.connect();
// 獲取所有響應頭字段
Map<String, List<String>> map = connection.getHeaderFields();
// 遍歷所有的響應頭字段
for (String key : map.keySet()) {
// System.out.println(key + "--->" + map.get(key));
}
// 定義 BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("發送GET請求出現異常!" + e);
e.printStackTrace();
}
// 使用finally塊來關閉輸入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
/**
* 向指定 URL 發送POST方法的請求
*
* @param url 發送請求的 URL
* @param param 請求參數,請求參數應該是 name1=value1&name2=value2 的形式。
* @return 所代表遠程資源的響應結果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打開和URL之間的連接
URLConnection conn = realUrl.openConnection();
// 設置通用的請求屬性
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
// 發送POST請求必須設置如下兩行
conn.setDoOutput(true);
conn.setDoInput(true);
// 獲取URLConnection對象對應的輸出流
out = new PrintWriter(conn.getOutputStream());
// 發送請求參數
out.print(param);
// flush輸出流的緩沖
out.flush();
// 定義BufferedReader輸入流來讀取URL的響應
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
System.out.println("發送 POST 請求出現異常!" + e);
e.printStackTrace();
}
//使用finally塊來關閉輸出流、輸入流
finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}
大致就是這些了,運行有問題的也可以底下評論,畢竟我知道被逼瘋的痛苦。不過最好是一樣使用jeecg-boot這一套的,比較熟悉