From d3fd27eb69b20e3817077b4a8cf0702781eef99a 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/3] =?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 | 177 +++++++++++++++++++++++++++++++ sites/components/slider/index.md | 31 ++++++ 5 files changed, 332 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..7c96ca63 --- /dev/null +++ b/devui/slider/src/slider.tsx @@ -0,0 +1,177 @@ +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){ + newPosition=sliderWidth + } + //计算slider的实际像素每段的长度 + const LengthPerStep=sliderWidth/((props.max-props.min)/props.step) + //计算实际位移的取整段数 + const steps=Math.round(newPosition/LengthPerStep) + //实际的偏移像素 + + let value:number=steps*LengthPerStep + //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 + if(value>sliderWidth){ + value=sliderWidth + } + //这个是向左偏移百分比的值 + percentDispaly.value= Math.round(value*100/sliderWidth)+'%' + //更新输入框的值 + + inputValue.value=Math.round(value*(props.max-props.min)/sliderWidth)+props.min + //设置当前所在的位置 + currentPosition.value=value; + } + + + 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{ + 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..2501e60d --- /dev/null +++ b/sites/components/slider/index.md @@ -0,0 +1,31 @@ +# Slider滑动输入条 + +滑动输入条 + +### 何时使用 +当用户需要再数值区间内进行选择时使用 + +### 基本用法 +
+ +
+ +
+ +
+ +```html + + + + + +``` + + + + + + + + -- Gitee From 7063e4849bc6fd33c7eec0a3260136ddce873835 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 17:50:11 +0800 Subject: [PATCH 2/3] =?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/src/slider.tsx | 39 +++++++++++++++++++++++++------- sites/components/slider/index.md | 8 ++++++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx index 7c96ca63..054ad37c 100644 --- a/devui/slider/src/slider.tsx +++ b/devui/slider/src/slider.tsx @@ -57,6 +57,7 @@ export default defineComponent({ //把当前值给startPosition,以便后面再重新拖拽时,会以当前的位置计算偏移 startPosition=currentPosition.value newPostion.value=startPosition + } @@ -72,6 +73,8 @@ export default defineComponent({ currentX=event.clientX; + + pxOffset=currentX-startX; //移动的x方向上的偏移+初始位置等于新位置 newPostion.value=startPosition+pxOffset; @@ -95,27 +98,41 @@ export default defineComponent({ if(newPosition<0){ newPosition=0; - }else if(newPosition>sliderWidth){ - newPosition=sliderWidth + }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) + + //实际的偏移像素 - let value:number=steps*LengthPerStep - //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 - if(value>sliderWidth){ - value=sliderWidth - } + 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; + currentPosition.value=value; + + //比如props.max为50 setp等于3 滑到48时,再滑动不能到51 + } @@ -139,6 +156,12 @@ export default defineComponent({ 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}`)){ diff --git a/sites/components/slider/index.md b/sites/components/slider/index.md index 2501e60d..202f05c1 100644 --- a/sites/components/slider/index.md +++ b/sites/components/slider/index.md @@ -3,14 +3,20 @@ 滑动输入条 ### 何时使用 -当用户需要再数值区间内进行选择时使用 +当用户需要在数值区间内进行选择时使用 ### 基本用法

+ +### 带有输入框的滑动组件 +

+ +### 可设置Step的滑动组件 +

-- Gitee From 96a834b85ea11e9430cde3acf3861dd7e2c5d6ca Mon Sep 17 00:00:00 2001 From: Bob <731653765@qq.com> Date: Wed, 25 Aug 2021 11:11:20 +0000 Subject: [PATCH 3/3] update devui/slider/src/slider.tsx. --- devui/slider/src/slider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devui/slider/src/slider.tsx b/devui/slider/src/slider.tsx index 054ad37c..a65b4db2 100644 --- a/devui/slider/src/slider.tsx +++ b/devui/slider/src/slider.tsx @@ -103,7 +103,7 @@ export default defineComponent({ //当到达类似98%时,进行边界判定,设置值为100% currentPosition.value=sliderWidth - inputValue.value=100 + inputValue.value=props.max percentDispaly.value= '100%' return -- Gitee