element-ui的<el-slider />实现多段范围取值

<template>
  <my-range v-model="content" />
</template>

<script>
import myRange from 'myRange'
export default {
  data: () => ({
    content:''
  })
}
</script>

封装页面.vue

<template>
  <div class="my-range-slider">
    <div class="mrs-runway" @dblclick.stop="onAddPoint">
      <div
        v-for="(u, x) in info"
        :key="u.code"
        class="mrs-li"
        :style="{ width: u.percent, left: u.startPos }"
        @dblclick.stop
      >
        <el-tooltip ref="T1" placement="top" :content="u.startValue">
          <div
            class="mrs-btn l"
            @dblclick="onDeletePoint(x, true)"
            @mousedown.stop="onDragStart($event, x, 'T1')"
          />
        </el-tooltip>
        <div class="mrs-bar" @mousedown.stop="onDragStart($event, x)" />
        <el-tooltip ref="T2" placement="top" :content="u.endValue">
          <div
            class="mrs-btn r"
            @mousedown.stop="onDragStart($event, x, 'T2')"
            @dblclick="onDeletePoint(x)" />
        </el-tooltip>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  model: {
    prop: 'value',
    event: 'input'
  },
  props: {
    // 4~118;130;162~375
    value: { type: String, required: true },
    min: { type: Number, default: 0 },
    max: { type: Number, default: 300 },
    rangeSign: { type: String, default: '~' }, // 范围标识
    cutSign: { type: String, default: ';|;' } // 段分割标识
  },
  data: () => ({
    isDraging: false,
    info: []
  }),
  watch: {
    value: {
      immediate: true,
      handler(n) {
        try {
          const str = (n || '').replace(/\s/g, '');
          const array = str ? str.split(new RegExp(this.cutSign)) : ['0'];
          const reg = new RegExp(this.rangeSign);
          this.info = array.map((u) => {
            const [v1, v2] = reg.test(u) ? u.split(reg) : [u, u];
            if (isNaN(v1)) {
              const errorsign = u.replace(/\d+|;/g, '').substr(0, 1);
              const obj = { message: `当前使用分隔符的是${errorsign}, 请属性rangeSign加上="${errorsign}"` };
              throw obj;
            }
            // 防止值超过范围
            const v3 = Math.max(+v1, this.min);
            const v4 = Math.min(typeof v2 === 'undefined' ? +v1 : +v2, this.max);
            // 防止 输入大到小
            const v5 = Math.min(v3, v4);
            const v6 = Math.max(v3, v4);
            return {
              code: v5 + '==' + v6,
              percent: this.onValueToPercent(v6 - v5),
              startPos: this.onValueToPercent(v5 - this.min),
              startValue: String(v5),
              endValue: String(v6)
            };
          });
          this.$nextTick(this.onEmit);
        } catch (e) {
          new Error(e.message);
        }
      }
    }
  },
  methods: {
    // 获取移动后的 百分比
    onValueToPercent(v) {
      const maxValue = this.max - this.min;
      return `${(((v) / maxValue) * 100).toFixed(2)}%`;
    },
    // 获取移动后的 值
    onMoveValue(initValue, pageX) {
      const maxValue = this.max - this.min;
      let v = Math.max(this.min, initValue + (maxValue * pageX) / 100);
      v = Math.min(this.max, v);
      return String(+v.toFixed(0));
    },
    // 鼠标按下
    onDragStart(e, initIndex, initTRef) {
      this.isDraging = false;
      window.addEventListener('mousemove', this.onDragging);
      window.addEventListener('mouseup', this.onDragEnd);
      window.addEventListener('mouseleave', this.onDragEnd);
      const row = this.info[initIndex];
      const initBarLeftDistance = this.onOffsetLeft(e.target) - this.onOffsetLeft(this.$el);
      this.downStart = {
        initIndex,
        initPercent: +row.percent.replace('%', ''),
        initStartPos: +row.startPos.replace('%', ''),
        initStartValue: +row.startValue,
        initEndtValue: +row.endValue,
        initPageX: e.pageX,
        initTRef, // tooltip的ref
        initIsLeft: initTRef === 'T1', // 左边按钮
        initIsRight: initTRef === 'T2', // 右边按钮
        initIsCenter: !initTRef, // 中间的bar
        // 移动bar时 限制 左边可移动
        initBarLeftDistance,
        // 移动bar时 限制 右边可移动
        initBarRightDistance: this.$el.offsetWidth - initBarLeftDistance - e.target.offsetWidth
      };
    },
    // 鼠标移动
    onDragging(e) {
      this.isDraging = true;
      const { initIndex, initPercent, initStartPos, initStartValue, initEndtValue, initPageX, initTRef, initIsLeft, initIsRight, initBarLeftDistance, initBarRightDistance } = this.downStart;
      // pos当前移动百分比  不是像素
      let pos = +((e.pageX - initPageX) / this.$el.offsetWidth * 100).toFixed(2);
      const row = this.info[initIndex];
      if (initIsLeft) { // 左边按钮操作
        if (pos <= initPercent) { // 不超过当前段的右边
          pos = initStartPos + pos <= 0 ? -initStartPos : pos;
          this.$set(row, 'percent', `${initPercent - pos}%`);
          this.$set(row, 'startPos', `${initStartPos + pos}%`);
          this.$set(row, 'startValue', this.onMoveValue(initStartValue, pos));
        } else {
          pos = initStartPos + pos >= 100 ? 100 - initStartPos : pos;
          this.$set(row, 'percent', `${pos - initPercent}%`);
          this.$set(row, 'startPos', `${initStartPos + initPercent}%`);
          this.$set(row, 'startValue', String(initEndtValue));
          this.$set(row, 'endValue', this.onMoveValue(initEndtValue, pos - initPercent));
        }
      } else if (initIsRight) { // 右边按钮操作
        if (-pos <= initPercent) { // 不超过当前段的左边
          pos = initStartPos + pos + initPercent >= 100 ? 100 - initStartPos - initPercent : pos;
          this.$set(row, 'percent', `${initPercent + pos}%`);
          this.$set(row, 'endValue', this.onMoveValue(initEndtValue, pos));
        } else {
          pos = initStartPos + pos + initPercent <= 0 ? -initStartPos - initPercent : pos;
          this.$set(row, 'percent', `${-pos - initPercent}%`);
          this.$set(row, 'startPos', `${initStartPos - (-pos - initPercent)}%`);
          this.$set(row, 'startValue', this.onMoveValue(initStartValue, initPercent + pos));
          this.$set(row, 'endValue', String(initStartValue));
        }
      } else { // 中间bar操作
        let newPageX = Math.max(-initBarLeftDistance, e.pageX - initPageX);
        newPageX = Math.min(initBarRightDistance, newPageX);
        pos = +(newPageX / this.$el.offsetWidth * 100).toFixed(2);
        this.$set(row, 'startPos', `${initStartPos + pos}%`);
        this.$set(row, 'startValue', this.onMoveValue(initStartValue, pos));
        this.$set(row, 'endValue', this.onMoveValue(initEndtValue, pos));
      }
      const TRefdom = this.$refs[initTRef];
      initTRef && TRefdom && TRefdom.forEach(u => u.updatePopper());
    },
    // 鼠标按起
    onDragEnd() {
      window.removeEventListener('mousemove', this.onDragging);
      window.removeEventListener('mouseup', this.onDragEnd);
      window.removeEventListener('mouseleave', this.onDragEnd);
      if (this.isDraging) {
        this.info.map((u) => {
          u.code = u.startValue + '==' + u.endValue;
          return u;
        });
        this.$nextTick(this.onEmit);
      }
      this.isDraging = false;
    },
    // 增加点
    onAddPoint(e) {
      window.removeEventListener('mousemove', this.onDragging);
      window.removeEventListener('mouseup', this.onDragEnd);
      window.removeEventListener('mouseleave', this.onDragEnd);
      const outerWidth = this.$el.offsetWidth;
      const v = Math.floor(((e.pageX - this.onOffsetLeft(this.$el)) / outerWidth) * (this.max - this.min)) + this.min;
      this.info.push({
        code: v + '==' + v,
        percent: '0%',
        startPos: this.onValueToPercent(v),
        startValue: String(v),
        endValue: String(v)
      });
      this.$nextTick(this.onEmit);
    },
    // 删除点
    onDeletePoint(index, isLeft) {
      const row = this.info[index];
      const { startValue: S, endValue: E, startPos, percent } = row;
      if (+S !== +E) {
        this.$set(row, 'code', !isLeft ? S + '==' + S : E + '==' + E);
        this.$set(row, 'percent', '0%');
        this.$set(row, 'startPos', !isLeft ? startPos : `${+startPos.replace('%', '') + +percent.replace('%', '')}%`);
        this.$set(row, 'startValue', !isLeft ? S : E);
        this.$set(row, 'endValue', !isLeft ? S : E);
      } else {
        this.info.splice(index, 1); // ranges是一个点 删除
      }
      this.$nextTick(this.onEmit);
    },
    // 提交更新组件
    onEmit() {
      let currentMax = [-Infinity, -Infinity];
      const result = [];
      const array = this.info.sort((a, b) =>
        +a.startValue > +b.startValue ? 1 : -1
      );
      array.forEach((u) => {
        const [v1, v2] = [+u.startValue, +u.endValue];
        const M = currentMax[1];
        const splitRangeSign = this.rangeSign.substr(0, 1);
        if (M < v1) {
          result.push(v1 === v2 ? v1.toString() : v1 + splitRangeSign + v2);
          currentMax = [v1, v2];
        } else if (M >= v1 && M < v2) {
          result[result.length - 1] = currentMax[0] + splitRangeSign + v2;
          currentMax = [+currentMax[0], v2];
        } else {
          //
        }
      });
      const splitCutSign = this.cutSign.substr(0, 1);
      const string = result.join(splitCutSign);
      this.$emit('input', string);
      this.$emit('change', string);
    },
    // 距离最左边的距离
    onOffsetLeft(dom, distance) {
      distance = distance || 0;
      if (['BODY', null].includes(dom.offsetParent.nodeName)) {
        distance += dom.offsetLeft;
        return distance;
      } else {
        distance += dom.offsetLeft;
        return this.onOffsetLeft(dom.offsetParent, distance);
      }
    }
  }
};
</script>

<style lang="less">
@gary: #e4e7ed;
@blue: #409eff;
@height: 6px;
.my-range-slider {
  position: relative;
  padding: 7px 0;
  margin-bottom: 100px;
  user-select: none;
  .mrs-runway {
    height: @height;
    background-color: @gary;
    border-radius: 3px;
    cursor: pointer;
  }
  .mrs-li {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    height: 100%;
    width: 100%;
  }
  .mrs-bar {
    height: @height;
    background-color: @blue;
    position: absolute;
    border-radius: 3px;
    top: 7px;
    left: 0;
    right: 0;
  }
  .mrs-btn {
    position: absolute;
    top: 0;
    width: 15px;
    height: 15px;
    border: 2px solid @blue;
    background-color: #fff;
    border-radius: 15px;
    transition: 0.2s;
    transition: transform 0.3s;
    z-index: 5;
    &.l {
      left: 0;
      margin-left: -10px;
    }
    &.r {
      right: 0;
      margin-right: -10px;
    }

    &:hover {
      transform: scale(1.2);
    }
  }
}
</style>

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
<?xml version='1.0' encoding='utf-8'?> <args version="0.0.1"> <page name="离线设备"> <editable>True</editable> <step>2</step> <name>刷新间隔(ms):</name> <scope> <max>500</max> <min>1</min> </scope> <type>int</type> <value>3</value> <row>0</row> <column>1</column> <object>renovate</object> <widget>lab<em>el_spin</widget> </em> <editable>True</editable> <step>4</step> <name>单帧长度:</name> <scope> <max>300</max> <min>1</min> </scope> <type>int</type> <value>2</value> <row>1</row> <column>1</column> <object>Singleframe</object> <widget>lab<em>el_spin</widget> </em> <editable>True</editable> <step>6</step> <name>起始位置:</name> <scope> <max>200</max> <min>1</min> </scope> <type>int</type> <value>1</value> <row>2</row> <column>1</column> <object>position</object> <widget>lab<em>el_spin</widget> </em> </page> <page name="信号分析"> <editable>True</editable> <name>分析算法选择</name> <max></max> <min></min> <scope> <chosen>1,2,3</chosen> </scope> <type>int</type> <value>1</value> <row>1</row> <column>0</column> <rowpage>1</rowpage> <columnpage>3</columnpage> <widget>lab<em>el_combo</widget> </em> <editable>True</editable> <name>mod<em>el</name> <type>str</type> <value>true</value> <row>2</row> <column>0</column> <widget>check</widget> </em> <editable>True</editable> <name>1</name> <type>str</type> <value>false</value> <row>2</row> <column>1</column> <widget>check</widget> <editable>True</editable> <name>2</name> <type>str</type> <value>false</value> <row>2</row> <column>2</column> <widget>check</widget> <editable>True</editable> <row>3</row> <column>0</column> <rowpage>3</rowpage> <columnpage>3</columnpage> <widget>lab<em>el_slider</widget> </em> </page> </args>解读一下这段xml文件
06-09

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值