文件
hyb-oa/.trellis/spec/hyb-oa/frontend-react-vite-design.md
T

25 KiB

Frontend React / Vite / Design

Source Of Truth

Frontend visual and interaction design uses the installed frontend-design skill as the primary design authority. docs/产品设计规则.md remains a product experience and business-boundary fact source, not a visual, brand or aesthetic rulebook. For PC/mobile/WeCom pages, add Playwright or visual verification when reasonable.

This spec keeps hyb-oa-specific React/Vite, shared-component, business-boundary and verification contracts. Do not copy broad aesthetic guidance from frontend-design into this file; use the skill directly for design judgment.

Rules

  • Use existing React/Vite/Tailwind patterns and shared components before adding page-local UI systems.
  • When frontend-design and older project visual rules conflict, follow frontend-design for visual direction while preserving security, privacy, Trellis, deployment and business data boundaries.
  • Default product framing is a small-team Chinese workbench for domestic tech startups with fewer than 10 people. User-facing copy should be natural Chinese; avoid English product words such as Portal, Dashboard and Workflow unless they are internal test fixtures or technical identifiers.
  • PC pages should keep dense but orderly OA layouts: clear header, filters, table/list, status, details and predictable actions.
  • Mobile pages must reuse the existing unified mobile shell, card/list rows, safe-area and sticky action patterns unless the current task intentionally redesigns them with frontend-design.
  • Do not add decorative landing-page styling to operational OA pages.
  • Run cd client && npm run typecheck && npm run build for frontend changes.

Frozen / Boundary Items

Frozen modules and Enterprise WeChat-owned HR/attendance entries must be labeled or hidden according to the current Trellis task; do not silently re-enable them.

  • Use the shared WorkspaceNotice pattern for trial boundaries, safety notes, and post-trial capabilities instead of page-local warning blocks.
  • Trial-facing PC lists must hide obvious TEST/SMOKE records by default when a page-level toggle exists; the toggle may reveal them for troubleshooting.

Scenario: Business Flow Tests And Frozen Modules

1. Scope / Trigger

  • Trigger: Playwright or QA shell tests need to create workflow data while some product modules are frozen for launch.
  • Applies to client/tests/e2e/**, scripts/qa/run-business-flow-gates.sh, and frontend route/permission assertions.

2. Signatures

  • QA command: BUSINESS_FLOW_GROUPS=<groups> bash scripts/qa/run-business-flow-gates.sh local.
  • Frozen-module API expectation: non-admin launch roles may receive 403 for frozen module list/create/submit endpoints.
  • Enabled workflow test data sources should use active entities such as leave, reimbursement or procurement unless the task explicitly reopens the frozen module.

3. Contracts

  • Do not use a frozen module as fixture setup for an unrelated enabled feature. Example: mobile approval offline sync should not create a travel request when travel permissions are intentionally frozen.
  • Permission direct-access tests must assert the frozen contract directly, such as employee GET /api/travel-requests?... returning 403.
  • UI route smoke may still verify that a frozen page displays a boundary state without entering a management or write-capable mode.
  • Do not loosen roles or seed permissions merely to make old E2E data setup pass.

4. Validation & Error Matrix

  • Frozen role calls frozen module create/submit/list endpoint -> 403 or the documented feature-gate status.
  • Enabled workflow feature needs pending task data -> create data through an enabled workflow entity.
  • E2E fails with PERM_001 while setting up unrelated feature data -> update the test data source before changing app permissions.

5. Good/Base/Bad Cases

  • Good: mobile approval offline test creates a submitted leave request, then verifies pending-task deep link, offline queue, sync success and conflict.
  • Base: travel-specific tests are skipped or admin-scoped while travel remains frozen.
  • Bad: grant employee travel.create only so a mobile approval test can build a fixture.

6. Tests Required

  • Focused E2E group that previously failed.
  • Full affected business-flow group after the focused fix.
  • cd client && npm run typecheck && npm run build when TypeScript test code changes.

7. Wrong vs Correct

Wrong

await createSubmittedTravelRequestViaApi({ user: EMPLOYEE });

Correct

await createSubmittedLeaveViaApi({ user: EMPLOYEE });

Convention: PC Operation List Density

What: PC list/workflow pages that combine status metrics, tabs/filters, range controls, export and table/list content should reuse the shared operation classes in client/src/index.css:

  • ops-status-strip for compact status metrics.
  • ops-command-bar, ops-command-bar__row, ops-command-bar__tabs, ops-command-bar__actions and ops-filter-label for tabs, filters and secondary actions.
  • ops-table-focus for the primary desktop table container.
  • ops-inline-alert for temporary mode hints such as batch operation notices.
  • PrimaryTable already applies ops-table-focus by default; page code may add responsive visibility classes such as hidden md:block, but should not recreate a separate table frame.
  • Finance/admin status summaries should prefer the shared finance-status-strip and finance-metric-card classes unless the task introduces a new reusable metric component.

Why: This keeps high-frequency PC pages dense and predictable without recreating page-local Tailwind command bars. It also lets frontend-design change the visual treatment once while preserving component contracts.

Example:

<StatStrip columns={4} className="ops-status-strip">
  <MetricCard title="待办总数" value={summary.pendingTotal} />
</StatStrip>

<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
  <div className="ops-command-bar__row">
    <Tabs value={status}>
      <TabsList className="ops-command-bar__tabs !h-auto !justify-start !bg-slate-50">
        <TabsTrigger value="pending">待处理</TabsTrigger>
      </TabsList>
    </Tabs>
    <div className="ops-command-bar__actions">...</div>
  </div>
</FilterBar>

<PrimaryTable className="hidden md:block">...</PrimaryTable>

Tests Required:

  • cd client && npm run typecheck
  • cd client && npm run build
  • Focused Playwright screenshot coverage when changing login, Portal, primary PC list pages or command-bar/table styling.

Convention: Global Shell And Page Header Baseline

What: PC page redesigns should preserve the shared Wave0-2 shell and header baseline instead of recreating page-local wrappers:

  • AppShellLayout owns the PC shell background and content offset through app-shell and app-shell-main.
  • Sidebar and NavLinks own role-aware task navigation. The mental model is a short primary task list plus collapsed 更多; management configuration is represented by one 管理 entry instead of many sidebar rows.
  • Topbar is a compact command surface: page title, group badge, user context, global search and notifications. Do not expand it back into a large hero.
  • PageHeader and WorkspaceHeader should use page-header-shell, page-header-shell__eyebrow, page-header-shell__title, page-header-shell__subtitle and page-header-shell__actions.
  • Auth guidance and step hints may use ux-command-note, ux-step-list and ux-step-item when they explain the next action without adding page-local warning cards.

Why: The shell/header baseline gives Wave3-5 pages a stable visual entry point and prevents each route from inventing a different title/action layout.

Navigation Rules:

  • Sidebar entries should be role-aware and task-first for domestic tech startups under 10 people; do not expose all permission-allowed modules as a default module catalog.
  • Approvers see 待我处理; ordinary employees should not see 待办 or 审批待办 in the sidebar by default.
  • PC leave navigation uses 请假记录 unless the current task explicitly opens OA emergency leave submission.
  • 个人设置 stays in the topbar account menu. Notifications stay in the topbar bell and notification center.
  • Finance labels should use work names such as 付款, 应付账款 and 票据; internal platform words like AP 运营, ERP 集成 and 功能开关 should be hidden behind management wording or renamed for users.
  • 更多 is a lightweight collapsed row for low-frequency entries; do not add a sidebar explanation card, subtitle or onboarding copy around it.
  • Playwright navigation helpers should use exact names or anchored regex for short sidebar labels such as 管理, otherwise fuzzy role-name matching can click longer labels such as 预算管理.

Example:

<WorkspaceHeader
  eyebrow="审批"
  title="审批中心"
  subtitle="先看待处理,再进入详情核对依据。"
/>

Tests Required:

  • cd client && npm run typecheck
  • cd client && npm run build
  • Focused Playwright coverage when changing AppShellLayout, Sidebar, Topbar, NavLinks, PageHeader, WorkspaceHeader, login, Portal or other A-class pages.

Convention: A-Class PC Task-First Pages

What: A-class PC pages such as Portal, approvals, reimbursement list and reimbursement detail should make the current user task obvious before exposing secondary operations:

  • Treat the primary persona as a small-team operator wearing multiple hats, not a dedicated OA clerk. The first screen should reduce decision cost for a founder, finance/admin owner, project lead or employee who may switch roles during the same session.
  • First visible task surface should answer: what needs attention, what can be started, and where recent progress lives.
  • Keep one primary action in the first screen. Allow at most two secondary entries; move export, batch, delete, templates, complex filters, statistics and admin/finance tools into 更多, 高级 / 台账, detail actions, role-only panels or confirmation dialogs.
  • Do not turn small-team workflows into platform-builder surfaces by default: approval designers, broad configuration matrices, multi-level enterprise dashboards and batch admin consoles need explicit Trellis task scope.
  • Explanation text should only be default-visible for risk, exception or empty states. Do not use repeated subtitles or notice cards to explain normal workflow steps that the layout can express.
  • Low-frequency capabilities are hidden or moved, not removed, unless the current Trellis task explicitly deletes the capability.
  • Portal/workbench first screen should use a compact status summary plus 今日待处理; do not use a persistent welcome hero, fixed 报销进度 button, general announcement feed, or folded placeholder sections such as 团队与通知, 角色重点 or 试点观测摘要.
  • Workbench sections after 今日待处理 must be conditional: 团队卡点 only for real blockers, 管理提醒 only for real admin/trial issues, and 我的进度 only for reliable status updates. Empty data should disappear, not render as collapsed drawers or statistic cards.
  • Each workbench action row should state what the item is, why the current user is involved, current status/timing, and one next action. Do not represent raw application counts as progress unless the payload includes an actionable or status-level signal.
  • PC reimbursement list defaults to the initiator's own forms even for users with finance/approval roles. The first screen may use task cards for rejected corrections, drafts and reviewing progress; ledger/export/batch, proxy submit, templates and travel-linking remain secondary and must be hidden unless the current user has a real matching capability. Row workflow copy should distinguish 待你处理 from read-only 当前节点.

Why: hyb-oa serves small teams with overlapping roles. Core pages must make the next click clear for ordinary employees and approvers, while keeping finance and admin tools available without dominating the default path.

Tests Required:

  • cd client && npm run typecheck
  • cd client && npm run build
  • Focused Playwright or screenshot checks for changed A-class PC pages, asserting the task surface, primary action and secondary/advanced entry behavior.

Convention: Approval Task List Defaults

What: PC approval task lists such as /workflow/tasks should make 查看处理 the default row action. List-level 通过 is only a secondary shortcut for low-risk items where the row itself carries enough decision context.

Contracts:

  • The row must answer what the item is, who requested it, why it is assigned to the current user, how long it has waited, and the public business reference.
  • History entry text should be explicit, such as 已处理记录; avoid user-facing 高级 / 历史 on the default task surface.
  • Time labels should prefer 已等待多久 and 接收于 over database-shaped labels such as 更新时间 when the source field is task creation/arrival time.
  • A quick 通过 button may appear only when the task is pending, the server allows approve, the item is a low-risk leave/attendance task, required row summary fields are present, and the task has not exceeded the pending window available to the frontend. Reimbursement, procurement, travel, contract and other amount/evidence-heavy tasks should route through 查看处理 first.
  • The confirm dialog for list quick approval must still explain action consequences and include the specific task summary.

Why: Small-team approvers need to clear work quickly without training themselves to approve financial or evidence-heavy items from an under-specified row.

Convention: Workbench Onboarding And Profile Scope

What: PC workbench and account settings should stay task-first, not become training or permission-maintenance surfaces:

  • Do not add a persistent 快速上手 / onboarding checklist to the workbench by default. If first-login guidance is needed, use a dismissible welcome dialog with at most three real business entries.
  • Welcome or onboarding entries must point to executable user tasks, such as 处理待办, 提交报销 or 请假; do not use explanatory entries like 了解流程, and do not route users to settings or notifications as a substitute for task guidance.
  • 个人设置 default content is account identity, notification preference and Enterprise WeChat binding. Organization, report line and role summary may be folded under 账号详情; permission code lists and full employee dossier fields stay out of the default view.

Why: Small teams need fewer default decisions. Persistent onboarding and permission-heavy profile pages make ordinary users inspect system internals before doing work.

Tests Required:

  • Focused Playwright coverage for changed workbench or profile routes.
  • Screenshot evidence for first-login welcome, workbench first screen and profile default view when changing these surfaces.

Convention: Detail And Mobile Command Surfaces

What: PC detail pages and notification-like flows should reuse the Wave3 surface classes in client/src/index.css before adding page-local Tailwind card systems:

  • detail-workspace-grid for the main PC detail two-column workspace.
  • detail-command-panel, detail-fact-grid and detail-fact-card for compact status, summary, payment, export and evidence facts.
  • detail-evidence-card for attachments, invoices and other proof material.
  • detail-timeline and detail-timeline-item for approval progress and history.
  • detail-action-stack and detail-action-card for approve/reject/return, payment and other high-risk command groups.
  • notification-inbox, notification-row-card and notification-row-card--unread for PC notification streams; avoid reverting notifications to a desktop table unless a task explicitly requires tabular comparison.

Mobile key pages should reuse the mobile command classes in client/src/styles/mobile-touch.css:

  • mobile-focus-card for the current task, current list or account summary.
  • mobile-command-surface for tabs, search and filters.
  • mobile-list-card--focus and mobile-list-card--unread for state emphasis.

Why: Detail pages should feel like quiet command surfaces: status, summary, primary action, evidence and history have predictable places. Mobile pages should stay card/list based, with no PC tables, no horizontal overflow and no button text squeeze.

Tests Required:

  • cd client && npm run typecheck
  • cd client && npm run build
  • Focused Playwright screenshot coverage for changed PC detail, notification and mobile pages.
  • UI design rules check when changing shared visual classes.

Scenario: Auth Pages And PWA Updates

1. Scope / Trigger

  • Trigger: login, password change, password recovery or other unauthenticated forms are active while the PWA service worker finds a new frontend version.
  • Applies to client/src/pwa.ts, client/src/layouts/AuthLayout.tsx, and auth routes such as /login, /change-password, /forgot-password, and future reset-password pages.

2. Signatures

  • Event name: hyb-oa:pwa-update-ready.
  • Event detail: { applyUpdate?: () => void | Promise<void> }.
  • Route predicate: auth input pages must be checked by pathname before calling updateServiceWorker(true).

3. Contracts

  • Auth input pages must not auto-reload when onNeedRefresh() fires.
  • Auth input pages must show an explicit update prompt with a user-triggered refresh action.
  • Non-auth pages may keep automatic service-worker update behavior when the task does not change broader runtime policy.
  • Passwords and verification codes must stay only in React component state; do not write them to localStorage, sessionStorage, URL params, reports, or Trellis task evidence.
  • Password visibility controls must use icon buttons with accessible labels and preserve the current input value.

4. Validation & Error Matrix

  • PWA update on /login -> dispatch update-ready event and keep form state.
  • PWA update on /forgot-password -> dispatch update-ready event and keep email/code/password fields in memory.
  • User clicks refresh in the prompt -> run the captured update handler.
  • User dismisses prompt -> no reload and no form reset.
  • Password reset request returns success for any email -> show generic sent message; do not reveal whether the account exists.

5. Good/Base/Bad Cases

  • Good: user is typing a password, update prompt appears, typed values remain.
  • Base: user is on a normal portal page, PWA update can refresh per existing runtime behavior.
  • Bad: service-worker update auto-refreshes an auth form and clears credentials.
  • Bad: tests use real-looking personal email addresses or persist example passwords outside the test file.

6. Tests Required

  • Frontend typecheck and build.
  • Focused Playwright auth UX test asserting:
    • login password can be shown/hidden without losing value;
    • forgot-password flow calls request-code and reset endpoints;
    • auth-page PWA update prompt appears without clearing inputs.
  • UI design rules check when auth page layout changes.
  • Any Playwright test that renders /login, /forgot-password or another auth route with mocked /api/* responses must mock /api/auth/capabilities with emailDelivery.ready, emailLoginCode.enabled and passwordReset.enabled. Returning {} for unknown auth APIs can crash auth pages before the visual or navigation assertion actually runs.

7. Wrong vs Correct

Wrong

onNeedRefresh() {
  void updateServiceWorker?.(true);
}

Correct

onNeedRefresh() {
  if (isAuthInputPath(window.location.pathname)) {
    window.dispatchEvent(new CustomEvent(PWA_UPDATE_READY_EVENT, { detail: { applyUpdate } }));
    return;
  }
  applyUpdate();
}

Scenario: Auth Page Layout And Login Entry Hierarchy

1. Scope / Trigger

  • Trigger: login, password recovery, forced password change or future unauthenticated auth forms need layout changes.
  • Applies to client/src/layouts/AuthLayout.tsx, shared auth components under client/src/components/auth/, and auth routes such as /login, /forgot-password and /change-password.

2. Component Contract

  • Auth routes should use one shared auth panel for brand, page title, subtitle, content and optional footer links.
  • AuthLayout owns only the auth page shell: background, width constraint, centering, PWA update prompt and route outlet.
  • Page implementations own only their form fields, submit actions, validation messages and route-specific login-entry choices.

3. Visual Contracts

  • Do not nest a form card inside another login card.
  • Put the brand block inside the auth panel so logo, system name, title and form share one visual axis.
  • Keep the panel width stable around the current auth-shell width; auth pages should feel like an OA workstation entry, not a marketing split hero.
  • Place 忘记密码? beside the password label or directly below the password input on the right; do not bury it in help text or page footer.
  • Keep the DOM/tab order aligned with the main form flow even when CSS places 忘记密码? visually beside the password label: username/email input -> password input -> secondary controls. Do not render the forgot-password link before the password input in DOM order.
  • Use password visibility icon buttons with accessible labels and preserve the current input value.

4. Login Entry Hierarchy

  • Normal browser: account/password login is primary; Enterprise WeChat and OIDC entries belong below the primary submit button as secondary methods.
  • Enterprise WeChat WebView: Enterprise WeChat one-click entry is primary; account/password login remains available as the fallback path.
  • Enterprise WeChat entry changes must remain frontend entry hierarchy changes unless the task explicitly approves backend OAuth, binding or WeCom API work.
  • Email verification-code login is a separate login mode. Do not infer it from password-recovery verification-code flows without a backend login-code contract and abuse-control plan.

5. Tests Required

  • Frontend typecheck and build.
  • Focused auth E2E covering:
    • password visibility and forgot-password navigation on /login;
    • keyboard Tab from the username/email field lands on the password input, not the forgot-password link;
    • password recovery request-code and reset flow on /forgot-password;
    • normal-browser Enterprise WeChat entry as secondary CTA;
    • WeCom WebView Enterprise WeChat entry as primary CTA with password fallback.
  • UI design rules check and Playwright screenshot evidence when the auth layout changes visibly.

6. Wrong vs Correct

Wrong

<AuthLayout>
  <Card>
    <Card>
      <LoginForm />
    </Card>
  </Card>
</AuthLayout>

Correct

<AuthPanel title="登录工作台" subtitle="账号密码登录">
  <LoginForm />
  <SecondaryLoginMethods />
</AuthPanel>

Wrong

<Label htmlFor="login-password">密码</Label>
<Link to="/forgot-password">忘记密码?</Link>
<PasswordInput id="login-password" />

Correct

<Label htmlFor="login-password">密码</Label>
<PasswordInput id="login-password" />
<Link className="row-start-1" to="/forgot-password">忘记密码?</Link>

Convention: Finance And Admin Console Surfaces

What: PC finance and C-class admin pages should reuse Wave4 shared classes from client/src/index.css before creating page-local card/table layouts:

  • finance-status-strip, finance-metric-card, finance-metric-card__label and finance-metric-card__value for compact five-column status summaries.
  • finance-workspace-grid, finance-command-panel, finance-list-card and finance-list-card--active for payment/AP/invoice operational workspaces.
  • admin-command-panel, admin-tile-grid, admin-tile-card and admin-workspace-grid for settings, workflow, feature-flag, user, master data, notification-channel and audit pages.

Why: Finance/admin pages are lower-frequency but high-risk operational surfaces. They need dense scanability, stable command regions and clear risk hierarchy without turning every page into a bespoke Tailwind card system.

Example:

<div className="finance-status-strip" data-testid="payment-batches-summary">
  <div className="finance-metric-card">
    <div className="finance-metric-card__label">可处理</div>
    <div className="finance-metric-card__value">{actionableCount}</div>
  </div>
</div>

<div className="finance-workspace-grid">
  <section className="finance-command-panel">...</section>
  <section className="finance-command-panel">...</section>
</div>

Tests Required:

  • cd client && npm run typecheck
  • cd client && npm run build
  • Focused Playwright coverage for changed finance/admin pages. The Wave4 baseline test is client/tests/e2e/pc-finance-admin-visual-wave4.spec.ts.
  • UI design rules check when changing these shared classes.