kn-hooks 0.0.9 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "kn-hooks",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "scripts": {
5
5
  "dev": "cross-env env_api=dev env_package=dev webpack-dev-server --progress",
6
6
  "build": "cross-env env_api=prod env_package=prod webpack --config webpack.config.js",
7
7
  "build:dev": "cross-env env_api=dev env_package=dev webpack",
8
- "report": "cross-env env_api=prod env_package=prod report=true webpack --config webpack.test.config.js"
8
+ "report": "cross-env env_api=prod env_package=prod report=true webpack --config webpack.test.config.js",
9
+ "doc": "./node_modules/.bin/jsdoc src/**/*.js --configure .jsdoc.json --verbose",
10
+ "md": "./node_modules/.bin/jsdoc2md --plugin ./dmd/lib/index.js --files src/useRollingPagination/*.js src/useSwitch/*.js > md/api.md"
9
11
  },
10
12
  "main": "src/index.js",
11
13
  "dependencies": {
@@ -32,13 +34,19 @@
32
34
  "babel-plugin-import": "^1.13.3",
33
35
  "cross-env": "7.0.3",
34
36
  "css-loader": "3.4.2",
37
+ "dmd-bitbucket": "^0.1.10",
38
+ "dmd-plugin-example": "^0.1.0",
39
+ "docdash": "^2.0.1",
35
40
  "html-webpack-plugin": "4.0.3",
41
+ "jsdoc": "^4.0.2",
42
+ "jsdoc-to-markdown": "^8.0.0",
36
43
  "mini-css-extract-plugin": "0.9.0",
37
44
  "react": "16.13.0",
38
45
  "react-dom": "16.13.0",
39
46
  "rollup": "^2.50.5",
40
47
  "rollup-plugin-jsx": "^1.0.3",
41
48
  "rollup-plugin-terser": "^7.0.2",
49
+ "taffydb": "^2.7.3",
42
50
  "terser-webpack-plugin": "4.2.3",
43
51
  "webpack": "4.46.0",
44
52
  "webpack-bundle-analyzer": "3.6.1",
package/readme.md CHANGED
@@ -2,11 +2,20 @@
2
2
 
3
3
  ## 目录
4
4
 
5
- * useSwitch - 开关控制
6
- * useRefSwitch - 开关控制的ref版本
7
- * useClipboard - 剪贴板工具
8
- * useDictionary - 字典管理及下拉选项框渲染
9
- * 按需打包
5
+ * [使用方法](#start)
6
+ * [按需打包](#package)
7
+
8
+ * API
9
+ * [useDictionary - 字典管理及下拉选项框渲染](md/useDictionary.md)
10
+ * [useSwitch - 开关控制](md/useSwitch.md)
11
+ * [useClipboard - 剪贴板](md/useClipboard.md)
12
+ * [useCounter - 计数器](md/useCounter.md)
13
+ * [usePagination - 分页管理](md/usePagination.md)
14
+ * [usePaginationWithForm - 支持Antd-From的分页管理](md/usePaginationWithForm.md)
15
+
16
+
17
+
18
+ <a id='start'></a>
10
19
 
11
20
  ## 使用方法
12
21
 
@@ -25,200 +34,12 @@ const Index=()=>{
25
34
  ```
26
35
 
27
36
 
28
- ## @useSwitch
29
-
30
- 控制开关
31
-
32
- #### Demo
33
- [![codesandbox](http://img.vuedata.cn/codesandboxicon1.png?imageMogr2/thumbnail/!30x)](https://codesandbox.io/s/useswitch-qd7dr?file=/index.js)
34
-
35
-
36
- #### API
37
-
38
- ```typescript
39
- const result:Result = useSwitch(switch?:boolean);
40
- ```
41
-
42
- #### Params
43
-
44
- 参数|说明|类型|默认值
45
- --|:--:|:--:|--:
46
- ?switch|默认为打开还是关闭状态|boolean|false
47
-
48
- #### Result: Object
49
-
50
- 参数|说明|类型
51
- --|:--:|--:
52
- state|当前开关状态|boolean
53
- count|当前开关计数器|number
54
- toggle|切换开关|()=>void
55
- open|打开开关|(force?: boolean)=>void
56
- close|关闭开关|(force?: boolean)=>void
57
-
58
- > 当使用open和close控制开关时为成对计数模式
59
- > 每次open都会增加`开计数器`,而每次close会减少`开计数器`
60
- > 当`开计数器`为0时才会真正关闭开关
61
-
62
- ##### open: (force?: boolean)=>void
63
-
64
- 参数|说明|类型|默认值
65
- --|:--:|:--:|--:
66
- ?force|是否强制切换到打开状态|boolean|false
67
-
68
- ##### close: (force?: boolean)=>void
69
-
70
- 参数|说明|类型|默认值
71
- --|:--:|:--:|--:
72
- ?force|是否强制切换到关闭状态|boolean|false
73
-
74
-
75
- ## @useRefSwitch
76
-
77
- useSwitch的ref版本,返回的是一个useRef,使用时通过 current 来访问switch对象
78
-
79
- ```javascript
80
- const loading = useRefSwitch();
81
- return (
82
- <p>{loading.current.state}</p>
83
- )
84
-
85
- ```
86
-
87
- ## @useClipboard
88
-
89
- 将字符串复制到剪贴板
90
-
91
- #### Demo
92
-
93
- [![codesandbox](http://img.vuedata.cn/codesandboxicon1.png?imageMogr2/thumbnail/!30x)](https://codesandbox.io/s/useclipboard-b4ryc?file=/index.js)
94
-
95
-
96
- #### API
97
-
98
- ```typescript
99
- const result:Result = useClipboard(onSuccess?: ()=>void);
100
- ```
101
-
102
- #### Params
103
-
104
- 参数|说明|类型|默认值
105
- --|:--:|:--:|--:
106
- onSuccess|默认的成功回调函数|()=>void|无
107
-
108
- #### Result: (text,onSuccess)=>void
109
-
110
- 参数|说明|类型
111
- --|:--:|--:
112
- text|需要复制的字符串|string|无
113
- ?onSuccess|成功回调|()=>void|无
114
-
115
-
116
-
117
- ## @useDictionary
118
-
119
- 字典管理
120
-
121
- #### DEMO
122
-
123
- [![codesandbox](http://img.vuedata.cn/codesandboxicon1.png?imageMogr2/thumbnail/!30x)](https://codesandbox.io/s/usedictionary-j7cw8?file=/index.js)
124
-
125
-
126
- #### API
127
-
128
- ```typescript
129
- const result:Result = useDictionary({
130
- api:()=>Promise,
131
- });
132
- ```
133
-
134
- #### Params
135
-
136
- 参数|说明|类型|默认值
137
- --|:--:|:--:|--:
138
- api|获取字典数据的接口函数|()=>Promise|无
139
- ?beforeApi|调用接口前的钩子,用于处理传参|(params)=>any|无
140
- ?afterApi|调用接口后的钩子,用于处理返回结果|(params)=>TypeItem[]|无
141
- ?SelectOption|指定<Select.Option>对象|any|无
142
- ?RadioOption|指定<Radio.Button>对象|any|无
143
- ?idKey|字典项目中id的键值名|string|id
144
- ?nameKey|字典项目中name的键值名|string|name
145
- ?labelKey|字典项目中label的键值名|string|label
146
-
147
- #### TypeItem-字典项数据结构
148
-
149
- 参数|说明|类型|默认值
150
- --|:--:|:--:|--:
151
- id|数据唯一id|string|无
152
- name|数据唯一id对应的别名|string|无
153
- label|展示给用户看的文字|string|无
154
-
155
- #### Result: Object
156
-
157
- 参数|说明|类型
158
- --|:--:|--:
159
- types|字典项的值|TypeItem[]
160
- selectOptions|供渲染<Select>的列表,Antd下用|any
161
- radioOptions|供渲染<Radio>的列表,Antd下用|any
162
- getId|搜索字典项中,label或name匹配labelOrName的项目,返回其id的值|(labelOrName:string)=>string
163
- getName|搜索字典项中,id匹配id的项目,返回其name的值|(id:string)=>string
164
- getLabel|搜索字典项中,id匹配id的项目,返回其label的值|(id:string)=>string
165
- reload|重新调用字典接口刷新字典列表|()=>void
166
-
167
- #### useDictionary.SET({SelectOption,RadioOption})=>void
168
- 全局指定渲染selectbox和radio的组件
169
-
170
- 参数|说明|类型
171
- --|:--:|--:
172
- SelectOption|对应Antd里 Select.Option|antd组件
173
- RadioOption|对应Antd里 Radio.Button|antd组件
174
-
175
- #### Example
176
-
177
- ```typescript
178
- import {Select,Radio} from 'antd';
179
- import useDictionary from '@/useDictionary';
180
- useDictionary.SET({SelectOption:Select.Option,RadioOption:Radio.Button});
181
-
182
- const Index=()=>{
183
-
184
- const emCity = useDictionary({
185
- api:()=>{
186
- return new Promise(resolve=>{
187
- resolve({
188
- code:0,
189
- data:{
190
- body:[
191
- {id:1,name:'shanghai',label:'上海'},
192
- {id:2,name:'beijing',label:'北京'},
193
- {id:3,name:'guangzhou',label:'广州'}
194
- ]
195
- }
196
- })
197
- })
198
- }
199
- });
200
-
201
- return (
202
- <section >
203
- <Select defaultValue={1}>
204
- {
205
- emCity.selectOptions
206
- }
207
- </Select>
208
- <p>分割线</p>
209
- <Radio.Group defaultValue={1}>
210
- {
211
- emCity.radioOptions
212
- }
213
- </Radio.Group>
214
- </section>
215
- )
216
- }
217
- ```
218
-
37
+ <a id='package'></a>
219
38
 
220
39
  ## 按需打包
221
40
 
41
+ > webpack4及以上版本无需下面配置
42
+
222
43
  webpack增加`bable-plugin-import`插件,给babel plugins增加配置
223
44
  ```javascript
224
45
  // rules: [
package/src/index.js CHANGED
@@ -1,13 +1,18 @@
1
1
  import useClipboard from './useClipboard/index.js';
2
2
  import useSwitch from './useSwitch/index.js';
3
- import useRefSwitch from './useRefSwitch/index.js';
4
3
  import useDictionary from './useDictionary/index.js';
4
+ import useCounter from './useCounter/index.js';
5
+ import usePagination from './usePagination/index.js';
6
+ import usePaginationWithForm from './usePaginationWithForm/index.js';
7
+
5
8
 
6
9
  export {
7
10
  useClipboard,
8
11
  useSwitch,
9
- useRefSwitch,
10
12
  useDictionary,
13
+ useCounter,
14
+ usePagination,
15
+ usePaginationWithForm
11
16
  }
12
17
 
13
18
 
@@ -1,17 +1,24 @@
1
+ /**
2
+ * @module useClipboard
3
+ */
1
4
  import { useState, useMemo, useEffect, useRef } from 'react';
2
5
  import ClipboardJS from 'clipboard';
3
6
 
7
+
4
8
  /**
5
9
  * 剪贴板工具
6
- * props:{
7
- * onSuccess?: Function;//复制成功后的通用回调
8
- * }
9
10
  *
10
- * return clip:(
11
- * text: string, //待复制的文案
12
- * onSuccess?: Function //复制成功后的回调
13
- * )=>void
11
+ * [Demo - CodeSandBox]{@link https://codesandbox.io/s/useclipboard-b4ryc?file=/index.js}
12
+ *
13
+ * @param {Object} [props] -
14
+ * @param {callback} [props.onSuccess] - 成功复制到剪贴板后的回调 ()=>void
15
+ * @return {UseClipboardResult}
14
16
  *
17
+ * @example
18
+ const clipboard= useClipboard();
19
+ const onCopy=()=>{
20
+ clipboard.copy('text',()=>{console.log('success')})
21
+ }
15
22
  */
16
23
  const useClipboard = (props) => {
17
24
  const { onSuccess } = props;
@@ -77,4 +84,20 @@ const useClipboard = (props) => {
77
84
  return actions;
78
85
  };
79
86
 
87
+
88
+ /**
89
+ * 复制函数
90
+ * @typedef {callback} FunCopy
91
+ * @property {string} text - 需要被复制的字符串
92
+ * @property {callback} onCurSuccess - 复制成功的回调 ()=>void
93
+ */
94
+
95
+
96
+ /**
97
+ * UseClipboardResult返回的hook
98
+ * @typedef {Object} UseClipboardResult
99
+ * @property {FunCopy} copy - 触发复制字符串的函数
100
+ *
101
+ */
102
+
80
103
  export default useClipboard;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @module useCounter
3
+ */
4
+ import { useState, useMemo } from 'react';
5
+
6
+
7
+ /**
8
+ * 加法计数器
9
+ * @return {UseCounterResult}
10
+ * @example
11
+ const counter= useCounter();
12
+ const onAdd=()=>{counter.addCount()}
13
+ return <span>当前计数器:{counter.count}</span>
14
+ */
15
+ const useCounter = () => {
16
+ const [count,setCount] = useState(1);
17
+ const action = useMemo(()=>{
18
+ return {
19
+ count,
20
+ addCount:()=>{setCount(v=>v+1)}
21
+ }
22
+ },[count,setCount]);
23
+ return action;
24
+ };
25
+
26
+ /**
27
+ * @typedef UseCounterResult
28
+ * @property {Object} req
29
+ * @property {number} req.count - 当前计数器的值
30
+ * @property {function} req.addCount - 计数器的值+1。 ()=>void
31
+ *
32
+ */
33
+
34
+ export default useCounter;
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @module useDictionary
3
+ */
1
4
  import React,{ useState, useMemo, useEffect } from 'react';
2
5
 
3
6
  const DEFAULT_CONFIG={
@@ -7,37 +10,62 @@ const DEFAULT_CONFIG={
7
10
 
8
11
  /**
9
12
  * 字典管理
10
- * props:{
11
- * api:async ()=>any, 调用字段查询的接口函数,默认返回的数据格式为{ code:0 ,data:{body:[{label:'',id:'',name:''},]}}
12
- * beforeApi?: (params:any)=>any , 调用接口前的钩子,用于处理接口的参数
13
- * afterApi?: (response:any)=>any[], 接口调用后的钩子,用于处理接口返回的值
14
- * SelectOption?: any, 指定<Select.Option>对象是谁
15
- * RadioOption?: any, 指定<Radio.Button>对象是谁
16
- * idKey?: string,//字段id的key键值
17
- * nameKey?: string,//字段name的key键值
18
- * labelKey?: string,//字段label的key键值
19
- * }
20
- * 字典数据的结构为
21
- * {
22
- * id:'',数据唯一id
23
- * name:'',数据唯一id对应的别名
24
- * label:'',展示给用户看的文字
25
- * }
26
13
  *
14
+ * [Demo - CodeSandBox]{@link https://codesandbox.io/s/usedictionary-j7cw8?file=/index.js}
27
15
  *
28
- * return {
29
- * types:any[],具体字典列表
30
- * selectOptions:any[],供渲染<Select>的列表
31
- * radioOptions:any[],供渲染<Radio>的列表
32
- * getId:(labelOrName:string)=>string,搜索字典项中,label或name匹配labelOrName的项目,返回其id的值
33
- * getName:(id:string)=>string,搜索字典项中,id匹配id的项目,返回其name的值
34
- * getLabel:(id:string)=>string,搜索字典项中,id匹配id的项目,返回其label的值
35
- * reload:()=>void,重新调用字典接口刷新字典列表
36
- * }
16
+ * @param {Object} props
17
+ * @param {Api} props.api - 用于获取字典列表的接口
18
+ * @param {string} [props.idKey='id'] - 字段id的key键值
19
+ * @param {string} [props.nameKey='name'] - 字段name的key键值
20
+ * @param {string} [props.labelKey='label'] - 字段label的key键值
21
+ * @param {ReactDom} [props.SelectOption] - 指定\<Select.Option\>对象是谁
22
+ * @param {ReactDom} [props.RadioOption] - 指定\<Radio.Button\>对象是谁
23
+ * @param {callback} [props.beforeApi] - (request:object)=>object 接口调用前的参数拦截器
24
+ * @param {callback} [props.afterApi] - (reponse:object)=>object[] 接口调用后的拦截器
37
25
  *
38
- * useDictionary.SET:({SelectOption,RadioOption})=>void,全局设置SelectOption和RadioOption
39
26
  *
27
+ * @return {UseDictionaryResult}
40
28
  *
29
+ * @example
30
+ import {Select,Radio} from 'antd';
31
+ import useDictionary from '@/useDictionary';
32
+ useDictionary.SET({SelectOption:Select.Option,RadioOption:Radio.Button});
33
+
34
+ const Index=()=>{
35
+
36
+ const emCity = useDictionary({
37
+ api:()=>{
38
+ return new Promise(resolve=>{
39
+ resolve({
40
+ code:0,
41
+ data:{
42
+ body:[
43
+ {id:1,name:'shanghai',label:'上海'},
44
+ {id:2,name:'beijing',label:'北京'},
45
+ {id:3,name:'guangzhou',label:'广州'}
46
+ ]
47
+ }
48
+ })
49
+ })
50
+ }
51
+ });
52
+
53
+ return (
54
+ <section >
55
+ <Select defaultValue={1}>
56
+ {
57
+ emCity.selectOptions
58
+ }
59
+ </Select>
60
+ <p>分割线</p>
61
+ <Radio.Group defaultValue={1}>
62
+ {
63
+ emCity.radioOptions
64
+ }
65
+ </Radio.Group>
66
+ </section>
67
+ )
68
+ }
41
69
  */
42
70
  const useDictionary=(props)=>{
43
71
 
@@ -153,4 +181,46 @@ export const SetConfig = ({SelectOption,RadioOption})=>{
153
181
  if(RadioOption)DEFAULT_CONFIG.RadioOption = RadioOption;
154
182
  };
155
183
 
184
+
185
+
186
+ /**
187
+ * @typedef Api
188
+ * @property {Object} params - 调用接口用到的参数
189
+ * @returns {Object}
190
+ */
191
+
192
+
193
+
194
+ /**
195
+ * @typedef DictionaryItem
196
+ * 字典数据的结构
197
+ * @property {string} id - 数据唯一ID
198
+ * @property {string} name - 数据唯一id对应的别名
199
+ * @property {string} label - 展示给用户看的文字
200
+ *
201
+ */
202
+
203
+ /**
204
+ * @typedef UseDictionaryResult
205
+ * @property {DictionaryItem[]} types - 字典数据列表
206
+ * @property {ReactDOM[]} selectOptions - 供Antd渲染\<Select\>的列表
207
+ * @property {ReactDOM[]} radioOptions - 供Antd渲染\<Radio\>的列表
208
+ * @property {function} getId - (labelOrName:string)=>string,搜索字典项中,label或name匹配labelOrName的项目,返回其id的值
209
+ * @property {function} getName - (id:string)=>string,搜索字典项中,id匹配id的项目,返回其name的值
210
+ * @property {function} getLabel - (id:string)=>string,搜索字典项中,id匹配id的项目,返回其label的值
211
+ * @property {function} reload - ()=>void,重新调用字典接口刷新字典列表
212
+ *
213
+ */
214
+
215
+ /**
216
+ * @function SET
217
+ * @description 全局设置SelectOption和RadioOption
218
+ *
219
+ * @param {Object} params
220
+ * @param {ReactDom} params.SelectOption - Antd的SelectOption组件
221
+ * @param {ReactDom} params.RadioOption - Antd的SelectOption组件
222
+ * @returns {void}
223
+ */
224
+
225
+
156
226
  export default useDictionary
@@ -0,0 +1,264 @@
1
+
2
+ /**
3
+ * @module usePagination
4
+ */
5
+ import { useState,useMemo,useRef } from 'react';
6
+
7
+ /**
8
+ * 分页管理器
9
+ * @param {Object} props
10
+ * @param {function} props.service - 发送请求的方法,默认分页使用current和pageSize,如有特殊需求通过beforeService拦截处理
11
+ * @param {Pagination} [props.pagination] - 默认分页信息
12
+ * @version 1.0.0
13
+ *
14
+ * @example
15
+ // 移动端滚动加载案例
16
+ const page = usePagination({
17
+ service:GET_LIST,
18
+ pagination:{pageSize:10},
19
+
20
+ });
21
+
22
+ useEffect(()=>{
23
+ const fnFeforeService = (params)=>{
24
+ // 假如你这里需要变更接口字段名称的话
25
+ params.page=params.current;
26
+ params.keyword='abc';
27
+ return params;
28
+ }
29
+
30
+ const fnAfterService = (response)=>{
31
+ // 这里你可以翻译及二次处理你的接口字段
32
+ let req={
33
+ code:response.errorCode
34
+ data:response.list,
35
+ page:response.pageInfo
36
+ };
37
+ response.code=response;
38
+ return response;
39
+ },
40
+
41
+ page.addListener('beforeService',fnFeforeService);
42
+ page.addListener('afterService',fnAfterService);
43
+ return ()=>{
44
+ page.removeListener('beforeService',fnFeforeService);
45
+ page.removeListener('afterService',fnAfterService)
46
+ }
47
+ },[])
48
+
49
+ const onReset=()=>{page.reset();}
50
+
51
+ const onPageChange=()=>{
52
+ let value=document.querySelector('#inputPage').value;
53
+ page.update({pagination:{current:+value}})
54
+ }
55
+ const onNext=()=>{page.nextPage();}
56
+
57
+ const renderTable=()=>{
58
+ let renderData= [];
59
+ page?.data?.forEach(list=>{
60
+ renderData=[...renderData,...list];
61
+ })
62
+
63
+ return (
64
+ <ul>
65
+ {
66
+ renderData?.map((item,idx)=>{
67
+ return <li key={idx}>[{idx}]{item}</li>
68
+ })
69
+ }
70
+ </ul>
71
+ )
72
+
73
+ }
74
+ return (
75
+ <ul>
76
+ {renderTable()}
77
+ </ul>
78
+ )
79
+ *
80
+ * @returns {UsePaginationResult}
81
+ */
82
+ const usePagination=(props)=>{
83
+
84
+ const {service}= props;
85
+
86
+ const DEFAULT_PAGE_SIZE=20;
87
+ const DEFAULT_PAGE_CURRENT=1;
88
+ const [pagination,setPagination] = useState({
89
+ current: DEFAULT_PAGE_CURRENT,
90
+ pageSize: props?.pagination?.pageSize??DEFAULT_PAGE_SIZE
91
+ });
92
+ const [data,setData] = useState(null);
93
+ const refListener= useRef({
94
+ beforeService:[],
95
+ afterService:[]
96
+ });
97
+
98
+ const update= async ({pagination:_pagination,clear=false}={})=>{
99
+ _pagination = _pagination ?? pagination;
100
+ _pagination = {...pagination,..._pagination};
101
+
102
+ const {current,pageSize} = _pagination;
103
+
104
+ let params = {current,pageSize};
105
+ let listener=refListener.current;
106
+ const {beforeService} = listener;
107
+ if(beforeService){
108
+ console.log('[usePagination] beforeService',params)
109
+ for(let i=0;i<beforeService.length;i++){
110
+ params = beforeService[i](params);
111
+ if(typeof params?.then == 'function'){
112
+ params = await params;
113
+ }
114
+ if(!params){return;}
115
+ }
116
+ }
117
+ if(!params)requrn;
118
+ console.log('[usePagination] service',params)
119
+ let req = await service(params);
120
+ const {afterService} = listener;
121
+ if(afterService){
122
+ console.log('[usePagination] afterService',req)
123
+ for(let i=0;i<afterService.length;i++){
124
+ req = afterService[i](req);
125
+ if( typeof req?.then == 'function' ){
126
+ req = await req;
127
+ }
128
+ }
129
+ }
130
+ console.log('[usePagination] response',req)
131
+ let response={};
132
+ if(req?.code==0){
133
+ let {page:{current,pageSize,total},data:reqData} = req;
134
+ current=+current;
135
+ pageSize=+pageSize;
136
+ total=+total;
137
+ const startIdx= (current-1)*pageSize;
138
+ const more = current*pageSize<total;
139
+ response.pagination={
140
+ current,pageSize,total,startIdx,more
141
+ };
142
+ setPagination(response.pagination);
143
+ response.data= clear?[]:(data||[]);
144
+
145
+ if(response.data.length<current){
146
+ for(let i=0;i<current;i++){
147
+ response.data[i]=response.data[i]||[];
148
+ }
149
+ }
150
+ let pageIdx= current-1;
151
+ response.data[pageIdx]= response.data[pageIdx]||[];
152
+ response.data[pageIdx]= reqData||[];
153
+ response.data=[...response.data];
154
+ setData(response.data);
155
+ }else{
156
+ setData(data||[]);
157
+ }
158
+ return response;
159
+ }
160
+
161
+ const nextPage= async ()=>{
162
+ if(!pagination.more){
163
+ return false;
164
+ }
165
+ return update({pagination:{current:pagination.current+1}})
166
+ }
167
+
168
+ const reset= ()=>{
169
+ return update({pagination:{current:1},clear:true})
170
+ }
171
+
172
+ const addListener=(type,fn)=>{
173
+ if(!refListener.current[type]){
174
+ refListener.current[type]=[];
175
+ }
176
+
177
+ const repeat= refListener.current[type].some(callback=>{
178
+ if(callback==fn){return true;}
179
+ return false;
180
+ })
181
+ if(repeat){return;}
182
+ refListener.current[type].push(fn);
183
+ }
184
+
185
+ const removeListener=(type,fn)=>{
186
+ if(!refListener.current[type]){
187
+ refListener.current[type]=[];
188
+ }
189
+ let list = refListener.current[type];
190
+ for(let i=0;i<list.length;i++){
191
+ if(list[i] == fn){
192
+ list.spice(i,1);
193
+ }
194
+ }
195
+ }
196
+
197
+ const action= useMemo(()=>{
198
+ return {
199
+ pagination,
200
+ update,
201
+ reset,
202
+ nextPage,
203
+ data,
204
+ addListener,
205
+ removeListener
206
+ }
207
+ },[pagination,data,refListener])
208
+
209
+ return action;
210
+ }
211
+
212
+
213
+
214
+ /**
215
+ * 分页信息
216
+ * @typedef {Object} Pagination
217
+ * @property {number} pageSize=20 - 分页大小
218
+ * @property {number} current=1 - 当前页码
219
+ * @property {number} total=1 - 总记录数
220
+ * @property {number} [startIdx=0] - 当前页面下第一条数据的序号
221
+ * @property {boolean} [more] - 是否还有下一页
222
+ *
223
+ */
224
+
225
+
226
+ /**
227
+ * 分页数据结果
228
+ * @typedef {Object} PageDataResult
229
+ * @property {Pagination} pagination - 最新分页信息
230
+ * @property {Object[][]} data - 分页数据集合
231
+ */
232
+
233
+
234
+
235
+ /**
236
+ * 分页查询结果
237
+ * @typedef {callback} FunUpdate
238
+ * @property {Pagination} [pagination] - 最新分页信息
239
+ * @property {boolean} [clear] - 是否清空数据
240
+ * @returns {Promise<PageDataResult>} 最新的分页数据结果
241
+ */
242
+
243
+
244
+ /**
245
+ * usePagination的返回对象
246
+ * @typedef {Object} UsePaginationResult
247
+ * @property {Object[][]} data - 分页数据集合
248
+ * @property {Pagination} paginnation - 分页信息
249
+ * @property {FunUpdate} update - 查询方法
250
+ * @property {FunUpdate} next - 获取下一页数据
251
+ * @property {function} addListener - 监听事件 (type='beforeService'|'afterService',fn:FunListener)=>object
252
+ * @property {function} removeListener - 移除监听事件 (type,fn:FunListener)=>void
253
+ */
254
+
255
+
256
+ /**
257
+ * 事件监听方法
258
+ * @typedef {callback} FunListener
259
+ * @property {Object} params - 被拦截的数据对象
260
+ * @returns {Object|Promise<Object>} 处理完毕的数据对象或者Promise
261
+ */
262
+
263
+
264
+ export default usePagination;
@@ -0,0 +1,86 @@
1
+
2
+ /**
3
+ * @module usePaginationWithForm
4
+ */
5
+ import { useState, useMemo, useEffect, useRef } from 'react';
6
+ import usePagination from '../usePagination';
7
+
8
+
9
+ /**
10
+ * 支持Antd-Form的usePagination
11
+ * 作用是在查询接口前自动获取form表单内的字段,并作为接口查询参数进行查询
12
+ * 使用方法及参数字段参考 [usePagination]{@link usePagination.md}
13
+ *
14
+ * @param {Object} props
15
+ * @param {Object} props.form - Form表单的ref,在接口调用前会校验获取form表单内的数据,提交到接口参数内查询
16
+ *
17
+ * @example
18
+ const [form] = Form.useForm();
19
+ const page = usePaginationWithForm({
20
+ service:GET_LIST,
21
+ pagination:{pageSize:10},
22
+ form:form
23
+ });
24
+
25
+ return(
26
+ <Form form={form} style={{width:'600px'}} layout="inline">
27
+ <Form.Item label='关键字' name='keyword' rules={[
28
+ {
29
+ max:5,
30
+ message:'最多5个字符'
31
+ }
32
+ ]}>
33
+ <Input />
34
+ </Form.Item>
35
+ <Button onClick={onSearch} type='primary'>查询</Button>
36
+ <Button onClick={onReset} type='primary'>重置</Button>
37
+ </Form>
38
+ )
39
+ */
40
+ const usePaginationWithForm = (props) => {
41
+ const {service,pagination}= props;
42
+ const [form] = useState(props.form);
43
+ const page = usePagination({service,pagination})
44
+
45
+ useEffect(()=>{
46
+ const fnFeforeService = async (params)=>{
47
+ // 假如你这里需要变更接口字段名称的话
48
+ const values = await getSearchValue();
49
+ if(!values)return;
50
+ params= {...params,...values}
51
+ return params;
52
+ };
53
+ page.addListener('beforeService',fnFeforeService);
54
+ return ()=>{
55
+ page.removeListener('beforeService',fnFeforeService);
56
+ }
57
+ },[])
58
+
59
+ const getSearchValue = async () => {
60
+ try {
61
+ let value = {};
62
+ if (form) {
63
+ value = await form.validateFields();
64
+ }
65
+ return value;
66
+ } catch (ex) {
67
+ return;
68
+ }
69
+ };
70
+
71
+
72
+
73
+ const reset = () => {
74
+ if(form){
75
+ form.resetFields();
76
+ }
77
+ page.reset();
78
+ };
79
+
80
+ return {
81
+ ...page,
82
+ reset,
83
+ };
84
+ };
85
+
86
+ export default usePaginationWithForm
@@ -1,16 +1,19 @@
1
+ /**
2
+ * useSwitch
3
+ * @module useSwitch
4
+ */
1
5
  import { useState, useMemo, useRef, useEffect } from 'react';
2
6
 
3
7
 
8
+
4
9
  /**
5
10
  * 控制开关
6
- * props?: boolean; 默认开关状态
7
- * return {
8
- * state:boolean,当前开关状态
9
- * open:(force?:boolean)=>void,手动切换至开
10
- * close:(force?:boolean)=>void,手动切换至关,当不指定force为true时,每次open将增加计数器,close将减少计数器,当计数器为0时才真正关闭
11
- * toggle:()=>void,切换开关
12
- * count: number,当前计数器
13
- * }
11
+ *
12
+ * [Demo-CodeSandbox]{@link https://codesandbox.io/s/useswitch-qd7dr?file=/index.js}
13
+ *
14
+ * @param {boolean} [props=false] - 默认的开关状态
15
+ * @return {UseSwitchResult}
16
+ *
14
17
  *
15
18
  */
16
19
  const useSwitch = (props=false) => {
@@ -62,4 +65,16 @@ const useSwitch = (props=false) => {
62
65
  return {state:value,count,...actions};
63
66
  };
64
67
 
68
+ /**
69
+ * useSwitchHookResult
70
+ * @typedef {Object} UseSwitchResult
71
+ * @property {boolean} state - 当前开关状态
72
+ * @property {number} count - 当前开关计数器
73
+ * @property {function} open - 切换至开模式 (force?:boolean)=>void
74
+ * @property {function} close - 切换至关模式 (force?:boolean)=>void
75
+ * @property {function} toggle - 切换至反向模式 ()=>void
76
+ *
77
+ */
78
+
79
+
65
80
  export default useSwitch;
@@ -0,0 +1,15 @@
1
+ import React,{ useState, useMemo, useEffect, useRef } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import useCounter from '@/useCounter';
4
+
5
+
6
+ const Index=()=>{
7
+ const [count,add] = useCounter();
8
+ return (
9
+ <section>
10
+ <h2>计数器:{count}</h2>
11
+ <button onClick={add}>触发增加计数器</button>
12
+ </section>
13
+ )
14
+ }
15
+ ReactDOM.render(<Index />, document.getElementById('main-view'));
@@ -0,0 +1,119 @@
1
+ import React,{ useState, useMemo, useEffect, useRef } from 'react';
2
+ import ReactDOM, { render } from 'react-dom';
3
+ import usePagination from '@/usePagination';
4
+ import {Pagination,Button,Input,Table} from 'antd';
5
+
6
+
7
+
8
+ const GET_LIST=(params)=>{
9
+ const list=[]
10
+ const total=200;
11
+ for(let i=0;i<total;i++){
12
+ list.push(`数据${i+1}`)
13
+ }
14
+ const {current,pageSize,...others}=params;
15
+ const startIdx = (current-1) * pageSize;
16
+ let data = list.splice(startIdx,pageSize);
17
+ data=data.map((item,idx)=>{
18
+ return {id:`第${current}页-${item}`, value:`第${current}页-${item}`,params:others}
19
+ })
20
+ return Promise.resolve({
21
+ code:0,
22
+ data,
23
+ page:{
24
+ current:current,
25
+ pageSize:pageSize,
26
+ total,
27
+ }
28
+ })
29
+ }
30
+
31
+ const Index=()=>{
32
+ const page = usePagination({
33
+ service:GET_LIST,
34
+ pagination:{pageSize:10}
35
+ });
36
+
37
+
38
+ useEffect(()=>{
39
+ const fnFeforeService = (params)=>{
40
+ // 假如你这里需要变更接口字段名称的话
41
+ params.beforeService='beforeService';
42
+ return params;
43
+ };
44
+
45
+ const fnAfterService = (response)=>{
46
+ // 这里你可以翻译及二次处理你的接口字段
47
+ response.message='afterService';
48
+ return response;
49
+ };
50
+
51
+ page.addListener('beforeService',fnFeforeService);
52
+ page.addListener('afterService',fnAfterService);
53
+ page.update()
54
+ return ()=>{
55
+ page.removeListener('beforeService',fnFeforeService);
56
+ page.removeListener('afterService',fnAfterService)
57
+ }
58
+ },[])
59
+
60
+ const onReset=()=>{page.reset();}
61
+
62
+
63
+ const onPageChange=(current,pageSize)=>{
64
+ page.update({pagination:{current,pageSize}})
65
+ }
66
+
67
+ const onNext=()=>{page.nextPage();}
68
+
69
+ const columns=[{
70
+ dataIndex:'value',
71
+ title:'数据值',
72
+ render:(_,record,idx)=>{
73
+ return `[${idx}]${record.value}`;
74
+ }
75
+ },{
76
+ dataIndex:'params',
77
+ title:'入参值',
78
+ render:(_,record,idx)=>{
79
+ return record.params?JSON.stringify(record.params):'-';
80
+ }
81
+ }];
82
+
83
+ const getDataSource=()=>{
84
+ let req=[];
85
+ if(page?.data && page?.data?.length>0){
86
+ for(let i=0;i<page.data.length;i++){
87
+ req= [...req,...page.data[i]];
88
+ }
89
+ }
90
+ return req;
91
+ }
92
+ return (
93
+ <section style={{padding:'12px'}}>
94
+ <section>
95
+ <Button onClick={onNext} type='primary'>加载下一页</Button>
96
+ <Button onClick={onReset} type='primary'>重置</Button>
97
+
98
+
99
+ <Table
100
+ rowKey={'id'}
101
+ loading={!page?.data?.length>0}
102
+ columns={columns}
103
+ dataSource={getDataSource()}
104
+ pagination={false}
105
+ />
106
+
107
+ <Pagination
108
+ current={page?.pagination?.current}
109
+ pageSize={page?.pagination?.pageSize}
110
+ onChange={onPageChange}
111
+ total={page?.pagination?.total}
112
+ />
113
+
114
+ </section>
115
+
116
+ </section>
117
+ )
118
+ }
119
+ ReactDOM.render(<Index />, document.getElementById('main-view'));
@@ -0,0 +1,101 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import {Form,Button,Input,Table} from 'antd';
4
+
5
+ import usePaginationWithForm from '@/usePaginationWithForm';
6
+
7
+
8
+
9
+
10
+ const GET_LIST=(params)=>{
11
+ const list=[]
12
+ const total=200;
13
+ for(let i=0;i<total;i++){
14
+ list.push(`数据${i+1}`)
15
+ }
16
+ const {current,pageSize,...others}=params;
17
+ const startIdx = (current-1) * pageSize;
18
+ let data = list.splice(startIdx,pageSize);
19
+ data=data.map((item,idx)=>{
20
+ return {id:''+idx, value:`第${current}页-${item}`,params:others}
21
+ })
22
+ return Promise.resolve({
23
+ code:0,
24
+ data,
25
+ page:{
26
+ current:current,
27
+ pageSize:pageSize,
28
+ total,
29
+ }
30
+ })
31
+ }
32
+
33
+ const Index=()=>{
34
+
35
+ const [form] = Form.useForm();
36
+ const page = usePaginationWithForm({
37
+ service:GET_LIST,
38
+ pagination:{pageSize:10},
39
+ form:form
40
+ });
41
+
42
+
43
+ const onSearch=()=>{
44
+ page.update();
45
+ };
46
+
47
+ const onReset=()=>{page.reset();}
48
+
49
+ const onPageChange=(current,pageSize)=>{
50
+ page.update({pagination:{current,pageSize}})
51
+ }
52
+
53
+ const columns=[{
54
+ dataIndex:'value',
55
+ title:'数据值',
56
+ render:(_,record,idx)=>{
57
+ return `[${idx}]${record.value}`;
58
+ }
59
+ },{
60
+ dataIndex:'params',
61
+ title:'入参值',
62
+ render:(_,record,idx)=>{
63
+ return record.params?JSON.stringify(record.params):'-';
64
+ }
65
+ }];
66
+ return (
67
+ <section style={{padding:'12px'}}>
68
+ <section>
69
+ <Form form={form} style={{width:'600px'}} layout="inline">
70
+ <Form.Item label='关键字' name='keyword' rules={[
71
+ {
72
+ max:5,
73
+ message:'最多5个字符'
74
+ }
75
+ ]}>
76
+ <Input />
77
+ </Form.Item>
78
+ <Button onClick={onSearch} type='primary'>查询</Button>
79
+ <Button onClick={onReset} type='primary'>重置</Button>
80
+ </Form>
81
+
82
+
83
+ <Table
84
+ rowKey={'id'}
85
+ loading={!page?.data?.length>0}
86
+ columns={columns}
87
+ dataSource={page?.data? page?.data[page?.pagination?.current-1]:[]}
88
+ pagination={{
89
+ current:page?.pagination?.current,
90
+ pageSize:page?.pagination?.pageSize,
91
+ total:page?.pagination?.total,
92
+ onChange:onPageChange
93
+ }}
94
+ />
95
+
96
+ </section>
97
+
98
+ </section>
99
+ )
100
+ }
101
+ ReactDOM.render(<Index />, document.getElementById('main-view'));
@@ -1,16 +1,13 @@
1
1
  import React,{ useState, useMemo, useEffect, useRef } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
- import {useClipboard,useSwitch} from 'kn-hooks';
3
+ import useSwitch from '@/useSwitch';
4
4
 
5
5
 
6
6
  const Index=()=>{
7
7
  const loading = useSwitch(false);
8
- const clip = useClipboard({onSuccess:()=>{alert('已复制到剪贴板')}});
9
8
  return (
10
9
  <section>
11
- <h1 onClick={()=>{
12
- clip('4433')
13
- }}>当前状态:{loading.state?"打开":"关闭"}</h1>
10
+ <h1>当前状态:{loading.state?"打开":"关闭"}</h1>
14
11
  <h2>计数器:{loading.count}</h2>
15
12
  <button onClick={()=>{loading.toggle();}}>切换</button>
16
13
  <button onClick={()=>{loading.open();}}>打开</button>
package/src/.DS_Store DELETED
Binary file
@@ -1,165 +0,0 @@
1
- import { useState, useMemo, useEffect, useRef } from 'react';
2
-
3
-
4
- const useFormTableSearch = (props) => {
5
- const [search] = useState(props.initSearch || {});
6
- const [formRef] = useState(props.formRef);
7
- const [service] = useState(() => props.service);
8
- const [beforeSearch] = useState(() => props.beforeSearch);
9
- const [beforeService] = useState(() => props.beforeService);
10
- const [loading, setLoading] = useState(props.loading || false);
11
-
12
- const [pagination, setPagination] = useState(() => {
13
- const temp = { ...props.pagination, page: 1, pageSize: 10, total: 0, startIdx: 0 };
14
- if (props.pagination) {
15
- temp.startIdx = temp.pageSize * (temp.page - 1);
16
- }
17
- return temp;
18
- });
19
- const [updateData] = useState(() => props.updateData);
20
- const [orderInfo, setOrderInfo] = useState({});
21
-
22
- const getSearchValue = async () => {
23
- try {
24
- let value = {};
25
- if (formRef) {
26
- value = await formRef.validateFields();
27
- } else {
28
- value = search;
29
- }
30
- if (beforeSearch) {
31
- value = beforeSearch(value);
32
- }
33
- return value;
34
- } catch (ex) {
35
- return;
36
- }
37
- };
38
- const formatSearchValue = async ({ searchValue, pageValue, orderValue }) => {
39
- if (!searchValue) {
40
- searchValue = await getSearchValue();
41
- }
42
- searchValue = searchValue || search;
43
- pageValue = pageValue || pagination;
44
- orderValue = orderValue || orderInfo;
45
- const order: any[] = [];
46
- if (orderValue) {
47
- Object.keys(orderValue).map((name) => {
48
- if (orderValue[name] === 'ascend') {
49
- order.push({ [`${name}`]: 'ASC' });
50
- } else if (orderValue[name] === 'descend') {
51
- order.push({ [`${name}`]: 'DESC' });
52
- }
53
- });
54
- }
55
- const { page, pageSize } = pageValue;
56
- let params = {
57
- ...searchValue,
58
- page: {
59
- pageNum: page,
60
- pageSize: pageSize,
61
- },
62
- };
63
- if (order && order.length > 0) {
64
- params.page.orders = order;
65
- }
66
- if (beforeService) params = beforeService(params);
67
- return params;
68
- };
69
- const refresh = async ({ searchValue, pageValue, orderValue }) => {
70
- setLoading(true);
71
- searchValue = searchValue || search;
72
- pageValue = pageValue || pagination;
73
- orderValue = orderValue || orderInfo;
74
-
75
- const params = await formatSearchValue({ searchValue, pageValue, orderValue });
76
- if (!params) {
77
- setLoading(false);
78
- return;
79
- }
80
- const ret: any = await service(params);
81
-
82
- if (ret?.data?.page) {
83
- const { pageNum, pageSize: size, totalRows } = ret.data.page;
84
- setPagination({
85
- page: pageNum || 1,
86
- pageSize: size || 10,
87
- total: totalRows,
88
- startIdx: (pageNum - 1) * size || 0,
89
- });
90
- }
91
-
92
- if (updateData) updateData(ret);
93
- setLoading(false);
94
- return ret;
95
- };
96
-
97
- const onPaginationChange = async (pageInfo: any, sorterInfo?: any) => {
98
- let { page: current, pageSize }: any = pagination;
99
- let orderValue = orderInfo;
100
- if (pageInfo) {
101
- current = pageInfo.current;
102
- pageSize = pageInfo.pageSize;
103
- }
104
-
105
- if (sorterInfo) {
106
- const { field, order } = sorterInfo;
107
- if (field) {
108
- if (order) {
109
- orderInfo[field] = order;
110
- } else {
111
- delete orderInfo[field];
112
- }
113
- }
114
- orderValue = orderInfo;
115
- setOrderInfo({ ...orderInfo });
116
- }
117
-
118
- const searchValue: any = await getSearchValue();
119
-
120
- refresh({
121
- searchValue,
122
- pageValue: { page: current, pageSize },
123
- orderValue,
124
- });
125
- };
126
-
127
- const onSorter = async (sorter: any) => {
128
- const { field, order } = sorter;
129
- if (field) {
130
- if (order) {
131
- orderInfo[field] = order;
132
- } else {
133
- delete orderInfo[field];
134
- }
135
- }
136
- setOrderInfo({ ...orderInfo });
137
- console.log(`${JSON.stringify(orderInfo)}`);
138
- refresh();
139
- };
140
-
141
- const btnSearch = async () => {
142
- const value: any = await getSearchValue();
143
- if (value) {
144
- refresh({ searchValue: value });
145
- }
146
- };
147
-
148
- const btnReset = () => {
149
- formRef.resetFields();
150
- };
151
-
152
- return {
153
- onPaginationChange,
154
- pagination,
155
- btnSearch,
156
- search,
157
- refresh,
158
- btnReset,
159
- onSorter,
160
- loading,
161
- formatSearchValue,
162
- };
163
- };
164
-
165
- export default useFormTableSearch
@@ -1,15 +0,0 @@
1
-
2
- import { useEffect,useRef, useMemo } from 'react';
3
- import useSwitch from '../useSwitch';
4
-
5
- const useRefSwitch=(props=false)=>{
6
- const loading = useSwitch(props);
7
- const ref = useRef();
8
- ref.current = loading;
9
- useEffect(()=>{
10
- ref.current = loading;
11
- },[loading]);
12
-
13
- return useMemo(()=>ref,[]);
14
- }
15
- export default useRefSwitch;
package/test/.DS_Store DELETED
Binary file
@@ -1,19 +0,0 @@
1
- import React,{ useState, useMemo, useEffect, useRef } from 'react';
2
- import ReactDOM from 'react-dom';
3
- import useRefSwitch from '@/useRefSwitch';
4
-
5
- const Index=()=>{
6
- const loading = useRefSwitch(false);
7
- return (
8
- <section>
9
- <h1>当前状态:{loading.current.state?"打开":"关闭"}</h1>
10
- <h2>计数器:{loading.current.count}</h2>
11
- <button onClick={()=>{loading.current.toggle();}}>切换</button>
12
- <button onClick={()=>{loading.current.open();}}>打开</button>
13
- <button onClick={()=>{loading.current.close();}}>关闭</button>
14
- <button onClick={()=>{loading.current.open(true);}}>强制打开</button>
15
- <button onClick={()=>{loading.current.close(true);}}>强制关闭</button>
16
- </section>
17
- )
18
- }
19
- ReactDOM.render(<Index />, document.getElementById('main-view'));