WordPress产品分类添加,自动排序插件

效果图如下

**目前这个预览菜单这个效果有点问题,但是不影响实际排序,有懂源码的朋友可以自行修改一下,**目录结构
menu
  -assets
    menu.css

menu.js

menu.php

源码如下
menu.php文件

复制代码
<?php
/**
 * Plugin Name: 菜单整理
 * Description: 将 WooCommerce 产品分类添加到现有菜单中。
 * Version: 1.2
 * Author: 朵啦
 * License: GPL2
 */

// 防止直接访问文件
if (!defined('ABSPATH')) {
    exit;
}

// 注册插件设置页面
add_action('admin_menu', 'cmo_add_admin_menu');
function cmo_add_admin_menu() {
    add_menu_page(
        '分类菜单管理',      // 页面标题
        '分类菜单',          // 菜单标题
        'manage_options',    // 权限
        'cmo-settings',      // 菜单 slug
        'cmo_settings_page', // 回调函数
        'dashicons-menu',    // 图标
        60                   // 位置
    );
}

// 引入 JS 和 CSS 文件
add_action('admin_enqueue_scripts', 'cmo_enqueue_scripts');
function cmo_enqueue_scripts() {
    wp_enqueue_script('cmo-menu-js', plugin_dir_url(__FILE__) . 'assets/menu.js', ['jquery'], false, true);
    wp_enqueue_style('cmo-menu-css', plugin_dir_url(__FILE__) . 'assets/menu.css');
}

// 使 ajaxurl 变量在前端 JavaScript 中可用
add_action('admin_enqueue_scripts', 'add_ajax_url');
function add_ajax_url() {
    wp_localize_script('cmo-menu-js', 'ajaxurl', admin_url('admin-ajax.php'));
}

// 设置页面的显示内容
function cmo_settings_page() {
    ?>
    <div class="wrap">
        <h1>WooCommerce 分类菜单管理</h1>
        <form method="post" action="options.php">
            <?php
            settings_fields('cmo_settings_group');
            
            function cmo_section_text() {
                echo '<p>选择要添加到菜单的产品分类</p>';
            }

            do_settings_sections('cmo-settings');
            
            ?>
        </form>

        <h2>菜单操作</h2>
        <form method="post" action="" id="menu-action-form">
            <label for="cmo_menu_selector">选择菜单:</label>
            <?php cmo_menu_selector(); ?>
            <button type="button" id="cmo_add_to_menu" class="button button-primary">添加分类到选定菜单</button>
            <button type="button" id="cmo_backup_menu" class="button">备份当前菜单</button>
            <button type="button" id="cmo_restore_menu" class="button">恢复备份菜单</button>
        </form>

        <div id="menu-preview">
            <h3>菜单预览</h3>
            <div id="preview-content"></div>
        </div>
    </div>
    <?php
}

// 注册设置字段
add_action('admin_init', 'cmo_settings_init');
function cmo_settings_init() {
    register_setting('cmo_settings_group', 'cmo_selected_categories');

    add_settings_section(
        'cmo_main_section', 
        '选择要添加到菜单的产品分类', 
        'cmo_section_text', 
        'cmo-settings'
    );

    add_settings_field(
        'cmo_categories_field', 
        '产品分类', 
        'cmo_categories_field_callback', 
        'cmo-settings', 
        'cmo_main_section'
    );
}

// 分类选择字段回调,递归展示分类
function cmo_categories_field_callback($parent = 0, $level = 0) {
    if ($parent == 0 || $level == 0) {
        // 只在最顶层展示全选按钮
        echo '<input type="checkbox" id="select-all"> 全选<br><div style="display: flex; flex-wrap: wrap;">';
    }

    $categories = get_terms([
        'taxonomy' => 'product_cat',
        'hide_empty' => false,
        'parent' => $parent // 通过 parent 参数递归获取子分类
    ]);

    $selected_categories = get_option('cmo_selected_categories', []);

    if (!is_array($selected_categories)) {
        $selected_categories = [];
    }

    foreach ($categories as $category) {
        // 缩进效果,表示分类层级
        $indent = str_repeat('   ', $level);

        echo '<div style="flex-basis: 100%; margin-left:' . ($level * 20) . 'px;">' . 
            '<input type="checkbox" name="cmo_selected_categories[]" value="' . esc_attr($category->term_id) . '" ' .
            checked(in_array($category->term_id, $selected_categories), true, false) . '> ' . esc_html($category->name) . '</div>';

        // 递归调用自己,展示子分类
        cmo_categories_field_callback($category->term_id, $level + 1);
    }

    if ($parent == 0 && $level == 0) {
        // 结束顶层div
        echo '</div>';
    }
}

// 生成分类菜单选择器
function cmo_menu_selector() {
    $menus = wp_get_nav_menus();
    echo '<select name="cmo_selected_menu" id="cmo_menu_selector">';
    foreach ($menus as $menu) {
        echo '<option value="' . esc_attr($menu->term_id) . '">' . esc_html($menu->name) . '</option>';
    }
    echo '</select>';
}

// 添加分类到菜单的功能
add_action('wp_ajax_cmo_add_to_menu', 'cmo_add_categories_to_menu');
function cmo_add_categories_to_menu() {
    if (!isset($_POST['menu_id'])) {
        wp_send_json_error('菜单ID未设置');
    }

    $menu_id = intval($_POST['menu_id']);
    
    if (!isset($_POST['selected_categories']) || empty($_POST['selected_categories'])) {
        wp_send_json_error('未选择任何分类');
    }

    $selected_categories = $_POST['selected_categories'];
    
    // 创建一个数组来保存分类和菜单项的 ID 关联
    $category_menu_items = [];

    // 循环处理选中的分类
    foreach ($selected_categories as $category_id) {
        $category = get_term($category_id, 'product_cat');
        
        // 获取当前分类的父分类 ID
        $parent_id = $category->parent;
        
        // 如果父分类已存在菜单项,则将其设置为子菜单项
        $parent_menu_item_id = isset($category_menu_items[$parent_id]) ? $category_menu_items[$parent_id] : 0;

        // 添加菜单项,并保存它的 ID
        $menu_item_id = wp_update_nav_menu_item($menu_id, 0, [
            'menu-item-title' => esc_html($category->name),
            'menu-item-url' => get_term_link($category),
            'menu-item-status' => 'publish',
            'menu-item-parent-id' => $parent_menu_item_id, // 指定父菜单项
        ]);

        // 将当前分类的菜单项 ID 保存到数组中,供子分类使用
        $category_menu_items[$category_id] = $menu_item_id;
    }

    wp_send_json_success('分类已成功添加到菜单');
}

// 备份当前菜单
add_action('wp_ajax_cmo_backup_menu', 'cmo_backup_menu');
function cmo_backup_menu() {
    if (!isset($_POST['menu_id'])) {
        wp_send_json_error('菜单ID未设置');
    }

    $menu_id = intval($_POST['menu_id']);
    $menu_items = wp_get_nav_menu_items($menu_id);

    if ($menu_items) {
        update_option('cmo_menu_backup_' . $menu_id, $menu_items);
        wp_send_json_success('菜单已成功备份');
    }

    wp_send_json_error('备份失败');
}

// 恢复备份菜单
add_action('wp_ajax_cmo_restore_menu', 'cmo_restore_menu');
function cmo_restore_menu() {
    if (!isset($_POST['menu_id'])) {
        wp_send_json_error('菜单ID未设置');
    }

    $menu_id = intval($_POST['menu_id']);
    $backup = get_option('cmo_menu_backup_' . $menu_id);

    if ($backup) {
        foreach ($backup as $item) {
            wp_update_nav_menu_item($menu_id, 0, [
                'menu-item-title' => esc_html($item->title),
                'menu-item-url' => $item->url,
                'menu-item-status' => 'publish',
            ]);
        }
        wp_send_json_success('菜单已成功恢复');
    }

    wp_send_json_error('没有备份可恢复');
}

// 预览菜单内容
add_action('wp_ajax_cmo_preview_menu', 'cmo_preview_menu');
function cmo_preview_menu() {
    if (!isset($_POST['menu_id'])) {
        wp_send_json_error('菜单ID未设置');
    }

    $menu_id = intval($_POST['menu_id']);
    $menu_items = wp_get_nav_menu_items($menu_id);

    if (empty($menu_items)) {
        wp_send_json_error('该菜单没有内容');
    }

    $html = '<ul class="menu-preview">';

    foreach ($menu_items as $item) {
        // 根据菜单项的 parent 判断是否是子项
        if ($item->menu_item_parent == 0) {
            $html .= '<li class="menu-item">' . esc_html($item->title);
            
            // 查找子项
            $html .= get_menu_child_items($menu_items, $item->ID);
            $html .= '</li>';
        }
    }

    $html .= '</ul>';

    wp_send_json_success($html);
}

// 获取子菜单项的递归函数
function get_menu_child_items($menu_items, $parent_id) {
    $child_items = '';
    foreach ($menu_items as $item) {
        if ($item->menu_item_parent == $parent_id) {
            if ($child_items == '') {
                $child_items .= '<ul class="submenu">';
            }
            $child_items .= '<li class="menu-item">' . esc_html($item->title);
            $child_items .= get_menu_child_items($menu_items, $item->ID);
            $child_items .= '</li>';
        }
    }
    if ($child_items != '') {
        $child_items .= '</ul>';
    }
    return $child_items;
}


?>

menu.css文件

复制代码
/* 调整预览框的高度和宽度 */
#menu-preview {
    margin-top: 20px;
    border: 1px solid #ddd;
    padding: 10px;
    background-color: #f9f9f9;
    width: 100%; /* 让框的宽度适应容器 */
    height: 400px; /* 设置高度为400px,具体可根据需要调整 */
    overflow-y: auto; /* 让框的内容可以滚动 */
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
}

/* 一级菜单样式 */
.menu-preview {
    display: flex;
    flex-direction: row;
    list-style: none;
    padding: 0;
    margin: 0;
}

.menu-item {
    position: relative;
    padding: 10px 20px;
    background-color: #f0f0f0;
    margin-right: 10px;
    cursor: default;
    border: 1px solid #ccc; /* 添加边框 */
    border-radius: 5px; /* 圆角效果 */
    font-weight: bold; /* 让文字加粗 */
    transition: background-color 0.3s ease; /* 添加背景颜色的过渡效果 */
}

/* 一级菜单悬浮效果 */
.menu-item:hover {
    background-color: #e0e0e0;
    border-color: #b0b0b0; /* 悬浮时改变边框颜色 */
}

/* 子菜单样式 */
.submenu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    background-color: white;
    list-style: none;
    padding: 0;
    margin: 0;
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
    z-index: 10; /* 确保子菜单在顶层显示 */
    opacity: 0; /* 初始透明度 */
    visibility: hidden; /* 初始不可见 */
    transition: opacity 0.3s ease, visibility 0.3s ease; /* 过渡效果 */
}

/* 子菜单项的样式 */
.submenu .menu-item {
    padding: 10px;
    margin-right: 0;
    white-space: nowrap;
    background-color: #ffffff;
    border: 1px solid #ddd; /* 给子菜单项添加边框 */
    border-radius: 3px;
}

/* 子菜单项的悬浮效果 */
.submenu .menu-item:hover {
    background-color: #f0f0f0;
}

/* 一级菜单悬浮时显示子菜单 */
.menu-item:hover .submenu {
    display: block;
    opacity: 1; /* 显示时渐变透明度 */
    visibility: visible; /* 显示可见 */
    z-index: 999;
}

/* 让一级菜单项和子菜单保持距离 */
.menu-item:hover .submenu {
    margin-top: 5px;
}

/* 调整子菜单的位置 */
.submenu {
    min-width: 200px; /* 给子菜单设置最小宽度 */
    z-index: 1000;
}

/* 鼠标移开子菜单后延迟消失 */
.menu-item {
    transition: background-color 0.3s ease;
}

/* 子菜单在鼠标移开后延迟消失 */
.menu-item:hover .submenu {
    transition: opacity 0.3s ease, visibility 0.3s ease;
}

.menu-item .submenu {
    transition-delay: 1.5s; /* 添加延迟消失效果 */
}

/* 阻止点击行为,确保只是预览 */
.menu-preview a {
    pointer-events: none;
    color: #333;
    text-decoration: none;
    cursor: default;
}

menu.js

复制代码
document.addEventListener('DOMContentLoaded', function() {
    
    // 禁用菜单预览的点击事件
    const previewLinks = document.querySelectorAll('.menu-preview .menu-item');
    previewLinks.forEach(function(link) {
        link.addEventListener('click', function(event) {
            event.preventDefault();  // 禁用默认的点击行为
        });
    });

    // 处理菜单悬停显示子菜单
    const menuItems = document.querySelectorAll('.menu-item');
    
    menuItems.forEach(function(menuItem) {
        let timer; // 定义延时计时器

        menuItem.addEventListener('mouseenter', function() {
            clearTimeout(timer); // 清除离开时的计时器,确保子菜单正常显示
            const submenu = this.querySelector('.submenu');
            if (submenu) {
                submenu.style.display = 'block';
                submenu.style.opacity = '1';
                submenu.style.visibility = 'visible';
            }
        });

        menuItem.addEventListener('mouseleave', function() {
            const submenu = this.querySelector('.submenu');
            if (submenu) {
                timer = setTimeout(function() {
                    submenu.style.opacity = '0';
                    submenu.style.visibility = 'hidden';
                }, 1500); // 鼠标离开 1.5 秒后隐藏子菜单
            }
        });
    });
    
    // 全选功能
    const selectAllCheckbox = document.getElementById('select-all');
    if (selectAllCheckbox) {
        selectAllCheckbox.addEventListener('click', function () {
            const checkboxes = document.querySelectorAll('input[name="cmo_selected_categories[]"]');
            checkboxes.forEach(checkbox => checkbox.checked = this.checked);
            console.log('全选按钮已点击');
        });
    }

    // 按钮点击事件
    const addToMenuButton = document.getElementById('cmo_add_to_menu');
    if (addToMenuButton) {
        addToMenuButton.addEventListener('click', function (event) {
            console.log('添加分类到选定菜单按钮已点击');
            handleMenuAction('cmo_add_to_menu', '添加分类到选定菜单', event);
        });
    }

    const backupMenuButton = document.getElementById('cmo_backup_menu');
    if (backupMenuButton) {
        backupMenuButton.addEventListener('click', function (event) {
            handleMenuAction('cmo_backup_menu', '备份当前菜单', event);
        });
    }

    const restoreMenuButton = document.getElementById('cmo_restore_menu');
    if (restoreMenuButton) {
        restoreMenuButton.addEventListener('click', function (event) {
            handleMenuAction('cmo_restore_menu', '恢复备份菜单', event);
        });
    }

    // 预览菜单
    const menuSelector = document.getElementById('cmo_menu_selector');
    if (menuSelector) {
        menuSelector.addEventListener('change', function () {
            const menuId = this.value;
            loadMenuPreview(menuId);
        });
    }
});

// 处理按钮点击的AJAX请求
function handleMenuAction(action, message, event) {
    const menuId = document.getElementById('cmo_menu_selector').value;
    console.log('处理菜单:', menuId);
    const button = event.target;
    button.disabled = true;
    button.innerHTML = '处理中...';

    // 获取选中的分类
    const selectedCategories = Array.from(document.querySelectorAll('input[name="cmo_selected_categories[]"]:checked')).map(input => input.value);

    if (selectedCategories.length === 0) {
        alert("未选择任何分类");
        button.disabled = false;
        button.innerHTML = message;
        console.log('未选择分类');
        return;
    }

    console.log('发送的分类:', selectedCategories);

    // 发送 AJAX 请求
    jQuery.post(ajaxurl, {
        action: action,
        menu_id: menuId,
        selected_categories: selectedCategories  // 传递选中的分类数据
    }, function(response) {
        console.log('Response:', response);
        button.disabled = false;
        button.innerHTML = message;

        if (response.success) {
            alert(response.data);
            console.log('操作成功');
            if (action === 'cmo_add_to_menu' || action === 'cmo_preview_menu') {
                loadMenuPreview(menuId);
            }
        } else {
            console.log('操作失败:', response.data);
            alert('操作失败: ' + response.data);
        }
    });
}

// 加载菜单预览
function loadMenuPreview(menuId) {
    document.getElementById('preview-content').innerHTML = '加载中...';
    jQuery.post(ajaxurl, {
        action: 'cmo_preview_menu',
        menu_id: menuId
    }, function (response) {
        if (response.success) {
            document.getElementById('preview-content').innerHTML = response.data;
        } else {
            document.getElementById('preview-content').innerHTML = '预览加载失败';
        }
    });
}