import React, { createRef } from 'react';
import { FormItem, FormControlProps, FormBaseControl } from './Item';
import Button from '../../components/Button';
import {
  createObject,
  getTree,
  getVariable,
  isMobile,
  isVisible,
  setVariable,
  spliceTree
} from '../../utils/helper';
import { RendererData, Action, Payload, ApiObject } from '../../types';
import { isEffectiveApi } from '../../utils/api';
import { dataMapping } from '../../utils/tpl-builtin';
import findIndex from 'lodash/findIndex';
import { SimpleMap } from '../../utils/SimpleMap';
import { Icon } from '../../components/icons';
import { TableSchema } from '../Table';
import { SchemaApi } from '../../Schema';
import find from 'lodash/find';
import { IScopedContext, ScopedContext } from '../../Scoped';
import { ActionSchema } from '../Action';
import { Schema, SchemaNode } from '../../types';
import { CRUDStore, ICRUDStore } from '../../store/crud';
import Popconfirm from 'antd/lib/Popconfirm'
import cloneDeep from 'lodash/cloneDeep';
import { List } from 'immutable';
import debounce from 'lodash/debounce';
import { message, Drawer, Button as AntBtn, Modal, List as AntList } from 'antd';
import throttle from 'lodash/throttle';
import { findDOMNode } from 'react-dom';
import { exchangeType } from '../../utils/utils';
import ActionPopup from '../../components/Lion/ActionPopup';
import ActionSheet from '../../components/Lion/ActionSheet';
import { tools } from '../../utils/shell/tools';
import { Shell } from '../../utils/shell';
import VirtualList from 'rc-virtual-list';
import { EventEnum, EventSub } from '../../utils/sub';
export interface TableControlSchema
  extends FormBaseControl,
  Omit<TableSchema, 'type'> {
  type: 'input-table-field';

  /**
   * 可新增
   */
  addable?: boolean;

  /**
   * 可复制新增
   */
  copyable?: boolean;

  /**
   * 复制按钮文字
   */
  copyBtnLabel?: string;

  /**
   * 复制按钮图标
   */
  copyBtnIcon?: string;

  /**
   * 是否显示复制按钮
   */
  copyAddBtn?: boolean;

  /**
   * 是否可以拖拽排序
   */
  draggable?: boolean;

  /**
   * 新增 API
   */
  addApi?: SchemaApi;

  /**
   * 新增按钮文字
   */
  addBtnLabel?: string;

  /**
   * 新增按钮图标
   */
  addBtnIcon?: string;

  /**
   * 显示新增按钮
   */
  showAddBtn?: boolean;

  /**
   * 可否删除
   */
  removable?: boolean;

  /**
   * 删除的 API
   */
  deleteApi?: SchemaApi;

  /**
   * 可否编辑
   */
  editable?: boolean;

  /**
   * 更新按钮名称
   */
  editBtnLabel?: string;

  /**
   * 更新按钮图标
   */
  editBtnIcon?: string;

  /**
   * 确认按钮文字
   */
  confirmBtnLabel?: string;

  /**
   * 确认按钮图标
   */
  confirmBtnIcon?: string;

  /**
   * 取消按钮文字
   */
  cancelBtnLabel?: string;

  /**
   * 取消按钮图标
   */
  cancelBtnIcon?: string;

  /**
   * 删除按钮文字
   */
  deleteBtnLabel?: string;

  /**
   * 删除按钮图标
   */
  deleteBtnIcon?: string;

  /**
   * 更新 API
   */
  updateApi?: SchemaApi;

  /**
   * 初始值，新增的时候
   */
  scaffold?: any;

  /**
   * 删除确认文字
   */
  deleteConfirmText?: string;

  /**
   * 值字段
   */
  valueField?: string;

  /**
   * 是否为确认的编辑模式。
   */
  needConfirm?: boolean;

  /**
   * 是否可以访问父级数据，正常 combo 已经关联到数组成员，是不能访问父级数据的。
   */
  canAccessSuperData?: boolean;

  /**
   * 是否显示序号
   */
  showIndex?: boolean;

  /**
   * 分页个数，默认不分页
   */
  perPage?: number;

  // Jay
  /**
   * 批量操作
   */
  bulkActions?: Array<ActionSchema>;

  /**
   * 单条操作
   */
  itemActions?: Array<ActionSchema>;
  tableLayout: 'vertical' | 'horizontal';
  /**
   * 新增时获取默认值
   */
  addNeedApi?: SchemaApi;
  /**
   * 实时保存时刷新数据，异步获取初始化数据接口
   */
  reloadApi?: SchemaApi,
  /**
   * 单元格边框
   */
  setBorder?: boolean,
}

export interface TableProps
  extends FormControlProps,
  Omit<
    TableControlSchema,
    'type' | 'className' | 'descriptionClassName' | 'inputClassName'
  > {
  // Jay
  store: ICRUDStore;
  onInputTableChange: (value: any, name: string) => void;
  name: string;
  isProperty?: boolean;//是否在属性表里
  span?: number;//在属性表中占几栏
  isDetail?: boolean;//是否详情页
}

export interface TableState {
  items: Array<any>;
  raw?: any;
  columns: Array<any>;
  editIndex: number;
  isCreateMode?: boolean;
  page?: number;
  // Jay
  operationType: 'add' | 'edit' | 'delete' | undefined; // 增删改
  insertItems: Array<any>;
  updateItems: Array<any>;
  deleteItems: Array<any>;
  hasClickedToolbar?: boolean; // 是否点击过工具栏
  editStatus: boolean;//是否正在编辑行
  newAddRow: number;
  [key: string]: any;
  addVisible: boolean;
  addData: any;//新增的数据
  actionShow: boolean;
  addLoading: boolean;
  containerHeight: number;//虚拟滚动容器高度
  itemHeight: number;//虚拟滚动行高
}

const dateTypeList = ['input-date', 'input-datetime', 'input-time', 'input-month', 'input-quarter', 'input-year'];

export default class FormTable extends React.Component<TableProps, TableState> {
  static defaultProps = {
    placeholder: '暂无数据',
    scaffold: {},
    addBtnIcon: 'plus',
    copyBtnIcon: 'copy',
    editBtnIcon: 'pencil',
    deleteBtnIcon: 'minus',
    confirmBtnIcon: 'check',
    cancelBtnIcon: 'close',
    valueField: '',
    tableLayout: 'horizontal',
    span: 3,
    showIndex: false
  };

  static propsList: Array<string> = [
    'onChange',
    'name',
    'columns',
    'label',
    'scaffold',
    'showAddBtn',
    'addable',
    'removable',
    'copyable',
    'editable',
    'addApi',
    'updateApi',
    'deleteApi',
    'needConfirm',
    'canAccessSuperData',
    'formStore',
    'span'
  ];

  entries: SimpleMap<any, number>;
  entityId: number = 1;
  subForms: any = {};
  rowPrinstine: Array<any> = [];
  editting: any = {};
  constructor(props: TableProps) {
    super(props);
    // Jay
    // 为原有的行数据添加isOrigin标识，代表是原有的
    const items = Array.isArray(props.value) ? cloneDeep(props.value) : []
    // 这是针对于非异步请求的数据
    items.forEach((item: any) => item.isOrigin = true)
    console.log(this.props)
    EventSub.emit(EventEnum.ClearSelectCache, this?.props?.name)
    this.state = {
      columns: this.buildColumns(props),
      editIndex: -1,
      // items: Array.isArray(props.value) ? props.value.concat() : [],
      items,
      operationType: undefined,
      insertItems: [],
      updateItems: [],
      deleteItems: [],
      hasClickedToolbar: false,
      editStatus: false,
      newAddRow: -1,
      addVisible: false,
      addData: {},
      actionShow: false,
      addLoading: false,
      containerHeight: 500,
      itemHeight: 200
    };

    this.entries = new SimpleMap();
    this.buildItemProps = this.buildItemProps.bind(this);
    this.handleSaveTableOrder = this.handleSaveTableOrder.bind(this);
    this.handleTableSave = this.handleTableSave.bind(this)
    this.getEntryId = this.getEntryId.bind(this);
    this.subFormRef = this.subFormRef.bind(this);
    this.handlePageChange = this.handlePageChange.bind(this);
    this.emitValue = this.emitValue.bind(this);
    // Jay
    this.startEdit = this.startEdit.bind(this);
    this.handleInputChange = this.handleInputChange.bind(this)
    this.handleStoreChange = debounce(this.handleStoreChange.bind(this), 10)
    this.throttleAddItem = throttle(this.throttleAddItem.bind(this), 1000, { leading: true, trailing: false });
    this.tableThrottleAdd = throttle(this.tableThrottleAdd.bind(this), 1000, { leading: true, trailing: false });
    this.handleSaveOrder = throttle(this.handleSaveOrder.bind(this), 1000, { leading: true, trailing: false });
    this.handleComfirm = throttle(this.handleComfirm.bind(this), 1000, { leading: true, trailing: false });
    this.renderHeaderToolbar = this.renderHeaderToolbar.bind(this)
    this.renderFooterToolbar = this.renderFooterToolbar.bind(this)
    this.editing = false
    // Aug
    this.handleTableBatchEdit = this.handleTableBatchEdit.bind(this);
    this.handleAction = this.handleAction.bind(this)
    this.startRowEdit = this.startRowEdit.bind(this)
    this.removeItem = this.removeItem.bind(this)
  }

  // Jay
  // DidMount时没有增删改，对form组件的store.inputTableData[name]赋值为空对象
  componentDidMount() {
    this.emitValue()
  }
  //是否点击的是工具栏的新增
  isSelfAdd = false;
  originTableData = []
  componentDidUpdate(prevProps: TableProps, prevState: TableState) {
    const props = this.props;
    let toUpdate: any = null;
    if (props.columns !== prevProps.columns) {
      toUpdate = {
        ...toUpdate,
        columns: this.buildColumns(props)
      };
    }
    if (props.value !== prevProps.value) {
      // Jay
      // 为原有的行数据添加isOrigin标识，代表是原有的
      // ...this.state.items是为了保留之前的数据
      // 因为有这种情况出现，通过form的target传过来的数据会把之前value覆盖掉（例如form.target + picker回显），导致input-table之前的值丢失
      // new Set去重sdk会出错
      // 通过autoFill填充的数据直接覆盖之前的数据
      // 通过顶部工具栏添加的数据也是需要保留之前的数据
      // props.updateType === 'reload' 当数据来源为reload，直接覆盖之前的数据
      let temp: any[] = Array.isArray(props.value) ? this.props.autoFillObj[this.props.name] || props.updateType === 'reload' || props.isDetail ? props.value : [...(!this.editing ? this.state.items : this.items), ...props.value.slice(this.state.items.length)] : []
      this.editing && (this.editing = false)
      //处理点击工具栏按钮的新增数据
      if ((props.value?.length - prevProps.value?.length === 1) && props.tableLayout === 'vertical' && this.isSelfAdd) {
        temp = temp.map((item, index) => {
          if (index === temp.length - 1) {
            return {
              ...item,
              isAdd: true
            }
          }
          return item
        })
        this.isSelfAdd = false
      }
      // 这行是针对于异步请求获取的value
      // !this.state.hasClickedToolbar是指不是通过点击工具栏（picker）等动作获取数据
      // autoFillObj：wrapControl传过来的props，Options.tsx syncAutoFill方法: 只有点击具有autoFill的表单项，才会保存autoFill对象到store.autoFillObj
      const isOrigin = (prevProps.value?.length === 0 || !prevProps.value) && !this.state.hasClickedToolbar && !this.props.autoFillObj[this.props.name]

      const items = List(temp)
      items.forEach((item: any) => {
        if (isOrigin) {
          item.isOrigin = true
        }
      })

      toUpdate = {
        ...toUpdate,
        items: items.toJSON().filter(iobj => !!iobj),
        editIndex: -1,
        raw: undefined
      };
      // Jay 重置store.autoFillObj，若不重置，则this.props.autoFillObj[this.props.name]就有可能一直为true，就无法判断是否是autoFill回传的数据
      if (this.props.autoFillObj[this.props.name]) {
        this.props.store?.setAutoFillObj({})
      }
      // Aug 重置store.updateType
      if (props.updateType === 'reload') {
        props.store?.setUpdateType()
      }
    }
    toUpdate && this.setState(toUpdate, this.handleInputChange);
    // if ((props.value?.length - prevProps.value?.length === 1) && props.tableLayout === 'vertical' && this.isSelfAdd) {
    //   this.handleTableSave({}, props.value[props.value.length - 1], String(props.value.length - 1), true)
    //   this.isSelfAdd = false
    // }
    if (!this.originTableData.length && props.store.data?.[props.name]?.length > 0) {
      this.originTableData = props.store.data?.[props.name]
    }
    if (this.tiledItemRef.current && this.state.items.length > 10) {
      if (this.tiledItemRef.current.clientHeight && this.state.containerHeight !== this.tiledItemRef.current.clientHeight) {
        this.setState({ containerHeight: this.tiledItemRef.current.clientHeight })
      }
      const virtualItem = this.tiledItemRef.current.querySelector('.ant-list-item');
      if (virtualItem && virtualItem.clientHeight !== this.state.itemHeight) {
        this.setState({ itemHeight: virtualItem.clientHeight })
      }
    }
  }

  componentWillUnmount() {
    this.entries.dispose();
    for (const key in this.subForms) {
      delete this.subForms[key]
    }
  }

  subFormRef(form: any, x: number, y: number) {
    this.subForms[`${x}-${y}`] = form;
  }

  startRowEdit(val: boolean = true) {
    this.setState({ editStatus: val })
  }

  async validate(): Promise<string | void> {
    const { value, minLength, maxLength, translate: __, columns } = this.props;

    // todo: 如果当前正在编辑中，表单提交了，应该先让正在编辑的东西提交然后再做验证。
    if (~this.state.editIndex) {
      return __('Table.editing');
    }

    if (minLength && (!Array.isArray(value) || value.length < minLength)) {
      return __('Combo.minLength', { minLength });
    } else if (maxLength && Array.isArray(value) && value.length > maxLength) {
      return __('Combo.maxLength', { maxLength });
    } else {
      const subForms: Array<any> = [];
      Object.keys(this.subForms).forEach(
        key => this.subForms[key] && subForms.push(this.subForms[key])
      );
      if (subForms.length) {
        const results = await Promise.all(
          subForms.map(item => item.validate())
        );

        let msg = ~results.indexOf(false) ? __('Form.validateFailed') : '';
        let uniqueColumn = '';

        if (
          !msg &&
          Array.isArray(columns) &&
          Array.isArray(value) &&
          columns.some(item => {
            if (item.unique && item.name) {
              let exists: Array<any> = [];

              return value.some((obj: any) => {
                const value = getVariable(obj, item.name);

                if (~exists.indexOf(value)) {
                  uniqueColumn = `${item.label || item.name}`;
                  return true;
                }

                exists.push(value);
                return false;
              });
            }

            return false;
          })
        ) {
          msg = __('InputTable.uniqueError', {
            label: uniqueColumn
          });
        }

        return msg;
      }
    }
  }

  getValuesByApi = async () => {
    const { reloadApi, env, data } = this.props;
    if (!reloadApi) return [];
    const payLoad = await env.fetcher(reloadApi, data);
    if (payLoad.ok) {
      return payLoad.data || []
    } else {
      message.error(payLoad.msg)
      return [];
    }
  }

  async emitValue() {
    let items = (this.state.items).filter(item => !item.__isPlaceholder);
    const { onChange } = this.props;
    if (!items || items.length == 0) {
      items = await this.getValuesByApi();
      items.length && (items.forEach(item => item.isOrigin = true));
    }
    // setTimeout(() => {
    //   onChange?.(items);
    //   // Jay
    //   this.handleInputChange(items)
    // }, 200)
    onChange?.(items);
    this.handleInputChange()
  }

  async doAction(action: Action, ctx: RendererData, ...rest: Array<any>) {
    const {
      onAction,
      valueField,
      env,
      needConfirm,
      addable,
      addApi,
      translate: __
    } = this.props;

    if (action.actionType === 'add') {
      if (addable === false) {
        return;
      }

      const items = this.state.items.concat();

      if (addApi || action.payload) {
        let toAdd = null;

        if (isEffectiveApi(addApi, ctx)) {
          const payload = await env.fetcher(addApi, ctx);
          if (payload && !payload.ok) {
            env.notify('error', payload.msg || __('fetchFailed'));
            return;
          } else if (payload && payload.ok) {
            toAdd = payload.data;
          }
        } else {
          toAdd = dataMapping(action.payload, ctx);
        }

        toAdd = Array.isArray(toAdd) ? toAdd : [toAdd];
        toAdd.forEach((toAdd: any) => {
          if (
            !valueField ||
            !find(
              items,
              item => item[valueField as string] == toAdd[valueField as string]
            )
          ) {
            // 不要重复加入
            items.push(toAdd);
          }
        });

        this.setState(
          {
            items
          },
          () => {
            this.emitValue();

            if (toAdd.length === 1 && needConfirm !== false) {
              this.startEdit(items.length - 1, true);
            }
          }
        );

        return;
      } else {
        return this.addItem(items.length - 1);
      }
    } else if (
      action.actionType === 'remove' ||
      action.actionType === 'delete'
    ) {
      if (!valueField) {
        return env.alert(__('Table.valueField'));
      } else if (!action.payload) {
        return env.alert(__('Table.playload'));
      }

      const items = this.state.items.concat();
      let toRemove: any = dataMapping(action.payload, ctx);
      toRemove = Array.isArray(toRemove) ? toRemove : [toRemove];

      toRemove.forEach((toRemove: any) => {
        const idx = findIndex(
          items,
          item => item[valueField as string] == toRemove[valueField as string]
        );
        if (~idx) {
          items.splice(idx, 1);
        }
      });

      this.setState(
        {
          items
        },
        () => this.emitValue()
      );

      // todo 如果配置删除 Api 怎么办？
      return;
    }

    return onAction && onAction(action, ctx, ...rest);
  }

  copyItem(index: number) {
    const { needConfirm } = this.props;
    const items = this.editing ? this.items : this.state.items.concat(); // Jay
    // Jay
    let item = items[index]
    if (item.isOrigin) {
      item = cloneDeep(items[index])
      delete item.isOrigin
    }

    // items.splice(index + 1, 0, items[index]);
    items.splice(index + 1, 0, item);

    index = Math.min(index + 1, items.length - 1);
    this.setState(
      {
        items
      },
      () => {
        if (needConfirm === false) {
          this.emitValue();
        } else {
          this.startEdit(index, true);
        }
      }
    );
  }

  // Jay 整理insert、update、delete数据结构
  arrangeData(temp?: any[]) {
    const { deleteItems } = this.state;
    const items = temp || this.state.items
    let data: any = {};
    const insertItems = items.filter(item => !item.isOrigin || item.isAdd)
    const updateItems = items.filter(item => item.isOrigin && item.isUpdate)
    if (insertItems.length !== 0) {
      data.insert = cloneDeep(insertItems).map((item: any) => {
        item.isAdd && delete item.isAdd
        item.isUpdate && delete item.isUpdate
        // 对主从表的ids、items、selectedItems做兼容处理，如果还有问题就去处理主从表 LionTable.tsx renderToolBars
        item.items && delete item.items
        item.selectedItems && delete item.selectedItems
        item.ids && delete item.ids
        return item
      })
    }
    if (updateItems.length !== 0) {
      data.update = cloneDeep(updateItems).map((item: any) => {
        delete item.isOrigin
        delete item.isUpdate

        item.items && delete item.items
        item.selectedItems && delete item.selectedItems
        item.ids && delete item.ids
        return item
      })
    }
    if (deleteItems.length !== 0) {
      data.delete = cloneDeep(deleteItems).map((item: any) => {
        delete item.isOrigin
        item.isUpdate && delete item.isUpdate

        item.items && delete item.items
        item.selectedItems && delete item.selectedItems
        item.ids && delete item.ids
        return item
      })
    }

    return data
  }

  //平铺模式的新增
  async throttleAddItem(index: number) {
    const { tableLayout } = this.props;
    if (isMobile()) {
      const value = await this.getValue();
      this.setState({ addVisible: true, addData: value })
    } else {
      await this.addItem(index);
      if (tableLayout === 'vertical') {
        const table = findDOMNode(this) as Element;
        const target = table.querySelector('.tiled-item:last-child');
        target?.scrollIntoView({ behavior: 'smooth', block: 'start' });
      }
    }
  }

  //表格模式新增
  tableThrottleAdd() {
    const { disabled } = this.props;
    !disabled && this.addItem(this.state.items.length)
  }

  getValue = async () => {
    const { needConfirm, columns, storeData, addNeedApi, addable, scaffold, env, store } = this.props;
    let value: any = {
      __isPlaceholder: true
    };
    if (Array.isArray(columns)) {
      columns.forEach(column => {
        if (
          (typeof column.value !== 'undefined' || typeof (column?.quickEdit as any)?.value !== 'undefined') &&
          typeof column.name === 'string'
        ) {
          setVariable(value, column.name, column.value || (column?.quickEdit as any)?.value);
        }
        // Jay
        // 新建行时每列的值为null
        else if (typeof column.name === 'string') {
          value[column.name] = null
        }
      });
    }
    //新增的时候获取不到主表的值
    if (storeData) {
      Object.keys(value).forEach(key => {
        if (storeData.hasOwnProperty(key) && storeData[key]) {
          value[key] = storeData[key]
        }
      })
    }
    value = {
      ...value,
      ...scaffold
    };

    if (addable && addNeedApi) {
      const payLoad = await env.fetcher(addNeedApi, storeData || store.data);
      if (payLoad.ok && payLoad.data) {
        value = {
          ...value,
          ...payLoad.data
        }
      }
    }
    if (needConfirm === false) {
      delete value.__isPlaceholder;
    }
    return value;
  }

  async addItem(index: number) {
    const { needConfirm } = this.props;
    const items = this.editing ? this.items : this.state.items.concat(); // Jay
    const value = await this.getValue();
    items.splice(index + 1, 0, value);
    index = Math.min(index + 1, items.length - 1);
    this.setState(
      {
        items,
        hasClickedToolbar: true,
        editStatus: true,
      },
      () => {
        this.dealAddRows();
        if (needConfirm === false) {
          // Aug 修复新增时table数据域没有更新的问题
          this.handleInputChange(items)
          this.handleStoreChange(items)
        } else {
          this.startEdit(index, true);
        }
      }
    );
  }

  //当配有addApi时，vertical模式下，需要实时保存，记录当前新增的行
  dealAddRows = () => {
    const { addApi, tableLayout } = this.props;
    const { items } = this.state;
    if (addApi && tableLayout === 'vertical') {
      this.setState({
        newAddRow: items.length - 1
      })
    }
  }

  startEdit(index: number, isCreate: boolean = false) {
    this.setState({
      editIndex: index,
      isCreateMode: isCreate,
      raw: (this.editing ? this.items : this.state.items)[index],

      columns: this.buildColumns(this.props, isCreate)
    });
  }

  async removeItem(index: number, clickDelete = false) {
    const {
      value,
      onChange,
      deleteApi,
      deleteConfirmText,
      env,
      data,
      translate: __,
      tableLayout
    } = this.props;
    // let newValue = Array.isArray(value) ? value.concat() : [];
    // Jay
    // value是父组件传过来的，如果是通过form的target传过来
    // 为什么使用this.state.items：因为有这种情况出现，通过form的target传过来的数据会把之前value覆盖掉（例如form.target + picker回显），导致input-table之前的值丢失

    let newValue = Array.isArray(this.state.items) ? this.editing ? this.items : this.state.items : []; // Jay
    const item = newValue[index];

    if (!item) {
      return;
    }

    const ctx: any = createObject(data, item);
    if (tableLayout === 'vertical') {
      if (this.state.newAddRow === -1 && isEffectiveApi(deleteApi, ctx)) {

        // 提交数据的结构改成和运行平台的增删改
        ctx.isOrigin && delete ctx.isOrigin
        ctx.isUpdate && delete ctx.isUpdate
        const result = await env.fetcher(deleteApi, ctx);

        if (result.ok) {
          env.notify('success', __('CRUD.deleted'))
        }

        if (!result.ok) {
          env.notify('error', __('deleteFailed'));
          return;
        }
      }
    } else {
      if (this.state.editIndex !== -1 && isEffectiveApi(deleteApi, ctx) || (clickDelete && isEffectiveApi(deleteApi, ctx))) {

        // 提交数据的结构改成和运行平台的增删改
        ctx.isOrigin && delete ctx.isOrigin
        ctx.isUpdate && delete ctx.isUpdate
        const result = await env.fetcher(deleteApi, ctx);

        if (result.ok) {
          env.notify('success', __('CRUD.deleted'))
        }

        if (!result.ok) {
          env.notify('error', result.msg || __('deleteFailed'));
          return;
        }
      }
    }

    this.removeEntry(item);
    newValue.splice(index, 1);
    // onChange(newValue)
    // Jay
    // 这样是让componentDidUpdate中的this.state.items始终是最新值

    // this.setState({
    //   items: newValue
    // }, () => {
    //   onChange(newValue)
    // })
    // Aug 修复删除时table数据域没有更新的问题
    this.handleInputChange(newValue)
    this.handleStoreChange(newValue)
    // Jay
    if (this.state.operationType === 'delete') {

      // 未提交表单前删除新增行不计入deleteItems
      // 若编辑过原有的行，然后再删除该行则不计入updateItems
      if (item.isOrigin) {
        const updateItems = newValue.filter(item => item.isOrigin && item.isUpdate)

        this.setState({
          updateItems,
          deleteItems: [...this.state.deleteItems, item]
        }
          // Aug 上面已经有了这里就不用了吧
          // , this.handleInputChange
        )
      } else {
        // 未提交表单前删除新增行，要把insertItems的item删除掉
        this.setState({
          insertItems: newValue.filter(item => !item.isOrigin)
        }
          // Aug 上面已经有了这里就不用了吧
          // , this.handleInputChange
        )
      }
    }
    if (this.state.newAddRow !== -1) {
      this.setState({ newAddRow: -1 })
    }
    if (this.formGroup.size) {
      this.formGroup.delete(index)
    }
  }

  buildItemProps(item: any, index: number) {
    if (this.props.needConfirm === false) {
      return {
        quickEditEnabled: true
      };
    } else if (
      !this.props.editable &&
      !this.props.addable &&
      !this.state.isCreateMode
    ) {
      return null;
    }

    const perPage = this.props.perPage;
    const page = this.state.page || 1;
    let offset = 0;
    if (typeof perPage === 'number' && perPage) {
      offset = (page - 1) * perPage;
    }

    return {
      quickEditEnabled: this.state.editIndex === index + offset
    };
  }

  removeItemQuick = async (index: number) => {
    const { data, deleteApi, env, translate: __, reloadApi } = this.props;
    const newValue = this.state.items.concat();
    const delItem = newValue[index];
    if (!delItem) return;
    const ctx: any = createObject(data, delItem);
    delete ctx.isOrigin;
    delete ctx.isUpdate;
    delete ctx.isAdd;
    const result = await env.fetcher(deleteApi!, ctx);
    if (result.ok) {
      newValue.splice(index, 1);
      const reloadData = await env.fetcher(reloadApi!, data)
      if (reloadData.ok) {
        const rows: any[] = reloadData.data?.map((item: any) => ({
          ...item,
          isOrigin: true,
          isAdd: false,
          isUpdate: false
        }));
        this.setState({ items: rows }, () => {
          this.handleInputChange(rows)
          this.handleStoreChange(rows)
        })
        env.notify('success', '操作成功')
      } else {
        const newItems = newValue.map(item => ({ ...item, isOrigin: true, isAdd: false, isUpdate: false }))
        this.setState({ items: newItems }, () => {
          this.handleInputChange(newItems)
          this.handleStoreChange(newItems)
        })
        env.notify('error', reloadData.msg)
      }
    }
    if (!result.ok) {
      env.notify('error', result.msg);
      return;
    }
  }

  handleCloseFiled = () => {
    if (this.hasEdit) {
      return Modal.confirm({
        content: '新的修改没有保存，确认要离开？',
        okText: '确定',
        okType: 'primary',
        cancelText: '取消',
        className: 'ant-modal-adaptation-style',
        zIndex: 1011,
        onOk: () => {
          this.setState({ addData: {}, addVisible: false })
        }
      })
    }
    return this.setState({ addData: {}, addVisible: false })
  }

  handleComfirm = async () => {
    const { addApi, data, env, reloadApi } = this.props;
    const { items, addData } = this.state;
    const res = await this.addForm.validate();
    if (res) {
      if (addApi) {
        this.setState({ addLoading: true });
        const ctx = createObject(data, addData);
        const payLoad = await env.fetcher(addApi, ctx);
        if (payLoad.ok) {
          if (reloadApi) {
            const reloadData = await env.fetcher(reloadApi, data);
            if (reloadData.ok) {
              const rows: any[] = reloadData.data.map((item: any) => ({
                ...item,
                isOrigin: true,
                isAdd: false,
                isUpdate: false
              }));
              this.setState({ addVisible: false, addData: {}, items: rows }, () => {
                this.handleInputChange(rows);
                this.handleStoreChange(rows);
              })
            } else {
              message.error(payLoad.msg)
            }
          } else {
            const newItems = Array.isArray(items) ? [...items, addData] : [addData];
            this.setState({ items: newItems, addVisible: false, addData: {} }, () => {
              this.handleInputChange(newItems);
              this.handleStoreChange(newItems);
            })
            message.success(payLoad.msg || '操作成功！')
          }
        } else {
          message.error(payLoad.msg)
        }
        this.setState({ addLoading: false });
      } else {
        const newData = { ...addData, isAdd: true };
        const newItems = Array.isArray(items) ? [...items, newData] : [newData];
        this.setState({ items: newItems, addVisible: false, addData: {} }, () => {
          this.handleInputChange(newItems);
          this.handleStoreChange(newItems);
        })
      }
    }
  }

  addForm: any;
  hasEdit = false;
  renderNewForm = () => {
    const { store, render, storeData } = this.props;
    const { columns, addData } = this.state;
    const ctx = createObject(storeData, addData)
    const formItems = columns.map(item => {
      let ishidden = false;
      if (item.hiddenOn) {
        ishidden = !isVisible({ hiddenOn: item.hiddenOn }, ctx)
      }
      return {
        ...item,
        ...(item.quickEdit as obj || {}),
        type: exchangeType(item.quickEdit?.type || item.type),
        staticShow: item.type === 'number' ? (item.quickEdit ? false : true) : undefined,
        isFieldTable: true,
        defaultOpen: false,
        hiddenOn: ishidden ? 'true' : (item.columnHiddenOn || item.quickEdit?.columnHiddenOn),
      }
    })
    const formSchema = {
      type: "form",
      wrapWithPanel: false,
      mode: 'horizontal',
      body: formItems
    }
    return render('', formSchema, {
      data: ctx,
      store,
      getFormInstance: (form: any) => {
        this.addForm = form
      },
      onChange: (value: any, name: any, submit?: boolean, changePristine?: boolean) => {
        this.setState({ addData: { ...addData, ...this.moneyParser(name, formItems, 'line') } });
        this.hasEdit = true;
      }
    })
  }

  editFooter = () => (<>
    <AntBtn onClick={this.handleCloseFiled} loading={this.state.addLoading}>取消</AntBtn>
    <AntBtn type='primary' onClick={this.handleComfirm} loading={this.state.addLoading}>确定</AntBtn>
  </>)

  buildColumns(props: TableProps, isCreateMode = false): Array<any> {
    let columns: Array<any> = Array.isArray(props.columns)
      ? props.columns.concat()
      : [];
    const { classPrefix: ns, translate: __, needConfirm, env, tableLayout, deleteApi, reloadApi } = this.props;
    if (tableLayout !== 'horizontal') return columns;
    let btns = [];
    props.editable && btns.push({
      children: ({
        key,
        rowIndex,
        data,
        offset
      }: {
        key: any;
        rowIndex: number;
        data: any;
        offset: number;
      }) =>
        <Button
          classPrefix={ns}
          size="sm"
          key={key}
          level="link"
          tooltipPlacement='left'
          // tooltip={isMobile() ? '' : __('Table.editRow')}
          tooltipContainer={
            env?.getTopModalContainer || undefined
          }
          onClick={() => {
            this.setState({
              operationType: 'edit',
              editStatus: true
            }, this.startEdit(rowIndex + offset)!)
          }
          }
        >
          {props.updateBtnLabel || props.editBtnLabel ? (
            <span>{props.updateBtnLabel || props.editBtnLabel}</span>
          ) : null}
          {/* 兼容之前的写法 */}
          {typeof props.updateBtnIcon !== 'undefined' ? (
            props.updateBtnIcon ? (
              <Icon icon={props.updateBtnIcon} className="icon" />
            ) : null
          ) : props.editBtnIcon ? (
            <Icon icon={props.editBtnIcon} className="icon" />
          ) : null}
        </Button>
    });
    if (props.removable) {
      btns.push({
        children: ({
          key,
          rowIndex,
          data,
          offset
        }: {
          key: any;
          rowIndex: number;
          data: any;
          offset: number;
        }) =>
          (data && data.__isPlaceholder) &&
            needConfirm !== false ? null : (
            <Popconfirm title={__('Table.deleteRow')}
              okText={__('confirm')} cancelText={__('cancel')}
              onConfirm={() => {
                this.setState({ operationType: 'delete' }, () => deleteApi && reloadApi ? this.removeItemQuick(rowIndex + offset) : this.removeItem(rowIndex + offset, true))
              }}>
              <Button
                classPrefix={ns}
                size="sm"
                key={key}
                level="link"
                // tooltip={__('Table.deleteRow')}
                tooltipContainer={
                  env?.getTopModalContainer || undefined
                }
              >
                {props.deleteBtnLabel ? (
                  <span>{props.deleteBtnLabel}</span>
                ) : null}
                {props.deleteBtnIcon ? (
                  <Icon icon={props.deleteBtnIcon} className="icon" />
                ) : null}
              </Button>
            </Popconfirm>
          )
      });
    }

    if (btns.length) {
      let operation = columns.find(item => item.type === 'operation');

      if (!operation) {
        operation = {
          type: 'operation',
          buttons: [],
          label: __('Table.operation'),
          className: 'v-middle nowrap',
          fixed: 'right',
          width: [props.removable, props.editable].filter(item => !!item).length * 30 + 'px',
          innerClassName: 'm-n'
        };
        columns.push(operation);
      }
      operation.buttons = Array.isArray(operation.buttons)
        ? operation.buttons.concat()
        : [];
      operation.buttons.unshift.apply(operation.buttons, btns);
    }

    return columns;
  }

  columnToQuickEdit(column: any) {
    const quickEdit: any = {
      type: 'input-text'
    };

    if (
      (column.type &&
        /^input\-|(?:select|picker|checkbox|checkboxes|editor|transfer|radios)$/i.test(
          column.type
        )) ||
      ~['textarea', 'combo', 'condition-builder', 'group'].indexOf(column.type)
    ) {
      return {
        ...column,
        label: ''
      };
    }

    return quickEdit;
  }

  // Jay
  // input-table增删改的回调
  handleInputChange(items?: any[]) {
    // onInputTableChange是form(index.tsx)的handleInputTableChange方法
    // onInputTableChange是form(index.tsx)->wrapControl->wrapControl包裹的组件(如input-table)的props
    const { onInputTableChange, name } = this.props
    onInputTableChange?.(this.arrangeData(items), name)
  }
  handleStoreChange(items: any[]) {
    this.props.onChange(items);
    this.props.formStore?.changeValue(this.props.name, items)
  }


  async handleTableSave(
    rows: Array<object> | object,
    diff: Array<object> | object,
    rowIndexes: Array<string> | string,
    isNew = false,
    hasBlur = false,
    needReload = false
  ) {
    const { perPage } = this.props;
    if (~this.state.editIndex && !Array.isArray(rows)) {
      const items = this.editing ? this.items : this.state.items.concat(); // Jay
      const origin = items[this.state.editIndex];

      if (!origin) {
        return;
      }

      const value: any = {
        ...rows
      };
      if (value.isOrigin && !value.isAdd) {
        value.isUpdate = true
      }
      this.entries.set(value, this.entries.get(origin) || this.entityId++);
      this.entries.delete(origin);
      items.splice(this.state.editIndex, 1, value);

      this.setState({
        items
      }, () => {
        this.handleInputChange(items)
      });
      return;
    }

    const page = this.state.page;
    let items = this.editing ? this.items : this.state.items.concat(); // Jay

    if (Array.isArray(rows)) {
      (rowIndexes as Array<string>).forEach((rowIndex, index) => {
        const indexes = rowIndex.split('.').map(item => parseInt(item, 10));

        if (page && page > 1 && typeof perPage === 'number') {
          indexes[0] += (page - 1) * perPage;
        }
        const origin = getTree(items, indexes);

        const data = {
          ...origin,
          ...(diff as Array<object>)[index]
        };

        items = spliceTree(items, indexes, 1, data);
      });
    } else {
      const indexes = (rowIndexes as string)
        .split('.')
        .map(item => parseInt(item, 10));

      if (page && page > 1 && typeof perPage === 'number') {
        indexes[0] += (page - 1) * perPage;
      }

      const origin = getTree(items, indexes);
      const data = {
        ...origin,
        ...diff
      };

      // Jay
      // needConfirm为false的情况下，编辑原有行添加isUpdate的标识
      if (data.isOrigin && !data.isAdd) {
        data.isUpdate = true
      }
      if (isNew) {//这里是为了处理添加headerToolbar的新增最终没有传到后端的问题
        if (data.isOrigin || data.isUpdate) {

        } else {
          data.isUpdate = false
          data.isAdd = true
        }
      }

      items = spliceTree(items, indexes, 1, data);
      this.entries.set(data, this.entries.get(origin) || this.entityId++);
      // this.entries.delete(origin); // 反正最后都会清理的，先不删了吧。
    }
    // Jay 优化编辑时的性能
    this.props.setEdited?.()
    this.editing = true
    this.items = items
    this.updateForm(items, diff, rowIndexes, isNew, hasBlur, needReload)
  }
  updateForm = async (items: any[], diff: Array<object> | object, rowIndexes: string[] | string, isNew = false, hasBlur = false, needReload = false) => {
    const { tableLayout, updateApi, env, data, reloadApi } = this.props;
    this.setState({ items }, () => {
      this.handleInputChange(items)
      this.handleStoreChange(items)
    })
    if (!isNew && updateApi && this.state.newAddRow === -1 && tableLayout === 'vertical' && !hasBlur) {
      const updateIndex = Number(rowIndexes as string)
      const res = await this.formGroup.get(updateIndex)?.validate();
      if (res) {
        const origin = getTree(items, updateIndex);
        const newData = {
          ...origin,
          ...diff
        };
        const ctx = createObject(data, newData)
        const payLoad = await env.fetcher(updateApi, ctx);
        if (!payLoad.ok) {
          message.error(payLoad.msg);
        }
        if (reloadApi) {
          const reloadData = await env.fetcher(reloadApi, data);
          if (reloadData.ok) {
            const rows: any[] = reloadData.data.map((item: any) => ({
              ...item,
              isOrigin: true,
              isAdd: false,
              isUpdate: false
            }));
            this.setState({ items: rows }, () => {
              this.handleInputChange(rows)
              this.handleStoreChange(rows)
            })
            this.items = rows
          } else {
            message.error(reloadData.msg);
          }
        }
      }
    }
  }

  handleBlur = async (index: number) => {
    const { tableLayout, updateApi, data, env, reloadApi } = this.props;
    if (updateApi && this.state.newAddRow === -1 && tableLayout === 'vertical') {
      const res = await this.formGroup.get(index)?.validate();
      if (res) {
        const origin = getTree(this.state.items, index);
        const ctx = createObject(data, origin);
        const payLoad = await env.fetcher(updateApi, ctx);
        if (!payLoad.ok) {
          message.error(payLoad.msg);
        }
        if (reloadApi) {
          const reloadData = await env.fetcher(reloadApi, data);
          if (reloadData.ok) {
            const rows: any[] = reloadData.data.map((item: any) => ({
              ...item,
              isOrigin: true,
              isAdd: false,
              isUpdate: false
            }));
            this.setState({ items: rows }, () => {
              this.handleInputChange(rows)
              this.handleStoreChange(rows)
            })
          } else {
            message.error(reloadData.msg)
          }
        }
      }
    }
  }

  // Jay
  items: any[];
  editing: boolean;
  //Aug input-table批量修改
  handleTableBatchEdit(values: any) {
    this.state.items.forEach((item, index) => {
      this.handleTableSave(item, values, index + '')
    })

  }

  handleSaveTableOrder(moved: Array<object>, rows: Array<object>) {
    const { onChange } = this.props;

    onChange(rows.map((item: object) => ({ ...item })));
  }

  handlePageChange(page: number) {
    this.setState({ page });
  }

  removeEntry(entry: any) {
    if (this.entries.has(entry)) {
      this.entries.delete(entry);
    }
  }

  getEntryId(entry: any) {
    if (!this.entries.has(entry)) {
      this.entries.set(entry, this.entityId++);
    }

    return String(this.entries.get(entry));
  }

  renderFilterToggler() {
    const { store, classnames: cx, translate: __ } = this.props;

    if (!store.filterTogggable) {
      return null;
    }

    return (
      <button
        onClick={() => store.setFilterVisible(!store.filterVisible)}
        className={cx('Button Button--sm Button--default', {
          'is-active': store.filterVisible
        })}
      >
        <Icon icon="filter" className="icon m-r-xs" />
        {__('CRUD.filter')}
      </button>
    );
  }

  renderExportCSV() {
    const {
      store,
      classPrefix: ns,
      classnames: cx,
      translate: __,
      loadDataOnce,
      api
    } = this.props;

    return (
      <Button
        classPrefix={ns}
        onClick={() =>
          store.exportAsCSV({
            loadDataOnce,
            api
          })
        }
        size="sm"
      >
        {__('CRUD.exportCSV')}
      </Button>
    );
  }

  renderToolbar(
    toolbar?: SchemaNode,
    index: number = 0,
    childProps: any = {},
    toolbarRenderer?: (toolbar: SchemaNode, index: number) => React.ReactNode
  ) {
    if (!toolbar) {
      return null;
    }

    const type = (toolbar as Schema).type || toolbar;

    if (type === 'bulkActions' || type === 'bulk-actions') {
    } else if (type === 'filter-toggler') {
      return this.renderFilterToggler();
    } else if (type === 'export-csv') {
      return this.renderExportCSV();
    }
    else if (Array.isArray(toolbar)) {
      const children: Array<any> = toolbar
        .map((toolbar, index) => ({
          dom: this.renderToolbar(toolbar, index, childProps, toolbarRenderer),
          toolbar
        }))
        .filter(item => item.dom);
      const len = children.length;
      const cx = this.props.classnames;
      if (len) {
        return (
          <div className={cx('Crud-toolbar')} key={index}>
            {children.map(({ toolbar, dom: child }, index) => {
              const type = (toolbar as Schema).type || toolbar;
              let align =
                toolbar.align || (type === 'pagination' ? 'right' : 'left');
              return (
                <div
                  key={index}
                  className={cx(
                    'Crud-toolbar-item',
                    align ? `Crud-toolbar-item--${align}` : '',
                    toolbar.className
                  )}
                  onClick={() => {
                    if (!this.state.hasClickedToolbar) {
                      this.setState({ hasClickedToolbar: true })
                    }
                  }}
                >
                  {child}
                </div>
              );
            })}
          </div>
        );
      }
      return null;
    }

    const result = toolbarRenderer
      ? toolbarRenderer(toolbar, index)
      : undefined;

    if (result !== void 0) {
      return result;
    }

    const { render, store } = this.props;
    const $$editable = childProps.$$editable;

    // Jay
    // actionType为dialog和ajax时 不传 handleAction，依然可以生效
    const toolbarProps: any = {}
    if ((toolbar as any).actionType === "export") {
      toolbarProps.onAction = this.handleAction
    }

    return render(`toolbar/${index}`, toolbar, {
      // 包两层，主要是为了处理以下 case
      // 里面放了个 form，form 提交过来的时候不希望把 items 这些发送过来。
      // 因为会把数据呈现在地址栏上。
      data: createObject(
        createObject(store.filterData, {
          items: childProps.items,
          selectedItems: childProps.selectedItems,
          unSelectedItems: childProps.unSelectedItems
        }),
        {}
      ),
      ...toolbarProps,
      $$editable
    });
  }

  renderHeaderToolbar(
    childProps: any,
    toolbarRenderer?: (toolbar: SchemaNode, index: number) => React.ReactNode
  ) {
    let { toolbar, toolbarInline, headerToolbar } = this.props;

    if (toolbar) {
      if (Array.isArray(headerToolbar)) {
        headerToolbar = toolbarInline
          ? headerToolbar.concat(toolbar)
          : [headerToolbar, toolbar];
      } else if (headerToolbar) {
        headerToolbar = [headerToolbar, toolbar];
      } else {
        headerToolbar = toolbar;
      }
    }

    return this.renderToolbar(
      headerToolbar || [],
      0,
      childProps,
      toolbarRenderer
    );
  }

  renderFooterToolbar(
    childProps: any,
    toolbarRenderer?: (toolbar: SchemaNode, index: number) => React.ReactNode
  ) {
    let { toolbar, toolbarInline, footerToolbar } = this.props;

    if (toolbar) {
      if (Array.isArray(footerToolbar)) {
        footerToolbar = toolbarInline
          ? footerToolbar.concat(toolbar)
          : [footerToolbar, toolbar];
      } else if (footerToolbar) {
        footerToolbar = [footerToolbar, toolbar];
      } else {
        footerToolbar = toolbar;
      }
    }

    return this.renderToolbar(footerToolbar, 0, childProps, toolbarRenderer);
  }
  // actionType为dialog和ajax时 不传 handleAction，依然可以生效
  // handleAction 暂时仅支持export
  handleAction(
    e: React.UIEvent<any> | undefined,
    action: Action,
    ctx: any,
    throwErrors: boolean = false,
    delegate?: IScopedContext
  ): any {
    const {
      onAction,
      store,
      messages,
      pickerMode,
      env,
      pageField,
      stopAutoRefreshWhenModalIsOpen
    } = this.props;

    if (action.actionType === 'dialog') {
    } else if (
      pickerMode &&
      (action.actionType === 'confirm' || action.actionType === 'submit')
    ) {
      store.setCurrentAction(action);
      return Promise.resolve({
        items: store.selectedItems.concat()
      });
    }
    else if (action.actionType === 'export') {
      store.setCurrentAction(action);
      let exportData: any = {};
      let _ids: string = '';
      let _selectedItems: any[] = this.props?.store?.selectedItems?.toJSON();

      _selectedItems.map((_select: any) => {
        _ids += _select.hasOwnProperty(this.props.primaryField) ? _select[this.props.primaryField] + ',' : ''
      });

      exportData = { ...exportData, ...this.props?.store?.query, ...this.props?.storeData };
      exportData.selectedItems = _selectedItems;
      exportData.primaryField = this.props.primaryField;
      exportData.ids = (action?.exportType === 2 ? ctx[this.props.primaryField as any] : _ids.slice(0, _ids.length - 1))
      store.openLionExport(exportData, env, true)
      return false
    }
    else {
      onAction(e, action, ctx, throwErrors, delegate || this.context);
    }
  }

  handleDiscard = () => {
    this.setState({ operationType: 'delete' }, () => this.removeItem(this.state.items.length - 1))
  }

  reload = async (subPath?: string, query?: any, ctx?: any) => {
    const { reloadApi, env, data } = this.props;
    if (reloadApi) {
      const reloadData = await env.fetcher(reloadApi, data);
      if (reloadData.ok) {
        const rows: any[] = reloadData.data.map((item: any) => ({
          ...item,
          isOrigin: true,
          isAdd: false,
          isUpdate: false
        }));
        this.setState({ items: rows }, () => {
          this.handleInputChange(rows)
          this.handleStoreChange(rows)
        })
      } else {
        message.error(reloadData.msg)
      }
    }
  }

  handleSaveOrder = async (targetIndex: number) => {
    const { addApi, env, data, reloadApi } = this.props;
    const { items } = this.state;
    const res = await this.formGroup.get(targetIndex)?.validate();
    if (res) {
      if (addApi) {
        this.setState({ addLoading: true })
        const ctx = createObject(data, items[items.length - 1])
        const payLoad = await env.fetcher(addApi, ctx);
        if (payLoad.ok) {
          this.setState({ newAddRow: -1 })
          if (reloadApi) {
            const reloadData = await env.fetcher(reloadApi, data);
            if (reloadData.ok) {
              const rows: any[] = reloadData.data.map((item: any) => ({
                ...item,
                isOrigin: true,
                isAdd: false,
                isUpdate: false
              }));
              this.setState({ items: rows }, () => {
                this.handleInputChange(rows)
                this.handleStoreChange(rows)
              })
            } else {
              message.error(payLoad.msg)
            }
          } else {
            message.success(payLoad.msg || '操作成功！')
          }
        } else {
          message.error(payLoad.msg)
        }
        this.setState({ addLoading: false })
      }
    }
  }

  genExtra = (delIndex: number) => {
    const { classPrefix: ns, translate: __, env, needConfirm, prinstine, removable, disabled, addable, showAddBtn,
      deleteApi, reloadApi } = this.props;
    const { newAddRow } = this.state;
    const initData = prinstine || this.originTableData;
    if (newAddRow === delIndex) return null;
    return (<div>
      {addable && showAddBtn !== false && <Button
        disabled={disabled || newAddRow !== -1}
        size="sm"
        level='link'
        onClick={() => this.throttleAddItem(this.state.items.length)}
      >
        {this.props.addBtnLabel ? (
          <span>{this.props.addBtnLabel}</span>
        ) : __('Combo.add')}
      </Button>}
      {
        needConfirm !== false ? null : <Popconfirm title={__('Table.deleteRow')}
          okText={__('confirm')} cancelText={__('cancel')}
          onConfirm={(e) => {
            e?.stopPropagation()
            this.setState({ operationType: 'delete' }, () => deleteApi && reloadApi ? this.removeItemQuick(delIndex) : this.removeItem(delIndex))
          }}>
          <Button
            classPrefix={ns}
            size="sm"
            level="link"
            disabled={!removable && delIndex <= initData.length - 1 || newAddRow !== -1}
            tooltipContainer={
              env?.getTopModalContainer || undefined
            }
          >
            {this.props.deleteBtnLabel ? (
              <span>{this.props.deleteBtnLabel}</span>
            ) : __('delete')}
          </Button>
        </Popconfirm>
      }
    </div>)
  }
  genExtraisModle = (delIndex: number, addBtnLabel?: boolean) => {
    const { classPrefix: ns, translate: __, env, needConfirm, prinstine, removable, disabled, addable, showAddBtn, classnames: cx, deleteApi, reloadApi } = this.props;
    const { newAddRow, items } = this.state;
    const initData = prinstine || this.originTableData;
    if (newAddRow === delIndex) return null;
    return (addBtnLabel ? <>
      {addable && !(disabled || newAddRow !== -1) && (items?.length - 1) == delIndex && showAddBtn !== false && <>
        <Button
          type="button"
          className={cx('AddBtnLabel')}
          onClick={() => this.throttleAddItem(this.state.items.length)}
        >
          {this.props.addBtnLabel ? (
            <span>{this.props.addBtnLabel}</span>
          ) : __('Combo.add')}
        </Button>
        {this.renderMobileActions()}</>}
    </> : <>
      {
        needConfirm !== false ? null : <ActionPopup
          props={this.props}
          disabled={!removable && delIndex <= initData.length - 1 || newAddRow !== -1}
          onConfirm={(e) => {
            e?.stopPropagation()
            this.setState({ operationType: 'delete' }, () => deleteApi && reloadApi ? this.removeItemQuick(delIndex) : this.removeItem(delIndex))
          }} />
      }
    </>)
  }
  // 将数字还原
  parser = (value: string, prefix: string, suffix: string, kilobitSeparator: string) => {
    if (value && typeof value == 'string') {
      prefix && (value = value.replace(prefix, ''));
      suffix && (value = value.replace(suffix, ''));
      kilobitSeparator && (value = value.replace(/,/g, ''));
    }
    return value;
  };

  //金额类型格式转化，去掉千分位，前后缀等
  moneyParser = (obj: obj, formItems: any[], mode: 'property' | 'line') => {
    const newObj = { ...obj }
    Object.entries(obj).forEach(([k, v]) => {
      const formItem = mode === 'property' ? formItems.find(item => item.content?.type === 'input-number' && item.content?.name === k)
        : formItems.find(item => item.type === 'input-number' && item.name === k)
      if (formItem) {
        if (typeof v == 'string' || v == undefined) {
          newObj[k] = v
        } else {
          const { kilobitSeparator, prefix, suffix, precision } = formItem;
          let fixedValue = null;
          if (precision == 0 || precision) {
            fixedValue = v.toFixed(Number(precision))
          }
          newObj[k] = Number(this.parser((precision == 0 || precision) ? fixedValue : v, prefix, suffix, kilobitSeparator))
        }
      }
    })
    return newObj;
  }

  //标题
  renderLabel = () => {
    const { label, labelName } = this.props;
    let fieldTitle = (label || labelName || '');
    const labelTag = this.props.required || fieldTitle.includes('</font>') ? <span style={{ color: 'red', fontSize: isMobile() ? '12px' : '14px' }}>*</span> : null
    const labelContent = fieldTitle.replace("<font color='red'>*</font>", '') || ''
    return { labelTag, labelContent }
  }

  formGroup = new Map<number, any>();
  changeValues = '';
  tiledItemRef = createRef<HTMLDivElement>();
  renderPropertyMode = () => {
    const { span, render, translate: __, store, storeData, editable, classnames: cx, reloadApi, primaryField } = this.props;
    const { columns, items, newAddRow } = this.state;
    const formItems = columns.map(item => {
      let ishidden = false;
      if (item.hiddenOn) {
        ishidden = !isVisible({ hiddenOn: item.hiddenOn }, storeData)
      }
      return {
        content: {
          ...item,
          ...(item.quickEdit as obj || {}),
          type: (item.quickEdit as obj)?.type || (!item.quickEdit && item.type === 'mapping' ? 'static-mapping' : '') || (!item.quickEdit && item.type?.includes('date') ? 'static-' + item.type : '') || (item as obj).type || 'static',
          label: null,
          inheritWidth: true,
          isFieldTable: true,
          updateImmediately: true,
          labelRemark: null,
          remark: null,
          defaultOpen: false,
          classNameExpr: undefined
        },
        ...item,
        inheritWidth: true,
        hiddenOn: ishidden ? 'true' : (item.columnHiddenOn || item.quickEdit?.columnHiddenOn),
        span: item.colSpan,
        label: item.required ? (item.label?.includes('*') ? item.label : "<font color='red'>*</font>" + item.label) : item.label,
        classNameExpr: item.classNameExpr || this.props.rowClassNameExpr
      }
    })
    const formSchema = {
      type: "form",
      wrapWithPanel: false,
      mode: 'horizontal',
      store,
      name: 'property-form',
      body: {
        type: "property",
        items: formItems,
        column: span
      }
    }
    const { labelTag, labelContent } = this.renderLabel()
    const ItemRender = (item: obj, index: number) => {
      return <div className='tiled-item' key={index + ''}>
        <div className={`tiled-item-title ${newAddRow === index ? 'isAdding' : ''}`} style={{ display: 'flex' }}>
          <div className='tiled-item-title-left'>
            <span style={{ color: 'var(--saas-font-size-black)' }}>
              {labelTag}
              {labelContent + (index === 0 && items.length === 1 ? '' : (index + 1))}
            </span>
            {newAddRow === index &&
              <span style={{ marginLeft: 32 }}>
                {__('Table.moved', { moved: 1 })}
                <button
                  type="button"
                  className={cx('Button Button--xs Button--success m-l-sm')}
                  onClick={() => this.handleSaveOrder(index)}
                >
                  <Icon icon="check" className="icon m-r-xs" loading={this.state.addLoading} />
                  {__('Form.submit')}
                </button>
                <button
                  type="button"
                  className={cx('Button Button--xs Button--danger m-l-sm')}
                  onClick={this.handleDiscard}
                >
                  <Icon icon="close" className="icon m-r-xs" />
                  {__('Table.discard')}
                </button>
              </span>}
          </div>
          {this.genExtra(index)}
        </div>
        {render('', newAddRow !== -1 ? (newAddRow === index ? formSchema : {
          ...formSchema,
          body: {
            ...formSchema.body,
            items: formSchema.body.items.map(formIns => ({
              ...formIns,
              content: {
                ...formIns.content,
                disabled: true
              }
            }))
          }
        }) : formSchema, {
          data: { ...storeData, ...item },
          store: store,
          onBlur: this.props.updateApi ? () => this.handleBlur(index) : undefined,
          getFormInstance: (form: any) => {
            this.formGroup.set(index, form)
          },
          onChange: (value: any, name: any, submit?: boolean, changePristine?: boolean) => {
            // 线上失焦的时候也会触发onChange， 所以要处理一下,这次的值等于上次的值就不触发了
            if (this.props.addApi) {
              //是否是输入框，带有失焦事件，在失焦时进行实时提交，替他则值改变时提交
              const hasBlur = formItems.find(fItem => fItem.content?.type.includes('input') && !dateTypeList.includes(fItem.type) && fItem.name === Object.keys(name)?.[0]);
              if (JSON.stringify(name) !== this.changeValues) {
                this.handleTableSave(storeData?.[this.props.name]?.[index] || store.data?.[this.props.name]?.[index], this.moneyParser(name, formItems, 'property'), String(index), false, !!hasBlur, !!reloadApi)
              }
              this.changeValues = JSON.stringify(name)
            } else {
              this.handleTableSave(storeData?.[this.props.name]?.[index] || store.data?.[this.props.name]?.[index], this.moneyParser(name, formItems, 'property'), String(index))
            }
          }
        }
        )}
      </div>
    }
    return <div className={`property-tiled ${items.length > 25 ? 'virtual-tiled-table' : ''}`} ref={this.tiledItemRef}>
      {items.length ? (items.length > 25 ? <AntList className='table-vertical-virtual-list'>
        <VirtualList
          data={items}
          height={this.state.containerHeight}
          itemHeight={this.state.itemHeight}
          itemKey={primaryField || ''}
        >
          {(item: obj, index: number) => {
            return <AntList.Item key={(item[primaryField ?? ''] || '') + index}>
              <div className='virtual-list-row-item'>
                {ItemRender(item, index)}
              </div>
            </AntList.Item>
          }}
        </VirtualList>
      </AntList> : items.map((item, index) => ItemRender(item, index))) : (
        <div className='empty-container'>
          <div className='empty-title'>
            {labelTag}
            {labelContent}
          </div>
          {!editable && <div className='empty-content'>{__('placeholder.noData')}</div>}
        </div>
      )}
    </div>
  }

  renderLineMode = () => {
    const { render, label, labelName, translate: __, store, storeData, editable, reloadApi, primaryField } = this.props;
    const { columns, items, newAddRow } = this.state;
    const formItems = columns.map(item => {
      let ishidden = false;
      if (item.hiddenOn) {
        ishidden = !isVisible({ hiddenOn: item.hiddenOn }, storeData)
      }
      return {
        ...item,
        ...(item.quickEdit as obj || {}),
        type: exchangeType(item.quickEdit?.type || item.type),
        staticShow: item.type === 'number' ? (item.quickEdit ? false : true) : undefined,
        isFieldTable: true,
        updateImmediately: true,
        defaultOpen: false,
        hiddenOn: ishidden ? 'true' : (item.columnHiddenOn || item.quickEdit?.columnHiddenOn),
        remark: null
      }
    })
    const formSchema = {
      type: "form",
      wrapWithPanel: false,
      mode: 'horizontal',
      store,
      body: formItems
    }
    const { labelContent, labelTag } = this.renderLabel();
    let fieldTitle = (label || labelName || '');
    fieldTitle = fieldTitle.includes('</font>') ? fieldTitle.replace("<font color='red'>*</font>", '') : fieldTitle;
    const ItemRender = (item: obj, index: number) => {
      return <div className='tiled-item' key={index + ''}>
        <div className='tiled-item-titlename' style={{ height: "32px", fontSize: '14px', lineHeight: '32px' }}>
          <span className='tiled-item-titlename-fieldTitle'>
            {labelTag}
            {labelContent + (index === 0 && items.length === 1 ? '' : (index + 1))}
          </span>
          {this.genExtraisModle(index)}
        </div>
        {render('', newAddRow !== -1 ? (newAddRow === index ? formSchema : {
          ...formSchema,
          body: {
            ...formSchema,
            body: formSchema.body.map(formIns => ({
              ...formIns,
              disabled: true
            }))
          }
        }) : formSchema, {
          data: { ...storeData, ...item },
          store: store,
          onBlur: this.props.updateApi ? () => this.handleBlur(index) : undefined,
          getFormInstance: (form: any) => {
            this.formGroup.set(index, form)
          },
          onChange: (value: any, name: any, submit?: boolean, changePristine?: boolean) => {
            if (this.props.updateApi) {
              //是否是输入框，带有失焦事件，在失焦时进行实时提交，替他则值改变时提交
              const hasBlur = formItems.find(fItem => fItem.type.includes('input') && !dateTypeList.includes(fItem.type) && fItem.name === Object.keys(name)?.[0]);
              if (JSON.stringify(name) !== this.changeValues) {
                this.handleTableSave(storeData?.[this.props.name]?.[index] || store.data?.[this.props.name]?.[index], this.moneyParser(name, formItems, 'line'), String(index), false, !!hasBlur, !!reloadApi)
              }
              this.changeValues = JSON.stringify(name)
            } else {
              this.handleTableSave(storeData?.[this.props.name]?.[index] || store.data?.[this.props.name]?.[index], this.moneyParser(name, formItems, 'line'), String(index))
            }
          }
        })}
        <div className={`tiled-item-title ${newAddRow === index ? 'isAdding' : ''}`}>
          <div className='tiled-item-title-left'>
            {newAddRow === index ?
              <>
                <Button
                  level="danger"
                  type="button"
                  onClick={this.handleDiscard}
                >
                  {__('Table.discard')}
                </Button>
                <Button
                  level="info"
                  type="button"
                  onClick={() => this.handleSaveOrder(index)}
                >
                  {__('Form.submit')}
                </Button>
              </> :
              <>{this.genExtraisModle(index, true)}</>
            }
          </div>
        </div>
      </div>
    }
    return <div className={`line-tiled ${items.length > 10 ? 'virtual-tiled-table' : ''}`} ref={this.tiledItemRef}>
      {items.length ? (items.length > 10 ? <AntList className='table-vertical-virtual-list'>
        <VirtualList
          data={items}
          height={this.state.containerHeight}
          itemHeight={this.state.itemHeight}
          itemKey={primaryField || ''}
        >
          {(item: obj, index: number) => {
            return <AntList.Item key={(item[primaryField ?? ''] || '') + index}>
              <div className='virtual-list-row-item'>{ItemRender(item, index)}</div>
            </AntList.Item>
          }}
        </VirtualList>
      </AntList> : items.map((item, index) => ItemRender(item, index))) : (
        <div className='empty-container'>
          <div className='empty-title'>
            {labelTag}
            {labelContent}
          </div>
          {!editable && <div className='empty-content'>{__('placeholder.noData')}</div>}
        </div>
      )}
    </div >
  }

  renderMobileActions = () => {
    const { headerToolbar, render, classnames: cx, addable, showAddBtn, tableLayout } = this.props;
    if (!headerToolbar) return null;
    const num = addable && showAddBtn !== false ? 1 : 2;
    return headerToolbar?.length > num ?
      <ActionSheet
        isOpened={this.state.actionShow}
        container={this.props.env.getModalContainer}
        style={{ height: 'auto' }}
        round
        onHide={(e: any) => {
          e.stopPropagation();
          this.setState({ actionShow: false });
        }}
        popupContent={
          <div style={{ width: '100%', textAlign: 'center', paddingTop: 5 }}>
            {headerToolbar.map((btn: any, index: number) => <div
              id="amis-action-sheet-btn-wrapper"
              className="action-sheet-btn-wrapper"
              onClick={e => {
                e.stopPropagation();
                this.setState({ actionShow: false });
              }}
            >
              {render('', btn, {
                setFieldAdd: tableLayout === 'vertical' ? () => { this.isSelfAdd = true } : null,
                key: index + ''
              })}
            </div>)}
          </div>
        }
      >
        <Button
          type="button"
          className={cx('AddBtnLabel', 'toolbars-btn')}
          onClick={() => this.setState({ actionShow: true })}>
          <span>
            ...
          </span>
        </Button>
      </ActionSheet> : headerToolbar?.map((btn: Action, index: number) => render('', btn, {
        setFieldAdd: tableLayout === 'vertical' ? () => { this.isSelfAdd = true } : null,
        key: index + ''
      }))
  }

  renderMobileDrawer = () => {
    const { labelName, label, translate: __ } = this.props;
    const { editIndex, items, addVisible } = this.state;
    let fieldTitle = label || labelName || '';
    fieldTitle = fieldTitle.includes('</font>') ? fieldTitle.replace("<font color='red'>*</font>", '') : fieldTitle;
    return <Drawer
      placement="left"
      contentWrapperStyle={{ bottom: '0', top: 'unset' }}
      height="auto"
      mask
      className={`columns-toggler-drawer field-table-drawer ${tools.isIOS && !Shell.hasShell() ? 'ios-device' : ''}`}
      title={__('Combo.add') + '：' + (fieldTitle ? fieldTitle + '-' : '') + (editIndex === -1 ? items.length + 1 : editIndex + 1)}
      zIndex={1011}
      destroyOnClose
      getContainer={this.props.env.getModalContainer}
      onClose={this.handleCloseFiled}
      footer={this.editFooter()}
      visible={addVisible}>
      <div className='form-content-container'>
        {this.renderNewForm()}
      </div>
    </Drawer>
  }

  render() {
    const {
      className,
      showAddBtn,
      disabled,
      render,
      placeholder,
      draggable,
      addable,
      columnsTogglable,
      combineNum,
      combineFromIndex,
      translate: __,
      canAccessSuperData,
      expandConfig,
      affixRow,
      prefixRow,
      formInited,
      showIndex,
      perPage,
      classnames: cx,
      label,
      labelName,
      removable,
      tableLayout,
      headerToolbar,
      isProperty,
      updateApi,
      addApi,
      classPrefix,
      rowClassNameExpr
    } = this.props;

    const { editStatus, addVisible } = this.state;
    if (formInited === false) {
      return null;
    }

    let items = this.state.items;
    let showPager = false;
    const page = this.state.page || 1;
    let offset = 0;
    let lastPage = 1;
    if (typeof perPage === 'number' && perPage && items.length > perPage) {
      lastPage = Math.ceil(items.length / perPage);
      items = items.slice((page - 1) * perPage, page * perPage);
      showPager = true;
      offset = (page - 1) * perPage;
    }
    let fieldTitle = (label || labelName || '');
    return (
      <div className={cx('InputTable', className, {
        'Property-tiled': isProperty && tableLayout === 'vertical',
        'Line-tiled': !isProperty && tableLayout === 'vertical',
        'is-mobile': isMobile(),
        'horizontal': tableLayout === 'horizontal'
      })}>
        {
          isProperty && tableLayout === 'vertical' && this.renderPropertyMode()
        }
        {
          !isProperty && tableLayout === 'vertical' && this.renderLineMode()
        }
        {tableLayout === 'horizontal' && <div>
          {isProperty && !!fieldTitle && <div className='fieldTitle'>
            {fieldTitle.includes('</font>') ?
              <>
                <span style={{ color: 'red', fontSize: '14px' }}>*</span>
                {fieldTitle.replace("<font color='red'>*</font>", '')}
              </> : fieldTitle}
          </div>}
          {
            render(
              'body',
              {
                type: 'field-table-horizontal',
                placeholder: __(placeholder),
                columns: this.state.columns,
                affixHeader: false,
                prefixRow,
                affixRow,
                autoFillHeight: this.props.autoFillHeight ?? true,
                setBorder: this.props.setBorder,
                rowClassNameExpr
              },
              {
                tableName: this.props.name,
                value: undefined,
                saveImmediately: true,
                disabled,
                draggable: draggable && !~this.state.editIndex,
                items: items,
                getEntryId: this.getEntryId,
                onSave: this.handleTableSave,
                onSaveOrder: this.handleSaveTableOrder,
                onBatchEdit: this.handleTableBatchEdit,
                buildItemProps: this.buildItemProps,
                quickEditFormRef: this.subFormRef,
                columnsTogglable: columnsTogglable,
                combineNum: combineNum,
                combineFromIndex: combineFromIndex,
                expandConfig,
                canAccessSuperData,
                reUseRow: false,
                editIndex: this.state.editIndex,
                setEditIndex: (index: number) => this.setState({ editIndex: index }),
                offset,
                // Jay
                itemBadge: this.props.itemBadge,
                headerToolbarRender: this.renderHeaderToolbar,
                footerToolbarRender: this.renderFooterToolbar,
                tableLayout: this.props.tableLayout,
                editStatus,
                startRowEdit: this.startRowEdit,
                removeItem: (index: number) => { this.setState({ operationType: 'delete' }, () => this.removeItem(index)) },
                markoperationType: (operationType: 'add' | 'edit' | 'delete' | undefined) => this.setState({ operationType }),
                labelName: labelName || label,
                removable,
                originPrinstine: this.props.prinstine ?? this.originTableData,
                tableType: 'input-table',//来自input-table不需要分页，把加载更多按钮去掉
                updateApi,
                addApi,
                moneyParser: this.moneyParser,
                reloadApi: this.props.reloadApi,
                classPrefix,
                showIndex
              }
            )
          }
        </div>}
        {(addable && showAddBtn !== false) || headerToolbar || showPager ? (
          <div className={cx('InputTable-toolbar', { 'property-empty-toolbar': isProperty && !items.length && tableLayout === 'vertical' })}>
            {addable && showAddBtn !== false && tableLayout === 'vertical' && items?.length === 0 && (
              isMobile() ? <Button
                type="button"
                disabled={disabled}
                className={cx('AddBtnLabel')}
                onClick={() => this.throttleAddItem(this.state.items.length)}
              >
                {this.props.addBtnLabel ? (
                  <span>{this.props.addBtnLabel}</span>
                ) : __('Combo.add')}
              </Button> :
                <Button
                  disabled={disabled}
                  size="sm"
                  level='enhance'
                  onClick={() => this.throttleAddItem(this.state.items.length)}
                >
                  {this.props.addBtnLabel ? (
                    <span>{this.props.addBtnLabel}</span>
                  ) : __('Combo.add')}
                </Button>
            )}
            {addable && showAddBtn !== false && tableLayout === 'horizontal' ?
              (isMobile() ? <Button
                type="button"
                disabled={disabled}
                className={cx('AddBtnLabel')}
                onClick={this.tableThrottleAdd}
              >
                {this.props.addBtnLabel ? (
                  <span>{this.props.addBtnLabel}</span>
                ) : __('Combo.add')}
              </Button> : <div className={`add-row ${disabled ? 'disabled' : ''}`} onClick={this.tableThrottleAdd}>
                <i className="fa fa-plus" style={{ fontSize: 18, color: "#ccc" }} aria-hidden="true"></i>
              </div>)
              : null
            }
            {
              tableLayout !== 'horizontal' && !isMobile() && headerToolbar?.map((btn: Action, index: number) => render('', btn, {
                setFieldAdd: () => { this.isSelfAdd = true },
                key: index + ''
              }))
            }
            {
              isMobile() ? (items.length > 0 && tableLayout === 'vertical' ? null : this.renderMobileActions()) : null
            }
          </div>
        ) : null
        }
        {
          addVisible && this.renderMobileDrawer()
        }
      </div>
    );
  }
}

@FormItem({
  type: 'input-table-field',
  // Jay
  storeType: CRUDStore.name
})
export class FormTableRenderer extends FormTable {
  static contextType = ScopedContext;

  constructor(props: TableProps, context: IScopedContext) {
    super(props);

    const scoped = context;
    scoped.registerComponent(this);
  }

  componentWillUnmount() {
    const scoped = this.context as IScopedContext;
    scoped.unRegisterComponent(this);
  }
}
