From a63236efbae63cd0d710a5b8f41591a965544cf2 Mon Sep 17 00:00:00 2001 From: Tevin <tingquanren@163.com> Date: Wed, 21 Apr 2021 09:49:59 +0800 Subject: [PATCH] 实现数值阀组件 --- forms/numberValve/CNumberValve.vue | 184 ++++++++++++++++++++++++++++++ forms/numberValve/index.js | 10 + forms/numberValve/cNumberValve.scss | 142 +++++++++++++++++++++++ 3 files changed, 336 insertions(+), 0 deletions(-) diff --git a/forms/numberValve/CNumberValve.vue b/forms/numberValve/CNumberValve.vue new file mode 100644 index 0000000..e19731e --- /dev/null +++ b/forms/numberValve/CNumberValve.vue @@ -0,0 +1,184 @@ +/** + * CNumberValve + * @author Tevin + */ + +<template> + <view class="c-number-valve"> + <view + class="c-number-input" + @tap="evt => handleOpen()" + > + <AtInput + ref="input" + :name="itemRes.name" + :title="itemRes.label" + :required="itemRes.required" + :disabled="itemRes.disabled" + :error="itemRes.error" + :value="itemRes.formData[itemRes.name]" + > + <view class="at-icon at-icon-chevron-right"></view> + </AtInput> + </view> + <AtRange + :min="range[0]" + :max="range[1]" + :value="[0, itemRes.formData[itemRes.name]]" + :onChange="evt => evt && itemRes.onChange(evt[1])" + /> + <AtFloatLayout + :isOpened="opened" + :onClose="evt => handleClose()" + > + <view class="c-number-valve-slider"> + <view + class="container" + ref="slider" + > + <view class="min"><text class="text">{{range[0]}}</text></view> + <view class="max"><text class="text">{{range[1]}}</text></view> + <view class="rail"></view> + <view + class="track" + :style="{width:sliderLeft + '%'}" + ></view> + <view + class="slider" + :style="{left:sliderLeft + '%'}" + > + <view + class="circle" + @touchstart="evt => handleTouchStart()" + @touchmove="evt => handleTouchMove(evt)" + ></view> + <view class="current"><text class="text">{{current}}</text></view> + </view> + </view> + <view class="btn"> + <AtButton + size="small" + :onClick="evt => handleChangeVal('left')" + >< 减小</AtButton> + <AtButton + size="small" + :onClick="evt => handleChangeVal('right')" + >增加 ></AtButton> + </view> + <view class="tips">请<text class="bold"> 拖动滑块 </text>或<text class="bold"> 点击按钮 </text>改变数值</view> + </view> + </AtFloatLayout> + </view> +</template> + +<script> +import Taro from '@tarojs/taro'; +import { $ } from '@tarojs/extend'; +import { AtInput, AtFloatLayout, AtButton, AtRange } from 'taro-ui-vue'; +import './cNumberValve.scss'; + +export default { + name: 'CNumberValve', + components: { + AtInput, + AtFloatLayout, + AtButton, + AtRange, + }, + props: { + // 表单数据资源(表单组件内部机制专用) + itemRes: Object, + // 占位提示 + placeholder: String, + // 取值范围 + range: { + type: Array, + default: [0, 100], + }, + // 步长 + step: { + type: Number, + default: 1, + }, + }, + data() { + return { + opened: false, + rect: { width: 0, left: 0 }, + current: 0, + sliderLeft: 0, + }; + }, + computed: {}, + methods: { + handleOpen() { + this.opened = true; + setTimeout(() => { + this.updateRect(); + }, 100); + }, + handleClose() { + this.opened = false; + }, + handleTouchStart() { + this.updateRect(); + }, + handleTouchMove(event) { + if (this.disabled) { + return; + } + event.stopPropagation(); + const clientX = event.touches[0].clientX; + const targetValue = clientX - this.rect.left; + const distance = Math.min(Math.max(targetValue, 0), this.rect.width); + const sliderLeft = Math.floor((distance / this.rect.width) * 100); + const sliderValue = + Math.round((sliderLeft / 100) * (this.range[1] - this.range[0])) + + this.range[0]; + this.sliderLeft = sliderLeft; + this.current = sliderValue; + this.itemRes.onChange(sliderValue); + }, + updateRect() { + let $slider = null; + if (process.env.TARO_ENV === 'h5') { + $slider = $(this.$refs.slider.$el); + } else if (process.env.TARO_ENV === 'weapp') { + $slider = $(this.$refs.slider); + } + Taro.createSelectorQuery() + .select('#' + this.$refs.slider.uid) + .boundingClientRect(rect => { + this.rect.width = rect.width; + this.rect.left = rect.left; + }) + .exec(); + }, + handleChangeVal(type, value) { + this.updateRect(); + let currentNext = 0; + if (type === 'left') { + currentNext = this.current - this.step; + } else if (type === 'right') { + currentNext = this.current + this.step; + } else if (type === 'change') { + currentNext = value; + } + const sliderLeft = Math.round( + ((currentNext - this.range[0]) / (this.range[1] - this.range[0])) * 100 + ); + this.sliderLeft = sliderLeft; + this.current = currentNext; + this.itemRes.onChange(currentNext); + }, + }, + mounted() { + const itemName = this.itemRes.name; + this.$watch('itemRes.formData.' + itemName, value => { + if (value !== this.current) { + this.handleChangeVal('change', value); + } + }); + }, +}; +</script> \ No newline at end of file diff --git a/forms/numberValve/cNumberValve.scss b/forms/numberValve/cNumberValve.scss new file mode 100644 index 0000000..82afead --- /dev/null +++ b/forms/numberValve/cNumberValve.scss @@ -0,0 +1,142 @@ +/** + * number-valve + * @author Tevin + */ + +@import "../../common/sassMixin"; + + +.c-number-valve { + .at-input__input { + .weui-input { + pointer-events: none; + } + } + .at-input__children { + &::after { + display: none; + } + .at-icon-chevron-right { + font-size: 48px; + color: #ccc; + } + } + .at-input__icon { + display: none; + } + .at-input__container { + input { + pointer-events: none; + } + } + .at-float-layout { + padding: 0; + .at-float-layout__container { + height: 450px; + max-height: 100%; + min-height: auto; + } + .layout-body { + height: 100%; + min-height: auto; + } + .layout-body__content { + height: 100%; + min-height: auto; + } + } + .c-number-valve-slider { + height: 100%; + padding: 40px 25PX; + box-sizing: border-box; + .container { + position: relative; + display: flex; + align-items: center; + width: 100%; + height: 28PX; + padding: 35PX 0 50PX; + .min { + @include position(absolute, 20PX 0 n n); + height: 60PX; + border-left: #eee 1PX dashed; + .text { + @include position(absolute, 64PX 0 n n); + transform: translateX(-50%); + } + } + .max { + @include position(absolute, 20PX n n 0); + height: 60PX; + border-right: #eee 1PX dashed; + .text { + @include position(absolute, 64PX n n 0); + transform: translateX(50%); + } + + } + .rail { + width: 100%; + height: 2PX; + background-color: #e2e2e2; + box-sizing: border-box; + overflow: hidden; + } + .track { + position: absolute; + height: 2PX; + background-color: #2093df; + } + .slider { + position: absolute; + top: 35PX; + left: 0; + box-sizing: border-box; + .circle { + position: relative; + z-index: 2; + width: 28PX; + height: 28PX; + margin-left: -14PX; + border-radius: 50%; + background-color: #2093df; + box-shadow: 0 0 4PX 0 rgba(0, 0, 0, 0.2); + &::after { + display: block; + @include position(absolute, -50% -50%); + width: 200%; + height: 200%; + background-color: rgba(#000, 0); + content: " "; + } + } + .current { + @include position(absolute, n 0 0 n, 1); + height: 46PX; + border-left: #eee 1PX solid; + .text { + @include position(absolute, -24PX 0 n n); + transform: translateX(-50%); + } + } + } + } + .btn { + @include flexbox(flex, space-between center); + width: calc(100% + 40PX); + margin-left: -20PX; + .at-button { + margin: 0; + } + } + .tips { + @include position(absolute, n 0 10px n); + width: 100%; + text-align: center; + color: #999; + .bold { + font-weight: bold; + } + } + } +} \ No newline at end of file diff --git a/forms/numberValve/index.js b/forms/numberValve/index.js new file mode 100644 index 0000000..e833185 --- /dev/null +++ b/forms/numberValve/index.js @@ -0,0 +1,10 @@ +/** + * CNumberValve + * @author Tevin + */ + +import CNumberValve from '@components/forms/numberValve/CNumberValve.vue'; + +export { + CNumberValve, +} \ No newline at end of file -- Gitblit v1.9.1