<template>
  <el-table
    ref="ftable"
    :show-summary="sumcol && sumcol.length > 0"
    :summary-method="sumComputed"
    :row-key="rowKey"
    :data="data"
    border
    stripe
    :max-height="height"
    @select="selectChange"
    @select-all="selectAll"
  >
    <el-table-column type="selection" align="center" width="60" v-if="useSelect" />
    <template v-for="i in fields" :key="i.model">
      <template v-if="!i.custom">
        <el-table-column sortable align="center" :label="i.label" :prop="i.model" :formatter="toFormatter"></el-table-column>
      </template>
      <slot :name="i.model"></slot>
    </template>
    <el-table-column align="center" label="操作" v-if="opera">
      <template #default="{ row, $index }">
        <template v-for="i in opera" :key="i.method">
          <el-link :type="i.type ? i.type : 'primary'" :underline="false" @click="toOpera(row, $index, i)" class="opera__btn">{{ i.label }}</el-link>
        </template>
      </template>
    </el-table-column>
  </el-table>
  <el-row class="page__row" v-if="total">
    <el-col :span="24">
      <el-pagination small :page-size="limit" background layout="->, total, prev, pager, next " :total="total" @current-change="pageChange" />
    </el-col>
  </el-row>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
  name: 'FTable',
});
</script>

<script setup lang="ts">
import _ from 'lodash';
import { ElMessageBox } from 'element-plus';
import type { Action } from 'element-plus';
import { ElTable } from 'element-plus';
import type { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults';
import { IOpera, IField, OperaType, rowKeyType } from '@/types/FTable';
import { ref, defineProps, defineEmits, withDefaults, watch, nextTick } from 'vue';
interface IProps {
  /** 数据 */
  data: Array<any>;
  /** 显示设置 */
  fields: Array<IField>;
  /** 操作设置 */
  opera?: Array<IOpera>;
  // limit: number;
  /** 总数 */
  total?: number;
  /** 最大高度 */
  height?: number;
  /**显示格式化函数 */
  toFormat?: (obj: any) => string;
  /**行数据的key */
  rowKey?: rowKeyType | string;
  /**合计字段 */
  sumcol?: Array<string>;
  /**使用多选 */
  useSelect?: boolean;
  /**选择的数据 */
  selected?: Array<any>;
}
const props = withDefaults(defineProps<IProps>(), {
  rowKey: '_id',
  useSelect: true,
});
let limit = 10;
const emits = defineEmits<{
  (e: 'query', p: any): void;
  (e: 'update:selected', p: any): void;
  (e: string, p: any): void;
}>();
/**
 * 运行操作
 * @prop {Record<string,any>} row 行数据
 * @prop {number} index 行数据
 * @prop {Record<string,any>} opera 行数据
 */
let toOpera = (row: Record<string, any>, index: number, opera: IOpera) => {
  const { confirm, method, methodZh } = opera;
  if (!confirm) {
    emits(method, { row, index });
  } else {
    let word;
    if (methodZh) word = methodZh;
    else word = _.get(OperaType, method);
    ElMessageBox.confirm(`您确定要${word}该数据吗?`, '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
      callback: (res: Action) => {
        if (res === 'confirm') {
          // 执行函数
          emits(method, { row, index });
        } else {
          // 取消
          console.log('cancel');
        }
      },
    });
  }
};

/**
 * 表格显示格式化
 * @prop {Record<string,any>} row 行数据
 * @prop {Record<string,any>} column 列设置数据
 * @prop {any} cellValue 表格数据
 * @prop {number} index 索引
 * @return string
 */
let toFormatter = (row: Record<string, any>, column: Record<string, any>, cellValue: any, index: number): string => {
  let this_fields = props.fields.filter((fil) => fil.model === column.property);
  if (this_fields.length > 0) {
    let format = _.get(this_fields[0], `format`, false);
    if (format) {
      let res;
      if (_.isFunction(format)) {
        res = format(cellValue);
      } else if (_.isFunction(props.toFormat)) {
        res = props.toFormat({
          model: this_fields[0].model,
          value: cellValue,
          row,
        });
      }
      return res;
    } else return cellValue;
  }
  return cellValue;
};

// #region 计算部分
interface SummaryMethodProps<T> {
  columns: TableColumnCtx<T>[];
  data: T[];
}
/**
 * 计算函数
 * @prop {Array} columns 列数组
 * @prop {Array} data 表格中所有的数据
 * @return Array
 * */
let sumComputed = (param: SummaryMethodProps<any>) => {
  const { columns, data } = param;
  let result: string[] = [];
  if (columns.length <= 0 || data.length <= 0) return [];
  const reg = new RegExp(/^\d+$/);
  for (const column of columns) {
    const prop = _.get(column, 'property');
    if (!prop) {
      result.push('');
      continue;
    }
    // 判断是否需要计算
    if (!props.sumcol) return result;
    const inlist = props.sumcol.find((f) => f == prop);
    if (!inlist) {
      result.push('');
      continue;
    }
    let res;
    // 整理出要计算的属性(只取出数字或者可以为数字的值)
    const resetList = data.map((i) => {
      const d = _.get(i, prop);
      const res = reg.test(d);
      if (res) return d * 1;
      else return 0;
    });
    res = `${resetList.reduce((p, n) => p + n, 0)}`;
    result.push(res);
  }
  result[0] = '合计';
  return result;
};
// #endregion

// #region 选择部分
const ftable = ref<InstanceType<typeof ElTable>>();
/**选择/反选 */
let selectChange = (selection: any, row: any) => {
  if (!props.selected) return;
  let selected = _.cloneDeep(props.selected);
  let has = props.selected.find((i) => i[props.rowKey] === row[props.rowKey]);
  if (has) {
    selected = props.selected.filter((f) => f[props.rowKey] !== row[props.rowKey]);
  } else {
    selected.push(row);
  }
  emits('update:selected', selected);
};
/**全选/反选 */
let selectAll = (selection: any) => {
  if (!props.selected) return;
  let selected = _.cloneDeep(props.selected);
  let res = [];
  if (selection.length > 0) {
    res = _.uniqBy(selected.concat(selection), props.rowKey);
  } else {
    res = _.differenceBy(selected, props.data, props.rowKey);
  }
  emits('update:selected', res);
};
/**渲染选项 */
let initSelection = async () => {
  // 清除已选择的选项样式
  if (!props.selected) return;
  await nextTick();
  let selected = props.selected;
  ftable.value?.clearSelection();
  selected.forEach((info) => {
    let d = props.data.filter((p) => p[props.rowKey] === info[props.rowKey]);
    if (d.length > 0) ftable.value?.toggleRowSelection(d[0], true);
  });
};
watch(
  () => props.data,
  (val) => initSelection(),
  { deep: true, immediate: true }
);
// #endregion

/**
 * 换页
 * @prop {number} page 当前页
 */
let pageChange = (page: number) => {
  const query = { skip: (page - 1) * limit, limit };
  emits('query', query);
};
</script>

<style lang="scss" scoped>
.opera__btn {
  padding-right: 10px;
}
.page__row {
  padding: 15px 0;
}
</style>
