215 lines
10 KiB
TypeScript
215 lines
10 KiB
TypeScript
'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>
|
||
)
|
||
}
|