design-context.md 11 KB

Comet Design Handoff

  • Change: weapp-capsule-safearea
  • Phase: design
  • Mode: compact
  • Context hash: aa611532c57837edccdaa3c5221ab2e70b42ac50630cca269c20b0a2dbbd2af3

Generated-by: comet-handoff.sh

OpenSpec remains the canonical capability spec. This handoff is a deterministic, source-traceable context pack, not an agent-authored summary.

openspec/changes/weapp-capsule-safearea/proposal.md

  • Source: openspec/changes/weapp-capsule-safearea/proposal.md
  • Lines: 1-30
  • SHA256: ff88164caf99258a7df621a84b1c163d9e934971c2b80ea01f6fccedbb6e528c

    ## Why
    
    微信小程序右上角存在原生胶囊按钮(菜单/关闭),宽度在不同机型下约为 87px。当前项目所有页面均使用 `navigationStyle: custom` 自定义导航栏,并把部分操作按钮(通知、新增、今天等)放在右上角,导致部署到微信真机后胶囊遮挡功能按钮,影响使用。需要引入全局统一的胶囊安全区组件,让顶部栏右侧操作区自动避让胶囊。
    
    ## What Changes
    
    - 新增全局胶囊安全区组件 `CapsuleSafeArea`,基于 `uni.getMenuButtonBoundingClientRect()` 计算右侧安全宽度。
    - 改造 `TopBar` 组件,右侧插槽自动包裹在安全区内,保证操作按钮不会被胶囊遮挡。
    - 调整三个已确认存在遮挡问题的页面顶部区域:
    - `pages/home/home`(各角色首页右上角通知铃铛)
    - `pages/schedule/schedule`(排班页右上角「今天」「新增排班」)
    - `pages/task/task`(任务页右上角「新增/发布」按钮)
    - 胶囊安全区采用「右侧操作按钮整体左移」策略,改动最小且保持按钮可点。
    - 非微信环境(H5/App)回退到默认右侧间距,避免异常。
    
    ## Capabilities
    
    ### New Capabilities
    
    - `weapp-capsule-safearea`: 微信小程序胶囊安全区适配,提供全局组件和 TopBar 集成。
    
    ### Modified Capabilities
    
    - 无
    
    ## Impact
    
    - 受影响的文件:`src/components/common/TopBar.vue`、新增 `src/components/common/CapsuleSafeArea.vue`、`src/pages/home/home.vue`、`src/pages/schedule/schedule.vue`、`src/pages/task/task.vue`。
    - 依赖:仅使用 uni-app 内置 API `uni.getMenuButtonBoundingClientRect()` 与 `uni.getSystemInfoSync()`。
    - 不涉及后端接口、数据模型、API 变更。
    

openspec/changes/weapp-capsule-safearea/design.md

  • Source: openspec/changes/weapp-capsule-safearea/design.md
  • Lines: 1-66
  • SHA256: acc12d11b7486d4edaac7949821ba1529b66b165023c1c0897993724dccfe889

    ## Context
    
    清道夫微信小程序使用 `navigationStyle: custom` 实现自定义导航栏。所有页面顶部的标题、返回按钮、功能按钮(通知、新增、筛选等)都由组件自行渲染。然而微信小程序右上角存在系统胶囊按钮(菜单 + 关闭),在 iPhone 上宽度约 87px、安卓上约 96px,且距离屏幕右侧有一定边距。当前部分页面把操作按钮直接放在右上角,导致真机运行时胶囊遮挡按钮。
    
    已有组件:`StatusBar.vue` 负责状态栏占位,`TopBar.vue` 负责标题栏。两者均未考虑胶囊区域。
    
    ## Goals / Non-Goals
    
    **Goals:**
    - 提供可在任何页面/组件复用的胶囊安全区方案。
    - 改造 `TopBar.vue`,使其右侧插槽内容自动避让胶囊。
    - 修复 `pages/home/home`、`pages/schedule/schedule`、`pages/task/task` 的遮挡问题。
    - 保持 H5/App 等非微信环境下的正常显示( graceful fallback)。
    
    **Non-Goals:**
    - 不调整底部 tabBar 结构。
    - 不改页面整体布局风格。
    - 不处理胶囊以外的系统 UI(如灵动岛、底部横条)。
    
    ## Decisions
    
    ### 1. 新增 `CapsuleSafeArea.vue` 组件
    - 通过 `uni.getMenuButtonBoundingClientRect()` 获取胶囊坐标与宽度。
    - 通过 `uni.getSystemInfoSync()` 获取屏幕宽度。
    - 计算右侧需要避让的最小宽度:`screenWidth - menuButton.left + 8px`(额外 8px 缓冲)。
    - 在微信小程序下渲染一个占位 view;在其他平台返回 0 或固定值,避免影响布局。
    - 提供 `width` 插槽 prop 或默认 slot,方便 `TopBar` 直接使用。
    
    ### 2. `TopBar.vue` 集成安全区
    - 右侧 `<slot name="right" />` 外层用 `CapsuleSafeArea` 包裹,使右侧内容左侧平移。
    - 保持标题居左或根据左侧内容自适应,不改动标题样式。
    - 为避免标题被挤压过窄,标题区使用 `flex-1 min-w-0 truncate`。
    
    ### 3. 页面顶部操作按钮统一使用 `TopBar`
    - `pages/home/home` 三个首页当前直接在页面内写 header,需把右上角通知铃铛移入 `TopBar` 的 right slot,或给按钮加 `mr-[safeWidth]`。
    - `pages/task/task` 的 `TaskListView` 顶部「新增」按钮纳入 `TopBar` right slot。
    - `pages/schedule/schedule` 的 `ScheduleView` 顶部「今天」「新增排班」按钮纳入 `TopBar` right slot。
    
    ### 4. 策略选择:右侧按钮整体左移
    - 对比方案:
    - 按钮换行:改动大,破坏视觉一致性。
    - 右侧留空白:浪费空间且用户看不到原因。
    - 按钮整体左移:改动最小,符合微信小程序主流做法,保留按钮可点击区域。
    - 选定「按钮整体左移」。
    
    ## Risks / Trade-offs
    
    - **[Risk]** `uni.getMenuButtonBoundingClientRect()` 在小程序初始化后可能异步变化(如旋转屏幕)。
    - **Mitigation:** 在 `onMounted` 获取并监听窗口尺寸变化;如获取失败则使用基于屏幕宽度的默认估算值。
    - **[Risk]** 不同机型胶囊尺寸差异大。
    - **Mitigation:** 使用动态 API 获取真实尺寸,而非写死 px。
    - **[Risk]** 顶部标题栏空间被压缩,标题可能显示不全。
    - **Mitigation:** 标题使用 `truncate` 截断,并确保左侧返回按钮只在需要时显示。
    - **[Trade-off]** H5/App 下无胶囊,安全区宽度为 0,布局与之前一致。
    
    ## Migration Plan
    
    - 新增 `CapsuleSafeArea.vue`。
    - 修改 `TopBar.vue`,集成安全区。
    - 修改三个页面/视图组件,使右上角按钮通过 `TopBar` right slot 渲染。
    - 构建 `npm run build:mp-weixin` 验证无编译错误。
    - 在微信开发者工具真机调试中检查胶囊未遮挡按钮。
    
    ## Open Questions
    
    - 是否需要在每个页面单独处理 `StatusBar + TopBar`,还是统一封装一个 `PageHeader`?本次保持最小改动,只改 `TopBar`。
    

openspec/changes/weapp-capsule-safearea/tasks.md

  • Source: openspec/changes/weapp-capsule-safearea/tasks.md
  • Lines: 1-16
  • SHA256: 03218da571a4b7615bbf048d47b0b4479114a6ee5485fec873c6b65a84415757

    ## 1. Core component
    
    - [ ] 1.1 Create `src/components/common/CapsuleSafeArea.vue` to compute and expose capsule safe width.
    - [ ] 1.2 Modify `src/components/common/TopBar.vue` to wrap the right slot with `CapsuleSafeArea`.
    
    ## 2. Page header fixes
    
    - [ ] 2.1 Update `src/components/home/DispatchHome.vue`, `SalesHome.vue`, `ConstructionHome.vue` to place the notification bell inside `TopBar` right slot.
    - [ ] 2.2 Update `src/components/task/TaskListView.vue` to place the add/publish button inside `TopBar` right slot.
    - [ ] 2.3 Update `src/components/dispatch/ScheduleView.vue` to place "今天" and "新增排班" buttons inside `TopBar` right slot.
    
    ## 3. Validation
    
    - [ ] 3.1 Run `npm run type-check` and fix any TypeScript errors.
    - [ ] 3.2 Run `npm run build:mp-weixin` and confirm the build succeeds.
    - [ ] 3.3 Commit each completed task with a clear message.
    

openspec/changes/weapp-capsule-safearea/specs/weapp-capsule-safearea/spec.md

  • Source: openspec/changes/weapp-capsule-safearea/specs/weapp-capsule-safearea/spec.md
  • Lines: 1-51
  • SHA256: 5983ff126f33775ed1825d40ff0fb8f36d74fa8e2e0e8f7d7874f05a2b3719bc

    ## ADDED Requirements
    
    ### Requirement: CapsuleSafeArea component calculates safe margin
    The system SHALL provide a `CapsuleSafeArea` component that computes the minimum right-side margin needed to avoid the WeChat mini-program capsule button, and wraps its default slot with that left margin.
    
    #### Scenario: WeChat environment
    - **WHEN** the app runs in WeChat mini-program
    - **THEN** the component uses `uni.getMenuButtonBoundingClientRect()` and `uni.getSystemInfoSync()` to compute `safeWidth = screenWidth - menuButton.left + 8px`, and applies `margin-left: safeWidth` to the wrapped content
    
    #### Scenario: Non-WeChat environment
    - **WHEN** the app runs on H5 or App
    - **THEN** the component returns a safe width of `0` so the layout remains unchanged
    
    #### Scenario: API failure fallback
    - **WHEN** `uni.getMenuButtonBoundingClientRect()` fails or returns invalid data
    - **THEN** the component falls back to a conservative estimate based on `screenWidth` (approximately 28% of screen width) so the layout remains safe on common devices
    
    ### Requirement: TopBar right slot is protected by capsule safe area
    The system SHALL wrap the `TopBar` right slot inside `CapsuleSafeArea` so any right-side action buttons are shifted left of the capsule automatically. Pages using `TopBar` shall not need to manually add capsule-safe spacing.
    
    #### Scenario: Right slot contains a button
    - **WHEN** a page places a button in the `TopBar` right slot
    - **THEN** the button is rendered with a left margin equal to the capsule safe width and remains fully clickable
    
    ### Requirement: Home page notification bell avoids capsule
    The system SHALL ensure the notification bell on `pages/home/home` (all roles) does not overlap the WeChat capsule by rendering the home header with `TopBar` and placing the bell inside the `TopBar` right slot.
    
    #### Scenario: Dispatch home renders notification icon
    - **WHEN** `DispatchHome.vue` renders the top-right notification bell
    - **THEN** the header uses `TopBar`, the left slot keeps the existing avatar/greeting, and the bell is placed inside the `TopBar` right slot and is not covered by the capsule
    
    ### Requirement: Task page add button avoids capsule
    The system SHALL ensure the "新增/发布" button on `pages/task/task` does not overlap the WeChat capsule by rendering the task list header with `TopBar` and placing the button inside the `TopBar` right slot.
    
    #### Scenario: TaskListView renders add button
    - **WHEN** `TaskListView.vue` renders the top-right add button
    - **THEN** the header uses `TopBar` with the page title, and the button is placed inside the `TopBar` right slot and is not covered by the capsule
    
    ### Requirement: Schedule page actions avoid capsule
    The system SHALL ensure the "今天" and "新增排班" buttons on `pages/schedule/schedule` do not overlap the WeChat capsule by placing both buttons inside the `TopBar` right slot.
    
    #### Scenario: ScheduleView renders top actions
    - **WHEN** `ScheduleView.vue` renders the top-right action buttons
    - **THEN** both "今天" and "新增排班" buttons are placed inside the `TopBar` right slot and are not covered by the capsule, and the month-switching row below `TopBar` only keeps the month navigation on the left
    
    ### Requirement: Build passes and layout remains valid on non-WeChat platforms
    The system SHALL keep `npm run build:mp-weixin` and `npm run type-check` passing, and non-WeChat layouts must not regress.
    
    #### Scenario: Build verification
    - **WHEN** the project is built for WeChat or type-checked
    - **THEN** no TypeScript or template errors are introduced