style(admin): 分组管理表格整理

- 折叠箭头改成和前台侧栏 Menu 一样的 CSS 角标(6px 折角,展开/收起旋转)
- 分组默认全部收起,首次拿到数据时统一折叠
- 操作列:加子分组 -> 新建子分组,并与编辑、删除一起配上图标
- 表格上方说明前加「说明:」
This commit is contained in:
2026-11-01 19:36:42 +08:00
parent 923903b4b2
commit 784a7c54ac
2 changed files with 63 additions and 10 deletions
+36 -3
View File
@@ -975,22 +975,51 @@
gap: var(--su-space-2);
}
/* 折叠箭头:与前台侧栏 Menu 的 caret 同一套画法(CSS 画的 6px 角标) */
.group-caret {
width: 20px;
display: inline-flex;
flex: none;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding: 0;
border: none;
background: none;
color: var(--su-text-muted);
font-size: var(--su-font-size-sm);
line-height: 1;
cursor: pointer;
}
.group-caret::before {
content: '';
width: 6px;
height: 6px;
border-left: 1px solid currentColor;
border-bottom: 1px solid currentColor;
opacity: 0.4;
transform: rotate(315deg);
transition:
transform var(--su-duration-base) var(--su-ease-in-out),
opacity var(--su-duration-base) var(--su-ease-in-out);
}
.group-caret:hover::before {
opacity: 0.7;
}
.group-caret-open::before {
opacity: 0.7;
transform: rotate(135deg);
}
.group-caret-empty {
cursor: default;
}
.group-caret-empty::before {
display: none;
}
/* 分组图标选择:搜索 + 图标网格 */
.icon-picker {
display: flex;
@@ -1080,12 +1109,16 @@
}
.group-action {
display: inline-flex;
align-items: center;
gap: var(--su-space-1);
padding: 0;
border: none;
background: none;
color: var(--su-color-primary);
font-size: var(--su-font-size-sm);
cursor: pointer;
white-space: nowrap;
}
.group-action:hover {
+27 -7
View File
@@ -22,7 +22,7 @@ import {
toast,
type IconName,
} from '@stash/ui'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { changePassword, fetchSession, logout, type SessionState } from '../../auth'
import { LoginDialog } from '../auth/LoginDialog'
import { GroupMark as GroupGlyph } from '../common/GroupMark'
@@ -495,6 +495,24 @@ function GroupsPanel({
const [collapsed, setCollapsed] = useState<Set<string>>(new Set())
const [dialog, setDialog] = useState<GroupDialogState>(null)
// 默认全部收起:数据第一次到位时,把所有有子节点的分组塞进 collapsed
const seeded = useRef(false)
useEffect(() => {
if (seeded.current || groups.length === 0) return
seeded.current = true
const keys = new Set<string>()
const walk = (nodes: GroupNode[]) => {
for (const node of nodes) {
if (node.children.length > 0) {
keys.add(node.key)
walk(node.children)
}
}
}
walk(groups)
setCollapsed(keys)
}, [groups])
const rows = useMemo(() => {
const result: { node: GroupNode; level: number }[] = []
const walk = (nodes: GroupNode[], level: number) => {
@@ -529,7 +547,7 @@ function GroupsPanel({
return (
<Space direction="column" gap="md" align="stretch" className="admin-form">
<Text size="sm" tone="muted">
分组用于控制前台侧边栏与卡片;可多层嵌套,根分组决定格式。内容(条目)只挂在分组上。
说明:分组用于控制前台侧边栏与卡片;可多层嵌套,根分组决定格式。内容(条目)只挂在分组上。
</Text>
<div className="group-toolbar">
@@ -563,12 +581,11 @@ function GroupsPanel({
{hasChildren ? (
<button
type="button"
className="group-caret"
className={`group-caret${open ? ' group-caret-open' : ''}`}
aria-label={open ? `折叠 ${node.title}` : `展开 ${node.title}`}
aria-expanded={open}
onClick={() => toggle(node.key)}
>
{open ? '▾' : '▸'}
</button>
/>
) : (
<span className="group-caret group-caret-empty" aria-hidden="true" />
)}
@@ -593,13 +610,15 @@ function GroupsPanel({
className="group-action"
onClick={() => setDialog({ mode: 'child', parent: node })}
>
加子分组
<Icon name="add_line" />
新建子分组
</button>
<button
type="button"
className="group-action"
onClick={() => setDialog({ mode: 'edit', group: node })}
>
<Icon name="edit_4_line" />
编辑
</button>
<button
@@ -607,6 +626,7 @@ function GroupsPanel({
className="group-action group-action-danger"
onClick={() => onDelete(node.key, node.title)}
>
<Icon name="delete_2_line" />
删除
</button>
</div>