diff --git a/devui/date-picker/components/helper.ts b/devui/date-picker/components/helper.ts
index a756d082c922766301f6912bc2d86f4843a7eb39..805d977b50cf556a030f9e51fa2e37b756de3963 100644
--- a/devui/date-picker/components/helper.ts
+++ b/devui/date-picker/components/helper.ts
@@ -1,4 +1,4 @@
-import { invokeCallback, subDateDay } from './utils'
+import { invokeCallback, subDateDay, betweenDate } from './utils'
import { TDateCell, TDatePanelDataProps, TDatePanelProps } from './types'
export const getDateKey = (date: Date): string => {
@@ -6,16 +6,13 @@ export const getDateKey = (date: Date): string => {
}
export const cellClassName = (props: TDatePanelDataProps, day: TDateCell, base = 'cell'): string => {
- if(day.current !== 0) {
- return `${base} disabled`
- }
- const { dateMin, dateMax } = props
- if(dateMin && subDateDay(day.date, dateMin) < 0) {
- return `${base} disabled`
- }
- if(dateMax && subDateDay(dateMax, day.date) < 0) {
+
+ if(!betweenDate(day.date, props.dateMin, props.dateMax)) {
return `${base} disabled`
+ } else if(day.current !== 0) {
+ return `${base} not-current`
}
+
const key = getDateKey(day.date)
if (props.type === 'range') {
if (props.dateStart) {
@@ -40,9 +37,10 @@ export const cellClassName = (props: TDatePanelDataProps, day: TDateCell, base =
}
export const trigEvent = (props: TDatePanelProps, day: TDateCell): void => {
- if(day.current !== 0) {
+ if(!betweenDate(day.date, props.dateMin, props.dateMax)) {
return
}
+
if (props.type === 'range') {
if (!props.dateStart) {
invokeCallback(props.onSelectStart, day.date)
diff --git a/devui/date-picker/components/panel/index.scss b/devui/date-picker/components/panel/index.scss
index 57ae53ec08f1f14132b3877b3b4ce868480e154b..9fb2adaea7a4cb6fec4a8d7a3e2a749d65cda733 100644
--- a/devui/date-picker/components/panel/index.scss
+++ b/devui/date-picker/components/panel/index.scss
@@ -13,7 +13,6 @@ $panel-cell-active-hover-color: #ffffff;
.devui-calendar-panel {
width: $panel-width;
- height: $panel-height;
padding: $panel-padding;
box-sizing: border-box;
overflow: hidden;
@@ -55,9 +54,14 @@ $panel-cell-active-hover-color: #ffffff;
background-color: $devui-disabled-bg;
}
- &.disabled {
+ &.disabled,
+ &.not-current {
color: $devui-disabled-text;
}
+
+ &.disabled {
+ cursor: not-allowed;
+ }
}
}
@@ -75,4 +79,25 @@ $panel-cell-active-hover-color: #ffffff;
cursor: pointer;
list-style: none;
}
+
+ .today-container {
+ padding: 8px 8px;
+ display: flex;
+ flex-direction: row;
+ justify-content: flex-end;
+
+ &.disabled {
+ .today-button {
+ border: 1px solid #cccccc;
+ cursor: not-allowed;
+ }
+ }
+
+ .today-button {
+ border: 1px solid #0066cc;
+ border-radius: 3px;
+ padding: 2px 20px;
+ font-size: 12px;
+ }
+ }
}
diff --git a/devui/date-picker/components/panel/index.tsx b/devui/date-picker/components/panel/index.tsx
index fdef8292063c881f5d933a38e360176e4f3790a1..71526888a66bbf866e3006990317a96a2020a9ed 100644
--- a/devui/date-picker/components/panel/index.tsx
+++ b/devui/date-picker/components/panel/index.tsx
@@ -1,10 +1,12 @@
import { TDatePanelProps } from '../types'
-import { getMonthWeeklyDays, WEEK_DAYS } from '../utils'
+import { getMonthWeeklyDays, WEEK_DAYS, betweenDate } from '../utils'
import { handleDateEnter, cellClassName, trigEvent } from '../helper'
import Toolbar from '../toolbar'
+import TodayDefault from '../today-default'
import './index.scss'
const CalendarDatePanel = (props: TDatePanelProps) => {
+ const today = new Date()
return (
{
row.map(day => {
return (
trigEvent(props as TDatePanelProps, day)}
- onMouseenter={() => handleDateEnter(props as TDatePanelProps, day)}
+ class={cellClassName(props, day)}
+ onClick={() => trigEvent(props, day)}
+ onMouseenter={() => handleDateEnter(props, day)}
>{day.date.getDate()}
)
})
})
}
+ {props.type !== 'range' ? (
+ {
+ typeof props.onToday === 'function' && props.onToday(today, 0)
+ }}
+ />
+ ) : null}
)
}
diff --git a/devui/date-picker/components/popup/index.scss b/devui/date-picker/components/popup/index.scss
new file mode 100644
index 0000000000000000000000000000000000000000..d9208c50646644ddf80b52eef45fa9df09284277
--- /dev/null
+++ b/devui/date-picker/components/popup/index.scss
@@ -0,0 +1,14 @@
+.devui-datepicker-popup {
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 0;
+ height: 0;
+ background-color: rgba(0, 0, 0, 0.2);
+ z-index: 99;
+ overflow: visible;
+
+ .popup-tracing {
+ position: absolute;
+ }
+}
diff --git a/devui/date-picker/components/popup/index.tsx b/devui/date-picker/components/popup/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f241927b34a43764e07ffe34803e639e634c4857
--- /dev/null
+++ b/devui/date-picker/components/popup/index.tsx
@@ -0,0 +1,109 @@
+import { defineComponent, reactive, renderSlot, ref, useSlots, onMounted, onUnmounted, onBeforeUpdate } from 'vue'
+import { EventManager, isIn, traceNode, invokeFunction } from '../../utils'
+import {
+ handlePositionFactory,
+ formatValue,
+ formatPlaceholder,
+ getAttachInputDom,
+} from '../../helper'
+
+import './index.scss'
+
+type TState = {
+ x?: string
+ y?: string
+ attachInputDom?: string
+ show: boolean
+ st: boolean
+}
+
+export default defineComponent({
+ name: 'DDatePickerPopup',
+ props: {
+ attach: { type: String },
+ onBinding: { type: Function },
+ onClosed: { type: Function },
+ onOpen: { type: Function },
+ show: { type: Boolean },
+ },
+ setup(props) {
+
+ const container = ref()
+ const evtman = new EventManager()
+ const state = reactive({
+ x: '0',
+ y: '0',
+ st: true,
+ show: !!props.show,
+ })
+
+ let el: Element | null = null
+
+ // 弹出层跟踪
+ const handlePosition = handlePositionFactory(state, props, container)
+
+ onBeforeUpdate(() => {
+ state.show = !!props.show
+ })
+
+ onMounted(() => {
+ // 获取绑定节点(默认input)
+ el = getAttachInputDom(props)
+ // 绑定节点不存在,作为普通组件展示。
+ if (!el) {
+ state.st = true
+ state.show = true
+ return
+ } else {
+ state.show = false
+ state.st = false
+ }
+
+ invokeFunction(props.onBinding)
+
+ // 绑定节点click事件处理弹出层显示
+ evtman.append(el, 'click', () => {
+ if(!state.show) {
+ state.show = true
+ invokeFunction(props.onOpen)
+ }
+ })
+ // document层处理`点击其他区域隐藏`
+ evtman.append(document, 'click', (e: MouseEvent) => {
+ if (!state.show || e.target === el || isIn(e.target as Node, container.value)) {
+ return
+ }
+ state.show = false
+ invokeFunction(props.onClosed)
+ // reset()
+ })
+ // 对绑定节点做scroll跟踪,并绑定跟踪事件
+ traceNode(el).forEach(node => {
+ evtman.append(node, 'scroll', handlePosition)
+ })
+ })
+
+ onUnmounted(() => {
+ evtman.dispose()
+ })
+
+ return () => {
+ const defaultSlot = renderSlot(useSlots(), 'default')
+ if (state.st) {
+ return defaultSlot
+ }
+ handlePosition()
+ return (
+
+ )
+ }
+ }
+})
\ No newline at end of file
diff --git a/devui/date-picker/components/today-default/index.tsx b/devui/date-picker/components/today-default/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9ed09cbf7bfe60ed4fd7f2fbf9ae7a53afdeefb4
--- /dev/null
+++ b/devui/date-picker/components/today-default/index.tsx
@@ -0,0 +1,19 @@
+type TProps = {
+ onSelected?: (date: Date) => void
+ disabled?: boolean
+}
+
+const TodayDefault = (props: TProps) => {
+ const { onSelected = () => 0, disabled = false } = props
+ return (
+
+
+
+ )
+}
+
+export default TodayDefault
\ No newline at end of file
diff --git a/devui/date-picker/components/types.ts b/devui/date-picker/components/types.ts
index 66db15209623c554c4ba609bdfb04080a869fd4e..d11595ee1943cd19a29f241a552e009653e6a3e5 100644
--- a/devui/date-picker/components/types.ts
+++ b/devui/date-picker/components/types.ts
@@ -38,12 +38,13 @@ export type TDatePanelEventProps = TDateToolbarEventProps & {
onSelectStart?: TEventCallback
onSelectEnd?: TEventCallback
onSelecting?: TEventCallback
+ onToday?: TEventCallback
onChange?: (type: TDatePanelType, config: TDateSelectingBase) => void
}
export type TDatePanelDataProps = TDateToolbarDataProps & TDateSelectingBase
-export type TDatePanelProps = TDatePanelDataProps & TDatePanelEventProps
+export type TDatePanelProps = { showToday?: boolean; } & TDatePanelDataProps & TDatePanelEventProps
export type TProps = ({
diff --git a/devui/date-picker/components/utils.ts b/devui/date-picker/components/utils.ts
index cb2c02a57e3d9f1f25dc11efe0b2c125adddc389..23150961ab6f902b807f804df1a5bbd91147257b 100644
--- a/devui/date-picker/components/utils.ts
+++ b/devui/date-picker/components/utils.ts
@@ -146,4 +146,41 @@ const _parseInt = (str: any, dftVal?: number) => {
export const parseTime = (str?: string) : [number, number, number, number] => {
const [h, m, s, ms] = str.split(/[\:\.]+/)
return [_parseInt(h, 0), _parseInt(m, 0), _parseInt(s, 0), _parseInt(ms, 0)]
+}
+
+type TDateCounterType = 'd' | 'm' | 'y'
+
+export const compareDateSort = (d1: Date, d2: Date, type: TDateCounterType = 'd') => {
+ const t1 = dateCounter(d1, type), t2 = dateCounter(d2, type)
+ return t1 < t2 ? -1 : t1 > t2 ? 1 : 0
+}
+
+export const dateCounter = (date: Date, type: TDateCounterType) => {
+ switch(type) {
+ case 'y': return date.getFullYear()
+ case 'm': return date.getFullYear() * 12 + date.getMonth()
+ }
+ return date.getTime() / ONE_DAY >> 0
+}
+export const borderDateFactory = (factor: (d1: Date, d2: Date) => Date) => (...ds: Date[]) => {
+ return ds.length < 2 ? ds[0] || new Date() : ds.reduce((r, v) => factor(r, v))
+}
+export const getMinDate = borderDateFactory((d1: Date, d2: Date) => compareDateSort(d1, d2) < 0 ? d1 : d2)
+export const getMaxDate = borderDateFactory((d1: Date, d2: Date) => compareDateSort(d1, d2) < 0 ? d2 : d1)
+
+/**
+ * d 是否在 [left, right] 区间
+ * @param date 日期
+ * @param left 最小日期
+ * @param right 最大日期
+ * @returns boolean
+ */
+export const betweenDate = (date: Date, left: any, right: any): boolean => {
+ if(left instanceof Date && compareDateSort(left, date, 'd') > 0) {
+ return false
+ }
+ if(right instanceof Date && compareDateSort(date, right, 'd') > 0) {
+ return false
+ }
+ return true
}
\ No newline at end of file
diff --git a/devui/date-picker/date-picker.scss b/devui/date-picker/date-picker.scss
index 26d48111a251df99f3aacb48adcc067654f0aa39..a577a9938d4883ed621e14f1069fc568d541c82a 100644
--- a/devui/date-picker/date-picker.scss
+++ b/devui/date-picker/date-picker.scss
@@ -5,17 +5,6 @@ $border-width: 1px;
$border-style: solid;
$border-color: #dddddd;
-.devui-datepicker-global-viewport {
- position: fixed;
- left: 0;
- top: 0;
- width: 0;
- height: 0;
- background-color: rgba(0, 0, 0, 0.2);
- z-index: 99;
- overflow: visible;
-}
-
.devui-datepicker-container {
margin: 0;
padding: 0;
diff --git a/devui/date-picker/date-picker.tsx b/devui/date-picker/date-picker.tsx
index c8285aca5db6294a46a03b07d711d75162b0eaab..e6a66364e0c9a46751576a684459aaab5195531d 100644
--- a/devui/date-picker/date-picker.tsx
+++ b/devui/date-picker/date-picker.tsx
@@ -1,12 +1,11 @@
-import { defineComponent, reactive, onMounted, onUnmounted, ref } from 'vue'
-import {
- EventManager, isIn,
- traceNode, invokeFunction,
-} from './utils'
+import type { UnwrapRef } from 'vue'
+import { defineComponent, reactive, onMounted } from 'vue'
+import { invokeFunction } from './utils'
+import { compareDateSort } from './components/utils'
+import Popup from './components/popup'
import {
TState,
- handlePositionFactory,
handleCalendarSwitchState,
formatValue,
formatPlaceholder,
@@ -18,6 +17,36 @@ import Calendar from './components/calendar'
import './date-picker.scss'
import { parseDate } from './components/utils'
+const formatProps = (props: any): TState => {
+ const state: TState = {
+ range: !!props.range,
+ show: false,
+ input: props.attachInputDom,
+ }
+ state.current = parseDate(props.dateMin) || new Date()
+ state.next = new Date(state.current.getFullYear(), state.current.getMonth() + 1, 1)
+ return state
+}
+
+const formatRange = (state: UnwrapRef) => {
+ const [start, end] = [state.start, state.end].sort((a, b) => a.getTime() - b.getTime())
+
+ state.start = start
+ state.end = end
+
+ if (compareDateSort(start, end, 'm') !== 0) {
+ state.current = start
+ state.next = end
+ } else {
+ if (compareDateSort(start, state.current) < 0) {
+ state.current = start
+ }
+ if (compareDateSort(state.next, end) < 0) {
+ state.next = end
+ }
+ }
+}
+
export default defineComponent({
name: 'DDatepicker',
props: {
@@ -30,29 +59,14 @@ export default defineComponent({
attachInputDom: { type: String },
dateMin: { type: String },
dateMax: { type: String },
+ showToday: { type: Boolean, default: false },
},
setup(props, ctx) {
- const container = ref()
- const evtman = new EventManager()
- const current = new Date()
-
- const state = reactive({
- range: !!props.range,
- current,
- next: new Date(current.getFullYear(), current.getMonth() + 1, 1),
- show: false,
- input: props.attachInputDom,
- st: true,
- x: '0',
- y: '0',
- })
-
- // 弹出层跟踪
- const handlePosition = handlePositionFactory(state, props, container)
+ const state = reactive(formatProps(props))
// 绑定层显示值、placehoder值设置
- const setBindingDom = (el: any = getAttachInputDom(state, props)) => {
+ const setBindingDom = (el: any = getAttachInputDom(props)) => {
const value = formatValue(state, props)
const placeholder = formatPlaceholder(props)
@@ -69,66 +83,21 @@ export default defineComponent({
return value
}
- const reset = () => {
- state.hover = null
- state.current = null
- state.next = null
- if (state.start) {
- if (state.end && Math.abs(state.end.getMonth() - state.start.getMonth()) > 0) {
- state.next = state.end
- }
- } else {
- state.end = null
- }
- }
-
onMounted(() => {
- // 获取绑定节点(默认input)
- const el = getAttachInputDom(state, props)
- // 绑定节点不存在,作为普通组件展示。
- if (!el) {
- // 显示组件
- state.show = true
- return
- } else {
- // 作为弹出层,先隐藏
- state.show = false
- }
-
- setBindingDom(el)
-
- // 绑定节点click事件处理弹出层显示
- evtman.append(el, 'click', () => !state.show && (state.show = true))
- // document层处理`点击其他区域隐藏`
- evtman.append(document, 'click', (e: MouseEvent) => {
- if (!state.show || e.target === el || isIn(e.target as Node, container.value)) {
- return
- }
- state.show = false
- reset()
- })
- // 对绑定节点做scroll跟踪,并绑定跟踪事件
- traceNode(el).forEach(node => {
- evtman.append(node, 'scroll', handlePosition)
- })
- })
-
- onUnmounted(() => {
- evtman.dispose()
+ setBindingDom()
})
return () => {
- handlePosition()
- setBindingDom()
return (
-
-
+
state.show = true}
+ onClosed={() => {
+ state.show = false
+ }}
+ >
+
{
- state.current = state.end = state.hover = undefined
+ state.end = state.hover = undefined
state.start = date
}}
onChange={() => {
@@ -150,9 +119,26 @@ export default defineComponent({
state.show = false
}
}}
- onSelected={(date: Date) => state.start = date}
+ onToday={(date: Date) => {
+ state.current = date
+ state.start = date
+ const output = setBindingDom()
+ invokeFunction(props.selectedDateChange, output)
+ if (props.autoClose) {
+ state.show = false
+ }
+ }}
+ onSelected={(date: Date) => {
+ state.start = date
+ if (compareDateSort(state.current, date) !== 0) {
+ state.current = date
+ }
+ }}
onSelectStart={(date: Date) => state.start = date}
- onSelectEnd={(date: Date) => state.end = date}
+ onSelectEnd={(date: Date) => {
+ state.end = date
+ formatRange(state)
+ }}
onSelecting={(date: Date) => state.hover = date}
onPreviousYear={(date: Date, pos: number) => handleCalendarSwitchState(state, 0, pos, date)}
onPreviousMonth={(date: Date, pos: number) => handleCalendarSwitchState(state, 1, pos, date)}
@@ -160,7 +146,7 @@ export default defineComponent({
onNextYear={(date: Date, pos: number) => handleCalendarSwitchState(state, 3, pos, date)}
/>
-
+
)
}
}
diff --git a/devui/date-picker/helper.ts b/devui/date-picker/helper.ts
index 0bc57373451e73cf5ea77621be18a878322136fb..5b002481c495052c48c87553c82ff24dc9ac173d 100644
--- a/devui/date-picker/helper.ts
+++ b/devui/date-picker/helper.ts
@@ -10,9 +10,6 @@ export type TState = {
hover?: Date
show?: boolean
input?: string
- st?: boolean
- x?: string
- y?: string
}
/**
@@ -109,8 +106,8 @@ export const handleValue = (id: string | undefined, output: string) => {
* 获取绑定节点
* @returns
*/
-export const getAttachInputDom = (state: TState, props: any) => {
- const { attachInputDom } = props || {}
+export const getAttachInputDom = (props: any) => {
+ const { attach, attachInputDom = attach } = props || {}
if (!attachInputDom || typeof attachInputDom !== 'string') {
return null
}
@@ -118,7 +115,6 @@ export const getAttachInputDom = (state: TState, props: any) => {
if (!el) {
return null
}
- state.st = false
return el
}
@@ -129,14 +125,21 @@ export const getAttachInputDom = (state: TState, props: any) => {
* @param container
* @returns
*/
-export const handlePositionFactory = (state: TState, props: any, container: Ref
) => () => {
+export const handlePositionFactory = (state: {
+ x?: string
+ y?: string
+ attachInputDom?: string
+ show?: boolean
+ st?: boolean
+}, props: any, container: Ref) => () => {
if (!state.show) {
state.x = `-100%`
state.y = `-100%`
return
}
- const el = getAttachInputDom(state, props)
+ const el = getAttachInputDom(props)
if (!el) {
+ state.st = true
return
}
const { left, top, width, height } = el.getBoundingClientRect()
diff --git a/sites/components/date-picker/index.md b/sites/components/date-picker/index.md
index 84dd4a5aac49d5a16c261cb83e9df8c47b620391..3501f359ed24551606dca473712204ac6e0b6252 100644
--- a/sites/components/date-picker/index.md
+++ b/sites/components/date-picker/index.md
@@ -97,7 +97,7 @@ export default defineComponent({
### 区间限制
```jsx
@@ -118,21 +118,41 @@ export default defineComponent({
暂定通过`querySelector`查找节点,绑定真实`dom`节点。此方案待定。
```jsx
+
+
+```
+
+
+
+```jsx
+
+
```