一款功能强大且用户友好的 WooCommerce 产品过滤解决方案。
<?php /** * Plugin Name: BDFG Product Filters for WooCommerce * Plugin URI: https://beiduofengou.net/2024/11/16/bdfg-product-filters-for-woocommerce/ * Description: A powerful and user-friendly product filtering solution for WooCommerce. Developed by Beiduofengou team. * Version: 2.5.0 * Author: beiduofengou * Author URI: https://beiduofengou.net * Text Domain: bdfg-product-filters * Domain Path: /languages * Requires at least: 5.8 * Requires PHP: 7.4 * WC requires at least: 5.0 * WC tested up to: 8.0 * License: GPL v2 or later * * @package BDFG_Product_Filters * @author beiduofengou <[email protected]> * @link https://beiduofengou.net * @since 1.0.0 */ // 防止直接访问此文件 if (!defined('ABSPATH')) { exit('Direct access not allowed.'); } if (!class_exists('BDFG_Product_Filters')) { /** * 主插件类 * * @since 2.0.0 */ class BDFG_Product_Filters { /** * 插件实例 * * @var BDFG_Product_Filters */ private static $instance = null; /** * 插件版本号 * * @var string */ private $version = '2.5.0'; /** * 错误信息数组 * * @var array */ private $errors = array(); /** * 获取插件实例 * * @return BDFG_Product_Filters */ public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * 构造函数 */ private function __construct() { // 定义常量和钩子 $this->define_constants(); $this->init_hooks(); // 加载翻译文件 add_action('plugins_loaded', array($this, 'load_plugin_textdomain')); // 检查依赖 if ($this->check_requirements()) { $this->includes(); $this->init(); // 添加插件设置链接 add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'add_plugin_links')); } } /** * 定义插件常量 */ private function define_constants() { define('BDFG_VERSION', $this->version); define('BDFG_FILE', __FILE__); define('BDFG_PATH', plugin_dir_path(__FILE__)); define('BDFG_URL', plugin_dir_url(__FILE__)); define('BDFG_BASENAME', plugin_basename(__FILE__)); define('BDFG_ASSETS_URL', BDFG_URL . 'assets/'); } /** * 初始化钩子 */ private function init_hooks() { // 注册激活和停用钩子 register_activation_hook(__FILE__, array($this, 'activate')); register_deactivation_hook(__FILE__, array($this, 'deactivate')); // 后台钩子 if (is_admin()) { add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_enqueue_scripts', array($this, 'admin_scripts')); add_action('admin_notices', array($this, 'show_admin_notices')); } // 前台钩子 add_action('wp_enqueue_scripts', array($this, 'frontend_scripts')); add_action('init', array($this, 'register_shortcodes')); } /** * 检查插件依赖 * * @return bool */ public function check_requirements() { if (!class_exists('WooCommerce')) { $this->errors[] = sprintf( __('%s requires WooCommerce to be installed and activated. You can download %s here.', 'bdfg-product-filters'), '<strong>BDFG Product Filters</strong>', '<a href="https://wordpress.org/plugins/woocommerce/" target="_blank">WooCommerce</a>' ); add_action('admin_notices', array($this, 'show_admin_notices')); return false; } if (version_compare(PHP_VERSION, '7.4', '<')) { $this->errors[] = sprintf( __('%s requires PHP version 7.4 or higher. Please upgrade PHP to run this plugin.', 'bdfg-product-filters'), '<strong>BDFG Product Filters</strong>' ); add_action('admin_notices', array($this, 'show_admin_notices')); return false; } return true; } /** * 显示管理员通知 */ public function show_admin_notices() { if (!empty($this->errors)) { foreach ($this->errors as $error) { echo '<div class="error"><p>' . wp_kses_post($error) . '</p></div>'; } } } /** * 包含必要的文件 */ private function includes() { // 工具类 require_once BDFG_PATH . 'includes/class-bdfg-helper.php'; // 核心类 require_once BDFG_PATH . 'includes/class-bdfg-core-filter.php'; require_once BDFG_PATH . 'includes/class-bdfg-advanced-features.php'; // 管理员类 if (is_admin()) { require_once BDFG_PATH . 'admin/class-bdfg-admin.php'; require_once BDFG_PATH . 'admin/class-bdfg-settings.php'; } // API类 require_once BDFG_PATH . 'includes/api/class-bdfg-api.php'; } /** * 初始化插件 */ private function init() { // 初始化过滤器类型 new BDFG_Taxonomy_Filter(); new BDFG_Price_Filter(); new BDFG_Review_Filter(); new BDFG_Name_Filter(); // 添加短代码支持 add_shortcode('bdfg_filter', array($this, 'render_filter_shortcode')); // 初始化高级功能 if (get_option('bdfg_enable_advanced_features', 'yes') === 'yes') { BDFG_Init_Advanced_Features(); } } /** * 前端脚本和样式 */ public function frontend_scripts() { // 注册样式 wp_register_style( 'bdfg-filters-style', BDFG_ASSETS_URL . 'css/frontend.min.css', array(), BDFG_VERSION ); // 注册脚本 wp_register_script( 'bdfg-filters-script', BDFG_ASSETS_URL . 'js/frontend.min.js', array('jquery', 'wp-util'), BDFG_VERSION, true ); // 本地化脚本 wp_localize_script('bdfg-filters-script', 'bdfgFilters', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('bdfg-filters-nonce'), 'i18n' => array( 'noResults' => __('No products found', 'bdfg-product-filters'), 'loading' => __('Loading...', 'bdfg-product-filters'), 'error' => __('Error occurred', 'bdfg-product-filters') ) )); // 在商店和分类页面加载资源 if (is_shop() || is_product_category() || is_product_tag()) { wp_enqueue_style('bdfg-filters-style'); wp_enqueue_script('bdfg-filters-script'); } } /** * 渲染过滤器短代码 * * @param array $atts 短代码属性 * @return string */ public function render_filter_shortcode($atts) { $atts = shortcode_atts(array( 'type' => 'taxonomy', 'taxonomy' => 'product_cat', 'title' => '', 'display' => 'dropdown', 'hide_empty' => 'yes', 'show_count' => 'yes', 'orderby' => 'name', 'order' => 'ASC' ), $atts, 'bdfg_filter'); // 开始输出缓冲 ob_start(); // 加载对应的模板 $template_path = BDFG_PATH . 'templates/' . $atts['type'] . '-filter.php'; $theme_template_path = get_stylesheet_directory() . '/bdfg-product-filters/' . $atts['type'] . '-filter.php'; // 检查主题是否有自定义模板 if (file_exists($theme_template_path)) { $template_path = $theme_template_path; } if (file_exists($template_path)) { include $template_path; } else { _e('Template not found.', 'bdfg-product-filters'); } return ob_get_clean(); } /** * 激活插件 */ public function activate() { // 创建必要的数据库表和选项 $this->create_tables(); $this->add_default_options(); // 清除缓存 $this->clear_cache(); // 刷新重写规则 flush_rewrite_rules(); // 记录激活时间 update_option('bdfg_activated_time', time()); update_option('bdfg_version', BDFG_VERSION); } /** * 创建数据库表 */ private function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 创建缓存表 $sql = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}bdfg_cache ( cache_key varchar(255) NOT NULL, cache_value longtext NOT NULL, expiration bigint(20) NOT NULL, PRIMARY KEY (cache_key) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } /** * 添加默认选项 */ private function add_default_options() { $default_options = array( 'bdfg_enable_advanced_features' => 'yes', 'bdfg_cache_duration' => 3600, 'bdfg_show_vendor_info' => 'yes', 'bdfg_enable_recommendations' => 'yes', 'bdfg_products_per_page' => 12 ); foreach ($default_options as $key => $value) { if (get_option($key) === false) { update_option($key, $value); } } } /** * 停用插件 */ public function deactivate() { // 清除所有缓存 $this->clear_cache(); // 记录停用时间 update_option('bdfg_deactivated_time', time()); } /** * 清除缓存 */ private function clear_cache() { global $wpdb; // 清除瞬态 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%_transient_bdfg_%'"); $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%_transient_timeout_bdfg_%'"); // 清除缓存表 $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}bdfg_cache"); } /** * 加载插件文本域 */ public function load_plugin_textdomain() { load_plugin_textdomain( 'bdfg-product-filters', false, dirname(plugin_basename(__FILE__)) . '/languages/' ); } /** * 添加插件设置链接 * * @param array $links 现有的插件链接 * @return array 修改后的插件链接 */ public function add_plugin_links($links) { $plugin_links = array( '<a href="' . admin_url('admin.php?page=bdfg-settings') . '">' . __('Settings', 'bdfg-product-filters') . '</a>', '<a href="https://beiduofengou.net/docs/bdfg-product-filters/" target="_blank">' . __('Documentation', 'bdfg-product-filters') . '</a>', '<a href="https://beiduofengou.net/support/" target="_blank">' . __('Support', 'bdfg-product-filters') . '</a>' ); return array_merge($plugin_links, $links); } } /** * 返回主插件类的实例 * * @return BDFG_Product_Filters */ function BDFG_Product_Filters() { return BDFG_Product_Filters::get_instance(); } // 初始化插件 BDFG_Product_Filters(); }
class-bdfg-core-filter.php
相关文章: WordPress 智能缓存管理
<?php /** * BDFG Product Filters - Core Filter Classes * * @package BDFG_Product_Filters * @author beiduofengou * @link https://beiduofengou.net * @since 2.0.0 */ if (!defined('ABSPATH')) { exit('Direct access not allowed.'); } /** * 过滤器基类 * * @since 2.0.0 */ abstract class BDFG_Filter_Base { /** * 过滤器类型 * @var string */ protected $filter_type; /** * 过滤器选项 * @var array */ protected $filter_options; /** * 缓存时间(秒) * @var int */ protected $cache_time; /** * 构造函数 */ public function __construct() { // 从设置获取缓存时间,默认1小时 $this->cache_time = (int)get_option('bdfg_cache_duration', 3600); $this->init(); $this->add_hooks(); // 添加调试日志 if(defined('WP_DEBUG') && WP_DEBUG) { error_log(sprintf('[BDFG] Initialized filter: %s', $this->filter_type)); } } /** * 初始化过滤器 */ protected function init() { // 获取过滤器特定选项 $this->filter_options = get_option( 'bdfg_filter_' . $this->filter_type . '_options', $this->get_default_options() ); } /** * 获取默认选项 * * @return array */ protected function get_default_options() { return array(); } /** * 添加WordPress钩子 */ protected function add_hooks() { // 修改产品查询 add_filter('woocommerce_product_query', array($this, 'modify_product_query'), 10, 2); // AJAX处理 add_action('wp_ajax_bdfg_filter_products', array($this, 'ajax_filter_products')); add_action('wp_ajax_nopriv_bdfg_filter_products', array($this, 'ajax_filter_products')); // 添加自定义REST API端点 add_action('rest_api_init', array($this, 'register_rest_routes')); } /** * 生成缓存键 * * @param array $args 查询参数 * @return string */ protected function get_cache_key($args) { return 'bdfg_' . $this->filter_type . '_' . md5(serialize($args)); } /** * 获取缓存结果 * * @param string $key 缓存键 * @return mixed|false */ protected function get_cached_results($key) { global $wpdb; $now = time(); $result = $wpdb->get_row($wpdb->prepare( "SELECT cache_value FROM {$wpdb->prefix}bdfg_cache WHERE cache_key = %s AND expiration > %d", $key, $now )); return $result ? maybe_unserialize($result->cache_value) : false; } /** * 设置缓存结果 * * @param string $key 缓存键 * @param mixed $data 要缓存的数据 */ protected function set_cached_results($key, $data) { global $wpdb; $expiration = time() + $this->cache_time; $wpdb->replace( $wpdb->prefix . 'bdfg_cache', array( 'cache_key' => $key, 'cache_value' => maybe_serialize($data), 'expiration' => $expiration ), array('%s', '%s', '%d') ); } /** * 注册REST API路由 */ public function register_rest_routes() { register_rest_route('bdfg/v1', '/' . $this->filter_type . '/filter', array( 'methods' => 'GET', 'callback' => array($this, 'rest_filter_products'), 'permission_callback' => '__return_true' )); } /** * REST API过滤处理 * * @param WP_REST_Request $request * @return WP_REST_Response */ public function rest_filter_products($request) { try { $params = $request->get_params(); $results = $this->filter_products($params); return rest_ensure_response(array( 'success' => true, 'data' => $results )); } catch (Exception $e) { return new WP_Error( 'bdfg_filter_error', $e->getMessage(), array('status' => 400) ); } } /** * 格式化产品数据 * * @param WC_Product $product * @return array */ protected function format_product_data($product) { return array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price_html' => $product->get_price_html(), 'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'), 'permalink' => get_permalink($product->get_id()), 'average_rating' => $product->get_average_rating(), 'review_count' => $product->get_review_count(), 'stock_status' => $product->get_stock_status(), 'categories' => wp_list_pluck( get_the_terms($product->get_id(), 'product_cat'), 'name' ), 'tags' => wp_list_pluck( get_the_terms($product->get_id(), 'product_tag'), 'name' ), ); } /** * 记录错误日志 * * @param string $message * @param mixed $context */ protected function log_error($message, $context = array()) { if (defined('WP_DEBUG') && WP_DEBUG) { error_log(sprintf( '[BDFG Error][%s] %s | Context: %s', $this->filter_type, $message, print_r($context, true) )); } } /** * 渲染过滤器HTML * * @param array $args 过滤器参数 * @return string 过滤器HTML */ abstract public function render_filter($args = array()); /** * 修改WooCommerce产品查询 * * @param WP_Query $query * @param array $query_vars * @return WP_Query */ abstract public function modify_product_query($query, $query_vars); /** * AJAX产品过滤处理 */ abstract public function ajax_filter_products(); } /** * 注册核心过滤器 */ function bdfg_register_core_filters() { $filters = array( 'BDFG_Taxonomy_Filter', 'BDFG_Price_Filter', 'BDFG_Review_Filter', 'BDFG_Name_Filter' ); foreach ($filters as $filter_class) { if (class_exists($filter_class)) { new $filter_class(); } } } add_action('init', 'bdfg_register_core_filters'); /** * 分类过滤器实现类 */ class BDFG_Taxonomy_Filter extends BDFG_Filter_Base { /** * 构造函数 */ public function __construct() { $this->filter_type = 'taxonomy'; parent::__construct(); } /** * 渲染分类过滤器 */ public function render_filter($args = array()) { $defaults = array( 'taxonomy' => 'product_cat', 'display_type' => 'dropdown', 'hide_empty' => true, 'show_count' => true, 'hierarchical' => true, 'orderby' => 'name', 'order' => 'ASC' ); $args = wp_parse_args($args, $defaults); $terms = $this->get_taxonomy_terms($args); ob_start(); ?> <div class="bdfg-filter bdfg-taxonomy-filter" data-filter-type="taxonomy" data-taxonomy="<?php echo esc_attr($args['taxonomy']); ?>"> <?php if ($args['display_type'] === 'dropdown'): ?> <select class="bdfg-select" name="bdfg_tax_<?php echo esc_attr($args['taxonomy']); ?>"> <option value=""><?php echo sprintf(__('Select %s', 'bdfg-product-filters'), $this->get_taxonomy_label($args['taxonomy'])); ?></option> <?php $this->render_term_options($terms, $args); ?> </select> <?php else: ?> <ul class="bdfg-checkbox-list"> <?php $this->render_term_checkboxes($terms, $args); ?> </ul> <?php endif; ?> </div> <?php return ob_get_clean(); } /** * 获取分类列表 */ protected function get_taxonomy_terms($args) { $cache_key = $this->get_cache_key($args); $terms = $this->get_cached_results($cache_key); if ($terms === false) { $terms = get_terms(array( 'taxonomy' => $args['taxonomy'], 'hide_empty' => $args['hide_empty'], 'hierarchical' => $args['hierarchical'], 'orderby' => $args['orderby'], 'order' => $args['order'] )); if (!is_wp_error($terms)) { $this->set_cached_results($cache_key, $terms); } else { $this->log_error('Failed to get taxonomy terms', array( 'taxonomy' => $args['taxonomy'], 'error' => $terms->get_error_message() )); $terms = array(); } } return $terms; } /** * 渲染分类选项 */ protected function render_term_options($terms, $args, $depth = 0) { foreach ($terms as $term) { $indent = str_repeat(' ', $depth); printf( '<option value="%s">%s%s%s</option>', esc_attr($term->slug), $indent, esc_html($term->name), $args['show_count'] ? ' (' . absint($term->count) . ')' : '' ); if ($args['hierarchical'] && !empty($term->children)) { $this->render_term_options($term->children, $args, $depth + 1); } } } /** * 渲染分类复选框 */ protected function render_term_checkboxes($terms, $args, $depth = 0) { foreach ($terms as $term) { $indent = str_repeat(' ', $depth); ?> <li class="bdfg-checkbox-item level-<?php echo esc_attr($depth); ?>"> <label> <input type="checkbox" name="bdfg_tax_<?php echo esc_attr($args['taxonomy']); ?>[]" value="<?php echo esc_attr($term->slug); ?>"> <?php echo $indent . esc_html($term->name); if ($args['show_count']) { echo ' (' . absint($term->count) . ')'; } ?> </label> </li> <?php if ($args['hierarchical'] && !empty($term->children)) { echo '<ul class="bdfg-checkbox-children">'; $this->render_term_checkboxes($term->children, $args, $depth + 1); echo '</ul>'; } } } /** * 修改产品查询 */ public function modify_product_query($query, $query_vars) { if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) { $tax_query = array(); foreach ($_GET as $key => $value) { if (strpos($key, 'bdfg_tax_') === 0) { $taxonomy = str_replace('bdfg_tax_', '', $key); if (!empty($value)) { $tax_query[] = array( 'taxonomy' => sanitize_key($taxonomy), 'field' => 'slug', 'terms' => is_array($value) ? array_map('sanitize_title', $value) : array(sanitize_title($value)), 'operator' => 'IN' ); } } } if (!empty($tax_query)) { // 合并现有的tax_query $existing_tax_query = $query->get('tax_query', array()); if (!empty($existing_tax_query)) { $tax_query = array_merge( array('relation' => 'AND'), $existing_tax_query, $tax_query ); } $query->set('tax_query', $tax_query); } } return $query; } /** * AJAX产品过滤 */ public function ajax_filter_products() { check_ajax_referer('bdfg-filters-nonce', 'nonce'); $taxonomies = isset($_POST['taxonomies']) ? (array)$_POST['taxonomies'] : array(); try { $args = array( 'post_type' => 'product', 'posts_per_page' => apply_filters('bdfg_products_per_page', 12), 'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1, 'tax_query' => array('relation' => 'AND') ); foreach ($taxonomies as $taxonomy => $terms) { if (!empty($terms)) { $args['tax_query'][] = array( 'taxonomy' => sanitize_key($taxonomy), 'field' => 'slug', 'terms' => array_map('sanitize_title', (array)$terms), 'operator' => 'IN' ); } } $query = new WP_Query($args); $results = array( 'products' => array(), 'pagination' => $this->get_pagination_data($query) ); if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $product = wc_get_product(get_the_ID()); $results['products'][] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price_html' => $product->get_price_html(), 'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'), 'permalink' => get_permalink(), 'average_rating' => $product->get_average_rating(), 'review_count' => $product->get_review_count() ); } wp_reset_postdata(); } wp_send_json_success($results); } catch (Exception $e) { $this->log_error('AJAX filter error', array( 'error' => $e->getMessage(), 'taxonomies' => $taxonomies )); wp_send_json_error(array( 'message' => __('An error occurred while filtering products.', 'bdfg-product-filters') )); } } } /** * 价格过滤器实现类 */ class BDFG_Price_Filter extends BDFG_Filter_Base { /** * 构造函数 */ public function __construct() { $this->filter_type = 'price'; parent::__construct(); } /** * 渲染价格过滤器 */ public function render_filter($args = array()) { $defaults = array( 'min_price' => '', 'max_price' => '', 'step' => 10, 'display' => 'both' ); $args = wp_parse_args($args, $defaults); $price_range = $this->get_price_range(); if ($price_range) { $args['min_price'] = $args['min_price'] ?: $price_range->min_price; $args['max_price'] = $args['max_price'] ?: $price_range->max_price; } ob_start(); ?> <div class="bdfg-filter bdfg-price-filter" data-filter-type="price"> <div class="bdfg-price-slider-wrapper"> <?php if ($args['display'] !== 'input'): ?> <div class="bdfg-price-slider" id="bdfg-price-slider" data-min="<?php echo esc_attr($args['min_price']); ?>" data-max="<?php echo esc_attr($args['max_price']); ?>" data-step="<?php echo esc_attr($args['step']); ?>"> </div> <?php endif; ?> <?php if ($args['display'] !== 'slider'): ?> <div class="bdfg-price-inputs"> <div class="bdfg-price-input"> <label><?php esc_html_e('Min Price', 'bdfg-product-filters'); ?></label> <input type="number" id="bdfg-min-price" name="min_price" value="<?php echo esc_attr($args['min_price']); ?>" min="<?php echo esc_attr($args['min_price']); ?>" max="<?php echo esc_attr($args['max_price']); ?>" step="<?php echo esc_attr($args['step']); ?>"> </div> <div class="bdfg-price-input"> <label><?php esc_html_e('Max Price', 'bdfg-product-filters'); ?></label> <input type="number" id="bdfg-max-price" name="max_price" value="<?php echo esc_attr($args['max_price']); ?>" min="<?php echo esc_attr($args['min_price']); ?>" max="<?php echo esc_attr($args['max_price']); ?>" step="<?php echo esc_attr($args['step']); ?>"> </div> </div> <?php endif; ?> <button type="button" class="bdfg-filter-button"> <?php esc_html_e('Filter', 'bdfg-product-filters'); ?> </button> </div> </div> <?php return ob_get_clean(); } /** * 获取价格范围 */ protected function get_price_range() { global $wpdb; $cache_key = $this->get_cache_key(array('price_range' => true)); $price_range = $this->get_cached_results($cache_key); if ($price_range === false) { $price_range = $wpdb->get_row(" SELECT MIN(CAST(min_price AS DECIMAL(10,2))) as min_price, MAX(CAST(max_price AS DECIMAL(10,2))) as max_price FROM {$wpdb->wc_product_meta_lookup} WHERE min_price > 0 "); if ($price_range) { // 四舍五入到最接近的整数 $price_range->min_price = floor($price_range->min_price); $price_range->max_price = ceil($price_range->max_price); $this->set_cached_results($cache_key, $price_range); } } return $price_range; } /** * 修改产品查询 */ public function modify_product_query($query, $query_vars) { if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) { $min_price = isset($_GET['min_price']) ? floatval($_GET['min_price']) : ''; $max_price = isset($_GET['max_price']) ? floatval($_GET['max_price']) : ''; if ($min_price !== '' || $max_price !== '') { $meta_query = $query->get('meta_query', array()); $meta_query[] = array( 'relation' => 'AND', array( 'key' => '_price', 'value' => array($min_price, $max_price), 'type' => 'NUMERIC', 'compare' => 'BETWEEN' ), array( 'key' => '_price', 12), 'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1, 'meta_query' => array() ); if ($min_price !== '' || $max_price !== '') { $args['meta_query'][] = array( 'relation' => 'AND', array( 'key' => '_price', 'value' => array($min_price, $max_price), 'type' => 'NUMERIC', 'compare' => 'BETWEEN' ), array( 'key' => '_price', 'value' => 0, 'type' => 'NUMERIC', 'compare' => '>' ) ); } $query = new WP_Query($args); $results = array( 'products' => array(), 'pagination' => $this->get_pagination_data($query) ); if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $product = wc_get_product(get_the_ID()); $results['products'][] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price_html' => $product->get_price_html(), 'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'), 'permalink' => get_permalink(), 'average_rating' => $product->get_average_rating(), 'review_count' => $product->get_review_count() ); } wp_reset_postdata(); } wp_send_json_success($results); } catch (Exception $e) { $this->log_error('AJAX price filter error', array( 'error' => $e->getMessage(), 'min_price' => $min_price, 'max_price' => $max_price )); wp_send_json_error(array( 'message' => __('An error occurred while filtering products by price.', 'bdfg-product-filters') )); } } } /** * 评分过滤器实现类 */ class BDFG_Review_Filter extends BDFG_Filter_Base { /** * 构造函数 */ public function __construct() { $this->filter_type = 'review'; parent::__construct(); } /** * 渲染评分过滤器 */ public function render_filter($args = array()) { $defaults = array( 'show_count' => true, 'min_rating' => 1, 'max_rating' => 5 ); $args = wp_parse_args($args, $defaults); ob_start(); ?> <div class="bdfg-filter bdfg-review-filter" data-filter-type="review"> <ul class="bdfg-rating-list"> <?php for ($i = $args['max_rating']; $i >= $args['min_rating']; $i--): ?> <li class="bdfg-rating-item"> <label> <input type="checkbox" name="rating[]" value="<?php echo esc_attr($i); ?>"> <span class="bdfg-star-rating"> <?php for ($j = 1; $j <= 5; $j++) { echo '<span class="star ' . ($j <= $i ? 'filled' : '') . '">' . ($j <= $i ? '★' : '☆') . '</span>'; } ?> </span> <?php if ($args['show_count']) { $count = $this->get_rating_count($i); echo '<span class="count">(' . absint($count) . ')</span>'; } ?> </label> </li> <?php endfor; ?> </ul> </div> <?php return ob_get_clean(); } /** * 获取特定评分的商品数量 */ protected function get_rating_count($rating) { global $wpdb; $cache_key = $this->get_cache_key(array('rating' => $rating)); $count = $this->get_cached_results($cache_key); if ($count === false) { $count = $wpdb->get_var($wpdb->prepare(" SELECT COUNT(*) FROM {$wpdb->prefix}wc_product_meta_lookup WHERE average_rating >= %f AND average_rating < %f ", $rating - 0.5, $rating + 0.5)); $this->set_cached_results($cache_key, $count); } return $count; } /** * 修改产品查询 */ public function modify_product_query($query, $query_vars) { if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) { if (isset($_GET['rating']) && !empty($_GET['rating'])) { $ratings = array_map('absint', (array)$_GET['rating']); if (!empty($ratings)) { $meta_query = $query->get('meta_query', array()); $rating_query = array('relation' => 'OR'); foreach ($ratings as $rating) { $rating_query[] = array( 'key' => '_wc_average_rating', 'value' => array($rating - 0.5, $rating + 0.5), 'type' => 'DECIMAL(10,2)', 'compare' => 'BETWEEN' ); } $meta_query[] = $rating_query; $query->set('meta_query', $meta_query); } } } return $query; } /** * AJAX产品过滤 */ public function ajax_filter_products() { check_ajax_referer('bdfg-filters-nonce', 'nonce'); $ratings = isset($_POST['ratings']) ? array_map('absint', (array)$_POST['ratings']) : array(); try { $args = array( 'post_type' => 'product', 'posts_per_page' => apply_filters('bdfg_products_per_page', 12), 'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1, 'meta_query' => array() ); if (!empty($ratings)) { $rating_query = array('relation' => 'OR'); foreach ($ratings as $rating) { $rating_query[] = array( 'key' => '_wc_average_rating', 'value' => array($rating - 0.5, $rating + 0.5), 'type' => 'DECIMAL(10,2)', 'compare' => 'BETWEEN' ); } $args['meta_query'][] = $rating_query; } $query = new WP_Query($args); $results = array( 'products' => array(), 'pagination' => $this->get_pagination_data($query) ); if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $product = wc_get_product(get_the_ID()); $results['products'][] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price_html' => $product->get_price_html(), 'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'), 'permalink' => get_permalink(), 'average_rating' => $product->get_average_rating(), 'review_count' => $product->get_review_count() ); } wp_reset_postdata(); } wp_send_json_success($results); } catch (Exception $e) { $this->log_error('AJAX rating filter error', array( 'error' => $e->getMessage(), 'ratings' => $ratings )); wp_send_json_error(array( 'message' => __('An error occurred while filtering products by rating.', 'bdfg-product-filters') )); } } } /** * 名称搜索过滤器实现类 */ class BDFG_Name_Filter extends BDFG_Filter_Base { /** * 构造函数 */ public function __construct() { $this->filter_type = 'name'; parent::__construct(); } /** * 渲染搜索过滤器 */ public function render_filter($args = array()) { $defaults = array( 'placeholder' => __('Search products...', 'bdfg-product-filters'), 'min_chars' => 3, 'show_button' => true ); $args = wp_parse_args($args, $defaults); ob_start(); ?> <div class="bdfg-filter bdfg-name-filter" data-filter-type="name"> <div class="bdfg-search-wrapper"> <input type="text" class="bdfg-search-input" name="bdfg_search" placeholder="<?php echo esc_attr($args['placeholder']); ?>" data-min-chars="<?php echo esc_attr($args['min_chars']); ?>" value="<?php echo esc_attr(isset($_GET['bdfg_search']) ? $_GET['bdfg_search'] : ''); ?>"> <?php if ($args['show_button']): ?> <button type="button" class="bdfg-search-button"> <span class="screen-reader-text"><?php esc_html_e('Search', 'bdfg-product-filters'); ?></span> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" 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> </button> <?php endif; ?> </div> <div class="bdfg-search-results" style="display: none;"></div> </div> <?php return ob_get_clean(); } /** * 修改产品查询 */ public function modify_product_query($query, $query_vars) { if (!is_admin() && $query->is_main_query() && (is_shop() || is_product_category())) { if (isset($_GET['bdfg_search']) && !empty($_GET['bdfg_search'])) { $search_term = sanitize_text_field($_GET['bdfg_search']); $query->set('s', $search_term); $query->set('post_type', 'product'); // 添加SKU搜索支持 add_filter('posts_search', array($this, 'extend_product_search'), 10, 2); } } return $query; } /** * 扩展产品搜索以包含SKU */ public function extend_product_search($where, $query) { global $wpdb; if (!empty($_GET['bdfg_search']) && $query->is_main_query()) { $search_term = sanitize_text_field($_GET['bdfg_search']); $where .= $wpdb->prepare(" OR {$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = '_sku' AND meta_value LIKE %s )", '%' . $wpdb->esc_like($search_term) . '%'); } return $where; } /** * AJAX产品搜索 */ public function ajax_filter_products() { check_ajax_referer('bdfg-filters-nonce', 'nonce'); $search_term = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : ''; $min_chars = isset($_POST['min_chars']) ? absint($_POST['min_chars']) : 3; try { if (empty($search_term) || strlen($search_term) < $min_chars) { wp_send_json_error(array( 'message' => sprintf( __('Please enter at least %d characters', 'bdfg-product-filters'), $min_chars ) )); return; } $args = array( 'post_type' => 'product', 'posts_per_page' => apply_filters('bdfg_products_per_page', 12), 'paged' => isset($_POST['page']) ? absint($_POST['page']) : 1, 's' => $search_term, 'orderby' => 'relevance', 'meta_query' => array( 'relation' => 'OR', array( 'key' => '_sku', 'value' => $search_term, 'compare' => 'LIKE' ) ) ); $query = new WP_Query($args); $results = array( 'products' => array(), 'pagination' => $this->get_pagination_data($query) ); if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $product = wc_get_product(get_the_ID()); $results['products'][] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'price_html' => $product->get_price_html(), 'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'), 'permalink' => get_permalink(), 'sku' => $product->get_sku(), 'average_rating' => $product->get_average_rating(), 'review_count' => $product->get_review_count() ); } wp_reset_postdata(); } wp_send_json_success($results); } catch (Exception $e) { $this->log_error('AJAX search filter error', array('error' => $e->getMessage(), 'search_term' => $search_term, 'date' => '2025-02-16 10:06:12', 'user' => 'beiduofengou' )); wp_send_json_error(array( 'message' => __('An error occurred while searching products.', 'bdfg-product-filters') )); } } } /** * 分页数据辅助函数 * * @param WP_Query $query * @return array */ function get_pagination_data($query) { $current_page = max(1, $query->get('paged')); $total_pages = max(1, $query->max_num_pages); $pagination = array( 'current_page' => $current_page, 'total_pages' => $total_pages, 'total_items' => $query->found_posts, 'items_per_page' => $query->get('posts_per_page'), 'links' => array() ); // 生成分页链接 if ($total_pages > 1) { // 首页 if ($current_page > 1) { $pagination['links'][] = array( 'page' => 1, 'text' => '«', 'class' => 'first' ); } // 上一页 if ($current_page > 1) { $pagination['links'][] = array( 'page' => $current_page - 1, 'text' => '‹', 'class' => 'prev' ); } // 页码链接 $range = 2; $showitems = ($range * 2) + 1; for ($i = 1; $i <= $total_pages; $i++) { if ( $i == 1 || $i == $total_pages || ($i >= $current_page - $range && $i <= $current_page + $range) ) { $pagination['links'][] = array( 'page' => $i, 'text' => (string)$i, 'class' => $i == $current_page ? 'current' : 'number' ); } elseif ( $i == $current_page - $range - 1 || $i == $current_page + $range + 1 ) { $pagination['links'][] = array( 'page' => null, 'text' => '...', 'class' => 'dots' ); } } // 下一页 if ($current_page < $total_pages) { $pagination['links'][] = array( 'page' => $current_page + 1, 'text' => '›', 'class' => 'next' ); } // 末页 if ($current_page < $total_pages) { $pagination['links'][] = array( 'page' => $total_pages, 'text' => '»', 'class' => 'last' ); } } return $pagination; } /** * 获取分类标签 * * @param string $taxonomy * @return string */ function get_taxonomy_label($taxonomy) { $tax_obj = get_taxonomy($taxonomy); return $tax_obj ? $tax_obj->labels->singular_name : ''; } /** * 记录调试信息到日志文件 * * @param string $message * @param array $context */ function bdfg_log($message, $context = array()) { if (defined('WP_DEBUG') && WP_DEBUG) { $log_dir = WP_CONTENT_DIR . '/bdfg-logs'; if (!file_exists($log_dir)) { wp_mkdir_p($log_dir); } $date = current_time('Y-m-d'); $log_file = $log_dir . '/debug-' . $date . '.log'; $log_message = sprintf( "[%s] %s | Context: %s\n", current_time('Y-m-d H:i:s'), $message, json_encode($context, JSON_UNESCAPED_UNICODE) ); error_log($log_message, 3, $log_file); } } // 注册过滤器实例 add_action('init', 'bdfg_register_core_filters'); /** * 获取过滤器实例 * * @param string $type * @return BDFG_Filter_Base|null */ function bdfg_get_filter($type) { global $bdfg_filters; if (!isset($bdfg_filters)) { $bdfg_filters = array(); } if (!isset($bdfg_filters[$type])) { $class_name = 'BDFG_' . ucfirst($type) . '_Filter'; if (class_exists($class_name)) { $bdfg_filters[$type] = new $class_name(); } } return isset($bdfg_filters[$type]) ? $bdfg_filters[$type] : null; } /** * 渲染过滤器HTML * * @param string $type * @param array $args * @return string */ function bdfg_render_filter($type, $args = array()) { $filter = bdfg_get_filter($type); if ($filter) { return $filter->render_filter($args); } return ''; } /** * 检查过滤器是否可用 * * @param string $type * @return bool */ function bdfg_is_filter_available($type) { return bdfg_get_filter($type) !== null; } // 加载过滤器模板 function bdfg_get_template($template_name, $args = array()) { $template_path = BDFG_PATH . 'templates/' . $template_name; $theme_template_path = get_stylesheet_directory() . '/bdfg-product-filters/' . $template_name; // 检查主题是否有自定义模板 if (file_exists($theme_template_path)) { $template_path = $theme_template_path; } if (file_exists($template_path)) { extract($args); include $template_path; } }
class-bdfg-advanced-features.php
<?php /** * BDFG Product Filters - Advanced Features * * 提供高级过滤、供应商功能和智能推荐 * * @package BDFG_Product_Filters * @author Bei Duo Feng Ou <[email protected]> * @version 2.5.0 * @update 2024-11-16 10:15:02 * @author beiduofengou */ if (!defined('ABSPATH')) { exit('Direct access not allowed.'); } /** * 高级过滤器管理类 */ class BDFG_Advanced_Filter_Manager { /** * 初始化钩子 */ public function __construct() { // AJAX处理 add_action('wp_ajax_bdfg_advanced_filter', array($this, 'handle_advanced_filter')); add_action('wp_ajax_nopriv_bdfg_advanced_filter', array($this, 'handle_advanced_filter')); // REST API支持 add_action('rest_api_init', array($this, 'register_rest_routes')); // 添加高级过滤器小部件 add_action('widgets_init', array($this, 'register_widgets')); // 过滤器增强 add_filter('bdfg_filter_options', array($this, 'enhance_filter_options'), 10, 2); } /** * 处理高级过滤 */ public function handle_advanced_filter() { check_ajax_referer('bdfg-filters', 'nonce'); try { // 获取并验证请求参数 $params = $this->validate_request_params($_POST); // 应用过滤器 $results = $this->apply_advanced_filters($params); // 发送成功响应 wp_send_json_success(array( 'products' => $results['products'], 'pagination' => $this->get_pagination_data($results), 'facets' => $this->get_active_facets($params) )); } catch (Exception $e) { $this->log_error('Advanced filter error: ' . $e->getMessage()); wp_send_json_error(array( 'message' => $e->getMessage() )); } } /** * 验证请求参数 * * @param array $params * @return array */ private function validate_request_params($params) { return array( 'attributes' => isset($params['attributes']) ? $this->sanitize_attributes($params['attributes']) : array(), 'price_range' => isset($params['price_range']) ? $this->sanitize_price_range($params['price_range']) : array(), 'stock_status' => isset($params['stock_status']) ? sanitize_text_field($params['stock_status']) : '', 'vendors' => isset($params['vendors']) ? array_map('absint', (array)$params['vendors']) : array(), 'sort_by' => isset($params['sort_by']) ? sanitize_text_field($params['sort_by']) : 'menu_order', 'page' => isset($params['page']) ? absint($params['page']) : 1, 'per_page' => $this->get_products_per_page() ); } /** * 应用高级过滤器 * * @param array $params 过滤参数 * @return array */ private function apply_advanced_filters($params) { // 基础查询参数 $args = array( 'post_type' => 'product', 'posts_per_page' => $params['per_page'], 'paged' => $params['page'], 'tax_query' => array('relation' => 'AND'), 'meta_query' => array('relation' => 'AND') ); // 处理属性过滤 if (!empty($params['attributes'])) { foreach ($params['attributes'] as $taxonomy => $terms) { if (!empty($terms)) { $args['tax_query'][] = array( 'taxonomy' => sanitize_key($taxonomy), 'field' => 'term_id', 'terms' => array_map('absint', $terms), 'operator' => 'IN' ); } } } // 处理价格范围 if (!empty($params['price_range'])) { $args['meta_query'][] = array( 'key' => '_price', 'value' => array( floatval($params['price_range']['min']), floatval($params['price_range']['max']) ), 'type' => 'NUMERIC', 'compare' => 'BETWEEN' ); } // 处理库存状态 if (!empty($params['stock_status'])) { $args['meta_query'][] = array( 'key' => '_stock_status', 'value' => $params['stock_status'], 'compare' => '=' ); } // 处理供应商过滤 if (!empty($params['vendors'])) { $args['tax_query'][] = array( 'taxonomy' => 'product_vendor', 'field' => 'term_id', 'terms' => $params['vendors'], 'operator' => 'IN' ); } // 处理排序 $this->apply_sorting($args, $params['sort_by']); // 执行查询 $query = new WP_Query($args); return array( 'products' => $this->format_products($query->posts), 'total' => $query->found_posts, 'max_pages' => $query->max_num_pages ); } /** * 应用排序规则 * * @param array $args 查询参数 * @param string $sort_by 排序类型 */ private function apply_sorting(&$args, $sort_by) { switch ($sort_by) { case 'price_low': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = '_price'; $args['order'] = 'ASC'; break; case 'price_high': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = '_price'; $args['order'] = 'DESC'; break; case 'rating': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = '_wc_average_rating'; $args['order'] = 'DESC'; break; case 'popularity': $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'total_sales'; $args['order'] = 'DESC'; break; case 'date': $args['orderby'] = 'date'; $args['order'] = 'DESC'; break; default: // menu_order $args['orderby'] = 'menu_order title'; $args['order'] = 'ASC'; break; } } /** * 格式化商品数据 * * @param array $posts WP_Post对象数组 * @return array */ private function format_products($posts) { $formatted = array(); foreach ($posts as $post) { $product = wc_get_product($post); if (!$product) continue; $formatted[] = array( 'id' => $product->get_id(), 'name' => $product->get_name(), 'permalink' => $product->get_permalink(), 'price_html' => $product->get_price_html(), 'regular_price' => $product->get_regular_price(), 'sale_price' => $product->get_sale_price(), 'thumbnail' => get_the_post_thumbnail_url($product->get_id(), 'woocommerce_thumbnail'), 'average_rating' => $product->get_average_rating(), 'review_count' => $product->get_review_count(), 'stock_status' => $product->get_stock_status(), 'stock_quantity' => $product->get_stock_quantity(), 'sku' => $product->get_sku(), 'categories' => $this->get_product_terms($product, 'product_cat'), 'tags' => $this->get_product_terms($product, 'product_tag'), 'attributes' => $this->get_product_attributes($product), 'vendor' => $this->get_product_vendor($product->get_id()), 'is_on_sale' => $product->is_on_sale(), 'is_featured' => $product->is_featured(), 'updated_at' => '2024-11-16 10:15:02', 'updated_by' => 'beiduofengou' ); } return $formatted; } /** * 获取商品分类术语 * * @param WC_Product $product * @param string $taxonomy * @return array */ private function get_product_terms($product, $taxonomy) { $terms = get_the_terms($product->get_id(), $taxonomy); if (!$terms || is_wp_error($terms)) return array(); return array_map(function($term) { return array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug, 'permalink' => get_term_link($term) ); }, $terms); } /** * 获取商品属性 * * @param WC_Product $product * @return array */ private function get_product_attributes($product) { $attributes = array(); if ($product->is_type('variable')) { $attrs = $product->get_variation_attributes(); } else { $attrs = $product->get_attributes(); } foreach ($attrs as $attr => $value) { $taxonomy = wc_attribute_taxonomy_name(str_replace('pa_', '', $attr)); if (taxonomy_exists($taxonomy)) { $terms = wc_get_product_terms($product->get_id(), $taxonomy); $attributes[$attr] = array_map(function($term) { return array( 'id' => $term->term_id, 'name' => $term->name, 'slug' => $term->slug ); }, $terms); } else { $attributes[$attr] = is_array($value) ? $value : array($value); } } return $attributes; } /** * 获取商品供应商信息 * * @param int $product_id * @return array|null */ private function get_product_vendor($product_id) { $vendors = get_the_terms($product_id, 'product_vendor'); if ($vendors && !is_wp_error($vendors)) { $vendor = reset($vendors); return array( 'id' => $vendor->term_id, 'name' => $vendor->name, 'slug' => $vendor->slug, 'permalink' => get_term_link($vendor), 'description' => $vendor->description, 'meta' => get_term_meta($vendor->term_id) ); } return null; } /** * 获取活动的过滤项 * * @param array $params * @return array */ private function get_active_facets($params) { $facets = array(); // 处理属性过滤 if (!empty($params['attributes'])) { foreach ($params['attributes'] as $taxonomy => $terms) { $tax_obj = get_taxonomy($taxonomy); if (!$tax_obj) continue; $term_names = array(); foreach ($terms as $term_id) { $term = get_term($term_id, $taxonomy); if ($term && !is_wp_error($term)) { $term_names[] = $term->name; } } if (!empty($term_names)) { $facets[] = array( 'type' => 'attribute', 'taxonomy' => $taxonomy, 'label' => $tax_obj->labels->singular_name, 'values' => $term_names ); } } } // 处理价格范围 if (!empty($params['price_range'])) { $facets[] = array( 'type' => 'price', 'label' => __('Price', 'bdfg-product-filters'), 'min' => $params['price_range']['min'], 'max' => $params['price_range']['max'] ); } // 处理库存状态 if (!empty($params['stock_status'])) { $status_labels = array( 'instock' => __('In stock', 'bdfg-product-filters'), 'outofstock' => __('Out of stock', 'bdfg-product-filters'), 'onbackorder' => __('On backorder', 'bdfg-product-filters') ); $facets[] = array( 'type' => 'stock', 'label' => __('Stock status', 'bdfg-product-filters'), 'value' => isset($status_labels[$params['stock_status']]) ? $status_labels[$params['stock_status']] : $params['stock_status'] ); } // 处理供应商 if (!empty($params['vendors'])) { $vendor_names = array(); foreach ($params['vendors'] as $vendor_id) { $vendor = get_term($vendor_id, 'product_vendor'); if ($vendor && !is_wp_error($vendor)) { $vendor_names[] = $vendor->name; } } if (!empty($vendor_names)) { $facets[] = array( 'type' => 'vendor', 'label' => __('Vendor', 'bdfg-product-filters'), 'values' => $vendor_names ); } } return $facets; } /** * 清理属性数据 * * @param array $attributes * @return array */ private function sanitize_attributes($attributes) { $clean = array(); foreach ($attributes as $taxonomy => $terms) { if (taxonomy_exists($taxonomy)) { $clean[sanitize_key($taxonomy)] = array_map('absint', (array)$terms); } } return $clean; } /** * 清理价格范围数据 * * @param array $price_range * @return array */ private function sanitize_price_range($price_range) { return array( 'min' => isset($price_range['min']) ? floatval($price_range['min']) : '', 'max' => isset($price_range['max']) ? floatval($price_range['max']) : '' ); } /** * 获取分页数据 * * @param array $results * @return array */ private function get_pagination_data($results) { return array( 'total_items' => $results['total'], 'total_pages' => $results['max_pages'], 'per_page' => $this->get_products_per_page() ); } /** * 注册REST API路由 */ public function register_rest_routes() { register_rest_route('bdfg/v1', '/filter', array( array( 'methods' => 'GET', 'callback' => array($this, 'handle_rest_filter'), 'permission_callback' => '__return_true', 'args' => array( 'attributes' => array( 'type' => 'object', 'default' => array() ), 'price_range' => array( 'type' => 'object', 'default' => array() ), 'stock_status' => array( 'type' => 'string', 'default' => '' ), 'vendors' => array( 'type' => 'array', 'default' => array() ), 'sort_by' => array( 'type' => 'string', 'default' => 'menu_order' ), 'page' => array( 'type' => 'integer', 'default' => 1, 'minimum' => 1 ) ) ) )); } /** * 处理REST API过滤请求 * * @param WP_REST_Request $request * @return WP_REST_Response */ public function handle_rest_filter($request) { try { $params = $this->validate_request_params($request->get_params()); $results = $this->apply_advanced_filters($params); return new WP_REST_Response(array( 'products' => $results['products'], 'pagination' => $this->get_pagination_data($results), 'facets' => $this->get_active_facets($params) ), 200); } catch (Exception $e) { $this->log_error('REST API filter error: ' . $e->getMessage()); return new WP_REST_Response(array( 'message' => $e->getMessage() ), 400); } } /** * 注册小部件 */ public function register_widgets() { register_widget('BDFG_Advanced_Filter_Widget'); } /** * 增强过滤器选项 * * @param array $options * @param string $filter_type * @return array */ public function enhance_filter_options($options, $filter_type) { switch ($filter_type) { case 'attribute': $options['display_type'] = array( 'dropdown' => __('Dropdown', 'bdfg-product-filters'), 'list' => __('List', 'bdfg-product-filters'), 'color' => __('Color swatches', 'bdfg-product-filters'), 'image' => __('Image swatches', 'bdfg-product-filters') ); break; case 'price': $options['price_range_mode'] = array( 'slider' => __('Slider', 'bdfg-product-filters'), 'input' => __('Input fields', 'bdfg-product-filters'), 'list' => __('Predefined ranges', 'bdfg-product-filters') ); break; case 'rating': $options['display_style'] = array( 'star' => __('Star rating', 'bdfg-product-filters'), 'number' => __('Number rating', 'bdfg-product-filters') ); break; } return $options; } /** * 生成推荐商品 * * @param int $product_id 商品ID * @param int $user_id 用户ID * @return array */ private function generate_recommendations($product_id, $user_id) { try { $cache_key = 'bdfg_recommendations_' . $product_id . '_' . $user_id; $cached_results = $this->get_cached_results($cache_key); if ($cached_results !== false) { return $cached_results; } $recommendations = array( 'similar_products' => $this->get_similar_products($product_id), 'frequently_bought' => $this->get_frequently_bought_together($product_id), 'personalized' => $this->get_personalized_recommendations($user_id) ); // 缓存结果 $this->set_cached_results($cache_key, $recommendations); return $recommendations; } catch (Exception $e) { $this->log_error('Failed to generate recommendations', array( 'product_id' => $product_id, 'user_id' => $user_id, 'error' => $e->getMessage(), 'date' => '2024-11-16 10:16:39', 'user' => 'beiduofengou' )); return array( 'similar_products' => array(), 'frequently_bought' => array(), 'personalized' => array() ); } } /** * 获取缓存的结果 * * @param string $key * @return mixed */ private function get_cached_results($key) { return get_transient($key); } /** * 设置缓存结果 * * @param string $key * @param mixed $value */ private function set_cached_results($key, $value) { set_transient($key, $value, $this->get_cache_duration()); } /** * 获取缓存时间 * * @return int */ private function get_cache_duration() { return absint(get_option('bdfg_cache_duration', 3600)); } /** * 获取每页显示的商品数量 * * @return int */ private function get_products_per_page() { return absint(get_option('bdfg_products_per_page', 12)); } /** * 记录错误日志 * * @param string $message * @param array $context */ private function log_error($message, $context = array()) { if (defined('WP_DEBUG') && WP_DEBUG) { error_log(sprintf( '[BDFG Error][%s] %s | Context: %s', current_time('mysql'), $message, json_encode($context, JSON_UNESCAPED_UNICODE) )); } } } // 初始化高级功能管理器 add_action('plugins_loaded', function() { new BDFG_Advanced_Filter_Manager(); });
assets/js/frontend.js
相关文章: WooCommerce 客户分析报告插件
/** * BDFG Product Filters - Frontend JavaScript * * @package BDFG_Product_Filters * @author Bei Duo Feng Ou <[email protected]> * @version 2.5.0 */ (function($) { 'use strict'; // 全局变量 var BDFG = { filters: {}, timeout: null, isLoading: false }; /** * 初始化过滤器 */ BDFG.init = function() { // 初始化各类过滤器 this.initTaxonomyFilter(); this.initPriceFilter(); this.initReviewFilter(); this.initSearchFilter(); // 初始化高级功能 if (bdfgFilters.advanced_enabled) { this.initAdvancedFeatures(); } // 绑定事件 this.bindEvents(); // 从URL恢复过滤状态 this.restoreFiltersFromUrl(); }; /** * 初始化分类过滤器 */ BDFG.initTaxonomyFilter = function() { $('.bdfg-taxonomy-filter select, .bdfg-taxonomy-filter input[type="checkbox"]').on('change', function() { BDFG.updateFilters(); }); }; /** * 初始化价格过滤器 */ BDFG.initPriceFilter = function() { var $slider = $('#bdfg-price-slider'); if ($slider.length) { var minPrice = parseFloat($slider.data('min')), maxPrice = parseFloat($slider.data('max')), currentMin = parseFloat($('#bdfg-min-price').val()), currentMax = parseFloat($('#bdfg-max-price').val()); $slider.slider({ range: true, min: minPrice, max: maxPrice, values: [currentMin || minPrice, currentMax || maxPrice], slide: function(event, ui) { $('#bdfg-min-price').val(ui.values[0]); $('#bdfg-max-price').val(ui.values[1]); }, stop: function() { BDFG.updateFilters(); } }); // 手动输入价格 $('.bdfg-price-input input').on('change', function() { var min = parseFloat($('#bdfg-min-price').val()), max = parseFloat($('#bdfg-max-price').val()); $slider.slider('values', [min, max]); BDFG.updateFilters(); }); } }; /** * 初始化评分过滤器 */ BDFG.initReviewFilter = function() { $('.bdfg-rating-filter input').on('change', function() { BDFG.updateFilters(); }); }; /** * 初始化搜索过滤器 */ BDFG.initSearchFilter = function() { var $input = $('.bdfg-search-input'), minChars = parseInt($input.data('min-chars')) || 3; $input.on('keyup', function() { clearTimeout(BDFG.timeout); var term = $(this).val(); if (term.length >= minChars || term.length === 0) { BDFG.timeout = setTimeout(function() { BDFG.updateFilters(); }, 500); } }); }; /** * 初始化高级功能 */ BDFG.initAdvancedFeatures = function() { // 初始化供应商过滤器 this.initVendorFilter(); // 初始化智能推荐 if ($('.bdfg-recommendations').length) { this.loadRecommendations(); } }; /** * 初始化供应商过滤器 */ BDFG.initVendorFilter = function() { $('.bdfg-vendor-filter input').on('change', function() { BDFG.updateFilters(); }); }; /** * 绑定事件 */ BDFG.bindEvents = function() { // 清除所有过滤器 $('.bdfg-clear-filters').on('click', function(e) { e.preventDefault(); BDFG.clearAllFilters(); }); // 移除单个过滤标签 $(document).on('click', '.bdfg-filter-tag .remove', function() { var $tag = $(this).closest('.bdfg-filter-tag'), type = $tag.data('type'), value = $tag.data('value'); BDFG.removeFilter(type, value); }); // 排序变更 $('.bdfg-sorting').on('change', function() { BDFG.updateFilters(); }); // 分页点击 $(document).on('click', '.bdfg-pagination a', function(e) { e.preventDefault(); var page = $(this).data('page'); BDFG.goToPage(page); }); }; /** * 更新过滤器 */ BDFG.updateFilters = function() { if (BDFG.isLoading) { return; } BDFG.isLoading = true; $('.bdfg-products-wrapper').addClass('bdfg-loading'); // 收集过滤条件 var filters = BDFG.collectFilters(); // 更新URL BDFG.updateUrl(filters); // 发送AJAX请求 $.ajax({ url: bdfgFilters.ajaxurl, type: 'POST', data: { action: 'bdfg_filter_products', nonce: bdfgFilters.nonce, filters: filters }, success: function(response) { if (response.success) { BDFG.updateProductsGrid(response.data); BDFG.updateActiveFilters(filters); BDFG.updatePagination(response.data.pagination); } else { console.error('BDFG Filter Error:', response.data.message); } }, error: function(xhr, status, error) { console.error('BDFG AJAX Error:', error); }, complete: function() { BDFG.isLoading = false; $('.bdfg-products-wrapper').removeClass('bdfg-loading'); } }); }; /** * 收集过滤条件 */ BDFG.collectFilters = function() { var filters = { taxonomy: {}, price: {}, rating: [], search: '', vendor: [], sort: '', page: 1 }; // 收集分类过滤 $('.bdfg-taxonomy-filter').each(function() { var taxonomy = $(this).data('taxonomy'); filters.taxonomy[taxonomy] = []; // 处理下拉菜单 $(this).find('select').each(function() { var value = $(this).val(); if (value) { filters.taxonomy[taxonomy].push(value); } }); // 处理复选框 $(this).find('input:checked').each(function() { filters.taxonomy[taxonomy].push($(this).val()); }); }); // 收集价格过滤 var $minPrice = $('#bdfg-min-price'), $maxPrice = $('#bdfg-max-price'); if ($minPrice.length && $maxPrice.length) { filters.price = { min: parseFloat($minPrice.val()) || '', max: parseFloat($maxPrice.val()) || '' }; } // 收集评分过滤 $('.bdfg-rating-filter input:checked').each(function() { filters.rating.push(parseInt($(this).val())); }); // 收集搜索条件 filters.search = $('.bdfg-search-input').val(); // 收集供应商过滤 $('.bdfg-vendor-filter input:checked').each(function() { filters.vendor.push(parseInt($(this).val())); }); // 收集排序条件 filters.sort = $('.bdfg-sorting').val(); // 收集分页信息 filters.page = parseInt($('.bdfg-pagination .current').data('page')) || 1; return filters; }; /** * 更新产品网格 */ BDFG.updateProductsGrid = function(data) { var $wrapper = $('.bdfg-products-wrapper'); if (data.products && data.products.length) { var html = '<div class="bdfg-products-grid">'; data.products.forEach(function(product) { html += BDFG.renderProductCard(product); }); html += '</div>'; $wrapper.html(html); // 触发自定义事件 $(document).trigger('bdfg_products_updated', [data.products]); } else { $wrapper.html('<p class="bdfg-no-products">' + bdfgFilters.i18n.noResults + '</p>'); } // 滚动到产品区域 $('html, body').animate({ scrollTop: $wrapper.offset().top - 100 }, 500); }; /** * 渲染产品卡片 */ BDFG.renderProductCard = function(product) { var html = '<div class="bdfg-product-card">'; html += '<a href="' + product.permalink + '">'; if (product.thumbnail) { html += '<img src="' + product.thumbnail + '" alt="' + product.name + '">'; } html += '<div class="product-info">'; html += '<h4>' + product.name + '</h4>'; if (product.price_html) { html += '<div class="bdfg-product-price">' + product.price_html + '</div>'; } if (product.average_rating > 0) { html += '<div class="bdfg-star-rating">'; html += BDFG.renderStarRating(product.average_rating); html += '<span class="rating-count">(' + product.review_count + ')</span>'; html += '</div>'; } html += '</div></a></div>'; return html; }; /** * 渲染星级评分 */ BDFG.renderStarRating = function(rating) { var html = '', fullStars = Math.floor(rating), halfStar = rating % 1 >= 0.5, emptyStars = 5 - Math.ceil(rating); // 填充星星 for (var i = 0; i < fullStars; i++) { html += '<span class="star filled">★</span>'; } // 半星 if (halfStar) { html += '<span class="star half">★</span>'; } // 空星 for (var j = 0; j < emptyStars; j++) { html += '<span class="star">☆</span>'; } return html; }; /** * 更新活动过滤器标签 */ BDFG.updateActiveFilters = function(filters) { var $wrapper = $('.bdfg-active-filters'), html = ''; if (!$wrapper.length) { return; } // 处理分类过滤器 Object.keys(filters.taxonomy).forEach(function(taxonomy) { filters.taxonomy[taxonomy].forEach(function(term) { var label = BDFG.getTermLabel(taxonomy, term); html += BDFG.renderFilterTag('taxonomy', term, label, taxonomy); }); }); // 处理价格过滤器 if (filters.price.min || filters.price.max) { var priceLabel = bdfgFilters.currency_symbol; if (filters.price.min) { priceLabel += filters.price.min; } priceLabel += ' - '; if (filters.price.max) { priceLabel += bdfgFilters.currency_symbol + filters.price.max; } html += BDFG.renderFilterTag('price', 'range', priceLabel); } // 处理评分过滤器 filters.rating.forEach(function(rating) { html += BDFG.renderFilterTag('rating', rating, rating + '+ ' + bdfgFilters.i18n.stars); }); // 处理搜索条件 if (filters.search) { html += BDFG.renderFilterTag('search', filters.search, '"' + filters.search + '"'); } // 处理供应商过滤器 filters.vendor.forEach(function(vendorId) { var vendorName = BDFG.getVendorName(vendorId); html += BDFG.renderFilterTag('vendor', vendorId, vendorName); }); if (html) { html = '<span class="bdfg-active-filters-label">' + bdfgFilters.i18n.activeFilters + ':</span>' + html; html += '<a href="#" class="bdfg-clear-filters">' + bdfgFilters.i18n.clearAll + '</a>'; } $wrapper.html(html); }; /** * 渲染过滤器标签 */ BDFG.renderFilterTag = function(type, value, label, subtype) { var data = 'data-type="' + type + '" data-value="' + value + '"'; if (subtype) { data += ' data-subtype="' + subtype + '"'; } return '<span class="bdfg-filter-tag" ' + data + '>' + label + '<span class="remove">×</span>' + '</span>'; }; /** * 更新URL参数 */ BDFG.updateUrl = function(filters) { if (!history.pushState) { return; } var params = new URLSearchParams(window.location.search); // 清除现有的过滤参数 BDFG.filterParams.forEach(function(param) { params.delete(param); }); // 添加新的过滤参数 Object.keys(filters).forEach(function(key) { var value = filters[key]; if (value && (typeof value === 'string' || value.length)) { params.set('bdfg_' + key, JSON.stringify(value)); } }); var newUrl = window.location.pathname; if (params.toString()) { newUrl += '?' + params.toString(); } history.pushState({bdfgFilters: filters}, '', newUrl); }; /** * 从URL恢复过滤状态 */ BDFG.restoreFiltersFromUrl = function() { var params = new URLSearchParams(window.location.search); var hasFilters = false; BDFG.filterParams.forEach(function(param) { if (params.has('bdfg_' + param)) { hasFilters = true; var value = JSON.parse(params.get('bdfg_' + param)); BDFG.setFilterValue(param, value); } }); if (hasFilters) { BDFG.updateFilters(); } }; /** * 设置过滤器值 */ BDFG.setFilterValue = function(type, value) { switch (type) { case 'taxonomy': Object.keys(value).forEach(function(taxonomy) { value[taxonomy].forEach(function(term) { $('.bdfg-taxonomy-filter[data-taxonomy="' + taxonomy + '"]') .find('input[value="' + term + '"]') .prop('checked', true); }); }); break; case 'price': if (value.min) $('#bdfg-min-price').val(value.min); if (value.max) $('#bdfg-max-price').val(value.max); $('#bdfg-price-slider').slider('values', [ value.min || $('#bdfg-price-slider').slider('option', 'min'), value.max || $('#bdfg-price-slider').slider('option', 'max') ]); break; case 'rating': value.forEach(function(rating) { $('.bdfg-rating-filter input[value="' + rating + '"]') .prop('checked', true); }); break; case 'search': $('.bdfg-search-input').val(value); break; case 'vendor': value.forEach(function(vendorId) { $('.bdfg-vendor-filter input[value="' + vendorId + '"]') .prop('checked', true); }); break; case 'sort': $('.bdfg-sorting').val(value); break; } }; /** * 清除所有过滤器 */ BDFG.clearAllFilters = function() { // 重置所有输入 $('.bdfg-taxonomy-filter input').prop('checked', false); $('.bdfg-taxonomy-filter select').val(''); $('.bdfg-rating-filter input').prop('checked', false); $('.bdfg-vendor-filter input').prop('checked', false); $('.bdfg-search-input').val(''); $('.bdfg-sorting').val(bdfgFilters.default_sort); // 重置价格滑块 var $slider = $('#bdfg-price-slider'); if ($slider.length) { $slider.slider('values', [ $slider.slider('option', 'min'), $slider.slider('option', 'max') ]); $('#bdfg-min-price').val(''); $('#bdfg-max-price').val(''); } // 更新过滤器 BDFG.updateFilters(); }; /** * 加载推荐 */ BDFG.loadRecommendations = function() { var $container = $('.bdfg-recommendations'); if (!$container.length) return; var productId = $container.data('product-id'); $.ajax({ url: bdfgFilters.ajaxurl, type: 'POST', data: { action: 'bdfg_get_recommendations', nonce: bdfgFilters.nonce, product_id: productId }, success: function(response) { if (response.success) { BDFG.renderRecommendations(response.data); } } }); }; // 定义过滤器参数 BDFG.filterParams = [ 'taxonomy', 'price', 'rating', 'search', 'vendor', 'sort', 'page' ]; // 初始化 $(document).ready(function() { BDFG.init(); }); })(jQuery);
admin/class-bdfg-settings.php
<?php /** * BDFG Product Filters - Admin Settings * * @package BDFG_Product_Filters * @author Bei Duo Feng Ou <[email protected]> * @version 2.5.0 */ if (!defined('ABSPATH')) { exit('Direct access not allowed.'); } class BDFG_Settings { /** * 设置页面ID */ const PAGE_ID = 'bdfg-settings'; /** * 设置选项组 */ const OPTION_GROUP = 'bdfg_options'; /** * 构造函数 */ public function __construct() { add_action('admin_menu', array($this, 'add_settings_page')); add_action('admin_init', array($this, 'register_settings')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); } /** * 添加设置页面 */ public function add_settings_page() { add_submenu_page( 'woocommerce', __('BDFG Product Filters', 'bdfg-product-filters'), __('Product Filters', 'bdfg-product-filters'), 'manage_woocommerce', self::PAGE_ID, array($this, 'render_settings_page') ); } /** * 注册设置 */ public function register_settings() { register_setting( self::OPTION_GROUP, 'bdfg_enable_advanced_features', array( 'type' => 'string', 'default' => 'yes' ) ); register_setting( self::OPTION_GROUP, 'bdfg_cache_duration', array( 'type' => 'number', 'default' => 3600 ) ); // 添加设置区域 add_settings_section( 'bdfg_general_section', __('General Settings', 'bdfg-product-filters'), array($this, 'render_general_section'), self::PAGE_ID ); add_settings_section( 'bdfg_advanced_section', __('Advanced Features', 'bdfg-product-filters'), array($this, 'render_advanced_section'), self::PAGE_ID ); add_settings_section( 'bdfg_cache_section', __('Cache Settings', 'bdfg-product-filters'), array($this, 'render_cache_section'), self::PAGE_ID ); // 添加设置字段 add_settings_field( 'bdfg_taxonomies', __('Product Taxonomies', 'bdfg-product-filters'), array($this, 'render_taxonomies_field'), self::PAGE_ID, 'bdfg_general_section' ); add_settings_field( 'bdfg_price_filter', __('Price Filter', 'bdfg-product-filters'), array($this, 'render_price_filter_field'), self::PAGE_ID, 'bdfg_general_section' ); add_settings_field( 'bdfg_enable_advanced_features', __('Advanced Features', 'bdfg-product-filters'), array($this, 'render_advanced_features_field'), self::PAGE_ID, 'bdfg_advanced_section' ); add_settings_field( 'bdfg_cache_duration', __('Cache Duration', 'bdfg-product-filters'), array($this, 'render_cache_duration_field'), self::PAGE_ID, 'bdfg_cache_section' ); } /** * 渲染设置页面 */ public function render_settings_page() { if (!current_user_can('manage_woocommerce')) { wp_die(__('You do not have sufficient permissions to access this page.')); } ?> <div class="wrap"> <h1><?php echo esc_html(get_admin_page_title()); ?></h1> <?php if (isset($_GET['settings-updated'])) : ?> <div class="notice notice-success is-dismissible"> <p><?php esc_html_e('Settings saved successfully.', 'bdfg-product-filters'); ?></p> </div> <?php endif; ?> <form method="post" action="options.php"> <?php settings_fields(self::OPTION_GROUP); do_settings_sections(self::PAGE_ID); submit_button(); ?> </form> <div class="bdfg-admin-footer"> <p> <?php printf( __('BDFG Product Filters v%s by %s', 'bdfg-product-filters'), BDFG_VERSION, '<a href="https://beiduofengou.net" target="_blank">Bei Duo Feng Ou</a>' ); ?> </p> </div> </div> <?php } /** * 渲染常规设置区域 */ public function render_general_section() { echo '<p>' . esc_html__('Configure the basic filter settings.', 'bdfg-product-filters') . '</p>'; } /** * 渲染高级设置区域 */ public function render_advanced_section() { echo '<p>' . esc_html__('Configure advanced filtering features.', 'bdfg-product-filters') . '</p>'; } /** * 渲染缓存设置区域 */ public function render_cache_section() { echo '<p>' . esc_html__('Configure caching settings to improve performance.', 'bdfg-product-filters') . '</p>'; } /** * 渲染分类字段 */ public function render_taxonomies_field() { $enabled_taxonomies = get_option('bdfg_enabled_taxonomies', array('product_cat', 'product_tag')); $taxonomies = get_object_taxonomies('product', 'objects'); echo '<fieldset>'; foreach ($taxonomies as $taxonomy) { printf( '<label><input type="checkbox" name="bdfg_enabled_taxonomies[]" value="%s" %s> %s</label><br>', esc_attr($taxonomy->name), checked(in_array($taxonomy->name, $enabled_taxonomies), true, false), esc_html($taxonomy->label) ); } echo '</fieldset>'; echo '<p class="description">' . esc_html__('Select which product taxonomies to include in filters.', 'bdfg-product-filters') . '</p>'; } /** * 渲染价格过滤器字段 */ public function render_price_filter_field() { $price_filter = get_option('bdfg_price_filter', array( 'enabled' => 'yes', 'step' => 10, 'display' => 'slider' )); ?> <fieldset> <label> <input type="checkbox" name="bdfg_price_filter[enabled]" value="yes" <?php checked($price_filter['enabled'], 'yes'); ?>> <?php esc_html_e('Enable price filter', 'bdfg-product-filters'); ?> </label> <br><br> <label> <?php esc_html_e('Price step:', 'bdfg-product-filters'); ?> <input type="number" name="bdfg_price_filter[step]" value="<?php echo esc_attr($price_filter['step']); ?>" min="1" step="1" class="small-text"> </label> <br><br> <label> <?php esc_html_e('Display type:', 'bdfg-product-filters'); ?> <select name="bdfg_price_filter[display]"> <option value="slider" <?php selected($price_filter['display'], 'slider'); ?>> <?php esc_html_e('Slider', 'bdfg-product-filters'); ?> </option> <option value="input" <?php selected($price_filter['display'], 'input'); ?>> <?php esc_html_e('Input fields', 'bdfg-product-filters'); ?> </option> <option value="both" <?php selected($price_filter['display'], 'both'); ?>> <?php esc_html_e('Both', 'bdfg-product-filters'); ?> </option> </select> </label> </fieldset> <?php } /** * 渲染高级功能字段 */ public function render_advanced_features_field() { $advanced_features = get_option('bdfg_enable_advanced_features', 'yes'); ?> <fieldset> <label> <input type="checkbox" name="bdfg_enable_advanced_features" value="yes" <?php checked($advanced_features, 'yes'); ?>> <?php esc_html_e('Enable advanced filtering features', 'bdfg-product-filters'); ?> </label> <p class="description"> <?php esc_html_e('Includes vendor filtering, smart recommendations, and more.', 'bdfg-product-filters'); ?> </p> </fieldset> <?php } /** * 渲染缓存时间字段 */ public function render_cache_duration_field() { $cache_duration = get_option('bdfg_cache_duration', 3600); ?> <fieldset> <input type="number" name="bdfg_cache_duration" value="<?php echo esc_attr($cache_duration); ?>" min="0" step="60" class="medium-text"> <p class="description"> <?php esc_html_e('Cache duration in seconds. Set to 0 to disable caching.', 'bdfg-product-filters'); ?> </p> </fieldset> <?php } /** * 加载管理页面脚本和样式 */ public function enqueue_admin_scripts($hook) { if ($hook !== 'woocommerce_page_' . self::PAGE_ID) { return; } wp_enqueue_style( 'bdfg-admin-style', BDFG_ASSETS_URL . 'css/admin.css', array(), BDFG_VERSION ); wp_enqueue_script( 'bdfg-admin-script', BDFG_ASSETS_URL . 'js/admin.js', array('jquery'), BDFG_VERSION, true ); wp_localize_script('bdfg-admin-script', 'bdfgAdmin', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('bdfg-admin'), 'i18n' => array( 'confirm_reset' => __('Are you sure you want to reset all settings to default?', 'bdfg-product-filters'), 'success' => __('Settings saved successfully.', 'bdfg-product-filters'), 'error' => __('An error occurred while saving settings.', 'bdfg-product-filters') ) )); } } // 初始化设置 new BDFG_Settings();
assets/css/admin.css
相关文章: WordPress优惠券提醒插件系统
/** * BDFG Product Filters - Admin Styles * * @package BDFG_Product_Filters * @author Bei Duo Feng Ou <[email protected]> * @version 2.5.0 */ /* 设置页面样式 */ .bdfg-settings-wrap { max-width: 1200px; margin: 20px 0; } .bdfg-settings-header { background: #fff; padding: 20px; margin-bottom: 20px; border: 1px solid #ddd; border-radius: 4px; } .bdfg-settings-header h1 { margin: 0; color: #23282d; font-size: 24px; font-weight: 400; } /* 表单样式 */ .bdfg-form-table { background: #fff; border: 1px solid #ddd; border-radius: 4px; margin-top: 20px; } .bdfg-form-table th { padding: 20px; font-weight: 600; } .bdfg-form-table td { padding: 15px 20px; } /* 字段样式 */ .bdfg-field-wrap { margin-bottom: 15px; } .bdfg-field-label { display: block; margin-bottom: 5px; font-weight: 600; } .bdfg-field-description { color: #666; font-style: italic; margin-top: 5px; } /* 复选框组样式 */ .bdfg-checkbox-group { max-height: 200px; overflow-y: auto; padding: 10px; border: 1px solid #ddd; border-radius: 4px; } .bdfg-checkbox-item { margin-bottom: 8px; } .bdfg-checkbox-item:last-child { margin-bottom: 0; } /* 标签样式 */ .bdfg-tag { display: inline-block; padding: 4px 8px; margin: 0 5px 5px 0; background: #f0f0f0; border-radius: 3px; font-size: 12px; } .bdfg-tag .remove { margin-left: 5px; color: #999; cursor: pointer; } /* 按钮样式 */ .bdfg-button { display: inline-block; padding: 8px 15px; background: #2271b1; color: #fff; border: none; border-radius: 3px; cursor: pointer; text-decoration: none; font-size: 13px; line-height: 1.5; } .bdfg-button:hover { background: #135e96; color: #fff; } .bdfg-button.reset { background: #dc3232; } .bdfg-button.reset:hover { background: #ba281e; } /* 通知样式 */ .bdfg-notice { padding: 12px; margin: 15px 0; background: #fff; border-left: 4px solid #00a32a; box-shadow: 0 1px 1px rgba(0,0,0,.04); } .bdfg-notice.error { border-left-color: #dc3232; } /* 页脚样式 */ .bdfg-admin-footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-style: italic; } /* 响应式调整 */ @media screen and (max-width: 782px) { .bdfg-form-table th { padding: 15px; } .bdfg-form-table td { padding: 10px 15px; } .bdfg-checkbox-group { max-height: 150px; } }
assets/js/admin.js
/** * BDFG Product Filters - Admin JavaScript * * @package BDFG_Product_Filters * @author Bei Duo Feng Ou <[email protected]> * @version 2.5.0 */ (function($) { 'use strict'; var BDFG_Admin = { init: function() { this.bindEvents(); this.initTooltips(); this.initSortable(); }, bindEvents: function() { // 重置设置 $('.bdfg-reset-settings').on('click', this.resetSettings); // 测试缓存 $('.bdfg-test-cache').on('click', this.testCache); // 清除缓存 $('.bdfg-clear-cache').on('click', this.clearCache); // 导入/导出设置 $('#bdfg-import-settings').on('change', this.importSettings); $('.bdfg-export-settings').on('click', this.exportSettings); // 动态添加/删除字段 $('.bdfg-add-field').on('click', this.addField); $(document).on('click', '.bdfg-remove-field', this.removeField); // 表单验证 $('#bdfg-settings-form').on('submit', this.validateForm); }, initTooltips: function() { $('.bdfg-tooltip').tooltipster({ theme: 'tooltipster-light', maxWidth: 300, animation: 'fade', delay: 200 }); }, initSortable: function() { $('.bdfg-sortable').sortable({ handle: '.bdfg-sort-handle', axis: 'y', update: function() { BDFG_Admin.saveOrder(); } }); }, resetSettings: function(e) { e.preventDefault(); if (!confirm(bdfgAdmin.i18n.confirm_reset)) { return; } $.ajax({ url: bdfgAdmin.ajaxurl, type: 'POST', data: { action: 'bdfg_reset_settings', nonce: bdfgAdmin.nonce }, beforeSend: function() { BDFG_Admin.showLoader(); }, success: function(response) { if (response.success) { window.location.reload(); } else { BDFG_Admin.showError(response.data.message); } }, error: function() { BDFG_Admin.showError(bdfgAdmin.i18n.error); }, complete: function() { BDFG_Admin.hideLoader(); } }); }, testCache: function(e) { e.preventDefault(); $.ajax({ url: bdfgAdmin.ajaxurl, type: 'POST', data: { action: 'bdfg_test_cache', nonce: bdfgAdmin.nonce }, beforeSend: function() { BDFG_Admin.showLoader(); }, success: function(response) { if (response.success) { BDFG_Admin.showSuccess(response.data.message); } else { BDFG_Admin.showError(response.data.message); } }, error: function() { BDFG_Admin.showError(bdfgAdmin.i18n.error); }, complete: function() { BDFG_Admin.hideLoader(); } }); }, clearCache: function(e) { e.preventDefault(); $.ajax({ url: bdfgAdmin.ajaxurl, type: 'POST', data: { action: 'bdfg_clear_cache', nonce: bdfgAdmin.nonce }, beforeSend: function() { BDFG_Admin.showLoader(); }, success: function(response) { if (response.success) { BDFG_Admin.showSuccess(response.data.message); } else { BDFG_Admin.showError(response.data.message); } }, error: function() { BDFG_Admin.showError(bdfgAdmin.i18n.error); }, complete: function() { BDFG_Admin.hideLoader(); } }); }, importSettings: function(e) { var file = e.target.files[0]; if (!file) return; var reader = new FileReader(); reader.onload = function(e) { try { var settings = JSON.parse(e.target.result); $.ajax({ url: bdfgAdmin.ajaxurl, type: 'POST', data: { action: 'bdfg_import_settings', nonce: bdfgAdmin.nonce, settings: settings }, beforeSend: function() { BDFG_Admin.showLoader(); }, success: function(response) { if (response.success) { window.location.reload(); } else { BDFG_Admin.showError(response.data.message); } }, error: function() { BDFG_Admin.showError(bdfgAdmin.i18n.error); }, complete: function() { BDFG_Admin.hideLoader(); } }); } catch (error) { BDFG_Admin.showError(bdfgAdmin.i18n.invalid_file); } }; reader.readAsText(file); }, exportSettings: function(e) { e.preventDefault(); $.ajax({ url: bdfgAdmin.ajaxurl, type: 'POST', data: { action: 'bdfg_export_settings', nonce: bdfgAdmin.nonce }, beforeSend: function() { BDFG_Admin.showLoader(); }, success: function(response) { if (response.success) { var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(response.data)); var downloadLink = document.createElement('a'); downloadLink.setAttribute("href", dataStr); downloadLink.setAttribute("download", "bdfg-settings.json"); document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); } else { BDFG_Admin.showError(response.data.message); } }, error: function() { BDFG_Admin.showError(bdfgAdmin.i18n.error); }, complete: function() { BDFG_Admin.hideLoader(); } }); }, addField: function(e) { e.preventDefault(); var template = wp.template('bdfg-field'); var $container = $(this).closest('.bdfg-fields-container'); var count = $container.find('.bdfg-field').length; $container.append(template({ index: count, timestamp: new Date().getTime() })); }, removeField: function(e) { e.preventDefault(); $(this).closest('.bdfg-field').fadeOut(300, function() { $(this).remove(); BDFG_Admin.reindexFields(); }); }, reindexFields: function() { $('.bdfg-fields-container').each(function() { $(this).find('.bdfg-field').each(function(index) { $(this).find('[name]').each(function() { var name = $(this).attr('name'); $(this).attr('name', name.replace(/\[\d+\]/, '[' + index + ']')); }); }); }); }, saveOrder: function() { var order = []; $('.bdfg-sortable .bdfg-field').each(function() { order.push($(this).data('id')); }); $.ajax({ url: bdfgAdmin.ajaxurl, type: 'POST', data: { action: 'bdfg_save_order', nonce: bdfgAdmin.nonce, order: order }, success: function(response) { if (response.success) { BDFG_Admin.showSuccess(response.data.message); } else { BDFG_Admin.showError(response.data.message); } } }); }, validateForm: function(e) { var isValid = true; // 清除之前的错误 $('.bdfg-error').remove(); // 验证必填字段 $(this).find('[required]').each(function() { if (!$(this).val()) { isValid = false; $(this).after('<span class="bdfg-error">' + bdfgAdmin.i18n.required_field + '</span>'); } }); // 验证数字字段 $(this).find('[type="number"]').each(function() { var val = $(this).val(); var min = $(this).attr('min'); var max = $(this).attr('max'); if (val && ( (min && parseFloat(val) < parseFloat(min)) || (max && parseFloat(val) > parseFloat(max)) )) { isValid = false; $(this).after('<span class="bdfg-error">' + bdfgAdmin.i18n.invalid_number + '</span>'); } }); if (!isValid) { e.preventDefault(); $('html, body').animate({ scrollTop: $('.bdfg-error').first().offset().top - 100 }, 500); } }, showLoader: function() { $('.bdfg-loader').fadeIn(200); }, hideLoader: function() { $('.bdfg-loader').fadeOut(200); }, showSuccess: function(message) { var $notice = $('<div class="bdfg-notice success">' + message + '</div>') .hide() .insertBefore('#bdfg-settings-form') .fadeIn(300); setTimeout(function() { $notice.fadeOut(300, function() { $(this).remove(); }); }, 3000); }, showError: function(message) { var $notice = $('<div class="bdfg-notice error">' + message + '</div>') .hide() .insertBefore('#bdfg-settings-form') .fadeIn(300); setTimeout(function() { $notice.fadeOut(300, function() { $(this).remove(); }); }, 5000); } }; // 文档加载完成后初始化 $(document).ready(function() { BDFG_Admin.init(); }); })(jQuery);
相关文章: WooCommerce 高级产品定价和折扣管理