文章目录[隐藏]
实战教程:在WordPress中集成天气与地图显示插件
引言:为什么WordPress需要集成天气与地图功能?
在当今数字化时代,网站的功能丰富性直接关系到用户体验和网站价值。对于许多类型的WordPress网站来说,集成天气和地图功能可以显著提升实用性和用户粘性。旅游博客需要展示目的地天气,本地商家网站需要显示店铺位置,活动策划网站需要提供场地地图和天气信息——这些场景都说明了集成这些功能的必要性。
传统的解决方案是使用第三方小工具或iframe嵌入,但这些方法往往存在加载速度慢、样式不统一、功能受限等问题。通过WordPress代码二次开发实现这些功能,不仅可以获得更好的性能和控制权,还能确保与网站主题完美融合,提供一致的用户体验。
本教程将引导您完成在WordPress中集成天气与地图显示功能的完整过程,从API选择到代码实现,再到前端展示,为您提供一个完整的解决方案。
第一部分:准备工作与环境配置
1.1 选择合适的天气和地图API
在开始编码之前,我们需要选择合适的数据源。对于天气数据,有几个流行的API可供选择:
- OpenWeatherMap:提供免费层级的API调用,包含当前天气、预报和历史数据
- WeatherAPI:简单易用,免费套餐包含300万次调用/月
- AccuWeather:数据准确但免费层级限制较多
对于地图功能,主流选择包括:
- Google Maps API:功能强大但需要绑定信用卡(有免费额度)
- Mapbox:提供美观的地图样式和灵活的定制选项
- Leaflet + OpenStreetMap:完全开源免费的解决方案
考虑到成本和易用性,本教程将使用:
- 天气数据:OpenWeatherMap API(免费版)
- 地图显示:Leaflet + OpenStreetMap(完全免费)
1.2 注册API密钥
OpenWeatherMap注册步骤:
- 访问 OpenWeatherMap官网
- 点击"Sign Up"创建账户
- 登录后进入API Keys页面
- 生成新的API密钥并保存
Leaflet无需API密钥,可以直接使用。
1.3 WordPress开发环境准备
确保您具备以下环境:
- 本地或线上的WordPress安装(建议5.0以上版本)
- 代码编辑器(VS Code、Sublime Text等)
- FTP客户端或文件管理器(用于上传代码)
- 基本的PHP、JavaScript和HTML/CSS知识
1.4 创建自定义插件目录
为了避免主题更新导致代码丢失,我们将创建一个独立插件:
- 在WordPress的
wp-content/plugins/目录下创建新文件夹weather-map-integration - 在该文件夹中创建主插件文件
weather-map-integration.php
第二部分:创建基础插件结构
2.1 插件头部信息
打开weather-map-integration.php,添加以下插件声明:
<?php
/**
* Plugin Name: Weather & Map Integration
* Plugin URI: https://yourwebsite.com/
* Description: 在WordPress中集成天气与地图显示功能
* Version: 1.0.0
* Author: 您的名称
* Author URI: https://yourwebsite.com/
* License: GPL v2 or later
* Text Domain: weather-map-integration
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
2.2 定义插件常量
在插件头部信息后添加以下常量定义:
// 定义插件路径和URL常量
define('WMI_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WMI_PLUGIN_URL', plugin_dir_url(__FILE__));
// 定义API密钥常量(在实际使用中应从设置页面获取)
define('WMI_OPENWEATHER_API_KEY', 'your_openweather_api_key_here');
// 插件版本
define('WMI_VERSION', '1.0.0');
2.3 创建插件主类
使用面向对象的方式组织插件代码:
class Weather_Map_Integration {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 初始化钩子
add_action('init', array($this, 'init'));
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
// 注册短代码
add_shortcode('weather_display', array($this, 'weather_shortcode'));
add_shortcode('map_display', array($this, 'map_shortcode'));
add_shortcode('weather_map_combo', array($this, 'weather_map_combo_shortcode'));
}
public function init() {
// 初始化代码
load_plugin_textdomain('weather-map-integration', false, dirname(plugin_basename(__FILE__)) . '/languages');
}
// 其他方法将在后续部分添加
}
// 初始化插件
Weather_Map_Integration::get_instance();
第三部分:创建管理设置页面
3.1 添加管理菜单
在插件类中添加以下方法:
public function add_admin_menu() {
add_menu_page(
'天气与地图设置',
'天气地图',
'manage_options',
'weather-map-settings',
array($this, 'settings_page'),
'dashicons-location-alt',
80
);
add_submenu_page(
'weather-map-settings',
'API设置',
'API设置',
'manage_options',
'weather-map-api-settings',
array($this, 'api_settings_page')
);
add_submenu_page(
'weather-map-settings',
'显示设置',
'显示设置',
'manage_options',
'weather-map-display-settings',
array($this, 'display_settings_page')
);
}
3.2 注册设置选项
public function register_settings() {
// API设置
register_setting('wmi_api_settings', 'wmi_openweather_api_key');
register_setting('wmi_api_settings', 'wmi_default_city');
register_setting('wmi_api_settings', 'wmi_default_country');
// 显示设置
register_setting('wmi_display_settings', 'wmi_temperature_unit');
register_setting('wmi_display_settings', 'wmi_map_height');
register_setting('wmi_display_settings', 'wmi_map_width');
register_setting('wmi_display_settings', 'wmi_default_zoom');
register_setting('wmi_display_settings', 'wmi_show_attribution');
// API设置部分
add_settings_section(
'wmi_api_section',
'API配置',
array($this, 'api_section_callback'),
'weather-map-api-settings'
);
add_settings_field(
'wmi_openweather_api_key',
'OpenWeatherMap API密钥',
array($this, 'api_key_field_callback'),
'weather-map-api-settings',
'wmi_api_section'
);
add_settings_field(
'wmi_default_city',
'默认城市',
array($this, 'default_city_field_callback'),
'weather-map-api-settings',
'wmi_api_section'
);
// 显示设置部分
add_settings_section(
'wmi_display_section',
'显示设置',
array($this, 'display_section_callback'),
'weather-map-display-settings'
);
add_settings_field(
'wmi_temperature_unit',
'温度单位',
array($this, 'temperature_unit_field_callback'),
'weather-map-display-settings',
'wmi_display_section'
);
// 更多设置字段...
}
public function api_section_callback() {
echo '<p>配置天气和地图API的相关设置。请确保您已注册相应的API服务。</p>';
}
public function api_key_field_callback() {
$api_key = get_option('wmi_openweather_api_key', '');
echo '<input type="text" id="wmi_openweather_api_key" name="wmi_openweather_api_key" value="' . esc_attr($api_key) . '" class="regular-text" />';
echo '<p class="description">请输入您的OpenWeatherMap API密钥。如果没有,请访问<a href="https://openweathermap.org/api" target="_blank">OpenWeatherMap官网</a>注册获取。</p>';
}
// 其他字段回调函数...
3.3 创建设置页面模板
public function api_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('wmi_api_settings');
do_settings_sections('weather-map-api-settings');
submit_button('保存设置');
?>
</form>
</div>
<?php
}
public function display_settings_page() {
if (!current_user_can('manage_options')) {
return;
}
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<form action="options.php" method="post">
<?php
settings_fields('wmi_display_settings');
do_settings_sections('weather-map-display-settings');
submit_button('保存设置');
?>
</form>
</div>
<?php
}
第四部分:天气功能实现
4.1 创建天气数据获取类
在插件目录下创建includes/class-weather-data.php:
<?php
if (!defined('ABSPATH')) {
exit;
}
class WMI_Weather_Data {
private $api_key;
private $base_url = 'https://api.openweathermap.org/data/2.5/';
public function __construct($api_key = '') {
$this->api_key = $api_key ?: get_option('wmi_openweather_api_key', '');
}
/**
* 获取当前天气数据
*/
public function get_current_weather($city, $country = '', $units = 'metric') {
$location = $city;
if (!empty($country)) {
$location .= ',' . $country;
}
$transient_key = 'wmi_current_weather_' . md5($location . $units);
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
$url = $this->base_url . 'weather';
$args = array(
'q' => $location,
'appid' => $this->api_key,
'units' => $units,
'lang' => $this->get_language_code()
);
$response = wp_remote_get(add_query_arg($args, $url));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['cod']) && $data['cod'] != 200) {
return array('error' => $data['message'] ?? '未知错误');
}
// 缓存数据10分钟
set_transient($transient_key, $data, 10 * MINUTE_IN_SECONDS);
return $data;
}
/**
* 获取天气预报数据
*/
public function get_forecast($city, $country = '', $units = 'metric', $days = 5) {
$location = $city;
if (!empty($country)) {
$location .= ',' . $country;
}
$transient_key = 'wmi_forecast_' . md5($location . $units . $days);
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
$url = $this->base_url . 'forecast';
$args = array(
'q' => $location,
'appid' => $this->api_key,
'units' => $units,
'cnt' => $days * 8, // 每3小时一个数据点
'lang' => $this->get_language_code()
);
$response = wp_remote_get(add_query_arg($args, $url));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['cod']) && $data['cod'] != 200) {
return array('error' => $data['message'] ?? '未知错误');
}
// 缓存数据30分钟
set_transient($transient_key, $data, 30 * MINUTE_IN_SECONDS);
return $data;
}
/**
* 根据IP获取位置信息
*/
public function get_location_by_ip() {
$transient_key = 'wmi_location_ip_' . $_SERVER['REMOTE_ADDR'];
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
// 使用ipapi.co服务(免费)
$response = wp_remote_get('https://ipapi.co/json/');
if (is_wp_error($response)) {
return array('city' => 'Beijing', 'country' => 'CN');
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
$location = array(
'city' => $data['city'] ?? 'Beijing',
'country' => $data['country'] ?? 'CN'
);
// 缓存24小时
set_transient($transient_key, $location, 24 * HOUR_IN_SECONDS);
return $location;
}
/**
* 获取语言代码
*/
private function get_language_code() {
$locale = get_locale();
$lang_map = array(
'zh_CN' => 'zh_cn',
'zh_TW' => 'zh_tw',
'en_US' => 'en',
'en_GB' => 'en',
'es_ES' => 'es',
'fr_FR' => 'fr',
'de_DE' => 'de',
'ja' => 'ja',
'ko_KR' => 'kr'
);
return $lang_map[$locale] ?? 'en';
}
/**
* 处理天气数据为前端可用格式
*/
public function process_weather_data($weather_data) {
if (isset($weather_data['error'])) {
return $weather_data;
}
$processed = array(
'location' => $weather_data['name'] . ', ' . ($weather_data['sys']['country'] ?? ''),
'temperature' => round($weather_data['main']['temp']),
'feels_like' => round($weather_data['main']['feels_like']),
'description' => $weather_data['weather'][0]['description'],
'icon' => $weather_data['weather'][0]['icon'],
'humidity' => $weather_data['main']['humidity'],
'pressure' => $weather_data['main']['pressure'],
'wind_speed' => $weather_data['wind']['speed'],
'wind_deg' => $weather_data['wind']['deg'],
'sunrise' => date('H:i', $weather_data['sys']['sunrise']),
'sunset' => date('H:i', $weather_data['sys']['sunset']),
'timestamp' => time()
);
return $processed;
}
}
4.2 创建天气显示短代码
在主插件类中添加天气短代码方法:
public function weather_shortcode($atts) {
$atts = shortcode_atts(array(
'city' => '',
'country' => '',
'units' => get_option('wmi_temperature_unit', 'metric'),
'show_forecast' => 'false',
'forecast_days' => 3,
'layout' => 'compact', // compact, detailed, card
'title' => '当前天气'
), $atts, 'weather_display');
// 如果没有指定城市,尝试根据IP获取
if (empty($atts['city'])) {
$weather_data = new WMI_Weather_Data();
$location = $weather_data->get_location_by_ip();
$atts['city'] = $location['city'];
$atts['country'] = $location['country'];
}
// 获取天气数据
$weather_api = new WMI_Weather_Data();
$current_weather = $weather_api->get_current_weather($atts['city'], $atts['country'], $atts['units']);
if (isset($current_weather['error'])) {
return '<div class="wmi-error">无法获取天气数据: ' . esc_html($current_weather['error']) . '</div>';
}
$processed_weather = $weather_api->process_weather_data($current_weather);
// 获取天气预报(如果需要)
$forecast_data = array();
if ($atts['show_forecast'] === 'true') {
$forecast = $weather_api->get_forecast($atts['city'], $atts['country'], $atts['units'], $atts['forecast_days']);
if (!isset($forecast['error'])) {
$forecast_data = $this->process_forecast_data($forecast);
}
}
// 生成输出HTML
ob_start();
?>
<div class="wmi-weather-container wmi-layout-<?php echo esc_attr($atts['layout']); ?>"
data-city="<?php echo esc_attr($atts['city']); ?>"
data-country="<?php echo esc_attr($atts['country']); ?>"
data-units="<?php echo esc_attr($atts['units']); ?>">
<div class="wmi-weather-header">
<h3 class="wmi-weather-title"><?php echo esc_html($atts['title']); ?></h3>
<div class="wmi-location"><?php echo esc_html($processed_weather['location']); ?></div>
</div>
<div class="wmi-current-weather">
<div class="wmi-weather-main">
<div class="wmi-temperature">
<span class="wmi-temp-value"><?php echo esc_html($processed_weather['temperature']); ?></span>
<span class="wmi-temp-unit">°<?php echo $atts['units'] === 'metric' ? 'C' : 'F'; ?></span>
</div>
<div class="wmi-weather-icon">
<img src="https://openweathermap.org/img/wn/<?php echo esc_attr($processed_weather['icon']); ?>@2x.png"
alt="<?php echo esc_attr($processed_weather['description']); ?>">
</div>
</div>
<div class="wmi-weather-details">
<div class="wmi-weather-desc"><?php echo esc_html($processed_weather['description']); ?></div>
<div class="wmi-weather-meta">
<span class="wmi-feels-like">体感温度: <?php echo esc_html($processed_weather['feels_like']); ?>°</span>
<span class="wmi-humidity">湿度: <?php echo esc_html($processed_weather['humidity']); ?>%</span>
<span class="wmi-wind">风速: <?php echo esc_html($processed_weather['wind_speed']); ?> m/s</span>
</div>
</div>
</div>
<?php if (!empty($forecast_data)): ?>
<div class="wmi-forecast">
<h4 class="wmi-forecast-title"><?php echo esc_html($atts['forecast_days']); ?>天预报</h4>
<div class="wmi-forecast-days">
<?php foreach ($forecast_data as $day): ?>
<div class="wmi-forecast-day">
<div class="wmi-forecast-date"><?php echo esc_html($day['date']); ?></div>
<div class="wmi-forecast-icon">
<img src="https://openweathermap.org/img/wn/<?php echo esc_attr($day['icon']); ?>.png"
alt="<?php echo esc_attr($day['description']); ?>">
</div>
<div class="wmi-forecast-temp">
<span class="wmi-forecast-temp-max"><?php echo esc_html($day['temp_max']); ?>°</span>
<span class="wmi-forecast-temp-min"><?php echo esc_html($day['temp_min']); ?>°</span>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<div class="wmi-weather-footer">
<div class="wmi-sun-times">
<span class="wmi-sunrise">日出: <?php echo esc_html($processed_weather['sunrise']); ?></span>
<span class="wmi-sunset">日落: <?php echo esc_html($processed_weather['sunset']); ?></span>
</div>
<div class="wmi-update-time">
更新时间: <?php echo date('H:i', $processed_weather['timestamp']); ?>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
private function process_forecast_data($forecast_data) {
$daily_data = array();
$grouped_by_day = array();
// 按日期分组
foreach ($forecast_data['list'] as $item) {
$date = date('Y-m-d', $item['dt']);
if (!isset($grouped_by_day[$date])) {
$grouped_by_day[$date] = array();
}
$grouped_by_day[$date][] = $item;
}
// 处理每天的数据
$count = 0;
foreach ($grouped_by_day as $date => $day_items) {
if ($count >= 5) break; // 最多显示5天
$temps = array();
$icons = array();
$descriptions = array();
foreach ($day_items as $item) {
$temps[] = $item['main']['temp'];
$icons[] = $item['weather'][0]['icon'];
$descriptions[] = $item['weather'][0]['description'];
}
// 获取最常见的图标和描述
$icon_counts = array_count_values($icons);
arsort($icon_counts);
$most_common_icon = key($icon_counts);
$desc_counts = array_count_values($descriptions);
arsort($desc_counts);
$most_common_desc = key($desc_counts);
$daily_data[] = array(
'date' => date('m/d', strtotime($date)),
'day' => date('D', strtotime($date)),
'temp_max' => round(max($temps)),
'temp_min' => round(min($temps)),
'icon' => $most_common_icon,
'description' => $most_common_desc
);
$count++;
}
return $daily_data;
}
## 第五部分:地图功能实现
### 5.1 创建地图数据类
在插件目录下创建`includes/class-map-data.php`:
<?php
if (!defined('ABSPATH')) {
exit;
}
class WMI_Map_Data {
/**
* 获取位置坐标
*/
public function get_coordinates($location) {
$transient_key = 'wmi_coordinates_' . md5($location);
$cached_data = get_transient($transient_key);
if ($cached_data !== false) {
return $cached_data;
}
// 使用Nominatim(OpenStreetMap的搜索服务)
$url = 'https://nominatim.openstreetmap.org/search';
$args = array(
'q' => $location,
'format' => 'json',
'limit' => 1,
'addressdetails' => 1
);
$response = wp_remote_get(add_query_arg($args, $url), array(
'headers' => array(
'User-Agent' => 'WordPress Weather Map Integration/1.0'
)
));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (empty($data)) {
return array('error' => '未找到位置信息');
}
$coordinates = array(
'lat' => $data[0]['lat'],
'lon' => $data[0]['lon'],
'display_name' => $data[0]['display_name'],
'address' => $data[0]['address']
);
// 缓存30天
set_transient($transient_key, $coordinates, 30 * DAY_IN_SECONDS);
return $coordinates;
}
/**
* 获取多个标记点
*/
public function get_multiple_markers($locations) {
$markers = array();
foreach ($locations as $location) {
if (is_array($location) && isset($location['lat'], $location['lng'])) {
$markers[] = array(
'lat' => $location['lat'],
'lng' => $location['lng'],
'title' => $location['title'] ?? '',
'description' => $location['description'] ?? ''
);
} else {
$coords = $this->get_coordinates($location);
if (!isset($coords['error'])) {
$markers[] = array(
'lat' => $coords['lat'],
'lng' => $coords['lon'],
'title' => is_array($location) ? ($location['title'] ?? '') : $location,
'description' => $coords['display_name']
);
}
}
}
return $markers;
}
/**
* 计算地图边界
*/
public function calculate_bounds($markers) {
if (empty($markers)) {
return array(
'south' => 0,
'west' => 0,
'north' => 0,
'east' => 0
);
}
$lats = array_column($markers, 'lat');
$lngs = array_column($markers, 'lng');
return array(
'south' => min($lats),
'west' => min($lngs),
'north' => max($lats),
'east' => max($lngs)
);
}
}
### 5.2 创建地图显示短代码
在主插件类中添加地图短代码方法:
public function map_shortcode($atts) {
$atts = shortcode_atts(array(
'location' => '',
'lat' => '',
'lng' => '',
'zoom' => get_option('wmi_default_zoom', 13),
'height' => get_option('wmi_map_height', '400px'),
'width' => get_option('wmi_map_width', '100%'),
'markers' => '', // JSON格式或逗号分隔
'show_search' => 'false',
'show_controls' => 'true',
'map_style' => 'streets', // streets, satellite, terrain
'title' => '位置地图'
), $atts, 'map_display');
// 生成唯一ID
$map_id = 'wmi-map-' . uniqid();
// 处理位置数据
$locations = array();
if (!empty($atts['markers'])) {
// 尝试解析JSON
$markers_json = json_decode($atts['markers'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$locations = $markers_json;
} else {
// 逗号分隔的字符串
$marker_list = explode(',', $atts['markers']);
foreach ($marker_list as $marker) {
$locations[] = trim($marker);
}
}
} elseif (!empty($atts['location'])) {
$locations[] = $atts['location'];
} elseif (!empty($atts['lat']) && !empty($atts['lng'])) {
$locations[] = array(
'lat' => $atts['lat'],
'lng' => $atts['lng'],
'title' => $atts['title']
);
} else {
// 默认位置
$weather_data = new WMI_Weather_Data();
$default_location = $weather_data->get_location_by_ip();
$locations[] = $default_location['city'] . ', ' . $default_location['country'];
}
// 获取坐标
$map_api = new WMI_Map_Data();
$markers = $map_api->get_multiple_markers($locations);
// 计算地图边界
$bounds = $map_api->calculate_bounds($markers);
ob_start();
?>
<div class="wmi-map-container" id="<?php echo esc_attr($map_id); ?>-container">
<?php if (!empty($atts['title'])): ?>
<h3 class="wmi-map-title"><?php echo esc_html($atts['title']); ?></h3>
<?php endif; ?>
<?php if ($atts['show_search'] === 'true'): ?>
<div class="wmi-map-search">
<input type="text" id="<?php echo esc_attr($map_id); ?>-search"
placeholder="搜索地点..." class="wmi-map-search-input">
<button type="button" class="wmi-map-search-button">搜索</button>
</div>
<?php endif; ?>
<div class="wmi-map"
id="<?php echo esc_attr($map_id); ?>"
style="height: <?php echo esc_attr($atts['height']); ?>;
width: <?php echo esc_attr($atts['width']); ?>;">
</div>
<div class="wmi-map-info">
<?php if (!empty($markers) && count($markers) === 1): ?>
<div class="wmi-location-info">
<strong><?php echo esc_html($markers[0]['title'] ?: '位置'); ?>:</strong>
<span><?php echo esc_html($markers[0]['description']); ?></span>
</div>
<?php endif; ?>
</div>
</div>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
if (typeof L !== 'undefined') {
initWmiMap('<?php echo esc_js($map_id); ?>', {
markers: <?php echo json_encode($markers); ?>,
bounds: <?php echo json_encode($bounds); ?>,
zoom: <?php echo intval($atts['zoom']); ?>,
showControls: <?php echo $atts['show_controls'] === 'true' ? 'true' : 'false'; ?>,
mapStyle: '<?php echo esc_js($atts['map_style']); ?>'
});
}
});
</script>
<?php
return ob_get_clean();
}
### 5.3 创建组合短代码
public function weather_map_combo_shortcode($atts) {
$atts = shortcode_atts(array(
'location' => '',
'city' => '',
'country' => '',
'layout' => 'side-by-side', // side-by-side, map-above, weather-above
'height' => '500px',
'show_forecast' => 'true',
'show_search' => 'true',
'title' => '天气与位置'
), $atts, 'weather_map_combo');
// 确定位置
if (!empty($atts['location'])) {
$location_parts = explode(',', $atts['location']);
$atts['city'] = trim($location_parts[0]);
if (isset($location_parts[1])) {
$atts['country'] = trim($location_parts[1]);
}
}
ob_start();
?>
<div class="wmi-combo-container wmi-layout-<?php echo esc_attr($atts['layout']); ?>">
<h2 class="wmi-combo-title"><?php echo esc_html($atts['title']); ?></h2>
<div class="wmi-combo-content">
<div class="wmi-combo-weather">
<?php
echo $this->weather_shortcode(array(
'city' => $atts['city'],
'country' => $atts['country'],
'show_forecast' => $atts['show_forecast'],
'layout' => 'detailed',
'title' => ''
));
?>
</div>
<div class="wmi-combo-map">
<?php
$location_str = !empty($atts['country']) ?
$atts['city'] . ', ' . $atts['country'] :
$atts['city'];
echo $this->map_shortcode(array(
'location' => $location_str,
'height' => $atts['height'],
'show_search' => $atts['show_search'],
'title' => ''
));
?>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
## 第六部分:前端资源加载与样式
### 6.1 注册和加载脚本样式
在主插件类中添加以下方法:
public function enqueue_frontend_scripts() {
// 加载Leaflet地图库
wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css', array(), '1.7.1');
wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', array(), '1.7.1', true);
// 加载插件样式
wp_enqueue_style('wmi-frontend-style', WMI_PLUGIN_URL . 'assets/css/frontend.css', array(), WMI_VERSION);
// 加载插件脚本
wp_enqueue_script('wmi-frontend-script', WMI_PLUGIN_URL . 'assets/js/frontend.js', array('jquery', 'leaflet-js'), WMI_VERSION, true);
// 本地化脚本
wp_localize_script('wmi-frontend-script', 'wmi_ajax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wmi_ajax_nonce'),
'default_location' => $this->get_default_location(),
'strings' => array(
'loading' => __('加载中...', 'weather-map-integration'),
'error' => __('发生错误', 'weather-map-integration'),
'search_placeholder' => __('输入地点
