From a08db8590a3f63b290ea1fc6cf6190021fbd719f Mon Sep 17 00:00:00 2001 From: shunli Date: Sun, 26 Apr 2026 14:07:11 +0800 Subject: [PATCH] =?UTF-8?q?R67=20A1-A6=EF=BC=9A=E5=AE=98=E7=BD=91=E9=A3=8E?= =?UTF-8?q?=E6=A0=BC=E5=89=8D=E7=AB=AF=20UI=20=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/EmptyStateCard.tsx | 8 +- client/src/components/common/PageHeader.tsx | 8 +- client/src/components/navigation/NavLinks.tsx | 22 +- client/src/components/navigation/Sidebar.tsx | 15 +- client/src/components/navigation/Topbar.tsx | 34 +-- .../patterns/EntityOverviewHero.tsx | 2 +- .../src/components/patterns/ListWorkspace.tsx | 2 +- client/src/components/patterns/MetricCard.tsx | 9 +- .../src/components/patterns/WorkbenchHero.tsx | 2 +- client/src/components/shell/AppBrand.tsx | 10 +- client/src/components/ui/button.tsx | 10 +- client/src/design/theme.css | 36 +-- client/src/design/tokens.ts | 36 +-- client/src/index.css | 253 +++++++++++++++--- client/src/layouts/AppShellLayout.tsx | 5 +- client/src/layouts/AuthLayout.tsx | 18 +- client/src/pages/LoginPage.tsx | 42 +-- client/src/pages/PortalHomePage.tsx | 40 ++- client/tailwind.config.js | 16 +- issues/2026-04-26_r67-ui-refresh.csv | 7 + 20 files changed, 390 insertions(+), 185 deletions(-) create mode 100644 issues/2026-04-26_r67-ui-refresh.csv diff --git a/client/src/components/common/EmptyStateCard.tsx b/client/src/components/common/EmptyStateCard.tsx index 8ca8f055..f12f7d07 100644 --- a/client/src/components/common/EmptyStateCard.tsx +++ b/client/src/components/common/EmptyStateCard.tsx @@ -10,8 +10,12 @@ type EmptyStateCardProps = { export function EmptyStateCard({ title, description, footer, icon }: EmptyStateCardProps) { return (
- {icon ?
{icon}
: null} -

{title}

+ {icon ? ( +
+ {icon} +
+ ) : null} +

{title}

{description}

{footer ?
{footer}
: null}
diff --git a/client/src/components/common/PageHeader.tsx b/client/src/components/common/PageHeader.tsx index ec44de7b..d70f9c30 100644 --- a/client/src/components/common/PageHeader.tsx +++ b/client/src/components/common/PageHeader.tsx @@ -15,9 +15,11 @@ export function PageHeader({ title, subtitle, actions, eyebrow, meta, className return (
- {eyebrow ?
{eyebrow}
: null} -

{title}

- {subtitle ?

{subtitle}

: null} + {eyebrow ? ( +
{eyebrow}
+ ) : null} +

{title}

+ {subtitle ?

{subtitle}

: null} {meta ?
{meta}
: null}
{actions ?
{actions}
: null} diff --git a/client/src/components/navigation/NavLinks.tsx b/client/src/components/navigation/NavLinks.tsx index 61578b1d..2fb052aa 100644 --- a/client/src/components/navigation/NavLinks.tsx +++ b/client/src/components/navigation/NavLinks.tsx @@ -75,10 +75,10 @@ function renderNavItem(item: ResolvedNavItem, onNavigate?: () => void) { onClick={() => onNavigate?.()} className={({ isActive }) => cn( - "group flex items-center gap-3 rounded-2xl px-3 py-2.5 text-sm transition-colors", + "group flex items-center gap-3 rounded-2xl px-3 py-2.5 text-sm transition duration-200", isActive - ? "bg-zinc-900 text-white shadow-sm" - : "text-zinc-600 hover:bg-zinc-50 hover:text-zinc-900", + ? "bg-primary text-white shadow-sm" + : "text-[var(--color-text-secondary)] hover:-translate-y-0.5 hover:bg-white/70 hover:text-[var(--color-text-primary)] hover:shadow-sm", ) } > @@ -88,8 +88,8 @@ function renderNavItem(item: ResolvedNavItem, onNavigate?: () => void) { className={cn( "flex size-9 items-center justify-center rounded-xl border text-[20px] transition-colors", isActive - ? "border-white/15 bg-white/10 text-white" - : "border-zinc-200 bg-white text-zinc-500 group-hover:border-zinc-300 group-hover:text-zinc-900", + ? "border-white/15 bg-white/15 text-white" + : "border-slate-200 bg-white text-[var(--color-text-tertiary)] group-hover:border-slate-300 group-hover:text-primary", )} > {item.icon} @@ -109,8 +109,8 @@ function renderSection(title: string, description: string, items: ResolvedNavIte return (
-
{title}
-
{description}
+
{title}
+
{description}
{items.map((item) => renderNavItem(item, onNavigate))}
@@ -123,13 +123,13 @@ function renderBackupSection(items: ResolvedNavItem[], onNavigate?: () => void) } return ( -
- +
+
备用模块
-
默认收起,不干扰主路径
+
默认收起,不干扰主路径
- 按需展开 + 按需展开
{items.map((item) => renderNavItem(item, onNavigate))}
diff --git a/client/src/components/navigation/Sidebar.tsx b/client/src/components/navigation/Sidebar.tsx index a846a32c..6dacb320 100644 --- a/client/src/components/navigation/Sidebar.tsx +++ b/client/src/components/navigation/Sidebar.tsx @@ -3,16 +3,19 @@ import { NavLinks } from "./NavLinks"; export function Sidebar() { return ( -
-
+
近 7 天登录覆盖率
{adoptionSummary.login7dRate}%
-
+
周报提交率
{adoptionSummary.weeklySubmissionRate}%
-
+
团队状态覆盖率
{adoptionSummary.presenceCoverageRate}%
-
+
当前公告数
{adoptionSummary.announcementCount}
-
+
本周观测提示
    {adoptionSummary.hints.map((hint) => ( @@ -549,7 +547,7 @@ function GettingStartedCard({ @@ -564,12 +562,12 @@ function GettingStartedCard({ ))} -
    +
    完成进度 {completedCount}/{items.length}
    -
    +
    {icon}
    @@ -652,7 +650,7 @@ function TodoAggregatePanel({ items }: { items: TodoAggregateItem[] }) {
    {items.length ? items.map((item) => ( - +
    { const badge = resolveTeamStatusBadge(item); return ( -
    +
    @@ -712,7 +710,7 @@ function RecentActivitiesPanel({ items }: { items: PortalRecentActivity[] }) {
    {safeItems.length ? safeItems.map((item) => ( - +
    diff --git a/client/tailwind.config.js b/client/tailwind.config.js index 775bea8c..bb83601b 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -6,19 +6,20 @@ export default { extend: { colors: { primary: { - DEFAULT: "#18181b", // zinc-900 - light: "#27272a", // zinc-800 - accent: "#197fe6" // 蓝色用于少量强调 + DEFAULT: "#00398a", + light: "#002e6e", + accent: "#13a8b8" }, - secondary: "#71717a", // zinc-500 + secondary: "#5e718d", background: { - light: "#fafafa", // zinc-50 - dark: "#111921" + light: "#f5f7f8", + dark: "#0f1723" }, - border: "#e4e4e7" // zinc-200 + border: "#e2e8f0" }, fontFamily: { sans: [ + "Public Sans", "system-ui", "-apple-system", "BlinkMacSystemFont", @@ -31,6 +32,7 @@ export default { "sans-serif" ], display: [ + "Public Sans", "system-ui", "-apple-system", "BlinkMacSystemFont", diff --git a/issues/2026-04-26_r67-ui-refresh.csv b/issues/2026-04-26_r67-ui-refresh.csv new file mode 100644 index 00000000..13956932 --- /dev/null +++ b/issues/2026-04-26_r67-ui-refresh.csv @@ -0,0 +1,7 @@ +"ID","Title","Description","Acceptance","Test_Method","Tools","Dev_Status","Review1_Status","Regression_Status","Files","Dependencies","Notes" +"A1","R67 UI 改造合同建档与范围冻结","创建 R67 前端 UI 改造执行合同,冻结第一轮范围:只改前端视觉基础、App Shell、共享组件、Portal 首页与登录页;视觉方向参考公司官网 www.hyinda.com;不改后端 API、权限、workflow、状态机、Prisma 迁移和导航可见性规则。","CSV 列完整,Dependencies 为 JSON 数组字符串;A2-A6 范围与批准方案一致;Notes 明确无后端、无迁移、无权限与状态机变更,包含回滚路径。","命令:python3 - <<'PY' 校验 CSV 12 列、状态值、Dependencies JSON 数组字符串;业务:对照 AGENTS.md 确认 R67 仅覆盖前端 UI 改造,不触碰 workflow、权限、审批规则、迁移。","manual","DONE","DONE","DONE","issues/2026-04-26_r67-ui-refresh.csv","[]","回滚路径:删除本 CSV 草稿即可回到 R66 基线;无后端 API、无 Prisma 迁移、无权限/状态机/workflow 变更。验证:CSV 结构校验通过,范围已按批准方案冻结;设计参考 www.hyinda.com 的深海蓝、冷浅灰、白卡、几何 Logo 与水声科技感。" +"A2","视觉基础与设计 token 刷新","参考公司官网 www.hyinda.com,将当前灰白后台基调升级为深海蓝科技感轻量运营工作台:更新 CSS 变量、Tailwind token、页面背景、卡片层级、焦点与状态色,减少同质化白卡。","1) 全局背景、卡片、表格、badge、输入控件视觉层级更清晰;2) 不引入新 npm 依赖;3) 不破坏既有类名兼容性;4) 页面在桌面和移动端保持可读。","命令:cd client && npm run typecheck && npm run build;业务:打开 Portal、列表页、系统设置和移动入口,确认共享样式未造成文字不可读、内容溢出或点击目标缩小。","manual","DONE","DONE","TODO","client/src/design/theme.css|client/src/design/tokens.ts|client/src/index.css|client/tailwind.config.*","[""A1""]","回滚路径:恢复 R66 token 与共享 CSS;不新增依赖,不改业务逻辑。官网参考:主色 #00398a,冷浅灰背景 #f5f7f8,白色卡片,slate 文本,几何 Logo。 验证:client typecheck/build 通过;员工、管理者、管理员三类账号人工视觉复核待执行。" +"A3","App Shell 与导航视觉重构","优化侧边栏、顶部栏、移动菜单与导航项视觉层级,让用户先看主路径、再看管理入口、最后看备用模块;保留 R66 小团队导航显隐规则。","1) 主导航、管理入口、备用模块层级更明显;2) 当前路由状态、搜索、通知、用户问候区更清晰;3) 移动菜单仍可正常打开/关闭;4) 不改变导航数据来源与权限过滤。","命令:cd client && npm run typecheck && npm run build;业务:使用员工、管理者、管理员账号检查 Portal、请假、报销、系统设置、备用模块入口,确认可见性与 R66 一致。","manual","DONE","DONE","TODO","client/src/layouts/AppShellLayout.tsx|client/src/components/navigation/Sidebar.tsx|client/src/components/navigation/Topbar.tsx|client/src/components/navigation/NavLinks.tsx|client/src/components/shell/AppBrand.tsx","[""A2""]","回滚路径:恢复 Shell 与导航组件到 R66;仅改视觉和布局 class,不改权限与导航 schema。 验证:client typecheck/build 通过;员工、管理者、管理员三类账号人工视觉复核待执行。" +"A4","共享工作台组件刷新","重构 PageScaffold、WorkbenchHero、SectionPanel、MetricCard、EmptyState 等共享展示模式,让页面不再像一组相同白色卡片堆叠,同时保持现有组件 API。","1) 共享组件 API 不破坏现有页面调用;2) Hero、指标卡、内容面板、空态卡有明确视觉角色差异;3) 警告/危险状态仍可辨识;4) 大量复用页面自动受益。","命令:cd client && npm run typecheck && npm run build;业务:抽查 Portal、组织架构、公告、报销详情、系统设置页面,确认共享组件样式一致且无布局回归。","manual","DONE","DONE","TODO","client/src/components/patterns/*.tsx|client/src/components/common/EmptyStateCard.tsx|client/src/components/common/PageHeader.tsx|client/src/index.css","[""A2""]","回滚路径:恢复共享组件和 CSS class;保持组件 props 不变,降低回滚风险。 验证:client typecheck/build 通过;员工、管理者、管理员三类账号人工视觉复核待执行。" +"A5","Portal 与登录页旗舰体验刷新","将 Portal 首页和登录页作为第一轮样板页,强化今日待办、快捷动作、团队状态、近期动态和进入方式的视觉重点,减少用户觉得“丑”和“像后台模板”的感知。","1) Portal 首屏能快速识别今日要做什么;2) 快捷动作、待办、团队状态、动态流区分明显;3) 登录页保持企业微信/账号密码回退逻辑不变;4) 文案和视觉更适合小团队日常使用。","命令:cd client && npm run typecheck && npm run build;业务:员工视角检查 Portal 首屏、快捷操作、待办、公告;管理者视角检查团队状态、周报缺口、试点观测;浏览器和企业微信入口登录逻辑保持可理解。","manual","DONE","DONE","TODO","client/src/pages/PortalHomePage.tsx|client/src/layouts/AuthLayout.tsx|client/src/pages/LoginPage.tsx","[""A3"",""A4""]","回滚路径:恢复 Portal/Login 相关组件;不改 API 调用、query key、登录流程、权限判断。 验证:client typecheck/build 通过;员工、管理者、管理员三类账号人工视觉复核待执行。" +"A6","R67 验证、状态回填与提交收口","完成 UI 改造后执行前端验证、必要 smoke 检查、人工视觉复核和 CSV 状态回填,形成 R67 提交。","1) A2-A5 的 Test_Method 已执行并记录结果;2) client typecheck/build 通过;3) 受影响主路径人工复核通过或记录未执行原因;4) git commit 成功,提交信息引用 R67 A1-A6。","命令:git diff --check && cd client && npm run typecheck && npm run build;业务:按员工、管理者、管理员三类入口复核 Portal、导航、登录、通知入口、搜索入口和移动菜单。","manual","DOING","TODO","TODO","issues/2026-04-26_r67-ui-refresh.csv|client/src/**","[""A5""]","回滚路径:若验证失败,不回填 DONE,不提交;必要时整批恢复 R67 前端视觉改动。 当前进度:自动验证已完成 client typecheck/build;仍待 git diff --check、CSV 校验与三类账号人工视觉复核。"