import React from 'react';
import { findDOMNode } from 'react-dom';
import { Renderer, RendererProps } from '../../factory';
import { SchemaNode, Action, Schema, ResizeEvent } from '../../types';
import forEach from 'lodash/forEach';
import { filter } from '../../utils/tpl';
// import './ColumnToggler';
import Checkbox from '../../components/Checkbox';
import Button from '../../components/Button';
import { TableStore, ITableStore, IColumn, IRow, STableStore } from '../../store/table';
import { saveAs } from 'file-saver';
import { Shell } from '../../utils/shell/index';
import {
  anyChanged,
  getScrollParent,
  difference,
  noop,
  isArrayChildrenModified,
  getVariable,
  removeHTMLTag,
  eachTree,
  isObject,
  createObject,
  isMobile,
  numberFormatter,
  uuidv4,
  domUtils
} from '../../utils/helper';
import {
  isPureVariable,
  resolveVariable,
  resolveVariableAndFilter
} from '../../utils/tpl-builtin';
import debounce from 'lodash/debounce';
import Sortable from 'sortablejs';
import { resizeSensor } from '../../utils/resize-sensor';
import find from 'lodash/find';
import { Icon } from '../../components/icons';
import { TableCell } from './TableCell';
import { HeadCellFilterDropDown } from './HeadCellFilterDropdown';
import { HeadCellBatchEditDropdown } from './HeadCellBatchEditDropdown';
import { TableContent } from './TableContent';
import {
  BaseSchema,
  SchemaApi,
  SchemaClassName,
  SchemaCollection,
  SchemaObject,
  SchemaTokenizeableString
} from '../../Schema';
import { SchemaPopOver } from '../PopOver';
import { SchemaQuickEdit } from '../QuickEdit';
import { SchemaCopyable } from '../Copyable';
import { SchemaRemark } from '../Remark';
import { toDataURL, getImageDimensions } from '../../utils/image';
import { TplSchema } from '../Tpl';
import { MappingSchema } from '../Mapping';
import { isAlive, getSnapshot } from 'mobx-state-tree';
import ColumnToggler, { Temp, productColumnInfo } from './ColumnToggler';
import { BadgeSchema } from '../../components/Badge';
import offset from '../../utils/offset';
import { getStyleNumber } from '../../utils/dom';
import cloneDeep from 'lodash/cloneDeep';
import { ContextMenu } from '../Lion/components/LionContextMenu';
import isEqual from 'lodash/isEqual';
import { message, Modal, Tooltip, Button as AntButton, Drawer, Checkbox as AntCheckbox } from 'antd';
import Popover from 'antd/lib/popover';
import { tools } from '../../utils/shell/tools';
import Bubble from '../../components/ScrollMB/Bubble';
import { EventEnum, EventSub } from '../../utils/sub';
import { DATAKEYID } from '../../store/crud';
import { getColumnsFilterType } from '../../components/table/SecondFilter/types';
import { calcFn, translateNumber } from '../../utils/utils';
// import TipsContanier from '../../components/TipsContanier';
import { CaretDownOutlined, CaretUpOutlined, FileSyncOutlined, MinusSquareOutlined, PlusSquareOutlined, ProjectOutlined } from '@ant-design/icons';
import { getLocalStorage, setLocalStorage } from '../../utils/storage';
import { clone, flatMap } from 'lodash';
import { normalizeApi } from '../../utils/api';
import DataStatic from './DataStatic';
import DataCross from './DataCross';
import { uuid } from '../Lion/utils/utils';
import ProcessToolsModal from './ProcessToolsModal';
import FindAndReplace, { RowData } from './FindAndReplace';
import { ICross, buildCrossColumn, buildCrossColumn1, buildCrossData, buildCrossData1, getChangeRows } from './cross';
import tableCtxMenuStore from './tableCtxMenuStore';
import { Provider } from 'mobx-react';

import AiTool from './AiTool';
import SqlOptimize from './SqlOptimize';
import DataCharts from './DataCharts';
/**
 * 表格列，不指定类型时默认为文本类型。
 */
export type TableColumnObject = {
  /**
   * 列标题
   */
  label: string;

  /**
   * 配置是否固定当前列
   */
  fixed?: 'left' | 'right' | 'none';

  /**
   * 绑定字段名
   */
  name?: string;

  /**
   * 配置查看详情功能
   */
  popOver?: SchemaPopOver;

  /**
   * 配置快速编辑功能
   */
  quickEdit?: SchemaQuickEdit;

  /**
   * 作为表单项时，可以单独配置编辑时的快速编辑面板。
   */
  quickEditOnUpdate?: SchemaQuickEdit;

  /**
   * 配置点击复制功能
   */
  copyable?: SchemaCopyable;

  /**
   * 配置是否可以排序
   */
  sortable?: boolean;

  /**
   * 是否可快速搜索
   */
  searchable?: boolean | SchemaObject;

  /**
   * 配置是否默认展示
   */
  toggled?: boolean;

  /**
   * 列宽度
   */
  width?: number | string;

  /**
   * 列对齐方式
   */
  align?: 'left' | 'right' | 'center' | 'justify';

  /**
   * 列样式表
   */
  className?: string;

  /**
   * 单元格样式表达式
   */
  classNameExpr?: string;

  /**
   * 列头样式表
   */
  labelClassName?: string;

  /**
   * todo
   */
  filterable?:
  | boolean
  | {
    source?: string;
    options?: Array<any>;
  };

  /**
   * 结合表格的 footable 一起使用。
   * 填写 *、xs、sm、md、lg指定 footable 的触发条件，可以填写多个用空格隔开
   */
  breakpoint?: '*' | 'xs' | 'sm' | 'md' | 'lg';

  /**
   * 提示信息
   */
  remark?: SchemaRemark;

  /**
   * 默认值, 只有在 inputTable 里面才有用
   */
  value?: any;

  /**
   * 是否唯一, 只有在 inputTable 里面才有用
   */
  unique?: boolean;

  /**
   * 配置过长是否需要隐藏
   */
  ellipsis?: boolean;
};

export type TableColumnWithType = SchemaObject & TableColumnObject;
export type TableColumn = TableColumnWithType | TableColumnObject;
export interface CountItem {
  name: string, label: string, formula: string, type: string, rule: string
}

/**
 * Table 表格渲染器。
 * 文档：https://baidu.gitee.io/amis/docs/components/table
 */
export interface TableSchema extends BaseSchema {
  /**
   * 指定为表格渲染器。
   */
  type: 'table' | 'static-table';

  /**
   * 是否固定表头
   */
  affixHeader?: boolean;

  /**
   * 表格的列信息
   */
  columns?: Array<TableColumn>;

  /**
   * 展示列显示开关，自动即：列数量大于或等于5个时自动开启
   */
  columnsTogglable?: boolean | 'auto';

  /**
   * 是否开启底部展示功能，适合移动端展示
   */
  footable?:
  | boolean
  | {
    expand?: 'first' | 'all' | 'none';

    /**
     * 是否为手风琴模式
     */
    accordion?: boolean;
  };

  /**
   * 底部外层 CSS 类名
   */
  footerClassName?: SchemaClassName;

  /**
   * 顶部外层 CSS 类名
   */
  headerClassName?: SchemaClassName;

  /**
   * 占位符
   */
  placeholder?: string;

  /**
   * 是否显示底部
   */
  showFooter?: boolean;

  /**
   * 是否显示头部
   */
  showHeader?: boolean;

  /**
   * 数据源：绑定当前环境变量
   */
  source?: SchemaTokenizeableString;

  /**
   * 表格 CSS 类名
   */
  tableClassName?: SchemaClassName;

  /**
   * 标题
   */
  title?: string;

  /**
   * 工具栏 CSS 类名
   */
  toolbarClassName?: SchemaClassName;

  /**
   * 合并单元格配置，配置数字表示从左到右的多少列自动合并单元格。
   */
  combineNum?: number;

  /**
   * 合并单元格配置，配置从第几列开始合并。
   */
  combineFromIndex?: number;

  /**
   * 顶部总结行
   */
  prefixRow?: Array<SchemaObject>;

  /**
   * 底部总结行
   */
  affixRow?: Array<CountItem>;

  /**
   * 总结行位置
   * 0合并显示，1对齐单元格显示
   */
  affixRowPosition?: 0 | 1;
  /**
   * 是否可调整列宽
   */
  resizable?: boolean;

  /**
   * 行样式表表达式
   */
  rowClassNameExpr?: string;

  /**
   * 行角标
   */
  itemBadge?: BadgeSchema;

  /**
   * 开启查询区域，会根据列元素的searchable属性值，自动生成查询条件表单
   */
  autoGenerateFilter?: boolean;
  // Jay
  // 保存列配置接口
  saveColApi?: SchemaApi;
  columnInfo?: Record<string, { index: number; hidden: number }>; // 列排序、隐藏配置
  /**
   * 表格布局方式
   */
  tableLayout: 'vertical' | 'horizontal';

  /**
  * 为当前表单添加排序
  */
  showIndex?: boolean;
  /**
   * 交叉制表的合计列
   */
  countField?: Array<CountItem>;
}

export interface TableProps extends RendererProps {
  title?: string; // 标题
  header?: SchemaNode;
  footer?: SchemaNode;
  actions?: Action[];
  className?: string;
  headerClassName?: string;
  footerClassName?: string;
  store: ITableStore;
  columns?: Array<any>;
  // originColumns?: Array<any>;
  headingClassName?: string;
  toolbarClassName?: string;
  headerToolbarClassName?: string;
  footerToolbarClassName?: string;
  tableClassName?: string;
  source?: string;
  selectable?: boolean;
  selected?: Array<any>;
  maxKeepItemSelectionLength?: number;
  valueField?: string;
  draggable?: boolean;
  columnsTogglable?: boolean | 'auto';
  affixHeader?: boolean;
  affixColumns?: boolean;
  combineNum?: number | string;
  combineFromIndex?: number;
  showIndex: boolean;
  footable?:
  | boolean
  | {
    expand?: 'first' | 'all' | 'none';
    expandAll?: boolean;
    accordion?: boolean;
  };
  expandConfig?: {
    expand?: 'first' | 'all' | 'none';
    expandAll?: boolean;
    accordion?: boolean;
  };
  itemCheckableOn?: string;
  itemDraggableOn?: string;
  itemActions?: Array<Action>;
  onSelect: (
    selectedItems: Array<object>,
    unSelectedItems: Array<object>
  ) => void;
  onPristineChange?: (data: object, rowIndexe: string) => void;
  onSave?: (
    items: Array<object> | object,
    diff: Array<object> | object,
    rowIndexes: Array<string> | string,
    unModifiedItems?: Array<object>,
    rowOrigins?: Array<object> | object,
    resetOnFailed?: boolean,
    shouldHideLoaing?: boolean
  ) => void;
  dataCheckRegist?: (fn: any) => void;
  onSaveOrder?: (moved: Array<object>, items: Array<object>) => void;
  onQuery: (values: object) => void;
  onImageEnlarge?: (data: any, target: any) => void;
  buildItemProps?: (item: any, index: number) => any;
  checkOnItemClick?: boolean;
  hideCheckToggler?: boolean;
  rowClassName?: string;
  rowClassNameExpr?: string;
  popOverContainer?: any;
  canAccessSuperData?: boolean;
  reUseRow?: boolean;
  itemBadge?: BadgeSchema;
  onLoadMore: () => void;
  handleResetData: () => void;
  setChildStore?: (store: any) => void;
  setLoading?: (loading: boolean) => void;
  handleMutilSort?: (orderColumns: Map<string, any>, filterColumns?: Map<string, any>, loadDataOnce?: boolean, modifiedData?: any) => void;
  preSortAble?: boolean;
  infinteLoad?: boolean;
  renderFilter?: (val?: boolean) => JSX.Element | null;
  clearSelectedItems?: () => void;
  changeSelectedRow?: (index: number) => void;
  showColumnsFilter: boolean;
  changeTableMode?: (mode: 'vertical' | 'horizontal') => void;
  filterData?: any;
  getAllData: () => Promise<any>;
  currentSelectedRow: number;
  cross?: ICross;
  isSqlData?: boolean;//是否是数据查询
  renderAggregate?: () => JSX.Element
}

// 本地编辑map
export const EDITWIDTHKEY = 'localEditWidthMap'

type ExportExcelToolbar = SchemaNode & {
  api?: SchemaApi;
  columns?: string[];
  filename?: string;
};

/**
 * 将 url 转成绝对地址
 */
const getAbsoluteUrl = (function () {
  let link: HTMLAnchorElement;
  return function (url: string) {
    if (!link) link = document.createElement('a');
    link.href = url;
    return link.href;
  };
})();

type CheckAllType = 0 | 1 | 2 | 3;

interface TableState {
  // position: [rowIndex: number, colName: string];
  // contextMenuVisible: boolean;
  pullY: number;
  rotateX: number;
  rotateY: number;
  offlineX: number;
  offlineY: number;
  indexColShow: boolean;//控制是否显示索引列
  columnsTogglerShow: boolean;
  toolbarShow: boolean;
  tableMode: 'vertical' | 'horizontal';
  columnSettingTemps: Temp[];
  selectedIndex?: number;
  processToolsModalList: Array<{ key: string, label: string, schema: any, toolType: string, subTitle?: string }>;
  currentKey: string;
  activeCol?: string;
  activeRow?: number;
  fieldTranslate: boolean;//显示字段翻译
  showAiTool?: boolean
}

export default class Table extends React.Component<TableProps, TableState> {
  static propsList: Array<string> = [
    'header',
    'headerToolbarRender',
    'footer',
    'footerToolbarRender',
    'footable',
    'expandConfig',
    'placeholder',
    'tableClassName',
    'headingClassName',
    'source',
    'selectable',
    'columnsTogglable',
    'affixHeader',
    'affixColumns',
    'headerClassName',
    'footerClassName',
    'selected',
    'multiple',
    'primaryField',
    'hideQuickSaveBtn',
    'itemCheckableOn',
    'itemDraggableOn',
    'checkOnItemClick',
    'hideCheckToggler',
    'itemAction',
    'itemActions',
    'combineNum',
    'combineFromIndex',
    'items',
    'columns',
    'valueField',
    'saveImmediately',
    'rowClassName',
    'rowClassNameExpr',
    'popOverContainer',
    'headerToolbarClassName',
    'toolbarClassName',
    'footerToolbarClassName',
    'itemBadge',
    'autoFillHeight',
    'showIndex',
    'affixRowPosition',
    'crudRenderToolbarFunc'
  ];
  static defaultProps: Partial<TableProps> = {
    className: '',
    placeholder: 'placeholder.noData',
    tableClassName: '',
    source: '$items',
    selectable: false,
    // columnsTogglable: 'auto', //Aug
    columnsTogglable: false,
    affixHeader: true,
    headerClassName: '',
    footerClassName: '',
    toolbarClassName: '',
    headerToolbarClassName: '',
    footerToolbarClassName: '',
    primaryField: 'id',
    itemCheckableOn: '',
    itemDraggableOn: '',
    // hideCheckToggler: false,
    hideCheckToggler: isMobile(), //Aug
    footable: isMobile(), // Aug 移动端开启底部查看详情，默认只展示6列
    canAccessSuperData: false,
    resizable: true,
    tableLayout: isMobile() ? 'vertical' : 'horizontal',
    infinteLoad: isMobile(),
    showIndex: false,
    affixRowPosition: 0
  };
  tableId = uuid();
  table?: HTMLTableElement | null;
  sortable?: Sortable;
  dragTip?: HTMLElement;
  affixedTable?: HTMLTableElement;
  parentNode?: HTMLElement | Window;
  lastScrollLeft: number = -1;
  lastScrollTop: number = -1;
  totalWidth: number = 0;
  totalHeight: number = 0;
  outterWidth: number = 0;
  outterHeight: number = 0;
  unSensor?: Function;
  updateTableInfoLazy: () => void;
  widths: {
    [propName: string]: number;
  } = {};
  widths2: {
    [propName: string]: number;
  } = {};
  heights: {
    [propName: string]: number;
  } = {};
  renderedToolbars: Array<string> = [];
  subForms: any = {};
  sortCols: any[];
  tableWindowRef: React.RefObject<any>
  constructor(props: TableProps) {
    super(props);
    this.handleOutterScroll = this.handleOutterScroll.bind(this);
    this.affixDetect = this.affixDetect.bind(this);
    this.updateTableInfoLazy = debounce(this.updateTableInfo.bind(this), 250, {
      trailing: true,
      leading: true
    });
    this.tableRef = this.tableRef.bind(this);
    this.affixedTableRef = this.affixedTableRef.bind(this);
    this.handleAction = this.handleAction.bind(this);
    this.handleCheck = this.handleCheck.bind(this);
    this.handleCheckAll = this.handleCheckAll.bind(this);
    this.handleQuickChange = this.handleQuickChange.bind(this);
    this.handleSave = this.handleSave.bind(this);
    this.handleSaveOrder = this.handleSaveOrder.bind(this);
    this.reset = this.reset.bind(this);
    this.dragTipRef = this.dragTipRef.bind(this);
    this.getPopOverContainer = this.getPopOverContainer.bind(this);
    this.renderCell = this.renderCell.bind(this);
    this.renderHeadCell = this.renderHeadCell.bind(this);
    this.renderToolbar = this.renderToolbar.bind(this);
    this.handleMouseMove = this.handleMouseMove.bind(this);
    this.handleMouseLeave = this.handleMouseLeave.bind(this);
    this.handleColumnToggle = this.handleColumnToggle.bind(this);
    this.renderAutoFilterForm = this.renderAutoFilterForm.bind(this);
    this.updateAutoFillHeight = this.updateAutoFillHeight.bind(this);
    // Jay
    this.updateAutoFillHeightTimes = 0;
    this.handleColumns.bind(this);
    this.handleMultiColumnSort = this.handleMultiColumnSort.bind(this);
    this.props.getTableStore?.(this.props.store);
    this.props.getTableInstance?.(this);
    this.handleModleColumn = this.handleModleColumn.bind(this);
    // 父级容器注册方法
    this.props.dataCheckRegist?.(this.handleModleColumn)
    this.tableWindowRef = React.createRef()
    this.state = {
      // position: [0, ''],
      // contextMenuVisible: false,
      pullY: 0,
      rotateX: 24,
      rotateY: 24,
      offlineX: 24,
      offlineY: props.tableLayout == 'horizontal' ? 84 : 24,
      indexColShow: !!props.showIndex,
      columnsTogglerShow: false,
      toolbarShow: false,
      tableMode: props.tableLayout,
      columnSettingTemps: [],
      processToolsModalList: [],
      currentKey: '',
      activeCol: undefined,
      activeRow: undefined,
      fieldTranslate: false,
      showAiTool: false
    };


    const {
      store,
      columns,
      selectable,
      columnsTogglable,
      draggable,
      orderBy,
      orderDir,
      multiple,
      footable,
      primaryField,
      itemCheckableOn,
      itemDraggableOn,
      hideCheckToggler,
      combineFromIndex,
      expandConfig,
      formItem,
      keepItemSelectionOnPageChange,
      maxKeepItemSelectionLength,
      isPick,
      showIndex,
      tableLayout
    } = props;
    let combineNum = props.combineNum;
    if (typeof combineNum === 'string') {
      combineNum = parseInt(
        resolveVariableAndFilter(combineNum, props.data, '| raw'),
        10
      );
    }
    // Aug
    const mobileUI = isMobile();
    // Aug 移动端默认只展示6项
    let _columns: any[] = columns || [];
    if (
      mobileUI &&
      tableLayout === 'vertical' &&
      footable &&
      columns &&
      columns.length > 6
    ) {
      _columns.map((item: any, index: number) => {
        if (index > 5) {
          item.breakpoint = '*';
        }
      });
    }
    this.sortCols = this.handleColumns(_columns, {});
    store.update({
      selectable,
      draggable,
      columns: this.sortCols, // Aug
      rawColumns: this.handleColumns(_columns, {
        sort: false
      }), // Jay
      columnsTogglable,
      orderBy,
      orderDir,
      multiple,
      showIndex,
      footable,
      expandConfig,
      primaryField,
      itemCheckableOn,
      itemDraggableOn,
      hideCheckToggler: isMobile() && isPick ? false : hideCheckToggler,
      combineNum,
      combineFromIndex,
      keepItemSelectionOnPageChange,
      maxKeepItemSelectionLength,
      mobileUI, //Aug
      orderColumns: new Map(),
      filterColumns: new Map(),
      tableLayout,
      columnsInfo: this.props.columnInfo
    });
    // debugger

    formItem && isAlive(formItem) && formItem.setSubStore(store);
    Table.syncRows(store, this.props, undefined).then(res => { res && this.syncSelected() })
  }

  setIndexCol = (show: boolean) => {
    this.setState({ indexColShow: show })
  }

  // Jay
  handleColumns(
    columns: any[],
    options: {
      sort?: boolean;
    }
  ) {
    const { sort = true } = options || {};
    let handleCols = cloneDeep(columns);
    const { store } = this.props
    const columnInfo = store.columnsInfo && Object.keys(store.columnsInfo).length ? store.columnsInfo : this.props.columnInfo;
    const { foldColumns } = this.props;
    // columns的长度比columnInfo的长度大时，不进行排序
    // const flag = columnInfo && (Object.keys(columnInfo).length <= handleCols.length)
    if (columnInfo && Object.keys(columnInfo).length && sort) {
      let tempArr: any[] = [];
      const tempArrNofixed: any[] = [];
      const tempArrFixedLeft: any[] = [];
      const tempArrFixedRight: any[] = [];

      handleCols.forEach(col => {
        // 如果column的hidden是true，则不允许被设置
        if (col?.hidden) col.canSet = false;
        if (col.name && columnInfo[col.name]) {
          if (columnInfo[col.name]?.hidden === 0) {
            col.hidden = col?.hidden ?? false;
          } else {
            col.hidden = col?.hidden ?? true;
          }
          if (columnInfo[col.name]?.fixed) {
            col.fixed = columnInfo[col.name]?.fixed;
          }
          tempArr[columnInfo[col.name].index] = col;
        } else {
          if (col.fixed === 'left') {
            tempArrFixedLeft.push(col);
          } else if (col.fixed === 'right') {
            tempArrFixedRight.push(col);
          } else {
            tempArrNofixed.push(col);
          }
        }
      });

      tempArr = tempArr.filter(Boolean);
      let fixedRightBefore = tempArr.length;
      for (let i = tempArr.length - 1; i >= 0; i--) {
        if (tempArr[i].fixed !== 'right') {
          fixedRightBefore = i + 1;
          break;
        }
      }

      tempArr.splice(fixedRightBefore, 0, ...tempArrNofixed);
      tempArr = tempArrFixedLeft.concat(tempArr.concat(tempArrFixedRight));
      handleCols = tempArr;
    } else {
      handleCols.forEach(col => {
        // 如果column的hidden是true，则不允许被设置
        if (col?.hidden) col.canSet = false;
      });
    }
    // 列折叠
    const mobileUI = isMobile();
    if (!mobileUI) {
      for (let i = handleCols.length - 1; i >= 0; i--) {
        const item = handleCols[i];
        if (item?.type === 'operation') {
          item.style = { overFlow: 'hidden' };
          item.label = [
            {
              type: 'button',
              label: '',
              size: 'xs',
              level: 'link',
              icon: foldColumns?.includes('scale' + item.name)
                ? 'fa fa-step-backward'
                : 'fa fa-step-forward',
              actionType: 'scale' + item.name
            },
            item.label
          ];
          if (i < handleCols.length - 4) break;
        }
      }
    }
    // 本地编辑列宽字段
    const localEditWidthMap = getLocalStorage(EDITWIDTHKEY)?.[this.props.crudName]

    return handleCols.map(_ => {
      if (localEditWidthMap?.[_.name]) {
        _.editWidth = localEditWidthMap?.[_.name]
      }
      return _
    });
  }

  static async syncRows(
    store: ITableStore,
    props: TableProps,
    prevProps?: TableProps
  ) {
    const source = props.source;
    const value = props.value || props.items;
    let rows: Array<object> = [];
    let updateRows = false;
    if (
      Array.isArray(value) &&
      (!prevProps || (prevProps.value || prevProps.items) !== value)
    ) {
      updateRows = true;
      rows = value;
    } else if (typeof source === 'string') {
      const resolved = resolveVariableAndFilter(source, props.data, '| raw');
      const prev = prevProps
        ? resolveVariableAndFilter(source, prevProps.data, '| raw')
        : null;

      if (prevProps?.type !== props.type) {
        updateRows = true
        rows = resolved
      } else if (prev && prev === resolved) {
        updateRows = false;
      } else if (Array.isArray(resolved)) {
        updateRows = true;
        rows = resolved;
      }
    }
    if (updateRows) {
      const rawDatas = [...(props.items ?? [])]
      if (props.cross && props.type == 'cross' && rawDatas.length > 0) {
        const columns = store.rawColumns.filter(column => column.name)
        const rowFields = flatMap(props.cross.rowFields ?? [], field => {
          const target = columns.find(column => column.name === field.name)
          return target ? Object.assign(field, target) : []
        })
        const colFields = flatMap(props.cross.columnFields ?? [], field => {
          const target = columns.find(column => column.name === field.name)
          return target ? Object.assign(field, target) : []
        })
        const valueFields = flatMap(props.cross.valueFields.split(',') ?? [], field => {
          const target = columns.find(column => column.name === field)
          return target ? Object.assign({ name: field }, target) : []
        })
        props.setLoading?.(true)
        const crossColumns = props.crossColumns ?? (props.cross.positionType == 0 ? buildCrossColumn(rowFields, colFields, valueFields, rawDatas, false) : buildCrossColumn1(rowFields, colFields, rawDatas))
        const crossDatas = props.cross.positionType == 0 ? await buildCrossData(rowFields, colFields, crossColumns, rawDatas, props.affixRow?.length > 0) : buildCrossData1(rowFields, colFields, valueFields, crossColumns, rawDatas)
        props.setLoading?.(false)
        rows = crossDatas.map((_: any) => ({ ..._, [DATAKEYID]: _[DATAKEYID] || uuidv4() }))
        store.updateColumns(crossColumns)
        store.reInitData({
          page: 1,
          perPage: props.perPage || 10000,
          count: rows.length,
          total: rows.length,
          items: rows,
          itemsRaw: rows,
          dataSetId: uuidv4(),
          sortItemsRaw: rows,
          ...props.query
        })
      } else if (props.cross && props.type == 'table') {
        store.updateColumns(props.columns!)
        rows = rawDatas
        store.reInitData({
          count: rows.length,
          total: rows.length,
          items: rows,
          itemsRaw: rows,
          dataSetId: uuidv4()
        })
      }
      store.initRows(rows, props.getEntryId, props.reUseRow, { caculateWidth: props.autoWidth, autoUnfold: props?.tree?.unfoldedLevel && (props.tree.initiallyOpen > 0 ? props.tree.initiallyOpen : Infinity) });
    }
    typeof props.selected !== 'undefined' &&
      store.updateSelected(props.selected, props.valueField);
    return updateRows;
  }
  caculateLeft(force = false): any {
    const thArray = Array.from(this.table?.querySelectorAll('table>thead>tr>th') || [])
    // 一样了就不重新计算了
    if (thArray.length === this.leftDistances?.length && !force) return
    const leftDistances: number[] = []
    const offsetWidths = thArray.map((_: any) => _.offsetWidth)
    offsetWidths.map((offsetWidth, index) =>
      leftDistances.push((leftDistances[index - 1] || 0) + (offsetWidths[index - 1] || 0))
    )
    this.leftDistances = leftDistances
  }
  componentDidMount() {
    let parent: HTMLElement | Window | null = getScrollParent(
      findDOMNode(this) as HTMLElement
    );
    this.caculateLeft()
    if (!parent || parent === document.body) {
      parent = window;
    }


    this.parentNode = parent;
    this.updateTableInfo();

    const dom = findDOMNode(this) as HTMLElement;
    // 这个主要给外部store做引用用的,因为不是组件自身的功能，所以有可能会没有故增加容错
    this.props.setChildStore?.(this.props.store)
    if (dom.closest('.modal-body')) {
      return;
    }
    const { classPrefix: ns } = this.props;
    const tableContentWrap = dom.querySelector(
      `.${ns}Table-contentWrap`
    ) as HTMLElement;
    tableContentWrap?.addEventListener('scroll', this.scrollPingBox);
    this.affixDetect();
    parent?.addEventListener('scroll', this.affixDetect);
    window.addEventListener('resize', this.affixDetect);
    this.updateAutoFillHeight();
    window.addEventListener('resize', this.updateAutoFillHeight);
    document.body.addEventListener(ResizeEvent.DIALOGRESIZEENDEVENT, this.updateAutoFillHeight);
    this.getColumnSettingList()
  }

  scrollPingBoxContainer: any

  scrollPingBox = (e: any) => {
    if (!this.table) return;
    const { classPrefix: ns } = this.props;
    const table = findDOMNode(this) as HTMLElement;
    // 如果没有不重新获取drag
    const container = this.scrollPingBoxContainer || table.querySelector(
      `.${ns}Table-contentWrap`
    ) as HTMLElement;
    this.scrollPingBoxContainer = container
    const scrollLeft = container.scrollLeft;
    const scrollWidth = container.scrollWidth;
    const clientWidth = container.clientWidth;

    // 判断是否滚动到最左侧
    if (scrollLeft === 0) {
      container.classList.remove('table-ping-left');
    } else {
      container.classList.add('table-ping-left');
    }

    // 判断是否滚动到最右侧
    if (Math.abs(scrollLeft + clientWidth - scrollWidth) <= 1) {
      container.classList.remove('table-ping-right');
    } else {
      container.classList.add('table-ping-right');
    }
  }

  mutationObserver: MutationObserver | null
  // contetnt高度更新的节流标志-防止瞬间多次不同高度撑开容器导致高度混乱
  contetnHighUpdateTimer: any
  updateAutoFillHeightTimes: number; // Jay 记录次数
  /**
   * 自动设置表格高度占满界面剩余区域
   * 用 css 实现有点麻烦，要改很多结构，所以先用 dom hack 了，避免对之前的功能有影响
   */
  updateAutoFillHeight() {
    const { autoFillHeight, footerToolbar, classPrefix: ns, isPick, footer, isStatic, type, tableLayout } = this.props;
    const isCross = type === 'cross'
    if (!this.table && !isMobile()) return;
    const table = findDOMNode(this) as HTMLElement;
    const tableContent = table.querySelector(
      `.${ns}Table-content`
    ) as HTMLElement;
    const tableContentWrap = table.querySelector(
      `.${ns}Table-contentWrap`
    ) as HTMLElement;
    const footToolbar = table.querySelector(
      `.${ns}Table-footToolbar`
    ) as HTMLElement;
    const tableFooter = table.querySelector(
      `.${ns}Table-footer`
    ) as HTMLElement;
    const tableHeading = table.querySelector(
      `.${ns}Table-heading`
    ) as HTMLElement;
    if (isMobile()) {
      //tab页平铺模式样式处理
      const tabsDom = table.closest(`.${ns}Tabs--content-tiled`);
      if (tabsDom) {
        // 如果是移动端的平铺模式，则限制最大高度，让一个tab的内容尽量铺满一屏
        tableContent.style.maxHeight = '55vh';
        if (tableLayout === 'vertical') {
          tableContent.style.minHeight = '250px'
        }
        return;
      }
      //在picker中，使用的是modal，高度与普通的计算方式不同
      if (isPick) {
        const tableHeight = document.body.clientHeight - offset(table).top;
        table.style.height = tableHeight - 49 - 16 + 'px';
        return;
      }
      let distanceClass = table.closest(`.${ns}Distance`);
      if (distanceClass) {
        //在grid中占满grid的高度
        (table.closest(`.${ns}Crud`) as HTMLDivElement).style.height = '100%';
        table.style.height = '100%';
      } else {
        //如果是行按钮打开的抽屉，可能会有footer，要把footer高度减掉
        const drawerFooter = table.closest(`.${ns}Drawer-content`)?.querySelector(`.${ns}Drawer-footer`);
        // 循环计算父级节点的 pddding，这里不考虑父级节点还可能会有其它兄弟节点的情况了
        let allParentPaddingButtom = 0;
        let parentNode = tableContent.parentElement;
        while (parentNode) {
          const paddingButtom = getStyleNumber(parentNode, 'padding-bottom');
          const borderBottom = getStyleNumber(
            parentNode,
            'border-bottom-width'
          );
          allParentPaddingButtom =
            allParentPaddingButtom + paddingButtom + borderBottom;
          parentNode = parentNode.parentElement;
        }
        let tableHeight =
          document.body.clientHeight -
          offset(table).top -
          (drawerFooter ? drawerFooter.clientHeight + 1 : 0) -
          allParentPaddingButtom;
        // tab情况下，后面的table由于display：none，所以offset(table).top为0，所以继承最小的那个
        if (offset(table).top === 0) {
          const tabsContent = table.closest(`.${ns}Tabs-content`) as HTMLDivElement;
          tableHeight =
            document.body.clientHeight -
            offset(tabsContent).top -
            (drawerFooter ? drawerFooter.clientHeight + 1 : 0) -
            allParentPaddingButtom - 24;//多出来的24是tabs-pane的padding
        }
        if (autoFillHeight && tableHeight >= 250) {
          //当表格可展示高度大于250的占满剩余空间
          table.style.height = tableHeight + 'px';
          table.style.maxHeight = 'unset';
        } else {
          table.style.maxHeight = document.body.clientHeight - 38 + 'px';
          if (tableLayout === 'vertical') {
            tableContent.style.minHeight = '250px';
          }
        }
      }
    } else {
      if (!autoFillHeight) return;
      let distanceClass = table.closest(`.${ns}Distance`);
      if (distanceClass) {
        //在grid中占满grid的高度
        (table.closest(`.${ns}Crud`) as HTMLDivElement).style.height = '100%';
        table.style.height = '100%';
        return;
      }
      //tab页平铺模式样式处理
      const tabsDom = table.closest(`.${ns}Tabs--content-tiled`);
      if (tabsDom) {
        tableContent.style.minHeight = '250px'
        return
      };
      if (!tableContent) {
        return;
      }
      const viewportHeight = window.innerHeight;
      const tableContentHeading = tableHeading ? offset(tableHeading).height : 0;
      // 有时候会拿不到 footToolbar？
      let footToolbarHeight = footToolbar ? offset(footToolbar).height : 0;
      let tableFooterHeight = tableFooter ? offset(tableFooter).height : 0;

      if (!footToolbarHeight && footerToolbar && footerToolbar.length) {
        footToolbarHeight = 48;
      }
      if (!tableFooterHeight && footer && (!Array.isArray(footer) || footer.length)) {
        tableFooterHeight = 25;
      }
      const hasfooter = this.renderFooter();
      if (!hasfooter) {
        footToolbarHeight = 0;
        tableFooterHeight = 0;
      }
      const tableContentWrapMarginButtom = getStyleNumber(
        tableContentWrap,
        'margin-bottom'
      );
      // 循环计算父级节点的 pddding，这里不考虑父级节点还可能会有其它兄弟节点的情况了
      let allParentPaddingButtom = 0;
      let parentNode = tableContent.parentElement;
      while (parentNode) {
        const paddingButtom = getStyleNumber(parentNode, 'padding-bottom');
        const borderBottom = getStyleNumber(parentNode, 'border-bottom-width');
        allParentPaddingButtom =
          allParentPaddingButtom + paddingButtom + borderBottom;
        parentNode = parentNode.parentElement;
      }
      const getContentViewHeight = () => {
        // 计算 table-content 在 dom 中的位置
        const tableContentTop = offset(tableContent).top;
        // console.log('高度参数比较', {
        //   viewportHeight,
        //   tableContentTop,
        //   tableContentWrapMarginButtom,
        //   footToolbarHeight,
        //   tableContentHeading,
        //   allParentPaddingButtom,
        //   others: 12 - (this.props.inModal ? 48 : 0)
        // })
        return viewportHeight -
          tableContentTop -
          tableContentWrapMarginButtom -
          footToolbarHeight -
          tableFooterHeight -
          tableContentHeading -
          allParentPaddingButtom -
          20 - (this.props.inModal ? 48 : 0)
      }
      tableContent.style.height = `${getContentViewHeight()}px`;
      tableContent.style.minHeight = '250px';
      if (isStatic || isCross) {
        const Content = table?.closest('[class*="modal-content"]')
        const Head = Content?.querySelector('[class*="modal-header"]')
        const Container = ((Content?.getBoundingClientRect().height || 0) - (Head?.getBoundingClientRect().height || 0))
        const footHeight = table.querySelector('[class*="Table-toolbar"]')?.clientHeight || 0;
        const staticHeight = Container - 48 - 48 - footHeight;
        tableContent.style.height = staticHeight + 'px';
      }
      const MutationObserver = window.MutationObserver;
      this.mutationObserver = new MutationObserver((e) => {
        // 对高度更新进行节流-防止瞬间多次不同高度撑开容器导致高度混乱
        clearTimeout(this.contetnHighUpdateTimer)
        const tableContentDom: any = this.tableContainer.current?.querySelector(`.${ns}Table-content`)
        this.contetnHighUpdateTimer = setTimeout(() => {
          if (tableContent && tableContentDom) {
            tableContentDom.style.height = `${getContentViewHeight()}px`;
            tableContentDom.style.minHeight = '250px'
          }
        }, 10)
      });

      const formToolbar = table.previousElementSibling;
      if (formToolbar && !isStatic && !isCross) {
        this.mutationObserver.observe(formToolbar as HTMLElement, {
          attributes: true,
          childList: true
        });
      }

      const tableToolbar = table.querySelector('[class*="Table-toolbar"]');
      if (tableToolbar && !isStatic && !isCross) {
        this.mutationObserver.observe(tableToolbar as HTMLElement, {
          attributes: true,
          childList: true,
          subtree: true
        });
      }

      if (table && !isStatic && !isCross) {
        this.mutationObserver.observe(table as HTMLElement, {
          childList: true
        });
      }


      let DistanceClass = table.closest(`.${ns}Distance`);
      let tableContents = DistanceClass?.querySelectorAll(
        `.${ns}Table-content`
      );
      if (tableContents && tableContents?.length > 1) {
        let tablea = tableContents[1] as HTMLElement;
        let tableb = tableContents[0] as HTMLElement;
        tablea.style.height = tableb.style.height;
      }
    }
  }

  componentDidUpdate(prevProps: TableProps, prevState: TableState) {
    const props = this.props;
    const store = props.store;
    this.caculateLeft()

    // 默认的更新属性
    const defaultUpdateProps: Partial<STableStore> = {
      selectable: props.selectable,
      columnsTogglable: props.columnsTogglable,
      draggable: props.draggable,
      orderBy: props.orderBy,
      orderDir: props.orderDir,
      multiple: props.multiple,
      showIndex: props.showIndex,
      primaryField: props.primaryField,
      footable: props.footable,
      itemCheckableOn: props.itemCheckableOn,
      itemDraggableOn: props.itemDraggableOn,
      hideCheckToggler: props.hideCheckToggler,
      combineNum: props.combineNum as any,
      combineFromIndex: props.combineFromIndex,
      expandConfig: props.expandConfig
    }

    if (
      anyChanged(
        [
          'selectable',
          'columnsTogglable',
          'draggable',
          'orderBy',
          'orderDir',
          'multiple',
          'footable',
          'primaryField',
          'itemCheckableOn',
          'itemDraggableOn',
          'hideCheckToggler',
          'combineNum',
          'combineFromIndex',
          'expandConfig'
        ],
        prevProps,
        props
      )
    ) {
      if (typeof props.combineNum === 'string') {
        const combineNum = parseInt(
          resolveVariableAndFilter(props.combineNum, props.data, '| raw'),
          10
        );
        defaultUpdateProps.combineNum = combineNum
      }

      store.update(defaultUpdateProps);
    }

    if (prevProps.columns !== props.columns) {
      this.sortCols = this.handleColumns(props.columns || [], {});
      store.update({
        ...defaultUpdateProps,
        columns: this.sortCols,
        rawColumns: this.handleColumns(props.columns || [], { sort: false })
      });
    }

    // 自动列宽更新
    // if (prevProps.autoWidth !== props.autoWidth && props.autoWidth) {
    //   this.props.store.setTextWidth(this.props.store.rows.map(_ => _.data))
    // }

    if (prevProps.foldColumns !== props.foldColumns) {
      store.updateOperation(props.foldColumns);
    }
    // 数据源id更新
    if (prevProps.data.dataSetId !== props.data.dataSetId) {
      store.updateDataSetId(props.data.dataSetId)
    }

    if (
      anyChanged(['source', 'value', 'items', 'type'], prevProps, props) ||
      (!props.value &&
        !props.items &&
        (props.data !== prevProps.data ||
          (typeof props.source === 'string' && isPureVariable(props.source))))
    ) {
      if (this.props.isPick) {
        this.updateAutoFillHeight()
      }
      Table.syncRows(store, props, prevProps).then(res => {
        if (res) {
          const selectedTimer = setTimeout(() => {
            this.syncSelected();
            clearTimeout(selectedTimer)
          }, 0)
        }
      })
    } else if (isArrayChildrenModified(prevProps.selected!, props.selected!)) {
      const prevSelectedRows = store.selectedRows
        .map(item => item.id)
        .join(',');
      store.updateSelected(props.selected || [], props.valueField);
      const selectedRows = store.selectedRows.map(item => item.id).join(',');
      prevSelectedRows !== selectedRows && this.syncSelected();
    }
    //解决tabs一次性加载时，后面的table高度计算时获取不到在dom中的定位导致高度计算错误
    if (props.tabsdefer !== prevProps.tabsdefer) {
      this.updateAutoFillHeight()
    }
    if (this.state.processToolsModalList.length > 0 && prevState.processToolsModalList.length == 0) {
      this.updateAutoFillHeight()
    }
    // 更新预存值得缓存-
    this.updateTableInfoLazy();
  }

  componentWillUnmount() {
    const { formItem, store, classPrefix: ns } = this.props;
    const parent = this.parentNode;
    // EventSub.off(EventEnum.ClearVistiMap) // 取消表格订阅
    // EventSub.off(EventEnum.ShowVistiMap) // 取消表格订阅
    const dom = findDOMNode(this) as HTMLElement;
    const tableContentWrap = dom.querySelector(
      `.${ns}Table-contentWrap`
    ) as HTMLElement;
    tableContentWrap?.removeEventListener('scroll', this.scrollPingBox);
    parent && parent?.removeEventListener('scroll', this.affixDetect);
    delete this.parentNode  // 解除引用
    for (const key in this.subForms) {
      delete this.subForms[key]
    }
    window.removeEventListener('resize', this.affixDetect);
    window.removeEventListener('resize', this.updateAutoFillHeight);
    document.body.removeEventListener(ResizeEvent.DIALOGRESIZEENDEVENT, this.updateAutoFillHeight);
    this.mutationObserver?.disconnect();// 移除监听
    this.mutationObserver = null;
    (this.updateTableInfoLazy as any).cancel();
    this.table = null
    this.unSensor && this.unSensor();
    formItem && isAlive(formItem) && formItem.setSubStore(null);
    this.destroyDragTable()
    try {
      store.orderColumns.clear()
      store.filterColumns.clear()
    } catch { }
  }

  subFormRef = (form: any, x: number, y: number) => {
    const { quickEditFormRef } = this.props;

    quickEditFormRef && quickEditFormRef(form, x, y);
    this.subForms[`${x}-${y}`] = form;
    form && this.props.store.addForm(form.props.store, y);
  }

  handleAction(e: React.UIEvent<any>, action: Action, ctx: object | null, isItemAction: boolean = false) {
    const { onAction } = this.props;
    // todo
    onAction(e, action, ctx, undefined, undefined, isItemAction);
  }

  handleCheck(item: IRow, value: boolean, shift?: boolean) {
    const { store } = this.props;
    if (shift) {
      store.toggleShift(item);
    } else {
      item.toggle();
    }
    this.syncSelected();
  }

  // 0全选 1反选 2不选
  handleCheckAll(type: CheckAllType) {
    const { store } = this.props;
    switch (type) {
      case 0:
        store.checkAll();
        break;
      case 1:
        store.checkReverse();
        break;
      case 2:
        store.clear();
        break;
      case 3:
        store.toggleAll();
        break;
      default:
        return;
    }
    this.syncSelected();
  }

  handleQuickChange(
    item: IRow,
    values: object,
    saveImmediately?: boolean | any,
    savePristine?: boolean,
    resetOnFailed?: boolean
  ) {
    if (!isAlive(item)) {
      return;
    }

    const {
      onSave,
      onPristineChange,
      saveImmediately: propsSaveImmediately,
      store,
      primaryField
    } = this.props;

    item.change(values, savePristine);
    store.recordEditValues(item, values)

    // 值发生变化了，需要通过 onSelect 通知到外面，否则会出现数据不同步的问题
    item.modified && this.syncSelected();

    if (savePristine) {
      onPristineChange?.(item.data, item.path);
      return;
    } else if (!saveImmediately && !propsSaveImmediately) {
      return;
    }

    if (saveImmediately && saveImmediately.api) {
      this.props.onAction(
        null,
        {
          actionType: 'ajax',
          api: saveImmediately.api
        },
        values
      );
      return;
    }

    if (!onSave) {
      return;
    }

    onSave(
      item.data,
      difference(item.data, item.pristine, ['id', primaryField]),
      item.path,
      undefined,
      item.pristine,
      resetOnFailed
    );
  }

  async handleSave() {
    const { store, onSave, primaryField, levelField, type, cross, items } = this.props;
    if (!onSave || !store.modifiedRows.length) {
      return;
    }

    const submitCount = localStorage.getItem('submitCount') || 0
    // 记录一下提交次数
    localStorage.setItem('submitCount', +submitCount + 1 + '')

    // 验证所有表单项，没有错误才继续
    const subForms: Array<any> = [];
    Object.keys(this.subForms).forEach(
      key =>
        this.subForms[key] &&
        store.modifiedRows.find(
          item => item.index?.toString() === key.split('-')[1]
        ) &&
        subForms.push(this.subForms[key])
    );
    if (subForms.length) {
      const result = await Promise.all(subForms.map(item => {
        return item.validate()
      }));
      const falseResltIdx = result.findIndex((bool, index) => bool === false)
      // 如果发现第一个不符合结果的表格项目-滚动到屏幕正中央
      // if (~falseResltIdx && subForms[falseResltIdx]?.contentRef?.current) {
      //   (subForms[falseResltIdx]?.contentRef?.current as HTMLElement).scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' })
      //   return;
      // }
      if (~falseResltIdx) return
    }


    // const rows = store.modifiedRows.map(item => item.data)
    const rowIndexes = []
    const diff = []
    const resultArray = [];
    const orginArr = [];

    for (const item of store.modifiedRows) {
      let originItemIndex
      let originItem
      let newObj

      // bi-table的数据有改
      if (this.props.type === 'lion-bi-table') {
        originItemIndex = store.rows.toJSON().findIndex((_: any) => _?.data[DATAKEYID] === item[DATAKEYID])
        originItem = store.rows.toJSON()?.[originItemIndex].pristine
      } else {
        originItemIndex = store.data.itemsRaw.findIndex((_: any) => _[DATAKEYID] === item[DATAKEYID])
        originItem = store.data.itemsRaw[originItemIndex]
      }

      newObj = this.compareObjects(item, originItem);
      // 修改值数组
      resultArray.push(newObj);
      // 变化值 id 变化项 数组
      diff.push({ changeItemKey: originItem[DATAKEYID], diff: difference(item, originItem, ['id', primaryField].concat(levelField?.split(',') ?? []).concat(cross?.rowFields.map(field => field.name) ?? [])) })
      // 修改的原始数组
      orginArr.push(originItem)
      // index值数组
      rowIndexes.push(originItemIndex)
    }

    const rows = type === 'cross' ? getChangeRows(diff.map(item => item.diff), items, cross!) : resultArray;
    const unModifiedRows = store.rows
      .filter(item => !item.modified)
      .map(item => item.data);

    // 全部保存了在隐藏
    EventSub.emit(EventEnum.ClearVistiMap)

    onSave(
      rows,
      diff,
      rowIndexes,
      unModifiedRows,
      store.modifiedRows.map(item => item.pristine),
      false,
      true
    );
  }

  compareObjects(obj1: any, obj2: any) {
    const newObj: any = {};
    Object.entries(obj2).forEach(([key, value]) => {
      if (typeof obj1[key] === "object" && typeof value === "object") {
        if (!isEqual(obj1[key], value)) {
          newObj[key] = obj1[key]
          newObj[`OLD_${key}`] = value
        } else
          newObj[key] = obj1[key];
      } else if (obj1[key] !== obj2[key]) {
        newObj[key] = obj1[key]
        newObj[`OLD_${key}`] = value
      } else {
        newObj[key] = obj1[key];
      }
    })
    if (typeof newObj === "object" && Object.keys(newObj).length === 0) {
      return null
    }
    return newObj;
  }

  handleSaveOrder() {
    const { store, onSaveOrder } = this.props;

    if (!onSaveOrder || !store.movedRows.length) {
      return;
    }

    onSaveOrder(
      store.movedRows.map(item => item.data),
      store.rows.map(item => item.getDataWithModifiedChilden())
    );
  }

  syncSelected() {
    const { store, onSelect, } = this.props;
    if (onSelect) {
      onSelect(
        store.selectedRows.map(item => item.data),
        store.unSelectedRows.map(item => item.data)
      )
    }
  }

  reset() {
    const { store } = this.props;
    store.reset();
    EventSub.emit(EventEnum.ClearVistiMap)
    const subForms: Array<any> = [];
    Object.keys(this.subForms).forEach(
      key => this.subForms[key] && subForms.push(this.subForms[key])
    );
    subForms.forEach(item => item.clearErrors());
  }

  bulkUpdate(value: any, items: Array<object>) {
    const { store, primaryField } = this.props;

    if (primaryField && value.ids) {
      const ids = value.ids.split(',');
      const rows = store.rows.filter(item =>
        find(ids, (id: any) => id && id == item.data[primaryField])
      );
      const newValue = { ...value, ids: undefined };
      rows.forEach(row => row.change(newValue));
    } else {
      const rows = store.rows.filter(item => ~items.indexOf(item.pristine));
      rows.forEach(row => row.change(value));
    }
  }

  getSelected() {
    const { store } = this.props;

    return store.selectedRows.map(item => item.data);
  }

  affixDetect() {
    const { autoFillHeight = true } = this.props;
    if (!this.props.affixHeader || !this.table || autoFillHeight) {
      return;
    }

    const ns = this.props.classPrefix;
    const dom = findDOMNode(this) as HTMLElement;
    const clip = (this.table as HTMLElement).getBoundingClientRect();
    const offsetY =
      this.props.affixOffsetTop ?? this.props.env.affixOffsetTop ?? 0;
    const headingHeight =
      dom.querySelector(`.${ns}Table-heading`)?.getBoundingClientRect()
        .height || 0;
    const headerHeight =
      dom.querySelector(`.${ns}Table-headToolbar`)?.getBoundingClientRect()
        .height || 0;

    const affixed =
      clip.top - headerHeight - headingHeight < offsetY &&
      clip.top + clip.height - 40 > offsetY;
    // const affixedDom = dom.querySelector(`.${ns}Table-fixedTop`) as HTMLElement;

    // affixedDom.style.cssText += `top: ${offsetY}px;width: ${(this.table.parentNode as HTMLElement).offsetWidth
    //   }px`;
    // affixed
    //   ? affixedDom.classList.add('in')
    //   : affixedDom.classList.remove('in');
    // store.markHeaderAffix(clip.top < offsetY && (clip.top + clip.height - 40) > offsetY);
  }

  updateTableInfo() {
    if (!this.table) {
      return;
    }

    const table = this.table;
    const outter = table.parentNode as HTMLElement;
    //处理横屏后ios的样式兼容性问题
    if (tools.isIOS) {
      const sensor = outter.querySelector('.resize-sensor') as HTMLElement;
      if (this.props.tableRotate) {
        sensor.style.width = table.clientWidth + 'px';
        sensor.style.height = table.clientHeight + 'px';
      } else {
        sensor.style.width = 'auto';
        sensor.style.height = 'auto';
      }
    }
    const ns = this.props.classPrefix;

    // 完成宽高都没有变化就直接跳过了。
    // if (this.totalWidth === table.scrollWidth && this.totalHeight === table.scrollHeight) {
    //     return;
    // }

    this.totalWidth = table.scrollWidth;
    this.totalHeight = table.scrollHeight;
    this.outterWidth = outter.offsetWidth;
    this.outterHeight = outter.offsetHeight;

    let widths: {
      [propName: string]: number;
    } = (this.widths = {});
    let widths2: {
      [propName: string]: number;
    } = (this.widths2 = {});
    let heights: {
      [propName: string]: number;
    } = (this.heights = {});

    // heights.header = table
    //   .querySelector('thead>tr:last-child')!
    //   .getBoundingClientRect().height;
    // heights.header2 = table
    //   .querySelector('thead>tr:first-child')!
    //   .getBoundingClientRect().height;
    // Aug
    heights.header =
      table.querySelector('thead>tr:last-child')?.getBoundingClientRect()
        .height || 0;
    heights.header2 =
      table.querySelector('thead>tr:first-child')?.getBoundingClientRect()
        .height || 0;

    forEach(
      table.querySelectorAll('thead>tr:last-child>th'),
      (item: HTMLElement) => {
        widths[item.getAttribute('data-index') as string] =
          item.getBoundingClientRect().width;
      }
    );

    forEach(
      table.querySelectorAll('thead>tr:first-child>th'),
      (item: HTMLElement) => {
        widths2[item.getAttribute('data-index') as string] =
          item.getBoundingClientRect().width;
      }
    );

    forEach(
      table.querySelectorAll('tbody>tr>*:last-child'),
      (item: HTMLElement, index: number) =>
        (heights[index] = item.getBoundingClientRect().height)
    );

    // 让 react 去更新非常慢，还是手动更新吧。
    const dom = findDOMNode(this) as HTMLElement;

    forEach(
      // 折叠 footTable 不需要改变
      dom.querySelectorAll(
        `.${ns}Table-fixedLeft>table, .${ns}Table-fixedRight>table`
      ),
      (table: HTMLTableElement) => {
        let totalWidth = 0;
        let totalWidth2 = 0;
        forEach(
          table.querySelectorAll('thead>tr:last-child>th'),
          (item: HTMLElement) => {
            const width = widths[item.getAttribute('data-index') as string];
            item.style.cssText += `width: ${width}px; height: ${heights.header}px`;
            totalWidth += width;
          }
        );
        forEach(
          table.querySelectorAll('thead>tr:first-child>th'),
          (item: HTMLElement) => {
            const width = widths2[item.getAttribute('data-index') as string];
            item.style.cssText += `width: ${width}px; height: ${heights.header2}px`;
            totalWidth2 += width;
          }
        );

        forEach(table.querySelectorAll('colgroup>col'), (item: HTMLElement) => {
          const width = widths[item.getAttribute('data-index') as string];
          item.setAttribute('width', `${width}`);
        });

        forEach(
          table.querySelectorAll('tbody>tr'),
          (item: HTMLElement, index) => {
            item.style.cssText += `height: ${heights[index]}px`;
          }
        );

        table.style.cssText += `width: ${Math.max(
          totalWidth,
          totalWidth2
        )}px;table-layout: auto;`;
      }
    );

    this.lastScrollLeft = -1;
    this.handleOutterScroll();
  }

  handleOutterScroll() {
    const outter = (this.table as HTMLElement).parentNode as HTMLElement;
    //安卓机横屏后操作颠倒
    if (this.props.tableRotate && tools.isAndroid && Shell.hasShell()) {
      const scrollTop = outter.scrollTop;
      if (this.btnDragging) return;
      if (scrollTop === this.lastScrollTop) return;
      this.lastScrollLeft = scrollTop;
      const table = this.affixedTable;
      if (table) {
        table.style.cssText += `transform: translateX(-${scrollTop}px)`;
      }
    } else {
      const scrollLeft = outter.scrollLeft;
      if (this.btnDragging) return;
      if (scrollLeft === this.lastScrollLeft) return;
      this.lastScrollLeft = scrollLeft;
      let leading = scrollLeft === 0;
      let trailing = Math.ceil(scrollLeft) + this.outterWidth >= this.totalWidth;

      const ns = this.props.classPrefix;
      const dom = findDOMNode(this) as HTMLElement;

      const fixedLeft = dom.querySelectorAll(`.${ns}Table-fixedLeft`);
      if (fixedLeft && fixedLeft.length) {
        for (let i = 0, len = fixedLeft.length; i < len; i++) {
          let node = fixedLeft[i];
          leading ? node.classList.remove('in') : node.classList.add('in');
        }
      }

      const fixedRight = dom.querySelectorAll(`.${ns}Table-fixedRight`);
      if (fixedRight && fixedRight.length) {
        for (let i = 0, len = fixedRight.length; i < len; i++) {
          let node = fixedRight[i];
          trailing ? node.classList.remove('in') : node.classList.add('in');
        }
      }
      const table = this.affixedTable;
      if (table) {
        table.style.cssText += `transform: translateX(-${scrollLeft}px)`;
      }

      // Jay
      const { store } = this.props;
      const leftFixedColumns = store.leftFixedColumns;
      const lastLeftFixedIndex =
        leftFixedColumns[leftFixedColumns.length - 1]?.index;
      // 给table添加类名
      const fixedLeftLastEl = dom.querySelector('.fixed-left-last');
      if (fixedLeftLastEl) {
        const fixedLeftLastElRelativeLeft = fixedLeftLastEl.getBoundingClientRect().left -
          dom.getBoundingClientRect().left
        if (
          leading ||
          (
            fixedLeftLastElRelativeLeft > store.stickyWidths[lastLeftFixedIndex])
        ) {
          dom.classList.remove('fix-left');
        } else if (
          fixedLeftLastElRelativeLeft <= store.stickyWidths[lastLeftFixedIndex]
        ) {
          // 左边要固定的列相对于表格的水平距离小于等于其 sticky left
          dom.classList.add('fix-left');
        }
      }
      if (trailing) {
        dom.classList.remove('fix-right');
      } else {
        dom.classList.add('fix-right');
      }

      let isNaN = false;
      const rightFixedColumns = cloneDeep(store.rightFixedColumns);
      const rightFixedColumnsReverse = rightFixedColumns?.reverse();
      const columnWidths: { [key: string]: number } = {};
      this.table?.querySelectorAll('thead>tr:nth-child(1)>th').forEach(item => {
        columnWidths[item.getAttribute('data-index') as string] =
          item.clientWidth;
      });
      this.table?.querySelectorAll('thead>tr:nth-child(2)>th').forEach(item => {
        columnWidths[item.getAttribute('data-index') as string] =
          item.clientWidth;
      });
      const stickyWidths: { [key: string]: number } = {};
      if (leftFixedColumns[0]?.index) {
        stickyWidths[`${leftFixedColumns[0].index}`] =
          columnWidths[`${leftFixedColumns[0].index}`];
      }
      if (rightFixedColumnsReverse?.[0]?.index) {
        stickyWidths[`${rightFixedColumnsReverse[0].index}`] =
          columnWidths[`${rightFixedColumns[0].index}`];
      }
      leftFixedColumns?.reduce((acc, cv, ci, arr) => {
        let a: number = acc;
        if (ci !== 0) {
          a = acc + columnWidths[arr[ci - 1].index];
        }
        a !== a && (isNaN = true);
        stickyWidths[`${cv.index}`] = a;
        return a;
      }, 0);

      rightFixedColumnsReverse?.reduce((acc, cv, ci, arr) => {
        let a: number = acc;
        if (ci !== 0) {
          a = acc + columnWidths[arr[ci - 1].index];
        }
        a !== a && (isNaN = true);
        stickyWidths[`${cv.index}`] = a;
        return a;
      }, -2);
      const equal = isEqual(stickyWidths, store.stickyWidths);
      // 避免重复render组件，提高性能
      if (!isNaN && !equal) {
        store.setStickyWidths(stickyWidths);
      }
    }
  }

  tableRef(ref: HTMLTableElement) {
    this.table = ref;

    if (ref) {
      this.unSensor = resizeSensor(
        ref.parentNode as HTMLElement,
        this.updateTableInfoLazy
      );
    } else {
      this.unSensor && this.unSensor();
      delete this.unSensor;
    }
  }

  dragTipRef(ref: any) {
    if (!this.dragTip && ref) {
      this.initDragging();
    } else if (this.dragTip && !ref) {
      this.destroyDragging();
    }

    this.dragTip = ref;
  }

  affixedTableRef(ref: HTMLTableElement) {
    this.affixedTable = ref;
  }

  initDragging() {
    const store = this.props.store;
    const ns = this.props.classPrefix;
    this.sortable = new Sortable(
      (this.table as HTMLElement).querySelector('tbody') as HTMLElement,
      {
        group: 'table',
        animation: 150,
        handle: `.${ns}Table-dragCell`,
        filter: `.${ns}Table-dragCell.is-dragDisabled`,
        ghostClass: 'is-dragging',
        onEnd: (e: any) => {
          // 没有移动
          if (e.newIndex === e.oldIndex) {
            return;
          }

          const parent = e.to as HTMLElement;
          if (e.oldIndex < parent.childNodes.length - 1) {
            parent.insertBefore(e.item, parent.childNodes[e.oldIndex]);
          } else {
            parent.appendChild(e.item);
          }

          store.exchange(e.oldIndex, e.newIndex);
        }
      }
    );
  }

  destroyDragging() {
    this.sortable && this.sortable.destroy();
  }

  getPopOverContainer() {
    return findDOMNode(this);
  }

  handleMouseMove(e: React.MouseEvent<any>) {
    const tr: HTMLElement = (e.target as HTMLElement).closest(
      'tr[data-id]'
    ) as HTMLElement;

    if (!tr) {
      return;
    }

    const { store, affixColumns, itemActions } = this.props;

    if (
      (affixColumns === false ||
        (store.leftFixedColumns.length === 0 &&
          store.rightFixedColumns.length === 0)) &&
      (!itemActions || !itemActions.filter(item => !item.hiddenOnHover).length)
    ) {
      return;
    }

    const id = tr.getAttribute('data-id') as string;
    const row = store.hoverRow;

    if (row?.id === id) {
      return;
    }
    eachTree<IRow>(store.rows, (item: IRow) => item.setIsHover(item.id === id));
  }

  handleMouseLeave() {
    const store = this.props.store;
    const row = store.hoverRow;

    row?.setIsHover(false);
  }

  draggingTr: HTMLTableRowElement;
  originIndex: number;
  draggingSibling: Array<HTMLTableRowElement>;
  onDraging: boolean // 拖动中标识

  handleDragStart = (e: React.DragEvent) => {
    const store = this.props.store;
    const target = e.currentTarget;
    const tr = (this.draggingTr = target.closest('tr')!);
    const id = tr.getAttribute('data-id')!;
    const tbody = tr.parentNode!;
    this.originIndex = Array.prototype.indexOf.call(tbody.childNodes, tr);

    tr.classList.add('is-dragging');

    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', id);

    e.dataTransfer.setDragImage(tr, 0, 0);
    const item = store.getRowById(id)!;
    store.collapseAllAtDepth(item.depth);

    let siblings: Array<IRow> = store.rows;
    if (item.parentId) {
      const parent = store.getRowById(item.parentId)!;
      siblings = parent.children as any;
    }
    siblings = siblings.filter(sibling => sibling !== item);

    tbody.addEventListener('dragover', this.handleDragOver);
    tbody.addEventListener('drop', this.handleDrop);

    this.draggingSibling = siblings.map(item => {
      let tr: HTMLTableRowElement = tbody.querySelector(
        `tr[data-id="${item.id}"]`
      ) as HTMLTableRowElement;

      tr.classList.add('is-drop-allowed');

      return tr;
    });
    tr.addEventListener('dragend', this.handleDragEnd);
  }

  handleDragOver = (e: any) => {
    if (!e.target) {
      return;
    }
    e.preventDefault();
    e.dataTransfer!.dropEffect = 'move';

    const overTr: HTMLElement = (e.target as HTMLElement).closest('tr')!;
    if (
      !overTr ||
      !~overTr.className.indexOf('is-drop-allowed') ||
      overTr === this.draggingTr
    ) {
      return;
    }

    const tbody = overTr.parentElement!;
    const dRect = this.draggingTr.getBoundingClientRect();
    const tRect = overTr.getBoundingClientRect();
    let ratio = dRect.top < tRect.top ? 0.1 : 0.9;

    const next = (e.clientY - tRect.top) / (tRect.bottom - tRect.top) > ratio;
    tbody.insertBefore(this.draggingTr, (next && overTr.nextSibling) || overTr);
  }

  handleDrop = () => {
    const store = this.props.store;
    const tr = this.draggingTr;
    const tbody = tr.parentElement!;
    const index = Array.prototype.indexOf.call(tbody.childNodes, tr);
    const item: IRow = store.getRowById(tr.getAttribute('data-id')!) as any;

    // destroy
    this.handleDragEnd();

    store.exchange(this.originIndex, index, item);
  }

  handleDragEnd = () => {
    const tr = this.draggingTr;
    const tbody = tr.parentElement!;
    const index = Array.prototype.indexOf.call(tbody.childNodes, tr);
    tbody.insertBefore(
      tr,
      tbody.childNodes[
      index < this.originIndex ? this.originIndex + 1 : this.originIndex
      ]
    );

    tr.classList.remove('is-dragging');
    tr.removeEventListener('dragend', this.handleDragEnd);
    tbody.removeEventListener('dragover', this.handleDragOver);
    tbody.removeEventListener('drop', this.handleDrop);
    this.draggingSibling.forEach(item =>
      item.classList.remove('is-drop-allowed')
    );
  }

  handleImageEnlarge = (info: any, target: { rowIndex: number; colIndex: number }) => {
    const onImageEnlarge = this.props.onImageEnlarge;

    // 如果已经是多张了，直接跳过
    if (Array.isArray(info.list)) {
      return onImageEnlarge && onImageEnlarge(info, target);
    }

    // 从列表中收集所有图片，然后作为一个图片集合派送出去。
    const store = this.props.store;
    const column = store.columns[target.colIndex].pristine;

    let index = target.rowIndex;
    const list: Array<any> = [];
    store.rows.forEach((row, i) => {
      const src = resolveVariable(column.name, row.data);

      if (!src) {
        if (i < target.rowIndex) {
          index--;
        }
        return;
      }

      list.push({
        src,
        originalSrc: column.originalSrc
          ? filter(column.originalSrc, row.data)
          : src,
        title: column.enlargeTitle
          ? filter(column.enlargeTitle, row.data)
          : column.title
            ? filter(column.title, row.data)
            : undefined,
        caption: column.enlargeCaption
          ? filter(column.enlargeCaption, row.data)
          : column.caption
            ? filter(column.caption, row.data)
            : undefined
      });
    });

    if (list.length > 1) {
      onImageEnlarge &&
        onImageEnlarge(
          {
            ...info,
            list,
            index
          },
          target
        );
    } else {
      onImageEnlarge && onImageEnlarge(info, target);
    }
  }

  // 以下变量都是用于列宽度调整拖拽
  resizeLine: HTMLElement;
  // resizeLineLeft: number;
  targetTh: HTMLElement;
  targetThWidth: number;
  lineStartX: number;
  resizeColumn: IColumn;

  // 开始设置拖拽线
  setDragLine = (targetTh?: Element) => {
    const dragLine = this.dragLineShadowRef.current as HTMLElement
    dragLine.style.height = this.table!.querySelector('tbody')!.clientHeight + 'px';
    dragLine.style.top = (targetTh || this.targetTh).clientHeight + 1 + 'px';
    dragLine.style.display = 'unset'
  }

  // 开始设置拖拽线
  hideDragLine = () => {
    const dragLine = this.dragLineShadowRef.current as HTMLElement
    dragLine.style.display = 'none'
  }




  // 开始列宽度调整
  handleColResizeMouseDown = (e: React.MouseEvent<HTMLElement>, col: IColumn) => {
    this.onDraging = true
    this.lineStartX = e.clientX;
    const currentTarget = e.currentTarget;
    this.resizeColumn = col;
    this.resizeLine = currentTarget;
    // this.resizeLineLeft = parseInt(
    //   getComputedStyle(this.resizeLine).getPropertyValue('left'),
    //   10
    // );
    this.targetTh = this.resizeLine.parentElement! as HTMLElement;
    this.targetThWidth = this.targetTh.getBoundingClientRect().width;
    this.setDragLine()

    document.addEventListener('mousemove', this.handleColResizeMouseMove);
    document.addEventListener('mouseup', this.handleColResizeMouseUp);
  }

  // 垂直线拖拽移动
  handleColResizeMouseMove = (e: MouseEvent) => {
    const targetThRect = this.targetTh.getBoundingClientRect()
    const moveX = e.clientX - this.lineStartX;

    this.dragLineShadowRef!.current!.style.left = targetThRect.left - (this.table as HTMLElement)?.getBoundingClientRect?.().left + this.targetThWidth + moveX + 'px'
    this.targetTh.style.width = this.targetThWidth + moveX + 'px';
  }

  // 垂直线拖拽结束
  handleColResizeMouseUp = (e: MouseEvent) => {
    requestAnimationFrame(() => {
      this.onDraging = false
      this.hideDragLine()
    })
    if (isMobile()) return
    const { store } = this.props;
    const editWidthMap = getLocalStorage(EDITWIDTHKEY) || {}
    const newCols = store.columnsData.map(item => {
      if (item.name === this.resizeColumn.name) {
        editWidthMap[this.props.crudName || ''] = { ...editWidthMap[this.props.crudName || ''], [this.resizeColumn.name || '']: this.targetTh.style.width }
        setLocalStorage(EDITWIDTHKEY, editWidthMap)
        return {
          ...item,
          pristine: { ...item.pristine, editWidth: this.targetTh.style.width }
        };
      }
      return item;
    });
    this.handleColumnToggle(newCols, {}, false);
    document.removeEventListener('mousemove', this.handleColResizeMouseMove);
    document.removeEventListener('mouseup', this.handleColResizeMouseUp);
  }
  //获取设置列模板
  getColumnSettingList = async () => {
    const { saveColApi, env } = this.props;
    if (saveColApi) {
      const api = normalizeApi(saveColApi?.url || '', 'get');
      const { data } = await env.fetcher(api);
      const list = data?.filter((item: Temp) => item.tempKey) || [];
      this.setState({ columnSettingTemps: list });
    }
  }
  //设置列保存到远程
  sendColumns = (saveCols: Record<string, any>, targetTemp?: Temp, isDraggled: boolean = false) => {
    const { saveColApi } = this.props
    this.props.env
      .fetcher(
        { url: saveColApi.url, method: saveColApi.method },
        { ...targetTemp, columnInfo: saveCols }
      )
      .then(res => {
        if (res.ok) {
          !isDraggled && message.success(res.msg)
          this.getColumnSettingList()
        } else {
          !isDraggled && message.error(res.msg)
        }
      });
  }
  //拖拽设置列时防抖
  debounceSendColumns = debounce(this.sendColumns.bind(this), 1000 * 60)
  // Jay
  handleColumnToggle(
    columns: Array<IColumn> | undefined,
    saveCols: Record<string, any>,
    canFetch: boolean,
    targetTemp?: Temp,
    needDebounce?: boolean //开启防抖
  ) {
    const { store, saveColApi, crudName } = this.props;
    if (canFetch && saveColApi && crudName) {
      needDebounce ? this.debounceSendColumns(saveCols, targetTemp, true) : this.sendColumns(saveCols, targetTemp)
    }
    if (columns) {
      this.sortCols = columns.map(col => col.pristine);
      store.updateColumnsInfo(clone(saveCols));
      store.updateColumns(columns);
    }
  }

  renderAutoFilterForm(): React.ReactNode {
    const {
      render,
      store,
      onSearchableFromReset,
      onSearchableFromSubmit,
      onSearchableFromInit,
      classnames: cx,
      translate: __
    } = this.props;
    const searchableColumns = store.searchableColumns;
    const activedSearchableColumns = store.activedSearchableColumns;

    if (!searchableColumns.length) {
      return null;
    }

    const groupedSearchableColumns: Array<Record<string, any>> = [
      { body: [], md: 4 },
      { body: [], md: 4 },
      { body: [], md: 4 }
    ];

    activedSearchableColumns.forEach((column, index) => {
      groupedSearchableColumns[index % 3].body.push({
        ...column.searchable,
        name: column.searchable?.name ?? column.name,
        label: column.searchable?.label ?? column.label,
        mode: 'horizontal'
      });
    });

    return render(
      'searchable-form',
      {
        type: 'form',
        api: null,
        title: '',
        mode: 'normal',
        submitText: __('search'),
        body: [
          {
            type: 'grid',
            columns: groupedSearchableColumns
          }
        ],
        actions: [
          {
            type: 'dropdown-button',
            label: __('Table.searchFields'),
            className: cx('Table-searchableForm-dropdown', 'mr-2'),
            level: 'link',
            trigger: 'click',
            size: 'sm',
            align: 'right',
            buttons: searchableColumns.map(column => {
              return {
                type: 'checkbox',
                className: cx('Table-searchableForm-checkbox'),
                name: `__search_${column.searchable?.name ?? column.name}`,
                option: column.searchable?.label ?? column.label,
                value: column.enableSearch,
                badge: {
                  offset: [-10, 5],
                  visibleOn: `${column.toggable && !column.toggled && column.enableSearch
                    }`
                },
                onChange: (value: boolean) => {
                  column.setEnableSearch(value);
                }
              };
            })
          },
          {
            type: 'submit',
            label: __('search'),
            level: 'primary',
            className: 'w-18'
          },
          {
            type: 'reset',
            label: __('reset'),
            className: 'w-18'
          }
        ]
      },
      {
        key: 'searchable-form',
        panelClassName: cx('Table-searchableForm'),
        actionsClassName: cx('Table-searchableForm-footer'),
        onReset: onSearchableFromReset,
        onSubmit: onSearchableFromSubmit,
        onInit: onSearchableFromInit,
        formStore: undefined
      }
    );
  }

  renderHeading() {
    let {
      title,
      store,
      hideQuickSaveBtn,
      data,
      classnames: cx,
      saveImmediately,
      headingClassName,
      quickSaveApi,
      translate: __,
    } = this.props;

    if (
      title ||
      (quickSaveApi &&
        !saveImmediately &&
        store.modified &&
        !hideQuickSaveBtn) ||
      store.moved
    ) {


      return (
        <div className={cx('Table-heading', headingClassName)} key="heading">
          {!saveImmediately && store.modified && !hideQuickSaveBtn ? (
            <span>
              {__('Table.modified', {
                modified: store.modified
              })}
              <button
                type="button"
                className={cx('Button Button--xs Button--success m-l-sm')}
                onClick={this.handleSave}
              >
                <Icon icon="check" className="icon m-r-xs" />
                {__('Form.submit')}
              </button>
              <button
                type="button"
                className={cx('Button Button--xs Button--danger m-l-sm')}
                onClick={this.reset}
              >
                <Icon icon="close" className="icon m-r-xs" />
                {__('Table.discard')}
              </button>
            </span>
          ) : store.moved ? (
            <span>
              {__('Table.moved', {
                moved: store.moved
              })}
              <button
                type="button"
                className={cx('Button Button--xs Button--success m-l-sm')}
                onClick={this.handleSaveOrder}
              >
                <Icon icon="check" className="icon m-r-xs" />
                {__('Form.submit')}
              </button>
              <button
                type="button"
                className={cx('Button Button--xs Button--danger m-l-sm')}
                onClick={this.reset}
              >
                <Icon icon="close" className="icon m-r-xs" />
                {__('Table.discard')}
              </button>
            </span>
          ) : title ? (
            filter(title, data)
          ) : (
            ''
          )}
        </div>
      );
    }

    return null;
  }
  handleModleColumn() {
    const { store, saveImmediately, hideQuickSaveBtn, translate: __ } = this.props;

    if (!saveImmediately && store.modified && !hideQuickSaveBtn) {
      return new Promise<boolean>(resolve => {
        const modal = Modal.confirm({
          title: (
            <div>
              <div style={{ position: 'absolute', right: '10px', top: '14px', fontSize: '14px', cursor: "pointer", width: 14 }} onClick={() => { modal.destroy() }} >
                <Icon icon="close" className="icon" />
              </div>
            </div>
          ),
          content: __('Table.modified', {
            modified: store.modified
          }),
          okText: '提交',
          cancelText: "放弃",
          maskClosable: false,
          keyboard: false,
          okButtonProps: { style: { borderRadius: 4 } },
          cancelButtonProps: { style: { borderRadius: 4 } },
          onOk: async () => { await this.handleSave(), resolve(true) },
          onCancel: () => { this.reset, resolve(true) }
        })
      })
    }
    return Promise.resolve(true)
  }

  async handleMultiColumnSort(name: string, isMultiple?: boolean, columnMapValue?: object) {
    const { store, handleMutilSort, markSort, loadDataOnce, mode, type } = this.props;
    markSort();
    // 多列排
    let orderColumns = new Map<string, { order: string, map?: object }>();

    if (isMultiple) {
      // 复制排序
      (store.orderColumns as Map<string, { order: string, map?: object }>).forEach((value, key) => {
        orderColumns.set(key, value);
      });
      if (orderColumns.has(name)) {
        if (orderColumns.get(name)?.order === 'desc') {
          orderColumns.delete(name);
        } else {
          orderColumns.set(name, { order: 'desc', map: columnMapValue });
        }
      } else {
        orderColumns.set(name, { order: 'asc', map: columnMapValue });
      }
      //更新一个新的
    } else {
      // 单列排
      const current = store.getOrderColumn(name)?.order;
      const value = current ? (current === 'asc' ? 'desc' : null) : 'asc';
      if (value) {
        orderColumns.set(name, { order: value, map: columnMapValue });
      }
    }
    store.update({ orderColumns });
    if (mode === 'lion-bi-table' || type === 'lion-bi-table') {
      this.props.setLoading!(true);
      handleMutilSort!(orderColumns, store.filterColumns, loadDataOnce, store.modefiedMap.modifiedDataSet);
    } else {
      store.handleMutilSort?.(loadDataOnce);
    }
  }

  renderHeadCell(column: IColumn & { batchEdit?: boolean }, props?: any) {
    const {
      store,
      query,
      onQuery,
      multiple,
      env,
      render,
      classPrefix: ns,
      resizable,
      classnames: cx,
      autoGenerateFilter,
      onBatchEdit,

    } = this.props;
    const { style } = props;
    // const orderName = orders && Array.from(orders.keys()).pop() as string || undefined
    // const orderColumn = orderName && orders.get(orderName)
    // 获取文字计算长度-快速编辑的不用算
    let textWidth = ''
    textWidth = this.props.store?.textWidhMap?.[column.pristine.name]?.textLength

    if (column.type === '__checkme') {
      return (
        <ContextMenu
          menuItems={[
            { id: 0, title: '全选' },
            { id: 1, title: '反选' },
            { id: 2, title: '不选' }
          ]}
          onItemClick={index => multiple && this.handleCheckAll(index as CheckAllType)}
          key={Date()}
        >
          <th
            {...props}
            style={{ cursor: 'pointer', textAlign: 'center', ...style }}
            className={cx(column.pristine.className, 'header-cell')}
            onClick={() => {
              multiple && this.handleCheckAll(3);
            }}
          >
            {store.rows.length && multiple ? (
              <Checkbox
                classPrefix={ns}
                partial={!store.allChecked}
                checked={store.someChecked}
                disabled={store.disabledHeadCheckbox}
              // onChange={this.handleCheckAll}
              />
            ) : (
              <div>{'\u00A0'}</div>
            )}
          </th>
        </ContextMenu>
      );
    } else if (column.type === '__dragme') {
      return <th {...props} className={cx(column.pristine.className, 'header-cell')} />;
    } else if (column.type === '__expandme') {
      const expandRowsDepth = store.getExpandedRows().filter(row => row.expandable && row.expanded).map(row => row.depth)
      const maxDepth = expandRowsDepth.length > 0 ? Math.max(...expandRowsDepth) + 1 : 1
      return (
        <th {...props} style={{ ...props.style, minWidth: maxDepth * 20 }} className={cx(column.pristine.className, 'header-cell')} colSpan={store.mobileUI ? 2 : undefined}>
          {(store.footable &&
            (store.footable.expandAll === false || store.footable.accordion)) ||
            (store.expandConfig &&
              (store.expandConfig.expandAll === false ||
                store.expandConfig.accordion)) ? null : (
            <a
              className={cx(
                'Table-expandBtn',
                store.allExpanded ? 'is-active' : ''
              )}
              // data-tooltip="展开/收起全部"
              // data-position="top"
              onClick={store.toggleExpandAll}
            >
              <Icon icon="right-arrow-bold" className="icon" />
            </a>
          )}
        </th>
      );
    } else if (column.type === '__pseudoColumn') {
      return <th {...props} className={cx(column.pristine.className, 'fixed-left-last', 'header-cell')} >
      </th>;
    }

    let affix = [];

    if (column.sortable && column.name) {
      if (store.orderColumns?.has(column.name) && ['asc', 'desc'].includes(store.getOrderColumn?.(column.name as string)?.order || '')) {
        affix.push(
          <div className={cx('TableCell-sortBtn', store.orderColumns?.has(column.name) && ['asc', 'desc'].includes(store.getOrderColumn?.(column.name as string)?.order || '') && 'show-sort')}>
            <CaretUpOutlined className={cx(
              'TableCell-sortBtn--down',
              store.orderColumns?.has(column.name) &&
                store.getOrderColumn?.(column.name as string)?.order === 'asc' ? 'is-active' : ''
            )} />
            <CaretDownOutlined className={cx(
              'TableCell-sortBtn--up',
              store.orderColumns?.has(column.name) && store.getOrderColumn?.(column.name as string)?.order === 'desc' ? 'is-active' : ''
            )} />
          </div>
        )
      }
    }
    let filterActive = false;
    if (this.props.showColumnsFilter && column.sortable && column.name) {
      const filterType = getColumnsFilterType(column.type)
      const columnName = column.name ?? column.label
      const isActive = store.filterColumns.has(columnName)
      filterActive = isActive
      if (filterType) {
        affix.push(
          <HeadCellFilterDropDown
            {...this.props}
            isActive={isActive}
            isShow={this.props.showColumnsFilter}
            filterType={filterType}
            defaultFilters={store.filterColumns.get(columnName)}
            column={column}
            columnFileds={store.filteredColumns.filter(column => column.name && column.name != 'operation').map(column => ({ label: (column.groupName ? `${column.groupName} ·` : '') + column.label, name: column.name! }))}
            onConfirm={(values, caseSensitive) => {
              store.filterColumns.set(columnName, values.map(item => ({ ...item, label: column.label, filterType, fieldName: columnName })))
              if (this.props.mode === 'lion-bi-table' || this.props.type === 'lion-bi-table') {
                this.props.handleColumnFilter(store.filterColumns, store.orderColumns, caseSensitive)
              } else {
                const total = store.handleMutilSort(this.props.loadDataOnce, caseSensitive)
                if (total != undefined) this.props.setTotal(total)
              }
            }}
            onReset={() => {
              store.filterColumns.delete(columnName)
              if (this.props.mode === 'lion-bi-table' || this.props.type === 'lion-bi-table') {
                this.props.handleColumnFilter(store.filterColumns, store.orderColumns)
              } else {
                const total = store.handleMutilSort(this.props.loadDataOnce)
                if (total != undefined) this.props.setTotal(total)
              }
            }}
          />
        )
      }
    }

    props.style = props.style || {};
    // 宽度顺序为 编辑宽度 自动列宽宽度 原始宽度
    if (column.pristine.width) {
      props.style.width = column.pristine.width;
    }
    // 快速编辑不处理
    if (this.props.autoWidth) {
      props.style.width = textWidth // 直接使用width
    }
    if (column.pristine.editWidth) {
      props.style.width = column.pristine.editWidth;
    }

    if (column.pristine.align) {
      props.style = props.style || {};
      props.style.textAlign = column.pristine.align;
    }

    const resizeLine = (
      <div
        className={cx('Table-content-colDragLine')}
        key={`resize-${column.index}`}
        onMouseDown={e => this.handleColResizeMouseDown(e, column)}
      ></div>
    );

    const order = store.getOrderColumn?.(column.name ?? '')?.order;
    const title =
      order === 'asc'
        ? '点击降序'
        : order === 'desc'
          ? '点击取消排序'
          : '点击升序';

    const divStyle = { ...props.style }


    const tableTh = (
      <Tooltip
        title={title}
        mouseEnterDelay={0.4}
        color="white"
        placement="top"
        overlayInnerStyle={{ color: 'black' }}
        overlayStyle={{
          visibility: column.sortable ? 'visible' : 'hidden'
        }}
      >
        <div
          className={cx(
            `${ns}TableCell--title`,
            column.pristine.className,
            column.pristine.labelClassName,
            { ellipsis: column.pristine.ellipsis }
          )}
          onDoubleClick={() => {
            this.props.store.rows.map(_ => _.data)
          }}
          style={{ width: `calc( 100% - ${affix.length * 16}px)` }}
          draggable
          onDragStart={this.onHeaderDragStart}
          onContextMenu={e => e.preventDefault()}
        >
          {column.label ? render('tpl', this.props.isSqlData ? (this.state.fieldTranslate ? column.label : column.name) : column.label) : null}

          {column.remark
            ? render('remark', {
              type: 'remark',
              tooltip: column.remark,
              container: env?.getTopModalContainer
            })
            : null}

          {/* Aug input-table批量修改 */}
          {column.pristine.batchEdit ? (
            <HeadCellBatchEditDropdown
              {...this.props}
              onBatchEdit={onBatchEdit}
              name={column.name}
              batchEdit={column.pristine.batchEdit}
              popOverContainer={this.getPopOverContainer}
            />
          ) : null}
        </div>
      </Tooltip>
    );

    return (
      <th
        {...props}
        onClick={e => {
          if (this.onDraging) return // 拖动中直接返回
          if (column.sortable && column.name) {
            this.handleMultiColumnSort(column.name, e.ctrlKey, column.map)
          }
        }}
        table-name={this.props.name}
        column-name={column.name}
        title={column.label}
        className={cx(props ? (props as any).className : '', {
          'TableCell--sortable': column.sortable,
          'TableCell--searchable': column.searchable,
          'TableCell--filterable': column.sortable && column.name,
          'Table-operationCell': column.type === 'operation',
          'TableCell-dragglable': column.type !== 'operation',
        }, 'header-cell')}
        style={{ ...props.style, cursor: 'pointer', padding: this.props.autoWidth ? '1px 2px' : undefined }}
      >
        {/* // chencicsy 用于执行复制操作 */}
        <div className={cx('Table-head-container', filterActive && 'show-filter')} style={divStyle}>
          {tableTh}
          {/* 统计栏宽度计算出来 */}
          {affix.length > 0 && <div className={cx('Table-head-affix')} style={{ width: affix.length * 16 }}>{affix}</div>}
        </div>
        {resizable === false ? null : resizeLine}
      </th>
    );
  }

  draggingTh: HTMLTableCellElement;
  originColumnName: string;
  draggingSiblingTh: Array<HTMLTableCellElement>;
  draggingThOffsetX: number;
  draggingThStartX: number;
  leftDistances: number[]; // 标题左侧距离
  draggingThStartLeft: number; // 开始的做节点
  // onDraging: boolean // 拖动中标识
  dragShadowRef: React.RefObject<HTMLDivElement> = React.createRef();
  dragLineShadowRef: React.RefObject<HTMLDivElement> = React.createRef();
  onHeaderDragStart = (e: any) => {
    if (!this.dragShadowRef.current) return
    const store = this.props.store;
    const target = e.currentTarget;
    const th = (this.draggingTh = target.closest('th')!);
    const name = th.getAttribute('column-name')!;
    const tr = th.parentNode!;
    this.dragShadowRef.current!.style.width = th.clientWidth + 'px';
    this.dragShadowRef.current!.style.height = this.table!.querySelector('tbody')!.clientHeight + 'px';
    this.dragShadowRef.current!.style.top = th.clientHeight + 'px';
    const offsetx = e.clientX - (e.clientX - (this.table!.getBoundingClientRect().left)) - this.table!.getBoundingClientRect().left;
    this.draggingThStartX = e.clientX;
    this.draggingThOffsetX = offsetx;
    this.draggingThStartLeft = (th.getBoundingClientRect().left - (this.table!.getBoundingClientRect().left)) - offsetx
    this.dragShadowRef.current!.style.left = this.draggingThStartLeft + 'px';
    this.originColumnName = name;
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', name);
    e.dataTransfer.setDragImage(document.createElement('div'), 0, 0);
    let siblings: Array<IColumn> = cloneDeep(store.filteredColumns);
    tr.addEventListener('dragover', this.onHeaderDragOver);
    tr.addEventListener('drop', this.onHeaderDrop);
    tr.addEventListener('dragend', this.onHeaderDragEnd);
    // (tr as HTMLDivElement).classList.add('is-headerdrop-allowed');
    this.draggingSiblingTh = siblings.map(item => {
      if (item.name) {
        let th: HTMLTableCellElement = tr.querySelector(
          `th[column-name="${item.name}"]`
        ) as HTMLTableCellElement;

        // th.classList.add('is-headerdrop-allowed');

        return th;
      } else {
        let th: HTMLTableCellElement = tr.querySelector(
          `th[data-index="${item.index}"]`
        ) as HTMLTableCellElement;
        // th.classList.add('is-headerdrop-not-allowed')
        return th;
      }
    });
  }


  onHeaderDragOver = (e: any) => {
    e.preventDefault();
    if (this.dragShadowRef.current) {
      const tableBound = (this.table as HTMLElement)?.getBoundingClientRect?.()
      const thArray = Array.from(this.table?.querySelectorAll('table>thead>tr>th') || [])
      this.dragShadowRef.current.style.left = this.draggingThStartLeft + (e.clientX - this.draggingThStartX) + 'px'
      const targetThIndex = this.leftDistances.findIndex(_ => _ > e.clientX - tableBound.left) - 1
      const startThIndex = this.leftDistances.findIndex(_ => _ > this.draggingThStartX - tableBound.left) - 1
      const targetTh = thArray[targetThIndex]
      // console.log(startThIndex, targetThIndex)
      if (this.dragLineShadowRef.current) {
        const dragLine = this.dragLineShadowRef.current
        this.setDragLine(targetTh)
        const targetThBound = targetTh.getBoundingClientRect()
        // 如果是刚开始的列不展示拖拽线
        if (targetThIndex !== startThIndex) {
          if (targetThBound.x > this.draggingThStartX)
            dragLine.style.left = targetThBound.x - (this.table as HTMLElement)?.getBoundingClientRect?.().left + targetThBound.width - 1 + 'px'
          else
            dragLine.style.left = targetThBound.x - (this.table as HTMLElement)?.getBoundingClientRect?.().left + 1 + 'px'
        } else { this.hideDragLine() }
      }
    } else {
      if (this.dragShadowRef.current) {
        const dragLine = this.dragLineShadowRef.current as any
        this.hideDragLine()
      }
    }
  }

  onHeaderDrop = (e: any) => {
    const thArray = Array.from(this.table?.querySelectorAll('table>thead>tr>th') || [])
    const targetTh = thArray[this.leftDistances.findIndex(_ => _ > e.clientX - (this.table as HTMLElement)?.getBoundingClientRect?.().left) - 1]
    if (this.dragLineShadowRef.current)
      this.dragLineShadowRef.current.style.display = 'none'
    const name = targetTh?.getAttribute('column-name');
    // console.log(targetTh, this.leftDistances.findIndex(_ => _ > e.clientX - (this.table as HTMLElement)?.getBoundingClientRect?.().left) - 1)
    if (this.originColumnName && name) {
      this.swapColumnPosition(this.originColumnName, name)
    }
    this.onHeaderDragEnd();
  }

  onHeaderDragEnd = () => {
    this.dragShadowRef.current!.style.width = '0px';
    this.dragShadowRef.current!.style.height = '0px';
    this.dragShadowRef.current!.style.top = '0px';
    this.dragShadowRef.current!.style.left = '0px';
    requestAnimationFrame(() => {
      this.caculateLeft(true)
    })
    // this.draggingTh.classList.remove('is-header-dragging');
    // this.draggingSiblingTh.forEach(item =>
    //   item.classList.remove('is-drop-allowed')
    // );
    const tr = this.draggingTh.parentNode;
    if (tr) {
      tr.removeEventListener('dragover', this.onHeaderDragOver);
      tr.removeEventListener('drop', this.onHeaderDrop);
      tr.removeEventListener('dragend', this.onHeaderDragEnd);
    }
  }

  renderCell(
    region: string,
    column: IColumn,
    item: IRow,
    props: any,
    ignoreDrag = false
  ) {
    const {
      render,
      store,
      multiple,
      classPrefix: ns,
      classnames: cx,
      checkOnItemClick,
      popOverContainer,
      canAccessSuperData,
      itemBadge,
      translate: __,
      tableRotate,
      setBorder,
      cross,
    } = this.props;

    if (column.name && item.rowSpans[column.name] === 0) {
      return null;
    }

    // Jay
    const { rowClassName, rowClassNameExpr } = this.props;
    const stickyWidths = store.stickyWidths;
    const leftFixedColumns = store.leftFixedColumns;
    const lastLeftFixedIndex =
      leftFixedColumns[leftFixedColumns.length - 1]?.index;
    const rightFixedColumns = store.rightFixedColumns;
    const firstRightFixedIndex = rightFixedColumns[0]?.index;
    const style: any = {};
    if (column.fixed) {
      style.position = 'sticky'
      column.fixed === 'left' && (style.left = stickyWidths?.[`${column.index}`]);
      column.fixed === 'right' &&
        (style.right = stickyWidths?.[`${column.index}`]);
      style.zIndex = 1
      style.boxShadow = setBorder ? '1px 0px #f0f0f0' : ''
    }




    if (column.type === '__checkme') {
      return (
        <td
          key={props.key}
          // onContextMenu={e => {
          //   e.preventDefault();
          //   this.setState({ position: [0, ''] });
          // }}
          onClick={checkOnItemClick ? noop : this.handleCheck.bind(this, item)}
          className={cx(
            column.pristine.className,
            // Jay
            rowClassNameExpr
              ? filter(rowClassNameExpr, item.data)
              : rowClassName,
            setBorder ? 'td-border' : '',
            {
              'fixed-left-last': column.index === lastLeftFixedIndex,
              'fixed-right-first': column.index === firstRightFixedIndex
            }
          )}
          style={{ ...style, cursor: 'pointer', width: '27px' }}
        >
          <Checkbox
            classPrefix={ns}
            type={multiple ? 'checkbox' : 'radio'}
            checked={item.checked}
            disabled={!item.checkable}
          // onChange={
          //   checkOnItemClick ? noop : this.handleCheck.bind(this, item)
          // }
          />
        </td>
      );
    } else if (column.type === '__dragme') {
      return (
        <td
          key={props.key}
          className={cx(
            column.pristine.className,
            // Jay
            rowClassNameExpr
              ? filter(rowClassNameExpr, item.data)
              : rowClassName,
            {
              'fixed-left-last': column.index === lastLeftFixedIndex
            }
          )}
          style={style}
        >
          {item.draggable ? <Icon icon="drag-bar" className="icon" /> : null}
        </td>
      );
    } else if (column.type === '__expandme') {
      return (
        <td
          key={props.key}
          className={cx(
            column.pristine.className,
            // Jay
            rowClassNameExpr
              ? filter(rowClassNameExpr, item.data)
              : rowClassName,
            {
              'fixed-left-last': column.index === lastLeftFixedIndex
            }
          )}
          style={style}
          //Aug
          colSpan={store.mobileUI ? 2 : 1}
        >
          {item.depth > 2
            ? Array.from({ length: item.depth - 2 }).map((_, index) => (
              <i key={index} className={cx('Table-divider-' + (index + 1))} />
            ))
            : null}

          {item.expandable ? (
            // Aug 加入mobileUi
            !store.mobileUI || this.state.tableMode == 'horizontal' ? (
              <a
                className={cx(
                  'Table-expandBtn',
                  item.expanded ? 'is-active' : ''
                )}
                // data-tooltip="展开/收起"
                // data-position="top"
                onClick={() => {
                  item.toggleExpanded();
                  this.updateTableInfo()
                }}
              >
                <Icon icon="right-arrow-bold" className="icon" />
              </a>
            ) : (
              <a className={cx('')} onClick={item.toggleExpanded}>
                {item.expanded ? __('PutAway') : __('More')}
              </a>
            )
          ) : <a
            style={{ color: '#756756', cursor: 'unset' }}
            className={cx(
              'Table-expandBtn'
            )}
          // data-tooltip="展开/收起"
          // data-position="top"
          >
            <Icon icon="right-arrow-bold" className="icon" />
          </a>}
        </td>
      );
    } else if (column.type === '__pseudoColumn') {
      return <td key={props.key} style={{ ...style, display: 'block', width: (this.props.store.rows.length + '').length * 13, textAlign: 'center' }}
        className={cx(column.pristine.className,
          rowClassNameExpr
            ? filter(rowClassNameExpr, item.data)
            : rowClassName,
          setBorder ? 'td-border' : '', 'fixed-left-last')} >
        {props.rowIndex + 1}
      </td>;
    }

    let prefixContent: React.ReactNode = null;

    if (
      !ignoreDrag &&
      column.isPrimary &&
      store.isNested &&
      store.draggable &&
      item.draggable
    ) {
      prefixContent = (
        <a
          draggable
          onDragStart={this.handleDragStart}
          className={cx('Table-dragBtn')}
        >
          <Icon icon="drag-bar" className="icon" />
        </a>
      );
    }

    // Jay
    // 对于input-table-dynamic组件的原始数据做判断
    // 禁用
    const { $path } = this.props;
    const isInputTableDynamic = /(input-table-dynamic)/.test($path);
    const isOrigin = item.pristine.isOrigin && isInputTableDynamic;
    //分组时由于value都是undefined，所以不会更新
    let groupValue = {};
    if (column.pristine.group?.length) {
      column.pristine.group.forEach((ele: any) => {
        groupValue = {
          ...groupValue,
          [ele.name]: item.locals[ele.name]
        }
      });
    }
    const subProps: any = {
      ...props,
      className: this.state.activeRow === props.rowIndex && this.state.activeCol === column.name ? (props.classNam || '') + ' active-cell' : (props.className || ''),
      btnDisabled: store.dragging,
      data: item.locals,
      value: column.pristine.group?.length ? groupValue :
        column.name
          ? resolveVariable(
            column.name,
            canAccessSuperData ? item.locals : item.data
          ) : column.value,
      popOverContainer: popOverContainer || this.getPopOverContainer,
      rowSpan: item.rowSpans[column.name as string],
      quickEditFormRef: this.subFormRef,
      prefixContent,
      onImageEnlarge: this.handleImageEnlarge,
      canAccessSuperData,
      row: item,
      inputFocusShowPicker: false,
      crudColumn: column.type == 'mapping' ? column : undefined,
      itemBadge,
      showBadge:
        !props.isHead &&
        itemBadge &&
        store.firstToggledColumnIndex === props.colIndex,
      // Jay
      disabled: isOrigin,
      // showContextMenu: this.showContextMenu,
      isTableContent: true,
      crossValueFields: cross?.valueFields
    };
    delete subProps.label;
    const { foldColumns } = this.props;
    if (column.type === 'operation') {
      const fold = foldColumns?.includes('scale' + column.name);
      return render(
        region,
        {
          ...column.pristine,
          column: column.pristine,
          type: 'cell',
          fold
        },
        { ...subProps, fold, tableRotate }
      );
    }
    const firstColumn = this.props?.columns?.filter((col: any) => col.name && !col.name.includes('__'))?.[0];
    prefixContent == prefixContent || (firstColumn?.name === column.name && store.rows.some(rowItem => rowItem.children?.length) ?
      <span className='expand-tag' onClick={() => {
        item.toggleExpanded();
        this.updateTableInfo();
      }} >
        {item.expandable ? (item.expanded ? <MinusSquareOutlined /> : <PlusSquareOutlined />) : <PlusSquareOutlined style={{ opacity: 0 }} />}
      </span> : null)
    if (column.type === 'mapping' && !column.pristine.quickEdit)
      return render(
        region,
        {
          ...column.pristine,
          column: column.pristine,
          type: 'simple-table-cell'
        },
        {
          ...subProps,
          prefixContent,
          classNames: subProps.className + firstColumn?.name === column.name ? ' expand-cell' : ''
        }
      );
    return render(
      region,
      {
        ...column.pristine,
        column: column.pristine,
        type: 'cell'
      },
      {
        ...subProps,
        prefixContent,
        classNames: subProps.className + firstColumn?.name === column.name ? ' expand-cell' : ''
      }
    );
  }

  // showContextMenu = (rowIndex: number, colName: string) => {
  //   const visible = (colName != undefined && colName != 'operation')
  //   tableCtxMenuStore.updatePosition([rowIndex, colName])
  //   tableCtxMenuStore.onContextMenuVisibleChange(visible);

  //   // this.setState({ position: [rowIndex, colName], contextMenuVisible: visible });
  // }

  renderToolbar(toolbar: SchemaNode) {
    const type = (toolbar as Schema)?.type || (toolbar as string);
    if (type === 'find-replace') {
      this.renderedToolbars.push(type);
      return this.renderFindReplace(toolbar as any);
    } else if (type === 'data-statics') {
      this.renderedToolbars.push(type);
      return this.renderDataStatic(toolbar as any);
    } else if (type === 'data-cross') {
      this.renderedToolbars.push(type);
      return this.renderDataCross(toolbar as any)
    } else if (type === 'columns-toggler') {
      this.renderedToolbars.push(type);
      return this.renderColumnsToggler(toolbar as any);
    } else if (type === 'detail-model') {
      this.renderedToolbars.push(type);
      return this.renderDetailModelToggler(toolbar);
    } else if (type === 'drag-toggler') {
      this.renderedToolbars.push(type);
      return this.renderDragToggler();
    } else if (type === 'export-excel') {
      this.renderedToolbars.push(type);
      return this.renderExportExcel(toolbar);
    } else if (type === 'check-all') {
      //Aug
      this.renderedToolbars.push(type);
      return this.renderCheckAll();
    } else if (type === 'field-translate') {
      this.renderedToolbars.push(type);
      return this.renderFieldTranslate(toolbar as any);
    } else if (type === "data-chart") {
      this.renderedToolbars.push(type);
      return this.renderDataCharts(toolbar as any);
    } else if (type === 'sql-optimize') {
      this.renderedToolbars.push(type);
      return this.renderSqlOptimize(toolbar as Action)
    }

    return void 0;
  }

  /** 渲染sql优化dom */
  renderSqlOptimize = (action: Action) => {
    const { store, columns, data, env } = this.props;
    const columnsArr = columns?.map((item) => item.name) ?? [];
    const selectedList = store.selectedRows?.map((item) => item.data).filter(Boolean) ?? [];
    return (
      <SqlOptimize env={env} selectedList={selectedList} action={action} columns={columnsArr} sqlStr={data?.sql} store={store}></SqlOptimize>
    );
  }

  renderDataCharts = (action: Action) => {
    const { store, selected, aliasTitle, tabTitle, name, primaryField, loadDataOnce, getAllData, env } = this.props;
    const selectedItems = selected?.concat() ?? store.selectedRows.map(item => item.data) ?? [];
    const items = store.rows.map(item => item.data)
    const colList = store.columnsData.filter(col => !col.pristine.hidden && col.type !== 'operation')
      .map((item: any) => item.toJSON ? item.toJSON() : item)
    const itemRaws = store.data?.itemsRaw
    return <DataCharts crudTitle={aliasTitle || tabTitle || ''} container={env.getTopModalContainer ?? this.tableContainer.current}
      data={{ items, selectedItems, itemRaws }} columns={colList} action={action} name={name}
      handleDealData={this.handleDealData} primaryField={primaryField} loadDataOnce={loadDataOnce}
      getAllData={getAllData} />
  }

  renderFieldTranslate = (action: Action) => {
    const { fieldTranslate } = this.state;
    return <div className='show-field-translate-container'>
      <AntCheckbox checked={fieldTranslate} onChange={(e) => this.setState({ fieldTranslate: e.target.checked })}>{action.label || '显示字段翻译'}</AntCheckbox>
    </div>
  }

  updateActiveCell = (row?: number, col?: string) => {
    this.setState({ activeRow: row, activeCol: col })
  }

  //替换
  onBulkReplace = (rowDataList: RowData[]) => {
    const { store } = this.props;
    const modefiedMap = cloneDeep(store.modefiedMap)
    rowDataList.forEach(item => {
      const rowItem = store.rows[item.index];
      const values = item.rowData;
      rowItem.change(values);
      modefiedMap.modifiedDataSet[rowItem.data[DATAKEYID]] = values;
    })
    store.bulkRecordEditValues(modefiedMap)
  }

  renderFindReplace = (action: Action) => {
    const { store, currentSelectedRow, translate, env } = this.props;
    const selectedList = store.selectedRows.map(item => item.index) ?? [];
    const items = store.rows.map(item => item.data)
    const colList = store.columnsData.filter(col => !col.pristine.hidden && col.type !== 'operation')
      .map((item: any) => item.toJSON ? item.toJSON() : item).filter(item => !item.type || ['plain', 'input-text', 'number', 'input-number', 'static-number', 'html', 'static-html', 'input-rich-text', 'date', 'input-date', 'datetime', 'input-datetime', 'time', 'input-time', 'input-date-range', 'input-time-range', 'input-datetime-range', 'input-month', 'input-quarter', 'input-year', 'textarea'].includes(item.type))
    const canReplace = colList.some(item => !!item.pristine?.quickEdit)
    // const itemRaws = store.data?.itemsRaw
    return <FindAndReplace selectedList={selectedList} data={items} columns={colList}
      action={action} onBulkReplace={this.onBulkReplace} canReplace={canReplace}
      currentCheckIndex={currentSelectedRow} updateActiveCell={this.updateActiveCell}
      container={this.tableContainer.current} translate={translate} />
  }

  // Aug
  renderCheckAll() {
    const {
      store,
      multiple,
      selectable,
      classnames: cx,
      classPrefix: ns,
      translate: __
    } = this.props;
    if (
      !store.selectable ||
      !multiple ||
      !selectable ||
      store.dragging ||
      !store.rows.length
    ) {
      return null;
    }

    return (
      <div key="checkall" className={cx('Mobile-checkall')}>
        <Checkbox
          classPrefix={ns}
          type={multiple ? 'checkbox' : 'radio'}
          checked={store.allChecked}
          onChange={() => this.handleCheckAll(3)}
          inline
        />
        <span>{__('Select.checkAll')}</span>
      </div>
    );
  }

  renderColumnsToggler(config?: any) {
    const {
      className,
      store,
      classPrefix: ns,
      classnames: cx,
      headerToolbar,
      ...rest
    } = this.props;
    const __ = rest.translate;
    const env = rest.env;

    if (isMobile()) {
      return (
        <div className='toolbar-item' onClick={() => this.handleToolsClick(config.type)}>
          <div className='toolbar-item-icon'>
            <Icon icon={config.icon} className="batch-manage-icon" />
          </div>
          <div className='toolbar-item-name'>
            {config?.label}
          </div>
        </div>
      )

    }
    return (
      <ColumnToggler
        {...rest}
        {...(isObject(config) ? config : {})}
        tooltip={config?.tooltip || __('Table.columnsVisibility')}
        tooltipContainer={
          env?.getTopModalContainer || undefined
        }
        align={config?.align ?? 'left'}
        isActived={store.hasColumnHidden()}
        classnames={cx}
        classPrefix={ns}
        key="columns-toggable"
        size={config?.size || 'sm'}
        label={
          config?.label || <Icon icon="columns" className="icon m-r-none" />
        }
        draggable={config?.draggable}
        columns={store.columnsData}
        getRawColumns={() => store.rawColumnsData} // Jay
        onColumnToggle={this.handleColumnToggle}
        updateColumnSettingList={(list: Temp[]) => this.setState({ columnSettingTemps: list })}
        indexColShow={this.state.indexColShow}
        setIndexCol={this.setIndexCol}
        flushDebounced={() => this.debounceSendColumns?.flush?.()}
        // originColumns={rest.originColumns}
        columnSettingTemps={this.state.columnSettingTemps}
      >
      </ColumnToggler>
    );
  }

  renderDataStatic = (action?: Action) => {
    const { store, selected, loadDataOnce, getAllData, aliasTitle, tabTitle, name, isStatic } = this.props;
    const selectedItems = selected?.concat() ?? store.selectedRows.map(item => item.data) ?? [];
    const items = store.rows.map(item => item.data)
    const colList = store.columnsData.filter(col => !col.pristine.hidden && col.type !== 'operation')
      .map((item: any) => item.toJSON ? item.toJSON() : item)
    const itemRaws = store.data?.itemsRaw
    return <DataStatic originHeaderToolbar={this.props.headerToolbar} crudTitle={aliasTitle || tabTitle || ''} container={this.props.env?.getTopModalContainer || this.tableContainer.current}
      getAllData={getAllData} data={{ items, selectedItems, itemRaws }} loadDataOnce={loadDataOnce}
      columns={colList} action={action} name={name} isStatic={isStatic} handleDealData={this.handleDealData} />
  }

  renderDataCross = (action: Action) => {
    if (isMobile()) return null
    return (
      <DataCross
        tableName={this.props.name}
        action={action}
        columns={this.props.store.filteredColumns}
        data={{ items: this.props.store.rows.map(row => row.data), selectedItems: this.props.store.selectedRows.map(row => row.data) }}
        modalContainer={this.props.env?.getTopModalContainer || this.tableContainer.current}
        getAllData={this.props.getAllData}
        onOK={(cross, crossColumns, datas, countColumns) => {
          const id = uuid()
          const linkTitle = `${this.props.aliasTitle?.trim?.()}-交叉制表`
          const schema = {
            aliasTitle: linkTitle,
            type: "crud",
            mode: 'cross',
            name: id,
            cross,
            crossColumns,
            "affixHeader": true,
            "columns": this.props.columns,
            "defaultData": datas,
            'affixRow': countColumns,
            "perPage": 10000,
            "autoFillHeight": true,
            "multiple": true,
            "setBorder": true,
            "keepItemSelectionOnPageChange": false,
            "syncLocation": false,
            "headerToolbar": [{
              "redDot": false,
              "actionType": "export",
              "type": "action",
              "name": "default_export",
              "label": "导出",
              "icon": "#icon-tooltool_download",
              "tooltip": "默认导出",
              "align": "right",
              "close": true,
              "tooltipPlacement": "top"
            },
            {
              "type": "data-statics",
              "label": "统计",
              "icon": "#icon-tooltotal",
              "align": "right"
            },
            {
              "type": "data-cross",
              "label": "交叉制表",
              "icon": "#icon-toolcross",
              "align": "right"
            }],
            "footerToolbar": [{
              "type": "statistics",
              "align": "right"
            }],
            "checkOnItemClick": false,
            "header": [],
            "footer": []
          }
          this.handleDealData('data-cross', linkTitle, schema)
        }}
      />
    )
  }


  renderDragToggler() {
    const { store, env, draggable, classPrefix: ns, translate: __ } = this.props;

    if (!draggable || store.isNested) {
      return null;
    }

    return (
      <Button
        disabled={!!store.modified}
        classPrefix={ns}
        key="dragging-toggle"
        tooltip={__('Table.startSort')}
        tooltipContainer={
          env?.getTopModalContainer || undefined
        }
        size="sm"
        active={store.dragging}
        onClick={(e: React.MouseEvent<any>) => {
          e.preventDefault();
          store.toggleDragging();
          store.dragging && store.clear();
        }}
        iconOnly
      >
        <Icon icon="exchange" className="icon" />
      </Button>
    );
  }

  dragTable: Sortable;
  destroyDragTable = () => {
    this.dragTable && this.dragTable.destroy();
  };
  //列拖拽
  initDragTable = () => {
    const { classPrefix: ns } = this.props;
    const el = (findDOMNode(this) as HTMLDivElement).querySelector(
      `.table-head-drag`
    ) as HTMLDivElement;
    if (el) {
      this.dragTable = new Sortable(el, {
        group: `.${ns}Table-table`,
        animation: 150,
        removeCloneOnHide: true,
        // 提供class可拖动
        handle: `.header-cell`,
        ghostClass: 'tableCell-dragglable-dragging',
        dragClass: 'tableCell-dragglable-dragging',
        filter: (e, target) => {
          return target.className.includes('__') || target.className.includes('operation')
        },
        scroll: false,
        fallbackOnBody: false,
        removeOnSpill: true,
        draggable: `.${ns}TableCell-dragglable`,
        onEnd: (e: any) => {
          if (e.newIndex === e.oldIndex || this.onDraging) {
            return;
          }
          this.swapColumnPosition(e.oldIndex, e.newIndex);
        },
      });
    }
  };

  swapColumnPosition = (oldName: string, targetName: string) => {
    const { store, } = this.props;
    let finalColumns = cloneDeep(store.columnsData);
    const newColumns = cloneDeep(store.filteredColumns);
    const oldColumn = newColumns.find(item => item.name === oldName);
    const newColumn = newColumns.find(item => item.name === targetName);
    if (oldColumn && newColumn) {
      const originOldIndex = finalColumns.findIndex(item => item.name === oldColumn.name);
      const originNewIndex = finalColumns.findIndex(item => item.name === newColumn.name);
      finalColumns.splice(
        originNewIndex > originOldIndex ? originNewIndex + 1 : originNewIndex,
        0,
        oldColumn
      );
      originOldIndex > originNewIndex
        ? finalColumns.splice(originOldIndex + 1, 1)
        : finalColumns.splice(originOldIndex, 1)
      const saveColInfo = productColumnInfo(finalColumns)
      let targetTemp = this.state.columnSettingTemps?.length > 0 ? { ...this.state.columnSettingTemps[0], columnInfo: saveColInfo } : undefined
      this.handleColumnToggle(finalColumns, saveColInfo, true, targetTemp, true)
    }
  };

  renderExportExcel(toolbar: ExportExcelToolbar) {
    const {
      store,
      env,
      classPrefix: ns,
      classnames: cx,
      translate: __,
      columns,
      data
    } = this.props;

    if (!columns) {
      return null;
    }

    return (
      <Button
        classPrefix={ns}
        onClick={() => {
          import('exceljs').then(async (ExcelJS: any) => {
            let rows = [];
            let tmpStore;
            let filename = 'data';
            // 支持配置 api 远程获取
            if (typeof toolbar === 'object' && toolbar.api) {
              const res = await env.fetcher(toolbar.api, data);
              if (!res.data) {
                env.notify('warning', __('placeholder.noData'));
                return;
              }
              if (Array.isArray(res.data)) {
                rows = res.data;
              } else {
                rows = res.data.rows || res.data.items;
              }
              // 因为很多方法是 store 里的，所以需要构建 store 来处理
              tmpStore = TableStore.create(getSnapshot(store));
              tmpStore.initRows(rows);
              rows = tmpStore.rows;
            } else {
              rows = store.rows;
            }

            if (typeof toolbar === 'object' && toolbar.filename) {
              filename = filter(toolbar.filename, data, '| raw');
            }

            if (rows.length === 0) {
              env.notify('warning', __('placeholder.noData'));
              return;
            }

            const workbook = new ExcelJS.Workbook();
            const worksheet = workbook.addWorksheet('sheet', {
              properties: { defaultColWidth: 15 }
            });
            worksheet.views = [{ state: 'frozen', xSplit: 0, ySplit: 1 }];

            const filteredColumns = toolbar.columns
              ? columns.filter(column => {
                const filterColumnsNames = toolbar.columns!;
                if (filterColumnsNames.indexOf(column.name) !== -1) {
                  return true;
                }
                return false;
              })
              : columns;

            const firstRowLabels = filteredColumns.map(column => {
              return column.label;
            });
            const firstRow = worksheet.getRow(1);
            firstRow.values = firstRowLabels;
            worksheet.autoFilter = {
              from: {
                row: 1,
                column: 1
              },
              to: {
                row: 1,
                column: firstRowLabels.length
              }
            };
            // 用于 mapping source 的情况
            const remoteMappingCache: any = {};
            // 数据从第二行开始
            let rowIndex = 1;
            for (const row of rows) {
              rowIndex += 1;
              const sheetRow = worksheet.getRow(rowIndex);
              let columIndex = 0;
              for (const column of filteredColumns) {
                columIndex += 1;
                const name = column.name!;
                const value = getVariable(row.data, name);
                if (
                  typeof value === 'undefined' &&
                  !(column as TplSchema).tpl
                ) {
                  continue;
                }
                // 处理合并单元格
                if (name in row.rowSpans) {
                  if (row.rowSpans[name] === 0) {
                    continue;
                  } else {
                    // start row, start column, end row, end column
                    worksheet.mergeCells(
                      rowIndex,
                      columIndex,
                      rowIndex + row.rowSpans[name] - 1,
                      columIndex
                    );
                  }
                }

                const type = (column as BaseSchema).type || 'plain';
                const body = column?.body;

                if (type === 'image' && value) {
                  try {
                    const imageData = await toDataURL(value);
                    const imageDimensions = await getImageDimensions(imageData);
                    let imageWidth = imageDimensions.width;
                    let imageHeight = imageDimensions.height;
                    // 限制一下图片高宽
                    const imageMaxSize = 100;
                    if (imageWidth > imageHeight) {
                      if (imageWidth > imageMaxSize) {
                        imageHeight = (imageMaxSize * imageHeight) / imageWidth;
                        imageWidth = imageMaxSize;
                      }
                    } else {
                      if (imageHeight > imageMaxSize) {
                        imageWidth = (imageMaxSize * imageWidth) / imageHeight;
                        imageHeight = imageMaxSize;
                      }
                    }
                    const imageMatch = imageData.match(/data:image\/(.*);/);
                    let imageExt = 'png';
                    if (imageMatch) {
                      imageExt = imageMatch[1];
                    }
                    // 目前 excel 只支持这些格式，所以其它格式直接输出 url
                    if (
                      imageExt != 'png' &&
                      imageExt != 'jpeg' &&
                      imageExt != 'gif'
                    ) {
                      sheetRow.getCell(columIndex).value = value;
                      continue;
                    }
                    const imageId = workbook.addImage({
                      base64: imageData,
                      extension: imageExt
                    });
                    const linkURL = getAbsoluteUrl(value);
                    worksheet.addImage(imageId, {
                      // 这里坐标位置是从 0 开始的，所以要减一
                      tl: { col: columIndex - 1, row: rowIndex - 1 },
                      ext: {
                        width: imageWidth,
                        height: imageHeight
                      },
                      hyperlinks: {
                        tooltip: linkURL
                      }
                    });
                  } catch (e) {
                    console.warn(e.stack);
                  }
                } else if (type == 'link') {
                  const linkURL = getAbsoluteUrl(value);
                  sheetRow.getCell(columIndex).value = {
                    text: value,
                    hyperlink: linkURL
                  };
                } else if (
                  type === 'mapping' ||
                  (type === 'container' && body.type === 'mapping')
                ) {
                  // 拷贝自 Mapping.tsx
                  let map =
                    type === 'mapping'
                      ? (column as MappingSchema).map
                      : column?.body?.map;
                  const source =
                    type === 'mapping'
                      ? (column as MappingSchema).source
                      : column?.body?.source;
                  if (source) {
                    let sourceValue = source;
                    if (isPureVariable(source)) {
                      sourceValue = resolveVariableAndFilter(
                        source as string,
                        data,
                        '| raw'
                      );
                    }

                    const mapKey = JSON.stringify(source);
                    if (mapKey in remoteMappingCache) {
                      map = remoteMappingCache[mapKey];
                    } else {
                      const res = await env.fetcher(sourceValue, data);
                      if (res.data) {
                        remoteMappingCache[mapKey] = res.data;
                        map = res.data;
                      }
                    }
                  }

                  if (
                    typeof value !== 'undefined' &&
                    map &&
                    (map[value] ?? map['*'])
                  ) {
                    const viewValue =
                      map[value] ??
                      (value === true && map['1']
                        ? map['1']
                        : value === false && map['0']
                          ? map['0']
                          : map['*']); // 兼容平台旧用法：即 value 为 true 时映射 1 ，为 false 时映射 0
                    sheetRow.getCell(columIndex).value =
                      removeHTMLTag(viewValue);
                  } else {
                    sheetRow.getCell(columIndex).value = removeHTMLTag(value);
                  }
                } else {
                  if ((column as TplSchema).tpl) {
                    sheetRow.getCell(columIndex).value = removeHTMLTag(
                      filter(
                        (column as TplSchema).tpl,
                        createObject(data, row.data)
                      )
                    );
                  } else if (
                    type === 'lion-cell-file' ||
                    type === 'lion-cell-img'
                  ) {
                    sheetRow.getCell(columIndex).value = value.value || '';
                  } else {
                    sheetRow.getCell(columIndex).value = value;
                  }
                }
              }
            }

            const buffer = await workbook.xlsx.writeBuffer();

            if (buffer) {
              var blob = new Blob([buffer], {
                type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
              });
              saveAs(blob, filename + '.xlsx');
            }
          });
        }}
        size="sm"
      >
        {(toolbar as Schema).label || __('CRUD.exportExcel')}
      </Button>
    );
  }

  renderActions(region: string) {
    let { actions, render, store, classnames: cx, data, headerActions, headerBulkActions } = this.props;
    actions = Array.isArray(actions) ? actions.concat() : [];
    if (region === 'header') {
      actions = actions.concat(headerActions || [], (headerBulkActions || []).map((item: any) => ({ ...item, isBulk: true })))
    }
    // Aug
    let btn;
    if (
      !~this.renderedToolbars.indexOf('check-all') &&
      !store.hideCheckToggler &&
      store.mobileUI &&
      (btn = this.renderCheckAll())
    ) {
      actions.unshift({
        type: 'button',
        children: btn
      });
    }

    if (
      store.draggable &&
      !store.isNested &&
      region === 'header' &&
      store.rows.length > 1 &&
      !~this.renderedToolbars.indexOf('drag-toggler')
    ) {
      actions.push({
        type: 'button',
        children: this.renderDragToggler()
      });
    }

    return Array.isArray(actions) && actions.length ? (
      <div className={cx('Table-actions')}>
        {actions.map((action, key) =>
          action.isBulk ? this.props.bulkActionRender?.(action, key) : render(
            `action/${key}`,
            {
              type: 'button',
              ...(action as any)
            },
            {
              onAction: this.handleAction,
              key,
              btnDisabled: store.dragging,
              data: store.getData(data)
            }
          )
        )}
      </div>
    ) : null;
  }
  renderDetailModelToggler = (config) => {
    // const switchModelRender = {
    //   label: __(this.state.tableMode === 'vertical' ? 'Table.switchToHorizontal' : 'Table.switchToVertical'),
    //   type: 'detail-model', icon: this.state.tableMode === 'vertical' ? <FileSyncOutlined /> : <ProjectOutlined />
    // }
    const { translate: __ } = this.props;
    const label = __(this.state.tableMode === 'vertical' ? 'Table.switchToHorizontal' : 'Table.switchToVertical');
    const icon = this.state.tableMode === 'vertical' ? <FileSyncOutlined /> : <ProjectOutlined />;

    return (
      <>
        <div className='toolbar-item' onClick={() => this.handleToolsClick(config.type)}>
          <div className='toolbar-item-icon'>{icon}</div>
          <div className='toolbar-item-name'>{label}</div>
        </div>
      </>
    )
  }
  handleToolsClick = (type: string) => {
    const { store, handleResetData } = this.props;
    switch (type) {
      case 'columns-toggler':
        this.setState({ columnsTogglerShow: true })
        this.debounceSendColumns?.cancel()
        break;
      case 'detail-model':
      case 'switch-layout':
        this.setState({ tableMode: this.state.tableMode == 'vertical' ? 'horizontal' : 'vertical' }, () => {
          store.update({
            footable: this.state.tableMode === 'vertical' ? true : false
          })
        })
        break;
      default:
        break;
    }
  }
  /** 移动端 - 渲染工具 */
  renderTools = () => {
    const {
      headerToolbar,
      translate: __,
      classnames: cx,
      env,
      store,
      data,
      filterRender,
      isPick,
      clearSelectedItems,
      crudRenderToolbarFunc,
      multiple,
    } = this.props;
    /** 折叠在工具里的tools */
    const foldedHeaderTools = [];
    /** 在筛选/多选右侧的tools */
    const unfoldedHeaderTools = [];

    /** 将移动端需要显示的功能过滤出来： 目前工具栏仅保留 ai工具，设置，详情模式 */
    // const switchModelRender = {
    //   label: __(this.state.tableMode === 'vertical' ? 'Table.switchToHorizontal' : 'Table.switchToVertical'),
    //   type: 'detail-model', icon: this.state.tableMode === 'vertical' ? <FileSyncOutlined /> : <ProjectOutlined />
    // }
    headerToolbar.forEach((item) => {
      const { type, isToolBar, foldable } = item;
      /** 是否是移动端的工具 */
      if (isToolBar) {
        /** foldable: 是否归到折叠菜单中 */
        /** 配置非折叠的工具 */
        if (!foldable) {
          unfoldedHeaderTools.push(item);
        } else {
          foldedHeaderTools.push(item);
        }
      }
    });

    // const tools = headerToolbar.filter((item: any) => (['columns-toggler'].includes(item.type) && store.columnsTogglable) || ['help', 'ai-tool', 'reload'].includes(item.type));
    /** 再添加一个 列表模式切换 */
    // foldedHeaderTools.push({
    //   label: __(this.state.tableMode === 'vertical' ? 'Table.switchToHorizontal' : 'Table.switchToVertical'),
    //   type: 'switch-layout', icon: this.state.tableMode === 'vertical' ? <FileSyncOutlined /> : <ProjectOutlined />
    // });
    /** 筛选 */
    const isFilter = !!Object.keys(data.filterParam || {}).length;
    const filterNode = filterRender && filterRender(isFilter);
    /** 选择 */
    const checkAll =
      isPick && multiple ? (
        this.renderCheckAll()
      ) : this.props.headerBulkActions?.length ||
        this.props.footerBulkActions?.length ? (
        <span
          className={cx('Mobile-batch-manage', {
            'is-active': !store.hideCheckToggler
          })}
          onClick={e => {
            e.preventDefault();
            clearSelectedItems?.();
            store.toggableHideCheck();
            store.clear();
          }}
        >
          {store.hideCheckToggler ? (
            <>
              {/* <i className="fa fa-tasks batch-manage-icon"></i> */}
              <Icon icon="#icon-tooltool_list" className="batch-manage-icon" />
              <span className="batch-text">{__('CRUD.select')}</span>
            </>
          ) : (
            __('Wizard.finish')
          )}
        </span>
      ) : null;
    return (<>
      {foldedHeaderTools?.length ? (<Popover showArrow={false} placement="bottom" getPopupContainer={env.getModalContainer}
        trigger="click" overlayClassName="table-toolbar-pop" autoAdjustOverflow visible={this.state.toolbarShow}
        onOpenChange={(open) => this.setState({ toolbarShow: open })}
        content={
          <div className='toolbar-container' onClick={() => this.setState({ toolbarShow: false })}>
            {
              foldedHeaderTools.map((item: any, index: number) => {
                return this.renderToolbar(item) ?? crudRenderToolbarFunc(item);
              })
            }
          </div>}>
        <div className={cx('Mobile-batch-manage')} onClick={() => this.setState({ toolbarShow: true })}>
          {/* <i className="fa fa-cog" aria-hidden="true"></i> */}
          <Icon icon='#icon-toolbox' className="icon" />
          <span className='batch-text'>{__('Table.tools')}</span>
        </div>
      </Popover>) : null}
      {/* 前端维护的固定的刷新/筛选 */}
      {filterNode}
      {checkAll}

      {/* 手动配置的 展示在右侧的按钮 */}
      {
        unfoldedHeaderTools.map((item) => {
          return (
            <div className={cx('Mobile-batch-manage')}>
              {this.renderToolbar(item) ?? crudRenderToolbarFunc(item)}
            </div>
          )
        })
      }
    </>)
  }

  renderPagenation = () => {
    const { store, selected, classnames: cx, multiple, popOverContainer, footerToolbarRender, footerToolbar } = this.props;
    const column = store.filteredColumns[0] && store.filteredColumns[0].type == "__checkme";
    const selectedItems = selected?.concat() ?? store.selectedRows.map(item => item.data) ?? [];
    let _footer = footerToolbar || [];
    // 居右的这里展示
    const leftChild = footerToolbarRender ? footerToolbarRender(
      {
        ...this.props,
        selectedItems: store.selectedRows.map(item => item.data),
        items: store.rows.map(item => item.data)
      },
      this.renderToolbar,
      isMobile() ? _footer : _footer.filter((item: any) => item.align !== 'right') //Aug
    ) : null;
    // 居左的在这边
    const rightChild = footerToolbarRender ? footerToolbarRender(
      {
        ...this.props,
        selectedItems: store.selectedRows.map(item => item.data),
        items: store.rows.map(item => item.data)
      },
      this.renderToolbar,
      _footer.filter((item: any) => item.align == 'right') //Aug
    ) : null;
    const checkAllWidth = (this.table?.querySelector('thead')?.querySelector('th')?.clientWidth ?? 25) + 'px'
    return <div className={cx('Table-affix')} >
      {
        column && multiple && this.state.selectedIndex != undefined && (
          <span className={cx('Table-affixs')} style={{ marginRight: 8, alignItems: 'center' }}>
            {column && multiple && <span className={cx('Table-affix-Arr')} >
              {this.renderHeadCell(store.filteredColumns[0], { style: { width: checkAllWidth, paddingBottom: '2px' } })}
            </span>}
            <div className={cx('Table-affix-text')} >
              {store.filterColumns?.size > 0 && <span>已筛选{store.rows.length}条数据</span>}
              {(selectedItems.length == 0 ? "" : `已选：${selectedItems.length}行` + " , ")}
              {/* 有勾选效果展示第几行 */}
              {this.props.store.filteredColumns?.find(_ => _.type === '__checkme') ? (this.state.selectedIndex != undefined && `当前：第${this.state.selectedIndex + 1}行`) : null}
            </div>
          </span>
        )
      }
      <>
        {leftChild}
        {rightChild}
      </>
    </div>
  }

  renderHeader(editable?: boolean) {
    const {
      header,
      headerClassName,
      toolbarClassName,
      headerToolbarClassName,
      headerToolbarRender,
      render,
      showHeader,
      store,
      classnames: cx,
      data,
      filterRender, //Aug
      translate: __,
      isPick,
      multiple,
      clearSelectedItems,
      handleResetData
    } = this.props;
    if (showHeader === false) {
      return null;
    }

    // Aug
    if (isMobile()) {
      {
        /* 搜索 */
      }
      // const isFilter = !!Object.keys(data.filterParam || {}).length;
      // const filterNode = filterRender && filterRender(isFilter);
      {
        /* 批量 */
      }
      // const checkAll =
      //   isPick && multiple ? (
      //     this.renderCheckAll()
      //   ) : this.props.headerBulkActions?.length ||
      //     this.props.footerBulkActions?.length ? (
      //     <span
      //       className={cx('Mobile-batch-manage', {
      //         'is-active': !store.hideCheckToggler
      //       })}
      //       onClick={e => {
      //         e.preventDefault();
      //         clearSelectedItems?.();
      //         store.toggableHideCheck();
      //         store.clear();
      //       }}
      //     >
      //       {store.hideCheckToggler ? (
      //         <>
      //           <i className="fa fa-tasks batch-manage-icon"></i>
      //           <span className="batch-text">{__('CRUD.select')}</span>
      //         </>
      //       ) : (
      //         __('Wizard.finish')
      //       )}
      //     </span>
      //   ) : null;
      return <div className={cx('Mobile-header-toolbar-wrapper')}>
        {/* 页眉 */}
        {/* {this.props.tipsHeader && isMobile() ? <TipsContanier
          data={store.data}
          cx={cx}
          props={this.props}
          contentString={'你好 页眉'}
          tipsContent={this.props.tipsHeader}
          pageUniqueMark={this.props.name + 'header'}
        ></TipsContanier> : null} */}
        {
          this.props.tipsHeader && isMobile() ? render('alert', this.props.tipsHeader, { cx, pageUniqueMark: this.props.name + 'header', data: store.data }) : null
        }
        {this.props.renderAggregate?.()}
        {/* 页眉 */}
        <div style={{ marginTop: '4px' }}>
          <div className="filter-conditions">
            {this.props.type !== 'lion-bi-table' && typeof data.total == 'number' ? __('CRUD.total', { total: data.total }) : null}
            {data.selectedItems?.length ? (
              <span
                style={{ marginLeft: 10 }}
              >{__('CRUD.checked', { count: data.selectedItems.length })}</span>
            ) : null}
          </div>
          <div className="header-btns">
            {this.renderTools()}
          </div>
        </div>
      </div>
    }

    const otherProps: any = {};
    // editable === false && (otherProps.$$editable = false);

    const child = headerToolbarRender
      ? headerToolbarRender(
        {
          ...this.props,
          selectedItems: store.selectedRows.map(item => item.data),
          items: store.rows.map(item => item.data),
          unSelectedItems: store.unSelectedRows.map(item => item.data),
          ...otherProps
        },
        this.renderToolbar,
        isMobile() ? null : this.renderPagenation
      )
      : null;

    const actions = this.renderActions('header');
    const toolbarNode =
      actions || child || store.dragging ? (
        <div
          className={cx(
            'Table-toolbar Table-headToolbar',
            toolbarClassName,
            headerToolbarClassName
          )}
          style={{ paddingTop: this.props.headerCanShow ? '8px' : '2px' }}
          key="header-toolbar"
        >
          {actions}
          {child}
          {store.dragging ? (
            <div className={cx('Table-dragTip')} ref={this.dragTipRef}>
              {__('Table.dragTip')}
            </div>
          ) : null}
        </div>
      ) : null;
    const headerNode =
      header && (!Array.isArray(header) || header.length) ? (
        <div className={cx('Table-header', headerClassName)} key="header">
          {render('header', header, {
            ...(editable === false ? otherProps : null),
            data: store.getData(data)
          })}
        </div>
      ) : null;

    return headerNode && toolbarNode
      ? [headerNode, toolbarNode]
      : headerNode || toolbarNode || null;
  }

  onToolsChange = (key: string) => {
    this.setState({ currentKey: key })
  }

  onToolsClose = (key: string) => {
    this.setState((prev) => ({
      processToolsModalList: prev.processToolsModalList.filter(item => item.key !== key),
      currentKey: ''
    }))
  }

  //使用二次加工工具处理完数据后，新增一个弹窗
  handleDealData = (toolType: string, label: string, schema: any, subTitle?: string) => {
    const { processToolsModalList } = this.state;
    this.setState({
      currentKey: schema.name,
      processToolsModalList: [...processToolsModalList, { toolType, key: schema.name, label, schema, subTitle }]
    })
  }

  getTabContainer() {
    return domUtils.closest(this.tableContainer.current as HTMLElement, 'div.amis-scope')
  }


  affix = (isAll?: boolean) => {
    const { store, affixRow, selected } = this.props;
    const selectedItems = selected?.concat() ?? store.selectedRows.map(item => item.data) ?? [];
    const items = isAll ? store.rows.map(item => item.data) : selectedItems;
    const calcValue = (groupItem: any) => {
      const rawName = groupItem.rawName ?? groupItem.name
      const field = affixRow?.find((aitem: CountItem) => rawName === aitem.name)
      if (field) {
        const formulaRule = field.formula?.toLocaleLowerCase()
        const showText = (field.label ?? '') + '：'
        let value: any = calcFn(formulaRule, rawName ?? '', items)
        if (formulaRule === 'count') {
          value = showText + value
          return value
        }
        if (groupItem?.showUppercase) {
          value = showText + translateNumber(value, groupItem?.showUppercase)
          return value
        }
        value = value.toFixed(groupItem?.precision ?? 2)
        if (groupItem?.kilobitSeparator && groupItem?.showUppercase === 0) {
          value = numberFormatter(value, groupItem?.precision ?? 2);
        }
        value = `${showText}${groupItem.prefix ?? ''}${value}${groupItem.type === 'progress' ? '%' : (groupItem.suffix ?? '')}`
        return value
      }
      return null
    }
    if (affixRow?.length > 0 && items.length > 0) {
      const columns: Array<IColumn> = store.filteredColumns;
      const affixRes = columns.map(item => {
        if (item.pristine?.group?.length) {
          let list = item.pristine.group.map((groupItem: any) => calcValue(groupItem))
          list = list.filter((info: any) => info != null)
          return list.length > 0 ? list.join(',') : null
        }
        return calcValue(item.pristine)
      })
      return (isAll ? "本页小计：" : "已选：") + affixRes.filter(info => info != null).join(',')
    }
    return null;
  }
  renderFooter() {
    const {
      footer,
      toolbarClassName,
      footerToolbarClassName,
      footerClassName,
      footerToolbarRender,
      headerToolbar, //Aug
      footerToolbar, //Aug
      render,
      showFooter,
      store,
      data,
      classnames: cx,
      tableRotate,
      translate,
      headerActions,
      env
    } = this.props;
    const { currentKey, processToolsModalList } = this.state;
    if (showFooter === false) {
      return null;
    }
    let _footer = footerToolbar || [];
    if (store.mobileUI) {
      // 勾选批量状态时
      if (!store.hideCheckToggler) {
        _footer = ['headerBulkActions', 'footerBulkActions'];
      } else {
        if (headerToolbar) {
          // 移动端将所有操作统一放在在底部
          _footer = _footer.concat(headerActions || [], headerToolbar);
        }
        // 非勾选状态不渲染批量操作 分页也不渲染
        _footer = _footer.filter((item: any) => {
          const type = (item as Schema).type || item;
          return ![
            'bulk-actions',
            'headerBulkActions',
            'footerBulkActions',
            'bulkActions',
            'pagination',
            'statistics',
            'switch-per-page'
          ].includes(type);
        });
      }
    }

    // 居右的这里展示
    const leftChild = footerToolbarRender
      ? footerToolbarRender(
        {
          ...this.props,
          selectedItems: store.selectedRows.map(item => item.data),
          items: store.rows.map(item => item.data)
        },
        this.renderToolbar,
        isMobile() ? _footer : _footer.filter((item: any) => item.align !== 'right') //Aug
      )
      : null;
    const actions = this.renderActions('footer');
    let footerNode =
      footer && (!Array.isArray(footer) || footer.length) ? (
        <div className={cx('Table-footer', footerClassName)} key="footer">
          {render('footer', footer, {
            data: store.getData(data)
          })}
        </div>
      ) : null;
    const toolbarNode =
      actions || leftChild || processToolsModalList.length > 0 ? (
        <div
          className={cx(
            'Table-toolbar Table-footToolbar',
            toolbarClassName,
            isMobile() ? 'mobile-toolbar' : '',
            footerToolbarClassName
          )}
          key="footer-toolbar"
        >
          {actions}
          {/* // 移动端的这个要留下 */}
          {isMobile() ? leftChild : null}
          {
            tableRotate && footerNode
          }
          {/* 弹窗收起的部分 */}
          {processToolsModalList.length > 0 && <ProcessToolsModal render={render}
            onChange={this.onToolsChange} translate={translate}
            onDelete={this.onToolsClose}
            currentKey={currentKey}
            env={env}
            container={this.getTabContainer() || env?.getModalContainer?.() || this.tableContainer.current}
            modalGroup={processToolsModalList} />}
        </div>
      ) : null;
    return footerNode && toolbarNode
      ? [toolbarNode, tableRotate ? null : footerNode]
      : (tableRotate ? null : footerNode) || toolbarNode || null;
  }

  itemCheckHandle = (item: any) => {
    this.props.checkOnItemClick ? noop() : this.handleCheck.bind(this, item)(item)
  }

  changeSelectedRow = (index) => {
    this.setState({ selectedIndex: index })
    this.props.changeSelectedRow?.(index)
  }

  // itemContenxtMenuHandle = (visible: boolean) => {
  //   if (!visible) {
  //     this.setState({ contextMenuVisible: false });
  //   }
  // }
  /** ai助手render */
  renderAiTool(action?: Action) {
    const newData = createObject(
      this.props.storeForAiTool?.filterData ?? {},
      this.props.storeForAiTool?.query ?? {},
      {},
      true,
    );
    const { curApiForCache = '' } = this.props.storeForAiTool?.getRawCacheData?.()
    return (
      <AiTool
        pageId={this.props.name ?? ''}
        action={action}
        env={{ ...this.props.env, container: this.tableContainer?.current }}
        data={newData}
        paramData={curApiForCache}
        render={this.props.render}
        open={this.state.showAiTool}
        onClose={() => this.setState({ showAiTool: false })}
      />
    )
  }

  renderTableContent() {
    const {
      classnames: cx,
      tableClassName,
      store,
      placeholder,
      render,
      checkOnItemClick,
      buildItemProps,
      rowClassNameExpr,
      rowClassName,
      prefixRow,
      locale,
      affixRow,
      tableContentClassName,
      translate,
      itemAction,
      autoFillHeight,
      itemActions,
      primaryField,
      infinteLoad,
      onLoadMore,
      loadHasMore,
      loading,
      tableType,
      tableRotate,
      handleJump,
      loadDataOnce,
      classPrefix,
      loadmoreLoading,
      showIndex,
      footerToolbar,
      env
    } = this.props;

    // 理论上来说 store.rows 应该也行啊
    // 不过目前看来只有这样写它才会重新更新视图
    store.rows.length;
    const columns = store.filteredColumns;

    return (
      <TableContent
        renderAffix={this.affix}
        tableId={this.tableId}
        tableName={this.props.name}
        autoWidth={this.props.autoWidth}
        tableClassName={cx(
          store.combineNum > 0 ? 'Table-table--withCombine' : '',
          {
            'Table-table--checkOnItemClick': checkOnItemClick,
            // Aug 包含勾选框的情况
            'has-checkbox':
              store.mobileUI && columns.some(item => item.type === '__checkme'),
            'checkbox-show': !store.hideCheckToggler,
            'hideCheckToggler': store.hideCheckToggler,
            'hideIndexCol': !this.state.indexColShow
          },
          tableClassName
        )}
        checkItem={this.itemCheckHandle}
        // showContextMenu={this.showContextMenu}
        setBorder={this.props.setBorder}
        multiple={this.props.multiple}
        canAccessSuperData={this.props.canAccessSuperData}
        tableWindowInstance={this.tableWindowRef}
        className={tableContentClassName}
        itemActions={itemActions}
        itemAction={itemAction}
        store={store}
        classnames={cx}
        columns={columns}
        // columnsGroup={store.columnGroup}
        rows={store.rows}
        placeholder={placeholder}
        render={render}
        handleResetData={this.props.handleResetData}
        onMouseMove={this.handleMouseMove}
        onScroll={this.handleOutterScroll}
        tableRef={this.tableRef}
        renderHeadCell={this.renderHeadCell}
        renderCell={this.renderCell}
        onCheck={this.handleCheck}
        onQuickChange={store.dragging ? undefined : this.handleQuickChange}
        footable={store.footable}
        footableColumns={store.footableColumns}
        checkOnItemClick={checkOnItemClick}
        buildItemProps={buildItemProps}
        onAction={this.handleAction}
        rowClassNameExpr={rowClassNameExpr}
        rowClassName={rowClassName}
        data={store.data}
        prefixRow={prefixRow}
        affixRow={affixRow}
        locale={locale}
        translate={translate}
        // Jay
        autoFillHeight={autoFillHeight}
        primaryField={primaryField}
        // position={this.state.position}
        // contextMenuVisible={this.state.contextMenuVisible}
        // onContextMenuVisibleChange={this.itemContenxtMenuHandle}
        infinteLoad={infinteLoad}
        onLoadMore={onLoadMore}
        loadHasMore={loadHasMore}
        tableType={tableType}
        tableLayout={this.state.tableMode}
        loading={loading}
        handleMultiColumnSort={this.handleMultiColumnSort}
        tableRotate={tableRotate}
        handleJump={handleJump}
        loadDataOnce={loadDataOnce}
        classPrefix={classPrefix}
        loadmoreLoading={loadmoreLoading}
        showPerPage={footerToolbar?.some((item: any) => item.type == 'switch-per-page') ?? false}
        changeSelectedRow={this.changeSelectedRow}
        type={this.props.type}
        countField={this.props.countField}
        affixRowPosition={this.props.affixRowPosition}
        activeRow={this.state.activeRow}
        activeCol={this.state.activeCol}
        hiddenRowHighlight={this.props.type == 'cross' && this.props.cross?.positionType === 1}
        env={env}
      />
    );
  }
  tableContainer: React.RefObject<HTMLDivElement> = React.createRef();
  //拖拽的按钮
  btnDragging: 'offline' | 'rotate' | null = null;
  //起始位置
  originX = 0;
  originY = 0;
  //拖动过程中的临时位置
  movingX = 0;
  movingY = 0;

  btnTouchStart = (e: React.TouchEvent<HTMLDivElement>, btn: typeof this.btnDragging = 'rotate') => {
    e.persist()
    e.preventDefault()
    e.stopPropagation()
    if (e.touches.length > 0) {
      const touch = e.touches[0];
      this.originX = this.movingX = touch.clientX; // 记录触摸开始的横坐标
      this.originY = this.movingY = touch.clientY; // 记录触摸开始的纵坐标
      this.btnDragging = btn;
    }
  }
  btnToucheMove = (e: React.TouchEvent<HTMLDivElement>, btn: typeof this.btnDragging = 'rotate') => {
    e.persist()
    e.preventDefault()
    e.stopPropagation()
    const { classPrefix: ns } = this.props;
    const table = findDOMNode(this) as HTMLDivElement;
    const tabsDom = table.closest(`.${ns}Tabs--content-tiled`) as HTMLDivElement;
    if (tabsDom) {
      tabsDom.style.overflow = 'hidden';
    }
    if (e.changedTouches.length > 0) {
      this.btnDragging = btn;
      const touch = e.changedTouches[0];
      const endX = touch.clientX; // 触摸结束的横坐标
      const endY = touch.clientY; // 触摸结束的纵坐标
      const moveX = endX - this.movingX; // 横向移动距离
      const moveY = endY - this.movingY; // 纵向移动距离
      if (moveX == 0 && moveY == 0) {
      } else {
        this.movingX = touch.clientX;
        this.movingY = touch.clientY;
        const { classPrefix: ns } = this.props;
        const tableContent = this.tableContainer.current?.querySelector(`.${ns}Table-content`)
        if (this.btnDragging === 'rotate') {
          let nx = this.props.tableRotate ? this.state.rotateX - moveY : this.state.rotateX - moveX;
          let ny = this.props.tableRotate ? this.state.rotateY + moveX : this.state.rotateY - moveY;
          //边界处理
          if (nx <= 2) nx = 2;
          if (nx >= (this.tableContainer.current?.clientWidth || document.body.clientWidth) - 50) nx = (this.tableContainer.current?.clientWidth || document.body.clientWidth) - 50;
          if (ny <= 2) ny = 2;
          if (ny >= tableContent?.clientHeight! - 50) ny = tableContent?.clientHeight! - 50
          this.setState({ rotateX: nx, rotateY: ny })
        }
        if (this.btnDragging === 'offline') {
          let nx = this.state.offlineX - moveX;
          let ny = this.state.offlineY - moveY;
          if (nx <= 2) nx = 2;
          if (nx >= (this.tableContainer.current?.clientWidth || document.body.clientWidth) - 50) nx = (this.tableContainer.current?.clientWidth || document.body.clientWidth) - 50;
          if (ny <= 2) ny = 2;
          if (ny >= tableContent?.clientHeight! - 50) ny = tableContent?.clientHeight! - 50
          this.setState({ offlineX: nx, offlineY: ny })
        }
      }
    }
  }
  btnTouchEnd = (e: React.TouchEvent<HTMLDivElement>) => {
    e.persist()
    e.preventDefault()
    e.stopPropagation()
    const { classPrefix: ns } = this.props;
    const table = findDOMNode(this) as HTMLDivElement;
    const tabsDom = table.closest(`.${ns}Tabs--content-tiled`) as HTMLDivElement;
    if (tabsDom) {
      tabsDom.style.overflow = 'auto';
    }
    if (e.changedTouches.length > 0) {
      const touch = e.changedTouches[0];
      const endX = touch.clientX; // 触摸结束的横坐标
      const endY = touch.clientY; // 触摸结束的纵坐标
      // 如果拖动的距离很小，认为是一次单击事件
      const distance = Math.sqrt(
        (endX - this.originX) ** 2 + (endY - this.originY) ** 2
      );
      if (distance < 1) {
        // 在这里处理单击事件
        if (this.btnDragging === 'rotate') {
          this.props?.handleTableRotate()
        }
        if (this.btnDragging === 'offline') {
          this.props?.handleClickOffline()
        }
      }
    }
    this.btnDragging = null;
  }

  render() {
    const {
      className,
      store,
      classnames: cx,
      autoFillHeight = true,
      autoGenerateFilter,
      isPick,
      multiple,
      tableRotate,
      offlineSchema,
      render,
      renderAggregate
    } = this.props;
    this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了，已经渲染了就不重复渲染了。
    const heading = this.renderHeading();
    const header = this.renderHeader();
    const footer =
      isPick && multiple && isMobile() ? null : this.renderFooter();

    return (
      <Provider tableCtxMenuStore={tableCtxMenuStore}>

        <div
          className={cx('Table', `Table-${this.state.tableMode}`, className, {
            'Table--unsaved': !!store.modified || !!store.moved,
            'Table--autoFillHeight': autoFillHeight,
            'is-mobile': store.mobileUI,
            tableRotate
          })}
          ref={this.tableContainer}
          table-name={this.props.name}
        >
          {autoGenerateFilter ? this.renderAutoFilterForm() : null}
          {!isMobile() && renderAggregate?.()}
          {header}
          {heading}
          <div
            ref={this.tableWindowRef}
            className={cx('Table-contentWrap table-ping-right', {
              hasHeader: header,
              hasFooter: footer,
              isIos: Shell.hasShell() && tools.isIOS,//处理横屏后ios中sticky样式的兼容问题
              isXsIos: Shell.hasShell() && tools.isIOS && document.body.clientHeight <= 680
            })}
            onMouseLeave={this.handleMouseLeave}
            style={{ position: 'relative' }}
          >

            <div style={{ height: this.state.pullY, textAlign: 'center' }}>
              {this.state.pullY > 0 && <Bubble y={80} />}
            </div>
            {<div ref={this.dragShadowRef}
              onDrop={this.onHeaderDrop}
              onDragEnd={this.onHeaderDragEnd}
              onDragOver={this.onHeaderDragOver} draggable="true" style={{ position: 'absolute', zIndex: 10, background: 'rgba(0,0,0,.1)' }} className='drag-shadow' />}
            {<div ref={this.dragLineShadowRef}
              style={{ position: 'absolute', width: '1px', display: 'none', zIndex: 10, border: '1px dashed rgb(37 79 210 / 54%)' }} className='drag-shadow' />}
            {this.renderTableContent()}
            {
              isMobile() && Shell.hasShell() && offlineSchema && offlineSchema.offlinePageType !== 'detail' && < div
                className='offline-opt-in'
                onTouchStart={(e) => this.btnTouchStart(e, 'offline')}
                onTouchMove={(e) => this.btnToucheMove(e, 'offline')}
                onTouchEnd={this.btnTouchEnd}
                style={{ bottom: this.state.offlineY, right: this.state.offlineX }}
              >
                <Icon icon='offline-icon' className='icon' />
              </div>
            }
            {
              isMobile() && Shell.hasShell() && this.state.tableMode === 'horizontal' && this.props.tableTranspose && <div
                className='mobile-view-mode'
                onTouchStart={(e) => this.btnTouchStart(e, 'rotate')}
                onTouchMove={(e) => this.btnToucheMove(e, 'rotate')}
                onTouchEnd={this.btnTouchEnd}
                style={{ bottom: this.state.rotateY, right: this.state.rotateX }}
              >
                <Icon icon='table-rotate' className='icon' />
              </div>
            }
          </div>
          {/* 页脚 */}
          {this.props.tipsFooter && isMobile() ? <div style={{ background: '#fff', padding: '6px 12px 0' }}>
            {/* <TipsContanier
              data={store.data}
              cx={cx}
              props={this.props}
              tipsContent={this.props.tipsFooter}
              pageUniqueMark={this.props.name + 'footer'}
            ></TipsContanier> */}
            {
              render('alert', this.props.tipsFooter, { cx, pageUniqueMark: this.props.name + 'footer', data: store.data })
            }
          </div> : null}
          {footer}
          {this.state.columnsTogglerShow && <Drawer placement='bottom' mask className={`columns-toggler-drawer ${tools.isIOS && !Shell.hasShell() ? 'ios-device' : ''}`}
            getContainer={this.props.env.getModalContainer} width='100vw' height='90vh' title={'设置列'}
            visible={this.state.columnsTogglerShow} onClose={() => this.setState({ columnsTogglerShow: false })}>
            <ColumnToggler
              {...this.props}
              parentDom={this.props.env.getModalContainer}
              defaultIsOpened={true}
              isActived={store.hasColumnHidden()}
              key="columns-toggable"
              draggable={true}
              updateColumnSettingList={(list: Temp[]) => this.setState({ columnSettingTemps: list })}
              columns={store.columnsData}
              getRawColumns={() => store.rawColumnsData}
              onColumnToggle={this.handleColumnToggle}
              indexColShow={this.state.indexColShow}
              setIndexCol={this.setIndexCol}
              closeDrawer={() => this.setState({ columnsTogglerShow: false })}
              // originColumns={this.props.originColumns}
              columnSettingTemps={this.state.columnSettingTemps}
            />
          </Drawer>}
          {/* {this.state.showAiTool && isMobile() && this.renderAiTool()} */}
        </div >
      </Provider>

    );
  }
}

@Renderer({
  name: 'table',
  storeType: TableStore.name,
  test: (_path, schema) => schema?.type === 'table' || schema?.type === 'cross',
  shouldSyncSuperStore: (_, props) => props.type === 'cross' ? false : undefined
})
export class TableRenderer extends Table { }

export { TableCell };
