WebApp【公共组件库】@前端(For Git Submodule)
Tevin
2021-04-21 a63236efbae63cd0d710a5b8f41591a965544cf2
实现数值阀组件
3 files added
336 ■■■■■ changed files
forms/numberValve/CNumberValve.vue 184 ●●●●● patch | view | raw | blame | history
forms/numberValve/cNumberValve.scss 142 ●●●●● patch | view | raw | blame | history
forms/numberValve/index.js 10 ●●●●● patch | view | raw | blame | history
forms/numberValve/CNumberValve.vue
New file
@@ -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')"
                    >&#60; 减小</AtButton>
                    <AtButton
                        size="small"
                        :onClick="evt => handleChangeVal('right')"
                    >增加 &#62;</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>
forms/numberValve/cNumberValve.scss
New file
@@ -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;
            }
        }
    }
}
forms/numberValve/index.js
New file
@@ -0,0 +1,10 @@
/**
 * CNumberValve
 * @author Tevin
 */
import CNumberValve from '@components/forms/numberValve/CNumberValve.vue';
export {
    CNumberValve,
}