<template>
  <div class="m-table">
    <el-table
      ref="tableRef"
      v-loading="loading"
      :default-expand-all="defaultExpandAll"
      :data="data"
      :height="height"
      :row-key="rowKey"
      :row-style="rowStyle"
      :cell-style="cellStyle"
      :row-class-name="rowClassName"
      :cell-class-name="cellClassName"
      :header-row-class-name="headerRowClassName"
      :header-row-style="headerRowStyle"
      :header-cell-class-name="headerCellClassName"
      :header-cell-style="headerCellStyle"
      :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      :span-method="spanMethod"
      v-bind="tableCfg"
      :border="true"
      :stripe="false"
      :highlight-current-row="true"
      size="default"
      @select="selectEvent"
      @select-all="selectAllEvent"
      @row-click="rowClickEvent"
      @row-dblclick="rowDblClickEvent"
      @current-change="currentChangeEvent"
      @expand-change="expandChangeEvent"
    >
      <!-- 单条展开看详情 -->
      <el-table-column v-if="isExpand" type="expand">
        <template #default="scope" v-if="isExpand && slotName">
          <slot :name="slotName" :row="scope.row" :index="scope.$index"></slot>
        </template>
      </el-table-column>
      <!-- 复选框 -->
      <el-table-column
        v-if="isSelection"
        type="selection"
        width="50"
        header-align="center"
        :align="'center'"
      />
      <!-- 序号 -->
      <el-table-column
        v-if="isOrder"
        label="序号"
        type="index"
        fixed="left"
        width="60"
      />
      <!-- 其他通过类型配置 -->
      <table-column
        v-for="(item, inex) in columns"
        :key="inex"
        :item="item"
        @clickfun="clickFun"
      >
        <template v-for="name in slotsKey" v-slot:[name]="data">
          <slot :name="name" v-bind="data"></slot>
        </template>
      </table-column>
    </el-table>
    <template v-if="showPagination">
      <m-pagination
        v-model:pageSize="pagination.pageSize"
        :total="pagination.total"
        v-model:page="pagination.page"
        @size-change="handlePageSizeChange"
        @page-change="handlePageChange"
      >
      </m-pagination>
    </template>
  </div>
</template>

<script>
import {
  defineComponent,
  reactive,
  toRefs,
  watch,
  ref,
  computed,
  getCurrentInstance,
  onMounted,
} from "vue";
import tableColumn from "./table-column";
import { ElMessage } from "element-plus";
import MPagination from "../../MPagination/index";
import Sortable from "sortablejs";

export default defineComponent({
  name: "MTable",
  components: {
    "m-pagination": MPagination,
    "table-column": tableColumn,
  },
  props: {
    /**
     * 是否能单条展开显示详情
     */
    isExpand: {
      type: Boolean,
      default: false,
    },
    /**
     * 显示复选框
     */
    isSelection: {
      type: Boolean,
      default: true,
    },
    /**
     * 显示排序
     */
    isOrder: {
      type: Boolean,
      default: true,
    },
    /**
     * 表格高度
     */
    height: {
      type: [Number, String],
      default: "100%",
    },
    /**
     * 表格数据
     */
    data: {
      type: Array,
      required: true,
      default: () => [],
    },
    /**
     * 加载数据中
     */
    loading: {
      type: Boolean,
      default: false,
    },
    /**
     * 列配置
     * fixed {string / boolean} [true / 'left' / 'right'] 列是否固定在左侧或者右侧。 true 表示固定在左侧
     * prop {string} 字段名称 对应列内容的字段名
     * name {string} 显示的标题
     * width {string / number} 对应列的宽度
     * align {string} [left / center / right] 'center' 对齐方式
     * headerAlign {string} [left / center / right] 'center' 表头对齐方式
     * sortable {boolean} false 对应列是否可以排序
     * ellipsis {boolean} true 当内容过长被隐藏时显示 tooltip
     * type {string} ['image', 'button'] 列展示类型
     * buttonList {Array} type为button时按钮列表示例：[{show: 展示条件 {func},回调参数为{row,item}, methodName: 事件名称,name: 按钮名称， 其他(type,around,width,text)属性为lebutton属性}]
     * color {object} 颜色值与prop对应关系示例：{red: 0, blue: 1, green: [1,2]}
     * format {string/func} [enum, money,date,link,timeStamp] 格式化类型,为func时回调参数为{row, index, item.prop, item}
     * enum {object} format为enum时，枚举对象
     * methodName {string} 事件名称
     * component 组件名称
     * slotName 插槽名称
     * children 多级表头使用
     * slotHeaderName 自定义表头插槽
     */
    columns: {
      type: Array,
      required: true,
      default: () => [],
    },
    /**
     * 行样式
     */
    rowStyle: {
      type: Function,
    },
    /**
     * 列样式
     */
    cellStyle: {
      type: Function,
    },
    /**
     * 行class 名
     */
    rowClassName: {
      type: Function,
    },
    /**
     * 列class 名
     */
    cellClassName: {
      type: Function,
    },
    /**
     * 表头行的 className 的回调方法，也可以使用字符串为所有表头行设置一个固定的 className。
     */
    headerRowClassName: {
      type: [Function, String],
      default: "m-table-header-row",
    },
    /**
     * 表头行的 style 的回调方法，也可以使用一个固定的 Object 为所有表头行设置一样的 Style。
     */
    headerRowStyle: {
      type: [Function, Object],
    },
    /**
     * 表头单元格的 className 的回调方法，也可以使用字符串为所有表头单元格设置一个固定的 className。
     */
    headerCellClassName: {
      type: [Function, String],
    },
    /**
     * 表头单元格的 style 的回调方法，也可以使用一个固定的 Object 为所有表头单元格设置一样的 Style。
     */
    headerCellStyle: {
      type: [Function, Object],
    },
    /**
     * 表格主键 (一般不传, 树形表格必传)
     */
    rowKey: {
      type: [String, Function],
    },
    /**
     * 是否默认展开所有行，当 Table 包含展开行存在或者为树形表格时有效
     */
    defaultExpandAll: {
      type: Boolean,
      default: false,
    },
    /**
     * isExpand 为true 时，显示详情内容的插槽
     */
    slotName: {
      type: String,
      default: "",
    },
    /**
     * 实现合并行或列
     */
    spanMethod: {
      type: Function,
    },
    /**
     * 其他不常用 element table 具有的属性
     */
    tableCfg: {
      type: Object,
    },
    /**
     * 是否展示分页器
     */
    showPagination: {
      type: Boolean,
      default: false,
    },
    /**
     * 展示分页器的配置参数 {pageSize: '每页条数', total: '总页数', page: '当前页'}
     */
    pagination: {
      type: Object,
      default: () => {},
    },
    /**
     * 是否启用拖拽组件
     */
    isDrag: {
      type: Boolean,
      default: false,
    },
    /**
     * 开启拖拽功能后，需指定拖拽组件父集元素classname
     */
    dragDivClassName: {
      type: String,
      default: "",
    },
  },
  emits: [
    /**
     * 选中值发生变化时触发 | 回调参数：selectList-选中数据, row-单行数据
     */
    "select",
    /**
     * 当某一行被点击时会触发该事件 | 回调参数：row-单行数据, column, event
     */
    "row-click",
    /**
     * 当某一行被双击时会触发该事件 | 回调参数：row-单行数据, column, event
     */
    "row-dblclick",
    /**
     * 当表格的当前行发生变化的时候会触发该事件 | 回调参数：currentRow, oldCurrentRow
     */
    "current-change",
    /**
     * 当用户对某一行展开或者关闭的时候会触发该事件 | 回调参数：row-单行数据,(expandedRows | expanded)
     */
    "expand-change",
    /**
     * pagesize发生变化时触发，详情请看lePagination组件文档 | 回调参数：value
     */
    "size-change",
    /**
     * page发生变化时触发，详情请看lePagination组件文档 | 回调参数：value
     */
    "page-change",
    /**
     * 当拖拽表格行顺序变化时触发 | 回调参数：oldIndex, newIndex
     */
    "sort",
  ],
  setup(props, { slots }) {
    let { proxy } = getCurrentInstance();
    const tableRef = ref();
    const state = reactive({
      selectRow: null,
      slotsKey: Object.keys(slots) || [],
    });
    onMounted(() => {
      // 开启拖拽&&设置rowkey时，创建sortable
      if (props.isDrag && props.rowKey) {
        rowDrop();
      }
    });
    // 拖拽
    function rowDrop() {
      let tbodyClassname = ".m-table .el-table__body-wrapper tbody";
      if (props.dragDivClassName != "") {
        tbodyClassname = `.${props.dragDivClassName} .m-table .el-table__body-wrapper tbody`;
      }
      let tbody = document.querySelector(`${tbodyClassname}`);
      Sortable.create(tbody, {
        group: {
          // 相同的组之间可以相互拖拽
          name: "table",
          pull: true,
          put: true,
        },
        animation: 150, // ms, number 单位：ms，定义排序动画的时间
        onStart: function (e) {
          // 开始拖拽出发该函数
        },
        onSort: function (e) {
          // 发生排序发生该事件
        },
        onEnd(e) {
          // 结束拖拽
          // 如果拖拽结束后顺序发生了变化，则对数据进行修改
          if (e.oldIndex !== e.newIndex) {
            setNodeSort(e.oldIndex, e.newIndex);
          }
        },
      });
    }
    // 拖拽完成修改数据排序，从1开始
    function setNodeSort(oldIndex, newIndex) {
      proxy.$emit("sort", oldIndex, newIndex);
    }
    /**
     * 复选框 选择事件
     *  @property {Array} selectList  勾选数据行
     *  @property {Object} row  单行数据
     */
    function selectEvent(selectList, row) {
      proxy.$emit("select", selectList, row);
    }
    /**
     * 复选框 全选事件
     * @property {Array} selectList  所有数据
     */
    function selectAllEvent(selectList) {
      proxy.$emit("select", selectList, null);
    }
    /**
     * 单行点击事件
     * @property {Object} row  单行数据
     * @property {Object} column  单行配置项
     * @property {Event} event  事件
     */
    function rowClickEvent(row, column, event) {
      state.selectRow = row;
      proxy.$emit("row-click", row, column, event);
    }
    /**
     * 单行双击事件
     * @property {Object} row  单行数据
     * @property {Object} column  单行配置项
     * @property {Event} event  事件
     */
    function rowDblClickEvent(row, column, event) {
      proxy.$emit("row-dblclick", row, column, event);
    }
    /**
     * 当表格的当前行发生变化的时候会触发该事件
     * @property {Object} currentRow  当前行
     * @property {Object} oldCurrentRow
     */
    function currentChangeEvent(currentRow, oldCurrentRow) {
      proxy.$emit("current-change", currentRow, oldCurrentRow);
    }
    /**
     * 当用户对某一行展开或者关闭的时候会触发该事件
     * @property {Object} row  单行数据
     * @property {expandedRows | expanded} expandedRows （展开行时，回调的第二个参数为 expandedRows；树形表格时第二参数为 expanded）
     */
    function expandChangeEvent(row, expanded) {
      proxy.$emit("expand-change", row, expanded);
    }
    function onError() {
      ElMessage({ message: "哎呀, 发生了错误", type: "error" });
    }
    // 调用父组件事件
    function clickFun(methodName, row, index, item) {
      if (!proxy.$parent[methodName]) return;
      proxy.$parent[methodName](row, index, item?.prop, item);
    }
    // 切换每页条数page-size
    function handlePageSizeChange(val) {
      proxy.$emit("size-change", val);
    }
    // 切换页 page
    function handlePageChange(val) {
      proxy.$emit("page-change", val);
    }

    watch(
      () => props.data,
      (val) => {
        // 刷新页面会造成store丢失，重新获取info
        if (val && state.selectRow) {
          let row = val.find(
            (item) =>
              item[props.rowKey || "id"] ===
              state.selectRow[props.rowKey || "id"]
          );
          console.log(tableRef.value, "tableRef.value");
          if (tableRef.value) {
            tableRef.value.setCurrentRow(row);
          }
          if (!row) return;
          state.selectRow = null;
        }
      },
      {
        deep: true,
        immediate: true,
      }
    );

    return {
      ...toRefs(state),
      selectEvent,
      selectAllEvent,
      rowClickEvent,
      rowDblClickEvent,
      currentChangeEvent,
      expandChangeEvent,
      onError,
      clickFun,
      handlePageSizeChange,
      handlePageChange,
    };
  },
});
</script>
<style lang="scss" scoped>
@import "../../../styles/MTable/index.scss";
</style>
