写的一个基于之前的门店功能,通过wordpress的REST API 获取后门店数据可以直接在前台渲染成门店地图,最终效果如下图所示。
注,基于其他主题下开发,样式适配了其他主题,根据实际可能需要调整。
之前在wordpress里创建了门店类型的自定义Post Type,https://www.zkcoi.com/365up/program/2342.html

一PHP的部分
包括获取省市区的列表,门店列表等
ssq.txt保存的是省市区的json数据,有需要的可以下载
省市区的json数据文件:ssq.txt
//注册获取省市区的REST API
add_action('rest_api_init', function() {
register_rest_route('wp/v1', '/ssq/', array(
'methods' => 'GET',
'callback' => 'getSSQ',
));
});
// 添加跨域请求头部
add_action('init', function() {
header('Access-Control-Allow-Origin: *.zkcoi.com');
});
// 获取 ssq 数据的回调函数
function getSSQ() {
$cache_key = 'ssq_data'; // 缓存键名
$cache_time = WEEK_IN_SECONDS; // 缓存时间(1周)
// 尝试从 wp_cache 中读取缓存数据
$cached_data = wp_cache_get($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
$cacheUrl = __DIR__."/html/ssq.txt";
$content = @file_get_contents($cacheUrl);
// 将获取到的数据转换为 JSON 数组
$json_array = json_decode($content, true);
// 将获取到的数据保存到缓存中,缓存时间为 $cache_time
wp_cache_set($cache_key, $json_array, null, $cache_time);
return $json_array;
}
// 获取门店数组的回调函数
function get_store_addresses_by_location_rest($data) {
$cache_key = 'store_addresses_' . md5(serialize($data)); // 缓存键名
$cache_time = DAY_IN_SECONDS; // 缓存时间(1天)
$result = wp_cache_get($cache_key);
if ($result) {
return rest_ensure_response($result);
}
$args = array(
'post_type' => 'store',
'posts_per_page' => -1,
'meta_key' => 'store_address',
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'store_address',
'compare' => 'EXISTS'
),
array(
'key' => 'store_address',
// 'value' => '北京', // 默认为北京
'compare' => 'LIKE'
)
)
);
// 省份
if (!empty($data['sheng'])) {
$args['meta_query'][1]['value'] = $data['sheng'];
}
// 城市
if (!empty($data['shi'])) {
$args['meta_query'][] = array(
'key' => 'store_address',
'value' => $data['shi'],
'compare' => 'LIKE'
);
}
// 区县
if (!empty($data['qu'])) {
$args['meta_query'][] = array(
'key' => 'store_address',
'value' => $data['qu'],
'compare' => 'LIKE'
);
}
$query = new WP_Query($args);
$posts = $query->posts;
$stores = array_map(function ($post) {
return [
'name' => $post->post_title,
'address' => get_post_meta($post->ID, 'store_address', true),
'coordinate' => [
'longitude' => floatval(explode(',', get_post_meta($post->ID, 'store_coordinate', true))[0]),
'latitude' => floatval(explode(',', get_post_meta($post->ID, 'store_coordinate', true))[1]),
],
'content' => $post->post_content,
'phoneNum' => get_post_meta($post->ID, 'store_phone', true) ?: "4008001650",
'imgUrl' => get_the_post_thumbnail_url($post->ID, 'medium'),
];
}, $posts);
wp_reset_postdata();
// 将数组转换为 JSON 格式并返回 REST API 响应对象
$result = rest_ensure_response($stores);
// 设置响应头,指定返回的数据类型为 JSON
$result->set_headers(array('Content-Type' => 'application/json;charset=UTF-8'));
// 将查询结果缓存
wp_cache_set($cache_key, $stores, '', $cache_time);
return $result;
}
//注册获取门店的REST API
add_action('rest_api_init', function() {
register_rest_route('wp/v1', '/store', array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'get_store_addresses_by_location_rest',
));
});
二、js部分
jQuery(document).ready(function($) {
$(function () {
var amap = new AMap.Map("map", {
zoom: 11
});
var list = []; // 门店列表
var dqs = {}; // 地区筛选条件
var ajax_getSSQ = '/wp-json/wp/v1/ssq/';
// 获取门店列表
function getList() {
var sheng = $('#province option:selected').text(); // 获取所选省份的文本内容
var shi = $('#city option:selected').text(); // 获取所选城市的文本内容
var qu = $('#district option:selected').text(); // 获取所选区县的文本内容
var url = '/wp-json/wp/v1/store?'; // 初始化url时只传递省份和区县参数
if (sheng !== "请选择" && sheng !== "市辖区") { // 当所选省份不为“请选择”或“市辖区”时,将省份参数拼接到url中
url += 'sheng=' + encodeURIComponent(sheng);
}
if (shi !== "请选择" && shi !== "市辖区") { // 当所选城市不为“请选择”或“市辖区”时,将城市参数拼接到url中
url += '&shi=' + encodeURIComponent(shi);
}
if (qu !== "请选择" && qu !== "市辖区") { // 当所选区县不为“请选择”或“市辖区”时,将区县参数拼接到url中
url += '&qu=' + encodeURIComponent(qu);
}
$.ajax({
url: url,
method: 'GET',
dataType: 'json',
success: function(response) {
// 将返回的 JSON 字符串转换为 JSON 对象
var data = response;
// 点击门店列表项时,更新地图中心点和标记
$("#stores").on("click", ".store-item", function () {
var index = $(this).index(".store-item");
var poi = new AMap.LngLat(data[index].coordinate.longitude, data[index].coordinate.latitude);
amap.setZoomAndCenter(14, poi);
});
if (data.length > 0) {
var stores = $("#stores");
stores.empty(); // 先清空门店列表
// 添加门店列表项
$.each(data, function (i) {
var store = this;
if(store.coordinate.latitude && store.coordinate.longitude){ // 判断门店坐标是否存在
var poi = new AMap.LngLat(store.coordinate.longitude, store.coordinate.latitude);
var html = '<div class="store-item">' +
'<div class="store-b hand pad8' + (i == 0 ? ' active' : '') + '">' +
'<div class="store-b1 fl">' + (i + 1) + '</div>' +
'<div class="store-b2 overhide">' +
'<div class="store-b21 fe11">' + store.name + '</div>' +
'<div class="store-b22 co2 f13">地址:' + store.address + '</div>' +
'<div class="store-b23 " title="门店电话">电话:'+store.phoneNum+'</div>' + // 添加 info push 图标
'</div>' +
'</div>' +
'</div>';
stores.append(html); //依次添加到HTML中
// 添加门店地图标记
if (i == 0) {
amap.setZoomAndCenter(14, [store.coordinate.longitude, store.coordinate.latitude]);
addMarker(poi, store.name, store.address);
}
else{
addMarker(poi, store.name, store.address);
}
// 点击 info push 图标显示信息窗口
$(".store-item").eq(i).on("click", function () {
var infoContent = '<div class="info-window-content">' +
'<div class="info-logo"><img src="[填写logourl]" alt="sitename"></div>'+
'<div class="info-title">' + store.name + '</div>' +
'<div class="info-address">' + store.address + '</div>' +
'<div class="info-content"><a href="tel:'+store.phoneNum+'">' + store.content + '</a></div>' +
'<a class="btn btn-lg btn-primary btn-0" href="https://uri.amap.com/marker?position='+store.coordinate.longitude+','+store.coordinate.latitude+'&name=xx渠道门店-'+ store.name +'&src=www.zkcoi.com&callnative=1" target="_blank" rel="nofollow">点击查看路线</a>'
'</div>';
var infoWindow = new AMap.InfoWindow({
content: infoContent, // 信息窗口内容
offset: new AMap.Pixel(0, -30), // 信息窗口偏移量
autoMove: true // 开启信息窗口自动调整位置
});
infoWindow.open(amap, poi);
infoWindow.setSize(new AMap.Size(400, 0));
});
}
});
} else {
// 显示暂无门店信息
$("#stores").html('<div class="store-item"><div class="store-b hand pad8 active">' +
'<div class="store-b1 fl">1</div>' +
'<div class="store-b2 overhide">' +
'<div class="store-b21 fe11">暂无门店信息</div>' +
'<div class="store-b22 co2 f13">地址:暂无门店地址</div>' +
'</div></div></div>');
}
},
error: function(jqXHR, textStatus, errorThrown) {
alert(textStatus + ': ' + errorThrown);
}
});
}
// 初始化地区筛选条件
function initDq() {
$.get(ajax_getSSQ, function (res) {
var list = res;
var provinceSel = $("#province");
provinceSel.empty(); // 先清空省份下拉框
provinceSel.append('<option value="">请选择</option>');
// 添加省份选项
$.each(list, function (i, v) {
provinceSel.append('<option value="' + i + '">' + v.name + '</option>');
});
// 省份选项改变事件
provinceSel.change(function () {
$("#city option:first,#district option:first").prop("selected", 'selected');
var idx = this.value;
if (idx != "") {
var citySel = $("#city");
var districtSel = $("#district");
var cityList = list[idx].children;
citySel.empty(); // 先清空城市下拉框
citySel.append('<option value="">请选择</option>');
$.each(cityList, function (i, v) {
citySel.append('<option value="' + i + '">' + v.name + '</option>');
});
dqs.province = list[idx].name;
dqs.city = "";
dqs.district = "";
getList();
} else {
dqs = {};
getList();
}
});
// 城市选项改变事件
$("#city").change(function () {
$("#district option:first").prop("selected", 'selected');
var idx = this.value;
if (idx != "") {
var districtSel = $("#district");
var districtList = list[$("#province")[0].value].children[idx].children;
districtSel.empty(); // 先清空区县下拉框
districtSel.append('<option value="">请选择</option>');
$.each(districtList, function (i, v) {
districtSel.append('<option value="' + i + '">' + v.name + '</option>');
});
if (list[$("#province")[0].value].children[idx].name.indexOf("市辖区") > -1) {
dqs.city = "";
} else {
dqs.city = list[$("#province")[0].value].children[idx].name;
}
dqs.district = "";
getList();
} else {
dqs.city = "";
dqs.district = "";
getList();
}
});
// 区县选项改变事件
$("#district").change(function () {
var idx = this.value;
if (idx != "") {
dqs.district = list[$("#province")[0].value].children[$("#city")[0].value].children[idx].name;
getList();
} else {
dqs.district = "";
getList();
}
});
});
}
// 添加地图标记
function addMarker(position, title, content) {
// 创建信息窗口内容,这里以门店名称和地址为例
var infoContent = '<div class="info-window-content">' +
'<div class="info-logo"><img src="[填写logourl]" alt="sitename"></div>'+
'<h3 class="info-title">' + title + '</h3>' +
'<p class="info-address">' + content + '</p>' +
'</div>';
// 创建一个 marker 并将它添加到地图上
var marker = new AMap.Marker({
position: position,
title: title
});
amap.add(marker);
// 点击 marker 显示信息窗口
AMap.event.addListener(marker, 'click', function () {
var infoWindow = new AMap.InfoWindow({
content: infoContent, // 信息窗口内容
offset: new AMap.Pixel(0, -30), // 信息窗口偏移量
autoMove: true // 开启信息窗口自动调整位置
});
infoWindow.open(amap, marker.getPosition());
});
}
// 初始化地区筛选条件
initDq();
});
});
三、html部分
前端调用方法很多,比如新建页面在模板根目录放在page-[page slug].php中,或者使用get_template_part()等等皆可。
<div class="container">
<div class="map-container">
<div class="store-positions">
<div class="form-group">
<label for="province">省份</label>
<select class="form-control" id="province">
<option value="">请选择</option>
</select>
</div>
<div class="form-group">
<label for="city">城市</label>
<select class="form-control" id="city">
<option value="">请选择</option>
</select>
</div>
<div class="form-group">
<label for="district">区县</label>
<select class="form-control" id="district">
<option value="">请选择</option>
</select>
</div>
</div>
<div class="store-wrapper">
<div class="store-map" id="map"></div>
<div class="store-list" id="stores">
<div class="store-item active">
<div class="store-b">
<div class="store-b1">1</div>
<div class="store-b2">
<div class="store-b21 fe11">请选择地区获取门店信息</div>
<div class="store-b22 co2 f13">地址:请选择地区获取门店地址</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://webapi.amap.com/maps?v=1.4.15&key=高德地图api key"></script>
四、css部分
.map-container {
width: 100%;
margin: 0 auto;
flex-direction: column;
align-items: center;
padding: 44px 0;
}
.store-wrapper {
display: flex;
width: 100%;
padding: 14px;
background-color:#f9f9f9;
}
.store-list {
height: auto;
background: #f9f9f9;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) inset;
padding: 14px;
box-sizing: border-box;
display: block;
word-wrap: normal;
margin-top: 20px;
}
.store-item {
position: relative;
align-items: center;
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease-in-out;
}
.store-b {
display: flex;
justify-content: center;
align-items: center;
height: auto;
}
.store-b1 {
display: flex;
justify-content: center;
align-items: center;
width: 32px;
height: 32px;
font-size: 18px;
font-weight: bold;
color: #fff;
background-color: #ff9800;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease-in-out;
}
.store-b1:hover {
background-color: #f00;
transform: scale(1.2);
}
.store-b2 {
display: flex;
flex-direction: column;
flex-grow: 1;
width: auto;
height: auto;
padding-left: 12px;
box-sizing: border-box;
transition: all 0.3s ease-in-out;
text-align: left;
}
.store-b21 {
font-size: 17px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
word-break: break-word;
}
.store-b22 ,.store-b23{
font-size: 14px;
font-weight: 300;
color: #666;
margin: 0;
word-break: break-word;
}
.info-content {
font-size: 16px;
font-weight: bold;
color: var(--theme-color);
}
.store-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
.store-item:hover .store-b1 {
background-color: #555;
}
.store-item.active .store-b1 {
background-color: #555;
}
@media (min-width: 768px) and (max-width: 991px) {
.map-container {
padding: 60px 0;
}
.store-positions {
justify-content: center;
margin: 0px 44px;
}
.store-wrapper{
display: block;
}
.store-map {
width: 100%;
max-width: 900px;
height: calc(44vw);
border: 1px solid #ff9800;
}
.store-list{
width: 100%;
margin: 0px 4px;
}
.store-b21 {
font-size: 16px;
}
.store-b22 {
font-size: 12px;
}
}
@media (min-width: 992px) and (max-width: 1239px) {
.map-container {
padding: 60px 0;
}
.store-positions {
justify-content: center;
margin: 0px 44px;
}
.store-wrapper{
justify-content: center;
}
.store-list{
width: 100%;
margin: 0px 44px;
}
.store-b21 {
font-size: 16px;
}
.store-b22 {
font-size: 12px;
}
}
@media (min-width: 1240px) {
.map-container {
padding: 44px 0;
display: flex;
}
.store-list {
max-width: 100%;
width: 100%;
margin: 0px;
}
.map-item {
margin-right: 20px;
}
.store-positions {
display: flex;
width: 100%;
justify-content: center;
}
.form-group {
display: inline-block;
width: 100%;
max-width: 300px;
margin: 10px;
}
label {
display: block;
font-size: 16px;
font-weight: bold;
margin-bottom: 10px;
}
select {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
background-color: #fff;
color: #555;
}
.store-map {
width: 100%;
max-width: 900px;
height: calc(24vw);
border: 1px solid #ff9800;
}
.info-window-content {
display: flex;
flex-direction: column;
justify-content: left;
align-items: flex-start;
font-family: 'Microsoft YaHei', sans-serif;
font-size: 16px;
line-height: 1.8;
position: relative;
padding: 0;
width: calc(100% - 2rem);
max-width: 380px;
}
.info-logo {
width: 67px;
height: 16px;
margin-right: 1rem;
margin-bottom: 0.75rem;
}
.info-title {
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 0.5rem;
}
.info-address {
margin-bottom: 0.5rem;
font-size: 14px;
font-weight: 300;
}
.info-phone {
margin-bottom: 0.5rem;
font-size: 14px;
font-weight: 300;
}
.info-content {
font-size: 14px;
font-weight: 400;
margin-bottom: 0.5rem;
text-align: left;
}
@media (max-width: 767px){
.info-window-content {
align-items: center;
text-align: center;
}
.info-logo {
margin-right: 0;
}
}
}
Vue版本
<?php
/*
Template Name: 门店地图Vue
*/
global $options; get_header("noscale");?>
<link rel="stylesheet" id="zke-stores-style-css" href="<?php echo ZKE_THEME_URI;?>/css/stores.css" type="text/css" media="all">
<script src="https://webapi.amap.com/maps?v=1.4.15&key=58fa9a9f71ea1c7d601a582006c0fad6"></script>
<script id="zke-stores-vue" type="text/javascript" src="<?php echo ZKE_THEME_URI;?>/js/vue.min.js" ></script>
<div class="container">
<div id="app">
<div class="map-container">
<div class="store-positions">
<div class="form-group">
<label for="province">省份</label>
<select class="form-control" v-model="selectedProvince" @change="getCityList">
<option value="">请选择</option>
<option v-for="(province, index) in provinces" :value="index" :key="index">{{ province.name }}</option>
</select>
</div>
<div class="form-group">
<label for="city">城市</label>
<select class="form-control" v-model="selectedCity" @change="getDistrictList">
<option value="">请选择</option>
<option v-for="(city, index) in cities" :value="index" :key="index">{{ city.name }}</option>
</select>
</div>
<div class="form-group">
<label for="district">区县</label>
<select class="form-control" v-model="selectedDistrict" @change="getList">
<option value="">请选择</option>
<option v-for="(district, index) in districts" :value="index" :key="index">{{ district.name }}</option>
</select>
</div>
</div>
<div class="store-wrapper">
<div class="store-map" id="map"></div>
<div class="store-list" id="stores">
<div class="store-item" v-for="(store, index) in storeList" :key="index" @click="showInfoWindow(index)">
<div class="store-b">
<div class="store-b1">{{ index + 1 }}</div>
<div class="store-b2">
<div class="store-b21 fe11">{{ store.name }}</div>
<div class="store-b22 co2 f13">地址:{{ store.address }}</div>
<div class="store-b23" title="门店电话">电话:{{ store.phoneNum }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
selectedProvince: "",
selectedCity: "",
selectedDistrict: "",
provinces: [],
cities: [],
districts: [],
storeList: [],
amap: null
};
},
mounted() {
this.amap = new AMap.Map("map", {
zoom: 11
});
this.initDq();
},
methods: {
initDq() {
fetch("https://www.zkcoi.com/wp-json/zke/v1/ssq/")
.then(response => response.json())
.then(data => {
this.provinces = data;
this.getList();
})
.catch(error => console.error("Error fetching provinces:", error));
},
getCityList() {
const selectedProvinceData = this.provinces[this.selectedProvince];
this.cities = selectedProvinceData ? selectedProvinceData.children : [];
this.districts = [];
this.storeList = [];
this.getList();
},
getDistrictList() {
const selectedCityData = this.provinces[this.selectedProvince].children[this.selectedCity];
this.districts = selectedCityData ? selectedCityData.children : [];
this.storeList = [];
this.getList();
},
getList() {
const sheng = this.provinces[this.selectedProvince];
const shi = this.cities[this.selectedCity];
const qu = this.districts[this.selectedDistrict];
let url = `https://www.zkcoi.com/wp-json/zke/v1/store?`;
if (sheng && sheng.name !== "请选择" && sheng.name !== "市辖区") {
url += `sheng=${encodeURIComponent(sheng.name)}`;
}
if (shi && shi.name !== "请选择" && shi.name !== "市辖区") {
url += `&shi=${encodeURIComponent(shi.name)}`;
}
if (qu && qu.name !== "请选择" && qu.name !== "市辖区") {
url += `&qu=${encodeURIComponent(qu.name)}`;
}
fetch(url)
.then(response => response.json())
.then(data => {
if (data.length === 0) {
// 如果没有数据,添加默认门店信息
this.storeList = [{
name: "请选择地区获取门店信息",
address: "请选择地区获取门店地址",
phoneNum:"400-XXX-XXX",
coordinate: {
longitude: 0,
latitude: 0
}
}];
} else {
this.storeList = data;
this.showMarkers(data);
}
})
.catch(error => console.error("Error fetching store list:", error));
},
showInfoWindow(index) {
const store = this.storeList[index];
if (store.coordinate && store.coordinate.latitude && store.coordinate.longitude) {
const poi = new AMap.LngLat(store.coordinate.longitude, store.coordinate.latitude);
let infoContent = `<div class="info-window-content">
<div class="info-logo"><img src="https://www.zkcoi.com/wp-content/uploads/2023/04/logo-mapinfo.png" alt="zkcoi"></div>
<h3 class="info-title">${store.name}</h3>
<p class="info-address">${store.address}</p>`;
if (store.imgUrl) {
infoContent += `<div class="info-img"><img class="max-width-img" src="${store.imgUrl}"></div>`;
}
infoContent += `<a class="btn btn-lg btn-primary btn-0" href="https://uri.amap.com/marker?position=${store.coordinate.longitude},${store.coordinate.latitude}&name=zkcoi渠道门店-${store.name}&src=www.zkcoi.com&callnative=1" target="_blank" rel="nofollow">点击查看路线</a>
</div>`;
// 创建信息窗口对象
const infoWindow = new AMap.InfoWindow({
offset: new AMap.Pixel(0, -50),
autoMove: true // 自动调整位置以确保信息窗口完全可见
});
// 更新信息窗口的内容并重新设置大小
infoWindow.setContent(infoContent);
infoWindow.setSize(new AMap.Size(400, 0)); // 设置大小,400为信息窗口的宽度,0为高度,0表示高度自适应
// 打开信息窗口
infoWindow.open(this.amap, poi);
// 将地图中心设置为标记位置
this.amap.setCenter(poi);
}
},
showMarkers(data) {
// 清除地图上的所有标记
this.amap.clearMap();
data.forEach((store, index) => {
if (store.coordinate && store.coordinate.latitude && store.coordinate.longitude) {
const poi = new AMap.LngLat(store.coordinate.longitude, store.coordinate.latitude);
const marker = new AMap.Marker({
position: poi,
title: store.name
});
marker.setMap(this.amap);
// 点击 marker 显示信息窗口
AMap.event.addListener(marker, 'click', () => {
this.showInfoWindow(index);
});
}
});
}
}
});
</script>
<?php get_footer();?>
本文作者:ZKCOI
文章名称:wordpress高德地图门店地图功能用AJAX异步获取门店数据
文章链接:https://www.zkcoi.com/365up/program/2664.html
本站资源仅供个人学习和交流,如若转载,请注明出处,详见《免责声明》。
微信扫一扫
支付宝扫一扫
