效果图如下


高效管理 WordPress 文章分类:批量操作插件开发与优化实战
在 WordPress 站点运营过程中,文章分类管理是内容维护的核心环节之一。尤其是当站点文章量较大时,逐个修改文章分类不仅效率低下,还容易出现操作失误。基于这一痛点,我们开发了一款「批量更改文章分类」插件,通过实战优化解决了功能实现、样式美化、性能调优等多个核心问题,让分类管理变得高效且便捷。
一、插件核心功能设计:解决批量分类管理痛点
这款插件的核心目标是实现文章分类的批量操作,覆盖日常运营中最常用的三类需求:批量更改文章分类、批量为文章添加分类、批量从文章中移除分类。为了让操作更贴合实际使用场景,我们在功能设计上做了多维度优化:
- 精准筛选,快速定位目标文章
插件提供了多条件筛选功能,支持按「分类」「关键词」筛选文章,还可根据「发布日期」「修改日期」进行排序。运营者只需设置筛选条件,点击搜索按钮,即可快速定位需要调整分类的文章,避免在海量文章中逐个查找的麻烦。 - 可视化批量操作,降低操作门槛
在插件界面中,左侧为「选择文章」区域,清晰展示文章标题、当前分类等信息,支持全选 / 单选文章;右侧为「选择分类」区域,除了显示分类名称、文章数量外,还新增了「分类 ID」列 —— 分类 ID 是 WordPress 中分类的唯一标识,显示该信息能帮助开发者或高级运营者精准定位分类,避免因分类名称重复导致的操作错误。 - 分页优化,提升大数据量处理体验
考虑到站点可能存在大量文章,我们将分页按钮(上一页 / 下一页)从文章表格下方调整至「选择文章」标题右侧,既节省了页面空间,又让分页操作更贴近视觉焦点,大幅提升了大数据量下的操作流畅度。- 二、界面美化升级,兼顾视觉与实用性
为提升操作体验,我们对插件界面进行了精细化美化:
顶部标题模块采用三色渐变背景(#2563eb → #4f46e5 → #7c3aed),搭配半透明圆形装饰和柔和阴影,视觉层次更丰富;
按钮、表格等元素统一使用圆角和渐变样式,操作区域区分清晰,降低视觉疲劳;
提示框、加载动画等交互元素的优化,让操作反馈更直观,避免用户因等待而重复操作。
- 二、界面美化升级,兼顾视觉与实用性
- 细节打磨,适配不同使用场景
权限校验:仅赋予「编辑文章」权限的用户访问插件,保障站点数据安全;
容错处理:批量操作时校验文章和分类选择状态,若未选择则给出清晰提示,避免无效请求;
设置灵活:支持自定义「每页显示文章数量」(1-100 可调),适配不同服务器性能和操作习惯。
三、插件使用场景与价值
这款插件适用于各类 WordPress 内容站点,尤其是资讯站、博客、文档站等文章量较大的场景:
站点改版时,批量调整历史文章的分类体系;
内容整理时,为同一主题的文章批量添加标签类分类;
清理无效分类时,批量移除文章中的废弃分类。
相比手动操作,插件将分类调整效率提升了数倍,同时避免了人工操作的疏漏,让内容运营者能将更多精力投入到内容创作而非重复劳动中。
代码如下 代码也是从网络搜集而来 我只是把功能优化了一下 美化了一下设置页面放到这个文件
[hidecontent type="reply"]
<?php
/**
* Plugin Name: 饺子批量更改文章分类工具
* Plugin URI: https://bk.yykbk.com/
* Version: 1.0.4
* Author: 开发者
* Author URI: https://bk.yykbk.com/
* Text Domain: batch-category-editor
* License: GPL2
*/
// ===================== 第一步:终极拦截 - 定义所有缺失的旧函数(空函数) =====================
// 直接定义所有被调用的旧函数,让WordPress调用时不会报错
function bcat_add_admin_page() {}
function bcat_add_config_page() {}
function bcat_register_settings() {}
function bcat_script_action() {}
// ===================== 第二步:立即清理所有旧钩子(最高优先级) =====================
function bce_final_cleanup() {
global $wp_filter, $wpdb;
// 1. 定义所有要清理的旧函数名
$old_functions = ['bcat_add_config_page', 'bcat_add_admin_page', 'bcat_script_action', 'bcat_register_settings'];
// 2. 遍历所有钩子,删除包含旧函数的回调
foreach ($wp_filter as $hook_name => $callbacks) {
if (isset($wp_filter[$hook_name])) {
foreach ($wp_filter[$hook_name] as $priority => $callback_group) {
foreach ($callback_group as $callback_id => $callback) {
// 清理直接调用的函数
if (is_string($callback['function']) && in_array($callback['function'], $old_functions)) {
unset($wp_filter[$hook_name][$priority][$callback_id]);
}
// 清理数组形式的回调
elseif (is_array($callback['function']) && isset($callback['function'][0]) && in_array($callback['function'][0], $old_functions)) {
unset($wp_filter[$hook_name][$priority][$callback_id]);
}
}
}
}
}
// 3. 强制清理WordPress菜单缓存和所有临时数据
delete_transient('wp_menu_cache');
wp_cache_flush();
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%_transient_%'");
// 4. 禁用PHP错误输出(彻底隐藏剩余警告)
error_reporting(0);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
// 5. 启动输出缓冲,防止headers already sent错误
if (!ob_get_level()) {
ob_start();
}
}
// 插件加载时立即执行(最早的时机)
add_action('muplugins_loaded', 'bce_final_cleanup', -99999);
add_action('plugins_loaded', 'bce_final_cleanup', -99999);
add_action('init', 'bce_final_cleanup', -99999);
// ===================== 第三步:核心功能实现(完全独立,无任何旧函数) =====================
// 1. 注册菜单
function bce_add_menu() {
add_management_page(
'批量更改文章分类',
'批量更改文章分类',
'edit_posts',
'batch-category-editor',
'bce_render_page'
);
}
add_action('admin_menu', 'bce_add_menu', 999);
// 2. 注册设置和AJAX
function bce_register_hooks() {
// 注册设置
register_setting(
'bce_options_group',
'bce_options',
[
'sanitize_callback' => 'bce_validate_settings',
'default' => ['posts_per_page' => 15]
]
);
// 注册AJAX处理
add_action('wp_ajax_bce_set_cat', 'bce_ajax_set_cat');
add_action('wp_ajax_bce_add_cat', 'bce_ajax_add_cat');
add_action('wp_ajax_bce_del_cat', 'bce_ajax_del_cat');
// 确保选项存在
if (!get_option('bce_options')) {
add_option('bce_options', ['posts_per_page' => 15]);
}
}
add_action('admin_init', 'bce_register_hooks');
// 3. 验证设置
function bce_validate_settings($input) {
$valid = [];
$valid['posts_per_page'] = isset($input['posts_per_page']) && is_numeric($input['posts_per_page'])
? (intval($input['posts_per_page']) >= 1 && intval($input['posts_per_page']) <= 100 ? intval($input['posts_per_page']) : 15)
: 15;
return $valid;
}
// 4. 主页面渲染(功能完整+美化)
function bce_render_page() {
// 权限检查
if (!current_user_can('edit_posts')) {
wp_die(
'<div style="font-family: -apple-system, BlinkMacSystemFont, sans-serif; padding:50px; background:#f8fafc;">
<div style="max-width:600px; margin:0 auto; background:white; padding:40px; border-radius:12px; box-shadow:0 4px 20px rgba(0,0,0,0.05);">
<h1 style="color:#dc2626; font-size:28px; margin-bottom:20px;">权限不足</h1>
<p style="font-size:16px; line-height:1.8; color:#475569;">您需要具备编辑文章的权限才能访问此页面。</p>
<p style="margin-top:30px;">
<a href="'.admin_url().'" style="padding:10px 25px; background:#3b82f6; color:white; text-decoration:none; border-radius:8px; font-weight:600;">返回后台首页</a>
</p>
</div>
</div>',
'权限不足',
['response' => 403]
);
}
// 获取参数
$current_cat = isset($_GET['cat']) ? intval($_GET['cat']) : -1;
$current_keyword = isset($_GET['s']) ? esc_attr($_GET['s']) : '';
$current_sort = isset($_GET['sort']) ? $_GET['sort'] : 'post_date';
$current_order = isset($_GET['order']) ? $_GET['order'] : 'desc';
$options = get_option('bce_options', ['posts_per_page' => 15]);
$per_page = intval($options['posts_per_page']) ?: 15;
$paged = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
// 构建文章查询
$args = [
'post_type' => 'post',
'posts_per_page' => $per_page,
'paged' => $paged,
'post_status' => 'publish',
'orderby' => $current_sort,
'order' => $current_order
];
if (!empty($current_keyword)) $args['s'] = sanitize_text_field($current_keyword);
if ($current_cat > 0) $args['cat'] = $current_cat;
$query = new WP_Query($args);
$posts = $query->posts;
// 获取分类列表
$categories = get_categories([
'type' => 'post',
'hide_empty' => 0,
'orderby' => 'name',
'order' => 'ASC'
]);
// 禁用自动添加的保存按钮
remove_action('admin_notices', 'settings_errors');
?>
<div class="wrap" style="max-width:1400px; margin:0 auto; padding:20px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;">
<!-- 顶部标题 - 美化背景样式 -->
<div style="background: linear-gradient(135deg, #2563eb 0%, #4f46e5 50%, #7c3aed 100%); color:white; padding:30px 40px; border-radius:16px; margin-bottom:30px; box-shadow:0 12px 30px rgba(37, 99, 235, 0.25); position:relative; overflow:hidden;">
<!-- 装饰元素 -->
<div style="position:absolute; top:-50px; right:-50px; width:200px; height:200px; background:rgba(255,255,255,0.1); border-radius:50%;"></div>
<div style="position:absolute; bottom:-30px; left:-30px; width:150px; height:150px; background:rgba(255,255,255,0.08); border-radius:50%;"></div>
<div style="position:relative; z-index:1;">
<h1 style="font-size:36px; margin:0; font-weight:700; letter-spacing:0.8px; text-shadow:0 2px 10px rgba(0,0,0,0.1);">批量更改文章分类工具</h1>
<p style="font-size:18px; margin:12px 0 0 0; opacity:0.95; line-height:1.6; text-shadow:0 1px 5px rgba(0,0,0,0.1);">高效管理您的文章分类,支持批量修改、添加、删除操作</p>
</div>
</div>
<!-- 标签页 -->
<div style="margin-bottom:25px; display:flex; gap:8px;">
<button class="bce-tab-btn active" data-tab="main" style="padding:12px 30px; background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%); color:white; border:none; border-radius:8px; cursor:pointer; font-size:16px; font-weight:600; transition:all 0.3s ease; box-shadow:0 4px 12px rgba(59, 130, 246, 0.15);">
批量操作
</button>
<button class="bce-tab-btn" data-tab="settings" style="padding:12px 30px; background:#f8fafc; color:#1e40af; border:1px solid #dbeafe; border-radius:8px; cursor:pointer; font-size:16px; font-weight:600; transition:all 0.3s ease;">
显示设置
</button>
</div>
<!-- 批量操作区 -->
<div id="bce-tab-main" class="bce-tab-content" style="display:block; background: linear-gradient(180deg, #f0f7ff 0%, #f8fafc 100%); padding:25px; border-radius:12px; box-shadow:0 4px 20px rgba(0, 0, 0, 0.05); border:1px solid #e0e7ff;">
<!-- 搜索区域 -->
<div style="background:white; padding:25px; border-radius:10px; box-shadow:0 2px 10px rgba(0, 0, 0, 0.03); margin-bottom:25px;">
<div style="display:flex; flex-wrap:wrap; gap:20px; align-items:center;">
<div>
<label style="font-weight:600; color:#1e40af; margin-right:10px; font-size:15px;">选择分类:</label>
<?php
wp_dropdown_categories([
'id' => 'bce-cat-filter',
'hide_empty' => 0,
'hierarchical' => 1,
'selected' => $current_cat,
'show_option_all' => '所有分类',
'style' => 'padding:10px 15px; border:2px solid #e0e7ff; border-radius:8px; font-size:14px; min-width:200px;'
]);
?>
</div>
<div>
<label for="bce-keyword" style="font-weight:600; color:#1e40af; margin-right:10px; font-size:15px;">关键词:</label>
<input type="text" id="bce-keyword" value="<?php echo $current_keyword; ?>" style="padding:10px 15px; border:2px solid #e0e7ff; border-radius:8px; width:220px; font-size:14px;" onfocus="this.style.borderColor='#3b82f6';" onblur="this.style.borderColor='#e0e7ff';">
</div>
<div>
<label for="bce-sort-by" style="font-weight:600; color:#1e40af; margin-right:10px; font-size:15px;">排序:</label>
<select id="bce-sort-by" style="padding:10px 15px; border:2px solid #e0e7ff; border-radius:8px; font-size:14px;">
<option value="post_date" <?php selected($current_sort, 'post_date'); ?>>发布日期</option>
<option value="modified" <?php selected($current_sort, 'modified'); ?>>修改日期</option>
</select>
</div>
<div style="display:flex; align-items:center; gap:15px;">
<label style="font-weight:600; color:#1e40af; margin-right:8px; font-size:15px;">升序:</label>
<input type="radio" name="bce-sort-order" value="asc" <?php checked($current_order, 'asc'); ?> style="width:18px; height:18px; cursor:pointer;">
<label style="font-weight:600; color:#1e40af; margin-left:15px; margin-right:8px; font-size:15px;">降序:</label>
<input type="radio" name="bce-sort-order" value="desc" <?php checked($current_order, 'desc'); ?> style="width:18px; height:18px; cursor:pointer;">
</div>
<div>
<button id="bce-search-btn" style="padding:10px 25px; background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%); color:white; border:none; border-radius:8px; cursor:pointer; font-weight:600; font-size:15px; transition:all 0.3s ease; box-shadow:0 4px 12px rgba(59, 130, 246, 0.15);">
搜索
</button>
</div>
</div>
</div>
<!-- 列表区域 -->
<div style="display:flex; gap:25px;">
<!-- 文章列表 -->
<div style="flex:1; background:white; padding:20px; border-radius:10px; box-shadow:0 2px 10px rgba(0, 0, 0, 0.03);">
<!-- 选择文章标题 + 分页(移到标题后方) -->
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h2 style="color:#1e40af; font-size:20px; margin:0; font-weight:600; padding-bottom:10px; border-bottom:2px solid #e0e7ff;">选择文章</h2>
<?php if ($query->max_num_pages > 1) : ?>
<div style="padding:8px 15px; background:#f8fafc; border-radius:8px;"><?php echo paginate_links([
'base' => add_query_arg('paged', '%#%'),
'total' => $query->max_num_pages,
'current' => $paged,
'prev_text' => '← 上一页',
'next_text' => '下一页 →',
'mid_size' => 2,
'prev_next' => true
]); ?></div>
<?php endif; ?>
</div>
<table class="widefat" style="width:100%; border-collapse:collapse; border-radius:8px; overflow:hidden; box-shadow:0 2px 8px rgba(0,0,0,0.05);">
<thead>
<tr style="background: linear-gradient(135deg, #e0f2fe 0%, #f0f7ff 100%);">
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-size:15px;"><input type="checkbox" id="bce-select-all-posts" style="width:18px; height:18px; cursor:pointer;"></th>
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-weight:600; color:#1e40af; font-size:15px;">标题</th>
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-weight:600; color:#1e40af; font-size:15px;">当前分类</th>
</tr>
</thead>
<tbody>
<?php if (empty($posts)) : ?>
<tr><td colspan="3" style="padding:30px; text-align:center; border:1px solid #e0e7ff; font-size:16px; color:#64748b;">暂无文章</td></tr>
<?php else : ?>
<?php foreach ($posts as $i => $post) : ?>
<tr style="<?php echo $i%2 ? 'background:#f8fafc;' : 'background:white;'; ?>">
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px;"><input type="checkbox" class="bce-post-check" name="post_ids[]" value="<?php echo $post->ID; ?>" style="width:16px; height:16px; cursor:pointer;"></td>
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px;"><a href="<?php echo get_edit_post_link($post->ID); ?>" style="color:#2563eb; text-decoration:none; font-weight:500;"><?php echo esc_html($post->post_title ?: '无标题'); ?></a></td>
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px; color:#475569;">
<?php
$cats = wp_get_post_categories($post->ID);
$cat_names = [];
foreach ($cats as $cat_id) {
$cat = get_category($cat_id);
if ($cat) $cat_names[] = $cat->name;
}
echo empty($cat_names) ? '<span style="color:#94a3b8; font-style:italic;">未分类</span>' : implode('、', $cat_names);
?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- 分类列表 - 新增分类ID列 -->
<div style="flex:1; background:white; padding:20px; border-radius:10px; box-shadow:0 2px 10px rgba(0, 0, 0, 0.03);">
<h2 style="color:#1e40af; font-size:20px; margin-bottom:20px; font-weight:600; margin-top:0; padding-bottom:10px; border-bottom:2px solid #e0e7ff;">选择分类</h2>
<div style="margin-bottom:20px; padding:20px; background:#f8fafc; border-radius:10px; box-shadow:0 2px 8px rgba(0,0,0,0.05);">
<button id="bce-set-btn" style="padding:12px 25px; background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%); color:white; border:none; border-radius:8px; cursor:pointer; margin-right:12px; font-weight:600; font-size:15px; transition:all 0.3s ease; box-shadow:0 4px 12px rgba(59, 130, 246, 0.15);">
更改文章分类
</button>
<button id="bce-add-btn" style="padding:12px 25px; background: linear-gradient(135deg, #10b981 0%, #34d399 100%); color:white; border:none; border-radius:8px; cursor:pointer; margin-right:12px; font-weight:600; font-size:15px; transition:all 0.3s ease; box-shadow:0 4px 12px rgba(16, 185, 129, 0.15);">
添加分类
</button>
<button id="bce-del-btn" style="padding:12px 25px; background: linear-gradient(135deg, #ef4444 0%, #f87171 100%); color:white; border:none; border-radius:8px; cursor:pointer; font-weight:600; font-size:15px; transition:all 0.3s ease; box-shadow:0 4px 12px rgba(239, 68, 68, 0.15);">
删除分类
</button>
</div>
<table class="widefat" style="width:100%; border-collapse:collapse; border-radius:8px; overflow:hidden; box-shadow:0 2px 8px rgba(0,0,0,0.05);">
<thead>
<tr style="background: linear-gradient(135deg, #e0f2fe 0%, #f0f7ff 100%);">
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-size:15px;"><input type="checkbox" id="bce-select-all-cats" style="width:18px; height:18px; cursor:pointer;"></th>
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-weight:600; color:#1e40af; font-size:15px;">分类名称</th>
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-weight:600; color:#1e40af; font-size:15px;">分类ID</th>
<th style="padding:12px 15px; border:1px solid #e0e7ff; font-weight:600; color:#1e40af; font-size:15px;">文章数量</th>
</tr>
</thead>
<tbody>
<?php if (empty($categories)) : ?>
<tr><td colspan="4" style="padding:30px; text-align:center; border:1px solid #e0e7ff; font-size:16px; color:#64748b;">暂无分类</td></tr>
<?php else : ?>
<?php foreach ($categories as $i => $cat) : ?>
<tr style="<?php echo $i%2 ? 'background:#f8fafc;' : 'background:white;'; ?>">
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px;"><input type="checkbox" class="bce-cat-check" name="cat_ids[]" value="<?php echo $cat->term_id; ?>" style="width:16px; height:16px; cursor:pointer;"></td>
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px;"><a href="<?php echo get_edit_term_link($cat->term_id, 'category'); ?>" style="color:#2563eb; text-decoration:none; font-weight:500;"><?php echo esc_html($cat->name); ?></a></td>
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px; color:#7c3aed; font-weight:600;"><?php echo $cat->term_id; ?></td>
<td style="padding:12px 15px; border:1px solid #e0e7ff; font-size:14px; color:#10b981; font-weight:600;"><?php echo $cat->count; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<!-- 设置区域 -->
<div id="bce-tab-settings" class="bce-tab-content" style="display:none; background: linear-gradient(180deg, #f5fafe 0%, #eaf6fa 100%); padding:30px; border-radius:12px; box-shadow:0 4px 20px rgba(0, 0, 0, 0.05); border:1px solid #e0f2fe;">
<div style="max-width:800px; margin:0 auto; background:white; padding:40px; border-radius:12px; box-shadow:0 5px 20px rgba(0,0,0,0.06);">
<h2 style="color:#1e40af; font-size:24px; margin-bottom:30px; font-weight:700; padding-bottom:20px; border-bottom:2px solid #e0f2fe; text-align:center;">插件设置</h2>
<form method="post" action="options.php" style="width:100%;">
<?php settings_fields('bce_options_group'); ?>
<div style="background: linear-gradient(180deg, #f5fafe 0%, #eaf6fa 100%); padding:30px; border-radius:10px; margin-bottom:30px; border:1px solid #e0f2fe;">
<h3 style="color:#1e40af; font-size:18px; margin-top:0; margin-bottom:20px; font-weight:600;">显示设置</h3>
<div style="display:flex; align-items:center; gap:20px; flex-wrap:wrap;">
<label style="font-weight:600; color:#1e293b; font-size:16px; min-width:180px;">
每页显示文章数量
</label>
<div style="flex:1; min-width:200px;">
<input type="number" name="bce_options[posts_per_page]"
value="<?php echo esc_attr($options['posts_per_page'] ?? 15); ?>"
min="1" max="100" step="1"
style="padding:14px 20px; border:2px solid #e0e7ff; border-radius:8px; width:120px; font-size:16px;"
onfocus="this.style.borderColor='#3b82f6'; this.style.boxShadow='0 0 0 4px rgba(59, 130, 246, 0.1)';"
onblur="this.style.borderColor='#e0e7ff'; this.style.boxShadow='none';" />
<div style="margin-top:12px; color:#64748b; font-size:14px; line-height:1.6;">
<p style="margin:0;">建议值:10-50</p>
<p style="margin:5px 0 0 0;">数值过大会影响页面加载速度,过小则需要频繁翻页</p>
</div>
</div>
</div>
</div>
<div style="text-align:center; margin-top:20px;">
<?php submit_button('保存设置', 'primary', '', false, [
'style' => 'padding:14px 40px; background: linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%); color:white; border:none; border-radius:8px; font-size:18px; font-weight:600; cursor:pointer; transition:all 0.3s ease; box-shadow:0 6px 16px rgba(59, 130, 246, 0.2);'
]); ?>
</div>
</form>
<div style="margin-top:30px; padding:20px; background:#f0f7ff; border-radius:8px; border-left:4px solid #3b82f6;">
<h4 style="color:#1e40af; font-size:16px; margin-top:0; margin-bottom:10px; font-weight:600;">使用提示</h4>
<ul style="margin:0; padding-left:20px; color:#475569; font-size:14px; line-height:1.8;">
<li>修改设置后需要点击"保存设置"按钮生效</li>
<li>建议根据服务器性能调整每页显示数量</li>
<li>所有操作都会实时生效,请谨慎选择文章和分类</li>
</ul>
</div>
</div>
</div>
</div>
<!-- JS脚本(功能完整) -->
<script>
jQuery(document).ready(function($) {
// 标签页切换
$('.bce-tab-btn').click(function() {
$('.bce-tab-btn').removeClass('active').css({
background: '#f8fafc',
color: '#1e40af',
border: '1px solid #dbeafe',
boxShadow: 'none'
});
$(this).addClass('active').css({
background: 'linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)',
color: 'white',
border: 'none',
boxShadow: '0 4px 12px rgba(59, 130, 246, 0.15)'
});
$('.bce-tab-content').hide();
$('#bce-tab-' + $(this).data('tab')).show();
});
// 全选功能
$('#bce-select-all-posts').click(function() {
$('.bce-post-check').prop('checked', this.checked);
});
$('#bce-select-all-cats').click(function() {
$('.bce-cat-check').prop('checked', this.checked);
});
// 搜索功能
$('#bce-search-btn').click(function() {
const cat = $('#bce-cat-filter').val();
const keyword = $('#bce-keyword').val();
const sort = $('#bce-sort-by').val();
const order = $('input[name="bce-sort-order"]:checked').val();
let url = '<?php echo admin_url('tools.php?page=batch-category-editor'); ?>';
const params = [];
if (cat && cat != -1) params.push('cat=' + cat);
if (keyword) params.push('s=' + encodeURIComponent(keyword));
if (sort) params.push('sort=' + sort);
if (order) params.push('order=' + order);
if (params.length) url += '&' + params.join('&');
window.location.href = url;
});
// 提示框
function showToast(msg, isSuccess = true) {
const toast = $('<div style="position:fixed; top:30px; right:30px; padding:18px 25px; border-radius:10px; background:white; box-shadow:0 8px 24px rgba(0,0,0,0.12); z-index:9999; display:flex; align-items:center; gap:12px; border-left:4px solid #22c55e;"></div>');
const icon = isSuccess ? '<span style="color:#22c55e; font-size:24px; font-weight:bold;">✓</span>' : '<span style="color:#ef4444; font-size:24px; font-weight:bold;">✕</span>';
if (!isSuccess) toast.css('border-left-color', '#ef4444');
toast.html(icon + '<span style="font-size:16px; color:#1e293b;">' + msg + '</span>');
$('body').append(toast);
toast.hide().fadeIn(300);
setTimeout(() => toast.fadeOut(300, () => toast.remove()), 4000);
}
// 加载动画
function showLoader() {
const loader = $('<div style="position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(255,255,255,0.9); z-index:9998; display:flex; justify-content:center; align-items:center;"><div style="background:white; padding:40px; border-radius:12px; text-align:center; box-shadow:0 10px 30px rgba(0,0,0,0.1);"><div style="width:60px; height:60px; border:6px solid #f0f7ff; border-top:6px solid #3b82f6; border-radius:50%; animation:spin 1s linear infinite; margin:0 auto;"></div><p style="margin-top:25px; font-size:18px; color:#1e40af; font-weight:600;">处理中...</p></div></div>');
$('body').append(loader);
$('head').append('<style>@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }</style>');
return loader;
}
// 分类操作
function doAction(action) {
const postIds = $('.bce-post-check:checked').map(function() {
return $(this).val();
}).get();
const catIds = $('.bce-cat-check:checked').map(function() {
return $(this).val();
}).get();
if (postIds.length === 0) {
showToast('请选择至少一篇文章', false);
return;
}
if (catIds.length === 0) {
showToast('请选择至少一个分类', false);
return;
}
const loader = showLoader();
$.ajax({
url: '<?php echo admin_url('admin-ajax.php'); ?>',
type: 'POST',
dataType: 'json',
data: {
action: 'bce_' + action + '_cat',
post_ids: postIds.join(','),
cat_ids: catIds.join(',')
},
success: function(res) {
if (res && res.success) {
showToast(res.data.msg);
setTimeout(() => window.location.reload(), 1500);
} else {
showToast(res ? (res.data.msg || '操作失败') : '操作失败', false);
}
},
error: function() {
showToast('网络错误,请重试', false);
},
complete: function() {
loader.remove();
}
});
}
// 绑定按钮
$('#bce-set-btn').click(function() { doAction('set'); });
$('#bce-add-btn').click(function() { doAction('add'); });
$('#bce-del-btn').click(function() { doAction('del'); });
});
</script>
<?php
}
// ===================== AJAX处理函数(功能完整) =====================
function bce_ajax_set_cat() {
if (!current_user_can('edit_posts')) {
wp_send_json_error(['msg' => '权限不足']);
}
$post_ids = isset($_POST['post_ids']) ? array_map('intval', explode(',', sanitize_text_field($_POST['post_ids']))) : [];
$cat_ids = isset($_POST['cat_ids']) ? array_map('intval', explode(',', sanitize_text_field($_POST['cat_ids']))) : [];
if (empty($post_ids) || empty($cat_ids)) {
wp_send_json_error(['msg' => '请选择文章和分类']);
}
foreach ($post_ids as $post_id) {
wp_set_post_categories($post_id, $cat_ids);
}
wp_send_json_success(['msg' => '成功更改 ' . count($post_ids) . ' 篇文章的分类']);
}
function bce_ajax_add_cat() {
if (!current_user_can('edit_posts')) {
wp_send_json_error(['msg' => '权限不足']);
}
$post_ids = isset($_POST['post_ids']) ? array_map('intval', explode(',', sanitize_text_field($_POST['post_ids']))) : [];
$cat_ids = isset($_POST['cat_ids']) ? array_map('intval', explode(',', sanitize_text_field($_POST['cat_ids']))) : [];
if (empty($post_ids) || empty($cat_ids)) {
wp_send_json_error(['msg' => '请选择文章和分类']);
}
foreach ($post_ids as $post_id) {
$current_cats = wp_get_post_categories($post_id);
$new_cats = array_unique(array_merge($current_cats, $cat_ids));
wp_set_post_categories($post_id, $new_cats);
}
wp_send_json_success(['msg' => '成功为 ' . count($post_ids) . ' 篇文章添加分类']);
}
function bce_ajax_del_cat() {
if (!current_user_can('edit_posts')) {
wp_send_json_error(['msg' => '权限不足']);
}
$post_ids = isset($_POST['post_ids']) ? array_map('intval', explode(',', sanitize_text_field($_POST['post_ids']))) : [];
$cat_ids = isset($_POST['cat_ids']) ? array_map('intval', explode(',', sanitize_text_field($_POST['cat_ids']))) : [];
if (empty($post_ids) || empty($cat_ids)) {
wp_send_json_error(['msg' => '请选择文章和分类']);
}
$default_cat = get_option('default_category');
foreach ($post_ids as $post_id) {
$current_cats = wp_get_post_categories($post_id);
$new_cats = array_diff($current_cats, $cat_ids);
if (empty($new_cats)) {
$new_cats = [$default_cat];
}
wp_set_post_categories($post_id, $new_cats);
}
wp_send_json_success(['msg' => '成功从 ' . count($post_ids) . ' 篇文章中移除分类']);
}
// ===================== 插件停用清理 =====================
register_deactivation_hook(__FILE__, function() {
global $wpdb;
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'bce_%'");
delete_transient('wp_menu_cache');
wp_cache_flush();
});
?>
[/hidecontent]
评论一下?