index.vue 7.87 KB
<script lang="ts" setup>
  import { Spin, Tooltip } from 'ant-design-vue';
  import {
    CopyOutlined,
    SettingOutlined,
    SwapOutlined,
    DeleteOutlined,
  } from '@ant-design/icons-vue';
  import { computed } from 'vue';
  import { PublicFormInstaceType, DataSourceType, SelectedWidgetKeys } from '../../index.type';
  import { fetchDatasourceComponent } from '../../../packages';
  import { ConfigType, CreateComponentType } from '../../../packages/index.type';
  import { ref } from 'vue';
  import { unref } from 'vue';
  import { watch } from 'vue';
  import { nextTick } from 'vue';
  import { useUpdateQueue } from './useUpdateQueue';
  import { useSort } from './useSort';
  import { SettingModal } from '../SettingModal';
  import { useModal } from '/@/components/Modal';
  import { ModalParamsType } from '/#/utils';
  import { DataActionModeEnum } from '/@/enums/toolEnum';
  import { toRaw } from 'vue';
  import cloneDeep from 'lodash-es/cloneDeep';
  import { isBoolean } from '/@/utils/is';
  import { DATA_SOURCE_LIMIT_NUMBER } from '../..';
  import { useMessage } from '/@/hooks/web/useMessage';

  const props = defineProps<{
    selectWidgetKeys: SelectedWidgetKeys;
    dataSource: DataSourceType[];
    componentConfig: CreateComponentType;
  }>();

  const emit = defineEmits<{
    (event: 'update:dataSource', data: DataSourceType[]): void;
  }>();

  const { createMessage } = useMessage();

  const [registerModal, { openModal }] = useModal();

  const { trackUpdate, triggerUpdate, clear } = useUpdateQueue(props);

  const spinning = ref(false);

  const dataSourceFormsEl = ref<{ uuid: string; instance: PublicFormInstaceType }[]>([]);

  const getComponent = computed(() => {
    try {
      const { componentKey } = props.selectWidgetKeys;
      const component = fetchDatasourceComponent({ key: componentKey } as ConfigType);
      return component;
    } catch (error) {
      return '';
    }
  });

  const hasSettingDesignIcon = computed(() => {
    const { componetDesign } = props.componentConfig.persetOption || {};
    return isBoolean(componetDesign) ? componetDesign : true;
  });

  const setDataSourceFormsEl = (uuid: string, instance: PublicFormInstaceType, index: number) => {
    const findIndex = unref(props.dataSource).findIndex((item) => item.uuid === uuid);
    if (~findIndex) {
      dataSourceFormsEl.value[index] = { uuid, instance };
      triggerUpdate(uuid, instance);
    }
  };

  const getFormValueByUUID = (uuid: string): Recordable => {
    const el = unref(dataSourceFormsEl).find((item) => item.uuid === uuid);
    if (el && el.instance) return el.instance.getFormValues();
    return {};
  };

  const getFormValues = (): DataSourceType[] => {
    // 过滤失效form
    dataSourceFormsEl.value = unref(dataSourceFormsEl).filter((item) => item.instance);

    return unref(dataSourceFormsEl).map((item) => {
      const value = item.instance?.getFormValues();
      const oldValue =
        props.dataSource.find((temp) => temp.uuid === item.uuid) || ({} as DataSourceType);
      return {
        componentInfo: toRaw(oldValue.componentInfo),
        ...value,
        uuid: item.uuid,
      };
    });
  };

  const setFormValues = (value: DataSourceType[]) => {
    value.forEach((item) => {
      const { uuid } = item;
      const el = unref(dataSourceFormsEl).find((item) => item.uuid === uuid);
      trackUpdate(uuid);
      if (el && el.instance) {
        triggerUpdate(uuid, el.instance);
      }
    });
  };

  const validate = async () => {
    try {
      for (const item of unref(dataSourceFormsEl)) {
        const errors = await item.instance?.validate?.();
        if (isBoolean(errors) && !errors) return { flag: false, errors };
      }
      return { flag: true, errors: [] };
    } catch (error) {
      console.error(error);
      return { flag: false, errors: error };
    }
  };

  const resetFormValues = async () => {
    dataSourceFormsEl.value = unref(dataSourceFormsEl).filter((item) => item.instance);
    unref(dataSourceFormsEl).forEach((item) => {
      item.instance && item.instance?.resetFormValues?.();
    });
  };

  const handleCopy = async (record: DataSourceType) => {
    const { key } = props.componentConfig || {};
    if (key === 'ComponentStructural' && props.dataSource.length >= 1) {
      createMessage.warning('结构体组件绑定的数据源不能超过1条');
      return;
    }
    if (key == 'HumidityComponent2' && props.dataSource.length >= 6) {
      createMessage.warning('绑定的数据源不能超过6条~');
      return;
    }

    if (props.dataSource.length >= DATA_SOURCE_LIMIT_NUMBER) {
      createMessage.warning('绑定的数据源不能超过10条~');
      return;
    }

    const allValues = await getFormValues();
    const currentRecord = getFormValueByUUID(record.uuid);
    const uuid = trackUpdate();
    const raw = toRaw(record);

    emit('update:dataSource', [
      ...allValues,
      { componentInfo: raw.componentInfo, ...currentRecord, uuid },
    ]);
  };

  const handleSetting = (record: DataSourceType, index: number) => {
    const values = getFormValues();

    openModal(true, {
      mode: DataActionModeEnum.UPDATE,
      record: { ...record, ...values[index] },
    } as ModalParamsType);
  };

  const handleDelete = (record: DataSourceType) => {
    const deleteElIndex = unref(dataSourceFormsEl).findIndex((item) => item.uuid === record.uuid);
    unref(dataSourceFormsEl).splice(deleteElIndex, 1);
    const raw = getFormValues();
    emit('update:dataSource', raw);
  };

  watch(
    () => props.dataSource,
    async (value) => {
      if (value && value.length) {
        nextTick();
        setFormValues(value);
      } else {
        dataSourceFormsEl.value = [];
        clear();
      }
    }
  );

  const handleSettingOk = (data: DataSourceType) => {
    const { uuid } = data;
    const _dataSource = cloneDeep(getFormValues());

    const index = _dataSource.findIndex((item) => item.uuid === uuid);

    _dataSource[index] = { ..._dataSource[index], ...data };

    emit('update:dataSource', _dataSource);
  };

  const { containerEl } = useSort(emit, getFormValues);

  defineExpose({
    getFormValues,
    validate,
    setFormValues,
    resetFormValues,
  } as PublicFormInstaceType);
</script>

<template>
  <section ref="containerEl">
    <Spin :spinning="spinning">
      <main v-for="(item, index) in dataSource" :key="item.uuid" class="flex">
        <label class="w-24 text-right pr-2">数据源{{ index + 1 }}</label>
        <component
          :ref="(event) => setDataSourceFormsEl(item.uuid, event, index)"
          class="flex-1 bg-light-50 dark:bg-dark-400 data-board-source-form"
          :is="getComponent"
          :component-config="componentConfig"
          :values="item"
        />
        <div class="w-28 flex gap-3 ml-2">
          <Tooltip title="复制">
            <CopyOutlined @click="handleCopy(item)" class="cursor-pointer text-lg !leading-32px" />
          </Tooltip>
          <Tooltip title="设置" v-if="hasSettingDesignIcon">
            <SettingOutlined
              @click="handleSetting(item, index)"
              class="cursor-pointer text-lg !leading-32px"
            />
          </Tooltip>
          <Tooltip title="拖拽排序">
            <SwapOutlined
              class="cursor-pointer text-lg !leading-32px svg:transform svg:rotate-90 sort-icon"
            />
          </Tooltip>
          <Tooltip title="删除">
            <DeleteOutlined
              @click="handleDelete(item)"
              class="cursor-pointer text-lg !leading-32px"
            />
          </Tooltip>
        </div>
      </main>
    </Spin>

    <SettingModal
      @register="registerModal"
      @ok="handleSettingOk"
      :component-config="componentConfig"
      :select-widget-keys="selectWidgetKeys"
    />
  </section>
</template>

<style scoped lang="less">
  .data-board-source-form {
    :deep(.ant-form-item-control-input-content) {
      > div > div {
        width: 100%;
      }
    }
  }
</style>