From 2123e5341334cfb5e0e9d365df6e94b8ca78b930 Mon Sep 17 00:00:00 2001 From: Dreamer <154239735@qq.com> Date: Tue, 5 Oct 2021 23:39:52 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0timePicker?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../time-picker/__tests__/time-picker.spec.ts | 8 + devui/time-picker/index.ts | 17 ++ .../popup-line/composables/use-popue-line.ts | 260 ++++++++++++++++++ .../src/components/popup-line/index.scss | 70 +++++ .../src/components/popup-line/index.tsx | 130 +++++++++ .../src/components/time-popup/index.scss | 54 ++++ .../src/components/time-popup/index.tsx | 113 ++++++++ .../time-picker/src/composables/use-popup.ts | 148 ++++++++++ devui/time-picker/src/time-picker-types.ts | 40 +++ devui/time-picker/src/time-picker.scss | 51 ++++ devui/time-picker/src/time-picker.tsx | 119 ++++++++ devui/time-picker/src/types.ts | 14 + devui/time-picker/src/utils.ts | 95 +++++++ docs/components/time-picker/index.md | 226 +++++++++++++++ 14 files changed, 1345 insertions(+) create mode 100644 devui/time-picker/__tests__/time-picker.spec.ts create mode 100644 devui/time-picker/index.ts create mode 100644 devui/time-picker/src/components/popup-line/composables/use-popue-line.ts create mode 100644 devui/time-picker/src/components/popup-line/index.scss create mode 100644 devui/time-picker/src/components/popup-line/index.tsx create mode 100644 devui/time-picker/src/components/time-popup/index.scss create mode 100644 devui/time-picker/src/components/time-popup/index.tsx create mode 100644 devui/time-picker/src/composables/use-popup.ts create mode 100644 devui/time-picker/src/time-picker-types.ts create mode 100644 devui/time-picker/src/time-picker.scss create mode 100644 devui/time-picker/src/time-picker.tsx create mode 100644 devui/time-picker/src/types.ts create mode 100644 devui/time-picker/src/utils.ts create mode 100644 docs/components/time-picker/index.md diff --git a/devui/time-picker/__tests__/time-picker.spec.ts b/devui/time-picker/__tests__/time-picker.spec.ts new file mode 100644 index 00000000..26f60722 --- /dev/null +++ b/devui/time-picker/__tests__/time-picker.spec.ts @@ -0,0 +1,8 @@ +import { mount } from '@vue/test-utils'; +import { TimePicker } from '../index'; + +describe('time-picker test', () => { + it('time-picker init render', async () => { + // todo + }) +}) diff --git a/devui/time-picker/index.ts b/devui/time-picker/index.ts new file mode 100644 index 00000000..6383339b --- /dev/null +++ b/devui/time-picker/index.ts @@ -0,0 +1,17 @@ +import type { App } from 'vue' +import TimePicker from './src/time-picker' + +TimePicker.install = function(app: App): void { + app.component(TimePicker.name, TimePicker) +} + +export { TimePicker } + +export default { + title: 'TimePicker 时间选择器', + category: '数据录入', + status: undefined, // TODO: 组件若开发完成则填入"已完成",并删除该注释 + install(app: App): void { + app.use(TimePicker as any) + } +} diff --git a/devui/time-picker/src/components/popup-line/composables/use-popue-line.ts b/devui/time-picker/src/components/popup-line/composables/use-popue-line.ts new file mode 100644 index 00000000..a25908cd --- /dev/null +++ b/devui/time-picker/src/components/popup-line/composables/use-popue-line.ts @@ -0,0 +1,260 @@ +import { Ref, ref } from 'vue' +import { ArrType } from '../../../types' + +const usePopupLine =( + hourListRef:Array,minuteListRef:Array,secondListRef:Array, + minTime:string,maxTime:string,format:string,timeListDom:Ref, + ):any =>{ + + const activeTime = ref('00:00:00') + const activeHour = ref('00') + const activeMinute = ref('00') + const activeSecond = ref('00') + + const activeTimeFun = (e:any, item:ArrType, index:number)=>{ + if(item.isDisabled){ + return false + }else{ + setTimeActive(item,index) + e.target.parentElement.scrollTop = index * 30 + } + } + + + const setTimeActive = (item:ArrType,index:number)=>{ + + let activeTimeList = [] + let acitveTimeValue = ref('') + if(item.flag == 'hour'){ + activeTimeList = hourListRef + acitveTimeValue = activeHour + getItemAstrict(item) + }else if(item.flag == 'minute'){ + activeTimeList = minuteListRef + acitveTimeValue = activeMinute + getItemAstrict(item) + }else if(item.flag == 'second'){ + activeTimeList = secondListRef + acitveTimeValue = activeSecond + } + + activeTimeList.map((tiemItem,tiemeIndex)=>{ + tiemItem.isActive = false + }) + activeTimeList[index].isActive = true + acitveTimeValue.value = activeTimeList[index].time + + + activeTime.value = `${activeHour.value}:${activeMinute.value}:${activeSecond.value}` + + if(activeTime.value < minTime){ + activeTime.value = minTime + resetTimeValue(minTime) + }else if(format == 'mm:ss' && `${activeMinute.value}:${activeSecond.value}` > maxTime.slice(3)){ + const newMinTime = minTime.slice(0,3) + maxTime.slice(3) + resetTimeValue(newMinTime) + }else if(activeTime.value > maxTime){ + activeTime.value = maxTime + resetTimeValue(maxTime) + } + } + + // 获取最大值 最小值 + const getItemAstrict = (item:ArrType):void=>{ + let min ='00' + let max ='00' + + const minTimeHour = minTime.split(':')[0] + const minTimeMinute = minTime.split(':')[1] + const minTimeSecond = minTime.split(':')[2] + + const maxTimeHour = maxTime.split(':')[0] + const maxTimeMinute = maxTime.split(':')[1] + const maxTimeSecond = maxTime.split(':')[2] + + if(item.flag == 'hour'){ + if(item.time == minTimeHour){ + min = minTimeMinute + // console.log('11') + setItemAstrict(minuteListRef,min,max) + activeMinute.value < minTimeMinute && setItemAstrict(secondListRef,minTimeSecond,max) + }else if ( item.time == maxTimeHour){ + max = maxTimeMinute + // console.log('22') + setItemAstrict(minuteListRef,min,max) + setItemAstrict(secondListRef,min,maxTimeSecond) + }else{ + // console.log('33') + setItemAstrict(minuteListRef,min,max) + setItemAstrict(secondListRef,min,max) + } + } + if(item.flag == 'minute' && format == 'mm:ss'){ + if(item.time == minTimeMinute){ + min = minTimeSecond + setItemAstrict(secondListRef,min,max) + }else if(item.time == maxTimeMinute){ + max = maxTimeSecond + setItemAstrict(secondListRef,min,max) + } else{ + setItemAstrict(secondListRef,min,max) + } + }else if(item.flag == 'minute'){ + if(activeHour.value == minTimeHour && item.time == minTimeMinute){ + min = minTimeSecond + setItemAstrict(secondListRef,min,max) + // console.log('44') + }else if(activeHour.value == maxTimeHour && item.time == maxTimeMinute){ + max = maxTimeSecond + // console.log('55') + setItemAstrict(secondListRef,min,max) + } else{ + // console.log('66') + setItemAstrict(secondListRef,min,max) + } + } + + } + // 设置最大值 最小值 + const setItemAstrict = (timeArr:Array,min:string,max:string) =>{ + timeArr.map(itme=>{ + if(min !='00' && itme.time < min){ + itme.isDisabled = true + }else if(max !='00' && itme.time > max){ + itme.isDisabled = true + }else{ + itme.isDisabled = false + } + }) + } + + // 指定时间 + const resetTimeValue = (time:string)=>{ + const timeValueArr = time.split(':') + const minTiveArr = minTime.split(':') + + let hh = 0 + let mm = 0 + let ss = 0 + + if(format == 'hh:mm:ss'){ + + hh = parseInt(timeValueArr[0]) + mm = parseInt(timeValueArr[1]) + ss = parseInt(timeValueArr[2]) + + timeListDom.value.children[0].scrollTop = hh * 30 + timeListDom.value.children[1].scrollTop = mm * 30 + timeListDom.value.children[2].scrollTop = ss * 30 + + activeHour.value = timeValueArr[0] + activeMinute.value = timeValueArr[1] + activeSecond.value = timeValueArr[2] + + resetTimeActive(hourListRef,timeValueArr[0]) + resetTimeActive(minuteListRef,timeValueArr[1]) + resetTimeActive(secondListRef,timeValueArr[2]) + + resetTimeAstrict(hourListRef,activeHour.value) + resetTimeAstrict(minuteListRef,activeMinute.value) + + } + else if(format == 'mm:hh:ss'){ + hh = parseInt(timeValueArr[0]) + mm = parseInt(timeValueArr[1]) + ss = parseInt(timeValueArr[2]) + + timeListDom.value.children[0].scrollTop = mm * 30 + timeListDom.value.children[1].scrollTop = hh * 30 + timeListDom.value.children[2].scrollTop = ss * 30 + + activeHour.value = timeValueArr[0] + activeMinute.value = timeValueArr[1] + activeSecond.value = timeValueArr[2] + + resetTimeActive(hourListRef,timeValueArr[0]) + resetTimeActive(minuteListRef,timeValueArr[1]) + resetTimeActive(secondListRef,timeValueArr[2]) + + resetTimeAstrict(hourListRef,activeHour.value) + resetTimeAstrict(minuteListRef,activeMinute.value) + + }else if(format == 'hh:mm'){ + + hh = parseInt(timeValueArr[0]) + mm = parseInt(timeValueArr[1]) + + timeListDom.value.children[0].scrollTop = hh * 30 + timeListDom.value.children[1].scrollTop = mm * 30 + + activeHour.value = timeValueArr[0] + activeMinute.value = timeValueArr[1] + + resetTimeActive(hourListRef,timeValueArr[0]) + resetTimeActive(minuteListRef,timeValueArr[1]) + + resetTimeAstrict(hourListRef,activeHour.value) + + }else if(format == 'mm:ss'){ + + mm = parseInt(timeValueArr[1]) + ss = parseInt(timeValueArr[2]) + + timeListDom.value.children[0].scrollTop = mm * 30 + timeListDom.value.children[1].scrollTop = ss * 30 + + activeHour.value = minTiveArr[0] + activeMinute.value = timeValueArr[1] + activeSecond.value = timeValueArr[2] + + resetTimeActive(minuteListRef,timeValueArr[1]) + resetTimeActive(secondListRef,timeValueArr[2]) + + resetTimeAstrict(minuteListRef,activeMinute.value) + } + // activeTime.value = `${activeHour.value}:${activeMinute.value}:${activeSecond.value}` + + } + + // 解决清空之后,再次打开 最大值最小值限制范围失效 + const resetTimeAstrict = (timeArr:Array,time:string) =>{ + timeArr.map(item=>{ + if(item.time == time){ + getItemAstrict(item) + } + }) + } + + // 指定选中 + const resetTimeActive = (timeArr:Array,itemValue:string) =>{ + timeArr.map( item =>{ + if(item.time == itemValue){ + item.isActive = true + }else{ + item.isActive = false + } + }) + } + + // 暂时返回选中 时 分 秒 + const getNewTime = ()=>{ + return { activeTime,activeHour,activeMinute,activeSecond } + } + + return{ + activeTime, + activeHour, + activeMinute, + activeSecond, + activeTimeFun, + resetTimeValue, + getNewTime + } +} + +export { + usePopupLine +} + + + diff --git a/devui/time-picker/src/components/popup-line/index.scss b/devui/time-picker/src/components/popup-line/index.scss new file mode 100644 index 00000000..b39d6ad2 --- /dev/null +++ b/devui/time-picker/src/components/popup-line/index.scss @@ -0,0 +1,70 @@ +@import '../../../../style/theme/color'; +@import '../../../../style/theme/shadow'; +@import '../../../../style/theme/corner'; +@import '../../../../style/core/_font'; + +.time-list { + width: 100%; + height: 250px; + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + border-top: 1px solid rgba(0, 0, 0, 0.2); + + .time-ul { + height: 244px; + display: inline-block; + overflow: hidden; + cursor: pointer; + padding: 0; + margin: 0 auto; + padding-right: 8px; + border-right: 1px solid rgba(0, 0, 0, 0.2); + scroll-behavior: smooth; + padding-top: 2px; + border-top: 1px solid rgba(0, 0, 0, 0); + + &:hover { + overflow-y: overlay; + // overflow-y:auto ; + // padding-right: 0; + } + + &:last-child { + border-right: none; + } + + li { + padding: 0; + // text-align: center; + // padding-left: 40px; + text-align: center; + height: 30px; + line-height: 30px; + width: calc(100% + 8px); + + &:last-child { + margin-bottom: 214px; + } + + &:hover { + background-color: #5d7ef80c; + color: #8aa2f0; + } + } + + .active-li { + background-color: #5e7ce0; + color: #ffffff; + + &:hover { + background-color: #5e7ce0; + color: #ffffff; + } + } + + .disabled-li { + background-color: $devui-disabled-bg; + color: $devui-disabled-text; + cursor: not-allowed; + } + } +} diff --git a/devui/time-picker/src/components/popup-line/index.tsx b/devui/time-picker/src/components/popup-line/index.tsx new file mode 100644 index 00000000..f3561449 --- /dev/null +++ b/devui/time-picker/src/components/popup-line/index.tsx @@ -0,0 +1,130 @@ +import { ref, defineComponent } from 'vue' +import { usePopupLine } from './composables/use-popue-line' +import { ArrType } from '../../types' +import './index.scss' + + +export default defineComponent({ + name:'TTimeList', + props:{ + hourList:{ + type:Array, + default:()=>[] + }, + minuteList:{ + type:Array, + default:()=>[] + }, + secondList:{ + type:Array, + default:()=>[] + }, + format:{ + type:String, + default:'hh:mm:ss' + }, + minTime:{ + type:String, + default:'00:00:00' + }, + maxTime:{ + type:String, + default:'23:59:59' + } + }, + setup(props,ctx,){ + const timeListDom = ref() + const { + getNewTime, + activeTimeFun, + resetTimeValue + } = usePopupLine( + props.hourList as Array, + props.minuteList as Array, + props.secondList as Array, + props.minTime, + props.maxTime, + props.format, + timeListDom, + ) + + const restScrooTop = ()=>{ + for (let i = 0; i < timeListDom.value.children.length; i++) { + timeListDom.value.children[i].scrollTop = 0 + } + } + + const setOutoTime = (time:string)=>{ + resetTimeValue(time) + } + + + + const TimeLi = (timeArr:Array):any=>{ + return ( + timeArr.map((item: ArrType, index: number) => { + return ( +
  • { activeTimeFun(e, item, index,) }} + >{item.time} +
  • + ) + }) + ) + } + + const TimeUl = (timeList:Array)=>{ + return ( +
      6?'33.33%':'50%'}}> {TimeLi(timeList)}
    + ) + } + + const formatTimeUl = ()=>{ + const timeList = { + 'hh':props.hourList, + 'mm':props.minuteList, + 'ss':props.secondList + } + + const timeFormatArr = (props.format as string).split(':') + + return( + timeFormatArr.map((timeItme)=>{ + return( + TimeUl(timeList[timeItme]) + ) + }) + ) + } + + //TODO: 区分浏览器内核,解决firefox中鼠标离开元素不继续滚动的情况 + const selectTimeFun = (e: MouseEvent) => { + e.stopPropagation() + console.log(e); + + // e.stopPropagation() + // const ua = navigator.userAgent + // if (ua.indexOf('Firefox') > -1) { + // resetTimeValue(activeTime) + // } + } + + ctx.expose({ + restScrooTop,setOutoTime,getNewTime + }) + + return ()=>{ + return ( +
    + { + formatTimeUl() + } +
    + ) + } + } +}) + + + \ No newline at end of file diff --git a/devui/time-picker/src/components/time-popup/index.scss b/devui/time-picker/src/components/time-popup/index.scss new file mode 100644 index 00000000..e8e2b6fc --- /dev/null +++ b/devui/time-picker/src/components/time-popup/index.scss @@ -0,0 +1,54 @@ +@import '../../../../style/theme/color'; +@import '../../../../style/theme/shadow'; +@import '../../../../style/theme/corner'; +@import '../../../../style/core/_font'; + +.time-popup { + height: 310px; + position: fixed; + z-index: -1; + background-color: #ffffff; + box-shadow: $devui-shadow-length-connected-overlay; + visibility: hidden; + opacity: 0; + transition: 0.4s; + -webkit-transition: 0.4s; + transition-property: visibility, opacity, z-index; +} + +.show-popup { + visibility: visible; + z-index: 1052; + opacity: 1; + transition-property: visibility, opacity, z-index; +} + +.popup-btn { + height: 60px; + padding: 0 10px; + display: flex; + justify-content: space-between; + align-items: center; + + .ok-btn { + width: 80px; + height: 26px; + line-height: 26px; + text-align: center; + background-color: #5e7ce0; + font-size: 14px; + color: #ffffff; + border-radius: 2px; + cursor: pointer; + } + + .slot-time { + font-size: $devui-font-size-card-title; + cursor: pointer; + } + + .slot-time:hover { + color: $devui-primary-hover; + text-decoration: underline; + } +} diff --git a/devui/time-picker/src/components/time-popup/index.tsx b/devui/time-picker/src/components/time-popup/index.tsx new file mode 100644 index 00000000..dbf8e7db --- /dev/null +++ b/devui/time-picker/src/components/time-popup/index.tsx @@ -0,0 +1,113 @@ +import { defineComponent, reactive, ref,watch ,onMounted} from 'vue'; +import { initializeTimeData,setTimeAstrict } from '../../utils' +import TimeList from '../popup-line/index' +import { Button } from '../../../../button/index'; + +import './index.scss' +export default defineComponent({ + name:'TTimePopup', + components:{ + TimeList,Button + }, + props:{ + showPopup:{ + type:Boolean, + default:false + }, + popupTop:{ + type:Number, + default:-100 + }, + popupLeft:{ + type:Number, + default:-100 + }, + popupWidth:{ + type:Number, + default:300 + }, + popupFormat:{ + type:String, + default:'hh:mm:ss' + }, + minTime:{ + type:String, + default:'00:00:00' + }, + maxTime:{ + type:String, + default:'23:59:59' + }, + bindData:{ + type:String, + default:'00:00:00' + } + }, + emits:['subData'], + setup(props,ctx){ + const popupDome = ref() + const timeListDom = ref() + const hourList = initializeTimeData('hour') + const minuteList = initializeTimeData('minute') + const secondList = initializeTimeData('second') + + onMounted(()=>{ + setTimeAstrict(hourList,minuteList,secondList,props.minTime,props.maxTime,props.popupFormat) + }) + + watch(()=>[props.showPopup,props.bindData],([showPopup,newTimeVal],[oldShowPopup,oldTimeVal])=>{ + + if(showPopup || newTimeVal != oldTimeVal){ + timeListDom.value.setOutoTime(newTimeVal) + }else{ + timeListDom.value.restScrooTop() + } + }) + + const changTimeData = ()=>{ + return timeListDom.value.getNewTime() + } + + const subDataFun = (e:MouseEvent)=>{ + e.stopPropagation() + ctx.emit('subData') + } + + ctx.expose({ + changTimeData + }) + + return()=>{ + return( + <> +
    + + + +
    + + ) + } + } +}) + diff --git a/devui/time-picker/src/composables/use-popup.ts b/devui/time-picker/src/composables/use-popup.ts new file mode 100644 index 00000000..74e7742e --- /dev/null +++ b/devui/time-picker/src/composables/use-popup.ts @@ -0,0 +1,148 @@ +import { Ref, ref } from 'vue' +import { TimeObj } from '../types' +import { getPositionFun } from '../utils' + +export default function usePicker( + hh:Ref,mm:Ref,ss:Ref,minTime:string,format:string, + autoOpen:boolean,disabled:boolean,value:string + ):any{ + const isActive = ref(false) + const showPopup = ref(false) + const devuiTimePicker = ref() + const inputDom = ref() + const left = ref(-100) + const top = ref(-100) + const timePopupDom = ref() + const timePickerValue = ref('') + const showClearIcon = ref(false) + const firsthandActiveTime = ref(`${hh.value}:${mm.value}:${ss.value}`) + const vModeValue = ref(value) + + const getPopupPosition = ()=>{ + getPositionFun(devuiTimePicker.value,left,top) + } + + const clickVerifyFun = (e: any) => { + e.stopPropagation() + isActive.value = false + showPopup.value = false + + if(disabled) return + + const path = (e.composedPath && e.composedPath()) || e.path + path.map( item => { + if (item == devuiTimePicker.value) { + if(firsthandActiveTime.value == '00:00:00'){ + vModeValue.value == '' + ? vModeValue.value = '00:00:00' + : '' + + vModeValue.value > minTime + ? firsthandActiveTime.value = vModeValue.value + : firsthandActiveTime.value = minTime + } + setInputValue() + isActive.value = true + showPopup.value = true + } + }) + } + + const getTimeValue = (e:MouseEvent)=>{ + e.stopPropagation() + if(showPopup.value){ + hh.value = timePopupDom.value.changTimeData().activeHour.value + mm.value = timePopupDom.value.changTimeData().activeMinute.value + ss.value = timePopupDom.value.changTimeData().activeSecond.value + firsthandActiveTime.value = `${hh.value}:${mm.value}:${ss.value}` + setInputValue() + } + } + + const setInputValue = ()=> { + if(format == 'hh:mm:ss'){ + vModeValue.value = `${hh.value}:${mm.value}:${ss.value}` + }else if(format == 'mm:hh:ss'){ + vModeValue.value = `${mm.value}:${hh.value}:${ss.value}` + }else if(format == 'hh:mm'){ + vModeValue.value = `${hh.value}:${mm.value}` + }else if(format == 'mm:ss'){ + vModeValue.value = `${mm.value}:${ss.value}` + } + } + + const clearAll = (e:MouseEvent)=>{ + e.stopPropagation() + showPopup.value = false + + if(minTime != '00:00:00'){ + const minTimeArr = minTime.split(':') + hh.value = minTimeArr[0] + mm.value = minTimeArr[1] + ss.value = minTimeArr[2] + }else{ + hh.value = '00' + mm.value = '00' + ss.value = '00' + } + firsthandActiveTime.value = `${hh.value}:${mm.value}:${ss.value}` + setInputValue() + } + + const isOutOpen =()=>{ + if(autoOpen){ + + const timeArr = vModeValue.value.split(':') + hh.value = timeArr[0] + mm.value = timeArr[1] + ss.value = timeArr[2] + + firsthandActiveTime.value = vModeValue.value + setInputValue() + isActive.value = true + showPopup.value = autoOpen + } + } + + // slot -- 选择时间 + const chooseTime = (slotTime:TimeObj) => { + if (slotTime.type) { + if (slotTime.type.toLowerCase() == 'hh') { + hh.value = slotTime.time + } else if (slotTime.type.toLowerCase() == 'mm') { + mm.value = slotTime.time + } else if (slotTime.type.toLowerCase() == 'ss') { + ss.value = slotTime.time + } + firsthandActiveTime.value = `${hh.value}:${mm.value}:${ss.value}` + setInputValue() + } else { + const timeArr = slotTime.time.split(':') + hh.value = timeArr[0] + mm.value = timeArr[1] + ss.value = timeArr[2] + firsthandActiveTime.value = `${hh.value}:${mm.value}:${ss.value}` + setInputValue() + } + } + + return { + isActive, + showPopup, + devuiTimePicker, + timePickerValue, + inputDom, + timePopupDom, + left,top, + showClearIcon, + firsthandActiveTime, + vModeValue, + getPopupPosition, + setInputValue, + getTimeValue, + clickVerifyFun, + isOutOpen, + clearAll, + chooseTime + } +} \ No newline at end of file diff --git a/devui/time-picker/src/time-picker-types.ts b/devui/time-picker/src/time-picker-types.ts new file mode 100644 index 00000000..a5e841d3 --- /dev/null +++ b/devui/time-picker/src/time-picker-types.ts @@ -0,0 +1,40 @@ +import { ExtractPropTypes, PropType } from 'vue'; + +export const timePickerProps = { + modelValue:{ + type: String, + default: '' + }, + placeholder: { + type: String, + default: '00:00:00' + }, + disabled: { + type: Boolean, + default: false + }, + timePickerWidth: { + type: Number, + default: 210 + }, + minTime: { + type: String, + default: '00:00:00' + // 默认时间优先级:minTime > modelValue + }, + maxTime: { + type: String, + default: '23:59:59' + }, + format:{ + type:String, + default:'hh:mm:ss' + }, + autoOpen:{ + type:Boolean, + default:false + } +} as const; + + +export type TimePickerProps = ExtractPropTypes; diff --git a/devui/time-picker/src/time-picker.scss b/devui/time-picker/src/time-picker.scss new file mode 100644 index 00000000..46cacddf --- /dev/null +++ b/devui/time-picker/src/time-picker.scss @@ -0,0 +1,51 @@ +@import '../../style/theme/color'; +@import '../../style/theme/shadow'; +@import '../../style/theme/corner'; +@import '../../style/core/_font'; + +.devui-time-picker { + width: 200px; + box-sizing: border-box; + display: flex; + justify-content: space-between; + align-items: center; + border: 1px solid $devui-form-control-line; + position: relative; + transition: 0.2s; + + &:hover { + border: 1px solid $devui-form-control-line-hover; + } + + .time-input { + border: none; + outline: none; + width: 100%; + } + + .time-input-icon { + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + + div { + text-align: right; + padding: 0 4px; + } + } +} + +.time-picker-active { + border: 1px solid $devui-form-control-line-active !important; +} + +.picker-disabled { + background-color: $devui-unavailable; + cursor: no-drop; + + * { + background-color: $devui-unavailable; + cursor: no-drop; + } +} diff --git a/devui/time-picker/src/time-picker.tsx b/devui/time-picker/src/time-picker.tsx new file mode 100644 index 00000000..0e96d620 --- /dev/null +++ b/devui/time-picker/src/time-picker.tsx @@ -0,0 +1,119 @@ +import { defineComponent, ref, onMounted, onUnmounted, watch } from 'vue' +import { TimePickerProps, timePickerProps } from './time-picker-types' +import { Icon } from '../../icon' +import usePicker from './composables/use-popup' +import TimePopup from './components/time-popup/index' + +import './time-picker.scss' + +export default defineComponent({ + name: 'DTimepicker', + components: { TimePopup }, + props: timePickerProps, + emits: ['selectedTimeChage','update:modelValue'], + setup(props: TimePickerProps, ctx) { + + const activeHour = ref('00') + const activeMinute = ref('00') + const activeSecond = ref('00') + const format = props.format.toLowerCase() + + const { + isActive, + showPopup, + devuiTimePicker, + inputDom, + left,top, + showClearIcon, + firsthandActiveTime, + chooseTime, + getTimeValue, + clickVerifyFun, + isOutOpen, + clearAll, + timePopupDom, + vModeValue, + getPopupPosition + } = usePicker(activeHour,activeMinute,activeSecond,props.minTime,format,props.autoOpen,props.disabled,props.modelValue) + + + const selectedTimeChage = (e:MouseEvent) => { + isActive.value = false + showPopup.value = false + ctx.emit('selectedTimeChage', vModeValue.value) + } + + onMounted(() => { + getPopupPosition() + isOutOpen() + document.addEventListener('click', clickVerifyFun) + document.addEventListener('click',getTimeValue) + document.addEventListener('scroll',getPopupPosition) + window.addEventListener('resize',getPopupPosition) + }) + onUnmounted(() => { + document.removeEventListener('click', clickVerifyFun) + document.removeEventListener('click',getTimeValue) + document.removeEventListener('scroll',getPopupPosition) + window.removeEventListener('resize',getPopupPosition) + }) + + watch(vModeValue,(newValue:string)=>{ + ctx.emit('update:modelValue',vModeValue.value) + if(newValue != props.minTime){ + showClearIcon.value = true + }else{ + showClearIcon.value = false + } + }) + + ctx.expose({ + clearAll,chooseTime + }) + + return () => { + return ( + <> +
    + + { + ctx.slots.customViewTemplate?.() + } + + +
    +
    + { + showClearIcon.value + ? + :'' + } +
    +
    + +
    +
    +
    + + ) + } + } +}) diff --git a/devui/time-picker/src/types.ts b/devui/time-picker/src/types.ts new file mode 100644 index 00000000..8c02163b --- /dev/null +++ b/devui/time-picker/src/types.ts @@ -0,0 +1,14 @@ + +export type timeType = 'hh' | 'HH' | 'mm' | 'MM' | 'ss' | 'SS'; +export type TimeObj = { + time: string + type?: timeType +} + +export type ArrType = { + type:'hour' | 'minute' | 'seconde' + isActive:boolean + isDisabled:boolean + time:string + flag:string +} diff --git a/devui/time-picker/src/utils.ts b/devui/time-picker/src/utils.ts new file mode 100644 index 00000000..16260778 --- /dev/null +++ b/devui/time-picker/src/utils.ts @@ -0,0 +1,95 @@ +import { Ref , reactive} from 'vue' +import { ArrType } from './types' + +/** + * 动态调整弹窗位置 + * @param element + * @returns { top , left } + */ +export function getPositionFun(el:Element,left:Ref,top:Ref):any{ + const inputDom = el.getBoundingClientRect() + const button = window.innerHeight - (inputDom.top + 20) + if(button > inputDom.top + 20){ + left.value = inputDom.x + top.value = inputDom.top + 20 + 10 + }else{ + left.value = inputDom.x + top.value = inputDom.top - 316 + } +} + +/** + * 初始化数据 + * @param type 类型( 时,分,秒 ) + * @returns Array