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

17 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.

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.
  • 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 active Issue; 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 primary navigation grouping. The mental model is 工作 -> 业务 -> 财务 -> 管理 -> collapsed 更多.
  • 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.

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: 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.