feat: move people structure tree to secondary sidebar

This commit is contained in:
江林铖 2026-09-04 13:22:34 +08:00
parent 74ec2f0c58
commit 1481f56eb2
3 changed files with 23 additions and 11 deletions

View File

@ -14,6 +14,7 @@ jest.mock('@/components/organization/OrganizationWorkspace', () => ({
data-embedded={String(props.embedded)}
data-scope={String(props.scopeRootName)}
data-heading={String(props.heading)}
data-tree-as-secondary-sidebar={String(props.treeAsSecondarySidebar)}
>
{String(props.description)}
</div>
@ -28,6 +29,7 @@ describe('PeopleStructureDashboard', () => {
expect(screen.getByTestId('organization-workspace')).toHaveAttribute('data-embedded', 'true')
expect(screen.getByTestId('organization-workspace')).toHaveAttribute('data-scope', '品牌中心')
expect(screen.getByTestId('organization-workspace')).toHaveAttribute('data-heading', '人员架构')
expect(screen.getByTestId('organization-workspace')).toHaveAttribute('data-tree-as-secondary-sidebar', 'true')
expect(screen.getByText(/公司组织架构 · 品牌中心在职人员/)).toBeInTheDocument()
})
})

View File

@ -15,6 +15,7 @@ export default function PeopleStructureDashboard() {
scopeRootName="品牌中心"
heading="人员架构"
description="公司组织架构 · 品牌中心在职人员 · 数据源:HR 花名册(每日 02:00 自动同步)"
treeAsSecondarySidebar
/>
</div>
</DepartmentShell>

View File

@ -98,6 +98,7 @@ type OrganizationWorkspaceProps = {
scopeRootName?: string
heading?: string
description?: string
treeAsSecondarySidebar?: boolean
}
function findOrganizationNode(nodes: OrgNode[], name: string): OrgNode | null {
@ -115,6 +116,7 @@ export default function OrganizationWorkspace({
scopeRootName,
heading = '组织架构',
description = '品牌中心在职人员 · 数据源:HR 花名册(每日 02:00 自动同步)',
treeAsSecondarySidebar = false,
}: OrganizationWorkspaceProps) {
const { data: session, status } = useSession()
const router = useRouter()
@ -296,10 +298,10 @@ export default function OrganizationWorkspace({
}
const content = (
<div className="max-w-[1400px] mx-auto px-5 py-6">
<div className={treeAsSecondarySidebar ? 'w-full' : 'mx-auto max-w-[1400px] px-5 py-6'}>
{/* direction: cloud-quarry (seed 32cb33b5) — THESIS: 部门即层叠石料块,人员是块上铭牌 */}
{/* 页头 */}
<div className="mb-5 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div className={`${treeAsSecondarySidebar ? 'border-b border-[#e2e8f0] bg-white px-5 py-5 sm:px-7' : 'mb-5'} flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between`}>
<div>
<h1 className="text-[26px] font-bold text-[#1a1c1d] tracking-tight">{heading}</h1>
<p className="text-[13px] text-[#86898e] mt-1">{description}</p>
@ -331,26 +333,30 @@ export default function OrganizationWorkspace({
</div>
{error && (
<div className="bg-[#fdf6e9] border border-[#e8c98a] text-[#a0671c] rounded-lg px-4 py-3 text-[13px] mb-4">
<div className={`${treeAsSecondarySidebar ? 'mx-5 mt-4 sm:mx-7' : 'mb-4'} rounded-lg border border-[#e8c98a] bg-[#fdf6e9] px-4 py-3 text-[13px] text-[#a0671c]`}>
{error}
</div>
)}
{loading && !data ? (
<div className="bg-white rounded-lg border border-[#e7e8ea] shadow-sm p-10 text-center text-[#86898e] text-sm">
<div className={`${treeAsSecondarySidebar ? 'm-5 sm:m-7' : ''} rounded-lg border border-[#e7e8ea] bg-white p-10 text-center text-sm text-[#86898e] shadow-sm`}>
加载中…
</div>
) : data ? (
<div className="grid grid-cols-1 lg:grid-cols-[300px_1fr] gap-5 items-start">
<div className={`grid grid-cols-1 items-start ${treeAsSecondarySidebar ? 'gap-0 lg:grid-cols-[232px_minmax(0,1fr)] lg:items-stretch' : 'gap-5 lg:grid-cols-[300px_1fr]'}`}>
{/* 左侧:组织树 */}
<aside className="bg-white rounded-lg border border-[#e7e8ea] shadow-sm overflow-hidden">
<div className="px-4 py-3 border-b border-[#eef0f3] flex items-center justify-between">
<span className="text-[12px] font-semibold text-[#1a1c1d]">
<aside className={treeAsSecondarySidebar
? 'overflow-hidden border-b border-[#e2e8f0] bg-[#f8fafc] lg:min-h-0 lg:border-b-0 lg:border-r'
: 'overflow-hidden rounded-lg border border-[#e7e8ea] bg-white shadow-sm'}
aria-label={treeAsSecondarySidebar ? '人员架构二级导航' : undefined}
>
<div className={`${treeAsSecondarySidebar ? 'border-b border-[#e2e8f0] px-4 py-4' : 'border-b border-[#eef0f3] px-4 py-3'} flex items-center justify-between`}>
<span className={`${treeAsSecondarySidebar ? 'text-sm font-bold text-[#101828]' : 'text-[12px] font-semibold text-[#1a1c1d]'}`}>
{scopeRootName ? `${scopeRootName}组织架构` : '组织架构'}
</span>
<span className="text-[11px] text-[#86898e]">{scopedTree.length} 顶层节点</span>
<span className={`${treeAsSecondarySidebar ? 'text-xs text-[#66758b]' : 'text-[11px] text-[#86898e]'}`}>{scopedTree.length} 顶层节点</span>
</div>
<nav className="max-h-[calc(100vh-260px)] overflow-y-auto py-2" aria-label="组织树">
<nav className={`${treeAsSecondarySidebar ? 'max-h-[42vh] p-2 lg:max-h-[calc(100dvh-188px)] lg:py-4' : 'max-h-[calc(100vh-260px)] py-2'} overflow-y-auto`} aria-label="组织树">
{scopeRootName && scopedTree.length === 0 && (
<p className="px-4 py-8 text-center text-[13px] leading-6 text-[#86898e]">
未找到“{scopeRootName}”组织节点
@ -447,7 +453,10 @@ export default function OrganizationWorkspace({
</aside>
{/* 右侧:人员明细 */}
<section className="bg-white rounded-lg border border-[#e7e8ea] shadow-sm overflow-hidden">
<section className={treeAsSecondarySidebar
? 'min-w-0 overflow-hidden bg-white'
: 'overflow-hidden rounded-lg border border-[#e7e8ea] bg-white shadow-sm'}
>
<div className="px-5 py-4 border-b border-[#eef0f3]">
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-2 min-w-0">