<!-- eslint-disable padding-line-between-statements -->
<template>
    <div :class="containerClass" data-scrollselectors=".p-datatable-wrapper">
        <slot></slot>
        <div v-if="loading" class="p-datatable-loading-overlay p-component-overlay">
            <slot v-if="$slots.loading" name="loading"></slot>
            <template v-else>
                <component v-if="$slots.loadingicon" :is="$slots.loadingicon" class="p-datatable-loading-icon" />
                <i v-else-if="loadingIcon" :class="['p-datatable-loading-icon pi-spin', loadingIcon]" />
                <SpinnerIcon v-else spin class="p-datatable-loading-icon" />
            </template>
        </div>
        <div v-if="$slots.header" class="p-datatable-header">
            <slot name="header"></slot>
        </div>
   
        <div class="p-datatable-wrapper" > <!--:style="{ maxHeight: scrollHeight  }"-->
             <table ref="table"  :class="tableStyleClass" :style="[tableStyle]" v-bind="tableProps"> <!--, slotProps.spacerStyle -->
                        <DTTableHeader
                            :columnGroup="headerColumnGroup"
                            :columns="columns"
                            :rowGroupMode="rowGroupMode"
                            :groupRowsBy="groupRowsBy"
                            :groupRowSortField="groupRowSortField"
                            :reorderableColumns="reorderableColumns"
                            :resizableColumns="resizableColumns"
                            :allRowsSelected="allRowsSelected"
                            :empty="empty"
                            :sortMode="sortMode"
                            :sortField="d_sortField"
                            :sortOrder="d_sortOrder"
                            :multiSortMeta="d_multiSortMeta"
                            :filters="d_filters"
                            :filtersStore="filters"
                            :filterDisplay="filterDisplay"
                            :filterInputProps="filterInputProps"
                            :headerCheckboxIconTemplate="$slots.headercheckboxicon"
                            @column-click="onColumnHeaderClick($event)"
                            @column-mousedown="onColumnHeaderMouseDown($event)"
                            @filter-change="onFilterChange"
                            @filter-apply="onFilterApply"
                            @column-dragstart="onColumnHeaderDragStart($event)"
                            @column-dragover="onColumnHeaderDragOver($event)"
                            @column-dragleave="onColumnHeaderDragLeave($event)"
                            @column-drop="onColumnHeaderDrop($event)"
                            @column-resizestart="onColumnResizeStart($event)"
                            @checkbox-change="toggleRowsWithCheckbox($event)"
                        />
                        <!--:class="slotProps.styleClass" -->
                        <DTTableBody
                            ref="bodyRef"
                            :value="d_primaryData"
                            :columns="columns"
                            :empty="empty"
                            :currentRow="d_currentRow"
                            :selectionMode="selectionMode"
                            :contextMenu="contextMenu"
                            :contextMenuSelection="contextMenuSelection"
                            :rowGroupMode="rowGroupMode"
                            :groupRowsBy="groupRowsBy"
                            :expandableRowGroups="expandableRowGroups"
                            :rowClass="rowClass"
                            :rowStyle="rowStyle"
                            :compareSelectionBy="compareSelectionBy"
                            :scrollable="scrollable"
                            :expandedRowIcon="expandedRowIcon"
                            :collapsedRowIcon="collapsedRowIcon"
                            :expandedRows="expandedRows"
                            :expandedRowKeys="d_expandedRowKeys"
                            :expandedRowGroups="expandedRowGroups"
                            :disabled="disabled"
                            :readonly="readonly"
                            :templates="$slots"
                 
                            :currentRowHighlight="currentRowHighlight"
                            @rowgroup-toggle="toggleRowGroup"
                            @row-click="onRowClick($event)"
                            @row-dblclick="onRowDblClick($event)"
                            @row-rightclick="onRowRightClick($event)"
                            @row-touchend="onRowTouchEnd"
                            @row-mousedown="onRowMouseDown"
                            @row-dragstart="onRowDragStart($event)"
                            @row-dragover="onRowDragOver($event)"
                            @row-dragleave="onRowDragLeave($event)"
                            @row-dragend="onRowDragEnd($event)"
                            @row-drop="onRowDrop($event)"
                            @row-toggle="toggleRow($event)"
                            @radio-change="toggleRowWithRadio($event)"
                            @checkbox-change="toggleRowWithCheckbox($event)"
                            @cell-edit-init="onCellEditInit($event)"
                            @cell-edit-complete="onCellEditComplete($event)"
                            @cell-edit-cancel="onCellEditCancel($event)"
                            @body-keydown="onBodyKeyDown($event)"
                            @item-change="onItemChange"
                        />
                   <DTTableFooter :columnGroup="footerColumnGroup" :columns="columns"   />
              </table>
        </div>
        <div v-if="$slots.footer" class="p-datatable-footer">
            <slot name="footer"></slot>
        </div>
       
        <DTPaginator
            v-if="paginator"
            :total="d_pageParams.total"
            :size="d_pageParams.size"
            :current="d_pageParams.current"
            :pageLinkSize="pageLinkSize"
            :template="paginatorTemplate"
            :rowsPerPageOptions="rowsPerPageOptions"
            :currentPageReportTemplate="currentPageReportTemplate"
            class="p-paginator-bottom"
            @pageChange="onPageChange($event)"
        >
            <template v-if="$slots.paginatorstart" #start>
                <slot name="paginatorstart"></slot>
            </template>
            <template v-if="$slots.paginatorend" #end>
                <slot name="paginatorend"></slot>
            </template>
            <template v-if="$slots.paginatorfirstpagelinkicon" #firstpagelinkicon>
                <slot name="paginatorfirstpagelinkicon"></slot>
            </template>
            <template v-if="$slots.paginatorprevpagelinkicon" #prevpagelinkicon>
                <slot name="paginatorprevpagelinkicon"></slot>
            </template>
            <template v-if="$slots.paginatornextpagelinkicon" #nextpagelinkicon>
                <slot name="paginatornextpagelinkicon"></slot>
            </template>
            <template v-if="$slots.paginatorlastpagelinkicon" #lastpagelinkicon>
                <slot name="paginatorlastpagelinkicon"></slot>
            </template>
        </DTPaginator>
       

        <div ref="resizeHelper" class="p-column-resizer-helper" style="display: none"></div>
        <span v-if="reorderableColumns" ref="reorderIndicatorUp" class="p-datatable-reorder-indicator-up" style="position: absolute; display: none">
            <component :is="$slots.reorderindicatorupicon || 'ArrowDownIcon'" />
        </span>
        <span v-if="reorderableColumns" ref="reorderIndicatorDown" class="p-datatable-reorder-indicator-down" style="position: absolute; display: none">
            <component :is="$slots.reorderindicatordownicon || 'ArrowUpIcon'" />
        </span>
    </div>
</template>

<script>
import { FilterMatchMode, FilterOperator, FilterService } from 'primevue/api';
import ArrowDownIcon from 'primevue/icons/arrowdown';
import ArrowUpIcon from 'primevue/icons/arrowup';
import SpinnerIcon from 'primevue/icons/spinner';
import Paginator from 'primevue/paginator';
import { DomHandler, ObjectUtils, UniqueComponentId,DataHandler,RowStatus } from 'primevue/utils';
import VirtualScroller from 'primevue/virtualscroller';
import TableBody from './TableBody.vue';
import TableFooter from './TableFooter.vue';
import TableHeader from './TableHeader.vue';
import { computed } from 'vue';

export default {
    name: 'DataTable',
    emits: [
        'value-change',
        'page-change',
        'sort',
        'filter',
        'row-click',
        'row-dblclick',
        'row-focus-changed',
        'row-select-changed',
        'update:contextMenuSelection',
        'row-contextmenu',
        'row-unselect-all',
        'row-select-all',
        'select-all-change',
        'column-resize-end',
        'column-reorder',
        'row-reorder',
        'update:expandedRows',
        'row-collapse',
        'row-expand',
        'update:expandedRowGroups',
        'rowgroup-collapse',
        'rowgroup-expand',
        'update:filters',
        'state-restore',
        'state-save',
        'cell-edit-init',
        'cell-edit-complete',
        'cell-edit-cancel',
        'row-new-init',
        'item-change'
       
    ],
    props: {
        paginator: {
            type: Boolean,
            default: false
        },

        pageSize:{
            type:Number,
            default:20
        },

        paginatorTemplate: {
            type: [Object, String],
            default: 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown'
        },
        pageLinkSize: {
            type: Number,
            default: 5
        },
        rowsPerPageOptions: {
            type: Array,
            default:()=>[20,50,100,200,500,1000] // 解决直接定义为数组时报以下错误 Type of the default value for 'rowsPerPageOptions' prop must be a function 
        },

        currentPageReportTemplate: {
            type: String,
            default: ' {currentPage} / {pageCount}'
        },

        alwaysShowPaginator: {
            type: Boolean,
            default: true
        },

        loading: {
            type: Boolean,
            default: false
        },
        loadingIcon: {
            type: String,
            default: undefined
        },
        sortField: {
            type: [String, Function],
            default: null
        },
        sortOrder: {
            type: Number,
            default: null
        },
        defaultSortOrder: {
            type: Number,
            default: 1
        },
        multiSortMeta: {
            type: Array,
            default: null
        },
        sortMode: {
            type: String,
            default: 'single'
        },
        removableSort: {
            type: Boolean,
            default: false
        },
        filters: {
            type: Object,
            default: null
        },
        filterDisplay: {
            type: String,
            default: null
        },
        globalFilterFields: {
            type: Array,
            default: null
        },
        filterLocale: {
            type: String,
            default: undefined
        },
      
        selectionMode: {
            type: String,
            default: null
        },
        compareSelectionBy: {
            type: String,
            default: 'deepEquals'
        },
        metaKeySelection: {
            type: Boolean,
            default: true
        },
        contextMenu: {
            type: Boolean,
            default: false
        },
        contextMenuSelection: {
            type: Object,
            default: null
        },

        rowHover: {
            type: Boolean,
            default: false
        },

        currentRowHighlight:{
            type: Boolean,
            default: true
        },

        csvSeparator: {
            type: String,
            default: ','
        },
        exportFilename: {
            type: String,
            default: 'download'
        },
        exportFunction: {
            type: Function,
            default: null
        },
        resizableColumns: {
            type: Boolean,
            default: false
        },
        columnResizeMode: {
            type: String,
            default: 'fit'
        },
        reorderableColumns: {
            type: Boolean,
            default: false
        },
        expandedRows: {
            type: Array,
            default: null
        },
        expandedRowIcon: {
            type: String,
            default: undefined
        },
        collapsedRowIcon: {
            type: String,
            default: undefined
        },
        rowGroupMode: {
            type: String,
            default: null
        },
        groupRowsBy: {
            type: [Array, String, Function],
            default: null
        },
        expandableRowGroups: {
            type: Boolean,
            default: false
        },
        expandedRowGroups: {
            type: Array,
            default: null
        },
        stateStorage: {
            type: String,
            default: 'session'
        },
        stateKey: {
            type: String,
            default: null
        },
       
        rowClass: {
            type: null,
            default: null
        },
        rowStyle: {
            type: null,
            default: null
        },
        scrollable: {
            type: Boolean,
            default: true   /*默认改为滚动显示*/
        },
        
        scrollHeight: {
            type: String,
            default: null
        },
        
        showGridlines: {
            type: Boolean,
            default: false
        },
        stripedRows: {
            type: Boolean,
            default: false
        },
        tableStyle: {
            type: null,
            default: null
        },
        tableClass: {
            type: String,
            default: null
        },
        tableProps: {
            type: null,
            default: null
        },
        filterInputProps: {
            type: null,
            default: null
        },

        /*按回车是跳入下一行还是下一列*/
        enterToNextRow:{
            type: Boolean,
            default: false
        },
        
        disabled:{
             type:Boolean,
             default:false
         },
        readonly:{
            type:Boolean,
            default:false
        }

    },

    provide(){
        return {
               "DataTable":computed(()=> {
                return {
                    "enterToNextRow":this.enterToNextRow,
                    "primaryData": this.d_primaryData,
                    "evaluate":  this.evaluate
                }
            })
        }},

    data() {
        return {
            d_primaryData:null,
            d_currentRow: -1 ,  /*初始值设置为-1,这样才能通过watch 去触发row-focus-changed事件 */
            d_pageParams:{
                current: 1,
                size: this.pageSize ,
                total: 0
            },
            d_sortField: this.sortField,
            d_sortOrder: (this.sortOrder===null) ? this.defaultSortOrder: this.sortOrder ,
            d_multiSortMeta: this.multiSortMeta ? [...this.multiSortMeta] : [],
            d_groupRowsSortMeta: null,
            d_expandedRowKeys: null,
            d_columnOrder: null,
            d_filters: this.cloneFilters(this.filters),
            d_modified:false,
        };
    },
    uniqueRowId:0,
    lastFocusRowId:-1,
    filteredData:null,
    deletedData:null,
    rowTouched: false,
    anchorRowIndex: null,
    rangeRowIndex: null,
    documentColumnResizeListener: null,
    documentColumnResizeEndListener: null,
    lastResizeHelperX: null,
    resizeColumnElement: null,
    columnResizing: false,
    colReorderIconWidth: null,
    colReorderIconHeight: null,
    draggedColumn: null,
    draggedRowIndex: null,
    droppedRowIndex: null,
    rowDragging: null,
    columnWidthsState: null,
    tableWidthState: null,
    columnWidthsRestored: false,

    watch: {
        d_currentRow(newValue){
            if(this.d_primaryData && newValue>=0 && newValue< this.d_primaryData.length){
                this.rowFocusChanged(newValue);
            }
        },

        // sortField(newValue) {
        //     this.d_sortField = newValue;
        // },
        // sortOrder(newValue) {
        //     this.d_sortOrder = newValue;
        // },
        // multiSortMeta(newValue) {
        //     this.d_multiSortMeta = newValue;
        // },
        
        d_sortField(newValue){
           this.sort();
        },
        d_sortOrder(newValue) {
            this.sort();
        },
        // d_multiSortMeta(newValue) {
        //     this.$nextTick(()=>this.sort());
        // },

        d_filters(newValue){
            this.filter();
        },
        // filters: {
        //     deep: true,
        //     handler: function (newValue) {
        //         this.d_filters = this.cloneFilters(newValue);
        //     }
        // }

        pageSize(newValue){
            this.d_pageParams.size = newValue;
        }
    },

    beforeMount() {
        this.uniqueRowId=0;
        this.lastFocusRowId=-1;

        if (this.isStateful()) {
            this.restoreState();
        }
    },
    mounted() {
        this.$el.setAttribute(this.attributeSelector, '');

        if (this.isStateful() && this.resizableColumns) {
            this.restoreColumnWidths();
        }
    },
    beforeUnmount() {
        this.unbindColumnResizeEvents();
        this.destroyStyleElement();
    },

    updated() {
        if (this.isStateful()) {
            this.saveState();
        }
    },
    methods: {
        columnProp(col, prop) {
            return ObjectUtils.getVNodeProp(col, prop);
        },
        onPageChange(event) {

            const {size,total, current} =event;

            this.d_pageParams = { size, total, current};
            this.$emit('page-change', this.d_pageParams);
        },

        onColumnHeaderClick(e) {
            const event = e.originalEvent;
            const column = e.column;

            if (this.columnProp(column, 'sortable')) {
                const targetNode = event.target;
                const columnField = this.columnProp(column, 'sortField') || this.columnProp(column, 'field');

                if (
                    DomHandler.hasClass(targetNode, 'p-sortable-column') ||
                    DomHandler.hasClass(targetNode, 'p-column-title') ||
                    DomHandler.hasClass(targetNode, 'p-column-header-content') ||
                    DomHandler.hasClass(targetNode, 'p-sortable-column-icon') ||
                    DomHandler.hasClass(targetNode.parentElement, 'p-sortable-column-icon')
                ) {
                    DomHandler.clearSelection();

                    if (this.sortMode === 'single') {
                        if (this.d_sortField === columnField) {
                            if (this.removableSort && this.d_sortOrder * -1 === this.defaultSortOrder) {
                                this.d_sortOrder = null;
                                this.d_sortField = null;
                            } else {
                                this.d_sortOrder = this.d_sortOrder * -1;
                            }
                        } else {
                            this.d_sortOrder = this.defaultSortOrder;
                            this.d_sortField = columnField;
                        }

                       this.resetPage();

                    } else if (this.sortMode === 'multiple') {
                        let metaKey = event.metaKey || event.ctrlKey;

                        if (!metaKey) {
                            this.d_multiSortMeta = this.d_multiSortMeta.filter((meta) => meta.field === columnField);
                        }

                        this.addMultiSortField(columnField);
                     }
                }
            }
        },
        sortSingle(value) {
            if (this.groupRowsBy && this.groupRowsBy === this.sortField) {
                this.d_multiSortMeta = [
                    { field: this.sortField, order: this.sortOrder || this.defaultSortOrder },
                    { field: this.d_sortField, order: this.d_sortOrder }
                ];

                return this.sortMultiple(value);
            }

            let data = [...value];
            
            data.sort((data1, data2) => {
                let value1 = ObjectUtils.resolveFieldData(data1, this.d_sortField);
                let value2 = ObjectUtils.resolveFieldData(data2, this.d_sortField);

                let result = null;

                if (value1 == null && value2 != null) result = -1;
                else if (value1 != null && value2 == null) result = 1;
                else if (value1 == null && value2 == null) result = 0;
                else if (typeof value1 === 'string' && typeof value2 === 'string') result = value1.localeCompare(value2, undefined, { numeric: true });
                else result = value1 < value2 ? -1 : value1 > value2 ? 1 : 0;

                return this.d_sortOrder * result;
            });

            return data;
        },
        sortMultiple(value) {

            if (this.groupRowsBy && (this.d_groupRowsSortMeta || (this.d_multiSortMeta.length && this.groupRowsBy === this.d_multiSortMeta[0].field))) {
                const firstSortMeta = this.d_multiSortMeta[0];

                !this.d_groupRowsSortMeta && (this.d_groupRowsSortMeta = firstSortMeta);

                if (firstSortMeta.field !== this.d_groupRowsSortMeta.field) {
                    this.d_multiSortMeta = [this.d_groupRowsSortMeta, ...this.d_multiSortMeta];
                }
            }

            let data = [...value];

            data.sort((data1, data2) => {
                return this.multisortField(data1, data2, 0);
            });

            return data;
        },
        multisortField(data1, data2, index) {
            const value1 = ObjectUtils.resolveFieldData(data1, this.d_multiSortMeta[index].field);
            const value2 = ObjectUtils.resolveFieldData(data2, this.d_multiSortMeta[index].field);
            let result = null;

            if (typeof value1 === 'string' || value1 instanceof String) {
                if (value1.localeCompare && value1 !== value2) {
                    return this.d_multiSortMeta[index].order * value1.localeCompare(value2, undefined, { numeric: true });
                }
            } else {
                result = value1 < value2 ? -1 : 1;
            }

            if (value1 === value2) {
                return this.d_multiSortMeta.length - 1 > index ? this.multisortField(data1, data2, index + 1) : 0;
            }

            return this.d_multiSortMeta[index].order * result;
        },
        addMultiSortField(field) {
            let index = this.d_multiSortMeta.findIndex((meta) => meta.field === field);

            if (index >= 0) {
                if (this.removableSort && this.d_multiSortMeta[index].order * -1 === this.defaultSortOrder) this.d_multiSortMeta.splice(index, 1);
                else this.d_multiSortMeta[index] = { field: field, order: this.d_multiSortMeta[index].order * -1 };
            } else {
                this.d_multiSortMeta.push({ field: field, order: this.defaultSortOrder });
            }

            this.d_multiSortMeta = [...this.d_multiSortMeta];
        },
        
        sort(){
            if(!this.d_primaryData || this.d_primaryData.length ===0 )
                return;

            if (this.sorted) {
               const rowId = this.getRowId();

               if (this.sortMode === 'single')
                   this.d_primaryData = this.sortSingle(this.d_primaryData );
               else if (this.sortMode === 'multiple') 
                   this.d_primaryData=this.sortMultiple(this.d_primaryData );

                if(rowId){
                  this.setRowByRowId(rowId);
                }else{
                    this.d_currentRow=0;
                }

                this.rowFocusChanged(this.d_currentRow);
            }
        },

        filter() {
            const primaryData= this.d_primaryData || [];
            const filteredData = this.filteredData || [];
            let data=[...primaryData ,...filteredData];
            
            if(!data || data.length===0)
                return;

            let globalFilterFieldsArray;

            if (this.filters['global']) {
                globalFilterFieldsArray = this.globalFilterFields || this.columns.map((col) => this.columnProp(col, 'filterField') || this.columnProp(col, 'field'));
            }

            let primaryValue=[]
            let filteredValue = [];
            let rowId = this.getRowId();

            for (let i = 0; i < data.length; i++) {
                let localMatch = true;
                let globalMatch = false;
                let localFiltered = false;

                for (let prop in this.filters) {
                    if (Object.prototype.hasOwnProperty.call(this.filters, prop) && prop !== 'global') {
                        localFiltered = true;
                        let filterField = prop;
                        let filterMeta = this.filters[filterField];

                        if (filterMeta.operator) {
                            for (let filterConstraint of filterMeta.constraints) {
                                localMatch = this.executeLocalFilter(filterField, data[i], filterConstraint);

                                if ((filterMeta.operator === FilterOperator.OR && localMatch) || (filterMeta.operator === FilterOperator.AND && !localMatch)) {
                                    break;
                                }
                            }
                        } else {
                            localMatch = this.executeLocalFilter(filterField, data[i], filterMeta);
                        }

                        if (!localMatch) {
                            break;
                        }
                    }
                }

                if (this.filters['global'] && !globalMatch && globalFilterFieldsArray) {
                    for (let j = 0; j < globalFilterFieldsArray.length; j++) {
                        let globalFilterField = globalFilterFieldsArray[j];

                        globalMatch = FilterService.filters[this.filters['global'].matchMode || FilterMatchMode.CONTAINS](ObjectUtils.resolveFieldData(data[i], globalFilterField), this.filters['global'].value, this.filterLocale);

                        if (globalMatch) {
                            break;
                        }
                    }
                }

                let matches;

                if (this.filters['global']) {
                    matches = localFiltered ? localFiltered && localMatch && globalMatch : globalMatch;
                } else {
                    matches = localFiltered && localMatch;
                }

                if (matches) {
                    primaryValue.push(data[i]);
                }else{
                    filteredValue.push(data[i]);
                }
            }

            if (this.sorted && primaryValue && primaryValue.length>0 ) {
               if (this.sortMode === 'single')
                    primaryValue = this.sortSingle(primaryValue);
               else if (this.sortMode === 'multiple') 
                    primaryValue=this.sortMultiple(primaryValue);
            }

            this.filteredData= filteredValue;
            this.d_primaryData = primaryValue;

            if(rowId){
                this.setCurrentRowByRowId(rowId);
            }
            else{
                this.d_currentRow =0; 
            }

            this.rowFocusChanged(this.d_currentRow);
        },

        executeLocalFilter(field, rowData, filterMeta) {
            let filterValue = filterMeta.value;
            let filterMatchMode = filterMeta.matchMode || FilterMatchMode.STARTS_WITH;
            let dataFieldValue = ObjectUtils.resolveFieldData(rowData, field);
            let filterConstraint = FilterService.filters[filterMatchMode];

            return filterConstraint(dataFieldValue, filterValue, this.filterLocale);
        },

        onRowClick(e) {
           const event = e.originalEvent;
           const rowData = e.data;
           const rowIndex = e.index;
           const { x=0, y=0} = event;


           if (!this.d_primaryData ||  !rowData || DomHandler.isClickable(event.target)) {
               return;
           }
           
           if(this.d_currentRow!==rowIndex){
              this.d_currentRow= rowIndex;
           }
          
           if(x!==0 || y!==0)  //如果x和y都是0,说明是用 DomHandle.invokeElementMethod 执行的click方法
              this.$emit('row-click', e);

           if(!this.selectionMode)
                return;

           const metaSelection = this.rowTouched ? false : this.metaKeySelection;
           const metaKey = event.metaKey || event.ctrlKey;
           const selected = this.isSelected(rowData);

           if (this.isMultipleSelectionMode() && metaSelection && event.shiftKey && this.anchorRowIndex != null) {
                DomHandler.clearSelection();
                this.rangeRowIndex = rowIndex;
                this.selectRange(event);
           }
           else{
                 this.anchorRowIndex = rowIndex;
                 this.rangeRowIndex = rowIndex;

                if (metaSelection && this.isMultipleSelectionMode() ) {
                   if (metaKey) {
                         rowData['isSelected']=!selected;
                    } else {
                       this.doUnSelectAll();
                       rowData['isSelected'] = true;
                   }
                } 
                else {
                    if (this.selectionMode === 'single' && !selected ) {
                        let selectedRow=this.d_primaryData.find((item,index)=> index !=rowIndex && item['isSelected']  );

                        if(selectedRow){
                            selectedRow['isSelected']=false;
                        }

                        rowData['isSelected']=true;
                    }
                    else if (this.selectionMode === 'multiple') {
                        rowData['isSelected']= !selected;
                    }
                }
            }

            if(rowData['isSelected']!==selected){
                this.$emit('row-select-changed',rowData);
            }

            this.rowTouched = false;
        },

        onRowDblClick(e) {
            const event = e.originalEvent;

            if (DomHandler.isClickable(event.target)) {
                return;
            }

            this.$emit('row-dblclick', e);
        },

        onRowRightClick(event) {
            DomHandler.clearSelection();
            event.originalEvent.target.focus();

            this.$emit('update:contextMenuSelection', event.data);
            this.$emit('row-contextmenu', event);
        },

        onRowTouchEnd() {
            this.rowTouched = true;
        },
      
        onArrowDownKey(event, frozenRow ) {
            let data= this.getAllRenderRowData() || [];

            if(this.d_currentRow>=(data.length -1)){
                event.preventDefault();
                event.stopPropagation();

                return;
            }

            this.d_currentRow++;

            if (event.shiftKey && this.isMultipleSelectionMode() ) {
                const rowIndex = this.d_currentRow

                this.onRowClick({ originalEvent: event, data: data[rowIndex], index: rowIndex });
            }

            event.preventDefault();
            event.stopPropagation();
        },

        onArrowUpKey(event, frozenRow) {
            if(this.d_currentRow<=0) {
                event.preventDefault();
                event.stopPropagation();

                return;
            }
           
            this.d_currentRow -- ;

            if (event.shiftKey && this.isMultipleSelectionMode() ) {
                let data= this.getAllRenderRowData() || [];
                const rowIndex = this.d_currentRow

                 this.onRowClick({ originalEvent: event, data: data[rowIndex], index: rowIndex });
            }

            event.preventDefault();
            event.stopPropagation();
        },
        onHomeKey(event, frozenRow) {
            // const firstRow = this.findFirstSelectableRow();

            // firstRow && this.focusRowChange(row, firstRow);

            // if (event.ctrlKey && event.shiftKey && this.isMultipleSelectionMode() ) {
            //     ///const data = this.dataToRender(slotProps.rows);
            //    /// this.$emit('update:selection', data.slice(0, rowIndex + 1));
            // }

            // event.preventDefault();
            // event.stopPropagation();
        },
        onEndKey(event, frozenRow) {
            // const lastRow = this.findLastSelectableRow();

            // lastRow && this.focusRowChange(row, lastRow);

            // if (event.ctrlKey && event.shiftKey && this.isMultipleSelectionMode() ) {
            //     //const data = this.dataToRender(slotProps.rows);
            //    /// this.$emit('update:selection', data.slice(rowIndex, data.length));
            // }

            // event.preventDefault();
            // event.stopPropagation();
        },

        onEnterKey(event, frozenRow) {
            
        },
        
        onSpaceKey(event, row,  rowData, rowIndex, slotProps) {
             /*
            this.onEnterKey(event, row, rowData, rowIndex);

            if (event.shiftKey && this.selection !== null) {
                const data = this.dataToRender(slotProps.rows);
                let index;

                if (this.selection.length > 0) {
                    let firstSelectedRowIndex, lastSelectedRowIndex;

                    firstSelectedRowIndex = ObjectUtils.findIndexInList(this.selection[0], data);
                    lastSelectedRowIndex = ObjectUtils.findIndexInList(this.selection[this.selection.length - 1], data);

                    index = rowIndex <= firstSelectedRowIndex ? lastSelectedRowIndex : firstSelectedRowIndex;
                } else {
                    index = ObjectUtils.findIndexInList(this.selection, data);
                }

                const _selection = index !== rowIndex ? data.slice(Math.min(index, rowIndex), Math.max(index, rowIndex) + 1) : rowData;

                this.$emit('update:selection', _selection);
            }
          */
        },
        onTabKey(event, rowIndex) {
            // const body = this.$refs.bodyRef && this.$refs.bodyRef.$el;
            // const rows = DomHandler.find(body, 'tr.p-selectable-row');

            // if (event.code === 'Tab' && rows && rows.length > 0) {
            //     const firstSelectedRow = DomHandler.findSingle(body, 'tr.p-highlight');
            //     const focusedItem = DomHandler.findSingle(body, 'tr.p-selectable-row[tabindex="0"]');

            //     if (firstSelectedRow) {
            //         firstSelectedRow.tabIndex = '0';
            //         focusedItem && focusedItem !== firstSelectedRow && (focusedItem.tabIndex = '-1');
            //     } else {
            //         rows[0].tabIndex = '0';
            //         focusedItem !== rows[0] && (rows[rowIndex].tabIndex = '-1');
            //     }
            // }
        },
        findNextSelectableRow(row) {
            let nextRow = row.nextElementSibling;

            return nextRow;
        },
        findPrevSelectableRow(row) {
            let prevRow = row.previousElementSibling;

            return prevRow;
        },
        findFirstSelectableRow() {
            const body = this.$refs.bodyRef && this.$refs.bodyRef.$el;
            const firstRow = DomHandler.findSingle(body, 'tr');
            
            return firstRow;
        },
        findLastSelectableRow() {
            const body = this.$refs.bodyRef && this.$refs.bodyRef.$el;
            const rows = DomHandler.find(body, 'tr');

            return rows ? rows[rows.length - 1] : null;
        },

        focusRowChange(firstFocusableRow, currentFocusedRow) {
            firstFocusableRow.tabIndex = '-1';
            currentFocusedRow.tabIndex = '0';
            DomHandler.focus(currentFocusedRow);

            console.log("focusRowChange")
        },

        toggleRowWithRadio(event) {
            const rowData = event.data;

            if (rowData && !this.isSelected(rowData)) {
               this.doUnSelectAll();
               rowData['isSelected']=true;
               this.$emit('row-select-changed',rowData);
            }
        },

        toggleRowWithCheckbox(event) {
            const rowData = event.data;

            if(rowData){
               rowData['isSelected']=  !rowData['isSelected']
               this.$emit('row-select-changed',rowData);
            }
        },

        toggleRowsWithCheckbox(event) {
            const { originalEvent, checked } = event;

            if(checked){
                this.doSelectAll();
                this.$emit('row-select-all');
            }
            else{
                this.doUnSelectAll();
                this.$emit('row-unselect-all');
            }
        },

        isSingleSelectionMode() {
            return this.selectionMode === 'single';
        },
        isMultipleSelectionMode() {
            return this.selectionMode === 'multiple';
        },

        isSelected(rowData) {
            return (rowData && rowData['isSelected']);
        },

        findIndex(rowData, collection) {
            let index = -1;

            if (collection && collection.length) {
                for (let i = 0; i < collection.length; i++) {
                    if (this.equals(rowData, collection[i])) {
                        index = i;
                        break;
                    }
                }
            }

            return index;
        },
        
        updateExpandedRowKeys(expandedRows) {
            if (expandedRows && expandedRows.length) {
                this.d_expandedRowKeys = {};

                for (let data of expandedRows) {
                    this.d_expandedRowKeys[String(ObjectUtils.resolveFieldData(data, this.dataKey))] = 1;
                }
            } else {
                this.d_expandedRowKeys = null;
            }
        },
       
        equals(data1, data2) {
            return this.compareSelectionBy === 'equals' ? data1 === data2 : ObjectUtils.equals(data1, data2, this.dataKey);
        },
        selectRange(event) {
            if(!this.d_primaryData || this.d_primaryData.length ===0 )
                return;

            let rangeStart, rangeEnd;

            if (this.rangeRowIndex > this.anchorRowIndex) {
                rangeStart = this.anchorRowIndex;
                rangeEnd = this.rangeRowIndex;
            } else if (this.rangeRowIndex < this.anchorRowIndex) {
                rangeStart = this.rangeRowIndex;
                rangeEnd = this.anchorRowIndex;
            } else {
                rangeStart = this.rangeRowIndex;
                rangeEnd = this.rangeRowIndex;
            }

            if (this.lazy && this.paginator) {
                rangeStart -= this.first;
                rangeEnd -= this.first;
            }
            
            for (let i =0; i< this.d_primaryData.length; i++) {
                this.d_primaryData[i]['isSelected']=(i>=rangeStart && i<=rangeEnd);
            }
       },

        exportCSV(options, data) {
            let csv = '\ufeff';

            if (!data) {
                data = this.d_primaryData;
            }

            if(!data || this.data.length ===0 )
                return;

            //headers
            let headerInitiated = false;

            for (let i = 0; i < this.columns.length; i++) {
                let column = this.columns[i];

                if (this.columnProp(column, 'exportable') !== false && this.columnProp(column, 'field')) {
                    if (headerInitiated) csv += this.csvSeparator;
                    else headerInitiated = true;

                    csv += '"' + (this.columnProp(column, 'exportHeader') || this.columnProp(column, 'header') || this.columnProp(column, 'field')) + '"';
                }
            }

            //body
            if (data) {
                data.forEach((record) => {
                    csv += '\n';
                    let rowInitiated = false;

                    for (let i = 0; i < this.columns.length; i++) {
                        let column = this.columns[i];

                        if (this.columnProp(column, 'exportable') !== false && this.columnProp(column, 'field')) {
                            if (rowInitiated) csv += this.csvSeparator;
                            else rowInitiated = true;

                            let cellData = ObjectUtils.resolveFieldData(record, this.columnProp(column, 'field'));

                            if (cellData != null) {
                                if (this.exportFunction) {
                                    cellData = this.exportFunction({
                                        data: cellData,
                                        field: this.columnProp(column, 'field')
                                    });
                                } else cellData = String(cellData).replace(/"/g, '""');
                            } else cellData = '';

                            csv += '"' + cellData + '"';
                        }
                    }
                });
            }

            //footers
            let footerInitiated = false;

            for (let i = 0; i < this.columns.length; i++) {
                let column = this.columns[i];

                if (i === 0) csv += '\n';

                if (this.columnProp(column, 'exportable') !== false && this.columnProp(column, 'exportFooter')) {
                    if (footerInitiated) csv += this.csvSeparator;
                    else footerInitiated = true;

                    csv += '"' + (this.columnProp(column, 'exportFooter') || this.columnProp(column, 'footer') || this.columnProp(column, 'field')) + '"';
                }
            }

            DomHandler.exportCSV(csv, this.exportFilename);
        },
        resetPage() {
            const { size } =this.d_pageParams;

            this.d_pageParams={
                size,
                current:1,
                total:0
            }
        },

        onColumnResizeStart(event) {
            let containerLeft = DomHandler.getOffset(this.$el).left;

            this.resizeColumnElement = event.target.parentElement;
            this.columnResizing = true;
            this.lastResizeHelperX = event.pageX - containerLeft + this.$el.scrollLeft;

            this.bindColumnResizeEvents();
        },
        onColumnResize(event) {
            let containerLeft = DomHandler.getOffset(this.$el).left;

            DomHandler.addClass(this.$el, 'p-unselectable-text');
            this.$refs.resizeHelper.style.height = this.$el.offsetHeight + 'px';
            this.$refs.resizeHelper.style.top = 0 + 'px';
            this.$refs.resizeHelper.style.left = event.pageX - containerLeft + this.$el.scrollLeft + 'px';

            this.$refs.resizeHelper.style.display = 'block';
        },
        onColumnResizeEnd() {
            let delta = this.$refs.resizeHelper.offsetLeft - this.lastResizeHelperX;
            let columnWidth = this.resizeColumnElement.offsetWidth;
            let newColumnWidth = columnWidth + delta;
            let minWidth = this.resizeColumnElement.style.minWidth || 15;

            if (columnWidth + delta > parseInt(minWidth, 10)) {
                if (this.columnResizeMode === 'fit') {
                    let nextColumn = this.resizeColumnElement.nextElementSibling;
                    let nextColumnWidth = nextColumn.offsetWidth - delta;

                    if (newColumnWidth > 15 && nextColumnWidth > 15) {
                        this.resizeTableCells(newColumnWidth, nextColumnWidth);
                    }
                } else if (this.columnResizeMode === 'expand') {
                    const tableWidth = this.$refs.table.offsetWidth + delta + 'px';

                    const updateTableWidth = (el) => {
                        !!el && (el.style.width = el.style.minWidth = tableWidth);
                    };

                    updateTableWidth(this.$refs.table);

                    // if (!this.virtualScrollerDisabled) {
                    //     const body = this.$refs.bodyRef && this.$refs.bodyRef.$el;
                    //     const frozenBody = this.$refs.frozenBodyRef && this.$refs.frozenBodyRef.$el;

                    //     updateTableWidth(body);
                    //     updateTableWidth(frozenBody);
                    // }

                    this.resizeTableCells(newColumnWidth);
                }

                this.$emit('column-resize-end', {
                    element: this.resizeColumnElement,
                    delta: delta
                });
            }

            this.$refs.resizeHelper.style.display = 'none';
            this.resizeColumn = null;
            DomHandler.removeClass(this.$el, 'p-unselectable-text');

            this.unbindColumnResizeEvents();

            if (this.isStateful()) {
                this.saveState();
            }
        },
        resizeTableCells(newColumnWidth, nextColumnWidth) {
            let colIndex = DomHandler.index(this.resizeColumnElement);
            let widths = [];
            let headers = DomHandler.find(this.$refs.table, '.p-datatable-thead > tr > th');

            headers.forEach((header) => widths.push(DomHandler.getOuterWidth(header)));

            this.destroyStyleElement();
            this.createStyleElement();

            let innerHTML = '';
            let selector = `.p-datatable[${this.attributeSelector}] > .p-datatable-wrapper ${this.virtualScrollerDisabled ? '' : '> .p-virtualscroller'} > .p-datatable-table`;

            widths.forEach((width, index) => {
                let colWidth = index === colIndex ? newColumnWidth : nextColumnWidth && index === colIndex + 1 ? nextColumnWidth : width;
                let style = `width: ${colWidth}px !important; max-width: ${colWidth}px !important`;

                innerHTML += `
                    ${selector} > .p-datatable-thead > tr > th:nth-child(${index + 1}),
                    ${selector} > .p-datatable-tbody > tr > td:nth-child(${index + 1}),
                    ${selector} > .p-datatable-tfoot > tr > td:nth-child(${index + 1}) {
                        ${style}
                    }
                `;
            });

            this.styleElement.innerHTML = innerHTML;
        },
        bindColumnResizeEvents() {
            if (!this.documentColumnResizeListener) {
                this.documentColumnResizeListener = document.addEventListener('mousemove', () => {
                    if (this.columnResizing) {
                        this.onColumnResize(event);
                    }
                });
            }

            if (!this.documentColumnResizeEndListener) {
                this.documentColumnResizeEndListener = document.addEventListener('mouseup', () => {
                    if (this.columnResizing) {
                        this.columnResizing = false;
                        this.onColumnResizeEnd();
                    }
                });
            }
        },
        unbindColumnResizeEvents() {
            if (this.documentColumnResizeListener) {
                document.removeEventListener('document', this.documentColumnResizeListener);
                this.documentColumnResizeListener = null;
            }

            if (this.documentColumnResizeEndListener) {
                document.removeEventListener('document', this.documentColumnResizeEndListener);
                this.documentColumnResizeEndListener = null;
            }
        },
        onColumnHeaderMouseDown(e) {
            const event = e.originalEvent;
            const column = e.column;

            if (this.reorderableColumns && this.columnProp(column, 'reorderableColumn') !== false) {
                if (event.target.nodeName === 'INPUT' || event.target.nodeName === 'TEXTAREA' || DomHandler.hasClass(event.target, 'p-column-resizer')) event.currentTarget.draggable = false;
                else event.currentTarget.draggable = true;
            }
        },
        onColumnHeaderDragStart(event) {
            if (this.columnResizing) {
                event.preventDefault();

                return;
            }

            this.colReorderIconWidth = DomHandler.getHiddenElementOuterWidth(this.$refs.reorderIndicatorUp);
            this.colReorderIconHeight = DomHandler.getHiddenElementOuterHeight(this.$refs.reorderIndicatorUp);

            this.draggedColumn = this.findParentHeader(event.target);
            event.dataTransfer.setData('text', 'b'); // Firefox requires this to make dragging possible
        },
        onColumnHeaderDragOver(event) {
            let dropHeader = this.findParentHeader(event.target);

            if (this.reorderableColumns && this.draggedColumn && dropHeader) {
                event.preventDefault();
                let containerOffset = DomHandler.getOffset(this.$el);
                let dropHeaderOffset = DomHandler.getOffset(dropHeader);

                if (this.draggedColumn !== dropHeader) {
                    let targetLeft = dropHeaderOffset.left - containerOffset.left;
                    let columnCenter = dropHeaderOffset.left + dropHeader.offsetWidth / 2;

                    this.$refs.reorderIndicatorUp.style.top = dropHeaderOffset.top - containerOffset.top - (this.colReorderIconHeight - 1) + 'px';
                    this.$refs.reorderIndicatorDown.style.top = dropHeaderOffset.top - containerOffset.top + dropHeader.offsetHeight + 'px';

                    if (event.pageX > columnCenter) {
                        this.$refs.reorderIndicatorUp.style.left = targetLeft + dropHeader.offsetWidth - Math.ceil(this.colReorderIconWidth / 2) + 'px';
                        this.$refs.reorderIndicatorDown.style.left = targetLeft + dropHeader.offsetWidth - Math.ceil(this.colReorderIconWidth / 2) + 'px';
                        this.dropPosition = 1;
                    } else {
                        this.$refs.reorderIndicatorUp.style.left = targetLeft - Math.ceil(this.colReorderIconWidth / 2) + 'px';
                        this.$refs.reorderIndicatorDown.style.left = targetLeft - Math.ceil(this.colReorderIconWidth / 2) + 'px';
                        this.dropPosition = -1;
                    }

                    this.$refs.reorderIndicatorUp.style.display = 'block';
                    this.$refs.reorderIndicatorDown.style.display = 'block';
                }
            }
        },
        onColumnHeaderDragLeave(event) {
            if (this.reorderableColumns && this.draggedColumn) {
                event.preventDefault();
                this.$refs.reorderIndicatorUp.style.display = 'none';
                this.$refs.reorderIndicatorDown.style.display = 'none';
            }
        },
        onColumnHeaderDrop(event) {
            event.preventDefault();

            if (this.draggedColumn) {
                let dragIndex = DomHandler.index(this.draggedColumn);
                let dropIndex = DomHandler.index(this.findParentHeader(event.target));
                let allowDrop = dragIndex !== dropIndex;

                if (allowDrop && ((dropIndex - dragIndex === 1 && this.dropPosition === -1) || (dropIndex - dragIndex === -1 && this.dropPosition === 1))) {
                    allowDrop = false;
                }

                if (allowDrop) {
                    ObjectUtils.reorderArray(this.columns, dragIndex, dropIndex);
                    this.updateReorderableColumns();

                    this.$emit('column-reorder', {
                        originalEvent: event,
                        dragIndex: dragIndex,
                        dropIndex: dropIndex
                    });
                }

                this.$refs.reorderIndicatorUp.style.display = 'none';
                this.$refs.reorderIndicatorDown.style.display = 'none';
                this.draggedColumn.draggable = false;
                this.draggedColumn = null;
                this.dropPosition = null;
            }
        },
        findParentHeader(element) {
            if (element.nodeName === 'TH') {
                return element;
            } else {
                let parent = element.parentElement;

                while (parent.nodeName !== 'TH') {
                    parent = parent.parentElement;
                    if (!parent) break;
                }

                return parent;
            }
        },
        findColumnByKey(columns, key) {
            if (columns && columns.length) {
                for (let i = 0; i < columns.length; i++) {
                    let column = columns[i];

                    if (this.columnProp(column, 'columnKey') === key || this.columnProp(column, 'field') === key) {
                        return column;
                    }
                }
            }

            return null;
        },
        onRowMouseDown(event) {
            if (DomHandler.hasClass(event.target, 'p-datatable-reorderablerow-handle')) event.currentTarget.draggable = true;
            else event.currentTarget.draggable = false;
        },
        onRowDragStart(e) {
            const event = e.originalEvent;
            const index = e.index;

            this.rowDragging = true;
            this.draggedRowIndex = index;
            event.dataTransfer.setData('text', 'b'); // For firefox
        },
        onRowDragOver(e) {
            const event = e.originalEvent;
            const index = e.index;

            if (this.rowDragging && this.draggedRowIndex !== index) {
                let rowElement = event.currentTarget;
                let rowY = DomHandler.getOffset(rowElement).top + DomHandler.getWindowScrollTop();
                let pageY = event.pageY;
                let rowMidY = rowY + DomHandler.getOuterHeight(rowElement) / 2;
                let prevRowElement = rowElement.previousElementSibling;

                if (pageY < rowMidY) {
                    DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-bottom');

                    this.droppedRowIndex = index;
                    if (prevRowElement) DomHandler.addClass(prevRowElement, 'p-datatable-dragpoint-bottom');
                    else DomHandler.addClass(rowElement, 'p-datatable-dragpoint-top');
                } else {
                    if (prevRowElement) DomHandler.removeClass(prevRowElement, 'p-datatable-dragpoint-bottom');
                    else DomHandler.addClass(rowElement, 'p-datatable-dragpoint-top');

                    this.droppedRowIndex = index + 1;
                    DomHandler.addClass(rowElement, 'p-datatable-dragpoint-bottom');
                }

                event.preventDefault();
            }
        },
        onRowDragLeave(event) {
            let rowElement = event.currentTarget;
            let prevRowElement = rowElement.previousElementSibling;

            if (prevRowElement) {
                DomHandler.removeClass(prevRowElement, 'p-datatable-dragpoint-bottom');
            }

            DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-bottom');
            DomHandler.removeClass(rowElement, 'p-datatable-dragpoint-top');
        },
        onRowDragEnd(event) {
            this.rowDragging = false;
            this.draggedRowIndex = null;
            this.droppedRowIndex = null;
            event.currentTarget.draggable = false;
        },
        onRowDrop(event) {
            if(!this.d_primaryData ||this.d_primaryData.length ===0  )
                return;

            if (this.droppedRowIndex != null) {
                let dropIndex = this.draggedRowIndex > this.droppedRowIndex ? this.droppedRowIndex : this.droppedRowIndex === 0 ? 0 : this.droppedRowIndex - 1;

                ObjectUtils.reorderArray(this.d_primaryData, this.draggedRowIndex + this.d_first, dropIndex + this.d_first);
            }

            //cleanup
            this.onRowDragLeave(event);
            this.onRowDragEnd(event);
            event.preventDefault();
        },
        toggleRow(event) {
            let rowData = event.data;
            let expanded;
            let expandedRowIndex;
            let _expandedRows = this.expandedRows ? [...this.expandedRows] : [];

            if (this.dataKey) {
                expanded = this.d_expandedRowKeys ? this.d_expandedRowKeys[ObjectUtils.resolveFieldData(rowData, this.dataKey)] !== undefined : false;
            } else {
                expandedRowIndex = this.findIndex(rowData, this.expandedRows);
                expanded = expandedRowIndex > -1;
            }

            if (expanded) {
                if (expandedRowIndex == null) {
                    expandedRowIndex = this.findIndex(rowData, this.expandedRows);
                }

                _expandedRows.splice(expandedRowIndex, 1);
                this.$emit('update:expandedRows', _expandedRows);
                this.$emit('row-collapse', event);
            } else {
                _expandedRows.push(rowData);
                this.$emit('update:expandedRows', _expandedRows);
                this.$emit('row-expand', event);
            }
        },
        toggleRowGroup(e) {
            const event = e.originalEvent;
            const data = e.data;
            const groupFieldValue = ObjectUtils.resolveFieldData(data, this.groupRowsBy);
            let _expandedRowGroups = this.expandedRowGroups ? [...this.expandedRowGroups] : [];

            if (this.isRowGroupExpanded(data)) {
                _expandedRowGroups = _expandedRowGroups.filter((group) => group !== groupFieldValue);
                this.$emit('update:expandedRowGroups', _expandedRowGroups);
                this.$emit('rowgroup-collapse', { originalEvent: event, data: groupFieldValue });
            } else {
                _expandedRowGroups.push(groupFieldValue);
                this.$emit('update:expandedRowGroups', _expandedRowGroups);
                this.$emit('rowgroup-expand', { originalEvent: event, data: groupFieldValue });
            }
        },
        isRowGroupExpanded(rowData) {
            if (this.expandableRowGroups && this.expandedRowGroups) {
                let groupFieldValue = ObjectUtils.resolveFieldData(rowData, this.groupRowsBy);

                return this.expandedRowGroups.indexOf(groupFieldValue) > -1;
            }

            return false;
        },
        isStateful() {
            return this.stateKey != null;
        },
        getStorage() {
            switch (this.stateStorage) {
                case 'local':
                    return window.localStorage;

                case 'session':
                    return window.sessionStorage;

                default:
                    throw new Error(this.stateStorage + ' is not a valid value for the state storage, supported values are "local" and "session".');
            }
        },
        saveState() {
            const storage = this.getStorage();
            let state = {};

            if (this.paginator) {
                state.first = this.d_first;
                state.rows = this.d_rows;
            }

            if (this.d_sortField) {
                state.sortField = this.d_sortField;
                state.sortOrder = this.d_sortOrder;
            }

            if (this.d_multiSortMeta) {
                state.multiSortMeta = this.d_multiSortMeta;
            }

            if (this.hasFilters) {
                state.filters = this.filters;
            }

            if (this.resizableColumns) {
                this.saveColumnWidths(state);
            }

            if (this.reorderableColumns) {
                state.columnOrder = this.d_columnOrder;
            }

            if (this.expandedRows) {
                state.expandedRows = this.expandedRows;
                state.expandedRowKeys = this.d_expandedRowKeys;
            }

            if (this.expandedRowGroups) {
                state.expandedRowGroups = this.expandedRowGroups;
            }


            if (Object.keys(state).length) {
                storage.setItem(this.stateKey, JSON.stringify(state));
            }

            this.$emit('state-save', state);
        },
        restoreState() {
            const storage = this.getStorage();
            const stateString = storage.getItem(this.stateKey);
            const dateFormat = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/;

            const reviver = function (key, value) {
                if (typeof value === 'string' && dateFormat.test(value)) {
                    return new Date(value);
                }

                return value;
            };

            if (stateString) {
                let restoredState = JSON.parse(stateString, reviver);

                if (this.paginator) {
                    this.d_first = restoredState.first;
                    this.d_rows = restoredState.rows;
                }

                if (restoredState.sortField) {
                    this.d_sortField = restoredState.sortField;
                    this.d_sortOrder = restoredState.sortOrder;
                }

                if (restoredState.multiSortMeta) {
                    this.d_multiSortMeta = restoredState.multiSortMeta;
                }

                if (restoredState.filters) {
                    this.$emit('update:filters', restoredState.filters);
                }

                if (this.resizableColumns) {
                    this.columnWidthsState = restoredState.columnWidths;
                    this.tableWidthState = restoredState.tableWidth;
                }

                if (this.reorderableColumns) {
                    this.d_columnOrder = restoredState.columnOrder;
                }

                if (restoredState.expandedRows) {
                    this.d_expandedRowKeys = restoredState.expandedRowKeys;
                    this.$emit('update:expandedRows', restoredState.expandedRows);
                }

                if (restoredState.expandedRowGroups) {
                    this.$emit('update:expandedRowGroups', restoredState.expandedRowGroups);
                }

                this.$emit('state-restore', restoredState);
            }
        },
        saveColumnWidths(state) {
            let widths = [];
            let headers = DomHandler.find(this.$el, '.p-datatable-thead > tr > th');

            headers.forEach((header) => widths.push(DomHandler.getOuterWidth(header)));
            state.columnWidths = widths.join(',');

            if (this.columnResizeMode === 'expand') {
                state.tableWidth = DomHandler.getOuterWidth(this.$refs.table) + 'px';
            }
        },
        restoreColumnWidths() {
            if (this.columnWidthsState) {
                let widths = this.columnWidthsState.split(',');

                if (this.columnResizeMode === 'expand' && this.tableWidthState) {
                    this.$refs.table.style.width = this.tableWidthState;
                    this.$refs.table.style.minWidth = this.tableWidthState;
                    this.$el.style.width = this.tableWidthState;
                }

                if (ObjectUtils.isNotEmpty(widths)) {
                    this.createStyleElement();

                    let innerHTML = '';
                    let selector = `.p-datatable[${this.attributeSelector}] > .p-datatable-wrapper ${this.virtualScrollerDisabled ? '' : '> .p-virtualscroller'} > .p-datatable-table`;

                    widths.forEach((width, index) => {
                        let style = `width: ${width}px !important; max-width: ${width}px !important`;

                        innerHTML += `
                            ${selector} > .p-datatable-thead > tr > th:nth-child(${index + 1}),
                            ${selector} > .p-datatable-tbody > tr > td:nth-child(${index + 1}),
                            ${selector} > .p-datatable-tfoot > tr > td:nth-child(${index + 1}) {
                                ${style}
                            }
                        `;
                    });

                    this.styleElement.innerHTML = innerHTML;
                }
            }
        },
        onCellEditInit(event) {
            this.$emit('cell-edit-init', event);
        },
        onCellEditComplete(event) {
            this.$emit('cell-edit-complete', event);
        },
        onCellEditCancel(event) {
            this.$emit('cell-edit-cancel', event);
        },

        onItemChange(event){
            this.$emit('item-change', event);
        },
        hasGlobalFilter() {
            return this.filters && Object.prototype.hasOwnProperty.call(this.filters, 'global');
        },
        getChildren() {
            return this.$slots.default ? this.$slots.default() : null;
        },
        onFilterChange(filters) {
            this.d_filters = filters;
            this.filter();
        },
        
        onFilterApply() {
            this.$emit('update:filters', this.d_filters);
            this.filter();
        },

        cloneFilters() {
            let cloned = {};

            if (this.filters) {
                Object.entries(this.filters).forEach(([prop, value]) => {
                    cloned[prop] = value.operator
                        ? {
                              operator: value.operator,
                              constraints: value.constraints.map((constraint) => {
                                  return { ...constraint };
                              })
                          }
                        : { ...value };
                });
            }

            return cloned;
        },
        updateReorderableColumns() {
            let columnOrder = [];

            this.columns.forEach((col) => columnOrder.push(this.columnProp(col, 'columnKey') || this.columnProp(col, 'field')));
            this.d_columnOrder = columnOrder;
        },
        createStyleElement() {
            this.styleElement = document.createElement('style');
            this.styleElement.type = 'text/css';
            document.head.appendChild(this.styleElement);
        },
       
        destroyStyleElement() {
            if (this.styleElement) {
                document.head.removeChild(this.styleElement);
                this.styleElement = null;
            }
        },
        recursiveGetChildren(children, results) {
            if (!results) {
                results = [];
            }

            if (children && children.length) {
                children.forEach((child) => {
                    if (child.children instanceof Array) {
                        results.concat(this.recursiveGetChildren(child.children, results));
                    } else if (child.type.name == 'Column') {
                        results.push(child);
                    }
                });
            }

            return results;
        },
       
        hasSpacerStyle(style) {
            return ObjectUtils.isNotEmpty(style);
        },

        onBodyKeyDown(e){
            const event = e.originalEvent;
            const frozenRow =e.frozenRow; 

            switch (event.code) {
                case 'ArrowDown':
                    this.onArrowDownKey(event, frozenRow);
                   break;

                case 'ArrowUp':
                    this.onArrowUpKey(event, frozenRow );
                    break;

                /*
                case 'Home':
                    this.onHomeKey(event, frozenRow);
                    break;

                case 'End':
                    this.onEndKey(event, frozenRow);
                    break;

                case 'Enter':
                case 'NumpadEnter':
                    this.onEnterKey(event, frozenRow );
                    break;

              
                case 'Space':
                    this.onSpaceKey(event, frozenRow );
                    break;

                case 'Tab':
                    this.onTabKeyevent, frozenRow);
                    break;
                */
                default:
                    if (event.code === 'KeyA' && metaKey &&  this.isMultipleSelectionMode()) {
                       this.doSelectAll();
                       event.preventDefault();
                       event.stopPropagation();
                    }

                    break;
            }
        },

        doSelectAll(){
            if(!this.d_primaryData || this.d_primaryData.length===0 )
                return;

            for(let i=0; i< this.d_primaryData.length; i++){

                if( !this.d_primaryData[i]['isSelected']){
                   this.d_primaryData[i]['isSelected']=true;

                   this.$emit('row-select-changed',this.d_primaryData[i]);
                }
            }
        },

        doUnSelectAll(){
            if(!this.d_primaryData || this.d_primaryData.length ===0 )
                return;

            for(let i=0; i< this.d_primaryData.length; i++){
                if(this.d_primaryData[i]['isSelected']){
                     this.d_primaryData[i]['isSelected']=false;

                     this.$emit('row-select-changed',this.d_primaryData[i]);
                }
            }
        },

        setData(newData){
            if(Array.isArray(newData)){
                this._interalSetData(newData);
            }
            else{
                if(newData.hasOwnProperty("records") && newData.hasOwnProperty("current") && newData.hasOwnProperty("total")){
                    let { size, current, total }= newData;

                    if(!size){
                        size= this.d_pageParams.size; 
                    }

                    this._interalSetData(newData["records"]);
                    this.d_pageParams = { size, current, total};
                }
                else{
                    this._interalSetData( [ newData] ); 
                }
            }
        },

        _interalSetData(newData){
            this.reset();
            
            if(!newData || !newData.length )
                return;
           
             if (this.sorted) {
                if (this.sortMode === 'single')
                    newData = this.sortSingle(newData);
                else if (this.sortMode === 'multiple') 
                   newData = this.sortMultiple(newData);
             }

           
            this.d_primaryData = newData.map((item)=> DataHandler.Proxy(item, ++this.uniqueRowId, this._interalSetModified ) );
            this.d_currentRow =0;
            this.rowFocusChanged(0);
        },

        _interalSetModified(newData){
           this.d_modified=newData;
        },

        setRow(rowIndex){
            if(!this.d_primaryData || this.d_primaryData.length===0 )
                return;

            if(rowIndex<0 || rowIndex>=( this.d_primaryData.length -1) )
                rowIndex = this.d_primaryData.length -1;

            this.d_currentRow= rowIndex;
        },

        getRow(){
            return this.d_currentRow;
        },

        getRowByRowId(rowId){
            if(!rowId || !this.d_primaryData || this.d_primaryData.length===0  )
                return -1; 
            
            for(let i=0;i<this.d_primaryData.length;i++){
                if( this.d_primaryData[i]['rowId'] === rowId){
                    return i;
                }
            }
            
            return -1;
        },

        getRowData( rowIndex ){
           if(rowIndex===undefined || rowIndex===null || rowIndex===-1  )
              rowIndex = this.d_currentRow;

           if(!this.d_primaryData || this.d_primaryData.length===0 )
                return null;

           if(!this.d_primaryData ||  rowIndex< 0 || rowIndex>=this.d_primaryData.length ){
                throw new RangeError(`RowIndex:${rowIndex}无效`);
            }

            return this.d_primaryData[rowIndex];
        },

        getFirstRowData(){
           if(!this.d_primaryData || this.d_primaryData.length===0 )
               return null;

            return this.getRowData(0);
        },
        getLastRowData(){
            if(!this.d_primaryData || this.d_primaryData.length===0 )
               return null;

            return this.getRowData(this.d_primaryData.length -1 );
        },

        getRowId(){
            const rowData= this.getRowData( -1 );

            if( rowData && ('rowId' in rowData)){
                return rowData['rowId'];
            }
            
            return null;
        },
   
        setRowByRowId(rowId){
            if(!rowId || !this.d_primaryData || this.d_primaryData.length===0  )
                return ; 
            
            for(let i=0;i<this.d_primaryData.length;i++){
                if( this.d_primaryData[i]['rowId'] === rowId){
                    this.d_currentRow = i;
                    break;
                }
            }
        },

        //TODO 未考虑数据过滤和排序的情况，如果设置了过滤和排序，插入后的数据会过滤掉或显示的位置不对
        //所以应该考虑优化 processData,避免每次显示都重新过滤和排序数据
        insertRow(rowIndex){
             
            let rowData=null;
            let _columns= this.columns || [] ;

            if(this.uniqueRowId===null || this.uniqueRowId===undefined){
                this.uniqueRowId=0;
            }

            if(this.d_primaryData && this.d_primaryData.length){
                rowData= DataHandler.Initialize(this.d_primaryData[0], ++this.uniqueRowId , this._interalSetModified)
            }
            else{
                rowData={};  //先不进行Proxy处理，否则后面赋初始值会提示找不到属性
            }
         
            for (let i = 0; i < _columns.length; i++) {
                const column = _columns[i];
                const field= this.columnProp(column, 'field') || '';
                const initial = this.columnProp(column, 'initial')  ;
                const colType =  this.columnProp(column, 'colType') || '';

                if (field) {
                    rowData[field] =(initial!==null && initial!==undefined && initial!=='' )? ObjectUtils.initailValue(initial, colType): null ;
                }
            }

            if(!('__dataProxy' in rowData)){
                rowData=DataHandler.Proxy(rowData, ++this.uniqueRowId ,this._interalSetModified );
            }

            DomHandler.clearSelection();
            this.$emit('row-new-init', rowData);
            rowData['rowStatus'] = RowStatus.New;

            if(!this.d_primaryData){
                this.d_primaryData =[];
            }

            if(rowIndex < 0 || rowIndex>= this.d_primaryData.length ){
                this.d_primaryData.push(rowData);
            }
            else{
                this.d_primaryData.splice(rowIndex  ,0 , rowData);
            }

            if(this.d_currentRow< 0 ){
                this.d_currentRow =0
            }
            else if(this.d_currentRow>=rowIndex){
               this.d_currentRow++;
            }
            
            return rowData; // 返回新增行的数据，以便对新增行的数据进行赋值 
        },

        deleteRow(rowIndex){
            if( !this.d_primaryData || this.d_primaryData.length===0)
                return;

            //如果传入 -1 ，表示当前行
            if(rowIndex=== -1 ){
                rowIndex = this.d_currentRow;
            }

            if(rowIndex<0 || rowIndex>= this.d_primaryData.length ){
                return ;
            }

            DomHandler.clearSelection();
            let rowData = this.d_primaryData.splice(rowIndex,1)[0] ;
            let { rowStatus=RowStatus.NotModified } = rowData;
            
            //如果删除行之后当前行号大于记录数
            if(this.d_primaryData.length===0){
                this.d_currentRow=-1;
            }
            else if( this.d_currentRow>=rowIndex &&  this.d_currentRow>0 ) {
                this.d_currentRow--;
            }
            else if( this.d_currentRow>=0 && this.d_currentRow< this.d_primaryData.length){ //删除记录后行号没有改变，也解发发row-focus-changed事件
                this.$emit('row-focus-changed',{ rowIndex: this.d_currentRow, rowData: this.d_primaryData[this.d_currentRow ] })
            }

            if(rowStatus===RowStatus.NotModified || rowStatus=== RowStatus.DataModified){
                if(!this.deletedData){
                    this.deletedData=[];
                }

                this.deletedData.push( DataHandler.Restore(rowData));
                this.d_modified=true;
            }
        },

        setItem(rowIndex, fieldName,value){
            if(rowIndex===-1)
                rowIndex = this.d_currentRow;
           
           if(!this.d_primaryData ||  rowIndex< 0 || rowIndex>=this.d_primaryData.length ){
                throw new RangeError(`RowIndex:${rowIndex}无效`);
            }

            this.d_primaryData[rowIndex][fieldName] = value;
        },
        getItem(rowIndex, fieldName){
            if(rowIndex===-1)
                rowIndex = this.d_currentRow;
           
            if(!this.d_primaryData ||  rowIndex< 0 || rowIndex>=this.d_primaryData.length ){
                throw new RangeError(`RowIndex:${rowIndex}无效`);
            }
            
            return this.d_primaryData[rowIndex][fieldName] ;
        },

        reset(){
            this.d_primaryData = null;
            this.filteredData = null;
            this.deletedData = null;
            this.d_currentRow = -1;
            this.d_modified=false;
            this.resetPage();
            this.$emit('update:expandedRows',null);
        },

        resetUpdate(){
            const _resetUpdate=(values)=>{
                if(!values)
                    return ;
                
                for(let i=0;i<values.length;i++){
                    values[i].resetUpdate();
                }
            }

            _resetUpdate(this.d_primaryData);
            _resetUpdate(this.filteredData);
            this.deletedData = null;
            this.d_modified=false;
        },

        setFocus(rowIndex,field){
            if(!field){
                this.setRow(rowIndex);

                return;
            }

            const trNodeList= this.$refs.bodyRef.$el.querySelectorAll("tr");

            if(rowIndex<0 || rowIndex> (trNodeList.length -1) )
                rowIndex =trNodeList.length -1

            const cellEl = trNodeList[rowIndex].querySelector(`[data-field=${field}`);

            if(cellEl){
                this.d_currentRow = rowIndex;

                //下面这行代码会导致单元在表格中显示的位置发生改变
                //cellEl.scrollIntoView && cellEl.scrollIntoView({ block: 'nearest', inline: 'start' });
                setTimeout(()=>{ DomHandler.invokeElementMethod(cellEl, 'click') },50);
             }
        },

        rowFocusChanged(rowIndex){
            if(rowIndex>=0 && rowIndex< this.d_primaryData.length){
                const rowData = this.d_primaryData[rowIndex];
                const rowId = ('rowId' in rowData) ? rowData['rowId'] :rowIndex;

                if(rowId!==this.lastFocusRowId){
                    this.lastFocusRowId=rowId;
                    this.$emit('row-focus-changed',{ rowIndex, rowData  } );
                }
            }
        },

        evaluate(funName, field){

            switch(funName.toLowerCase()){
                case 'sum':
                    return this.evaluateSum(field);
                    break;

                case 'count':
                    return this.evaluateCount();
                    break;

                case 'average':
                case 'avg':
                   return this.evaluateAvg(field);
                    break;

                 default:
                    throw(new Error("不支持的计算公式:"+funName));
                    break;
            }

            throw(new Error("不支持的计算公式:"+funName));
        },
        
        evaluateSum(field){

            let data = this.d_primaryData || [];

            return data.reduce((preVal, curVal)=>{
                let  val= ObjectUtils.resolveFieldData(curVal, field) || 0 ;

                if( ( typeof val )==='string'){
                    val= Number.parseFloat(val);

                    if(Number.isNaN(val)){
                        val= 0 ;
                    }
                }

                return preVal+ val ; 
            },0);
        },

        evaluateCount(){
            return (this.d_primaryData ) ? this.d_primaryData.length :0  ;
        } ,

        evaluateAvg(field){
           
             let sum = this.evaluateSum(field);
             let count = this.evaluateCount();
             
            return (count>0) ? (sum / count ) : 0;
        },

        getData(){
            return this.d_primaryData  ;
        },

        primaryData(){
            return this.d_primaryData  ;
        },

        changedData(){
            let data=[];

            if(this.d_primaryData){
               data= this.d_primaryData.filter((item)=>{ 
                    const status =item['rowStatus'] || RowStatus.NotModified;

                    return ( status=== RowStatus.NewModified || status === RowStatus.DataModified ) ;
                } )
            }

            if(this.deletedData && this.deletedData.length){
                data =[...data, ...this.deletedData];
            }

            return data;
        },
        
        rowCount(){
            return (this.d_primaryData ) ? this.d_primaryData.length: 0  ;
        },

        deletedData(){
            return this.deletedData  ;
        },

        deletedCount(){
             return (this.deletedData ) ? this.deletedData.length :0  ;
        },

        filteredData(){
            return this.filteredData  ;
        },
        filteredCount(){
             return (this.filteredData ) ? this.filteredData.length :0   ;
        },

        isModified(){
          /*
          if(this.deletedData && this.deletedData.length)
              return true;

          if( this.d_primaryData &&
               this.d_primaryData.find( (item)=> item.rowStatus=== RowStatus.DataModified || item.rowStatus=== RowStatus.NewModified )){
            return true;
          }
           return false;
          */
           return this.d_modified;
        },
        
        pageParms(){
            return this.d_pageParams
        }
    },

    computed: {
        test22(){
          return this.d_modified;
        },
        containerClass() {
            return [
                'p-datatable p-component',
                'p-datatable-responsive-scroll',
                {
                    'p-datatable-hoverable-rows': this.rowHover || this.selectionMode,
                    'p-datatable-resizable': this.resizableColumns,
                    'p-datatable-resizable-fit': this.resizableColumns && this.columnResizeMode === 'fit',
                    'p-datatable-scrollable': this.scrollable,
                    'p-datatable-flex-scrollable': this.scrollable && this.scrollHeight === 'flex',
                    'p-datatable-striped': this.stripedRows,
                    'p-datatable-gridlines': this.showGridlines,
                    'p-datatable-grouped-header': this.headerColumnGroup != null,
                    'p-datatable-grouped-footer': this.footerColumnGroup != null
                }
            ];
        },
        tableStyleClass() {
            return [
                'p-datatable-table',
                {
                    'p-datatable-scrollable-table': this.scrollable,
                    'p-datatable-resizable-table': this.resizableColumns,
                    'p-datatable-resizable-table-fit': this.resizableColumns && this.columnResizeMode === 'fit'
                },
                this.tableClass
            ];
        },
        columns() {
            let children = this.getChildren();

            if (!children) {
                return;
            }

            const cols = this.recursiveGetChildren(children, []);

            if (this.reorderableColumns && this.d_columnOrder) {
                let orderedColumns = [];

                for (let columnKey of this.d_columnOrder) {
                    let column = this.findColumnByKey(cols, columnKey);

                    if (column && !this.columnProp(column, 'hidden')) {
                        orderedColumns.push(column);
                    }
                }

                return [...orderedColumns, ...cols.filter((item) => orderedColumns.indexOf(item) < 0)];
            }

            return cols;
        },
        headerColumnGroup() {
            const children = this.getChildren();

            if (children) {
                for (let child of children) {
                    if (child.type.name === 'ColumnGroup' && this.columnProp(child, 'type') === 'header') {
                        return child;
                    }
                }
            }

            return null;
        },
        footerColumnGroup() {
            const children = this.getChildren();

            if (children) {
                for (let child of children) {
                    if (child.type.name === 'ColumnGroup' && this.columnProp(child, 'type') === 'footer') {
                        return child;
                    }
                }
            }

            return null;
        },
        hasFilters() {
            return this.filters && Object.keys(this.filters).length > 0 && this.filters.constructor === Object;
        },

        totalRecordsLength() {
            // if (this.lazy) {
            //     return this.totalRecords;
            // } else {
            //     const data = this.processedData;

            //     return data ? data.length : 0;
            // }
            return 10;
        },
        empty() {
            return (!this.d_primaryData || this.d_primaryData.length === 0) ;
        },

        paginatorTop() {
            return this.paginator && (this.paginatorPosition !== 'bottom' || this.paginatorPosition === 'both');
        },
        paginatorBottom() {
            return this.paginator && (this.paginatorPosition !== 'top' || this.paginatorPosition === 'both');
        },
        sorted() {
            return this.d_sortField || (this.d_multiSortMeta && this.d_multiSortMeta.length > 0);
        },
        allRowsSelected() {
           if(!this.d_primaryData || this.d_primaryData.length===0 )
              return false;

            return ( this.d_primaryData.find((rowData)=>!this.isSelected(rowData))) ? false : true;
        },

        attributeSelector() {
            return UniqueComponentId();
        },
        groupRowSortField() {
            return this.sortMode === 'single' ? this.sortField : this.d_groupRowsSortMeta ? this.d_groupRowsSortMeta.field : null;
        },

        // virtualScrollerDisabled() {
        //     return ObjectUtils.isEmpty(this.virtualScrollerOptions) || !this.scrollable;
        // },

       
    },
    
    components: {
        DTPaginator: Paginator,
        DTTableHeader: TableHeader,
        DTTableBody: TableBody,
        DTTableFooter: TableFooter,
        DTVirtualScroller: VirtualScroller,
        ArrowDownIcon: ArrowDownIcon,
        ArrowUpIcon: ArrowUpIcon,
        SpinnerIcon: SpinnerIcon
    }
};
</script>

<style>
.p-datatable {
    position: relative;
    outline:none;
    display: flex;
    flex-direction: column;
}

.p-datatable > .p-datatable-wrapper {
    background-color: #FFFFFF;
    flex: 1;
    overflow: auto;
}

.p-datatable-table {
    border-spacing: 0px;
    width: 100%;
    table-layout: fixed;
    white-space: pre-wrap;  /*让空格保留正常的输出*/
    outline:none;
}

.p-datatable-tbody{
    outline: none;   /*body获得焦点时不显示边框*/
}

.p-datatable .p-sortable-column {
    cursor: pointer;
    user-select: none;
}

.p-datatable .p-sortable-column .p-column-title,
.p-datatable .p-sortable-column .p-sortable-column-icon,
.p-datatable .p-sortable-column .p-sortable-column-badge {
    vertical-align: middle;
}

.p-datatable .p-sortable-column .p-sortable-column-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.p-datatable-hoverable-rows {
    cursor: pointer;
}

/* Scrollable */
.p-datatable-scrollable{
    overflow: hidden;
}

.p-datatable-scrollable > .p-datatable-wrapper {
    position: relative;
}

.p-datatable-scrollable-table > .p-datatable-thead {
    position: sticky;
    top: 0;
    z-index: 1;
}

.p-datatable-scrollable-table > .p-datatable-frozen-tbody {
    position: sticky;
    z-index: 1;
}

.p-datatable-scrollable-table > .p-datatable-tfoot {
    position: sticky;
    /*bottom: 0;*/
    bottom: -1px;  /*设置为 -1px,可解决body滚动时，有部分内容可能会显示在footer底下的问题，应该是设置边框导致的*/
    z-index: 1;
}

.p-datatable-scrollable .p-frozen-column {
    position: sticky;
    background: inherit;
}

.p-datatable-scrollable th.p-frozen-column {
    z-index: 1;
}

.p-datatable-flex-scrollable {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.p-datatable-flex-scrollable > .p-datatable-wrapper {
    display: flex;
    flex-direction: column;
    flex: 1;
    height: 100%;
}

.p-datatable-scrollable-table > .p-datatable-tbody > .p-rowgroup-header {
    position: sticky;
    z-index: 1;
}

/* Resizable */
.p-datatable-resizable-table > .p-datatable-thead > tr > th,
.p-datatable-resizable-table > .p-datatable-tfoot > tr > td,
.p-datatable-resizable-table > .p-datatable-tbody > tr > td {
    overflow: hidden;
    white-space: nowrap;
}

.p-datatable-resizable-table > .p-datatable-thead > tr > th.p-resizable-column:not(.p-frozen-column) {
    background-clip: padding-box;
    position: relative;
}

.p-datatable-resizable-table-fit > .p-datatable-thead > tr > th.p-resizable-column:last-child .p-column-resizer {
    display: none;
}

.p-datatable .p-column-resizer {
    display: block;
    position: absolute !important;
    top: 0;
    right: 0;
    margin: 0;
    width: 0.5rem;
    height: 100%;
    padding: 0px;
    cursor: col-resize;
    border: 1px solid transparent;
}

/*
.p-datatable .p-column-header-content {
    display: flex;
    align-items: center;
}
*/

.p-datatable .p-column-resizer-helper {
    width: 1px;
    position: absolute;
    z-index: 10;
    display: none;
}

.p-datatable .p-row-editor-init,
.p-datatable .p-row-editor-save,
.p-datatable .p-row-editor-cancel {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
}

/* Expand */
.p-datatable .p-row-toggler {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
}

/* Reorder */
.p-datatable-reorder-indicator-up,
.p-datatable-reorder-indicator-down {
    position: absolute;
    display: none;
}

.p-reorderable-column,
.p-datatable-reorderablerow-handle {
    cursor: move;
}

/* Loader */
.p-datatable .p-datatable-loading-overlay {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
}

/* Filter */
.p-column-filter-row {
    display: flex;
    align-items: center;
    width: 100%;
}

.p-column-filter-menu {
    display: inline-flex;
    margin-left: auto;
}

.p-column-filter-row .p-column-filter-element {
    flex: 1 1 auto;
    width: 1%;
}

.p-column-filter-menu-button,
.p-column-filter-clear-button {
    display: inline-flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    text-decoration: none;
    overflow: hidden;
    position: relative;
}

.p-column-filter-overlay {
    position: absolute;
    top: 0;
    left: 0;
}

.p-column-filter-row-items {
    margin: 0;
    padding: 0;
    list-style: none;
}

.p-column-filter-row-item {
    cursor: pointer;
}

.p-column-filter-add-button,
.p-column-filter-remove-button {
    justify-content: center;
}

.p-column-filter-add-button .p-button-label,
.p-column-filter-remove-button .p-button-label {
    flex-grow: 0;
}

.p-column-filter-buttonbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.p-column-filter-buttonbar .p-button:not(.p-button-icon-only) {
    width: auto;
}

/* Responsive */
.p-datatable .p-datatable-tbody > tr > td > .p-column-title {
    display: none;
}

/* VirtualScroller */
.p-datatable-virtualscroller-spacer {
    display: flex;
}

.p-datatable .p-virtualscroller .p-virtualscroller-loading {
    transform: none !important;
    min-height: 0;
    position: sticky;
    top: 0;
    left: 0;
}
</style>
