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

11 KiB
Raw Permalink Blame History

name, overview, design, todos
name overview design todos
date-picker-component 在 fms-vue 组件库中新增 DatePicker(单日期,支持时间)与 RangePicker(日期范围,支持时间)两个组件,复用现有 Popover 作为弹层,使用 dayjs 处理格式化(format 展示格式、valueFormat 绑定值格式),沿用现有 SCSS 主题体系。暂不做快捷选项与 disabledDate。
architecture styleKeywords fontSystem colorSystem
framework component
vue tdesign
Enterprise
Clean
Form-Control
Theme-Aware
fontFamily heading subheading body
PingFang SC
size weight
16px 600
size weight
14px 500
size weight
13px 400
primary background text functional
#2563EB
#3B82F6
#FFFFFF
#F8FAFC
#1E293B
#64748B
#22C55E
#EF4444
#F1F5F9
id content status
add-dayjs-dep 在 package.json 增加 dayjs 依赖并安装 completed
id content status dependencies
create-date-utils 新建 date-utils.js,基于 dayjs 实现 format/parse/矩阵等纯函数 completed
add-dayjs-dep
id content status dependencies
create-calendar-panel 新建 calendar-panel.vue 共享月历与时间选择面板 completed
create-date-utils
id content status dependencies
create-date-picker 新建 date-picker.vue 单个日期时间选择器 completed
create-calendar-panel
id content status dependencies
create-range-picker 新建 range-picker.vue 范围选择器含 hover 预览 completed
create-calendar-panel
id content status dependencies
create-date-scss 新建 index.scss 沿用主题变量完成样式 completed
create-calendar-panel
create-date-picker
create-range-picker
id content status dependencies
add-demo-verify 在 App.vue 增加演示区块并运行 lint/fmt 验证 completed
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、主题切换。

架构设计

组件关系

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] 无需改动,直接复用

关键代码结构(接口约定)

// 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 直接使用。