<template>
  <div id="c-table">
    <el-table
      ref="table"
      :row-key="rowKey"
      :data="data"
      border
      stripe
      :max-height="height !== null ? height : ''"
      @select="handleSelectionChange"
      @select-all="handleSelectAll"
      :show-summary="useSum"
      @row-click="rowClick"
      :summary-method="computedSum"
    >
      <el-table-column type="selection" width="55" v-if="select" :prop="rowKey" :reserve-selection="true"> </el-table-column>
      <template v-for="(item, index) in fields">
        <template v-if="item.custom">
          <el-table-column :key="index" align="center" :label="item.label" v-bind="item.options" :show-overflow-tooltip="item.showTip || true">
            <template v-slot="{ row }">
              <slot :name="item.model" v-bind="{ item, row }"></slot>
            </template>
          </el-table-column>
        </template>
        <template v-else>
          <el-table-column
            :key="index"
            align="center"
            :label="item.label"
            :prop="item.model"
            :formatter="toFormatter"
            :sortable="true"
            v-bind="item.options"
            :show-overflow-tooltip="item.showTip === false ? item.showTip : true"
          >
          </el-table-column>
        </template>
      </template>
      <template v-if="opera.length > 0">
        <el-table-column label="操作" align="center" :width="operaWidth">
          <template v-slot="{ row, $index }">
            <template v-for="(item, index) in opera">
              <template v-if="display(item, row)">
                <template v-if="vOpera">
                  <el-link
                    v-opera="item.method"
                    :key="`${item.model}-column-${index}`"
                    :type="item.type || 'primary'"
                    :icon="item.icon || ''"
                    size="small"
                    style="padding-right: 10px"
                    :underline="false"
                    @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
                  >
                    {{ item.label }}
                  </el-link>
                </template>
                <template v-else>
                  <el-link
                    :key="`${item.model}-column-${index}`"
                    :type="item.type || 'primary'"
                    :icon="item.icon || ''"
                    size="small"
                    style="padding-right: 10px"
                    :underline="false"
                    @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index, item.confirmWord)"
                  >
                    {{ item.label }}
                  </el-link>
                </template>
              </template>
            </template>
          </template>
        </el-table-column>
      </template>
    </el-table>
    <el-row type="flex" align="middle" justify="end" v-if="usePage">
      <el-col :span="24" class="page">
        <el-pagination
          background
          layout="sizes,total, prev, pager, next"
          :page-sizes="[10, 20, 50, 100, 200]"
          :total="total"
          :page-size="limit"
          v-model:current-page="currentPage"
          @current-change="changePage"
          @size-change="sizeChange"
        >
        </el-pagination>
      </el-col>
    </el-row>
  </div>
</template>
<script setup lang="ts">
import type { Ref } from 'vue';
import { ref, toRefs, nextTick, getCurrentInstance } from 'vue';
import { ElMessageBox } from 'element-plus';

import _ from 'lodash';
const { proxy } = getCurrentInstance() as any;

// #region 传递

interface fieldsItem {
  custom: string;
  label: string;
  options: string;
  showTip: boolean;
  model: string;
}
interface operaItem {
  method: string;
  model: string;
  type: string;
  icon: string;
  confirmWord: string;
  label: string;
  confirm: boolean;
  methodZh: string;
}
interface dataItem {
  _id?: string;
}

const props = defineProps({
  fields: { type: Array<fieldsItem>, required: true },
  data: { type: Array<dataItem>, required: true },
  opera: { type: Array<operaItem>, default: () => [] },
  rowKey: { type: String, default: '_id' },
  select: { type: Boolean, default: false },
  selected: { type: Array, default: () => [] },
  usePage: { type: Boolean, default: true },
  total: { type: Number, default: 0 },
  useSum: { type: Boolean, default: false },
  sumcol: { type: Array, default: () => [] },
  sumres: { type: String, default: 'total' },
  // limit: { type: Number, default: 10 },
  height: null,
  operaWidth: { type: Number, default: 200 },
  vOpera: { type: Boolean, default: false },
});
const { fields } = toRefs(props);
const { data } = toRefs(props);
const { opera } = toRefs(props);
const { rowKey } = toRefs(props);
const { select } = toRefs(props);
const { selected } = toRefs(props);
const { usePage } = toRefs(props);
const { total } = toRefs(props);
const { useSum } = toRefs(props);
const { sumcol } = toRefs(props);
const { sumres } = toRefs(props);
// const { limit } = toRefs(props);
const { height } = toRefs(props);
const { operaWidth } = toRefs(props);
const { vOpera } = toRefs(props);
// #endregion
const emit = defineEmits(['method', 'handleSelect', 'query', 'rowClick']);

let pageSelected: Ref<any[]> = ref([]);
let currentPage: Ref<number> = ref(1);

let limit: number = proxy.$limit;
const toFormatter = (row: any, column: { property: string }, cellValue: string, index: string) => {
  let this_fields = fields.value.filter((fil) => fil.model === column.property);
  if (this_fields.length > 0) {
    let format: any = _.get(this_fields[0], `format`, false);
    if (format) {
      let res;
      if (_.isFunction(format)) {
        res = format(cellValue);
      } else {
        res = toFormat({
          model: this_fields[0].model,
          value: cellValue,
        });
      }
      return res;
    } else {
      return cellValue;
    }
  }
};
const toFormat = (e: { model: string; value: string }) => {};
const handleOpera = (data: string, method: any, confirm = false, methodZh: string, label: string, index: string, confirmWord: string) => {
  let self = true;
  if (_.isFunction(methodZh)) methodZh = methodZh(data);
  else if (!_.isString(methodZh)) {
    methodZh = label;
    self = false;
  }
  if (confirm) {
    let word = self ? methodZh : `您确认${methodZh}该数据?`;
    if (confirmWord) word = confirmWord;
    ElMessageBox.confirm(word, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
      .then(() => {
        emit(method, data, index);
      })
      .catch(() => {});
  } else emit(method, data, index);
};
const handleSelectionChange = (selection: string, row: never) => {
  //根据row是否再pageSelected中,判断是添加还是删除
  let isSelecteds = _.cloneDeep(pageSelected);
  const is_has = isSelecteds.value.findIndex((f) => f[rowKey.value] === row[rowKey.value]);
  if (is_has <= -1) {
    // 没有找到,属于添加
    isSelecteds.value.push(row);
  } else {
    // 找到了,删除
    isSelecteds.value.splice(is_has, 1);
  }
  pageSelected.value = isSelecteds.value;
  emit(`handleSelect`, isSelecteds);
};
const handleSelectAll = (selection: any) => {
  //处于没全选状态,选择之后一定是全选,只有处于全选状态时,才会反选(全取消)
  let res: any = [];
  if (selection.length > 0) {
    //全选
    res = _.uniqBy(pageSelected.value.concat(selection), rowKey.value);
  } else {
    //全取消
    res = _.differenceBy(pageSelected.value, data.value, rowKey.value);
  }
  pageSelected.value = res;
  emit(`handleSelect`, res);
};
const table = ref<any>();
const initSelection = () => {
  nextTick(() => {
    table.value.clearSelection();
    selected.value.forEach((info: any) => {
      let d = data.value.filter((p) => p._id === info._id);
      if (d.length > 0) table.value.toggleRowSelection(d[0]);
    });
  });
};
const selectReset = () => {
  table.value.clearSelection();
};
const changePage = (page: number = currentPage.value) => {
  emit('query', { skip: (page - 1) * limit, limit: limit });
};
const sizeChange = (limits: number) => {
  limit = limits;
  currentPage.value = 1;
  emit('query', { skip: 0, limit: limit });
};
const rowClick = (row: any, column: string, event: string) => {
  emit(`rowClick`, row);
};
const display = (item: operaItem, row: any) => {
  let display: any = _.get(item, `display`, true);
  if (display === true) return true;
  else {
    let res = display(row);
    return res;
  }
};
// 计算合计
const computedSum = (columns: any, data: any) => {
  if (columns.length <= 0 || data.length <= 0) return '';
  const result = [];
  const reg = new RegExp(/^\d+$/);
  for (const column of columns) {
    // 判断有没有prop属性
    const prop = _.get(column, 'property');
    if (!prop) {
      result.push('');
      continue;
    }
    // 判断是否需要计算
    const inlist = sumcol.value.find((f) => f == prop);
    if (!inlist) {
      result.push('');
      continue;
    }
    let res: number | unknown = 0;
    // 整理出要计算的属性(只取出数字或者可以为数字的值)
    const resetList = data.map((i: any) => {
      const d = _.get(i, prop);
      return d * 1;
    });
    let p1: any;
    if (sumres.value === 'total') {
      res = totalComputed(p1, resetList);
    } else if (sumres.value === 'avg') {
      res = avgComputed(resetList);
    } else if (sumres.value === 'max') {
      res = maxComputed(resetList);
    } else if (sumres.value === 'min') {
      res = minComputed(resetList);
    }
    result.push(res);
  }
  result[0] = '合计';
  return result;
};
// 合计计算
const totalComputed = (columns: any, data: any) => {
  const total = data.reduce((p: number, n: string) => p + parseFloat(n), 0);
  return total;
};
// 平均值计算
const avgComputed = (data: any) => {
  let p1: any;
  const total = totalComputed(p1, data);
  return _.round(_.divide(total, data.length), 2);
};
// 最大值计算
const maxComputed = (data: any) => {
  return _.max(data);
};
// 最小值计算
const minComputed = (data: any) => {
  return _.min(data);
};
</script>

<style scoped>
.page {
  background-color: #fff;
  padding: 8px;
  height: 50px;
}
.el-pagination {
  position: absolute;
  right: 10px;
  background-color: #fff;
}
</style>
