223 lines
11 KiB
Markdown
223 lines
11 KiB
Markdown
---
|
||
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 直接使用。 |