<template>
  <div class="comp-content comp-base-table">
    <TableSearch
      v-show="searchList.length > 0 && !hideSearch"
      ref="tableSearchRef"
      :searchItems="searchList"
      :searchLimitNum="searchLimitNum"
      :searchParams="searchParams"
      :defaultSearchParams="defaultSearchParams"
      :searchAttrs="searchAttrs"
      :hideSearchKeys="hideSearchKeys"
      @search="searchFun"
      @reset="resetSearchFun"
      @change="changeFun"
      @load="searchLoadFun"
    >
      <template v-for="name in Object.keys($slots)" :key="name" #[name]="scope">
        <slot :name="name" v-bind="scope" />
      </template>
    </TableSearch>
    <div class="options">
      <div class="item">
        <div v-if="listTitle" class="list-title">{{ listTitle }}</div>
      </div>
      <div class="item">
        <div class="btns">
          <slot name="leftOptions" />
          <template v-if="$slots['cardTemplate']">
            <el-tooltip v-if="viewType === 'list'" content="切换至卡片视图" placement="top">
              <div class="icon-btn" @click="viewTypeChangeFun('card')">
                <el-icon :size="18">
                  <List />
                </el-icon>
              </div>
            </el-tooltip>
            <el-tooltip v-else content="切换至列表视图" placement="top">
              <div class="icon-btn" @click="viewTypeChangeFun('list')">
                <el-icon :size="18">
                  <Menu />
                </el-icon>
              </div>
            </el-tooltip>
          </template>
          <el-tooltip v-if="exportApi && showExport" :content="$t('表格.导出')" placement="top">
            <div v-if="exportApi && showExport" class="icon-btn" @click="exportFun">
              <el-icon :size="18">
                <Download />
              </el-icon>
            </div>
          </el-tooltip>
          <el-tooltip v-if="showImport" :content="$t('表格.导入')" placement="top">
            <div v-if="showImport" class="icon-btn" @click="formDiaVisible = true">
              <el-icon :size="18">
                <Upload />
              </el-icon>
            </div>
          </el-tooltip>
          <el-tooltip :content="$t('表格.列表字段设置')" placement="top">
            <div class="icon-btn" @click="formDiaVisible = true">
              <el-icon :size="18">
                <Setting />
              </el-icon>
            </div>
          </el-tooltip>
        </div>
      </div>
    </div>
    <div v-if="viewType === 'list'" class="table">
      <vxe-table
        ref="vxeTableRef"
        :loading="loading"
        :data="tableData"
        v-bind="$attrs"
        :seq-config="{ startIndex: (current - 1) * size }"
        :column-config="{
          drag: true,
          resizable: true,
        }"
        :column-drag-config="{
          showIcon: false,
          trigger: 'cell',
        }"
        :height="fullHeight ? '100%' : ''"
      >
        <vxe-column
          v-if="showCheckbox"
          type="checkbox"
          width="60"
          align="left"
          header-align="left"
        ></vxe-column>
        <vxe-column v-if="showIndex" type="seq" width="80" align="left" header-align="left" />
        <vxe-column
          v-for="item in tableHeadVisible"
          :key="item.key"
          :field="item.key"
          :title="item.title"
          :min-width="item.width"
          align="left"
          header-align="left"
          :tree-node="item.treeNode"
          :sortable="false"
          :show-overflow="showOverflow"
        >
          <!-- <template #header>
            <HeadSearch :item="item" :data="headSearchParams" @search="headSearchFun" />
          </template> -->
          <template #default="{ row, column }">
            <span class="td-text">
              <span v-if="item.tableIcon === 'fileIcon' && isString(row[item.key])">
                <img :src="fileUploadAllocationIconFun(row[item.key])" class="img" />
              </span>
              <span v-if="item.tableIcon && item.tableIcon !== 'fileIcon'" class="icon">
                <el-icon :size="16">
                  <component :is="item.tableIcon" />
                </el-icon>
              </span>
              <slot v-if="$slots[item.key]" :name="item.key" :row="row" :column="column"></slot>
              <span v-else>{{ row[item.key] || '--' }}</span>
            </span>
          </template>
        </vxe-column>
        <vxe-column
          v-if="actionList.length > 0"
          title="操作"
          :width="actionAutoWidth"
          align="left"
          header-align="left"
          fixed="right"
        >
          <template #default="{ row, rowIndex }">
            <div class="actions">
              <template v-for="(action, aIndex) in actionList" :key="aIndex">
                <el-link
                  v-if="!(action.hide && action.hide(row)) && findVisibleIndex(row, action) <= 2"
                  class="action-item"
                  :type="action.type"
                  @click="actionClickFun(row, action, rowIndex)"
                >
                  {{ action.title }}
                </el-link>
              </template>
              <el-dropdown v-if="visibleNum(row) > 2">
                <el-icon class="more-icon">
                  <MoreFilled />
                </el-icon>
                <template #dropdown>
                  <el-dropdown-menu>
                    <template v-for="(action, aIndex) in actionList" :key="aIndex">
                      <el-dropdown-item
                        v-if="
                          !(action.hide && action.hide(row)) && findVisibleIndex(row, action) > 2
                        "
                        @click="actionClickFun(row, action, rowIndex)"
                      >
                        <el-link :type="action.type">{{ action.title }}</el-link>
                      </el-dropdown-item>
                    </template>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </template>
        </vxe-column>
      </vxe-table>
    </div>
    <div v-else class="card">
      <slot name="cardTemplate" :tableData="tableData" />
    </div>
    <div v-if="!hidePagination" class="pagination">
      <div>共 {{ pageTotal }} 项数据</div>
      <el-pagination
        v-model:current-page="current"
        v-model:page-size="size"
        background
        layout="sizes, prev, pager, next"
        :total="pageTotal"
        @size-change="sizeChangeFun"
        @current-change="currentChangeFun"
      />
    </div>
    <TableFormDia v-model="formDiaVisible" :name="tableName" @update="getHeadDataFun" />
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, nextTick } from 'vue';
import { ElMessageBox } from 'element-plus';
import { Download, Upload, Menu, List, MoreFilled } from '@element-plus/icons-vue';
import type { TableHead, ApiParams, ApiResult } from './types';
import TableSearch from './tableSearch.vue';
import TableFormDia from './tableFormDia.vue';
// import HeadSearch from './headSearch.vue';
import { getFormConfigureApi } from '@/api/system/systemData';
import { formOptionsFormat } from './lib';
import { exportFile, fileUploadAllocationIconFun } from '@/utils/file';
import { cloneDeep, isString } from 'lodash-es';

const emit = defineEmits([
  'searchChange',
  'load',
  'update:viewType',
  'search',
  'update:searchParams',
  'dataChange',
]);

interface Props {
  tableName?: string;
  api?: (params: ApiParams) => Promise<any> | undefined;
  exportApi?: any;
  exportFileName?: string;
  exportParams?: any;
  render?: (data: any, cb: (cbData: any) => void) => void | undefined;
  params?: any;
  head?: any;
  viewType?: string;
  searchItems?: any[];
  searchLimitNum?: number;
  showCheckbox?: boolean;
  showIndex?: boolean;
  hidePagination?: boolean;
  searchParams?: any;
  showExport?: boolean;
  showImport?: boolean;
  listTitle?: string;
  actionList?: any;
  showOverflow?: boolean;
  searchAttrs?: any;
  fullHeight?: boolean;
  defaultSearchParams?: any; // 默认搜索项目
  showNodeName?: string;
  hideSearchKeys?: any;
  hideSearch?: boolean; // 隐藏整个搜索栏
}

const props = withDefaults(defineProps<Props>(), {
  tableName: '',
  api: undefined,
  exportApi: undefined,
  exportFileName: '',
  exportParams: {},
  render: undefined,
  params: () => {},
  head: null,
  viewType: 'list', // list:列表 card:卡片
  searchItems: () => [] as any[],
  searchLimitNum: 0,
  showCheckbox: false,
  showIndex: false,
  hidePagination: false,
  searchParams: {},
  showExport: true,
  showImport: false,
  listTitle: '',
  actionList: [],
  showOverflow: true,
  searchAttrs: {},
  fullHeight: false,
  defaultSearchParams: {},
  showNodeName: '',
  hideSearchKeys: [],
  hideSearch: false,
});

const tableData = ref<any>([]);
const tableHead = ref<TableHead[]>([]);
const tableHeadVisible = ref<TableHead[]>([]);
const searchData = ref<object>({});
const current = ref(1);
const size = ref(20);
const pageTotal = ref(0);
const formDiaVisible = ref(false);
const loading = ref(false);
const vxeTableRef = ref<any>();
const tableSearchRef = ref<any>();
const searchList = ref<any>(props.searchItems);
const actionAutoWidth = ref(95);
const headSearchParams = ref<any>({});

const findVisibleIndex = (row: any, action: any) => {
  let index = 0;
  props.actionList.some((item: any) => {
    if (!(item.hide && item.hide(row))) {
      index++;
    }
    if (action.title === item.title) {
      return true;
    }
  });
  return index;
};

const visibleNum = (row: any) => {
  // 展示按钮数量
  return props.actionList.filter((d: any) => !(d.hide && d.hide(row))).length;
};

watch(
  () => props.actionList,
  (list: any) => {
    let width = 20; // cell内边距
    list.some((item: any, index: number) => {
      if (index < 2) {
        width += item.title.length * 14; // 一个汉字14
        width += 8; // 内边距
      } else {
        return true;
      }
    });
    if (list.length > 2) {
      width += 30; // 更多宽度
    }
    actionAutoWidth.value = width;
  },
  { immediate: true }
);

watch(
  () => props.params,
  (val: any) => {
    resetSearchFun(val);
  },
  { deep: true }
);

watch(
  () => props.searchItems,
  (val) => {
    searchList.value = val;
  },
  { deep: true, immediate: true }
);

onMounted(() => {
  initFun();
});

const initFun = () => {
  current.value = 1;
  searchData.value = cloneDeep(props.defaultSearchParams);
  if (!props.head) {
    getHeadDataFun();
  }
};

// 获取表头数据
const getHeadDataFun = () => {
  if (!props.tableName) {
    return;
  }
  const params = {
    formName: props.tableName,
  };
  tableHeadVisible.value = []; // vxe-table动态头部顺序改变时视图不更新，置空强制更新
  getFormConfigureApi(params).then((res: any) => {
    if (res.code === 200) {
      const formConfig = JSON.parse(res.data.formConfig);
      tableHead.value = formConfig;
      tableHeadVisible.value = formConfig.filter((item: any) => item.isShow);
      if (props.showNodeName) {
        for (let i = 0; i < tableHeadVisible.value.length; i++) {
          if (tableHeadVisible.value[i].key === props.showNodeName) {
            tableHeadVisible.value[i].treeNode = true;
          }
        }
      }
      if (props.searchItems.length === 0) {
        // 没有传入搜索配置，则默认搜索配置
        const searchBuild: any[] = [];
        formConfig.forEach((item: any) => {
          if (item.tableSearch) {
            searchBuild.push(item);
          }
        });
        searchList.value = searchBuild;
        formOptionsFormat(searchList.value);
      }
      getTableDataFun();
    }
  });
};
// 获取表单数据
const getTableDataFun = () => {
  const reqParams: ApiParams = {
    current: current.value,
    size: size.value,
    ...props.params,
    ...searchData.value,
    ...headSearchParams.value,
  };
  loading.value = true;
  if (props.api) {
    props
      .api(reqParams)
      ?.then((res: ApiResult) => {
        if (res.code === 200) {
          if (props.render) {
            props.render(res.data, (cbData: any) => {
              const { data, total } = cbData;
              tableData.value = extrasDataFun(data);
              pageTotal.value = total || 0;
            });
          } else {
            const { data, total } = res.data;
            tableData.value = extrasDataFun(data);
            pageTotal.value = total || 0;
          }
          emit('dataChange');
        }
      })
      .finally(() => {
        loading.value = false;
      });
  } else {
    if (props.render) {
      props.render(null, (data: any) => {
        tableData.value = extrasDataFun(data);
      });
    }
    loading.value = false;
  }
};
// 手动设置表单数据
const setDataFun = (data: any) => {
  tableData.value = [];
  nextTick(() => {
    tableData.value = extrasDataFun(data);
  });
};
// // 表头搜索
// const headSearchFun = (params: any) => {
//   headSearchParams.value = Object.assign(headSearchParams.value, params);
//   getTableDataFun();
// };
// 搜索
const searchFun = (data: object) => {
  current.value = 1;
  searchData.value = data;
  getTableDataFun();
  emit('search');
};
// 重置搜索
const resetSearchFun = (data: object) => {
  current.value = 1;
  searchData.value = data;
  headSearchParams.value = {};
  emit('update:searchParams', searchData.value);
  getTableDataFun();
};
// 重置表单
const resetFun = (reload?: boolean) => {
  if (reload) {
    current.value = 1;
  }
  getTableDataFun();
};
watch(
  () => props.head,
  (val) => {
    if (val) {
      tableHead.value = val;
      tableHeadVisible.value = val.filter((item: any) => item.isShow);
      resetSearchFun({});
    }
  },
  { deep: true, immediate: true }
);
// 分页变化
const sizeChangeFun = (value: number) => {
  size.value = value;
  current.value = 1;
  getTableDataFun();
};
// 当前页变化
const currentChangeFun = (value: number) => {
  current.value = value;
  getTableDataFun();
};
// 获取搜索参数
const getSearchParamsFun = () => {
  if (tableSearchRef.value) {
    return tableSearchRef.value.getSearchParamsFun();
  }
};
// 设置搜索参数
const setSearchParamsFun = (data: any) => {
  if (tableSearchRef.value) {
    return tableSearchRef.value.setSearchParamsFun(data);
  }
};
// 通过key获取搜索参数
const getSearchParamByKeyFun = (key: string) => {
  if (tableSearchRef.value) {
    return tableSearchRef.value.getSearchParamByKeyFun(key);
  }
};
// 通过key设置搜索参数
const setSearchParamByKeyFun = (key: string, data: any) => {
  if (tableSearchRef.value) {
    return tableSearchRef.value.setSearchParamByKeyFun(key, data);
  }
};
// 搜索变化
const changeFun = (data: any) => {
  emit('searchChange', data);
};
// 视图切换
const viewTypeChangeFun = (type: string) => {
  emit('update:viewType', type);
};
const setOptionsFun = (key: string, options: any[]) => {
  if (tableSearchRef.value) {
    return tableSearchRef.value.setOptionsFun(key, options);
  }
};
const searchLoadFun = () => {
  emit('load');
};
const actionClickFun = (row: any, action: any, index: number) => {
  const { click, needConfirm, confirmTip } = action;
  if (click) {
    if (needConfirm) {
      ElMessageBox.confirm(confirmTip || '确定操作吗？', '提示', {
        type: 'warning',
      })
        .then(() => {
          click(row, index);
        })
        .catch(() => {});
    } else {
      click(row, index);
    }
  }
};

const exportFun = () => {
  exportFile(props.exportApi, props.tableName, props.exportFileName, {
    ...searchData.value,
    ...props.exportParams,
  });
};

watch(
  () => props.tableName,
  () => {
    initFun();
  }
);

const extrasDataFun = (data = [] as any) => {
  data.forEach((item: any) => {
    for (const key in item) {
      if (key === 'extras' && item.extras) {
        item[key].forEach((val: any) => {
          item[val.propertyName] = val.propertyValue;
        });
      }
    }
  });
  return data;
};

defineExpose({
  tableData,
  tableRef: vxeTableRef,
  resetFun,
  setDataFun,
  getSearchParamsFun,
  getSearchParamByKeyFun,
  setSearchParamsFun,
  setSearchParamByKeyFun,
  setOptionsFun,
  getTableDataFun,
});
</script>

<style lang="scss" scoped>
.comp-base-table {
  display: flex;
  flex-direction: column;
  height: 100%;
  .td-text {
    .icon {
      margin-right: 2px;
      .el-icon {
        transform: translateY(2px);
      }
    }
    .img {
      width: 20px;
      height: 20px;
      margin-right: 8px;
      transform: translateY(3px);
    }
  }
  .options {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
    .item {
      display: flex;
      align-items: center;
      .list-title {
        font-size: 16px;
        color: var(--el-text-color-primary);
        font-weight: bold;
      }
      .btns {
        display: flex;
        align-items: center;
        .icon-btn {
          display: flex;
          align-items: center;
          justify-content: center;
          width: 24px;
          height: 24px;
          margin: 4px 0 4px 10px;
          border-radius: 4px;
          color: var(--el-text-color-secondary);
          cursor: pointer;
          &:hover {
            background-color: var(--el-border-color-light);
          }
        }
      }
    }
  }
  .table {
    height: 0;
    flex: 1;
    overflow: hidden;
  }
  .card {
    height: 0;
    flex: 1;
    overflow-y: auto;
    padding-bottom: 10px;
  }
  .actions {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    &::after {
      content: '';
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      width: 1px;
      background-color: var(--el-text-color-primary);
      filter: blur(5px);
    }
    .action-item {
      padding: 0 4px;
    }
    .more-icon {
      margin-left: 10px;
    }
  }
  .pagination {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 60px;
    padding: 30px 20px 10px;
    color: var(--el-text-color-secondary);
    font-size: 13px;
  }
}
</style>
