效果图如下
目前这个预览菜单这个效果有点问题,但是不影响实际排序,有懂源码的朋友可以自行修改一下,
目录结构
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"><?phpsettings_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) {// 结束顶层divecho '</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 = '预览加载失败';}}); }