From 1043e5ad8f417f0c42d77983d624495381d982f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chttpxiaobocom=E2=80=9D?= <731653765@qq.com> Date: Wed, 25 Aug 2021 16:34:14 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90slider=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E7=94=A8=E6=B3=95=EF=BC=8C=E8=BF=98=E6=9C=89toolTip?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E5=88=B6=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devui/slider/index.ts | 17 +++ devui/slider/src/slider-types.ts | 30 +++++ devui/slider/src/slider.scss | 77 ++++++++++++ devui/slider/src/slider.tsx | 200 +++++++++++++++++++++++++++++++ sites/components/slider/index.md | 37 ++++++ 5 files changed, 361 insertions(+) create mode 100644 devui/slider/index.ts create mode 100644 devui/slider/src/slider-types.ts create mode 100644 devui/slider/src/slider.scss create mode 100644 devui/slider/src/slider.tsx create mode 100644 sites/components/slider/index.md diff --git a/devui/slider/index.ts b/devui/slider/index.ts new file mode 100644 index 00000000..557c9fd2 --- /dev/null +++ b/devui/slider/index.ts @@ -0,0 +1,17 @@ +import type { App } from 'vue' +import Slider from './src/slider' + +Slider.install = function(app: App): void { + app.component(Slider.name, Slider) +} + +export { Slider } + +export default { + title: 'Slider 滑块', + category: '数据录入', + install(app: App): void { + + app.use(Slider as any) + } +} diff --git a/devui/slider/src/slider-types.ts b/devui/slider/src/slider-types.ts new file mode 100644 index 00000000..9bdc4bfa --- /dev/null +++ b/devui/slider/src/slider-types.ts @@ -0,0 +1,30 @@ +import type { ExtractPropTypes } from 'vue' + +export const sliderProps = { + /* test: { + type: Object as PropType<{ xxx: xxx }> + } */ + min:{ + type:Number, + default:0 + }, + max:{ + type:Number, + default:50 + }, + step:{ + type:Number, + default:1 + }, + disabled:{ + type:Boolean, + default:false + }, + showInput:{ + type:Boolean, + default:false + } + +} as const + +export type SliderProps = ExtractPropTypes diff --git a/devui/slider/src/slider.scss b/devui/slider/src/slider.scss new file mode 100644 index 00000000..11bc6926 --- /dev/null +++ b/devui/slider/src/slider.scss @@ -0,0 +1,77 @@ +@import '../../style/mixins/index'; +@import '../../style/theme/color'; +@import '../../style/theme/shadow'; +@import '../../style/theme/corner'; +@import '../../style/theme/font'; + +.devui-slider { + position: relative; + width: 70%; + display: block; + // + .devui-slider__runway { + position: relative; + width: 100%; + padding: 4px 0; + margin: 4px 0; + cursor: pointer; + box-sizing: border-box; + height: 5px; + display: flex; + align-items: center; + background-color: $devui-default-bg; + + .devui-slider__bar { + height: 6px; + background-color: $devui-default-line; + border-top-left-radius: $devui-border-radius; + border-bottom-left-radius: $devui-border-radius; + position: absolute; + } + + .devui-slider__button { + position: absolute; + width: 14px; + height: 14px; + border: 2px solid $devui-default-line; + background-color: $devui-default-bg; + border-radius: 50%; + margin-left: -7px; + transition: transform 0.2s ease-in-out; + } + + .devui-slider__button:hover { + transform: scale(1.2); + } + } + + .devui-min_count { + position: absolute; + top: 15px; + font-size: $devui-font-size; + color: $devui-text; + font-family: HuaweiFont, Helvetica, Arial, PingFangSC-Regular, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, Microsoft JhengHei; + } + + .devui-max_count { + position: absolute; + top: 15px; + right: 0; + font-size: $devui-font-size; + color: $devui-text; + font-family: HuaweiFont, Helvetica, Arial, PingFangSC-Regular, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, Microsoft JhengHei; + } + + .devui-input__wrap { + position: absolute; + right: -60px; + top: -12px; + padding: 5px 10px; + cursor: text; + margin-left: 20px; + + input { + width: 40px; + } + } +} diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx new file mode 100644 index 00000000..054ad37c --- /dev/null +++ b/devui/slider/src/slider.tsx @@ -0,0 +1,200 @@ +import './slider.scss' + +import { defineComponent,ref,computed,toRefs} from 'vue' +import { sliderProps, SliderProps } from './slider-types' +import { Input } from '../../input/index'; + +export default defineComponent({ + name: 'DSlider', + components:{Input}, + props: sliderProps, + emits: [], + setup(props: SliderProps) { + + let isClick=true; + + let startPosition=0; + //用以定位button的位置 + const currentPosition=ref(0); + //当前的位置以百分比显示 + const percentDispaly=ref('') + let currentX=0; + let startX=0; + + //移动或者点击后的实际偏移的像素 + let pxOffset=0 + //输入后的值 + const inputValue=ref(props.min) + + const newPostion=ref(0) + + const renderShowInput=()=>{ + return props.showInput? :'' + } + + function handleonMousedown(event:MouseEvent){ + //props.disabled状态是不能点击拖拽的 + if(props.disabled) return + //阻止默认事件 + event.preventDefault(); + dragStart(event); + //当鼠标开始移动时,进行坐标计算 + window.addEventListener('mousemove',onDragging) + //当鼠标抬起时,停止计算 + window.addEventListener('mouseup',onDragEnd) + + } + + + function dragStart(event:MouseEvent){ + + + + //防止mouseup触发父元素的click事件 + isClick=false; + //获取当前的x坐标值 + startX=event.clientX; + //把当前值给startPosition,以便后面再重新拖拽时,会以当前的位置计算偏移 + startPosition=currentPosition.value + newPostion.value=startPosition + + + } + + + /** + * + * @param event 鼠标事件 + * currentPosition:当前移动的X的坐标 + * offset:当前x坐标减去初始x坐标的偏移 + * + */ + function onDragging(event:MouseEvent){ + + currentX=event.clientX; + + + + pxOffset=currentX-startX; + //移动的x方向上的偏移+初始位置等于新位置 + newPostion.value=startPosition+pxOffset; + + setPostion(newPostion.value); + } + function onDragEnd(){ + //防止mouseup后立即执行click事件,mouseup后 + //会立即执行click,但是isClick=true 是100ms才出发,因此不会执行click事件,就跳出来了 + setTimeout(() => { + isClick=true; + }, 100); + + window.removeEventListener('mousemove',onDragging) + window.removeEventListener('mouseup',onDragEnd) + } + + function setPostion(newPosition:number){ + //获取slider的实际长度的像素 + const sliderWidth:number=document.querySelector('.devui-slider__runway').clientWidth + + if(newPosition<0){ + newPosition=0; + }else if(newPosition>=sliderWidth){ + + + //当到达类似98%时,进行边界判定,设置值为100% + currentPosition.value=sliderWidth + inputValue.value=100 + percentDispaly.value= '100%' + + return + + } + //计算slider的实际像素每段的长度 + const LengthPerStep=sliderWidth/((props.max-props.min)/props.step) + //计算实际位移的取整段数 + const steps=Math.round(newPosition/LengthPerStep) + + + //实际的偏移像素 + + const value:number=steps*LengthPerStep + + + //这个是向左偏移百分比的值 + percentDispaly.value= Math.round(value*100/sliderWidth)+'%' + + + + //更新输入框的值 + + inputValue.value=Math.round(value*(props.max-props.min)/sliderWidth)+props.min + //设置当前所在的位置 + currentPosition.value=value; + + //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 + + } + + + function handleClick(event){ + if(isClick){ + startX=event.target.getBoundingClientRect().left + currentX=event.clientX + + + + setPostion(currentX-startX) + }else + { + return + } + } + //输入框内的值 + function handleOnInput(event) { + inputValue.value=parseInt(event.target.value) + if(!inputValue.value){ + inputValue.value=props.min + percentDispaly.value='0%' + }else{ + if(inputValue.valueprops.max){ + inputValue.value=props.max; + } + const re=/^(?:[1-9]?\d|100)$/; + if(re.test(`${inputValue.value}`)){ + + percentDispaly.value=(inputValue.value-props.min)*100/(props.max-props.min)+'%' + } + } + + } + return ()=>( +
+ {/* 整个的长度 */} +
+ {/* 滑动后左边的进度条 */} +
+
+
+ {props.min} + {props.max} + + {/* */} + {renderShowInput()} +
+ + ) + }, +}) diff --git a/sites/components/slider/index.md b/sites/components/slider/index.md new file mode 100644 index 00000000..202f05c1 --- /dev/null +++ b/sites/components/slider/index.md @@ -0,0 +1,37 @@ +# Slider滑动输入条 + +滑动输入条 + +### 何时使用 +当用户需要在数值区间内进行选择时使用 + +### 基本用法 +
+ +
+ +### 带有输入框的滑动组件 +
+ +
+ +### 可设置Step的滑动组件 +
+ +
+ +```html + + + + + +``` + + + + + + + + -- Gitee From 180a9620ad6c3fd6c73fac9a3c5a9bcfbb58e043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chttpxiaobocom=E2=80=9D?= <731653765@qq.com> Date: Wed, 25 Aug 2021 19:16:57 +0800 Subject: [PATCH 2/6] fix: fix max vlaue --- devui/slider/src/slider.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx index 054ad37c..8657181b 100644 --- a/devui/slider/src/slider.tsx +++ b/devui/slider/src/slider.tsx @@ -98,12 +98,12 @@ export default defineComponent({ if(newPosition<0){ newPosition=0; - }else if(newPosition>=sliderWidth){ + }else if(Math.ceil(newPosition) >=Math.ceil(sliderWidth) ){ - + //当到达类似98%时,进行边界判定,设置值为100% currentPosition.value=sliderWidth - inputValue.value=100 + inputValue.value=props.max percentDispaly.value= '100%' return -- Gitee From bd66063a1ca61278c45272832a0bfde7aa7dcf74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chttpxiaobocom=E2=80=9D?= <731653765@qq.com> Date: Fri, 27 Aug 2021 15:58:30 +0800 Subject: [PATCH 3/6] feat: add disabled function --- devui/slider/src/slider-types.ts | 44 ++--- devui/slider/src/slider.scss | 43 +++- devui/slider/src/slider.tsx | 327 +++++++++++++++---------------- sites/components/slider/index.md | 7 + 4 files changed, 221 insertions(+), 200 deletions(-) diff --git a/devui/slider/src/slider-types.ts b/devui/slider/src/slider-types.ts index 9bdc4bfa..6525c0b5 100644 --- a/devui/slider/src/slider-types.ts +++ b/devui/slider/src/slider-types.ts @@ -1,30 +1,24 @@ -import type { ExtractPropTypes } from 'vue' - +import type { ExtractPropTypes } from 'vue'; export const sliderProps = { - /* test: { - type: Object as PropType<{ xxx: xxx }> - } */ - min:{ - type:Number, - default:0 + min: { + type: Number, + default: 0, }, - max:{ - type:Number, - default:50 + max: { + type: Number, + default: 50, }, - step:{ - type:Number, - default:1 + step: { + type: Number, + default: 1, }, - disabled:{ - type:Boolean, - default:false + disabled: { + type: Boolean, + default: false, }, - showInput:{ - type:Boolean, - default:false - } - -} as const - -export type SliderProps = ExtractPropTypes + showInput: { + type: Boolean, + default: false, + }, +} as const; +export type SliderProps = ExtractPropTypes; diff --git a/devui/slider/src/slider.scss b/devui/slider/src/slider.scss index 11bc6926..998b18c0 100644 --- a/devui/slider/src/slider.scss +++ b/devui/slider/src/slider.scss @@ -8,8 +8,14 @@ position: relative; width: 70%; display: block; - // - .devui-slider__runway { + + &.disabled { + cursor: not-allowed; + background-color: $devui-disabled-line; + border-color: $devui-disabled-line; + } + + &__runway { position: relative; width: 100%; padding: 4px 0; @@ -21,12 +27,25 @@ align-items: center; background-color: $devui-default-bg; + &.disabled { + cursor: not-allowed; + } + .devui-slider__bar { height: 6px; background-color: $devui-default-line; border-top-left-radius: $devui-border-radius; border-bottom-left-radius: $devui-border-radius; position: absolute; + + &.disabled { + background-color: $devui-disabled-line; + border-color: $devui-disabled-line; + } + + &.disabled:hover { + cursor: not-allowed; + } } .devui-slider__button { @@ -34,14 +53,24 @@ width: 14px; height: 14px; border: 2px solid $devui-default-line; - background-color: $devui-default-bg; + background-color: $devui-base-bg; border-radius: 50%; margin-left: -7px; transition: transform 0.2s ease-in-out; - } - .devui-slider__button:hover { - transform: scale(1.2); + &:hover { + transform: scale(1.2); + } + + &.disabled { + background-color: $devui-base-bg; + border-color: $devui-disabled-line; + } + + &.disabled:hover { + cursor: not-allowed; + transform: none; + } } } @@ -50,7 +79,6 @@ top: 15px; font-size: $devui-font-size; color: $devui-text; - font-family: HuaweiFont, Helvetica, Arial, PingFangSC-Regular, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, Microsoft JhengHei; } .devui-max_count { @@ -59,7 +87,6 @@ right: 0; font-size: $devui-font-size; color: $devui-text; - font-family: HuaweiFont, Helvetica, Arial, PingFangSC-Regular, Hiragino Sans GB, Microsoft YaHei, 微软雅黑, Microsoft JhengHei; } .devui-input__wrap { diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx index 8657181b..dc02240e 100644 --- a/devui/slider/src/slider.tsx +++ b/devui/slider/src/slider.tsx @@ -1,200 +1,193 @@ -import './slider.scss' - -import { defineComponent,ref,computed,toRefs} from 'vue' -import { sliderProps, SliderProps } from './slider-types' -import { Input } from '../../input/index'; +import { defineComponent, ref,computed } from 'vue'; +import { sliderProps } from './slider-types'; +import { Input } from '../../input'; +import './slider.scss'; export default defineComponent({ name: 'DSlider', - components:{Input}, + components: { + Input, + }, props: sliderProps, emits: [], - setup(props: SliderProps) { - - let isClick=true; - - let startPosition=0; + setup(props) { + let isClick = true; + let currentX = 0; + let startX = 0; + //移动或者点击后的实际偏移的像素 + let pxOffset = 0; + let startPosition = 0; //用以定位button的位置 - const currentPosition=ref(0); + const currentPosition = ref(0); //当前的位置以百分比显示 - const percentDispaly=ref('') - let currentX=0; - let startX=0; - - //移动或者点击后的实际偏移的像素 - let pxOffset=0 + const percentDispaly = ref(''); //输入后的值 - const inputValue=ref(props.min) - - const newPostion=ref(0) - - const renderShowInput=()=>{ - return props.showInput? :'' - } - - function handleonMousedown(event:MouseEvent){ - //props.disabled状态是不能点击拖拽的 - if(props.disabled) return - //阻止默认事件 - event.preventDefault(); - dragStart(event); - //当鼠标开始移动时,进行坐标计算 - window.addEventListener('mousemove',onDragging) - //当鼠标抬起时,停止计算 - window.addEventListener('mouseup',onDragEnd) - - } - - - function dragStart(event:MouseEvent){ - - - - //防止mouseup触发父元素的click事件 - isClick=false; - //获取当前的x坐标值 - startX=event.clientX; - //把当前值给startPosition,以便后面再重新拖拽时,会以当前的位置计算偏移 - startPosition=currentPosition.value - newPostion.value=startPosition - - - } - - - /** - * - * @param event 鼠标事件 - * currentPosition:当前移动的X的坐标 - * offset:当前x坐标减去初始x坐标的偏移 - * - */ - function onDragging(event:MouseEvent){ - - currentX=event.clientX; - - - - pxOffset=currentX-startX; - //移动的x方向上的偏移+初始位置等于新位置 - newPostion.value=startPosition+pxOffset; - + const inputValue = ref(props.min); + const newPostion = ref(0); + const renderShowInput = () => { + return props.showInput ? ( + + ) : ( + '' + ); + }; + function handleonMousedown(event: MouseEvent) { + //props.disabled状态是不能点击拖拽的 + if (props.disabled || props.disabled) return; + + //阻止默认事件 + event.preventDefault(); + dragStart(event); + //当鼠标开始移动时,进行坐标计算 + window.addEventListener('mousemove', onDragging); + //当鼠标抬起时,停止计算 + window.addEventListener('mouseup', onDragEnd); + } + function dragStart(event: MouseEvent) { + //防止mouseup触发父元素的click事件 + isClick = false; + //获取当前的x坐标值 + startX = event.clientX; + //把当前值给startPosition,以便后面再重新拖拽时,会以当前的位置计算偏移 + startPosition = currentPosition.value; + newPostion.value = startPosition; + } + /** + * + * @param event 鼠标事件 + * currentPosition:当前移动的X的坐标 + * offset:当前x坐标减去初始x坐标的偏移 + * + */ + function onDragging(event: MouseEvent) { + currentX = event.clientX; + pxOffset = currentX - startX; + //移动的x方向上的偏移+初始位置等于新位置 + newPostion.value = startPosition + pxOffset; setPostion(newPostion.value); - } - function onDragEnd(){ - //防止mouseup后立即执行click事件,mouseup后 - //会立即执行click,但是isClick=true 是100ms才出发,因此不会执行click事件,就跳出来了 - setTimeout(() => { - isClick=true; - }, 100); - - window.removeEventListener('mousemove',onDragging) - window.removeEventListener('mouseup',onDragEnd) - } - - function setPostion(newPosition:number){ - //获取slider的实际长度的像素 - const sliderWidth:number=document.querySelector('.devui-slider__runway').clientWidth + } + function onDragEnd() { + //防止mouseup后立即执行click事件,mouseup后 + //会立即执行click,但是isClick=true 是100ms才出发,因此不会执行click事件,就跳出来了 + setTimeout(() => { + isClick = true; + }, 100); + window.removeEventListener('mousemove', onDragging); + window.removeEventListener('mouseup', onDragEnd); + } - if(newPosition<0){ - newPosition=0; - }else if(Math.ceil(newPosition) >=Math.ceil(sliderWidth) ){ - - + function setPostion(newPosition: number) { + //获取slider的实际长度的像素 + const sliderWidth: number = document.querySelector( + '.devui-slider__runway' + ).clientWidth; + if (newPosition < 0) { + newPosition = 0; + } else if (Math.ceil(newPosition) >= Math.ceil(sliderWidth)) { //当到达类似98%时,进行边界判定,设置值为100% - currentPosition.value=sliderWidth - inputValue.value=props.max - percentDispaly.value= '100%' - - return - + currentPosition.value = sliderWidth; + inputValue.value = props.max; + percentDispaly.value = '100%'; + return; } //计算slider的实际像素每段的长度 - const LengthPerStep=sliderWidth/((props.max-props.min)/props.step) + const LengthPerStep = + sliderWidth / ((props.max - props.min) / props.step); //计算实际位移的取整段数 - const steps=Math.round(newPosition/LengthPerStep) - - + const steps = Math.round(newPosition / LengthPerStep); //实际的偏移像素 - - const value:number=steps*LengthPerStep - - + const value: number = steps * LengthPerStep; + //这个是向左偏移百分比的值 - percentDispaly.value= Math.round(value*100/sliderWidth)+'%' - - - + percentDispaly.value = Math.round((value * 100) / sliderWidth) + '%'; //更新输入框的值 - - inputValue.value=Math.round(value*(props.max-props.min)/sliderWidth)+props.min + inputValue.value = + Math.round((value * (props.max - props.min)) / sliderWidth) + props.min; //设置当前所在的位置 - currentPosition.value=value; - - //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 - - } - + currentPosition.value = value; - function handleClick(event){ - if(isClick){ - startX=event.target.getBoundingClientRect().left - currentX=event.clientX - - - - setPostion(currentX-startX) - }else - { - return + //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 } - } - //输入框内的值 - function handleOnInput(event) { - inputValue.value=parseInt(event.target.value) - if(!inputValue.value){ - inputValue.value=props.min - percentDispaly.value='0%' - }else{ - if(inputValue.valueprops.max){ - inputValue.value=props.max; + + function handleClick(event) { + if (!props.disabled&&isClick) { + startX = event.target.getBoundingClientRect().left; + currentX = event.clientX; + + setPostion(currentX - startX); + } else { + return; } - const re=/^(?:[1-9]?\d|100)$/; - if(re.test(`${inputValue.value}`)){ - - percentDispaly.value=(inputValue.value-props.min)*100/(props.max-props.min)+'%' + } + //输入框内的值 + function handleOnInput(event) { + inputValue.value = parseInt(event.target.value); + if (!inputValue.value) { + inputValue.value = props.min; + percentDispaly.value = '0%'; + } else { + if (inputValue.value < props.min) { + inputValue.value = props.min; + } + if (inputValue.value > props.max) { + inputValue.value = props.max; + } + const re = /^(?:[1-9]?\d|100)$/; + if (re.test(`${inputValue.value}`)) { + percentDispaly.value = + ((inputValue.value - props.min) * 100) / (props.max - props.min) + + '%'; + } } } + + const outerCls={ + 'devui-slider':true, + 'devui-disabled':props.disabled + } + + const runwayClazz=computed(()=>{ + + return props.disabled ? 'devui-slider__runway disabled':'devui-slider__runway' + + }) + + const barClazz=computed(()=>{ + + return props.disabled ? 'devui-slider__bar disabled':'devui-slider__bar' + + }) + const btnClazz=computed(()=>{ - } - return ()=>( -
+ return props.disabled ? 'devui-slider__button disabled':'devui-slider__button' + + }) + + return () => ( +
{/* 整个的长度 */} -
+
{/* 滑动后左边的进度条 */} -
-
+ >
- {props.min} - {props.max} - + {props.min} + {props.max} {/* */} {renderShowInput()} -
- - ) +
+ ); }, -}) +}); diff --git a/sites/components/slider/index.md b/sites/components/slider/index.md index 202f05c1..a24540fc 100644 --- a/sites/components/slider/index.md +++ b/sites/components/slider/index.md @@ -20,12 +20,19 @@
+### 禁止输入态 +
+ +
+ ```html + + ``` -- Gitee From a1fc2dd1c9b7bf3ceea3714ba53aab1bea85a595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chttpxiaobocom=E2=80=9D?= <731653765@qq.com> Date: Mon, 30 Aug 2021 19:05:42 +0800 Subject: [PATCH 4/6] feat: complete v-model,complete api document feat: formate code fix: fix document --- devui/slider/__tests__/slider.spec.ts | 2 + devui/slider/src/slider-types.ts | 22 ++-- devui/slider/src/slider.tsx | 141 +++++++++++--------------- sites/components/radio/index.md | 1 + sites/components/slider/index.md | 122 ++++++++++++++++++---- 5 files changed, 178 insertions(+), 110 deletions(-) create mode 100644 devui/slider/__tests__/slider.spec.ts diff --git a/devui/slider/__tests__/slider.spec.ts b/devui/slider/__tests__/slider.spec.ts new file mode 100644 index 00000000..9f9d6680 --- /dev/null +++ b/devui/slider/__tests__/slider.spec.ts @@ -0,0 +1,2 @@ +import { mount } from '@vue/test-utils'; +import DSlider from '../src/slider' \ No newline at end of file diff --git a/devui/slider/src/slider-types.ts b/devui/slider/src/slider-types.ts index 6525c0b5..42aa7688 100644 --- a/devui/slider/src/slider-types.ts +++ b/devui/slider/src/slider-types.ts @@ -1,24 +1,28 @@ import type { ExtractPropTypes } from 'vue'; export const sliderProps = { - min: { - type: Number, - default: 0, + disabled: { + type: Boolean, + default: false, }, max: { type: Number, - default: 50, + default: 100, }, - step: { + min: { type: Number, - default: 1, + default: 0, }, - disabled: { - type: Boolean, - default: false, + modelValue: { + type: Number, + default: 0, }, showInput: { type: Boolean, default: false, }, + step: { + type: Number, + default: 1, + }, } as const; export type SliderProps = ExtractPropTypes; diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx index dc02240e..3db77852 100644 --- a/devui/slider/src/slider.tsx +++ b/devui/slider/src/slider.tsx @@ -1,4 +1,4 @@ -import { defineComponent, ref,computed } from 'vue'; +import { defineComponent, ref, computed, onMounted } from 'vue'; import { sliderProps } from './slider-types'; import { Input } from '../../input'; import './slider.scss'; @@ -9,35 +9,39 @@ export default defineComponent({ Input, }, props: sliderProps, - emits: [], - setup(props) { + emits: ['update:modelValue'], + setup(props, ctx) { let isClick = true; - let currentX = 0; - let startX = 0; - //移动或者点击后的实际偏移的像素 - let pxOffset = 0; let startPosition = 0; - //用以定位button的位置 - const currentPosition = ref(0); + let startX = 0; + + const inputValue = ref(props.modelValue); + const currentPosition = ref(0); + const newPostion = ref(0); //当前的位置以百分比显示 const percentDispaly = ref(''); - //输入后的值 - const inputValue = ref(props.min); - const newPostion = ref(0); const renderShowInput = () => { - return props.showInput ? ( - - ) : ( - '' - ); + return props.showInput ? : ''; }; + + //当传入modelValue时用以定位button的位置 + if (props.modelValue > props.max) { + percentDispaly.value = '100%'; + } else if (props.modelValue < props.min) { + percentDispaly.value = '0%'; + } else { + percentDispaly.value = ((props.modelValue - props.min) * 100) / (props.max - props.min) + '%'; + } + + //一挂载就进行当前位置的计算,以后的移动基于当前的位置移动 + onMounted(() => { + const sliderWidth = document.querySelector('.devui-slider__runway').clientWidth; + currentPosition.value = (sliderWidth * (inputValue.value - props.min)) / (props.max - props.min); + }); + function handleonMousedown(event: MouseEvent) { //props.disabled状态是不能点击拖拽的 if (props.disabled || props.disabled) return; - //阻止默认事件 event.preventDefault(); dragStart(event); @@ -52,6 +56,7 @@ export default defineComponent({ //获取当前的x坐标值 startX = event.clientX; //把当前值给startPosition,以便后面再重新拖拽时,会以当前的位置计算偏移 + startPosition = currentPosition.value; newPostion.value = startPosition; } @@ -63,10 +68,11 @@ export default defineComponent({ * */ function onDragging(event: MouseEvent) { - currentX = event.clientX; - pxOffset = currentX - startX; + const currentX = event.clientX; + const pxOffset = currentX - startX; //移动的x方向上的偏移+初始位置等于新位置 newPostion.value = startPosition + pxOffset; + setPostion(newPostion.value); } function onDragEnd() { @@ -78,45 +84,43 @@ export default defineComponent({ window.removeEventListener('mousemove', onDragging); window.removeEventListener('mouseup', onDragEnd); } - function setPostion(newPosition: number) { //获取slider的实际长度的像素 - const sliderWidth: number = document.querySelector( - '.devui-slider__runway' - ).clientWidth; + const sliderWidth: number = Math.round(document.querySelector('.devui-slider__runway').clientWidth); + if (newPosition < 0) { newPosition = 0; - } else if (Math.ceil(newPosition) >= Math.ceil(sliderWidth)) { - //当到达类似98%时,进行边界判定,设置值为100% - currentPosition.value = sliderWidth; - inputValue.value = props.max; - percentDispaly.value = '100%'; - return; } //计算slider的实际像素每段的长度 - const LengthPerStep = - sliderWidth / ((props.max - props.min) / props.step); + const LengthPerStep = sliderWidth / ((props.max - props.min) / props.step); //计算实际位移的取整段数 const steps = Math.round(newPosition / LengthPerStep); //实际的偏移像素 const value: number = steps * LengthPerStep; - //这个是向左偏移百分比的值 + //要是刚好划过半段切刚好超出最大长度的情况进行限定 + if (Math.round(value) >= sliderWidth) { + currentPosition.value = sliderWidth; + inputValue.value = props.max; + percentDispaly.value = '100%'; + ctx.emit('update:modelValue', props.max); + return; + } + + //向左偏移百分比的值 percentDispaly.value = Math.round((value * 100) / sliderWidth) + '%'; //更新输入框的值 - inputValue.value = - Math.round((value * (props.max - props.min)) / sliderWidth) + props.min; + inputValue.value = Math.round((value * (props.max - props.min)) / sliderWidth) + props.min; //设置当前所在的位置 - currentPosition.value = value; - - //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 + currentPosition.value = newPosition; + ctx.emit('update:modelValue', inputValue.value); } + //当点击滑动条时, function handleClick(event) { - if (!props.disabled&&isClick) { + if (!props.disabled && isClick) { startX = event.target.getBoundingClientRect().left; - currentX = event.clientX; - + const currentX = event.clientX; setPostion(currentX - startX); } else { return; @@ -137,55 +141,28 @@ export default defineComponent({ } const re = /^(?:[1-9]?\d|100)$/; if (re.test(`${inputValue.value}`)) { - percentDispaly.value = - ((inputValue.value - props.min) * 100) / (props.max - props.min) + - '%'; + percentDispaly.value = ((inputValue.value - props.min) * 100) / (props.max - props.min) + '%'; } } } - - const outerCls={ - 'devui-slider':true, - 'devui-disabled':props.disabled - } - - const runwayClazz=computed(()=>{ - - return props.disabled ? 'devui-slider__runway disabled':'devui-slider__runway' - - }) - - const barClazz=computed(()=>{ - - return props.disabled ? 'devui-slider__bar disabled':'devui-slider__bar' - - }) - const btnClazz=computed(()=>{ - - return props.disabled ? 'devui-slider__button disabled':'devui-slider__button' - - }) - + //添加disabled类 + const disableClzz = computed(() => { + return props.disabled ? ' disabled' : ''; + }); return () => ( -
+
{/* 整个的长度 */} -
+
{/* 滑动后左边的进度条 */} +
-
- {props.min} - {props.max} - {/* */} + {props.min} + {props.max} {renderShowInput()}
); diff --git a/sites/components/radio/index.md b/sites/components/radio/index.md index 3ab37dd6..7c6f9d68 100644 --- a/sites/components/radio/index.md +++ b/sites/components/radio/index.md @@ -42,6 +42,7 @@ export default defineComponent({ ::: + ### radio 根据条件终止切换操作 :::demo 根据条件判断,第二项禁止跳转。 diff --git a/sites/components/slider/index.md b/sites/components/slider/index.md index a24540fc..171e2e5b 100644 --- a/sites/components/slider/index.md +++ b/sites/components/slider/index.md @@ -1,44 +1,128 @@ -# Slider滑动输入条 +# Slider 滑动输入条 滑动输入条 ### 何时使用 + 当用户需要在数值区间内进行选择时使用 ### 基本用法 +
- +
### 带有输入框的滑动组件 -
- -
-### 可设置Step的滑动组件
- +
-### 禁止输入态 +### 可设置 Step 的滑动组件
- + +
```html - - - - - - - + + ``` +### 禁止输入态 +
+ +
+```html + + +``` - - - + + +### API + +d-slider 参数 + +| 参数 | 类型 | 默认 | 说明 | 跳转 | +| --------- | ------- | ----- | ------------------------------------------------------------------- | ---- | +| max | number | 100 | 可选,滑动输入条的最大值 |[基本用法](#基本用法) | +| min | number | 0 | 可选,滑动输入条的最小值 |[基本用法](#基本用法) | +| step | number | 1 | 可选,滑动输入条的步长,取值必须大于等于1,且必须可被(max-min)整除 |[基本用法](#可设置Step的滑动组件) | +| disabled | boolean | false | 可选,值为 true 时禁止用户输入 |[基本用法](#禁止输入态) | +| showInput | boolean | false | 可选,值为 true 显示输入框 |[基本用法](#带有输入框的滑动组件) | -- Gitee From 0b0cb70deb027ecef9d64d999b9ca6e33ea33480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chttpxiaobocom=E2=80=9D?= <731653765@qq.com> Date: Fri, 3 Sep 2021 22:49:32 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E5=88=AB=E5=A4=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=B7=B2=E6=94=B9=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sites/components/radio/index.md | 1 - 1 file changed, 1 deletion(-) diff --git a/sites/components/radio/index.md b/sites/components/radio/index.md index 7c6f9d68..3ab37dd6 100644 --- a/sites/components/radio/index.md +++ b/sites/components/radio/index.md @@ -42,7 +42,6 @@ export default defineComponent({ ::: - ### radio 根据条件终止切换操作 :::demo 根据条件判断,第二项禁止跳转。 -- Gitee From 6cbe2209475a785c3b077301c82be7b2888e2b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chttpxiaobocom=E2=80=9D?= <731653765@qq.com> Date: Mon, 6 Sep 2021 20:56:02 +0800 Subject: [PATCH 6/6] fix: change the disableClazz to disableClass --- devui/slider/src/slider.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx index 3db77852..46bc6d70 100644 --- a/devui/slider/src/slider.tsx +++ b/devui/slider/src/slider.tsx @@ -15,6 +15,7 @@ export default defineComponent({ let startPosition = 0; let startX = 0; + const sliderRunway = ref(null); const inputValue = ref(props.modelValue); const currentPosition = ref(0); const newPostion = ref(0); @@ -35,7 +36,7 @@ export default defineComponent({ //一挂载就进行当前位置的计算,以后的移动基于当前的位置移动 onMounted(() => { - const sliderWidth = document.querySelector('.devui-slider__runway').clientWidth; + const sliderWidth = sliderRunway.value.clientWidth; currentPosition.value = (sliderWidth * (inputValue.value - props.min)) / (props.max - props.min); }); @@ -86,8 +87,7 @@ export default defineComponent({ } function setPostion(newPosition: number) { //获取slider的实际长度的像素 - const sliderWidth: number = Math.round(document.querySelector('.devui-slider__runway').clientWidth); - + const sliderWidth: number = Math.round(sliderRunway.value.clientWidth); if (newPosition < 0) { newPosition = 0; } @@ -146,17 +146,17 @@ export default defineComponent({ } } //添加disabled类 - const disableClzz = computed(() => { + const disableClass = computed(() => { return props.disabled ? ' disabled' : ''; }); return () => (
{/* 整个的长度 */} -
+
{/* 滑动后左边的进度条 */} -
+
-- Gitee