kn-hooks 0.0.36 → 0.0.40

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,6 +1,6 @@
1
1
  {
2
2
  "name": "kn-hooks",
3
- "version": "0.0.36",
3
+ "version": "0.0.40",
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",
@@ -0,0 +1,293 @@
1
+ /**
2
+ * @module useDictionary
3
+ */
4
+ import React,{ useState, useMemo, useEffect } from 'react';
5
+
6
+
7
+ /**
8
+ * @template [T=object]
9
+ * @callback Api
10
+ * @param {Object} params - 调用接口用到的参数
11
+ * @returns {Promise<T>}
12
+ */
13
+
14
+
15
+ /**
16
+ * @typedef DictionaryItem
17
+ * 字典数据的结构
18
+ * @property {string} id - 数据唯一ID
19
+ * @property {string} name - 数据唯一id对应的别名
20
+ * @property {string} label - 展示给用户看的文字
21
+ * @property {boolean} [disabled] - 是否禁用
22
+ * @property {object} [data] - 原始数据
23
+ * @property {string} [subLabel] - 副标题
24
+ * @property {string} [type] - 是不是多选框
25
+ */
26
+
27
+
28
+ /**
29
+ * @typedef CreateOptions
30
+ * @property {DictionaryItem[]} types - 字典数据列表
31
+ * @property {string} [idKey='id'] - id的字段名
32
+ * @property {string} [nameKey='name'] - id的字段名
33
+ * @property {string} [labelKey='label'] - id的字段名
34
+ */
35
+
36
+
37
+ /**
38
+ * @typedef GetOptionsProps
39
+ * @property {object} props
40
+ * @property {(item:DictionaryItem)=>boolean} [props.onFilter] - 数据唯一id对应的别名
41
+ * @property {(item:DictionaryItem)=>boolean} [props.onDisabled] - 数据唯一id对应的别名
42
+ */
43
+
44
+ /**
45
+ * @typedef RenderOptionsProps
46
+ * @property {object} props
47
+ * @property {DictionaryItem[]} [props.options] - 数据唯一id对应的别名
48
+ * @property {JSX.Element} [props.OptionComponent] - 数据唯一id对应的别名
49
+ */
50
+
51
+
52
+ /**
53
+ * @typedef RenderOptions
54
+ * @property {(item:DictionaryItem)=>ReactDOM} onRender - 渲染拦截器
55
+ */
56
+
57
+ /**
58
+ * @typedef EnumResult
59
+ * @property {DictionaryItem[]} types - 字典数据列表
60
+ * @property {(labelOrName:string)=>string} getId - 搜索字典项中,label或name匹配labelOrName的项目,返回其id的值
61
+ * @property {(id:string)=>string} getName - 匹配id复合的对象,返回其name的值
62
+ * @property {(id:string)=>string} getLabel - 匹配id复合的对象,返回其label的值
63
+ * @property {(idOrNameOrLabel:string)=>DictionaryItem} getItem - 匹配id,label,name只要符合的返回对象
64
+ * @property {(props?:GetOptionsProps)=>DictionaryItem[]} getOptions - 获取选项列表
65
+ * @property {(props?:RenderOptions)=>ReactDOM[]} render - 触发渲染
66
+ *
67
+ */
68
+
69
+
70
+ /**
71
+ * @typedef CreateApiDictionaryOptions
72
+ * @property {Api} [api] - 用于获取字典列表的接口
73
+ * @property {(request:object)=>object} [beforeApi] - (request:object)=>object 接口调用前的参数拦截器
74
+ * @property {(response:object)=>object} [afterApi] - (reponse:object)=>object[] 接口调用后的拦截器
75
+ * @property {DictionaryItem[]} [defaultTypes] - 如果字典不是通过api获取,可以在这里设置字典的内容
76
+ */
77
+
78
+ /**
79
+ * @typedef {CreateOptions & CreateApiDictionaryOptions} CreateDictionaryOptions
80
+ */
81
+
82
+ /**
83
+ * @typedef CreateDictoryResult
84
+ * @property {()=>boolean} isReady - 是否加载完毕
85
+ * @property {()=>void} reload - 重新加载
86
+ *
87
+ */
88
+
89
+ /**
90
+ * @typedef {CreateDictoryResult & EnumResult} UseDictionaryResult
91
+ */
92
+
93
+
94
+ const DEFAULT_CONFIG={
95
+ beforeApi:null,
96
+ afterApi:null,
97
+ idKey:'id',
98
+ nameKey:'name',
99
+ labelKey:'label'
100
+ };
101
+
102
+
103
+ /**
104
+ * @function
105
+ * @description 全局设置SelectOption和RadioOption
106
+ * @param {Object} params
107
+ *
108
+ * @returns {void}
109
+ */
110
+ export const SetConfig = ({beforeApi,afterApi,idKey,nameKey,labelKey})=>{
111
+ if(beforeApi)DEFAULT_CONFIG.beforeApi = beforeApi;
112
+ if(afterApi)DEFAULT_CONFIG.afterApi = afterApi;
113
+ if(idKey)DEFAULT_CONFIG.idKey = idKey;
114
+ if(nameKey)DEFAULT_CONFIG.nameKey = nameKey;
115
+ if(labelKey)DEFAULT_CONFIG.labelKey = labelKey;
116
+ };
117
+
118
+
119
+ /**
120
+ * 创建一个静态的字典
121
+ * @param {CreateOptions} props
122
+ * @returns {EnumResult}
123
+ */
124
+ export const createEnum=props=>{
125
+ const {
126
+ idKey = DEFAULT_CONFIG.idKey,
127
+ nameKey = DEFAULT_CONFIG.nameKey,
128
+ labelKey = DEFAULT_CONFIG.labelKey,
129
+ types,
130
+ } = props;
131
+
132
+ const idMap={};
133
+ const labelMap={};
134
+ const nameMap={};
135
+
136
+ if(types){
137
+ types.forEach(item=>{
138
+ idMap[''+item[idKey]] = item;
139
+ labelMap[''+item[labelKey]] = item;
140
+ nameMap[''+item[nameKey]] = item;
141
+ })
142
+ }
143
+
144
+ const getLabel = (id) => {
145
+ let key = ''+id;
146
+ if(!types)return '';
147
+ if(idMap[key]){
148
+ return idMap[key][labelKey]
149
+ }
150
+ return '';
151
+ };
152
+ const getName = (id) => {
153
+ let key = ''+id;
154
+ if(!types)return '';
155
+ if(idMap[key]){
156
+ return idMap[key][nameKey]
157
+ }
158
+ return '';
159
+ };
160
+ const getId = (labelOrName) => {
161
+ let key = ''+labelOrName;
162
+ if(!types)return '';
163
+ if(nameMap[key]){
164
+ return nameMap[key][idKey]
165
+ }
166
+ if(labelMap[key]){
167
+ return labelMap[key][idKey]
168
+ }
169
+ return '';
170
+ };
171
+ const getItem=(idOrNameOrLabel)=>{
172
+ let key = ''+idOrNameOrLabel;
173
+ if(!types)return null;
174
+ if(idMap[key]){
175
+ return idMap[key]
176
+ }
177
+ if(nameMap[key]){
178
+ return nameMap[key]
179
+ }
180
+ if(labelMap[key]){
181
+ return labelMap[key]
182
+ }
183
+ return null;
184
+ };
185
+ const getOptions=(props)=>{
186
+ const {onFilter,onDisabled}=props;
187
+ let req=[];
188
+ if(!types)return req;
189
+ types.forEach(item=>{
190
+ let show=true;
191
+ let disabled=false;
192
+
193
+ if(onFilter){
194
+ show = onFilter(item);
195
+ }
196
+ if(onDisabled){
197
+ disabled = onDisabled(item);
198
+ }
199
+ if(show){
200
+ req.push({
201
+ ...item,
202
+ disabled:disabled,
203
+ })
204
+ }
205
+ })
206
+ return req;
207
+ }
208
+ const render=(props)=>{
209
+ const {onRender} = props;
210
+ let req=[];
211
+ if(!onRender)return req;
212
+ types.forEach(item=>{
213
+ if(onRender){
214
+ let component=onRender(item);
215
+ if(component){
216
+ req.push(component);
217
+ }
218
+ }
219
+ })
220
+ return req;
221
+ }
222
+ return { types, getLabel, getId, getName,getItem,getOptions,render };
223
+ }
224
+
225
+
226
+ /**
227
+ * @function
228
+ * @description 创建字典hooks工具
229
+ * @param {CreateDictionaryOptions} options
230
+ *
231
+ * @returns {()=>UseDictionaryResult}
232
+ */
233
+ export const createDictionary=options=>{
234
+ const {
235
+ api,
236
+ beforeApi = DEFAULT_CONFIG.beforeApi,
237
+ afterApi = DEFAULT_CONFIG.afterApi,
238
+ } = options;
239
+ const defaultTypes = options.defaultTypes || options.types || null;
240
+ return ()=>{
241
+ /**
242
+ * @type {[EnumResult,React.Dispatch<EnumResult>]}
243
+ */
244
+ const [enumItem, setEnumItem] = useState(()=>{
245
+ if(!api)return createEnum({...options,types:defaultTypes||[]})
246
+ return {types:null}
247
+ });
248
+
249
+ const init = async () => {
250
+ if(!api)return;
251
+ let params={};
252
+ let items=[];
253
+ if(beforeApi){
254
+ params = beforeApi();
255
+ }
256
+ let ret = await api(params);
257
+ if(afterApi){
258
+ items = afterApi(ret);
259
+ }else{
260
+ if (+ret?.code === 0) {
261
+ items = ret.data.body;
262
+ }
263
+ }
264
+ let value=createEnum({...options,types:items||[]});
265
+ setEnumItem(value);
266
+ };
267
+
268
+ const isReady = () => {
269
+ return enumItem?.types !== null;
270
+ };
271
+ const reload = () => {
272
+ init();
273
+ };
274
+
275
+ useEffect(() => {
276
+ init();
277
+ }, [api,beforeApi,afterApi]);
278
+
279
+ const actions = useMemo(() => {
280
+ return { isReady, reload,...enumItem };
281
+ }, [enumItem]);
282
+
283
+ return actions;
284
+ }
285
+ }
286
+
287
+
288
+
289
+
290
+ useDictionary.createDictionary=createDictionary;
291
+ useDictionary.SetConfig = SetConfig;
292
+
293
+ export default useDictionary
@@ -11,8 +11,14 @@ import useSwitch from '../useSwitch';
11
11
  * @property {Pagination} [pagination] - 默认分页信息
12
12
  * @property {Array<Function>} [beforeService] - api调用前监听方法列表
13
13
  * @property {Array<Function>} [afterService] - api调用后监听方法列表
14
+ * @property {string} [mode='pagination'] - pagination普通分页形式, scrollLoad 滚动加载,取值来源usePagination.MODE
15
+ * @property {boolean} [debug=false] - 是否进入调试模式
14
16
  */
15
17
 
18
+ const MODE={
19
+ pagination:'pagination',
20
+ scrollLoad:'scrollLoad',
21
+ }
16
22
  /**
17
23
  * 分页管理器
18
24
  *
@@ -90,7 +96,7 @@ import useSwitch from '../useSwitch';
90
96
  */
91
97
  const usePagination=(props)=>{
92
98
 
93
- const {service}= props;
99
+ const {service,mode=MODE.pagination,debug=false}= props;
94
100
 
95
101
  const DEFAULT_PAGE_SIZE=20;
96
102
  const DEFAULT_PAGE_CURRENT=1;
@@ -99,6 +105,8 @@ const usePagination=(props)=>{
99
105
  pageSize: props?.pagination?.pageSize??DEFAULT_PAGE_SIZE
100
106
  });
101
107
  const [data,setData] = useState(null);
108
+ const [scrollData,setScrollData] = useState(null);
109
+
102
110
  const refListener= useRef({
103
111
  beforeService:props?.beforeService??[],
104
112
  afterService:props?.afterService??[]
@@ -117,7 +125,7 @@ const usePagination=(props)=>{
117
125
  loading.open();
118
126
  const {beforeService} = listener;
119
127
  if(beforeService){
120
- console.log('[usePagination] beforeService',params)
128
+ if(debug)console.log('[usePagination] beforeService',params)
121
129
  for(let i=0;i<beforeService.length;i++){
122
130
  params = beforeService[i](params);
123
131
  if(typeof params?.then == 'function'){
@@ -130,11 +138,11 @@ const usePagination=(props)=>{
130
138
  loading.close();
131
139
  return;
132
140
  }
133
- console.log('[usePagination] service',params)
141
+ if(debug)console.log('[usePagination] service',params)
134
142
  let req = await service(params);
135
143
  const {afterService} = listener;
136
144
  if(afterService){
137
- console.log('[usePagination] afterService',req)
145
+ if(debug)console.log('[usePagination] afterService',req)
138
146
  for(let i=0;i<afterService.length;i++){
139
147
  req = afterService[i](req);
140
148
  if( typeof req?.then == 'function' ){
@@ -142,20 +150,26 @@ const usePagination=(props)=>{
142
150
  }
143
151
  }
144
152
  }
145
- console.log('[usePagination] response',req)
153
+ if(debug)console.log('[usePagination] response',req)
146
154
  let response={};
147
155
  if(req?.code==0){
148
- let {page:{current,total},data:reqData} = req;
156
+ let {page:{current,total=0},data:reqData} = req;
157
+ let _pageSize=pageSize;
149
158
  // 兼容没数据的时候如果pageSize为0会导致接下来刷新的时候也按0的pageSize来写入
150
159
  if(req?.page?.pageSize){
151
- pageSize=+pageSize;
160
+ _pageSize=+req?.page?.pageSize;
152
161
  }
153
162
  current=+current;
154
163
  total=+total;
155
- const startIdx= (current-1)*pageSize;
156
- const more = current*pageSize<total;
164
+ const startIdx= (current-1)*_pageSize;
165
+ let more;
166
+ if(mode==MODE.pagination){
167
+ more = current*_pageSize<total;
168
+ }else{
169
+ more = req.page.more;
170
+ }
157
171
  response.pagination={
158
- current,pageSize,total,startIdx,more
172
+ current,pageSize:_pageSize,total,startIdx,more
159
173
  };
160
174
  setPagination(response.pagination);
161
175
  response.data= clear?[]:(data||[]);
@@ -170,7 +184,19 @@ const usePagination=(props)=>{
170
184
  response.data[pageIdx]= reqData||[];
171
185
  response.data=[...response.data];
172
186
  setData(response.data);
187
+
188
+ // 滚动加载模式的数据组装
189
+ if(mode==MODE.scrollLoad){
190
+ let _scrollData=[];
191
+ for(let data of response.data ){
192
+ if(data!=null && Array.isArray(data) ){
193
+ _scrollData = [..._scrollData,...data];
194
+ }
195
+ }
196
+ setScrollData(_scrollData)
197
+ }
173
198
  }else{
199
+ setScrollData(scrollData||[]);
174
200
  setData(data||[]);
175
201
  }
176
202
  loading.close();
@@ -222,13 +248,15 @@ const usePagination=(props)=>{
222
248
  data,
223
249
  addListener,
224
250
  removeListener,
225
- loading
251
+ loading,
252
+ mode,
253
+ scrollData
226
254
  }
227
- },[pagination,data,refListener,loading])
255
+ },[pagination,data,refListener,loading,mode,scrollData])
228
256
 
229
257
  return action;
230
258
  }
231
-
259
+ usePagination.MODE=MODE;
232
260
 
233
261
 
234
262
  /**
@@ -295,6 +323,9 @@ const usePagination=(props)=>{
295
323
  * @property {function} addListener - 监听事件 (type='beforeService'|'afterService',fn:FunListener)=>object
296
324
  * @property {function} removeListener - 移除监听事件 (type,fn:FunListener)=>void
297
325
  * @property {object} loading - loading状态,它是一个useSwitch
326
+ * @property {string} [mode='pagination'] - pagination普通分页形式, scrollLoad 滚动加载
327
+ * @property {Object[]} scrollData - 滚动加载模式下的数据集合
328
+ *
298
329
  */
299
330
 
300
331
 
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * @module usePaginationWithForm
4
4
  */
5
- import { useState, useMemo, useEffect, useRef } from 'react';
5
+ import { useState, useEffect } from 'react';
6
6
  import usePagination from '../usePagination';
7
7
 
8
8
  /**
@@ -102,6 +102,7 @@ const usePaginationWithForm = (props) => {
102
102
  };
103
103
  };
104
104
 
105
+ usePaginationWithForm.MODE=usePagination.MODE;
105
106
 
106
107
  /**
107
108
  * usePaginationWithForm的返回对象