<?php
// 功能：栏目管理文件
// 作者：启肯科技
// Q Q：328493711
// 微信：13738373177（同手机）
// 官网：http://www.qiken.com
// 说明：QKCMS-轻量级AI智能内容管理建站系统
// 版权：© 2025 温州启肯网络有限公司 保留所有权利，盗版必究。



// 先引入common.php文件
if (!defined('COMMON_LOADED')) {
    require_once '../common.php';
}

// 检查访问权限
if (!defined('ADMIN_LOADED')) {
    // 允许POST请求
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (!defined('COMMON_LOADED')) {
            define('COMMON_LOADED', true);
        }
        // 初始化数据库连接
        global $conn;
        $conn = get_db_connection();
        define('ADMIN_LOADED', true);
    } else {
        header('HTTP/1.1 403 Forbidden');
        exit('Access Denied');
    }
}

if (!isset($_SESSION)) {
    start_admin_session();
}

if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
    global $conn;
    $conn = get_db_connection();
}

$catgroup_table = 'catgroup';
$catgroup_exists = false;
$check_catgroup_table = $conn->query("SHOW TABLES LIKE 'catgroup'");
if ($check_catgroup_table && $check_catgroup_table->num_rows > 0) {
    $catgroup_exists = true;
}
if ($check_catgroup_table) $check_catgroup_table->close();
if (!$catgroup_exists) {
    $check_qk_catgroup_table = $conn->query("SHOW TABLES LIKE 'qkcms_catgroup'");
    if ($check_qk_catgroup_table && $check_qk_catgroup_table->num_rows > 0) {
        $catgroup_table = 'qkcms_catgroup';
        $catgroup_exists = true;
    }
    if ($check_qk_catgroup_table) $check_qk_catgroup_table->close();
}

if (!function_exists('qk_get_adcategory_category_templates')) {
    function qk_get_adcategory_category_templates()
    {
        if (function_exists('qk_get_template_options_by_page_type')) {
            $template_files = qk_get_template_options_by_page_type('list');
            foreach ($template_files as $path => $name) {
                if (strpos($path, 'skin/') !== 0 && strpos($name, '（默认）') !== false) {
                    $template_files[$path] = '0_' . ltrim((string)$name, '0_');
                }
            }
            return $template_files;
        }
        return array('list.html' => 'list.html（默认）');
    }
}

if (!function_exists('qk_get_adcategory_catgroup_templates')) {
    function qk_get_adcategory_catgroup_templates()
    {
        if (function_exists('qk_get_template_options_by_page_type')) {
            $template_files = qk_get_template_options_by_page_type('catgroup');
            foreach ($template_files as $path => $name) {
                if (strpos($path, 'skin/') !== 0 && strpos($name, '（默认）') !== false) {
                    $template_files[$path] = '0_' . ltrim((string)$name, '0_');
                }
            }
            return $template_files;
        }
        return array('catgroup.html' => 'catgroup.html（默认）');
    }
}

if (!function_exists('qk_get_adcategory_client_categories')) {
    function qk_get_adcategory_client_categories($categories)
    {
        if (!is_array($categories)) {
            return array();
        }

        return array_map(function ($cat) {
            return array(
                'id' => $cat['id'],
                'name' => $cat['name'],
                'archiveid' => isset($cat['archiveid']) ? intval($cat['archiveid']) : 0,
                'virtual_top' => isset($cat['virtual_top']) ? intval($cat['virtual_top']) : 0,
                'virtual_top_anchor' => isset($cat['virtual_top_anchor']) ? intval($cat['virtual_top_anchor']) : 0,
                'sort' => isset($cat['sort']) ? intval($cat['sort']) : 0,
                'thumbnail' => $cat['thumbnail'] ?? '',
                'template' => $cat['template'] ?? '',
                'custom_url' => $cat['custom_url'] ?? '',
                'display' => $cat['display'] ?? 0,
                'post_count' => $cat['post_count'] ?? 0
            );
        }, $categories);
    }
}

if (!function_exists('qk_get_adcategory_tree_payload')) {
    function qk_get_adcategory_tree_payload($conn, $catgroup_table, $search = '', $page = 1, $page_size = 30, $archiveid = 0)
    {
        $all_categories = getCategories('');
        if (!is_array($all_categories)) $all_categories = array();

        if ($archiveid > 0) {
            $all_categories = array_values(array_filter($all_categories, function ($cat) use ($archiveid) {
                return intval($cat['archiveid'] ?? 0) === $archiveid;
            }));
        } else {
            $all_categories = array_values($all_categories);
        }

        $matched_group_ids = array();
        $visible_group_ids = array();
        if ($search !== '') {
            $like = '%' . $conn->real_escape_string($search) . '%';
            $group_result = $conn->query("SELECT id FROM `{$catgroup_table}` WHERE name LIKE '{$like}'");
            if ($group_result) {
                while ($gr = $group_result->fetch_assoc()) {
                    $gid = intval($gr['id']);
                    if ($archiveid > 0 && $gid !== $archiveid) continue;
                    $matched_group_ids[] = $gid;
                }
                $group_result->close();

                $all_categories = array_values(array_filter($all_categories, function ($cat) use ($search, $matched_group_ids, &$visible_group_ids) {
                    $aid = intval($cat['archiveid'] ?? 0);
                    $name = (string)($cat['name'] ?? '');
                    $category_matched = function_exists('mb_stripos')
                        ? mb_stripos($name, $search, 0, 'UTF-8') !== false
                        : stripos($name, $search) !== false;
                    $group_matched = $aid > 0 && in_array($aid, $matched_group_ids, true);

                    if ($category_matched && $aid > 0 && !in_array($aid, $visible_group_ids, true)) {
                        $visible_group_ids[] = $aid;
                    }
                    if ($group_matched && !in_array($aid, $visible_group_ids, true)) {
                        $visible_group_ids[] = $aid;
                    }

                    return $category_matched || $group_matched;
                }));

                foreach ($matched_group_ids as $gid) {
                    if (!in_array($gid, $visible_group_ids, true)) {
                        $visible_group_ids[] = $gid;
                    }
                }
            }
        }

        $page = max(1, intval($page));
        $page_size = max(1, min(1000, intval($page_size)));

        $archive_category_counts = array(0 => 0);
        $archive_post_counts = array(0 => 0);
        $categories_by_group = array(0 => array());
        foreach ($all_categories as $cat) {
            $aid = isset($cat['archiveid']) ? intval($cat['archiveid']) : 0;
            if (!isset($archive_category_counts[$aid])) $archive_category_counts[$aid] = 0;
            if (!isset($archive_post_counts[$aid])) $archive_post_counts[$aid] = 0;
            if (!isset($categories_by_group[$aid])) $categories_by_group[$aid] = array();
            $archive_category_counts[$aid]++;
            $archive_post_counts[$aid] += intval($cat['post_count'] ?? 0);
            $archive_post_counts[0] += intval($cat['post_count'] ?? 0);
            $categories_by_group[$aid][] = $cat;
        }

        $groups = array();
        $groups_sql = "SELECT * FROM `{$catgroup_table}`";
        if ($search !== '') {
            if (!empty($visible_group_ids)) {
                $ids_str = implode(',', array_map('intval', $visible_group_ids));
                $groups_sql .= " WHERE id IN ({$ids_str})";
            } else {
                $groups_sql .= " WHERE 1=0";
            }
        } else if ($archiveid > 0) {
            $groups_sql .= " WHERE id = " . intval($archiveid);
        }
        $groups_sql .= " ORDER BY sort ASC, id ASC";
        $result = execute_query($conn, $groups_sql);
        if ($result) {
            while ($row = $result->fetch_assoc()) {
                $gid = intval($row['id']);
                $row['category_count'] = isset($archive_category_counts[$gid]) ? intval($archive_category_counts[$gid]) : 0;
                $row['post_count_total'] = isset($archive_post_counts[$gid]) ? intval($archive_post_counts[$gid]) : 0;
                if (empty($row['color']) || !preg_match('/^#[0-9a-fA-F]{6}$/', trim((string)$row['color']))) {
                    $row['color'] = '#64748b';
                }
                $groups[] = $row;
            }
            $result->close();
        }

        $ordered_page_group_ids = array();
        if (!empty($categories_by_group[0])) {
            $ordered_page_group_ids[] = 0;
        }
        foreach ($groups as $group) {
            $gid = intval($group['id']);
            if (!isset($categories_by_group[$gid])) $categories_by_group[$gid] = array();
            $ordered_page_group_ids[] = $gid;
        }

        $group_pages = array();
        $current_group_page = array();
        $current_group_units = 0;
        foreach ($ordered_page_group_ids as $gid) {
            $group_units = 1;
            if (!empty($current_group_page) && ($current_group_units + $group_units) > $page_size) {
                $group_pages[] = $current_group_page;
                $current_group_page = array();
                $current_group_units = 0;
            }
            $current_group_page[] = $gid;
            $current_group_units += $group_units;
        }
        if (!empty($current_group_page)) {
            $group_pages[] = $current_group_page;
        }
        if (empty($group_pages)) {
            $group_pages[] = array();
        }

        $total = count($ordered_page_group_ids);
        $total_pages = count($group_pages);
        if ($page > $total_pages) $page = $total_pages;
        $page_group_ids = $group_pages[$page - 1] ?? array();
        $page_group_lookup = array();
        foreach ($page_group_ids as $gid) {
            $page_group_lookup[intval($gid)] = true;
        }

        $page_categories = array_values(array_filter($all_categories, function ($cat) use ($page_group_lookup) {
            $aid = isset($cat['archiveid']) ? intval($cat['archiveid']) : 0;
            if (isset($page_group_lookup[$aid])) return true;
            $is_virtual = intval($cat['virtual_top'] ?? 0) === 1;
            if (!$is_virtual) return false;
            $anchor = intval($cat['virtual_top_anchor'] ?? 0);
            $key = abs($anchor);
            return isset($page_group_lookup[$key]);
        }));
        $groups = array_values(array_filter($groups, function ($group) use ($page_group_lookup) {
            return isset($page_group_lookup[intval($group['id'] ?? 0)]);
        }));

        return array(
            'categories' => $page_categories,
            'catgroups' => $groups,
            'templates' => qk_get_adcategory_category_templates(),
            'catgroup_templates' => qk_get_adcategory_catgroup_templates(),
            'total' => $total,
            'total_pages' => $total_pages,
            'current_page' => $page,
            'page_size' => $page_size
        );
    }
}

// 处理分类删除请求
if (isset($_GET['delete_category'])) {
    $category_id = intval($_GET['delete_category']);
    $is_ajax = isset($_GET['ajax']) && $_GET['ajax'] == '1';

    if ($is_ajax) {
        header('Content-Type: application/json');
    }

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (deleteCategory($category_id)) {
        if ($is_ajax) {
            echo json_encode(['success' => true, 'message' => '分类已成功删除']);
            exit;
        }
        echo '分类已成功删除';
        exit;
    } else {
        if ($is_ajax) {
            echo json_encode(['success' => false, 'message' => '分类删除失败']);
            exit;
        }
        echo '分类删除失败';
        exit;
    }
}

// 处理更新分类显示状态请求
if (isset($_POST['update_category_display']) || (isset($_POST['category_id']) && isset($_POST['display']))) {
    $is_ajax = isset($_POST['is_ajax']) || isset($_GET['ajax']);

    if ($is_ajax) {
        header('Content-Type: application/json');
    }

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    $category_id = intval($_POST['category_id']);
    $display = intval($_POST['display']);
    if (updateCategoryDisplayStatus($category_id, $display)) {
        if ($is_ajax) {
            echo json_encode(['success' => true, 'message' => '状态更新成功']);
            exit;
        }
        echo 'success';
        exit;
    } else {
        if ($is_ajax) {
            echo json_encode(['success' => false, 'message' => '状态更新失败']);
            exit;
        }
        echo 'error';
        exit;
    }
}

// 处理更新分类模板请求
if (isset($_GET['action']) && $_GET['action'] === 'update_category_template') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_POST['category_id']) || !isset($_POST['template'])) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }
    $category_id = intval($_POST['category_id']);
    $template = trim($_POST['template']);
    // 验证模板文件名格式（支持多级目录路径）
    $template_filename = basename($template);
    $template_page_type = function_exists('qk_get_template_page_type') ? qk_get_template_page_type($template_filename) : false;
    if (!preg_match('/^(skin\/)?([a-zA-Z0-9_-]+\/)*[a-zA-Z0-9_-]+\.(html|php)$/', $template) || $template_page_type !== 'list') {
        echo json_encode(['success' => false, 'message' => '模板文件名格式错误']);
        exit;
    }
    $result = execute_non_query($conn, "UPDATE categories SET template = ? WHERE id = ?", array($template, $category_id));
    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '模板更新成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '模板更新失败']);
    }
    exit;
}

// 处理更新顶级栏目模板请求
if (isset($_GET['action']) && $_GET['action'] === 'update_catgroup_template') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_POST['id']) || !isset($_POST['template'])) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }
    $id = intval($_POST['id']);
    $template = trim($_POST['template']);
    if ($id <= 0) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }
    $template_filename = basename($template);
    $template_page_type = function_exists('qk_get_template_page_type') ? qk_get_template_page_type($template_filename) : false;
    if (!preg_match('/^(skin\/)?([a-zA-Z0-9_-]+\/)*[a-zA-Z0-9_-]+\.(html|php)$/', $template) || $template_page_type !== 'catgroup') {
        echo json_encode(['success' => false, 'message' => '模板文件名格式错误']);
        exit;
    }
    $result = execute_non_query($conn, "UPDATE `{$catgroup_table}` SET template = ? WHERE id = ?", array($template, $id));
    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '顶级模板更新成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '顶级模板更新失败']);
    }
    exit;
}

// 处理更新分类自定义URL请求
if (isset($_GET['action']) && $_GET['action'] === 'update_category_custom_url') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_POST['category_id'])) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }
    $category_id = intval($_POST['category_id']);
    $custom_url = isset($_POST['custom_url']) ? trim($_POST['custom_url']) : '';

    // 验证URL格式
    if ($custom_url && !preg_match('/^[a-zA-Z0-9_\/.-]+$/', $custom_url)) {
        echo json_encode(['success' => false, 'message' => 'URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠']);
        exit;
    }

    // 检查URL是否与其他分类重复
    if ($custom_url) {
        $check_stmt = $conn->prepare("SELECT id FROM categories WHERE custom_url = ? AND id != ? LIMIT 1");
        $check_stmt->bind_param('si', $custom_url, $category_id);
        $check_stmt->execute();
        $check_result = $check_stmt->get_result();
        if ($check_result->num_rows > 0) {
            echo json_encode(['success' => false, 'message' => '该URL已被其他分类使用']);
            exit;
        }
        $check_stmt->close();

        // 检查是否与文章URL重复
        $check_stmt = $conn->prepare("SELECT id FROM posts WHERE custom_url = ? LIMIT 1");
        $check_stmt->bind_param('s', $custom_url);
        $check_stmt->execute();
        $check_result = $check_stmt->get_result();
        if ($check_result->num_rows > 0) {
            echo json_encode(['success' => false, 'message' => '该URL已被文章使用']);
            exit;
        }
        $check_stmt->close();
    }

    // 更新自定义URL
    $result = execute_non_query($conn, "UPDATE categories SET custom_url = ? WHERE id = ?", array($custom_url ?: null, $category_id));
    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '自定义URL更新成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '自定义URL更新失败：' . $conn->error]);
    }
    exit;
}

// 处理更新顶级栏目自定义URL请求
if (isset($_GET['action']) && $_GET['action'] === 'update_catgroup_custom_url') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_POST['id'])) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }
    $id = intval($_POST['id']);
    $custom_url = isset($_POST['custom_url']) ? trim($_POST['custom_url']) : '';
    if ($id <= 0) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    if ($custom_url !== '') {
        if (!preg_match('/^[a-zA-Z0-9_\/.-]+$/', $custom_url)) {
            echo json_encode(['success' => false, 'message' => 'URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠']);
            exit;
        }
    }

    if ($custom_url !== '') {
        $check_stmt = $conn->prepare("SELECT id FROM `{$catgroup_table}` WHERE custom_url = ? AND id != ? LIMIT 1");
        if ($check_stmt) {
            $check_stmt->bind_param('si', $custom_url, $id);
            $check_stmt->execute();
            $check_result = $check_stmt->get_result();
            if ($check_result && $check_result->num_rows > 0) {
                echo json_encode(['success' => false, 'message' => '该URL已被其他顶级栏目使用']);
                $check_stmt->close();
                exit;
            }
            $check_stmt->close();
        }
    }

    $result = execute_non_query($conn, "UPDATE `{$catgroup_table}` SET custom_url = ? WHERE id = ?", array($custom_url ?: null, $id));
    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '顶级URL更新成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '顶级URL更新失败']);
    }
    exit;
}

// 处理分类缩略图更新
if (isset($_POST['update_category_thumbnail']) && isset($_POST['category_id']) && isset($_POST['thumbnail'])) {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足，需要管理员权限']);
        exit;
    }

    $category_id = intval($_POST['category_id']);
    $thumbnail = $_POST['thumbnail'];

    // 处理临时文件
    if (strpos($thumbnail, '/uploads/Temp/') !== false) {
        $parsedUrl = parse_url($thumbnail);
        if (isset($parsedUrl['path'])) {
            $fileName = basename($parsedUrl['path']);
            if (!empty($fileName)) {
                $tempPath = TEMP_UPLOAD_DIR . $fileName;
                $finalPath = UPLOAD_DIR . $fileName;
                if (file_exists($tempPath)) {
                    if (rename($tempPath, $finalPath)) {
                        $thumbnail = qk_build_relative_upload_path($fileName);
                    } else if (copy($tempPath, $finalPath)) {
                        unlink($tempPath);
                        $thumbnail = qk_build_relative_upload_path($fileName);
                    }
                }
            }
        }
    }

    // 更新数据库
    $result = execute_non_query($conn, "UPDATE categories SET thumbnail = ? WHERE id = ?", array($thumbnail, $category_id));

    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '分类缩略图更新成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '分类缩略图更新失败']);
    }
    exit;
}

// ============================================
// 归档管理AJAX处理
// ============================================

// 添加归档
if (isset($_POST['action']) && $_POST['action'] === 'add_catgroup') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $name = isset($_POST['name']) ? trim($_POST['name']) : '';
    if (empty($name)) {
        echo json_encode(['success' => false, 'message' => '顶级栏目名称不能为空']);
        exit;
    }
    // 检查名称是否重复
    $check_result = execute_query($conn, "SELECT id FROM `{$catgroup_table}` WHERE name = ? LIMIT 1", array($name));
    if ($check_result && $check_result->num_rows > 0) {
        echo json_encode(['success' => false, 'message' => '顶级栏目名称不可重复']);
        exit;
    }

    $max_sort = execute_query($conn, "SELECT MAX(sort) as max_sort FROM `{$catgroup_table}`");
    $sort_row = $max_sort ? $max_sort->fetch_assoc() : array('max_sort' => 0);
    $default_sort = intval($sort_row['max_sort'] ?? 0) + 1;
    $sort = isset($_POST['sort']) ? intval($_POST['sort']) : $default_sort;
    if ($sort <= 0) $sort = $default_sort;

    $color = isset($_POST['color']) ? trim($_POST['color']) : '#64748b';
    if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) $color = '#64748b';

    $template = isset($_POST['template']) ? trim($_POST['template']) : 'catgroup.html';
    if ($template === '') $template = 'catgroup.html';
    $template_filename = basename($template);
    $template_page_type = function_exists('qk_get_template_page_type') ? qk_get_template_page_type($template_filename) : false;
    if (!preg_match('/^(skin\/)?([a-zA-Z0-9_-]+\/)*[a-zA-Z0-9_-]+\.(html|php)$/', $template) || $template_page_type !== 'catgroup') {
        echo json_encode(['success' => false, 'message' => '模板格式不正确']);
        exit;
    }

    $custom_url = isset($_POST['custom_url']) ? trim($_POST['custom_url']) : '';
    if ($custom_url !== '' && !preg_match('/^[a-zA-Z0-9_\/.-]+$/', $custom_url)) {
        echo json_encode(['success' => false, 'message' => 'URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠']);
        exit;
    }
    if ($custom_url !== '') {
        $check_custom_stmt = $conn->prepare("SELECT id FROM `{$catgroup_table}` WHERE custom_url = ? LIMIT 1");
        if ($check_custom_stmt) {
            $check_custom_stmt->bind_param('s', $custom_url);
            $check_custom_stmt->execute();
            $custom_result = $check_custom_stmt->get_result();
            if ($custom_result && $custom_result->num_rows > 0) {
                $check_custom_stmt->close();
                echo json_encode(['success' => false, 'message' => 'URL已存在，请更换']);
                exit;
            }
            $check_custom_stmt->close();
        }
    }

    $seo_title = isset($_POST['seo_title']) ? trim($_POST['seo_title']) : '';
    $seo_keywords = isset($_POST['seo_keywords']) ? trim($_POST['seo_keywords']) : '';
    $seo_description = isset($_POST['seo_description']) ? trim($_POST['seo_description']) : '';

    $insert_custom_url = $custom_url !== '' ? $custom_url : null;
    $insert_seo_title = $seo_title !== '' ? $seo_title : null;
    $insert_seo_keywords = $seo_keywords !== '' ? $seo_keywords : null;
    $insert_seo_description = $seo_description !== '' ? $seo_description : null;

    $result = execute_non_query($conn, "INSERT INTO `{$catgroup_table}` (name, color, sort, custom_url, seo_title, seo_keywords, seo_description, template) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", array($name, $color, $sort, $insert_custom_url, $insert_seo_title, $insert_seo_keywords, $insert_seo_description, $template));

    if ($result !== false) {
        $new_id = intval($conn->insert_id);
        $final_custom_url = $custom_url !== '' ? $custom_url : ('cg_id' . $new_id . '.html');
        if ($custom_url === '') {
            execute_non_query($conn, "UPDATE `{$catgroup_table}` SET custom_url = ? WHERE id = ?", array($final_custom_url, $new_id));
        }
        qk_clear_page_cache();
        echo json_encode([
            'success' => true,
            'message' => '顶级栏目添加成功',
            'id' => $new_id,
            'color' => $color,
            'template' => $template,
            'custom_url' => $final_custom_url
        ]);
    } else {
        echo json_encode(['success' => false, 'message' => '顶级栏目添加失败']);
    }
    exit;
}

// 编辑归档
if (isset($_POST['action']) && $_POST['action'] === 'edit_catgroup') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $id = isset($_POST['id']) ? intval($_POST['id']) : 0;
    $name = isset($_POST['name']) ? trim($_POST['name']) : '';
    if ($id <= 0 || empty($name)) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }
    // 检查名称是否重复（排除当前编辑的归档）
    $check_result = execute_query($conn, "SELECT id FROM `{$catgroup_table}` WHERE name = ? AND id != ? LIMIT 1", array($name, $id));
    if ($check_result && $check_result->num_rows > 0) {
        echo json_encode(['success' => false, 'message' => '顶级栏目名称不可重复']);
        exit;
    }

    $sort = isset($_POST['sort']) ? intval($_POST['sort']) : 0;
    if ($sort <= 0) $sort = 1;

    $color = isset($_POST['color']) ? trim($_POST['color']) : '';
    if ($color === '') $color = '#64748b';
    if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) {
        echo json_encode(['success' => false, 'message' => '颜色格式不正确']);
        exit;
    }

    $template = isset($_POST['template']) ? trim($_POST['template']) : '';
    if ($template === '') $template = 'catgroup.html';
    $template_filename = basename($template);
    $template_page_type = function_exists('qk_get_template_page_type') ? qk_get_template_page_type($template_filename) : false;
    if (!preg_match('/^(skin\/)?([a-zA-Z0-9_-]+\/)*[a-zA-Z0-9_-]+\.(html|php)$/', $template) || $template_page_type !== 'catgroup') {
        echo json_encode(['success' => false, 'message' => '模板格式不正确']);
        exit;
    }

    $custom_url = isset($_POST['custom_url']) ? trim($_POST['custom_url']) : '';
    if ($custom_url !== '' && !preg_match('/^[a-zA-Z0-9_\/.-]+$/', $custom_url)) {
        echo json_encode(['success' => false, 'message' => 'URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠']);
        exit;
    }
    if ($custom_url !== '') {
        $check_custom_stmt = $conn->prepare("SELECT id FROM `{$catgroup_table}` WHERE custom_url = ? AND id != ? LIMIT 1");
        if ($check_custom_stmt) {
            $check_custom_stmt->bind_param('si', $custom_url, $id);
            $check_custom_stmt->execute();
            $custom_result = $check_custom_stmt->get_result();
            if ($custom_result && $custom_result->num_rows > 0) {
                $check_custom_stmt->close();
                echo json_encode(['success' => false, 'message' => 'URL已存在，请更换']);
                exit;
            }
            $check_custom_stmt->close();
        }
    }

    $seo_title = isset($_POST['seo_title']) ? trim($_POST['seo_title']) : '';
    $seo_keywords = isset($_POST['seo_keywords']) ? trim($_POST['seo_keywords']) : '';
    $seo_description = isset($_POST['seo_description']) ? trim($_POST['seo_description']) : '';

    $update_custom_url = $custom_url !== '' ? $custom_url : null;
    $update_seo_title = $seo_title !== '' ? $seo_title : null;
    $update_seo_keywords = $seo_keywords !== '' ? $seo_keywords : null;
    $update_seo_description = $seo_description !== '' ? $seo_description : null;

    $result = execute_non_query(
        $conn,
        "UPDATE `{$catgroup_table}` SET name = ?, color = ?, sort = ?, custom_url = ?, seo_title = ?, seo_keywords = ?, seo_description = ?, template = ? WHERE id = ?",
        array($name, $color, $sort, $update_custom_url, $update_seo_title, $update_seo_keywords, $update_seo_description, $template, $id)
    );

    if ($result !== false) {
        $final_custom_url = $custom_url !== '' ? $custom_url : ('cg_id' . $id . '.html');
        if ($custom_url === '') {
            execute_non_query($conn, "UPDATE `{$catgroup_table}` SET custom_url = ? WHERE id = ?", array($final_custom_url, $id));
        }
        qk_clear_page_cache();
        echo json_encode([
            'success' => true,
            'message' => '顶级栏目更新成功',
            'id' => $id,
            'color' => $color,
            'sort' => $sort,
            'template' => $template,
            'custom_url' => $final_custom_url
        ]);
    } else {
        echo json_encode(['success' => false, 'message' => '顶级栏目更新失败']);
    }
    exit;
}

// 删除归档
if (isset($_POST['action']) && $_POST['action'] === 'delete_catgroup') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $id = isset($_POST['id']) ? intval($_POST['id']) : 0;
    if ($id <= 0) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    $children_count = 0;
    $children_result = execute_query($conn, "SELECT COUNT(*) AS cnt FROM categories WHERE archiveid = ?", array($id));
    if ($children_result) {
        $children_row = $children_result->fetch_assoc();
        $children_count = intval($children_row['cnt'] ?? 0);
        $children_result->close();
    }
    if (isset($_POST['check_only']) && $_POST['check_only'] == '1') {
        if ($children_count > 0) {
            echo json_encode(['success' => true, 'can_delete' => false, 'message' => '先删除子栏目 才可删除顶级栏目']);
        } else {
            echo json_encode(['success' => true, 'can_delete' => true]);
        }
        exit;
    }
    if ($children_count > 0) {
        echo json_encode(['success' => false, 'message' => '先删除子栏目 才可删除顶级栏目']);
        exit;
    }

    $result = execute_non_query($conn, "DELETE FROM `{$catgroup_table}` WHERE id = ?", array($id));
    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '顶级栏目删除成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '顶级栏目删除失败']);
    }
    exit;
}

if (isset($_POST['action']) && $_POST['action'] === 'batch_delete_selected') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $top_ids_raw = isset($_POST['top_ids']) ? trim((string)$_POST['top_ids']) : '';
    $category_ids_raw = isset($_POST['category_ids']) ? trim((string)$_POST['category_ids']) : '';

    $top_ids = array();
    if ($top_ids_raw !== '') {
        foreach (explode(',', $top_ids_raw) as $v) {
            $id = intval($v);
            if ($id > 0) $top_ids[$id] = $id;
        }
    }
    $top_ids = array_values($top_ids);

    $category_ids = array();
    if ($category_ids_raw !== '') {
        foreach (explode(',', $category_ids_raw) as $v) {
            $id = intval($v);
            if ($id > 0) $category_ids[$id] = $id;
        }
    }
    $category_ids = array_values($category_ids);

    if (empty($top_ids) && empty($category_ids)) {
        echo json_encode(['success' => false, 'message' => '请先选择要删除的栏目']);
        exit;
    }

    $deleted_top_count = 0;
    $deleted_category_count = 0;
    $moved_to_default_count = 0;

    $conn->begin_transaction();
    try {
        $final_delete_category_ids = array();
        foreach ($category_ids as $cid) {
            $final_delete_category_ids[intval($cid)] = intval($cid);
        }

        if (!empty($top_ids)) {
            $top_placeholders = implode(',', array_fill(0, count($top_ids), '?'));
            $children_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid IN ($top_placeholders)", $top_ids);
            $move_ids = array();
            if ($children_result) {
                while ($child_row = $children_result->fetch_assoc()) {
                    $child_id = intval($child_row['id'] ?? 0);
                    if ($child_id <= 0) continue;
                    if (isset($final_delete_category_ids[$child_id])) {
                        continue;
                    }
                    $move_ids[$child_id] = $child_id;
                }
                $children_result->close();
            }

            if (!empty($move_ids)) {
                $move_list = array_values($move_ids);
                $move_placeholders = implode(',', array_fill(0, count($move_list), '?'));
                $move_params = array_merge(array(0), $move_list);
                $move_result = execute_non_query($conn, "UPDATE categories SET archiveid = ? WHERE id IN ($move_placeholders)", $move_params);
                if ($move_result === false) {
                    throw new Exception('子栏目转移到默认栏目失败');
                }
                $moved_to_default_count = count($move_list);
            }

            $delete_top_result = execute_non_query($conn, "DELETE FROM `{$catgroup_table}` WHERE id IN ($top_placeholders)", $top_ids);
            if ($delete_top_result === false) {
                throw new Exception('顶级栏目删除失败');
            }
            $deleted_top_count = count($top_ids);
        }

        $final_delete_category_ids = array_values($final_delete_category_ids);
        foreach ($final_delete_category_ids as $cid) {
            if (!deleteCategory(intval($cid))) {
                throw new Exception('子栏目删除失败');
            }
            $deleted_category_count++;
        }

        $conn->commit();
        qk_clear_page_cache();
        echo json_encode([
            'success' => true,
            'message' => '批量删除成功',
            'deleted_top_count' => $deleted_top_count,
            'deleted_category_count' => $deleted_category_count,
            'moved_to_default_count' => $moved_to_default_count
        ]);
    } catch (Exception $e) {
        $conn->rollback();
        echo json_encode(['success' => false, 'message' => $e->getMessage()]);
    }
    exit;
}

// 更新归档排序
if (isset($_POST['action']) && $_POST['action'] === 'update_catgroup_sort') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $ids = isset($_POST['ids']) ? $_POST['ids'] : [];
    if (!is_array($ids) || empty($ids)) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    $conn->begin_transaction();
    try {
        foreach ($ids as $index => $id) {
            execute_non_query($conn, "UPDATE `{$catgroup_table}` SET sort = ? WHERE id = ?", array($index + 1, intval($id)));
        }
        $conn->commit();
        echo json_encode(['success' => true, 'message' => '排序更新成功']);
    } catch (Exception $e) {
        $conn->rollback();
        echo json_encode(['success' => false, 'message' => '排序更新失败']);
    }
    exit;
}

if (isset($_POST['action']) && $_POST['action'] === 'move_catgroup_drag') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $dragged_id = isset($_POST['dragged_id']) ? intval($_POST['dragged_id']) : 0;
    $target_id = isset($_POST['target_id']) ? intval($_POST['target_id']) : 0;
    $position = isset($_POST['position']) ? trim($_POST['position']) : 'after';

    if ($dragged_id <= 0 || $target_id <= 0 || $dragged_id === $target_id || !in_array($position, array('before', 'after'), true)) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    $ordered_ids = array();
    $result = execute_query($conn, "SELECT id FROM `{$catgroup_table}` ORDER BY sort ASC, id ASC");
    if ($result) {
        while ($row = $result->fetch_assoc()) {
            $ordered_ids[] = intval($row['id']);
        }
        $result->close();
    }
    $original_ordered_ids = $ordered_ids;

    if (!in_array($dragged_id, $ordered_ids, true) || !in_array($target_id, $ordered_ids, true)) {
        echo json_encode(['success' => false, 'message' => '目标不存在']);
        exit;
    }

    $ordered_ids = array_values(array_filter($ordered_ids, function ($id) use ($dragged_id) {
        return intval($id) !== intval($dragged_id);
    }));
    $target_index = array_search($target_id, $ordered_ids, true);
    if ($target_index === false) {
        echo json_encode(['success' => false, 'message' => '目标不存在']);
        exit;
    }
    $insert_index = $position === 'after' ? ($target_index + 1) : $target_index;
    array_splice($ordered_ids, $insert_index, 0, array($dragged_id));

    if ($ordered_ids === $original_ordered_ids) {
        echo json_encode(['success' => false, 'code' => 'noop', 'message' => '未发生变更']);
        exit;
    }

    $conn->begin_transaction();
    try {
        foreach ($ordered_ids as $index => $id) {
            execute_non_query($conn, "UPDATE `{$catgroup_table}` SET sort = ? WHERE id = ?", array($index + 1, intval($id)));
        }
        $conn->commit();
        echo json_encode(['success' => true, 'message' => '顶级栏目排序更新成功']);
    } catch (Exception $e) {
        $conn->rollback();
        echo json_encode(['success' => false, 'message' => '顶级栏目排序更新失败']);
    }
    exit;
}

// 设置归档颜色
if (isset($_POST['action']) && $_POST['action'] === 'set_catgroup_color') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $id = isset($_POST['id']) ? intval($_POST['id']) : 0;
    $color = isset($_POST['color']) ? trim($_POST['color']) : '';

    if ($id <= 0 || empty($color)) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    // 验证颜色格式
    if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) {
        echo json_encode(['success' => false, 'message' => '颜色格式错误']);
        exit;
    }

    $result = execute_non_query($conn, "UPDATE `{$catgroup_table}` SET color = ? WHERE id = ?", array($color, $id));

    if ($result !== false) {
        echo json_encode(['success' => true, 'message' => '颜色设置成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '颜色设置失败']);
    }
    exit;
}

// 批量重置所有归档颜色
if (isset($_POST['action']) && $_POST['action'] === 'reset_all_catgroup_colors') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    if (!isset($_POST['ids']) || !isset($_POST['colors']) || !is_array($_POST['ids']) || !is_array($_POST['colors']) || count($_POST['ids']) != count($_POST['colors'])) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    $conn->begin_transaction();
    try {
        foreach ($_POST['ids'] as $index => $id) {
            $id = intval($id);
            $color = trim($_POST['colors'][$index]);

            // 验证颜色格式
            if (!preg_match('/^#[0-9a-fA-F]{6}$/', $color)) {
                continue;
            }

            execute_non_query($conn, "UPDATE `{$catgroup_table}` SET color = ? WHERE id = ?", array($color, $id));
        }

        $conn->commit();
        echo json_encode(['success' => true, 'message' => '所有归档颜色已重置']);
    } catch (Exception $e) {
        $conn->rollback();
        echo json_encode(['success' => false, 'message' => '重置颜色失败：' . $e->getMessage()]);
    }
    exit;
}

// 更新分类归档
if (isset($_POST['action']) && $_POST['action'] === 'update_category_archive') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0;
    $archiveid = isset($_POST['archiveid']) ? intval($_POST['archiveid']) : 0;

    if ($category_id <= 0) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    $result = execute_non_query($conn, "UPDATE categories SET archiveid = ? WHERE id = ?", array($archiveid, $category_id));

    if ($result !== false) {
        qk_clear_page_cache();
        echo json_encode(['success' => true, 'message' => '顶级栏目更新成功']);
    } else {
        echo json_encode(['success' => false, 'message' => '顶级栏目更新失败']);
    }
    exit;
}

if (isset($_POST['action']) && $_POST['action'] === 'move_category_drag') {
    header('Content-Type: application/json');

    global $conn;
    if (!isset($conn) || !is_object($conn) || isset($conn->connect_error)) {
        $conn = get_db_connection();
    }

    if (function_exists('ensureCategorySortColumn')) {
        ensureCategorySortColumn();
    }

    if (!isset($_SESSION['user_id']) || !isset($_SESSION['levels_id']) || $_SESSION['levels_id'] != 3) {
        echo json_encode(['success' => false, 'message' => '权限不足']);
        exit;
    }

    $dragged_id = isset($_POST['dragged_id']) ? intval($_POST['dragged_id']) : 0;
    $target_type = isset($_POST['target_type']) ? trim($_POST['target_type']) : '';
    $target_id = isset($_POST['target_id']) ? intval($_POST['target_id']) : 0;
    $position = isset($_POST['position']) ? trim($_POST['position']) : 'after';
    if (!in_array($position, array('before', 'after', 'append'), true)) {
        $position = 'after';
    }
    $virtual_top = null;
    if (isset($_POST['virtual_top'])) {
        $virtual_top = intval($_POST['virtual_top']) === 1 ? 1 : 0;
    }
    $virtual_top_anchor = null;
    if (isset($_POST['virtual_top_anchor'])) {
        $virtual_top_anchor = intval($_POST['virtual_top_anchor']);
    }

    if ($dragged_id <= 0 || !in_array($target_type, array('top', 'child'), true)) {
        echo json_encode(['success' => false, 'message' => '参数错误']);
        exit;
    }

    $dragged_row_result = execute_query($conn, "SELECT * FROM categories WHERE id = ? LIMIT 1", array($dragged_id));
    $dragged_row = $dragged_row_result ? $dragged_row_result->fetch_assoc() : null;
    if ($dragged_row_result) $dragged_row_result->close();
    if (!$dragged_row) {
        echo json_encode(['success' => false, 'message' => '子栏目不存在']);
        exit;
    }

    if ($virtual_top_anchor !== null) {
        $current_archiveid = intval($dragged_row['archiveid'] ?? 0);
        $current_virtual_top = intval($dragged_row['virtual_top'] ?? 0) === 1 ? 1 : 0;
        $current_virtual_anchor = intval($dragged_row['virtual_top_anchor'] ?? 0);

        if ($current_archiveid === 0 && $current_virtual_top === 1 && $current_virtual_anchor === intval($virtual_top_anchor)) {
            echo json_encode(['success' => false, 'code' => 'noop', 'message' => '未发生变更']);
            exit;
        }

        $conn->begin_transaction();
        try {
            execute_non_query($conn, "UPDATE categories SET archiveid = 0, virtual_top = 1, virtual_top_anchor = ? WHERE id = ?", array(intval($virtual_top_anchor), $dragged_id));
            $conn->commit();
            qk_clear_page_cache();
            echo json_encode([
                'success' => true,
                'message' => '排序更新成功',
                'archiveid' => 0
            ]);
        } catch (Exception $e) {
            $conn->rollback();
            echo json_encode(['success' => false, 'message' => '排序更新失败']);
        }
        exit;
    }

    $source_archiveid = intval($dragged_row['archiveid'] ?? 0);
    $target_archiveid = $source_archiveid;
    $target_ids = array();

    $original_target_ids = array();
    $original_source_ids = array();

    if ($target_type === 'top') {
        $target_archiveid = max(0, $target_id);
        if ($target_archiveid > 0) {
            $target_group_result = execute_query($conn, "SELECT id FROM `{$catgroup_table}` WHERE id = ? LIMIT 1", array($target_archiveid));
            $target_group = $target_group_result ? $target_group_result->fetch_assoc() : null;
            if ($target_group_result) $target_group_result->close();
            if (!$target_group) {
                echo json_encode(['success' => false, 'message' => '顶级栏目不存在']);
                exit;
            }
        }

        $original_target_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid = ? ORDER BY CASE WHEN sort > 0 THEN 0 ELSE 1 END ASC, sort ASC, id ASC", array($target_archiveid));
        if ($original_target_result) {
            while ($row = $original_target_result->fetch_assoc()) {
                $original_target_ids[] = intval($row['id']);
            }
            $original_target_result->close();
        }

        $target_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid = ? AND id <> ? ORDER BY CASE WHEN sort > 0 THEN 0 ELSE 1 END ASC, sort ASC, id ASC", array($target_archiveid, $dragged_id));
        if ($target_result) {
            while ($row = $target_result->fetch_assoc()) {
                $target_ids[] = intval($row['id']);
            }
            $target_result->close();
        }
        if ($position === 'before') {
            array_unshift($target_ids, $dragged_id);
        } else {
            $target_ids[] = $dragged_id;
        }
    } else {
        if ($target_id <= 0 || $target_id === $dragged_id || !in_array($position, array('before', 'after'), true)) {
            echo json_encode(['success' => false, 'message' => '参数错误']);
            exit;
        }

        $target_row_result = execute_query($conn, "SELECT id, archiveid FROM categories WHERE id = ? LIMIT 1", array($target_id));
        $target_row = $target_row_result ? $target_row_result->fetch_assoc() : null;
        if ($target_row_result) $target_row_result->close();
        if (!$target_row) {
            echo json_encode(['success' => false, 'message' => '目标子栏目不存在']);
            exit;
        }

        $target_archiveid = intval($target_row['archiveid'] ?? 0);

        $original_target_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid = ? ORDER BY CASE WHEN sort > 0 THEN 0 ELSE 1 END ASC, sort ASC, id ASC", array($target_archiveid));
        if ($original_target_result) {
            while ($row = $original_target_result->fetch_assoc()) {
                $original_target_ids[] = intval($row['id']);
            }
            $original_target_result->close();
        }

        $target_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid = ? AND id <> ? ORDER BY CASE WHEN sort > 0 THEN 0 ELSE 1 END ASC, sort ASC, id ASC", array($target_archiveid, $dragged_id));
        if ($target_result) {
            while ($row = $target_result->fetch_assoc()) {
                $target_ids[] = intval($row['id']);
            }
            $target_result->close();
        }
        $target_index = array_search($target_id, $target_ids, true);
        if ($target_index === false) {
            echo json_encode(['success' => false, 'message' => '目标子栏目不存在']);
            exit;
        }
        $insert_index = $position === 'after' ? ($target_index + 1) : $target_index;
        array_splice($target_ids, $insert_index, 0, array($dragged_id));
    }

    $source_ids = array();
    if ($source_archiveid !== $target_archiveid) {
        $original_source_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid = ? ORDER BY CASE WHEN sort > 0 THEN 0 ELSE 1 END ASC, sort ASC, id ASC", array($source_archiveid));
        if ($original_source_result) {
            while ($row = $original_source_result->fetch_assoc()) {
                $original_source_ids[] = intval($row['id']);
            }
            $original_source_result->close();
        }

        $source_result = execute_query($conn, "SELECT id FROM categories WHERE archiveid = ? AND id <> ? ORDER BY CASE WHEN sort > 0 THEN 0 ELSE 1 END ASC, sort ASC, id ASC", array($source_archiveid, $dragged_id));
        if ($source_result) {
            while ($row = $source_result->fetch_assoc()) {
                $source_ids[] = intval($row['id']);
            }
            $source_result->close();
        }
    }

    if ($source_archiveid === $target_archiveid && $original_target_ids && $target_ids === $original_target_ids) {
        echo json_encode(['success' => false, 'code' => 'noop', 'message' => '未发生变更']);
        exit;
    }

    $conn->begin_transaction();
    try {
        if ($source_archiveid !== $target_archiveid) {
            foreach ($source_ids as $index => $id) {
                execute_non_query($conn, "UPDATE categories SET archiveid = ?, sort = ? WHERE id = ?", array($source_archiveid, $index + 1, intval($id)));
            }
        }

        foreach ($target_ids as $index => $id) {
            execute_non_query($conn, "UPDATE categories SET archiveid = ?, sort = ? WHERE id = ?", array($target_archiveid, $index + 1, intval($id)));
        }

        if ($virtual_top_anchor !== null) {
            $rt = execute_non_query($conn, "UPDATE categories SET virtual_top = 1, virtual_top_anchor = ? WHERE id = ?", array(intval($virtual_top_anchor), $dragged_id));
            if ($rt === false) {
                execute_non_query($conn, "UPDATE categories SET virtual_top_anchor = ? WHERE id = ?", array(intval($virtual_top_anchor), $dragged_id));
            }
        } else if ($virtual_top === 0) {
            $rt = execute_non_query($conn, "UPDATE categories SET virtual_top = 0, virtual_top_anchor = 0 WHERE id = ?", array($dragged_id));
            if ($rt === false) {
                execute_non_query($conn, "UPDATE categories SET virtual_top_anchor = 0 WHERE id = ?", array($dragged_id));
            }
        }

        $conn->commit();
        qk_clear_page_cache();
        $success_message = '排序更新成功';
        if ($virtual_top_anchor === null) {
            $success_message = $target_type === 'top' ? '子栏目绑定成功' : '子栏目排序更新成功';
        }
        echo json_encode([
            'success' => true,
            'message' => $success_message,
            'archiveid' => $target_archiveid
        ]);
    } catch (Exception $e) {
        $conn->rollback();
        $error_message = '排序更新失败';
        if ($virtual_top_anchor === null) {
            $error_message = $target_type === 'top' ? '子栏目绑定失败' : '子栏目排序更新失败';
        }
        echo json_encode(['success' => false, 'message' => $error_message]);
    }
    exit;
}

// 栏目管理页面内容
if ($active_tab == 'adcategory') {
    $search = isset($_GET['search']) ? trim($_GET['search']) : '';
    $archiveid = isset($_GET['archiveid']) ? intval($_GET['archiveid']) : 0;
    $current_page = 1;
    $posts_per_page = 30;

    // 优先解析新的简洁格式
    foreach ($_GET as $key => $value) {
        // 解析页码和每页显示数量：p{page}_s{per_page}
        if (preg_match('/^p(\d+)_s(\d+)$/', $key, $matches)) {
            $current_page = intval($matches[1]);
            $posts_per_page = intval($matches[2]);
        }
    }

    // 向后兼容，支持旧格式
    if (isset($_GET['page']) && $_GET['page'] > 0) {
        $current_page = intval($_GET['page']);
    }
    if (isset($_GET['posts_per_page'])) {
        $temp_posts_per_page = intval($_GET['posts_per_page']);
        if ($temp_posts_per_page >= 1 && $temp_posts_per_page <= 1000) $posts_per_page = $temp_posts_per_page;
    }

    $current_page = max(1, $current_page);
    $posts_per_page = max(1, min(1000, $posts_per_page));

    global $cache;
    $cache_key = 'categories_' . md5($search);
    unset($cache[$cache_key]);

    $tree_payload = qk_get_adcategory_tree_payload($conn, $catgroup_table, $search, $current_page, $posts_per_page, $archiveid);
    $categories = $tree_payload['categories'];
    $catgroups = $tree_payload['catgroups'];
    $total_categories = intval($tree_payload['total'] ?? 0);
    $all_catgroups_for_modal = array();
    $all_catgroups_result = execute_query($conn, "SELECT * FROM `{$catgroup_table}` ORDER BY sort ASC, id ASC");
    if ($all_catgroups_result) {
        while ($row = $all_catgroups_result->fetch_assoc()) {
            $all_catgroups_for_modal[] = $row;
        }
        $all_catgroups_result->close();
    }

?>
    <div class="section-header">
        <h2>栏目管理</h2>
        <div style="display: flex; align-items: flex-end; flex: 1; min-width: 0; margin-left: 30px; overflow: visible; justify-content: flex-end; gap: 10px;">
            <a href="#" id="batch-delete-selected-button" class="btn btn-danger action-button" onclick="batchDeleteSelectedCategories(); return false;" style="flex: none; width: 120px; align-items: center; justify-content: center; white-space: nowrap;">
                <svg class="add-category-btn-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 6px;">
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="15" y1="9" x2="9" y2="15"></line>
                    <line x1="9" y1="9" x2="15" y2="15"></line>
                </svg>
                <span class="add-category-btn-text">删除选中</span>
            </a>
            <a href="#" id="add-catgroup-top-button" class="btn btn-primary action-button" onclick="addCatgroup(); return false;" style="flex: none; width: 150px; display: inline-flex; align-items: center; justify-content: center; white-space: nowrap;">
                <svg class="add-category-btn-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="margin-right: 6px;">
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="12" y1="8" x2="12" y2="16"></line>
                    <line x1="8" y1="12" x2="16" y2="12"></line>
                </svg>
                <span class="add-category-btn-text">新增顶级栏目</span>
            </a>
            <a href="#" id="add-category-button" class="btn btn-primary action-button" style="flex: none; display: inline-flex; align-items: center; justify-content: center;">
                <svg class="add-category-btn-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" style="margin-right: 6px;">
                    <circle cx="12" cy="12" r="10"></circle>
                    <line x1="12" y1="8" x2="12" y2="16"></line>
                    <line x1="8" y1="12" x2="16" y2="12"></line>
                </svg>
                <span class="add-category-btn-text">新增子栏目</span>
            </a>
        </div>
    </div>
    <style>
        .section-header {
            overflow: visible !important;
        }

        #add-catgroup-top-button {
            background-color: #6b7280 !important;
            border-color: #6b7280 !important;
            color: #fff !important;
            margin-left: 0 !important;
        }

        #add-catgroup-top-button:hover {
            background-color: #4b5563 !important;
            border-color: #4b5563 !important;
        }

        #add-category-button {
            margin-left: 0 !important;
        }

        @media (max-width: 768px) {

            #batch-delete-selected-button,
            #add-catgroup-top-button,
            #add-category-button {
                width: 38px !important;
                height: 38px !important;
                min-width: 38px !important;
                padding: 0 !important;
            }

            #batch-delete-selected-button .add-category-btn-text,
            #add-catgroup-top-button .add-category-btn-text,
            #add-category-button .add-category-btn-text {
                display: none !important;
            }

            #batch-delete-selected-button .add-category-btn-icon,
            #add-catgroup-top-button .add-category-btn-icon,
            #add-category-button .add-category-btn-icon {
                margin-right: 0 !important;
            }
        }


        .archive-select {
            padding: 4px 8px;
            border: 1px solid #e5e7eb;
            border-radius: 4px;
            font-size: 12px;
            cursor: pointer;
        }

        .archive-select:focus {
            outline: none;
            border-color: var(--primary-color);
        }

        .tree-ungrouped-row td {
            background: #f8fbff !important;
        }

        .tree-top-row {
            background: #f8fbff;
        }

        .tree-level-tag {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 999px;
            font-size: 12px;
            line-height: 1.2;
            white-space: nowrap;
        }

        .tree-level-top {
            background: #dbeafe;
            color: #1e40af;
        }

        .tree-level-sub {
            background: #ffedd5;
            color: #9a3412;
        }

        .tree-level-virtual {
            background: #ede9fe;
            color: #5b21b6;
        }

        .tree-top-name {
            font-weight: 700;
            color: var(--text-color);
            font-size: 16px;
        }

        .tree-ungrouped-row .tree-top-name {
            color: #64748b !important;
        }

        .tree-virtual-top-row td {
            background: #f8fbff !important;
        }

        .tree-ungrouped-row .ungrouped-meta {
            color: #64748b;
            font-size: 13px;
            white-space: nowrap;
        }

        .tree-ungrouped-row select.form-control:disabled {
            background: #f1f5f9 !important;
            color: #64748b !important;
            border-color: #e2e8f0 !important;
            cursor: not-allowed !important;
            opacity: 1 !important;
        }

        .tree-ungrouped-row .ungrouped-action-btn {
            background: #e2e8f0 !important;
            border-color: #cbd5e1 !important;
            color: #64748b !important;
            cursor: not-allowed !important;
            opacity: 1 !important;
            box-shadow: none !important;
        }

        .tree-toggle-btn {
            width: 45px;
            height: 45px;
            border-radius: 50%;
            border: 1px solid #cbd5e1;
            background: #ffffff;
            color: #64748b;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            padding: 0;
            margin: 0 auto;
            line-height: 1;
            transition: all 0.2s ease;
        }

        .tree-toggle-btn:disabled {
            opacity: 0.55;
            cursor: default;
            pointer-events: none;
        }

        .tree-toggle-btn:hover:not(:disabled) {
            border-color: var(--primary-color);
            color: var(--primary-color);
            background: #f8fafc;
        }

        .tree-toggle-btn svg {
            width: 22px;
            height: 22px;
            transition: none;
        }

        .tree-toggle-btn.collapsed svg {
            transform: rotate(-90deg);
        }

        .tree-toggle-btn.animate svg {
            transition: transform 0.2s ease;
        }

        #categories-tbody.tree-collapse-pending .tree-toggle-btn svg {
            transition: none !important;
        }

        .admin-table.category-manage th:nth-child(4),
        .admin-table.category-manage td:nth-child(4),
        .tree-top-row td:nth-child(4),
        .tree-sub-row td:nth-child(4) {
            text-align: center !important;
            vertical-align: middle !important;
        }

        #categories-tbody.tree-collapse-pending .tree-sub-row {
            display: none !important;
        }

        .tree-sub-row .category-name-link {
            display: inline-flex;
            align-items: center;
            margin-left: 0;
        }

        .tree-sub-name-wrap {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            padding-left: 0;
        }

        .tree-sub-name-text {
            display: inline-block;
            line-height: 1.4;
        }
        .tree-sub-name-text.is-disabled {
            color: #94a3b8;
        }

        #categories-tbody tr.drag-sort-row {
            transition: box-shadow 0.18s ease, opacity 0.18s ease, background-color 0.18s ease;
        }

        #categories-tbody tr.drag-sort-row.drag-source {
            opacity: 0.55;
        }

        #categories-tbody tr.drag-sort-row.drag-over-before {
            background: color-mix(in srgb, var(--primary-color) 10%, #ffffff) !important;
            box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--primary-color) 42%, transparent);
        }

        #categories-tbody tr.drag-sort-row.drag-over-after {
            background: color-mix(in srgb, var(--primary-color) 10%, #ffffff) !important;
            box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--primary-color) 42%, transparent);
        }

        #categories-tbody tr.drag-sort-row.drag-over-parent {
            background: color-mix(in srgb, var(--primary-color) 10%, #ffffff) !important;
            box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--primary-color) 42%, transparent);
        }

        #categories-tbody tr.tree-virtual-top-row.drag-over-before td,
        #categories-tbody tr.tree-virtual-top-row.drag-over-after td,
        #categories-tbody tr.tree-virtual-top-row.drag-over-parent td {
            background: color-mix(in srgb, var(--primary-color) 10%, #ffffff) !important;
            box-shadow: inset 0 2px 0 color-mix(in srgb, var(--primary-color) 42%, transparent), inset 0 -2px 0 color-mix(in srgb, var(--primary-color) 42%, transparent);
        }

        #categories-tbody tr.tree-virtual-top-row.drag-over-before td:first-child,
        #categories-tbody tr.tree-virtual-top-row.drag-over-after td:first-child,
        #categories-tbody tr.tree-virtual-top-row.drag-over-parent td:first-child {
            box-shadow: inset 2px 0 0 color-mix(in srgb, var(--primary-color) 42%, transparent), inset 0 2px 0 color-mix(in srgb, var(--primary-color) 42%, transparent), inset 0 -2px 0 color-mix(in srgb, var(--primary-color) 42%, transparent);
        }

        #categories-tbody tr.tree-virtual-top-row.drag-over-before td:last-child,
        #categories-tbody tr.tree-virtual-top-row.drag-over-after td:last-child,
        #categories-tbody tr.tree-virtual-top-row.drag-over-parent td:last-child {
            box-shadow: inset -2px 0 0 color-mix(in srgb, var(--primary-color) 42%, transparent), inset 0 2px 0 color-mix(in srgb, var(--primary-color) 42%, transparent), inset 0 -2px 0 color-mix(in srgb, var(--primary-color) 42%, transparent);
        }

        .tree-branch-icon {
            position: relative;
            width: 16px;
            height: 16px;
            margin-right: 0;
            flex: 0 0 16px;
        }

        .tree-branch-icon::before {
            content: "";
            position: absolute;
            left: 0;
            top: -8px;
            bottom: 7px;
            width: 2px;
            background: #94a3b8;
        }

        .tree-branch-icon::after {
            content: "";
            position: absolute;
            left: 0;
            top: 8px;
            width: 19px;
            height: 2px;
            background: #94a3b8;
        }

        .category-manage th:nth-child(1),
        .category-manage td:nth-child(1) {
            width: 69.47px !important;
            min-width:69.47px !important;
            max-width:69.47px !important;
            white-space: nowrap;
            text-align: center !important;
            vertical-align: middle !important;
        }


        body .admin-table.category-manage th:nth-child(6),
        body .admin-table.category-manage td:nth-child(6) {
            width: 170px !important;
            min-width: 170px !important;
            max-width: 170px !important;
            white-space: nowrap !important;
        }

        .admin-table.category-manage th:nth-child(7),
        .admin-table.category-manage td:nth-child(7) {
            width: 170px !important;
            min-width: 170px !important;
            max-width: 170px !important;
            white-space: nowrap !important;
        }

        .admin-table.category-manage th:nth-child(5),
        .admin-table.category-manage td:nth-child(5) {
            width: 160px !important;
            min-width: 160px !important;
            max-width: 160px !important;
            white-space: nowrap !important;
        }

        .category-manage td:nth-child(4) img {
            display: block;
            margin: 0 auto;
        }

        .admin-table.category-manage th:nth-child(4),
        .admin-table.category-manage td:nth-child(4) {
            width: 110px !important;
            min-width: 110px !important;
            max-width: 110px !important;
        }

        .admin-table.category-manage th:nth-child(2),
        .admin-table.category-manage td:nth-child(2) {
            width: 80px !important;
            min-width: 80px !important;
            max-width: 80px !important;
        }

        .admin-table.category-manage th:nth-child(3),
        .admin-table.category-manage td:nth-child(3) {
            width: 70px !important;
            min-width: 70px !important;
            max-width: 70px !important;
        }

        #category-select-all,
        .batch-select-top,
        .batch-select-category {
            width: 18px !important;
            height: 18px !important;
            min-width: 18px !important;
            min-height: 18px !important;
            box-sizing: border-box;
            display: inline-block;
            vertical-align: middle;
            margin: 0;
            cursor: pointer;
        }

        #batch-delete-selected-button {
            display: none !important;
            margin-right: 0;
        }

        #batch-delete-selected-button.is-active {
            display: inline-flex !important;
        }
    </style>
    <script>
        function scrollToNavigationSettings() {
            setTimeout(() => {
                const navigationSection = document.getElementById('navigation-settings');
                if (navigationSection) {
                    navigationSection.scrollIntoView({
                        behavior: 'smooth'
                    });
                }
            }, 500);
        }
    </script>

    <div class="admin-card">
        <div class="table-container">
            <table class="admin-table category-manage">
                <thead>
                    <tr>
                        <th><input type="checkbox" id="category-select-all" /></th>
                        <th>层级</th>
                        <th>ID</th>
                        <th>缩略图</th>
                        <th>分类名称</th>
                        <th>绑定模板</th>
                        <th>URL（双击编辑）</th>
                        <th>篇数</th>
                        <th class="fixed-action-column">操作</th>
                    </tr>
                </thead>
                <script>
                    (function() {
                        try {
                            const raw = localStorage.getItem('adcategory_tree_collapsed_map');
                            window.__initialTreeCollapsed = raw ? JSON.parse(raw) : {};
                        } catch (e) {
                            window.__initialTreeCollapsed = {};
                        }
                        try {
                            let css = '';
                            for (const key in window.__initialTreeCollapsed) {
                                if (!Object.prototype.hasOwnProperty.call(window.__initialTreeCollapsed, key) || !window.__initialTreeCollapsed[key]) continue;
                                const safeKey = String(key).replace(/[^0-9]/g, '');
                                if (safeKey === '') continue;
                                css += '#categories-tbody tr.tree-sub-row[data-top-id="' + safeKey + '"]{display:none!important;}';
                                css += '#tree-toggle-' + safeKey + ' svg{transform:rotate(-90deg);}';
                            }
                            if (css) {
                                const style = document.createElement('style');
                                style.id = 'initial-tree-collapsed-style';
                                style.textContent = css;
                                document.head.appendChild(style);
                            }
                        } catch (e) {}
                    })();
                </script>
                <tbody id="categories-tbody">
                    <?php
                    // 初始加载首屏数据，减少闪烁
                    $initial_categories = $categories;
                    // 获取模板列表（用于首屏渲染）
                    $initial_templates = qk_get_adcategory_category_templates();
                    $initial_catgroup_templates = qk_get_adcategory_catgroup_templates();

                    // 输出allCategories JS变量
                    echo '<script>window.allCategories = ' . json_encode(qk_get_adcategory_client_categories($categories), JSON_UNESCAPED_UNICODE) . ';</script>';
                    echo '<script>window.allCatgroups = ' . json_encode($catgroups, JSON_UNESCAPED_UNICODE) . ';</script>';
                    echo '<script>window.allCatgroupsForModal = ' . json_encode($all_catgroups_for_modal, JSON_UNESCAPED_UNICODE) . ';</script>';
                    echo '<script>window.catgroupTemplateOptions = ' . json_encode($initial_catgroup_templates, JSON_UNESCAPED_UNICODE) . ';</script>';

                    $catgroup_map = array();
                    foreach ($catgroups as $group) {
                        $catgroup_map[intval($group['id'])] = $group;
                    }
                    $virtual_before = array();
                    $virtual_after = array();
                    $grouped_categories = array();
                    foreach ($initial_categories as $category) {
                        $is_virtual = intval($category['virtual_top'] ?? 0) === 1;
                        if ($is_virtual) {
                            $anchor = intval($category['virtual_top_anchor'] ?? 0);
                            if ($anchor < 0) {
                                $gid = abs($anchor);
                                if (!isset($virtual_before[$gid])) $virtual_before[$gid] = array();
                                $virtual_before[$gid][] = $category;
                            } else {
                                $gid = intval($anchor);
                                if (!isset($virtual_after[$gid])) $virtual_after[$gid] = array();
                                $virtual_after[$gid][] = $category;
                            }
                            continue;
                        }
                        $aid = isset($category['archiveid']) ? intval($category['archiveid']) : 0;
                        if ($aid <= 0 || !isset($catgroup_map[$aid])) $aid = 0;
                        if (!isset($grouped_categories[$aid])) $grouped_categories[$aid] = array();
                        $grouped_categories[$aid][] = $category;
                    }
                    if (!isset($grouped_categories[0])) $grouped_categories[0] = array();
                    $group_order = array_merge(array(0), array_keys($catgroup_map));

                    if (empty($initial_categories)): ?>
                        <tr>
                            <td colspan="9" style="text-align:center !important;height:75px;line-height:75px;font-size:16px;color:#64748b;">当前列表暂无数据</td>
                        </tr>
                        <?php else:
                        foreach ($group_order as $archive_id):
                            $group_items = isset($grouped_categories[$archive_id]) ? $grouped_categories[$archive_id] : array();
                            $before_items = isset($virtual_before[$archive_id]) ? $virtual_before[$archive_id] : array();
                            $after_items = isset($virtual_after[$archive_id]) ? $virtual_after[$archive_id] : array();
                            if ($archive_id === 0 && empty($group_items) && empty($after_items)) continue;
                            if ($archive_id > 0 && !empty($before_items)) {
                                usort($before_items, function ($a, $b) {
                                    $aSort = intval($a['sort'] ?? 0);
                                    $bSort = intval($b['sort'] ?? 0);
                                    $aPri = $aSort > 0 ? 0 : 1;
                                    $bPri = $bSort > 0 ? 0 : 1;
                                    if ($aPri !== $bPri) return $aPri - $bPri;
                                    if ($aSort !== $bSort) return $aSort - $bSort;
                                    return intval($b['id'] ?? 0) - intval($a['id'] ?? 0);
                                });
                                foreach ($before_items as $vcat):
                                    $vUrl = $vcat['custom_url'] ?: 'list_id' . $vcat['id'] . '.html';
                                    $vThumb = $vcat['thumbnail'] ?? '';
                                    $v_anchor_gid = abs(intval($vcat['virtual_top_anchor'] ?? 0));
                                    $v_top_color = '#64748b';
                                    if ($v_anchor_gid > 0 && isset($catgroup_map[$v_anchor_gid])) {
                                        $raw_v_color = trim((string)($catgroup_map[$v_anchor_gid]['color'] ?? ''));
                                        if (preg_match('/^#[0-9a-fA-F]{6}$/', $raw_v_color)) $v_top_color = $raw_v_color;
                                    }
                                ?>
                                    <tr class="tree-virtual-top-row" data-category-id="<?php echo intval($vcat['id']); ?>" data-virtual-top="1" data-virtual-anchor="<?php echo intval($vcat['virtual_top_anchor'] ?? 0); ?>">
                                        <td><input type="checkbox" class="batch-select-category" value="<?php echo intval($vcat['id']); ?>" /></td>
                                        <td><span class="tree-level-tag tree-level-virtual" title="伪顶级：本质是子栏目，仅在后台列表中以顶级样式混排显示">伪</span></td>
                                        <td><?php echo intval($vcat['id']); ?></td>
                                        <td>
                                            <button type="button" class="tree-toggle-btn collapsed" title="伪顶级无子栏目" disabled>
                                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
                                                    <polyline points="6 9 12 15 18 9"></polyline>
                                                </svg>
                                            </button>
                                        </td>
                                        <td>
                                            <a href="/admin?tab=articles&category_id=<?php echo intval($vcat['id']); ?>" class="category-name-link" title="查看该分类下的文章" onclick="return checkArticleCount(this)">
                                                <span class="tree-top-name" style="color: <?php echo $v_top_color; ?>;"><?php echo escape($vcat['name'] ?? ''); ?></span>
                                            </a>
                                        </td>
                                        <td>
                                            <select class="form-control" onchange="updateCategoryTemplate(<?php echo intval($vcat['id']); ?>, this.value)">
                                                <?php foreach ($initial_templates as $path => $name): ?>
                                                    <option value="<?php echo $path; ?>" <?php echo (($vcat['template'] ?? 'list.html') === $path ? 'selected' : ''); ?>><?php echo $name; ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </td>
                                        <td class="category-custom-url-cell" data-category-id="<?php echo intval($vcat['id']); ?>">
                                            <span class="custom-url-display" ondblclick="editCategoryCustomUrl(<?php echo intval($vcat['id']); ?>);" data-default-url="list_id<?php echo intval($vcat['id']); ?>.html" data-original-url="<?php echo escape($vUrl); ?>"><?php echo escape($vUrl); ?></span>
                                            <input type="text" class="custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;" value="<?php echo escape($vUrl); ?>" onblur="saveCategoryCustomUrl(<?php echo intval($vcat['id']); ?>, this)" onkeydown="if(event.key==='Enter'){this.blur();}if(event.key==='Escape'){cancelEditCategoryCustomUrl(<?php echo intval($vcat['id']); ?>);}">
                                        </td>
                                        <td><?php echo intval($vcat['post_count'] ?? 0); ?></td>
                                        <td class="fixed-action-column">
                                            <button type="button" onclick="previewCategory(<?php echo intval($vcat['id']); ?>);" class="btn btn-sm btn-secondary">预览</button>
                                            <button type="button" onclick="openEditCategoryModal(<?php echo intval($vcat['id']); ?>);" class="btn btn-sm btn-primary">编辑</button>
                                            <button type="button" onclick="deleteCategoryConfirm(<?php echo intval($vcat['id']); ?>);" class="btn btn-sm btn-danger">删除</button>
                                        </td>
                                    </tr>
                                <?php endforeach;
                            }
                            if ($archive_id === 0):
                        ?>
                                <tr class="tree-top-row tree-ungrouped-row" data-top-id="0">
                                    <td><input type="checkbox" disabled /></td>
                                    <td><span class="tree-level-tag tree-level-top" title="顶级栏目">顶</span></td>
                                    <td>G0</td>
                                    <td>
                                        <button type="button" class="tree-toggle-btn" data-top-id="0" title="展开/收起" id="tree-toggle-0">
                                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
                                                <polyline points="6 9 12 15 18 9"></polyline>
                                            </svg>
                                        </button>
                                        <script>
                                            (function() {
                                                var btn = document.getElementById('tree-toggle-0');
                                                if (btn && window.__initialTreeCollapsed && window.__initialTreeCollapsed['0']) {
                                                    btn.classList.add('collapsed');
                                                }
                                            })();
                                        </script>
                                    </td>
                                    <td><span class="tree-top-name">默认栏目（<?php echo count($group_items); ?>）</span></td>
                                    <td>
                                        <select class="form-control" disabled>
                                            <?php foreach ($initial_catgroup_templates as $path => $name): ?>
                                                <option value="<?php echo escape($path); ?>" <?php echo ($path === 'catgroup.html' ? 'selected' : ''); ?>><?php echo escape($name); ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </td>
                                    <td><span class="ungrouped-meta">cg_id0.html</span></td>
                                    <td>
                                        <?php
                                        $ungrouped_post_total = 0;
                                        foreach ($group_items as $gc) {
                                            $ungrouped_post_total += intval($gc['post_count'] ?? 0);
                                        }
                                        echo $ungrouped_post_total;
                                        ?>
                                    </td>
                                    <td class="fixed-action-column">
                                        <button type="button" class="btn btn-sm ungrouped-action-btn" disabled>预览</button>
                                        <button type="button" class="btn btn-sm ungrouped-action-btn" disabled>编辑</button>
                                        <button type="button" class="btn btn-sm ungrouped-action-btn" disabled>删除</button>
                                    </td>
                                </tr>
                            <?php
                            else:
                                $group = $catgroup_map[$archive_id];
                                $group_name = isset($group['name']) ? $group['name'] : ('归档' . $archive_id);
                                $group_template = isset($group['template']) ? $group['template'] : 'catgroup.html';
                                $group_url = isset($group['custom_url']) && $group['custom_url'] !== '' ? $group['custom_url'] : ('cg_id' . $archive_id . '.html');
                                $child_count = count($group_items);
                                $has_children = $child_count > 0;
                            ?>
                                <tr class="tree-top-row" data-top-id="<?php echo $archive_id; ?>" <?php if (!$has_children): ?> data-no-children="true" <?php endif; ?>>
                                    <td><input type="checkbox" class="batch-select-top" value="<?php echo $archive_id; ?>" /></td>
                                    <td><span class="tree-level-tag tree-level-top" title="顶级栏目">顶</span></td>
                                    <td>G<?php echo $archive_id; ?></td>
                                    <td>
                                        <?php if ($has_children): ?>
                                            <button type="button" class="tree-toggle-btn" data-top-id="<?php echo $archive_id; ?>" title="展开/收起" id="tree-toggle-<?php echo $archive_id; ?>">
                                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
                                                    <polyline points="6 9 12 15 18 9"></polyline>
                                                </svg>
                                            </button>
                                            <script>
                                                (function() {
                                                    var btn = document.getElementById('tree-toggle-<?php echo $archive_id; ?>');
                                                    if (btn && window.__initialTreeCollapsed && window.__initialTreeCollapsed['<?php echo $archive_id; ?>']) {
                                                        btn.classList.add('collapsed');
                                                    }
                                                })();
                                            </script>
                                        <?php else: ?>
                                            <button type="button" class="tree-toggle-btn collapsed" data-top-id="<?php echo $archive_id; ?>" title="无子栏目" disabled>
                                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
                                                    <polyline points="6 9 12 15 18 9"></polyline>
                                                </svg>
                                            </button>
                                        <?php endif; ?>
                                    </td>
                                    <?php
                                    $top_name_color = '#64748b';
                                    if (isset($group['color'])) {
                                        $raw_top_color = trim((string)$group['color']);
                                        if (preg_match('/^#[0-9a-fA-F]{6}$/', $raw_top_color)) $top_name_color = $raw_top_color;
                                    }
                                    ?>
                                    <td><span class="tree-top-name" style="color: <?php echo $top_name_color; ?>;"><?php echo escape($group_name . (intval($group['category_count'] ?? 0) > 0 ? '（' . intval($group['category_count'] ?? 0) . '）' : '')); ?></span></td>
                                    <td>
                                        <select class="form-control" onchange="updateCatgroupTemplate(<?php echo $archive_id; ?>, this.value)">
                                            <?php foreach ($initial_catgroup_templates as $path => $name): ?>
                                                <option value="<?php echo escape($path); ?>" <?php echo ($group_template === $path ? 'selected' : ''); ?>><?php echo escape($name); ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </td>
                                    <td class="catgroup-custom-url-cell" data-catgroup-id="<?php echo $archive_id; ?>">
                                        <span class="catgroup-custom-url-display" ondblclick="editCatgroupCustomUrl(<?php echo $archive_id; ?>);" data-default-url="cg_id<?php echo $archive_id; ?>.html" data-original-url="<?php echo escape($group_url); ?>"><?php echo escape($group_url); ?></span>
                                        <input type="text" class="catgroup-custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;" value="<?php echo escape($group_url); ?>" onblur="saveCatgroupCustomUrl(<?php echo $archive_id; ?>, this)" onkeydown="if(event.key==='Enter'){this.blur();}if(event.key==='Escape'){cancelEditCatgroupCustomUrl(<?php echo $archive_id; ?>);}">
                                    </td>
                                    <td><?php echo intval($group['post_count_total'] ?? 0); ?></td>
                                    <td class="fixed-action-column">
                                        <button type="button" onclick="previewCatgroup(<?php echo $archive_id; ?>);" class="btn btn-sm btn-secondary">预览</button>
                                        <button type="button" onclick="editCatgroup(<?php echo $archive_id; ?>)" class="btn btn-sm btn-primary">编辑</button>
                                        <button type="button" onclick="deleteCatgroup(<?php echo $archive_id; ?>, event)" class="btn btn-sm btn-danger">删除</button>
                                    </td>
                                </tr>
                            <?php
                            endif;
                            foreach ($group_items as $category):
                                $currentUrl = $category['custom_url'] ?: 'list_id' . $category['id'] . '.html';
                                // 处理缩略图
                                $thumbnail_url = $category['thumbnail'];
                            ?>
                                <tr class="tree-sub-row" data-category-id="<?php echo $category['id']; ?>" data-archiveid="<?php echo $category['archiveid'] ?? 0; ?>" data-top-id="<?php echo $archive_id; ?>">
                                    <td><input type="checkbox" class="batch-select-category" value="<?php echo $category['id']; ?>" /></td>
                                    <td><span class="tree-level-tag tree-level-sub" title="子栏目">子</span></td>
                                    <td><?php echo $category['id']; ?></td>
                                    <td>
                                        <?php if (!empty($thumbnail_url)): ?>
                                            <img src="<?php echo escape($thumbnail_url); ?>" alt="<?php echo escape($category['name']); ?>" style="width: 45px; height: 45px; object-fit: cover; border-radius: 50%; cursor: pointer;" onclick="replaceCategoryThumbnail(<?php echo $category['id']; ?>, this);">
                                        <?php else: ?>
                                            <span class="text-muted" style="cursor: pointer;" onclick="replaceCategoryThumbnail(<?php echo $category['id']; ?>, this);">点击上传</span>
                                        <?php endif; ?>
                                    </td>
                                    <td>
                                        <a href="/admin?tab=articles&category_id=<?php echo $category['id']; ?>" class="category-name-link" title="查看该分类下的文章" onclick="return checkArticleCount(this)">
                                            <span class="tree-sub-name-wrap">
                                                <span class="tree-branch-icon"></span>
                                                <span class="tree-sub-name-text<?php echo (isset($category['display']) && intval($category['display']) === 0) ? ' is-disabled' : ''; ?>"><?php echo escape($category['name']); ?></span>
                                            </span>
                                        </a>
                                    </td>
                                    <td>
                                        <select class="form-control" onchange="updateCategoryTemplate(<?php echo $category['id']; ?>, this.value)">
                                            <?php foreach ($initial_templates as $path => $name): ?>
                                                <option value="<?php echo $path; ?>" <?php echo ($category['template'] === $path ? 'selected' : ''); ?>><?php echo $name; ?></option>
                                            <?php endforeach; ?>
                                        </select>
                                    </td>
                                    <td class="category-custom-url-cell" data-category-id="<?php echo $category['id']; ?>">
                                        <span class="custom-url-display" ondblclick="editCategoryCustomUrl(<?php echo $category['id']; ?>);" data-default-url="list_id<?php echo $category['id']; ?>.html" data-original-url="<?php echo escape($currentUrl); ?>"><?php echo escape($currentUrl); ?></span>
                                        <input type="text" class="custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;" value="<?php echo escape($currentUrl); ?>" onblur="saveCategoryCustomUrl(<?php echo $category['id']; ?>, this)" onkeydown="if(event.key==='Enter'){this.blur();}if(event.key==='Escape'){cancelEditCategoryCustomUrl(<?php echo $category['id']; ?>);}">
                                    </td>
                                    <td><?php echo ($category['post_count'] ?? 0); ?></td>
                                    <?php /* 开关列临时隐藏：后续可能迁移到模态弹窗，保留原事件 submitCategoryDisplayForm 用于复用 */ ?>
                                    <td class="fixed-action-column">
                                        <button type="button" onclick="previewCategory(<?php echo $category['id']; ?>);" class="btn btn-sm btn-secondary">预览</button>
                                        <button type="button" onclick="openEditCategoryModal(<?php echo $category['id']; ?>);" class="btn btn-sm btn-primary">编辑</button>
                                        <button type="button" onclick="deleteCategoryConfirm(<?php echo $category['id']; ?>);" class="btn btn-sm btn-danger">删除</button>
                                    </td>
                                </tr>
                    <?php endforeach;
                            if (!empty($after_items)) {
                                usort($after_items, function ($a, $b) {
                                    $aSort = intval($a['sort'] ?? 0);
                                    $bSort = intval($b['sort'] ?? 0);
                                    $aPri = $aSort > 0 ? 0 : 1;
                                    $bPri = $bSort > 0 ? 0 : 1;
                                    if ($aPri !== $bPri) return $aPri - $bPri;
                                    if ($aSort !== $bSort) return $aSort - $bSort;
                                    return intval($b['id'] ?? 0) - intval($a['id'] ?? 0);
                                });
                                foreach ($after_items as $vcat):
                                    $vUrl = $vcat['custom_url'] ?: 'list_id' . $vcat['id'] . '.html';
                                    $v_anchor_gid = abs(intval($vcat['virtual_top_anchor'] ?? 0));
                                    $v_top_color = '#64748b';
                                    if ($v_anchor_gid > 0 && isset($catgroup_map[$v_anchor_gid])) {
                                        $raw_v_color = trim((string)($catgroup_map[$v_anchor_gid]['color'] ?? ''));
                                        if (preg_match('/^#[0-9a-fA-F]{6}$/', $raw_v_color)) $v_top_color = $raw_v_color;
                                    }
                                ?>
                                    <tr class="tree-virtual-top-row" data-category-id="<?php echo intval($vcat['id']); ?>" data-virtual-top="1" data-virtual-anchor="<?php echo intval($vcat['virtual_top_anchor'] ?? 0); ?>">
                                        <td><input type="checkbox" class="batch-select-category" value="<?php echo intval($vcat['id']); ?>" /></td>
                                        <td><span class="tree-level-tag tree-level-virtual" title="伪顶级：本质是子栏目，仅在后台列表中以顶级样式混排显示">伪</span></td>
                                        <td><?php echo intval($vcat['id']); ?></td>
                                        <td>
                                            <button type="button" class="tree-toggle-btn collapsed" title="伪顶级无子栏目" disabled>
                                                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
                                                    <polyline points="6 9 12 15 18 9"></polyline>
                                                </svg>
                                            </button>
                                        </td>
                                        <td>
                                            <a href="/admin?tab=articles&category_id=<?php echo intval($vcat['id']); ?>" class="category-name-link" title="查看该分类下的文章" onclick="return checkArticleCount(this)">
                                                <span class="tree-top-name" style="color: <?php echo $v_top_color; ?>;"><?php echo escape($vcat['name'] ?? ''); ?></span>
                                            </a>
                                        </td>
                                        <td>
                                            <select class="form-control" onchange="updateCategoryTemplate(<?php echo intval($vcat['id']); ?>, this.value)">
                                                <?php foreach ($initial_templates as $path => $name): ?>
                                                    <option value="<?php echo $path; ?>" <?php echo (($vcat['template'] ?? 'list.html') === $path ? 'selected' : ''); ?>><?php echo $name; ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </td>
                                        <td class="category-custom-url-cell" data-category-id="<?php echo intval($vcat['id']); ?>">
                                            <span class="custom-url-display" ondblclick="editCategoryCustomUrl(<?php echo intval($vcat['id']); ?>);" data-default-url="list_id<?php echo intval($vcat['id']); ?>.html" data-original-url="<?php echo escape($vUrl); ?>"><?php echo escape($vUrl); ?></span>
                                            <input type="text" class="custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;" value="<?php echo escape($vUrl); ?>" onblur="saveCategoryCustomUrl(<?php echo intval($vcat['id']); ?>, this)" onkeydown="if(event.key==='Enter'){this.blur();}if(event.key==='Escape'){cancelEditCategoryCustomUrl(<?php echo intval($vcat['id']); ?>);}">
                                        </td>
                                        <td><?php echo intval($vcat['post_count'] ?? 0); ?></td>
                                        <td class="fixed-action-column">
                                            <button type="button" onclick="previewCategory(<?php echo intval($vcat['id']); ?>);" class="btn btn-sm btn-secondary">预览</button>
                                            <button type="button" onclick="openEditCategoryModal(<?php echo intval($vcat['id']); ?>);" class="btn btn-sm btn-primary">编辑</button>
                                            <button type="button" onclick="deleteCategoryConfirm(<?php echo intval($vcat['id']); ?>);" class="btn btn-sm btn-danger">删除</button>
                                        </td>
                                    </tr>
                                <?php endforeach;
                            }
                        endforeach;
                    endif; ?>
                </tbody>
            </table>
        </div>
    </div>

    <script>
        function escHtml(text) {
            return String(text == null ? '' : text)
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }

        function getSelectedCategoryDeleteIds() {
            const topIds = Array.from(document.querySelectorAll('#categories-tbody .batch-select-top:checked')).map(el => parseInt(el.value || '0', 10)).filter(v => v > 0);
            const categoryIds = Array.from(document.querySelectorAll('#categories-tbody .batch-select-category:checked')).map(el => parseInt(el.value || '0', 10)).filter(v => v > 0);
            return {
                topIds,
                categoryIds
            };
        }

        function updateBatchDeleteButtonVisibility() {
            const btn = document.getElementById('batch-delete-selected-button');
            const selectAll = document.getElementById('category-select-all');
            if (!btn) return;
            const allSelectable = Array.from(document.querySelectorAll('#categories-tbody .batch-select-top, #categories-tbody .batch-select-category')).filter(el => !el.disabled);
            const selectedCount = allSelectable.filter(el => el.checked).length;
            if (selectedCount > 0) btn.classList.add('is-active');
            else btn.classList.remove('is-active');
            if (selectAll) {
                if (allSelectable.length === 0) {
                    selectAll.checked = false;
                    selectAll.indeterminate = false;
                } else {
                    selectAll.checked = selectedCount === allSelectable.length;
                    selectAll.indeterminate = selectedCount > 0 && selectedCount < allSelectable.length;
                }
            }
        }

        function resetBatchDeleteSelectionState() {
            const selectAll = document.getElementById('category-select-all');
            if (selectAll) {
                selectAll.checked = false;
                selectAll.indeterminate = false;
            }
            document.querySelectorAll('#categories-tbody .batch-select-top, #categories-tbody .batch-select-category').forEach(function(el) {
                if (!el.disabled) el.checked = false;
            });
            updateBatchDeleteButtonVisibility();
        }

        function batchDeleteSelectedCategories() {
            const picked = getSelectedCategoryDeleteIds();
            const total = picked.topIds.length + picked.categoryIds.length;
            if (total <= 0) {
                showToast('请先勾选要删除的栏目', 'warning');
                return;
            }
            const submitDelete = function() {
                const xhr = new XMLHttpRequest();
                xhr.open('POST', '/admin/adcategory.php', true);
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                xhr.onreadystatechange = function() {
                    if (xhr.readyState !== 4) return;
                    try {
                        const response = JSON.parse(xhr.responseText || '{}');
                        if (xhr.status === 200 && response.success) {
                            showToast(response.message || '批量删除成功', 'success', 2000);
                            reloadAdcategoryList({
                                deltaTotal: -1 * parseInt(response.deleted_category_count || 0, 10)
                            });
                        } else {
                            showToast(response.message || '批量删除失败', 'error', 2000);
                        }
                    } catch (e) {
                        showToast('批量删除失败', 'error', 2000);
                    }
                };
                const payload = 'action=batch_delete_selected'
                    + '&top_ids=' + encodeURIComponent(picked.topIds.join(','))
                    + '&category_ids=' + encodeURIComponent(picked.categoryIds.join(','));
                xhr.send(payload);
            };

            const msg = '确定要删除选中的栏目吗？';
            if (typeof confirmDialog === 'function') {
                confirmDialog(msg, submitDelete);
            } else if (confirm(msg)) {
                submitDelete();
            }
        }

        // 渲染分类列表
        function renderCategoriesList(data) {
            const categories = data.categories || [];
            const table = document.querySelector('.table-container table');
            if (!table) return;
            if (Array.isArray(data.catgroups)) {
                window.allCatgroups = data.catgroups.slice();
            }
            if (data.catgroup_templates && typeof data.catgroup_templates === 'object') {
                window.catgroupTemplateOptions = data.catgroup_templates;
            }
            if (data.templates && typeof data.templates === 'object') {
                window.categoryTemplateOptions = data.templates;
            }

            // 更新allCategories数组
            window.allCategories = categories.map(cat => ({
                id: cat.id,
                name: cat.name,
                archiveid: cat.archiveid || 0,
                virtual_top: parseInt(cat.virtual_top || 0, 10) || 0,
                virtual_top_anchor: parseInt(cat.virtual_top_anchor || 0, 10) || 0,
                sort: cat.sort || 0,
                thumbnail: cat.thumbnail,
                template: cat.template,
                custom_url: cat.custom_url,
                display: cat.display,
                post_count: cat.post_count
            }));

            let tbodyHtml = '';
            const safeCatgroups = Array.isArray(window.allCatgroups) ? window.allCatgroups.slice() : [];
            const templates = window.categoryTemplateOptions || data.templates || {
                'list.html': 'list.html（默认）'
            };
            const topTemplates = window.catgroupTemplateOptions || data.catgroup_templates || {
                'catgroup.html': 'catgroup.html（默认）'
            };
            {
                safeCatgroups.sort((a, b) => (parseInt(a.sort || 0) - parseInt(b.sort || 0)) || (parseInt(a.id || 0) - parseInt(b.id || 0)));
                const knownGroupIds = new Set(safeCatgroups.map(g => parseInt(g.id || 0)));
                const grouped = {};
                const virtualBefore = {};
                const virtualAfter = {};
                const normalized = categories.map(c => {
                    const aidRaw = parseInt(c.archiveid || 0);
                    const aid = (aidRaw > 0 && knownGroupIds.has(aidRaw)) ? aidRaw : 0;
                    return {
                        ...c,
                        _aid: aid,
                        _virtualTop: parseInt(c.virtual_top || 0, 10) === 1,
                        _virtualAnchor: (() => {
                            const raw = parseInt(c.virtual_top_anchor || 0, 10) || 0;
                            const absVal = Math.abs(raw);
                            if (absVal !== 0 && !knownGroupIds.has(absVal)) return 0;
                            return raw;
                        })()
                    };
                }).sort((a, b) => {
                    const aSort = parseInt(a.sort || 0, 10);
                    const bSort = parseInt(b.sort || 0, 10);
                    const aPriority = aSort > 0 ? 0 : 1;
                    const bPriority = bSort > 0 ? 0 : 1;
                    return (aPriority - bPriority) || (aSort - bSort) || (parseInt(b.id || 0, 10) - parseInt(a.id || 0, 10));
                });
                normalized.forEach(c => {
                    if (c._virtualTop) {
                        const anchor = parseInt(c._virtualAnchor || 0, 10) || 0;
                        if (anchor < 0) {
                            const key = Math.abs(anchor);
                            if (!virtualBefore[key]) virtualBefore[key] = [];
                            virtualBefore[key].push(c);
                        } else {
                            const key = anchor;
                            if (!virtualAfter[key]) virtualAfter[key] = [];
                            virtualAfter[key].push(c);
                        }
                        return;
                    }
                    if (!grouped[c._aid]) grouped[c._aid] = [];
                    grouped[c._aid].push(c);
                });

                const buildTopTemplateOptionsHtml = (selectedVal) => {
                    let html = '';
                    for (const [templatePath, templateName] of Object.entries(topTemplates)) {
                        const selected = String(selectedVal || 'catgroup.html') === String(templatePath) ? 'selected' : '';
                        html += '<option value="' + escHtml(templatePath) + '" ' + selected + '>' + escHtml(templateName) + '</option>';
                    }
                    return html;
                };

                const renderUngroupedTopRow = () => {
                    const items = grouped[0] || [];
                    const vAfter = virtualAfter[0] || [];
                    if (!items.length && !vAfter.length) return;
                    let postTotal = 0;
                    items.forEach(it => {
                        postTotal += parseInt(it.post_count || 0);
                    });
                    let ungroupedTemplateOptions = '';
                    for (const [templatePath, templateName] of Object.entries(topTemplates)) {
                        const selected = String(templatePath) === 'catgroup.html' ? 'selected' : '';
                        ungroupedTemplateOptions += '<option value="' + escHtml(templatePath) + '" ' + selected + '>' + escHtml(templateName) + '</option>';
                    }
                    tbodyHtml += '<tr class="tree-top-row tree-ungrouped-row" data-top-id="0">';
                    tbodyHtml += '<td><input type="checkbox" disabled></td>';
                    tbodyHtml += '<td><span class="tree-level-tag tree-level-top" title="顶级栏目">顶</span></td>';
                    tbodyHtml += '<td>G0</td>';
                    tbodyHtml += '<td><button type="button" class="tree-toggle-btn" data-top-id="0" title="展开/收起"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button></td>';
                    tbodyHtml += '<td><span class="tree-top-name">默认栏目（' + items.length + '）</span></td>';
                    tbodyHtml += '<td><select class="form-control" disabled>' + ungroupedTemplateOptions + '</select></td>';
                    tbodyHtml += '<td><span class="ungrouped-meta">cg_id0.html</span></td>';
                    tbodyHtml += '<td>' + postTotal + '</td>';
                    tbodyHtml += '<td class="fixed-action-column">';
                    tbodyHtml += '<button type="button" class="btn btn-sm ungrouped-action-btn" disabled>预览</button> ';
                    tbodyHtml += '<button type="button" class="btn btn-sm ungrouped-action-btn" disabled>编辑</button> ';
                    tbodyHtml += '<button type="button" class="btn btn-sm ungrouped-action-btn" disabled>删除</button>';
                    tbodyHtml += '</td>';
                    tbodyHtml += '</tr>';
                };

                const renderGroupTopRow = (group) => {
                    const gid = parseInt(group.id || 0);
                    const safeGroupName = escHtml(group.name || ('归档' + gid));
                    const safeGroupColor = (group && group.color) ? escHtml(group.color) : '#64748b';
                    const safeTemplate = group.template || 'catgroup.html';
                    const safeUrl = escHtml(group.custom_url || ('cg_id' + gid + '.html'));
                    const groupCount = parseInt(group.post_count_total || 0);
                    const childCount = parseInt(group.category_count || 0);
                    const displayName = safeGroupName + (childCount > 0 ? '（' + childCount + '）' : '');
                    const hasChildren = childCount > 0;
                    tbodyHtml += '<tr class="tree-top-row" data-top-id="' + gid + '"' + (!hasChildren ? ' data-no-children="true"' : '') + '>';
                    tbodyHtml += '<td><input type="checkbox" class="batch-select-top" value="' + gid + '"></td>';
                    tbodyHtml += '<td><span class="tree-level-tag tree-level-top" title="顶级栏目">顶</span></td>';
                    tbodyHtml += '<td>G' + gid + '</td>';
                    tbodyHtml += '<td>' + (hasChildren ? '<button type="button" class="tree-toggle-btn" data-top-id="' + gid + '" title="展开/收起"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button>' : '<button type="button" class="tree-toggle-btn collapsed" data-top-id="' + gid + '" title="无子栏目" disabled><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button>') + '</td>';
                    tbodyHtml += '<td><span class="tree-top-name" style="color:' + safeGroupColor + ';">' + displayName + '</span></td>';
                    tbodyHtml += '<td><select class="form-control" onchange="updateCatgroupTemplate(' + gid + ', this.value)">' + buildTopTemplateOptionsHtml(safeTemplate) + '</select></td>';
                    tbodyHtml += '<td class="catgroup-custom-url-cell" data-catgroup-id="' + gid + '">';
                    tbodyHtml += '<span class="catgroup-custom-url-display" ondblclick="editCatgroupCustomUrl(' + gid + ');" data-default-url="cg_id' + gid + '.html" data-original-url="' + safeUrl + '">' + safeUrl + '</span>';
                    tbodyHtml += '<input type="text" class="catgroup-custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;" value="' + safeUrl + '" onblur="saveCatgroupCustomUrl(' + gid + ', this)" onkeydown="if(event.key===\'Enter\'){this.blur();}if(event.key===\'Escape\'){cancelEditCatgroupCustomUrl(' + gid + ');}">';
                    tbodyHtml += '</td>';
                    tbodyHtml += '<td>' + groupCount + '</td>';
                    tbodyHtml += '<td class="fixed-action-column">';
                    tbodyHtml += '<button type="button" onclick="previewCatgroup(' + gid + ')" class="btn btn-sm btn-secondary">预览</button> ';
                    tbodyHtml += '<button type="button" onclick="editCatgroup(' + gid + ')" class="btn btn-sm btn-primary">编辑</button> ';
                    tbodyHtml += '<button type="button" onclick="deleteCatgroup(' + gid + ', event)" class="btn btn-sm btn-danger">删除</button>';
                    tbodyHtml += '</td>';
                    tbodyHtml += '</tr>';
                };

                const renderCategoryRow = (category) => {
                    const thumbnail_url = category.thumbnail || '';
                    const currentUrl = category.custom_url || 'list_id' + category.id + '.html';
                    const archiveId = parseInt(category._aid || 0);
                    const group = safeCatgroups.find(g => parseInt(g.id) === archiveId);

                    tbodyHtml += '<tr class="tree-sub-row" data-category-id="' + category.id + '" data-archiveid="' + (category.archiveid || 0) + '" data-top-id="' + archiveId + '">';
                    tbodyHtml += '<td><input type="checkbox" class="batch-select-category" value="' + category.id + '"></td>';
                    tbodyHtml += '<td><span class="tree-level-tag tree-level-sub" title="子栏目">子</span></td>';
                    tbodyHtml += '<td>' + category.id + '</td>';
                    if (thumbnail_url) {
                        tbodyHtml += '<td><img src="' + thumbnail_url + '" alt="' + escHtml(category.name) + '" style="width: 45px; height: 45px; object-fit: cover; border-radius: 50%; cursor: pointer;" onclick="replaceCategoryThumbnail(' + category.id + ', this);"></td>';
                    } else {
                        tbodyHtml += '<td><span class="text-muted" style="cursor: pointer;" onclick="replaceCategoryThumbnail(' + category.id + ', this);">点击上传</span></td>';
                    }

                    tbodyHtml += '<td><a href="/admin?tab=articles&category_id=' + category.id + '" class="category-name-link" title="查看该分类下的文章" onclick="return checkArticleCount(this)"><span class="tree-sub-name-wrap">';
                    tbodyHtml += '<span class="tree-branch-icon"></span>';
                    const hasDisplayField = Object.prototype.hasOwnProperty.call(category, 'display');
                    const isCategoryDisabled = parseInt(hasDisplayField ? category.display : 1, 10) === 0;
                    tbodyHtml += '<span class="tree-sub-name-text' + (isCategoryDisabled ? ' is-disabled' : '') + '">' + escHtml(category.name) + '</span></span></a></td>';
                    tbodyHtml += '<td><select class="form-control" onchange="updateCategoryTemplate(' + category.id + ', this.value)">';

                    for (const [templatePath, templateName] of Object.entries(templates)) {
                        const selected = category.template === templatePath ? 'selected' : '';
                        tbodyHtml += '<option value="' + escHtml(templatePath) + '" ' + selected + '>' + escHtml(templateName) + '</option>';
                    }

                    tbodyHtml += '</select></td>';
                    tbodyHtml += '<td class="category-custom-url-cell" data-category-id="' + category.id + '">';
                    tbodyHtml += '<span class="custom-url-display" ondblclick="editCategoryCustomUrl(' + category.id + ');" data-default-url="list_id' + category.id + '.html" data-original-url="' + escHtml(currentUrl) + '">' + escHtml(currentUrl) + '</span>';
                    tbodyHtml += '<input type="text" class="custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;"';
                    tbodyHtml += 'value="' + escHtml(currentUrl) + '"';
                    tbodyHtml += 'placeholder="如: abc.html"';
                    tbodyHtml += 'onblur="saveCategoryCustomUrl(' + category.id + ', this)"';
                    tbodyHtml += 'onkeydown="if(event.key===\'Enter\'){this.blur();}if(event.key===\'Escape\'){cancelEditCategoryCustomUrl(' + category.id + ');}">';
                    tbodyHtml += '</td>';
                    tbodyHtml += '<td>' + (category.post_count || 0) + '</td>';
                    // 开关列临时隐藏：后续可能迁移到模态弹窗，保留 submitCategoryDisplayForm 事件函数用于复用
                    // 原结构（供复制）:
                    // <td><form class="inline-form"><input name="update_display"><input name="category_id"><label class="theme-switch"><input onchange="submitCategoryDisplayForm(this)"></label></form></td>
                    tbodyHtml += '<td class="fixed-action-column">';
                    tbodyHtml += '<button type="button" onclick="previewCategory(' + category.id + ');" class="btn btn-sm btn-secondary">预览</button> ';
                    tbodyHtml += '<button type="button" onclick="openEditCategoryModal(' + category.id + ');" class="btn btn-sm btn-primary">编辑</button> ';
                    tbodyHtml += '<button type="button" onclick="deleteCategoryConfirm(' + category.id + ');" class="btn btn-sm btn-danger">删除</button>';
                    tbodyHtml += '</td>';
                    tbodyHtml += '</tr>';
                };

                const renderVirtualTopRow = (category) => {
                    const currentUrl = category.custom_url || 'list_id' + category.id + '.html';
                    const anchorId = Math.abs(parseInt(category._virtualAnchor || 0, 10) || 0);
                    const anchorGroup = safeCatgroups.find(g => parseInt(g.id || 0, 10) === anchorId);
                    const safeAnchorColor = (anchorGroup && anchorGroup.color) ? escHtml(anchorGroup.color) : '#64748b';
                    tbodyHtml += '<tr class="tree-virtual-top-row" data-category-id="' + category.id + '" data-virtual-top="1" data-virtual-anchor="' + (parseInt(category._virtualAnchor || 0, 10) || 0) + '">';
                    tbodyHtml += '<td><input type="checkbox" class="batch-select-category" value="' + category.id + '"></td>';
                    tbodyHtml += '<td><span class="tree-level-tag tree-level-virtual" title="伪顶级：本质是子栏目，仅在后台列表中以顶级样式混排显示">伪</span></td>';
                    tbodyHtml += '<td>' + category.id + '</td>';
                    tbodyHtml += '<td><button type="button" class="tree-toggle-btn collapsed" title="伪顶级无子栏目" disabled><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></button></td>';
                    tbodyHtml += '<td><a href="/admin?tab=articles&category_id=' + category.id + '" class="category-name-link" title="查看该分类下的文章" onclick="return checkArticleCount(this)"><span class="tree-top-name" style="color:' + safeAnchorColor + ';">' + escHtml(category.name) + '</span></a></td>';
                    tbodyHtml += '<td><select class="form-control" onchange="updateCategoryTemplate(' + category.id + ', this.value)">';
                    for (const [templatePath, templateName] of Object.entries(templates)) {
                        const selected = category.template === templatePath ? 'selected' : '';
                        tbodyHtml += '<option value="' + escHtml(templatePath) + '" ' + selected + '>' + escHtml(templateName) + '</option>';
                    }
                    tbodyHtml += '</select></td>';
                    tbodyHtml += '<td class="category-custom-url-cell" data-category-id="' + category.id + '">';
                    tbodyHtml += '<span class="custom-url-display" ondblclick="editCategoryCustomUrl(' + category.id + ');" data-default-url="list_id' + category.id + '.html" data-original-url="' + escHtml(currentUrl) + '">' + escHtml(currentUrl) + '</span>';
                    tbodyHtml += '<input type="text" class="custom-url-input" draggable="false" ondragstart="return false;" ondrag="return false;" style="display:none; width: 140px; padding: 4px; font-size: 12px; border: 1px solid var(--primary-color); outline: none;"';
                    tbodyHtml += 'value="' + escHtml(currentUrl) + '"';
                    tbodyHtml += 'placeholder="如: abc.html"';
                    tbodyHtml += 'onblur="saveCategoryCustomUrl(' + category.id + ', this)"';
                    tbodyHtml += 'onkeydown="if(event.key===\'Enter\'){this.blur();}if(event.key===\'Escape\'){cancelEditCategoryCustomUrl(' + category.id + ');}">';
                    tbodyHtml += '</td>';
                    tbodyHtml += '<td>' + (category.post_count || 0) + '</td>';
                    tbodyHtml += '<td class="fixed-action-column">';
                    tbodyHtml += '<button type="button" onclick="previewCategory(' + category.id + ');" class="btn btn-sm btn-secondary">预览</button> ';
                    tbodyHtml += '<button type="button" onclick="openEditCategoryModal(' + category.id + ');" class="btn btn-sm btn-primary">编辑</button> ';
                    tbodyHtml += '<button type="button" onclick="deleteCategoryConfirm(' + category.id + ');" class="btn btn-sm btn-danger">删除</button>';
                    tbodyHtml += '</td>';
                    tbodyHtml += '</tr>';
                };

                renderUngroupedTopRow();
                (grouped[0] || []).forEach(renderCategoryRow);
                (virtualAfter[0] || []).forEach(renderVirtualTopRow);
                safeCatgroups.forEach(g => {
                    const gid = parseInt(g.id || 0);
                    (virtualBefore[gid] || []).forEach(renderVirtualTopRow);
                    renderGroupTopRow(g);
                    (grouped[gid] || []).forEach(renderCategoryRow);
                    (virtualAfter[gid] || []).forEach(renderVirtualTopRow);
                });
            }

            if (!tbodyHtml) {
                tbodyHtml = '<tr><td colspan="9" style="text-align:center !important;height:75px;line-height:75px;font-size:16px;color:#64748b;">当前列表暂无数据</td></tr>';
            }

            // 只更新tbody部分，保持表头不变
            const tbody = table.querySelector('tbody');
            if (tbody) {
                tbody.classList.add('tree-collapse-pending');
                tbody.innerHTML = tbodyHtml;
            } else {
                // 如果没有tbody，创建一个
                table.innerHTML += '<tbody>' + tbodyHtml + '</tbody>';
            }

            bindTreeToggleEvents();
            applyTreeCollapsedState();
            bindAdcategoryDragSort();
            resetBatchDeleteSelectionState();
        }

        function getTreeCollapsedMap() {
            try {
                const raw = localStorage.getItem('adcategory_tree_collapsed_map');
                if (!raw) return {};
                const parsed = JSON.parse(raw);
                return parsed && typeof parsed === 'object' ? parsed : {};
            } catch (e) {
                return {};
            }
        }

        function reloadAdcategoryList(options = {}) {
            if (typeof HeaderPagination === 'undefined' || !HeaderPagination.state) {
                window.location.reload();
                return;
            }
            const deltaTotal = parseInt(options.deltaTotal || 0);
            if (deltaTotal !== 0) {
                const nextTotal = Math.max(0, parseInt(HeaderPagination.state.total || 0) + deltaTotal);
                const nextTotalPages = Math.max(1, Math.ceil(nextTotal / HeaderPagination.state.pageSize));
                HeaderPagination.state.total = nextTotal;
                HeaderPagination.state.totalPages = nextTotalPages;
                if (HeaderPagination.state.page > nextTotalPages) {
                    HeaderPagination.state.page = nextTotalPages;
                }
            }
            HeaderPagination.load();
        }

        function setTreeCollapsedMap(map) {
            try {
                localStorage.setItem('adcategory_tree_collapsed_map', JSON.stringify(map || {}));
            } catch (e) {}
        }

        function updateToggleButtonIcon(btn, collapsed) {
            if (!btn) return;
            if (collapsed) btn.classList.add('collapsed');
            else btn.classList.remove('collapsed');
        }

        function applyTreeCollapsedState() {
            const map = getTreeCollapsedMap();
            const subRows = document.querySelectorAll('#categories-tbody tr.tree-sub-row');
            subRows.forEach(row => {
                const topId = String(row.getAttribute('data-top-id') || '0');
                const collapsed = !!map[topId];
                row.style.display = collapsed ? 'none' : '';
            });
            const toggleBtns = document.querySelectorAll('#categories-tbody .tree-toggle-btn');
            toggleBtns.forEach(btn => {
                if (btn.disabled) return;
                const topId = String(btn.getAttribute('data-top-id') || '0');
                const collapsed = !!map[topId];
                updateToggleButtonIcon(btn, collapsed);
            });
            const initialStyle = document.getElementById('initial-tree-collapsed-style');
            if (initialStyle) initialStyle.remove();
            const tbody = document.getElementById('categories-tbody');
            if (tbody) {
                requestAnimationFrame(() => {
                    tbody.classList.remove('tree-collapse-pending');
                });
            }
        }

        function bindTreeToggleEvents() {
            const toggleBtns = document.querySelectorAll('#categories-tbody .tree-toggle-btn');
            toggleBtns.forEach(btn => {
                if (btn.dataset.bound === '1') return;
                btn.dataset.bound = '1';
                btn.addEventListener('click', function() {
                    const topId = String(this.getAttribute('data-top-id') || '0');
                    this.classList.add('animate');
                    const map = getTreeCollapsedMap();
                    map[topId] = !map[topId];
                    setTreeCollapsedMap(map);
                    applyTreeCollapsedState();
                    setTimeout(() => {
                        this.classList.remove('animate');
                    }, 220);
                });
            });
        }

        const adcategoryDragState = {
            type: '',
            id: 0,
            sourceRow: null,
            isVirtual: false
        };
        let adcategoryDragSaving = false;

        function clearAdcategoryDragClasses() {
            document.querySelectorAll('#categories-tbody .drag-source, #categories-tbody .drag-over-before, #categories-tbody .drag-over-after, #categories-tbody .drag-over-parent').forEach(el => {
                el.classList.remove('drag-source', 'drag-over-before', 'drag-over-after', 'drag-over-parent');
            });
        }

        function resetAdcategoryDragState() {
            adcategoryDragState.type = '';
            adcategoryDragState.id = 0;
            adcategoryDragState.sourceRow = null;
            adcategoryDragState.isVirtual = false;
            clearAdcategoryDragClasses();
        }

        function getDragInsertPosition(event, row) {
            const rect = row.getBoundingClientRect();
            return (event.clientY - rect.top) < (rect.height / 2) ? 'before' : 'after';
        }

        function markAdcategoryDragSource() {
            if (adcategoryDragState.sourceRow) {
                adcategoryDragState.sourceRow.classList.add('drag-source');
            }
        }

        function sendAdcategoryDragRequest(payload, successMessage, errorMessage) {
            if (adcategoryDragSaving) return;
            adcategoryDragSaving = true;
            const xhr = new XMLHttpRequest();
            xhr.open('POST', '/admin/adcategory.php', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function() {
                if (xhr.readyState !== 4) return;
                adcategoryDragSaving = false;
                try {
                    const response = JSON.parse(xhr.responseText || '{}');
                    if (response && response.code === 'noop') {
                        return;
                    }
                    if (xhr.status === 200 && response.success) {
                        showToast(response.message || successMessage, 'success', 1500);
                        reloadAdcategoryList();
                    } else {
                        showToast(response.message || errorMessage, 'error', 2000);
                    }
                } catch (e) {
                    showToast(errorMessage, 'error', 2000);
                }
            };
            xhr.send(payload);
        }

        function moveCatgroupByDrag(draggedId, targetId, position) {
            sendAdcategoryDragRequest(
                'action=move_catgroup_drag' +
                '&dragged_id=' + encodeURIComponent(String(draggedId)) +
                '&target_id=' + encodeURIComponent(String(targetId)) +
                '&position=' + encodeURIComponent(position),
                '顶级栏目排序更新成功',
                '顶级栏目排序更新失败'
            );
        }

        function moveCategoryByDrag(draggedId, targetType, targetId, position, virtualTopAnchor = null, virtualTop = null) {
            let extra = '';
            if (virtualTopAnchor !== null && virtualTopAnchor !== undefined && virtualTopAnchor !== '') {
                extra = '&virtual_top_anchor=' + encodeURIComponent(String(virtualTopAnchor));
            }
            if (virtualTop !== null && virtualTop !== undefined && (virtualTop === 0 || virtualTop === 1 || virtualTop === '0' || virtualTop === '1')) {
                extra += '&virtual_top=' + encodeURIComponent(String(parseInt(virtualTop, 10) === 1 ? 1 : 0));
            }
            const isVirtualSort = virtualTopAnchor !== null && virtualTopAnchor !== undefined && virtualTopAnchor !== '';
            sendAdcategoryDragRequest(
                'action=move_category_drag' +
                '&dragged_id=' + encodeURIComponent(String(draggedId)) +
                '&target_type=' + encodeURIComponent(targetType) +
                '&target_id=' + encodeURIComponent(String(targetId)) +
                '&position=' + encodeURIComponent(position) +
                extra,
                isVirtualSort ? '排序更新成功' : (targetType === 'top' ? '子栏目绑定成功' : '子栏目排序更新成功'),
                isVirtualSort ? '排序更新失败' : (targetType === 'top' ? '子栏目绑定失败' : '子栏目排序更新失败')
            );
        }

        function bindAdcategoryDragSort() {
            const topRows = document.querySelectorAll('#categories-tbody tr.tree-top-row');
            topRows.forEach(row => {
                if (row.dataset.dragBound === '1') return;
                row.dataset.dragBound = '1';
                row.classList.add('drag-sort-row');
                const topId = parseInt(row.getAttribute('data-top-id') || '0', 10);

                if (topId > 0) {
                    row.draggable = true;
                    row.title = '拖拽调整顶级栏目顺序';
                    row.addEventListener('dragstart', function(event) {
                        const target = event.target;
                        if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.classList.contains('custom-url-input') || target.classList.contains('catgroup-custom-url-input')) {
                            event.preventDefault();
                            return;
                        }
                        adcategoryDragState.type = 'top';
                        adcategoryDragState.id = topId;
                        adcategoryDragState.sourceRow = this;
                        this.classList.add('drag-source');
                        if (event.dataTransfer) {
                            event.dataTransfer.effectAllowed = 'move';
                            event.dataTransfer.setData('text/plain', 'top:' + topId);
                        }
                    });
                }

                row.addEventListener('dragover', function(event) {
                    if (adcategoryDragSaving || !adcategoryDragState.type) return;
                    if (adcategoryDragState.type === 'top') {
                        if (topId <= 0 || topId === adcategoryDragState.id) return;
                        event.preventDefault();
                        clearAdcategoryDragClasses();
                        markAdcategoryDragSource();
                        this.classList.add(getDragInsertPosition(event, this) === 'before' ? 'drag-over-before' : 'drag-over-after');
                        return;
                    }
                    if (adcategoryDragState.type === 'child') {
                        event.preventDefault();
                        clearAdcategoryDragClasses();
                        markAdcategoryDragSource();
                        this.classList.add(getDragInsertPosition(event, this) === 'before' ? 'drag-over-before' : 'drag-over-after');
                    }
                });

                row.addEventListener('dragleave', function() {
                    setTimeout(() => {
                        if (!this.matches(':hover')) {
                            this.classList.remove('drag-over-before', 'drag-over-after', 'drag-over-parent');
                        }
                    }, 10);
                });

                row.addEventListener('drop', function(event) {
                    if (adcategoryDragSaving || !adcategoryDragState.type) return;
                    event.preventDefault();
                    clearAdcategoryDragClasses();
                    if (adcategoryDragState.type === 'top') {
                        if (topId <= 0 || topId === adcategoryDragState.id) {
                            resetAdcategoryDragState();
                            return;
                        }
                        moveCatgroupByDrag(adcategoryDragState.id, topId, getDragInsertPosition(event, this));
                        resetAdcategoryDragState();
                        return;
                    }
                    if (adcategoryDragState.type === 'child') {
                        const insertPos = getDragInsertPosition(event, this);
                        if (adcategoryDragState.isVirtual) {
                            const anchor = insertPos === 'before' ? -topId : topId;
                            const currentAnchor = parseInt(adcategoryDragState.sourceRow?.getAttribute('data-virtual-anchor') || '0', 10) || 0;
                            if (currentAnchor === anchor) {
                                resetAdcategoryDragState();
                                return;
                            }
                            moveCategoryByDrag(adcategoryDragState.id, 'top', topId, insertPos, anchor);
                        } else {
                            moveCategoryByDrag(adcategoryDragState.id, 'top', topId, insertPos, null, 0);
                        }
                        resetAdcategoryDragState();
                    }
                });

                row.addEventListener('dragend', function() {
                    resetAdcategoryDragState();
                });
            });

            const childRows = document.querySelectorAll('#categories-tbody tr.tree-sub-row, #categories-tbody tr.tree-virtual-top-row');
            childRows.forEach(row => {
                if (row.dataset.dragBound === '1') return;
                row.dataset.dragBound = '1';
                row.classList.add('drag-sort-row');
                const categoryId = parseInt(row.getAttribute('data-category-id') || '0', 10);
                const isVirtualRow = row.getAttribute('data-virtual-top') === '1' || row.classList.contains('tree-virtual-top-row');
                const topId = parseInt(row.getAttribute('data-top-id') || '0', 10);
                row.draggable = true;
                row.title = isVirtualRow ? '拖拽调整伪顶级位置' : '拖拽调整子栏目顺序';

                row.addEventListener('dragstart', function(event) {
                    const target = event.target;
                    if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.classList.contains('custom-url-input') || target.classList.contains('catgroup-custom-url-input')) {
                        event.preventDefault();
                        return;
                    }
                    adcategoryDragState.type = 'child';
                    adcategoryDragState.id = categoryId;
                    adcategoryDragState.sourceRow = this;
                    adcategoryDragState.isVirtual = isVirtualRow;
                    this.classList.add('drag-source');
                    if (event.dataTransfer) {
                        event.dataTransfer.effectAllowed = 'move';
                        event.dataTransfer.setData('text/plain', 'child:' + categoryId);
                    }
                });

                row.addEventListener('dragover', function(event) {
                    if (adcategoryDragSaving || !adcategoryDragState.type || adcategoryDragState.id === categoryId) return;
                    if (adcategoryDragState.type === 'top') {
                        if (isVirtualRow) {
                            event.preventDefault();
                            clearAdcategoryDragClasses();
                            markAdcategoryDragSource();
                            this.classList.add(getDragInsertPosition(event, this) === 'before' ? 'drag-over-before' : 'drag-over-after');
                        }
                        return;
                    }
                    if (this.getAttribute('data-virtual-top') === '1') return;
                    if (adcategoryDragState.isVirtual) return;
                    event.preventDefault();
                    clearAdcategoryDragClasses();
                    markAdcategoryDragSource();
                    this.classList.add(getDragInsertPosition(event, this) === 'before' ? 'drag-over-before' : 'drag-over-after');
                });

                row.addEventListener('dragleave', function() {
                    setTimeout(() => {
                        if (!this.matches(':hover')) {
                            this.classList.remove('drag-over-before', 'drag-over-after');
                        }
                    }, 10);
                });

                row.addEventListener('drop', function(event) {
                    if (adcategoryDragSaving || !adcategoryDragState.type || adcategoryDragState.id === categoryId) return;
                    event.preventDefault();
                    if (adcategoryDragState.type === 'top') {
                        clearAdcategoryDragClasses();
                        if (isVirtualRow) {
                            const insertPos = getDragInsertPosition(event, this);
                            const anchor = insertPos === 'before' ? adcategoryDragState.id : -adcategoryDragState.id;
                            const currentAnchor = parseInt(this.getAttribute('data-virtual-anchor') || '0', 10) || 0;
                            if (currentAnchor === anchor) {
                                resetAdcategoryDragState();
                                return;
                            }
                            moveCategoryByDrag(categoryId, 'top', adcategoryDragState.id, insertPos, anchor);
                        }
                        resetAdcategoryDragState();
                        return;
                    }
                    if (this.getAttribute('data-virtual-top') === '1') {
                        showToast('子栏目无法绑定子栏目', 'warning', 2000);
                        clearAdcategoryDragClasses();
                        resetAdcategoryDragState();
                        return;
                    }
                    if (adcategoryDragState.isVirtual) {
                        showToast('伪顶级请拖到顶级行前/后', 'warning', 2000);
                        clearAdcategoryDragClasses();
                        resetAdcategoryDragState();
                        return;
                    }
                    const position = getDragInsertPosition(event, this);
                    clearAdcategoryDragClasses();
                    moveCategoryByDrag(adcategoryDragState.id, 'child', categoryId, position);
                    resetAdcategoryDragState();
                });

                row.addEventListener('dragend', function() {
                    resetAdcategoryDragState();
                });
            });
        }

        document.addEventListener('change', function(event) {
            const target = event.target;
            if (!target) return;
            if (target.id === 'category-select-all') {
                const checked = !!target.checked;
                document.querySelectorAll('#categories-tbody .batch-select-top, #categories-tbody .batch-select-category').forEach(function(el) {
                    if (!el.disabled) el.checked = checked;
                });
                updateBatchDeleteButtonVisibility();
                return;
            }
            if (target.classList.contains('batch-select-top') || target.classList.contains('batch-select-category')) {
                updateBatchDeleteButtonVisibility();
            }
        });

        // 初始化栏目管理页面
        document.addEventListener('DOMContentLoaded', function() {
            // 延迟执行，确保seapage.php的insertUI先完成
            setTimeout(function() {
                bindTreeToggleEvents();
                applyTreeCollapsedState();
                bindAdcategoryDragSort();
                resetBatchDeleteSelectionState();
                if (typeof HeaderPagination !== 'undefined') {
                    // 设置初始状态，与PHP预渲染保持一致
                    HeaderPagination.state.page = <?php echo $current_page ?? 1; ?>;
                    HeaderPagination.state.pageSize = <?php echo $posts_per_page ?? 30; ?>;
                    HeaderPagination.state.total = <?php echo $total_categories ?? 0; ?>;
                    HeaderPagination.state.totalPages = Math.ceil(HeaderPagination.state.total / HeaderPagination.state.pageSize);

                    // 设置缓存，标记当前页已加载
                    HeaderPagination.cache.page = <?php echo $current_page ?? 1; ?>;
                    HeaderPagination.cache.pageSize = <?php echo $posts_per_page ?? 30; ?>;
                    HeaderPagination.cache.totalPages = HeaderPagination.state.totalPages;

                    // 标记为已初始化，避免重复加载
                    HeaderPagination.initialized = true;

                    // 更新UI，不调用load()避免重复渲染
                    HeaderPagination.updateUI();
                }
            }, 100);
        });
    </script>
<?php
}

// 分类相关的JavaScript函数
?>
<script>
    function reloadAdcategoryTreeAfterDelete() {
        reloadAdcategoryList({
            deltaTotal: -1
        });
    }

    function deleteCategoryConfirm(categoryId) {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', '/admin/admin.php?tab=adcategory&delete_category=' + categoryId + '&ajax=1', true);
        xhr.onload = function() {
            if (xhr.status >= 200 && xhr.status < 300) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        showToast('分类删除成功', 'success', 2000);
                        reloadAdcategoryTreeAfterDelete();
                    } else {
                        showToast('分类删除失败：' + (response.message || '未知错误'), 'error', 2000);
                    }
                } catch (e) {
                    showToast('分类删除失败：响应解析错误', 'error', 2000);
                }
            } else {
                showToast('网络请求失败，请重试', 'error', 2000);
            }
        };
        xhr.onerror = function() {
            showToast('网络连接错误，请检查您的网络', 'error', 2000);
        };
        xhr.send();
    }

    // 替换分类缩略图
    function replaceCategoryThumbnail(categoryId, imgElement) {
        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'image/png,image/jpeg,image/gif';
        fileInput.style.display = 'none';
        document.body.appendChild(fileInput);

        fileInput.click();

        fileInput.addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (!file) return;

            // 验证文件类型
            const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
            const allowedExtensions = ['.png', '.jpg', '.jpeg', '.gif'];
            const fileExtension = '.' + file.name.split('.').pop().toLowerCase();

            if (!allowedTypes.includes(file.type) || !allowedExtensions.includes(fileExtension)) {
                showToast('只能上传 PNG、JPG、GIF 格式的图片文件！', 'error');
                fileInput.remove();
                return;
            }

            const formData = new FormData();
            formData.append('thumbnail', file);
            formData.append('upload_category_thumbnail', '1');

            const xhr = new XMLHttpRequest();
            xhr.open('POST', '/admin/ajaxx.php', true);
            xhr.onload = function() {
                if (xhr.status === 200) {
                    try {
                        const response = JSON.parse(xhr.responseText);
                        if (response.success) {
                            const imageUrl = response.thumbnail_url;
                            // 更新图片预览
                            if (imgElement.tagName === 'IMG') {
                                imgElement.src = imageUrl;
                            } else {
                                // 如果是文字，替换为图片
                                const img = document.createElement('img');
                                img.src = imageUrl;
                                img.alt = '分类缩略图';
                                img.style.width = '60px';
                                img.style.height = '45px';
                                img.style.objectFit = 'cover';
                                img.style.borderRadius = '4px';
                                img.style.cursor = 'pointer';
                                img.onclick = function() {
                                    replaceCategoryThumbnail(categoryId, this);
                                };
                                imgElement.parentNode.replaceChild(img, imgElement);
                            }
                            // 更新数据库
                            updateCategoryThumbnail(categoryId, imageUrl);
                        } else {
                            showToast('上传失败：' + response.message, 'error');
                        }
                    } catch (e) {
                        showToast('上传失败：解析响应失败', 'error');
                    }
                } else {
                    showToast('上传失败：网络错误', 'error');
                }
                fileInput.remove();
            };
            xhr.onerror = function() {
                showToast('上传失败：网络错误', 'error');
                fileInput.remove();
            };
            xhr.send(formData);
        });
    }

    function updateCategoryThumbnail(categoryId, thumbnail) {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        showToast('缩略图更新成功', 'info');
                    } else {
                        showToast('缩略图更新失败', 'error');
                    }
                } catch (e) {
                    showToast('缩略图更新失败', 'error');
                }
            }
        };
        xhr.send('update_category_thumbnail=1&category_id=' + categoryId + '&thumbnail=' + encodeURIComponent(thumbnail));
    }

    function submitCategoryDisplayForm(checkbox) {
        const form = checkbox.closest('form');
        const formData = new FormData(form);
        formData.append('is_ajax', '1');

        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php', true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    try {
                        const response = JSON.parse(xhr.responseText);
                        if (response.success) {
                            showToast('状态更新成功', 'info');
                        } else {
                            checkbox.checked = !checkbox.checked;
                            showToast('状态更新失败', 'error');
                        }
                    } catch (e) {
                        if (xhr.responseText === 'success') {
                            showToast('状态更新成功', 'info');
                        } else {
                            checkbox.checked = !checkbox.checked;
                            showToast('状态更新失败', 'error');
                        }
                    }
                } else {
                    checkbox.checked = !checkbox.checked;
                    showToast('状态更新失败', 'error');
                }
            }
        };
        xhr.send(formData);
    }

    function updateCategoryTemplate(categoryId, template) {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php?action=update_category_template', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        showToast('模板更新成功', 'info');
                        // 不需要刷新整个列表，只更新当前行的模板状态
                        const selectElement = document.querySelector('select[onchange="updateCategoryTemplate(' + categoryId + ', this.value)"]');
                        if (selectElement) {
                            // 保持当前选择状态
                            selectElement.value = template;
                        }
                    } else {
                        showToast('模板更新失败', 'error');
                    }
                } catch (e) {
                    showToast('模板更新失败', 'error');
                }
            }
        };
        xhr.send('category_id=' + categoryId + '&template=' + encodeURIComponent(template));
    }

    function updateCatgroupTemplate(id, template) {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php?action=update_catgroup_template', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        const group = (window.allCatgroups || []).find(g => parseInt(g.id) === parseInt(id));
                        if (group) group.template = template;
                        showToast('顶级模板更新成功', 'info');
                    } else {
                        showToast(response.message || '顶级模板更新失败', 'error');
                    }
                } catch (e) {
                    showToast('顶级模板更新失败', 'error');
                }
            }
        };
        xhr.send('id=' + id + '&template=' + encodeURIComponent(template));
    }

    // 更新分类归档
    function updateCategoryArchive(categoryId, archiveid) {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        showToast('分类归档绑定成功', 'success', 1500);

                        // 更新allCategories数组中的archiveid
                        const category = (window.allCategories || []).find(cat => parseInt(cat.id) === parseInt(categoryId));
                        if (category) {
                            category.archiveid = parseInt(archiveid);
                        }

                        // 重新加载数据（确保显示正确）
                        reloadAdcategoryList();
                    } else {
                        showToast(response.message || '分类归档绑定失败', 'error');
                    }
                } catch (e) {
                    showToast('分类归档绑定失败', 'error');
                }
            }
        };
        xhr.send('action=update_category_archive&category_id=' + categoryId + '&archiveid=' + archiveid);
    }

    function editCategoryCustomUrl(categoryId) {
        const cell = document.querySelector('.category-custom-url-cell[data-category-id="' + categoryId + '"]');
        if (!cell) return;

        const display = cell.querySelector('.custom-url-display');
        const input = cell.querySelector('.custom-url-input');

        display.style.display = 'none';
        input.style.display = 'inline-block';
        input.focus();
        // 选中输入框内容
        input.select();
    }

    function cancelEditCategoryCustomUrl(categoryId) {
        const cell = document.querySelector('.category-custom-url-cell[data-category-id="' + categoryId + '"]');
        if (!cell) return;

        const display = cell.querySelector('.custom-url-display');
        const input = cell.querySelector('.custom-url-input');

        display.style.display = 'inline';
        input.style.display = 'none';
        // 恢复原始值
        const originalUrl = display.getAttribute('data-original-url');
        input.value = originalUrl;
    }

    function saveCategoryCustomUrl(categoryId, input) {
        const cell = document.querySelector('.category-custom-url-cell[data-category-id="' + categoryId + '"]');
        if (!cell) return;

        const display = cell.querySelector('.custom-url-display');
        const newUrl = input.value.trim();
        const defaultUrl = display.getAttribute('data-default-url') || 'list_id' + categoryId + '.html';

        // 如果为空，使用默认URL
        if (!newUrl) {
            saveCategoryCustomUrlToServer(categoryId, '', display, defaultUrl, false);
            return;
        }

        // 验证URL格式
        if (!/^[a-zA-Z0-9_\/.-]+$/.test(newUrl)) {
            showToast('URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠', 'error');
            input.value = display.getAttribute('data-original-url') || defaultUrl;
            return;
        }

        // 检查是否与默认URL相同
        if (newUrl === defaultUrl) {
            saveCategoryCustomUrlToServer(categoryId, '', display, defaultUrl, false);
            return;
        }

        saveCategoryCustomUrlToServer(categoryId, newUrl, display, defaultUrl, true);
    }

    function saveCategoryCustomUrlToServer(categoryId, customUrl, display, defaultUrl, showSuccessToast = true) {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php?action=update_category_custom_url', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        if (showSuccessToast) {
                            showToast('URL更新成功', 'info');
                        }
                        // 更新显示
                        const newDisplayUrl = customUrl || defaultUrl;
                        display.textContent = newDisplayUrl;
                        display.setAttribute('data-original-url', newDisplayUrl);
                        display.style.display = 'inline';
                        const input = display.nextElementSibling;
                        if (input && input.classList.contains('custom-url-input')) {
                            input.style.display = 'none';
                            input.value = newDisplayUrl;
                        }
                    } else {
                        showToast(response.message || 'URL更新失败', 'error');
                        // 恢复原始值
                        const originalUrl = display.getAttribute('data-original-url') || defaultUrl;
                        const input = display.nextElementSibling;
                        if (input && input.classList.contains('custom-url-input')) {
                            input.value = originalUrl;
                        }
                    }
                } catch (e) {
                    showToast('URL更新失败', 'error');
                }
            }
        };
        xhr.send('category_id=' + categoryId + '&custom_url=' + encodeURIComponent(customUrl));
    }

    function editCatgroupCustomUrl(catgroupId) {
        const cell = document.querySelector('.catgroup-custom-url-cell[data-catgroup-id="' + catgroupId + '"]');
        if (!cell) return;
        const display = cell.querySelector('.catgroup-custom-url-display');
        const input = cell.querySelector('.catgroup-custom-url-input');
        if (!display || !input) return;
        display.style.display = 'none';
        input.style.display = 'inline-block';
        input.focus();
        input.select();
    }

    function cancelEditCatgroupCustomUrl(catgroupId) {
        const cell = document.querySelector('.catgroup-custom-url-cell[data-catgroup-id="' + catgroupId + '"]');
        if (!cell) return;
        const display = cell.querySelector('.catgroup-custom-url-display');
        const input = cell.querySelector('.catgroup-custom-url-input');
        if (!display || !input) return;
        display.style.display = 'inline';
        input.style.display = 'none';
        input.value = display.getAttribute('data-original-url') || display.getAttribute('data-default-url') || ('cg_id' + catgroupId + '.html');
    }

    function saveCatgroupCustomUrl(catgroupId, input) {
        const cell = document.querySelector('.catgroup-custom-url-cell[data-catgroup-id="' + catgroupId + '"]');
        if (!cell) return;
        const display = cell.querySelector('.catgroup-custom-url-display');
        if (!display) return;
        const newUrl = (input.value || '').trim();
        const defaultUrl = display.getAttribute('data-default-url') || ('cg_id' + catgroupId + '.html');
        const originalDisplayUrl = (display.getAttribute('data-original-url') || defaultUrl).trim();
        const originalCustomUrl = originalDisplayUrl === defaultUrl ? '' : originalDisplayUrl;
        const targetCustomUrl = newUrl === '' || newUrl === defaultUrl ? '' : newUrl;
        if (targetCustomUrl === originalCustomUrl) {
            display.style.display = 'inline';
            input.style.display = 'none';
            input.value = originalDisplayUrl;
            return;
        }
        if (!newUrl) {
            saveCatgroupCustomUrlToServer(catgroupId, '', display, defaultUrl, false);
            return;
        }
        if (!/^[a-zA-Z0-9_\/.-]+$/.test(newUrl)) {
            showToast('URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠', 'error');
            input.value = display.getAttribute('data-original-url') || defaultUrl;
            return;
        }
        if (newUrl === defaultUrl) {
            saveCatgroupCustomUrlToServer(catgroupId, '', display, defaultUrl, false);
            return;
        }
        saveCatgroupCustomUrlToServer(catgroupId, newUrl, display, defaultUrl, true);
    }

    function saveCatgroupCustomUrlToServer(catgroupId, customUrl, display, defaultUrl, showSuccessToast = true) {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', '/admin/adcategory.php?action=update_catgroup_custom_url', true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function() {
            if (xhr.readyState !== 4) return;
            if (xhr.status === 200) {
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (response.success) {
                        if (showSuccessToast) showToast('顶级URL更新成功', 'info');
                        const newDisplayUrl = customUrl || defaultUrl;
                        display.textContent = newDisplayUrl;
                        display.setAttribute('data-original-url', newDisplayUrl);
                        display.style.display = 'inline';
                        const input = display.nextElementSibling;
                        if (input && input.classList.contains('catgroup-custom-url-input')) {
                            input.style.display = 'none';
                            input.value = newDisplayUrl;
                        }
                        const group = (window.allCatgroups || []).find(g => parseInt(g.id) === parseInt(catgroupId));
                        if (group) group.custom_url = customUrl || '';
                    } else {
                        showToast(response.message || '顶级URL更新失败', 'error');
                        const originalUrl = display.getAttribute('data-original-url') || defaultUrl;
                        const input = display.nextElementSibling;
                        if (input && input.classList.contains('catgroup-custom-url-input')) input.value = originalUrl;
                    }
                } catch (e) {
                    showToast('顶级URL更新失败', 'error');
                }
            } else {
                showToast('顶级URL更新失败', 'error');
            }
        };
        xhr.send('id=' + catgroupId + '&custom_url=' + encodeURIComponent(customUrl));
    }

    function previewCategory(categoryId) {
        const cell = document.querySelector('.category-custom-url-cell[data-category-id="' + categoryId + '"]');
        if (!cell) return;

        const display = cell.querySelector('.custom-url-display');
        const currentUrl = display.textContent.trim();
        const previewUrl = currentUrl.startsWith('http') ? currentUrl : window.location.origin + '/' + currentUrl;

        window.open(previewUrl, '_blank');
    }

    function previewCatgroup(catgroupId) {
        const group = (window.allCatgroups || []).find(g => parseInt(g.id) === parseInt(catgroupId));
        let currentUrl = '';
        if (group && group.custom_url) currentUrl = String(group.custom_url).trim();
        if (!currentUrl) currentUrl = 'cg_id' + catgroupId + '.html';
        const previewUrl = currentUrl.startsWith('http') ? currentUrl : (window.location.origin + '/' + currentUrl.replace(/^\/+/, ''));
        window.open(previewUrl, '_blank');
    }

    // ============================================
    // 顶级栏目（无选项卡/无拖拽绑定）
    // ============================================
    window.allCategories = window.allCategories || <?php echo json_encode(qk_get_adcategory_client_categories($categories), JSON_UNESCAPED_UNICODE); ?>;
    window.allCatgroups = window.allCatgroups || <?php echo json_encode($catgroups, JSON_UNESCAPED_UNICODE); ?>;
    window.allCatgroupsForModal = window.allCatgroupsForModal || <?php echo json_encode($all_catgroups_for_modal, JSON_UNESCAPED_UNICODE); ?>;

    function editCatgroup(id) {
        const group = (window.allCatgroups || []).find(g => parseInt(g.id) === parseInt(id));
        if (!group) return;
        openCatgroupModal('edit', group);
    }

    function openCatgroupModal(mode, group = null) {
        const isEdit = mode === 'edit';
        let modalContainer = document.getElementById('modal-container');
        if (!modalContainer) {
            modalContainer = document.createElement('div');
            modalContainer.id = 'modal-container';
            document.body.appendChild(modalContainer);
        }
        modalContainer.innerHTML = '';

        const defaultNextId = (window.allCatgroups || []).reduce((m, g) => Math.max(m, parseInt(g.id || 0)), 0) + 1;
        const templateOptions = window.catgroupTemplateOptions || {
            'catgroup.html': 'catgroup.html（默认）'
        };
        let templateHtml = '';
        for (const [path, label] of Object.entries(templateOptions)) {
            templateHtml += '<option value="' + escHtml(path) + '">' + escHtml(label) + '</option>';
        }

        const gid = isEdit ? parseInt(group.id) : 0;
        const initName = isEdit ? String(group.name || '') : '';
        const initColor = (isEdit && group.color) ? String(group.color) : '#64748b';
        const initSort = isEdit ? parseInt(group.sort || 1) : ((window.allCatgroups || []).length + 1);
        const initTemplate = (isEdit && group.template) ? String(group.template) : 'catgroup.html';
        const initUrl = (isEdit && group.custom_url) ? String(group.custom_url) : ('cg_id' + defaultNextId + '.html');
        const initSeoTitle = (isEdit && group.seo_title) ? String(group.seo_title) : '';
        const initSeoKeywords = (isEdit && group.seo_keywords) ? String(group.seo_keywords) : '';
        const initSeoDescription = (isEdit && group.seo_description) ? String(group.seo_description) : '';

        const modal = document.createElement('div');
        modal.className = 'modal';
        modal.innerHTML = `
            <div class="modal-backdrop"></div>
            <div class="modal-content catgroup-modal-content">
                <div class="modal-header">
                    <h3>${isEdit ? '编辑顶级栏目' : '新增顶级栏目'}</h3>
                    <button type="button" class="modal-close">×</button>
                </div>
                <div class="modal-body">
                    <form id="catgroup-form" method="post">
                        <div class="form-section">
                            <div class="form-group" style="display:flex; align-items:end; gap:8px;">
                                <div style="flex:1;">
                                    <label for="catgroup-name">顶级栏目名称</label>
                                    <div style="position:relative;">
                                        <input type="text" id="catgroup-name" class="form-control" placeholder="请输入顶级栏目名称" required style="padding-right:42px;" value="${escHtml(initName)}">
                                        <button type="button" id="catgroup-color-btn" title="选择颜色" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);width:24px;height:24px;border-radius:50%;border:1px solid #d1d5db;background:${escHtml(initColor)};cursor:pointer;padding:0;transition:transform .2s ease, box-shadow .2s ease, border-color .2s ease;"></button>
                                        <div id="catgroup-color-panel" style="display:none;position:absolute;right:8px;top:calc(100% + 8px);z-index:20;width:220px;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 10px 24px rgba(0,0,0,.14);padding:10px;">
                                            <div style="display:grid;grid-template-columns:repeat(8,1fr);gap:6px;margin-bottom:10px;">
                                                <button type="button" class="catgroup-color-swatch" data-color="#64748b" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#64748b;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#ef4444" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#ef4444;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#f97316" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#f97316;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#eab308" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#eab308;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#22c55e" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#22c55e;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#06b6d4" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#06b6d4;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#8b5cf6" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#8b5cf6;cursor:pointer;"></button>
                                                <button type="button" class="catgroup-color-swatch" data-color="#ec4899" style="width:20px;height:20px;border-radius:50%;border:1px solid #d1d5db;background:#ec4899;cursor:pointer;"></button>
                                            </div>
                                            <label for="catgroup-color-text" style="display:block;font-size:12px;color:#6b7280;margin-bottom:6px;">自定义颜色值</label>
                                            <input type="text" id="catgroup-color-text" class="form-control" value="${escHtml(initColor)}" maxlength="7" placeholder="#64748b">
                                        </div>
                                    </div>
                                </div>
                                <div style="flex:1;">
                                    <label for="catgroup-url">顶级栏目URL</label>
                                    <div style="position:relative;">
                                        <input type="text" id="catgroup-url" class="form-control" value="${escHtml(initUrl)}" placeholder="如：cg_id55.html" style="padding-right:42px;">
                                        <button type="button" id="catgroup-url-reset" title="恢复默认URL" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);width:24px;height:24px;border-radius:50%;border:1px solid #d1d5db;background:#f8fafc;color:#475569;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;transition:all .2s ease;">
                                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
                                                <path d="M9 14L5 10l4-4"></path>
                                                <path d="M5 10h8a4 4 0 0 1 0 8h-1"></path>
                                            </svg>
                                        </button>
                                    </div>
                                </div>
                            </div>

                            <div class="form-group" style="display:flex; align-items:end; gap:8px;">
                                <div style="flex:1;">
                                    <label for="catgroup-template">绑定模板</label>
                                    <select id="catgroup-template" class="form-control">${templateHtml}</select>
                                </div>
                                <div style="flex:1;">
                                    <label for="catgroup-sort">排序</label>
                                    <input type="number" id="catgroup-sort" class="form-control" min="1" value="${isFinite(initSort) ? initSort : 1}">
                                </div>
                            </div>

                            <div class="form-group">
                                <label for="catgroup-seo-title">SEO标题</label>
                                <input type="text" id="catgroup-seo-title" class="form-control" value="${escHtml(initSeoTitle)}" placeholder="请输入SEO标题">
                            </div>
                            <div class="form-group">
                                <label for="catgroup-seo-keywords">SEO关键词</label>
                                <input type="text" id="catgroup-seo-keywords" class="form-control" value="${escHtml(initSeoKeywords)}" placeholder="请输入SEO关键词，多个关键词用逗号分隔">
                            </div>
                            <div class="form-group">
                                <label for="catgroup-seo-description">SEO描述</label>
                                <textarea id="catgroup-seo-description" class="form-control" placeholder="请输入SEO描述" rows="3" style="min-height:82px;resize:vertical;">${escHtml(initSeoDescription)}</textarea>
                            </div>
                        </div>
                        <div class="modal-actions">
                            <button type="submit" id="catgroup-submit" class="submit-button" style="background-color:var(--primary-color);width:480px;margin:0 auto;display:block;transition:opacity 0.2s ease;">立即保存</button>
                        </div>
                        <style>
                          .submit-button:hover{opacity:0.9;}
                          .catgroup-modal-content{height:600px !important;max-height:600px !important;}
                          .catgroup-modal-content .modal-body{padding-top:20px !important;padding-bottom:20px !important;}
                          .catgroup-modal-content .form-section{gap:10px !important;min-width:0 !important;}
                          .catgroup-modal-content .form-group{margin:0 !important;}
                          .catgroup-modal-content label{margin-bottom:6px !important;}
                          .catgroup-modal-content #catgroup-seo-description{margin-bottom:16px !important;}
                          .catgroup-modal-content .modal-actions{margin-top:-5px !important;}
                        </style>
                    </form>
                </div>
            </div>
        `;
        modalContainer.appendChild(modal);
        if (typeof addModalStyles === 'function') addModalStyles();
        modal.style.display = 'flex';
        setTimeout(() => modal.classList.add('modal-visible'), 10);

        const closeModal = () => {
            modal.classList.remove('modal-visible');
            setTimeout(() => {
                if (modalContainer) modalContainer.innerHTML = '';
                document.removeEventListener('keydown', handleEscKey);
                document.removeEventListener('click', handleColorPanelOutside);
            }, 300);
        };
        const closeButtons = modal.querySelectorAll('.modal-close');
        closeButtons.forEach(button => button.addEventListener('click', () => closeModal()));
        const backdrop = modal.querySelector('.modal-backdrop');
        backdrop.addEventListener('click', () => closeModal());
        const handleEscKey = function(e) {
            if (e.key === 'Escape') closeModal();
        };
        document.addEventListener('keydown', handleEscKey);

        const submitBtn = document.getElementById('catgroup-submit');
        const nameInput = document.getElementById('catgroup-name');
        const urlInput = document.getElementById('catgroup-url');
        const urlResetBtn = document.getElementById('catgroup-url-reset');
        const sortInput = document.getElementById('catgroup-sort');
        const templateInput = document.getElementById('catgroup-template');
        const seoTitleInput = document.getElementById('catgroup-seo-title');
        const seoKeywordsInput = document.getElementById('catgroup-seo-keywords');
        const seoDescriptionInput = document.getElementById('catgroup-seo-description');

        const colorPanel = document.getElementById('catgroup-color-panel');
        const colorSwatches = modal.querySelectorAll('.catgroup-color-swatch');
        const colorTextInput = document.getElementById('catgroup-color-text');
        const colorBtn = document.getElementById('catgroup-color-btn');

        setTimeout(() => nameInput && nameInput.focus(), 30);

        if (templateInput) templateInput.value = initTemplate;

        const syncColorPreview = (val) => {
            if (colorBtn) colorBtn.style.background = val;
        };
        syncColorPreview(initColor);
        if (colorBtn && colorPanel) {
            colorBtn.addEventListener('click', function(e) {
                e.stopPropagation();
                colorPanel.style.display = colorPanel.style.display === 'none' || colorPanel.style.display === '' ? 'block' : 'none';
            });
            colorBtn.addEventListener('mouseenter', function() {
                this.style.transform = 'translateY(-50%) scale(1.1)';
                this.style.boxShadow = '0 0 0 3px rgba(109, 126, 223, 0.18)';
                this.style.borderColor = '#6d7edf';
            });
            colorBtn.addEventListener('mouseleave', function() {
                this.style.transform = 'translateY(-50%)';
                this.style.boxShadow = 'none';
                this.style.borderColor = '#d1d5db';
            });
        }
        if (colorSwatches && colorSwatches.length) {
            colorSwatches.forEach(btn => {
                btn.addEventListener('mouseenter', function() {
                    this.style.transform = 'scale(1.14)';
                    this.style.boxShadow = '0 4px 10px rgba(0,0,0,.22)';
                    this.style.borderColor = '#111827';
                });
                btn.addEventListener('mouseleave', function() {
                    this.style.transform = 'scale(1)';
                    this.style.boxShadow = 'none';
                    this.style.borderColor = '#d1d5db';
                });
                btn.addEventListener('click', function() {
                    const val = this.getAttribute('data-color') || '#64748b';
                    if (colorTextInput) colorTextInput.value = val;
                    syncColorPreview(val);
                    if (colorPanel) colorPanel.style.display = 'none';
                });
            });
        }
        if (colorTextInput) {
            colorTextInput.addEventListener('input', function() {
                const v = (this.value || '').trim();
                if (/^#[0-9a-fA-F]{6}$/.test(v)) syncColorPreview(v);
            });
        }
        const handleColorPanelOutside = function(e) {
            if (!colorPanel || !colorBtn) return;
            if (colorPanel.style.display !== 'block') return;
            if (!colorPanel.contains(e.target) && e.target !== colorBtn) {
                colorPanel.style.display = 'none';
            }
        };
        document.addEventListener('click', handleColorPanelOutside);

        if (urlResetBtn && urlInput) {
            urlResetBtn.addEventListener('click', function() {
                const targetId = isEdit ? gid : defaultNextId;
                urlInput.value = 'cg_id' + targetId + '.html';
            });
            urlResetBtn.addEventListener('mouseenter', function() {
                this.style.background = '#eef2ff';
                this.style.borderColor = '#6d7edf';
                this.style.color = '#3f51b5';
                this.style.boxShadow = '0 0 0 3px rgba(109,126,223,.15)';
            });
            urlResetBtn.addEventListener('mouseleave', function() {
                this.style.background = '#f8fafc';
                this.style.borderColor = '#d1d5db';
                this.style.color = '#475569';
                this.style.boxShadow = 'none';
            });
        }

        const form = document.getElementById('catgroup-form');
        form.addEventListener('submit', function(e) {
            e.preventDefault();
            const name = (nameInput.value || '').trim();
            const sort = parseInt(sortInput.value || 1);
            const customUrl = (urlInput.value || '').trim();
            const color = (colorTextInput.value || '').trim();
            const template = (templateInput.value || 'catgroup.html').trim();
            const seoTitle = (seoTitleInput.value || '').trim();
            const seoKeywords = (seoKeywordsInput.value || '').trim();
            const seoDescription = (seoDescriptionInput.value || '').trim();

            if (!name) return showToast('顶级栏目名称不能为空', 'error');
            const duplicate = (window.allCatgroups || []).some(g => (!isEdit || parseInt(g.id) !== gid) && String(g.name || '').toLowerCase() === name.toLowerCase());
            if (duplicate) return showToast('顶级栏目名称不可重复', 'error', 2000);
            if (customUrl && !/^[a-zA-Z0-9_\/.-]+$/.test(customUrl)) return showToast('URL格式错误，只能包含字母、数字、下划线、连字符、点和斜杠', 'error');
            if (color && !/^#[0-9a-fA-F]{6}$/.test(color)) return showToast('颜色格式不正确', 'error');

            submitBtn.disabled = true;
            submitBtn.textContent = '提交中...';

            const xhr = new XMLHttpRequest();
            xhr.open('POST', '/admin/adcategory.php', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function() {
                if (xhr.readyState !== 4) return;
                submitBtn.disabled = false;
                submitBtn.textContent = '立即保存';
                if (xhr.status !== 200) {
                    showToast(isEdit ? '顶级栏目更新失败' : '顶级栏目添加失败', 'error');
                    return;
                }
                try {
                    const response = JSON.parse(xhr.responseText);
                    if (!response.success) {
                        showToast(response.message || (isEdit ? '顶级栏目更新失败' : '顶级栏目添加失败'), 'error');
                        return;
                    }

                    if (!isEdit) {
                        const newArchiveId = response.id;
                        const newArchiveColor = response.color || color || '#64748b';
                        const newArchiveTemplate = response.template || template || 'catgroup.html';
                        const newArchiveCustomUrl = response.custom_url || customUrl || ('cg_id' + newArchiveId + '.html');
                        window.allCatgroups.push({
                            id: newArchiveId,
                            name: name,
                            color: newArchiveColor,
                            sort: sort,
                            template: newArchiveTemplate,
                            custom_url: newArchiveCustomUrl,
                            seo_title: seoTitle,
                            seo_keywords: seoKeywords,
                            seo_description: seoDescription,
                            category_count: 0,
                            post_count_total: 0
                        });
                        reloadAdcategoryList();
                        closeModal();
                        showToast('顶级栏目添加成功', 'success');
                        return;
                    }

                    const idx = window.allCatgroups.findIndex(g => parseInt(g.id) === gid);
                    if (idx !== -1) {
                        window.allCatgroups[idx].name = name;
                        window.allCatgroups[idx].color = response.color || color;
                        window.allCatgroups[idx].sort = response.sort || sort;
                        window.allCatgroups[idx].template = response.template || template;
                        window.allCatgroups[idx].custom_url = response.custom_url || customUrl;
                        window.allCatgroups[idx].seo_title = seoTitle;
                        window.allCatgroups[idx].seo_keywords = seoKeywords;
                        window.allCatgroups[idx].seo_description = seoDescription;
                    }
                    reloadAdcategoryList();
                    closeModal();
                    showToast('顶级栏目更新成功', 'success');
                } catch (e) {
                    showToast(isEdit ? '顶级栏目更新失败' : '顶级栏目添加失败', 'error');
                }
            };

            const payload =
                'action=' + (isEdit ? 'edit_catgroup' : 'add_catgroup') +
                (isEdit ? ('&id=' + encodeURIComponent(String(gid))) : '') +
                '&name=' + encodeURIComponent(name) +
                '&sort=' + encodeURIComponent(String(sort > 0 ? sort : 1)) +
                '&custom_url=' + encodeURIComponent(customUrl) +
                '&color=' + encodeURIComponent(color || '#64748b') +
                '&template=' + encodeURIComponent(template || 'catgroup.html') +
                '&seo_title=' + encodeURIComponent(seoTitle) +
                '&seo_keywords=' + encodeURIComponent(seoKeywords) +
                '&seo_description=' + encodeURIComponent(seoDescription);
            xhr.send(payload);
        });
    }

    function addCatgroup() {
        openCatgroupModal('add');
    }

    // 删除归档
    function deleteCatgroup(id, event) {
        if (event) {
            event.stopPropagation();
            event.preventDefault();
        }

        const doDeleteCatgroup = function() {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', '/admin/adcategory.php', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    try {
                        const response = JSON.parse(xhr.responseText);
                        if (response.success) {
                            const index = window.allCatgroups.findIndex(g => parseInt(g.id) === parseInt(id));
                            if (index !== -1) {
                                window.allCatgroups.splice(index, 1);
                            }
                            reloadAdcategoryList();
                            showToast('顶级栏目删除成功', 'success');
                        } else {
                            showToast(response.message || '顶级栏目删除失败', 'error', 2000);
                        }
                    } catch (e) {
                        showToast('顶级栏目删除失败', 'error');
                    }
                }
            };
            xhr.send('action=delete_catgroup&id=' + id);
        };

        const precheckXhr = new XMLHttpRequest();
        precheckXhr.open('POST', '/admin/adcategory.php', true);
        precheckXhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        precheckXhr.onreadystatechange = function() {
            if (precheckXhr.readyState !== 4 || precheckXhr.status !== 200) return;
            try {
                const response = JSON.parse(precheckXhr.responseText);
                if (!response.success) {
                    showToast(response.message || '顶级栏目删除失败', 'error', 2000);
                    return;
                }
                if (!response.can_delete) {
                    showToast(response.message || '先删除子栏目 才可删除顶级栏目', 'warning', 2000);
                    return;
                }
                if (typeof window.confirmDialog === 'function') {
                    window.confirmDialog('确定要删除这个顶级栏目吗？', doDeleteCatgroup, function() {});
                    return;
                }
                if (window.confirm('确定要删除这个顶级栏目吗？')) {
                    doDeleteCatgroup();
                }
            } catch (e) {
                showToast('顶级栏目删除失败', 'error', 2000);
            }
        };
        precheckXhr.send('action=delete_catgroup&id=' + id + '&check_only=1');
    }
</script>
