Files
workspace/code/fms/.codebuddy/plans/date-picker-component_04c90160.md
T
2026-08-16 22:01:32 +08:00

223 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: date-picker-component
overview: 在 fms-vue 组件库中新增 DatePicker(单日期,支持时间)与 RangePicker(日期范围,支持时间)两个组件,复用现有 Popover 作为弹层,使用 dayjs 处理格式化(format 展示格式、valueFormat 绑定值格式),沿用现有 SCSS 主题体系。暂不做快捷选项与 disabledDate。
design:
architecture:
framework: vue
component: tdesign
styleKeywords:
- Enterprise
- Clean
- Form-Control
- Theme-Aware
fontSystem:
fontFamily: PingFang SC
heading:
size: 16px
weight: 600
subheading:
size: 14px
weight: 500
body:
size: 13px
weight: 400
colorSystem:
primary:
- "#2563EB"
- "#3B82F6"
background:
- "#FFFFFF"
- "#F8FAFC"
text:
- "#1E293B"
- "#64748B"
functional:
- "#22C55E"
- "#EF4444"
- "#F1F5F9"
todos:
- id: add-dayjs-dep
content: 在 package.json 增加 dayjs 依赖并安装
status: completed
- id: create-date-utils
content: 新建 date-utils.js,基于 dayjs 实现 format/parse/矩阵等纯函数
status: completed
dependencies:
- add-dayjs-dep
- id: create-calendar-panel
content: 新建 calendar-panel.vue 共享月历与时间选择面板
status: completed
dependencies:
- create-date-utils
- id: create-date-picker
content: 新建 date-picker.vue 单个日期时间选择器
status: completed
dependencies:
- create-calendar-panel
- id: create-range-picker
content: 新建 range-picker.vue 范围选择器含 hover 预览
status: completed
dependencies:
- create-calendar-panel
- id: create-date-scss
content: 新建 index.scss 沿用主题变量完成样式
status: completed
dependencies:
- create-calendar-panel
- create-date-picker
- create-range-picker
- id: add-demo-verify
content: 在 App.vue 增加演示区块并运行 lint/fmt 验证
status: completed
dependencies:
- create-date-picker
- create-range-picker
- create-date-scss
---
## 用户需求
在 fms-vue 组件库中新增 Date 日期选择组件,包含单个日期选择(DatePicker)与范围选择(RangePicker),并支持时间(时分秒)选择。
## 产品概述
组件以「只读输入框触发器 + 点击弹出面板」形式工作,面板复用现有 Popover 组件。DatePicker 选择单个日期时间,RangePicker 选择起止区间。两者共享一个日历面板组件。用户可在输入框看到按 format 格式化后的文本,选中后通过 v-model(或 valueFormat 字符串)回传值。
## 核心特性
- 单个日期时间选择(DatePicker)与范围选择(RangePicker)
- 点击输入框弹出日历面板,复用 Popover(manual 受控、followTriggerWidth、点击外部/Esc 关闭、定位翻转)
- 支持 format 设置展示格式(数组时多格式匹配,以第一个为准,参考 dayjs#format);valueFormat 设置绑定值格式(设为后 value/defaultValue/v-model 可为字符串,change 返回同格式字符串)
- 支持时间选择(时/分/秒),showTime 控制
- 日历面板:年月切换、星期表头、日期网格(含上下月补位)、今日标记、选中态、范围区间高亮与 hover 预览
- 清除按钮(clearable)、禁用态(disabled)、占位符(placeholder)
- 沿用现有 SCSS 主题变量与暗色/主题切换体系
- 日期处理使用 dayjs
- 暂不做:快捷选项(presets)、disabledDate 禁用日期(面板预留 disabled 钩子)
## 技术栈
- 框架:Vue 3.5 + `<script setup>`(与现有组件一致)
- 样式:SCSS,`<style scoped lang="scss">` + `@use './index.scss'`,复用现有主题 CSS 变量
- 弹层:复用 `src/components/ui/popover/popover.vue`(trigger="manual"、:open 受控、followTriggerWidth、arrow=false、placement="bottom-start")
- 图标:`@lucide/vue`(Calendar / ChevronLeft / ChevronRight / X 等)
- 日期库:dayjs(需新增依赖)
- 无统一出口文件,沿用按需 import 方式
## 实现策略
核心是把「触发器 + Popover + 日历面板」三层解耦,与现有 Select 组件结构对标。日历面板作为共享受控组件,被两个 Picker 复用;日期解析/格式化全部收敛到 `date-utils.js` 纯函数(基于 dayjs),保证可单测、可复用。
### 关键技术决策
1. **复用 Popover 而非自建浮层**:Popover 已具备定位翻转、点击外部关闭、Esc 关闭、followTriggerWidth,且 Select 已验证此模式。自建会重复这些逻辑且易出 bug。
2. **date-utils.js 纯函数封装 dayjs**:`format(date, fmt)`、`parse(str, fmt)`、`getMonthMatrix(year, month)`、`isSameDay`、`isSameMonth`、`addMonth`、`compareDate`、`isInRange` 等。Picker 只处理交互与状态,日期计算一律下沉,便于测试与扩展。
3. **format 多格式匹配**:format 为数组时,解析/展示以第一个为准(参考 dayjs#format 与 @rc-component/picker 约定)。`format` 用于展示文本,`valueFormat` 用于绑定值序列化。
4. **modelValue 类型**:内部统一用原生 `Date`(或 dayjs 对象)做运算;对外通过 `valueFormat` 决定 v-model 是 `Date` 还是字符串。RangePicker 为 `[start, end]`(对应 Date 或字符串数组)。
5. **范围 hover 预览**:RangePicker 内部维护 `hoverValue`,end 未选定前,鼠标划过日期实时高亮 `start~hover` 区间。
### 性能与可靠性
- 日历矩阵每月 42 格,`computed` 缓存,月份切换才重算,无额外遍历开销。
- Popover 仅在 open 时注册全局监听,关闭即清理(Popover 已处理),无长期监听泄漏。
- dayjs 解析失败(非法字符串)时回退 null 并保留输入框文本,避免崩溃。
## 实现要点(执行细节)
- **dayjs 依赖**:在 `fms-vue/package.json` 的 dependencies 增加 `"dayjs": "^1.11.13"`,执行 `pnpm install`。
- **复用现有 SCSS 变量**:配色、圆角、阴影沿用 `src/theme/tokens.css` 与现有组件 `index.scss` 中的 CSS 变量,确保暗色/主题切换生效。
- **日历面板 disabled 钩子**:格子渲染时预留 `(cell) => boolean` 判断位(默认返回 false),便于后续扩展 disabledDate。
- **保持 Select 交互习惯**:触发器类 Input 框、清除按钮、键盘(左右/上下/回车/Esc)可后续补,第一版至少支持点击与 Esc。
- **App.vue 演示**:参照现有 message/notification 演示区块,新增 DatePicker / RangePicker 演示,验证弹层定位、选择、清除、valueFormat 双向绑定、范围 hover、主题切换。
## 架构设计
### 组件关系
```mermaid
graph TD
DatePicker --> Popover
RangePicker --> Popover
DatePicker --> CalendarPanel
RangePicker --> CalendarPanel
CalendarPanel --> dateUtils[date-utils.js / dayjs]
Popover --> position[utils/position.js]
```
## 目录结构
```
fms-vue/
├── package.json # [MODIFY] 新增 dayjs 依赖
├── src/
│ ├── App.vue # [MODIFY] 新增 DatePicker/RangePicker 演示区块
│ └── components/ui/
│ ├── date/
│ │ ├── date-utils.js # [NEW] 基于 dayjs 的纯函数:format/parse/getMonthMatrix/isSameDay/isSameMonth/addMonth/compareDate/isInRange
│ │ ├── calendar-panel.vue # [NEW] 共享月历面板:年月切换、星期表头、日期网格、今日/选中/范围高亮、时间选择行;emit pick
│ │ ├── date-picker.vue # [NEW] 单个日期时间选择:类 Input 触发器 + Popover + calendar-panel;支持 format/valueFormat/clearable/disabled/showTime;v-model 为 Date 或字符串
│ │ ├── range-picker.vue # [NEW] 范围选择:start/end/hoverValue 管理,第一次点设 start、第二次设 end 并关闭;modelValue 为 [start,end]
│ │ └── index.scss # [NEW] 样式,沿用主题变量,选择器前缀 .fms-date-picker/.fms-range-picker/.fms-calendar-panel
│ └── popover/
│ └── popover.vue # [REUSE] 无需改动,直接复用
```
## 关键代码结构(接口约定)
```js
// date-utils.js 核心导出(接口级,非实现体)
export function format(date, fmt = 'YYYY-MM-DD HH:mm:ss')
export function parse(str, fmt = 'YYYY-MM-DD HH:mm:ss')
export function getMonthMatrix(year, month) // 返回 6×7 日期元信息数组
export function isSameDay(a, b)
export function isInRange(date, start, end)
```
DatePicker props(要点):
- `modelValue: Date | string`
- `format: string | string[]`(默认 'YYYY-MM-DD HH:mm:ss')
- `valueFormat: string`(设置后 modelValue 可为字符串)
- `showTime: boolean`
- `clearable: boolean`、`disabled: boolean`、`placeholder: string`
RangePicker props(要点):
- `modelValue: [Date|string, Date|string]`
- 其余同 DatePicker(无单值 showTime 独立开关,范围自带时间行)
## 设计风格
沿用现有 fms-vue 组件库视觉语言(与 Select/Input/Message 一致),采用简洁、规整的企业级表单控件风格。整体使用现有主题 CSS 变量,支持暗色与主题切换。
## 页面/组件设计
### DatePicker 触发器
- 顶部:类 Input 只读输入框,左侧日历图标,右侧清除/下拉箭头,聚焦与打开态有边框高亮。
- 底部无(弹层内处理)。
### 弹出面板(CalendarPanel)
- 顶部区块:左箭头 + 当前「YYYY 年 MM 月」+ 右箭头,居中布局,hover 箭头有背景反馈。
- 中部区块:星期表头(日一二三四五六),6×7 日期网格;今日有圆点/描边,选中日期实心高亮,范围区间内浅色填充,hover 预览区间浅色。
- 底部区块(showTime):时/分/秒三列数字步进器或滚动选择,确认/此刻快捷。
- 动画:面板随 Popover 的 `popover` transition 淡入下移出现。
### RangePicker 触发器
- 两个输入框段(开始 ~ 结束),中间分隔波浪号,其余同 DatePicker。
## Agent Extensions
### Skill
- **ui-ux-pro-max**
- Purpose: 设计 DatePicker/RangePicker 及日历面板的视觉风格、间距、交互细节与暗色适配,确保与现有组件库视觉一致且美观。
- Expected outcome: 产出日历面板网格、触发器、时间选择行的布局与样式规范,融入现有主题体系。
### SubAgent
- **code-explorer**
- Purpose: 在实现前进一步确认 popover.vue、select.vue、input.vue、tokens.css 的具体类名与 SCSS 变量,避免样式冲突或重复定义。
- Expected outcome: 提供可复用的 CSS 变量名与 BEM 类名清单,供 index.scss 直接使用。