vben
2021-08-24 56a966cfbf8db5b29a42185f0f25a0e800c30dbb
src/components/Table/src/hooks/useColumns.ts
@@ -1,97 +1,204 @@
import { BasicColumn, BasicTableProps } from '../types/table';
import { PaginationProps } from '../types/pagination';
import { unref, ComputedRef, Ref, computed, watch, ref } from 'vue';
import { isBoolean, isArray, isObject } from '/@/utils/is';
import { PAGE_SIZE } from '../const';
import { useProps } from './useProps';
import type { BasicColumn, BasicTableProps, CellFormat, GetColumnsParams } from '../types/table';
import type { PaginationProps } from '../types/pagination';
import type { ComputedRef } from 'vue';
import { computed, Ref, ref, toRaw, unref, watch } from 'vue';
import { renderEditCell } from '../components/editable';
import { usePermission } from '/@/hooks/web/usePermission';
import { useI18n } from '/@/hooks/web/useI18n';
import { isArray, isBoolean, isFunction, isMap, isString } from '/@/utils/is';
import { cloneDeep, isEqual } from 'lodash-es';
import { formatToDate } from '/@/utils/dateUtil';
import { ACTION_COLUMN_FLAG, DEFAULT_ALIGN, INDEX_COLUMN_FLAG, PAGE_SIZE } from '../const';
function handleItem(item: BasicColumn, ellipsis: boolean) {
  const { key, dataIndex, children } = item;
  item.align = item.align || DEFAULT_ALIGN;
  if (ellipsis) {
    if (!key) {
      item.key = dataIndex;
    }
    if (!isBoolean(item.ellipsis)) {
      Object.assign(item, {
        ellipsis,
      });
    }
  }
  if (children && children.length) {
    handleChildren(children, !!ellipsis);
  }
}
function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
  if (!children) return;
  children.forEach((item) => {
    const { children } = item;
    handleItem(item, ellipsis);
    handleChildren(children, ellipsis);
  });
}
function handleIndexColumn(
  propsRef: ComputedRef<BasicTableProps>,
  getPaginationRef: ComputedRef<boolean | PaginationProps>,
  columns: BasicColumn[],
) {
  const { t } = useI18n();
  const { showIndexColumn, indexColumnProps, isTreeTable } = unref(propsRef);
  let pushIndexColumns = false;
  if (unref(isTreeTable)) {
    return;
  }
  columns.forEach(() => {
    const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG);
    if (showIndexColumn) {
      pushIndexColumns = indIndex === -1;
    } else if (!showIndexColumn && indIndex !== -1) {
      columns.splice(indIndex, 1);
    }
  });
  if (!pushIndexColumns) return;
  const isFixedLeft = columns.some((item) => item.fixed === 'left');
  columns.unshift({
    flag: INDEX_COLUMN_FLAG,
    width: 50,
    title: t('component.table.index'),
    align: 'center',
    customRender: ({ index }) => {
      const getPagination = unref(getPaginationRef);
      if (isBoolean(getPagination)) {
        return `${index + 1}`;
      }
      const { current = 1, pageSize = PAGE_SIZE } = getPagination;
      return ((current < 1 ? 1 : current) - 1) * pageSize + index + 1;
    },
    ...(isFixedLeft
      ? {
          fixed: 'left',
        }
      : {}),
    ...indexColumnProps,
  });
}
function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
  const { actionColumn } = unref(propsRef);
  if (!actionColumn) return;
  const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG);
  if (hasIndex === -1) {
    columns.push({
      ...columns[hasIndex],
      fixed: 'right',
      ...actionColumn,
      flag: ACTION_COLUMN_FLAG,
    });
  }
}
export function useColumns(
  refProps: ComputedRef<BasicTableProps>,
  getPaginationRef: ComputedRef<false | PaginationProps>
  propsRef: ComputedRef<BasicTableProps>,
  getPaginationRef: ComputedRef<boolean | PaginationProps>,
) {
  const { propsRef } = useProps(refProps);
  const columnsRef = ref(unref(propsRef).columns) as unknown as Ref<BasicColumn[]>;
  let cacheColumns = unref(propsRef).columns;
  const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
  const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
  const getColumnsRef = computed(() => {
    const columns = cloneDeep(unref(columnsRef));
    handleIndexColumn(propsRef, getPaginationRef, columns);
    handleActionColumn(propsRef, columns);
    if (!columns) {
      return [];
    }
    const { ellipsis } = unref(propsRef);
    columns.forEach((item) => {
      const { customRender, slots } = item;
      handleItem(
        item,
        Reflect.has(item, 'ellipsis') ? !!item.ellipsis : !!ellipsis && !customRender && !slots,
      );
    });
    return columns;
  });
  function isIfShow(column: BasicColumn): boolean {
    const ifShow = column.ifShow;
    let isIfShow = true;
    if (isBoolean(ifShow)) {
      isIfShow = ifShow;
    }
    if (isFunction(ifShow)) {
      isIfShow = ifShow(column);
    }
    return isIfShow;
  }
  const { hasPermission } = usePermission();
  const getViewColumns = computed(() => {
    const viewColumns = sortFixedColumn(unref(getColumnsRef));
    const columns = cloneDeep(viewColumns);
    return columns
      .filter((column) => {
        return hasPermission(column.auth) && isIfShow(column);
      })
      .map((column) => {
        const { slots, dataIndex, customRender, format, edit, editRow, flag } = column;
        if (!slots || !slots?.title) {
          column.slots = { title: `header-${dataIndex}`, ...(slots || {}) };
          column.customTitle = column.title;
          Reflect.deleteProperty(column, 'title');
        }
        const isDefaultAction = [INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG].includes(flag!);
        if (!customRender && format && !edit && !isDefaultAction) {
          column.customRender = ({ text, record, index }) => {
            return formatCell(text, format, record, index);
          };
        }
        // edit table
        if ((edit || editRow) && !isDefaultAction) {
          column.customRender = renderEditCell(column);
        }
        return column;
      });
  });
  watch(
    () => unref(propsRef).columns,
    (columns) => {
      columnsRef.value = columns;
      cacheColumnsRef.value = columns;
      cacheColumns = columns?.filter((item) => !item.flag) ?? [];
    },
    {
      immediate: true,
    }
  );
  const getColumnsRef = computed(() => {
    const props = unref(propsRef);
    const { showIndexColumn, indexColumnProps, ellipsis, actionColumn, isTreeTable } = props;
    const columns = unref(columnsRef);
    if (!columns) {
      return [];
  function setCacheColumnsByField(dataIndex: string | undefined, value: Partial<BasicColumn>) {
    if (!dataIndex || !value) {
      return;
    }
    let pushIndexColumns = false;
    columns.forEach((item) => {
      const { key, dataIndex } = item;
      item.align = item.align || 'center';
      if (ellipsis) {
        if (!key) {
          item.key = dataIndex;
        }
        if (!isBoolean(item.ellipsis)) {
          Object.assign(item, {
            ellipsis,
          });
        }
      }
      const indIndex = columns.findIndex((column) => column.flag === 'INDEX');
      if (showIndexColumn && !isTreeTable) {
        pushIndexColumns = indIndex === -1;
      } else if (!showIndexColumn && !isTreeTable && indIndex !== -1) {
        columns.splice(indIndex, 1);
    cacheColumns.forEach((item) => {
      if (item.dataIndex === dataIndex) {
        Object.assign(item, value);
        return;
      }
    });
    if (pushIndexColumns) {
      const isFixedLeft = columns.some((item) => item.fixed === 'left');
      columns.unshift({
        flag: 'INDEX',
        width: 50,
        title: '序号',
        align: 'center',
        customRender: ({ index }) => {
          const getPagination = unref(getPaginationRef);
          if (isBoolean(getPagination)) {
            return `${index + 1}`;
          }
          const { current = 1, pageSize = PAGE_SIZE } = getPagination;
          const currentIndex = (current - 1) * pageSize + index + 1;
          return currentIndex;
        },
        ...(isFixedLeft
          ? {
              fixed: 'left',
            }
          : {}),
        ...indexColumnProps,
      });
    }
    if (actionColumn) {
      const hasIndex = columns.findIndex((column) => column.flag === 'ACTION');
      columns.push({
        ...(hasIndex === -1 ? columns[hasIndex] : {}),
        fixed: 'right',
        ...actionColumn,
        flag: 'ACTION',
      });
    }
    return columns;
  });
  function setColumns(columns: BasicColumn[] | string[]) {
  }
  /**
   * set columns
   * @param columnList key|column
   */
  function setColumns(columnList: Partial<BasicColumn>[] | string[]) {
    const columns = cloneDeep(columnList);
    if (!isArray(columns)) return;
    if (columns.length <= 0) {
@@ -100,15 +207,119 @@
    }
    const firstColumn = columns[0];
    if (isObject(firstColumn)) {
      columnsRef.value = columns as any;
    const cacheKeys = cacheColumns.map((item) => item.dataIndex);
    if (!isString(firstColumn)) {
      columnsRef.value = columns as BasicColumn[];
    } else {
      const newColumns = unref(cacheColumnsRef).filter((item) =>
        (columns as string[]).includes(item.key! || item.dataIndex!)
      );
      const columnKeys = columns as string[];
      const newColumns: BasicColumn[] = [];
      cacheColumns.forEach((item) => {
        if (columnKeys.includes(item.dataIndex! || (item.key as string))) {
          newColumns.push({
            ...item,
            defaultHidden: false,
          });
        } else {
          newColumns.push({
            ...item,
            defaultHidden: true,
          });
        }
      });
      // Sort according to another array
      if (!isEqual(cacheKeys, columns)) {
        newColumns.sort((prev, next) => {
          return (
            cacheKeys.indexOf(prev.dataIndex as string) -
            cacheKeys.indexOf(next.dataIndex as string)
          );
        });
      }
      columnsRef.value = newColumns;
    }
  }
  return { getColumnsRef, setColumns };
  function getColumns(opt?: GetColumnsParams) {
    const { ignoreIndex, ignoreAction, sort } = opt || {};
    let columns = toRaw(unref(getColumnsRef));
    if (ignoreIndex) {
      columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG);
    }
    if (ignoreAction) {
      columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG);
    }
    if (sort) {
      columns = sortFixedColumn(columns);
    }
    return columns;
  }
  function getCacheColumns() {
    return cacheColumns;
  }
  return {
    getColumnsRef,
    getCacheColumns,
    getColumns,
    setColumns,
    getViewColumns,
    setCacheColumnsByField,
  };
}
function sortFixedColumn(columns: BasicColumn[]) {
  const fixedLeftColumns: BasicColumn[] = [];
  const fixedRightColumns: BasicColumn[] = [];
  const defColumns: BasicColumn[] = [];
  for (const column of columns) {
    if (column.fixed === 'left') {
      fixedLeftColumns.push(column);
      continue;
    }
    if (column.fixed === 'right') {
      fixedRightColumns.push(column);
      continue;
    }
    defColumns.push(column);
  }
  return [...fixedLeftColumns, ...defColumns, ...fixedRightColumns].filter(
    (item) => !item.defaultHidden,
  );
}
// format cell
export function formatCell(text: string, format: CellFormat, record: Recordable, index: number) {
  if (!format) {
    return text;
  }
  // custom function
  if (isFunction(format)) {
    return format(text, record, index);
  }
  try {
    // date type
    const DATE_FORMAT_PREFIX = 'date|';
    if (isString(format) && format.startsWith(DATE_FORMAT_PREFIX)) {
      const dateFormat = format.replace(DATE_FORMAT_PREFIX, '');
      if (!dateFormat) {
        return text;
      }
      return formatToDate(text, dateFormat);
    }
    // Map
    if (isMap(format)) {
      return format.get(text);
    }
  } catch (error) {
    return text;
  }
}