<template>
    <div v-if="isShow"
         :style="{width: props.width === null ? '100%' :props.width+'px' }">
        <div class="xc-table_v2"
             ref="tableRef">
            <div class="header"
                 ref="headerRef">
                <template v-for="(item,index) in props.columns">
                    <div class="th"
                         @click="headerClick(item)"
                         :style="item.fixedStyle">
                        <template v-if="item.type">
                            <component :is="typeEnum[item.type].header()"/>
                        </template>
                        {{ item.name }}
                    </div>
                </template>
            </div>
            <div v-bind="containerProps"
                 class="xc-body_v2"
                 :style="{height : props.height + 'px'}">
                <div v-bind="wrapperProps"
                     ref="dataRef">
                    <div v-for="item in list"
                         :key="props.rowKey === null ?  item.index : props.rowKey"
                         @contextmenu.prevent.stop="contextmenu(item.data,item.index,$event)"
                         :style="rowStyle(item.data, item.index)"
                         :class="rowClass(item.data, item.index)"
                         @dblclick.stop="dbRowClick($event,item.data,  item.index)"
                         @click="rowClick(item.data, $event, item.index)"
                         class="row">
                        <template v-for="(he,heindex) in props.columns">
                            <div :title="item.data[he.code]"
                                 :style="he.fixedStyle"
                                 class="cell">
                                <template v-if="he.type">
                                    <component :is="typeEnum[he.type].body(item)"/>
                                </template>
                                <template v-else-if="he.cellRenderer">
                                    <component
                                            :is="he.cellRenderer({data: item.data,index:item.index,cellData: item.data[he.code]})"/>
                                </template>
                                <template v-else>
                                    {{ item.data[he.code] }}
                                </template>
                            </div>
                        </template>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup name='XcTableV2' lang="tsx">
import {useIntersectionObserver, useVirtualList} from "@vueuse/core";
import {computed, nextTick, onMounted, ref, Ref, h} from 'vue'
import {ElCheckbox} from "element-plus";

const props = defineProps({
    data: Array,
    columns: {
        type: Array,
        default: null
    },
    height: {
        type: Number,
        default: 300
    },
    rowStyle: {
        type: Function,
        default: null
    },
    rowClass: {
        type: Function,
        default: null
    },
    rowKey: {
        type: String,
        default: null
    },
    width: {
        type: Number,
        default: null
    },
})

const tempData = computed(() => {
    return props.data
})

const emits = defineEmits(['rowClick', 'dbRowClick', 'contextmenu'])

const {list, containerProps, wrapperProps, scrollTo} = useVirtualList(
    tempData,
    {
        itemHeight: 23,
    },
)

const headerClick = (item) => {
    if (item.headerFunc) {
        item.headerFunc()
    }
}

const rowStyle = (data, index) => {
    if (props.rowStyle === null) return
    return props.rowStyle(data, index)
}

const rowClass = (data, index) => {
    if (props.rowClass === null) return
    return props.rowClass(data, index)
}

let timer = null
const rowClick = (item, event, index) => {
    if (timer) {
        clearTimeout(timer)
    }
    timer = setTimeout(() => {
        emits('rowClick', item, index, event)
    }, 300)
}

const dbRowClick = (event, data, index) => {
    if (timer) {
        clearTimeout(timer)
    }
    emits('dbRowClick', {event, data, index})
}

const contextmenu = (data, index, event) => {
    emits('contextmenu', data, index, event)
}

const tableRef: Ref<HTMLElement | null> = ref(null)
const dataRef: Ref<HTMLElement | null> = ref(null)
const headerRef: Ref<HTMLElement | null> = ref(null)


const typeEnum = {
    "selected": {
        header: () => {
            return (<ElCheckbox modelValue={selectedAll.value}
                                indeterminate={intermediate.value}
                                onClick={(e) => {
                                    e.stopImmediatePropagation();
                                    e.preventDefault();
                                    selectedAll.value = !selectedAll.value
                                    clickSelectedAll(selectedAll.value)
                                }}
            />)
        },
        body: ({data, index}) => {
            return h(ElCheckbox, {
                modelValue: data.$selected,
                key: 'table-checkbox' + index,
                onClick: (e) => {
                    data.$selected = !data.$selected
                    clickSelected(e, data.$selected, data, index)
                }
            }, null)
        }
    },
    "index": {
        header: () => (<span>排序</span>),
        body: ({index}) => (
            <span>{index + 1}</span>
        )
    }
}

const selectData = ref({})
const intermediate = ref(false)
const selectedAll = ref(false)

const selectedRow = (val, data, index) => {
    if (val) {
        selectData.value[index] = data
    } else {
        delete selectData.value[index]
    }
    data.$selected = val
    let length = Object.keys(selectData.value).length
    intermediate.value = length > 0
    selectedAll.value = props.data.length === length
    if (selectedAll.value) {
        intermediate.value = false
    }
}

const clickSelected = (e: Event, val, data, index) => {
    if (e !== null) {
        e.stopImmediatePropagation();
        e.preventDefault();
    }
    selectedRow(val, data, index)
}


const clickSelectedAll = (val: boolean) => {
    for (let i = 0, len = props.data.length; i < len; i++) {
        let item = props.data[i]
        selectedRow(val, item, i)
    }
}

const getSelectedData = () => {
    let temp = []
    for (const key in selectData.value) {
        temp.push(selectData.value[key])
    }
    return temp
}

const scrollToByData = (data) => {
    let temp = JSON.stringify(data)
    let index = -1
    for (let i = 0, len = tempData.value.length; i < len; i++) {
        let item = JSON.stringify(tempData.value[i])
        if (item === temp) {
            index = i
            break;
        }
    }
    if (index > 0) {
        scrollTo(index)
    }
}

const scrollToByKey = (key) => {
    if (props.rowKey === null) {
        throw new Error('请先选择一个 key')
    }
    let index = -1
    for (let i = 0, len = tempData.value.length; i < len; i++) {
        if (tempData.value[i][props.rowKey] == key) {
            index = i
            break;
        }
    }
    if (index > 0) {
        scrollTo(index - 2)
    }
    return index
}

const fixedStyle = (item, index) => {
    let str = {width: item.width + 'px'}
    if (item.fixed) {
        let px = 0
        if (item.fixed === 'right') {
            for (let i = index + 1, len = props.columns.length; i < len; i++) {
                let item = props.columns[i]
                px += item.width
            }
        }
        str['position'] = 'sticky!important';
        str[item.fixed] = px + 'px'
        str['z-index'] = 2
        str['background'] = '#fff'
    }
    return str
}

const clearSelected = () => {
    clickSelectedAll(false)
}

let tempScrollTop = 0
const isShow = ref(false)

const fixedRight = () => {
    let temp = null
    if (props.columns === null) {
        return
    }
    props.columns.forEach((item, index) => {
        if (temp === null && item.fixed && item.fixed === 'right') {
            temp = item
        }
        item.fixedStyle = fixedStyle(item, index);
    });
    temp.fixedStyle['border-left'] = '1px solid #ebeef5'
}

onMounted(async () => {
    fixedRight()
    isShow.value = true
    await nextTick()
    useIntersectionObserver(
        tableRef,
        ([{isIntersecting}], observerElement) => {
            if (isIntersecting) {
                containerProps.ref.value.scrollTop = tempScrollTop
            }
        },
    )
    containerProps.ref.value.addEventListener('scroll', () => {
        tempScrollTop = containerProps.ref.value.scrollTop
        headerRef.value.scrollLeft = containerProps.ref.value.scrollLeft
    })
})

defineExpose({
    selectedRow,
    getSelectedData,
    scrollToByData,
    scrollToByKey,
    scrollTo,
    clearSelected
})


</script>

<style lang="scss">

.xc-table_v2 {
  overflow-x: hidden;
  color: black;
  background-color: white;

  .header {
    height: max-content;
    display: flex;
    align-items: center;
    border-bottom: 1px solid #ebeef5;
    overflow-x: hidden;

    .th {
      box-sizing: border-box;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      flex-grow: 0;
      flex-shrink: 0
    }
  }

  .xc-body_v2 {

    &::-webkit-scrollbar {
      height: 6px;
    }

    .row {
      height: 22px;
      display: flex;
      align-items: center;
      border-bottom: 1px solid #ebeef5;

      &:hover {
        background-color: #e0ecff;
      }

    }

    .cell {
      overflow: hidden;
      display: flex;
      align-items: center;
      text-overflow: ellipsis;
      white-space: nowrap;
      height: 100%;
      flex-grow: 0;
      flex-shrink: 0;
      background-color: inherit;
      color: inherit;
    }

  }
}


</style>
