All files / src/components/u-combo-slider.vue index.vue

61.9% Statements 13/21
12.5% Branches 2/16
20% Functions 1/5
65% Lines 13/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217      7x   7x     7x                                                                                                                                                                                                                                                                                                                                                                                                                                
<template>
<div :class="$style.root">
    <div :class="$style.main" :unit="!!unit" :multiple="multiple">
         <!-- @override: 增加提示 tip -->
        <div :class="$style.tip" vusion-slot-name="tip" vusion-slot-name-edit="tip">
            <slot name="tip">
                {{ tip }}
                <s-empty
                    v-if="(!$slots.tip)
                        && !tip
                        && $env.VUE_APP_DESIGNER
                        && !!$attrs['vusion-node-path']">
                </s-empty>
            </slot>
        </div>
        <u-slider :class="$style.slider" @mousedown.native="onMousedown" :value="currentValue" @input="onInput" @slide="onSlide"
            :min="min" :max="max" :step="step" :precision="precision" :range="range" :readonly="readonly" :disabled="disabled"
            :show-tooltip="showTooltip" :tooltip="tooltip" :placement="placement" :multiple="multiple"
        ></u-slider>
         <!-- @override: 增加数值显示 -->
        <div :class="$style.scales">
            <span :class="$style.scale">{{ min }}{{ unit }}</span>
            <span :class="$style.scale">{{ ((min - 0) + (max - 0)) / 2 >> 0 }}{{ unit }}</span>
            <span :class="$style.scale">{{ max }}{{ unit }}</span>
        </div>
    </div>
    <u-number-input :class="$style.input" :value="currentValue" @change="onInput($event.value, 'numberInput')" @validate="onValidate"
        :min="numberMin" :max="numberMax" :step="step || 1" :precision="precision"
        :formatter="formatter" :hide-buttons="hideButtons"
        :readonly="readonly" :disabled="disabled" v-if="!multiple"
    ></u-number-input>
    <slot>
        <!-- @override: 增加单位显示 -->
        <span>{{ unit }}</span>
    </slot>
</div>
</template>
 
<script>
import MField from '../m-field.vue';
import SEmpty from '../s-empty.vue';
 
export default {
    name: 'u-combo-slider',
    components: { SEmpty },
    mixins: [MField],
    props: {
        value: { type: [Number, Array], default: 0 },
        min: { type: Number, default: 0 },
        max: { type: Number, default: 100 },
        step: { type: Number, default: 1, validator: (step) => step >= 0 },
        precision: {
            type: Number,
            default: 1,
            validator: (precision) => precision > 0,
        },
        range: {
            type: Array,
            default() {
                return [];
            },
        },
        formatter: { type: [String, Object] },
        syncOn: { type: String, default: 'input' },
        hideButtons: { type: Boolean, default: true },
        unit: String,
        tip: String,
        readonly: { type: Boolean, default: false },
        disabled: { type: Boolean, default: false },
        showTooltip: { type: Boolean, default: false },
        tooltip: String,
        placement: { type: String, default: 'top' },
        layout: { type: String, default: 'flex' },
        multiple: { type: Boolean, default: false },
    },
    data() {
        return { currentValue: this.value, isMousedown: false };
    },
    computed: {
        numberMin() {
            return Math.max(
                this.min,
                this.range[0] === undefined ? -Infinity : this.range[0],
            );
        },
        numberMax() {
            return Math.min(
                this.max,
                this.range[1] === undefined ? Infinity : this.range[1],
            );
        },
    },
    watch: {
        value(value) {
            this.currentValue = value;
        },
        currentValue(currentValue, oldValue) {
            this.$emit('change', { value: currentValue, oldValue }, this);
        },
    },
    created() {
        document.body.addEventListener('mouseup', this.onMouseup, false);
    },
    destroyed() {
        document.body.removeEventListener('mouseup', this.onMouseup, false);
    },
    methods: {
        onValidate($event) {
            if (this.syncOn === 'blur')
                return;
            const value = $event.value; // 最小值的情况不同步,不然会显得很怪异
            if (!$event.valid && value === this.min)
                return;
            this.currentValue = value;
            this.$emit('input', value, this);
            this.$emit('update:value', value, this);
        },
        onInput(value, type) {
            this.currentValue = value;
            this.$emit('input', value, this);
            this.$emit('update:value', value, this);
            if (type) {
                // 区分onInput事件的来源 满足特定需求
                this.$emit('number-input', value);
            }
        },
        onSlide($event) {
            this.$emit('slide', $event, this);
        },
        onMousedown(e) {
            this.isMousedown = true;
        },
        onMouseup(e) {
            if (this.isMousedown) {
                this.$emit('slide-end', this.currentValue);
                this.isMousedown = false;
            }
        },
    },
};
</script>
 
<style module>
.root {
  text-align: center;
  display: flex;
  align-items: baseline;
}
 
.slider {
    display: inline-block;
    width: var(--combo-slider-slider-width);
}
 
.input {
    margin-left: var(--combo-slider-input-margin-left);
    width: var(--combo-slider-input-width) !important;
    margin-right: 0.5em;
    padding: 0 4px;
    height: 32px;
    line-height: 32px;
    text-align: center;
    background-color: var(--combo-slider-input-background) !important;;
    border-color: var(--combo-slider-input-border-color) !important;;
    border-radius: var(--combo-slider-input-border-radius) !important;;
    color: var(--combo-slider-input-color) !important;;
}
 
.main {
    display: inline-block;
    width: calc(100% - var(--combo-slider-input-width) - 20px);
    vertical-align: -20px;
}
.main[unit="true"] {
    width: calc(100% - var(--combo-slider-input-width) - 20px - var(--combo-slider-input-margin-left) - 0.5em);
}
 
.main[multiple] {
    width: 100%;
}
 
.body {
    margin: 0 7px;
}
 
.tip {
    float: left;
    margin-left: 7px;
    margin-bottom: 4px;
    font-size: 12px;
    color: var(--combo-slider-tip-color);
    display: flex;
    flex-wrap: wrap;
    align-items: var(--combo-slider-tip-align-items);
}
 
.scales {
    margin: 0 7px;
    font-size: 12px;
    color: var(--combo-slider-scales-color);
}
 
.scale {
    display: inline-block;
    width: 33.333333%;
    text-align: center;
}
 
.scale:first-child {
    text-align: left;
}
 
.scale:last-child {
    text-align: right;
}
</style>