@hzab/form-render 1.6.17 → 1.6.18

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/CHANGELOG.md CHANGED
@@ -1,283 +1,287 @@
1
- # @hzab/form-render@1.6.17
2
-
3
- fix: 修复上传组件,文件名和后缀名取值逻辑
4
-
5
- # @hzab/form-render@1.6.16
6
-
7
- fix: ArrayTable value 空问题兼容
8
- fix: 修复人员选择组件中 isObject 函数 ts 类型不明确导致在部分项目中出现类型错误提示的问题
9
-
10
- # @hzab/form-render@1.6.15
11
-
12
- feat: 新增 formily js change 事件
13
-
14
- # @hzab/form-render@1.6.14
15
-
16
- fix: 修复 TreeCheckbox 隐藏不能点击问题。
17
-
18
- # @hzab/form-render@1.6.13
19
-
20
- feat:locationListPicker 在已有元素范围内可新增地址标记
21
-
22
- # @hzab/form-render@1.6.12
23
-
24
- feat: PersonnelSelect 组件优化传值方式,支持复数据传入、返回
25
-
26
- # @hzab/form-render@1.6.11
27
-
28
- feat: editor 添加配置 merage
29
-
30
- # @hzab/form-render@1.6.10
31
-
32
- feat: 富文本编辑器实例像外暴露
33
-
34
- # @hzab/form-render@1.6.9
35
-
36
- feat: PersonnelSelect 组件
37
-
38
- # @hzab/form-render@1.6.8
39
-
40
- feat:富文本组件添加私有化部署文件上传与 oss 上传的区分
41
-
42
- # @hzab/form-render@1.6.7
43
-
44
- fix: 修复 LocationListPicker 组件在编辑时第一次无法正确渲染的 bug。
45
-
46
- # @hzab/form-render@1.6.5
47
-
48
- fix: 修复 LocationListPicker 组件在编辑时第一次无法正确渲染的 bug。
49
- feat: LocationPicker 渲染时增加范围
50
-
51
- # @hzab/form-render@1.6.3
52
-
53
- fix: 修复 LocationListPicker 组件的 Pupup 弹窗在某些时候漂移的 bug。
54
-
55
- # @hzab/form-render@1.6.2
56
-
57
- feat:自增列表组件新增删除二次确认功能
58
-
59
- # @hzab/form-render@1.6.1
60
-
61
- fix:列表新增-上传组件删除后清除对应的值
62
-
63
- # @hzab/form-render@1.6.0
64
-
65
- feat: 私有部署文件上传
66
- fix: 文件名称是否 hash 支持配置 useHashName
67
-
68
- # @hzab/form-render@1.5.1
69
-
70
- fix: schemaScope 使用 Ref 缓存,解决 onChange tempData 更新问题
71
-
72
- # @hzab/form-render@1.5.0
73
-
74
- feat: ossUpload 文件目录配置 dir 前缀 oss-upload 文件目录;dir 格式格式化
75
- feat: 上传组件多模式上传,出参格式配置
76
- fix: 字符串事件处理(onChange)
77
-
78
- # @hzab/form-render@1.4.1
79
-
80
- fix: Upload 修复历史遗留问题,解决 单项上传始终解析为 对象/字符串 无法切换会数组的情况
81
-
82
- # @hzab/form-render@1.4.0
83
-
84
- break: location-list-picker 添加多区域规划
85
- fix: LocationListPicker 点位信息弹窗位置问题修复
86
-
87
- # @hzab/form-render@1.3.3
88
-
89
- fix: 富文本异步数据问题修复
90
-
91
- # @hzab/form-render@1.3.2
92
-
93
- fix: schema props 为 schemaScope 字符串的情况
94
-
95
- # @hzab/form-render@1.3.1
96
-
97
- feat: 表单 onChange 事件(目前仅做触发,值计算后续优化)
98
-
99
- # @hzab/form-render@1.3.0
100
-
101
- break: 去除 lib,默认 src
102
-
103
- # @hzab/form-render@1.2.5
104
-
105
- - fix: LocationListPicker 组件高度调整与删除点位修复
106
-
107
- # @hzab/form-render@1.2.4
108
-
109
- fix: ossUpload 入参问题修复
110
- fix: UserSelect InfoRender
111
-
112
- # @hzab/form-render@1.2.3
113
-
114
- - fix: 富文本添加 loading 样式
115
-
116
- # @hzab/form-render@1.2.2
117
-
118
- - fix: Upload 预览 src(修复版)
119
-
120
- # @hzab/form-render@1.2.1
121
-
122
- - fix: 富文本 video 样式调整
123
-
124
- # @hzab/form-render@1.2.0
125
-
126
- - feat: Upload 增加 video 视频上传配置
127
-
128
- # @hzab/form-render@1.1.7
129
-
130
- - fix: Upload 预览 src
131
-
132
- # @hzab/form-render@1.1.6
133
-
134
- fix: 时间范围修复
135
-
136
- # @hzab/form-render@1.1.5
137
-
138
- fix: Upload 预览优化
139
-
140
- # @hzab/form-render@1.1.4
141
-
142
- fix: Upload 添加内部地址拼接
143
-
144
- # @hzab/form-render@1.1.3
145
-
146
- fix: Upload 添加预览配置
147
-
148
- # @hzab/form-render@1.1.2
149
-
150
- - feat: LocationListPicker 优化;列表激活状态、新增按钮提示、marker 激活状态及相关交互
151
-
152
- # @hzab/form-render@1.1.1
153
-
154
- - Upload OSS 上传支持数组对象格式值
155
-
156
- # @hzab/form-render@1.1.0
157
-
158
- - feat: LocationListPicker 组件
159
-
160
- # @hzab/form-render@1.0.3
161
-
162
- - fix: dayjs 自定义解析器,解决 YYYY 年 MM 月 DD 日 格式问题
163
-
164
- # @hzab/form-render@1.0.2
165
-
166
- - fix: Upload 预览文件判断逻辑
167
-
168
- # @hzab/form-render@1.0.1
169
-
170
- - fix: 提交结果为字符串处理 isOssUpload || isStrRes
171
-
172
- # @hzab/form-render@1.0.0
173
-
174
- - feat: @formily/antd 使用 c-formily-antd 替换,formily 相关包升级至 2.3.1 "@formily/core": "^2.3.1", "@formily/react": "^2.3.1", "c-formily-antd": "^2.3.1",
175
-
176
- # @hzab/form-render@0.7.4
177
-
178
- - RichEditor zIndex
179
-
180
- # @hzab/form-render@0.7.3
181
-
182
- - fix: Upload 通过 accept 判断文件类型
183
-
184
- # @hzab/form-render@0.7.2
185
-
186
- - fix: ossUpload axios 数据结构兼容
187
-
188
- # @hzab/form-render@0.7.1
189
-
190
- - Upload props customRequest
191
-
192
- # @hzab/form-render@0.7.0
193
-
194
- - 富文本组件 RichEditor
195
-
196
- # @hzab/form-render@0.6.2
197
-
198
- - fix: TreeCheckbox tabs 强制渲染,解决 checkbox 没有渲染导致值丢失的问题
199
-
200
- # @hzab/form-render@0.6.1
201
-
202
- - fix: LocationPicker MapSearch 只读模式选择结果只修改地图中心点
203
- - fix: LocationPicker 解决 AMap 异步加载导致 setPickPoint 异常的问题
204
-
205
- # @hzab/form-render@0.6.0
206
-
207
- - feat: TreeCheckbox 支持无线层 tab
208
- - feat: TreeCheckbox 支持合并叶子节点
209
- - refactor: svg-icon.js -> svg-icon.jsx
210
-
211
- # @hzab/form-render@0.5.5
212
-
213
- - fix: TreeCheckbox key 问题修复
214
- - fix: TreeCheckbox 数据兼容处理
215
-
216
- # @hzab/form-render@0.5.4
217
-
218
- - feat: LocationPicker mapInit 设置值取增加临时存储的 formatVal 的逻辑
219
-
220
- # @hzab/form-render0.5.3
221
-
222
- - feat: LocationPicker MapSearch 清除改为 input, map 请求优化
223
-
224
- # @hzab/form-render0.5.2
225
-
226
- - feat: TreeCheckbox tabBox 最外层允许配置成 tab
227
-
228
- # @hzab/form-render0.5.1
229
-
230
- - fix: LocationPicker 去除 changMode 参数,仅允许 点击地图选点
231
- - fix: LocationPicker 获取地址逻辑修复
232
- - fix: DatePicker 问题修复
233
-
234
- # @hzab/form-render0.5.0
235
-
236
- - fix LocationPicker disabled || readOnly 不展示 Notice
237
- - fix: LocationPicker 异步数据回填逻辑处理
238
- - fix: LocationPicker 去除 isObjectRes 参数
239
-
240
- # @hzab/form-render0.4.2
241
-
242
- - LocationPicker getCurPosition 逻辑优化
243
-
244
- # @hzab/form-render0.4.1
245
-
246
- - LocationPicker useField 容错
247
-
248
- # @hzab/form-render0.4.0
249
-
250
- - LocationPicker 地图选点组件
251
- - UserSelect 去除 defaultOpen 属性
252
-
253
- # @hzab/form-render0.3.0
254
-
255
- - feat: TreeCheckbox
256
-
257
- # @hzab/form-render0.2.5
258
-
259
- - fix: ant-form-inline style
260
-
261
- # @hzab/form-render0.2.3
262
-
263
- - export Components
264
-
265
- # @hzab/form-render0.2.2
266
-
267
- - Upload beforeUploadCheck
268
-
269
- # @hzab/form-render0.2.1
270
-
271
- - Upload preview video、img、audio
272
-
273
- # @hzab/form-render0.2.0
274
-
275
- - 人员选择组件
276
-
277
- # @hzab/form-render0.1.0
278
-
279
- - Upload 组件
280
-
281
- # @hzab/form-render0.0.1
282
-
283
- 组件初始化
1
+ # @hzab/form-render@1.6.18
2
+
3
+ fix:修复人员选择组件单选模式自定义节点展示以及选中判断问题
4
+
5
+ # @hzab/form-render@1.6.17
6
+
7
+ fix: 修复上传组件,文件名和后缀名取值逻辑
8
+
9
+ # @hzab/form-render@1.6.16
10
+
11
+ fix: ArrayTable value 空问题兼容
12
+ fix: 修复人员选择组件中 isObject 函数 ts 类型不明确导致在部分项目中出现类型错误提示的问题
13
+
14
+ # @hzab/form-render@1.6.15
15
+
16
+ feat: 新增 formily js 的 change 事件
17
+
18
+ # @hzab/form-render@1.6.14
19
+
20
+ fix: 修复 TreeCheckbox 隐藏不能点击问题。
21
+
22
+ # @hzab/form-render@1.6.13
23
+
24
+ feat:locationListPicker 在已有元素范围内可新增地址标记
25
+
26
+ # @hzab/form-render@1.6.12
27
+
28
+ feat: PersonnelSelect 组件优化传值方式,支持复数据传入、返回
29
+
30
+ # @hzab/form-render@1.6.11
31
+
32
+ feat: editor 添加配置 merage
33
+
34
+ # @hzab/form-render@1.6.10
35
+
36
+ feat: 富文本编辑器实例像外暴露
37
+
38
+ # @hzab/form-render@1.6.9
39
+
40
+ feat: PersonnelSelect 组件
41
+
42
+ # @hzab/form-render@1.6.8
43
+
44
+ feat:富文本组件添加私有化部署文件上传与 oss 上传的区分
45
+
46
+ # @hzab/form-render@1.6.7
47
+
48
+ fix: 修复 LocationListPicker 组件在编辑时第一次无法正确渲染的 bug。
49
+
50
+ # @hzab/form-render@1.6.5
51
+
52
+ fix: 修复 LocationListPicker 组件在编辑时第一次无法正确渲染的 bug。
53
+ feat: LocationPicker 渲染时增加范围
54
+
55
+ # @hzab/form-render@1.6.3
56
+
57
+ fix: 修复 LocationListPicker 组件的 Pupup 弹窗在某些时候漂移的 bug。
58
+
59
+ # @hzab/form-render@1.6.2
60
+
61
+ feat:自增列表组件新增删除二次确认功能
62
+
63
+ # @hzab/form-render@1.6.1
64
+
65
+ fix:列表新增-上传组件删除后清除对应的值
66
+
67
+ # @hzab/form-render@1.6.0
68
+
69
+ feat: 私有部署文件上传
70
+ fix: 文件名称是否 hash 支持配置 useHashName
71
+
72
+ # @hzab/form-render@1.5.1
73
+
74
+ fix: schemaScope 使用 Ref 缓存,解决 onChange tempData 更新问题
75
+
76
+ # @hzab/form-render@1.5.0
77
+
78
+ feat: ossUpload 文件目录配置 dir 前缀 oss-upload 文件目录;dir 格式格式化
79
+ feat: 上传组件多模式上传,出参格式配置
80
+ fix: 字符串事件处理(onChange)
81
+
82
+ # @hzab/form-render@1.4.1
83
+
84
+ fix: Upload 修复历史遗留问题,解决 单项上传始终解析为 对象/字符串 无法切换会数组的情况
85
+
86
+ # @hzab/form-render@1.4.0
87
+
88
+ break: location-list-picker 添加多区域规划
89
+ fix: LocationListPicker 点位信息弹窗位置问题修复
90
+
91
+ # @hzab/form-render@1.3.3
92
+
93
+ fix: 富文本异步数据问题修复
94
+
95
+ # @hzab/form-render@1.3.2
96
+
97
+ fix: schema props 为 schemaScope 字符串的情况
98
+
99
+ # @hzab/form-render@1.3.1
100
+
101
+ feat: 表单 onChange 事件(目前仅做触发,值计算后续优化)
102
+
103
+ # @hzab/form-render@1.3.0
104
+
105
+ break: 去除 lib,默认 src
106
+
107
+ # @hzab/form-render@1.2.5
108
+
109
+ - fix: LocationListPicker 组件高度调整与删除点位修复
110
+
111
+ # @hzab/form-render@1.2.4
112
+
113
+ fix: ossUpload 入参问题修复
114
+ fix: UserSelect InfoRender
115
+
116
+ # @hzab/form-render@1.2.3
117
+
118
+ - fix: 富文本添加 loading 样式
119
+
120
+ # @hzab/form-render@1.2.2
121
+
122
+ - fix: Upload 预览 src(修复版)
123
+
124
+ # @hzab/form-render@1.2.1
125
+
126
+ - fix: 富文本 video 样式调整
127
+
128
+ # @hzab/form-render@1.2.0
129
+
130
+ - feat: Upload 增加 video 视频上传配置
131
+
132
+ # @hzab/form-render@1.1.7
133
+
134
+ - fix: Upload 预览 src
135
+
136
+ # @hzab/form-render@1.1.6
137
+
138
+ fix: 时间范围修复
139
+
140
+ # @hzab/form-render@1.1.5
141
+
142
+ fix: Upload 预览优化
143
+
144
+ # @hzab/form-render@1.1.4
145
+
146
+ fix: Upload 添加内部地址拼接
147
+
148
+ # @hzab/form-render@1.1.3
149
+
150
+ fix: Upload 添加预览配置
151
+
152
+ # @hzab/form-render@1.1.2
153
+
154
+ - feat: LocationListPicker 优化;列表激活状态、新增按钮提示、marker 激活状态及相关交互
155
+
156
+ # @hzab/form-render@1.1.1
157
+
158
+ - Upload OSS 上传支持数组对象格式值
159
+
160
+ # @hzab/form-render@1.1.0
161
+
162
+ - feat: LocationListPicker 组件
163
+
164
+ # @hzab/form-render@1.0.3
165
+
166
+ - fix: dayjs 自定义解析器,解决 YYYY 年 MM 月 DD 日 格式问题
167
+
168
+ # @hzab/form-render@1.0.2
169
+
170
+ - fix: Upload 预览文件判断逻辑
171
+
172
+ # @hzab/form-render@1.0.1
173
+
174
+ - fix: 提交结果为字符串处理 isOssUpload || isStrRes
175
+
176
+ # @hzab/form-render@1.0.0
177
+
178
+ - feat: @formily/antd 使用 c-formily-antd 替换,formily 相关包升级至 2.3.1 "@formily/core": "^2.3.1", "@formily/react": "^2.3.1", "c-formily-antd": "^2.3.1",
179
+
180
+ # @hzab/form-render@0.7.4
181
+
182
+ - RichEditor zIndex
183
+
184
+ # @hzab/form-render@0.7.3
185
+
186
+ - fix: Upload 通过 accept 判断文件类型
187
+
188
+ # @hzab/form-render@0.7.2
189
+
190
+ - fix: ossUpload axios 数据结构兼容
191
+
192
+ # @hzab/form-render@0.7.1
193
+
194
+ - Upload props customRequest
195
+
196
+ # @hzab/form-render@0.7.0
197
+
198
+ - 富文本组件 RichEditor
199
+
200
+ # @hzab/form-render@0.6.2
201
+
202
+ - fix: TreeCheckbox tabs 强制渲染,解决 checkbox 没有渲染导致值丢失的问题
203
+
204
+ # @hzab/form-render@0.6.1
205
+
206
+ - fix: LocationPicker MapSearch 只读模式选择结果只修改地图中心点
207
+ - fix: LocationPicker 解决 AMap 异步加载导致 setPickPoint 异常的问题
208
+
209
+ # @hzab/form-render@0.6.0
210
+
211
+ - feat: TreeCheckbox 支持无线层 tab
212
+ - feat: TreeCheckbox 支持合并叶子节点
213
+ - refactor: svg-icon.js -> svg-icon.jsx
214
+
215
+ # @hzab/form-render@0.5.5
216
+
217
+ - fix: TreeCheckbox key 问题修复
218
+ - fix: TreeCheckbox 数据兼容处理
219
+
220
+ # @hzab/form-render@0.5.4
221
+
222
+ - feat: LocationPicker mapInit 设置值取增加临时存储的 formatVal 的逻辑
223
+
224
+ # @hzab/form-render0.5.3
225
+
226
+ - feat: LocationPicker MapSearch 清除改为 input, map 请求优化
227
+
228
+ # @hzab/form-render0.5.2
229
+
230
+ - feat: TreeCheckbox tabBox 最外层允许配置成 tab
231
+
232
+ # @hzab/form-render0.5.1
233
+
234
+ - fix: LocationPicker 去除 changMode 参数,仅允许 点击地图选点
235
+ - fix: LocationPicker 获取地址逻辑修复
236
+ - fix: DatePicker 问题修复
237
+
238
+ # @hzab/form-render0.5.0
239
+
240
+ - fix: LocationPicker disabled || readOnly 不展示 Notice
241
+ - fix: LocationPicker 异步数据回填逻辑处理
242
+ - fix: LocationPicker 去除 isObjectRes 参数
243
+
244
+ # @hzab/form-render0.4.2
245
+
246
+ - LocationPicker getCurPosition 逻辑优化
247
+
248
+ # @hzab/form-render0.4.1
249
+
250
+ - LocationPicker useField 容错
251
+
252
+ # @hzab/form-render0.4.0
253
+
254
+ - LocationPicker 地图选点组件
255
+ - UserSelect 去除 defaultOpen 属性
256
+
257
+ # @hzab/form-render0.3.0
258
+
259
+ - feat: TreeCheckbox
260
+
261
+ # @hzab/form-render0.2.5
262
+
263
+ - fix: ant-form-inline style
264
+
265
+ # @hzab/form-render0.2.3
266
+
267
+ - export Components
268
+
269
+ # @hzab/form-render0.2.2
270
+
271
+ - Upload beforeUploadCheck
272
+
273
+ # @hzab/form-render0.2.1
274
+
275
+ - Upload preview video、img、audio
276
+
277
+ # @hzab/form-render0.2.0
278
+
279
+ - 人员选择组件
280
+
281
+ # @hzab/form-render0.1.0
282
+
283
+ - Upload 组件
284
+
285
+ # @hzab/form-render0.0.1
286
+
287
+ 组件初始化
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/form-render",
3
- "version": "1.6.17",
3
+ "version": "1.6.18",
4
4
  "description": "",
5
5
  "main": "src",
6
6
  "scripts": {
@@ -1,300 +1,305 @@
1
- import React, { useEffect, useState, useRef, useCallback, useMemo } from "react";
2
- import { Select, List, Avatar, Spin } from "antd";
3
- import { connect, mapProps } from "@formily/react";
4
- import { debounce, isObject, isPlainObject } from "lodash";
5
- import type { LabelValue, ScrollPagination, RemoteSelectProps } from "./type";
6
-
7
- import "./index.less";
8
-
9
- const defaultListItemConfigs = [
10
- { label: "姓名:", key: "userName" },
11
- { label: "主部门:", key: "parentName" },
12
- { label: "主驻点:", key: "orgName" },
13
- { label: "手机号:", key: "phoneNumber" },
14
- { label: "身份证号:", key: "idnumber" },
15
- ];
16
-
17
- const transfromLabelInValueData = (list: LabelValue[], labelKey: string, valueKey: string): LabelValue[] => {
18
- try {
19
- return list.map((item) => ({ ...item, label: item?.[labelKey], value: item?.[valueKey] }));
20
- } catch (e) {
21
- console.error(e);
22
- return list;
23
- }
24
- };
25
-
26
- const RemoteSelect: React.FC<RemoteSelectProps> = ({
27
- loadData,
28
- searchData,
29
- renderItem,
30
- listItemConfigs,
31
- labelKey = "userName",
32
- valueKey = "userId",
33
- disabledKey = "disabled",
34
- initialPagination = {},
35
- avataProps = {},
36
- disabledStyle,
37
- customItemNode,
38
- labelInValue = false,
39
- isClearable = false,
40
- ...selectProps
41
- }) => {
42
- const [search, setSearch] = useState("");
43
- const [loading, setLoading] = useState(false);
44
- const [loadingMore, setLoadingMore] = useState(false);
45
- const [list, setList] = useState<LabelValue[]>([]);
46
- const [cacheDeafultList, setCacheDeafultList] = useState<LabelValue[]>([]);
47
- const [pagination, setPagination] = useState<ScrollPagination>({
48
- pageNum: 1,
49
- pageSize: 10,
50
- ...initialPagination,
51
- });
52
- const selectInstance = useRef<any>(null);
53
- const total = useRef(0);
54
- const initSearch = useRef(true);
55
-
56
- const debounceLoadData = debounce(async (isScrollLoad = false) => {
57
- try {
58
- isScrollLoad ? setLoadingMore(true) : setLoading(true);
59
-
60
- const result = await loadData(search, {
61
- ...pagination,
62
- pageNum: isScrollLoad ? pagination.pageNum + 1 : 1,
63
- });
64
- const { list, pagination: resPagination } = result;
65
-
66
- total.current = resPagination.total;
67
-
68
- const transformatList: LabelValue[] = transfromLabelInValueData(list, labelKey, valueKey);
69
-
70
- setList((prev) => (isScrollLoad ? [...prev, ...transformatList] : transformatList));
71
- } finally {
72
- isScrollLoad ? setLoadingMore(false) : setLoading(false);
73
- }
74
- }, 500);
75
-
76
- const debounceHandleScroll = async (e: React.UIEvent<HTMLDivElement>) => {
77
- const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
78
- const isNearBottom = scrollHeight - scrollTop - clientHeight < 30;
79
-
80
- if (isNearBottom && !loadingMore && list.length <= total.current) {
81
- await getData(true);
82
- setPagination((pre) => ({ ...pre, pageNum: pagination.pageNum + 1 }));
83
- }
84
- };
85
-
86
- const getData = useCallback(debounceLoadData, [search, pagination, loadData]);
87
-
88
- // 处理滚动加载
89
- const handleScroll = useCallback(debounceHandleScroll, [search, loadingMore, loadData]);
90
-
91
- // 重置list滚动条
92
- const resetListScroll = () => {
93
- const listNode = document.getElementById("abt-list");
94
- listNode && listNode.scrollTo({ top: 0, behavior: "auto" });
95
- };
96
-
97
- // 处理搜索
98
- const handleSearch = debounce((value: string) => {
99
- resetListScroll();
100
- setSearch(value);
101
- setPagination((prev) => ({ ...prev, pageNum: 1 }));
102
- }, 1000);
103
-
104
- const handleOnDropdownVisibleChange = (open: boolean) => {
105
- if (!isClearable) return;
106
- if (open) {
107
- getData();
108
- } else {
109
- resetListScroll();
110
- setList([]);
111
- setPagination((prev) => ({ ...prev, pageNum: 1 }));
112
- }
113
- };
114
-
115
- // 处理选项点击事件
116
- const handleItemClick = (item: LabelValue) => {
117
- const { value: currentValue, mode, onChange } = selectProps;
118
- const itemId = item.value;
119
-
120
- // 多选模式处理
121
- const handleMultipleSelection = () => {
122
- const selectedOptions: any[] = Array.isArray(currentValue) ? currentValue : [currentValue];
123
-
124
- if (labelInValue) {
125
- const isSelected = selectedOptions.some((opt) => opt.value === itemId);
126
-
127
- return isSelected
128
- ? selectedOptions.filter((opt) => opt.value !== itemId) // 移除已选项
129
- : [...selectedOptions, item]; // 添加新选项
130
- }
131
-
132
- const isSelected = selectedOptions.some((opt) => opt === itemId);
133
-
134
- return isSelected
135
- ? selectedOptions.filter((opt) => opt !== itemId) // 移除已选项
136
- : [...selectedOptions, item]; // 添加新选项
137
- };
138
-
139
- // 单选模式处理
140
- const handleSingleSelection = () => {
141
- const isCurrentSelected = (currentValue as LabelValue)?.value === itemId;
142
- return isCurrentSelected ? undefined : item;
143
- };
144
-
145
- let resultValue: string | number | any[] | LabelValue;
146
-
147
- // 执行模式对应处理
148
- const newValue = mode === "multiple" ? handleMultipleSelection() : handleSingleSelection();
149
-
150
- if (mode === "multiple") {
151
- if (labelInValue) resultValue = newValue;
152
- else resultValue = newValue.map((x: LabelValue | string | number) => (typeof x === "object" ? x?.value : x));
153
- } else {
154
- labelInValue ? (resultValue = newValue) : (resultValue = (newValue as unknown as LabelValue).value);
155
- }
156
-
157
- // 触发变更回调
158
- onChange?.(resultValue, currentValue as any);
159
-
160
- // 单选模式自动关闭下拉框
161
- if (mode !== "multiple" && selectInstance.current) selectInstance.current.blur();
162
- };
163
-
164
- // 合并配置
165
- const itemConfigs = useMemo(() => {
166
- if (Array.isArray(listItemConfigs) && listItemConfigs?.length) return listItemConfigs;
167
- return defaultListItemConfigs;
168
- }, [listItemConfigs]);
169
-
170
- useEffect(() => {
171
- getData();
172
- }, [search]);
173
- useEffect(() => {
174
- // 当value为一个id或者id数组时,需要进行数据查询用以回显label等信息
175
- if (!labelInValue && selectProps.value && typeof searchData === "function" && initSearch.current) {
176
- searchData?.(selectProps.value).then((res: Record<string, any>) => {
177
- setCacheDeafultList(res?.list || []);
178
- setList(res?.list || []);
179
- });
180
- // 只有初始化时执行
181
- initSearch.current = false;
182
- }
183
- }, [selectProps.value]);
184
-
185
- // 默认渲染项(增加选中状态和点击处理)
186
- const defaultRenderItem = useCallback(
187
- (item: LabelValue) => {
188
- const { mode, value: selectedValue } = selectProps;
189
- const { value, [disabledKey]: isDisabled = false } = item;
190
- // 选中状态判断
191
- const getSelectionStatus = () => {
192
- if (mode === "multiple") {
193
- if (!Array.isArray(selectedValue)) return false;
194
- const selectedValues = (selectedValue || [])?.map((opt) =>
195
- (isObject(opt) as unknown as Record<string, any>) ? opt?.value ?? opt?.value : opt,
196
- );
197
- return selectedValues.includes(value);
198
- }
199
-
200
- if (Array.isArray(cacheDeafultList)) {
201
- return cacheDeafultList.map((x) => x.value).includes(value);
202
- }
203
-
204
- return isObject(selectedValue) ? (selectedValue as LabelValue)?.value === value : selectedValue === value;
205
- };
206
-
207
- // 禁用样式生成
208
- const generateStyle = (disabled: boolean): React.CSSProperties => {
209
- if (typeof disabledStyle === "function") {
210
- return disabledStyle(disabled);
211
- }
212
-
213
- return {
214
- cursor: disabled ? "not-allowed" : "pointer",
215
- backgroundColor: disabled ? "#f0f0f0" : getSelectionStatus() ? "#e6f7ff" : "inherit",
216
- color: disabled ? "#999" : "inherit",
217
- filter: disabled ? "grayscale(100%)" : "none",
218
- };
219
- };
220
-
221
- // 头像组件
222
- const avatarComponent = <Avatar alt={item.userName} size={60} {...avataProps} src={item.avatar} />;
223
-
224
- // 用户信息列表
225
- const userInfoContent = (
226
- <ul className="abt-user-item-info">
227
- {itemConfigs.map((configItem, index) => (
228
- <li key={configItem?.key || index} className="abt-user-item-info__item">
229
- <span>{configItem?.label}</span>
230
- <span>{item?.[configItem.key]}</span>
231
- </li>
232
- ))}
233
- </ul>
234
- );
235
-
236
- return (
237
- <List.Item key={value} onClick={() => !isDisabled && handleItemClick(item)} style={generateStyle(isDisabled)}>
238
- {customItemNode || (
239
- <div className="abt-user-item" key={item?.value}>
240
- <div className="abt-user-item-avatar">{avatarComponent}</div>
241
- {userInfoContent}
242
- </div>
243
- )}
244
- </List.Item>
245
- );
246
- },
247
- [selectProps.value, list?.length],
248
- );
249
-
250
- // 自定义下拉内容
251
- const dropdownRender = () => (
252
- <div>
253
- <Spin spinning={loading}>
254
- <List
255
- id={"abt-list"}
256
- loading={loadingMore}
257
- dataSource={list}
258
- renderItem={renderItem ? renderItem : defaultRenderItem}
259
- /*@ts-ignore*/
260
- onScroll={handleScroll}
261
- style={{ maxHeight: 250, overflowY: "auto" }}
262
- />
263
- </Spin>
264
- </div>
265
- );
266
-
267
- return (
268
- <Select
269
- {...selectProps}
270
- ref={selectInstance}
271
- onDropdownVisibleChange={handleOnDropdownVisibleChange}
272
- showSearch
273
- onSearch={handleSearch}
274
- filterOption={false}
275
- dropdownRender={dropdownRender}
276
- placeholder="请选择人员"
277
- options={list.map((item) => ({
278
- value: item.userId,
279
- label: item.userName,
280
- ...item,
281
- }))}
282
- />
283
- );
284
- };
285
-
286
- const PersonnelSelect = connect(
287
- RemoteSelect,
288
- mapProps(
289
- {
290
- loading: true,
291
- },
292
- (props) => {
293
- return {
294
- ...props,
295
- };
296
- },
297
- ),
298
- );
299
-
300
- export { RemoteSelect, PersonnelSelect };
1
+ import React, { useEffect, useState, useRef, useCallback, useMemo } from "react";
2
+ import { Select, List, Avatar, Spin } from "antd";
3
+ import { connect, mapProps } from "@formily/react";
4
+ import { debounce, isObject } from "lodash";
5
+ import type { LabelValue, ScrollPagination, RemoteSelectProps } from "./type";
6
+
7
+ import "./index.less";
8
+
9
+ const defaultListItemConfigs = [
10
+ { label: "姓名:", key: "userName" },
11
+ { label: "主部门:", key: "parentName" },
12
+ { label: "主驻点:", key: "orgName" },
13
+ { label: "手机号:", key: "phoneNumber" },
14
+ { label: "身份证号:", key: "idnumber" },
15
+ ];
16
+
17
+ const transfromLabelInValueData = (list: LabelValue[], labelKey: string, valueKey: string): LabelValue[] => {
18
+ try {
19
+ return list.map((item) => ({ ...item, label: item?.[labelKey], value: item?.[valueKey] }));
20
+ } catch (e) {
21
+ console.error(e);
22
+ return list;
23
+ }
24
+ };
25
+
26
+ const RemoteSelect: React.FC<RemoteSelectProps> = ({
27
+ loadData,
28
+ searchData,
29
+ renderItem,
30
+ listItemConfigs,
31
+ labelKey = "userName",
32
+ valueKey = "userId",
33
+ disabledKey = "disabled",
34
+ initialPagination = {},
35
+ avataProps = {},
36
+ disabledStyle,
37
+ customItemNode,
38
+ labelInValue = false,
39
+ isClearable = false,
40
+ ...selectProps
41
+ }) => {
42
+ const [search, setSearch] = useState("");
43
+ const [loading, setLoading] = useState(false);
44
+ const [loadingMore, setLoadingMore] = useState(false);
45
+ const [list, setList] = useState<LabelValue[]>([]);
46
+ // const [cacheDeafultList, setCacheDeafultList] = useState<LabelValue[]>([]);
47
+ const [pagination, setPagination] = useState<ScrollPagination>({
48
+ pageNum: 1,
49
+ pageSize: 10,
50
+ ...initialPagination,
51
+ });
52
+ const selectInstance = useRef<any>(null);
53
+ const total = useRef(0);
54
+ const initSearch = useRef(true);
55
+
56
+ const debounceLoadData = debounce(async (isScrollLoad = false) => {
57
+ try {
58
+ isScrollLoad ? setLoadingMore(true) : setLoading(true);
59
+
60
+ const result = await loadData(search, {
61
+ ...pagination,
62
+ pageNum: isScrollLoad ? pagination.pageNum + 1 : 1,
63
+ });
64
+ const { list, pagination: resPagination } = result;
65
+
66
+ total.current = resPagination.total;
67
+
68
+ const transformatList: LabelValue[] = transfromLabelInValueData(list, labelKey, valueKey);
69
+
70
+ setList((prev) => (isScrollLoad ? [...prev, ...transformatList] : transformatList));
71
+ } finally {
72
+ isScrollLoad ? setLoadingMore(false) : setLoading(false);
73
+ }
74
+ }, 500);
75
+
76
+ const debounceHandleScroll = async (e: React.UIEvent<HTMLDivElement>) => {
77
+ const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
78
+ const isNearBottom = scrollHeight - scrollTop - clientHeight < 30;
79
+
80
+ if (isNearBottom && !loadingMore && list.length <= total.current) {
81
+ await getData(true);
82
+ setPagination((pre) => ({ ...pre, pageNum: pagination.pageNum + 1 }));
83
+ }
84
+ };
85
+
86
+ const getData = useCallback(debounceLoadData, [search, pagination, loadData]);
87
+
88
+ // 处理滚动加载
89
+ const handleScroll = useCallback(debounceHandleScroll, [search, loadingMore, loadData]);
90
+
91
+ // 重置list滚动条
92
+ const resetListScroll = () => {
93
+ const listNode = document.getElementById("abt-list");
94
+ listNode && listNode.scrollTo({ top: 0, behavior: "auto" });
95
+ };
96
+
97
+ // 处理搜索
98
+ const handleSearch = debounce((value: string) => {
99
+ resetListScroll();
100
+ setSearch(value);
101
+ setPagination((prev) => ({ ...prev, pageNum: 1 }));
102
+ }, 1000);
103
+
104
+ const handleOnDropdownVisibleChange = (open: boolean) => {
105
+ if (!isClearable) return;
106
+ if (open) {
107
+ getData();
108
+ } else {
109
+ reset();
110
+ }
111
+ };
112
+
113
+ const reset = () => {
114
+ setSearch("");
115
+ resetListScroll();
116
+ setList([]);
117
+ setPagination((prev) => ({ ...prev, pageNum: 1 }));
118
+ };
119
+
120
+ // 处理选项点击事件
121
+ const handleItemClick = (item: LabelValue) => {
122
+ const { value: currentValue, mode, onChange } = selectProps;
123
+ const itemId = item.value;
124
+
125
+ // 多选模式处理
126
+ const handleMultipleSelection = () => {
127
+ const selectedOptions: any[] = Array.isArray(currentValue) ? currentValue : [currentValue];
128
+
129
+ if (labelInValue) {
130
+ const isSelected = selectedOptions.some((opt) => opt.value === itemId);
131
+
132
+ return isSelected
133
+ ? selectedOptions.filter((opt) => opt.value !== itemId) // 移除已选项
134
+ : [...selectedOptions, item]; // 添加新选项
135
+ }
136
+
137
+ const isSelected = selectedOptions.some((opt) => opt === itemId);
138
+
139
+ return isSelected
140
+ ? selectedOptions.filter((opt) => opt !== itemId) // 移除已选项
141
+ : [...selectedOptions, item]; // 添加新选项
142
+ };
143
+
144
+ // 单选模式处理
145
+ const handleSingleSelection = () => {
146
+ const isCurrentSelected = (currentValue as LabelValue)?.value === itemId;
147
+ return isCurrentSelected ? undefined : item;
148
+ };
149
+
150
+ let resultValue: string | number | any[] | LabelValue;
151
+
152
+ // 执行模式对应处理
153
+ const newValue = mode === "multiple" ? handleMultipleSelection() : handleSingleSelection();
154
+
155
+ if (mode === "multiple") {
156
+ if (labelInValue) resultValue = newValue;
157
+ else resultValue = newValue.map((x: LabelValue | string | number) => (typeof x === "object" ? x?.value : x));
158
+ } else {
159
+ labelInValue ? (resultValue = newValue) : (resultValue = (newValue as unknown as LabelValue).value);
160
+ }
161
+
162
+ // 触发变更回调
163
+ onChange?.(resultValue, currentValue as any);
164
+
165
+ // 单选模式自动关闭下拉框
166
+ if (mode !== "multiple" && selectInstance.current) selectInstance.current.blur();
167
+ };
168
+
169
+ // 合并配置
170
+ const itemConfigs = useMemo(() => {
171
+ if (Array.isArray(listItemConfigs) && listItemConfigs?.length) return listItemConfigs;
172
+ return defaultListItemConfigs;
173
+ }, [listItemConfigs]);
174
+
175
+ useEffect(() => {
176
+ getData();
177
+ }, [search]);
178
+ useEffect(() => {
179
+ // 当value为一个id或者id数组时,需要进行数据查询用以回显label等信息
180
+ if (selectProps.value && typeof searchData === "function" && initSearch.current) {
181
+ searchData?.(selectProps.value).then((res: Record<string, any>) => {
182
+ const listData = transfromLabelInValueData(res.list || [], labelKey, valueKey);
183
+ // setCacheDeafultList(listData);
184
+ setList(listData);
185
+ });
186
+ // 只有初始化时执行
187
+ initSearch.current = false;
188
+ }
189
+ }, [selectProps.value]);
190
+
191
+ // 默认渲染项(增加选中状态和点击处理)
192
+ const defaultRenderItem = useCallback(
193
+ (item: LabelValue) => {
194
+ const { mode, value: selectedValue } = selectProps;
195
+ const { value, [disabledKey]: isDisabled = false } = item;
196
+ // 选中状态判断
197
+ const getSelectionStatus = () => {
198
+ if (mode === "multiple") {
199
+ if (!Array.isArray(selectedValue)) return false;
200
+ const selectedValues = (selectedValue || [])?.map((opt) =>
201
+ (isObject(opt) as unknown as Record<string, any>) ? opt?.value ?? opt?.value : opt,
202
+ );
203
+ return selectedValues.includes(value);
204
+ }
205
+
206
+ return isObject(selectedValue) ? (selectedValue as LabelValue)?.value === value : selectedValue === value;
207
+ };
208
+
209
+ // 禁用样式生成
210
+ const generateStyle = (disabled: boolean): React.CSSProperties => {
211
+ if (typeof disabledStyle === "function") {
212
+ return disabledStyle(disabled);
213
+ }
214
+
215
+ return {
216
+ cursor: disabled ? "not-allowed" : "pointer",
217
+ backgroundColor: disabled ? "#f0f0f0" : getSelectionStatus() ? "#e6f7ff" : "inherit",
218
+ color: disabled ? "#999" : "inherit",
219
+ filter: disabled ? "grayscale(100%)" : "none",
220
+ };
221
+ };
222
+
223
+ // 头像组件
224
+ const avatarComponent = <Avatar alt={item.userName} size={60} {...avataProps} src={item.avatar} />;
225
+
226
+ // 用户信息列表
227
+ const userInfoContent = (
228
+ <ul className="abt-user-item-info">
229
+ {itemConfigs.map((configItem, index) => (
230
+ <li key={configItem?.key || index} className="abt-user-item-info__item">
231
+ <span>{configItem?.label}</span>
232
+ <span>{item?.[configItem.key]}</span>
233
+ </li>
234
+ ))}
235
+ </ul>
236
+ );
237
+ const itemStyle = generateStyle(isDisabled);
238
+
239
+ const renderCustomItemNode = typeof customItemNode === "function" ? customItemNode?.(item, itemStyle) : null;
240
+
241
+ return (
242
+ <List.Item key={value} onClick={() => !isDisabled && handleItemClick(item)} style={itemStyle}>
243
+ {renderCustomItemNode || (
244
+ <div className="abt-user-item" key={item?.value}>
245
+ <div className="abt-user-item-avatar">{avatarComponent}</div>
246
+ {userInfoContent}
247
+ </div>
248
+ )}
249
+ </List.Item>
250
+ );
251
+ },
252
+ [selectProps.value, list?.length],
253
+ );
254
+
255
+ // 自定义下拉内容
256
+ const dropdownRender = () => (
257
+ <div>
258
+ <Spin spinning={loading}>
259
+ <List
260
+ id={"abt-list"}
261
+ loading={loadingMore}
262
+ dataSource={list}
263
+ renderItem={renderItem ? renderItem : defaultRenderItem}
264
+ /*@ts-ignore*/
265
+ onScroll={handleScroll}
266
+ style={{ maxHeight: 250, overflowY: "auto" }}
267
+ />
268
+ </Spin>
269
+ </div>
270
+ );
271
+
272
+ return (
273
+ <Select
274
+ filterOption={false}
275
+ showSearch
276
+ placeholder="请选择人员"
277
+ {...selectProps}
278
+ ref={selectInstance}
279
+ onDropdownVisibleChange={handleOnDropdownVisibleChange}
280
+ onSearch={handleSearch}
281
+ dropdownRender={dropdownRender}
282
+ options={list.map((item) => ({
283
+ value: item?.[valueKey] || item?.userId,
284
+ label: item?.[labelKey] || item.userName,
285
+ ...item,
286
+ }))}
287
+ />
288
+ );
289
+ };
290
+
291
+ const PersonnelSelect = connect(
292
+ RemoteSelect,
293
+ mapProps(
294
+ {
295
+ loading: true,
296
+ },
297
+ (props) => {
298
+ return {
299
+ ...props,
300
+ };
301
+ },
302
+ ),
303
+ );
304
+
305
+ export { RemoteSelect, PersonnelSelect };