遇到任何组件和文档有关问题,请提交 issue
如果我看的更远,那是因为我站在巨人的肩膀上。
vue-fantable
English | 中文
介绍
如果你的项目使用 vue2,建议使用 vue-easytable
本项目由 vue-easytable 更新而来,支持 Vue3,使用 ESM(建议 node V18+)体积更小,像使用 vue-easytable 一样,使用 vue-fantable 吧。
特点
- 采用虚拟滚动技术,支持 30 万行数据展示和实时编辑
- 永久开源免费。当然你也可以选择捐赠,保证项目长期维护和功能加速开发
API & 文档
组件使用方式和 vue-easytable 尽可能保持一致。
安装
确保 Vue 版本至少为 3.2
npm install vue-fantable
# or
yarn add vue-fantable
使用
讲一下内容添加到 main.js:
import { createApp } from "vue";
import "vue-fantable/libs/theme-default.css";
import App from './app.vue'
import VueFantable from "vue-fantable";
const app = createApp(App)
app.use(VueFantable);
app.mounted('#app')
示例 1
<template>
<fan-table :columns="columns" :table-data="tableData" :max-height="400"/>
</template>
<script >
export default {
data() {
return {
columns: [
{ field: "name", key: "a", title: "Name", align: "center" },
{ field: "date", key: "b", title: "Date", align: "left" },
{ field: "hobby", key: "c", title: "Hobby", align: "right" },
{ field: "address", key: "d", title: "Address" },
],
tableData: [
{
name: "John",
date: "1900-05-20",
hobby: "coding and coding repeat",
address: "No.1 Century Avenue, Shanghai",
},
{
name: "Dickerson",
date: "1910-06-20",
hobby: "coding and coding repeat",
address: "No.1 Century Avenue, Beijing",
},
{
name: "Larsen",
date: "2000-07-20",
hobby: "coding and coding repeat",
address: "No.1 Century Avenue, Chongqing",
},
{
name: "Geneva",
date: "2010-08-20",
hobby: "coding and coding repeat",
address: "No.1 Century Avenue, Xiamen",
},
{
name: "Jami",
date: "2020-09-20",
hobby: "coding and coding repeat",
address: "No.1 Century Avenue, Shenzhen",
},
],
};
},
};
</script>
示例 2
<template>
<div class="spreadsheet">
<fan-table style="word-break: break-word" fixed-header :scroll-width="0" :max-height="500" border-y :columns="columns"
:table-data="tableData" row-key-field-name="rowKey" :virtual-scroll-option="virtualScrollOption"
:cell-autofill-option="cellAutofillOption" :edit-option="editOption"
:contextmenu-body-option="contextmenuBodyOption" :contextmenu-header-option="contextmenuHeaderOption"
:row-style-option="rowStyleOption" :column-width-resize-option="columnWidthResizeOption" />
</div>
</template>
<script lang="jsx">
const COLUMN_KEYS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N']
export default {
name: 'SpreadSheet',
data() {
return {
// start row index
startRowIndex: 0,
columnWidthResizeOption: {
enable: true,
},
virtualScrollOption: {
// 是否开启
enable: true,
scrolling: this.scrolling,
},
cellAutofillOption: {
directionX: true,
directionY: true,
beforeAutofill: ({
direction,
sourceSelectionRangeIndexes,
targetSelectionRangeIndexes,
sourceSelectionData,
targetSelectionData,
}) => { },
afterAutofill: ({
direction,
sourceSelectionRangeIndexes,
targetSelectionRangeIndexes,
sourceSelectionData,
targetSelectionData,
}) => { },
},
// edit option 可控单元格编辑
editOption: {
beforeCellValueChange: ({ row, column, changeValue }) => { },
afterCellValueChange: ({ row, column, changeValue }) => { },
},
// contextmenu header
contextmenuHeaderOption: {
// before contextmenu show. 你可以在这里更改 contextmenu 配置
beforeShow: ({
isWholeColSelection,
selectionRangeKeys,
selectionRangeIndexes,
}) => {
// do something
},
afterMenuClick: ({
type,
selectionRangeKeys,
selectionRangeIndexes,
}) => {
// do something
},
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', },
],
},
contextmenuBodyOption: {
// before contextmenu show. 你可以在这里更改 contextmenu 配置
beforeShow: ({
isWholeRowSelection,
selectionRangeKeys,
selectionRangeIndexes,
}) => {
console.log('---contextmenu body beforeShow--')
console.log('isWholeRowSelection::', isWholeRowSelection)
console.log('selectionRangeKeys::', selectionRangeKeys)
console.log(
'selectionRangeIndexes::',
selectionRangeIndexes,
)
},
afterMenuClick: ({
type,
selectionRangeKeys,
selectionRangeIndexes,
}) => {
console.log('---contextmenu body afterMenuClick--')
console.log('type::', type)
console.log('selectionRangeKeys::', selectionRangeKeys)
console.log('selectionRangeIndexes::', selectionRangeIndexes)
},
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', },
],
},
rowStyleOption: {
clickHighlight: false,
hoverHighlight: false,
},
tableData: [],
}
},
computed: {
// current local
columns() {
let columns = [
{
field: 'index',
key: 'index',
// is operation column
operationColumn: true,
title: '',
width: 55,
fixed: 'left',
renderBodyCell: this.renderRowIndex,
},
]
columns = columns.concat(
COLUMN_KEYS.map((keyValue) => {
return {
title: keyValue,
field: keyValue,
key: keyValue,
width: 90,
edit: true,
}
}),
)
return columns
},
},
created() {
this.initTableData()
},
methods: {
// 渲染 row index
renderRowIndex({ row, column, rowIndex }) {
return (<span>{rowIndex + this.startRowIndex + 1}</span>)
},
scrolling({
startRowIndex,
visibleStartIndex,
visibleEndIndex,
visibleAboveCount,
visibleBelowCount,
}) {
this.startRowIndex = startRowIndex
},
initTableData() {
const tableData = []
for (let i = 0; i < 5000; i++) {
const 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.push(dataItem)
}
this.tableData = tableData
},
},
}
</script>
<style lang="less">
.spreadsheet {
padding: 0 100px;
margin: 30px 0;
}
</style>
更多功能支持
其它基础组件
Table 组件
如果没有你想要的的功能,请告诉我
支持环境
Edge | Firefox | Chrome | Safari | Opera |
---|
Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
贡献者们
感谢 easytable 原项目工作者们,以及维护者 huangshuwei 🙏,本项目继承自 vue-easytable@2.27.1。
如何贡献
- 点击 :star: 让更多的人了解到我们
- 如果你希望参与贡献,欢迎 Pull Request
License
http://www.opensource.org/licenses/mit-license.php