New file |
| | |
| | | /** |
| | | * 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> |