├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .versionrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── images └── demo.gif ├── package.json ├── packages ├── demo │ ├── .editorconfig │ ├── .gitignore │ ├── babel.config.js │ ├── config │ │ ├── dev.js │ │ ├── index.js │ │ └── prod.js │ ├── package.json │ ├── project.config.json │ ├── project.tt.json │ ├── src │ │ ├── app.config.ts │ │ ├── app.less │ │ ├── app.ts │ │ ├── index.html │ │ └── pages │ │ │ └── index │ │ │ ├── index.config.ts │ │ │ ├── index.less │ │ │ └── index.tsx │ ├── tsconfig.json │ └── types │ │ └── global.d.ts └── table │ ├── .babelrc │ ├── README.md │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── components │ │ ├── LoadMore │ │ │ ├── index.less │ │ │ └── index.tsx │ │ ├── Loading │ │ │ ├── index.less │ │ │ └── index.tsx │ │ └── Table │ │ │ ├── Empty.tsx │ │ │ ├── Row.tsx │ │ │ ├── Title.tsx │ │ │ ├── index.less │ │ │ ├── index.tsx │ │ │ └── types.ts │ ├── hooks │ │ ├── index.ts │ │ ├── useQuery.ts │ │ ├── useRendered.ts │ │ ├── useUniqueId.ts │ │ └── useUpdateState.ts │ ├── index.ts │ ├── styles │ │ ├── index.less │ │ └── theme.less │ └── utils │ │ └── index.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── release.js └── update-pkg.js ├── tsconfig.json └── turbo.json /.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 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | packages/**/*.md 2 | scripts/**/*.js 3 | scripts 4 | commitlint.config.js 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:react/recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:prettier/recommended", 12 | "eslint-config-prettier" 13 | ], 14 | "settings": { 15 | "react": { 16 | "version": "detect" 17 | } 18 | }, 19 | "parserOptions": { 20 | "sourceType": "module", 21 | "ecmaVersion": 2021 22 | }, 23 | "plugins": ["react", "@typescript-eslint"], 24 | "parser": "@typescript-eslint/parser", 25 | "rules": { 26 | "@typescript-eslint/no-explicit-any": "off", 27 | "@typescript-eslint/no-var-requires": "off", 28 | "react/react-in-jsx-scope": "off", 29 | "react/self-closing-comp": ["error"], 30 | "react/jsx-uses-react": "off", 31 | "quotes": ["error", "single"], 32 | "jsx-quotes": ["error", "prefer-single"], 33 | "no-debugger": "error" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | /packages/*/node_modules 8 | /*/node_modules 9 | /packages/*/dist/* 10 | 11 | # testing 12 | coverage 13 | 14 | # next.js 15 | .next/ 16 | out/ 17 | build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | .pnpm-debug.log* 28 | 29 | # local env files 30 | .env.local 31 | .env.development.local 32 | .env.test.local 33 | .env.production.local 34 | 35 | # turbo 36 | .turbo 37 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm exec commitlint --config commitlint.config.js --edit "${1}" -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm exec lint-staged -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | git-checks=false 2 | registry=https://registry.npmmirror.com/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | pnpm-lock.yaml -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "bracketSameLine": false, 4 | "endOfLine": "lf", 5 | "semi": false, 6 | "singleQuote": true, 7 | "jsxSingleQuote": true, 8 | "tabWidth": 2, 9 | "trailingComma": "all" 10 | } 11 | -------------------------------------------------------------------------------- /.versionrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | releaseCommitMessageFormat: 'release: {{currentTag}}', 3 | types: [ 4 | { type: 'release', section: 'release version' }, 5 | { type: 'feat', section: ' Features' }, 6 | { type: 'feature', section: 'Features' }, 7 | { type: 'fix', section: 'Bug Fixes' }, 8 | { type: 'perf', section: 'Performance Improvements' }, 9 | { type: 'revert', section: 'Reverts' }, 10 | { type: 'docs', section: 'Documentation', hidden: true }, 11 | { type: 'style', section: 'Styles', hidden: true }, 12 | { type: 'chore', section: 'Miscellaneous Chores', hidden: true }, 13 | { type: 'refactor', section: 'Code Refactoring', hidden: true }, 14 | { type: 'test', section: 'Tests', hidden: true }, 15 | { type: 'build', section: 'Build System', hidden: true }, 16 | { type: 'ci', section: 'Continuous Integration', hidden: true }, 17 | ], 18 | } 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.7.5](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.7.4...taro-react-table-v1.7.5) (2024-02-05) 6 | 7 | ### Bug Fixes 8 | 9 | - 表格滚动性能优化 ([b125379](https://github.com/qiuweikangdev/taro-react-table/commit/b12537926f2bf34508eddc9e80d2fc4f8de1ea5a)) 10 | 11 | ### Features 12 | 13 | - update-pkg ([194e049](https://github.com/qiuweikangdev/taro-react-table/commit/194e049bed03f85f70b88a387332838cc17e92e8)) 14 | 15 | ### [1.7.4](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.7.3...taro-react-table-v1.7.4) (2023-02-06) 16 | 17 | ### [1.7.3](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.7.2...taro-react-table-v1.7.3) (2022-12-08) 18 | 19 | ### Bug Fixes 20 | 21 | - **table:** striped ([eabccb6](https://github.com/qiuweikangdev/taro-react-table/commit/eabccb60dabebbe4bbf8b233dfeeaf7d17c13a68)) 22 | 23 | ### [1.7.2](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.7.1...taro-react-table-v1.7.2) (2022-12-05) 24 | 25 | ### Bug Fixes 26 | 27 | - **table:** remove adaptive column, set colum width ([1e69d45](https://github.com/qiuweikangdev/taro-react-table/commit/1e69d4598b9383ede670d4724aad0b2a4ebc605c)) 28 | 29 | ### [1.7.1](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.7.0...taro-react-table-v1.7.1) (2022-12-02) 30 | 31 | ### Bug Fixes 32 | 33 | - **table:** row adaptive height ([f15e1cd](https://github.com/qiuweikangdev/taro-react-table/commit/f15e1cd8c92d554069164cfc4e47506c67c6218e)) 34 | 35 | ## [1.7.0](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.6.2...taro-react-table-v1.7.0) (2022-11-27) 36 | 37 | ### Bug Fixes 38 | 39 | - **table:** fixed column scrolling overlapping ([b5d6367](https://github.com/qiuweikangdev/taro-react-table/commit/b5d6367ce943764a69e1f9fd98242d554f690506)) 40 | - **table:** uniform use Taro.pxTransform ([88d3d82](https://github.com/qiuweikangdev/taro-react-table/commit/88d3d823c14406c6d089e8aaa8cebc847feccaf1)) 41 | 42 | ### Features 43 | 44 | - **table:** add size parameter spacing size ([f2ecadb](https://github.com/qiuweikangdev/taro-react-table/commit/f2ecadb3cab376e7173811fdb6b42758767de8c6)) 45 | 46 | ### [1.6.2](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.6.1...taro-react-table-v1.6.2) (2022-11-22) 47 | 48 | ### [1.6.1](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.6.0...taro-react-table-v1.6.1) (2022-11-14) 49 | 50 | ### Bug Fixes 51 | 52 | - **table:** fix column header width ([f650665](https://github.com/qiuweikangdev/taro-react-table/commit/f650665963f25880d0cc5fc4c47f18a12e3d06df)) 53 | 54 | ## [1.6.0](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.5.1...taro-react-table-v1.6.0) (2022-11-13) 55 | 56 | ### Features 57 | 58 | - **table:** column adaptive width、striped [#2](https://github.com/qiuweikangdev/taro-react-table/issues/2) ([3e3790a](https://github.com/qiuweikangdev/taro-react-table/commit/3e3790a6ad6cb83f546dcf1af71401c24463259d)) 59 | 60 | ### [1.5.1](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.5.0...taro-react-table-v1.5.1) (2022-10-09) 61 | 62 | ### Bug Fixes 63 | 64 | - determine whether the tag exists ([e8e68f1](https://github.com/qiuweikangdev/taro-react-table/commit/e8e68f11641afce532bbdaafa5f1a1b7d2cdb420)) 65 | - **table:** scroll fixed ([2035b9e](https://github.com/qiuweikangdev/taro-react-table/commit/2035b9ef97030f81bfcdc562fcb9c8fad1264dbd)) 66 | 67 | ### Features 68 | 69 | - release.js generate version/tags ([e202bb1](https://github.com/qiuweikangdev/taro-react-table/commit/e202bb18daedb920e79f297a62b4c935df0e26c8)) 70 | 71 | ## [1.5.0](https://github.com/qiuweikangdev/taro-react-table/compare/taro-react-table-v1.4.0...taro-react-table-v1.5.0) (2022-10-01) 72 | 73 | ### Features 74 | 75 | - add linter and formatter ([096c042](https://github.com/qiuweikangdev/taro-react-table/commit/096c042b8a384e7544dacebdcfafd1799196b247)) 76 | - check message when commit ([b096898](https://github.com/qiuweikangdev/taro-react-table/commit/b096898c8b25c5ec4d5983fb574cf07ee12be626)) 77 | - commitlint check message ([128c873](https://github.com/qiuweikangdev/taro-react-table/commit/128c873b41a0b89f4b49f69d34a7835f441a7653)) 78 | - **table:** renderEmpty ([e4b9dde](https://github.com/qiuweikangdev/taro-react-table/commit/e4b9ddedda7075bb354b7dee1277855d7afe1665)) 79 | - update-pkg ([19e12bb](https://github.com/qiuweikangdev/taro-react-table/commit/19e12bb8c0b60b6b72cadaf0f3a54e6af39c294d)) 80 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 qiuweikangdev 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # taro-react-table 2 | 3 | 基于 Taro3、React 的 H5 和微信小程序多端表格组件 4 | 5 | - 兼容 H5、微信小程序 6 | - 自定义样式 7 | - 自定义排序 8 | - 固定表头 9 | - 滚动上拉加载 10 | 11 | ![](https://raw.githubusercontent.com/qiuweikangdev/taro-react-table/master/images/demo.gif) 12 | 13 | # 安装 14 | 15 | ```bash 16 | npm install taro-react-table 17 | ``` 18 | 19 | # 配置 20 | 21 | - config/index.js 配置 22 | - 让 taro 去通过 postcss 编译处理 `taro-react-table`模块, 需要对 `taro-react-table` 里的样式单位进行转换适配 23 | 24 | ```js 25 | // config/index.js 26 | const config = { 27 | h5: { 28 | esnextModules: ['taro-react-table'], 29 | }, 30 | } 31 | ``` 32 | 33 | # 导入组件 34 | 35 | ```js 36 | import Table from 'taro-react-table' 37 | import 'taro-react-table/dist/index.css' 38 | ``` 39 | 40 | # 参数说明 41 | 42 | ## Table 43 | 44 | | 参数 | 描述 | 类型 | 必传 | 默认值 | 45 | | ----------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------- | ---- | ---------- | 46 | | 本身参数 | 参考[ScrollView](https://taro-docs.jd.com/taro/docs/components/viewContainer/scroll-view) | | | | 47 | | `dataSource` | 数据源 | object[] | 是 | `[]` | 48 | | `columns` | 表格列的配置描述,具体项见下表 | Columns[] | 是 | `[]` | 49 | | `rowKey` | 表格行 key 的取值,可以是字符串或一个函数 | string \| function(record): string | 是 | `key` | 50 | | `wrapperClass` | 外层容器的类名 | string | 否 | | 51 | | `wrapperStyle` | 外层容器的样式 | object | 否 | | 52 | | `className` | ScrollView 容器类名 | string | 否 | | 53 | | `style` | ScrollView 容器样式 | object | 否 | | 54 | | `rowClassName` | 行类名 | string | 否 | | 55 | | `rowStyle` | 行样式 | object | 否 | | 56 | | `colClassName` | 单元格类名 | string | 否 | | 57 | | `colStyle` | 单元格样式 | object | 否 | | 58 | | `titleStyle` | 标题样式 | object | 否 | | 59 | | `titleClassName` | 标题类名 | string | 否 | | 60 | | `titleClassName` | 标题类名 | string | 否 | | 61 | | `loading` | 是否显示加载 | boolean | 否 | | 62 | | `loadingText` | 加载文本 | string | 否 | '' | 63 | | `loadStatus` | 加载状态 | loading \| noMore \| null | 否 | null | 64 | | `unsort` | 设置是否取消排序 (一般需求不需要取消排序,设置 true 可开启取消排序) | boolean | 否 | false | 65 | | `showHeader` | 是否显示表头 | boolean | 否 | true | 66 | | `noMoreText` | loadStatus 为 noMore 状态显示文案 | string | 否 | 没有更多了 | 67 | | `loadLoadingText` | loadStatus 为 loading 状态显示文案 | string | 否 | 加载中... | 68 | | `distance` | 小于视窗距离多少开始触发 onLoad | number | 否 | 30 | 69 | | `showLoad` | 是否显示 load 加载状态,为 false 时不触发 onLoad 事件 | boolean | 否 | true | 70 | | `fixedLoad` | 是否固定加载更多,不随 X 轴滚动而滚动 | boolean | 否 | true | 71 | | `emptyText` | 空数据显示文本 | string \| ReactNode | 否 | 暂无数据 | 72 | | `cellEmptyText` | 单元格空数据显示文本 | string | 否 | - | 73 | | `renderEmpty` | 自定义渲染空数据 | ReactNode | 否 | | 74 | | `size` | 间距大小 | 'small' \| 'middle' \| 'large' \| number | 否 | middle | 75 | | `colWidth` | 默认列宽度 | number | 否 | 120 | 76 | | `striped` | 是否显示斑马纹 (为 true 时和 even 效果一样) | 'odd' \| 'even' \| boolean | 否 | false | 77 | 78 | ## Events 79 | 80 | | 事件名 | 描述 | 类型 | 必传 | 默认值 | 81 | | ---------- | -------------------------------------------------------------------- | --------------------------------------- | ---- | ------ | 82 | | `onLoad` | 滚动底部触发,用于上拉加载,(注意:需要设置 height 高度,才能触发) | Function | 否 | | 83 | | `onSorter` | 点击表头按钮触发排序 | ({ column, field, order }: SorterEvent) | 否 | | 84 | | `onRow` | 行点击事件 | function(record, index) | 否 | | 85 | 86 | ## Column 87 | 88 | | 参数 | 描述 | 类型 | 必传 | 默认值 | 89 | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ----------- | ------ | 90 | | `title` | 标题 | string | JSX.Element | 是 | 91 | | `dataIndex` | 列数据在数据项中对应的路径 | string | 是 | `[]` | 92 | | `key` | 表格行 key 的取值,可以是字符串或一个函数 | string \| function(record): string | 否 | `key` | 93 | | `align` | 设置该列文本对齐方式 | string | 否 | center | 94 | | `style` | 标题样式 | object | 否 | | 95 | | `align` | 外层容器的类名 | string | 否 | | 96 | | `className` | 标题类名 | string | 否 | | 97 | | `render` | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引 | function(text, record, index) {} | 否 | | 98 | | `width` | ScrollView 容器类名 | string | 否 | | 99 | | `fixed` | 固定列 | left \| right | 否 | | 100 | | `sorter` | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | CompareFn | 否 | | 101 | | `sortOrder` | 排序的受控属性,外界可用此控制列的排序,可设置为 `ascend` `descend` false | boolean \| string | | | 102 | | `onCell` | 单元格点击事件 | function(record, index) | 否 | | 103 | 104 | # 使用 105 | 106 | ```jsx 107 | import { useState } from 'react' 108 | import 'taro-react-table/dist/index.css' 109 | import Table,{ Columns, LoadStatus, SorterEvent } from 'taro-react-table' 110 | 111 | export default function Demo() { 112 | const [loading, setLoading] = useState(false) 113 | const [dataSource, setDataSource] = useState([ 114 | { 115 | name1: '无人之境1', 116 | name2: '打回原形1', 117 | name3: '防不胜防1', 118 | name4: '十面埋伏1', 119 | name5: 'k歌之王1', 120 | name6: '岁月如歌1', 121 | }, 122 | { 123 | name1: '无人之境2', 124 | name2: '打回原形2', 125 | name3: '防不胜防2', 126 | name4: '十面埋伏2', 127 | name5: 'k歌之王2', 128 | name6: '岁月如歌2', 129 | }, 130 | { 131 | name1: '无人之境3', 132 | name2: '打回原形3', 133 | name3: '防不胜防3', 134 | name4: '十面埋伏3', 135 | name5: 'k歌之王3', 136 | name6: '岁月如歌3', 137 | }, 138 | { 139 | name1: '无人之境4', 140 | name2: '打回原形4', 141 | name3: '防不胜防4', 142 | name4: '十面埋伏4', 143 | name5: 'k歌之王4', 144 | name6: '岁月如歌4', 145 | }, 146 | { 147 | name1: '无人之境5', 148 | name2: '打回原形5', 149 | name3: '防不胜防5', 150 | name4: '十面埋伏5', 151 | name5: 'k歌之王5', 152 | name6: '岁月如歌5', 153 | }, 154 | { 155 | name1: '无人之境6', 156 | name2: '打回原形6', 157 | name3: '防不胜防6', 158 | name4: '十面埋伏6', 159 | name5: 'k歌之王6', 160 | name6: '岁月如歌6', 161 | }, 162 | ]) 163 | const [loadStatus, setLoadStatus] = useState(null) 164 | const [sortInfo, setSortInfo] = useState>({ 165 | field: 'name1', 166 | order: 'ascend', 167 | }) 168 | 169 | const columns: Columns[] = [ 170 | { 171 | title: 'Song1', 172 | dataIndex: 'name1', 173 | sorter: true, 174 | fixed: 'left', 175 | width: 100, 176 | sortOrder: sortInfo.field == 'name1' && sortInfo.order, 177 | }, 178 | { 179 | title: 'Song2', 180 | width: 100, 181 | dataIndex: 'name2', 182 | }, 183 | { 184 | title: 'Song3', 185 | dataIndex: 'name3', 186 | }, 187 | { 188 | title: 'Song4', 189 | dataIndex: 'name4', 190 | }, 191 | { 192 | title: 'Song5', 193 | dataIndex: 'name5', 194 | }, 195 | { 196 | title: 'Song6', 197 | dataIndex: 'name6', 198 | }, 199 | ] 200 | 201 | const getList = async (): Promise => { 202 | return new Promise((resolve, reject) => { 203 | setTimeout(() => { 204 | const list = [...dataSource] 205 | for (let i = 1; i < 10; i++) { 206 | const size = list.length + 1 207 | list.push({ 208 | name1: `无人之境${size}`, 209 | name2: `打回原形${size}`, 210 | name3: `防不胜防${size}`, 211 | name4: `十面埋伏${size}`, 212 | name5: `k歌之王${size}`, 213 | name6: `岁月如歌${size}`, 214 | }) 215 | } 216 | resolve(list) 217 | }, 1000) 218 | }) 219 | } 220 | 221 | const onLoad = async e => { 222 | setLoadStatus('loading') 223 | const list = await getList() 224 | setDataSource(list) 225 | setLoadStatus(list.length > 20 ? 'noMore' : null) 226 | } 227 | 228 | // 排序回调 229 | const onSorter = ({ column, field, order }: SorterEvent) => { 230 | console.log(column, field, order) 231 | // 模拟排序加载效果 232 | setLoading(state => !state) 233 | setSortInfo({ order, field }) 234 | const tempList = [...dataSource] 235 | setTimeout(() => { 236 | setLoading(false) 237 | tempList.reverse() 238 | setDataSource(tempList) 239 | }, 1000) 240 | } 241 | 242 | return ( 243 |
252 | ) 253 | } 254 | 255 | ``` 256 | 257 | # 友情推荐 258 | 259 | | 项目 | 描述 | 260 | | ------------------------------------------------------------------------- | ------------------------------------------------ | 261 | | [taro-react-echarts](https://github.com/qiuweikangdev/taro-react-echarts) | 基于 Taro3、React 的 H5 和微信小程序多端图表组件 | 262 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | const components = fs 5 | .readdirSync(path.resolve(__dirname, 'packages'), { withFileTypes: true }) 6 | .filter((dirent) => dirent.isDirectory()) 7 | .map((dirent) => dirent.name) 8 | 9 | /** @type {import('cz-git').UserConfig} */ 10 | module.exports = { 11 | extends: ['@commitlint/config-conventional'], 12 | rules: { 13 | 'body-leading-blank': [1, 'always'], 14 | 'footer-leading-blank': [1, 'always'], 15 | 'header-max-length': [2, 'always', 72], 16 | 'scope-case': [2, 'always', 'lower-case'], 17 | 'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']], 18 | 'subject-empty': [2, 'never'], 19 | 'subject-full-stop': [2, 'never', '.'], 20 | 'type-case': [2, 'always', 'lower-case'], 21 | 'type-empty': [2, 'never'], 22 | 'type-enum': [ 23 | 2, 24 | 'always', 25 | ['chore', 'docs', 'feat', 'fix', 'test', 'refactor', 'revert', 'style', 'release'], 26 | ], 27 | }, 28 | prompt: { 29 | typesAppend: [{ value: 'release', name: 'release: release version' }], 30 | scopes: [...components], 31 | allowCustomIssuePrefixs: false, 32 | allowEmptyIssuePrefixs: false, 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuweikangdev/taro-react-table/deaa62059e544e3d7cb1d1e17904cf054e743f65/images/demo.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "workspaces": [ 4 | "packages/*" 5 | ], 6 | "license": "MIT", 7 | "scripts": { 8 | "clear": "rimraf packages/*/{dist,node_modules}", 9 | "dev:h5": "cross-env NODE_ENV=dev pnpm update:pkg && turbo run dev:h5 --filter=demo", 10 | "dev:weapp": "cross-env NODE_ENV=dev pnpm update:pkg && turbo run dev:weapp --filter=demo", 11 | "build": "cross-env NODE_ENV=prod pnpm update:pkg && turbo run build:table --filter=taro-react-table", 12 | "turbo:publish": "cross-env NODE_ENV=prod pnpm update:pkg && turbo run publish", 13 | "update:pkg": "node ./scripts/update-pkg.js", 14 | "format": "prettier --write \"**/*.{js,ts,tsx,md}\"", 15 | "lint": "eslint --fix \"**/*.{js,ts,tsx}\" ", 16 | "prepare": "husky install", 17 | "release": "node ./scripts/release.js", 18 | "commit": "git cz" 19 | }, 20 | "devDependencies": { 21 | "@commitlint/cli": "^17.1.2", 22 | "@commitlint/config-conventional": "^17.1.0", 23 | "commander": "^9.4.1", 24 | "commitizen": "^4.2.5", 25 | "conventional-changelog-cli": "^2.2.2", 26 | "cross-env": "^7.0.3", 27 | "cz-conventional-changelog": "^3.3.0", 28 | "cz-git": "^1.3.11", 29 | "eslint": "^8.23.1", 30 | "eslint-config-prettier": "^8.5.0", 31 | "eslint-plugin-import": "^2.12.0", 32 | "eslint-plugin-prettier": "^4.2.1", 33 | "eslint-plugin-react": "^7.8.2", 34 | "eslint-plugin-react-hooks": "^4.2.0", 35 | "husky": "^8.0.1", 36 | "lint-staged": "^13.0.3", 37 | "prettier": "latest", 38 | "rimraf": "^3.0.2", 39 | "standard-version": "^9.5.0", 40 | "turbo": "latest", 41 | "typescript": "^4.1.0" 42 | }, 43 | "lint-staged": { 44 | "*.{js,ts,jsx,tsx}": "eslint --fix", 45 | "*.{md,json}": "prettier --write" 46 | }, 47 | "config": { 48 | "commitizen": { 49 | "path": "node_modules/cz-git" 50 | } 51 | }, 52 | "engines": { 53 | "npm": ">=7.0.0", 54 | "node": ">=14.0.0" 55 | }, 56 | "packageManager": "pnpm@7.8.0", 57 | "version": "1.5.0" 58 | } 59 | -------------------------------------------------------------------------------- /packages/demo/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /packages/demo/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | deploy_versions/ 3 | .temp/ 4 | .rn_temp/ 5 | node_modules/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /packages/demo/babel.config.js: -------------------------------------------------------------------------------- 1 | // babel-preset-taro 更多选项和默认值: 2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md 3 | module.exports = { 4 | presets: [ 5 | [ 6 | 'taro', 7 | { 8 | framework: 'react', 9 | ts: true, 10 | }, 11 | ], 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /packages/demo/config/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"development"', 4 | }, 5 | defineConstants: {}, 6 | mini: {}, 7 | h5: { 8 | devServer: { 9 | host: '127.0.0.1', 10 | port: 8888, 11 | }, 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /packages/demo/config/index.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | projectName: '.\\packages\\demo', 3 | date: '2022-10-1', 4 | designWidth: 750, 5 | deviceRatio: { 6 | 640: 2.34 / 2, 7 | 750: 1, 8 | 828: 1.81 / 2, 9 | }, 10 | sourceRoot: 'src', 11 | outputRoot: 'dist', 12 | plugins: [], 13 | defineConstants: {}, 14 | copy: { 15 | patterns: [], 16 | options: {}, 17 | }, 18 | framework: 'react', 19 | compiler: { 20 | type: 'webpack5', 21 | prebundle: { 22 | enable: false, 23 | }, 24 | }, 25 | cache: { 26 | enable: false, // Webpack 持久化缓存配置,建议开启。默认配置请参考:https://docs.taro.zone/docs/config-detail#cache 27 | }, 28 | mini: { 29 | postcss: { 30 | pxtransform: { 31 | enable: true, 32 | config: {}, 33 | }, 34 | url: { 35 | enable: true, 36 | config: { 37 | limit: 1024, // 设定转换尺寸上限 38 | }, 39 | }, 40 | cssModules: { 41 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 42 | config: { 43 | namingPattern: 'module', // 转换模式,取值为 global/module 44 | generateScopedName: '[name]__[local]___[hash:base64:5]', 45 | }, 46 | }, 47 | }, 48 | }, 49 | h5: { 50 | publicPath: '/', 51 | staticDirectory: 'static', 52 | postcss: { 53 | autoprefixer: { 54 | enable: true, 55 | config: {}, 56 | }, 57 | cssModules: { 58 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 59 | config: { 60 | namingPattern: 'module', // 转换模式,取值为 global/module 61 | generateScopedName: '[name]__[local]___[hash:base64:5]', 62 | }, 63 | }, 64 | }, 65 | }, 66 | rn: { 67 | appName: 'taroDemo', 68 | postcss: { 69 | cssModules: { 70 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true 71 | }, 72 | }, 73 | }, 74 | } 75 | 76 | module.exports = function (merge) { 77 | if (process.env.NODE_ENV === 'development') { 78 | return merge({}, config, require('./dev')) 79 | } 80 | return merge({}, config, require('./prod')) 81 | } 82 | -------------------------------------------------------------------------------- /packages/demo/config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | NODE_ENV: '"production"', 4 | }, 5 | defineConstants: {}, 6 | mini: {}, 7 | h5: { 8 | /** 9 | * WebpackChain 插件配置 10 | * @docs https://github.com/neutrinojs/webpack-chain 11 | */ 12 | // webpackChain (chain) { 13 | // /** 14 | // * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。 15 | // * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer 16 | // */ 17 | // chain.plugin('analyzer') 18 | // .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []) 19 | // /** 20 | // * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。 21 | // * @docs https://github.com/chrisvfritz/prerender-spa-plugin 22 | // */ 23 | // const path = require('path') 24 | // const Prerender = require('prerender-spa-plugin') 25 | // const staticDir = path.join(__dirname, '..', 'dist') 26 | // chain 27 | // .plugin('prerender') 28 | // .use(new Prerender({ 29 | // staticDir, 30 | // routes: [ '/pages/index/index' ], 31 | // postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') }) 32 | // })) 33 | // } 34 | }, 35 | } 36 | -------------------------------------------------------------------------------- /packages/demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "version": "1.0.0", 4 | "private": true, 5 | "description": "demo", 6 | "templateInfo": { 7 | "name": "default", 8 | "typescript": true, 9 | "css": "less" 10 | }, 11 | "scripts": { 12 | "build:weapp": "taro build --type weapp", 13 | "build:swan": "taro build --type swan", 14 | "build:alipay": "taro build --type alipay", 15 | "build:tt": "taro build --type tt", 16 | "build:h5": "taro build --type h5", 17 | "build:rn": "taro build --type rn", 18 | "build:qq": "taro build --type qq", 19 | "build:jd": "taro build --type jd", 20 | "build:quickapp": "taro build --type quickapp", 21 | "dev:weapp": "npm run build:weapp -- --watch", 22 | "dev:swan": "npm run build:swan -- --watch", 23 | "dev:alipay": "npm run build:alipay -- --watch", 24 | "dev:tt": "npm run build:tt -- --watch", 25 | "dev:h5": "npm run build:h5 -- --watch", 26 | "dev:rn": "npm run build:rn -- --watch", 27 | "dev:qq": "npm run build:qq -- --watch", 28 | "dev:jd": "npm run build:jd -- --watch", 29 | "dev:quickapp": "npm run build:quickapp -- --watch" 30 | }, 31 | "browserslist": [ 32 | "last 3 versions", 33 | "Android >= 4.1", 34 | "ios >= 8" 35 | ], 36 | "author": "", 37 | "dependencies": { 38 | "@babel/runtime": "^7.7.7", 39 | "@tarojs/components": "3.5.5", 40 | "@tarojs/helper": "3.5.5", 41 | "@tarojs/plugin-framework-react": "3.5.5", 42 | "@tarojs/plugin-platform-alipay": "3.5.5", 43 | "@tarojs/plugin-platform-jd": "3.5.5", 44 | "@tarojs/plugin-platform-qq": "3.5.5", 45 | "@tarojs/plugin-platform-swan": "3.5.5", 46 | "@tarojs/plugin-platform-tt": "3.5.5", 47 | "@tarojs/plugin-platform-weapp": "3.5.5", 48 | "@tarojs/react": "3.5.5", 49 | "@tarojs/router": "3.5.5", 50 | "@tarojs/runtime": "3.5.5", 51 | "@tarojs/shared": "3.5.5", 52 | "@tarojs/taro": "3.5.5", 53 | "@tarojs/taro-h5": "3.5.5", 54 | "react": "^18.0.0", 55 | "react-dom": "^18.0.0", 56 | "taro-react-table": "workspace:*" 57 | }, 58 | "devDependencies": { 59 | "@babel/core": "^7.8.0", 60 | "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5", 61 | "@tarojs/cli": "3.5.5", 62 | "@tarojs/webpack5-runner": "3.5.5", 63 | "@types/react": "^18.0.0", 64 | "@types/react-dom": "17.0.2", 65 | "@types/webpack-env": "^1.13.6", 66 | "@typescript-eslint/eslint-plugin": "^5.20.0", 67 | "@typescript-eslint/parser": "^5.20.0", 68 | "babel-preset-taro": "3.5.5", 69 | "eslint": "^8.12.0", 70 | "eslint-config-taro": "3.5.5", 71 | "eslint-plugin-import": "^2.12.0", 72 | "eslint-plugin-react": "^7.8.2", 73 | "eslint-plugin-react-hooks": "^4.2.0", 74 | "react-refresh": "^0.11.0", 75 | "stylelint": "^14.4.0", 76 | "typescript": "^4.1.0", 77 | "webpack": "5.69.0" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /packages/demo/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "./dist", 3 | "projectname": ".\\packages\\demo", 4 | "description": "demo", 5 | "appid": "touristappid", 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": false, 9 | "enhance": false, 10 | "compileHotReLoad": false, 11 | "postcss": false, 12 | "minified": false 13 | }, 14 | "compileType": "miniprogram" 15 | } 16 | -------------------------------------------------------------------------------- /packages/demo/project.tt.json: -------------------------------------------------------------------------------- 1 | { 2 | "miniprogramRoot": "./", 3 | "projectname": ".\\packages\\demo", 4 | "appid": "testAppId", 5 | "setting": { 6 | "es6": false, 7 | "minified": false 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/demo/src/app.config.ts: -------------------------------------------------------------------------------- 1 | export default defineAppConfig({ 2 | pages: ['pages/index/index'], 3 | window: { 4 | backgroundTextStyle: 'light', 5 | navigationBarBackgroundColor: '#fff', 6 | navigationBarTitleText: 'WeChat', 7 | navigationBarTextStyle: 'black', 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /packages/demo/src/app.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuweikangdev/taro-react-table/deaa62059e544e3d7cb1d1e17904cf054e743f65/packages/demo/src/app.less -------------------------------------------------------------------------------- /packages/demo/src/app.ts: -------------------------------------------------------------------------------- 1 | import { Component, PropsWithChildren } from 'react' 2 | import './app.less' 3 | 4 | class App extends Component { 5 | componentDidMount() {} 6 | 7 | componentDidShow() {} 8 | 9 | componentDidHide() {} 10 | 11 | render() { 12 | // this.props.children 是将要会渲染的页面 13 | return this.props.children 14 | } 15 | } 16 | 17 | export default App 18 | -------------------------------------------------------------------------------- /packages/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | .\\packages\\demo 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/demo/src/pages/index/index.config.ts: -------------------------------------------------------------------------------- 1 | export default definePageConfig({ 2 | navigationBarTitleText: '首页', 3 | }) 4 | -------------------------------------------------------------------------------- /packages/demo/src/pages/index/index.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuweikangdev/taro-react-table/deaa62059e544e3d7cb1d1e17904cf054e743f65/packages/demo/src/pages/index/index.less -------------------------------------------------------------------------------- /packages/demo/src/pages/index/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import Table, { Columns, LoadStatus, SorterEvent } from 'taro-react-table' 3 | import './index.less' 4 | 5 | export default function Demo() { 6 | const [loading, setLoading] = useState(false) 7 | const [dataSource, setDataSource] = useState([ 8 | { 9 | name1: '无人之境1', 10 | name2: '打回原形1', 11 | name3: '防不胜防1', 12 | name4: '十面埋伏1', 13 | name5: 'k歌之王1', 14 | name6: '岁月如歌1', 15 | }, 16 | { 17 | name1: '无人之境2', 18 | name2: '打回原形2', 19 | name3: '防不胜防2', 20 | name4: '十面埋伏2', 21 | name5: 'k歌之王2', 22 | name6: '岁月如歌2', 23 | }, 24 | { 25 | name1: '无人之境3', 26 | name2: '打回原形3', 27 | name3: '防不胜防3', 28 | name4: '十面埋伏3', 29 | name5: 'k歌之王3', 30 | name6: '岁月如歌3', 31 | }, 32 | { 33 | name1: '无人之境4', 34 | name2: '打回原形4', 35 | name3: '防不胜防4', 36 | name4: '十面埋伏4', 37 | name5: 'k歌之王4', 38 | name6: '岁月如歌4', 39 | }, 40 | { 41 | name1: '无人之境5', 42 | name2: '打回原形5', 43 | name3: '防不胜防5', 44 | name4: '十面埋伏5', 45 | name5: 'k歌之王5', 46 | name6: '岁月如歌5', 47 | }, 48 | { 49 | name1: '无人之境6', 50 | name2: '打回原形6', 51 | name3: '防不胜防6', 52 | name4: '十面埋伏6', 53 | name5: 'k歌之王6', 54 | name6: '岁月如歌6', 55 | }, 56 | ]) 57 | const [loadStatus, setLoadStatus] = useState(null) 58 | const [sortInfo, setSortInfo] = useState>({ 59 | field: 'name1', 60 | order: 'ascend', 61 | }) 62 | 63 | const columns: Columns[] = [ 64 | { 65 | title: 'Song1', 66 | dataIndex: 'name1', 67 | sorter: true, 68 | fixed: 'left', 69 | width: 100, 70 | sortOrder: sortInfo.field == 'name1' && sortInfo.order, 71 | }, 72 | { 73 | title: 'Song2', 74 | width: 100, 75 | dataIndex: 'name2', 76 | }, 77 | { 78 | title: 'Song3', 79 | dataIndex: 'name3', 80 | }, 81 | { 82 | title: 'Song4', 83 | dataIndex: 'name4', 84 | }, 85 | { 86 | title: 'Song5', 87 | dataIndex: 'name5', 88 | }, 89 | { 90 | title: 'Song6', 91 | dataIndex: 'name6', 92 | }, 93 | ] 94 | 95 | const getList = async (): Promise => { 96 | return new Promise((resolve, reject) => { 97 | setTimeout(() => { 98 | const list = [...dataSource] 99 | for (let i = 1; i < 10; i++) { 100 | const size = list.length + 1 101 | list.push({ 102 | name1: `无人之境${size}`, 103 | name2: `打回原形${size}`, 104 | name3: `防不胜防${size}`, 105 | name4: `十面埋伏${size}`, 106 | name5: `k歌之王${size}`, 107 | name6: `岁月如歌${size}`, 108 | }) 109 | } 110 | resolve(list) 111 | }, 1000) 112 | }) 113 | } 114 | 115 | const onLoad = async (e) => { 116 | setLoadStatus('loading') 117 | const list = await getList() 118 | setDataSource(list) 119 | setLoadStatus(list.length > 20 ? 'noMore' : null) 120 | } 121 | 122 | // 排序回调 123 | const onSorter = ({ column, field, order }: SorterEvent) => { 124 | console.log(column, field, order) 125 | // 模拟排序加载效果 126 | setLoading((state) => !state) 127 | setSortInfo({ order, field }) 128 | const tempList = [...dataSource] 129 | setTimeout(() => { 130 | setLoading(false) 131 | tempList.reverse() 132 | setDataSource(tempList) 133 | }, 1000) 134 | } 135 | 136 | return ( 137 | 146 | ) 147 | } 148 | -------------------------------------------------------------------------------- /packages/demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "removeComments": false, 6 | "preserveConstEnums": true, 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true, 11 | "outDir": "lib", 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "strictNullChecks": true, 15 | "sourceMap": true, 16 | "baseUrl": ".", 17 | "rootDir": ".", 18 | "jsx": "react-jsx", 19 | "allowJs": true, 20 | "resolveJsonModule": true, 21 | "typeRoots": ["node_modules/@types"] 22 | }, 23 | "include": ["./src", "./types"], 24 | "compileOnSave": false 25 | } 26 | -------------------------------------------------------------------------------- /packages/demo/types/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.png' 4 | declare module '*.gif' 5 | declare module '*.jpg' 6 | declare module '*.jpeg' 7 | declare module '*.svg' 8 | declare module '*.css' 9 | declare module '*.less' 10 | declare module '*.scss' 11 | declare module '*.sass' 12 | declare module '*.styl' 13 | 14 | declare namespace NodeJS { 15 | interface ProcessEnv { 16 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/table/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /packages/table/README.md: -------------------------------------------------------------------------------- 1 | # taro-react-table 2 | 3 | 基于 Taro3、React 的 H5 和微信小程序多端表格组件 4 | 5 | - 兼容 H5、微信小程序 6 | - 自定义样式 7 | - 自定义排序 8 | - 固定表头 9 | - 滚动上拉加载 10 | 11 | ![](https://raw.githubusercontent.com/qiuweikangdev/taro-react-table/master/images/demo.gif) 12 | 13 | # 安装 14 | 15 | ```bash 16 | npm install taro-react-table 17 | ``` 18 | 19 | # 配置 20 | 21 | - config/index.js 配置 22 | - 让 taro 去通过 postcss 编译处理 `taro-react-table`模块, 需要对 `taro-react-table` 里的样式单位进行转换适配 23 | 24 | ```js 25 | // config/index.js 26 | const config = { 27 | h5: { 28 | esnextModules: ['taro-react-table'], 29 | }, 30 | } 31 | ``` 32 | 33 | # 导入组件 34 | 35 | ```js 36 | import Table from 'taro-react-table' 37 | import 'taro-react-table/dist/index.css' 38 | ``` 39 | 40 | # 参数说明 41 | 42 | ## Table 43 | 44 | | 参数 | 描述 | 类型 | 必传 | 默认值 | 45 | | ----------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------- | ---- | ---------- | 46 | | 本身参数 | 参考[ScrollView](https://taro-docs.jd.com/taro/docs/components/viewContainer/scroll-view) | | | | 47 | | `dataSource` | 数据源 | object[] | 是 | `[]` | 48 | | `columns` | 表格列的配置描述,具体项见下表 | Columns[] | 是 | `[]` | 49 | | `rowKey` | 表格行 key 的取值,可以是字符串或一个函数 | string \| function(record): string | 是 | `key` | 50 | | `wrapperClass` | 外层容器的类名 | string | 否 | | 51 | | `wrapperStyle` | 外层容器的样式 | object | 否 | | 52 | | `className` | ScrollView 容器类名 | string | 否 | | 53 | | `style` | ScrollView 容器样式 | object | 否 | | 54 | | `rowClassName` | 行类名 | string | 否 | | 55 | | `rowStyle` | 行样式 | object | 否 | | 56 | | `colClassName` | 单元格类名 | string | 否 | | 57 | | `colStyle` | 单元格样式 | object | 否 | | 58 | | `titleStyle` | 标题样式 | object | 否 | | 59 | | `titleClassName` | 标题类名 | string | 否 | | 60 | | `titleClassName` | 标题类名 | string | 否 | | 61 | | `loading` | 是否显示加载 | boolean | 否 | | 62 | | `loadingText` | 加载文本 | string | 否 | '' | 63 | | `loadStatus` | 加载状态 | loading \| noMore \| null | 否 | null | 64 | | `unsort` | 设置是否取消排序 (一般需求不需要取消排序,设置 true 可开启取消排序) | boolean | 否 | false | 65 | | `showHeader` | 是否显示表头 | boolean | 否 | true | 66 | | `noMoreText` | loadStatus 为 noMore 状态显示文案 | string | 否 | 没有更多了 | 67 | | `loadLoadingText` | loadStatus 为 loading 状态显示文案 | string | 否 | 加载中... | 68 | | `distance` | 小于视窗距离多少开始触发 onLoad | number | 否 | 30 | 69 | | `showLoad` | 是否显示 load 加载状态,为 false 时不触发 onLoad 事件 | boolean | 否 | true | 70 | | `fixedLoad` | 是否固定加载更多,不随 X 轴滚动而滚动 | boolean | 否 | true | 71 | | `emptyText` | 空数据显示文本 | string \| ReactNode | 否 | 暂无数据 | 72 | | `cellEmptyText` | 单元格空数据显示文本 | string | 否 | - | 73 | | `renderEmpty` | 自定义渲染空数据 | ReactNode | 否 | | 74 | | `size` | 间距大小 | 'small' \| 'middle' \| 'large' \| number | 否 | middle | 75 | | `colWidth` | 默认列宽度 | number | 否 | 120 | 76 | | `striped` | 是否显示斑马纹 (为 true 时和 even 效果一样) | 'odd' \| 'even' \| boolean | 否 | false | 77 | 78 | ## Events 79 | 80 | | 事件名 | 描述 | 类型 | 必传 | 默认值 | 81 | | ---------- | -------------------------------------------------------------------- | --------------------------------------- | ---- | ------ | 82 | | `onLoad` | 滚动底部触发,用于上拉加载,(注意:需要设置 height 高度,才能触发) | Function | 否 | | 83 | | `onSorter` | 点击表头按钮触发排序 | ({ column, field, order }: SorterEvent) | 否 | | 84 | | `onRow` | 行点击事件 | function(record, index) | 否 | | 85 | 86 | ## Column 87 | 88 | | 参数 | 描述 | 类型 | 必传 | 默认值 | 89 | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ----------- | ------ | 90 | | `title` | 标题 | string | JSX.Element | 是 | 91 | | `dataIndex` | 列数据在数据项中对应的路径 | string | 是 | `[]` | 92 | | `key` | 表格行 key 的取值,可以是字符串或一个函数 | string \| function(record): string | 否 | `key` | 93 | | `align` | 设置该列文本对齐方式 | string | 否 | center | 94 | | `style` | 标题样式 | object | 否 | | 95 | | `align` | 外层容器的类名 | string | 否 | | 96 | | `className` | 标题类名 | string | 否 | | 97 | | `render` | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引 | function(text, record, index) {} | 否 | | 98 | | `width` | ScrollView 容器类名 | string | 否 | | 99 | | `fixed` | 固定列 | left \| right | 否 | | 100 | | `sorter` | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | CompareFn | 否 | | 101 | | `sortOrder` | 排序的受控属性,外界可用此控制列的排序,可设置为 `ascend` `descend` false | boolean \| string | | | 102 | | `onCell` | 单元格点击事件 | function(record, index) | 否 | | 103 | 104 | # 使用 105 | 106 | ```jsx 107 | import { useState } from 'react' 108 | import 'taro-react-table/dist/index.css' 109 | import Table,{ Columns, LoadStatus, SorterEvent } from 'taro-react-table' 110 | 111 | export default function Demo() { 112 | const [loading, setLoading] = useState(false) 113 | const [dataSource, setDataSource] = useState([ 114 | { 115 | name1: '无人之境1', 116 | name2: '打回原形1', 117 | name3: '防不胜防1', 118 | name4: '十面埋伏1', 119 | name5: 'k歌之王1', 120 | name6: '岁月如歌1', 121 | }, 122 | { 123 | name1: '无人之境2', 124 | name2: '打回原形2', 125 | name3: '防不胜防2', 126 | name4: '十面埋伏2', 127 | name5: 'k歌之王2', 128 | name6: '岁月如歌2', 129 | }, 130 | { 131 | name1: '无人之境3', 132 | name2: '打回原形3', 133 | name3: '防不胜防3', 134 | name4: '十面埋伏3', 135 | name5: 'k歌之王3', 136 | name6: '岁月如歌3', 137 | }, 138 | { 139 | name1: '无人之境4', 140 | name2: '打回原形4', 141 | name3: '防不胜防4', 142 | name4: '十面埋伏4', 143 | name5: 'k歌之王4', 144 | name6: '岁月如歌4', 145 | }, 146 | { 147 | name1: '无人之境5', 148 | name2: '打回原形5', 149 | name3: '防不胜防5', 150 | name4: '十面埋伏5', 151 | name5: 'k歌之王5', 152 | name6: '岁月如歌5', 153 | }, 154 | { 155 | name1: '无人之境6', 156 | name2: '打回原形6', 157 | name3: '防不胜防6', 158 | name4: '十面埋伏6', 159 | name5: 'k歌之王6', 160 | name6: '岁月如歌6', 161 | }, 162 | ]) 163 | const [loadStatus, setLoadStatus] = useState(null) 164 | const [sortInfo, setSortInfo] = useState>({ 165 | field: 'name1', 166 | order: 'ascend', 167 | }) 168 | 169 | const columns: Columns[] = [ 170 | { 171 | title: 'Song1', 172 | dataIndex: 'name1', 173 | sorter: true, 174 | fixed: 'left', 175 | width: 100, 176 | sortOrder: sortInfo.field == 'name1' && sortInfo.order, 177 | }, 178 | { 179 | title: 'Song2', 180 | width: 100, 181 | dataIndex: 'name2', 182 | }, 183 | { 184 | title: 'Song3', 185 | dataIndex: 'name3', 186 | }, 187 | { 188 | title: 'Song4', 189 | dataIndex: 'name4', 190 | }, 191 | { 192 | title: 'Song5', 193 | dataIndex: 'name5', 194 | }, 195 | { 196 | title: 'Song6', 197 | dataIndex: 'name6', 198 | }, 199 | ] 200 | 201 | const getList = async (): Promise => { 202 | return new Promise((resolve, reject) => { 203 | setTimeout(() => { 204 | const list = [...dataSource] 205 | for (let i = 1; i < 10; i++) { 206 | const size = list.length + 1 207 | list.push({ 208 | name1: `无人之境${size}`, 209 | name2: `打回原形${size}`, 210 | name3: `防不胜防${size}`, 211 | name4: `十面埋伏${size}`, 212 | name5: `k歌之王${size}`, 213 | name6: `岁月如歌${size}`, 214 | }) 215 | } 216 | resolve(list) 217 | }, 1000) 218 | }) 219 | } 220 | 221 | const onLoad = async e => { 222 | setLoadStatus('loading') 223 | const list = await getList() 224 | setDataSource(list) 225 | setLoadStatus(list.length > 20 ? 'noMore' : null) 226 | } 227 | 228 | // 排序回调 229 | const onSorter = ({ column, field, order }: SorterEvent) => { 230 | console.log(column, field, order) 231 | // 模拟排序加载效果 232 | setLoading(state => !state) 233 | setSortInfo({ order, field }) 234 | const tempList = [...dataSource] 235 | setTimeout(() => { 236 | setLoading(false) 237 | tempList.reverse() 238 | setDataSource(tempList) 239 | }, 1000) 240 | } 241 | 242 | return ( 243 |
252 | ) 253 | } 254 | 255 | ``` 256 | 257 | # 友情推荐 258 | 259 | | 项目 | 描述 | 260 | | ------------------------------------------------------------------------- | ------------------------------------------------ | 261 | | [taro-react-echarts](https://github.com/qiuweikangdev/taro-react-echarts) | 基于 Taro3、React 的 H5 和微信小程序多端图表组件 | 262 | -------------------------------------------------------------------------------- /packages/table/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "taro-react-table", 3 | "version": "1.7.5", 4 | "description": "taro表格", 5 | "scripts": { 6 | "build:table": "rimraf dist && rollup --config ./rollup.config.js", 7 | "publish:table": "pnpm publish --filter=taro-react-table" 8 | }, 9 | "keywords": [ 10 | "taro", 11 | "table", 12 | "react", 13 | "mobile", 14 | "component", 15 | "taro-table", 16 | "taro-react-table" 17 | ], 18 | "author": "qiuweikang", 19 | "license": "MIT", 20 | "main": "dist/index.js", 21 | "module": "dist/index.esm.js", 22 | "css": "dist/index.css", 23 | "publishConfig": { 24 | "registry": "https://registry.npmjs.org", 25 | "main": "dist/index.js", 26 | "module": "dist/index.esm.js", 27 | "types": "dist/index.d.ts" 28 | }, 29 | "files": [ 30 | "dist", 31 | "package.json", 32 | "README.md", 33 | "LICENSE" 34 | ], 35 | "browserslist": [ 36 | "last 3 versions", 37 | "Android >= 4.1", 38 | "ios >= 8" 39 | ], 40 | "devDependencies": { 41 | "@babel/core": "^7.8.0", 42 | "@babel/preset-env": "^7.19.1", 43 | "@rollup/plugin-alias": "^3.1.9", 44 | "@rollup/plugin-babel": "^5.3.1", 45 | "@rollup/plugin-commonjs": "^22.0.2", 46 | "@rollup/plugin-node-resolve": "^14.1.0", 47 | "@rollup/plugin-typescript": "^8.5.0", 48 | "@tarojs/cli": "3.5.5", 49 | "@tarojs/components": "3.5.5", 50 | "@tarojs/plugin-framework-react": "3.5.5", 51 | "@tarojs/plugin-platform-weapp": "3.5.5", 52 | "@tarojs/react": "3.5.5", 53 | "@tarojs/runtime": "3.5.5", 54 | "@tarojs/shared": "3.5.5", 55 | "@tarojs/taro": "3.5.5", 56 | "@tarojs/taro-h5": "3.5.5", 57 | "@types/echarts": "^4.9.16", 58 | "@types/react": "^18.0.0", 59 | "@types/react-dom": "17.0.2", 60 | "@types/webpack-env": "^1.13.6", 61 | "babel-preset-taro": "3.5.5", 62 | "postcss": "^8.4.17", 63 | "react": "^18.0.0", 64 | "react-dom": "^18.0.0", 65 | "rollup": "^2.79.0", 66 | "rollup-plugin-postcss": "^4.0.2", 67 | "typescript": "^4.1.0" 68 | }, 69 | "dependencies": { 70 | "classnames": "^2.3.1" 71 | }, 72 | "peerDependencies": { 73 | "@tarojs/components": ">=3", 74 | "@tarojs/taro": ">=3", 75 | "react": ">=16.9", 76 | "react-dom": ">=16.9" 77 | }, 78 | "bugs": { 79 | "url": "https://github.com/qiuweikangdev/taro-react-table/issues" 80 | }, 81 | "homepage": "https://github.com/qiuweikangdev/taro-react-table#readme" 82 | } 83 | -------------------------------------------------------------------------------- /packages/table/rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import RollupTypescript from '@rollup/plugin-typescript' 3 | import RollupBabel from '@rollup/plugin-babel' 4 | import { nodeResolve } from '@rollup/plugin-node-resolve' 5 | import RollupCommonjs from '@rollup/plugin-commonjs' 6 | import RollupAlias from '@rollup/plugin-alias' 7 | import RollupPostcss from 'rollup-plugin-postcss' 8 | import pkg from './package.json' 9 | 10 | const resolveFile = (source) => path.resolve(__dirname, source) 11 | 12 | const externalPackages = [ 13 | 'react', 14 | 'react-dom', 15 | '@tarojs/components', 16 | '@tarojs/runtime', 17 | '@tarojs/taro', 18 | '@tarojs/react', 19 | ] 20 | export default { 21 | input: resolveFile('src/index.ts'), 22 | output: [ 23 | { 24 | file: resolveFile(pkg.publishConfig.main), 25 | format: 'cjs', 26 | sourcemap: true, 27 | exports: 'auto', 28 | }, 29 | { 30 | file: resolveFile(pkg.publishConfig.module), 31 | format: 'es', 32 | sourcemap: true, 33 | exports: 'auto', 34 | }, 35 | ], 36 | external: externalPackages, 37 | plugins: [ 38 | RollupAlias({ 39 | entries: [{ find: '@', replacement: resolveFile('src') }], 40 | }), 41 | RollupPostcss({ 42 | extract: resolveFile(pkg.css), 43 | minimize: true, 44 | }), 45 | nodeResolve(), 46 | RollupCommonjs(), 47 | RollupBabel({ 48 | exclude: 'node_modules/**', 49 | }), 50 | RollupTypescript(), 51 | ], 52 | } 53 | -------------------------------------------------------------------------------- /packages/table/src/components/LoadMore/index.less: -------------------------------------------------------------------------------- 1 | .load-more { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | margin: 10px 0px; 6 | 7 | .no-more-text { 8 | font-size: 32px; 9 | color: #969799; 10 | } 11 | &-sticky { 12 | position: sticky; 13 | z-index: 92; 14 | top: 0; 15 | left: 50%; 16 | transform: translateX(-50%); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/table/src/components/LoadMore/index.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { View, Text } from '@tarojs/components' 3 | import classNames from 'classnames' 4 | import Loading from '../Loading' 5 | import './index.less' 6 | 7 | export type LoadMoreProps = { 8 | status?: 'loading' | 'noMore' | null 9 | loadingText?: string 10 | noMoreText?: string 11 | size: number 12 | } 13 | 14 | function LoadMore({ status, loadingText, noMoreText, size }: LoadMoreProps) { 15 | return ( 16 | 17 | {status === 'loading' && } 18 | {status == 'noMore' && size > 0 && ( 19 | {noMoreText || '没有更多了'} 20 | )} 21 | 22 | ) 23 | } 24 | 25 | export default memo(LoadMore) 26 | -------------------------------------------------------------------------------- /packages/table/src/components/Loading/index.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/index.less'; 2 | 3 | .loading-wrapper { 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | 8 | .loading { 9 | position: relative; 10 | width: 35px; 11 | height: 35px; 12 | border: 2px solid @theme-color; 13 | border-top-color: rgba(0, 0, 0, 0.2); 14 | border-right-color: rgba(0, 0, 0, 0.2); 15 | border-bottom-color: rgba(0, 0, 0, 0.2); 16 | border-radius: 100%; 17 | margin: 0px 20px; 18 | animation: circle infinite 0.75s linear; 19 | } 20 | 21 | .loading-text { 22 | font-size: 32px; 23 | color: #969799; 24 | } 25 | } 26 | 27 | 28 | @keyframes circle { 29 | 0% { 30 | transform: rotate(0); 31 | } 32 | 33 | 100% { 34 | transform: rotate(360deg); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/table/src/components/Loading/index.tsx: -------------------------------------------------------------------------------- 1 | import { View, Text } from '@tarojs/components' 2 | import './index.less' 3 | 4 | export type LoadingProps = { 5 | text?: string | null | undefined 6 | } 7 | 8 | function Loading({ text = '加载中...' }: LoadingProps) { 9 | return ( 10 | 11 | 12 | {text} 13 | 14 | ) 15 | } 16 | 17 | export default Loading 18 | -------------------------------------------------------------------------------- /packages/table/src/components/Table/Empty.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { View, Text } from '@tarojs/components' 3 | import classNames from 'classnames' 4 | import { EmptyProps } from './types' 5 | import './index.less' 6 | 7 | function Empty({ text = '暂无数据', fixedEmpty = true, renderEmpty }: EmptyProps) { 8 | return ( 9 | 10 | {renderEmpty || {text}} 11 | 12 | ) 13 | } 14 | 15 | export default memo(Empty) 16 | -------------------------------------------------------------------------------- /packages/table/src/components/Table/Row.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import classNames from 'classnames' 3 | import { View } from '@tarojs/components' 4 | import { calculateFixedDistance, getNumberSize, getSize, isNil, showStriped } from '../../utils' 5 | import { Columns, RowProps } from './types' 6 | import './index.less' 7 | 8 | function Row(props: RowProps) { 9 | const { 10 | dataSourceItem, 11 | rowStyle = {}, 12 | rowClassName = '', 13 | colClassName = '', 14 | colStyle = {}, 15 | columns, 16 | index, 17 | cellEmptyText = '-', 18 | onRow, 19 | striped = false, 20 | size, 21 | colWidth, 22 | } = props 23 | 24 | return ( 25 | 30 | {columns.map((columnItem: Columns, colIndex: number): JSX.Element => { 31 | const text = dataSourceItem[columnItem.dataIndex] 32 | let width: string | number = colWidth 33 | let result 34 | if (columnItem.width) { 35 | width = columnItem.width 36 | } 37 | if (columnItem.render) { 38 | const render = columnItem.render(text, dataSourceItem, index) 39 | result = render 40 | } else { 41 | result = !isNil(text) ? String(text) : cellEmptyText 42 | } 43 | 44 | return ( 45 | onRow?.(dataSourceItem, index)} 67 | > 68 | columnItem?.onCell?.(dataSourceItem, index)} 73 | > 74 | {result} 75 | 76 | 77 | ) 78 | })} 79 | 80 | ) 81 | } 82 | 83 | export default memo(Row) 84 | -------------------------------------------------------------------------------- /packages/table/src/components/Table/Title.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useRef, useMemo } from 'react' 2 | import classNames from 'classnames' 3 | import { View, Text } from '@tarojs/components' 4 | import { calculateFixedDistance, getNumberSize, getSize } from '../../utils' 5 | import { Columns, SortOrder, TitleProps } from './types' 6 | import { useUniqueId } from '../../hooks' 7 | import { TaroElement } from '@tarojs/runtime' 8 | import './index.less' 9 | 10 | function Title(props: TitleProps) { 11 | const { 12 | setColumns, 13 | columns, 14 | column, 15 | index, 16 | titleStyle = {}, 17 | titleClassName = '', 18 | unsort, 19 | setDataSource, 20 | dataSource = [], 21 | onSorter, 22 | size, 23 | colWidth, 24 | } = props 25 | 26 | const genId = useUniqueId() 27 | const titleRef = useRef(null) 28 | 29 | const handleClickTitle = (col: Columns, colIndex: number) => { 30 | const sorter = col.sorter 31 | if (sorter) { 32 | const tempColumns = [...columns] 33 | tempColumns.forEach((item: Columns, index: number): void => { 34 | if (index !== colIndex) { 35 | delete item.sortOrder 36 | } 37 | }) 38 | /** 39 | * 排序有三种状态:点击升序、点击降序、取消排序。一般需求只需要升序和降序,不需要取消排序 40 | * 可通过unsort来设置 是否支持取消排序 41 | */ 42 | let curCol: SortOrder[] = ['ascend', 'descend'] 43 | if (unsort) { 44 | // undefined/false:取消排序,ascend:升序,descend:降序 45 | curCol = ['ascend', 'descend', undefined] 46 | } 47 | const curIndex: number = curCol.indexOf(tempColumns[colIndex].sortOrder) 48 | const next: SortOrder = (tempColumns[colIndex].sortOrder = 49 | curCol[(curIndex + 1) % curCol.length]) 50 | 51 | if (typeof sorter === 'function') { 52 | const tempDataSource = [...dataSource] 53 | const sortDataSource = tempDataSource.sort((a, b) => sorter(a, b)) 54 | setDataSource(sortDataSource) 55 | } 56 | setColumns(tempColumns) 57 | onSorter?.({ column, field: col.dataIndex, order: next }) 58 | } 59 | } 60 | 61 | const titleWidth = useMemo(() => { 62 | const width = column.width || colWidth 63 | return getSize(width) 64 | }, [column.width, index, colWidth]) 65 | 66 | return ( 67 | handleClickTitle(column, index)} 69 | className={classNames(['taro-table-title'], { 70 | ['taro-table-fixed']: column.fixed, 71 | [titleClassName]: true, 72 | })} 73 | style={{ 74 | [column.fixed as string]: 75 | column.fixed && 76 | calculateFixedDistance({ 77 | fixedType: column.fixed, 78 | index, 79 | columns, 80 | colWidth, 81 | }), 82 | ...column.titleStyle, 83 | ...titleStyle, 84 | width: titleWidth, 85 | padding: `${getSize(0)} ${getSize(getNumberSize(size))}`, 86 | }} 87 | key={column.key || column.dataIndex} 88 | > 89 | 94 | {column.title} 95 | {column.sorter && ( 96 | 97 | 104 | 111 | 112 | )} 113 | 114 | 115 | ) 116 | } 117 | 118 | export default memo(Title) 119 | -------------------------------------------------------------------------------- /packages/table/src/components/Table/index.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/index.less'; 2 | 3 | .taro-table-wrapper { 4 | padding: 10px; 5 | position: relative; 6 | 7 | .taro-table-scroll { 8 | min-height: 100%; 9 | min-width: 100%; 10 | 11 | .taro-table-head { 12 | display: flex; 13 | align-items: center; 14 | justify-content: flex-start; 15 | width: max-content; 16 | min-width: 100%; 17 | height: 100px; 18 | background-color: @head-color; 19 | 20 | .taro-table-title { 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | height: 100px; 25 | font-weight: 600; 26 | color: @title-color; 27 | background: @head-color; 28 | box-sizing: border-box; 29 | &-text { 30 | font-size: 34px; 31 | padding: 0px 10px; 32 | } 33 | &-text-wrapper { 34 | display: flex; 35 | } 36 | &-sort-wrwapper { 37 | display: flex; 38 | flex-direction: column; 39 | align-items: center; 40 | justify-content: center; 41 | padding: 0px 10px; 42 | 43 | .title-ascend { 44 | border-top: 10px solid transparent; 45 | border-left: 10px solid transparent; 46 | border-right: 10px solid transparent; 47 | border-bottom: 10px solid @sorter-color; 48 | margin-bottom: 10px; 49 | 50 | &-active { 51 | border-bottom-color: @theme-color; 52 | } 53 | } 54 | 55 | .title-descend { 56 | border-top: 10px solid @sorter-color; 57 | border-left: 10px solid transparent; 58 | border-right: 10px solid transparent; 59 | border-bottom: 10px solid transparent; 60 | 61 | &-active { 62 | border-top-color: @theme-color; 63 | } 64 | } 65 | } 66 | } 67 | 68 | .taro-table-fixed { 69 | position: sticky; 70 | z-index: 92; 71 | top: 0; 72 | } 73 | 74 | &-scroll { 75 | position: sticky; 76 | top: -1px; 77 | z-index: 93; 78 | } 79 | } 80 | 81 | .taro-table-body { 82 | min-height: 100%; 83 | 84 | .taro-table-row { 85 | display: flex; 86 | align-items: stretch; 87 | justify-content: flex-start; 88 | height: max-content; 89 | width: max-content; 90 | min-width: 100%; 91 | position: relative; 92 | margin:20px 0px; 93 | 94 | .taro-table-col { 95 | display: flex; 96 | align-items: center; 97 | justify-content: center; 98 | font-size: 34px; 99 | padding: 10px 0px; 100 | background-color: #fff; 101 | box-sizing: border-box; 102 | &-striped { 103 | background-color: @striped-color; 104 | } 105 | 106 | &-text { 107 | &-ellipsis { 108 | white-space: nowrap; 109 | text-overflow: ellipsis; 110 | overflow: hidden; 111 | } 112 | } 113 | } 114 | 115 | .taro-table-col-fixed { 116 | position: sticky; 117 | z-index: 92; 118 | top: 0; 119 | } 120 | } 121 | 122 | .taro-table-empty { 123 | text-align: center; 124 | margin: 20px 0px; 125 | font-size: 32px; 126 | color: @empty-color; 127 | display: inline; 128 | &-sticky { 129 | position: sticky; 130 | z-index: 92; 131 | top: 0; 132 | left: 50%; 133 | transform: translateX(-50%); 134 | } 135 | } 136 | } 137 | } 138 | 139 | .taro-table-content-wrapper { 140 | .taro-table-content { 141 | &.taro-table-content-scroll-x { 142 | overflow-x: auto; 143 | } 144 | } 145 | } 146 | 147 | .taro-table-load-wrapper { 148 | height: 65px; 149 | } 150 | .taro-table-empty-wrapper, 151 | .taro-table-load-wrapper { 152 | width: 100%; 153 | display: flex; 154 | } 155 | 156 | .taro-table-loading { 157 | position: absolute; 158 | top: 0px; 159 | bottom: 0px; 160 | text-align: center; 161 | width: 100%; 162 | display: flex; 163 | justify-content: center; 164 | align-items: center; 165 | background-color: #fff; 166 | opacity: 0.8; 167 | z-index: 94; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /packages/table/src/components/Table/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | memo, 3 | useRef, 4 | forwardRef, 5 | useImperativeHandle, 6 | ForwardRefRenderFunction, 7 | useCallback, 8 | useEffect, 9 | } from 'react' 10 | import classNames from 'classnames' 11 | import { nextTick } from '@tarojs/taro' 12 | import { BaseEventOrig, ScrollView, ScrollViewProps, View } from '@tarojs/components' 13 | import Row from './Row' 14 | import Title from './Title' 15 | import Empty from './Empty' 16 | import Loading from '../Loading' 17 | import LoadMore from '../LoadMore' 18 | import { useQuery, useUpdateState, useUniqueId, useRendered } from '../../hooks' 19 | import { ScrollDetail, LoadStatus, DataSource, TableProps, Columns, TableHandle } from './types' 20 | import { getSize } from '../../utils' 21 | import './index.less' 22 | 23 | const Table: ForwardRefRenderFunction> = ( 24 | { 25 | columns: pColumns = [], 26 | dataSource: pDataSource = [], 27 | rowKey = '', 28 | style = {}, 29 | scrollY = true, 30 | scrollX = true, 31 | rowStyle = {}, 32 | rowClassName = '', 33 | colClassName = '', 34 | colStyle = {}, 35 | titleStyle = {}, 36 | titleClassName = '', 37 | className = '', 38 | wrapperClass = {}, 39 | wrapperStyle = {}, 40 | loadStatus: pLoadStatus = null, 41 | loading = false, 42 | onLoad, 43 | onSorter, 44 | unsort = false, // 设置取消排序 【一般需求不需要取消排序,设置true可开启取消排序】 45 | showHeader = true, 46 | loadingText = '', 47 | noMoreText, 48 | loadLoadingText, 49 | onRow, 50 | distance = 30, 51 | showLoad = true, 52 | fixedLoad = true, // 是否固定加载更多,不随横向滚动而滚动 53 | emptyText, 54 | cellEmptyText, 55 | renderEmpty, 56 | striped = false, 57 | size = 'middle', 58 | colWidth = 120, 59 | ...props 60 | }, 61 | ref, 62 | ) => { 63 | const scrollRef = useRef(null) 64 | const loadWrapperRef = useRef(null) 65 | const headRef = useRef(null) 66 | const emptyWrapperRef = useRef(null) 67 | const scrollDetailRef = useRef({ 68 | scrollLeft: 0, 69 | scrollHeight: 0, 70 | scrollTop: 0, 71 | }) 72 | const [dataSource, setDataSource] = useUpdateState(pDataSource) 73 | const [columns, setColumns] = useUpdateState(pColumns) 74 | const [loadStatus] = useUpdateState(pLoadStatus) 75 | const scrollDistanceRef = useRef(0) 76 | const [, { getRefSize }] = useQuery() 77 | const genId = useUniqueId() 78 | 79 | // scroll load 80 | const onScrollToLower = async (e) => { 81 | if (scrollRef?.current) { 82 | const { height: tableHeight } = await getRefSize(scrollRef?.current) 83 | const { scrollHeight = 0, scrollTop = 0 } = scrollDetailRef.current 84 | props?.onScrollToLower?.(e) 85 | if (!showLoad) return 86 | if (loadStatus === 'noMore') return // 无更多数据 87 | if (Math.round(scrollHeight) === Math.round(tableHeight)) return // 无数据 88 | if (scrollTop === 0) return 89 | const diff = scrollHeight - (scrollTop + tableHeight) 90 | // 小于视窗距离多少开始触发onLoad, 默认30 91 | if (diff < distance && loadStatus != 'loading') { 92 | setTimeout(() => { 93 | onLoad?.(e) 94 | }, 300) 95 | } 96 | } 97 | } 98 | 99 | const onScroll = async (e: BaseEventOrig) => { 100 | if (scrollRef?.current) { 101 | const { scrollTop, scrollHeight, scrollLeft } = e.detail 102 | const { height: tableHeight } = await getRefSize(scrollRef.current) 103 | const diff = scrollHeight - (Math.round(scrollTop) + tableHeight) 104 | scrollDistanceRef.current = diff 105 | scrollDetailRef.current = { scrollTop, scrollHeight, scrollLeft } 106 | props?.onScroll?.(e) 107 | } 108 | } 109 | 110 | // set fixed width 111 | const setFixedWidth = ({ width, fixedDom }) => { 112 | fixedDom.style.width = getSize(width) 113 | fixedDom.style.maxWidth = getSize(width) 114 | } 115 | 116 | // scroll fixed 117 | const onScrollFixed = useCallback(async () => { 118 | if (headRef.current) { 119 | const { width: headWidth } = await getRefSize(headRef.current) 120 | if (headWidth) { 121 | if (loadWrapperRef.current && fixedLoad) { 122 | setFixedWidth({ width: headWidth, fixedDom: loadWrapperRef.current }) 123 | } 124 | if (emptyWrapperRef.current && !dataSource.length) { 125 | setFixedWidth({ width: headWidth, fixedDom: emptyWrapperRef.current }) 126 | } 127 | } 128 | } 129 | }, [dataSource.length, fixedLoad, getRefSize]) 130 | 131 | const renderTableEmpty = useRendered(() => { 132 | return ( 133 | 134 | 135 | 136 | ) 137 | }) 138 | 139 | const renderTableHead = useRendered(() => { 140 | return ( 141 | showHeader && 142 | columns.length > 0 && ( 143 | 150 | {columns.length === 0 151 | ? renderTableEmpty 152 | : columns.map((item: Columns, index: number): JSX.Element => { 153 | return ( 154 | 169 | ) 170 | })} 171 | </View> 172 | ) 173 | ) 174 | }) 175 | 176 | const renderTableBody = useRendered(() => { 177 | return ( 178 | <View className='taro-table-body'> 179 | {dataSource.length > 0 && columns.length > 0 180 | ? dataSource.map((item: DataSource, index: number): JSX.Element => { 181 | let key 182 | if (typeof rowKey === 'function') { 183 | key = rowKey(item) 184 | } else { 185 | key = item[rowKey] 186 | } 187 | if (!key) { 188 | key = `row-item-${index}` 189 | } 190 | return ( 191 | <Row 192 | rowClassName={rowClassName} 193 | rowStyle={rowStyle} 194 | colClassName={colClassName} 195 | colStyle={colStyle} 196 | columns={columns} 197 | key={key} 198 | dataSourceItem={item} 199 | index={index} 200 | // widthMap={titleWidthMap} 201 | onRow={onRow} 202 | cellEmptyText={cellEmptyText} 203 | striped={striped} 204 | size={size} 205 | colWidth={colWidth} 206 | /> 207 | ) 208 | }) 209 | : loadStatus != 'loading' && renderTableEmpty} 210 | </View> 211 | ) 212 | }) 213 | 214 | const renderTableLoad = useRendered(() => { 215 | const isShowLoad = showLoad && (dataSource.length || loadStatus) 216 | return ( 217 | isShowLoad && ( 218 | <View 219 | ref={loadWrapperRef} 220 | className={classNames('taro-table-load-wrapper', { 221 | ['taro-table-load-wrapper-center']: !fixedLoad, 222 | })} 223 | > 224 | <LoadMore 225 | status={loadStatus} 226 | size={dataSource.length} 227 | noMoreText={noMoreText} 228 | loadingText={loadLoadingText} 229 | /> 230 | </View> 231 | ) 232 | ) 233 | }) 234 | 235 | useEffect(() => { 236 | if (columns.length || !dataSource.length) { 237 | nextTick(() => { 238 | onScrollFixed() 239 | }) 240 | } 241 | }, [columns, dataSource, onScrollFixed]) 242 | 243 | useImperativeHandle(ref, () => ({ scrollRef, scrollDistance: scrollDistanceRef.current })) 244 | 245 | return ( 246 | <View className={classNames(['taro-table-wrapper', wrapperClass])} style={wrapperStyle}> 247 | {loading && ( 248 | <View className='taro-table-loading'> 249 | <Loading text={loadingText} /> 250 | </View> 251 | )} 252 | <ScrollView 253 | {...props} 254 | ref={scrollRef} 255 | className={classNames(['taro-table-scroll', className])} 256 | scrollX={scrollX} 257 | scrollY={scrollY} 258 | style={{ ...style, overflow: 'auto' }} 259 | onScrollToLower={onScrollToLower} 260 | onScroll={onScroll} 261 | id={genId('taro-table-scroll')} 262 | > 263 | <View className='taro-table-content-wrapper'> 264 | {renderTableHead} 265 | {renderTableBody} 266 | {renderTableLoad} 267 | </View> 268 | </ScrollView> 269 | </View> 270 | ) 271 | } 272 | 273 | export default memo(forwardRef(Table)) 274 | -------------------------------------------------------------------------------- /packages/table/src/components/Table/types.ts: -------------------------------------------------------------------------------- 1 | import { CommonEventFunction, ScrollViewProps } from '@tarojs/components' 2 | import { CSSProperties, ReactNode, RefObject } from 'react' 3 | 4 | export type ScrollDetail = { 5 | scrollLeft: number 6 | scrollTop: number 7 | scrollHeight: number 8 | } 9 | 10 | export type Fixed = 'left' | 'right' 11 | 12 | export type LoadStatus = 'loading' | 'noMore' | null 13 | 14 | export type DataSource<T = unknown> = { 15 | [prop: string]: T 16 | } 17 | 18 | export type SortOrder = 'descend' | 'ascend' | undefined | false 19 | 20 | export type CompareFn<T> = (a: T, b: T) => number 21 | 22 | export type SorterEvent = { 23 | column: Columns 24 | order: SortOrder 25 | field: string 26 | } 27 | 28 | export type Columns<T = unknown> = { 29 | title: string | JSX.Element 30 | dataIndex: string 31 | key?: string 32 | align?: 'left' | 'right' | 'center' 33 | style?: CSSProperties 34 | titleStyle?: CSSProperties 35 | className?: string 36 | titleClassName?: string 37 | render?: (text?: any, record?: T, index?: number) => any 38 | width?: number 39 | fixed?: Fixed 40 | sorter?: boolean | CompareFn<T> 41 | sortOrder?: SortOrder 42 | onCell?: (record: T, rowIndex: number) => void 43 | ellipsis?: boolean 44 | } 45 | 46 | export type TableHandle = { 47 | scrollRef: RefObject<HTMLElement> 48 | scrollDistance: number 49 | } 50 | 51 | export type StripedType = 'odd' | 'even' | boolean 52 | 53 | export type TableProps<T = unknown> = Omit<ScrollViewProps, 'style'> & { 54 | dataSource: T[] 55 | columns: Columns<T>[] 56 | rowKey?: string | ((item: T) => React.Key) 57 | wrapperClass?: string 58 | wrapperStyle?: CSSProperties 59 | className?: string 60 | colStyle?: CSSProperties 61 | colClassName?: string 62 | rowStyle?: CSSProperties 63 | rowClassName?: string 64 | titleStyle?: CSSProperties 65 | titleClassName?: string 66 | style?: CSSProperties 67 | loading?: boolean 68 | loadingText?: string 69 | loadStatus?: LoadStatus 70 | onLoad?: CommonEventFunction 71 | onSorter?: ({ column, field, order }: SorterEvent) => void 72 | unsort?: boolean 73 | showHeader?: boolean 74 | noMoreText?: string 75 | loadLoadingText?: string 76 | onRow?: (record: T, index: number) => void 77 | distance?: number 78 | showLoad?: boolean 79 | fixedLoad?: boolean 80 | emptyText?: string 81 | cellEmptyText?: string 82 | renderEmpty?: ReactNode 83 | striped?: StripedType 84 | size?: SpaceSize 85 | colWidth?: number 86 | } 87 | 88 | export type RowProps<T = unknown> = { 89 | index: number 90 | dataSourceItem: DataSource<T> 91 | columns: Columns<T>[] 92 | rowStyle?: CSSProperties 93 | rowClassName?: string 94 | colStyle?: CSSProperties 95 | colClassName?: string 96 | bordered?: boolean 97 | borderBottom?: boolean 98 | onRow?: (record: T, index: number) => void 99 | cellEmptyText?: string 100 | widthMap?: Record<number, number> 101 | striped?: StripedType 102 | size?: SpaceSize 103 | colWidth: number 104 | } 105 | 106 | export type TitleProps<T = unknown> = { 107 | columns: Columns[] 108 | column: Columns 109 | setColumns?: any 110 | setDataSource?: any 111 | dataSource?: T[] 112 | index: number 113 | fixed?: boolean 114 | titleStyle?: CSSProperties 115 | titleClassName?: string 116 | onSorter?: ({ column, field, order }: SorterEvent) => void 117 | unsort?: boolean 118 | colWidth: number 119 | size?: SpaceSize 120 | } 121 | 122 | export type EmptyProps = { 123 | text?: string 124 | fixedEmpty?: boolean 125 | renderEmpty?: ReactNode 126 | } 127 | 128 | export type SizeType = 'small' | 'middle' | 'large' | undefined 129 | 130 | export type SpaceSize = SizeType | number 131 | 132 | export const spaceSize = { 133 | small: 8, 134 | middle: 16, 135 | large: 24, 136 | } 137 | -------------------------------------------------------------------------------- /packages/table/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | import useUpdateState from './useUpdateState' 2 | import useQuery from './useQuery' 3 | import useUniqueId from './useUniqueId' 4 | import useRendered from './useRendered' 5 | 6 | export { useUpdateState, useQuery, useUniqueId, useRendered } 7 | -------------------------------------------------------------------------------- /packages/table/src/hooks/useQuery.ts: -------------------------------------------------------------------------------- 1 | import { createSelectorQuery, NodesRef, SelectorQuery } from '@tarojs/taro' 2 | import { TaroElement } from '@tarojs/runtime' 3 | import { useCallback, useRef } from 'react' 4 | 5 | export type SelectorMethod = { 6 | getRefSize: getRefSizeType 7 | } 8 | 9 | export type getRefSizeType = ( 10 | element: TaroElement | HTMLElement, 11 | ) => Promise<NodesRef.BoundingClientRectCallbackResult> 12 | 13 | function useQuery(): [SelectorQuery, SelectorMethod] { 14 | const query = useRef<SelectorQuery>(createSelectorQuery()) 15 | 16 | const querySelector = useCallback( 17 | (selector: string): NodesRef => { 18 | return query.current.select(selector) 19 | }, 20 | [query], 21 | ) 22 | 23 | const getRefSize = useCallback<getRefSizeType>( 24 | (element: TaroElement | HTMLElement) => { 25 | return new Promise((resolve, reject) => { 26 | if (!element) { 27 | reject({}) 28 | } else { 29 | try { 30 | const selectorQuery = querySelector('#' + element.id).boundingClientRect((result) => { 31 | resolve(result || {}) 32 | }) 33 | selectorQuery.exec() 34 | } catch (e) { 35 | reject(e) 36 | } 37 | } 38 | }) 39 | }, 40 | [querySelector], 41 | ) 42 | 43 | return [ 44 | query.current, 45 | { 46 | getRefSize, 47 | }, 48 | ] 49 | } 50 | 51 | export default useQuery 52 | -------------------------------------------------------------------------------- /packages/table/src/hooks/useRendered.ts: -------------------------------------------------------------------------------- 1 | type UseRenderedCallback<T> = () => T 2 | 3 | const useRendered = <T = any>(fn: UseRenderedCallback<T>): T => { 4 | return fn?.() 5 | } 6 | export default useRendered 7 | -------------------------------------------------------------------------------- /packages/table/src/hooks/useUniqueId.ts: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | 3 | const prefix = 'taro-react-table' 4 | 5 | const useUniqueId = () => { 6 | const genId = useCallback((id: string | number) => { 7 | return `${prefix}__${Number(Math.random().toString().substring(2) + Date.now()).toString( 8 | 36, 9 | )}_${id}` 10 | }, []) 11 | 12 | return genId 13 | } 14 | 15 | export default useUniqueId 16 | -------------------------------------------------------------------------------- /packages/table/src/hooks/useUpdateState.ts: -------------------------------------------------------------------------------- 1 | import { Dispatch, SetStateAction, useEffect, useState } from 'react' 2 | 3 | export default function useUpdateState<T = unknown>(value): [T, Dispatch<SetStateAction<T>>] { 4 | const [state, setState] = useState<T>(value) 5 | useEffect(() => { 6 | setState(value) 7 | }, [value]) 8 | return [state, setState] 9 | } 10 | -------------------------------------------------------------------------------- /packages/table/src/index.ts: -------------------------------------------------------------------------------- 1 | import Table from './components/Table' 2 | 3 | export type { 4 | TableProps, 5 | DataSource, 6 | Columns, 7 | RowProps, 8 | TitleProps, 9 | Fixed, 10 | LoadStatus, 11 | SorterEvent, 12 | } from './components/Table/types' 13 | 14 | export type { LoadMoreProps } from './components/LoadMore' 15 | 16 | export type { LoadingProps } from './components/Loading' 17 | 18 | export default Table 19 | -------------------------------------------------------------------------------- /packages/table/src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import './theme.less'; 2 | -------------------------------------------------------------------------------- /packages/table/src/styles/theme.less: -------------------------------------------------------------------------------- 1 | @theme-color: #ff2f31; 2 | @sorter-color: #e2e2e2; 3 | @striped-color: #fafafa; 4 | @head-color: #fafafa; 5 | @empty-color: #969799; 6 | @title-color:rgba(0, 0, 0, 0.75); 7 | -------------------------------------------------------------------------------- /packages/table/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import Taro from '@tarojs/taro' 2 | import { Columns, Fixed, spaceSize, SpaceSize, StripedType } from '../components/Table/types' 3 | 4 | export const getSize = (size: string | number): string => { 5 | if (typeof size === 'number') { 6 | return Taro.pxTransform((size as number) * 2) 7 | } else { 8 | return String(size) 9 | } 10 | } 11 | /** 12 | * 固定列的时候计算偏移量 13 | * @param options 14 | */ 15 | export function calculateFixedDistance(options: { 16 | fixedType: Fixed 17 | index: number 18 | columns: Columns[] 19 | colWidth: number 20 | }) { 21 | const { fixedType, index, columns, colWidth = 0 } = options 22 | let result = 0 23 | if (fixedType === 'left') { 24 | // 计算当前列之前的列宽度总和 25 | result = columns.reduce((acc, cur, i) => { 26 | if (i + 1 <= index) { 27 | return acc + (cur.width || colWidth) 28 | } else { 29 | return acc 30 | } 31 | }, 0) 32 | } else { 33 | result = columns.reduceRight((acc, cur, i) => { 34 | // 计算当前列之后的列宽度总和 35 | if (i - 1 >= index) { 36 | return acc + (cur.width || colWidth) 37 | } else { 38 | return acc 39 | } 40 | }, 0) 41 | } 42 | return getSize(result) 43 | } 44 | 45 | export function isNil(value: unknown) { 46 | return value == null 47 | } 48 | 49 | export function pickValid(object: Record<string, unknown>) { 50 | const temp = { ...object } 51 | Object.keys(temp).forEach((item) => { 52 | const key = object[item] 53 | if (key === '' || key === null || key === undefined) { 54 | delete temp[item] 55 | } 56 | }) 57 | return temp 58 | } 59 | 60 | export function getNumberSize(size: SpaceSize) { 61 | return typeof size === 'string' ? spaceSize[size] : size || 0 62 | } 63 | 64 | export function showStriped(striped: StripedType = false, index) { 65 | if ([true, 'even'].includes(striped)) { 66 | return index % 2 !== 0 67 | } 68 | return striped === 'odd' && index % 2 == 0 69 | } 70 | -------------------------------------------------------------------------------- /packages/table/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "declaration": true, 6 | "declarationDir": "dist", 7 | "outDir": "dist", 8 | "removeComments": false, 9 | "preserveConstEnums": true, 10 | "moduleResolution": "node", 11 | "experimentalDecorators": true, 12 | "noImplicitAny": false, 13 | "allowSyntheticDefaultImports": true, 14 | "noUnusedLocals": true, 15 | "skipLibCheck": true, 16 | "noUnusedParameters": false, 17 | "strictNullChecks": true, 18 | "sourceMap": true, 19 | "resolveJsonModule": true, 20 | "baseUrl": "src", 21 | "rootDir": "src", 22 | "jsx": "react-jsx", 23 | "typeRoots": [ 24 | "node_modules/@types", 25 | "global.d.ts" 26 | ], 27 | 28 | }, 29 | "compileOnSave": false, 30 | "exclude": [ 31 | "node_modules/**/*", 32 | "dist/*", 33 | ], 34 | "include": [ 35 | "src/**/*" 36 | ], 37 | } 38 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "packages/*" 3 | -------------------------------------------------------------------------------- /scripts/release.js: -------------------------------------------------------------------------------- 1 | // generate version/tags 2 | 3 | const { execSync } = require('child_process') 4 | const pkg = require('../packages/table/package.json') 5 | const { program } = require('commander') 6 | 7 | program.option('-r, --release <version>', 'package version') 8 | program.parse() 9 | 10 | const { release } = program.opts() 11 | const tag = `${pkg.name}-v${release}` 12 | try { 13 | if (release) { 14 | const value = execSync(`git tag -l ${tag}`).toString('utf8') 15 | if (!value) { 16 | execSync( 17 | `cd packages/table && standard-version -r ${release} -t ${pkg.name}-v --infile ../../CHANGELOG.md`, 18 | ) 19 | execSync(`git push origin ${tag}`) 20 | execSync(`git push origin HEAD`) 21 | } else { 22 | throw `${tag} already exists` 23 | } 24 | } else { 25 | throw 'release does not exist' 26 | } 27 | } catch (e) { 28 | console.log(e) 29 | } 30 | -------------------------------------------------------------------------------- /scripts/update-pkg.js: -------------------------------------------------------------------------------- 1 | // update table package.json 2 | 3 | const pkg = require('../packages/table/package.json') 4 | const { writeFileSync } = require('fs') 5 | const path = require('path') 6 | if (process.env.NODE_ENV === 'prod') { 7 | Object.assign(pkg, { 8 | main: 'dist/index.js', 9 | module: 'dist/index.esm.js', 10 | }) 11 | } else { 12 | Object.assign(pkg, { 13 | main: 'src/index.ts', 14 | module: 'src/index.ts', 15 | }) 16 | } 17 | 18 | writeFileSync( 19 | path.resolve(process.cwd(), 'packages/table/package.json'), 20 | JSON.stringify(pkg, null, 2), 21 | ) 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "declaration": true, 6 | "removeComments": false, 7 | "preserveConstEnums": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "noImplicitAny": false, 11 | "allowSyntheticDefaultImports": true, 12 | "noUnusedLocals": true, 13 | "skipLibCheck": true, 14 | "noUnusedParameters": false, 15 | "strictNullChecks": true, 16 | "sourceMap": true, 17 | "resolveJsonModule": true, 18 | "jsx": "react-jsx", 19 | "typeRoots": [ 20 | "node_modules/@types", 21 | "global.d.ts" 22 | ], 23 | 24 | }, 25 | "compileOnSave": true, 26 | "exclude": [ 27 | "node_modules/**/*", 28 | "dist/*" 29 | ], 30 | "include": [ 31 | "packages/**/*" 32 | ], 33 | } 34 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "pipeline": { 4 | "dev:h5": { 5 | "outputs": ["dist/**"] 6 | }, 7 | "dev:weapp": { 8 | "outputs": ["dist/**"] 9 | }, 10 | "build:table": { 11 | "outputs": ["dist/**"] 12 | }, 13 | "publish:table": { 14 | "dependsOn": ["build:table"] 15 | }, 16 | "publish": { 17 | "dependsOn": ["publish:table"] 18 | } 19 | } 20 | } 21 | --------------------------------------------------------------------------------