prepare('SELECT * FROM categories WHERE key = ? LIMIT 1'); $stCat->execute([$cat]); $catRow = $stCat->fetch(); if (!$catRow) { http_response_code(404); die('分类不存在'); } $saved = isset($_GET['ok']); $errorMsg = ''; // ---------- 保存处理 ---------- if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') { if (!csrf_verify()) { $errorMsg = '安全校验失败,请刷新页面重试。'; } else { $raw = (string)($_POST['data'] ?? ''); $data = json_decode($raw, true); if (!is_array($data)) { $errorMsg = '提交数据格式错误。'; } else { try { $pdo->beginTransaction(); // 现有块 $st = $pdo->prepare('SELECT id FROM nav_blocks WHERE cat_id = ?'); $st->execute([(int)$catRow['id']]); $oldBlocks = array_map('intval', array_column($st->fetchAll(), 'id')); // 现有链接(按块分组) $oldItemsByBlock = []; foreach ($oldBlocks as $bid) { $st = $pdo->prepare('SELECT id FROM nav_items WHERE block_id = ?'); $st->execute([$bid]); $oldItemsByBlock[$bid] = array_map('intval', array_column($st->fetchAll(), 'id')); } // 本轮保留的块 id $keepBlocks = []; $keepItems = []; foreach ($data as $b) { if (is_array($b) && isset($b['title'])) { $id = (int)($b['id'] ?? 0); if ($id > 0) $keepBlocks[] = $id; foreach (($b['items'] ?? []) as $it) { if (is_array($it) && (int)($it['id'] ?? 0) > 0) { $keepItems[] = (int)$it['id']; } } } } // 删除被移除的块及其链接 $delBlocks = array_values(array_diff($oldBlocks, $keepBlocks)); foreach ($delBlocks as $dbid) { $pdo->prepare('DELETE FROM nav_items WHERE block_id = ?')->execute([$dbid]); $pdo->prepare('DELETE FROM nav_blocks WHERE id = ?')->execute([$dbid]); unset($oldItemsByBlock[$dbid]); } // 删除被移除的链接 foreach ($oldItemsByBlock as $bid => $ids) { $del = array_values(array_diff($ids, $keepItems)); foreach ($del as $did) { $pdo->prepare('DELETE FROM nav_items WHERE id = ?')->execute([$did]); } } // 写回块与链接 $stInsBlock = $pdo->prepare('INSERT INTO nav_blocks (cat_id, title, sort) VALUES (?,?,?)'); $stUpdBlock = $pdo->prepare('UPDATE nav_blocks SET title = ?, sort = ? WHERE id = ?'); $stInsItem = $pdo->prepare('INSERT INTO nav_items (block_id, label, url, note, sort) VALUES (?,?,?,?,?)'); $stUpdItem = $pdo->prepare('UPDATE nav_items SET label = ?, url = ?, note = ?, sort = ? WHERE id = ? AND block_id = ?'); $blockSort = 0; foreach ($data as $b) { if (!is_array($b) || !isset($b['title'])) continue; $title = trim((string)$b['title']); if ($title === '') continue; $bid = (int)($b['id'] ?? 0); $blockBelongs = in_array($bid, $oldBlocks, true); if ($bid > 0 && $blockBelongs) { $stUpdBlock->execute([$title, $blockSort, $bid]); } else { $stInsBlock->execute([(int)$catRow['id'], $title, $blockSort]); $bid = (int)$pdo->lastInsertId(); } $itemSort = 0; foreach (($b['items'] ?? []) as $it) { if (!is_array($it)) continue; $label = trim((string)($it['label'] ?? '')); $url = trim((string)($it['url'] ?? '')); $note = trim((string)($it['note'] ?? '')); if ($label === '' && $url === '') continue; $iid = (int)($it['id'] ?? 0); if ($iid > 0 && in_array($iid, $oldItemsByBlock[$bid] ?? [], true)) { $stUpdItem->execute([$label, $url, $note, $itemSort, $iid, $bid]); } else { $stInsItem->execute([$bid, $label, $url, $note, $itemSort]); } $itemSort++; } $blockSort++; } $pdo->commit(); touch_last_updated(); header('Location: nav.php?c=' . urlencode($cat) . '&ok=1'); exit; } catch (Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $errorMsg = '保存失败:' . $e->getMessage(); } } } } // ---------- 读取现有数据用于展示 ---------- $st = $pdo->prepare('SELECT * FROM nav_blocks WHERE cat_id = ? ORDER BY sort ASC, id ASC'); $st->execute([(int)$catRow['id']]); $blocks = $st->fetchAll(); $itemsMap = []; if ($blocks) { $ids = array_map('intval', array_column($blocks, 'id')); $in = implode(',', array_fill(0, count($ids), '?')); $st = $pdo->prepare("SELECT * FROM nav_items WHERE block_id IN ($in) ORDER BY sort ASC, id ASC"); $st->execute($ids); foreach ($st->fetchAll() as $it) { $itemsMap[(int)$it['block_id']][] = $it; } } $catNames = []; foreach ($catKeys as $ck) { $row = $pdo->query("SELECT key, name FROM categories WHERE key = '" . $ck . "'")->fetch(); $catNames[$ck] = $row['name'] ?? $ck; } $csrfVal = csrf_token(); layout_head('导航内容管理'); admin_topbar('nav'); ?>

导航内容管理

管理 的导航块与链接;块内最多 15 个链接(3 小列 × 5 行),超出部分请拆分到新块。

⚡ 批量导入导航链接(

在下面一次粘贴多行内容,点击「解析导入」后会按格式自动生成导航块,并追加到下方编辑器末尾,可再编辑确认后统一点「保存全部修改」。
每行一条,支持几种写法: 名称 | 网址 [ | 备注](也兼容 网址 | 名称)或 [名称](网址)名称 https://网址;以 ## 块名 开头的行开启一个新导航块;未写块名则归入“批量导入”。 单个块最多 15 个链接,超出会自动拆分为 块名 2块名 3

导入仅是“填入编辑区”,不会立即生效,请务必点击下方的「保存全部修改」。