---
title: 开发文档
order: 3
category: pro
---

# PisellTimeNavigator 组件开发文档

## 组件信息

**组件名称**：PisellTimeNavigator（Time Navigator）

**组件版本**：1.0.0

**组件分类**：Pro Components / Data Entry

**开发状态**：开发中

**最后更新**：2026-03-11

---

## 组件描述

PisellTimeNavigator 是可拖动的时间导航轴组件，用于在指定时间范围内进行时间浏览、选择与控制。组件不接入业务数据，仅提供时间刻度渲染、当前时间指示、拖动浏览、吸附选择与时间变更事件输出，作为多视图共享的全局时间控制器。

**核心价值**：

- 统一时间导航交互模型，避免各业务重复实现时间轴
- 解耦时间控制与业务数据
- 支持高频拖动与流畅浏览（含可选惯性滚动）
- 支持横向/竖向、轴动/指针动两种模式

**依赖组件**：PisellScrollView（横向滚动 + 左右箭头）、dayjs、@pisell/utils（locales.getText）

**适用场景**：餐饮 Floor Plan、预订时间跳转、日程/排班时间轴、统计报表时间跳转、Kiosk 大屏等。

---

## 组件需求点罗列

### 功能需求

1. **时间数据**
   - value 为 dayjs 兼容值（Dayjs | string，string 交 dayjs 解析）；onChange 回传 Dayjs
   - 默认时间范围：单日 00:00–24:00；可配置营业时间（如 10:00–23:00）或 24 小时

2. **当前时间与 Realtime**
   - 内置「Now」固定入口（Figma 右侧按钮：背景 #FFF4ED，文字 #FF692E，左箭头）
   - 点击 Now：时间轴回到当前系统时间并进入 Realtime 模式
   - Realtime 模式：value 自动跟随系统当前时间更新（需暴露，如 `realtime: boolean` 或通过 onChange 持续回传）

3. **刻度与层级**
   - AxisViewport：固定高度（Figma 48px）、可横向滚动，用 PisellScrollView 包裹（lrArrows）
   - BackgroundLayer：背景区间（当前时间渐变、禁用时间段、营业时间范围、选中区间），仅视觉/限制选择
   - ScaleLayer：大刻度（整点）、小刻度（如 15min）、小时标签、跨天分隔线
   - CursorLayer：当前选中时间指示（竖线 + 时间气泡）；浏览态 #344054，Realtime 态 #FF692E
   - GestureLayer：拖动、点击跳转、惯性滑动（简单版）、键盘（左右 + 回车）、吸附

4. **模式**
   - **Axis Moves（默认）**：指示器固定视口中央，刻度移动
   - **Cursor Moves**：刻度固定，指示器在轴上移动
   - **横向（默认）/ 竖向**：竖向严格按 Figma 竖向节点（14226-21944、14226-22025）：时间从上到下递增、上下箭头区、每格 80px 高、横线指示器 + 气泡、Now 在底部

5. **吸附与禁用**
   - 默认吸附到 step（整点、15min、30min 或自定义粒度）
   - 禁用时间段：可配置，禁止选中（点击/吸附到该区间无效）

6. **交互**
   - 点击刻度跳转该时间；点击 Now 回到当前并进入 Realtime
   - 键盘：左右键步进、回车确认；支持 Tab 聚焦后操作
   - 惯性滚动：基于 pointer 速度估算 + requestAnimationFrame 衰减，首版可做简单版

7. **样式（严格按 Figma）**
   - **横向**：容器白底、边框 #F2F4F7、高 48px；左右箭头区 #EAECF0 左 71px / 右 35px；刻度约 82px/格，数字 18px Medium 黑色，小圆点 6px；当前时间渐变 to rgba(255,105,46,0.5)；指示器竖线 3px，气泡 10px 2px 圆角 2px；Now 按钮圆角 8px、阴影，位于右侧。
   - **竖向**：容器白底、边框 #F2F4F7、上下 padding 8px；上下箭头区 #EAECF0（旋转块 54×88–90px），占 54px 高；刻度每格 80px 高，时间 12px #333，分割线 #D0D5DD / 细分线 #E5E5E5 0.5px；指示器横线 2px，气泡 px 6px 圆角 4px；Now 按钮在底部、宽 48px、箭头向上。

8. **多语言**
   - 所有文案（如「Now」、时间格式等）走 `locales.getText`，中英繁

---

## 组件 API 罗列

见当前组件目录下 `types.ts`。要点如下：

- **value**：Dayjs | string | null，当前选中时间；string 由 dayjs 解析
- **onChange**：(value: Dayjs) => void，时间变更回调
- **realtime**：boolean，是否 Realtime 模式（value 自动跟随系统时间）
- **defaultValue**：非受控时初始时间
- **range**：{ start: string, end: string } 或单日 00:00–24:00
- **stepMinutes**：吸附步长（分钟），如 15、30、60
- **cursorMode**：'axis-moves' | 'cursor-moves'
- **orientation**：'horizontal' | 'vertical'
- **businessHours**：{ start: string, end: string }，营业时间（视觉 + 可选限制）
- **disabledRanges**：Array<{ start: string, end: string }>，禁用区间，禁止选中
- **showNowButton**：是否显示 Now 按钮，默认 true
- **snapToStep**：是否吸附到 step，默认 false
- **pastTimeFade**：过去时间淡化背景色，可配置
- **样式**：className、style、容器/刻度/气泡/Now 按钮样式覆盖
- **locale**：dayjs locale
- **timeFormat**：气泡时间展示格式（如 HH:mm）

---

## 组件模块拆解与实现方案

### 文件结构

```
pisellTimeNavigator/
├── index.tsx
├── PisellTimeNavigator.tsx    # 主组件：状态、ScrollView、层级组合
├── PisellTimeNavigator.less   # 按 Figma 变量与类名前缀
├── types.ts
├── components/
│   ├── index.tsx
│   ├── AxisViewport.tsx       # 视口容器（含宽高、overflow）
│   ├── BackgroundLayer.tsx    # 背景区间（渐变、禁用、营业、选中）
│   ├── ScaleLayer.tsx         # 刻度 + 标签 + 小圆点
│   ├── CursorLayer.tsx        # 竖线 + 时间气泡（双态颜色）
│   ├── GestureLayer.tsx       # 拖动、点击、键盘、惯性
│   └── NowButton.tsx          # Now 按钮（固定入口，i18n）
├── hooks/
│   ├── index.ts
│   ├── useTimeAxis.ts         # 时间 ↔ 像素 换算、step 计算
│   ├── useRealtime.ts         # 实时时钟 tick、realtime 状态
│   └── useDragInertia.ts      # 可选：惯性滚动
└── utils/
    └── index.ts               # 刻度生成、区间相交、dayjs 兼容解析
```

### 组成部分与引用关系

- **PisellTimeNavigator** 为根，内部使用 **PisellScrollView**（overflow="x"，scrollActionsConfig type="lrArrows"）包裹 **AxisViewport**。
- **AxisViewport** 内自上而下/自左而右：BackgroundLayer → ScaleLayer → CursorLayer → GestureLayer（同层叠或按 z-index）；**NowButton** 绝对定位于右侧（Figma）。
- 时间 ↔ 像素：由 range、step、容器宽度在 **useTimeAxis** 中算出 scale 与 tick 列表；拖动/点击时由像素反算时间并做 step 吸附与 disabledRanges 校验。
- **useRealtime**：当 realtime 为 true 时，用 setInterval(1000) 或 requestAnimationFrame 更新 value 并触发 onChange。
- 键盘：在 GestureLayer 或根 div 上 onKeyDown，左右步进 step，回车可视为确认（或 no-op，依产品定）。

### 结构图

**横向**：PisellScrollView overflow=x、lrArrows；AxisViewport 固定高度 48px；Now 按钮绝对定位右侧。

**竖向**：PisellScrollView overflow=y、tbArrows；AxisViewport 固定宽度（Figma 54px + 内容）；刻度每格 80px 高；指示器为横线 + 气泡；Now 按钮绝对定位底部。

```
PisellTimeNavigator
  └── PisellScrollView (横向: overflow=x, lrArrows | 竖向: overflow=y, tbArrows)
        └── AxisViewport (横向: 高 48px | 竖向: 宽 54px+ 内容，刻度格 80px 高)
              ├── BackgroundLayer（背景块）
              ├── ScaleLayer（横向: 数字+小圆点 | 竖向: 数字+分割线 #D0D5DD/#E5E5E5）
              ├── CursorLayer（横向: 竖线+气泡 | 竖向: 横线 2px+气泡 圆角 4px）
              ├── GestureLayer（透明层，处理指针/键盘）
              └── NowButton（横向: 右侧 | 竖向: 底部，箭头向上）
```

---

## 实现要点

1. **dayjs**：value、defaultValue、range、businessHours、disabledRanges 均用 dayjs 解析；内部统一 Dayjs，onChange 只回传 Dayjs。
2. **受控/非受控**：仅支持受控（value + onChange）或非受控（defaultValue + onChange），不混用。
3. **Figma**：颜色、尺寸、圆角、字体按设计稿常量写入 less 或 CSS 变量，不安装 Tailwind。
4. **惯性滚动**：可选；实现为记录 pointermove 速度，pointerup 后 requestAnimationFrame 做指数衰减更新 scrollLeft，直至速度接近 0。
5. **禁用区间**：点击或吸附得到候选时间后，若落在 disabledRanges 内则忽略或吸附到最近可用点。

---

## 组件要足够的通用、可扩展

- 通过 range、stepMinutes、businessHours、disabledRanges 适配不同营业时间与限制。
- 通过 cursorMode、orientation 适配不同产品习惯。
- 通过样式覆盖（className、style、各层样式 props）适配主题。
- 通过 locale、timeFormat 适配多语言与时间格式。
