WordPress產品分類新增,自動排序外掛

还好阿卡發表於2024-09-29

效果圖如下

目前這個預覽選單這個效果有點問題,但是不影響實際排序,有懂原始碼的朋友可以自行修改一下,
目錄結構
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 = '預覽載入失敗';
        }
    });
}

  

相關文章