<template>
    <div class="tableListBtn info-table">
        <template v-if="!options.isHideSet">
            <SettingHeaders
                ref="settingHeadersRef"
                :columns="columns"
                :options="options"
                @setColumns="setColumns"
            />
        </template>
        <el-table
            ref="multipleTable"
            :key="updateKeys"
            v-loading="typeof options.loading === 'function' ? options.loading() : options.loading?.isLoading"
            :data="handleList.list"
            :empty-text="$t('dataIsEmpty')"
            :stripe="options.stripe === undefined ? true : options.stripe"
            :border="options.border === undefined ? true : false"
            :scrollbar-always-on="options.hasScrollBar"
            :show-header="options.showHeader"
            :header-cell-style="headerStyle"
            :highlight-current-row="options.chooseOne"
            :height="tableHeight"
            :max-height="options.maxHeight || '100%'"
            :size="options.size"
            :row-class-name="options.getRowClassName || getRowClassName"
            :row-style="getRowStyle"
            :row-key="options.rowKey"
            :default-expand-all="options.expandAll"
            :expand-row-keys="options.expandRowKeys"
            :tree-props="options.treeProps"
            :span-method="spanMethod"
            @expand-change="expandChange"
            @current-change="handleCurrentChange"
            @row-dblclick="handleRowDblclick"
            @row-click="handleOneRowClick"
            @sort-change="handleSortChange"
            @select-all="handleSelectAll"
            @select="handleSelectionChange"
            @header-dragend="hederWidthChange"
        >
            <el-table-column
                v-if="options.isDrop"
                width="40px"
            >
                <template #default>
                    <div class="handle">
                        <Sort style="width: 14px; height: 14px; " />
                    </div>
                </template>
            </el-table-column>
            <el-table-column
                v-if="options.chooseOne"
                width="46px"
                :resizable="false"
                class-name="column--radio"
            >
                <template #default="props">
                    <el-radio
                        v-model="currentRadio"
                        :label="props.row[options.rowKey]"
                        @click.stop="handleRowClick($event, props.row)"
                    />
                </template>
            </el-table-column>
            <!-- 多选'列 -->
            <el-table-column
                v-if="options.multiple"
                type="selection"
                align="center"
                :reserve-selection="!!options.rowKey && options.reserveSelection"
                :selectable="options.selectable"
            />
            <!-- 序号'列 -->
            <el-table-column
                v-if="options.index"
                :label="$t('tableIndex')"
                align="center"
                :width="lang === 'zh' ? 62 : 68"
                type="index"
            >
                <template #default="scope">
                    <span>{{
                            ((options.pageNum?.() || 1) - 1) * (options.pageSize?.() || 20) + scope.$index + 1
                        }}</span>
                </template>
            </el-table-column>
            <!-- 展开行'列 -->
            <el-table-column
                v-if="options.expand"
                type="expand"
            >
                <template v-slot:default="props">
                    <slot
                        name="expandSlot"
                        :expandProps="props.row"
                    />
                </template>
            </el-table-column>
            <!-- 配置'列 -->
            <template
                v-for="(column, index) in showColumns.list"
                :key="column.prop"
            >
                <el-table-column
                    v-if="typeof column.show === 'function' ? column.show() : true"
                    :sortable="column.sortable ? column.sortable : false"
                    :prop="column.prop"
                    :label="options.i18n === true ? column.label : $t(column.label)"
                    :align="column.align"
                    :width="column.width"
                    :min-width="headerWidth(column, 'minWidth')"
                    :show-overflow-tooltip="options.tooltip !== false"
                    :label-class-name="column.highlight ? 'highlight handleColumn' : 'handleColumn'"
                    :fixed="column.fixed"
                    :resizable="(props.operates && props.operates.list.length > 0) || (index !== showColumns.list.length - 1)"
                >
                    <!-- 表头 筛选 -->
                    <template
                        v-slot:header="scope"
                    >
                        <expand-dom
                            v-if="column.headerRender"
                            :render="column.headerRender"
                            :column="column"
                        />
                        <el-tooltip
                            v-if="!!column.tips"
                            effect="dark"
                            :content="$t(column.tips)"
                            placement="top"
                        >
                            <i class="el-icon-warning-outline" style="height: 16px;"></i>
                        </el-tooltip>
                        {{ scope.column.label }}
                        <ColumnFilter
                            v-if="!!column.filterMethod"
                            :tableOptions="options"
                            :column="column"
                            :parameter="parameter"
                        />
                    </template>
                    <!-- 内容渲染 -->
                    <template v-slot:default="scope">
                        <!-- 二级表头 -->
                        <template v-if="column.columns">
                            <el-table-column
                                v-for="(subColumn, secIndex) in column.columns"
                                :key="'sec' + secIndex"
                                :prop="subColumn?.prop"
                                :label="options.i18n === true ? `${subColumn.label}` : $t(subColumn.label)"
                                :min-width="subColumn.minWidth"
                                :show-overflow-tooltip="options.tooltip !== false"
                            >
                                <template v-slot:default="scope">
                                    <template v-if="subColumn.columns">
                                        <el-table-column
                                            v-for="(threeColumn, secIndex) in subColumn.columns"
                                            :key="'sec' + secIndex"
                                            :prop="threeColumn?.prop"
                                            :label="options.i18n === true ? `${threeColumn.label}` : $t(threeColumn.label)"
                                            :min-width="threeColumn.minWidth"
                                            :show-overflow-tooltip="options.tooltip !== false"
                                        >
                                            <template v-slot:default="scope">
                                                <span
                                                    v-if="threeColumn.formatter"
                                                    v-html="threeColumn.formatter(scope.row)"
                                                />
                                                <span
                                                    v-else
                                                    v-html="getProp(scope.row, threeColumn.prop)"
                                                />
                                            </template>
                                        </el-table-column>
                                    </template>
                                    <span
                                        v-if="subColumn.formatter"
                                        v-html="subColumn.formatter(scope.row)"
                                    />
                                    <span
                                        v-else
                                        v-html="getProp(scope.row, subColumn.prop)"
                                    />
                                </template>
                            </el-table-column>
                        </template>
                        <!-- formatter -->
                        <template v-if="column.formatter">
                            <span
                                @click="column.method?.(scope.row, column)"
                                v-html="column.formatter(scope.row, column, scope.$index)"
                            />
                        </template>
                        <!-- render -->
                        <template v-else-if="column.render">
                            <expand-dom
                                :column="column"
                                :row="scope.row"
                                :render="column.render"
                            />
                        </template>
                        <template v-else-if="column.templateConfig">
                            <component
                                v-if="column.formShow ? column.formShow(scope.row) : true"
                                :is="stepComponents[column.formType]"
                                :row="column.templateConfig(scope.row, column)"
                                :modelData="scope.row"
                            ></component>
                        </template>
                        <!-- 可编辑列-->
                        <template
                            v-else-if="column.enableEdit?.(scope.row, scope.$index)"
                        >
                            <el-input
                                v-model="scope.row[column.prop]"
                                size="small"
                                :placeholder="$t('pleaseEnterContent')"
                            />
                        </template>
                        <!-- 普通列 -->
                        <template v-else>
                            <span :id="scope.row.id">{{ getProp(scope.row, column.prop) }}</span>
                        </template>
                    </template>
                </el-table-column>
            </template>
            <!-- 按钮操作组 -->
            <el-table-column
                v-if="!isHiddenOperates"
                :label="$t('operation')"
                :width="`${initWidth}px`"
                :fixed="operates.fixed"
                :resizable="false"
                align="left"
            >
                <template v-slot="scope">
                    <span class="operateBox">
                        <!-- 按钮-->
                        <template v-for="(btn, index) in showOperations">
                            <span
                                v-if="(!btn.show || (typeof btn.show === 'function' ? btn.show(scope.row, scope.$index) : btn.show)) && filterButtonHandler(btn.code)"
                                :key="index"
                                class="operateBtn"
                            >
                                <el-tooltip
                                    effect="dark"
                                    placement="top"
                                    :disabled="!btn.circle"
                                    :content="typeof btn.label === 'function' ?
                                                    $t(btn.label(scope.row)) :
                                                    $t(btn.label)"
                                >
                                    <el-button
                                        size="small"
                                        :type="btn.type === 'text' ? 'primary' : btn.type || 'primary'"
                                        :plain="btn.plain"
                                        :icon="btn.icon"
                                        :circle="btn.icon ? btn.circle : false"
                                        :test-cy="btn.code"
                                        :loading="typeof btn.loading === 'function' ? btn.loading(scope.$index) : false"
                                        :disabled="typeof btn.disabled === 'function' ? btn.disabled(scope.row, scope.$index) : btn.disabled"
                                        :text="btn.text !== undefined ? btn.text : true"
                                        @click="btn.method(scope.$index, scope.row)"
                                    >
                                        <template v-if="btn.label && !btn.circle">
                                            {{
                                                typeof btn.label === 'function' ?
                                                    $t(btn.label(scope.row)) :
                                                    $t(btn.label)
                                            }}
                                        </template>
                                    </el-button>
                                </el-tooltip>
                            </span>
                        </template>
                        <!-- showNumber(非负整数) 剩余显示到更多 -->
                        <template
                            v-if="typeof operates.showNumber === 'number' && someButtonHandler(props.operates.list) - operates.showNumber > 0"
                        >
                            <info-dropdown
                                :row="moreOperations(scope.$index, scope.row)"
                            />
                        </template>
                    </span>

                </template>
            </el-table-column>
        </el-table>
    </div>
</template>
<script lang="ts" setup>
import infoDropdown from '../infoDropdown/index.vue'
import ColumnFilter from './ColumnFilter2.vue'
import SettingHeaders from './SettingHeaders.vue'
import {
    OptionsInterface,
    ColumnsInterface,
    ListInterface,
    operatesList,
    OperatesInterface,
    Options
} from './model/table'
import { ObjectInterface } from '../../script/index'
// import type { emitType } from './model/emit'
import componentPath from './formComponents'
import { filterButtonHandler, someButtonHandler } from '../../script/util'
import { MoreFilled, Sort  } from '@element-plus/icons-vue'
import { ref, reactive, computed, watch, PropType, h, onMounted, onUnmounted, nextTick, ComputedRef } from 'vue'
import { useSortable } from './sortable'

const props = defineProps({
    list: {
        type: Array as PropType<ListInterface[]>,
        default: () => [],
        required: true
    },
    columns: {
        type: Array as PropType<ColumnsInterface[]>,
        default: () => [],
        required: true
    },
    spanMethod: {
        type: Function,
        default: () => {}
    },
    options: {
        type: Object as PropType<OptionsInterface>,
        default: () => {
        }
    },
    operates: {
        type: Object as PropType<OperatesInterface>,
        default: () => {
        }
    },
    parameter: {
        type: Object,
        default: () => {}
    }
})
const emits = defineEmits(['handleCurrentChange', 'scrollLoad', 'scrollLoadAllList', 'dropEnd', 'handleRowClick', 'handleOneRowClick', 'handleRowDblclick', 'handleSelectionChange', 'handleSortChange', 'expandChange'])
const stepComponents = componentPath
const lang = localStorage.getItem('language') || 'zh'

const options: ComputedRef<OptionsInterface> = computed(() => {
    const defaultOptions = new Options(props.options)
    return  { ...defaultOptions, ...props.options }
})

const tableHeight = computed(() => {
    return typeof options.value.height === 'function' ? options.value.height() : (options.value.height || '100%')
})

const currentRadio = ref('')
const settingHeadersRef = ref()
const updateKey = ref(0)
const updateKeys = ref(0)
const initWidth = ref(94)
const showColumns = reactive<ListInterface>({
    list: []
})
const handleList = reactive<ListInterface>({ // 表格数据
    list: []
})

showColumns.list = props.columns || []
const { createColumnSort, createRowSort, destroySort } = useSortable(props, emits, options, (args) => {
    setColumnsGroup(args)
})

/* 类名与样式 */
const headerStyle = computed(() => {
    return {
        // background: '#F1F2F5',
        ...options.value.headerStyle
    }
})
const headerWidth = (column, type) => {
    let label = column.label
    let chartWidth = 16
    if (!options.value.i18n) {
        label = $t(column.label)
        chartWidth = lang === 'zh' ? 16 : 10
    }
    let filterWidth = column.type ? 20 : 0
    let sortWidth = column.sortable ? 24 : 0
    let width = label.length * chartWidth + 32 + filterWidth + sortWidth
    if (column[type] && width < column[type]) {
        width = column[type]
    }
    return width
}
const hederWidthChange = (newWidth, oldWidth, column) => {
    if (newWidth < column.minWidth) {
        column.width = column.minWidth
    }
    nextTick(createColumnSort)
}
const getRowClassName = (data: ObjectInterface): string => {
    return data.row.children?.length === 0 ? 'row-expand-cover' : ''
}
const getRowStyle = (data: ObjectInterface): any => {
    if (options.value.getRowStyle) {
        return options.value?.getRowStyle({ row: data.row, rowIndex: data.rowIndex })
    }
}
const partition = ref(false) // 是否是手动分割滚动加载
/* 选中与点击 */
const handleRowClick = (e: ObjectInterface, val: ObjectInterface): void => {
    if (e.target.tagName === 'INPUT') return
    currentRadio.value = val[options.value.rowKey]
    emits('handleRowClick', val)
}
const handleRowDblclick = (val: ObjectInterface): void => {
    emits('handleRowDblclick', val)
}
// 某一行被点击
const handleOneRowClick = (row: ObjectInterface, column, event): void => {
    emits('handleOneRowClick', row, column, event)
}
// 当前行变化
const handleCurrentChange = (val: ObjectInterface): void => {
    emits('handleCurrentChange', val)
}
/* 操作栏相关逻辑 */
const isHiddenOperates = computed(() => {
   return !props.operates || (typeof props.operates.isHiddenOperates === 'function' ? props.operates.isHiddenOperates() : props.operates.isHiddenOperates)
})
const showOperations = computed((): operatesList[] => {
    return props.operates.showNumber === 0 ? [] : props.operates.list.slice(0, props.operates.showNumber as number || props.operates.list.length)
})
const moreOperations = computed(() => {
    return function (index: number, row: ObjectInterface) {
        const start: number | boolean | undefined = props.operates.showNumber
        let more: operatesList[] = props.operates.list
            .slice(start as number)
            .filter((item) => {
                return item.show ? item.show(row) : true
            })
        let dropdown = more.map((item, i) => {
            return {
                command: i,
                name:
                    ((typeof item.label === 'function')
                        ? item.label(row)
                        : item.label),
                icon: item.icon,
                disabled: () => (typeof item.disabled === 'function') ? item.disabled(row) : item.disabled,
                method: item.method,
                code: item.code
            }
        }) as any[]
        return {
            dropdown: () => dropdown,
            method: (command: string) => {
                dropdown[command].method(index, row)
            },
            ...moreConfig
        }
    }
})
const moreConfig = reactive({
    title: () => 'more',
    link: true,
    icon: MoreFilled,
    size: 'small',
    i18n: props.operates?.i18n || true
})
/* 选择行相关逻辑 */
const multipleTable = ref<any>(null)
// 切换选择
const toggleSelection = (rows: ObjectInterface[]): void => {
    if (rows.length) {
        nextTick(() => {
            rows.forEach((row) => {
                if (options.value.chooseOne) {
                    // multipleTable.value.setCurrentRow(row)
                    currentRadio.value = row[options.value.rowKey]
                } else {
                    multipleTable.value.toggleRowSelection(row, true)
                }
            })
        })
    } else {
        multipleTable.value.clearSelection()
    }
}
// 全选
const handleSelectAll = (val: ObjectInterface[]): void => {
    // let selection: ObjectInterface[] =
    //     val.length === 0
    //         ? val
    //         : options.value.selectable
    //             ? props.list.filter((row, index) => {
    //                 return options.value.selectable!(row, index)
    //             })
    //             : props.list
    let allSelect = multipleTable.value?.getSelectionRows() || [];
    let selection = val.length === 0 ? val : allSelect
    // 全选还是全不选
    // val为空 或val不包含可操作的`allow.length === 0` 则为全不选
    // val不为空 且包含可操作的 则为全选
    //  全选时 当不可操作的默认为选中状态`fixed.length > 0`直接返回list 否则从list过滤(可操作的)
    if (options.value.selectable) {
        const allow = val.filter(options.value.selectable) // 可操作的
        const fixed = val.filter((row:object, index:number) => !options.value.selectable?.(row, index)) // 不可操作的
        selection = allow.length === 0 ? [] : fixed.length > 0 ? allSelect : allSelect.filter(options.value.selectable)
    }
    toggleSelection(selection)
    emits('handleSelectionChange', selection)
}
// 多行选中
const handleSelectionChange = (val: ObjectInterface[]): void => {
    emits('handleSelectionChange', val)
}
// 重置选中项
const resetSelection = (): void => {
    multipleTable.value.clearSelection()
}
/* 数据处理 */
const getProp: any = (rowData: object, prop: string) => {
    try {
        return rowData[prop]
    } catch (err) {
        // eslint-disable-next-line no-console
        console.error(err)
    }
}
/* 排序 */
const handleSortChange = (val: ObjectInterface): void => {
    emits('handleSortChange', val)
}
/* 展开收起 */
const expandChange = (val: ObjectInterface): void => {
    emits('expandChange', val)
}

/* 设置表头列（确定） */
let headers: string[] = reactive([])
const setColumns = (val: string[]): void => {
    headers.splice(0)
    headers.push(...val)
}

const propArrHandler = () => showColumns.list.filter((item: ObjectInterface) => {
    return typeof item.show === 'function' ? item.show() : true
}).map((column: ObjectInterface) => column.prop)

// 拖拽排序
const setColumnsGroup = ({newIndex, oldIndex}) => { // showColumn.list中的索引
    const currentRow = showColumns.list.splice(oldIndex, 1)[0] // 取出当前行
    showColumns.list.splice(newIndex, 0, currentRow) // 插入到新位置
    if (!options.value.isHideSet) {
        const preRow = showColumns.list[newIndex - 1] // 取出前一行
        const nextRow = showColumns.list[newIndex + 1] // 取出后一行
        let list = settingHeadersRef.value.columnsGroup.list
        const index = list.findIndex((item: ObjectInterface) => item.value === currentRow.prop)
        const currentRow2 = list.splice(index, 1)[0]
        if (newIndex > oldIndex) { // 向后移
           const targetIndex = list.findIndex((item: ObjectInterface) => item.value === preRow.prop)
           list.splice(targetIndex + 1, 0, currentRow2)
        } else { // 向前移
            const targetIndex = list.findIndex((item: ObjectInterface) => item.value === nextRow.prop)
            list.splice(targetIndex, 0, currentRow2)
        }
        settingHeadersRef.value.entity.prop = propArrHandler()
        settingHeadersRef.value.columnsGroup.list = list
        // 拖拽排序后 保存
        settingHeadersRef.value.saveColumns()
    }
}

let timeout = 0
const getMore = (e: any) => {
    clearTimeout(timeout)
    timeout = window.setTimeout(() => {
        emits('scrollLoad', e)
    }, 50)
}
const handleAllList = (e: any) => { // 处理全部数据(传入所有 手动分割)
    emits('scrollLoadAllList', e, handleList.list) // 全部分页数据下滚时候的回调
    if (e.target.scrollTop && ((e.target.scrollHeight - 20) <= (e.target.scrollTop + e.target.clientHeight))) {
        clearTimeout(timeout)
        timeout = window.setTimeout(() => {
            handleList.list.push(...props.list.slice(handleList.list.length, handleList.list.length + options.value.listSize))
        }, 50)
    }
}
const executeInit = () => { // 异步数据回调，是否执行加载数据
    if (!options.value.isAllList) {
        handleList.list = props.list
    }
    // 滚动加载数据时候，只有第一次加载从0开始，如果外层想要重新从0开始，比如过滤，就要将partition = true
    if (partition.value) {
        partition.value = false
        handleList.list = props.list.slice(0, options.value.listSize)
    }
}
const columnWidth = (length: number) => {
    if (length > 0 && !isHiddenOperates.value) {
        const operations = [...multipleTable.value.$el.querySelectorAll('.operateBox')]
        let maxValue = 60
        if (!operations.length) return maxValue
        operations.forEach((item) => {
            const operWidth = Array.from(item.children).reduce((acc, child:any) => acc + child.offsetWidth, 0) as number
            maxValue = Math.max(maxValue, operWidth)
        })
        let sum = maxValue + operations[0]?.children.length * 2 + 32
        if (typeof props.operates.width === 'number' && props.operates.width > sum) {
            sum = props.operates.width
        }
        initWidth.value = sum
    }
}
onMounted(() => { // 初始化
    if (typeof props.operates?.width === 'number') {
        initWidth.value = props.operates.width
    }
    const table = multipleTable.value.$el
    setTimeout(() => {
        if (options.value.isDrop) { // 行
            createRowSort()
        }
        if (options.value.isColumnDrop) { // 列
            createColumnSort()
        }
        table?.addEventListener('scroll', (e: any) => {
            if (options.value.isAllList) { // 是否传入全部数据
                handleAllList(e)
            } else {
                getMore(e)
            }
        }, true)
        if (options.value.isAllList) {
            partition.value = true
        }
        executeInit()
    }, 500)
})
onUnmounted(() => {
    destroySort()
})
const watchList = (newVal) => {
    executeInit()
    !options.value.reserveSelection && resetSelection()
    nextTick(() => {
        columnWidth(newVal.length)
    })
}
watch(props.list, (newVal) => {
    watchList(newVal)
})
watch(() => props.list, (newVal) => {
    watchList(newVal)
})
watch(() => headers, (newVal, oldValue) => {
    const list = props.columns
    const arr: any = []
    headers.forEach(item => {
        const columnsItem = list.find((c) => c.prop === item) // 业务模块中不能出现重复的prop
        columnsItem && arr.push(columnsItem)
    })
    showColumns.list = arr
}, {
    deep: true
})
watch(() => (window as any).loadButtons, () => {
    if (!props.operates) return
    props.operates.list = props.operates.list.filter(item => !item.code || filterButtonHandler(item.code))
}, {
    immediate: true,
    deep: true
})

/* 最终展示列相关逻辑 */
const expandDom = (props: ObjectInterface, ctx: ObjectInterface) => {
    const params = {
        row: ctx.attrs.row,
        column: ctx.attrs.column
    }
    return ctx.attrs.render(h, params)
}

defineExpose({
    toggleSelection,
    multipleTable,
    partition,
    currentRadio
})
</script>
<style lang="scss" scoped>
@use './index.scss';
:deep(.el-table__inner-wrapper) {
    &::before {
        display: none;
    }
    table tr:nth-last-child(1) td {
        border-bottom: none !important;
    }
}
:deep(.el-table__column-resize-proxy) {
    border-left: 1px solid var(--info-primary-color-2);
}
</style>
