diff --git a/stash-web/src/App.css b/stash-web/src/App.css index fa54630..c44b4c3 100644 --- a/stash-web/src/App.css +++ b/stash-web/src/App.css @@ -787,7 +787,67 @@ } .admin-form { - margin-top: var(--su-space-5); + margin-top: 0; +} + +.admin-form-narrow { + max-width: 820px; +} + +/* 每个后台页面统一的标题区 */ +.admin-panel-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--su-space-5); + padding-bottom: var(--su-space-4); + border-bottom: 1px solid var(--su-border); + margin-bottom: var(--su-space-2); +} + +.admin-panel-heading { + min-width: 0; +} + +.admin-panel-title { + margin: 0; + color: var(--su-text-main); + font-size: 20px; + font-weight: 700; + line-height: 1.3; + letter-spacing: -0.01em; +} + +.admin-panel-desc { + max-width: 760px; + margin: var(--su-space-2) 0 0; + color: var(--su-text-muted); + font-size: var(--su-font-size-sm); + line-height: 1.65; +} + +.admin-panel-actions { + display: flex; + flex: none; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + gap: var(--su-space-2); +} + +/* 表单标签统一加粗,避免组件默认字重在不同字体下发虚 */ +.admin-panel label { + font-weight: 600; +} + +.admin-inline-actions { + display: flex; + gap: var(--su-space-2); +} + +.admin-form-divider { + margin: var(--su-space-1) 0; + border-top: 1px dashed var(--su-border-strong); } .admin-row { @@ -896,6 +956,7 @@ } .admin-actions { + max-width: 820px; margin-top: var(--su-space-6); } diff --git a/stash-web/src/components/admin/AdminNext.tsx b/stash-web/src/components/admin/AdminNext.tsx index c10d3ef..306e2e7 100644 --- a/stash-web/src/components/admin/AdminNext.tsx +++ b/stash-web/src/components/admin/AdminNext.tsx @@ -30,7 +30,7 @@ import { type IconName, type MenuItem, } from '@stash/ui' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react' import { changePassword, fetchSession, logout, type SessionState } from '../../auth' import { LoginDialog } from '../auth/LoginDialog' import { GroupMark as GroupGlyph } from '../common/GroupMark' @@ -127,6 +127,27 @@ function flattenCardsGroups(groups: GroupNode[]) { return flattenGroups(groups) } +/** 后台每个页面统一的标题区:加粗标题 + 说明 + 右侧操作 */ +function PanelHeader({ + title, + description, + children, +}: { + title: string + description?: string + children?: ReactNode +}) { + return ( + + + {title} + {description !== undefined && {description}} + + {children !== undefined && {children}} + + ) +} + export function AdminNext() { const [session, setSession] = useState(null) const [loginOpen, setLoginOpen] = useState(false) @@ -292,9 +313,9 @@ export function AdminNext() { - 站点设置 + 后台管理 - 分组是唯一容器;侧边栏和内容卡片都只是勾选分组来组合展示。 + 管理内容、首页展示与站点设置。 @@ -332,7 +353,10 @@ export function AdminNext() { {tab === 'home-sidebar' && ( - 侧边栏 + - 内容卡片 - - 说明:卡片是首页上的一个区块;在这里勾选它包含哪些分组,并调整这些分组的先后顺序与预览条数。 - 分组是否启用仍由「分组管理」决定,停用的分组不会出现在卡片里。 - + - - 说明:分组用于控制前台侧边栏与卡片;可多层嵌套,根分组决定格式。内容(条目)只挂在分组上。 - - - + } onClick={() => setDialog({ mode: 'root' })} > 新建根分组 - + - - 说明:数据只属于一个分组;选中主分组时会连同其所有子分组的数据一起列出。 - - - - - ({ - value: option.key, - label: option.title, - }))} - value={group?.key ?? ''} - onChange={setGroupKey} - /> + + + + ({ + value: option.key, + label: option.title, + }))} + value={group?.key ?? ''} + onChange={setGroupKey} + /> + + } + disabled={group === undefined} + onClick={() => setDialog({ key: null, groupKey })} + > + 新建数据 + - } - disabled={group === undefined} - onClick={() => setDialog({ key: null, groupKey })} - > - 新建数据 - - + {group?.title ?? ''} 下共 {rows.length} 条(含子分组) @@ -1333,11 +1356,7 @@ function SidebarPanel({ saving: boolean }) { return ( - - - 说明:分组在侧边栏的显隐只由「分组管理 → 状态」决定;这里只管三块侧栏卡片。 - - +
{description}