<!-- @format -->

<template>
  <div class="ns-table" :class="{ 'ns-table-no-search': !(formConfig?.schemas.length > 0) }">
    <!-- tabletitle -->
    <!-- <div
      class="ns-table-title"
      @click="
        () => {
          showBack ? navigateBack() : '';
        }
      "
      v-if="tableTitle">
      <ns-icon v-if="showBack" class="backIcon" name="left" />{{ tableTitle }}
    </div> -->
    <div class="ns-table-container">
      <!-- todo drag -->

      <div class="ns-part-tree" v-if="!isEmpty(treeConfig)">
        <ns-tree-api v-bind="getTreeBindValue" @select="treeSelect" />
      </div>
      <div class="ns-part-table">
        <a-spin :spinning="tableState.loading">
          <div class="ns-table-search" v-if="!isEmpty(formConfig)">
            <ns-form
              ref="formElRef"
              class="ns-table-form"
              :showAction="true"
              v-bind="formConfig"
              :expand="expand"
              :showExpand="showExpand"
              :expandAll="expandAll"
              :title="formConfig.title"
              :showExpandAll="showExpandAll"
              :model="formModel"
              @finish="formFinish" />
          </div>
          <div class="ns-table-main">
            <ns-table-header
              v-if="!isEmpty(headerActions) || tableTitle"
              :headerActions="headerActions"
              :searchData="formModel"
              :tableTitle="tableTitle"
              :data="tableState.selectedRows">
              <template #header="data">
                <slot name="header" v-bind="data || {}"></slot>
              </template>
            </ns-table-header>
            <ns-basic-table ref="tableElRef" v-bind="getTableBindValues" :dataSource="tableData">
              <template #emptyText>
                <template v-if="tableState.loadError">
                  <div class="ns-table-content">
                    <div class="fetch-error">
                      <p>{{ tableState.loadErrorMessage }}</p>
                      <ns-button type="primary" ghost @click="reload">重新加载</ns-button></div
                    ></div
                  >
                </template>
                <template v-else-if="tableState.loading"
                  ><div class="ns-table-content"></div
                ></template>
                <template v-else>
                  <div class="ns-table-content"> <a-empty /> </div>
                </template>
              </template>

              <template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
                <slot :name="item" v-bind="data || {}"></slot>
                <template v-if="item === 'bodyCell'">
                  <template v-if="data?.column?.textEllipsis">
                    <span
                      class="tool-tips"
                      :style="{ width: `${data.column.textWidth || data.column.width}px` }">
                      <ns-tooltip
                        placement="top"
                        v-if="
                          data.column.customRender
                            ? data.column.customRender(data)
                            : data.record[data.column.dataIndex]
                        ">
                        <template #title>
                          <span>{{
                            data.column.customRender
                              ? data.column.customRender(data)
                              : data.record[data.column.dataIndex] || '-'
                          }}</span>
                        </template>
                        <span class="text-ellipsis">{{
                          data.column.customRender
                            ? data.column.customRender(data)
                            : data.record[data.column.dataIndex] || '-'
                        }}</span>
                      </ns-tooltip>
                      <span class="text-ellipsis" v-else> - </span>
                    </span>
                  </template>
                  <template v-if="data.column.dataIndex === 'tableAction'">
                    <ns-table-action
                      :data="data.record"
                      :searchData="formModel"
                      :columnActions="getColumnActions" />
                  </template>
                  <template v-if="data.column.edit">
                    <ns-table-cell
                      :value="data.text"
                      :record="data.record"
                      :column="data.column"
                      :index="data.index" />
                  </template>
                </template>
                <template v-if="item === 'footer'">
                  <ns-table-footer :footerActions="footerActions" :data="ediRowData" />
                </template>
              </template>

              <template #bodyCell="data" v-if="!Object.keys($slots).includes('bodyCell')">
                <template v-if="data.column.textEllipsis">
                  <span
                    class="tool-tips"
                    :style="{ width: `${data.column.textWidth || data.column.width}px` }">
                    <ns-tooltip
                      placement="top"
                      v-if="
                        data.column.customRender
                          ? data.column.customRender(data)
                          : data.record[data.column.dataIndex]
                      ">
                      <template #title>
                        <span>{{
                          data.column.customRender
                            ? data.column.customRender(data)
                            : data.record[data.column.dataIndex] || '-'
                        }}</span>
                      </template>
                      <span class="text-ellipsis">{{
                        data.column.customRender
                          ? data.column.customRender(data)
                          : data.record[data.column.dataIndex] || '-'
                      }}</span>
                    </ns-tooltip>
                    <span class="text-ellipsis" v-else> - </span>
                  </span>
                </template>
                <template v-if="data.column.dataIndex === 'tableAction'">
                  <ns-table-action
                    :data="data.record"
                    :searchData="formModel"
                    :columnActions="getColumnActions" />
                </template>
                <template v-if="data.column.edit">
                  <ns-table-cell
                    :value="data.text"
                    :record="data.record"
                    :column="data.column"
                    :index="data.index" />
                </template>
              </template>

              <template
                #footer
                v-if="!Object.keys($slots).includes('footer') && !isEmpty(footerActions)">
                <ns-table-footer :footerActions="footerActions" :data="ediRowData" />
              </template>
            </ns-basic-table>
          </div>
        </a-spin>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
  import { computed, defineComponent, provide, reactive, ref, unref, watch } from 'vue';
  import { RequestParams } from '/nerv-lib/component/table/table';
  import {
    cloneDeep,
    debounce,
    throttle,
    get,
    isArray,
    isEmpty,
    isEqual,
    isFunction,
    isObject,
    isString,
    isUndefined,
  } from 'lodash-es';
  import NsTableAction from './table-action.vue';
  import NsTableHeader from './table-header.vue';
  import NsTableFooter from './table-footer.vue';
  import NsTableCell from './edit/table-cell.vue';
  import { useParams } from '/nerv-lib/use/use-params';
  import { transformColumns } from '/nerv-lib/component/table/table-columns';
  import NsBasicTable from '/nerv-lib/component/table/basic-table.vue';
  import { tableProps } from '/nerv-lib/component/table/props';
  import { AxiosRequestConfig } from 'axios';
  import { useApi } from '/nerv-lib/use/use-api';
  import { useRoute } from 'vue-router';
  import { useTableEdit } from '/nerv-lib/component/table/use-table-edit';
  import { Form } from 'ant-design-vue';
  import { stringUtil } from '/nerv-lib/util/string-util';
  import { useTableRefresh } from '/nerv-lib/component/table/use-table-refresh';
  import { tableConfig } from '/nerv-base/config/table.config';
  import { useTableSession } from '/nerv-lib/component/table/use-table-session';
  import { useTableColumn } from '/nerv-lib/component/table/use-table-column';
  import { useNavigate } from '/nerv-lib/use/use-navigate';
  export default defineComponent({
    name: 'NsTable',
    components: {
      NsBasicTable,
      NsTableAction,
      NsTableHeader,
      NsTableFooter,
      NsTableCell,
    },
    props: tableProps,

    emits: ['cellChange', 'update:value', 'dataSourceChange', 'update:dataSource'],
    setup(props, { attrs, emit }) {
      const tableElRef = ref(null);
      const formElRef = ref(null);
      const dataRef = ref([]);
      const treeParamsRef = ref({});
      const formParamsRef = ref({});
      const orderRef = ref({});
      const formModel = reactive<Recordable>({});
      const tableData = ref<Recordable[]>([]);
      const tableState = reactive({
        selectedRowKeys: [],
        selectedRows: [],
        loading: false,
        loadError: false,
        loadErrorMessage: '',
        loadinterval: 0,
      });
      const clearCheck = () => {
        tableState.selectedRowKeys = [];
        tableState.selectedRows = [];
      };
      const route = useRoute();
      const { getColumnActionWidth } = useTableColumn({
        columnActions: Object.assign({}, tableConfig.columnActions, props.columnActions),
      });
      const { navigateBack } = useNavigate();
      const defaultPageRef = ref(1 - props.pageFieldOffset);

      const { setTableSession } = useTableSession(
        formModel,
        formParamsRef,
        defaultPageRef,
        treeParamsRef,
      );

      const { delayFetch } = useTableRefresh({ props, reload });

      watch(
        [() => props.value, () => props.dataSource],
        () => {
          tableData.value = props.value || props.dataSource || [];
        },
        {
          immediate: true,
        },
      );

      const formItemContext = Form.useInjectFormItemContext();

      const getColumnActions = computed(() => {
        const { actions } = props.columnActions as any;
        const _tableConfig = cloneDeep(tableConfig);
        if (actions) {
          _tableConfig.columnActions.width = getColumnActionWidth(actions);
        }
        return Object.assign(_tableConfig.columnActions, props.columnActions);
      });

      const getColumns = computed(() => {
        const columns = transformColumns(cloneDeep(props.columns || []));
        const { title, width, dataIndex, fixed } = getColumnActions.value;
        if (props.columnActions) {
          columns.push({
            title,
            width,
            dataIndex,
            fixed,
          });
        }
        return columns;
      });

      watch(
        () => tableData.value,
        (val) => {
          // console.log(val, tableData.value);

          if (isEqual(val, tableData.value)) return;

          const data = cloneDeep(tableData.value);
          if (props.editable) {
            Object.keys(data).forEach((key) => {
              delete data[key][props.rowKey];
            });
          }
          // emit('update:value', data);
          emit('dataSourceChange', data);
          formItemContext.onFieldChange();
        },
        {
          deep: true,
        },
      );

      const tableEdit = useTableEdit({
        dataSource: tableData,
        columns: getColumns,
        rowKey: props.rowKey,
        editable: ref(props.editable),
      });
      provide('tableEdit', tableEdit);

      const { getParams } = useParams();

      const rowSelection = computed(() => {
        const { rowSelection } = props;
        if (rowSelection === false || rowSelection === null) {
          return null;
        }
        return Object.assign(
          {
            fixed: true,
            columnWidth: 48,
            preserveSelectedRowKeys: true, // 跨页选中默认不清除选中key
            selectedRowKeys: tableState.selectedRowKeys,
            onChange: (selectedRowKeys: never[], selectedRows: never[]) => {
              tableState.selectedRowKeys = selectedRowKeys;
              tableState.selectedRows = selectedRows;
            },
          },
          isFunction(rowSelection) ? rowSelection(tableState) : rowSelection,
        );
      });

      const customizeRenderEmpty = computed(() => {
        return () => '暂无数据';
      });

      const formFinish = debounce((data: object) => {
        formParamsRef.value = data;
        fetch({
          page: 1,
        });
      }, 300);

      function setLoading(loading: boolean) {
        tableState.loading = loading;
      }

      const tableChangeEvent = (pagination: Props, filters: [], sorter: any) => {
        console.log('params', pagination, filters, sorter);
        if (sorter?.field) {
          if (sorter.order) {
            orderRef.value = {
              [props.paramsOrderField]: stringUtil.toLine(
                `${sorter.field} ${sorter.order.replace('end', '')}`,
              ),
            };
          } else {
            orderRef.value = { [props.paramsOrderField]: '' }; //覆盖默认params
          }
          fetch({
            page: pagination?.current || getPagination.value?.current || 1,
            pageSize: pagination?.pageSize,
          });
        } else if (pagination?.current) {
          fetch({
            page: pagination?.current,
            pageSize: pagination.pageSize,
          });
        }
      };

      // pagination
      const getPagination: Recordable | Boolean = computed(() => {
        const { pagination } = props;
        const { getPageParams } = attrs;

        if (pagination) {
          const current = get(dataRef.value, props.pageField);

          function getTotal() {
            let total = 0;
            if (isFunction(getPageParams)) {
              total = getPageParams(dataRef)['total'];
            } else {
              total = get(dataRef.value, props.totalField);
            }
            return total;
          }

          return {
            showQuickJumper: true,
            showLessItems: true,
            showSizeChanger: true,
            showTotal: (total: number, range: Array<number>) => {
              return `显示第${range[0]}到${range[1]}条记录 ,共 ${
                get(dataRef.value, props.totalField) || total
              } 条记录`;
            },
            ...(pagination as Props),
            total: getTotal(),
            current: (current >= 0 ? current : 1) + props.pageFieldOffset, // 后端1 开始
            pageSize: get(dataRef.value, props.sizeField),
          };
        }
        return false;
      });

      const getTableBindValues = computed(() => {
        const { params, dynamicParams } = props;
        return {
          ...attrs,
          ...props,
          rowSelection: rowSelection.value,
          params: dynamicParams
            ? getParams({ ...route.params, ...route.query }, dynamicParams, params)
            : params || {},
          columns: getColumns.value,
          pagination: getPagination.value,
          onChange: tableChangeEvent,
        };
      });

      watch(
        () => getTableBindValues.value.api,
        () => {
          // console.log(getTableBindValues.value.api);
          // fetch(); //路由切换导致api切换 导致发送请求
        },
        {
          immediate: true,
        },
      );

      // watch(
      //     () =>  getTableBindValues.value.params,
      //     () => {
      //       fetch();
      //     },
      //     {
      //       immediate: true,
      //     },
      // );

      /**
       * 请求函数
       * @param requestParams 主要是传入页面，部分变量闭包处理
       * @param clearDelay 是否需要清除刷新时间（页面操作之后，自动刷新重新计算）
       */

      function fetch(requestParams: RequestParams = {}, clearDelay = true) {
        clearDelay && delayFetch();
        if (tableState.loadinterval) {
          clearTimeout(tableState.loadinterval);
        }
        if (tableState.loading) {
          tableState.loadinterval = setTimeout(() => {
            fetch(requestParams, clearDelay);
          }, 500);
          return;
        }
        const { api, pagination } = props;
        const { page, pageSize } = requestParams;
        if (api) {
          let pageParams: Recordable = {};

          if (pagination !== false) {
            pageParams = {
              [props.paramsPageField]: page ? page - props.pageFieldOffset : defaultPageRef.value, // 后端0 开始
              [props.paramsPageSizeField]:
                pageSize || getPagination.value?.pageSize || props.defaultPageSize,
            };
          } else {
            pageParams = {
              [props.paramsPageField]: defaultPageRef.value, // 后端0 开始
              [props.paramsPageSizeField]:
                pageSize || getPagination.value?.pageSize || props.defaultPageSize,
            };
          }
          const httpParams = {
            ...getTableBindValues.value.params,
            ...pageParams,
            ...formParamsRef.value,
            ...treeParamsRef.value,
            ...orderRef.value,
          };
          if (!checkrequiredParams(httpParams)) {
            console.log('check fail');
            return;
          }

          setTableSession(pageParams[props.paramsPageField]);

          clearDelay && setLoading(true);

          const requestConfig: AxiosRequestConfig = { method: 'get' };
          const { httpRequest } = useApi();
          httpRequest({
            api,
            params: httpParams,
            pathParams: { ...route.params, ...route.query },
            requestConfig,
          })
            .then((res: any) => {
              tableState.loadError = false;
              tableState.loadErrorMessage = '';
              dataRef.value = res;
              tableData.value = get(unref(dataRef), props.listField);
              //saas项目配置
              if (attrs['getPageParams']) {
                const getPageParams = attrs['getPageParams'];
                let realPage = getPageParams(dataRef, pageParams.page)['page'];
                if (realPage !== pageParams.page) {
                  fetch({ page: realPage });
                }
              }
              emit('update:dataSource', tableData.value);
              clearDelay && setLoading(false);
            })
            .catch((error: any) => {
              const { response, code, message } = error || {};
              let errMessage = response?.data?.msg;
              const err: string = error?.toString?.() ?? '';
              if (code === 'ECONNABORTED' && message.indexOf('timeout') !== -1) {
                errMessage = '接口请求超时,请刷新页面重试!';
              }
              if (err?.includes('Network Error')) {
                errMessage = '网络异常，请检查您的网络连接是否正常!';
              }

              // console.log(getPagination.value);
              tableState.loadError = true;
              tableState.loadErrorMessage = errMessage;
              clearDelay && setLoading(false);
            });
        }
      }

      /**
       * 检测requiredParams是否全部获得数据
       * @param params
       */
      function checkrequiredParams(params: Recordable) {
        const { params: dynamicParams } = getTableBindValues.value as any;
        let { requiredParams } = props;
        if (requiredParams) {
          if (requiredParams === true) requiredParams = dynamicParams as any;
          if (isFunction(requiredParams)) {
            console.error(
              'Property dynamicParams of props cannot set to Function when using requiredParams',
            );
            return false;
          } else {
            if (isString(requiredParams)) {
              if (isUndefined(params[requiredParams])) return false;
            } else if (isArray(requiredParams)) {
              for (let i = 0, l = requiredParams.length; i < l; i++) {
                if (isUndefined(params[requiredParams[i]])) return false;
              }
            } else if (isObject(requiredParams)) {
              const keys = Object.keys(requiredParams);
              for (let i = 0, l = keys.length; i < l; i++) {
                if (isUndefined(params[keys[i]])) return false;
              }
            }
          }
          return true;
        }
        return true;
      }

      function treeSelect(
        selectedKeys: never[],
        e: {
          selected: boolean;
          selectedNodes: { props: { dataRef: any } }[];
          node: any;
          event: any;
        },
      ) {
        console.log(selectedKeys, e);
        const { dataRef } = e.node;
        treeParamsRef.value = getParams(dataRef, getTreeBindValue.value.dynamicParams);
        fetch({
          page: 1,
        });
      }

      const getTreeData = computed(() => {
        return props?.treeConfig?.treeData || [];
      });

      const getTreeWidth = computed(() => {
        return props?.treeConfig?.width || '300px';
      });

      const getTreeBindValue = computed(() => ({
        ...props?.treeConfig,
      }));
      //todo 异步加载|| 树形接口

      function reload(clearDelay = true) {
        const pagination = unref(getPagination);
        fetch(
          {
            page: pagination === false ? 1 : pagination.current,
          },
          clearDelay,
        );
      }

      provide('reload', reload); //提供刷新功能
      provide('clearCheck', clearCheck); //提供清空选中功能

      return {
        navigateBack,
        reload,
        clearCheck,
        formElRef,
        tableElRef,
        getColumnActions,
        getTableBindValues,
        formModel,
        tableState,
        isEmpty,
        formFinish,
        tableChangeEvent,
        treeSelect,
        getTreeBindValue,
        getTreeWidth,
        getTreeData,
        customizeRenderEmpty,
        tableData,
        treeParamsRef,
        formParamsRef,
      };
    },
  });
</script>

<style lang="less" scoped>
  .backIcon {
    cursor: pointer;
    margin-right: 6px;
  }
  .ns-table-title {
    text-align: left;
    height: 46px;
    line-height: 46px;
    //font-size: 16px;
    font-size: 18px;
    font-weight: bold;
    user-select: text;
    padding-left: 16px;
    background: #fff;
    width: calc(100% + 32px);
    margin-left: -16px;
    cursor: pointer;
  }

  .ns-table-container {
    display: flex;
    .ns-part-tree {
      width: 300px;
      // padding: 16px;
      overflow-y: auto;
    }
    .ns-part-table {
      flex: 1;
      min-width: 0;
    }
  }
  :deep(.ant-spin-container) {
    display: flex;
    flex-direction: column;
    .ns-table-main {
      height: 100%;
    }
  }
  .ns-table-content {
    // background: #e5ebf0;
    margin: 16px;
  }

  :deep(.ant-spin-nested-loading > div > .ant-spin) {
    max-height: none;
  }
  .ns-table-search {
    padding-top: 16px;
  }
  :deep(.ant-form-item) {
    margin-bottom: 16px;
  }

  .ns-table {
    position: relative;
    // min-height: 400px;
    // background: #e5ebf0;
    .ant-spin-nested-loading {
      height: 100%;
      // min-height: 400px;
    }

    .ns-table-content {
      min-height: 300px;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .fetch-error {
      p {
        line-height: 40px;
        padding: 0;
        margin: 0;
        font-size: 16px;
      }

      .ant-btn {
        width: 88px;
      }
    }
  }

  .text-ellipsis {
    display: inline-block;
    vertical-align: top;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
  .tool-tips {
    display: inline-block;
    vertical-align: top;
    padding: 0;
    word-wrap: break-word;
    word-break: break-word;
    width: 100%;
  }
</style>
