<template>
  <!-- 查询表单 -->
  <SearchForm
    v-show="isShowSearch"
    :columns="searchColumns"
    :search-param="searchParam"
    :search-col="searchCol"
    :search="_search"
    :reset="_reset"
    :collapsedRows="props.collapsedRows"
  />

  <!-- 表格 -->
  <div class="card flex-1 mt-2">
    <!-- 表格主体 -->
    <div>
      <slot name="table_top"></slot>
      <vxe-grid ref="xGrid" v-bind="gridOptions" :data="processTableData" :loading="loading">
        <template #toolbar_buttons>
          <slot name="left_buttons"></slot>
        </template>
        <template #toolbar_tools>
          <slot name="right_tools"></slot>
        </template>
      </vxe-grid>
    </div>
    <!-- 分页组件 -->
    <slot name="pagination">
      <Pagination
        v-if="props.showPagination"
        :pageable="pageable"
        :handle-size-change="handleSizeChange"
        :handle-current-change="handleCurrentChange"
      />
    </slot>
  </div>
</template>

<script setup>
  import SearchForm from '@/components/SearchForm/index.vue';
  import Pagination from '@/components/Pagination/index.vue';
  import { computed, reactive, ref, unref, provide } from 'vue';
  import { useProTable } from '@/hooks/useProTable';
  import { handleProp } from '@/utils';

  const emit = defineEmits(['search', 'reset']);
  const props = defineProps({
    api: {
      type: Function,
    },
    config: {
      type: Object,
      required: true,
    },
    showToolBar: {
      type: Boolean,
      default: true,
    },
    showPagination: {
      type: Boolean,
      default: true,
    },
    searchCol: {
      type: Object,
      default: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }),
    },
    initParam: {
      type: Object,
      default: () => ({}),
    },
    dataCallback: {
      type: Function,
    },
    paramCallback: {
      type: Function,
    },
    requestError: {
      type: Function,
    },
    collapsedRows: {
      type: Number,
      default: 1,
    },
    data: {
      type: Array,
    },
  });

  const xGrid = ref(null);
  const {
    tableData,
    loading,
    pageable,
    gridConfig,
    searchParam,
    searchInitParam,
    getTableList,
    search,
    reset,
    handleSizeChange,
    handleCurrentChange,
    totalParam,
  } = useProTable(
    props?.api,
    props.initParam,
    props.showPagination,
    props.dataCallback,
    props.requestError,
    props.paramCallback
  );

  // 显示搜索
  const isShowSearch = ref(true);
  // 计算表格高度
  const tableHeight = computed(() => `calc(100% - ${props.showPagination ? 52 : 0}px)`);
  // 表格配置项
  const gridOptions = computed(() => Object.assign({}, gridConfig.value, props.config));

  // 处理表格数据
  const processTableData = computed(() => {
    if (!props.data) return tableData.value;
    if (!props.pagination) return props.data;
    return props.data.slice(
      (pageable.value.pageNum - 1) * pageable.value.pageSize,
      pageable.value.pageSize * pageable.value.pageNum
    );
  });

  // 过滤需要搜索的配置项 && 排序
  const searchColumns = computed(() => {
    return flatColumns.value
      ?.filter((item) => item.search?.el || item.search?.render)
      .sort((a, b) => a.search.order - b.search.order);
  });

  // 定义 enumMap 存储 enum 值（避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择）
  const enumMap = ref(new Map());
  const setEnumMap = async ({ field, enum: enumValue }) => {
    if (!enumValue) return;

    // 如果当前 enumMap 存在相同的值 return
    if (
      enumMap.value.has(field) &&
      (typeof enumValue === 'function' || enumMap.value.get(field) === enumValue)
    )
      return;

    // 当前 enum 为静态数据，则直接存储到 enumMap
    if (typeof enumValue !== 'function') return enumMap.value.set(field, unref(enumValue));

    // 为了防止接口执行慢，而存储慢，导致重复请求，所以预先存储为[]，接口返回后再二次存储
    enumMap.value.set(field, []);

    // 当前 enum 为后台数据需要请求数据，则调用该请求接口，并存储到 enumMap
    const { data } = await enumValue();
    enumMap.value.set(field, data);
  };

  // 注入 enumMap
  provide('enumMap', enumMap);

  const _search = () => {
    search();
    emit('search', totalParam.value);
  };

  const _reset = () => {
    reset();
    emit('reset');
  };

  // 接收 columns 并设置为响应式
  const tableColumns = reactive(props.config.columns);

  // 扁平化 columns
  const flatColumns = computed(() => flatColumnsFunc(tableColumns));

  // 扁平化 columns 的方法
  const flatColumnsFunc = (columns, flatArr = []) => {
    columns.forEach(async (col) => {
      if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
      flatArr.push(col);

      // 设置 enumMap
      await setEnumMap(col);
    });
    return flatArr.filter((item) => !item._children?.length);
  };

  // 设置 搜索表单默认排序 && 搜索表单项的默认值
  searchColumns.value?.forEach((column, index) => {
    column.search.order = column.search?.order ?? index + 2;
    const key = column.search?.key ?? handleProp(column.field);
    const defaultValue = column.search?.defaultValue;
    if (defaultValue !== undefined && defaultValue !== null) {
      searchParam.value[key] = defaultValue;
      searchInitParam.value[key] = defaultValue;
    }
  });

  defineExpose({
    element: xGrid,

    // Function
    getTableList,
    search,
    reset,
    handleSizeChange,
    handleCurrentChange,
  });
</script>
<style lang="scss" scoped></style>
