From 10b653e30f40c0470a78b74d6b993510d2915192 Mon Sep 17 00:00:00 2001 From: CNWei Date: Sun, 1 Nov 2026 20:27:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E5=90=8E=E5=8F=B0=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=B7=A6=E4=BE=A7=E5=AF=BC=E8=88=AA=20+=20=E5=8F=B3?= =?UTF-8?q?=E4=BE=A7=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 原来的横向 Tabs 换成库里的 Menu,左侧固定导航、右侧面板 - 首页管理收成一组:侧边栏 / 内容卡片 / 页头 三个子项,页头从顶层移入 - 页头、资料仍需手动保存,保存/撤销按钮留在各自面板底部 - 窄屏(<=860px)导航折到内容上方 - 后台内容区宽度不变(1340),导航列 190px + 24px 间距 --- stash-web/src/App.css | 34 +++++ stash-web/src/components/admin/AdminNext.tsx | 144 +++++++++++-------- 2 files changed, 116 insertions(+), 62 deletions(-) diff --git a/stash-web/src/App.css b/stash-web/src/App.css index 22e3633..f287654 100644 --- a/stash-web/src/App.css +++ b/stash-web/src/App.css @@ -53,6 +53,40 @@ padding: 0 var(--su-space-5); } +/* 后台:左侧导航 + 右侧内容 */ +.admin-shell { + display: grid; + grid-template-columns: 190px minmax(0, 1fr); + gap: var(--su-space-6); + align-items: start; + margin-top: var(--su-space-5); +} + +.admin-nav { + position: sticky; + top: calc(var(--su-header-height) + var(--su-space-5)); + padding-right: var(--su-space-4); + border-right: 1px solid var(--su-border); +} + +.admin-panel { + min-width: 0; +} + +@media (max-width: 860px) { + .admin-shell { + grid-template-columns: 1fr; + } + + .admin-nav { + position: static; + padding-right: 0; + border-right: none; + border-bottom: 1px solid var(--su-border); + padding-bottom: var(--su-space-3); + } +} + /* 后台「首页 Logo」的实时预览:外观交给库里的 Brand,这里只管外框 */ .brand-preview { display: flex; diff --git a/stash-web/src/components/admin/AdminNext.tsx b/stash-web/src/components/admin/AdminNext.tsx index ef6323b..394e72c 100644 --- a/stash-web/src/components/admin/AdminNext.tsx +++ b/stash-web/src/components/admin/AdminNext.tsx @@ -7,13 +7,13 @@ import { ICON_NAMES, IconButton, Input, + Menu, Modal, Notice, PasswordInput, Select, Space, Switch, - Tabs, Tag, Text, Textarea, @@ -21,6 +21,7 @@ import { Toaster, toast, type IconName, + type MenuItem, } from '@stash/ui' import { useEffect, useMemo, useRef, useState } from 'react' import { changePassword, fetchSession, logout, type SessionState } from '../../auth' @@ -63,7 +64,31 @@ import type { Project } from '../../api' * - 内容是条目,挂在分组上; * - 侧边栏、首页卡片都只是「勾选分组」的展示组合。 */ -type TabKey = 'groups' | 'content' | 'home' | 'header' | 'profile' | 'system' +type TabKey = + | 'groups' + | 'content' + | 'home-sidebar' + | 'home-cards' + | 'home-header' + | 'profile' + | 'system' + +/** 左侧导航:首页管理下面挂三个子项,页头也归到这一组 */ +const ADMIN_NAV: MenuItem[] = [ + { key: 'groups', label: '分组管理' }, + { key: 'content', label: '数据管理' }, + { + key: 'home', + label: '首页管理', + children: [ + { key: 'home-sidebar', label: '侧边栏' }, + { key: 'home-cards', label: '内容卡片' }, + { key: 'home-header', label: '页头' }, + ], + }, + { key: 'profile', label: '资料' }, + { key: 'system', label: '系统' }, +] const FORMAT_OPTIONS = [ { value: 'apps', label: '应用' }, @@ -276,33 +301,28 @@ export function AdminNext() { )} {authenticated && draft !== null && ( - <> - setTab(key as TabKey)} - /> - - {tab === 'groups' && ( - setPending({ kind: 'group', id: key, title })} +
+ - {tab === 'content' && } +
+ {tab === 'groups' && ( + setPending({ kind: 'group', id: key, title })} + /> + )} - {tab === 'home' && ( -
+ {tab === 'content' && } + + {tab === 'home-sidebar' && (
侧边栏
+ )} -
- + {tab === 'home-cards' && (
内容卡片 @@ -337,44 +357,44 @@ export function AdminNext() { onDelete={(id, title) => setPending({ kind: 'card', id: String(id), title })} />
-
- )} + )} - {tab === 'header' && ( - setDraft({ ...draft, header })} - site={draft.site} - onChangeSite={(site) => setDraft({ ...draft, site })} - /> - )} + {tab === 'home-header' && ( + setDraft({ ...draft, header })} + site={draft.site} + onChangeSite={(site) => setDraft({ ...draft, site })} + /> + )} - {tab === 'profile' && ( - setDraft({ ...draft, profile })} - /> - )} + {tab === 'profile' && ( + setDraft({ ...draft, profile })} + /> + )} - {tab === 'system' && } + {tab === 'system' && } - {(tab === 'header' || tab === 'profile') && ( - - - - - )} - + {(tab === 'home-header' || tab === 'profile') && ( + + + + + )} +
+
)}