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