<template>
  <div class="sel-table">
    <el-table
      :id="id"
      ref="table"
      :show-header="showHeader"
      :size="size"
      :border="border"
      :height="height"
      :span-method="objectSpanMethod"
      :header-cell-style="headerCellStyle"
      :data="tableData"
      :row-key="props.rowKey"
      :current-row-key="props.rowKey"
      @sort-change="sortChange"
      @row-click="rowClick"
      @filter-change="filterChange"
      @selection-change="handleSelectionChange"
    >
      <el-table-column width="45" v-if="props?.treeProps"></el-table-column>
      <el-table-column type="selection" width="55" align="center" v-if="select"></el-table-column>
      <el-table-column type="index"
                       :label="typeof index==='string'?index:''"
                       :index="calcIndex"
                       align="center"
                       :width="typeof index==='string'?index.length*32:64"
                       :fixed="indexFixed"
                       v-if="!!index">
      </el-table-column>
      <columns
        v-for="(item,cindex) in useColumns"
        :key="'c'+cindex"
        :item="item"
        :edit="edit"
      >
        <template #[item.key]="{row,config}" v-for="(item) in slotColumns">
          <slot :name="item.key" :row="row" :config="config"></slot>
        </template>
      </columns>
      <el-table-column
        v-if="option&&option.btns.length>0"
        align="center"
        v-bind="option.props"
        :width="option.width"
        :label="option.label||option.name||'操作'"
      >
        <template #default="scope">
					<span :key="index" v-for="(btn,index) in option.btns">
						<el-button
              :key="index"
              v-if="showFunction(scope.row,btn)"
              class="optionBtn"
              type="text"
              v-bind="btn.props"
              :style="btn.style"
              @click="btn.handler&&btn.handler(scope.row,tableData)"
            >{{ btn.name }}</el-button>
					</span>
        </template>
      </el-table-column>
    </el-table>
    <div class="sel-page" v-if="usePage">
      <el-pagination
        :size="usePageConfig.size"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :background="usePageConfig?.background"
        :current-page="usePage"
        :page-sizes="usePageConfig?.pageSizes"
        :page-size="usePageSize"
        :layout="usePageConfig?.layout"
        :total="usePageConfig?.total"
      ></el-pagination>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { randomCode, transformToTree, treeToTransForm, isFunction } from 'utils'
import { computed, nextTick, onMounted, provide, ref, watch } from 'vue'
import Columns from './columns.vue'
import { cloneDeep, set } from 'lodash-es'
import { FI } from '../../Form/types'
import { useVModel } from '@vueuse/core'

defineOptions({
  name: 'SGroupTable'
})
const emits = defineEmits([
  'update:data',
  'changeRow',
  'selectChange',
  'sortChange',
  'rowClick',
  'filterChange',
  'changePageNumber',
  'changePageSize',
  'handleSelectionChange',
  'update:selection'
])

interface ElTableProps {
  rowKey: string,
  treeProps?: any
}

interface MapperType {
  label: string
  value: string
  children: string
}

interface TableBtn {
  label: string
  props?: any
  handler?: Function
  show?: boolean | Function | string
}

interface TableOption {
  name?: string
  width: string | number
  props?: any
  btns: Array<TableBtn>
}

interface PageConfig {
  size: string
  page: number
  total: number
  pageSize: number
  pageSizes?: Array<number>
  layout: string
  background: boolean
}

interface Style {
  [x: string]: string
}

interface Props {
  data: Array<any>
  id: string | number
  /**
   * 需要合并行的字段数组
   */
  spanKey: Array<string>
  showHeader: boolean
  edit: boolean
  props: ElTableProps
  headerCellStyle: Style
  mapper: MapperType
  columns: Array<FI>
  indexFixed: string
  /**
   * 边框
   */
  border: boolean
  /**
   * 自动滚动
   */
  autoScroll: boolean
  /**
   * 序号
   */
  index: boolean | string
  /**
   * 序号处理函数
   */
  calculator: Function
  /**
   * 选择框
   */
  select: boolean
  selection: Array<any>
  selectKey: string
  /**
   * 操作列配置
   */
  option: TableOption
  height: string
  /**
   * 可选值 'large' | 'small' | 'mini' | 'medium'
   */
  size: 'large' | 'small' | 'mini' | 'medium'
  /**
   * 分页配置
   */
  pageConfig: PageConfig
}

const cProps = withDefaults(defineProps<Props>(), {
  data: () => ([]),
  spanKey: () => ([]),
  showHeader: true,
  edit: false,
  props: () => ({
    rowKey: '_rowKey'
  }),
  headerCellStyle: () => ({
    background: '#F6F6F7',
    color: '#8689a3',
    fontSize: '14px'
  }),
  mapper: () => ({
    label: 'label',
    value: 'value',
    children: 'children'
  }),
  columns: () => ([]),
  border: true,
  index: false,
  select: false,
  option: () => ({
    btns: []
  }),
  size: 'small',
  autoScroll: false,
  selection: () => ([]),
  selectKey: '_rowKey',
  pageConfig: () => ({
    page: 0,
    total: 0,
    pageSize: 10,
    size: 'small',
    layout: 'total,prev, pager, next',
    background: true
  })
})

const useColumns = computed(() => {
  return cProps.columns.map((item: any) => {
    if (!item.code) {
      item.code = 'ele' + randomCode(18)
    }
    return item
  })
})

const tableData = computed({
  get () {
    const uData = (cProps.data || [])?.map((item, index) => {
      item._rowKey = index
      return item
    }) || []
    return cloneDeep(uData)
  },
  set (val) {
    emits('update:data', val)
  }
})

const usePage = computed({
  get () {
    return cProps.pageConfig?.page || 0
  },
  set (pageNumber) {
    emits('changePageNumber', pageNumber)
  }
})
const usePageSize = computed({
  get () {
    return cProps.pageConfig?.pageSize
  },
  set (pageSize) {
    emits('changePageSize', pageSize)
  }
})

const usePageConfig = computed(() => {
  return Object.assign({
    page: 0,
    total: 0,
    pageSize: 10,
    layout: 'total,prev, pager, next',
    background: true
  }, cProps.pageConfig)
})

const table = ref(null)
let scrollTimer: any = null
const activeScroll = ref(true)
const autoScrollRef = computed<boolean>(() => cProps.autoScroll === true)

watch(() => cProps.selection, (val) => {
  doSelection()
}, {
  deep: true
})

watch(() => tableData.value, (val) => {
  doSelection()
}, {
  deep: true
})

function doSelection () {
  tableData.value.forEach(item => {
    if (cProps.selection?.includes(item[cProps.selectKey])) {
      table.value.toggleRowSelection(item, true)
    } else {
      table.value.toggleRowSelection(item, false)
    }
  })
}

defineExpose({
  table,
  tableData
})

async function autoScroll (stop: boolean) {
  if (stop) {
    clearInterval(scrollTimer)
    scrollTimer = null
    return
  }
  await nextTick()
  if (!table.value) return
  const divData = table.value.$refs.bodyWrapper?.querySelector('.el-scrollbar__wrap')
  const active = ref(-0.2)
  const ing = () => {
    if (activeScroll.value) {
      active.value += 0.2
    }
    if (activeScroll.value && Math.abs(divData.clientHeight + active.value - divData.scrollHeight) < 1 && active.value >
      0) {
      setTimeout(() => {
        active.value = 0.2
      }, 500)
    }
    if (activeScroll.value) {
      divData.scrollTop = active.value
    }
    requestAnimationFrame(ing)
  }
  ing()
  divData.onmouseenter = function () {
    activeScroll.value = false
  }
  divData.onmouseleave = function () {
    activeScroll.value = true
  }
}

/** control */
onMounted(() => {
  autoScroll(!autoScrollRef.value)
})

/*
 * 表格数据改变
 * */
function changeValue (value, scope, valueKey, trigger) {
  const row = scope.row
  const tableDataC = cloneDeep(tableData.value)
  const key = cProps.props?.rowKey || Object.keys(row)[0]
  const platData = treeToTransForm(tableDataC, cProps.props?.treeProps)
  const item = platData.find(item => item[key] === row[key])
  set(item, valueKey, value)
  const newTableData = transformToTree(platData, cProps.props?.treeProps)
  emits('changeRow', item)
  tableData.value = newTableData
}

/*
 * 类型为slot的列
 * */
const slotColumns = computed(() => {
  const pColumns = useColumns.value.filter((item: any) => item.type === 'slot')
  const children = []
  useColumns.value.filter((item: any) => item.type === 'group').map((item: any) => {
    item?.children.filter((item: any) => item.type === 'slot').map(child => {
      children.push(child)
    })
  })
  return [...pColumns, ...children]
})
/*
 * 注入改变数值的函数，用于处理递归列情况下的数据
 * */
provide('changeValue', changeValue)
/*
 * 计算跨行的列数
 * */
const spanCol = computed(() => {
  const indexArr = []
  const select = cProps.select
  const index = cProps.index
  cProps.spanKey.map(key => {
    const i = cProps.columns.findIndex((item: any) => item.key === key)
    if ((index && !select) || (!index && select)) {
      indexArr.push(i + 1)
    }
    if (index && select) {
      indexArr.push(i + 2)
    }
    if (!index && !select) {
      indexArr.push(i)
    }
  })
  return indexArr
})

/*
 * 跨行的列数根据数据转换为跟数据相关的合并对象
 * */
function calcSpanObj () {
  const spanObj = []
  const spanKey = cProps.spanKey
  spanKey.map((key, index) => {
    const data = cloneDeep(tableData.value)
    let group = []
    data.map(item => {
      if (index >= 1) {
        let bkey = key
        let bvalue = item[key]
        for (let i = 0; i < index; i++) {
          bkey += spanKey[index - 1 - i]
          bvalue += item[spanKey[index - 1 - i]]
        }
        item[bkey] = bvalue
        group.push(bvalue)
      } else {
        group.push(item[key])
      }
    })
    const arr = []
    group = [...new Set(group)]
    group.map(item => {
      const obj: any = {}
      obj.SelfIdKey = item
      if (index >= 1) {
        let bkey = key
        for (let i = 0; i < index; i++) {
          bkey += spanKey[index - 1 - i]
        }
        obj[bkey] = 0
      } else {
        obj[key] = 0
      }
      arr.push(obj)
    })
    const treeAll = cloneDeep(data).concat([...new Set(arr)])
    let bkey = key
    for (let i = 0; i < index; i++) {
      bkey += spanKey[index - 1 - i]
    }
    const treeData = transformToTree(cloneDeep(treeAll), {
      children: 'children',
      idKey: 'SelfIdKey',
      pIdKey: bkey,
      label: 'name'
    })
    spanObj.push(treeData)
  })
  return spanObj
}

/*
 * element的跨行跨列函数
 * */
function objectSpanMethod ({ rowIndex, columnIndex }) {
  const spanObj = calcSpanObj()
  const rowIndexArr = []
  const lengthArr = []
  let result = {}
  const index = spanCol.value.indexOf(columnIndex)
  if (index >= 0) {
    spanObj[index].map((item, index) => {
      if (item.children) {
        lengthArr.push(item.children.length)
      }
    })
    let sum = 0
    lengthArr.map((length, index) => {
      rowIndexArr.push(sum)
      sum = sum + length
    })
    if (rowIndexArr.indexOf(rowIndex) >= 0) {
      result = {
        rowspan: lengthArr[rowIndexArr.indexOf(rowIndex)],
        colspan: 1
      }
    } else {
      result = {
        rowspan: 0,
        colspan: 0
      }
    }
    if (columnIndex === spanCol.value[index]) {
      return result
    }
  }
}

/*
 * 分页计算序号
 * */
function calcIndex (val) {
  if (cProps.calculator) {
    return cProps.calculator(val)
  } else {
    return (((cProps.pageConfig?.page || 1) - 1) * (cProps.pageConfig?.pageSize || 10)) + val + 1
  }
}

/*
 * dom节点显示隐藏函数
 * */
function showFunction (value, item) {
  if (isFunction(item.show)) {
    return item.show(value, item)
  } else if (item.show === undefined) {
    return true
  } else {
    return !!item.show
  }
}

function handleSizeChange (pageSize) {
  usePageSize.value = pageSize
}

function handleCurrentChange (pageNumber) {
  usePage.value = pageNumber
}

function handleSelectionChange (selection) {
  emits('selectChange', selection)
}

function sortChange (data) {
  emits('sortChange', data)
}

function rowClick (data) {
  emits('rowClick', data)
}

function filterChange (data) {
  emits('filterChange', data)
}
</script>

<style lang="scss">
.sel-table {
  .sel-page {
    display: flex;
    justify-content: flex-end;
    margin-top: 20px;

    .el-pager {
      padding-left: 0;
      margin-top: 0;
      margin-bottom: 0;
    }
  }

  .optionBtn {
    margin: 0 4px;
  }
}
</style>
