gongxiangp2/components/department-dashboard/TeamFunctionsDashboard.tsx

215 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import Link from 'next/link'
import { ArrowLeft, Blocks, BriefcaseBusiness, Check, Layers3, UsersRound } from 'lucide-react'
import { useState } from 'react'
import DepartmentShell from './DepartmentShell'
type FunctionBranch = {
label: string
items: string[]
}
type TeamFunction = {
name: string
lead: string
description: string
tone: 'coral' | 'navy' | 'blue' | 'teal'
branches: FunctionBranch[]
}
const TEAM_FUNCTIONS: TeamFunction[] = [
{
name: '品牌创意A部',
lead: '伯言',
description: '负责朴美、索菲亚、顾家品牌的视觉创意与内容前置策划,统筹品牌调性、商品卖点及页面与短视频创意。',
tone: 'coral',
branches: [
{ label: '服务品牌', items: ['朴美', '索菲亚', '顾家'] },
{ label: '前置能力', items: ['品牌视觉策划设计', 'AI+平面+策划', '编导/短视频策划'] },
{ label: '职责', items: ['品牌调性', '商品卖点', '页面/短视频创意', '经验与复盘'] },
],
},
{
name: '品牌创意B部',
lead: '清云',
description: '负责欧意朗、卓禾、亿家达品牌的视觉创意与内容前置策划,推动品牌辅导、页面及短视频创意落地。',
tone: 'navy',
branches: [
{ label: '服务品牌', items: ['欧意朗', '卓禾', '亿家达'] },
{ label: '前置能力', items: ['品牌视觉策划设计', 'AI+平面+策划', '品牌辅导'] },
{ label: '职责', items: ['品牌调性', '商品卖点', '页面/短视频创意', '经验与复盘'] },
],
},
{
name: '空间设计部',
lead: '希文',
description: '负责空间、置景、灯光、机位与拍摄一体化,为内容生产提供场景和拍摄执行能力。',
tone: 'blue',
branches: [
{ label: '定位', items: ['空间、置景、灯光、机位、拍摄一体化'] },
{ label: '岗位', items: ['美术指导', '空间设计师', '摄影师'] },
{ label: '职责', items: ['场景概念', '布局搭建', '灯光机位', '拍摄执行', '场景复用'] },
],
},
{
name: '综合支持部',
lead: '无邪',
description: '承接3D、AI、平面、修图、视频后期等生产支持,沉淀资产标准并推进批量生产。',
tone: 'teal',
branches: [
{ label: '岗位', items: ['3D设计师', '平面设计师', 'AIGC设计师', '视频后期', '修图'] },
{ label: '职责', items: ['3D/AI/平面/修图', '剪辑与包装', '资产与标准', '批量生产'] },
],
},
]
const toneStyles = {
coral: {
accent: '#e4473f',
accentSoft: '#fff1ef',
accentMuted: '#a62f2a',
line: '#f2b3ae',
node: '#fff9f8',
},
navy: {
accent: '#26364f',
accentSoft: '#eef2f7',
accentMuted: '#26364f',
line: '#a9b5c7',
node: '#fafcff',
},
blue: {
accent: '#2384e8',
accentSoft: '#edf6ff',
accentMuted: '#176fc1',
line: '#a9cef6',
node: '#fbfdff',
},
teal: {
accent: '#138d73',
accentSoft: '#edf9f5',
accentMuted: '#087052',
line: '#a4d6c8',
node: '#fbfffd',
},
} as const
function Branch({ branch, tone }: { branch: FunctionBranch; tone: TeamFunction['tone'] }) {
const colors = toneStyles[tone]
return (
<section className="relative grid gap-3 md:grid-cols-[108px_minmax(0,1fr)] md:items-start md:gap-5">
<div className="relative flex min-h-10 items-center md:justify-end">
<span
className="inline-flex min-h-9 items-center rounded-lg border px-3 py-2 text-sm font-bold"
style={{ borderColor: colors.line, backgroundColor: colors.accentSoft, color: colors.accentMuted }}
>
{branch.label}
</span>
</div>
<div className="relative grid gap-2 sm:grid-cols-2 xl:grid-cols-3">
{branch.items.map((item) => (
<div
key={item}
data-team-item={item}
className="flex min-h-10 items-center gap-2 rounded-lg border px-3 py-2 text-sm font-semibold leading-5"
style={{ borderColor: colors.line, backgroundColor: colors.node, color: '#26364f' }}
>
<Check className="h-3.5 w-3.5 shrink-0" style={{ color: colors.accent }} aria-hidden="true" />
<span>{item}</span>
</div>
))}
</div>
</section>
)
}
export default function TeamFunctionsDashboard() {
const [selectedTeamName, setSelectedTeamName] = useState(TEAM_FUNCTIONS[0].name)
const selectedTeam = TEAM_FUNCTIONS.find((team) => team.name === selectedTeamName) || TEAM_FUNCTIONS[0]
const colors = toneStyles[selectedTeam.tone]
return (
<DepartmentShell>
<main className="mx-auto max-w-[1600px] px-4 pb-16 pt-7 sm:px-6 lg:px-8">
<header className="border-b border-[#dfe4ea] pb-6">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-[#fff8d9] text-[#725400]">
<Blocks className="h-6 w-6" aria-hidden="true" />
</div>
<h1 className="mt-6 text-balance text-3xl font-bold tracking-[-0.03em] text-[#101828] sm:text-4xl">团队职能</h1>
<p className="mt-3 max-w-3xl text-base leading-7 text-[#66758b] sm:text-lg">
按团队查看品牌服务、前置能力、岗位与职责边界,作为协作分工和流程归属的统一参考。
</p>
</header>
<section aria-labelledby="team-functions-map-title" className="mt-6 overflow-hidden rounded-xl border border-[#dfe5ed] bg-white shadow-[0_6px_18px_rgba(15,23,42,0.05)] lg:grid lg:grid-cols-[232px_minmax(0,1fr)]">
<aside aria-label="团队职能二级部门导航" className="border-b border-[#e4e9f0] bg-[#f8fafc] lg:border-b-0 lg:border-r">
<div className="hidden border-b border-[#e4e9f0] px-4 py-4 lg:block">
<p className="text-xs font-bold text-[#66758b]">部门</p>
<h2 id="team-functions-map-title" className="mt-1 text-sm font-bold text-[#101828]">品牌中心职责地图</h2>
</div>
<nav className="flex gap-1 overflow-x-auto p-2 lg:block lg:space-y-1 lg:overflow-visible" aria-label="选择部门">
{TEAM_FUNCTIONS.map((team) => {
const teamColors = toneStyles[team.tone]
const selected = selectedTeam.name === team.name
return (
<button
key={team.name}
type="button"
aria-current={selected ? 'page' : undefined}
onClick={() => setSelectedTeamName(team.name)}
className={`flex min-h-[58px] shrink-0 items-center gap-3 rounded-lg px-3 py-2.5 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff] lg:w-full ${selected ? 'bg-[#1677ff] text-white' : 'text-[#4f6077] hover:bg-[#eef2f6] hover:text-[#101828]'}`}
>
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg" style={{ backgroundColor: selected ? 'rgba(255,255,255,0.16)' : teamColors.accentSoft, color: selected ? '#fff' : teamColors.accent }}>
<UsersRound className="h-4 w-4" aria-hidden="true" />
</span>
<span className="min-w-0">
<span className="block truncate text-sm font-bold">{team.name}</span>
<span className={`mt-0.5 block truncate text-xs ${selected ? 'text-white/75' : 'text-[#667085]'}`}>负责人:{team.lead}</span>
</span>
</button>
)
})}
</nav>
</aside>
<section aria-label={`${selectedTeam.name}部门介绍`} className="min-w-0">
<header className="flex flex-col gap-4 border-b border-[#e4e9f0] px-5 py-5 sm:px-7 sm:py-6 lg:flex-row lg:items-start lg:justify-between">
<div className="flex min-w-0 items-start gap-3">
<span className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl" style={{ backgroundColor: colors.accentSoft, color: colors.accent }}>
<UsersRound className="h-5 w-5" aria-hidden="true" />
</span>
<div className="min-w-0">
<p className="text-xs font-bold" style={{ color: colors.accentMuted }}>当前部门</p>
<h2 className="mt-1 text-2xl font-bold tracking-[-0.02em] text-[#101828]">{selectedTeam.name}</h2>
<p className="mt-1 text-sm font-semibold" style={{ color: colors.accentMuted }}>负责人:{selectedTeam.lead}</p>
</div>
</div>
<div className="flex flex-wrap gap-2 text-xs font-bold text-[#5f7189]" aria-label="职责地图说明">
<span className="inline-flex items-center gap-1.5 rounded-lg bg-[#f8fafc] px-3 py-2"><BriefcaseBusiness className="h-3.5 w-3.5" aria-hidden="true" />服务与定位</span>
<span className="inline-flex items-center gap-1.5 rounded-lg bg-[#f8fafc] px-3 py-2"><Layers3 className="h-3.5 w-3.5" aria-hidden="true" />能力与岗位</span>
</div>
</header>
<div className="px-5 py-5 sm:px-7 sm:py-6">
<p className="max-w-3xl text-base leading-7 text-[#4f6077]">{selectedTeam.description}</p>
<div className="relative mt-6 space-y-4 md:space-y-5 md:before:absolute md:before:bottom-5 md:before:left-[116px] md:before:top-5 md:before:w-px md:before:bg-[#e9edf3]">
{selectedTeam.branches.map((branch) => <Branch key={branch.label} branch={branch} tone={selectedTeam.tone} />)}
</div>
</div>
</section>
</section>
<Link
href="/"
className="mt-8 inline-flex min-h-11 items-center gap-2 rounded-lg bg-[#1677ff] px-4 text-sm font-bold text-white transition-colors hover:bg-[#0f67df] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff] focus-visible:ring-offset-2"
>
<ArrowLeft className="h-4 w-4" aria-hidden="true" />
返回部门看板
</Link>
</main>
</DepartmentShell>
)
}