├── .prettierignore ├── pnpm-workspace.yaml ├── .stylelintrc ├── vv-react-table ├── src │ ├── vite-env.d.ts │ ├── ReactTable │ │ ├── global.d.ts │ │ ├── utils │ │ │ ├── log.ts │ │ │ ├── full.ts │ │ │ ├── index.ts │ │ │ └── onContextMenu.ts │ │ ├── components │ │ │ ├── TableHeader.tsx │ │ │ └── SelectInput.tsx │ │ ├── style │ │ │ └── index.scss │ │ ├── props.ts │ │ ├── columns.tsx │ │ ├── types │ │ │ └── index.ts │ │ └── index.tsx │ ├── main.tsx │ └── App.tsx ├── vite.config.ts ├── README.md ├── tsconfig.node.json ├── index.html ├── package.pro.json ├── tsconfig.json ├── scripts │ ├── tsconfig.json │ └── build.ts ├── public │ └── vite.svg ├── package.json └── rollup.config.js ├── public ├── logo.png ├── rwm.jpeg ├── logo2.png ├── logo3.png └── index.css ├── .husky ├── pre-commit └── commit-msg ├── .eslintrc.js ├── .gitignore ├── .fatherrc.ts ├── .editorconfig ├── tsconfig.json ├── .prettierrc.js ├── docs ├── index.md └── guide │ └── index.md ├── .dumirc.ts ├── LICENSE ├── README.md ├── package.json └── src └── vv-react-table └── index.md /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | *.yaml 3 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - vv-react-table 3 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@umijs/lint/dist/config/stylelint" 3 | } 4 | -------------------------------------------------------------------------------- /vv-react-table/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyh0371/vv-react-table/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/rwm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyh0371/vv-react-table/HEAD/public/rwm.jpeg -------------------------------------------------------------------------------- /public/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyh0371/vv-react-table/HEAD/public/logo2.png -------------------------------------------------------------------------------- /public/logo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyh0371/vv-react-table/HEAD/public/logo3.png -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/global.d.ts: -------------------------------------------------------------------------------- 1 | declare type Obj = { 2 | [key: string]: any; 3 | }; 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: require.resolve('@umijs/lint/dist/config/eslint'), 3 | }; 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit "${1}" 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | .dumi/tmp 3 | .dumi/tmp-test 4 | .dumi/tmp-production 5 | docs-dist 6 | node_modules 7 | vv-react-table/node_modules 8 | vv-react-table/dist 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'father'; 2 | 3 | export default defineConfig({ 4 | // more father config: https://github.com/umijs/father/blob/master/docs/config.md 5 | esm: { output: 'dist' }, 6 | }); 7 | -------------------------------------------------------------------------------- /vv-react-table/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /vv-react-table/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(); 6 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/utils/log.ts: -------------------------------------------------------------------------------- 1 | const name = 'vv-react-table'; 2 | 3 | /** 4 | * @name 封装 console.error 5 | * @param info 6 | */ 7 | export const errorLog = (info: string) => { 8 | console.error(`${name}:${info}`); 9 | }; 10 | -------------------------------------------------------------------------------- /vv-react-table/README.md: -------------------------------------------------------------------------------- 1 | ## 基于 `rsuite-table` 封装的数据驱动虚拟列表 2 | 3 | https://juejin.cn/post/7034451277878657055#heading-7 4 | 5 | ## 固定列支持 ✅ 6 | 7 | 支持 checkbox 和 radio ✅ 8 | 9 | ## 合并单元格支持 10 | 11 | ## 右键菜单 12 | 13 | ## 编辑行 14 | 15 | ## 列拖拽 16 | -------------------------------------------------------------------------------- /vv-react-table/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | h1.dumi-default-hero-title { 2 | font-size: 100px; 3 | } 4 | 5 | #root .dumi-default-logo img { 6 | height: 50px; 7 | } 8 | 9 | #root .dumi-default-hero { 10 | height: 700px; 11 | } 12 | 13 | #root .dumi-default-features .dumi-default-features-item { 14 | text-align: center; 15 | } 16 | 17 | .dumi-default-search-bar-svg { 18 | left: 10px; 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "declaration": true, 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "jsx": "react", 8 | "baseUrl": "./", 9 | "paths": { 10 | "@@/*": [".dumi/tmp/*"], 11 | "src": ["src"], 12 | "src/*": ["src/*", "*"] 13 | } 14 | }, 15 | "include": [".dumi/**/*", ".dumirc.ts", "src/**/*"] 16 | } 17 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/components/TableHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HeaderCell } from 'rsuite-table'; 3 | 4 | interface TableHeaderPropsType { 5 | children: React.ReactNode; 6 | } 7 | const TableHeader: React.FC = (props) => { 8 | // console.log('children', children); 9 | 10 | return {props.children}; 11 | }; 12 | 13 | export default TableHeader; 14 | -------------------------------------------------------------------------------- /vv-react-table/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/style/index.scss: -------------------------------------------------------------------------------- 1 | @import './theme.scss'; 2 | 3 | .rs-table-cell-header .rs-table-cell-content { 4 | line-height: unset; 5 | } 6 | .vv-table-isFull { 7 | position: fixed; 8 | top: 0; 9 | bottom: 0; 10 | right: 0; 11 | left: 0; 12 | background-color: #fff; 13 | } 14 | .rs-table .vv-bg-active .rs-table-cell { 15 | background-color: pink; 16 | } 17 | 18 | .lyh-react-table-wrapper { 19 | height: 100%; 20 | } 21 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pluginSearchDirs: false, 3 | plugins: [ 4 | require.resolve('prettier-plugin-organize-imports'), 5 | require.resolve('prettier-plugin-packagejson'), 6 | ], 7 | printWidth: 80, 8 | proseWrap: 'never', 9 | singleQuote: true, 10 | trailingComma: 'all', 11 | overrides: [ 12 | { 13 | files: '*.md', 14 | options: { 15 | proseWrap: 'preserve', 16 | }, 17 | }, 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hero: 3 | title: vv-react-table 4 | description: 5 | actions: 6 | - text: 开始使用 7 | link: /components/vv-react-table 8 | - text: 查看源码 9 | link: https://github.com/lyh0371/vv-react-table 10 | features: 11 | - title: TypeScript 12 | emoji: ✌ 13 | description: 友好的类型提示 14 | - title: 丰富的案例说明 15 | emoji: ✊ 16 | description: 支持大多数使用场景 17 | - title: 可扩展 18 | emoji: 🤝 19 | description: 超强的扩展性 20 | --- 21 | -------------------------------------------------------------------------------- /.dumirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | 3 | export default defineConfig({ 4 | outputPath: 'docs-dist', 5 | base: '/', 6 | themeConfig: { 7 | name: '首页', 8 | logo: '/logo.png', 9 | prefersColor: { 10 | default: 'light', 11 | switch: false, 12 | }, 13 | footer: 14 | '刘小灰出品', 15 | }, 16 | 17 | styles: [`/index.css`], 18 | }); 19 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/utils/full.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name 使table全屏 3 | * @param target table DOM 4 | */ 5 | 6 | let _index = 2000; 7 | export const fullCore = (target: HTMLDivElement) => { 8 | const className = 'vv-table-isFull'; 9 | // 判断是不是全屏状态 10 | const tableStatus = target.classList.contains(className); 11 | 12 | if (!tableStatus) { 13 | // 放大 14 | target.style.zIndex = `${_index + 1}`; 15 | target.classList.add(className); 16 | } else { 17 | target.classList.remove(className); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /vv-react-table/package.pro.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vv-react-table", 3 | "version": "0.0.5", 4 | "type": "module", 5 | "main": "index.esm.js", 6 | "module": "index.esm.js", 7 | "types": "type/index.d.ts", 8 | "style": "index.esm.css", 9 | "keywords": ["vv-react-table", "table"], 10 | "license": "MIT", 11 | "peerDependencies": { 12 | "rsuite-table": "^5.8.1" 13 | }, 14 | "dependencies": { 15 | "ahooks": "^3.7.4", 16 | "lodash": "^4.17.21", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "prop-types": "^15.8.1", 20 | "rsuite-table": "^5.8.1" 21 | }, 22 | "browserslist": ["> 1%", "not ie 11", "not op_mini all"] 23 | } 24 | -------------------------------------------------------------------------------- /vv-react-table/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "ESNext", 5 | "baseUrl": ".", 6 | "useDefineForClassFields": true, 7 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "module": "ESNext", 15 | "moduleResolution": "Node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx" 20 | }, 21 | "include": ["src"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /vv-react-table/scripts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "ESNext", 5 | "baseUrl": ".", 6 | "useDefineForClassFields": true, 7 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "module": "commonjs", 15 | "moduleResolution": "Node", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx" 20 | }, 21 | "include": ["src"], 22 | "references": [{ "path": "../tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/props.ts: -------------------------------------------------------------------------------- 1 | import { cloneDeep } from 'lodash'; 2 | 3 | import { columnsType, ReactTableType } from './types'; 4 | 5 | export const columnProps = (props: columnsType) => { 6 | type itemType = Partial; 7 | const propsItem: itemType = cloneDeep(props); 8 | delete propsItem.dataIndex; 9 | delete propsItem.title; 10 | delete propsItem.render; 11 | delete propsItem.rightClickMenu; 12 | delete propsItem.contextMenu; 13 | 14 | return propsItem; 15 | }; 16 | 17 | export const tableProps = (props: ReactTableType) => { 18 | type propsType = Partial; 19 | const propsItem: propsType = cloneDeep(props); 20 | delete propsItem.columns; 21 | delete propsItem.rowSelection; 22 | delete propsItem.dbClickFull; 23 | delete propsItem.rightClickMenu; 24 | return propsItem; 25 | }; 26 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './log'; 2 | export function flatMap(array: T[], callback: (value: T, index: number, array: T[]) => U[]): U[] { 3 | const result: U[] = []; 4 | 5 | array.forEach((value, index) => { 6 | result.push(...callback(value, index, array)); 7 | }); 8 | 9 | return result; 10 | } 11 | 12 | export function fromEntries(entries: Iterable) { 13 | const result: { [k in PropertyKey]: T } = {}; 14 | for (const [key, value] of entries) { 15 | result[key as any] = value; 16 | } 17 | return result; 18 | } 19 | 20 | export const arrayUtils = { 21 | diff(arr1: string[], arr2: Iterable) { 22 | const set = new Set(arr2); 23 | return arr1.filter((x) => !set.has(x)); 24 | }, 25 | merge(arr1: string[], arr2: string[]) { 26 | const set = new Set(arr1); 27 | return arr1.concat(arr2.filter((x) => !set.has(x))); 28 | }, 29 | } as const; 30 | 31 | export function always(value: T) { 32 | return (...args: any[]) => value; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | title: 指南 4 | order: -1 5 | group: 6 | title: 介绍 7 | order: -1 8 | --- 9 | 10 | # 什么是 vv-react-table 11 | 12 | vv-react-table 是一款基于 [rsuite-table](https://github.com/rsuite/rsuite-table)二次封装的虚拟表格组件。支持通过 JSON 配置生成虚拟表格,并对 `rsuite-table` 进行了扩展 13 | 14 | ## 功能 15 | 16 | vv-react-table 主要支持以下功能: 17 | 18 | - 🚀 **表格宽度拖拽** 需设置固定宽度即可实现宽度拖拽 19 | - 🔍 **拖拽行和列**:表格行和列可自由拖拽 20 | - 🎨 **右键菜单**:支持给每一列单独设置右键菜单,右键菜单样式可自定义 21 | - 🚥 **表头及列的合并**:通过简单配置即可时间表头和列的合并 22 | - 💡 **行内编辑**:对行内元素进行编辑 23 | - 💎 更多高级用法请看[示例](/components/vv-react-table) 24 | 25 | ## 使用 26 | 27 | 1. 下载依赖 28 | 29 | ```shell 30 | 31 | npm install vv-react-table -S 32 | # or 33 | yarn add vv-react-table -S 34 | # or 35 | pnpm install vv-react-table -S 36 | 37 | ``` 38 | 39 | 2. 引入组件及样式 40 | 41 | ```js 42 | import ReactTable from 'vv-react-table'; 43 | import 'vv-react-table/index.esm.css'; 44 | ``` 45 | 46 | > 未了支持 ESM 实现大统,`vv-react-table` 只支持 ESM 规范 47 | 48 | ## 问题反馈 49 | 50 | 如果在使用过程中发现任何问题、或者有改善建议,欢迎在 GitHub Issues 进行反馈:https://github.com/lyh0371/vv-react-table/issues 51 | 52 | 或加我微信(lyh1347635797)直接反馈(加微信请备注虚拟表格): 53 | 54 |
55 | 56 |
57 | -------------------------------------------------------------------------------- /vv-react-table/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | sp-ui 4 |

vv-react-table

5 |

6 |

7 | GitHub stars 8 | GitHub forks 9 |

10 | 11 | # 什么是 vv-react-table 12 | 13 | vv-react-table 是一款基于 [rsuite-table](https://github.com/rsuite/rsuite-table)二次封装的虚拟表格组件。支持通过 JSON 配置生成虚拟表格,并对 `rsuite-table` 进行了扩展 14 | 15 | ## 功能 16 | 17 | vv-react-table 主要支持以下功能: 18 | 19 | - 🚀 **表格宽度拖拽** 需设置固定宽度即可实现宽度拖拽 20 | - 🔍 **拖拽行和列**:表格行和列可自由拖拽 21 | - 🎨 **右键菜单**:支持给每一列单独设置右键菜单,右键菜单样式可自定义 22 | - 🚥 **表头及列的合并**:通过简单配置即可时间表头和列的合并 23 | - 💡 **行内编辑**:对行内元素进行编辑 24 | - 💎 更多高级用法请看[示例](http://www.h5love.cn/components/vv-react-table) 25 | 26 | ## 使用 27 | 28 | 1. 下载依赖 29 | 30 | ```shell 31 | 32 | yarn install vv-react-table -S 33 | 34 | ``` 35 | 36 | 2. 引入组件及样式 37 | 38 | ```js 39 | import ReactTable from 'vv-react-table'; 40 | import 'vv-react-table/index.esm.css'; 41 | ``` 42 | 43 | ## 问题反馈 44 | 45 | 如果在使用过程中发现任何问题、或者有改善建议,欢迎在 GitHub Issues 进行反馈:https://github.com/lyh0371/vv-react-table/issues 46 | 47 | 或加我微信(lyh1347635797)直接反馈(加微信请备注虚拟表格): 48 | 49 |
50 | 51 |
52 | -------------------------------------------------------------------------------- /vv-react-table/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vv-react-table/vv-react-table", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "main": "./src/ReactTable", 7 | "scripts": { 8 | "build": "rimraf dist/ && esno ./scripts/build.ts", 9 | "build:web": "tsc && vite build", 10 | "d": "rimraf dist/ && rollup --config rollup.config.js", 11 | "dev": "vite", 12 | "preview": "vite preview" 13 | }, 14 | "dependencies": { 15 | "ahooks": "^3.7.4", 16 | "lodash": "^4.17.21", 17 | "react": "^18.2.0", 18 | "react-dom": "^18.2.0", 19 | "rsuite-table": "^5.8.1" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.20.12", 23 | "@babel/preset-env": "^7.20.2", 24 | "@babel/preset-react": "^7.18.6", 25 | "@babel/preset-typescript": "^7.18.6", 26 | "@rollup/plugin-babel": "^6.0.3", 27 | "@rollup/plugin-commonjs": "^24.0.0", 28 | "@rollup/plugin-json": "^6.0.0", 29 | "@rollup/plugin-node-resolve": "^15.0.1", 30 | "@types/lodash": "^4.14.191", 31 | "@types/node-sass": "^4.11.3", 32 | "@types/react": "^18.0.26", 33 | "@types/react-dom": "^18.0.9", 34 | "@vitejs/plugin-react": "^3.0.0", 35 | "esno": "^0.16.3", 36 | "less": "^4.1.3", 37 | "node-sass": "^8.0.0", 38 | "prop-types": "^15.8.1", 39 | "rimraf": "^3.0.2", 40 | "rollup": "^3.9.1", 41 | "rollup-plugin-dts": "^5.1.1", 42 | "rollup-plugin-postcss": "^4.0.2", 43 | "rollup-plugin-typescript2": "^0.34.1", 44 | "sass": "^1.57.1", 45 | "scss": "^0.2.4", 46 | "ts-node": "^10.9.1", 47 | "typescript": "^4.9.3", 48 | "vite": "^4.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/columns.tsx: -------------------------------------------------------------------------------- 1 | import { Cell, Column, ColumnGroup, HeaderCell } from 'rsuite-table'; 2 | import { columnProps } from './props'; 3 | import { columnsType } from './types'; 4 | 5 | const TableColumns = (columns: columnsType[]) => { 6 | const baseColums = (item: columnsType, index: number) => { 7 | return ( 8 | 9 | {item.title} 10 | {item.render && typeof item.render === 'function' ? ( 11 | 12 | {(rowData, index) => { 13 | return item.render!(rowData as columnsType, index); 14 | }} 15 | 16 | ) : ( 17 | 18 | )} 19 | 20 | ); 21 | }; 22 | 23 | return ( 24 | <> 25 | {columns 26 | .filter((item) => !item.hidden) 27 | .map((item, index) => { 28 | console.log(item.columnChildren); 29 | 30 | if (item.columnChildren) { 31 | return ( 32 | 39 | {item.columnChildren.map((citem: any) => 40 | baseColums(citem, index), 41 | )} 42 | 43 | ); 44 | } 45 | return baseColums(item, index); 46 | })} 47 | 48 | ); 49 | }; 50 | export default TableColumns; 51 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/types/index.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ColumnProps, RowDataType, TableProps } from 'rsuite-table'; 3 | 4 | export type RowSelection = { 5 | type?: 'checkbox' | 'radio'; // 类型 6 | key: string; // 唯一标识 7 | defaultSelectedAllRows?: boolean; // 是否默认选中全部 8 | defaultSelectedKeys?: string[]; // 默认选中 9 | onChange: ( 10 | ids: string[], 11 | rows: Obj[], 12 | id: string | undefined, 13 | row: Obj, 14 | ) => void; // 点击选择的执行函数 15 | selectRender?: () => void; // 可以自定义选择逻辑 比如样式 是否可以选中 16 | fixed?: 'left'; 17 | width?: number; 18 | }; 19 | //每一列的类型 20 | export interface columnsType extends ColumnProps { 21 | /** 22 | * 每一列的类型 23 | */ 24 | title: string; // 头部标题 25 | dataIndex?: string; // 字段 26 | hidden?: boolean; // 是否隐藏 27 | contextMenu?: boolean; // 是否开启右键菜单 28 | rightClickMenu?: { 29 | render: (row: any, index: number) => React.ReactNode; 30 | }; // 自定义此列右键菜单 31 | render?: (row: any, index?: number) => JSX.Element; 32 | columnChildren?: columnsType[]; // 合并头部单元格 33 | groupHeaderHeight?: number; 34 | } 35 | 36 | // table 类型 37 | export interface ReactTableType 38 | extends Omit { 39 | /** 40 | * table props 类型 41 | */ 42 | columns: columnsType[]; 43 | rowSelection?: RowSelection; // 是否支持选择行 44 | data: RowDataType[]; 45 | dbClickFull?: boolean; 46 | rightClickMenu?: { 47 | render: (row: any, index: number) => React.ReactNode; 48 | }; // 右键菜单 49 | } 50 | 51 | export interface TColumn extends Omit { 52 | /** 53 | * column 类型 54 | */ 55 | dataIndex?: keyof T; 56 | render?: (row: T, index?: number) => JSX.Element; 57 | } 58 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/utils/onContextMenu.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { RowDataType } from 'rsuite-table'; 4 | import { columnsType } from '../types'; 5 | import { errorLog } from './log'; 6 | 7 | const onContextMenu = ( 8 | e: React.MouseEvent, 9 | rowData: RowDataType, 10 | index: number, 11 | item: columnsType, 12 | rightClickMenu?: { 13 | render: (row: any, index: number) => React.ReactNode; 14 | }, 15 | ) => { 16 | if (item.contextMenu) { 17 | // 说明开启了右键菜单 18 | 19 | // 先关闭所有的右键菜单 20 | const vvMenu = document.querySelector('#vv-menu') as HTMLDivElement; 21 | if (vvMenu) { 22 | vvMenu.style.display = 'none'; 23 | document.body.removeChild(vvMenu); 24 | } 25 | const divWarp = document.createElement('div'); 26 | divWarp.style.position = 'absolute'; 27 | divWarp.setAttribute('id', 'vv-menu'); 28 | divWarp.style.zIndex = '1000'; 29 | divWarp.style.top = `${e.clientY}px`; 30 | divWarp.style.left = `${e.clientX}px`; 31 | divWarp.style.background = '#fff'; 32 | divWarp.style.boxShadow = '1px 1px 10px #ccc'; 33 | document.body.appendChild(divWarp); 34 | 35 | let renderDome = rightClickMenu?.render(rowData, index); 36 | if (item.rightClickMenu) { 37 | if (!item.rightClickMenu.render) { 38 | errorLog('请提供 render函数'); 39 | return false; 40 | } 41 | renderDome = item.rightClickMenu?.render(rowData, index); 42 | } 43 | ReactDOM.createRoot(divWarp as HTMLElement).render(renderDome); 44 | document.body.addEventListener('click', (e) => { 45 | const vvMenu = document.querySelector('#vv-menu') as HTMLDivElement; 46 | if (vvMenu) { 47 | vvMenu.style.display = 'none'; 48 | document.body.removeChild(vvMenu); 49 | } 50 | }); 51 | 52 | e.preventDefault(); 53 | } 54 | }; 55 | 56 | export default onContextMenu; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vv-react-table", 3 | "version": "0.0.1", 4 | "description": "", 5 | "homepage": "http://www.h5love.cn/", 6 | "license": "MIT", 7 | "module": "dist/index.js", 8 | "types": "dist/index.d.ts", 9 | "files": [ 10 | "dist" 11 | ], 12 | "scripts": { 13 | "build": "father build", 14 | "build:watch": "father dev", 15 | "dev": "dumi dev", 16 | "docs:build": "dumi build", 17 | "doctor": "father doctor", 18 | "lint": "npm run lint:es && npm run lint:css", 19 | "lint:css": "stylelint \"{src,test}/**/*.{css,less}\"", 20 | "lint:es": "eslint \"{src,test}/**/*.{js,jsx,ts,tsx}\"", 21 | "prepare": "husky install && dumi setup", 22 | "prepublishOnly": "father doctor && npm run build", 23 | "start": "npm run dev" 24 | }, 25 | "commitlint": { 26 | "extends": [ 27 | "@commitlint/config-conventional" 28 | ] 29 | }, 30 | "lint-staged": { 31 | "*.{md,json}": [ 32 | "prettier --write --no-error-on-unmatched-pattern" 33 | ], 34 | "*.{css,less}": [ 35 | "stylelint --fix", 36 | "prettier --write" 37 | ], 38 | "*.{js,jsx}": [ 39 | "eslint --fix", 40 | "prettier --write" 41 | ], 42 | "*.{ts,tsx}": [ 43 | "eslint --fix", 44 | "prettier --parser=typescript --write" 45 | ] 46 | }, 47 | "dependencies": { 48 | "@vv-react-table/vv-react-table": "workspace:^0.0.0" 49 | }, 50 | "devDependencies": { 51 | "@commitlint/cli": "^17.1.2", 52 | "@commitlint/config-conventional": "^17.1.0", 53 | "@types/react": "^18.0.26", 54 | "@umijs/lint": "^4.0.0", 55 | "dumi": "^2.0.2", 56 | "eslint": "^8.23.0", 57 | "father": "^4.1.0", 58 | "husky": "^8.0.1", 59 | "lint-staged": "^13.0.3", 60 | "prettier": "^2.7.1", 61 | "prettier-plugin-organize-imports": "^3.0.0", 62 | "prettier-plugin-packagejson": "^2.2.18", 63 | "react": "^18.2.0", 64 | "react-dom": "^18.0.0", 65 | "stylelint": "^14.9.1" 66 | }, 67 | "peerDependencies": { 68 | "react": ">=16.9.0", 69 | "react-dom": ">=16.9.0" 70 | }, 71 | "publishConfig": { 72 | "access": "public" 73 | }, 74 | "authors": [] 75 | } 76 | -------------------------------------------------------------------------------- /vv-react-table/scripts/build.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @name 使用 rollup 对 vv-react-table 打包 3 | */ 4 | import babel from '@rollup/plugin-babel'; 5 | import commonjs from '@rollup/plugin-commonjs'; 6 | import json from '@rollup/plugin-json'; 7 | import resolve from '@rollup/plugin-node-resolve'; 8 | import fs from 'fs'; 9 | import { OutputOptions, rollup, RollupOptions } from 'rollup'; 10 | import dts from 'rollup-plugin-dts'; 11 | import postcss from 'rollup-plugin-postcss'; 12 | import typescript from 'rollup-plugin-typescript2'; 13 | const external = ['lodash', 'rsuite-table', 'react', 'react-dom']; 14 | const inputConfig: RollupOptions = { 15 | input: 'src/ReactTable/index.tsx', 16 | plugins: [ 17 | typescript({ 18 | tsconfig: 'tsconfig.json', 19 | }), 20 | commonjs(), 21 | resolve({ 22 | extensions: ['.mjs', '.js', '.jsx', '.json', '.node'], 23 | }), 24 | 25 | json(), 26 | babel({ 27 | presets: ['@babel/preset-env'], 28 | extensions: ['.js', '.jsx', '.ts', '.tsx'], 29 | exclude: '**/node_modules/**', 30 | babelHelpers: 'bundled', 31 | }), 32 | postcss({ 33 | extract: true, 34 | }), 35 | ], 36 | external, 37 | treeshake: { 38 | moduleSideEffects: false, 39 | }, 40 | }; 41 | 42 | // 打包组件 43 | const buildCore = async () => { 44 | const build = await rollup(inputConfig); 45 | return build.write({ 46 | dir: 'dist', 47 | format: 'esm', 48 | entryFileNames: '[name].esm.js', 49 | } as OutputOptions); 50 | }; 51 | 52 | // 生成.d.ts文件 53 | const dtsCore = async () => { 54 | const build = await rollup({ 55 | input: 'src/ReactTable/index.tsx', 56 | plugins: [ 57 | postcss({ 58 | extract: true, 59 | }), 60 | dts({ 61 | compilerOptions: { 62 | allowJs: true, 63 | }, 64 | }), 65 | ], 66 | }); 67 | return build.write({ 68 | filename: 'index.d.ts', 69 | dir: 'dist/type', 70 | } as OutputOptions); 71 | }; 72 | 73 | // 处理package.json 74 | 75 | const pkgCore = async () => { 76 | let pkg = fs.readFileSync('package.pro.json', 'utf-8'); 77 | fs.writeFileSync('dist/package.json', pkg); 78 | }; 79 | 80 | // 复制 README 81 | 82 | const copyReadme = () => { 83 | fs.copyFile('../README.md', 'dist/README.md', () => {}); 84 | }; 85 | 86 | // 打包所有 87 | const buildAll = async () => { 88 | await buildCore(); 89 | await dtsCore(); 90 | await pkgCore(); 91 | copyReadme(); 92 | }; 93 | buildAll(); 94 | -------------------------------------------------------------------------------- /vv-react-table/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import typescript from 'rollup-plugin-typescript2'; 3 | import json from '@rollup/plugin-json'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | import babel from '@rollup/plugin-babel'; 6 | import dts from 'rollup-plugin-dts'; 7 | import postcss from 'rollup-plugin-postcss'; 8 | import sass from 'node-sass'; 9 | const external = ['lodash', 'rsuite-table', 'react', 'react-dom']; 10 | const babelOptions = { 11 | presets: ['@babel/preset-env'], 12 | extensions: ['.js', '.jsx', '.ts', '.tsx', '.scss'], 13 | exclude: '**/node_modules/**', 14 | }; 15 | 16 | const processScss = function (context) { 17 | return new Promise((resolve, reject) => { 18 | sass.compile( 19 | { 20 | file: context, 21 | }, 22 | function (err, result) { 23 | if (!err) { 24 | resolve(result); 25 | } else { 26 | reject(result); 27 | } 28 | } 29 | ); 30 | sass.compile(context, {}).then( 31 | function (output) { 32 | if (output && output.css) { 33 | resolve(output.css); 34 | } else { 35 | reject({}); 36 | } 37 | }, 38 | function (err) { 39 | reject(err); 40 | } 41 | ); 42 | }); 43 | }; 44 | const config = (arg) => ({ 45 | plugins: [ 46 | typescript({ 47 | tsconfig: 'tsconfig.json', 48 | }), 49 | commonjs(), 50 | resolve({ 51 | extensions: ['.mjs', '.js', '.jsx', '.json', '.node'], 52 | }), 53 | postcss({ 54 | extract: true, 55 | process: processScss, 56 | }), 57 | json(), 58 | babel(babelOptions), 59 | ], 60 | external, 61 | treeshake: { 62 | moduleSideEffects: false, 63 | }, 64 | 65 | ...arg, 66 | }); 67 | 68 | const input = 'src/ReactTable/index.tsx'; 69 | 70 | export default [ 71 | config({ 72 | input: input, 73 | output: { 74 | dir: 'dist', 75 | format: 'esm', 76 | entryFileNames: '[name].esm.js', 77 | chunkFileNames: 'chunks/ali-react-table-[name]-[hash].esm.js', 78 | }, 79 | }), 80 | config({ 81 | input: input, 82 | output: { 83 | dir: 'dist', 84 | format: 'cjs', 85 | entryFileNames: '[name].js', 86 | chunkFileNames: 'chunks/ali-react-table-[name]-[hash].js', 87 | }, 88 | }), 89 | { 90 | input: input, 91 | output: [{ filename: 'index.d.ts', dir: 'dist/es/type', format: 'esm' }], 92 | plugins: [ 93 | postcss({ 94 | extract: true, 95 | process: processScss, 96 | }), 97 | dts({ 98 | exclude: ['*.scss', '*.css'], 99 | }), 100 | ], 101 | }, 102 | ]; 103 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/components/SelectInput.tsx: -------------------------------------------------------------------------------- 1 | import { useUpdateEffect } from 'ahooks'; 2 | import React, { useRef, useState } from 'react'; 3 | import { Cell, Column, HeaderCell, RowDataType } from 'rsuite-table'; 4 | import { RowSelection } from '../types'; 5 | import { errorLog } from '../utils'; 6 | const SelectInput: React.FC<{ 7 | rowSelection: RowSelection; 8 | data: RowDataType[]; 9 | headerHeight?: number; 10 | rowHeight?: number | ((rowData?: RowDataType) => number); 11 | }> = ({ 12 | rowSelection: { 13 | width = 30, 14 | type = 'checkbox', 15 | fixed = 'left', 16 | key = 'id', 17 | onChange, 18 | }, 19 | data, 20 | headerHeight = 40, 21 | rowHeight = 46, 22 | }) => { 23 | const [ids, setIds] = useState([]); 24 | const keyId = useRef(''); 25 | const rowItem = useRef({}); 26 | const inputChange = (e: React.ChangeEvent, rowData: Obj) => { 27 | console.log('rowData', rowData); 28 | 29 | if (rowData[key] === undefined) { 30 | errorLog(`data里需要包含${key}`); 31 | return false; 32 | } 33 | const checked = (e.target as HTMLInputElement).checked; 34 | const id = rowData[key]; 35 | if (checked) { 36 | setIds([...ids, id]); 37 | rowItem.current = rowData; 38 | } else { 39 | setIds((ids) => ids.filter((idItem) => idItem != id)); 40 | rowItem.current = {}; 41 | } 42 | keyId.current = id; 43 | }; 44 | 45 | // 点击全选按钮执行 46 | const allInputChange = (e: React.ChangeEvent) => { 47 | const checked = (e.target as HTMLInputElement).checked; 48 | // 全选了 49 | if (checked) { 50 | setIds(() => 51 | data.map((item) => { 52 | return item[key]; 53 | }), 54 | ); 55 | } else { 56 | // 不全选 57 | setIds([]); 58 | } 59 | keyId.current = undefined; 60 | rowItem.current = {}; 61 | }; 62 | 63 | useUpdateEffect(() => { 64 | const rows = data.filter((item: Obj) => ids.includes(item[key])); 65 | onChange(ids, rows, keyId.current, rowItem.current); 66 | }, [ids]); 67 | 68 | return ( 69 | 70 | 71 | {type === 'checkbox' && ( 72 | allInputChange(e)} 76 | /> 77 | )} 78 | 79 | 85 | {(rowData) => { 86 | return ( 87 | id === rowData[key])} 91 | onChange={(e) => inputChange(e, rowData)} 92 | /> 93 | ); 94 | }} 95 | 96 | 97 | ); 98 | }; 99 | 100 | export default SelectInput; 101 | -------------------------------------------------------------------------------- /vv-react-table/src/ReactTable/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useRef } from 'react'; 2 | import { Cell, Column, ColumnGroup, HeaderCell, Table } from 'rsuite-table'; 3 | import SelectInput from './components/SelectInput'; 4 | import { columnProps, tableProps } from './props'; 5 | import './style/index.scss'; 6 | import { columnsType, ReactTableType } from './types'; 7 | import { fullCore } from './utils/full'; 8 | import onContextMenu from './utils/onContextMenu'; 9 | const ReactTable = (props: ReactTableType) => { 10 | const { 11 | columns, 12 | rowSelection, 13 | data, 14 | headerHeight = 40, 15 | rowHeight, 16 | dbClickFull, 17 | rightClickMenu, 18 | } = props; 19 | const vvTable = useRef(null); 20 | const vvTableWapper = useRef(null); 21 | // 把设置为 hidden 的过滤掉 22 | const noHiddenColumns = columns.filter((item) => !item.hidden); 23 | // 生成 Column props 24 | const showSelectInput = rowSelection && typeof rowSelection === 'object'; 25 | 26 | const baseColums = (item: columnsType, index: number, deep: 1 | 2) => { 27 | return ( 28 | 33 | {/* 头部 */} 34 | { 37 | if (!dbClickFull) return false; 38 | fullCore(vvTableWapper.current as unknown as HTMLDivElement); 39 | }} 40 | > 41 | {item.title} 42 | 43 | {/* 对应的数据 */} 44 | 45 | 52 | {(rowData, index) => { 53 | const renderItem = 54 | item.render && typeof item.render === 'function' 55 | ? item.render!(rowData as columnsType, index!) 56 | : rowData[item.dataIndex!]; 57 | 58 | return ( 59 |
{ 66 | onContextMenu(e, rowData, index!, item, rightClickMenu); 67 | }} 68 | > 69 | {renderItem} 70 |
71 | ); 72 | }} 73 |
74 |
75 | ); 76 | }; 77 | return ( 78 |
79 | 86 | {showSelectInput && 87 | data.length > 0 && 88 | SelectInput({ rowSelection, data, headerHeight, rowHeight })} 89 | {noHiddenColumns.map((item, index) => { 90 | if (item.columnChildren && item.columnChildren.length > 0) { 91 | return ( 92 | 100 | {/* TODO: 目前只支持两成嵌套 */} 101 | {item.columnChildren.map((citem: any) => 102 | baseColums(citem, index, 2), 103 | )} 104 | 105 | ); 106 | } 107 | return baseColums(item, index, 1); 108 | })} 109 |
110 |
111 | ); 112 | }; 113 | 114 | export * from './types'; 115 | 116 | export default memo(ReactTable); 117 | -------------------------------------------------------------------------------- /vv-react-table/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { SortType } from 'rsuite-table'; 3 | import ReactTable, { TColumn } from './ReactTable'; 4 | function App() { 5 | const [sortColumn, setSortColumn] = React.useState('id'); 6 | const [sortType, setSortType] = React.useState('asc'); 7 | const [editingRow, setEditingRow] = useState(new Map()); 8 | type Item = { 9 | aaa: string; 10 | bbb: string; 11 | ccc: string; 12 | name: string; 13 | id: string; 14 | }; 15 | 16 | let columns: TColumn[] = [ 17 | { 18 | title: '价格工厂1', 19 | dataIndex: 'name', 20 | contextMenu: true, 21 | minWidth: 100, 22 | sortable: true, 23 | rowSpan: (rowData) => { 24 | return rowData.rowSpan; 25 | }, 26 | }, 27 | { 28 | title: '价格工厂2', 29 | contextMenu: true, 30 | dataIndex: 'bbb', 31 | minWidth: 100, 32 | render: (row: Item, index) => { 33 | return editingRow.has(row.id) ? ( 34 | { 38 | const t = [...tableData]; 39 | t[index!].bbb = Number(e.target.value); 40 | setTableData(t); 41 | }} 42 | /> 43 | ) : ( 44 | {row.bbb} 45 | ); 46 | }, 47 | }, 48 | { 49 | title: '价格工厂3', 50 | dataIndex: 'bbb', 51 | minWidth: 100, 52 | contextMenu: true, 53 | rightClickMenu: { 54 | render(row, index) { 55 | return ( 56 |
{ 58 | alert(JSON.stringify(row)); 59 | }} 60 | style={{ 61 | padding: '5px 10px', 62 | cursor: 'pointer', 63 | }} 64 | > 65 |
我是自定义
66 |
67 | ); 68 | }, 69 | }, 70 | }, 71 | ]; 72 | 73 | const columns2: TColumn[] = [ 74 | { 75 | title: '工厂 title', 76 | dataIndex: 'aaa', 77 | minWidth: 100, 78 | columnChildren: [ 79 | { 80 | title: '配方代码 child', 81 | dataIndex: 'ccc', 82 | minWidth: 100, 83 | contextMenu: true, 84 | }, 85 | { 86 | title: '工厂 child', 87 | dataIndex: 'aaa', 88 | minWidth: 100, 89 | contextMenu: true, 90 | }, 91 | ], 92 | }, 93 | { 94 | title: '操作', 95 | align: 'center', 96 | 97 | width: 200, 98 | render: (row: Item, index) => { 99 | return editingRow.has(row.id) ? ( 100 |
101 | { 104 | setEditingRow((map) => { 105 | const newMap = new Map(map); 106 | newMap.delete(row.id); 107 | return newMap; 108 | }); 109 | }} 110 | > 111 | 保存 112 | 113 | { 115 | const t = [...tableData]; 116 | const oldRow = editingRow.get(row.id); 117 | t[index!] = oldRow; 118 | setTableData(t); 119 | setEditingRow((map) => { 120 | const newMap = new Map(map); 121 | newMap.delete(row.id); 122 | return newMap; 123 | }); 124 | }} 125 | > 126 | 取消编辑 127 | 128 |
129 | ) : ( 130 | 133 | setEditingRow((map) => new Map(map.set(row.id, row))) 134 | } 135 | > 136 | 编辑 137 | 138 | ); 139 | }, 140 | }, 141 | ]; 142 | 143 | // const tableData = useMemo( 144 | // () => 145 | // new Array(4).fill(undefined).map((item, index) => ({ 146 | // aaa: 1, 147 | // bbb: 2, 148 | // ccc: 3, 149 | // id: index.toString(), 150 | // })), 151 | // [columns], 152 | // ); 153 | // setAaa(() => aaa.set(1, 1)); 154 | 155 | const [tableData, setTableData] = useState([ 156 | { 157 | aaa: 1, 158 | bbb: 2, 159 | ccc: 3, 160 | id: '1', 161 | }, 162 | { 163 | aaa: 1, 164 | bbb: 2, 165 | ccc: 3, 166 | id: '2', 167 | }, 168 | { 169 | rowSpan: 3, 170 | aaa: 111, 171 | bbb: 2222, 172 | name: 'a', 173 | ccc: 333, 174 | id: '22', 175 | }, 176 | { 177 | aaa: 1, 178 | name: 'a', 179 | bbb: 2, 180 | ccc: 3, 181 | id: '3', 182 | }, 183 | { 184 | aaa: 1, 185 | name: 'a', 186 | bbb: 2, 187 | ccc: 3, 188 | id: '4', 189 | }, 190 | { 191 | aaa: 11, 192 | bbb: 22, 193 | ccc: 33, 194 | id: '5', 195 | }, 196 | ]); 197 | 198 | const tableDom = () => { 199 | setTimeout(() => { 200 | columns = [...columns, ...columns2]; 201 | }, 1000); 202 | return ( 203 |
204 | { 209 | console.log(sortColumn, sortType); 210 | sortType && setSortType(sortType); 211 | setSortColumn(sortColumn); 212 | }} 213 | rowSelection={{ 214 | key: 'id', 215 | type: 'checkbox', 216 | onChange: (ids, rows, id, row) => { 217 | console.log('ids', ids); 218 | console.log('rows', rows); 219 | console.log('id', id); 220 | console.log('row', row); 221 | }, 222 | }} 223 | renderTreeToggle={(icon, rowData: any) => { 224 | if (rowData.children && rowData.children.length === 0) { 225 | return 'loading'; 226 | } 227 | return icon; 228 | }} 229 | headerHeight={60} 230 | rowHeight={30} 231 | bordered 232 | dbClickFull 233 | rightClickMenu={{ 234 | render(row, index) { 235 | return ( 236 |
{ 238 | alert(JSON.stringify(row)); 239 | }} 240 | style={{ 241 | padding: '5px 10px', 242 | cursor: 'pointer', 243 | }} 244 | > 245 |
第一列
246 |
第二列
247 |
第三列
248 |
249 | ); 250 | }, 251 | }} 252 | columns={columns} 253 | data={tableData} 254 | >
255 |
256 | ); 257 | }; 258 | return ( 259 |
260 | {/* */} 261 | {tableDom()} 262 |
263 | ); 264 | } 265 | 266 | export default App; 267 | -------------------------------------------------------------------------------- /src/vv-react-table/index.md: -------------------------------------------------------------------------------- 1 | # 示例 2 | 3 | ## 基础用法 4 | 5 | 通过 `data` 设置表格的数据源,通过 `columns` 设置表格的列即可得到一个表格 6 | 7 | ```jsx 8 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 9 | import React, { useMemo } from 'react'; 10 | function Dome() { 11 | type Item = { 12 | column1: string, 13 | column2: string, 14 | column3: string, 15 | column4: string, 16 | }; 17 | 18 | const columns: TColumn[] = [ 19 | { 20 | title: '第一列', 21 | dataIndex: 'column1', 22 | minWidth: 100, 23 | align: 'center', 24 | }, 25 | 26 | { 27 | title: '第二列', 28 | dataIndex: 'column2', 29 | minWidth: 100, 30 | align: 'center', 31 | }, 32 | { 33 | title: '第三列', 34 | dataIndex: 'column3', 35 | minWidth: 100, 36 | align: 'center', 37 | }, 38 | { 39 | title: '第四列', 40 | dataIndex: 'column4', 41 | align: 'center', 42 | minWidth: 100, 43 | }, 44 | ]; 45 | 46 | const tableData = useMemo( 47 | () => 48 | new Array(100).fill(undefined).map((item, index) => ({ 49 | column1: 1, 50 | column2: 2, 51 | column3: 3, 52 | column4: 4, 53 | })), 54 | [columns], 55 | ); 56 | 57 | return ( 58 |
59 | 65 |
66 | ); 67 | } 68 | 69 | export default Dome; 70 | ``` 71 | 72 | ## 可选择 73 | 74 | 设置 `rowSelection` 使表格可被配置,配置 `type = checkbox | radio` 支持多选/单选 75 | 76 | **设置 `rowSelection` 需要给列表配置唯一标识 通过 key 进行配置** 77 | 78 | ```jsx 79 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 80 | import React, { useMemo } from 'react'; 81 | function Dome() { 82 | type Item = { 83 | column1: string, 84 | column2: string, 85 | column3: string, 86 | column4: string, 87 | }; 88 | 89 | const columns: TColumn[] = [ 90 | { 91 | title: '第一列', 92 | dataIndex: 'column1', 93 | minWidth: 100, 94 | align: 'center', 95 | }, 96 | 97 | { 98 | title: '第二列', 99 | dataIndex: 'column2', 100 | minWidth: 100, 101 | align: 'center', 102 | }, 103 | { 104 | title: '第三列', 105 | dataIndex: 'column3', 106 | minWidth: 100, 107 | align: 'center', 108 | }, 109 | { 110 | title: '第四列', 111 | dataIndex: 'column4', 112 | align: 'center', 113 | minWidth: 100, 114 | }, 115 | ]; 116 | 117 | const tableData = useMemo( 118 | () => 119 | new Array(100).fill(undefined).map((item, index) => ({ 120 | column1: 1, 121 | column2: 2, 122 | column3: 3, 123 | column4: 4, 124 | id: index.toString(), // 唯一标识 125 | })), 126 | [columns], 127 | ); 128 | 129 | return ( 130 |
131 | { 136 | console.log('ids', ids); 137 | console.log('rows', rows); 138 | console.log('id', id); 139 | console.log('row', row); 140 | }, 141 | }} 142 | height={300} 143 | bordered 144 | columns={columns} 145 | data={tableData} 146 | > 147 |
148 | ); 149 | } 150 | 151 | export default Dome; 152 | ``` 153 | 154 | ## 固定列 155 | 156 | 给某一列配置 `fixed` 属性可实现固定列,支持 `right | left` 固定到 ` 右边 | 左边` 157 | 158 | ```jsx 159 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 160 | import React, { useMemo } from 'react'; 161 | function Dome() { 162 | type Item = { 163 | column1: string, 164 | column2: string, 165 | column3: string, 166 | column4: string, 167 | }; 168 | 169 | const columns: TColumn[] = [ 170 | { 171 | title: '第一列', 172 | dataIndex: 'column1', 173 | width: 100, 174 | align: 'center', 175 | fixed: 'left', 176 | }, 177 | 178 | { 179 | title: '第二列', 180 | dataIndex: 'column2', 181 | width: 300, 182 | align: 'center', 183 | }, 184 | { 185 | title: '第三列', 186 | dataIndex: 'column3', 187 | width: 300, 188 | align: 'center', 189 | }, 190 | { 191 | title: '第四列', 192 | dataIndex: 'column4', 193 | align: 'center', 194 | width: 300, 195 | }, 196 | { 197 | title: '第五列', 198 | dataIndex: 'column5', 199 | align: 'center', 200 | width: 300, 201 | }, 202 | { 203 | title: '第六列', 204 | dataIndex: 'column6', 205 | align: 'center', 206 | width: 100, 207 | fixed: 'right', 208 | }, 209 | ]; 210 | 211 | const tableData = useMemo( 212 | () => 213 | new Array(100).fill(undefined).map((item, index) => ({ 214 | column1: 1, 215 | column2: 2, 216 | column3: 3, 217 | column4: 4, 218 | column5: 5, 219 | column6: 6, 220 | })), 221 | [columns], 222 | ); 223 | 224 | return ( 225 |
226 | 232 |
233 | ); 234 | } 235 | 236 | export default Dome; 237 | ``` 238 | 239 | ## 合并头部 240 | 241 | 通过配置 `columnChildren` 即可实现 242 | 243 | ```jsx 244 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 245 | import React, { useMemo } from 'react'; 246 | function Dome() { 247 | type Item = { 248 | column1: string, 249 | column2: string, 250 | column3: string, 251 | column4: string, 252 | column2_1: string, 253 | column2_2: string, 254 | }; 255 | 256 | const columns: TColumn[] = [ 257 | { 258 | title: '第一列', 259 | dataIndex: 'column1', 260 | minWidth: 100, 261 | align: 'center', 262 | }, 263 | 264 | { 265 | title: '第二列', 266 | dataIndex: 'column2', 267 | minWidth: 100, 268 | align: 'center', 269 | columnChildren: [ 270 | { 271 | title: '第二列_1', 272 | dataIndex: 'column2_1', 273 | minWidth: 100, 274 | }, 275 | { 276 | title: '第二列_2', 277 | dataIndex: 'column2_2', 278 | minWidth: 100, 279 | }, 280 | ], 281 | }, 282 | { 283 | title: '第三列', 284 | dataIndex: 'column3', 285 | minWidth: 100, 286 | align: 'center', 287 | }, 288 | { 289 | title: '第四列', 290 | dataIndex: 'column4', 291 | align: 'center', 292 | minWidth: 100, 293 | }, 294 | { 295 | title: '第五列', 296 | dataIndex: 'column5', 297 | align: 'center', 298 | minWidth: 100, 299 | }, 300 | { 301 | title: '第六列', 302 | dataIndex: 'column6', 303 | align: 'center', 304 | minWidth: 100, 305 | }, 306 | ]; 307 | 308 | const tableData = useMemo( 309 | () => 310 | new Array(100).fill(undefined).map((item, index) => ({ 311 | column1: 1, 312 | column2: 2, 313 | column3: 3, 314 | column4: 4, 315 | column5: 5, 316 | column6: 6, 317 | column2_1: '2_1', 318 | column2_2: '2_2', 319 | })), 320 | [columns], 321 | ); 322 | 323 | return ( 324 |
325 | 331 |
332 | ); 333 | } 334 | 335 | export default Dome; 336 | ``` 337 | 338 | ## 合并列 339 | 340 | 1. 在 `columns`中某一列配置 `rowSpan: (rowData) => { 341 | return rowData.rowSpan; 342 | }`说明合并行在哪一列发生 343 | 344 | 2. 通过给 `data` 配置 `rowSpan` 来控制要合并几行 345 | 346 | ```jsx 347 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 348 | import React, { useMemo } from 'react'; 349 | function Dome() { 350 | type Item = { 351 | column1: string, 352 | column2: string, 353 | column3: string, 354 | column4: string, 355 | }; 356 | 357 | const columns: TColumn[] = [ 358 | { 359 | title: '第一列', 360 | dataIndex: 'column1', 361 | minWidth: 100, 362 | align: 'center', 363 | rowSpan: (rowData) => { 364 | return rowData.rowSpan; 365 | }, 366 | }, 367 | 368 | { 369 | title: '第二列', 370 | dataIndex: 'column2', 371 | minWidth: 100, 372 | align: 'center', 373 | }, 374 | { 375 | title: '第三列', 376 | dataIndex: 'column3', 377 | minWidth: 100, 378 | align: 'center', 379 | }, 380 | { 381 | title: '第四列', 382 | dataIndex: 'column4', 383 | align: 'center', 384 | minWidth: 100, 385 | }, 386 | ]; 387 | 388 | const tableData = [ 389 | { 390 | column1: 1, 391 | column2: 2, 392 | column3: 3, 393 | column4: 4, 394 | }, 395 | { 396 | column1: 1, 397 | column2: 2, 398 | column3: 3, 399 | column4: 4, 400 | rowSpan: 2, 401 | }, 402 | { 403 | column1: 1, 404 | column2: 2, 405 | column3: 3, 406 | column4: 4, 407 | }, 408 | { 409 | column1: 1, 410 | column2: 2, 411 | column3: 3, 412 | column4: 4, 413 | }, 414 | { 415 | column1: 1, 416 | column2: 2, 417 | column3: 3, 418 | column4: 4, 419 | }, 420 | ]; 421 | 422 | return ( 423 |
424 | 430 |
431 | ); 432 | } 433 | 434 | export default Dome; 435 | ``` 436 | 437 | ## 列宽拖拽 438 | 439 | 给 `columns` 配置 `resizable=true` 即可支持列宽拖拽。**列宽拖拽需要配置固定宽度**`width` 440 | 441 | ```jsx 442 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 443 | import React, { useMemo } from 'react'; 444 | function Dome() { 445 | type Item = { 446 | column1: string, 447 | column2: string, 448 | column3: string, 449 | column4: string, 450 | }; 451 | 452 | const columns: TColumn[] = [ 453 | { 454 | title: '第一列', 455 | dataIndex: 'column1', 456 | width: 200, 457 | resizable: true, 458 | align: 'center', 459 | }, 460 | 461 | { 462 | title: '第二列', 463 | dataIndex: 'column2', 464 | width: 200, 465 | resizable: true, 466 | align: 'center', 467 | }, 468 | { 469 | title: '第三列', 470 | dataIndex: 'column3', 471 | width: 200, 472 | resizable: true, 473 | align: 'center', 474 | }, 475 | { 476 | title: '第四列', 477 | dataIndex: 'column4', 478 | align: 'center', 479 | width: 200, 480 | resizable: true, 481 | }, 482 | ]; 483 | 484 | const tableData = useMemo( 485 | () => 486 | new Array(100).fill(undefined).map((item, index) => ({ 487 | column1: 1, 488 | column2: 2, 489 | column3: 3, 490 | column4: 4, 491 | })), 492 | [columns], 493 | ); 494 | 495 | return ( 496 |
497 | 503 |
504 | ); 505 | } 506 | 507 | export default Dome; 508 | ``` 509 | 510 | ## 双击头部放大 511 | 512 | 设置 `dbClickFull` 513 | 514 | ```jsx 515 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 516 | import React, { useMemo } from 'react'; 517 | function Dome() { 518 | type Item = { 519 | column1: string, 520 | column2: string, 521 | column3: string, 522 | column4: string, 523 | }; 524 | 525 | const columns: TColumn[] = [ 526 | { 527 | title: '第一列', 528 | dataIndex: 'column1', 529 | minWidth: 100, 530 | align: 'center', 531 | }, 532 | 533 | { 534 | title: '第二列', 535 | dataIndex: 'column2', 536 | minWidth: 100, 537 | align: 'center', 538 | }, 539 | { 540 | title: '第三列', 541 | dataIndex: 'column3', 542 | minWidth: 100, 543 | align: 'center', 544 | }, 545 | { 546 | title: '第四列', 547 | dataIndex: 'column4', 548 | align: 'center', 549 | minWidth: 100, 550 | }, 551 | ]; 552 | 553 | const tableData = useMemo( 554 | () => 555 | new Array(100).fill(undefined).map((item, index) => ({ 556 | column1: 1, 557 | column2: 2, 558 | column3: 3, 559 | column4: 4, 560 | })), 561 | [columns], 562 | ); 563 | 564 | return ( 565 |
566 | 573 |
574 | ); 575 | } 576 | 577 | export default Dome; 578 | ``` 579 | 580 | ## 行内编辑 581 | 582 | 行内编辑功能可通过 `render` 函数自行扩展,如下所示: 583 | 584 | ### 场景一 585 | 586 | ```jsx 587 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 588 | import React, { useState } from 'react'; 589 | function Dome() { 590 | type Item = { 591 | column1: string, 592 | column2: string, 593 | column3: string, 594 | column4: string, 595 | }; 596 | 597 | const columns: TColumn[] = [ 598 | { 599 | title: '第一列', 600 | dataIndex: 'column1', 601 | minWidth: 100, 602 | align: 'center', 603 | }, 604 | 605 | { 606 | title: '第二列', 607 | dataIndex: 'column2', 608 | minWidth: 100, 609 | align: 'center', 610 | render: (row: Item, index) => { 611 | return ( 612 | { 616 | const t = [...tableData]; 617 | t[index!].column2 = Number(e.target.value); 618 | setTableData(t); 619 | }} 620 | /> 621 | ); 622 | }, 623 | }, 624 | { 625 | title: '第三列', 626 | dataIndex: 'column3', 627 | minWidth: 100, 628 | align: 'center', 629 | render: (row: Item, index) => { 630 | return ( 631 | { 635 | const t = [...tableData]; 636 | t[index!].column3 = Number(e.target.value); 637 | setTableData(t); 638 | }} 639 | /> 640 | ); 641 | }, 642 | }, 643 | { 644 | title: '第四列', 645 | dataIndex: 'column4', 646 | align: 'center', 647 | minWidth: 100, 648 | }, 649 | ]; 650 | const [tableData, setTableData] = useState([ 651 | { 652 | column1: 1, 653 | column2: 2, 654 | column3: 3, 655 | column4: 4, 656 | }, 657 | { 658 | column1: 11, 659 | column2: 22, 660 | column3: 33, 661 | column4: 44, 662 | }, 663 | ]); 664 | 665 | return ( 666 |
667 | 679 | 680 | 686 |
687 | ); 688 | } 689 | 690 | export default Dome; 691 | ``` 692 | 693 | ### 场景二 694 | 695 | ```jsx 696 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 697 | import React, { useState } from 'react'; 698 | 699 | function Dome() { 700 | const [editingRow, setEditingRow] = useState(new Map()); // 储存要编辑的行 701 | type Item = { 702 | column1: string, 703 | column2: string, 704 | column3: string, 705 | column4: string, 706 | }; 707 | 708 | const columns: TColumn[] = [ 709 | { 710 | title: '第一列', 711 | dataIndex: 'column1', 712 | minWidth: 100, 713 | align: 'center', 714 | }, 715 | 716 | { 717 | title: '第二列', 718 | dataIndex: 'column2', 719 | minWidth: 100, 720 | align: 'center', 721 | render: (row: Item, index) => { 722 | return editingRow.has(row.id) ? ( 723 | { 727 | const t = [...tableData]; 728 | t[index!].column2 = Number(e.target.value); 729 | setTableData(t); 730 | }} 731 | /> 732 | ) : ( 733 | {row.column2} 734 | ); 735 | }, 736 | }, 737 | { 738 | title: '第三列', 739 | dataIndex: 'column3', 740 | minWidth: 100, 741 | align: 'center', 742 | render: (row: Item, index) => { 743 | return editingRow.has(row.id) ? ( 744 | { 748 | const t = [...tableData]; 749 | t[index!].column3 = Number(e.target.value); 750 | setTableData(t); 751 | }} 752 | /> 753 | ) : ( 754 | {row.column3} 755 | ); 756 | }, 757 | }, 758 | { 759 | title: '第四列', 760 | dataIndex: 'column4', 761 | align: 'center', 762 | minWidth: 100, 763 | }, 764 | { 765 | title: '操作', 766 | align: 'center', 767 | width: 200, 768 | render: (row: Item, index) => { 769 | return editingRow.has(row.id) ? ( 770 |
771 | { 774 | setEditingRow((map) => { 775 | const newMap = new Map(map); 776 | newMap.delete(row.id); 777 | return newMap; 778 | }); 779 | }} 780 | > 781 | 保存 782 | 783 | { 785 | const t = [...tableData]; 786 | const oldRow = editingRow.get(row.id); 787 | t[index!] = oldRow; 788 | setTableData(t); 789 | setEditingRow((map) => { 790 | const newMap = new Map(map); 791 | newMap.delete(row.id); 792 | return newMap; 793 | }); 794 | }} 795 | > 796 | 取消编辑 797 | 798 |
799 | ) : ( 800 | 803 | setEditingRow((map) => new Map(map.set(row.id, row))) 804 | } 805 | > 806 | 编辑 807 | 808 | ); 809 | }, 810 | }, 811 | ]; 812 | const [tableData, setTableData] = useState([ 813 | { 814 | column1: 1, 815 | column2: 2, 816 | column3: 3, 817 | column4: 4, 818 | }, 819 | { 820 | column1: 11, 821 | column2: 22, 822 | column3: 33, 823 | column4: 44, 824 | }, 825 | ]); 826 | 827 | return ( 828 |
829 | 841 | 842 | 848 |
849 | ); 850 | } 851 | 852 | export default Dome; 853 | ``` 854 | 855 | ## 右键菜单 856 | 857 | - 通过给 `table` 设置 `rightClickMenu` 来控制右键菜单的 `UI` 858 | - 通过给 `colums` 设置 `contextMenu` 控制是否开启此列的右键菜单 859 | - 如果 `colums` 设置 `rightClickMenu` 则会覆盖全局 `rightClickMenu` 860 | 861 | > 右键菜单效果需点击下面“在独立页面打开”查看效果 862 | 863 | ```jsx 864 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 865 | import React, { useMemo } from 'react'; 866 | function Dome() { 867 | type Item = { 868 | column1: string, 869 | column2: string, 870 | column3: string, 871 | column4: string, 872 | }; 873 | 874 | const columns: TColumn[] = [ 875 | { 876 | title: '第一列', 877 | dataIndex: 'column1', 878 | minWidth: 100, 879 | align: 'center', 880 | contextMenu: true, 881 | }, 882 | 883 | { 884 | title: '第二列', 885 | dataIndex: 'column2', 886 | minWidth: 100, 887 | align: 'center', 888 | contextMenu: true, 889 | }, 890 | { 891 | title: '第三列', 892 | dataIndex: 'column3', 893 | minWidth: 100, 894 | align: 'center', 895 | contextMenu: true, 896 | }, 897 | { 898 | title: '自定义右键', 899 | dataIndex: 'column4', 900 | align: 'center', 901 | minWidth: 100, 902 | contextMenu: true, 903 | rightClickMenu: { 904 | render(row, index) { 905 | return ( 906 |
{ 908 | alert(JSON.stringify(row)); 909 | }} 910 | style={{ 911 | padding: '5px 10px', 912 | cursor: 'pointer', 913 | }} 914 | > 915 |
我是自定义
916 |
917 | ); 918 | }, 919 | }, 920 | }, 921 | ]; 922 | 923 | const tableData = useMemo( 924 | () => 925 | new Array(100).fill(undefined).map((item, index) => ({ 926 | column1: 1, 927 | column2: 2, 928 | column3: 3, 929 | column4: 4, 930 | })), 931 | [columns], 932 | ); 933 | 934 | return ( 935 |
936 | { 946 | alert(JSON.stringify(row)); 947 | }} 948 | style={{ 949 | padding: '5px 10px', 950 | cursor: 'pointer', 951 | }} 952 | > 953 |
第一列
954 |
第二列
955 |
第三列
956 |
957 | ); 958 | }, 959 | }} 960 | > 961 | 962 | ); 963 | } 964 | 965 | export default Dome; 966 | ``` 967 | 968 | ## 单独改变某一列样式 969 | 970 | 通过 `rowClassName` 可对某一行设置单独样式 971 | 972 | ```jsx 973 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 974 | import React, { useMemo } from 'react'; 975 | function Dome() { 976 | type Item = { 977 | column1: string, 978 | column2: string, 979 | column3: string, 980 | column4: string, 981 | }; 982 | 983 | const columns: TColumn[] = [ 984 | { 985 | title: '第一列', 986 | dataIndex: 'column1', 987 | minWidth: 100, 988 | align: 'center', 989 | }, 990 | 991 | { 992 | title: '第二列', 993 | dataIndex: 'column2', 994 | minWidth: 100, 995 | align: 'center', 996 | }, 997 | { 998 | title: '第三列', 999 | dataIndex: 'column3', 1000 | minWidth: 100, 1001 | align: 'center', 1002 | }, 1003 | { 1004 | title: '第四列', 1005 | dataIndex: 'column4', 1006 | align: 'center', 1007 | minWidth: 100, 1008 | }, 1009 | ]; 1010 | 1011 | const tableData = useMemo( 1012 | () => 1013 | new Array(100).fill(undefined).map((item, index) => ({ 1014 | column1: 1, 1015 | column2: 2, 1016 | column3: 3, 1017 | column4: 4, 1018 | status: Math.random() > 0.5, 1019 | })), 1020 | [columns], 1021 | ); 1022 | 1023 | return ( 1024 |
1025 | { 1031 | if (row && row.status) { 1032 | return 'vv-bg-active'; 1033 | } 1034 | }} 1035 | > 1036 |
1037 | ); 1038 | } 1039 | 1040 | export default Dome; 1041 | ``` 1042 | 1043 | ## 改变行高度 1044 | 1045 | - `rowHeight` 设置每一行高度 1046 | - `headerHeight` 设置表头高度 1047 | 1048 | ```jsx 1049 | import ReactTable, { TColumn } from '@vv-react-table/vv-react-table'; 1050 | import React, { useMemo } from 'react'; 1051 | function Dome() { 1052 | type Item = { 1053 | column1: string, 1054 | column2: string, 1055 | column3: string, 1056 | column4: string, 1057 | }; 1058 | 1059 | const columns: TColumn[] = [ 1060 | { 1061 | title: '第一列', 1062 | dataIndex: 'column1', 1063 | minWidth: 100, 1064 | align: 'center', 1065 | }, 1066 | 1067 | { 1068 | title: '第二列', 1069 | dataIndex: 'column2', 1070 | minWidth: 100, 1071 | align: 'center', 1072 | }, 1073 | { 1074 | title: '第三列', 1075 | dataIndex: 'column3', 1076 | minWidth: 100, 1077 | align: 'center', 1078 | }, 1079 | { 1080 | title: '第四列', 1081 | dataIndex: 'column4', 1082 | align: 'center', 1083 | minWidth: 100, 1084 | }, 1085 | ]; 1086 | 1087 | const tableData = useMemo( 1088 | () => 1089 | new Array(100).fill(undefined).map((item, index) => ({ 1090 | column1: 1, 1091 | column2: 2, 1092 | column3: 3, 1093 | column4: 4, 1094 | })), 1095 | [columns], 1096 | ); 1097 | 1098 | return ( 1099 |
1100 | 1108 |
1109 | ); 1110 | } 1111 | 1112 | export default Dome; 1113 | ``` 1114 | --------------------------------------------------------------------------------