文章目录[隐藏]
WordPress集成教程:连接公共交通API实现实时到站信息查询与展示
引言:为什么要在WordPress中集成公共交通信息?
在当今快节奏的城市生活中,实时公共交通信息已成为人们日常出行的重要参考。对于地方新闻网站、旅游博客、社区门户或企业网站而言,提供实时公交/地铁到站信息可以显著提升用户体验和网站实用性。WordPress作为全球最流行的内容管理系统,通过代码二次开发可以轻松集成这类实用功能。
本教程将详细指导您如何通过WordPress程序代码二次开发,连接公共交通API实现实时到站信息查询与展示功能。我们将从API选择、开发环境搭建、功能实现到前端展示,一步步构建一个完整的解决方案。
第一部分:准备工作与环境搭建
1.1 选择合适的公共交通API
在选择API前,需要考虑以下因素:
- 覆盖区域:确保API覆盖您需要的城市或地区
- 数据准确性:实时更新的频率和数据可靠性
- 成本:免费还是付费,调用限制如何
- 文档完整性:是否有完善的开发文档和示例
常用公共交通API推荐:
- TransitLand:覆盖全球多个城市的公共交通数据,提供丰富的API接口
- Google Maps Transit API:集成在Google Maps平台中,数据全面但可能有使用限制
- 本地交通部门API:许多城市的交通部门提供官方API,数据最权威
- Moovit API:提供全球范围内的公共交通数据
本教程将以一个模拟的公共交通API为例进行演示,实际应用中您需要替换为真实的API接口。
1.2 开发环境配置
在开始开发前,请确保您的WordPress环境满足以下条件:
- WordPress版本:5.0或更高版本
- PHP版本:7.4或更高版本
-
必要的插件:
- Advanced Custom Fields(可选,用于创建管理界面)
- 缓存插件(如WP Rocket或W3 Total Cache)
- 代码编辑器:VS Code、PHPStorm或Sublime Text
- 本地开发环境:XAMPP、MAMP或Local by Flywheel
1.3 创建自定义插件
为了避免主题更新导致功能丢失,我们将创建一个独立插件来实现功能:
<?php
/**
* Plugin Name: 公共交通实时信息查询
* Plugin URI: https://yourwebsite.com/
* Description: 在WordPress中集成公共交通API,显示实时到站信息
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('PT_API_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('PT_API_PLUGIN_URL', plugin_dir_url(__FILE__));
define('PT_API_CACHE_TIME', 300); // 缓存时间5分钟
// 初始化插件
require_once PT_API_PLUGIN_PATH . 'includes/class-public-transport-api.php';
require_once PT_API_PLUGIN_PATH . 'includes/class-api-handler.php';
require_once PT_API_PLUGIN_PATH . 'includes/class-shortcode-handler.php';
require_once PT_API_PLUGIN_PATH . 'includes/class-admin-settings.php';
// 初始化主类
function pt_api_init() {
$plugin = new Public_Transport_API();
$plugin->run();
}
add_action('plugins_loaded', 'pt_api_init');
第二部分:API连接与数据处理
2.1 创建API处理类
<?php
// includes/class-api-handler.php
class PT_API_Handler {
private $api_key;
private $api_endpoint;
private $cache_enabled;
public function __construct() {
// 从设置中获取API配置
$options = get_option('pt_api_settings');
$this->api_key = isset($options['api_key']) ? $options['api_key'] : '';
$this->api_endpoint = isset($options['api_endpoint']) ? $options['api_endpoint'] : 'https://api.example.com/transit';
$this->cache_enabled = isset($options['enable_cache']) ? $options['enable_cache'] : true;
}
/**
* 获取公交线路信息
*/
public function get_bus_routes($city = '') {
$cache_key = 'pt_bus_routes_' . md5($city);
// 检查缓存
if ($this->cache_enabled) {
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
}
// 构建API请求URL
$url = $this->api_endpoint . '/routes';
if (!empty($city)) {
$url .= '?city=' . urlencode($city);
}
// 发送API请求
$response = $this->make_api_request($url);
if ($response && isset($response['routes'])) {
// 缓存结果
if ($this->cache_enabled) {
set_transient($cache_key, $response['routes'], PT_API_CACHE_TIME);
}
return $response['routes'];
}
return false;
}
/**
* 获取实时到站信息
*/
public function get_realtime_arrivals($route_id, $stop_id) {
$cache_key = 'pt_arrivals_' . md5($route_id . $stop_id);
// 实时数据缓存时间较短
if ($this->cache_enabled) {
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
}
// 构建API请求URL
$url = $this->api_endpoint . '/realtime/arrivals';
$url .= '?route=' . urlencode($route_id);
$url .= '&stop=' . urlencode($stop_id);
// 发送API请求
$response = $this->make_api_request($url);
if ($response && isset($response['arrivals'])) {
// 实时数据只缓存1分钟
if ($this->cache_enabled) {
set_transient($cache_key, $response['arrivals'], 60);
}
return $response['arrivals'];
}
return false;
}
/**
* 搜索公交站点
*/
public function search_stops($query, $city = '') {
$cache_key = 'pt_stops_search_' . md5($query . $city);
if ($this->cache_enabled) {
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
}
// 构建API请求URL
$url = $this->api_endpoint . '/stops/search';
$url .= '?q=' . urlencode($query);
if (!empty($city)) {
$url .= '&city=' . urlencode($city);
}
// 发送API请求
$response = $this->make_api_request($url);
if ($response && isset($response['stops'])) {
if ($this->cache_enabled) {
set_transient($cache_key, $response['stops'], PT_API_CACHE_TIME);
}
return $response['stops'];
}
return false;
}
/**
* 发送API请求
*/
private function make_api_request($url) {
// 添加API密钥
$url .= (strpos($url, '?') === false ? '?' : '&') . 'api_key=' . $this->api_key;
// 设置请求参数
$args = array(
'timeout' => 15,
'redirection' => 5,
'httpversion' => '1.1',
'user-agent' => 'WordPress Public Transport Plugin/1.0',
'headers' => array(
'Accept' => 'application/json',
),
);
// 发送请求
$response = wp_remote_get($url, $args);
// 检查响应
if (is_wp_error($response)) {
error_log('公共交通API请求错误: ' . $response->get_error_message());
return false;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
// 检查JSON解析
if (json_last_error() !== JSON_ERROR_NONE) {
error_log('公共交通API JSON解析错误: ' . json_last_error_msg());
return false;
}
// 检查API返回的错误
if (isset($data['error'])) {
error_log('公共交通API错误: ' . $data['error']['message']);
return false;
}
return $data;
}
/**
* 获取支持的城市列表
*/
public function get_supported_cities() {
$cache_key = 'pt_supported_cities';
if ($this->cache_enabled) {
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
}
$url = $this->api_endpoint . '/cities';
$response = $this->make_api_request($url);
if ($response && isset($response['cities'])) {
if ($this->cache_enabled) {
set_transient($cache_key, $response['cities'], 24 * HOUR_IN_SECONDS);
}
return $response['cities'];
}
// 返回默认城市列表
return array(
array('id' => 'beijing', 'name' => '北京'),
array('id' => 'shanghai', 'name' => '上海'),
array('id' => 'guangzhou', 'name' => '广州'),
array('id' => 'shenzhen', 'name' => '深圳'),
);
}
}
2.2 创建短代码处理器
<?php
// includes/class-shortcode-handler.php
class PT_Shortcode_Handler {
private $api_handler;
public function __construct() {
$this->api_handler = new PT_API_Handler();
// 注册短代码
add_shortcode('bus_arrivals', array($this, 'render_arrivals_shortcode'));
add_shortcode('bus_route_search', array($this, 'render_search_shortcode'));
add_shortcode('bus_stop_info', array($this, 'render_stop_info_shortcode'));
// 注册AJAX处理
add_action('wp_ajax_pt_search_stops', array($this, 'ajax_search_stops'));
add_action('wp_ajax_nopriv_pt_search_stops', array($this, 'ajax_search_stops'));
add_action('wp_ajax_pt_get_arrivals', array($this, 'ajax_get_arrivals'));
add_action('wp_ajax_nopriv_pt_get_arrivals', array($this, 'ajax_get_arrivals'));
}
/**
* 渲染实时到站信息短代码
*/
public function render_arrivals_shortcode($atts) {
// 解析短代码属性
$atts = shortcode_atts(array(
'route' => '',
'stop' => '',
'title' => '实时到站信息',
'max_results' => 5,
'show_refresh' => true,
'city' => '',
), $atts, 'bus_arrivals');
// 生成唯一ID
$container_id = 'pt-arrivals-' . uniqid();
// 如果提供了route和stop,直接加载数据
$initial_data = '';
if (!empty($atts['route']) && !empty($atts['stop'])) {
$arrivals = $this->api_handler->get_realtime_arrivals($atts['route'], $atts['stop']);
if ($arrivals) {
$initial_data = json_encode($arrivals);
}
}
// 输出HTML
ob_start();
?>
<div id="<?php echo esc_attr($container_id); ?>" class="pt-arrivals-container" data-route="<?php echo esc_attr($atts['route']); ?>" data-stop="<?php echo esc_attr($atts['stop']); ?>" data-city="<?php echo esc_attr($atts['city']); ?>" data-max-results="<?php echo esc_attr($atts['max_results']); ?>">
<div class="pt-arrivals-header">
<h3><?php echo esc_html($atts['title']); ?></h3>
<?php if ($atts['show_refresh']) : ?>
<button class="pt-refresh-btn" onclick="ptRefreshArrivals('<?php echo esc_js($container_id); ?>')">
<span class="dashicons dashicons-update"></span> 刷新
</button>
<?php endif; ?>
</div>
<div class="pt-arrivals-body">
<?php if (empty($atts['route']) || empty($atts['stop'])) : ?>
<div class="pt-no-selection">
<p>请选择线路和站点查看实时到站信息</p>
<div class="pt-search-form">
<input type="text" class="pt-stop-search" placeholder="搜索站点..." data-target="<?php echo esc_attr($container_id); ?>">
<div class="pt-search-results"></div>
</div>
</div>
<?php else : ?>
<div class="pt-arrivals-list">
<!-- 动态加载内容 -->
</div>
<?php endif; ?>
</div>
<div class="pt-arrivals-footer">
<small>数据更新时间: <span class="pt-update-time">--:--:--</span></small>
</div>
</div>
<?php if (!empty($initial_data)) : ?>
<script>
jQuery(document).ready(function($) {
ptRenderArrivals('<?php echo esc_js($container_id); ?>', <?php echo $initial_data; ?>);
});
</script>
<?php endif; ?>
<?php
return ob_get_clean();
}
/**
* 渲染搜索短代码
*/
public function render_search_shortcode($atts) {
$atts = shortcode_atts(array(
'city' => '',
'placeholder' => '输入公交线路或站点名称...',
), $atts, 'bus_route_search');
$container_id = 'pt-search-' . uniqid();
ob_start();
?>
<div id="<?php echo esc_attr($container_id); ?>" class="pt-search-container">
<div class="pt-search-box">
<input type="text" class="pt-global-search" placeholder="<?php echo esc_attr($atts['placeholder']); ?>" data-city="<?php echo esc_attr($atts['city']); ?>">
<button class="pt-search-btn">
<span class="dashicons dashicons-search"></span>
</button>
</div>
<div class="pt-search-results-container">
<div class="pt-search-results"></div>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* AJAX搜索站点
*/
public function ajax_search_stops() {
// 验证nonce
if (!check_ajax_referer('pt_ajax_nonce', 'nonce', false)) {
wp_die('安全验证失败', 403);
}
$query = isset($_POST['query']) ? sanitize_text_field($_POST['query']) : '';
$city = isset($_POST['city']) ? sanitize_text_field($_POST['city']) : '';
if (empty($query)) {
wp_send_json_error('请输入搜索关键词');
}
$stops = $this->api_handler->search_stops($query, $city);
if ($stops) {
wp_send_json_success($stops);
} else {
wp_send_json_error('未找到相关站点');
}
}
/**
* AJAX获取到站信息
*/
public function ajax_get_arrivals() {
// 验证nonce
if (!check_ajax_referer('pt_ajax_nonce', 'nonce', false)) {
wp_die('安全验证失败', 403);
}
$route_id = isset($_POST['route']) ? sanitize_text_field($_POST['route']) : '';
$stop_id = isset($_POST['stop']) ? sanitize_text_field($_POST['stop']) : '';
if (empty($route_id) || empty($stop_id)) {
wp_send_json_error('缺少必要参数');
}
$arrivals = $this->api_handler->get_realtime_arrivals($route_id, $stop_id);
if ($arrivals) {
wp_send_json_success($arrivals);
} else {
wp_send_json_error('获取到站信息失败');
}
}
}
第三部分:前端展示与用户交互
3.1 添加前端样式
/* assets/css/public-transport.css */
/* 主容器样式 */
.pt-arrivals-container {
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
margin: 20px 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* 头部样式 */
.pt-arrivals-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
border-bottom: 2px solid #007cba;
padding-bottom: 10px;
}
.pt-arrivals-header h3 {
margin: 0;
color: #1d2327;
font-size: 1.5em;
}
.pt-refresh-btn {
background-color: #007cba;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
gap: 5px;
transition: background-color 0.3s;
}
.pt-refresh-btn:hover {
background-color: #005a87;
}
.pt-refresh-btn .dashicons {
font-size: 16px;
width: 16px;
height: 16px;
}
/ 到站信息列表 /
.pt-arrivals-list {
min-height: 200px;
}
.pt-arrival-item {
background: white;
border-radius: 6px;
padding: 15px;
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
}
.pt-arrival-item:hover {
transform: translateY(-2px);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
}
.pt-arrival-info {
flex: 1;
}
.pt-route-number {
display: inline-block;
background-color: #007cba;
color: white;
padding: 4px 12px;
border-radius: 20px;
font-weight: bold;
font-size: 14px;
margin-right: 10px;
}
.pt-destination {
font-weight: 600;
color: #1d2327;
font-size: 16px;
}
.pt-stop-name {
color: #646970;
font-size: 14px;
margin-top: 5px;
}
/ 时间信息 /
.pt-time-info {
text-align: right;
min-width: 120px;
}
.pt-arrival-time {
font-size: 24px;
font-weight: bold;
color: #007cba;
line-height: 1;
}
.pt-arrival-time.imminent {
color: #d63638;
}
.pt-arrival-time.soon {
color: #dba617;
}
.pt-time-unit {
font-size: 12px;
color: #646970;
display: block;
margin-top: 2px;
}
.pt-scheduled-time {
font-size: 12px;
color: #8c8f94;
margin-top: 5px;
}
/ 搜索框样式 /
.pt-search-form {
position: relative;
margin: 20px 0;
}
.pt-stop-search {
width: 100%;
padding: 12px 15px;
border: 2px solid #c3c4c7;
border-radius: 6px;
font-size: 16px;
transition: border-color 0.3s;
}
.pt-stop-search:focus {
outline: none;
border-color: #007cba;
}
.pt-search-results {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #c3c4c7;
border-radius: 0 0 6px 6px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
max-height: 300px;
overflow-y: auto;
z-index: 1000;
display: none;
}
.pt-search-result-item {
padding: 12px 15px;
border-bottom: 1px solid #f0f0f1;
cursor: pointer;
transition: background-color 0.2s;
}
.pt-search-result-item:hover {
background-color: #f6f7f7;
}
.pt-search-result-item:last-child {
border-bottom: none;
}
.pt-result-stop-name {
font-weight: 600;
color: #1d2327;
display: block;
}
.pt-result-route-list {
font-size: 12px;
color: #646970;
margin-top: 4px;
}
/ 全局搜索样式 /
.pt-search-container {
max-width: 600px;
margin: 30px auto;
}
.pt-search-box {
display: flex;
gap: 10px;
}
.pt-global-search {
flex: 1;
padding: 15px;
border: 2px solid #007cba;
border-radius: 8px;
font-size: 16px;
}
.pt-search-btn {
background-color: #007cba;
color: white;
border: none;
border-radius: 8px;
padding: 0 25px;
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
}
.pt-search-btn:hover {
background-color: #005a87;
}
/ 加载状态 /
.pt-loading {
text-align: center;
padding: 40px;
color: #646970;
}
.pt-loading-spinner {
border: 3px solid #f3f3f3;
border-top: 3px solid #007cba;
border-radius: 50%;
width: 40px;
height: 40px;
animation: pt-spin 1s linear infinite;
margin: 0 auto 15px;
}
@keyframes pt-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/ 空状态 /
.pt-no-selection {
text-align: center;
padding: 40px 20px;
color: #646970;
}
.pt-no-selection p {
margin-bottom: 20px;
font-size: 16px;
}
/ 响应式设计 /
@media (max-width: 768px) {
.pt-arrival-item {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.pt-time-info {
text-align: left;
width: 100%;
border-top: 1px solid #f0f0f1;
padding-top: 10px;
}
.pt-search-box {
flex-direction: column;
}
.pt-search-btn {
padding: 15px;
}
}
/ 夜间模式支持 /
@media (prefers-color-scheme: dark) {
.pt-arrivals-container {
background-color: #1d2327;
color: #f0f0f1;
}
.pt-arrival-item {
background-color: #2c3338;
}
.pt-arrivals-header h3,
.pt-destination {
color: #f0f0f1;
}
.pt-stop-search,
.pt-global-search {
background-color: #2c3338;
border-color: #4f555c;
color: #f0f0f1;
}
.pt-search-results {
background-color: #2c3338;
border-color: #4f555c;
}
}
### 3.2 添加JavaScript交互
// assets/js/public-transport.js
(function($) {
'use strict';
// 全局变量
var ptAjaxUrl = ptSettings.ajax_url;
var ptNonce = ptSettings.nonce;
/**
* 初始化插件功能
*/
function initPublicTransport() {
// 绑定搜索事件
$('.pt-stop-search').on('input', debounce(handleStopSearch, 300));
$('.pt-global-search').on('input', debounce(handleGlobalSearch, 300));
// 绑定搜索按钮点击事件
$('.pt-search-btn').on('click', handleSearchButtonClick);
// 绑定搜索结果点击事件
$(document).on('click', '.pt-search-result-item', handleResultItemClick);
// 自动刷新到站信息
initAutoRefresh();
}
/**
* 处理站点搜索
*/
function handleStopSearch(event) {
var $input = $(this);
var query = $input.val().trim();
var city = $input.data('city') || '';
var $results = $input.siblings('.pt-search-results');
var targetContainer = $input.data('target');
if (query.length < 2) {
$results.hide().empty();
return;
}
// 显示加载状态
$results.html('<div class="pt-loading"><div class="pt-loading-spinner"></div><p>搜索中...</p></div>').show();
$.ajax({
url: ptAjaxUrl,
type: 'POST',
data: {
action: 'pt_search_stops',
nonce: ptNonce,
query: query,
city: city
},
success: function(response) {
if (response.success && response.data.length > 0) {
renderSearchResults($results, response.data, targetContainer);
} else {
$results.html('<div class="pt-no-results"><p>未找到相关站点</p></div>').show();
}
},
error: function() {
$results.html('<div class="pt-error"><p>搜索失败,请稍后重试</p></div>').show();
}
});
}
/**
* 渲染搜索结果
*/
function renderSearchResults($container, results, targetContainer) {
var html = '';
results.forEach(function(stop) {
var routes = stop.routes ? stop.routes.join('、') : '多条线路';
html += '<div class="pt-search-result-item" data-stop-id="' + stop.id + '" data-stop-name="' + stop.name + '" data-routes="' + JSON.stringify(stop.routes || []) + '" data-target="' + targetContainer + '">';
html += '<span class="pt-result-stop-name">' + stop.name + '</span>';
html += '<span class="pt-result-route-list">途经线路: ' + routes + '</span>';
html += '</div>';
});
$container.html(html).show();
}
/**
* 处理搜索结果点击
*/
function handleResultItemClick() {
var $item = $(this);
var stopId = $item.data('stop-id');
var stopName = $item.data('stop-name');
var routes = $item.data('routes');
var targetContainer = $item.data('target');
// 隐藏搜索结果
$item.closest('.pt-search-results').hide().empty();
// 清空搜索框
$item.closest('.pt-search-form').find('.pt-stop-search').val('');
// 如果只有一个线路,自动选择
if (routes && routes.length === 1) {
loadArrivalsForStop(targetContainer, routes[0], stopId, stopName);
} else {
// 显示线路选择
showRouteSelection(targetContainer, routes, stopId, stopName);
}
}
/**
* 显示线路选择
*/
function showRouteSelection(containerId, routes, stopId, stopName) {
var $container = $('#' + containerId);
var html = '<div class="pt-route-selection">';
html += '<h4>选择线路 - ' + stopName + '</h4>';
html += '<div class="pt-route-list">';
routes.forEach(function(route) {
html += '<button class="pt-route-option" data-route="' + route + '" data-stop="' + stopId + '">' + route + '路</button>';
});
html += '</div></div>';
$container.find('.pt-arrivals-body').html(html);
// 绑定线路选择事件
$container.find('.pt-route-option').on('click', function() {
var routeId = $(this).data('route');
loadArrivalsForStop(containerId, routeId, stopId, stopName);
});
}
/**
* 加载到站信息
*/
function loadArrivalsForStop(containerId, routeId, stopId, stopName) {
var $container = $('#' + containerId);
var maxResults = $container.data('max-results') || 5;
// 显示加载状态
$container.find('.pt-arrivals-body').html('<div class="pt-loading"><div class="pt-loading-spinner"></div><p>加载到站信息中...</p></div>');
// 更新容器数据属性
$container.data('route', routeId);
$container.data('stop', stopId);
// 获取到站信息
$.ajax({
url: ptAjaxUrl,
type: 'POST',
data: {
action: 'pt_get_arrivals',
nonce: ptNonce,
route: routeId,
stop: stopId
},
success: function(response) {
if (response.success) {
ptRenderArrivals(containerId, response.data);
} else {
showError($container, response.data || '加载失败');
}
},
error: function() {
showError($container, '网络错误,请稍后重试');
}
});
}
/**
* 渲染到站信息
*/
window.ptRenderArrivals = function(containerId, arrivals) {
var $container = $('#' + containerId);
var maxResults = $container.data('max-results') || 5;
var routeId = $container.data('route');
var stopId = $container.data('stop');
if (!arrivals || arrivals.length === 0) {
$container.find('.pt-arrivals-body').html('<div class="pt-no-arrivals"><p>暂无到站信息</p></div>');
updateTimeStamp($container);
return;
}
// 限制显示数量
var displayArrivals = arrivals.slice(0, maxResults);
var html = '';
displayArrivals.forEach(function(arrival) {
var minutes = arrival.minutes || 0;
var timeClass = 'pt-arrival-time';
if (minutes <= 2) {
timeClass += ' imminent';
} else if (minutes <= 5) {
timeClass += ' soon';
}
html += '<div class="pt-arrival-item">';
html += '<div class="pt-arrival-info">';
html += '<span class="pt-route-number">' + (arrival.route || routeId) + '路</span>';
html += '<span class="pt-destination">' + (arrival.destination || '未知方向') + '</span>';
html += '<div class="pt-stop-name">' + (arrival.stop_name || '') + '</div>';
html += '</div>';
html += '<div class="pt-time-info">';
html += '<div class="' + timeClass + '">' + minutes + '<span class="pt-time-unit">分钟</span></div>';
if (arrival.scheduled_time) {
html += '<div class="pt-scheduled-time">计划: ' + arrival.scheduled_time + '</div>';
}
html += '</div>';
html += '</div>';
});
$container.find('.pt-arrivals-body').html(html);
updateTimeStamp($container);
};
/**
* 刷新到站信息
*/
window.ptRefreshArrivals = function(containerId) {
var $container = $('#' + containerId);
var routeId = $container.data('route');
var stopId = $container.data('stop');
if (!routeId || !stopId) {
return;
}
loadArrivalsForStop(containerId, routeId, stopId);
};
/**
* 更新时间戳
*/
function updateTimeStamp($container) {
var now = new Date();
var timeString = now.getHours().toString().padStart(2, '0') + ':' +
now.getMinutes().toString().padStart(2, '0') + ':' +
now.getSeconds().toString().padStart(2, '0');
$container.find('.pt-update-time').text(timeString);
}
/**
* 显示错误信息
*/
function showError($container, message) {
$container.find('.pt-arrivals-body').html('<div class="pt-error"><p>' + message + '</p></div>');
}
/**
* 初始化自动刷新
*/
function initAutoRefresh() {
// 每30秒刷新一次到站信息
setInterval(function() {
$('.pt-arrivals-container').each(function() {
var $container = $(this);
var routeId = $container.data('route');
var stopId = $container.data('stop');
if (routeId && stopId) {
// 静默刷新,不显示加载状态
$.ajax({
url: ptAjaxUrl,
type: 'POST',
data: {
action: 'pt_get_arrivals',
nonce: ptNonce,
route: routeId,
stop: stopId
},
success: function(response) {
if (response.success) {
ptRenderArrivals($container.attr('id'), response.data);
}
}
});
}
});
}, 30000); // 30秒
}
/**
* 防抖函数
*/
function debounce(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
};
}
/**
* 全局搜索处理
*/
function handleGlobalSearch(event) {
// 实现逻辑类似handleStopSearch,但用于全局搜索
console.log('全局搜索:', $(this).val());
}
function handleSearchButtonClick
