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')"
+                    >&#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>
\ 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