kohaiy / easytable

A powerful data table based on vuejs. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.
https://easytable.kohai.top/
19 stars 1 forks source link

Excel demo按照Vue3组合式函数写法写完后,展示正常,但滑动事件触发时表格非常卡,且表格渲染的行数不正常,白屏 #5

Closed mengxin-FE closed 2 months ago

mengxin-FE commented 2 months ago

在组件中引入以下hooks可复现

组件代码:

hooks代码: import { ref } from 'vue' import { h } from 'vue'

export function useEasyTable() {

// 列名
const COLUMN_KEYS = [
    "A",
    "B",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "J",
    "K",
    "L",
    "M",
    "N",
    "O",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "U",
    "V",
    "W",
    "X",
    "Y",
    "Z",
];
// const COLUMN_KEYS = [
//  {
//      "prop_name": "state",
//      "type": "viewOrderState",
//      "do_not_delete": true,
//      "#readonly_controlled": "\u53d7\u6743\u9650\u63a7\u5236\u7684\u72b6\u6001\uff0c\u8fd9\u79cd\u5b57\u6bb5\u4f1a\u6839\u636e\u64cd\u4f5c\u7528\u6237\u62e5\u6709\u7684\u6743\u9650\u8bbe\u7f6e\u662f\u5426\u53ea\u8bfb\uff0cpropName\u76ee\u524d\u9700\u8981\u662fcolumns\u4e2d\u5b58\u5728\u7684\u5c5e\u6027\uff0cby\uff0cpermission.js\u5b9a\u4e49\u7684\u6743\u9650",
//      "readonly_controlled": {
//          "propName": "state",
//          "type": "order",
//          "infectToRowWhenReadonly": false
//      },
//      "read_only": true,
//      "refresh": "row",
//      "label": "\u72b6\u6001",
//      "width": "80px",
//      "placeholder": "\u8bf7\u9009\u62e9\u7533\u8bf7\u5355\u72b6\u6001",
//      "extra": {
//          "url": "/api/restful/query/base/variable_option/options?object=li_ord_order&code=state&option=label__label,value__value"
//      }
//  },
//  {
//      "prop_name": "state",
//      "type": "viewOrderOperate",
//      "do_not_delete": true,
//      "label": "\u64cd\u4f5c",
//      "readonly_controlled": {
//          "propName": "state",
//          "type": "order",
//          "infectToRowWhenReadonly": false
//      },
//      "width": "160px",
//      "placeholder": "\u8bf7\u64cd\u4f5c",
//      "refresh": "row",
//      "read_only": true
//  },
//  {
//      "prop_name": "number",
//      "type": "elinput",
//      "do_not_delete": true,
//      "label": "\u7f16\u53f7",
//      "width": "160px",
//      "placeholder": "\u8bf7\u8f93\u5165\u7533\u8bf7\u5355\u7f16\u53f7"
//  },
//  {
//      "prop_name": "customer.name",
//      "type": "elinput",
//      "label": "\u5ba2\u6237",
//      "width": "240px",
//      "placeholder": "\u8bf7\u8f93\u5165\u5ba2\u6237"
//  },
//  {
//      "prop_name": "ownerId",
//      "show_prop_name": "owner.name",
//      "do_not_delete": true,
//      "type": "elrSelect",
//      "label": "\u5ba2\u6237\u7ecf\u7406",
//      "width": "100px",
//      "placeholder": "\u8bf7\u9009\u62e9\u5ba2\u6237\u7ecf\u7406",
//      "extra": {
//          "url": "/api/restful/query/system/user/all?in_filter=model.system.user.User.id__user_ids&user_ids.module=model.system.user_position.UserPosition.user_id&user_ids.in_filter=model.system.user_position.UserPosition.position_id__positions&user_ids.positions.module=model.system.position.Position.id&user_ids.positions.equal_filter=model.system.position.Position.code__sale&equalFilter=model.system.user.User.state_valid__valid&option=label__name,value__id&order_by=model.system.user.User.id&per_page=300"
//      }
//  },
//  {
//      "prop_name": "customer.contactName",
//      "type": "elinput",
//      "label": "\u8054\u7cfb\u4eba",
//      "width": "104px",
//      "placeholder": "\u8bf7\u8f93\u5165\u8054\u7cfb\u4eba"
//  },
//  {
//      "prop_name": "customer.contactPhone",
//      "do_not_delete": true,
//      "type": "elinput",
//      "label": "\u8054\u7cfb\u7535\u8bdd",
//      "width": "130px",
//      "placeholder": "\u8bf7\u8f93\u5165\u8054\u7cfb\u7535\u8bdd"
//  },
//  {
//      "prop_name": "customer.contactAddress",
//      "type": "elinput",
//      "label": "\u8054\u7cfb\u5730\u5740",
//      "width": "180px",
//      "placeholder": "\u8bf7\u8f93\u5165\u8054\u7cfb\u5730\u5740"
//  },
//  {
//      "prop_name": "inputTime",
//      "type": "eldate",
//      "label": "\u5f55\u5165\u65f6\u95f4",
//      "width": "110px",
//      "placeholder": "\u8bf7\u9009\u62e9\u5f55\u5165\u65f6\u95f4"
//  },
//  {
//      "prop_name": "remark",
//      "web_type": "elinput",
//      "type": "elinput",
//      "label": "\u5907\u6ce8\u4fe1\u606f",
//      "width": "160px",
//      "placeholder": "\u8bf7\u8f93\u5165\u5907\u6ce8\u4fe1\u606f",
//      "props": {
//          "options": [
//              {
//                  "value": "",
//                  "label": ""
//              }
//          ]
//      }
//  },
//  {
//      "prop_name": "priority",
//      "web_type": "elinput",
//      "type": "elinput",
//      "label": "\u4f18\u5148\u7ea7",
//      "width": "160px",
//      "placeholder": "\u8bf7\u8f93\u5165\u4f18\u5148\u7ea7",
//      "props": {
//          "options": [
//              {
//                  "value": "",
//                  "label": ""
//              }
//          ]
//      }
//  },
//  {
//      "prop_name": "shareReport",
//      "do_not_delete": true,
//      "type": "viewReporthShare",
//      "label": "\u62a5\u544a\u5206\u4eab",
//      "width": "180px",
//      "placeholder": "\u62a5\u544a\u5206\u4eab",
//      "read_only": true
//  }
// ];
const columns = ref([
    {
        field: "index",
        key: "index",
        // is operation column
        operationColumn: true,
        title: "",
        width: 55,
        fixed: "left",
        renderBodyCell: renderRowIndex,
    },
])
columns.value = columns.value.concat(
    COLUMN_KEYS.map((keyValue) => {
        return {
            title: keyValue,
            field: keyValue,
            key: keyValue,
            width: 90,
            edit: true,
        };
    })
);

const tableData = ref([])
// 初始化表格数据
function initTableData() {
    for (let i = 0; i < 5000; i++) {
        let dataItem = {
            rowKey: i,
        };

        COLUMN_KEYS.forEach((keyValue) => {
            dataItem[keyValue] = "";
        });

        if (i === 1 || i === 3) {
            dataItem["C"] = "YOU";
            dataItem["D"] = "CAN";
            dataItem["E"] = "TRY";
            dataItem["F"] = "ENTER";
            dataItem["G"] = "SOME";
            dataItem["H"] = "WORDS";
            dataItem["I"] = "!!!";
        }

        tableData.value.push(dataItem);
    }

    tableData.value = tableData.value;
}

// start row index
let startRowIndex = 0

// render row index
function renderRowIndex({ row, column, rowIndex }) {
    // return <span>{rowIndex + startRowIndex.value + 1}</span>;    // 需将本文件名后缀改为jsx

    // h函数 https://cn.vuejs.org/api/render-function.html#h
    return h('span', { class: '', innerHTML: rowIndex + startRowIndex + 1 })
}

function scrolling({startRowIndex, visibleStartIndex, visibleEndIndex, visibleAboveCount, visibleBelowCount}) {
    startRowIndex = startRowIndex;
}

const virtualScrollOption = {
    // 是否开启
    enable: true,
    scrolling: scrolling,
}

const columnWidthResizeOption = {
    enable: true,
}

const cellAutofillOption = {
    directionX: true,
    directionY: true,
    beforeAutofill: ({
        direction,
        sourceSelectionRangeIndexes,
        targetSelectionRangeIndexes,
        sourceSelectionData,
        targetSelectionData,
    }) => { },
    afterAutofill: ({
        direction,
        sourceSelectionRangeIndexes,
        targetSelectionRangeIndexes,
        sourceSelectionData,
        targetSelectionData,
    }) => { },
}

// edit option 可控单元格编辑
const editOption = {
    beforeCellValueChange: ({ row, column, changeValue }) => { },
    afterCellValueChange: ({ row, column, changeValue }) => { },
}

// contextmenu header option
const contextmenuHeaderOption = {
    /*
         before contextmenu show.
         In this function,You can change the `contextmenu` options
         */
    beforeShow: ({
        isWholeColSelection,
        selectionRangeKeys,
        selectionRangeIndexes,
    }) => {
        //
    },
    // after menu click
    afterMenuClick: ({
        type,
        selectionRangeKeys,
        selectionRangeIndexes,
    }) => {
        //
    },

    // contextmenus
    contextmenus: [
        {
            type: "CUT",
        },
        {
            type: "COPY",
        },
        {
            type: "SEPARATOR",
        },
        {
            type: "EMPTY_COLUMN",
        },
        {
            type: "SEPARATOR",
        },
        {
            type: "LEFT_FIXED_COLUMN_TO",
        },
        {
            type: "CANCEL_LEFT_FIXED_COLUMN_TO",
        },
        {
            type: "RIGHT_FIXED_COLUMN_TO",
        },
        {
            type: "CANCEL_RIGHT_FIXED_COLUMN_TO",
        },
    ],
}

// contextmenu body option
const contextmenuBodyOption = {
    /*
         before contextmenu show.
         In this function,You can change the `contextmenu` options
         */
    beforeShow: ({
        isWholeRowSelection,
        selectionRangeKeys,
        selectionRangeIndexes,
    }) => {
        console.log("---contextmenu body beforeShow--");
        console.log("isWholeRowSelection::", isWholeRowSelection);
        console.log("selectionRangeKeys::", selectionRangeKeys);
        console.log("selectionRangeIndexes::", selectionRangeIndexes);
    },
    // after menu click
    afterMenuClick: ({
        type,
        selectionRangeKeys,
        selectionRangeIndexes,
    }) => {
        console.log("---contextmenu body afterMenuClick--");
        console.log("type::", type);
        console.log("selectionRangeKeys::", selectionRangeKeys);
        console.log("selectionRangeIndexes::", selectionRangeIndexes);
    },

    // contextmenus
    contextmenus: [
        {
            type: "CUT",
        },
        {
            type: "COPY",
        },
        {
            type: "SEPARATOR",
        },
        {
            type: "INSERT_ROW_ABOVE",
        },
        {
            type: "INSERT_ROW_BELOW",
        },
        {
            type: "SEPARATOR",
        },
        {
            type: "REMOVE_ROW",
        },
        {
            type: "EMPTY_ROW",
        },
        {
            type: "EMPTY_CELL",
        },
    ],
}

const rowStyleOption = {
    clickHighlight: false,
    hoverHighlight: false,
}

return [initTableData, columns, tableData, virtualScrollOption, cellAutofillOption, editOption, contextmenuBodyOption, contextmenuHeaderOption, rowStyleOption, columnWidthResizeOption]

}

kohaiy commented 2 months ago

这段改成这样:

    // start row index
    const startRowIndex = ref(0)

    // render row index
    function renderRowIndex({ row, column, rowIndex }) {
        // return <span>{rowIndex + startRowIndex.value + 1}</span>;    // 需将本文件名后缀改为jsx

        // h函数 https://cn.vuejs.org/api/render-function.html#h
        return h('span', { class: '' }, rowIndex + startRowIndex.value + 1)
    }

    function scrolling({ startRowIndex: _startRowIndex, visibleStartIndex, visibleEndIndex, visibleAboveCount, visibleBelowCount }) {
        startRowIndex.value = _startRowIndex;
    }
kohaiy commented 2 months ago

columns 也要改成 computed:

    const columns = computed(() => [
        {
            field: "index",
            key: "index",
            // is operation column
            operationColumn: true,
            title: "",
            width: 55,
            fixed: "left",
            renderBodyCell: renderRowIndex,
        },
        ...COLUMN_KEYS.map((keyValue) => {
            return {
                title: keyValue,
                field: keyValue,
                key: keyValue,
                width: 90,
                edit: true,
            };
        })
    ])