index.vue 4.61 KB
<script lang="ts" setup>
  import { BasicForm, FormSchema, useForm } from '/@/components/Form';
  import { ComponentType, ColEx } from '/@/components/Form/src/types/index';
  import { computed } from '@vue/reactivity';
  import { isFunction } from '/@/utils/is';
  import { toRaw, unref } from 'vue';
  import { watch } from 'vue';
  import { nextTick } from 'vue';
  import { ref } from 'vue';
  import { onMounted } from 'vue';

  interface ValueItemType {
    value: any;
  }

  enum FormFieldsEnum {
    TOTAL_CONTROL = 'totalControl',
  }

  enum EmitEventEnum {
    UPDATE_VALUE = 'update:value',
  }

  const emit = defineEmits<{
    (event: EmitEventEnum.UPDATE_VALUE, value: ValueItemType[]): void;
  }>();

  const props = withDefaults(
    defineProps<{
      value: ValueItemType[];
      length?: number;
      component?: ComponentType;
      itemColProps?: Partial<ColEx>;
      itemLabel?: (index: number) => string;
      itemProps?: (index: number) => FormSchema;
      showTotalControl?: boolean;
      totalControlProps?: FormSchema;
    }>(),
    {
      value: () => [],
      length: 0,
      component: 'Switch',
      itemLabel: (index: number) => `#${index}`,
      itemProps: () => ({} as unknown as FormSchema),
      itemColProps: () => ({ span: 12 } as Partial<ColEx>),
      showTotalControl: true,
      totalControlProps: () => ({} as unknown as FormSchema),
    }
  );

  const batchSetValue = (value: any): ValueItemType[] => {
    const { length } = props;
    return Array.from({ length }, () => ({ value }));
  };

  const getTotalControlItem = computed(() => {
    const { totalControlProps, component, showTotalControl } = props;
    return {
      ...totalControlProps,
      field: FormFieldsEnum.TOTAL_CONTROL,
      component,
      ifShow: showTotalControl,
      componentProps: {
        onChange(value: any) {
          handleUpdateValue(batchSetValue(value));
        },
      },
    } as FormSchema;
  });

  const allDefaultValue = ref({});
  const getSchemas = () => {
    const { itemProps, itemLabel, length, component } = props;
    let label = isFunction(itemLabel) ? itemLabel : (index: number) => `#${index}`;
    let _itemProps = isFunction(itemProps) ? itemProps : () => ({});
    const schemas = Array.from(
      { length: props.length },
      (_item, index) =>
        ({
          ..._itemProps(index),
          label: label(index),
          field: index.toString(),
          component,
          componentProps: {
            onChange: async () => {
              await nextTick();
              handleUpdateValue();
            },
          },
        } as FormSchema)
    );

    length && schemas.unshift(toRaw(getTotalControlItem.value));
    allDefaultValue.value = unref(schemas).reduce(
      (prev, next) => ({ ...prev, [next.field]: next.defaultValue }),
      {}
    );
    return schemas;
  };

  const [registerForm, { getFieldsValue, setProps, setFieldsValue }] = useForm({
    showActionButtonGroup: false,
    schemas: toRaw(unref(getSchemas())),
    // baseColProps,
    baseColProps: props.itemColProps,
  });

  const handleUpdateValue = (value?: ValueItemType[]) => {
    if (value) {
      emit(EmitEventEnum.UPDATE_VALUE, value);
      return;
    }
    const allValue = getFieldsValue();
    const sortKeyList = Array.from({ length: props.length }, (_v, key) => key);
    const res = sortKeyList.map(
      (item) => ({ value: allValue[item] ?? unref(allDefaultValue)[item] } as ValueItemType)
    );

    emit(EmitEventEnum.UPDATE_VALUE, res);
  };

  const transformValue = (value: ValueItemType[]) => {
    const { length } = props;
    if (value.length !== length) {
      value = Array.from({ length: props.length }, () => ({ value: null } as ValueItemType));
    }
    return value.reduce((prev, next, index) => ({ ...prev, [index]: next.value }), {});
  };

  const initialized = ref(false);

  watch(
    () => props.value,
    async (target) => {
      if (target) {
        let flag = unref(initialized);
        if (!flag) {
          await nextTick();
        }
        const value = transformValue(target);
        setFieldsValue(value);

        if (!flag) {
          handleUpdateValue();
        }
      }
    },
    {
      immediate: true,
    }
  );

  watch(
    () => [props.length, props.component],
    () => {
      setProps({
        schemas: toRaw(unref(getSchemas())),
      });
      handleUpdateValue();
    }
  );

  onMounted(() => {
    initialized.value = true;
  });
</script>

<template>
  <BasicForm class="control-group-form" @register="registerForm" />
</template>

<style lang="less" scoped>
  .control-group-form {
    :deep(.ant-form-item-label) {
      font-weight: 700;
    }
  }
</style>