<template>
  <div class="comp-content">
    <TableSearch
      v-show="searchList.length > 0"
      ref="tableSearchRef"
      :searchItems="searchList"
      :searchLimitNum="searchLimitNum"
      :searchParams="searchParams"
      @search="searchFun"
      @reset="resetFun"
      @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" :content="$t('表格.导出')" placement="top" >
            <div v-if="exportApi" 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'
        }"
      >
        <vxe-column v-if="showCheckbox" type="checkbox" width="60" align="center" header-align="center"></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
          show-overflow
        >
          <template #default="{ row, column }">
            <slot :name="item.key" :row="row" :column="column">
              <span>{{ row[item.key] || '--' }}</span>
            </slot>
          </template>
        </vxe-column>
        <vxe-column
          v-if="$slots.tableActions || actionList.length > 0"
          title="操作"
          :width="$slots.tableActions ? actionsWidth : actionAutoWidth"
          align="left"
          header-align="left"
          fixed="right"
        >
          <template #default="{ row }">
            <div v-if="!$slots.tableActions" 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)">
                  {{ 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)">
                        <el-link :type="action.type">{{ action.title }}</el-link>
                      </el-dropdown-item>
                    </template>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
            <div v-if="$slots.tableActions" class="actions">
              <slot name="tableActions" :row="row" />
            </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 } from 'vue';
import { ElMessageBox } from 'element-plus';
import { Download, Upload, Setting, 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 { getFormConfigureApi } from '@/api/system/systemData';
import { formOptionsFormat } from './lib';
import { exportFile } from '@/utils/file';

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

interface Props {
  tableName?: string;
  api?: (params: ApiParams) => Promise<any> | undefined;
  exportApi?: any;
  exportFileName?: string;
  exportDict?: any;
  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;
  actionsWidth?: number;
  searchParams?: any;
  showExport?: boolean;
  showImport?: boolean;
  listTitle?: string;
  actionList?: any;
}

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

const tableData = ref<any[]>([]);
const tableHead = ref<TableHead[]>([]);
const tableHeadVisible = ref<TableHead[]>([]);
const searchData = ref<object>({});
const current = ref(1);
const size = ref(10);
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 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) => {
  resetFun(val);
}, { deep: true });

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

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

const initFun = () => {
  current.value = 1;
  searchData.value = {};
  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.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,
  };
  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 = data;
            pageTotal.value = total || 0;
          });
        } else {
          const { data, total } = res.data;
          tableData.value = data;
          pageTotal.value = total || 0;
        }
      }
    }).finally(() => {
      loading.value = false;
    });
  } else {
    if (props.render) {
      props.render(null, (data: any) => {
        tableData.value = data;
      });
    }
    loading.value = false;
  }
};
// 手动设置表单数据
const setDataFun = (data: any) => {
  tableData.value = data;
};
// 搜索
const searchFun = (data: object) => {
  searchData.value = data;
  getTableDataFun();
  emit('search');
};
// 重置表单
const resetFun = (data: object) => {
  current.value = 1;
  searchData.value = data;
  getTableDataFun();
};
watch(() => props.head, (val) => {
  if (val) {
    tableHead.value = val;
    tableHeadVisible.value = val.filter((item: any) => item.isShow);
    resetFun({});
  }
}, { 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) => {
  const { click, needConfirm, confirmTip } = action;
  if (click) {
    if (needConfirm) {
      ElMessageBox.confirm(confirmTip || '确定操作吗？', '提示', {
        type: 'warning',
      }).then(() => {
        click(row);
      }).catch(() => {});
    } else {
      click(row);
    }
  }
};

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

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

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

<style lang="scss" scoped>
.comp-content {
  .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;
          border-radius: 4px;
          color: var(--el-text-color-secondary);
          margin-left: 10px;
          cursor: pointer;
          &:hover {
            background-color: var(--el-border-color-light);
          }
        }
      }
    }
  }
  .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);
  }
}
</style>
