Vue2移动端(H5项目)项目封装switch组件支持动态设置开启关闭背景色、值及组件内显示文字描述、禁用、switch 的宽度

前言

近期产品需求:Vue2移动端项目需要在switch开关内显示文字,看Vantui没有对应功能,因此自己手撸写了这个组件。

一、最终效果

二、参数配置

1、代码示例:

html 复制代码
 <t-switch v-model="check"/>

2、配置参数(t-switch Attributes)

参数 说明 类型 默认值
v-model 绑定值 boolean / string / number ---
disabled 是否禁用 boolean false
width switch 的宽度(像素) number 55
active-icon-class switch 打开时所显示图标的类名,设置此项会忽略 active-text string ---
inactive-icon-class switch 关闭时所显示图标的类名,设置此项会忽略 inactive-text string ---
active-text switch 打开时的文字描述 string ---
inactive-text switch 关闭时的文字描述 string ---
active-value switch 打开时的值 boolean / string / number true
inactive-value switch 关闭时的值 boolean / string / number false
active-color switch 打开时的背景色 string #2b73bb
inactive-color switch 关闭时的背景色 string #ccc
name switch 对应的 name 属性 string ---

3、events 事件

事件名称 说明 回调参数
change switch 状态发生变化时的回调函数 新状态的值

三、源码

html 复制代码
<template>
  <div
    class="t-switch"
    :class="{ 'is-disabled': switchDisabled, 'is-checked': checked }"
    role="switch"
    :aria-checked="checked"
    :aria-disabled="switchDisabled"
    @click.prevent="switchValue"
  >
    <input
      class="t-switch__input"
      type="checkbox"
      @change="handleChange"
      ref="input"
      :id="id"
      :name="name"
      :true-value="activeValue"
      :false-value="inactiveValue"
      :disabled="switchDisabled"
      @keydown.enter="switchValue"
    />
    <span
      :class="['t-switch__label', 't-switch__label--left', !checked ? 'is-active' : '']"
      v-if="inactiveIconClass || inactiveText"
    >
      <i :class="[inactiveIconClass]" v-if="inactiveIconClass"></i>
      <span v-if="!inactiveIconClass && inactiveText" :aria-hidden="checked">{{ inactiveText }}</span>
    </span>
    <span class="t-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }"></span>
    <span
      :class="['t-switch__label', 't-switch__label--right', checked ? 'is-active' : '']"
      v-if="activeIconClass || activeText"
    >
      <i :class="[activeIconClass]" v-if="activeIconClass"></i>
      <span v-if="!activeIconClass && activeText" :aria-hidden="!checked">{{ activeText }}</span>
    </span>
  </div>
</template>
<script>

export default {
  name: 'TSwitch',
  props: {
    value: {
      type: [Boolean, String, Number],
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    },
    width: {
      type: Number,
      default: 55
    },
    activeIconClass: {
      type: String,
      default: ''
    },
    inactiveIconClass: {
      type: String,
      default: ''
    },
    activeText: String,
    inactiveText: String,
    activeColor: {
      type: String,
      default: '#2b73bb'
    },
    inactiveColor: {
      type: String,
      default: '#ccc'
    },
    activeValue: {
      type: [Boolean, String, Number],
      default: true
    },
    inactiveValue: {
      type: [Boolean, String, Number],
      default: false
    },
    name: {
      type: String,
      default: ''
    },
    id: String
  },
  data() {
    return {
      coreWidth: this.width
    };
  },
  created() {
    if (!~[this.activeValue, this.inactiveValue].indexOf(this.value)) {
      this.$emit('input', this.inactiveValue);
    }
  },
  computed: {
    checked() {
      return this.value === this.activeValue;
    },
    switchDisabled() {
      return this.disabled
    }
  },
  watch: {
    checked() {
      this.$refs.input.checked = this.checked;
      if (this.activeColor || this.inactiveColor) {
        this.setBackgroundColor();
      }
    }
  },
  methods: {
    handleChange(event) {
      const val = this.checked ? this.inactiveValue : this.activeValue;
      this.$emit('input', val);
      this.$emit('change', val);
      this.$nextTick(() => {
        if (this.$refs.input) {
          this.$refs.input.checked = this.checked;
        }
      });
    },
    setBackgroundColor() {
      let newColor = this.checked ? this.activeColor : this.inactiveColor;
      this.$refs.core.style.borderColor = newColor;
      this.$refs.core.style.backgroundColor = newColor;
    },
    switchValue() {
      !this.switchDisabled && this.handleChange();
    }
  },
  mounted() {
    /* istanbul ignore if */
    this.coreWidth = this.width || 40;
    if (this.activeColor || this.inactiveColor) {
      this.setBackgroundColor();
    }
    this.$refs.input.checked = this.checked;
    this.$refs['input'].focus()
  }
};
</script>
<style lang="scss" scoped>
.t-switch {
  display: -webkit-inline-box;
  display: -ms-inline-flexbox;
  display: inline-flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  position: relative;
  font-size: 14px;
  height: 22px;
  vertical-align: middle;
  &.is-disabled {
    opacity: 0.6;
    .t-switch__core,
    .t-switch__label {
      cursor: not-allowed;
    }
  }
  &.is-checked {
    .t-switch__core {
      &::after {
        left: 100%;
        margin-left: -19px;
      }
    }
  }
  .label-fade-enter,
  .label-fade-leave-active {
    opacity: 0;
  }
  .t-switch__label {
    margin: 0;
    position: absolute;
    display: none;
    color: #fff;
    &.is-active {
      display: inline-block;
      color: #fff;
    }
    * {
      line-height: 1;
      font-size: 14px;
      display: inline-block;
    }
  }
  .t-switch__label--left {
    right: 5px;
    z-index: 9;
  }
  .t-switch__label--right {
    left: 5px;
    z-index: 9;
  }
  .t-switch__input {
    position: absolute;
    width: 0;
    height: 0;
    opacity: 0;
    margin: 0;
  }
  .t-switch__core {
    margin: 0;
    position: relative;
    width: 40px;
    height: 22px;
    border: 1px solid #dcdfe6;
    outline: 0;
    border-radius: 10px;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
    background: #dcdfe6;
    -webkit-transition: border-color 0.3s, background-color 0.3s;
    transition: border-color 0.3s, background-color 0.3s;
    &:after {
      content: "";
      position: absolute;
      top: 1px;
      left: 1px;
      border-radius: 100%;
      -webkit-transition: all 0.3s;
      transition: all 0.3s;
      width: 18px;
      height: 18px;
      background-color: #fff;
    }
  }
}
</style>

相关文章

基于ElementUi再次封装基础组件文档


基于ant-design-vue再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档

相关推荐
ss27343 分钟前
基于Springboot + vue实现的小型养老院管理系统
vue.js·spring boot·后端
忆琳2 小时前
拖动上传组件内部自定义组件,保留拖动上传
vue.js
screct_demo4 小时前
详细讲一下Vue的路由Vue Router的安装,配置,基础用法和详细用法以及实践中应用
前端·javascript·vue.js
林涧泣4 小时前
【Uniapp-Vue3】使用ref定义响应式数据变量
前端·vue.js·uni-app
He guolin5 小时前
[Vue]的快速上手
前端·javascript·vue.js
CodeChampion5 小时前
68.基于SpringBoot + Vue实现的前后端分离-心灵治愈交流平台系统(项目 + 论文PPT)
java·vue.js·spring boot·mysql·elementui·node.js·idea
小破孩呦6 小时前
Vue3中使用 Vue Flow 流程图方法
前端·vue.js·流程图
zlting~6 小时前
【VUE】a链接下载跨域文件直接打开而非下载(解决办法)
前端·javascript·vue.js
计算机学姐7 小时前
基于SpringBoot的斯诺克球馆预约购票管理系统
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
nyf_unknown7 小时前
(vue)给循环遍历的el-select选择框加全选/清空/反选,禁选,添加行移除行功能
前端·javascript·vue.js