专为WooCommerce商店设计的全面搜索解决方案,极大提升您网站的产品搜索体验和功能。无论是提高用户体验还是增加转化率,本插件都能满足您的需求。
= 主要功能 =
* **智能搜索建议** – 当用户输入时实时显示相关产品建议,包含产品图片、价格和库存状态
* **模糊搜索匹配** – 即使用户拼写错误或使用近似词也能找到相关产品,特别适合中文拼音和多音字搜索
* **产品元标签自动生成** – 为产品页面自动生成SEO友好的元标签和结构化数据,提高搜索引擎可见度
* **高级产品过滤** – 允许用户通过价格、分类、评分、库存状态等多维度筛选产品
* **详细搜索分析** – 提供全面的搜索数据分析,包括热门搜索词、零结果搜索词和搜索趋势
* **响应式设计** – 完美适配各种屏幕尺寸,移动设备体验优秀
相关文章: WooCommerce 页脚增强器
<?php /** * Plugin Name: BDFG 产品搜索增强工具 * Plugin URI: https://beiduofengou.net/2025/01/12/bdfg-product-search/ * Description: 增强产品搜索体验,包括智能搜索建议、模糊搜索、元标签自动生成和高级过滤功能。 * Version: 1.0.1 * Author: BeiDuoFenGou * Author URI: https://beiduofengou.net * Text Domain: bdfg-product-search * Domain Path: /languages * Requires at least: 5.6 * Requires PHP: 7.4 * License: GPL v2 or later */ // 防止直接访问 if (!defined('ABSPATH')) { exit; // 直接访问退出 } // 定义插件常量 define('BDFG_PRODUCT_SEARCH_VERSION', '1.0.1'); define('BDFG_PRODUCT_SEARCH_PATH', plugin_dir_path(__FILE__)); define('BDFG_PRODUCT_SEARCH_URL', plugin_dir_url(__FILE__)); define('BDFG_PRODUCT_SEARCH_FILE', __FILE__); define('BDFG_PRODUCT_SEARCH_BASENAME', plugin_basename(__FILE__)); // 引入核心文件 require_once BDFG_PRODUCT_SEARCH_PATH . 'includes/class-bdfg-product-search.php'; // 初始化插件 function bdfg_product_search_init() { // 加载文本域 load_plugin_textdomain('bdfg-product-search', false, dirname(plugin_basename(__FILE__)) . '/languages/'); // 启动插件 BDFG_Product_Search::get_instance(); } add_action('plugins_loaded', 'bdfg_product_search_init'); // 插件激活时执行的操作 function bdfg_product_search_activate() { // 确保满足最低要求 if (version_compare(PHP_VERSION, '7.4', '<')) { deactivate_plugins(BDFG_PRODUCT_SEARCH_BASENAME); wp_die('BDFG 产品搜索增强工具需要 PHP 7.4 或更高版本。'); } if (!class_exists('WooCommerce')) { deactivate_plugins(BDFG_PRODUCT_SEARCH_BASENAME); wp_die('BDFG 产品搜索增强工具需要安装并激活 WooCommerce。'); } // 创建数据表等初始化操作 require_once BDFG_PRODUCT_SEARCH_PATH . 'includes/class-bdfg-product-search.php'; $plugin = BDFG_Product_Search::get_instance(); $plugin->activate(); // 刷新重写规则 flush_rewrite_rules(); } register_activation_hook(__FILE__, 'bdfg_product_search_activate'); // 插件停用时执行的操作 function bdfg_product_search_deactivate() { require_once BDFG_PRODUCT_SEARCH_PATH . 'includes/class-bdfg-product-search.php'; $plugin = BDFG_Product_Search::get_instance(); $plugin->deactivate(); // 刷新重写规则 flush_rewrite_rules(); } register_deactivation_hook(__FILE__, 'bdfg_product_search_deactivate');
includes/class-bdfg-product-search.php
<?php /** * BDFG 产品搜索增强工具核心类 * * @package BDFG_Product_Search * @since 1.0.0 */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class BDFG_Product_Search { /** * 单例实例 * * @var BDFG_Product_Search */ private static $instance = null; /** * 插件目录路径 * * @var string */ private $plugin_path; /** * 插件URL * * @var string */ private $plugin_url; /** * 插件版本 * * @var string */ private $version = '1.0.1'; /** * 选项名称 * * @var string */ private $option_name = 'bdfg_product_search_options'; /** * 插件选项 * * @var array */ private $options; /** * 获取单例实例 * * @return BDFG_Product_Search */ public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * 构造函数 */ private function __construct() { $this->plugin_path = BDFG_PRODUCT_SEARCH_PATH; $this->plugin_url = BDFG_PRODUCT_SEARCH_URL; $this->options = get_option($this->option_name, $this->get_default_options()); // 加载依赖文件 $this->load_dependencies(); // 初始化插件 $this->init(); } /** * 加载依赖文件 */ private function load_dependencies() { // 加载管理类 require_once $this->plugin_path . 'includes/class-bdfg-admin.php'; // 加载前端类 require_once $this->plugin_path . 'includes/class-bdfg-frontend.php'; // 加载分析类 require_once $this->plugin_path . 'includes/class-bdfg-analytics.php'; } /** * 获取默认选项 * * @return array */ private function get_default_options() { return array( 'enable_meta_tags' => 'yes', 'enable_autocomplete' => 'yes', 'enable_fuzzy_search' => 'yes', 'enable_filters' => 'yes', 'fuzzy_match_threshold' => 3, 'search_results_limit' => 10, 'cache_lifetime' => 24, // 小时 ); } /** * 初始化插件 */ private function init() { // 初始化管理界面 new BDFG_Admin($this->plugin_path, $this->plugin_url, $this->version, $this->option_name, $this->options); // 初始化前端功能 new BDFG_Frontend($this->plugin_path, $this->plugin_url, $this->version, $this->option_name, $this->options); // 初始化分析功能 new BDFG_Analytics($this->plugin_path, $this->plugin_url, $this->version, $this->option_name, $this->options); // 添加自定义短代码 add_shortcode('bdfg_product_search', array($this, 'search_form_shortcode')); } /** * 插件激活时执行 */ public function activate() { // 如果是第一次激活,保存默认选项 if (!get_option($this->option_name)) { update_option($this->option_name, $this->get_default_options()); } // 创建必要的数据库表 $this->create_tables(); // 创建定时任务 if (!wp_next_scheduled('bdfg_daily_maintenance')) { wp_schedule_event(time(), 'daily', 'bdfg_daily_maintenance'); } } /** * 插件停用时执行 */ public function deactivate() { // 清除定时任务 wp_clear_scheduled_hook('bdfg_daily_maintenance'); } /** * 创建必要的数据库表 */ private function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 创建搜索日志表 $search_logs_table = $wpdb->prefix . 'bdfg_search_logs'; if($wpdb->get_var("SHOW TABLES LIKE '$search_logs_table'") != $search_logs_table) { $sql = "CREATE TABLE $search_logs_table ( id bigint(20) NOT NULL AUTO_INCREMENT, search_term varchar(255) NOT NULL, user_id bigint(20) DEFAULT NULL, search_date datetime DEFAULT CURRENT_TIMESTAMP, results_count int(11) DEFAULT 0, ip_address varchar(100) DEFAULT '', user_agent text, PRIMARY KEY (id), KEY search_term (search_term), KEY search_date (search_date) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } // 创建搜索建议表 $suggestions_table = $wpdb->prefix . 'bdfg_search_suggestions'; if($wpdb->get_var("SHOW TABLES LIKE '$suggestions_table'") != $suggestions_table) { $sql = "CREATE TABLE $suggestions_table ( id bigint(20) NOT NULL AUTO_INCREMENT, search_term varchar(255) NOT NULL, suggestion varchar(255) NOT NULL, frequency int(11) DEFAULT 1, last_updated datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY term_suggestion (search_term, suggestion), KEY search_term (search_term), KEY frequency (frequency) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } } /** * 搜索表单短代码 * * @param array $atts 短代码属性 * @return string 短代码输出 */ public function search_form_shortcode($atts) { $atts = shortcode_atts(array( 'placeholder' => __('搜索BDFG产品...', 'bdfg-product-search'), 'button_text' => __('搜索', 'bdfg-product-search'), 'show_filters' => 'no', ), $atts, 'bdfg_product_search'); ob_start(); ?> <div class="bdfg-search-form-container"> <form role="search" method="get" class="bdfg-search-form" action="<?php echo esc_url(home_url('/')); ?>"> <div class="bdfg-search-input-wrap"> <input type="search" class="bdfg-search-field" placeholder="<?php echo esc_attr($atts['placeholder']); ?>" value="<?php echo get_search_query(); ?>" name="s" autocomplete="off" /> <input type="hidden" name="post_type" value="product" /> <button type="submit" class="bdfg-search-submit"><?php echo esc_html($atts['button_text']); ?><span class="bdfg-search-icon"></span></button> </div> <div class="bdfg-search-results"></div> </form> <?php if ($atts['show_filters'] === 'yes' && $this->options['enable_filters'] === 'yes') { // 显示过滤器 $frontend = new BDFG_Frontend($this->plugin_path, $this->plugin_url, $this->version, $this->option_name, $this->options); $frontend->add_product_filters(); } ?> </div> <?php return ob_get_clean(); } }
includes/class-bdfg-admin.php
相关文章: WordPress优惠券提醒插件系统
<?php /** * BDFG 产品搜索增强工具管理类 * * @package BDFG_Product_Search * @since 1.0.0 */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class BDFG_Admin { /** * 插件目录路径 * * @var string */ private $plugin_path; /** * 插件URL * * @var string */ private $plugin_url; /** * 插件版本 * * @var string */ private $version; /** * 选项名称 * * @var string */ private $option_name; /** * 插件选项 * * @var array */ private $options; /** * 构造函数 * * @param string $plugin_path 插件目录路径 * @param string $plugin_url 插件URL * @param string $version 插件版本 * @param string $option_name 选项名称 * @param array $options 插件选项 */ public function __construct($plugin_path, $plugin_url, $version, $option_name, $options) { $this->plugin_path = $plugin_path; $this->plugin_url = $plugin_url; $this->version = $version; $this->option_name = $option_name; $this->options = $options; // 初始化管理功能 $this->init(); } /** * 初始化管理功能 */ private function init() { // 添加管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 注册设置 add_action('admin_init', array($this, 'register_settings')); // 加载管理样式和脚本 add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts')); // 添加设置链接到插件页面 add_filter('plugin_action_links_' . BDFG_PRODUCT_SEARCH_BASENAME, array($this, 'plugin_action_links')); // 添加插件元信息 add_filter('plugin_row_meta', array($this, 'plugin_row_meta'), 10, 2); } /** * 添加管理菜单 */ public function add_admin_menu() { // 添加主菜单 add_menu_page( __('BDFG 产品搜索', 'bdfg-product-search'), __('BDFG 产品搜索', 'bdfg-product-search'), 'manage_options', 'bdfg-product-search', array($this, 'render_admin_page'), 'dashicons-search', 58 // 在WooCommerce和设置之间显示 ); // 添加子菜单 add_submenu_page( 'bdfg-product-search', __('设置', 'bdfg-product-search'), __('设置', 'bdfg-product-search'), 'manage_options', 'bdfg-product-search', array($this, 'render_admin_page') ); // 添加搜索分析子菜单 add_submenu_page( 'bdfg-product-search', __('搜索分析', 'bdfg-product-search'), __('搜索分析', 'bdfg-product-search'), 'manage_options', 'bdfg-search-analytics', array($this, 'render_analytics_page') ); // 添加帮助文档子菜单 add_submenu_page( 'bdfg-product-search', __('使用帮助', 'bdfg-product-search'), __('使用帮助', 'bdfg-product-search'), 'manage_options', 'bdfg-search-help', array($this, 'render_help_page') ); } /** * 注册插件设置 */ public function register_settings() { register_setting( 'bdfg_product_search_group', $this->option_name, array($this, 'sanitize_options') ); // 常规设置部分 add_settings_section( 'bdfg_product_search_general', __('常规设置', 'bdfg-product-search'), array($this, 'render_general_section'), 'bdfg-product-search' ); // 添加设置字段 add_settings_field( 'enable_meta_tags', __('产品元标签', 'bdfg-product-search'), array($this, 'render_checkbox_field'), 'bdfg-product-search', 'bdfg_product_search_general', array( 'id' => 'enable_meta_tags', 'label' => __('启用自动生成产品元标签', 'bdfg-product-search'), 'description' => __('为产品页面自动生成SEO友好的元标签,提高产品在搜索引擎中的可见度', 'bdfg-product-search'), ) ); add_settings_field( 'enable_autocomplete', __('智能搜索建议', 'bdfg-product-search'), array($this, 'render_checkbox_field'), 'bdfg-product-search', 'bdfg_product_search_general', array( 'id' => 'enable_autocomplete', 'label' => __('启用智能搜索建议功能', 'bdfg-product-search'), 'description' => __('当用户输入搜索词时实时显示相关产品建议,提升用户体验', 'bdfg-product-search'), ) ); add_settings_field( 'enable_fuzzy_search', __('模糊搜索', 'bdfg-product-search'), array($this, 'render_checkbox_field'), 'bdfg-product-search', 'bdfg_product_search_general', array( 'id' => 'enable_fuzzy_search', 'label' => __('启用模糊搜索功能', 'bdfg-product-search'), 'description' => __('即使用户拼写错误也能找到相关产品,适合中文词语变体和拼音搜索', 'bdfg-product-search'), ) ); add_settings_field( 'enable_filters', __('产品过滤器', 'bdfg-product-search'), array($this, 'render_checkbox_field'), 'bdfg-product-search', 'bdfg_product_search_general', array( 'id' => 'enable_filters', 'label' => __('启用高级产品过滤功能', 'bdfg-product-search'), 'description' => __('允许用户通过价格、类别、评分等过滤产品,优化购物体验', 'bdfg-product-search'), ) ); // 高级设置部分 add_settings_section( 'bdfg_product_search_advanced', __('高级设置', 'bdfg-product-search'), array($this, 'render_advanced_section'), 'bdfg-product-search' ); add_settings_field( 'fuzzy_match_threshold', __('模糊匹配阈值', 'bdfg-product-search'), array($this, 'render_number_field'), 'bdfg-product-search', 'bdfg_product_search_advanced', array( 'id' => 'fuzzy_match_threshold', 'label' => __('编辑距离阈值', 'bdfg-product-search'), 'description' => __('较低的值提供更精确的结果,较高的值能找到更多的近似匹配项(建议值:2-5)', 'bdfg-product-search'), 'min' => 1, 'max' => 10, 'step' => 1 ) ); add_settings_field( 'search_results_limit', __('搜索结果数量', 'bdfg-product-search'), array($this, 'render_number_field'), 'bdfg-product-search', 'bdfg_product_search_advanced', array( 'id' => 'search_results_limit', 'label' => __('结果', 'bdfg-product-search'), 'description' => __('自动完成下拉列表中显示的最大搜索结果数量', 'bdfg-product-search'), 'min' => 3, 'max' => 20, 'step' => 1 ) ); add_settings_field( 'cache_lifetime', __('缓存生命周期', 'bdfg-product-search'), array($this, 'render_number_field'), 'bdfg-product-search', 'bdfg_product_search_advanced', array( 'id' => 'cache_lifetime', 'label' => __('小时', 'bdfg-product-search'), 'description' => __('搜索结果缓存的有效期(小时)。设置为0禁用缓存。', 'bdfg-product-search'), 'min' => 0, 'max' => 72, 'step' => 1 ) ); } /** * 清理设置选项 * * @param array $options 提交的选项值 * @return array 清理后的选项值 */ public function sanitize_options($options) { $sanitized = array(); // 布尔选项 $sanitized['enable_meta_tags'] = isset($options['enable_meta_tags']) ? 'yes' : 'no'; $sanitized['enable_autocomplete'] = isset($options['enable_autocomplete']) ? 'yes' : 'no'; $sanitized['enable_fuzzy_search'] = isset($options['enable_fuzzy_search']) ? 'yes' : 'no'; $sanitized['enable_filters'] = isset($options['enable_filters']) ? 'yes' : 'no'; // 数值选项 $sanitized['fuzzy_match_threshold'] = isset($options['fuzzy_match_threshold']) ? intval($options['fuzzy_match_threshold']) : 3; $sanitized['fuzzy_match_threshold'] = max(1, min(10, $sanitized['fuzzy_match_threshold'])); $sanitized['search_results_limit'] = isset($options['search_results_limit']) ? intval($options['search_results_limit']) : 10; $sanitized['search_results_limit'] = max(3, min(20, $sanitized['search_results_limit'])); $sanitized['cache_lifetime'] = isset($options['cache_lifetime']) ? intval($options['cache_lifetime']) : 24; $sanitized['cache_lifetime'] = max(0, min(72, $sanitized['cache_lifetime'])); // 添加设置更新消息 add_settings_error( 'bdfg_product_search_messages', 'bdfg_product_search_message', __('设置已保存。', 'bdfg-product-search'), 'updated' ); return $sanitized; } /** * 渲染常规设置部分 */ public function render_general_section() { echo '<p>' . __('配置BDFG产品搜索增强工具的基本功能。这些选项影响用户在您网站上的搜索体验。', 'bdfg-product-search') . '</p>'; } /** * 渲染高级设置部分 */ public function render_advanced_section() { echo '<p>' . __('这些设置影响BDFG产品搜索的性能和行为。请谨慎修改这些值。', 'bdfg-product-search') . '</p>'; } /** * 渲染复选框字段 * * @param array $args 字段参数 */ public function render_checkbox_field($args) { $id = $args['id']; $label = $args['label']; $description = isset($args['description']) ? $args['description'] : ''; $checked = isset($this->options[$id]) && $this->options[$id] === 'yes' ? 'checked' : ''; echo '<label class="bdfg-switch">'; echo '<input type="checkbox" id="' . esc_attr($id) . '" name="' . esc_attr($this->option_name) . '[' . esc_attr($id) . ']" ' . $checked . ' />'; echo '<span class="bdfg-slider"></span>'; echo '</label>'; echo '<label for="' . esc_attr($id) . '" class="bdfg-checkbox-label">' . esc_html($label) . '</label>'; if (!empty($description)) { echo '<p class="description">' . esc_html($description) . '</p>'; } } /** * 渲染数字字段 * * @param array $args 字段参数 */ public function render_number_field($args) { $id = $args['id']; $label = $args['label']; $description = isset($args['description']) ? $args['description'] : ''; $min = isset($args['min']) ? $args['min'] : 0; $max = isset($args['max']) ? $args['max'] : 999; $step = isset($args['step']) ? $args['step'] : 1; $value = isset($this->options[$id]) ? $this->options[$id] : ''; echo '<div class="bdfg-number-field">'; echo '<input type="number" id="' . esc_attr($id) . '" name="' . esc_attr($this->option_name) . '[' . esc_attr($id) . ']" value="' . esc_attr($value) . '" min="' . esc_attr($min) . '" max="' . esc_attr($max) . '" step="' . esc_attr($step) . '" />'; echo ' <label for="' . esc_attr($id) . '">' . esc_html($label) . '</label>'; echo '</div>'; if (!empty($description)) { echo '<p class="description">' . esc_html($description) . '</p>'; } } /** * 渲染管理页面 */ public function render_admin_page() { if (!current_user_can('manage_options')) { return; } ?> <div class="wrap bdfg-admin-wrap"> <div class="bdfg-admin-header"> <div class="bdfg-logo"> <img src="<?php echo esc_url($this->plugin_url . 'assets/images/bdfg-logo.png'); ?>" alt="BDFG Logo"> <h1><?php _e('BDFG 产品搜索增强工具', 'bdfg-product-search'); ?></h1> </div> <div class="bdfg-version"> <span><?php _e('版本', 'bdfg-product-search'); ?>: <?php echo esc_html($this->version); ?></span> </div> </div> <div class="bdfg-admin-content"> <?php settings_errors('bdfg_product_search_messages'); ?> <div class="bdfg-admin-tabs"> <a class="nav-tab nav-tab-active" href="#settings"><?php _e('设置', 'bdfg-product-search'); ?></a> <a class="nav-tab" href="#shortcodes"><?php _e('短代码', 'bdfg-product-search'); ?></a> <a class="nav-tab" href="#support"><?php _e('支持', 'bdfg-product-search'); ?></a> </div> <div class="bdfg-tab-content active" id="settings-content"> <form method="post" action="options.php"> <?php settings_fields('bdfg_product_search_group'); do_settings_sections('bdfg-product-search'); submit_button(__('保存设置', 'bdfg-product-search')); ?> </form> </div> <div class="bdfg-tab-content" id="shortcodes-content"> <h2><?php _e('短代码使用说明', 'bdfg-product-search'); ?></h2> <div class="bdfg-shortcode-box"> <h3><?php _e('基本搜索表单', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search]</code> <p class="description"><?php _e('在任何页面或文章中插入BDFG高级产品搜索表单', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-shortcode-box"> <h3><?php _e('自定义搜索表单', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search placeholder="查找BDFG产品..." button_text="搜索" show_filters="yes"]</code> <p class="description"><?php _e('自定义搜索表单,包括占位文本、按钮文本和是否显示过滤器', 'bdfg-product-search'); ?></p> <table class="bdfg-shortcode-attrs"> <thead> <tr> <th><?php _e('参数', 'bdfg-product-search'); ?></th> <th><?php _e('默认值', 'bdfg-product-search'); ?></th> <th><?php _e('描述', 'bdfg-product-search'); ?></th> </tr> </thead> <tbody> <tr> <td><code>placeholder</code></td> <td><?php _e('搜索BDFG产品...', 'bdfg-product-search'); ?></td> <td><?php _e('搜索框占位文本', 'bdfg-product-search'); ?></td> </tr> <tr> <td><code>button_text</code></td> <td><?php _e('搜索', 'bdfg-product-search'); ?></td> <td><?php _e('搜索按钮文本', 'bdfg-product-search'); ?></td> </tr> <tr> <td><code>show_filters</code></td> <td>no</td> <td><?php _e('是否显示产品过滤器(yes/no)', 'bdfg-product-search'); ?></td> </tr> </tbody> </table> </div> </div> <div class="bdfg-tab-content" id="support-content"> <h2><?php _e('支持与帮助', 'bdfg-product-search'); ?></h2> <div class="bdfg-support-box"> <h3><?php _e('官方网站', 'bdfg-product-search'); ?></h3> <p><?php _e('访问我们的官方网站获取更多信息和资源:', 'bdfg-product-search'); ?> <a href="https://beiduofengou.net" target="_blank">https://beiduofengou.net</a></p> </div> <div class="bdfg-support-box"> <h3><?php _e('使用文档', 'bdfg-product-search'); ?></h3> <p><?php _e('查看详细的使用文档:', 'bdfg-product-search'); ?> <a href="https://beiduofengou.net/docs/bdfg-product-search" target="_blank"><?php _e('BDFG产品搜索增强工具文档', 'bdfg-product-search'); ?></a></p> </div> <div class="bdfg-support-box"> <h3><?php _e('联系我们', 'bdfg-product-search'); ?></h3> <p><?php _e('如遇到任何问题,请联系我们的支持团队:', 'bdfg-product-search'); ?> <a href="mailto:[email protected]">[email protected]</a></p> </div> </div> </div> <div class="bdfg-admin-footer"> <p><?php _e('由 BeiDuoFenGou 倾情打造', 'bdfg-product-search'); ?> © <?php echo date('Y'); ?> <a href="https://beiduofengou.net" target="_blank">beiduofengou.net</a></p> </div> </div> <script> jQuery(document).ready(function($) { // 标签切换 $('.bdfg-admin-tabs .nav-tab').click(function(e) { e.preventDefault(); var target = $(this).attr('href').replace('#', ''); $('.bdfg-admin-tabs .nav-tab').removeClass('nav-tab-active'); $(this).addClass('nav-tab-active'); $('.bdfg-tab-content').removeClass('active'); $('#' + target + '-content').addClass('active'); }); // 复制短代码到剪贴板 $('.bdfg-shortcode').click(function() { var shortcode = $(this).text(); var tempTextarea = $('<textarea>'); $('body').append(tempTextarea); tempTextarea.val(shortcode).select(); document.execCommand('copy'); tempTextarea.remove(); $(this).addClass('copied'); setTimeout(function() { $('.bdfg-shortcode').removeClass('copied'); }, 1000); }); }); </script> <?php } /** * 渲染搜索分析页面 */ public function render_analytics_page() { if (!current_user_can('manage_options')) { return; } // 获取分析数据 $analytics = new BDFG_Analytics($this->plugin_path, $this->plugin_url, $this->version, $this->option_name, $this->options); $data = $analytics->get_analytics_data(); ?> <div class="wrap bdfg-admin-wrap bdfg-analytics-wrap"> <div class="bdfg-admin-header"> <div class="bdfg-logo"> <img src="<?php echo esc_url($this->plugin_url . 'assets/images/bdfg-logo.png'); ?>" alt="BDFG Logo"> <h1><?php _e('BDFG 产品搜索分析', 'bdfg-product-search'); ?></h1> </div> </div> <div class="bdfg-admin-content"> <div class="bdfg-date-filter"> <form method="get"> <input type="hidden" name="page" value="bdfg-search-analytics"> <label for="date-range"><?php _e('日期范围:', 'bdfg-product-search'); ?></label> <select name="date_range" id="date-range"> <option value="7" <?php selected(isset($_GET['date_range']) ? $_GET['date_range'] : '30', '7'); ?>><?php _e('最近7天', 'bdfg-product-search'); ?></option> <option value="30" <?php selected(isset($_GET['date_range']) ? $_GET['date_range'] : '30', '30'); ?>><?php _e('最近30天', 'bdfg-product-search'); ?></option> <option value="90" <?php selected(isset($_GET['date_range']) ? $_GET['date_range'] : '30', '90'); ?>><?php _e('最近90天', 'bdfg-product-search'); ?></option> </select> <button type="submit" class="button"><?php _e('应用', 'bdfg-product-search'); ?></button> </form> </div> <div class="bdfg-analytics-dashboard"> <!-- 概览卡片 --> <div class="bdfg-analytics-row"> <div class="bdfg-card"> <div class="bdfg-card-header"><?php _e('搜索总次数', 'bdfg-product-search'); ?></div> <div class="bdfg-card-value"><?php echo esc_html($data['total_searches']); ?></div> <div class="bdfg-card-footer"><?php $change = isset($data['search_change']) ? $data['search_change'] : 0; $class = $change >= 0 ? 'positive' : 'negative'; $icon = $change >= 0 ? '↑' : '↓'; echo '<span class="' . esc_attr($class) . '">' . esc_html($icon . ' ' . abs($change) . '%') . '</span>'; _e(' 相比上期', 'bdfg-product-search'); ?></div> </div> <div class="bdfg-card"> <div class="bdfg-card-header"><?php _e('独立搜索词', 'bdfg-product-search'); ?></div> <div class="bdfg-card-value"><?php echo esc_html($data['unique_terms']); ?></div> <div class="bdfg-card-footer"><?php _e('不同的搜索关键词', 'bdfg-product-search'); ?></div> </div> <div class="bdfg-card"> <div class="bdfg-card-header"><?php _e('有结果率', 'bdfg-product-search'); ?></div> <div class="bdfg-card-value"><?php echo esc_html($data['success_rate']); ?>%</div> <div class="bdfg-card-footer"><?php _e('返回结果的搜索比例', 'bdfg-product-search'); ?></div> </div> <div class="bdfg-card"> <div class="bdfg-card-header"><?php _e('平均结果数', 'bdfg-product-search'); ?></div> <div class="bdfg-card-value"><?php echo esc_html($data['avg_results']); ?></div> <div class="bdfg-card-footer"><?php _e('每次搜索平均产品数', 'bdfg-product-search'); ?></div> </div> </div> <!-- 图表和表格 --> <div class="bdfg-analytics-row"> <div class="bdfg-analytics-col"> <div class="bdfg-analytics-card"> <h2><?php _e('搜索趋势', 'bdfg-product-search'); ?></h2> <div class="bdfg-chart-container"> <canvas id="bdfg-search-trends-chart"></canvas> </div> </div> </div> </div> <div class="bdfg-analytics-row"> <div class="bdfg-analytics-col"> <div class="bdfg-analytics-card"> <h2><?php _e('热门搜索词', 'bdfg-product-search'); ?></h2> <?php if (!empty($data['popular_searches'])): ?> <table class="bdfg-analytics-table"> <thead> <tr> <th><?php _e('搜索词', 'bdfg-product-search'); ?></th> <th><?php _e('搜索次数', 'bdfg-product-search'); ?></th> <th><?php _e('平均结果数', 'bdfg-product-search'); ?></th> <th><?php _e('详情', 'bdfg-product-search'); ?></th> </tr> </thead> <tbody> <?php foreach ($data['popular_searches'] as $search): ?> <tr> <td><?php echo esc_html($search->search_term); ?></td> <td><?php echo esc_html($search->count); ?></td> <td><?php echo esc_html(round($search->avg_results, 1)); ?></td> <td> <a href="<?php echo esc_url(admin_url('edit.php?post_type=product&s=' . urlencode($search->search_term))); ?>" target="_blank"> <?php _e('查看产品', 'bdfg-product-search'); ?> </a> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php else: ?> <p class="bdfg-no-data"><?php _e('暂无搜索数据', 'bdfg-product-search'); ?></p> <?php endif; ?> </div> </div> <div class="bdfg-analytics-col"> <div class="bdfg-analytics-card"> <h2><?php _e('零结果搜索词', 'bdfg-product-search'); ?></h2> <?php if (!empty($data['zero_results'])): ?> <table class="bdfg-analytics-table"> <thead> <tr> <th><?php _e('搜索词', 'bdfg-product-search'); ?></th> <th><?php _e('搜索次数', 'bdfg-product-search'); ?></th> <th><?php _e('最近搜索', 'bdfg-product-search'); ?></th> <th><?php _e('操作', 'bdfg-product-search'); ?></th> </tr> </thead> <tbody> <?php foreach ($data['zero_results'] as $search): ?> <tr> <td><?php echo esc_html($search->search_term); ?></td> <td><?php echo esc_html($search->count); ?></td> <td><?php echo esc_html(date_i18n(get_option('date_format'), strtotime($search->last_search))); ?></td> <td> <a href="<?php echo esc_url(admin_url('post-new.php?post_type=product&bdfg_search_term=' . urlencode($search->search_term))); ?>" class="button button-small"> <?php _e('创建产品', 'bdfg-product-search'); ?> </a> </td> </tr> <?php endforeach; ?> </tbody> </table> <?php else: ?> <p class="bdfg-no-data"><?php _e('没有零结果搜索记录', 'bdfg-product-search'); ?></p> <?php endif; ?> </div> </div> </div> </div> </div> <div class="bdfg-admin-footer"> <p><?php _e('由 BeiDuoFenGou 倾情打造', 'bdfg-product-search'); ?> © <?php echo date('Y'); ?> <a href="https://beiduofengou.net" target="_blank">beiduofengou.net</a></p> </div> </div> <script> jQuery(document).ready(function($) { var ctx = document.getElementById('bdfg-search-trends-chart').getContext('2d'); var chartData = <?php echo json_encode($data['trends']); ?>; var labels = chartData.map(function(item) { return item.date; }); var searchCounts = chartData.map(function(item) { return parseInt(item.count); }); new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: '<?php _e('每日搜索次数', 'bdfg-product-search'); ?>', data: searchCounts, backgroundColor: 'rgba(23, 107, 239, 0.1)', borderColor: 'rgba(23, 107, 239, 1)', borderWidth: 2, tension: 0.3, pointBackgroundColor: 'rgba(23, 107, 239, 1)', pointRadius: 4, pointHoverRadius: 6, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: true, grid: { color: 'rgba(0, 0, 0, 0.05)' } }, x: { grid: { display: false } } }, plugins: { legend: { display: false }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 10, titleColor: '#ffffff', bodyColor: '#ffffff', borderColor: 'rgba(255, 255, 255, 0.2)', borderWidth: 1, displayColors: false } }, interaction: { mode: 'index', intersect: false } } }); }); </script> <?php } /** * 渲染帮助页面 */ public function render_help_page() { if (!current_user_can('manage_options')) { return; } ?> <div class="wrap bdfg-admin-wrap bdfg-help-wrap"> <div class="bdfg-admin-header"> <div class="bdfg-logo"> <img src="<?php echo esc_url($this->plugin_url . 'assets/images/bdfg-logo.png'); ?>" alt="BDFG Logo"> <h1><?php _e('BDFG 产品搜索使用帮助', 'bdfg-product-search'); ?></h1> </div> </div> <div class="bdfg-admin-content"> <div class="bdfg-help-nav"> <ul> <li><a href="#getting-started" class="active"><?php _e('入门指南', 'bdfg-product-search'); ?></a></li> <li><a href="#features"><?php _e('功能介绍', 'bdfg-product-search'); ?></a></li> <li><a href="#shortcodes"><?php _e('短代码使用', 'bdfg-product-search'); ?></a></li> <li><a href="#faq"><?php _e('常见问题', 'bdfg-product-search'); ?></a></li> <li><a href="#troubleshooting"><?php _e('故障排除', 'bdfg-product-search'); ?></a></li> </ul> </div> <div class="bdfg-help-content"> <div id="getting-started" class="bdfg-help-section active"> <h2><?php _e('入门指南', 'bdfg-product-search'); ?></h2> <div class="bdfg-help-box"> <h3><?php _e('1. 安装与激活', 'bdfg-product-search'); ?></h3> <p><?php _e('安装并激活BDFG产品搜索增强工具插件后,您可以在左侧菜单栏中找到"BDFG 产品搜索"选项。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-help-box"> <h3><?php _e('2. 基本配置', 'bdfg-product-search'); ?></h3> <p><?php _e('进入"BDFG 产品搜索"设置页面,根据需求开启或关闭相关功能。默认情况下,所有主要功能均已启用。', 'bdfg-product-search'); ?></p> <img src="<?php echo esc_url($this->plugin_url . 'assets/images/help/settings.jpg'); ?>" alt="设置页面"> </div> <div class="bdfg-help-box"> <h3><?php _e('3. 添加搜索表单', 'bdfg-product-search'); ?></h3> <p><?php _e('使用短代码 [bdfg_product_search] 在任何页面或文章中插入产品搜索表单。', 'bdfg-product-search'); ?></p> <p><?php _e('或者,您可以使用小部件将搜索表单添加到侧边栏或其他小部件区域。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-help-box"> <h3><?php _e('4. 查看搜索分析', 'bdfg-product-search'); ?></h3> <p><?php _e('访问"搜索分析"页面,了解用户搜索行为,查看热门搜索词和零结果搜索词统计。', 'bdfg-product-search'); ?></p> </div> </div> <div id="features" class="bdfg-help-section"> <h2><?php _e('功能介绍', 'bdfg-product-search'); ?></h2> <div class="bdfg-help-columns"> <div class="bdfg-help-column"> <div class="bdfg-feature-box"> <h3><?php _e('智能搜索建议', 'bdfg-product-search'); ?></h3> <p><?php _e('在用户输入时实时显示相关产品建议,支持产品图片、价格和库存状态显示。减少输入时间,提高转化率。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-feature-box"> <h3><?php _e('模糊搜索匹配', 'bdfg-product-search'); ?></h3> <p><?php _e('即使用户拼写错误或使用近似词也能找到相关产品。特别适合中文拼音变体和多音字搜索场景。', 'bdfg-product-search'); ?></p> </div> </div> <div class="bdfg-help-column"> <div class="bdfg-feature-box"> <h3><?php _e('产品元标签自动生成', 'bdfg-product-search'); ?></h3> <p><?php _e('自动为产品页面生成SEO友好的元标签和结构化数据,提高产品在搜索引擎中的可见度和排名。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-feature-box"> <h3><?php _e('高级产品过滤器', 'bdfg-product-search'); ?></h3> <p><?php _e('允许用户通过价格范围、分类、评分和库存状态等多维度筛选产品,提升购物体验。', 'bdfg-product-search'); ?></p> </div> </div> </div> <div class="bdfg-help-box"> <h3><?php _e('详细搜索分析', 'bdfg-product-search'); ?></h3> <p><?php _e('提供全面的搜索数据分析,包括搜索量趋势、热门搜索词、零结果搜索词等,帮助店主调整产品策略。', 'bdfg-product-search'); ?></p> <img src="<?php echo esc_url($this->plugin_url . 'assets/images/help/analytics.jpg'); ?>" alt="搜索分析"> </div> </div> <div id="shortcodes" class="bdfg-help-section"> <h2><?php _e('短代码使用', 'bdfg-product-search'); ?></h2> <div class="bdfg-help-box"> <h3><?php _e('基本短代码', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search]</code> <p><?php _e('显示默认样式的BDFG产品搜索框。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-help-box"> <h3><?php _e('自定义占位符文本', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search placeholder="输入关键词搜索BDFG产品"]</code> <p><?php _e('自定义搜索框中显示的占位符文本。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-help-box"> <h3><?php _e('自定义按钮文本', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search button_text="立即查找"]</code> <p><?php _e('自定义搜索按钮上显示的文本。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-help-box"> <h3><?php _e('显示产品过滤器', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search show_filters="yes"]</code> <p><?php _e('在搜索框下方显示高级产品过滤器,允许用户筛选结果。', 'bdfg-product-search'); ?></p> </div> <div class="bdfg-help-box"> <h3><?php _e('组合使用', 'bdfg-product-search'); ?></h3> <code class="bdfg-shortcode">[bdfg_product_search placeholder="查找BDFG优质产品" button_text="开始搜索" show_filters="yes"]</code> <p><?php _e('组合使用多个参数自定义搜索表单。', 'bdfg-product-search'); ?></p> </div> </div> <div id="faq" class="bdfg-help-section"> <h2><?php _e('常见问题', 'bdfg-product-search'); ?></h2> <div class="bdfg-faq-item"> <h3><?php _e('BDFG产品搜索增强工具是否与其他搜索插件兼容?', 'bdfg-product-search'); ?></h3> <div class="bdfg-faq-answer"> <p><?php _e('是的,BDFG产品搜索增强工具专注于WooCommerce产品搜索,通常与其他针对文章和页面的搜索插件兼容。但为了避免潜在冲突,我们建议仅使用一个专门的产品搜索解决方案。', 'bdfg-product-search'); ?></p> </div> </div> <div class="bdfg-faq-item"> <h3><?php _e('模糊搜索功能会影响网站性能吗?', 'bdfg-product-search'); ?></h3> <div class="bdfg-faq-answer"> <p><?php _e('BDFG产品搜索采用优化算法和缓存机制,在大多数情况下不会显著影响网站性能。对于拥有数千产品的大型商店,我们建议将缓存生命周期设置为较长时间(12-24小时)以提高性能。', 'bdfg-product-search'); ?></p> </div> </div> <div class="bdfg-faq-item"> <h3><?php _e('插件是否支持多语言商店?', 'bdfg-product-search'); ?></h3> <div class="bdfg-faq-answer"> <p><?php _e('是的,BDFG产品搜索增强工具完全兼容多语言网站。插件支持翻译,并且模糊搜索算法经过优化,可处理各种字符集和语言特性,包括中文、英文和其他常见语言。', 'bdfg-product-search'); ?></p> </div> </div> <div class="bdfg-faq-item"> <h3><?php _e('如何自定义搜索结果的外观?', 'bdfg-product-search'); ?></h3> <div class="bdfg-faq-answer"> <p><?php _e('您可以通过两种方式自定义搜索结果外观:', 'bdfg-product-search'); ?></p> <ol> <li><?php _e('在您的主题中添加自定义CSS来覆盖默认样式', 'bdfg-product-search'); ?></li> <li><?php _e('在主题目录中创建 <code>bdfg-product-search/search-results.php</code> 模板文件来完全自定义结果HTML结构', 'bdfg-product-search'); ?></li> </ol> </div> </div> <div class="bdfg-faq-item"> <h3><?php _e('搜索分析数据会影响网站存储空间吗?', 'bdfg-product-search'); ?></h3> <div class="bdfg-faq-answer"> <p><?php _e('BDFG产品搜索采用高效的数据存储方式,搜索日志数据通常不会占用大量存储空间。系统还会自动清理超过90天的旧数据,确保数据库保持精简高效。', 'bdfg-product-search'); ?></p> </div> </div> </div> <div id="troubleshooting" class="bdfg-help-section"> <h2><?php _e('故障排除', 'bdfg-product-search'); ?></h2> <div class="bdfg-help-box"> <h3><?php _e('搜索自动完成不工作', 'bdfg-product-search'); ?></h3> <p><?php _e('如果智能搜索建议功能不工作,请检查:', 'bdfg-product-search'); ?></p> <ol> <li><?php _e('确认"启用智能搜索建议功能"选项已开启', 'bdfg-product-search'); ?></li> <li><?php _e('检查您的主题是否有JavaScript错误(使用浏览器控制台)', 'bdfg-product-search'); ?></li> <li><?php _e('确保您的网站未使用过于严格的内容安全策略(CSP)', 'bdfg-product-search'); ?></li> <li><?php _e('临时禁用其他可能冲突的插件', 'bdfg-product-search'); ?></li> </ol> </div> <div class="bdfg-help-box"> <h3><?php _e('搜索结果不相关', 'bdfg-product-search'); ?></h3> <p><?php _e('如果搜索结果相关性较低,可以尝试:', 'bdfg-product-search'); ?></p> <ol> <li><?php _e('调整"模糊匹配阈值"设置(较低的值会产生更精确的结果)', 'bdfg-product-search'); ?></li> <li><?php _e('确保产品标题、描述和属性包含相关的关键词', 'bdfg-product-search'); ?></li> <li><?php _e('为产品添加更多元数据和标签', 'bdfg-product-search'); ?></li> </ol> </div> <div class="bdfg-help-box"> <h3><?php _e('搜索分析数据未显示', 'bdfg-product-search'); ?></h3> <p><?php _e('如果搜索分析页面未显示数据,请检查:', 'bdfg-product-search'); ?></p> <ol> <li><?php _e('确认数据库中存在 bdfg_search_logs 表', 'bdfg-product-search'); ?></li> <li><?php _e('确保网站上至少有一些搜索活动', 'bdfg-product-search'); ?></li> <li><?php _e('检查WordPress错误日志中是否有相关错误', 'bdfg-product-search'); ?></li> </ol> </div> <div class="bdfg-help-box"> <h3><?php _e('联系支持', 'bdfg-product-search'); ?></h3> <p><?php _e('如果您遇到无法解决的问题,请联系我们的支持团队:', 'bdfg-product-search'); ?></p> <ul> <li><?php _e('邮件:', 'bdfg-product-search'); ?> <a href="mailto:[email protected]">[email protected]</a></li> <li><?php _e('官网支持:', 'bdfg-product-search'); ?> <a href="https://beiduofengou.net/support" target="_blank">https://beiduofengou.net/support</a></li> </ul> <p><?php _e('请在提交支持请求时提供以下信息:', 'bdfg-product-search'); ?></p> <ul> <li><?php _e('WordPress版本', 'bdfg-product-search'); ?></li> <li><?php _e('WooCommerce版本', 'bdfg-product-search'); ?></li> <li><?php _e('BDFG产品搜索增强工具版本', 'bdfg-product-search'); ?></li> <li><?php _e('您使用的主题', 'bdfg-product-search'); ?></li> <li><?php _e('问题的详细描述', 'bdfg-product-search'); ?></li> </ul> </div> </div> </div> </div> <div class="bdfg-admin-footer"> <p><?php _e('由 BeiDuoFenGou 倾情打造', 'bdfg-product-search'); ?> © <?php echo date('Y'); ?> <a href="https://beiduofengou.net" target="_blank">beiduofengou.net</a></p> </div> </div> <script> jQuery(document).ready(function($) { // 帮助页面导航 $('.bdfg-help-nav a').click(function(e) { e.preventDefault(); // 获取目标部分ID var target = $(this).attr('href'); // 更新活动导航项 $('.bdfg-help-nav a').removeClass('active'); $(this).addClass('active'); // 显示目标部分,隐藏其他部分 $('.bdfg-help-section').removeClass('active'); $(target).addClass('active'); // 平滑滚动到目标部分 $('html, body').animate({ scrollTop: $(target).offset().top - 50 }, 500); }); // FAQ项目交互 $('.bdfg-faq-item h3').click(function() { $(this).parent().toggleClass('active'); $(this).next('.bdfg-faq-answer').slideToggle(200); }); }); </script> <?php } /** * 加载管理样式和脚本 * * @param string $hook 当前管理页面的hook名称 */ public function admin_enqueue_scripts($hook) { // 仅在插件页面加载资源 if (strpos($hook, 'bdfg-product-search') === false && strpos($hook, 'bdfg-search-analytics') === false && strpos($hook, 'bdfg-search-help') === false) { return; } // 加载管理CSS wp_enqueue_style( 'bdfg-product-search-admin', $this->plugin_url . 'assets/css/admin-style.css', array(), $this->version ); // 加载管理JavaScript wp_enqueue_script( 'bdfg-product-search-admin', $this->plugin_url . 'assets/js/admin-script.js', array('jquery'), $this->version, true ); // 如果是分析页面,加载Chart.js if (strpos($hook, 'bdfg-search-analytics') !== false) { wp_enqueue_script( 'chart-js', $this->plugin_url . 'assets/js/chart.min.js', array(), '3.9.1', true ); } } /** * 添加插件操作链接 * * @param array $links 插件操作链接数组 * @return array 修改后的链接数组 */ public function plugin_action_links($links) { $plugin_links = array( '<a href="' . admin_url('admin.php?page=bdfg-product-search') . '">' . __('设置', 'bdfg-product-search') . '</a>', '<a href="' . admin_url('admin.php?page=bdfg-search-analytics') . '">' . __('分析', 'bdfg-product-search') . '</a>' ); return array_merge($plugin_links, $links); } /** * 添加插件元信息 * * @param array $links 插件元信息链接数组 * @param string $file 插件文件 * @return array 修改后的链接数组 */ public function plugin_row_meta($links, $file) { if (BDFG_PRODUCT_SEARCH_BASENAME === $file) { $row_meta = array( 'docs' => '<a href="https://beiduofengou.net/docs/bdfg-product-search" target="_blank">' . __('文档', 'bdfg-product-search') . '</a>', 'support' => '<a href="https://beiduofengou.net/support" target="_blank">' . __('支持', 'bdfg-product-search') . '</a>', ); return array_merge($links, $row_meta); } return $links; } }
includes/class-bdfg-frontend.php
<?php /** * BDFG 产品搜索增强工具前端类 * * @package BDFG_Product_Search * @since 1.0.0 */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class BDFG_Frontend { /** * 插件目录路径 * * @var string */ private $plugin_path; /** * 插件URL * * @var string */ private $plugin_url; /** * 插件版本 * * @var string */ private $version; /** * 选项名称 * * @var string */ private $option_name; /** * 插件选项 * * @var array */ private $options; /** * 构造函数 * * @param string $plugin_path 插件目录路径 * @param string $plugin_url 插件URL * @param string $version 插件版本 * @param string $option_name 选项名称 * @param array $options 插件选项 */ public function __construct($plugin_path, $plugin_url, $version, $option_name, $options) { $this->plugin_path = $plugin_path; $this->plugin_url = $plugin_url; $this->version = $version; $this->option_name = $option_name; $this->options = $options; // 初始化前端功能 $this->init(); } /** * 初始化前端功能 */ private function init() { // 加载前端脚本和样式 add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts')); // 添加产品元标签 if ($this->options['enable_meta_tags'] === 'yes') { add_action('wp_head', array($this, 'add_product_meta_tags'), 1); } // 添加搜索自动完成API端点 if ($this->options['enable_autocomplete'] === 'yes') { add_action('rest_api_init', array($this, 'register_autocomplete_endpoint')); } // 修改搜索查询以支持模糊搜索 if ($this->options['enable_fuzzy_search'] === 'yes') { add_filter('posts_search', array($this, 'fuzzy_search_filter'), 10, 2); } // 添加产品过滤功能 if ($this->options['enable_filters'] === 'yes') { add_action('woocommerce_before_shop_loop', array($this, 'add_product_filters'), 30); add_action('pre_get_posts', array($this, 'filter_products_query')); } // 注册BDFG搜索小部件 add_action('widgets_init', array($this, 'register_widgets')); } /** * 前端脚本和样式加载 */ public function enqueue_scripts() { // 加载主样式表 wp_enqueue_style( 'bdfg-product-search', $this->plugin_url . 'assets/css/bdfg-product-search.css', array(), $this->version ); // 自动完成功能 if ($this->options['enable_autocomplete'] === 'yes') { wp_enqueue_script( 'bdfg-product-search', $this->plugin_url . 'assets/js/bdfg-product-search.js', array('jquery'), $this->version, true ); wp_localize_script( 'bdfg-product-search', 'bdfg_product_search', array( 'ajax_url' => rest_url('bdfg/v1/autocomplete'), 'nonce' => wp_create_nonce('wp_rest'), 'results_limit' => $this->options['search_results_limit'], 'no_results_text' => __('未找到相关产品', 'bdfg-product-search'), 'typing_text' => __('正在搜索...', 'bdfg-product-search'), 'view_all_text' => __('查看全部结果', 'bdfg-product-search'), 'enable_fuzzy' => $this->options['enable_fuzzy_search'] === 'yes', 'currency_symbol' => get_woocommerce_currency_symbol(), 'site_url' => site_url(), ) ); } // 过滤功能 if ($this->options['enable_filters'] === 'yes') { wp_enqueue_style( 'bdfg-product-filters', $this->plugin_url . 'assets/css/bdfg-product-filters.css', array(), $this->version ); wp_enqueue_script( 'bdfg-product-filters', $this->plugin_url . 'assets/js/bdfg-product-filters.js', array('jquery', 'jquery-ui-slider'), $this->version, true ); } } /** * 添加产品页面的元标签 */ public function add_product_meta_tags() { // 只在产品页面添加 if (!is_singular('product')) { return; } global $post; // 获取产品数据 $product = wc_get_product($post->ID); if (!$product) { return; } // 获取产品信息 $title = $product->get_name(); $description = $product->get_short_description(); if (empty($description)) { $description = wp_trim_words($product->get_description(), 30); } // 如果描述仍然为空,使用标题 if (empty($description)) { $description = $title; } // 获取产品关键词 $keywords = array($title); // 添加产品分类作为关键词 $categories = get_the_terms($post->ID, 'product_cat'); if ($categories && !is_wp_error($categories)) { foreach ($categories as $category) { $keywords[] = $category->name; // 添加父分类 $parent_id = $category->parent; while ($parent_id) { $parent = get_term($parent_id, 'product_cat'); if ($parent && !is_wp_error($parent)) { $keywords[] = $parent->name; $parent_id = $parent->parent; } else { $parent_id = 0; } } } } // 添加产品标签 $tags = get_the_terms($post->ID, 'product_tag'); if ($tags && !is_wp_error($tags)) { foreach ($tags as $tag) { $keywords[] = $tag->name; } } // 添加SKU和品牌(如果有) $sku = $product->get_sku(); if (!empty($sku)) { $keywords[] = $sku; } // 获取额外的产品属性 $attributes = $product->get_attributes(); if (!empty($attributes)) { foreach ($attributes as $attribute) { if ($attribute->is_taxonomy()) { $terms = wp_get_post_terms($product->get_id(), $attribute->get_name(), array('fields' => 'names')); if (!is_wp_error($terms)) { $keywords = array_merge($keywords, $terms); } } else { $keywords[] = $attribute->get_options(); } } } // 删除重复项并限制关键词数量 $keywords = array_unique($keywords); $keywords = array_slice($keywords, 0, 20); // 输出标准元标签 echo '<meta name="description" content="' . esc_attr($description) . '" />' . "\n"; echo '<meta name="keywords" content="' . esc_attr(implode(', ', $keywords)) . '" />' . "\n"; // Open Graph 标签 echo '<meta property="og:title" content="' . esc_attr($title) . '" />' . "\n"; echo '<meta property="og:description" content="' . esc_attr($description) . '" />' . "\n"; echo '<meta property="og:type" content="product" />' . "\n"; echo '<meta property="og:url" content="' . esc_url(get_permalink()) . '" />' . "\n"; echo '<meta property="product:brand" content="' . esc_attr(get_bloginfo('name')) . '" />' . "\n"; // 添加产品图片 $image_id = $product->get_image_id(); if ($image_id) { $image_url = wp_get_attachment_image_src($image_id, 'large'); if ($image_url) { echo '<meta property="og:image" content="' . esc_url($image_url[0]) . '" />' . "\n"; echo '<meta property="og:image:width" content="' . esc_attr($image_url[1]) . '" />' . "\n"; echo '<meta property="og:image:height" content="' . esc_attr($image_url[2]) . '" />' . "\n"; } } // 添加价格和货币信息 echo '<meta property="product:price:amount" content="' . esc_attr($product->get_price()) . '" />' . "\n"; echo '<meta property="product:price:currency" content="' . esc_attr(get_woocommerce_currency()) . '" />' . "\n"; // 添加商品库存状态 echo '<meta property="product:availability" content="' . esc_attr($product->is_in_stock() ? 'in stock' : 'out of stock') . '" />' . "\n"; // 添加商品条形码/GTIN(如果有) $gtin = get_post_meta($product->get_id(), '_gtin', true); if (!empty($gtin)) { echo '<meta property="product:gtin" content="' . esc_attr($gtin) . '" />' . "\n"; } // 添加商品颜色(如果有) $color_attribute_name = 'pa_color'; if ($product->get_attribute($color_attribute_name)) { echo '<meta property="product:color" content="' . esc_attr($product->get_attribute($color_attribute_name)) . '" />' . "\n"; } // 添加TwitterCard标签 echo '<meta name="twitter:card" content="product" />' . "\n"; echo '<meta name="twitter:title" content="' . esc_attr($title) . '" />' . "\n"; echo '<meta name="twitter:description" content="' . esc_attr($description) . '" />' . "\n"; if ($image_id && $image_url) { echo '<meta name="twitter:image" content="' . esc_url($image_url[0]) . '" />' . "\n"; } // 添加结构化数据标记 (JSON-LD) $this->add_product_schema($product); } /** * 添加产品结构化数据 * * @param WC_Product $product WooCommerce产品对象 */ private function add_product_schema($product) { // 基本产品信息 $schema = array( '@context' => 'https://schema.org/', '@type' => 'Product', 'name' => $product->get_name(), 'description' => wp_strip_all_tags($product->get_short_description() ? $product->get_short_description() : $product->get_description()), 'sku' => $product->get_sku(), 'brand' => array( '@type' => 'Brand', 'name' => get_bloginfo('name') ), 'offers' => array( '@type' => 'Offer', 'url' => get_permalink($product->get_id()), 'price' => $product->get_price(), 'priceCurrency' => get_woocommerce_currency(), 'priceValidUntil' => date('Y-m-d', strtotime('+1 year')), 'availability' => $product->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock', 'seller' => array( '@type' => 'Organization', 'name' => get_bloginfo('name') ) ) ); // 添加产品分类 $categories = get_the_terms($product->get_id(), 'product_cat'); if ($categories && !is_wp_error($categories)) { $schema['category'] = $categories[0]->name; } // 添加产品图片 $image_id = $product->get_image_id(); if ($image_id) { $image_url = wp_get_attachment_image_src($image_id, 'large'); if ($image_url) { $schema['image'] = $image_url[0]; } } // 添加产品画廊图片 $gallery_image_ids = $product->get_gallery_image_ids(); if (!empty($gallery_image_ids)) { $gallery_images = array(); foreach ($gallery_image_ids as $gallery_image_id) { $gallery_image_url = wp_get_attachment_image_src($gallery_image_id, 'large'); if ($gallery_image_url) { $gallery_images[] = $gallery_image_url[0]; } } if (!empty($gallery_images)) { $schema['image'] = $gallery_images; } } } // 添加产品评分 if ($product->get_rating_count() > 0) { $schema['aggregateRating'] = array( '@type' => 'AggregateRating', 'ratingValue' => $product->get_average_rating(), 'reviewCount' => $product->get_rating_count(), 'bestRating' => '5', 'worstRating' => '1' ); // 获取评论作为评论项 $comments = get_comments(array( 'post_id' => $product->get_id(), 'status' => 'approve', 'number' => 5, )); if (!empty($comments)) { $reviews = array(); foreach ($comments as $comment) { $rating = get_comment_meta($comment->comment_ID, 'rating', true); if (!$rating) continue; $reviews[] = array( '@type' => 'Review', 'reviewRating' => array( '@type' => 'Rating', 'ratingValue' => $rating, 'bestRating' => '5', 'worstRating' => '1' ), 'author' => array( '@type' => 'Person', 'name' => $comment->comment_author ), 'reviewBody' => $comment->comment_content, 'datePublished' => date('c', strtotime($comment->comment_date)) ); } if (!empty($reviews)) { $schema['review'] = $reviews; } } } // 如果是变体产品,添加变体信息 if ($product->is_type('variable')) { $variations = $product->get_available_variations(); $variation_attributes = array(); if (!empty($variations)) { foreach ($variations as $variation) { $variation_obj = wc_get_product($variation['variation_id']); if ($variation_obj) { $variation_attributes[] = array( '@type' => 'Offer', 'price' => $variation_obj->get_price(), 'priceCurrency' => get_woocommerce_currency(), 'availability' => $variation_obj->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock', 'url' => get_permalink($variation_obj->get_id()), 'priceValidUntil' => date('Y-m-d', strtotime('+1 year')) ); } } if (!empty($variation_attributes)) { $schema['offers'] = $variation_attributes; } } } // 输出JSON-LD echo '<script type="application/ld+json">' . json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . '</script>' . "\n"; } /** * 注册自动完成API端点 */ public function register_autocomplete_endpoint() { register_rest_route('bdfg/v1', '/autocomplete', array( 'methods' => 'GET', 'callback' => array($this, 'get_autocomplete_results'), 'permission_callback' => '__return_true', 'args' => array( 'term' => array( 'required' => true, 'sanitize_callback' => 'sanitize_text_field' ) ) )); } /** * 获取自动完成搜索结果 * * @param WP_REST_Request $request 请求对象 * @return WP_REST_Response 响应对象 */ public function get_autocomplete_results($request) { $search_term = $request->get_param('term'); if (empty($search_term)) { return new WP_REST_Response(array(), 200); } // 记录用户访问信息 $this->log_search($search_term); $limit = intval($this->options['search_results_limit']); // 尝试从缓存获取结果 $cache_key = 'bdfg_autocomplete_' . md5($search_term . $limit); $cached_results = get_transient($cache_key); if (false !== $cached_results && $this->options['cache_lifetime'] > 0) { return new WP_REST_Response($cached_results, 200); } // 构建搜索查询 $args = array( 'post_type' => 'product', 'post_status' => 'publish', 'posts_per_page' => $limit, 's' => $search_term, 'orderby' => 'relevance', 'order' => 'DESC', ); // 执行查询 $query = new WP_Query($args); $results = array(); $has_results = false; if ($query->have_posts()) { $has_results = true; while ($query->have_posts()) { $query->the_post(); $product = wc_get_product(get_the_ID()); if (!$product) { continue; } $image_id = $product->get_image_id(); $image_url = $image_id ? wp_get_attachment_image_src($image_id, 'thumbnail')[0] : wc_placeholder_img_src('thumbnail'); // 获取产品价格 $regular_price = $product->get_regular_price(); $sale_price = $product->get_sale_price(); $on_sale = $product->is_on_sale(); $results[] = array( 'id' => $product->get_id(), 'title' => $product->get_name(), 'url' => get_permalink($product->get_id()), 'price_html' => $product->get_price_html(), 'regular_price' => $regular_price, 'sale_price' => $sale_price, 'on_sale' => $on_sale, 'image' => $image_url, 'stock' => $product->is_in_stock() ? __('有货', 'bdfg-product-search') : __('缺货', 'bdfg-product-search'), 'categories' => $this->get_product_categories($product->get_id()), 'rating' => $product->get_average_rating(), 'type' => $product->get_type(), 'excerpt' => $this->get_product_excerpt($product) ); } } wp_reset_postdata(); // 如果启用了模糊搜索且没有结果,尝试模糊搜索 if (empty($results) && $this->options['enable_fuzzy_search'] === 'yes') { $fuzzy_results = $this->perform_fuzzy_search($search_term, $limit); if (!empty($fuzzy_results)) { $has_results = true; $results = $fuzzy_results; } } // 记录搜索查询和结果数量 $this->log_search_query($search_term, count($results)); // 缓存结果 if ($this->options['cache_lifetime'] > 0) { set_transient($cache_key, $results, intval($this->options['cache_lifetime']) * HOUR_IN_SECONDS); } return new WP_REST_Response($results, 200); } /** * 执行模糊搜索 * * @param string $search_term 搜索词 * @param int $limit 结果数量限制 * @return array 搜索结果 */ private function perform_fuzzy_search($search_term, $limit) { global $wpdb; // 获取已发布产品的标题和ID $products = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_title, post_excerpt, post_content FROM {$wpdb->posts} WHERE post_type = 'product' AND post_status = 'publish' LIMIT 1000" ) ); $results = array(); $threshold = intval($this->options['fuzzy_match_threshold']); $search_term_lower = mb_strtolower($search_term); foreach ($products as $product_post) { // 检查产品标题 $title_distance = $this->levenshtein_distance($search_term_lower, mb_strtolower($product_post->post_title)); // 检查产品内容中的关键词 $content_match = false; if (strpos(mb_strtolower($product_post->post_content), $search_term_lower) !== false) { $content_match = true; } // 检查产品摘要中的关键词 $excerpt_match = false; if (strpos(mb_strtolower($product_post->post_excerpt), $search_term_lower) !== false) { $excerpt_match = true; } // 检查产品SKU $sku_match = false; $sku = $wpdb->get_var($wpdb->prepare( "SELECT meta_value FROM {$wpdb->postmeta} WHERE post_id = %d AND meta_key = '_sku'", $product_post->ID )); if ($sku && strpos(mb_strtolower($sku), $search_term_lower) !== false) { $sku_match = true; } // 如果满足任一条件,则添加产品到结果中 if ($title_distance <= $threshold || $content_match || $excerpt_match || $sku_match) { $product_obj = wc_get_product($product_post->ID); if (!$product_obj) { continue; } $image_id = $product_obj->get_image_id(); $image_url = $image_id ? wp_get_attachment_image_src($image_id, 'thumbnail')[0] : wc_placeholder_img_src('thumbnail'); // 获取产品价格 $regular_price = $product_obj->get_regular_price(); $sale_price = $product_obj->get_sale_price(); $on_sale = $product_obj->is_on_sale(); $results[] = array( 'id' => $product_obj->get_id(), 'title' => $product_obj->get_name(), 'url' => get_permalink($product_obj->get_id()), 'price_html' => $product_obj->get_price_html(), 'regular_price' => $regular_price, 'sale_price' => $sale_price, 'on_sale' => $on_sale, 'image' => $image_url, 'stock' => $product_obj->is_in_stock() ? __('有货', 'bdfg-product-search') : __('缺货', 'bdfg-product-search'), 'categories' => $this->get_product_categories($product_obj->get_id()), 'rating' => $product_obj->get_average_rating(), 'type' => $product_obj->get_type(), 'excerpt' => $this->get_product_excerpt($product_obj), 'distance' => $title_distance, 'fuzzy' => true ); } } // 按编辑距离排序 usort($results, function($a, $b) { return $a['distance'] - $b['distance']; }); // 限制结果数量 return array_slice($results, 0, $limit); } /** * 计算Levenshtein距离(编辑距离) * * @param string $str1 字符串1 * @param string $str2 字符串2 * @return int 编辑距离 */ private function levenshtein_distance($str1, $str2) { // 使用PHP内置的levenshtein函数 // 对于中文字符,先将字符串拆分为字符数组 if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $str1) || preg_match('/[\x{4e00}-\x{9fa5}]/u', $str2)) { // 中文字符处理 $s1 = preg_split('//u', $str1, -1, PREG_SPLIT_NO_EMPTY); $s2 = preg_split('//u', $str2, -1, PREG_SPLIT_NO_EMPTY); $n = count($s1); $m = count($s2); if ($n === 0) return $m; if ($m === 0) return $n; $distance = array(); for ($i = 0; $i <= $n; $i++) { $distance[$i] = array(); $distance[$i][0] = $i; } for ($j = 0; $j <= $m; $j++) { $distance[0][$j] = $j; } for ($i = 1; $i <= $n; $i++) { for ($j = 1; $j <= $m; $j++) { $cost = ($s1[$i - 1] === $s2[$j - 1]) ? 0 : 1; $distance[$i][$j] = min( $distance[$i - 1][$j] + 1, // 删除 $distance[$i][$j - 1] + 1, // 插入 $distance[$i - 1][$j - 1] + $cost // 替换 ); } } return $distance[$n][$m]; } else { // 非中文使用PHP内置函数 return levenshtein($str1, $str2); } } /** * 获取产品分类 * * @param int $product_id 产品ID * @return string 分类名称,逗号分隔 */ private function get_product_categories($product_id) { $terms = get_the_terms($product_id, 'product_cat'); if (empty($terms) || is_wp_error($terms)) { return ''; } $categories = array(); foreach ($terms as $term) { $categories[] = $term->name; } return implode(', ', $categories); } /** * 获取产品摘要 * * @param WC_Product $product 产品对象 * @return string 摘要文本 */ private function get_product_excerpt($product) { $excerpt = $product->get_short_description(); if (empty($excerpt)) { $excerpt = $product->get_description(); $excerpt = wp_trim_words($excerpt, 20); } return $excerpt; } /** * 记录搜索访问 * * @param string $search_term 搜索词 */ private function log_search($search_term) { // 记录用户搜索词,用于后续建议功能 $existing_searches = get_option('bdfg_recent_searches', array()); // 将当前搜索添加到开头 array_unshift($existing_searches, array( 'term' => $search_term, 'time' => time() )); // 保留最近的100个搜索 $existing_searches = array_slice($existing_searches, 0, 100); update_option('bdfg_recent_searches', $existing_searches); } /** * 记录搜索查询 * * @param string $search_term 搜索词 * @param int $results_count 结果数量 */ private function log_search_query($search_term, $results_count) { global $wpdb; $table_name = $wpdb->prefix . 'bdfg_search_logs'; $data = array( 'search_term' => $search_term, 'user_id' => get_current_user_id(), 'results_count' => $results_count, 'ip_address' => $this->get_client_ip(), 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '' ); $wpdb->insert($table_name, $data); // 如果是零结果搜索,更新建议表 if ($results_count === 0) { $suggestions_table = $wpdb->prefix . 'bdfg_search_suggestions'; // 检查是否已有此搜索词的建议 $existing = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $suggestions_table WHERE search_term = %s", $search_term )); if (!$existing) { // 添加到待处理建议列表 $wpdb->insert($suggestions_table, array( 'search_term' => $search_term, 'suggestion' => '', 'frequency' => 1, 'last_updated' => current_time('mysql') )); } else { // 更新频率 $wpdb->query($wpdb->prepare( "UPDATE $suggestions_table SET frequency = frequency + 1, last_updated = %s WHERE search_term = %s", current_time('mysql'), $search_term )); } } } /** * 获取客户端IP地址 * * @return string IP地址 */ private function get_client_ip() { $ip = ''; // 检查各种可能的IP源 if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { // 可能包含多个代理IP,取第一个 $ip_list = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $ip = trim($ip_list[0]); } elseif (!empty($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } // 确保IP格式有效 $ip = filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0'; return $ip; } /** * 修改搜索查询以支持模糊搜索 * * @param string $search 原搜索条件SQL * @param WP_Query $wp_query 查询对象 * @return string 修改后的搜索条件SQL */ public function fuzzy_search_filter($search, $wp_query) { global $wpdb; // 仅修改前端产品搜索查询 if (!$wp_query->is_search() || !$wp_query->is_main_query() || is_admin()) { return $search; } // 确保是产品搜索 $post_types = $wp_query->get('post_type'); if (empty($post_types) || (is_array($post_types) && !in_array('product', $post_types)) || $post_types !== 'product') { return $search; } $search_term = $wp_query->get('s'); if (empty($search_term)) { return $search; } // 清除现有的搜索条件 $search = ''; // 构建模糊搜索SQL $like = '%' . $wpdb->esc_like($search_term) . '%'; // 搜索标题、内容、摘要、SKU和产品属性 $search = $wpdb->prepare( " AND ( {$wpdb->posts}.post_title LIKE %s OR {$wpdb->posts}.post_content LIKE %s OR {$wpdb->posts}.post_excerpt LIKE %s OR EXISTS ( SELECT * FROM {$wpdb->postmeta} WHERE {$wpdb->postmeta}.post_id = {$wpdb->posts}.ID AND ( ({$wpdb->postmeta}.meta_key = '_sku' AND {$wpdb->postmeta}.meta_value LIKE %s) OR ({$wpdb->postmeta}.meta_key LIKE 'attribute_%%' AND {$wpdb->postmeta}.meta_value LIKE %s) OR ({$wpdb->postmeta}.meta_key = '_variation_description' AND {$wpdb->postmeta}.meta_value LIKE %s) ) ) OR EXISTS ( SELECT * FROM {$wpdb->term_relationships} JOIN {$wpdb->term_taxonomy} ON {$wpdb->term_relationships}.term_taxonomy_id = {$wpdb->term_taxonomy}.term_taxonomy_id JOIN {$wpdb->terms} ON {$wpdb->term_taxonomy}.term_id = {$wpdb->terms}.term_id WHERE {$wpdb->term_relationships}.object_id = {$wpdb->posts}.ID AND {$wpdb->terms}.name LIKE %s ) )", $like, $like, $like, $like, $like, $like, $like ); return $search; } /** * 添加产品过滤器 */ public function add_product_filters() { // 仅在产品存档页面和搜索结果页显示 if (!is_shop() && !is_product_category() && !is_product_tag() && !is_search()) { return; } // 获取分类列表 $categories = get_terms(array( 'taxonomy' => 'product_cat', 'hide_empty' => true, )); // 获取价格范围 $min_max_price = $this->get_min_max_product_price(); // 获取评分选项 $rating_options = array( 5 => __('五星 (★★★★★)', 'bdfg-product-search'), 4 => __('四星及以上 (★★★★☆+)', 'bdfg-product-search'), 3 => __('三星及以上 (★★★☆☆+)', 'bdfg-product-search'), 2 => __('两星及以上 (★★☆☆☆+)', 'bdfg-product-search'), 1 => __('一星及以上 (★☆☆☆☆+)', 'bdfg-product-search'), ); // 获取选中的过滤条件 $selected_category = isset($_GET['product_cat']) ? sanitize_text_field($_GET['product_cat']) : ''; $min_price = isset($_GET['min_price']) ? floatval($_GET['min_price']) : $min_max_price['min']; $max_price = isset($_GET['max_price']) ? floatval($_GET['max_price']) : $min_max_price['max']; $rating_filter = isset($_GET['rating_filter']) ? intval($_GET['rating_filter']) : 0; $stock_status = isset($_GET['stock_status']) ? sanitize_text_field($_GET['stock_status']) : ''; $on_sale = isset($_GET['on_sale']) ? sanitize_text_field($_GET['on_sale']) : ''; // 获取当前页面URL $current_url = $this->get_current_page_url(); ?> <div class="bdfg-product-filters"> <h3><?php _e('产品筛选', 'bdfg-product-search'); ?> <span class="bdfg-toggle-filters">+</span></h3> <form method="get" action="<?php echo esc_url($current_url); ?>" class="bdfg-filters-form"> <?php // 保留所有现有的URL参数,除了我们将在表单中设置的 $excluded_params = array('min_price', 'max_price', 'product_cat', 'rating_filter', 'stock_status', 'on_sale', 'paged'); foreach ($_GET as $key => $value) { if (!in_array($key, $excluded_params) && $key !== 'bdfg_filter_submit') { echo '<input type="hidden" name="' . esc_attr($key) . '" value="' . esc_attr($value) . '">'; } } ?> <div class="bdfg-filters-container"> <?php if (!empty($categories) && !is_product_category()): ?> <div class="bdfg-filter-group"> <label for="product_cat"><?php _e('产品分类', 'bdfg-product-search'); ?></label> <select name="product_cat" id="product_cat" class="bdfg-select"> <option value=""><?php _e('所有分类', 'bdfg-product-search'); ?></option> <?php foreach ($categories as $category): ?> <option value="<?php echo esc_attr($category->slug); ?>" <?php selected($selected_category, $category->slug); ?>> <?php echo esc_html($category->name); ?> (<?php echo esc_html($category->count); ?>) </option> <?php endforeach; ?> </select> </div> <?php endif; ?> <div class="bdfg-filter-group"> <label for="price-range"><?php _e('价格区间', 'bdfg-product-search'); ?></label> <div class="bdfg-price-slider-container"> <div class="bdfg-price-slider-values"> <span class="bdfg-price-label"><?php echo get_woocommerce_currency_symbol(); ?></span> <input type="number" name="min_price" id="min_price" value="<?php echo esc_attr($min_price); ?>" min="<?php echo esc_attr($min_max_price['min']); ?>" max="<?php echo esc_attr($min_max_price['max']); ?>" step="1" class="bdfg-price-input"> <span class="bdfg-price-separator">-</span> <span class="bdfg-price-label"><?php echo get_woocommerce_currency_symbol(); ?></span> <input type="number" name="max_price" id="max_price" value="<?php echo esc_attr($max_price); ?>" min="<?php echo esc_attr($min_max_price['min']); ?>" max="<?php echo esc_attr($min_max_price['max']); ?>" step="1" class="bdfg-price-input"> </div> <div class="bdfg-price-slider" data-min="<?php echo esc_attr($min_max_price['min']); ?>" data-max="<?php echo esc_attr($min_max_price['max']); ?>" data-current-min="<?php echo esc_attr($min_price); ?>" data-current-max="<?php echo esc_attr($max_price); ?>"></div> </div> </div> <div class="bdfg-filter-group"> <label for="rating_filter"><?php _e('商品评分', 'bdfg-product-search'); ?></label> <select name="rating_filter" id="rating_filter" class="bdfg-select"> <option value="0"><?php _e('所有评分', 'bdfg-product-search'); ?></option> <?php foreach ($rating_options as $rating => $label): ?> <option value="<?php echo esc_attr($rating); ?>" <?php selected($rating_filter, $rating); ?>> <?php echo esc_html($label); ?> </option> <?php endforeach; ?> </select> </div> <div class="bdfg-filter-group"> <label for="stock_status"><?php _e('库存状态', 'bdfg-product-search'); ?></label> <select name="stock_status" id="stock_status" class="bdfg-select"> <option value=""><?php _e('所有状态', 'bdfg-product-search'); ?></option> <option value="instock" <?php selected($stock_status, 'instock'); ?>><?php _e('有库存', 'bdfg-product-search'); ?></option> <option value="outofstock" <?php selected($stock_status, 'outofstock'); ?>><?php _e('缺货', 'bdfg-product-search'); ?></option> </select> </div> <div class="bdfg-filter-group bdfg-filter-checkbox"> <label for="on_sale"> <input type="checkbox" name="on_sale" id="on_sale" value="yes" <?php checked($on_sale, 'yes'); ?>> <?php _e('仅显示促销商品', 'bdfg-product-search'); ?> </label> </div> </div> <div class="bdfg-filter-actions"> <button type="submit" name="bdfg_filter_submit" value="1" class="bdfg-filter-button bdfg-apply-button"><?php _e('应用筛选', 'bdfg-product-search'); ?></button> <a href="<?php echo esc_url(remove_query_arg(array('min_price', 'max_price', 'rating_filter', 'stock_status', 'product_cat', 'on_sale', 'paged'))); ?>" class="bdfg-filter-button bdfg-reset-button"><?php _e('重置', 'bdfg-product-search'); ?></a> </div> </form> </div> <?php } /** * 获取当前页面URL * * @return string 当前页面URL */ private function get_current_page_url() { global $wp; // 获取当前URL(不含查询参数) $current_url = home_url($wp->request); return $current_url; } /** * 获取最低和最高产品价格 * * @return array 包含min和max的价格范围数组 */ private function get_min_max_product_price() { global $wpdb; $result = $wpdb->get_row(" SELECT MIN(meta_value+0) as min_price, MAX(meta_value+0) as max_price FROM {$wpdb->postmeta} WHERE meta_key = '_price' AND meta_value > 0 "); $min = $result ? floor($result->min_price) : 0; $max = $result ? ceil($result->max_price) : 1000; // 确保最小和最大价格有意义 if ($min <= 0) $min = 0; if ($max <= $min) $max = $min + 1000; return array( 'min' => $min, 'max' => $max, ); } /** * 应用产品过滤器 * * @param WP_Query $query WordPress查询对象 */ public function filter_products_query($query) { // 仅修改前端产品查询 if (is_admin() || !$query->is_main_query() || (!is_shop() && !is_product_category() && !is_product_tag() && !is_search())) { return; } // 产品分类过滤 if (isset($_GET['product_cat']) && !empty($_GET['product_cat'])) { $product_cat = sanitize_text_field($_GET['product_cat']); $query->set('tax_query', array( array( 'taxonomy' => 'product_cat', 'field' => 'slug', 'terms' => $product_cat, 'operator' => 'IN' ) )); } // 元查询数组 $meta_query = array(); // 价格过滤 if (isset($_GET['min_price']) && isset($_GET['max_price'])) { $min_price = floatval($_GET['min_price']); $max_price = floatval($_GET['max_price']); $meta_query[] = array( 'key' => '_price', 'value' => array($min_price, $max_price), 'compare' => 'BETWEEN', 'type' => 'NUMERIC', ); } // 评分过滤 if (isset($_GET['rating_filter']) && intval($_GET['rating_filter']) > 0) { $rating = intval($_GET['rating_filter']); $meta_query[] = array( 'key' => '_wc_average_rating', 'value' => $rating, 'compare' => '>=', 'type' => 'NUMERIC', ); } // 库存状态过滤 if (isset($_GET['stock_status']) && !empty($_GET['stock_status'])) { $stock_status = sanitize_text_field($_GET['stock_status']); $meta_query[] = array( 'key' => '_stock_status', 'value' => $stock_status, 'compare' => '=', ); } // 促销商品过滤 if (isset($_GET['on_sale']) && $_GET['on_sale'] === 'yes') { $product_ids_on_sale = wc_get_product_ids_on_sale(); $query->set('post__in', $product_ids_on_sale); } // 如果有元查询条件,设置查询 if (!empty($meta_query)) { $existing_meta_query = $query->get('meta_query'); if (!empty($existing_meta_query) && is_array($existing_meta_query)) { $meta_query = array_merge($existing_meta_query, $meta_query); } $query->set('meta_query', $meta_query); } } /** * 注册BDFG产品搜索小部件 */ public function register_widgets() { require_once $this->plugin_path . 'includes/class-bdfg-search-widget.php'; register_widget('BDFG_Search_Widget'); } }
includes/class-bdfg-analytics.php
相关文章: WooCommerce 预定功能插件
<?php /** * BDFG 产品搜索增强工具分析类 * * @package BDFG_Product_Search * @since 1.0.0 */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class BDFG_Analytics { /** * 插件目录路径 * * @var string */ private $plugin_path; /** * 插件URL * * @var string */ private $plugin_url; /** * 插件版本 * * @var string */ private $version; /** * 选项名称 * * @var string */ private $option_name; /** * 插件选项 * * @var array */ private $options; /** * 构造函数 * * @param string $plugin_path 插件目录路径 * @param string $plugin_url 插件URL * @param string $version 插件版本 * @param string $option_name 选项名称 * @param array $options 插件选项 */ public function __construct($plugin_path, $plugin_url, $version, $option_name, $options) { $this->plugin_path = $plugin_path; $this->plugin_url = $plugin_url; $this->version = $version; $this->option_name = $option_name; $this->options = $options; // 初始化分析功能 $this->init(); } /** * 初始化分析功能 */ private function init() { // 注册定时任务钩子 add_action('bdfg_daily_maintenance', array($this, 'daily_maintenance')); // 注册AJAX处理函数 add_action('wp_ajax_bdfg_export_analytics', array($this, 'export_analytics')); // 添加仪表盘小部件 add_action('wp_dashboard_setup', array($this, 'add_dashboard_widget')); } /** * 每日维护任务 */ public function daily_maintenance() { // 清理过期的缓存 $this->clear_expired_cache(); // 清理旧的搜索日志(90天前) $this->clean_old_search_logs(); // 生成搜索统计报告 $this->generate_search_report(); } /** * 清理过期的缓存 */ private function clear_expired_cache() { global $wpdb; // 删除所有BDFG相关的瞬时缓存 $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '_transient_bdfg_%' OR option_name LIKE '_transient_timeout_bdfg_%'" ); } /** * 清理旧的搜索日志 */ private function clean_old_search_logs() { global $wpdb; $table_name = $wpdb->prefix . 'bdfg_search_logs'; // 删除90天前的日志 $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE search_date < %s", date('Y-m-d H:i:s', strtotime('-90 days')) ) ); } /** * 生成搜索统计报告 */ private function generate_search_report() { $data = $this->get_analytics_data(); // 保存每日统计数据 $daily_stats = array( 'date' => date('Y-m-d'), 'total_searches' => $data['total_searches'], 'unique_terms' => $data['unique_terms'], 'success_rate' => $data['success_rate'], 'avg_results' => $data['avg_results'], 'popular_searches' => array_slice($data['popular_searches'], 0, 5), 'zero_results' => array_slice($data['zero_results'], 0, 5) ); $existing_reports = get_option('bdfg_search_reports', array()); $existing_reports[date('Y-m-d')] = $daily_stats; // 只保留最近30天的报告 if (count($existing_reports) > 30) { $existing_reports = array_slice($existing_reports, -30, 30, true); } update_option('bdfg_search_reports', $existing_reports); } /** * 获取分析数据 * * @param int $days 要分析的天数 * @return array 分析数据 */ public function get_analytics_data($days = 30) { global $wpdb; $table_name = $wpdb->prefix . 'bdfg_search_logs'; // 如果请求了不同的日期范围,使用指定的天数 if (isset($_GET['date_range']) && in_array($_GET['date_range'], array('7', '30', '90'))) { $days = intval($_GET['date_range']); } // 确定日期范围 $date_from = date('Y-m-d H:i:s', strtotime("-$days days")); // 获取总搜索次数 $total_searches = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE search_date >= %s", $date_from ) ); // 获取唯一搜索词数量 $unique_terms = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT search_term) FROM $table_name WHERE search_date >= %s", $date_from ) ); // 获取有结果的搜索比例 $success_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE search_date >= %s AND results_count > 0", $date_from ) ); $success_rate = $total_searches > 0 ? round(($success_count / $total_searches) * 100) : 0; // 获取平均结果数 $avg_results = $wpdb->get_var( $wpdb->prepare( "SELECT AVG(results_count) FROM $table_name WHERE search_date >= %s", $date_from ) ); $avg_results = round($avg_results, 1); // 获取热门搜索词 $popular_searches = $wpdb->get_results( $wpdb->prepare( "SELECT search_term, COUNT(*) as count, AVG(results_count) as avg_results FROM $table_name WHERE search_date >= %s GROUP BY search_term ORDER BY count DESC LIMIT 10", $date_from ) ); // 获取零结果搜索词 $zero_results = $wpdb->get_results( $wpdb->prepare( "SELECT search_term, COUNT(*) as count, MAX(search_date) as last_search FROM $table_name WHERE results_count = 0 AND search_date >= %s GROUP BY search_term ORDER BY count DESC, last_search DESC LIMIT 10", $date_from ) ); // 获取搜索趋势数据 $trends = $wpdb->get_results( $wpdb->prepare( "SELECT DATE(search_date) as date, COUNT(*) as count FROM $table_name WHERE search_date >= %s GROUP BY DATE(search_date) ORDER BY date ASC", $date_from ) ); // 计算搜索量变化率 $previous_period = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $table_name WHERE search_date >= %s AND search_date < %s", date('Y-m-d H:i:s', strtotime("-" . ($days * 2) . " days")), $date_from ) ); $search_change = 0; if ($previous_period > 0) { $search_change = round((($total_searches - $previous_period) / $previous_period) * 100); } return array( 'total_searches' => $total_searches, 'unique_terms' => $unique_terms, 'success_rate' => $success_rate, 'avg_results' => $avg_results, 'popular_searches' => $popular_searches, 'zero_results' => $zero_results, 'trends' => $trends, 'search_change' => $search_change, 'days' => $days ); } /** * 导出分析数据 */ public function export_analytics() { // 检查权限 if (!current_user_can('manage_options')) { wp_die(__('权限不足', 'bdfg-product-search')); } // 验证nonce if (!isset($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'bdfg_export_analytics')) { wp_die(__('安全检查失败', 'bdfg-product-search')); } global $wpdb; $table_name = $wpdb->prefix . 'bdfg_search_logs'; // 获取导出天数 $days = isset($_GET['days']) ? intval($_GET['days']) : 30; $date_from = date('Y-m-d H:i:s', strtotime("-$days days")); // 获取要导出的数据 $data = $wpdb->get_results( $wpdb->prepare( "SELECT search_term, results_count, search_date, user_id, ip_address FROM $table_name WHERE search_date >= %s ORDER BY search_date DESC", $date_from ), ARRAY_A ); // 设置CSV文件头 header('Content-Type: text/csv; charset=utf-8'); header('Content-Disposition: attachment; filename=bdfg-search-analytics-' . date('Y-m-d') . '.csv'); // 创建文件句柄 $output = fopen('php://output', 'w'); // 添加UTF-8 BOM fputs($output, "\xEF\xBB\xBF"); // 添加CSV表头 fputcsv($output, array( '搜索词', '结果数量', '搜索时间', '用户ID', 'IP地址' )); // 添加数据行 foreach ($data as $row) { fputcsv($output, $row); } fclose($output); exit; } /** * 添加仪表盘小部件 */ public function add_dashboard_widget() { if (current_user_can('manage_options')) { wp_add_dashboard_widget( 'bdfg_search_analytics_widget', __('BDFG产品搜索统计', 'bdfg-product-search'), array($this, 'render_dashboard_widget') ); } } /** * 渲染仪表盘小部件 */ public function render_dashboard_widget() { // 获取最近7天的数据 $data = $this->get_analytics_data(7); ?> <div class="bdfg-dashboard-widget"> <div class="bdfg-dashboard-stats"> <div class="bdfg-stat-box"> <span class="bdfg-stat-value"><?php echo esc_html($data['total_searches']); ?></span> <span class="bdfg-stat-label"><?php _e('搜索次数', 'bdfg-product-search'); ?></span> </div> <div class="bdfg-stat-box"> <span class="bdfg-stat-value"><?php echo esc_html($data['success_rate']); ?>%</span> <span class="bdfg-stat-label"><?php _e('有结果率', 'bdfg-product-search'); ?></span> </div> </div> <?php if (!empty($data['popular_searches'])): ?> <h4><?php _e('热门搜索词', 'bdfg-product-search'); ?></h4> <ul class="bdfg-search-terms"> <?php foreach (array_slice($data['popular_searches'], 0, 5) as $term): ?> <li> <span class="bdfg-term"><?php echo esc_html($term->search_term); ?></span> <span class="bdfg-count"><?php echo esc_html($term->count); ?></span> </li> <?php endforeach; ?> </ul> <?php endif; ?> <p class="bdfg-widget-footer"> <a href="<?php echo esc_url(admin_url('admin.php?page=bdfg-search-analytics')); ?>"><?php _e('查看完整报告', 'bdfg-product-search'); ?> →</a> </p> </div> <style> .bdfg-dashboard-stats { display: flex; justify-content: space-between; margin-bottom: 15px; } .bdfg-stat-box { text-align: center; padding: 10px; background: #f9f9f9; border-radius: 3px; flex: 1; margin: 0 5px; } .bdfg-stat-value { display: block; font-size: 24px; font-weight: bold; color: #176bef; } .bdfg-stat-label { font-size: 12px; color: #666; } .bdfg-search-terms { margin: 0; padding: 0; list-style: none; } .bdfg-search-terms li { display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #f0f0f0; } .bdfg-count { background: #f0f0f0; border-radius: 10px; padding: 2px 8px; font-size: 11px; } .bdfg-widget-footer { margin-top: 15px; text-align: right; } </style> <?php } }
includes/class-bdfg-search-widget.php
<?php /** * BDFG 产品搜索小部件类 * * @package BDFG_Product_Search * @since 1.0.0 */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class BDFG_Search_Widget extends WP_Widget { /** * 注册小部件 */ public function __construct() { parent::__construct( 'bdfg_product_search_widget', // 小部件ID __('BDFG 产品搜索', 'bdfg-product-search'), // 小部件名称 array( 'description' => __('添加BDFG高级产品搜索表单到侧边栏', 'bdfg-product-search'), 'classname' => 'widget_bdfg_product_search', ) ); } /** * 前端显示小部件 * * @param array $args 小部件参数 * @param array $instance 小部件实例 */ public function widget($args, $instance) { $title = !empty($instance['title']) ? $instance['title'] : __('产品搜索', 'bdfg-product-search'); $placeholder = !empty($instance['placeholder']) ? $instance['placeholder'] : __('搜索BDFG产品...', 'bdfg-product-search'); $button_text = !empty($instance['button_text']) ? $instance['button_text'] : __('搜索', 'bdfg-product-search'); $show_filters = !empty($instance['show_filters']) ? $instance['show_filters'] : 'no'; // 应用小部件过滤器 $title = apply_filters('widget_title', $title, $instance, $this->id_base); echo $args['before_widget']; if ($title) { echo $args['before_title'] . $title . $args['after_title']; } echo do_shortcode('[bdfg_product_search placeholder="' . esc_attr($placeholder) . '" button_text="' . esc_attr($button_text) . '" show_filters="' . esc_attr($show_filters) . '"]'); echo $args['after_widget']; } /** * 后端小部件表单 * * @param array $instance 当前实例 */ public function form($instance) { $title = !empty($instance['title']) ? $instance['title'] : __('产品搜索', 'bdfg-product-search'); $placeholder = !empty($instance['placeholder']) ? $instance['placeholder'] : __('搜索BDFG产品...', 'bdfg-product-search'); $button_text = !empty($instance['button_text']) ? $instance['button_text'] : __('搜索', 'bdfg-product-search'); $show_filters = !empty($instance['show_filters']) ? $instance['show_filters'] : 'no'; ?> <p> <label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php _e('标题:', 'bdfg-product-search'); ?></label> <input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>"> </p> <p> <label for="<?php echo esc_attr($this->get_field_id('placeholder')); ?>"><?php _e('占位文本:', 'bdfg-product-search'); ?></label> <input class="widefat" id="<?php echo esc_attr($this->get_field_id('placeholder')); ?>" name="<?php echo esc_attr($this->get_field_name('placeholder')); ?>" type="text" value="<?php echo esc_attr($placeholder); ?>"> </p> <p> <label for="<?php echo esc_attr($this->get_field_id('button_text')); ?>"><?php _e('按钮文本:', 'bdfg-product-search'); ?></label> <input class="widefat" id="<?php echo esc_attr($this->get_field_id('button_text')); ?>" name="<?php echo esc_attr($this->get_field_name('button_text')); ?>" type="text" value="<?php echo esc_attr($button_text); ?>"> </p> <p> <input type="checkbox" class="checkbox" id="<?php echo esc_attr($this->get_field_id('show_filters')); ?>" name="<?php echo esc_attr($this->get_field_name('show_filters')); ?>" value="yes" <?php checked($show_filters, 'yes'); ?>> <label for="<?php echo esc_attr($this->get_field_id('show_filters')); ?>"><?php _e('显示产品过滤器', 'bdfg-product-search'); ?></label> </p> <?php } /** * 更新小部件实例 * * @param array $new_instance 新实例 * @param array $old_instance 旧实例 * @return array 更新后的实例 */ public function update($new_instance, $old_instance) { $instance = array(); $instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : ''; $instance['placeholder'] = (!empty($new_instance['placeholder'])) ? sanitize_text_field($new_instance['placeholder']) : ''; $instance['button_text'] = (!empty($new_instance['button_text'])) ? sanitize_text_field($new_instance['button_text']) : ''; $instance['show_filters'] = (!empty($new_instance['show_filters'])) ? 'yes' : 'no'; return $instance; } }
assets/js/bdfg-product-search.js
相关文章: woocommerce产品分析插件
/** * BDFG 产品搜索增强工具前端JS * * @package BDFG_Product_Search * @since 1.0.0 * @author BeiDuoFenGou <[email protected]> * @last_modified 2025-03-06 14:01:02 */ (function($) { 'use strict'; // 初始化BDFG产品搜索 function initBDFGProductSearch() { // 获取所有搜索表单 $('.bdfg-search-field').each(function() { var $input = $(this); var $form = $input.closest('form'); var $results = $form.find('.bdfg-search-results'); var $submitBtn = $form.find('.bdfg-search-submit'); var xhr = null; var lastSearch = ''; var typingTimer; // 添加获取焦点事件 $input.on('focus', function() { if ($input.val().length > 0) { $results.show(); } }); // 添加键盘输入事件 $input.on('keyup', function(e) { var searchTerm = $input.val().trim(); // 清除之前的定时器 clearTimeout(typingTimer); // 上下键导航结果 if (e.keyCode === 38 || e.keyCode === 40) { navigateResults(e.keyCode === 38 ? 'up' : 'down'); return; } // 按下回车键 if (e.keyCode === 13) { var $selected = $results.find('.bdfg-search-item.selected'); if ($selected.length) { e.preventDefault(); window.location.href = $selected.attr('data-url'); } return; } // ESC键隐藏结果 if (e.keyCode === 27) { $results.hide(); return; } if (searchTerm.length < 2) { $results.hide(); return; } // 设置定时器延迟搜索,减少请求次数 typingTimer = setTimeout(function() { if (searchTerm === lastSearch) { return; } lastSearch = searchTerm; // 显示"正在搜索"提示 $results.html('<div class="bdfg-searching">' + bdfg_product_search.typing_text + '</div>').show(); // 如果有进行中的请求,中止它 if (xhr && xhr.readyState !== 4) { xhr.abort(); } // 发送AJAX请求 xhr = $.ajax({ url: bdfg_product_search.ajax_url, type: 'GET', data: { term: searchTerm }, beforeSend: function(xhr) { xhr.setRequestHeader('X-WP-Nonce', bdfg_product_search.nonce); }, success: function(response) { // 渲染搜索结果 renderResults(response, searchTerm); }, error: function(jqXHR, textStatus, errorThrown) { if (textStatus !== 'abort') { $results.html('<div class="bdfg-error">搜索出错,请重试。</div>'); } } }); }, 300); // 300ms延迟,避免频繁请求 }); // 点击文档其他区域关闭结果 $(document).on('click', function(e) { if (!$form.is(e.target) && $form.has(e.target).length === 0) { $results.hide(); } }); // 防止点击结果区域时触发表单提交 $results.on('click', function(e) { e.stopPropagation(); }); // 上下键导航结果 function navigateResults(direction) { var $items = $results.find('.bdfg-search-item'); var $selected = $results.find('.bdfg-search-item.selected'); var index = $items.index($selected); if (!$items.length) return; // 移除现有选择 $items.removeClass('selected'); if (direction === 'down') { // 向下导航 index = (index + 1) % $items.length; } else { // 向上导航 index = (index - 1 + $items.length) % $items.length; } // 设置新的选择项 $($items[index]).addClass('selected'); // 如果选中项不在视图中,滚动到可见区域 var $selectedItem = $($items[index]); var itemTop = $selectedItem.position().top; var itemHeight = $selectedItem.outerHeight(); var containerHeight = $results.height(); var scrollTop = $results.scrollTop(); if (itemTop < 0) { $results.scrollTop(scrollTop + itemTop); } else if (itemTop + itemHeight > containerHeight) { $results.scrollTop(scrollTop + itemTop - containerHeight + itemHeight); } } // 渲染搜索结果 function renderResults(results, searchTerm) { if (!results.length) { $results.html('<div class="bdfg-no-results">' + bdfg_product_search.no_results_text + '</div>'); return; } var html = '<ul class="bdfg-search-items">'; for (var i = 0; i < results.length; i++) { var result = results[i]; var productClass = 'bdfg-search-item'; if (i === 0) productClass += ' selected'; if (result.on_sale) productClass += ' on-sale'; html += '<li class="' + productClass + '" data-url="' + result.url + '">'; html += '<a href="' + result.url + '">'; // 产品图片 html += '<div class="bdfg-item-image">'; html += '<img src="' + result.image + '" alt="' + result.title + '">'; if (result.on_sale) { html += '<span class="bdfg-sale-badge">促销</span>'; } html += '</div>'; // 产品信息 html += '<div class="bdfg-item-info">'; html += '<h4 class="bdfg-item-title">' + highlightTerm(result.title, searchTerm) + '</h4>'; if (result.excerpt) { html += '<p class="bdfg-item-excerpt">' + truncateText(result.excerpt, 80) + '</p>'; } html += '<div class="bdfg-item-meta">'; // 价格 html += '<span class="bdfg-item-price">' + result.price_html + '</span>'; // 评分 if (result.rating > 0) { html += '<span class="bdfg-item-rating">'; html += getRatingStars(result.rating); html += '</span>'; } // 库存状态 html += '<span class="bdfg-item-stock ' + (result.stock === '有货' ? 'in-stock' : 'out-of-stock') + '">'; html += result.stock; html += '</span>'; html += '</div>'; // .bdfg-item-meta if (result.categories) { html += '<div class="bdfg-item-categories">' + result.categories + '</div>'; } // 如果是模糊匹配结果,添加提示 if (result.fuzzy) { html += '<div class="bdfg-fuzzy-match">模糊匹配结果</div>'; } html += '</div>'; // .bdfg-item-info html += '</a>'; html += '</li>'; } html += '</ul>'; // 添加查看全部结果链接 html += '<div class="bdfg-view-all">'; html += '<a href="' + bdfg_product_search.site_url + '?s=' + encodeURIComponent(searchTerm) + '&post_type=product">'; html += bdfg_product_search.view_all_text; html += '</a>'; html += '</div>'; $results.html(html); // 为结果项添加悬停效果 $results.find('.bdfg-search-item').hover( function() { $results.find('.bdfg-search-item').removeClass('selected'); $(this).addClass('selected'); }, function() { // 保持选中状态 } ); } // 高亮搜索词 function highlightTerm(text, term) { if (!term) return text; // 转义正则表达式特殊字符 term = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // 创建正则表达式 var regex = new RegExp('(' + term + ')', 'gi'); // 替换匹配项 return text.replace(regex, '<span class="bdfg-highlight">$1</span>'); } // 截断文本 function truncateText(text, length) { if (text.length <= length) return text; return text.substring(0, length) + '...'; } // 获取星级评分HTML function getRatingStars(rating) { var html = '<div class="bdfg-stars">'; var fullStars = Math.floor(rating); var halfStar = rating % 1 >= 0.5; var emptyStars = 5 - fullStars - (halfStar ? 1 : 0); // 填充星 for (var i = 0; i < fullStars; i++) { html += '<span class="bdfg-star bdfg-star-full">★</span>'; } // 半星 if (halfStar) { html += '<span class="bdfg-star bdfg-star-half">★</span>'; } // 空星 for (var j = 0; j < emptyStars; j++) { html += '<span class="bdfg-star bdfg-star-empty">☆</span>'; } html += '</div>'; return html; } }); } // 页面加载完成后初始化搜索 $(document).ready(function() { initBDFGProductSearch(); }); })(jQuery);
assets/js/bdfg-product-filters.js
/** * BDFG 产品搜索过滤器JS * * @package BDFG_Product_Search * @since 1.0.0 * @author BeiDuoFenGou <[email protected]> * @last_modified 2025-03-06 14:01:02 */ (function($) { 'use strict'; // 初始化价格滑块 function initPriceSlider() { var $slider = $('.bdfg-price-slider'); if ($slider.length) { $slider.each(function() { var $this = $(this); var $minInput = $('#min_price'); var $maxInput = $('#max_price'); var min = parseFloat($this.data('min')); var max = parseFloat($this.data('max')); var currentMin = parseFloat($this.data('current-min')); var currentMax = parseFloat($this.data('current-max')); $this.slider({ range: true, min: min, max: max, values: [currentMin, currentMax], slide: function(event, ui) { $minInput.val(ui.values[0]); $maxInput.val(ui.values[1]); } }); // 输入框变化时更新滑块 $minInput.on('change', function() { var value = parseFloat($(this).val()); if (isNaN(value)) { value = min; } if (value < min) { value = min; } if (value > $maxInput.val()) { value = $maxInput.val(); } $this.slider('values', 0, value); }); $maxInput.on('change', function() { var value = parseFloat($(this).val()); if (isNaN(value)) { value = max; } if (value > max) { value = max; } if (value < $minInput.val()) { value = $minInput.val(); } $this.slider('values', 1, value); }); }); } } // 初始化过滤器切换 function initFilterToggle() { $('.bdfg-toggle-filters').on('click', function() { var $this = $(this); var $container = $this.closest('.bdfg-product-filters').find('.bdfg-filters-container'); $container.slideToggle(300); if ($this.text() === '+') { $this.text('-'); } else { $this.text('+'); } }); } // 添加移动设备适配 function initMobileFilters() { if (window.matchMedia('(max-width: 768px)').matches) { $('.bdfg-product-filters h3').trigger('click'); } } // 页面加载完成后初始化 $(document).ready(function() { initPriceSlider(); initFilterToggle(); initMobileFilters(); }); })(jQuery);
assets/js/admin-script.js
/** * BDFG 产品搜索增强工具管理界面JS * * @package BDFG_Product_Search * @since 1.0.0 * @author BeiDuoFenGou <[email protected]> * @last_modified 2025-03-06 14:01:02 * @created_by BeiDuoFenGou */ (function($) { 'use strict'; // 初始化管理界面交互 function initAdminUI() { // 标签切换功能 $('.bdfg-admin-tabs .nav-tab').on('click', function(e) { e.preventDefault(); var target = $(this).attr('href').replace('#', ''); // 更新活动标签 $('.bdfg-admin-tabs .nav-tab').removeClass('nav-tab-active'); $(this).addClass('nav-tab-active'); // 显示目标内容 $('.bdfg-tab-content').removeClass('active'); $('#' + target + '-content').addClass('active'); // 存储用户偏好 if (typeof(Storage) !== "undefined") { localStorage.setItem('bdfg_active_tab', target); } }); // 恢复用户之前选择的标签 if (typeof(Storage) !== "undefined") { var lastTab = localStorage.getItem('bdfg_active_tab'); if (lastTab) { $('.bdfg-admin-tabs .nav-tab[href="#' + lastTab + '"]').trigger('click'); } } // 复制短代码功能 $('.bdfg-shortcode').on('click', function() { var $this = $(this); var shortcode = $this.text(); var tempTextarea = $('<textarea>'); $('body').append(tempTextarea); tempTextarea.val(shortcode).select(); document.execCommand('copy'); tempTextarea.remove(); $this.addClass('copied'); setTimeout(function() { $this.removeClass('copied'); }, 1000); // 显示复制成功提示 var $notification = $('<div class="bdfg-copy-notification">短代码已复制到剪贴板</div>'); $('body').append($notification); setTimeout(function() { $notification.addClass('show'); }, 10); setTimeout(function() { $notification.removeClass('show'); setTimeout(function() { $notification.remove(); }, 300); }, 2000); }); } // 初始化数据导出功能 function initDataExport() { $('#bdfg-export-analytics').on('click', function(e) { e.preventDefault(); var days = $('#export-days').val(); var url = $(this).data('url') + '&days=' + days; window.location.href = url; }); } // 初始化高级设置工具提示 function initTooltips() { $('.bdfg-has-tooltip').hover( function() { var $this = $(this); var tooltipText = $this.data('tooltip'); if (!tooltipText) return; var $tooltip = $('<div class="bdfg-tooltip">' + tooltipText + '</div>'); $this.append($tooltip); // 定位工具提示 var thisPos = $this.position(); var tooltipHeight = $tooltip.outerHeight(); $tooltip.css({ top: thisPos.top - tooltipHeight - 10, left: thisPos.left }); setTimeout(function() { $tooltip.addClass('show'); }, 10); }, function() { $(this).find('.bdfg-tooltip').remove(); } ); } // 页面加载完成后初始化管理界面 $(document).ready(function() { initAdminUI(); initDataExport(); initTooltips(); }); })(jQuery);
assets/css/bdfg-product-search.css
/** * BDFG 产品搜索增强工具前端样式 * * @package BDFG_Product_Search * @since 1.0.0 * @author BeiDuoFenGou <[email protected]> * @last_modified 2025-03-06 14:01:02 */ /* 搜索表单容器 */ .bdfg-search-form-container { position: relative; margin: 0 0 25px; width: 100%; } /* 搜索表单 */ .bdfg-search-form { position: relative; width: 100%; } /* 输入框包装 */ .bdfg-search-input-wrap { display: flex; position: relative; width: 100%; } /* 搜索字段 */ .bdfg-search-field { flex-grow: 1; height: 45px; padding: 10px 15px; font-size: 16px; line-height: 1.4; border: 2px solid #e3e3e3; border-right: none; border-radius: 4px 0 0 4px; background-color: #fff; box-sizing: border-box; transition: border-color 0.3s; outline: none; box-shadow: none; } .bdfg-search-field:focus { border-color: #176bef; } /* 搜索按钮 */ .bdfg-search-submit { height: 45px; padding: 0 20px; font-size: 14px; font-weight: 600; color: #fff; background-color: #176bef; border: 2px solid #176bef; border-radius: 0 4px 4px 0; cursor: pointer; transition: all 0.3s; outline: none; box-shadow: none; } .bdfg-search-submit:hover, .bdfg-search-submit:focus { background-color: #0e5ad7; border-color: #0e5ad7; } /* 搜索图标 */ .bdfg-search-icon { display: inline-block; width: 18px; height: 18px; margin-left: 5px; vertical-align: -4px; background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="%23ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>'); background-repeat: no-repeat; background-position: center; } /* 搜索结果容器 */ .bdfg-search-results { display: none; position: absolute; top: 100%; left: 0; width: 100%; max-height: 450px; margin-top: 4px; overflow-y: auto; background-color: #fff; border: 1px solid #e3e3e3; border-radius: 4px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); z-index: 9999; } /* 搜索中状态 */ .bdfg-searching { padding: 15px; text-align: center; color: #666; font-style: italic; } /* 无结果状态 */ .bdfg-no-results { padding: 15px; text-align: center; color: #666; } /* 搜索结果列表 */ .bdfg-search-items { list-style: none; margin: 0; padding: 0; } /* 搜索结果项 */ .bdfg-search-item { padding: 0; margin: 0; border-bottom: 1px solid #f0f0f0; transition: background-color 0.2s; } .bdfg-search-item:last-child { border-bottom: none; } .bdfg-search-item a { display: flex; padding: 10px; text-decoration: none !important; color: #333 !important; box-shadow: none !important; } .bdfg-search-item:hover, .bdfg-search-item.selected { background-color: #f9f9f9; } /* 搜索结果项图片 */ .bdfg-item-image { position: relative; width: 60px; min-width: 60px; height: 60px; margin-right: 12px; overflow: hidden; border-radius: 3px; } .bdfg-item-image img { width: 100%; height: 100%; object-fit: cover; display: block; } /* 促销标签 */ .bdfg-sale-badge { position: absolute; top: 0; right: 0; padding: 2px 5px; font-size: 10px; font-weight: 600; color: #fff; background-color: #e03232; border-radius: 0 0 0 3px; } /* 搜索结果项信息 */ .bdfg-item-info { flex-grow: 1; overflow: hidden; } .bdfg-item-title { margin: 0 0 5px; font-size: 14px; font-weight: 600; line-height: 1.3; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bdfg-item-excerpt { margin: 0 0 5px; font-size: 12px; line-height: 1.4; color: #666; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 元数据 */ .bdfg-item-meta { display: flex; align-items: center; flex-wrap: wrap; font-size: 12px; color: #666; } .bdfg-item-price { margin-right: 10px; font-weight: 600; color: #176bef; } .bdfg-item-rating { margin-right: 10px; } .bdfg-item-stock { font-size: 11px; padding: 2px 6px; border-radius: 10px; } .bdfg-item-stock.in-stock { background-color: #e8f5e9; color: #2e7d32; } .bdfg-item-stock.out-of-stock { background-color: #ffebee; color: #c62828; } /* 分类信息 */ .bdfg-item-categories { margin-top: 5px; font-size: 11px; color: #888; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 模糊匹配提示 */ .bdfg-fuzzy-match { margin-top: 5px; font-size: 10px; font-style: italic; color: #888; } /* 高亮匹配文本 */ .bdfg-highlight { font-weight: 700; background-color: #fff9c4; padding: 0 2px; } /* 评分星星 */ .bdfg-stars { display: inline-flex; line-height: 1; } .bdfg-star { font-size: 12px; color: #ffc107; } .bdfg-star-empty { color: #e0e0e0; } /* 查看全部结果链接 */ .bdfg-view-all { padding: 10px; text-align: center; border-top: 1px solid #f0f0f0; background-color: #f9f9f9; } .bdfg-view-all a { font-size: 13px; font-weight: 600; color: #176bef; text-decoration: none; } .bdfg-view-all a:hover { text-decoration: underline; } /* 错误状态 */ .bdfg-error { padding: 15px; text-align: center; color: #c62828; } /* 响应式调整 */ @media (max-width: 768px) { .bdfg-search-field { font-size: 14px; height: 40px; } .bdfg-search-submit { height: 40px; padding: 0 15px; font-size: 13px; } .bdfg-search-results { max-height: 350px; } .bdfg-item-image { width: 50px; min-width: 50px; height: 50px; } } /* BDFG品牌样式 */ .bdfg-powered { font-size: 11px; text-align: right; margin-top: 5px; color: #999; } .bdfg-powered a { color: #176bef; text-decoration: none; }
assets/css/bdfg-product-filters.css
/** * BDFG 产品搜索过滤器样式 * * @package BDFG_Product_Search * @since 1.0.0 * @author BeiDuoFenGou <[email protected]> * @last_modified 2025-03-06 14:01:02 */ /* 产品过滤器容器 */ .bdfg-product-filters { margin: 0 0 25px; padding: 15px; background-color: #f9f9f9; border-radius: 4px; border: 1px solid #eee; } /* 标题 */ .bdfg-product-filters h3 { margin: 0 0 15px; padding-bottom: 10px; font-size: 16px; border-bottom: 1px solid #e3e3e3; position: relative; cursor: pointer; } /* 切换按钮 */ .bdfg-toggle-filters { position: absolute; right: 0; top: 0; font-size: 20px; font-weight: bold; line-height: 1; color: #666; transition: transform 0.3s; } /* 过滤器表单容器 */ .bdfg-filters-container { display: block; } /* 过滤器组 */ .bdfg-filter-group { margin-bottom: 15px; } .bdfg-filter-group label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; color: #444; } /* 下拉选择器 */ .bdfg-select { width: 100%; height: 38px; padding: 5px 10px; font-size: 14px; border: 1px solid #ddd; border-radius: 4px; background-color: #fff; box-shadow: none; outline: none; transition: border-color 0.2s; } .bdfg-select:focus { border-color: #176bef; } /* 价格滑块容器 */ .bdfg-price-slider-container { padding: 10px 0; } /* 价格值显示 */ .bdfg-price-slider-values { display: flex; align-items: center; margin-bottom: 10px; } .bdfg-price-label { font-weight: bold; color: #333; } .bdfg-price-separator { margin: 0 8px; color: #666; } .bdfg-price-input { width: 80px; height: 32px; padding: 5px; font-size: 13px; border: 1px solid #ddd; border-radius: 3px; text-align: center; outline: none; } /* 价格滑块 */ .bdfg-price-slider { margin: 10px 7px 5px; height: 8px; border-radius: 4px; background-color: #e0e0e0; border: none; } .bdfg-price-slider .ui-slider-range { background-color: #176bef; border-radius: 4px; } .bdfg-price-slider .ui-slider-handle { width: 18px; height: 18px; background-color: #fff; border: 2px solid #176bef; border-radius: 50%; top: -5px; cursor: pointer; outline: none; } .bdfg-price-slider .ui-slider-handle:focus, .bdfg-price-slider .ui-slider-handle:active { border-color: #0e5ad7; background-color: #f9f9f9; } /* 复选框样式 */ .bdfg-filter-checkbox { margin-top: 15px; } .bdfg-filter-checkbox label { display: flex; align-items: center; cursor: pointer; } .bdfg-filter-checkbox input[type="checkbox"] { margin-right: 8px; } /* 按钮组 */ .bdfg-filter-actions { display: flex; margin-top: 20px; } .bdfg-filter-button { padding: 8px 15px; font-size: 14px; font-weight: 600; border: none; border-radius: 3px; cursor: pointer; text-decoration: none; text-align: center; transition: all 0.2s; } .bdfg-apply-button { color: #fff; background-color: #176bef; margin-right: 10px; } .bdfg-apply-button:hover { background-color: #0e5ad7; } .bdfg-reset-button { color: #333; background-color: #e0e0e0; display: inline-block; } .bdfg-reset-button:hover { background-color: #ccc; } /* 响应式调整 */ @media (max-width: 768px) { .bdfg-filters-container { display: none; } .bdfg-price-slider-values { flex-wrap: wrap; } .bdfg-price-input { width: 70px; } .bdfg-filter-button { flex: 1; padding: 10px 5px; } } /* 在小页面中使用紧凑布局 */ @media (max-width: 480px) { .bdfg-product-filters { padding: 10px; } .bdfg-price-input { width: 60px; } } /* BDFG品牌样式 */ .bdfg-filters-footer { margin-top: 15px; text-align: right; font-size: 11px; color: #999; } .bdfg-filters-footer a { color: #176bef; text-decoration: none; }
assets/css/admin-style.css
/** * BDFG 产品搜索增强工具管理界面样式 * * @package BDFG_Product_Search * @since 1.0.0 * @author BeiDuoFenGou <[email protected]> * @last_modified 2025-03-06 14:01:02 */ /* 主容器 */ .bdfg-admin-wrap { max-width: 1200px; margin: 20px 0; } /* 头部 */ .bdfg-admin-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid #e0e0e0; } .bdfg-logo { display: flex; align-items: center; } .bdfg-logo img { width: 40px; height: 40px; margin-right: 10px; } .bdfg-logo h1 { margin: 0; font-size: 22px; font-weight: 600; color: #333; } .bdfg-version { font-size: 13px; color: #666; background-color: #f0f0f0; padding: 4px 8px; border-radius: 10px; } /* 内容区域 */ .bdfg-admin-content { background-color: #fff; padding: 20px; border: 1px solid #e0e0e0; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } /* 标签导航 */ .bdfg-admin-tabs { margin-bottom: 20px; border-bottom: 1px solid #e0e0e0; } .nav-tab { margin-bottom: -1px; } .bdfg-tab-content { display: none; padding: 15px 0; } .bdfg-tab-content.active { display: block; } /* 表单样式 */ .form-table th { padding: 20px 10px 20px 0; width: 200px; } /* 开关样式 */ .bdfg-switch { position: relative; display: inline-block; width: 48px; height: 24px; margin-right: 10px; } .bdfg-switch input { opacity: 0; width: 0; height: 0; } .bdfg-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .3s; border-radius: 24px; } .bdfg-slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .3s; border-radius: 50%; } input:checked + .bdfg-slider { background-color: #176bef; } input:focus + .bdfg-slider { box-shadow: 0 0 1px #176bef; } input:checked + .bdfg-slider:before { transform: translateX(24px); } .bdfg-checkbox-label { font-weight: 600; vertical-align: middle; } /* 数字字段 */ .bdfg-number-field { display: flex; align-items: center; } .bdfg-number-field input { width: 70px; text-align: center; margin-right: 10px; } /* 短代码 */ .bdfg-shortcode-box { margin-bottom: 25px; padding: 15px; background-color: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 4px; } .bdfg-shortcode-box h3 { margin-top: 0; margin-bottom: 10px; font-size: 14px; font-weight: 600; } .bdfg-shortcode { display: inline-block; padding: 8px 12px; margin-bottom: 10px; font-family: monospace; font-size: 13px; color: #176bef; background-color: #fff; border: 1px solid #e0e0e0; border-radius: 3px; cursor: pointer; transition: all 0.2s; } .bdfg-shortcode:hover { border-color: #176bef; background-color: #f0f7ff; } .bdfg-shortcode.copied { border-color: #4caf50; background-color: #e8f5e9; color: #2e7d32; } .bdfg-shortcode-attrs { width: 100%; margin-top: 15px; border-collapse: collapse; } .bdfg-shortcode-attrs th, .bdfg-shortcode-attrs td { padding: 8px; text-align: left; border-bottom: 1px solid #e0e0e0; } .bdfg-shortcode-attrs th { background-color: #f0f0f0; font-weight: 600; } /* 复制提示 */ .bdfg-copy-notification { position: fixed; bottom: 20px; right: 20px; padding: 10px 15px; background-color: #333; color: #fff; border-radius: 3px; font-size: 14px; z-index: 9999; opacity: 0; transform: translateY(20px); transition: all 0.3s; } .bdfg-copy-notification.show { opacity: 1; transform: translateY(0); } /* 支持框 */ .bdfg-support-box { margin-bottom: 20px; padding: 15px; background-color: #f9f9f9; border-left: 4px solid #176bef; border-radius: 3px; } .bdfg-support-box h3 { margin-top: 0; margin-bottom: 10px; font-size: 15px; color: #333; } .bdfg-support-box p { margin: 0; } /* 分析面板样式 */ .bdfg-analytics-wrap { max-width: 1200px; } .bdfg-date-filter { display: flex; justify-content: flex-end; margin-bottom: 15px; } .bdfg-analytics-dashboard { margin-top: 20px; } .bdfg-analytics-row { display: flex; flex-wrap: wrap; margin: 0 -10px 20px; } .bdfg-analytics-col { padding: 0 10px; flex: 1; min-width: 300px; margin-bottom: 20px; } .bdfg-card { padding: 15px; background-color: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); text-align: center; margin: 0 5px 10px; flex: 1; min-width: 150px; } .bdfg-card-header { font-size: 14px; font-weight: normal; color: #666; margin-bottom: 5px; } .bdfg-card-value { font-size: 24px; font-weight: 600; color: #333; margin: 5px 0; } .bdfg-card-footer { font-size: 12px; color: #666; } .bdfg-card-footer .positive { color: #4caf50; } .bdfg-card-footer .negative { color: #f44336; } .bdfg-analytics-card { background-color: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); padding: 15px; margin-bottom: 20px; } .bdfg-analytics-card h2 { margin-top: 0; margin-bottom: 15px; font-size: 16px; font-weight: 600; color: #333; border-bottom: 1px solid #e0e0e0; padding-bottom: 10px; } .bdfg-chart-container { height: 300px; position: relative; } .bdfg-analytics-table { width: 100%; border-collapse: collapse; } .bdfg-analytics-table th, .bdfg-analytics-table td { padding: 8px 10px; text-align: left; border-bottom: 1px solid #e0e0e0; } .bdfg-analytics-table th { background-color: #f5f5f5; font-weight: 600; } .bdfg-analytics-table tr:hover td { background-color: #f9f9f9; } .bdfg-no-data { padding: 20px; text-align: center; color: #666; font-style: italic; } /* 帮助页面样式 */ .bdfg-help-wrap { display: flex; flex-direction: column; } .bdfg-help-nav { margin-bottom: 20px; border-bottom: 1px solid #e0e0e0; } .bdfg-help-nav ul { display: flex; flex-wrap: wrap; margin: 0; padding: 0; list-style: none; } .bdfg-help-nav li { margin: 0; padding: 0; } .bdfg-help-nav a { display: block; padding: 10px 15px; font-size: 14px; font-weight: 500; text-decoration: none; color: #333; border-bottom: 2px solid transparent; } .bdfg-help-nav a:hover { color: #176bef; } .bdfg-help-nav a.active { color: #176bef; border-bottom-color: #176bef; } .bdfg-help-content { flex: 1; } .bdfg-help-section { display: none; padding: 10px 0; } .bdfg-help-section.active { display: block; } .bdfg-help-box { margin-bottom: 25px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 4px; background-color: #f9f9f9; } .bdfg-help-box h3 { margin-top: 0; margin-bottom: 10px; font-size: 16px; color: #333; } .bdfg-help-box img { max-width: 100%; height: auto; margin: 10px 0; border: 1px solid #e0e0e0; border-radius: 4px; } .bdfg-help-columns { display: flex; flex-wrap: wrap; margin: 0 -10px; } .bdfg-help-column { flex: 1; padding: 0 10px; min-width: 300px; margin-bottom: 20px; } .bdfg-feature-box { padding: 15px; margin-bottom: 20px; background-color: #fff; border: 1px solid #e0e0e0; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); } .bdfg-feature-box h3 { margin-top: 0; color: #176bef; } .bdfg-faq-item { margin-bottom: 15px; border-bottom: 1px solid #e0e0e0; } .bdfg-faq-item h3 { position: relative; margin: 0; padding: 12px 0; font-size: 15px; font-weight: 500; color: #333; cursor: pointer; } .bdfg-faq-item h3::after { content: "+"; position: absolute; right: 0; top: 12px; font-size: 18px; font-weight: normal; color: #666; transition: transform 0.3s; } .bdfg-faq-item.active h3::after { content: "-"; } .bdfg-faq-answer { display: none; padding: 0 0 15px; } .bdfg-faq-item.active .bdfg-faq-answer { display: block; } /* 页脚 */ .bdfg-admin-footer { margin-top: 20px; padding-top: 15px; border-top: 1px solid #e0e0e0; text-align: center; color: #666; } .bdfg-admin-footer p { margin: 0; font-size: 13px; } .bdfg-admin-footer a { color: #176bef; text-decoration: none; } /* 工具提示 */ .bdfg-has-tooltip { position: relative; display: inline-block; margin-left: 5px; width: 16px; height: 16px; background-color: #f0f0f0; border-radius: 50%; color: #666; font-size: 12px; line-height: 16px; text-align: center; cursor: help; } .bdfg-tooltip { position: absolute; z-index: 100; width: 200px; padding: 8px 10px; background-color: #333; color: #fff; font-size: 12px; font-weight: normal; line-height: 1.4; border-radius: 3px; opacity: 0; transform: translateY(5px); transition: all 0.2s; pointer-events: none; } .bdfg-tooltip::after { content: ""; position: absolute; bottom: -5px; left: 50%; margin-left: -5px; border-width: 5px 5px 0; border-style: solid; border-color: #333 transparent transparent; } .bdfg-tooltip.show { opacity: 1; transform: translateY(0); } /* 适配暗色主题 */ @media (prefers-color-scheme: dark) { .admin-color-midnight .bdfg-admin-content, .admin-color-modern .bdfg-admin-content, .admin-color-ocean .bdfg-admin-content, .admin-color-coffee .bdfg-admin-content, .admin-color-ectoplasm .bdfg-admin-content, .admin-color-blue .bdfg-admin-content, .admin-color-sunrise .bdfg-admin-content { background-color: #32373c; border-color: #23282d; } } /* 响应式调整 */ @media (max-width: 782px) { .bdfg-admin-header { flex-direction: column; align-items: flex-start; } .bdfg-version { margin-top: 10px; } .bdfg-analytics-col { flex: 100%; } .bdfg-chart-container { height: 250px; } .form-table th { width: auto; } }