浏览代码

refactor: checkboxgroup

suzigang 4 年之前
父节点
当前提交
a33a0a0c65

+ 76 - 3
src/packages/checkbox/demo.vue

@@ -45,15 +45,48 @@
         >change复选框</nut-checkbox
       >
     </div>
+    <h4>checkboxGroup使用</h4>
+    <div class="show-demo group1">
+      <nut-checkboxgroup v-model="checkboxgroup1">
+        <nut-checkbox v-model="checkbox9" label="1">组合复选框</nut-checkbox>
+        <nut-checkbox v-model="checkbox10" label="2">组合复选框</nut-checkbox>
+        <nut-checkbox v-model="checkbox11" label="3">组合复选框</nut-checkbox>
+        <nut-checkbox v-model="checkbox12" label="4">组合复选框</nut-checkbox>
+      </nut-checkboxgroup>
+      <span>选中:{{ checkboxgroup1 }}</span>
+    </div>
+    <h4>checkboxGroup禁用</h4>
+    <div class="show-demo group2">
+      <nut-checkboxgroup v-model="checkboxgroup2" disabled>
+        <nut-checkbox v-model="checkbox13" label="1">组合复选框</nut-checkbox>
+        <nut-checkbox v-model="checkbox14" label="2">组合复选框</nut-checkbox>
+      </nut-checkboxgroup>
+    </div>
+    <h4>checkboxGroup 全选/取消</h4>
+    <div class="show-demo group1">
+      <nut-checkboxgroup
+        v-model="checkboxgroup3"
+        ref="group"
+        @change="changeBox4"
+      >
+        <nut-checkbox v-model="checkbox15" label="1">组合复选框</nut-checkbox>
+        <nut-checkbox v-model="checkbox16" label="2">组合复选框</nut-checkbox>
+      </nut-checkboxgroup>
+      <span class="btn">
+        <nut-button type="primary" @click="toggleAll(true)">全选</nut-button>
+        <nut-button type="primary" @click="toggleAll(false)">取消</nut-button>
+      </span>
+    </div>
   </div>
 </template>
 <script lang="ts">
-import { reactive, ref, toRefs } from 'vue';
+import { reactive, ref, toRefs, onMounted } from 'vue';
 import { createComponent } from '@/utils/create';
 import { Toast } from '@/nutui';
 const { createDemo } = createComponent('checkbox');
 export default createDemo({
   setup(props, context) {
+    const group = ref(null);
     const data = reactive({
       checkbox1: true,
       checkbox2: false,
@@ -62,7 +95,18 @@ export default createDemo({
       checkbox5: false,
       checkbox6: false,
       checkbox7: false,
-      checkbox8: false
+      checkbox8: false,
+      checkbox9: false,
+      checkbox10: false,
+      checkbox11: false,
+      checkbox12: false,
+      checkbox13: false,
+      checkbox14: false,
+      checkbox15: false,
+      checkbox16: false,
+      checkboxgroup1: ['2', '3'],
+      checkboxgroup2: ['2'],
+      checkboxgroup3: ['2']
     });
     const changeBox1 = (state: boolean, label: string) => {
       console.log(state, label);
@@ -76,10 +120,21 @@ export default createDemo({
       Toast.text(`您${state ? '选中' : '取消'}了${label}`);
     };
 
+    const changeBox4 = (label: any[]) => {
+      Toast.text(`${label.length ? '全选' : '取消全选'}`);
+    };
+
+    const toggleAll = (f: boolean) => {
+      (group.value as any).toggleAll(f);
+    };
+
     return {
       changeBox1,
       changeBox2,
       changeBox3,
+      changeBox4,
+      toggleAll,
+      group,
       ...toRefs(data)
     };
   }
@@ -104,6 +159,17 @@ export default createDemo({
     background-color: #ffffff;
     border-radius: 7px;
     box-shadow: 0px 1px 7px 0px rgba(237, 238, 241, 1);
+    &.group1 {
+      flex-direction: column;
+      ::v-deep(.nut-checkbox) {
+        margin-left: 15px;
+      }
+    }
+    &.group2 {
+      ::v-deep(.nut-checkbox) {
+        margin-left: 15px;
+      }
+    }
     p,
     span {
       display: block;
@@ -111,7 +177,14 @@ export default createDemo({
       color: #636363;
     }
     span {
-      font-size: 12px;
+      color: #1d1e1e;
+      font-size: 16px;
+      &.btn {
+        margin-top: 20px;
+        ::v-deep(.nut-button) {
+          margin-left: 10px;
+        }
+      }
     }
   }
 }

+ 76 - 3
src/packages/checkbox/doc.md

@@ -100,11 +100,70 @@ setup() {
 }
 ```
 
-## Prop
+## checkboxGroup使用
+
+```html
+<nut-checkboxgroup v-model="checkboxgroup1">
+  <nut-checkbox v-model="checkbox9" label="1">组合复选框</nut-checkbox>
+  <nut-checkbox v-model="checkbox10" label="2">组合复选框</nut-checkbox>
+  <nut-checkbox v-model="checkbox11" label="3">组合复选框</nut-checkbox>
+  <nut-checkbox v-model="checkbox12" label="4">组合复选框</nut-checkbox>
+</nut-checkboxgroup>
+```
+
+```ts
+setup() {
+  return {
+    checkbox9: false,
+    checkbox10: false,
+    checkbox11: false,
+    checkbox12: false,
+    checkboxgroup1: ['2', '3'],
+  };
+}
+```
+
+## checkboxGroup 全选/取消
+
+```html
+<nut-checkboxgroup v-model="checkboxgroup3" ref="group" @change="changeBox4">
+  <nut-checkbox v-model="checkbox15" label="1">组合复选框</nut-checkbox>
+  <nut-checkbox v-model="checkbox16" label="2">组合复选框</nut-checkbox>
+</nut-checkboxgroup>
+<span class="btn">
+  <nut-button type="primary" @click="toggleAll(true)">全选</nut-button>
+  <nut-button type="primary" @click="toggleAll(false)">取消</nut-button>
+</span>
+```
+
+```ts
+setup() {
+  const group = ref(null);
+  const changeBox4 = (label: any[]) => {
+    Toast.text(`${label.length ? '全选' : '取消全选'}`);
+  };
+
+  const toggleAll = (f: boolean) => {
+    (group.value as any).toggleAll(f);
+  };
+  return {
+    checkbox15: false,
+    checkbox16: false,
+    changeBox4: false,
+    checkbox12: false,
+    checkboxgroup3: ['2'],
+    group,
+    changeBox4,
+    toggleAll
+  };
+}
+```
+
+## Checkbox
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
-| v-model | 是否处于选中状态 | String | `false`
+| v-model | 是否处于选中状态 | Boolean | `false`
 | disabled | 是否禁用选择 | Boolean | `false`
 | text-position | 文本所在的位置,可选值:`left`,`right` | String | `right`
 | icon-size | [图标尺寸](#/icon) | String、Number | `18`
@@ -113,9 +172,23 @@ setup() {
 | label | 复选框的文本内容 | String | -
 
 
+## CheckboxGroup
 
-## Event
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| v-model | 当前选中项的标识符,和 `label` 相对应  | String | -
+| disabled | 是否禁用选择,将用于其下的全部复选框 | Boolean | `false`
+
+
+
+## Checkbox Event
 
 | 字段 | 说明 | 回调参数 
 |----- | ----- | ----- 
 | change | 值变化时触发 | (state, label),`state`代表当前状态,`label`表示当前选中的值
+
+## CheckboxGroup Event
+
+| 字段 | 说明 | 回调参数 
+|----- | ----- | ----- 
+| change | 值变化时触发 | label,`label`返回一个数组,表示当前选中项的集合

+ 41 - 16
src/packages/checkbox/index.vue

@@ -1,10 +1,12 @@
 <script lang="ts">
-import { h, computed } from 'vue';
+import { h, computed, inject, getCurrentInstance, onMounted } from 'vue';
 import { createComponent } from '@/utils/create';
 const { create, componentName } = createComponent('checkbox');
 import nutIcon from '@/packages/icon/index.vue';
+import CheckboxGroup from '@/packages/checkboxgroup/index.vue';
 
 export default create({
+  children: [CheckboxGroup],
   components: {
     nutIcon
   },
@@ -40,15 +42,27 @@ export default create({
   },
   emits: ['change', 'update:modelValue'],
   setup(props, { emit, slots }) {
-    const checked = computed(() => !!props.modelValue);
+    const parent: any = inject('parent');
+
+    const hasParent = computed(() => !!parent);
+
+    const pValue = computed(() => {
+      if (hasParent.value) {
+        return parent.value.value.includes(props.label);
+      } else {
+        return props.modelValue;
+      }
+    });
 
-    const label = computed(() => {
-      return props.label ? props.label : slots.default?.();
+    const pDisabled = computed(() => {
+      return hasParent.value ? parent.disabled : props.disabled;
     });
 
+    const checked = computed(() => !!props.modelValue);
+
     const color = computed(() => {
-      return !props.disabled
-        ? !props.modelValue
+      return !pDisabled.value
+        ? !pValue.value
           ? '#d6d6d6'
           : '#fa2c19'
         : '#f5f5f5';
@@ -60,9 +74,9 @@ export default create({
     };
 
     const renderIcon = () => {
-      const { iconName, iconSize, iconActiveName, modelValue } = props;
+      const { iconName, iconSize, iconActiveName } = props;
       return h(nutIcon, {
-        name: !modelValue ? iconName : iconActiveName,
+        name: !pValue.value ? iconName : iconActiveName,
         size: iconSize,
         color: color.value
       });
@@ -73,22 +87,33 @@ export default create({
         'view',
         {
           class: `${componentName}__label ${
-            props.disabled ? `${componentName}__label--disabled` : ''
+            pDisabled.value ? `${componentName}__label--disabled` : ''
           }`
         },
-        label.value
+        slots.default?.()
       );
     };
 
     const handleClick = (e: MouseEvent | TouchEvent) => {
-      if (props.disabled) return;
-      const text =
-        typeof label.value === 'string'
-          ? label.value
-          : label.value && label.value[0].children;
-      emitChange(!checked.value, text as string);
+      if (pDisabled.value) return;
+      emitChange(!checked.value, slots.default?.()[0].children as string);
+      if (hasParent.value) {
+        let value = parent.value.value;
+        let { label } = props;
+        const index = value.indexOf(label);
+        if (index > -1) {
+          value.splice(index, 1);
+        } else {
+          value.push(label);
+        }
+        parent.updateValue(value);
+      }
     };
 
+    onMounted(() => {
+      hasParent.value && parent['relation'](getCurrentInstance());
+    });
+
     return () => {
       return h(
         'view',

+ 51 - 57
src/packages/checkboxgroup/index.vue

@@ -1,12 +1,16 @@
-<template>
-  <view :class="['nut-checkboxgroup', 'nut-checkboxgroup-' + direction]">
-    <slot></slot>
-  </view>
-</template>
 <script lang="ts">
-import { watch, provide, getCurrentInstance } from 'vue';
+import {
+  h,
+  watch,
+  provide,
+  computed,
+  ComponentInternalInstance,
+  reactive,
+  ComponentPublicInstance
+} from 'vue';
 import { createComponent } from '@/utils/create';
-const { create } = createComponent('checkboxgroup');
+import { useExpose } from '@/utils/useExpose/index';
+const { create, componentName } = createComponent('checkboxgroup');
 
 export default create({
   props: {
@@ -17,22 +21,41 @@ export default create({
     disabled: {
       type: Boolean,
       default: false
-    },
-    size: {
-      type: String,
-      default: 'normal'
-    },
-    animation: {
-      type: Boolean,
-      default: true
-    },
-    direction: {
-      type: String,
-      default: 'horizontal'
     }
   },
   emits: ['change', 'update:modelValue'],
   setup(props, { slots, emit }) {
+    const state = reactive({
+      children: [] as ComponentPublicInstance[]
+    });
+
+    const relation = (child: ComponentInternalInstance) => {
+      if (child.proxy) {
+        state.children.push(child.proxy);
+      }
+    };
+
+    const updateValue = (value: any[]) => {
+      emit('update:modelValue', value);
+    };
+
+    const toggleAll = (checked: boolean) => {
+      let values: any[] = [];
+      if (!!checked) {
+        state.children.forEach((item: any) => {
+          values.push(item?.label);
+        });
+      }
+      emit('update:modelValue', values);
+    };
+
+    provide('parent', {
+      value: computed(() => props.modelValue),
+      disabled: props.disabled,
+      updateValue,
+      relation
+    });
+
     watch(
       () => props.modelValue,
       value => {
@@ -40,46 +63,17 @@ export default create({
       }
     );
 
-    function useExtend(apis: any) {
-      const instance = getCurrentInstance();
-      if (instance) {
-        Object.assign(instance.proxy, apis);
-      }
-    }
+    useExpose({ toggleAll });
 
-    const toggleAll = (checked: boolean) => {
-      const children = (slots as any)?.default();
-      if (checked === false) {
-        emit('update:modelValue', []);
-      } else if (checked === true) {
-        const labels = children.map(
-          (item: { props: { label: any } }) => item.props?.label
-        );
-        emit('update:modelValue', labels);
-      } else {
-        const names = children
-          .filter((item: { props: { label: any } }) => {
-            const label = item.props?.label;
-            const idx = props.modelValue.indexOf(label);
-            if (idx == -1) {
-              return label;
-            }
-          })
-          .map((item: { props: { label: any } }) => item.props?.label);
-        emit('update:modelValue', names);
-      }
+    return () => {
+      return h(
+        'view',
+        {
+          class: `${componentName}`
+        },
+        slots.default?.()
+      );
     };
-    useExtend({ toggleAll });
-
-    provide('checkboxgroup', {
-      parentNode: true,
-      changeVal: (val: []) => {
-        if (props.disabled) {
-          return false;
-        }
-        emit('update:modelValue', val);
-      }
-    });
   }
 });
 </script>

+ 1 - 1
src/packages/swiper/index.vue

@@ -52,7 +52,7 @@ import {
 } from 'vue';
 import { createComponent } from '@/utils/create';
 import { useTouch } from './use-touch';
-import { useExpose } from './use-expose';
+import { useExpose } from '@/utils/useExpose/index';
 const { create, componentName } = createComponent('swiper');
 import swiperItem from '@/packages/swiperitem/index.vue';
 export default create({

+ 8 - 0
src/utils/useExpose/index.ts

@@ -0,0 +1,8 @@
+import { getCurrentInstance } from 'vue';
+
+export function useExpose(apis: Record<string, any>) {
+  const instance = getCurrentInstance();
+  if (instance) {
+    Object.assign(instance.proxy, apis);
+  }
+}