docs(08-05-product-interaction-consistency): 固化交互治理任务规划与规范

这个提交包含在:
2026-08-07 06:50:10 +08:00
父节点 254ba2dbd1
当前提交 d4e9597b37
共修改 30 个文件,包含 1129 行新增和 23 行删除
@@ -252,13 +252,13 @@ reference, not a copy of Feishu or Ant Design branding.
detail panels, confirmations and more menus. Project-specific differences
should simplify or hide low-frequency work for small teams, not invent new
controls or unusual action flows.
- Page titles default to the route name only, such as `工作台`, `待我处理`, `报销`,
- Page titles default to the route name only, such as `工作台`, `待我审批`, `报销`,
`合同`, `采购`, `资产`, `个人设置` and `通知中心`. Page subtitles are off by default and
appear only for exception, empty, permission-denied, Enterprise WeChat/OA
boundary, external-system boundary or irreversible-action explanations.
- Normal workflows should not be explained in default-visible copy. Do not add
subtitles, card descriptions, helper rows or notice bars that restate obvious
actions such as "待我处理是处理工作", "查看通知消息", "先核对再处理" or "去处理要做的事".
actions such as "待我审批是处理工作", "查看通知消息", "先核对再处理" or "去处理要做的事".
- Header primary actions are reserved for creation or a real page-level primary
action: `新建报销`, `新建采购`, `新增合同`, `新增资产`, `新建项目`. Workbench,
pending-task, notification and read-only record pages must not force a header
@@ -274,7 +274,7 @@ reference, not a copy of Feishu or Ant Design branding.
`撤回`, `作废`, `归档`, `更多`. Do not use slogan-like or invented labels
such as `处理最急的一件`.
- Sidebar entries are role-aware common work entries, not an all-permissions
module catalog. Direct entries may include `工作台`, `待我处理`, `报销`, `采购`,
module catalog. Direct entries may include `工作台`, `待我审批`, `报销`, `采购`,
`合同`, `项目`, `资产`, `付款`, `应付账款`, `票据`, `团队`, `请假记录` and `管理`
when the current role has real use. Low-frequency, frozen, staged or
troubleshooting routes stay in `更多` or the `管理` landing page.
@@ -329,13 +329,13 @@ reference, not a copy of Feishu or Ant Design branding.
| Surface | Template | First-screen content | Primary action rule |
| --- | --- | --- | --- |
| `工作台` | 工作台总览 | Compact counts, common entries, real reminders | No forced header action. Use `去处理`, `新建报销`, `查看全部` inside relevant sections; do not show helper sentences for ordinary items. |
| `待我处理` | 任务处理列表 | `待处理` / `已处理` tabs, count, minimal filters, task list | Row action is `处理`; `已处理记录` is a tab, not a separate header button. |
| `待我审批` | 任务处理列表 | `待处理` / `已处理` tabs, count, minimal filters, task list | Row action is `处理`; `已处理记录` is a tab, not a separate header button. |
| `审批详情` | 详情工作区 | Summary, facts/evidence, right summary/action panel, history | One action bar: primary `通过`, secondary `退回`/`驳回`, `转审` in `更多`. |
| `报销` / `采购` / `合同` / `项目` / `资产` / `付款` / `应付账款` / `票据` | 业务台账列表 | One-row common filters plus table/card list | Header creation only when the route owns creation; risky tools use `更多`, clearly named role-only actions or detail pages; no default left-list/right-detail split. |
| Business details | 详情工作区 | Summary, facts, evidence, relations, history | Right-side state/action panel on PC; bottom primary action on mobile. |
| `管理` | 管理配置页 | Grouped landing: `人员组织`, `审批流程`, `基础资料`, `系统设置`, `审计排障` | Management subpages use search/filter, table and right operation column. |
| `个人设置` | 管理配置页 variant | `个人资料`, `账号安全`, `通知偏好`, `企业微信绑定` | No employee dossier, permission matrix or org maintenance in the default profile path. |
| `通知中心` | 业务台账列表 variant | Event inbox for state changes, reminders, failures and sync results | Notification click deep-links to business detail, workflow task or failure explanation; never duplicates `待我处理`. |
| `通知中心` | 业务台账列表 variant | Event inbox for state changes, reminders, failures and sync results | Notification click deep-links to business detail, workflow task or failure explanation; never duplicates `待我审批`. |
| `请假记录` / `考勤` | 业务台账列表 or record view | Enterprise WeChat-owned records, mirror status, exceptions and audit | Ordinary users do not see OA daily leave/punch approval creation; OA emergency entry only for authorized emergency/admin scope. |
| Mobile routes | Mobile expression of the same template | Top status/focus, card list or sectioned detail | One bottom safe-area primary action; secondary actions in `更多` or confirmation. |
@@ -682,7 +682,7 @@ point and prevents each route from inventing a different title/action layout.
the primary sidebar, including `合同`, `采购`, `资产` and `项目` when relevant.
`更多` is for low-frequency, frozen, staged or occasional entries, not a
place to bury common work.
- Approvers see `待我处理`; ordinary employees should not see `待办` or
- 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.
@@ -844,11 +844,11 @@ of exposing every allowed tool at once.
default.
- Initiator views emphasize current status, next step, supplement-material
needs, approval/payment progress and whether edit or withdraw is allowed.
- Approver and processor views emphasize `待我处理` and the current node task.
- Approver and processor views emphasize `待我审批` and the current node task.
Show approve, reject, return or transfer actions only when the server-backed
task assignment says the current user can act.
- Approval actions such as approve, reject, return and transfer execute only in
`待我处理`, workflow task detail, mobile approval detail or an explicitly
`待我审批`, workflow task detail, mobile approval detail or an explicitly
current task-processing panel. Business ledger rows and ordinary business
details use `查看处理`, `查看进度`, `补材料` or a link into the workflow task;
they must not duplicate approval action buttons.
@@ -946,7 +946,7 @@ screen.
- Keep 3 to 5 high-frequency filters visible by default. Prioritize search,
status, scope and date/time; add business-specific filters such as project,
person or type only when they are genuinely frequent.
- The default view must match the route's main job: `我的报销`, `待我处理`,
- The default view must match the route's main job: `我的报销`, `待我审批`,
`合同`, `资产` and similar task nouns. Do not default ordinary users
into company-wide management views.
- Rows and cards must answer what the object is, current state, why it relates
@@ -1349,6 +1349,89 @@ complexity from leaking into WeChat.
- If true Enterprise WeChat device validation is unavailable, record it as
paused manual validation; do not claim full WebView acceptance.
## Scenario: Shared Mobile Sticky Action Surface
### 1. Scope / Trigger
- Trigger: a mobile detail, approval or edit surface needs a bottom fixed
primary-action area, or an existing bottom action area is changed.
- Applies to mobile consumers and responsive business edit forms; it does not
move business permission or state-machine decisions into the shared shell.
### 2. Signatures
- Component: `MobileStickyActionBar({ children, className?, contentClassName?, inlineFromMedium?, testId? })` from
`client/src/components/patterns/MobileStickyActionBar.tsx`.
- Page spacing constant: `MOBILE_STICKY_ACTION_PAGE_CLASS`.
### 3. Contracts
- New or changed mobile bottom actions reuse `MobileStickyActionBar`; do not
copy its fixed positioning, safe-area, grid or shadow classes into a page.
- When a fixed action bar is rendered, the page's scroll/content container also
receives `MOBILE_STICKY_ACTION_PAGE_CLASS` so the final field, evidence card
or history row remains above the bar and device safe area.
- Apply the page class conditionally when actions are conditional; an absent bar
must not leave unexplained fixed-action padding.
- The shared component owns coordinates, safe-area treatment, responsive inline
conversion and action layout only. Consumers still own button text,
permission/state visibility, confirmation, pending and disabled behavior.
- `inlineFromMedium` may convert the action surface to an inline desktop/tablet
row for shared edit templates; mobile detail routes normally keep the fixed
form.
- `PaymentBatchesPage` retains its existing responsive payment-specific bar as
a known legacy exception until a payment-scoped task proves migration has no
business or layout regression. It is not a pattern to copy.
### 4. Validation & Error Matrix
- Bar visible + page class present -> final content remains reachable above the
fixed surface.
- Bar visible + page class missing -> spacing-contract failure.
- Bar hidden + unconditional page class -> unnecessary-padding failure.
- Consumer moves approval/payment state logic into the shared component ->
ownership-contract failure.
- Narrow viewport or device safe area -> actions remain readable and are not
covered by the home indicator.
### 5. Good/Base/Bad Cases
- Good: mobile approval and mobile reimbursement share the same action surface
while each page keeps its own action matrix and confirmation behavior.
- Base: `BusinessEditTemplate` uses `inlineFromMedium` and the same page-spacing
constant when bottom actions exist.
- Bad: paste a page-local `fixed inset-x-0 bottom-0` wrapper and compensate with
an unrelated hard-coded `pb-24`.
### 6. Tests Required
- A source contract asserts mobile approval, mobile reimbursement and
`BusinessEditTemplate` import the shared component and page-spacing constant.
- Focused Playwright covers at least two mobile consumers, including final
content reachability, visible/pending/disabled action states and a narrow
viewport safe-area regression.
- Run `cd client && npm run typecheck && npm run build`.
### 7. Wrong vs Correct
#### Wrong
```tsx
<main className="pb-24">
...
<div className="fixed inset-x-0 bottom-0">{actions}</div>
</main>
```
#### Correct
```tsx
<main className={cn(showActions && MOBILE_STICKY_ACTION_PAGE_CLASS)}>
...
{showActions ? <MobileStickyActionBar>{actions}</MobileStickyActionBar> : null}
</main>
```
## Convention: Notifications Versus Tasks
**What**: Task queues and notifications have separate jobs. A task queue is for
@@ -1356,7 +1439,7 @@ action; a notification center is for event awareness and navigation.
**Contracts**:
- `待我处理` is an action list. Include only items that require the current user
- `待我审批` is an action list. Include only items that require the current user
to process, supplement material, review, pay, approve, confirm or resolve an
exception.
- Task rows must have a clear next step, object summary, business state and
@@ -1482,6 +1565,88 @@ structure.
permission-denied pages, notification failure targets or shared error
components.
## Scenario: Ordinary User Error Message Resolution
### 1. Scope / Trigger
- Trigger: a frontend component catches an API or JavaScript error and needs to
show failure feedback on an ordinary employee, approver, finance or mobile
path.
- Explicit exception: `client/src/pages/AuditLogsPage.tsx` is a permissioned,
read-only technical detail surface and may display the minimum API code beside
its error message.
### 2. Signatures
- Shared resolver:
`resolveErrorMessage(error: unknown, fallback = "操作失败,请稍后重试"): string`
from `client/src/lib/resolveErrorMessage.ts`.
- API business mapping remains
`mapApiErrorMessage(code, details, fallbackMessage)` in
`client/src/api/errorMessages.ts`.
### 3. Contracts
- Ordinary user feedback calls `resolveErrorMessage` instead of concatenating
`error.code`, `error.message`, request methods or API paths locally.
- API codes may still drive control flow, such as detecting an already-processed
task or a disabled capability, but they must not be reused as display copy.
- The resolver returns mapped natural Chinese, removes a leading technical code
prefix and falls back when the candidate is empty, lacks natural Chinese or
resembles a URL, API/filesystem path, HTTP request line, stack frame or raw
JSON payload.
- Feedback placement remains separate from message parsing: field/material
problems are local, region load failures are persistent and retryable, and
short success feedback may use toast.
- `AuditLogsPage` is the only current code-plus-message exception. New admin or
troubleshooting exceptions require an explicit technical detail area and a
scoped Trellis decision; do not weaken the shared resolver.
### 4. Validation & Error Matrix
- Known API code with a mapped business message -> show the mapped natural
Chinese only.
- Candidate starts with `VALID_001:` or `500:` -> remove the prefix before
display.
- Candidate is English-only, contains `/api/`, a full path, HTTP method, stack
frame or raw JSON -> show the caller-provided fallback.
- Unknown value or empty message -> show the caller-provided fallback.
- Audit-log load/export failure caused by `ApiError` -> the read-only audit
error area may show `<code>: <message>`; ordinary pages may not.
### 5. Good/Base/Bad Cases
- Good: a reimbursement page uses
`resolveErrorMessage(error, "加载报销失败,请稍后重试")` and keeps the retry
action beside the persistent error.
- Base: a page uses `isApiError(error)` only to branch on a business condition,
then resolves any displayed failure through the shared helper.
- Bad: each page removes a code prefix with its own regex or displays
`` `${error.code}: ${error.message}` `` on an ordinary path.
### 6. Tests Required
- A source contract scans ordinary frontend consumers and rejects local
code-plus-message concatenation while listing `AuditLogsPage` as the explicit
exception.
- Focused tests cover a mapped API error, a leading code prefix, a technical
detail fallback and an unknown error.
- Run `cd client && npm run typecheck && npm run build`.
### 7. Wrong vs Correct
#### Wrong
```tsx
setError(isApiError(error) ? `${error.code}: ${error.message}` : "加载失败");
```
#### Correct
```tsx
setError(resolveErrorMessage(error, "加载失败,请稍后重试"));
```
## Convention: Dangerous Action Confirmation
**What**: Destructive, financial, approval, configuration, batch and external
@@ -1726,7 +1891,7 @@ training or permission-maintenance surfaces:
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
`待我审批`, `提交报销` 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 personal profile, password/security,