首页 / 应用软件 / 实战教程,在网站中添加基于位置的附近门店与服务查询功能

实战教程,在网站中添加基于位置的附近门店与服务查询功能

实战教程:在WordPress网站中添加基于位置的附近门店与服务查询功能

引言:位置服务在互联网应用中的重要性

在移动互联网时代,基于位置的服务(LBS)已成为各类网站和应用程序的标配功能。无论是电商平台、服务型企业还是内容网站,为用户提供附近门店查询、地理位置搜索等功能,都能显著提升用户体验和商业转化率。根据谷歌的研究,超过80%的消费者在寻找本地服务时会使用“附近”搜索功能,而其中76%的用户会在一天内访问相关门店。

WordPress作为全球最流行的内容管理系统,占据了互联网上超过43%的网站份额。然而,许多WordPress网站所有者并未充分利用位置服务的潜力。本教程将详细介绍如何通过代码二次开发,在WordPress网站中实现专业的附近门店与服务查询功能,无需依赖昂贵的第三方插件,同时保持对功能的完全控制。

第一部分:前期准备与环境配置

1.1 功能需求分析与设计

在开始编码之前,我们需要明确功能需求。一个完整的附近门店查询系统应包含以下核心功能:

  • 门店信息管理(名称、地址、坐标、联系方式、营业时间等)
  • 用户位置获取(自动检测或手动输入)
  • 距离计算与排序算法
  • 地图可视化展示
  • 筛选与搜索功能
  • 响应式设计,支持移动设备

1.2 开发环境搭建

确保你的WordPress开发环境满足以下要求:

  • WordPress 5.0以上版本
  • PHP 7.4以上(推荐8.0+)
  • MySQL 5.6以上或MariaDB 10.1以上
  • 启用Apache/Nginx的rewrite模块
  • 代码编辑器(VS Code、PHPStorm等)

1.3 获取地图API密钥

我们将使用Google Maps API作为地图服务,你也可以选择百度地图、高德地图等国内服务。以Google Maps为例:

  1. 访问Google Cloud Console(console.cloud.google.com)
  2. 创建新项目或选择现有项目
  3. 启用"Maps JavaScript API"、"Places API"和"Geocoding API"
  4. 创建API密钥并设置限制(推荐限制HTTP引用来源)

第二部分:数据库设计与门店管理

2.1 创建自定义数据表

虽然可以使用WordPress的自定义文章类型(CPT)存储门店信息,但为了更好的性能和查询效率,我们创建独立的数据表:

// 在插件激活时创建数据表
function create_store_locations_table() {
    global $wpdb;
    
    $table_name = $wpdb->prefix . 'store_locations';
    $charset_collate = $wpdb->get_charset_collate();
    
    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        store_name varchar(255) NOT NULL,
        address text NOT NULL,
        city varchar(100),
        state varchar(100),
        country varchar(100),
        postal_code varchar(20),
        latitude decimal(10,8) NOT NULL,
        longitude decimal(11,8) NOT NULL,
        phone varchar(30),
        email varchar(100),
        website varchar(255),
        description text,
        opening_hours text,
        categories varchar(255),
        featured tinyint(1) DEFAULT 0,
        active tinyint(1) DEFAULT 1,
        created_at datetime DEFAULT CURRENT_TIMESTAMP,
        updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (id),
        KEY latitude_longitude (latitude, longitude),
        KEY city_state (city, state),
        KEY categories (categories(191))
    ) $charset_collate;";
    
    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}
register_activation_hook(__FILE__, 'create_store_locations_table');

2.2 创建后台管理界面

为方便管理门店信息,我们需要在WordPress后台创建管理界面:

// 添加管理菜单
add_action('admin_menu', 'register_store_locations_menu');

function register_store_locations_menu() {
    add_menu_page(
        '门店管理',
        '门店位置',
        'manage_options',
        'store-locations',
        'store_locations_admin_page',
        'dashicons-location-alt',
        30
    );
    
    add_submenu_page(
        'store-locations',
        '添加新门店',
        '添加新门店',
        'manage_options',
        'add-new-store',
        'add_new_store_page'
    );
    
    add_submenu_page(
        'store-locations',
        '门店分类',
        '分类管理',
        'manage_options',
        'store-categories',
        'store_categories_page'
    );
}

// 主管理页面
function store_locations_admin_page() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'store_locations';
    
    // 处理批量操作
    if (isset($_POST['action']) && $_POST['action'] == 'bulk_delete') {
        // 验证nonce和权限
        check_admin_referer('bulk_action_nonce');
        
        if (!current_user_can('manage_options')) {
            wp_die('权限不足');
        }
        
        if (isset($_POST['store_ids'])) {
            $ids = array_map('intval', $_POST['store_ids']);
            $ids_placeholder = implode(',', array_fill(0, count($ids), '%d'));
            $wpdb->query($wpdb->prepare(
                "DELETE FROM $table_name WHERE id IN ($ids_placeholder)",
                $ids
            ));
            echo '<div class="notice notice-success"><p>已删除选中的门店</p></div>';
        }
    }
    
    // 分页查询
    $per_page = 20;
    $current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
    $offset = ($current_page - 1) * $per_page;
    
    $total_items = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
    $stores = $wpdb->get_results($wpdb->prepare(
        "SELECT * FROM $table_name ORDER BY created_at DESC LIMIT %d OFFSET %d",
        $per_page, $offset
    ));
    
    // 显示管理界面
    ?>
    <div class="wrap">
        <h1 class="wp-heading-inline">门店管理</h1>
        <a href="<?php echo admin_url('admin.php?page=add-new-store'); ?>" class="page-title-action">添加新门店</a>
        
        <form method="post" action="">
            <?php wp_nonce_field('bulk_action_nonce'); ?>
            <input type="hidden" name="action" value="bulk_delete">
            
            <div class="tablenav top">
                <div class="alignleft actions bulkactions">
                    <select name="bulk_action">
                        <option value="-1">批量操作</option>
                        <option value="delete">删除</option>
                    </select>
                    <input type="submit" class="button action" value="应用">
                </div>
                
                <div class="tablenav-pages">
                    <?php
                    $total_pages = ceil($total_items / $per_page);
                    echo paginate_links(array(
                        'base' => add_query_arg('paged', '%#%'),
                        'format' => '',
                        'prev_text' => '&laquo;',
                        'next_text' => '&raquo;',
                        'total' => $total_pages,
                        'current' => $current_page
                    ));
                    ?>
                </div>
            </div>
            
            <table class="wp-list-table widefat fixed striped">
                <thead>
                    <tr>
                        <td class="manage-column column-cb check-column">
                            <input type="checkbox" id="cb-select-all-1">
                        </td>
                        <th>ID</th>
                        <th>门店名称</th>
                        <th>地址</th>
                        <th>城市</th>
                        <th>电话</th>
                        <th>状态</th>
                        <th>操作</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($stores as $store): ?>
                    <tr>
                        <th scope="row" class="check-column">
                            <input type="checkbox" name="store_ids[]" value="<?php echo $store->id; ?>">
                        </th>
                        <td><?php echo $store->id; ?></td>
                        <td>
                            <strong><?php echo esc_html($store->store_name); ?></strong>
                            <?php if ($store->featured): ?>
                                <span class="dashicons dashicons-star-filled" style="color:#ffb900;"></span>
                            <?php endif; ?>
                        </td>
                        <td><?php echo esc_html($store->address); ?></td>
                        <td><?php echo esc_html($store->city); ?></td>
                        <td><?php echo esc_html($store->phone); ?></td>
                        <td>
                            <?php if ($store->active): ?>
                                <span class="status-active">启用</span>
                            <?php else: ?>
                                <span class="status-inactive">禁用</span>
                            <?php endif; ?>
                        </td>
                        <td>
                            <a href="<?php echo admin_url('admin.php?page=add-new-store&id=' . $store->id); ?>">编辑</a> |
                            <a href="#" class="delete-store" data-id="<?php echo $store->id; ?>">删除</a>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </form>
    </div>
    
    <script>
    jQuery(document).ready(function($) {
        $('.delete-store').on('click', function(e) {
            e.preventDefault();
            if (confirm('确定要删除这个门店吗?')) {
                var storeId = $(this).data('id');
                $.post(ajaxurl, {
                    action: 'delete_store',
                    store_id: storeId,
                    nonce: '<?php echo wp_create_nonce("delete_store_nonce"); ?>'
                }, function(response) {
                    if (response.success) {
                        location.reload();
                    } else {
                        alert('删除失败:' + response.data);
                    }
                });
            }
        });
    });
    </script>
    <?php
}

第三部分:核心功能开发

3.1 地理位置编码与坐标获取

将地址转换为地理坐标(经纬度)是位置服务的基础:

class LocationGeocoder {
    private $api_key;
    private $cache_time = 604800; // 缓存7天
    
    public function __construct($api_key) {
        $this->api_key = $api_key;
    }
    
    public function geocode_address($address) {
        // 检查缓存
        $cache_key = 'geocode_' . md5($address);
        $cached = get_transient($cache_key);
        
        if ($cached !== false) {
            return $cached;
        }
        
        // 调用Google Geocoding API
        $url = 'https://maps.googleapis.com/maps/api/geocode/json';
        $params = array(
            'address' => urlencode($address),
            'key' => $this->api_key,
            'language' => 'zh-CN'
        );
        
        $response = wp_remote_get($url . '?' . http_build_query($params));
        
        if (is_wp_error($response)) {
            return array(
                'success' => false,
                'error' => $response->get_error_message()
            );
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        if ($data['status'] !== 'OK') {
            return array(
                'success' => false,
                'error' => '地理编码失败:' . $data['status']
            );
        }
        
        $result = $data['results'][0];
        $location = array(
            'success' => true,
            'latitude' => $result['geometry']['location']['lat'],
            'longitude' => $result['geometry']['location']['lng'],
            'formatted_address' => $result['formatted_address'],
            'address_components' => $this->parse_address_components($result['address_components'])
        );
        
        // 缓存结果
        set_transient($cache_key, $location, $this->cache_time);
        
        return $location;
    }
    
    private function parse_address_components($components) {
        $parsed = array();
        foreach ($components as $component) {
            foreach ($component['types'] as $type) {
                $parsed[$type] = $component['long_name'];
            }
        }
        return $parsed;
    }
    
    public function calculate_distance($lat1, $lon1, $lat2, $lon2, $unit = 'km') {
        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + 
                cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        
        switch ($unit) {
            case 'km':
                return $miles * 1.609344;
            case 'm':
                return $miles * 1.609344 * 1000;
            default:
                return $miles;
        }
    }
}

3.2 附近门店查询算法

实现高效的附近门店搜索算法:

class NearbyStoresFinder {
    private $db;
    private $earth_radius = 6371; // 地球半径,单位公里
    
    public function __construct() {
        global $wpdb;
        $this->db = $wpdb;
    }
    
    public function find_nearby_stores($latitude, $longitude, $radius_km = 10, $limit = 20, $category = null) {
        $table_name = $this->db->prefix . 'store_locations';
        
        // 使用Haversine公式计算距离
        $haversine = "(
            $this->earth_radius * acos(
                cos(radians(%f)) * 
                cos(radians(latitude)) * 
                cos(radians(longitude) - radians(%f)) + 
                sin(radians(%f)) * 
                sin(radians(latitude))
            )
        )";
        
        $query = "SELECT *,
                  $haversine AS distance
                  FROM $table_name
                  WHERE active = 1";
        
        $params = array($latitude, $longitude, $latitude);
        
        // 添加分类筛选
        if ($category) {
            $query .= " AND FIND_IN_SET(%s, categories)";
            $params[] = $category;
        }
        
        $query .= " HAVING distance <= %d
                   ORDER BY distance ASC
                   LIMIT %d";
        
        $params[] = $radius_km;
        $params[] = $limit;
        
        $prepared_query = $this->db->prepare($query, $params);
        return $this->db->get_results($prepared_query);
    }
    
    public function find_stores_by_bounds($north, $south, $east, $west, $category = null) {
        $table_name = $this->db->prefix . 'store_locations';
        
        $query = "SELECT * FROM $table_name
                  WHERE active = 1
                  AND latitude BETWEEN %f AND %f
                  AND longitude BETWEEN %f AND %f";
        
        $params = array($south, $north, $west, $east);
        
        if ($category) {
            $query .= " AND FIND_IN_SET(%s, categories)";
            $params[] = $category;
        }
        
        $query .= " ORDER BY featured DESC, store_name ASC
                   LIMIT 100";
        
        $prepared_query = $this->db->prepare($query, $params);
        return $this->db->get_results($prepared_query);
    }
}

第四部分:前端界面与用户体验

4.1 创建搜索表单与结果展示

class StoreLocatorFrontend {
    private $api_key;
    
    public function __construct($api_key) {
        $this->api_key = $api_key;
        add_shortcode('store_locator', array($this, 'render_store_locator'));
        add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
        add_action('wp_ajax_search_nearby_stores', array($this, 'ajax_search_nearby_stores'));
        add_action('wp_ajax_nopriv_search_nearby_stores', array($this, 'ajax_search_nearby_stores'));
    }
    
    public function enqueue_scripts() {
        global $post;
        if (has_shortcode($post->post_content, 'store_locator')) {
            // 加载Google Maps API
            wp_enqueue_script(
                'google-maps-api',
                'https://maps.googleapis.com/maps/api/js?key=' . $this->api_key . '&libraries=places&language=zh-CN',
                array(),
                null,
                true
            );
            
            // 加载自定义脚本
            wp_enqueue_script(
                'store-locator-frontend',
                plugin_dir_url(__FILE__) . 'js/store-locator-frontend.js',
                array('jquery', 'google-maps-api'),
                '1.0.0',
                true
            );
            
            // 加载样式
            wp_enqueue_style(
                'store-locator-style',
                plugin_dir_url(__FILE__) . 'css/store-locator.css',
                array(),
                '1.0.0'
            );
            
            // 传递数据到前端
            wp_localize_script('store-locator-frontend', 'storeLocatorData', array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('store_locator_nonce'),
                'default_lat' => 39.9042, // 默认坐标(北京)
                'default_lng' => 116.4074,
                'default_radius' => 10,

4.2 构建响应式前端界面

public function render_store_locator($atts) {
    $atts = shortcode_atts(array(
        'default_radius' => 10,
        'max_results' => 50,
        'show_categories' => true,
        'show_filters' => true
    ), $atts);
    
    // 获取所有门店分类
    $categories = $this->get_store_categories();
    
    ob_start();
    ?>
    
    <div class="store-locator-container">
        <div class="store-locator-header">
            <h2>附近门店查询</h2>
            <p>输入地址或使用当前位置查找附近的门店</p>
        </div>
        
        <div class="store-locator-main">
            <!-- 搜索面板 -->
            <div class="search-panel">
                <div class="search-form">
                    <div class="form-group">
                        <label for="store-search-input">搜索地址或位置:</label>
                        <div class="search-input-wrapper">
                            <input type="text" 
                                   id="store-search-input" 
                                   class="search-input" 
                                   placeholder="输入地址、城市或邮政编码">
                            <button type="button" id="use-current-location" class="location-btn">
                                <span class="dashicons dashicons-location"></span>
                                使用当前位置
                            </button>
                        </div>
                    </div>
                    
                    <?php if ($atts['show_categories'] && !empty($categories)): ?>
                    <div class="form-group">
                        <label>服务分类:</label>
                        <div class="categories-filter">
                            <select id="store-category" class="category-select">
                                <option value="">所有分类</option>
                                <?php foreach ($categories as $category): ?>
                                <option value="<?php echo esc_attr($category); ?>">
                                    <?php echo esc_html($category); ?>
                                </option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                    </div>
                    <?php endif; ?>
                    
                    <div class="form-group">
                        <label>搜索半径:</label>
                        <div class="radius-slider">
                            <input type="range" 
                                   id="search-radius" 
                                   min="1" 
                                   max="50" 
                                   value="<?php echo esc_attr($atts['default_radius']); ?>"
                                   class="radius-range">
                            <span id="radius-value"><?php echo esc_attr($atts['default_radius']); ?> 公里</span>
                        </div>
                    </div>
                    
                    <div class="form-actions">
                        <button type="button" id="search-stores" class="search-btn">
                            搜索附近门店
                        </button>
                        <button type="button" id="reset-search" class="reset-btn">
                            重置搜索
                        </button>
                    </div>
                </div>
                
                <!-- 搜索结果列表 -->
                <div class="results-panel">
                    <div class="results-header">
                        <h3>搜索结果</h3>
                        <div class="results-count">
                            找到 <span id="results-count">0</span> 个门店
                        </div>
                    </div>
                    
                    <div class="results-list" id="stores-results">
                        <div class="no-results">
                            <p>请输入位置开始搜索</p>
                        </div>
                    </div>
                    
                    <div class="results-pagination" id="results-pagination" style="display: none;">
                        <button id="prev-page" class="pagination-btn" disabled>上一页</button>
                        <span id="page-info">第 1 页</span>
                        <button id="next-page" class="pagination-btn" disabled>下一页</button>
                    </div>
                </div>
            </div>
            
            <!-- 地图显示区域 -->
            <div class="map-panel">
                <div id="store-locator-map"></div>
                <div class="map-controls">
                    <button id="zoom-in" class="map-control-btn">+</button>
                    <button id="zoom-out" class="map-control-btn">-</button>
                    <button id="reset-map" class="map-control-btn">重置视图</button>
                </div>
            </div>
        </div>
        
        <!-- 门店详情模态框 -->
        <div id="store-details-modal" class="store-modal">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 id="modal-store-name"></h3>
                    <button class="modal-close">&times;</button>
                </div>
                <div class="modal-body">
                    <div class="store-info-grid">
                        <div class="info-item">
                            <span class="dashicons dashicons-location"></span>
                            <div>
                                <strong>地址:</strong>
                                <span id="modal-store-address"></span>
                            </div>
                        </div>
                        <div class="info-item">
                            <span class="dashicons dashicons-phone"></span>
                            <div>
                                <strong>电话:</strong>
                                <span id="modal-store-phone"></span>
                            </div>
                        </div>
                        <div class="info-item">
                            <span class="dashicons dashicons-clock"></span>
                            <div>
                                <strong>营业时间:</strong>
                                <div id="modal-store-hours"></div>
                            </div>
                        </div>
                        <div class="info-item">
                            <span class="dashicons dashicons-admin-site"></span>
                            <div>
                                <strong>距离:</strong>
                                <span id="modal-store-distance"></span>
                            </div>
                        </div>
                    </div>
                    <div class="store-description">
                        <h4>门店介绍</h4>
                        <p id="modal-store-description"></p>
                    </div>
                    <div class="store-actions">
                        <a href="#" id="modal-get-directions" class="action-btn" target="_blank">
                            <span class="dashicons dashicons-car"></span>
                            获取路线
                        </a>
                        <a href="#" id="modal-call-store" class="action-btn">
                            <span class="dashicons dashicons-phone"></span>
                            拨打电话
                        </a>
                        <a href="#" id="modal-visit-website" class="action-btn" target="_blank">
                            <span class="dashicons dashicons-external"></span>
                            访问网站
                        </a>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 加载状态 -->
    <div id="loading-overlay" style="display: none;">
        <div class="loading-spinner"></div>
        <p>正在搜索附近门店...</p>
    </div>
    
    <?php
    return ob_get_clean();
}

private function get_store_categories() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'store_locations';
    
    $categories = $wpdb->get_col(
        "SELECT DISTINCT categories FROM $table_name WHERE active = 1 AND categories IS NOT NULL"
    );
    
    $all_categories = array();
    foreach ($categories as $category_string) {
        $cat_array = explode(',', $category_string);
        foreach ($cat_array as $category) {
            $category = trim($category);
            if ($category && !in_array($category, $all_categories)) {
                $all_categories[] = $category;
            }
        }
    }
    
    sort($all_categories);
    return $all_categories;
}

4.3 前端JavaScript交互逻辑

// store-locator-frontend.js
(function($) {
    'use strict';
    
    class StoreLocator {
        constructor() {
            this.map = null;
            this.markers = [];
            this.infoWindow = null;
            this.currentLocation = null;
            this.currentRadius = parseInt(storeLocatorData.default_radius);
            this.currentCategory = '';
            this.currentPage = 1;
            this.storesPerPage = 10;
            this.allStores = [];
            
            this.init();
        }
        
        init() {
            this.initMap();
            this.bindEvents();
            this.initAutocomplete();
        }
        
        initMap() {
            const defaultLocation = {
                lat: parseFloat(storeLocatorData.default_lat),
                lng: parseFloat(storeLocatorData.default_lng)
            };
            
            this.map = new google.maps.Map(document.getElementById('store-locator-map'), {
                zoom: 12,
                center: defaultLocation,
                mapTypeControl: true,
                streetViewControl: false,
                fullscreenControl: true,
                styles: this.getMapStyles()
            });
            
            this.infoWindow = new google.maps.InfoWindow({
                maxWidth: 300
            });
            
            // 添加初始标记
            this.addInitialMarker(defaultLocation);
        }
        
        getMapStyles() {
            return [
                {
                    featureType: 'poi.business',
                    stylers: [{ visibility: 'on' }]
                },
                {
                    featureType: 'transit',
                    elementType: 'labels.icon',
                    stylers: [{ visibility: 'off' }]
                }
            ];
        }
        
        addInitialMarker(position) {
            const marker = new google.maps.Marker({
                position: position,
                map: this.map,
                icon: {
                    path: google.maps.SymbolPath.CIRCLE,
                    scale: 8,
                    fillColor: '#4285F4',
                    fillOpacity: 1,
                    strokeColor: '#FFFFFF',
                    strokeWeight: 2
                },
                title: '中心位置'
            });
            
            this.markers.push(marker);
        }
        
        bindEvents() {
            // 搜索按钮
            $('#search-stores').on('click', () => this.searchStores());
            
            // 使用当前位置
            $('#use-current-location').on('click', () => this.getCurrentLocation());
            
            // 半径滑块
            $('#search-radius').on('input', (e) => {
                const radius = parseInt(e.target.value);
                $('#radius-value').text(radius + ' 公里');
                this.currentRadius = radius;
            });
            
            // 分类选择
            $('#store-category').on('change', (e) => {
                this.currentCategory = e.target.value;
            });
            
            // 重置搜索
            $('#reset-search').on('click', () => this.resetSearch());
            
            // 分页按钮
            $('#prev-page').on('click', () => this.changePage(-1));
            $('#next-page').on('click', () => this.changePage(1));
            
            // 地图控制
            $('#zoom-in').on('click', () => this.map.setZoom(this.map.getZoom() + 1));
            $('#zoom-out').on('click', () => this.map.setZoom(this.map.getZoom() - 1));
            $('#reset-map').on('click', () => this.resetMapView());
            
            // 模态框关闭
            $('.modal-close, #store-details-modal').on('click', (e) => {
                if (e.target === e.currentTarget) {
                    this.closeModal();
                }
            });
            
            // 键盘事件
            $(document).on('keyup', (e) => {
                if (e.key === 'Escape') this.closeModal();
            });
        }
        
        initAutocomplete() {
            const input = document.getElementById('store-search-input');
            const autocomplete = new google.maps.places.Autocomplete(input, {
                types: ['geocode', 'establishment'],
                componentRestrictions: { country: 'cn' }
            });
            
            autocomplete.addListener('place_changed', () => {
                const place = autocomplete.getPlace();
                if (!place.geometry) {
                    alert('未找到该位置,请尝试更详细的地址');
                    return;
                }
                
                this.currentLocation = {
                    lat: place.geometry.location.lat(),
                    lng: place.geometry.location.lng()
                };
                
                this.updateMapCenter(this.currentLocation);
                this.searchStores();
            });
        }
        
        getCurrentLocation() {
            if (!navigator.geolocation) {
                alert('您的浏览器不支持地理位置功能');
                return;
            }
            
            $('#use-current-location').prop('disabled', true).text('定位中...');
            
            navigator.geolocation.getCurrentPosition(
                (position) => {
                    this.currentLocation = {
                        lat: position.coords.latitude,
                        lng: position.coords.longitude
                    };
                    
                    // 反向地理编码获取地址
                    const geocoder = new google.maps.Geocoder();
                    geocoder.geocode({ location: this.currentLocation }, (results, status) => {
                        if (status === 'OK' && results[0]) {
                            $('#store-search-input').val(results[0].formatted_address);
                        }
                    });
                    
                    this.updateMapCenter(this.currentLocation);
                    this.searchStores();
                    
                    $('#use-current-location').prop('disabled', false).html(
                        '<span class="dashicons dashicons-location"></span> 使用当前位置'
                    );
                },
                (error) => {
                    let errorMessage = '无法获取您的位置:';
                    switch(error.code) {
                        case error.PERMISSION_DENIED:
                            errorMessage += '用户拒绝了位置请求';
                            break;
                        case error.POSITION_UNAVAILABLE:
                            errorMessage += '位置信息不可用';
                            break;
                        case error.TIMEOUT:
                            errorMessage += '获取位置超时';
                            break;
                        default:
                            errorMessage += '未知错误';
                    }
                    alert(errorMessage);
                    
                    $('#use-current-location').prop('disabled', false).html(
                        '<span class="dashicons dashicons-location"></span> 使用当前位置'
                    );
                },
                {
                    enableHighAccuracy: true,
                    timeout: 10000,
                    maximumAge: 0
                }
            );
        }
        
        updateMapCenter(location) {
            this.map.setCenter(location);
            
            // 更新中心标记
            if (this.markers.length > 0) {
                this.markers[0].setPosition(location);
            }
        }
        
        async searchStores() {
            if (!this.currentLocation) {
                alert('请先选择或输入一个位置');
                return;
            }
            
            this.showLoading(true);
            
            try {
                const response = await $.ajax({
                    url: storeLocatorData.ajax_url,
                    method: 'POST',
                    data: {
                        action: 'search_nearby_stores',
                        nonce: storeLocatorData.nonce,
                        latitude: this.currentLocation.lat,
                        longitude: this.currentLocation.lng,
                        radius: this.currentRadius,
                        category: this.currentCategory
                    }
                });
                
                if (response.success) {
                    this.allStores = response.data.stores;
                    this.currentPage = 1;
                    this.displayResults();
                    this.displayMarkers();
                    this.updatePagination();
                } else {
                    throw new Error(response.data);
                }
            } catch (error) {
                console.error('搜索失败:', error);
                alert('搜索失败,请稍后重试');
            } finally {
                this.showLoading(false);
            }
        }
        
        displayResults() {
            const $resultsContainer = $('#stores-results');
            const startIndex = (this.currentPage - 1) * this.storesPerPage;
            const endIndex = startIndex + this.storesPerPage;
            const currentStores = this.allStores.slice(startIndex, endIndex);
            
            if (currentStores.length === 0) {
                $resultsContainer.html(`
                    <div class="no-results">
                        <p>在指定范围内未找到门店</p>
                        <p>尝试扩大搜索半径或选择其他位置</p>
                    </div>
                `);
                return;
            }
            
            let html = '';
            currentStores.forEach((store, index) => {
                html += this.getStoreCardHtml(store, startIndex + index + 1);
            });
            
            $resultsContainer.html(html);
            $('#results-count').text(this.allStores.length);
            
            // 绑定卡片点击事件
            $('.store-card').on('click', (e) => {
                const storeId = $(e.currentTarget).data('store-id');
                const store = this.allStores.find(s => s.id == storeId);
                if (store) {
                    this.showStoreDetails(store);
                }
            });
        }
        
        getStoreCardHtml(store, index) {
            const distance = parseFloat(store.distance).toFixed(1);
            const featuredClass = store.featured ? 'featured' : '';
            
            return `
                <div class="store-card ${featuredClass}" data-store-id="${store.id}">
                    <div class="store-card-header">
                        <span class="store-index">${index}</span>
                        <h4 class="store-name">${this.escapeHtml(store.store_name)}</h4>
                        ${store.featured ? '<span class="featured-badge">推荐</span>' : ''}
                    </div>
                    <div class="store-card-body">
                        <div class="store-info">
                            <p class="store-address">
                                <span class="dashicons dashicons-location"></span>
                                ${this.escapeHtml(store.address)}
                            </p>
                            ${store.phone ? `
                                <p class="store-phone">
                                    <span class="dashicons dashicons-phone"></span>
                                    ${this.escapeHtml(store.phone)}
                                </p>
                            ` : ''}
                            <p class="store-distance">
                                <span class="dashicons dashicons-admin-site"></span>
                                距离:${distance} 公里
                            </p>
                        </div>
                        <div class="store-actions">
                            <button class="btn-details" data-store-id="${store.id}">
                                查看详情
                            </button>
                            <button class="btn-directions" data-store-id="${store.id}">
                                路线规划
                            </button>
                        </div>
                    </div>
                </div>
            `;
        }
        
        displayMarkers() {
            // 清除现有标记
            this.clearMarkers();
            
            // 添加新标记
            this.allStores.forEach(store => {
                const marker = new google.maps.Marker({
                    position: {
                        lat: parseFloat(store.latitude),
                        lng: parseFloat(store.longitude)
                    },
                    map: this.map,
                    title: store.store_name,
                    icon: this.getMarkerIcon(store.featured)
                });
                
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5158.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部