手寫一個Vue版Checkbox組件

前言

此處省略八百字的前言......
怕內容太多理解須要點時間,我一句話作總結。創做使我快樂。javascript

疫情當前愉快的心情提升了個人免疫力。html

今天分享個人第N個Vue組件,Checkbox

1.組件設計

  • 把組件拆分爲CheckboxCheckboxGroup兩個組件。
  • Checkbox API
參數 說明 類型 可選值 默認值
type 類型 String default errer success warning default
value / v-model 綁定值 Boolean
label 選中狀態的值 String
icon 自定義選中的icon String iconselected
disabled 是否禁用Checkbox Boolean
  • CheckboxGroup API
參數 說明 類型 可選值 默認值
type 類型 String default errer success warning default
value / v-model 綁定值 Array
inline 是否串聯成一行 Boolean true
icon 自定義選中的icon String iconselected
disabled 是否禁用Checkbox Boolean
  • Checkbox CheckboxGroup配合使用時CheckboxGroup設置type icon disabled屬性可控制Checkboxtype icon disabled屬性。

2.實現

1.子組件獲取父組件實例的方法
/utils/index.js

/** * 查找上級是否有存在對應組件 * @param {*} context * @param {*} componentName * @param {*} componentNames */
export const findComponentUpward = (context, componentName, componentNames) => {
    if (typeof componentName === 'string') {
        componentNames = [componentName];
    } else {
        componentNames = componentName;
    }
    let parent = context.$parent;
    let name = parent.$options.name;
    while (parent && (!name || componentNames.indexOf(name) < 0)) {
        parent = parent.$parent;
        if (parent) { name = parent.$options.name; }
    }
    return parent;
};
複製代碼

2.mixins

/mixins/props.js

const type = {
    props: {
        type: {
            type: String,
            default: "default",
            validator: function (value) {
                return ["default", "errer", "success", "warning"].includes(value);
            }
        }
    }
}

module.exports = { type };
複製代碼
3.父組件獲取指定子組件的方法
/utils/index.js

/** * 根據組件名稱查找全部下級對應的組件(特定的層級關係,無法跨級查詢) * @param {*} context * @param {*} componentName */
export const findComponentsDownward = (context, componentName) => {
    const array = [];
    for (let i = 0; i < context.$children.length; i += 1) {
        if (context.$children[i].$options.name === componentName) {
            array.push(context.$children[i]);
        }
    }
    return array;
};
複製代碼
4.Checkbox組件實現

templatevue

<template>
  <label class="g7-Checkbox">
    <span :class="['g7-Checkbox-icon',`g7-text-color-${parentType}`,{['disabled']:parenDisabled}]">
      <transition name="fade">
        <Icon v-show="currentValue" :size="20" :icon="parentIcon" />
      </transition>
    </span>
    <span :class="['g7-Checkbox-text',{['disabled']:parenDisabled}]">
      <slot>{{label}}</slot>
    </span>
    <!-- 依據是否存在CheckboxGroup父組件,而使用不一樣的處理方式 -->
    <input v-if="parent" type="checkbox" :value="label" class="g7-Checkbox-input" @change="change" v-model="model" :disabled="parenDisabled" />
    <input v-else type="checkbox" class="g7-Checkbox-input" :checked="currentValue" @change="change" :disabled="parenDisabled" />
  </label>
</template>
複製代碼

javaScriptjava

<script>
import Icon from "../Icon";  //自定義的組件
import { findComponentUpward } from "../../utils";
import { type } from "../../mixins/props";
export default {
  name: "G-Checkbox",
  components: { Icon },
  mixins: [type],
  data() {
    return {
      parent: "",
      currentValue: this.value,
      model: []
    };
  },
  props: {
    label: {
      type: String
    },
    icon: {
      type: String,
      default: "iconselected"
    },
    value: {
      type: Boolean
    },
    disabled: {
      type: Boolean
    }
  },
  watch: {
    value(val) {
      this.currentValue = val;
    },
    model(val) {
      for (let i = 0; i < val.length; i += 1) {
        if (!this.label) {
          return;
        }
        if (val[i] === this.label) {
          this.currentValue = true;
          break;
        }
        this.currentValue = false;
      }
    }
  },
  methods: {
    /** * 根據入參判斷CheckboxGroup組件是否有指定的的值 */
    parentFnc(options, value) {
      if (value) {
        return this.parent[options] === value
          ? this[options]
          : this.parent[options];
      }
      return this.parent[options] ? this.parent[options] : this[options];
    },
    change(e) {
      if (this.parenDisabled) {
        return;
      }
      //若是存在父組件的實例,則觸發父組件對應的方法
      if (this.parent) {
        this.parent.change(this.model);
        return;
      }
      this.currentValue = e.target.checked;
      this.$emit("input", this.currentValue);
      this.$emit("on-change", this.currentValue);
    }
  },
  computed: {
    parentIcon() {
      if (this.parent) {
        const { parentFnc } = this;
        return parentFnc("icon", "iconselected");
      }
      return this.icon;
    },
    parentType() {
      if (this.parent) {
        const { parentFnc } = this;
        return parentFnc("type", "default");
      }
      return this.type;
    },
    parenDisabled() {
      if (this.parent) {
        const { parentFnc } = this;
        return parentFnc("disabled");
      }
      return this.disabled;
    }
  },
  mounted() {
    const parent = findComponentUpward(this, "G-Checkbox-Group");
    if (parent) {
      this.parent = parent;
      parent.updateModel();
    }
  }
};
</script>
複製代碼
5.CheckboxGroup組件實現
/CheckboxGroup/index.vue

<template>
  <div :class="['g7-CheckboxGroup',{['inline']:inline}]">
    <slot></slot>
  </div>
</template>

<script> import { type } from "../../mixins/props"; import { findComponentsDownward } from "../../utils/index"; export default { name: "G-Checkbox-Group", mixins: [type], data() { return { childrens: [], currentValue: this.value }; }, props: { value: { type: Array, default() { return []; } }, inline: { type: Boolean, default: true }, disabled: { type: Boolean }, icon: { type: String, default: "iconselected" } }, watch: { value(val) { this.currentValue = val; this.updateModel(); } }, methods: { updateModel() { this.childrens = findComponentsDownward(this, "G-Checkbox"); if (this.childrens) { this.childrens.forEach(element => { element.model = this.currentValue; element.currentValue = this.currentValue.indexOf(element.label) >= 0; }); } }, change(value) { this.currentValue = value; this.$emit("input", value); this.$emit("on-change", value); this.updateModel(); } }, mounted() { this.updateModel(); } }; </script>
複製代碼

調用代碼

<template>
  <demoTop gray text="Checkbox">
    <section class="demo-button-row">
      <h3>基本用法</h3>
      <div class="cell">
        <G-Checkbox>選項</G-Checkbox>
      </div>
    </section>
    <section class="demo-button-row">
      <h3>type類型</h3>
      <div class="cell">
        <G-Checkbox-Group>
          <G-Checkbox type="default" label="default"></G-Checkbox>
          <G-Checkbox type="errer" label="errer"></G-Checkbox>
          <G-Checkbox type="success" label="success"></G-Checkbox>
          <G-Checkbox type="warning" label="warning"></G-Checkbox>
        </G-Checkbox-Group>
      </div>
    </section>
    <section class="demo-button-row">
      <h3>搭配CheckboxGroup使用</h3>
      <div class="cell">
        <G-Checkbox-Group>
          <G-Checkbox label="選項一" />
          <G-Checkbox label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
    <section class="demo-button-row">
      <h3>v-model綁定</h3>
      <div class="cell">
        <G-Checkbox-Group v-model="model">
          <G-Checkbox label="選項一" />
          <G-Checkbox label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
    <section class="demo-button-row">
      <h3>禁用</h3>
      <div class="cell">
        <G-Checkbox-Group :value="['選項二']">
          <G-Checkbox disabled label="選項一" />
          <G-Checkbox disabled label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
    <section v-if="false" class="demo-button-row">
      <h3>禁用</h3>
      <div class="cell">
        <G-Checkbox-Group disabled :value="['選項二']">
          <G-Checkbox label="選項一" />
          <G-Checkbox label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
    <section v-if="false" class="demo-button-row">
      <h3>禁用</h3>
      <div class="cell">
        <G-Checkbox-Group type="success" :value="['選項二']">
          <G-Checkbox label="選項一" />
          <G-Checkbox label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
    <section v-if="false" class="demo-button-row">
      <h3>禁用</h3>
      <div class="cell">
        <G-Checkbox-Group icon="iconradioactive" :value="['選項二']">
          <G-Checkbox label="選項一" />
          <G-Checkbox label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
    <section class="demo-button-row">
      <h3>change事件</h3>
      <div class="cell">
        <G-Checkbox-Group @on-change="change">
          <G-Checkbox label="選項一" />
          <G-Checkbox label="選項二" />
          <G-Checkbox label="選項三" />
        </G-Checkbox-Group>
      </div>
    </section>
  </demoTop>
</template>

<script> export default { data() { return { model: ["選項二"] }; }, methods: { change(val) { this.$Toast.info(`選中:${val}`); } } }; </script>

<style lang="less" scoped> .demo-button-row { padding: 0 15px; h3 { margin: 0; padding: 15px 0; color: #84849a; font-weight: normal; font-size: 14px; } } </style>
複製代碼

效果圖

總結

本人水平有限,搬磚不易,不足之處請多指教!
各類各樣的業務組件通過內部業務的打磨後,會慢慢整理共享給各位大佬......less

相關文章
相關標籤/搜索