diff --git a/devui/time-picker/index.ts b/devui/time-picker/index.ts index c3baf1702b11e29f39c65cad8e54c91606b9b2f6..7535762542f21f511e69c6dcd2f056a6c5ba224f 100644 --- a/devui/time-picker/index.ts +++ b/devui/time-picker/index.ts @@ -10,8 +10,8 @@ export { TimePicker } export default { title: 'TimePicker 时间选择器', category: '数据录入', - status: '20%', + status: '80%', // TODO: 组件若开发完成则填入"已完成",并删除该注释 install(app: App): void { - app.use(TimePicker as any) + app.use(TimePicker as any) } } diff --git a/devui/time-picker/src/components/popup-line/composables/use-popup-line.ts b/devui/time-picker/src/components/popup-line/composables/use-popup-line.ts index a25908cdf47c52881cdc3f169cfe0adb75373b84..93f8a55d9c550ce6c7f3f98272e63069ab050eb1 100644 --- a/devui/time-picker/src/components/popup-line/composables/use-popup-line.ts +++ b/devui/time-picker/src/components/popup-line/composables/use-popup-line.ts @@ -16,7 +16,8 @@ const usePopupLine =( return false }else{ setTimeActive(item,index) - e.target.parentElement.scrollTop = index * 30 + e.target.parentElement.parentElement.scrollTop = index * 32 + } } @@ -39,9 +40,8 @@ const usePopupLine =( } activeTimeList.map((tiemItem,tiemeIndex)=>{ - tiemItem.isActive = false + tiemItem.isActive = index === tiemeIndex }) - activeTimeList[index].isActive = true acitveTimeValue.value = activeTimeList[index].time @@ -75,16 +75,13 @@ const usePopupLine =( 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) } @@ -103,13 +100,10 @@ const usePopupLine =( 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) } } @@ -143,9 +137,9 @@ const usePopupLine =( 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 + timeListDom.value.children[0].lastElementChild.children[0].scrollTop = hh * 32 + timeListDom.value.children[1].lastElementChild.children[0].scrollTop = mm * 32 + timeListDom.value.children[2].lastElementChild.children[0].scrollTop = ss * 32 activeHour.value = timeValueArr[0] activeMinute.value = timeValueArr[1] @@ -164,9 +158,9 @@ const usePopupLine =( 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 + timeListDom.value.children[0].lastElementChild.children[0].scrollTop = mm * 32 + timeListDom.value.children[1].lastElementChild.children[0].scrollTop = hh * 32 + timeListDom.value.children[2].lastElementChild.children[0].scrollTop = ss * 32 activeHour.value = timeValueArr[0] activeMinute.value = timeValueArr[1] @@ -184,8 +178,8 @@ const usePopupLine =( hh = parseInt(timeValueArr[0]) mm = parseInt(timeValueArr[1]) - timeListDom.value.children[0].scrollTop = hh * 30 - timeListDom.value.children[1].scrollTop = mm * 30 + timeListDom.value.children[0].lastElementChild.children[0].scrollTop = hh * 32 + timeListDom.value.children[1].lastElementChild.children[0].scrollTop = mm * 32 activeHour.value = timeValueArr[0] activeMinute.value = timeValueArr[1] @@ -200,8 +194,8 @@ const usePopupLine =( mm = parseInt(timeValueArr[1]) ss = parseInt(timeValueArr[2]) - timeListDom.value.children[0].scrollTop = mm * 30 - timeListDom.value.children[1].scrollTop = ss * 30 + timeListDom.value.children[0].lastElementChild.children[0].scrollTop = mm * 32 + timeListDom.value.children[1].lastElementChild.children[0].scrollTop = ss * 32 activeHour.value = minTiveArr[0] activeMinute.value = timeValueArr[1] @@ -212,8 +206,6 @@ const usePopupLine =( resetTimeAstrict(minuteListRef,activeMinute.value) } - // activeTime.value = `${activeHour.value}:${activeMinute.value}:${activeSecond.value}` - } // 解决清空之后,再次打开 最大值最小值限制范围失效 @@ -228,11 +220,7 @@ const usePopupLine =( // 指定选中 const resetTimeActive = (timeArr:Array,itemValue:string) =>{ timeArr.map( item =>{ - if(item.time == itemValue){ - item.isActive = true - }else{ - item.isActive = false - } + item.isActive = item.time === itemValue }) } @@ -241,6 +229,13 @@ const usePopupLine =( return { activeTime,activeHour,activeMinute,activeSecond } } + // 回到顶部 + const resetScrollTop = ()=>{ + for (let i = 0; i < timeListDom.value.children.length; i++) { + timeListDom.value.children[i].lastElementChild.children[0].scrollTop = 0 + } + } + return{ activeTime, activeHour, @@ -248,7 +243,8 @@ const usePopupLine =( activeSecond, activeTimeFun, resetTimeValue, - getNewTime + getNewTime, + resetScrollTop } } diff --git a/devui/time-picker/src/components/popup-line/index.scss b/devui/time-picker/src/components/popup-line/index.scss index ea679d8b5d462a77b82d619cb63de98c496e6d9b..52ba7c3eb5b43ab0561938029936b725f421f273 100644 --- a/devui/time-picker/src/components/popup-line/index.scss +++ b/devui/time-picker/src/components/popup-line/index.scss @@ -5,66 +5,72 @@ .devui-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); + height: 256px; + border-bottom: 1px solid $devui-dividing-line; - .time-ul { - height: 244px; + .time-item { + height: 100%; 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; - } + border-right: 1px solid $devui-dividing-line; + overflow-y: auto; &:last-child { border-right: none; } - li { + .time-ul { + width: 100%; + height: 100%; + cursor: pointer; padding: 0; - // text-align: center; - // padding-left: 40px; - text-align: center; - height: 30px; - line-height: 30px; - width: calc(100% + 8px); + margin: 0 auto; + list-style: none; + + .time-li { + display: block; + box-sizing: border-box; + list-style: none; + text-align: center; + margin: 0; + height: 26px; + overflow: hidden; + line-height: 26px; + margin-bottom: 6px; + font-size: 0; + color: $devui-text; + + &:hover { + background-color: $devui-list-item-hover-bg; + color: $devui-list-item-hover-text; + } - &:last-child { - margin-bottom: 214px; + span { + display: block; + font-size: 16px; + } } - &:hover { - background-color: #5d7ef80c; - color: #8aa2f0; + &:last-child::after { + content: ''; + display: block; + height: 224px; } - } - .active-li { - background-color: #5e7ce0; - color: #ffffff; + .active-li { + background-color: $devui-list-item-active-bg; + color: $devui-list-item-active-text; - &:hover { - background-color: #5e7ce0; - color: #ffffff; + &:hover { + background-color: $devui-list-item-active-bg; + color: $devui-list-item-active-text; + } } - } - .disabled-li { - background-color: $devui-disabled-bg; - color: $devui-disabled-text; - cursor: not-allowed; + .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 index 6a0462e5012115bda29f75f02fafc43bf5e11e3d..3cd2538158b7759516032fb3ad1dd5babf657333 100644 --- a/devui/time-picker/src/components/popup-line/index.tsx +++ b/devui/time-picker/src/components/popup-line/index.tsx @@ -1,11 +1,14 @@ -import { ref, defineComponent } from 'vue' +import { ref, defineComponent, onMounted } from 'vue' import { usePopupLine } from './composables/use-popup-line' import { ArrType } from '../../types' +import TimeScroll from '../time-scroll' + import './index.scss' export default defineComponent({ - name:'TTimeList', + name:'DTimeList', + components:{ TimeScroll }, props:{ hourList:{ type:Array, @@ -32,12 +35,14 @@ export default defineComponent({ default:'23:59:59' } }, + setup(props,ctx,){ const timeListDom = ref() const { getNewTime, activeTimeFun, - resetTimeValue + resetTimeValue, + resetScrollTop } = usePopupLine( props.hourList as Array, props.minuteList as Array, @@ -48,26 +53,20 @@ export default defineComponent({ 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} + > + {item.time}
  • ) }) @@ -76,7 +75,13 @@ export default defineComponent({ const TimeUl = (timeList:Array)=>{ return ( -
      6?'33.33%':'50%'}}> {TimeLi(timeList)}
    +
    6?'33.333%':'50%'}}> + +
      + {TimeLi(timeList)} +
    +
    +
    ) } @@ -98,20 +103,8 @@ export default defineComponent({ ) } - //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 + resetScrollTop,setOutoTime,getNewTime }) return ()=>{ diff --git a/devui/time-picker/src/components/time-popup/index.scss b/devui/time-picker/src/components/time-popup/index.scss index 19d8331aa7022a6c8f86076a5ffe10f2d46e7179..161c6f300c997374f3bbb45c28cafc08438de294 100644 --- a/devui/time-picker/src/components/time-popup/index.scss +++ b/devui/time-picker/src/components/time-popup/index.scss @@ -1,24 +1,26 @@ @import '../../../../style/theme/color'; @import '../../../../style/theme/shadow'; @import '../../../../style/theme/corner'; -@import '../../../../style/core/_font'; +@import '../../../../style/theme/z-index'; .devui-time-popup { height: 310px; + background-color: $devui-connected-overlay-bg; + box-shadow: 0 0 2px 2px $devui-shadow; + border-radius: $devui-border-radius; + border: 1px solid $devui-line; + overflow: hidden; 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: 0.3s; transition-property: visibility, opacity, z-index; } .devui-show-time-popup { visibility: visible; - z-index: 1052; + z-index: $devui-z-index-dropdown; opacity: 1; transition-property: visibility, opacity, z-index; } diff --git a/devui/time-picker/src/components/time-popup/index.tsx b/devui/time-picker/src/components/time-popup/index.tsx index 0eb222fb2da67a0919e719348cc7267da6e73e81..bba0cd3a1951d8e70e098091a2f3d6b34d1ec767 100644 --- a/devui/time-picker/src/components/time-popup/index.tsx +++ b/devui/time-picker/src/components/time-popup/index.tsx @@ -56,11 +56,10 @@ export default defineComponent({ }) watch(()=>[props.showPopup,props.bindData],([showPopup,newTimeVal],[oldShowPopup,oldTimeVal])=>{ - - if(showPopup || newTimeVal != oldTimeVal){ + if(showPopup || newTimeVal != oldTimeVal){ timeListDom.value.setOutoTime(newTimeVal) }else{ - timeListDom.value.restScrooTop() + timeListDom.value.resetScrollTop() } }) diff --git a/devui/time-picker/src/components/time-scroll/composables/use-time-scroll.ts b/devui/time-picker/src/components/time-scroll/composables/use-time-scroll.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1c039738c1961aadb011b8bb7f0ef01ecbc78d3 --- /dev/null +++ b/devui/time-picker/src/components/time-scroll/composables/use-time-scroll.ts @@ -0,0 +1,95 @@ +import { ref } from 'vue' + +export default function useTimeScroll():any{ + const scrollBoxDom = ref() + const scrollContentDom = ref() + const scrollThumbDom = ref() + const scrollTrackDom = ref() + + const isDown = ref(false) + + // 获取滚动条 thumb高度 + const getScrollHeight=()=>{ + const thumbHeight = (scrollContentDom.value.clientHeight / scrollContentDom.value.scrollHeight) * 100 + scrollThumbDom.value.style.height = thumbHeight + '%' + } + + // 设置滚动条 thumb位置 + const setVirtualScroll =()=>{ + const thumbMoveY = (scrollContentDom.value.scrollTop * 100 / scrollContentDom.value.clientHeight); + scrollThumbDom.value.style.transform = `translateY(${thumbMoveY}%)` + } + + // 点击轨道 thumb滚动到相应位置 + const clickTrackFun = (e:MouseEvent)=>{ + const offset = Math.abs(scrollTrackDom.value.getBoundingClientRect().top - e.clientY) + const thumbCenter = scrollThumbDom.value.offsetHeight / 2; + const thumbPosition = (offset - thumbCenter) * 100 / scrollContentDom.value.offsetHeight; + scrollContentDom.value.scrollTop = (thumbPosition * scrollContentDom.value.scrollHeight / 100); + scrollContentDom.value.style.top = scrollContentDom.value.scrollTop + 'px' + } + + // 鼠标拖到 + const mouseDownThum = ()=>{ + isDown.value = true + scrollTrackDom.value.style.opacity = 1 + } + // 鼠标离开 + const mouseOutThum = (e:MouseEvent)=>{ + isDown.value = false + thumbMouseMove(e) + } + + const thumbMouseMove = (e:any)=>{ + + const path = (e.composedPath && e.composedPath()) || e.path + + if(path.includes(scrollBoxDom.value) || isDown.value){ + scrollTrackDom.value.style.opacity = 1 + }else{ + scrollTrackDom.value.style.opacity = 0 + } + + if( !isDown.value ) return + clickTrackFun(e) + + } + + const getScrollWidth=()=>{ + const ua = navigator.userAgent + let marginRight = -20 + + if (ua.indexOf('Chrome') > -1) { + marginRight = -8 + }else{ + const outer = document.createElement('div'); + outer.className = 'devui-scrollbar-wrap'; + outer.style.width = '100px'; + outer.style.visibility = 'hidden'; + outer.style.position = 'absolute'; + outer.style.top = '-9999px'; + document.body.appendChild(outer); + + const widthNoScroll = outer.offsetWidth; + outer.style.overflow = 'scroll'; + + const inner = document.createElement('div'); + inner.style.width = '100%'; + outer.appendChild(inner); + + const widthWithScroll = inner.offsetWidth; + outer.parentNode.removeChild(outer); + + marginRight = (widthNoScroll - widthWithScroll + 3) * -1; + } + + return marginRight + } + + + return{ + scrollThumbDom,scrollTrackDom,scrollContentDom,scrollBoxDom,isDown, + getScrollHeight,setVirtualScroll,clickTrackFun,mouseDownThum,mouseOutThum,thumbMouseMove, + getScrollWidth + } +} \ No newline at end of file diff --git a/devui/time-picker/src/components/time-scroll/index.scss b/devui/time-picker/src/components/time-scroll/index.scss new file mode 100644 index 0000000000000000000000000000000000000000..b263596ea62e97f4942c681c03fed85e962751b2 --- /dev/null +++ b/devui/time-picker/src/components/time-scroll/index.scss @@ -0,0 +1,42 @@ +@import '../../../../style/theme/color'; + +.devui-scroll-box { + width: 100%; + height: 100%; + overflow: hidden; + position: relative; + + * { + user-select: none; + } + + .box-content { + height: 100%; + overflow-y: auto; + overflow-x: hidden; + scroll-behavior: smooth; + } + + .box-content-behavior-auto { + scroll-behavior: auto; + } + + .box-sroll { + width: 8px; + height: 100%; + position: absolute; + right: 0; + top: 0; + opacity: 0; + background-color: rgba(212, 107, 107, 0); + + .scroll-child { + width: 100%; + position: absolute; + top: 0; + right: 0; + background: $devui-line; + border-radius: 8px; + } + } +} diff --git a/devui/time-picker/src/components/time-scroll/index.tsx b/devui/time-picker/src/components/time-scroll/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..36cf4c2e9b6f041b235c8df502f5bd395d58c3af --- /dev/null +++ b/devui/time-picker/src/components/time-scroll/index.tsx @@ -0,0 +1,63 @@ +import { defineComponent, onBeforeUnmount, onMounted, onUnmounted } from 'vue' +import useTimeScroll from './composables/use-time-scroll' + +import './index.scss' + +export default defineComponent({ + name:'DTimeScroll', + setup(props,ctx){ + const { + scrollBoxDom, + scrollThumbDom, + scrollTrackDom, + scrollContentDom,isDown, + getScrollHeight, + setVirtualScroll, + clickTrackFun, + mouseDownThum, + mouseOutThum, + thumbMouseMove, + getScrollWidth + }=useTimeScroll() + const marginRight = getScrollWidth() + + onMounted(()=>{ + getScrollWidth() + getScrollHeight() + scrollBoxDom.value.addEventListener('click',setVirtualScroll) + scrollContentDom.value.addEventListener('scroll',setVirtualScroll) + scrollThumbDom.value.addEventListener('mousedown',mouseDownThum) + document.addEventListener('mouseup',mouseOutThum) + document.addEventListener('mousemove',thumbMouseMove) + }) + onBeforeUnmount(()=>{ + scrollBoxDom.value.removeEventListener('click',setVirtualScroll) + scrollContentDom.value.removeEventListener('scroll',setVirtualScroll) + scrollThumbDom.value.removeEventListener('mousedown',mouseDownThum) + }) + onUnmounted(()=>{ + document.removeEventListener('mouseup',mouseOutThum) + document.removeEventListener('mousemove',thumbMouseMove) + }) + + return()=>{ + return ( + <> +
    +
    + { + ctx.slots.default?.() + } +
    + +
    +
    +
    +
    + + ) + } + + } +}) \ No newline at end of file diff --git a/devui/time-picker/src/composables/use-time-picker.ts b/devui/time-picker/src/composables/use-time-picker.ts index 269b556a53da80e622f213518d30b8b80755ef7e..f80fdedb43b5463499f00facb82bcce7e2a01c1d 100644 --- a/devui/time-picker/src/composables/use-time-picker.ts +++ b/devui/time-picker/src/composables/use-time-picker.ts @@ -30,22 +30,24 @@ export default function useTimePicker( 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 inInputDom = path.includes(devuiTimePicker.value) + inInputDom && mouseInIputFun() + } + + const mouseInIputFun = ()=>{ + 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)=>{ diff --git a/devui/time-picker/src/time-picker-types.ts b/devui/time-picker/src/time-picker-types.ts index a5e841d38d6b31553ccd4b31f126b1008eb544af..498f69297a676a65871c8711c9c87bc22350cbf6 100644 --- a/devui/time-picker/src/time-picker-types.ts +++ b/devui/time-picker/src/time-picker-types.ts @@ -15,7 +15,7 @@ export const timePickerProps = { }, timePickerWidth: { type: Number, - default: 210 + default: 212 }, minTime: { type: String, diff --git a/devui/time-picker/src/time-picker.scss b/devui/time-picker/src/time-picker.scss index 46cacddf7a48bfa0c89528328199fa3a5836ad8d..91d712b0ff9933c0338b1d48bf31527cbb6805a4 100644 --- a/devui/time-picker/src/time-picker.scss +++ b/devui/time-picker/src/time-picker.scss @@ -21,6 +21,8 @@ border: none; outline: none; width: 100%; + background-color: $devui-base-bg; + color: $devui-text; } .time-input-icon { @@ -28,9 +30,12 @@ justify-content: space-between; align-items: center; cursor: pointer; + background-color: $devui-base-bg; div { text-align: right; + height: 25px; + line-height: 22px; padding: 0 4px; } } @@ -44,7 +49,7 @@ background-color: $devui-unavailable; cursor: no-drop; - * { + :not(.time-input-icon) { background-color: $devui-unavailable; cursor: no-drop; }